This PR introduces powerful code size optimizations. This is achieved
with an additional IR layer in between Yul and LLVM IR. It works quite
well (see below for a comparison) because it captures optimization gains
that neither solc nor LLVM can realize.
The feature is gated behind `--newyork` or a corresponding standard JSON
input setting and **off by default**.
### How it works
We implemented an SSA IR modelled after or inspired by the [MLIR SCF
dialect](https://mlir.llvm.org/docs/Dialects/SCFDialect/). Yul is
translated into this IR and the IR implements mainly the following
optimizations:
- **Type narrowing**: narrows 256-bit values to I1..I160 where provable
(bidirectional + interprocedural), so LLVM emits native 32/64-bit ops
instead
of software 256-bit arithmetic.
- **FMP range proof**: exposes the `mload(0x40) < heap_size` bound to
LLVM
IPSCCP, eliminating trailing overflow checks on FMP-derived offsets.
Single
largest code-size contributor.
- **Heap analysis**: skips big-endian byte-swaps where native
little-endian
layout is safe.
- **mem_opt / keccak fusion+folding**, **compound outlining** (mapping
sload/sstore), **guard narrowing**, **inlining** tuned for PolkaVM, and
**exact + fuzzy function dedup**.
### Results
The optimizations implemented with the newyork IR bring substantial code
size improvements.
- Integration contracts: approximately **-20% to -50%** code size.
- OpenZeppelin Wizard corpus: around **-47%** code size.
This table shows the code size improvements on various OpenZeppelin ERC
implementations, taken from their [contracts
wizard](https://wizard.openzeppelin.com/), with almost all features
enabled.
| Contract | Default: (bytes) | With `--newyork`: (bytes) | Reduction |
|---|---:|---:|---:|
| erc1155 | 59,206 | 30,649 | -48.2% |
| erc20 | 81,697 | 42,893 | -47.5% |
| erc721 | 92,052 | 49,423 | -46.3% |
| oz_gov | 141,861 | 81,159 | -42.8% |
| oz_rwa | 77,274 | 37,975 | -50.9% |
| oz_simple_erc20 | 29,950 | 16,554 | -44.7% |
| oz_stable | 82,117 | 39,032 | -52.5% |
| **Total** | **564,157** | **297,685** | **-47.2%** |
We believe this to be a reasonably representative dataset and conclude
that the newyork IR achieves drastic code size reductions on real world
dApp implementations.
### Methodology, security and runtime performance
Since the main contributors to these improvements are the FMP range
proofs and the type narrowing, we don't expect big runtime performance
regressions (it may even benefit runtime performance in some cases).
The PR also includes docs
(`book/src/developer_guide/newyork_{optimizer,ir}.md`) and many new
integration tests, including regression tests for known FMP soundness
traps.
The PR was authored collaboratively and reviewed extensively by the
`revive` maintainers, as well as Claude Opus, Claude Fable, Qwen,
Minimax and Deepseek LLMs, over a span of many months.
The `newyork` compiler pipeline introduces no new regressions over our
integration and differential test suites (containing over 30000 test
cases). This was achieved by careful manual reviews and many LLM bughunt
loops. Additionally, a final security review by Anthropic's Fable 5 LLM
found no remaining soundness issues. Nonetheless, like any brand new
software, please consider this an experimental feature upon release.
---------
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: neo <me@90f9.org>
Co-authored-by: LJ <81748770+elle-j@users.noreply.github.com>
Existing test coverage didn't catch a signed remainder overflow
semantics edge case behavior where at least one the operators is being
constant folded by LLVM instead of solc. This PR fixes the SREM
translation bug and adds missing test coverage for division ops with
constants.
Closes#524
---------
Signed-off-by: xermicus <cyrill@parity.io>
- Recursive processes spawn the currently running executable by default,
removing the annoyance of mixing different compiler builds during
contract compilation.
- Remove the unused `once_cell` dependency.
Signed-off-by: xermicus <bigcyrill@hotmail.com>
- Remove the superfluous byte swap in the `linkersymbol` builtin
translation.
- Support testing the `linkersymbol` builtin translation in the runner
specs.
---------
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
- Fixes multiple bugs in the Yul test util helpers.
- The simple Yul builder utility helper now returns the contract builds.
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Prevents unused functions in the emitted ELF object. Drive-by add a
missing test case (which misses a relocation under `-Oz` when all
internal functions are marked as private).
---------
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Separate between compilation and linker phases to allow deploy time
linking and back-porting era compiler changes to fix#91. Unlinked
contract binaries (caused by missing libraries or missing factory
dependencies in turn) are emitted as raw ELF object.
Few drive by fixes:
- #98
- A compiler panic on missing libraries definitions.
- Fixes some incosistent type forwarding in JSON output (empty string
vs. null object).
- Remove the unused fallback for size optimization setting.
- Remove the broken `--lvm-ir` mode.
- CI workflow fixes.
---------
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
This is to address issue #320
## Introduced changes
- Added new_required_for_tests() method that includes EVM bytecode flags
- Modified new_required() to exclude evm.bytecode and
evm.deployedBytecode
- Updated test utilities to explicitly request EVM bytecode when needed
Signed-off-by: 0xf333 <0x333@tuta.io>
- Factor the YUL crate out of `revive-solidity`.
- `revive-solidity` is in reality not a Solidity implementation but the
revive solidity compiler driver (`resolc`). By renaming we not only get
this straight but also a binary with the same name as the crate which
should be less confusing.
---------
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>