resolc v1.4.0
Supported `polkadot-sdk` rev: `2604.2.0`
### Added
- Support for solc v0.8.36 and the "Amsterdam" EVM version.
### Fixed
- Restored the top-level `revive_version` field in `--standard-json`
output and the `resolc_version` field in `--combined-json` output, both
of which stopped being populated in `v0.4.0`.
[#557](https://github.com/paritytech/revive/pull/557)
- ICE triggered by `--disable-solc-optimizer --newyork` where inlined
loops could emit an unterminated basic block in the inlined entrypoint.
[#561](https://github.com/paritytech/revive/pull/561)
- `--newyork`: dead-store elimination could drop a store whose memory
expansion an intervening `msize()` observes, so `msize()` read a stale
(unexpanded) value.
[#565](https://github.com/paritytech/revive/pull/565)
- `--newyork`: dead-store elimination could drop a store that a later
overlapping unaligned `mload` reads, returning zeroed memory instead of
the stored bytes. [#563](https://github.com/paritytech/revive/pull/563)
- `--newyork`: an unaligned full-word `mload`/`mstore` overlapping a
word kept native (little-endian) by the heap optimization returned
byte-swapped bytes.
[#562](https://github.com/paritytech/revive/pull/562)
- `--newyork`: a `calldatacopy`/`codecopy`/`returndatacopy` to a dynamic
destination that could overwrite the free-memory-pointer word truncated
a subsequent `mload(0x40)` to `0`.
[#564](https://github.com/paritytech/revive/pull/564)
- `--newyork`: a full-width value stored to the free-memory-pointer slot
(e.g. a function parameter `>= 2^64`) was narrowed to 64 bits, inserting
a checked truncation that consumed all gas.
[#567](https://github.com/paritytech/revive/pull/567)
- `--newyork`: an unaligned `mstore` corrupting the free-memory-pointer
word let a later store through the corrupted pointer silently succeed
where EVM runs out of gas.
[#566](https://github.com/paritytech/revive/pull/566)
- Fixed a bytecode divergence (yet functionally equivalent) between the
resolc Linux/macOS builds and the Windows build for contracts with loops
indexing array elements at constant offsets from a `uint8` loop variable
(e.g. `array[i]`/`array[i + 1]`), compiled with `--newyork` at `-Oz`
with the solc optimizer disabled.
[#570](https://github.com/paritytech/revive/pull/570)
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
## v1.3.0
Supported `polkadot-sdk` rev: `2604.2.0`
### Added
- The newyork IR code size optimization pipeline, selectable via the
`--newyork` CLI flag or the `settings.polkavm.newyork` standard JSON
input field.
This can reduce code size by up to 50%. Considered a new and
experimental feature, switched off by default.
[#519](https://github.com/paritytech/revive/pull/519)
### Changed
- The latest supported `polkadot-sdk` version is now `2604.2.0`
Signed-off-by: xermicus <cyrill@parity.io>
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>
Supported `polkadot-sdk` rev: `2603`
### Changed
- Updated `LLVM` from `21.1.8` to LLVM `22.1.4`
### Added
- Support for solc v0.8.35.
### Fixed
- A constant folding bug causing incorrect translation of a signed
remainder edge case. The affected pattern (Solidity % or Yul smod):
- SREM over the two constant operands `type(int256).min` and `-1`.
- At least one of the two constants needs folding (i.e. not just
`type(int256).min % -1`),
and is written specifically such that solc doesn't fold but LLVM does,
resulting in UB (signed-overflow).
- Fix `mulmod` returning out-of-range result for large moduli.
---------
Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This marks a milestone: Smart contracts on Polkadot Asset Hub are live
and we release the first compiler version!
Signed-off-by: xermicus <bigcyrill@hotmail.com>
- 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>
- Add the revive runtime function interface to minimize boiler plate
code.
- Outline heavily repeated code into dedicated functions to bring down
code size.
- The code size tests builds optimized for size.
- Function attributes are passed as slices.
This significantly brings down the code size for all OpenZeppelin wizard
contracts (using all possible features) compiled against OpenZeppelin
`v5.0.0` with size optimizations.
|contract|| `-Oz` main | `-Oz` PR || `-O3` main | `-O3` PR |
|-|-|-|-|-|-|-|
|erc1155.sol||100K|67K||114K|147K|
|erc20.sol||120K|90K||160K|191K|
|erc721.sol||128K|101K||178K|214K|
|governor.sol||226K|165K||293K|349K|
|rwa.sol||116K|85K||154K|185K|
|stable.sol||116K|86K||155K|192K|
On the flip side this introduces a heavy penalty for cycle optimized
builds. Setting the no-inline attributes for cycle optimized builds
helps a lot but heavily penalizes runtime speed (LLVM does not yet
inline everything properly - to be investigated later on).
Next steps:
- Modularize more functions
- Refactor the YUL function arguments to use pointers instead of values
- Afterwards check if LLVM still has trouble inline-ing properly on O3
or set the no-inline attribute if it does not penalize runtime
performance too bad.