From 11f82c84888f0926b0dce16f665e4d3a6221b926 Mon Sep 17 00:00:00 2001 From: xermicus Date: Fri, 5 Dec 2025 15:25:13 +0100 Subject: [PATCH] Support solc v0.8.31 (#430) - Support for solc v0.8.31. - Support for the `clz` Yul builtin. --------- Signed-off-by: xermicus --- .github/actions/get-solc/action.yml | 2 +- .github/workflows/reusable-build.yml | 2 +- CHANGELOG.md | 2 + crates/common/src/evm_version.rs | 10 ++++ crates/differential/genesis.json | 14 ++++- .../contracts/CountLeadingZeros.sol | 57 +++++++++++++++++++ crates/integration/src/tests.rs | 1 + .../polkavm/context/function/intrinsics.rs | 19 ++++++- .../llvm-context/src/polkavm/evm/bitwise.rs | 17 ++++++ crates/resolc/src/solc/mod.rs | 2 +- .../statement/expression/function_call/mod.rs | 8 +++ .../expression/function_call/name.rs | 4 ++ js/emscripten/build.js | 2 +- js/emscripten/package.json | 2 +- js/resolc/package.json | 2 +- package-lock.json | 9 ++- 16 files changed, 142 insertions(+), 11 deletions(-) create mode 100644 crates/integration/contracts/CountLeadingZeros.sol diff --git a/.github/actions/get-solc/action.yml b/.github/actions/get-solc/action.yml index 256c13c..5d9ffe4 100644 --- a/.github/actions/get-solc/action.yml +++ b/.github/actions/get-solc/action.yml @@ -19,7 +19,7 @@ runs: shell: bash run: | mkdir -p solc - curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.30/${SOLC_NAME} + curl -sSL --output solc/solc https://github.com/ethereum/solidity/releases/download/v0.8.31/${SOLC_NAME} - name: Make Solc Executable if: ${{ runner.os == 'Windows' }} diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index cc2cd1c..ec3354f 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -177,7 +177,7 @@ jobs: - name: Basic Sanity Check run: | mkdir -p solc - curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.30/soljson.js + curl -sSLo solc/soljson.js https://github.com/ethereum/solidity/releases/download/v0.8.31/soljson.js node -e " const soljson = require('solc/soljson'); const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js'); diff --git a/CHANGELOG.md b/CHANGELOG.md index 60bfb4d..113d8a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Supported `polkadot-sdk` rev: `2509.0.0` ### Added - The comprehensive revive compiler book documentation page: https://paritytech.github.io/revive/ +- Support for solc v0.8.31. +- Support for the `clz` Yul builtin. ### Changed - Instruct the LLVM backend and linker to `--relax` (may lead to smaller contract code size). diff --git a/crates/common/src/evm_version.rs b/crates/common/src/evm_version.rs index 041202b..32227a1 100644 --- a/crates/common/src/evm_version.rs +++ b/crates/common/src/evm_version.rs @@ -43,6 +43,12 @@ pub enum EVMVersion { /// The corresponding EVM version. #[serde(rename = "cancun")] Cancun, + /// The corresponding EVM version. + #[serde(rename = "prague")] + Prague, + /// The corresponding EVM version. + #[serde(rename = "osaka")] + Osaka, } impl TryFrom<&str> for EVMVersion { @@ -62,6 +68,8 @@ impl TryFrom<&str> for EVMVersion { "paris" => Self::Paris, "shanghai" => Self::Shanghai, "cancun" => Self::Cancun, + "prague" => Self::Prague, + "osaka" => Self::Osaka, _ => anyhow::bail!("Invalid EVM version: {}", value), }) } @@ -82,6 +90,8 @@ impl std::fmt::Display for EVMVersion { Self::Paris => write!(f, "paris"), Self::Shanghai => write!(f, "shanghai"), Self::Cancun => write!(f, "cancun"), + Self::Prague => write!(f, "prague"), + Self::Osaka => write!(f, "osaka"), } } } diff --git a/crates/differential/genesis.json b/crates/differential/genesis.json index 6a91218..050c491 100644 --- a/crates/differential/genesis.json +++ b/crates/differential/genesis.json @@ -15,6 +15,8 @@ "grayGlacierBlock": 0, "shanghaiTime": 0, "cancunTime": 0, + "pragueTime": 0, + "osakaTime": 0, "terminalTotalDifficulty": 0, "terminalTotalDifficultyPassed": true, "blobSchedule": { @@ -22,6 +24,16 @@ "target": 3, "max": 6, "baseFeeUpdateFraction": 3338477 + }, + "prague": { + "target": 6, + "max": 9, + "baseFeeUpdateFraction": 5007716 + }, + "osaka": { + "target": 6, + "max": 9, + "baseFeeUpdateFraction": 5007716 } } }, @@ -44,4 +56,4 @@ "balance": "1000000000" } } -} \ No newline at end of file +} diff --git a/crates/integration/contracts/CountLeadingZeros.sol b/crates/integration/contracts/CountLeadingZeros.sol new file mode 100644 index 0000000..2e6091b --- /dev/null +++ b/crates/integration/contracts/CountLeadingZeros.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.31; + +/* runner.json +{ + "differential": true, + "actions": [ + { + "Instantiate": { + "code": { + "Solidity": { + "contract": "CountLeadingZeros" + } + } + } + } + ] +} +*/ + +/// The EIP-7939 test vectors: +/// https://eips.ethereum.org/EIPS/eip-7939#test-cases +contract CountLeadingZeros { + function clz(uint256 x) internal pure returns (uint256 r) { + assembly { + r := clz(x) + } + } + + constructor() payable { + assert( + clz(0x000000000000000000000000000000000000000000000000000000000000000) + == 0x0000000000000000000000000000000000000000000000000000000000000100 + ); + assert( + clz(0x8000000000000000000000000000000000000000000000000000000000000000) + == 0x0000000000000000000000000000000000000000000000000000000000000000 + ); + assert( + clz(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + == 0x0000000000000000000000000000000000000000000000000000000000000000 + ); + assert( + clz(0x4000000000000000000000000000000000000000000000000000000000000000) + == 0x0000000000000000000000000000000000000000000000000000000000000001 + ); + assert( + clz(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) + == 0x0000000000000000000000000000000000000000000000000000000000000001 + ); + assert( + clz(0x0000000000000000000000000000000000000000000000000000000000000001) + == 0x00000000000000000000000000000000000000000000000000000000000000ff + ); + } +} diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index 7112ee4..4e47276 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -65,6 +65,7 @@ test_spec!(shift_arithmetic_right, "SAR", "SAR.sol"); test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol"); test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol"); test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol"); +test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol"); fn instantiate(path: &str, contract: &str) -> Vec { vec![Instantiate { diff --git a/crates/llvm-context/src/polkavm/context/function/intrinsics.rs b/crates/llvm-context/src/polkavm/context/function/intrinsics.rs index 03523c0..605d7dd 100644 --- a/crates/llvm-context/src/polkavm/context/function/intrinsics.rs +++ b/crates/llvm-context/src/polkavm/context/function/intrinsics.rs @@ -14,6 +14,8 @@ pub struct Intrinsics<'ctx> { pub byte_swap_word: FunctionDeclaration<'ctx>, /// Performs endianness swaps on i160 values pub byte_swap_eth_address: FunctionDeclaration<'ctx>, + /// Counts leading zeroes. + pub count_leading_zeros: FunctionDeclaration<'ctx>, } impl<'ctx> Intrinsics<'ctx> { @@ -26,6 +28,9 @@ impl<'ctx> Intrinsics<'ctx> { /// The corresponding intrinsic function name. pub const FUNCTION_BYTE_SWAP_ETH_ADDRESS: &'static str = "llvm.bswap.i160"; + /// The corresponding intrinsic function name. + pub const FUNCTION_COUNT_LEADING_ZEROS: &'static str = "llvm.ctlz.i256"; + /// A shortcut constructor. pub fn new( llvm: &'ctx inkwell::context::Context, @@ -53,11 +58,18 @@ impl<'ctx> Intrinsics<'ctx> { Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS, address_type.fn_type(&[address_type.as_basic_type_enum().into()], false), ); + let count_leading_zeros = Self::declare( + llvm, + module, + Self::FUNCTION_COUNT_LEADING_ZEROS, + word_type.fn_type(&[word_type.into(), llvm.bool_type().into()], false), + ); Self { trap, byte_swap_word, byte_swap_eth_address, + count_leading_zeros, } } @@ -85,12 +97,15 @@ impl<'ctx> Intrinsics<'ctx> { let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32); match name { - name if name == Self::FUNCTION_BYTE_SWAP_WORD => vec![word_type.as_basic_type_enum()], - name if name == Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS => { + _ if name == Self::FUNCTION_BYTE_SWAP_WORD => vec![word_type.as_basic_type_enum()], + _ if name == Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS => { vec![llvm .custom_width_int_type(revive_common::BIT_LENGTH_ETH_ADDRESS as u32) .as_basic_type_enum()] } + _ if name == Self::FUNCTION_COUNT_LEADING_ZEROS => { + vec![word_type.as_basic_type_enum()] + } _ => vec![], } } diff --git a/crates/llvm-context/src/polkavm/evm/bitwise.rs b/crates/llvm-context/src/polkavm/evm/bitwise.rs index d1db287..e9cb56c 100644 --- a/crates/llvm-context/src/polkavm/evm/bitwise.rs +++ b/crates/llvm-context/src/polkavm/evm/bitwise.rs @@ -261,3 +261,20 @@ pub fn byte<'ctx>( Ok(byte.as_basic_value_enum()) } + +/// Translates the CLZ instruction. +pub fn count_leading_zeros<'ctx>( + context: &mut Context<'ctx>, + value: inkwell::values::IntValue<'ctx>, +) -> anyhow::Result> { + Ok(context + .builder() + .build_call( + context.intrinsics().count_leading_zeros.function_value(), + &[value.into(), context.bool_const(false).into()], + "clz", + )? + .try_as_basic_value() + .left() + .expect("the llvm.ctlz should return a value")) +} diff --git a/crates/resolc/src/solc/mod.rs b/crates/resolc/src/solc/mod.rs index b27a982..ef129ac 100644 --- a/crates/resolc/src/solc/mod.rs +++ b/crates/resolc/src/solc/mod.rs @@ -23,7 +23,7 @@ pub mod version; pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0); /// The last supported version of `solc`. -pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 30); +pub const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 31); /// The Solidity compiler. pub trait Compiler { diff --git a/crates/yul/src/parser/statement/expression/function_call/mod.rs b/crates/yul/src/parser/statement/expression/function_call/mod.rs index b62b0cf..f9c0ad5 100644 --- a/crates/yul/src/parser/statement/expression/function_call/mod.rs +++ b/crates/yul/src/parser/statement/expression/function_call/mod.rs @@ -348,6 +348,14 @@ impl FunctionCall { ) .map(Some) } + Name::Clz => { + let arguments = self.pop_arguments_llvm::<1>(context)?; + revive_llvm_context::polkavm_evm_bitwise::count_leading_zeros( + context, + arguments[0].into_int_value(), + ) + .map(Some) + } Name::Byte => { let arguments = self.pop_arguments_llvm::<2>(context)?; revive_llvm_context::polkavm_evm_bitwise::byte( diff --git a/crates/yul/src/parser/statement/expression/function_call/name.rs b/crates/yul/src/parser/statement/expression/function_call/name.rs index 450f9e3..9e5eb0b 100644 --- a/crates/yul/src/parser/statement/expression/function_call/name.rs +++ b/crates/yul/src/parser/statement/expression/function_call/name.rs @@ -54,6 +54,8 @@ pub enum Name { Shr, /// signed arithmetic shift right `y` by `x` bits Sar, + /// number of leading zero bits of x, 256 if x == 0 + Clz, /// `n`th byte of `x`, where the most significant byte is the `0`th byte Byte, /// discard value x @@ -270,6 +272,7 @@ impl From<&str> for Name { "shl" => Self::Shl, "shr" => Self::Shr, "sar" => Self::Sar, + "clz" => Self::Clz, "byte" => Self::Byte, "pop" => Self::Pop, @@ -393,6 +396,7 @@ impl fmt::Display for Name { Self::Shl => "shl", Self::Shr => "shr", Self::Sar => "sar", + Self::Clz => "clz", Self::Byte => "byte", Self::Pop => "pop", diff --git a/js/emscripten/build.js b/js/emscripten/build.js index e04b9e6..f26b5fd 100644 --- a/js/emscripten/build.js +++ b/js/emscripten/build.js @@ -3,7 +3,7 @@ const path = require("path"); const { minify } = require("terser"); const SOLJSON_URI = - "https://binaries.soliditylang.org/wasm/soljson-v0.8.30+commit.73712a01.js"; + "https://binaries.soliditylang.org/wasm/soljson-v0.8.31+commit.fd3a2265.js"; const RESOLC_WASM_URI = process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm"; const RESOLC_WASM_TARGET_DIR = path.join( diff --git a/js/emscripten/package.json b/js/emscripten/package.json index eef2517..6ba9840 100644 --- a/js/emscripten/package.json +++ b/js/emscripten/package.json @@ -2,7 +2,7 @@ "name": "revive", "private": true, "dependencies": { - "solc": ">=0.8.0 <=0.8.30" + "solc": ">=0.8.0 <=0.8.31" }, "scripts": { "example:web": "http-server ./examples/web/", diff --git a/js/resolc/package.json b/js/resolc/package.json index dea0c78..2d7513b 100644 --- a/js/resolc/package.json +++ b/js/resolc/package.json @@ -34,6 +34,6 @@ "commander": "^13.1.0", "package-json": "^10.0.1", "resolve-pkg": "^2.0.0", - "solc": ">=0.8.0 <=0.8.30" + "solc": ">=0.8.0 <=0.8.31" } } diff --git a/package-lock.json b/package-lock.json index 803de2c..6657b20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "js/emscripten": { "name": "revive", "dependencies": { - "solc": ">=0.8.0 <=0.8.30" + "solc": ">=0.8.0 <=0.8.31" }, "devDependencies": { "@playwright/test": "^1.49.1", @@ -38,7 +38,7 @@ "commander": "^13.1.0", "package-json": "^10.0.1", "resolve-pkg": "^2.0.0", - "solc": ">=0.8.0 <=0.8.30" + "solc": ">=0.8.0 <=0.8.31" }, "bin": { "resolc": "dist/bin.js" @@ -1585,6 +1585,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/types": "8.32.1", @@ -1752,6 +1753,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2361,6 +2363,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -2519,6 +2522,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@ethersproject/abi": "5.8.0", "@ethersproject/abstract-provider": "5.8.0", @@ -4393,6 +4397,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver"