Support solc v0.8.31 (#430)

- Support for solc v0.8.31.
- Support for the `clz` Yul builtin.

---------

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2025-12-05 15:25:13 +01:00
committed by GitHub
parent d0c10e6d5c
commit 11f82c8488
16 changed files with 142 additions and 11 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ runs:
shell: bash shell: bash
run: | run: |
mkdir -p solc 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 - name: Make Solc Executable
if: ${{ runner.os == 'Windows' }} if: ${{ runner.os == 'Windows' }}
+1 -1
View File
@@ -177,7 +177,7 @@ jobs:
- name: Basic Sanity Check - name: Basic Sanity Check
run: | run: |
mkdir -p solc 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 " node -e "
const soljson = require('solc/soljson'); const soljson = require('solc/soljson');
const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js'); const createRevive = require('./target/wasm32-unknown-emscripten/release/resolc.js');
+2
View File
@@ -8,6 +8,8 @@ Supported `polkadot-sdk` rev: `2509.0.0`
### Added ### Added
- The comprehensive revive compiler book documentation page: https://paritytech.github.io/revive/ - 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 ### Changed
- Instruct the LLVM backend and linker to `--relax` (may lead to smaller contract code size). - Instruct the LLVM backend and linker to `--relax` (may lead to smaller contract code size).
+10
View File
@@ -43,6 +43,12 @@ pub enum EVMVersion {
/// The corresponding EVM version. /// The corresponding EVM version.
#[serde(rename = "cancun")] #[serde(rename = "cancun")]
Cancun, Cancun,
/// The corresponding EVM version.
#[serde(rename = "prague")]
Prague,
/// The corresponding EVM version.
#[serde(rename = "osaka")]
Osaka,
} }
impl TryFrom<&str> for EVMVersion { impl TryFrom<&str> for EVMVersion {
@@ -62,6 +68,8 @@ impl TryFrom<&str> for EVMVersion {
"paris" => Self::Paris, "paris" => Self::Paris,
"shanghai" => Self::Shanghai, "shanghai" => Self::Shanghai,
"cancun" => Self::Cancun, "cancun" => Self::Cancun,
"prague" => Self::Prague,
"osaka" => Self::Osaka,
_ => anyhow::bail!("Invalid EVM version: {}", value), _ => anyhow::bail!("Invalid EVM version: {}", value),
}) })
} }
@@ -82,6 +90,8 @@ impl std::fmt::Display for EVMVersion {
Self::Paris => write!(f, "paris"), Self::Paris => write!(f, "paris"),
Self::Shanghai => write!(f, "shanghai"), Self::Shanghai => write!(f, "shanghai"),
Self::Cancun => write!(f, "cancun"), Self::Cancun => write!(f, "cancun"),
Self::Prague => write!(f, "prague"),
Self::Osaka => write!(f, "osaka"),
} }
} }
} }
+13 -1
View File
@@ -15,6 +15,8 @@
"grayGlacierBlock": 0, "grayGlacierBlock": 0,
"shanghaiTime": 0, "shanghaiTime": 0,
"cancunTime": 0, "cancunTime": 0,
"pragueTime": 0,
"osakaTime": 0,
"terminalTotalDifficulty": 0, "terminalTotalDifficulty": 0,
"terminalTotalDifficultyPassed": true, "terminalTotalDifficultyPassed": true,
"blobSchedule": { "blobSchedule": {
@@ -22,6 +24,16 @@
"target": 3, "target": 3,
"max": 6, "max": 6,
"baseFeeUpdateFraction": 3338477 "baseFeeUpdateFraction": 3338477
},
"prague": {
"target": 6,
"max": 9,
"baseFeeUpdateFraction": 5007716
},
"osaka": {
"target": 6,
"max": 9,
"baseFeeUpdateFraction": 5007716
} }
} }
}, },
@@ -44,4 +56,4 @@
"balance": "1000000000" "balance": "1000000000"
} }
} }
} }
@@ -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
);
}
}
+1
View File
@@ -65,6 +65,7 @@ test_spec!(shift_arithmetic_right, "SAR", "SAR.sol");
test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol"); test_spec!(add_mod_mul_mod, "AddModMulModTester", "AddModMulMod.sol");
test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol"); test_spec!(memory_bounds, "MemoryBounds", "MemoryBounds.sol");
test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol"); test_spec!(selfdestruct, "Selfdestruct", "Selfdestruct.sol");
test_spec!(clz, "CountLeadingZeros", "CountLeadingZeros.sol");
fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> { fn instantiate(path: &str, contract: &str) -> Vec<SpecsAction> {
vec![Instantiate { vec![Instantiate {
@@ -14,6 +14,8 @@ pub struct Intrinsics<'ctx> {
pub byte_swap_word: FunctionDeclaration<'ctx>, pub byte_swap_word: FunctionDeclaration<'ctx>,
/// Performs endianness swaps on i160 values /// Performs endianness swaps on i160 values
pub byte_swap_eth_address: FunctionDeclaration<'ctx>, pub byte_swap_eth_address: FunctionDeclaration<'ctx>,
/// Counts leading zeroes.
pub count_leading_zeros: FunctionDeclaration<'ctx>,
} }
impl<'ctx> Intrinsics<'ctx> { impl<'ctx> Intrinsics<'ctx> {
@@ -26,6 +28,9 @@ impl<'ctx> Intrinsics<'ctx> {
/// The corresponding intrinsic function name. /// The corresponding intrinsic function name.
pub const FUNCTION_BYTE_SWAP_ETH_ADDRESS: &'static str = "llvm.bswap.i160"; 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. /// A shortcut constructor.
pub fn new( pub fn new(
llvm: &'ctx inkwell::context::Context, llvm: &'ctx inkwell::context::Context,
@@ -53,11 +58,18 @@ impl<'ctx> Intrinsics<'ctx> {
Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS, Self::FUNCTION_BYTE_SWAP_ETH_ADDRESS,
address_type.fn_type(&[address_type.as_basic_type_enum().into()], false), 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 { Self {
trap, trap,
byte_swap_word, byte_swap_word,
byte_swap_eth_address, 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); let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
match name { match name {
name if name == Self::FUNCTION_BYTE_SWAP_WORD => vec![word_type.as_basic_type_enum()], _ 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_ETH_ADDRESS => {
vec![llvm vec![llvm
.custom_width_int_type(revive_common::BIT_LENGTH_ETH_ADDRESS as u32) .custom_width_int_type(revive_common::BIT_LENGTH_ETH_ADDRESS as u32)
.as_basic_type_enum()] .as_basic_type_enum()]
} }
_ if name == Self::FUNCTION_COUNT_LEADING_ZEROS => {
vec![word_type.as_basic_type_enum()]
}
_ => vec![], _ => vec![],
} }
} }
@@ -261,3 +261,20 @@ pub fn byte<'ctx>(
Ok(byte.as_basic_value_enum()) 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<inkwell::values::BasicValueEnum<'ctx>> {
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"))
}
+1 -1
View File
@@ -23,7 +23,7 @@ pub mod version;
pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0); pub const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 0);
/// The last supported version of `solc`. /// 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. /// The Solidity compiler.
pub trait Compiler { pub trait Compiler {
@@ -348,6 +348,14 @@ impl FunctionCall {
) )
.map(Some) .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 => { Name::Byte => {
let arguments = self.pop_arguments_llvm::<2>(context)?; let arguments = self.pop_arguments_llvm::<2>(context)?;
revive_llvm_context::polkavm_evm_bitwise::byte( revive_llvm_context::polkavm_evm_bitwise::byte(
@@ -54,6 +54,8 @@ pub enum Name {
Shr, Shr,
/// signed arithmetic shift right `y` by `x` bits /// signed arithmetic shift right `y` by `x` bits
Sar, 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 /// `n`th byte of `x`, where the most significant byte is the `0`th byte
Byte, Byte,
/// discard value x /// discard value x
@@ -270,6 +272,7 @@ impl From<&str> for Name {
"shl" => Self::Shl, "shl" => Self::Shl,
"shr" => Self::Shr, "shr" => Self::Shr,
"sar" => Self::Sar, "sar" => Self::Sar,
"clz" => Self::Clz,
"byte" => Self::Byte, "byte" => Self::Byte,
"pop" => Self::Pop, "pop" => Self::Pop,
@@ -393,6 +396,7 @@ impl fmt::Display for Name {
Self::Shl => "shl", Self::Shl => "shl",
Self::Shr => "shr", Self::Shr => "shr",
Self::Sar => "sar", Self::Sar => "sar",
Self::Clz => "clz",
Self::Byte => "byte", Self::Byte => "byte",
Self::Pop => "pop", Self::Pop => "pop",
+1 -1
View File
@@ -3,7 +3,7 @@ const path = require("path");
const { minify } = require("terser"); const { minify } = require("terser");
const SOLJSON_URI = 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 = const RESOLC_WASM_URI =
process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm"; process.env.RELEASE_RESOLC_WASM_URI || "http://127.0.0.1:8080/resolc.wasm";
const RESOLC_WASM_TARGET_DIR = path.join( const RESOLC_WASM_TARGET_DIR = path.join(
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "revive", "name": "revive",
"private": true, "private": true,
"dependencies": { "dependencies": {
"solc": ">=0.8.0 <=0.8.30" "solc": ">=0.8.0 <=0.8.31"
}, },
"scripts": { "scripts": {
"example:web": "http-server ./examples/web/", "example:web": "http-server ./examples/web/",
+1 -1
View File
@@ -34,6 +34,6 @@
"commander": "^13.1.0", "commander": "^13.1.0",
"package-json": "^10.0.1", "package-json": "^10.0.1",
"resolve-pkg": "^2.0.0", "resolve-pkg": "^2.0.0",
"solc": ">=0.8.0 <=0.8.30" "solc": ">=0.8.0 <=0.8.31"
} }
} }
+7 -2
View File
@@ -18,7 +18,7 @@
"js/emscripten": { "js/emscripten": {
"name": "revive", "name": "revive",
"dependencies": { "dependencies": {
"solc": ">=0.8.0 <=0.8.30" "solc": ">=0.8.0 <=0.8.31"
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "^1.49.1", "@playwright/test": "^1.49.1",
@@ -38,7 +38,7 @@
"commander": "^13.1.0", "commander": "^13.1.0",
"package-json": "^10.0.1", "package-json": "^10.0.1",
"resolve-pkg": "^2.0.0", "resolve-pkg": "^2.0.0",
"solc": ">=0.8.0 <=0.8.30" "solc": ">=0.8.0 <=0.8.31"
}, },
"bin": { "bin": {
"resolc": "dist/bin.js" "resolc": "dist/bin.js"
@@ -1585,6 +1585,7 @@
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
"integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "8.32.1", "@typescript-eslint/scope-manager": "8.32.1",
"@typescript-eslint/types": "8.32.1", "@typescript-eslint/types": "8.32.1",
@@ -1752,6 +1753,7 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -2361,6 +2363,7 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz",
"integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -2519,6 +2522,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@ethersproject/abi": "5.8.0", "@ethersproject/abi": "5.8.0",
"@ethersproject/abstract-provider": "5.8.0", "@ethersproject/abstract-provider": "5.8.0",
@@ -4393,6 +4397,7 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"