diff --git a/solx-llvm b/solx-llvm index 4907939bf..36480c29b 160000 --- a/solx-llvm +++ b/solx-llvm @@ -1 +1 @@ -Subproject commit 4907939bfc1fa6a2925da73c0f2a8ca8432e2e7c +Subproject commit 36480c29b3decd45007919c14ebcf4a70b870e68 diff --git a/solx-mlir/sol_attr_stubs.cpp b/solx-mlir/sol_attr_stubs.cpp index 9f80646fa..f884a6ad0 100644 --- a/solx-mlir/sol_attr_stubs.cpp +++ b/solx-mlir/sol_attr_stubs.cpp @@ -107,6 +107,7 @@ MlirType solxCreateFixedBytesType(MlirContext ctx, uint32_t size) { MlirType solxCreateArrayType(MlirContext ctx, int64_t size, MlirType elementType, uint32_t dataLocation) { if (dataLocation > 5) abort(); + if (size < -1) abort(); auto *context = unwrap(ctx); auto location = static_cast(dataLocation); std::optional sizeOpt; diff --git a/solx-mlir/src/context/mod.rs b/solx-mlir/src/context/mod.rs index 3f2c600fb..937e24420 100644 --- a/solx-mlir/src/context/mod.rs +++ b/solx-mlir/src/context/mod.rs @@ -158,7 +158,9 @@ impl<'context> Context<'context> { /// 1. `canonicalize` /// 2. `sol-inline-modifiers` /// 3. `convert-sol-to-yul`: Sol → Yul - /// 4. `convert-yul-to-std`: Yul → func/arith/scf/cf/LLVM + /// 4. `convert-yul-to-std`: Yul → func/arith/scf/cf/LLVM, keeping the + /// memoryguard symbolic; the EVM backend's `evm-fold-memory-guard` pass + /// folds it on each spill retry /// 5. `canonicalize` /// 6. `convert-scf-to-cf` /// 7. `convert-func-to-llvm` @@ -186,7 +188,9 @@ impl<'context> Context<'context> { crate::ffi::mlirCreateConversionConvertSolToYulPass(), )); pass_manager.add_pass(melior::pass::Pass::from_raw( - crate::ffi::mlirCreateConversionConvertYulToStandardPass(), + crate::ffi::mlirSolCreateConvertYulToStandardPass( + /* symbolic_mem_guard = */ true, + ), )); pass_manager.add_pass(melior::pass::Pass::from_raw( crate::ffi::mlirCreateTransformsCanonicalizer(), @@ -269,7 +273,9 @@ impl<'context> Context<'context> { /// /// Parses the source, verifies it, lowers each `llvm.setimmutable` into heap stores at its /// id's `immutables` offsets, and translates to LLVM IR. - /// Returns owned `(LLVMContextRef, LLVMModuleRef)`. + /// Returns owned `(LLVMContextRef, LLVMModuleRef)`. The translated + /// module still carries the symbolic memoryguard calls; the EVM backend + /// folds them at the start of the optimization pipeline. /// /// # Errors /// diff --git a/solx-mlir/src/ffi.rs b/solx-mlir/src/ffi.rs index 9aff464d2..f5e640369 100644 --- a/solx-mlir/src/ffi.rs +++ b/solx-mlir/src/ffi.rs @@ -41,8 +41,10 @@ unsafe extern "C" { /// Creates the `convert-sol-to-yul` pass. pub fn mlirCreateConversionConvertSolToYulPass() -> MlirPass; - /// Creates the `convert-yul-to-std` pass. - pub fn mlirCreateConversionConvertYulToStandardPass() -> MlirPass; + /// Creates the `convert-yul-to-std` pass. `symbolic_mem_guard` lowers + /// `yul.memoryguard` to the `evm.memoryguard` intrinsic instead of a + /// constant. + pub fn mlirSolCreateConvertYulToStandardPass(symbolic_mem_guard: bool) -> MlirPass; // ---- Standard-to-LLVM conversion passes ---- diff --git a/solx-mlir/tests/lit/memory_guard.sol b/solx-mlir/tests/lit/memory_guard.sol new file mode 100644 index 000000000..9a090c316 --- /dev/null +++ b/solx-mlir/tests/lit/memory_guard.sol @@ -0,0 +1,13 @@ +// RUN: solx --emit-mlir=llvm %s | FileCheck %s + +// The free memory pointer's initializer stays symbolic through the pipeline; +// the EVM backend folds it to `guard + spill region size`. + +// CHECK: llvm.func @__entry() +// CHECK: "llvm.intrcall"({{.*}}) <{id = {{[0-9]+}} : i32, name = "evm.memoryguard"}> : (i256) -> i256 + +contract C { + function f(uint256 a) public pure returns (uint256) { + return a + 1; + } +} diff --git a/tests/solidity/simple/stack_spill_heap.sol b/tests/solidity/simple/stack_spill_heap.sol new file mode 100644 index 000000000..78eef9c17 --- /dev/null +++ b/tests/solidity/simple/stack_spill_heap.sol @@ -0,0 +1,50 @@ +//! { "modes": [ "Y", "E", "I" ], "cases": [ { +//! "name": "spillWithHeap", +//! "inputs": [ +//! { +//! "method": "spillWithHeap", +//! "calldata": [ "7" ] +//! } +//! ], +//! "expected": [ +//! "58321", "8", "9", "10", "11", "12", "13", "14" +//! ] +//! } ] } + +pragma solidity >=0.4.19; + +contract Test { + function g(uint256 x) internal pure returns (uint256) { + return x * 3 + 1; + } + + function spillWithHeap(uint256 a) public pure returns (uint256[8] memory buf) { + uint256 v0 = a + 1; + uint256 v1 = a + 2; + uint256 v2 = v1 + v0; + uint256 v3 = v2 + v1; + uint256 v4 = v3 + v2; + uint256 v5 = v4 + v3; + uint256 v6 = v5 + v4; + uint256 v7 = v6 + v5; + uint256 v8 = v7 + v6; + uint256 v9 = v8 + v7; + uint256 v10 = v9 + v8; + uint256 v11 = v10 + v9; + uint256 v12 = v11 + v10; + uint256 v13 = v12 + v11; + uint256 v14 = v13 + v12; + uint256 v15 = v14 + v13; + uint256 v16 = v15 + v14; + uint256 v17 = v16 + v15; + + for (uint256 i = 0; i < 8; i++) { + buf[i] = a + i; + } + + uint256 r = g(a); + r += v0 + v1 + v2 + v3 + v4 + v5 + v6 + v7 + v8 + v9 + v10 + v11 + + v12 + v13 + v14 + v15 + v16 + v17; + buf[0] += r; + } +}