diff --git a/CHANGELOG.md b/CHANGELOG.md index 32499e7..9e2bdfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ Supported `polkadot-sdk` rev: `2509.0.0` - Instruct the LLVM backend and linker to `--relax` (may lead to smaller contract code size). - Standard JSON mode: Don't forward EVM bytecode related output selections to solc. +### Fixed: +- The missing `STOP` instruction at the end of `code` blocks. + ## v0.5.0 This is a development pre-release. diff --git a/crates/integration/src/tests.rs b/crates/integration/src/tests.rs index 447db43..8ec7356 100644 --- a/crates/integration/src/tests.rs +++ b/crates/integration/src/tests.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use alloy_primitives::*; +use resolc::test_utils::build_yul; use revive_runner::*; use SpecsAction::*; @@ -520,3 +521,101 @@ fn create2_salt() { } .run(); } + +#[test] +fn code_block_stops() { + let code = &build_yul(&[( + "poc.yul", + r#"object "Test"{ + code { + tstore(0x7fd9d641,0x7b1e022) + returndatacopy(0x0,0x0,returndatasize()) + } + object "Test_deployed" { code{} } +}"#, + )]) + .unwrap()["poc.yul:Test"]; + + Specs { + actions: vec![ + Instantiate { + origin: TestAddress::Alice, + value: 0, + gas_limit: Some(GAS_LIMIT), + storage_deposit_limit: None, + code: Code::Bytes(code.to_vec()), + data: Default::default(), + salt: OptionalHex::default(), + }, + Call { + origin: TestAddress::Alice, + dest: TestAddress::Instantiated(0), + value: Default::default(), + gas_limit: None, + storage_deposit_limit: None, + data: Default::default(), + }, + VerifyCall(Default::default()), + ], + differential: false, + ..Default::default() + } + .run(); +} + +#[test] +fn code_block_with_nested_object_stops() { + let code = &build_yul(&[( + "poc.yul", + r#"object "Test" { + code { + function allocate(size) -> ptr { + ptr := mload(0x40) + if iszero(ptr) { ptr := 0x60 } + mstore(0x40, add(ptr, size)) + } + let size := datasize("Test_deployed") + let offset := allocate(size) + datacopy(offset, dataoffset("Test_deployed"), size) + return(offset, size) + } + object "Test_deployed" { + code { + sstore(0, 100) + } + object "Test" { + code { + revert(0,0) + } + } + } +}"#, + )]) + .unwrap()["poc.yul:Test"]; + + Specs { + actions: vec![ + Instantiate { + origin: TestAddress::Alice, + value: 0, + gas_limit: Some(GAS_LIMIT), + storage_deposit_limit: None, + code: Code::Bytes(code.to_vec()), + data: Default::default(), + salt: OptionalHex::default(), + }, + Call { + origin: TestAddress::Alice, + dest: TestAddress::Instantiated(0), + value: Default::default(), + gas_limit: None, + storage_deposit_limit: None, + data: Default::default(), + }, + VerifyCall(Default::default()), + ], + differential: false, + ..Default::default() + } + .run(); +} diff --git a/crates/yul/src/parser/statement/code.rs b/crates/yul/src/parser/statement/code.rs index e40b380..5b74a01 100644 --- a/crates/yul/src/parser/statement/code.rs +++ b/crates/yul/src/parser/statement/code.rs @@ -64,6 +64,9 @@ impl PolkaVMWriteLLVM for Code { fn into_llvm(self, context: &mut PolkaVMContext) -> anyhow::Result<()> { self.block.into_llvm(context)?; + // The EVM lets the code return implicitly. + revive_llvm_context::polkavm_evm_return::stop(context)?; + Ok(()) } }