diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index 83df15e..ed08958 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -1,10 +1,10 @@ { - "Baseline": 962, - "Computation": 4463, - "DivisionArithmetics": 40756, - "ERC20": 54427, - "Events": 1792, - "FibonacciIterative": 3065, - "Flipper": 3665, - "SHA1": 32923 + "Baseline": 983, + "Computation": 4207, + "DivisionArithmetics": 40509, + "ERC20": 47068, + "Events": 1791, + "FibonacciIterative": 3044, + "Flipper": 3405, + "SHA1": 33583 } \ No newline at end of file diff --git a/crates/integration/contracts/MSize.sol b/crates/integration/contracts/MSize.sol index c215368..5fb2415 100644 --- a/crates/integration/contracts/MSize.sol +++ b/crates/integration/contracts/MSize.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8; /* runner.json { + "differential": true, "actions": [ { "Instantiate": { @@ -23,12 +24,6 @@ pragma solidity ^0.8; "data": "f016832c" } }, - { - "VerifyCall": { - "success": true, - "output": "0000000000000000000000000000000000000000000000000000000000000060" - } - }, { "Call": { "dest": { @@ -36,12 +31,6 @@ pragma solidity ^0.8; }, "data": "f4a63aa5" } - }, - { - "VerifyCall": { - "success": true, - "output": "0000000000000000000000000000000000000000000000000000000000000084" - } } ] } diff --git a/crates/llvm-context/src/polkavm/const/runtime_api.rs b/crates/llvm-context/src/polkavm/const/runtime_api.rs index 7529a01..9de82e7 100644 --- a/crates/llvm-context/src/polkavm/const/runtime_api.rs +++ b/crates/llvm-context/src/polkavm/const/runtime_api.rs @@ -13,6 +13,10 @@ pub mod exports { } pub mod imports { + pub static SBRK: &str = "__sbrk_internal"; + + pub static MEMORY_SIZE: &str = "__msize"; + pub static ADDRESS: &str = "address"; pub static BALANCE: &str = "balance"; @@ -57,7 +61,9 @@ pub mod imports { /// All imported runtime API symbols. /// Useful for configuring common attributes and linkage. - pub static IMPORTS: [&str; 21] = [ + pub static IMPORTS: [&str; 23] = [ + SBRK, + MEMORY_SIZE, ADDRESS, BALANCE, BALANCE_OF, @@ -81,6 +87,3 @@ pub mod imports { VALUE_TRANSFERRED, ]; } - -/// PolkaVM __sbrk API symbol to extend the heap memory. -pub static SBRK: &str = "__sbrk"; diff --git a/crates/llvm-context/src/polkavm/context/function/runtime/entry.rs b/crates/llvm-context/src/polkavm/context/function/runtime/entry.rs index 5fa3cc4..ea7d348 100644 --- a/crates/llvm-context/src/polkavm/context/function/runtime/entry.rs +++ b/crates/llvm-context/src/polkavm/context/function/runtime/entry.rs @@ -61,7 +61,10 @@ impl Entry { context .get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)? .into(), - context.build_sbrk(context.integer_const(crate::polkavm::XLEN, 0))?, + context.build_sbrk( + context.xlen_type().const_zero(), + context.xlen_type().const_zero(), + )?, )?; context.set_global( diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index a964cca..8f54bcc 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -1088,16 +1088,20 @@ where Ok(truncated) } - /// Build a call to PolkaVM `sbrk` for extending the heap by `size`. + /// Build a call to PolkaVM `sbrk` for extending the heap from offset by `size`. + /// The allocation is aligned to 32 bytes. + /// + /// This emulates the EVM linear memory until the runtime supports metered memory. pub fn build_sbrk( &self, + offset: inkwell::values::IntValue<'ctx>, size: inkwell::values::IntValue<'ctx>, ) -> anyhow::Result> { Ok(self .builder() .build_call( - self.runtime_api_method(runtime_api::SBRK), - &[size.into()], + self.runtime_api_method(runtime_api::imports::SBRK), + &[offset.into(), size.into()], "call_sbrk", )? .try_as_basic_value() @@ -1106,14 +1110,29 @@ where .into_pointer_value()) } - /// Call PolkaVM `sbrk` for extending the heap by `size`, + /// Build a call to PolkaVM `msize` for querying the linear memory size. + pub fn build_msize(&self) -> anyhow::Result> { + Ok(self + .builder() + .build_call( + self.runtime_api_method(runtime_api::imports::MEMORY_SIZE), + &[], + "call_msize", + )? + .try_as_basic_value() + .left() + .expect("sbrk returns an int") + .into_int_value()) + } + + /// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`, /// trapping the contract if the call failed. - /// Returns the end of memory pointer. pub fn build_heap_alloc( &self, + offset: inkwell::values::IntValue<'ctx>, size: inkwell::values::IntValue<'ctx>, - ) -> anyhow::Result> { - let end_of_memory = self.build_sbrk(size)?; + ) -> anyhow::Result<()> { + let end_of_memory = self.build_sbrk(offset, size)?; let return_is_nil = self.builder().build_int_compare( inkwell::IntPredicate::EQ, end_of_memory, @@ -1131,7 +1150,7 @@ where self.set_basic_block(continue_block); - Ok(end_of_memory) + Ok(()) } /// Returns a pointer to `offset` into the heap, allocating @@ -1146,40 +1165,12 @@ where assert_eq!(offset.get_type(), self.xlen_type()); assert_eq!(length.get_type(), self.xlen_type()); + self.build_heap_alloc(offset, length)?; + let heap_start = self .get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)? .value .as_pointer_value(); - let heap_end = self.build_sbrk(self.integer_const(crate::polkavm::XLEN, 0))?; - let value_end = self.build_gep( - Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start), - &[self.builder().build_int_nuw_add(offset, length, "end")?], - self.byte_type(), - "heap_end_gep", - ); - let is_out_of_bounds = self.builder().build_int_compare( - inkwell::IntPredicate::UGT, - value_end.value, - heap_end, - "is_value_overflowing_heap", - )?; - - let out_of_bounds_block = self.append_basic_block("heap_offset_out_of_bounds"); - let heap_offset_block = self.append_basic_block("build_heap_pointer"); - self.build_conditional_branch(is_out_of_bounds, out_of_bounds_block, heap_offset_block)?; - - self.set_basic_block(out_of_bounds_block); - let size = self.builder().build_int_nuw_sub( - self.builder() - .build_ptr_to_int(value_end.value, self.xlen_type(), "value_end")?, - self.builder() - .build_ptr_to_int(heap_end, self.xlen_type(), "heap_end")?, - "heap_alloc_size", - )?; - self.build_heap_alloc(size)?; - self.build_unconditional_branch(heap_offset_block); - - self.set_basic_block(heap_offset_block); Ok(self.build_gep( Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start), &[offset], diff --git a/crates/llvm-context/src/polkavm/evm/context.rs b/crates/llvm-context/src/polkavm/evm/context.rs index d636058..741e20e 100644 --- a/crates/llvm-context/src/polkavm/evm/context.rs +++ b/crates/llvm-context/src/polkavm/evm/context.rs @@ -107,33 +107,6 @@ where Ok(context.word_const(0).as_basic_value_enum()) } -/// Translates the `msize` instruction. -pub fn msize<'ctx, D>( - context: &mut Context<'ctx, D>, -) -> anyhow::Result> -where - D: Dependency + Clone, -{ - let heap_end = context.build_sbrk(context.xlen_type().const_zero())?; - let heap_start = context - .get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)? - .value - .as_pointer_value(); - let heap_size = context.builder().build_int_nuw_sub( - context - .builder() - .build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?, - context - .builder() - .build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?, - "heap_size", - )?; - Ok(context - .builder() - .build_int_z_extend(heap_size, context.word_type(), "heap_size_extended")? - .as_basic_value_enum()) -} - /// Translates the `address` instruction. pub fn address<'ctx, D>( context: &mut Context<'ctx, D>, diff --git a/crates/llvm-context/src/polkavm/evm/memory.rs b/crates/llvm-context/src/polkavm/evm/memory.rs index fa1b82e..8db2db8 100644 --- a/crates/llvm-context/src/polkavm/evm/memory.rs +++ b/crates/llvm-context/src/polkavm/evm/memory.rs @@ -1,10 +1,29 @@ //! Translates the heap memory operations. +use inkwell::values::BasicValue; + use crate::polkavm::context::address_space::AddressSpace; use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::Context; use crate::polkavm::Dependency; +/// Translates the `msize` instruction. +pub fn msize<'ctx, D>( + context: &mut Context<'ctx, D>, +) -> anyhow::Result> +where + D: Dependency + Clone, +{ + Ok(context + .builder() + .build_int_z_extend( + context.build_msize()?, + context.word_type(), + "heap_size_extended", + )? + .as_basic_value_enum()) +} + /// Translates the `mload` instruction. /// Uses the main heap. pub fn load<'ctx, D>( diff --git a/crates/runner/src/specs.rs b/crates/runner/src/specs.rs index 5d4ffd1..b068d3c 100644 --- a/crates/runner/src/specs.rs +++ b/crates/runner/src/specs.rs @@ -289,16 +289,10 @@ impl Specs { else { panic!("the differential runner requires Code::Solidity source"); }; - - assert_ne!( - solc_optimizer, - Some(false), - "solc_optimizer must be enabled in differntial mode" - ); assert_ne!( pipeline, Some(revive_solidity::SolcPipeline::EVMLA), - "yul pipeline must be enabled in differntial mode" + "yul pipeline must be enabled in differential mode" ); assert!( salt.0.is_none(), @@ -311,9 +305,11 @@ impl Specs { ); let deploy_code = match std::fs::read_to_string(&path) { - Ok(solidity_source) => { - hex::encode(compile_evm_deploy_code(&contract, &solidity_source)) - } + Ok(solidity_source) => hex::encode(compile_evm_deploy_code( + &contract, + &solidity_source, + solc_optimizer.unwrap_or(true), + )), Err(err) => panic!( "failed to read solidity source\n . path: '{}'\n . error: {:?}", path.display(), diff --git a/crates/runtime-api/src/polkavm_imports.c b/crates/runtime-api/src/polkavm_imports.c index 108f96a..9e6f05d 100644 --- a/crates/runtime-api/src/polkavm_imports.c +++ b/crates/runtime-api/src/polkavm_imports.c @@ -3,9 +3,34 @@ #include "polkavm_guest.h" - // Missing builtins +#define EVM_WORD_SIZE 32 +#define ALIGN(size) ((size + EVM_WORD_SIZE - 1) & ~(EVM_WORD_SIZE - 1)) +#define MAX_MEMORY_SIZE (64 * 1024) +static char __memory[MAX_MEMORY_SIZE]; +static uint32_t __memory_size = 0; + +void * __sbrk_internal(uint32_t offset, uint32_t size) { + if (offset >= MAX_MEMORY_SIZE || size > MAX_MEMORY_SIZE) { + return NULL; + } + + uint32_t new_size = ALIGN(offset + size); + if (new_size > MAX_MEMORY_SIZE) { + return NULL; + } + if (new_size > __memory_size) { + __memory_size = new_size; + } + + return (void *)&__memory[__memory_size]; +} + +uint32_t __msize() { + return __memory_size; +} + void * memset(void *b, int c, size_t len) { uint8_t *dest = b; while (len-- > 0) *dest++ = c; @@ -37,18 +62,6 @@ void * memmove(void *dst, const void *src, size_t n) { return dst; } -void * __sbrk(uint32_t size) { - uint32_t address; - __asm__ __volatile__( - ".insn r 0xb, 1, 0, %[dst], %[sz], zero" - : [dst] "=r" (address) - : [sz] "ir" (size) - : - ); - return (void *)address; -} - - // Imports POLKAVM_IMPORT(void, input, uint32_t, uint32_t) diff --git a/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs b/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs index 58f00a6..dc4f963 100644 --- a/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs +++ b/crates/solidity/src/evmla/ethereal_ir/function/block/element/mod.rs @@ -1206,7 +1206,7 @@ where anyhow::bail!("The `BLOBBASEFEE` instruction is not supported until zkVM v1.5.0"); } InstructionName::MSIZE => { - revive_llvm_context::polkavm_evm_contract_context::msize(context).map(Some) + revive_llvm_context::polkavm_evm_memory::msize(context).map(Some) } InstructionName::CALLCODE => { diff --git a/crates/solidity/src/test_utils.rs b/crates/solidity/src/test_utils.rs index b88eeb5..f3f436c 100644 --- a/crates/solidity/src/test_utils.rs +++ b/crates/solidity/src/test_utils.rs @@ -276,18 +276,26 @@ pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec { /// Compile the EVM bin-runtime of `contract_name` found in given `source_code`. /// The `solc` optimizer will be enabled pub fn compile_evm_bin_runtime(contract_name: &str, source_code: &str) -> Vec { - compile_evm(contract_name, source_code, true) + compile_evm(contract_name, source_code, true, true) } /// Compile the EVM bin of `contract_name` found in given `source_code`. /// The `solc` optimizer will be enabled -pub fn compile_evm_deploy_code(contract_name: &str, source_code: &str) -> Vec { - compile_evm(contract_name, source_code, false) +pub fn compile_evm_deploy_code( + contract_name: &str, + source_code: &str, + solc_optimizer_enabled: bool, +) -> Vec { + compile_evm(contract_name, source_code, solc_optimizer_enabled, false) } -fn compile_evm(contract_name: &str, source_code: &str, runtime: bool) -> Vec { +fn compile_evm( + contract_name: &str, + source_code: &str, + solc_optimizer_enabled: bool, + runtime: bool, +) -> Vec { let pipeline = SolcPipeline::Yul; - let solc_optimizer_enabled = true; let id = CachedBlob { contract_name: contract_name.to_owned(), pipeline, diff --git a/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs b/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs index 8a16a48..dcc4423 100644 --- a/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs +++ b/crates/solidity/src/yul/parser/statement/expression/function_call/mod.rs @@ -963,9 +963,7 @@ impl FunctionCall { location ); } - Name::MSize => { - revive_llvm_context::polkavm_evm_contract_context::msize(context).map(Some) - } + Name::MSize => revive_llvm_context::polkavm_evm_memory::msize(context).map(Some), Name::Verbatim { input_size,