diff --git a/Cargo.lock b/Cargo.lock index 10377d6..bef186c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8695,16 +8695,16 @@ dependencies = [ [[package]] name = "polkavm" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d020e05b17d1d7612045ff1244efb33c37e54838be5c8a6af00e7e10adc019" +checksum = "1e169701bf52ffe9abafea76f0519d526c736a4deea7e2cd70fc51b3bbf1ba81" dependencies = [ "libc", "log", "picosimd", - "polkavm-assembler 0.34.0", - "polkavm-common 0.34.0", - "polkavm-linux-raw 0.34.0", + "polkavm-assembler 0.35.0", + "polkavm-common 0.35.0", + "polkavm-linux-raw 0.35.0", ] [[package]] @@ -8718,9 +8718,9 @@ dependencies = [ [[package]] name = "polkavm-assembler" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3145b14db78aaa7c37bd401906f0ccf3af2dc7ff6bebb3d89a88967ae14a4dc" +checksum = "6dddb5cf523ca1c8de5a1a265438aa0e96fbf812e9f90a2f300a4c550bd8a9e9" dependencies = [ "log", ] @@ -8748,13 +8748,13 @@ dependencies = [ [[package]] name = "polkavm-common" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1bdbd3d706e8fe695403f501c218f20550e48181e2a56033665ecba3eb4c2" +checksum = "b20e5a47dd9ad7bb43013676a18074fca2573dd8270774a7d2ad286b57bc710f" dependencies = [ "log", "picosimd", - "polkavm-assembler 0.34.0", + "polkavm-assembler 0.35.0", ] [[package]] @@ -8821,15 +8821,15 @@ dependencies = [ [[package]] name = "polkavm-disassembler" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803bd60570ae34a63fe97865d58d02db9bca5af6f4ef0059c6856edeb5ea5849" +checksum = "a5baf1b6eceba9f08810d9d17e54b82000e848f0877c3a339c75bb60b4db5dfc" dependencies = [ "clap", "iced-x86", - "polkavm 0.34.0", - "polkavm-common 0.34.0", - "polkavm-linker 0.34.0", + "polkavm 0.35.0", + "polkavm-common 0.35.0", + "polkavm-linker 0.35.0", ] [[package]] @@ -8850,16 +8850,16 @@ dependencies = [ [[package]] name = "polkavm-linker" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f49e142e81d23765cde731adf4263b4c3b77a06801ae603bbe5a71933de9ca0" +checksum = "96b5c8083bf6c33ff0d9254bbe500872c18a82f2801c5879693f10c16816275e" dependencies = [ "dirs", "gimli 0.31.1", "hashbrown 0.14.5", "log", "object 0.36.7", - "polkavm-common 0.34.0", + "polkavm-common 0.35.0", "regalloc2 0.9.3", "rustc-demangle", ] @@ -8872,9 +8872,9 @@ checksum = "42063d4a1c52e569f7794df27dab3e19c9fa8946184023257bdbb43eb4a94be5" [[package]] name = "polkavm-linux-raw" -version = "0.34.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4d1210eddaae35bb36479f99569fe6677ea47daefbeab1705ccc80ad246fc3" +checksum = "7689d791c6ab52782217ae904d0f5eb7b7cc125a90eb085069d38c15f5d5a53c" [[package]] name = "poly1305" @@ -9527,7 +9527,7 @@ dependencies = [ "anyhow", "libc", "lld-sys", - "polkavm-linker 0.34.0", + "polkavm-linker 0.35.0", "revive-builtins", "tempfile", ] @@ -9562,7 +9562,7 @@ dependencies = [ "itertools 0.14.0", "libc", "num", - "polkavm-common 0.34.0", + "polkavm-common 0.35.0", "polkavm-disassembler", "revive-common", "revive-linker", diff --git a/Cargo.toml b/Cargo.toml index 652f414..73fcc29 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,10 +49,10 @@ which = "8.0" path-slash = "0.2" rayon = "1.11" clap = { version = "4", default-features = false, features = ["derive"] } -polkavm-common = "0.34.0" -polkavm-linker = "0.34.0" -polkavm-disassembler = "0.34.0" -polkavm = "0.34.0" +polkavm-common = "0.35.0" +polkavm-linker = "0.35.0" +polkavm-disassembler = "0.35.0" +polkavm = "0.35.0" alloy-primitives = { version = "1.4", features = ["serde"] } alloy-sol-types = "1.4" alloy-genesis = "1.1.2" diff --git a/crates/integration/contracts/LinkerI32BoundaryFoldBug.sol b/crates/integration/contracts/LinkerI32BoundaryFoldBug.sol new file mode 100644 index 0000000..dcd2ca4 --- /dev/null +++ b/crates/integration/contracts/LinkerI32BoundaryFoldBug.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/// Regression: at `-O3` (the `OptimizerSettings::cycles()` middle/back-end +/// level used by the differential harness) LLVM proves the low 64 bits of +/// `(a2 + (a0 << 64)) ^ a2` cancel to zero and narrows the surviving +/// `| 0x80000000 | 0x80000001` work down to native i32 lane operations. +/// The constant `0x80000000` (`2^31`) is materialized with `lui x, 0x80000`. +/// During link time the polkavm-linker (`program_from_elf.rs`) constant-folds +/// the narrowed 32-bit op through `OperationKind::apply_const`, whose `op32!` +/// macro does `i64 -> i32` via `try_into().expect("operand overflow")`. The +/// tracked constant for `0x80000000` does not fit the signed i32 range, so the +/// fold panics with `operand overflow: TryFromIntError` — an ICE instead of a +/// clean compile. solc's EVM backend has no such narrowing (everything is +/// 256-bit), so the same source compiles cleanly there. +contract LinkerI32BoundaryFoldBug { + function test(int256 a0, int256 a2) external pure returns (int256) { + int256 t1 = (a2 + (a0 * int256(18446744073709551616))) ^ a2; // low-64 cancel + t1 = t1 | int256(2147483648); // 0x80000000 (i32 sign boundary) + t1 = t1 | int256(2147483649); // 0x80000001 + return t1; + } +} diff --git a/crates/integration/src/cases.rs b/crates/integration/src/cases.rs index d3f9901..8f684af 100644 --- a/crates/integration/src/cases.rs +++ b/crates/integration/src/cases.rs @@ -329,6 +329,20 @@ case!( const_return_overflow_bug, ); +sol!( + contract LinkerI32BoundaryFoldBug { + function test(int256 a0, int256 a2) external pure returns (int256); + } +); +case!( + "LinkerI32BoundaryFoldBug.sol", + LinkerI32BoundaryFoldBug, + testCall, + linker_i32_boundary_fold_bug, + a0: I256, + a2: I256 +); + // `PanicInterveneBug` has only a `fallback()` — invoked by sending // raw calldata that doesn't match any selector. Empty calldata works. case!( diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index d00af52..6040f52 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -494,6 +494,36 @@ fn const_return_offset_overflow_traps() { run_differential(actions); } +/// Regression: at `-O3` LLVM proves the low 64 bits of +/// `(a2 + (a0 << 64)) ^ a2` cancel and narrows the trailing +/// `| 0x80000000 | 0x80000001` down to native i32 lane operations, +/// materializing `0x80000000` (`2^31`) via `lui x, 0x80000`. The +/// polkavm-linker's constant propagation then folds the narrowed 32-bit +/// op through `OperationKind::apply_const`, whose `op32!` macro converts +/// `i64 -> i32` with `try_into().expect("operand overflow")`. The tracked +/// constant `0x80000000` does not fit the signed i32 range, so the fold +/// panics (`operand overflow: TryFromIntError`) — an ICE instead of a +/// clean compile. solc's EVM backend has no such narrowing (all ops are +/// 256-bit) so the same source compiles cleanly there. +#[test] +fn linker_i32_boundary_constant_fold() { + let mut actions = instantiate( + "contracts/LinkerI32BoundaryFoldBug.sol", + "LinkerI32BoundaryFoldBug", + ); + let a0 = I256::try_from(0x0123456789abcdef_i64).unwrap(); + let a2 = I256::try_from(-0x7654321076543210_i64).unwrap(); + actions.push(Call { + origin: TestAddress::Alice, + dest: TestAddress::Instantiated(0), + value: 0, + gas_limit: None, + storage_deposit_limit: None, + data: Contract::linker_i32_boundary_fold_bug(a0, a2).calldata, + }); + run_differential(actions); +} + /// Regression: `simplify.rs::find_panic_pattern_backwards` walks the /// statement list backwards from `revert(0, 0x24)` and accepts an /// `mstore` at offsets ≠ 0 / 4 as "safe filler" — it skips it in the