diff --git a/crates/benchmarks/BENCHMARKS.md b/crates/benchmarks/BENCHMARKS.md index be529a2..b02dcd2 100644 --- a/crates/benchmarks/BENCHMARKS.md +++ b/crates/benchmarks/BENCHMARKS.md @@ -17,56 +17,56 @@ | | `EVM` | `PVMInterpreter` | |:--------|:-------------------------|:-------------------------------- | -| **`0`** | `10.63 us` (✅ **1.00x**) | `10.35 us` (✅ **1.03x faster**) | +| **`0`** | `10.08 us` (✅ **1.00x**) | `10.32 us` (✅ **1.02x slower**) | ### OddPorduct | | `EVM` | `PVMInterpreter` | |:-------------|:--------------------------|:-------------------------------- | -| **`10000`** | `3.63 ms` (✅ **1.00x**) | `1.66 ms` (🚀 **2.19x faster**) | -| **`100000`** | `36.66 ms` (✅ **1.00x**) | `16.39 ms` (🚀 **2.24x faster**) | -| **`300000`** | `108.64 ms` (✅ **1.00x**) | `50.48 ms` (🚀 **2.15x faster**) | +| **`10000`** | `3.60 ms` (✅ **1.00x**) | `1.57 ms` (🚀 **2.28x faster**) | +| **`100000`** | `34.72 ms` (✅ **1.00x**) | `14.82 ms` (🚀 **2.34x faster**) | +| **`300000`** | `105.01 ms` (✅ **1.00x**) | `44.11 ms` (🚀 **2.38x faster**) | ### TriangleNumber | | `EVM` | `PVMInterpreter` | |:-------------|:-------------------------|:-------------------------------- | -| **`10000`** | `2.59 ms` (✅ **1.00x**) | `1.20 ms` (🚀 **2.17x faster**) | -| **`100000`** | `25.50 ms` (✅ **1.00x**) | `11.82 ms` (🚀 **2.16x faster**) | -| **`360000`** | `91.57 ms` (✅ **1.00x**) | `42.11 ms` (🚀 **2.17x faster**) | +| **`10000`** | `2.43 ms` (✅ **1.00x**) | `1.12 ms` (🚀 **2.17x faster**) | +| **`100000`** | `24.20 ms` (✅ **1.00x**) | `10.86 ms` (🚀 **2.23x faster**) | +| **`360000`** | `88.69 ms` (✅ **1.00x**) | `38.46 ms` (🚀 **2.31x faster**) | ### FibonacciRecursive | | `EVM` | `PVMInterpreter` | |:---------|:--------------------------|:--------------------------------- | -| **`12`** | `149.13 us` (✅ **1.00x**) | `154.35 us` (✅ **1.04x slower**) | -| **`16`** | `972.01 us` (✅ **1.00x**) | `924.33 us` (✅ **1.05x faster**) | -| **`20`** | `6.62 ms` (✅ **1.00x**) | `6.23 ms` (✅ **1.06x faster**) | -| **`24`** | `45.25 ms` (✅ **1.00x**) | `43.44 ms` (✅ **1.04x faster**) | +| **`12`** | `144.17 us` (✅ **1.00x**) | `150.85 us` (✅ **1.05x slower**) | +| **`16`** | `938.71 us` (✅ **1.00x**) | `922.11 us` (✅ **1.02x faster**) | +| **`20`** | `6.54 ms` (✅ **1.00x**) | `6.20 ms` (✅ **1.05x faster**) | +| **`24`** | `45.73 ms` (✅ **1.00x**) | `41.98 ms` (✅ **1.09x faster**) | ### FibonacciIterative | | `EVM` | `PVMInterpreter` | |:----------|:-------------------------|:-------------------------------- | -| **`64`** | `22.71 us` (✅ **1.00x**) | `31.48 us` (❌ *1.39x slower*) | -| **`128`** | `35.32 us` (✅ **1.00x**) | `41.87 us` (❌ *1.19x slower*) | -| **`256`** | `59.58 us` (✅ **1.00x**) | `63.43 us` (✅ **1.06x slower**) | +| **`64`** | `23.00 us` (✅ **1.00x**) | `31.88 us` (❌ *1.39x slower*) | +| **`128`** | `35.28 us` (✅ **1.00x**) | `42.43 us` (❌ *1.20x slower*) | +| **`256`** | `60.12 us` (✅ **1.00x**) | `61.20 us` (✅ **1.02x slower**) | ### FibonacciBinet | | `EVM` | `PVMInterpreter` | |:----------|:-------------------------|:-------------------------------- | -| **`64`** | `23.18 us` (✅ **1.00x**) | `47.33 us` (❌ *2.04x slower*) | -| **`128`** | `24.97 us` (✅ **1.00x**) | `50.37 us` (❌ *2.02x slower*) | -| **`256`** | `28.25 us` (✅ **1.00x**) | `53.69 us` (❌ *1.90x slower*) | +| **`64`** | `23.01 us` (✅ **1.00x**) | `47.74 us` (❌ *2.07x slower*) | +| **`128`** | `25.44 us` (✅ **1.00x**) | `49.67 us` (❌ *1.95x slower*) | +| **`256`** | `28.66 us` (✅ **1.00x**) | `53.01 us` (❌ *1.85x slower*) | ### SHA1 | | `EVM` | `PVMInterpreter` | |:----------|:--------------------------|:--------------------------------- | -| **`1`** | `132.75 us` (✅ **1.00x**) | `232.17 us` (❌ *1.75x slower*) | -| **`64`** | `240.82 us` (✅ **1.00x**) | `328.19 us` (❌ *1.36x slower*) | -| **`512`** | `1.03 ms` (✅ **1.00x**) | `1.03 ms` (✅ **1.01x faster**) | +| **`1`** | `135.87 us` (✅ **1.00x**) | `243.75 us` (❌ *1.79x slower*) | +| **`64`** | `258.45 us` (✅ **1.00x**) | `355.70 us` (❌ *1.38x slower*) | +| **`512`** | `1.10 ms` (✅ **1.00x**) | `1.09 ms` (✅ **1.01x faster**) | --- Made with [criterion-table](https://github.com/nu11ptr/criterion-table) diff --git a/crates/integration/codesize.json b/crates/integration/codesize.json index 4e2d0cd..28819e3 100644 --- a/crates/integration/codesize.json +++ b/crates/integration/codesize.json @@ -1,10 +1,10 @@ { - "Baseline": 1365, - "Computation": 2710, - "DivisionArithmetics": 9672, - "ERC20": 19131, - "Events": 2123, - "FibonacciIterative": 1964, - "Flipper": 2590, - "SHA1": 8918 + "Baseline": 950, + "Computation": 2222, + "DivisionArithmetics": 8802, + "ERC20": 17602, + "Events": 1628, + "FibonacciIterative": 1485, + "Flipper": 2082, + "SHA1": 8230 } \ No newline at end of file diff --git a/crates/llvm-context/src/polkavm/const/mod.rs b/crates/llvm-context/src/polkavm/const/mod.rs index 3285539..89fd234 100644 --- a/crates/llvm-context/src/polkavm/const/mod.rs +++ b/crates/llvm-context/src/polkavm/const/mod.rs @@ -9,6 +9,9 @@ pub static XLEN: usize = revive_common::BIT_LENGTH_X32; /// The calldata size global variable name. pub static GLOBAL_CALLDATA_SIZE: &str = "calldatasize"; +/// The spill buffer global variable name. +pub static GLOBAL_ADDRESS_SPILL_BUFFER: &str = "address_spill_buffer"; + /// The deployer call header size that consists of: /// - bytecode hash (32 bytes) pub const DEPLOYER_CALL_HEADER_SIZE: usize = revive_common::BYTE_LENGTH_WORD; 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 2154eca..14d8d93 100644 --- a/crates/llvm-context/src/polkavm/context/function/runtime/entry.rs +++ b/crates/llvm-context/src/polkavm/context/function/runtime/entry.rs @@ -31,6 +31,14 @@ impl Entry { context.xlen_type().get_undef(), ); + let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); + context.set_global( + crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER, + address_type, + AddressSpace::Stack, + address_type.const_zero(), + ); + Ok(()) } diff --git a/crates/llvm-context/src/polkavm/context/mod.rs b/crates/llvm-context/src/polkavm/context/mod.rs index de83e4f..d58623c 100644 --- a/crates/llvm-context/src/polkavm/context/mod.rs +++ b/crates/llvm-context/src/polkavm/context/mod.rs @@ -101,9 +101,6 @@ where solidity_data: Option, /// The Yul data. yul_data: Option, - - /// Hints whether the contracts deploy function stores immutables. - immutables: bool, } impl<'ctx, D> Context<'ctx, D> @@ -266,8 +263,6 @@ where solidity_data: None, yul_data: None, - - immutables: false, } } @@ -755,7 +750,9 @@ where address: inkwell::values::IntValue<'ctx>, ) -> anyhow::Result> { let address_type = self.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); - let address_pointer = self.build_alloca_at_entry(address_type, "address_pointer"); + let address_pointer = self + .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? + .into(); let address_truncated = self.builder() .build_int_truncate(address, address_type, "address_truncated")?; @@ -1431,14 +1428,4 @@ where pub fn optimizer_settings(&self) -> &OptimizerSettings { self.optimizer.settings() } - - /// Hint the deploy code exit routine to emit storing the immutables. - pub fn enable_immutables(&mut self) { - self.immutables = true; - } - - /// Returns if the contract stores or loads immutables. - pub fn has_immutables(&self) -> bool { - self.immutables - } } diff --git a/crates/llvm-context/src/polkavm/evm/bitwise.rs b/crates/llvm-context/src/polkavm/evm/bitwise.rs index 786ae79..0a02239 100644 --- a/crates/llvm-context/src/polkavm/evm/bitwise.rs +++ b/crates/llvm-context/src/polkavm/evm/bitwise.rs @@ -63,7 +63,6 @@ where let non_overflow_block = context.append_basic_block("shift_left_non_overflow"); let join_block = context.append_basic_block("shift_left_join"); - let result_pointer = context.build_alloca(context.word_type(), "shift_left_result_pointer"); let condition_is_overflow = context.builder().build_int_compare( inkwell::IntPredicate::UGT, shift, @@ -73,7 +72,6 @@ where context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.set_basic_block(overflow_block); - context.build_store(result_pointer, context.word_const(0))?; context.build_unconditional_branch(join_block); context.set_basic_block(non_overflow_block); @@ -81,11 +79,17 @@ where context .builder() .build_left_shift(value, shift, "shift_left_non_overflow_result")?; - context.build_store(result_pointer, value)?; context.build_unconditional_branch(join_block); context.set_basic_block(join_block); - context.build_load(result_pointer, "shift_left_result") + let result = context + .builder() + .build_phi(context.word_type(), "shift_left_value")?; + result.add_incoming(&[ + (&value, non_overflow_block), + (&context.word_const(0), overflow_block), + ]); + Ok(result.as_basic_value()) } /// Translates the bitwise shift right. @@ -101,7 +105,6 @@ where let non_overflow_block = context.append_basic_block("shift_right_non_overflow"); let join_block = context.append_basic_block("shift_right_join"); - let result_pointer = context.build_alloca(context.word_type(), "shift_right_result_pointer"); let condition_is_overflow = context.builder().build_int_compare( inkwell::IntPredicate::UGT, shift, @@ -111,7 +114,6 @@ where context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?; context.set_basic_block(overflow_block); - context.build_store(result_pointer, context.word_const(0))?; context.build_unconditional_branch(join_block); context.set_basic_block(non_overflow_block); @@ -121,11 +123,17 @@ where false, "shift_right_non_overflow_result", )?; - context.build_store(result_pointer, value)?; context.build_unconditional_branch(join_block); context.set_basic_block(join_block); - context.build_load(result_pointer, "shift_right_result") + let result = context + .builder() + .build_phi(context.word_type(), "shift_right_value")?; + result.add_incoming(&[ + (&value, non_overflow_block), + (&context.word_const(0), overflow_block), + ]); + Ok(result.as_basic_value()) } /// Translates the arithmetic bitwise shift right. @@ -145,8 +153,6 @@ where let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow"); let join_block = context.append_basic_block("shift_right_arithmetic_join"); - let result_pointer = - context.build_alloca(context.word_type(), "shift_right_arithmetic_result_pointer"); let condition_is_overflow = context.builder().build_int_compare( inkwell::IntPredicate::UGT, shift, @@ -174,11 +180,9 @@ where )?; context.set_basic_block(overflow_positive_block); - context.build_store(result_pointer, context.word_const(0))?; context.build_unconditional_branch(join_block); context.set_basic_block(overflow_negative_block); - context.build_store(result_pointer, context.word_type().const_all_ones())?; context.build_unconditional_branch(join_block); context.set_basic_block(non_overflow_block); @@ -188,11 +192,21 @@ where true, "shift_right_arithmetic_non_overflow_result", )?; - context.build_store(result_pointer, value)?; context.build_unconditional_branch(join_block); context.set_basic_block(join_block); - context.build_load(result_pointer, "shift_right_arithmetic_result") + let result = context + .builder() + .build_phi(context.word_type(), "shift_arithmetic_right_value")?; + result.add_incoming(&[ + (&value, non_overflow_block), + ( + &context.word_type().const_all_ones(), + overflow_negative_block, + ), + (&context.word_const(0), overflow_block), + ]); + Ok(result.as_basic_value()) } /// Translates the `byte` instruction, extracting the byte of `operand_2` diff --git a/crates/llvm-context/src/polkavm/evm/context.rs b/crates/llvm-context/src/polkavm/evm/context.rs index 8a90e30..2e293d1 100644 --- a/crates/llvm-context/src/polkavm/evm/context.rs +++ b/crates/llvm-context/src/polkavm/evm/context.rs @@ -2,6 +2,7 @@ use inkwell::values::BasicValue; +use crate::polkavm::context::pointer::Pointer; use crate::polkavm::context::Context; use crate::polkavm::Dependency; @@ -49,7 +50,9 @@ where D: Dependency + Clone, { let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS); - let address_pointer = context.build_alloca_at_entry(address_type, "origin_address"); + let address_pointer: Pointer<'_> = context + .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? + .into(); context.build_store(address_pointer, address_type.const_zero())?; context.build_runtime_call( revive_runtime_api::polkavm_imports::ORIGIN, @@ -97,13 +100,13 @@ where D: Dependency + Clone, { let output_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_out_ptr"); - let index_ptr = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr"); - context.build_store(index_ptr, index)?; + let index_pointer = context.build_alloca_at_entry(context.word_type(), "blockhash_index_ptr"); + context.build_store(index_pointer, index)?; context.build_runtime_call( revive_runtime_api::polkavm_imports::BLOCK_HASH, &[ - index_ptr.to_int(context).into(), + index_pointer.to_int(context).into(), output_pointer.to_int(context).into(), ], ); @@ -127,10 +130,9 @@ pub fn coinbase<'ctx, D>( where D: Dependency + Clone, { - let pointer = context.build_alloca_at_entry( - context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), - "coinbase_output", - ); + let pointer: Pointer<'_> = context + .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? + .into(); context.build_runtime_call( revive_runtime_api::polkavm_imports::BLOCK_AUTHOR, &[pointer.to_int(context).into()], @@ -155,10 +157,9 @@ pub fn address<'ctx, D>( where D: Dependency + Clone, { - let pointer = context.build_alloca_at_entry( - context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), - "address_output", - ); + let pointer: Pointer<'_> = context + .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? + .into(); context.build_runtime_call( revive_runtime_api::polkavm_imports::ADDRESS, &[pointer.to_int(context).into()], @@ -173,10 +174,9 @@ pub fn caller<'ctx, D>( where D: Dependency + Clone, { - let pointer = context.build_alloca_at_entry( - context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS), - "address_output", - ); + let pointer: Pointer<'_> = context + .get_global(crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER)? + .into(); context.build_runtime_call( revive_runtime_api::polkavm_imports::CALLER, &[pointer.to_int(context).into()], diff --git a/crates/llvm-context/src/polkavm/evm/ether_gas.rs b/crates/llvm-context/src/polkavm/evm/ether_gas.rs index 08cfd3a..bfa76e9 100644 --- a/crates/llvm-context/src/polkavm/evm/ether_gas.rs +++ b/crates/llvm-context/src/polkavm/evm/ether_gas.rs @@ -28,7 +28,7 @@ pub fn value<'ctx, D>( where D: Dependency + Clone, { - let output_pointer = context.build_alloca(context.value_type(), "value_transferred"); + let output_pointer = context.build_alloca_at_entry(context.value_type(), "value_transferred"); context.build_store(output_pointer, context.word_const(0))?; context.build_runtime_call( revive_runtime_api::polkavm_imports::VALUE_TRANSFERRED, @@ -46,8 +46,7 @@ where D: Dependency + Clone, { let address_pointer = context.build_address_argument_store(address)?; - - let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer"); + let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer"); let balance = context.builder().build_ptr_to_int( balance_pointer.value, context.xlen_type(), @@ -69,7 +68,7 @@ pub fn self_balance<'ctx, D>( where D: Dependency + Clone, { - let balance_pointer = context.build_alloca(context.word_type(), "balance_pointer"); + let balance_pointer = context.build_alloca_at_entry(context.word_type(), "balance_pointer"); let balance = context.builder().build_ptr_to_int( balance_pointer.value, context.xlen_type(), diff --git a/crates/llvm-context/src/polkavm/evm/immutable.rs b/crates/llvm-context/src/polkavm/evm/immutable.rs index 40343d9..60f2827 100644 --- a/crates/llvm-context/src/polkavm/evm/immutable.rs +++ b/crates/llvm-context/src/polkavm/evm/immutable.rs @@ -257,7 +257,6 @@ where anyhow::bail!("Immutables are not available if the contract part is undefined"); } Some(CodeType::Deploy) => { - context.enable_immutables(); let immutable_data_pointer = context .get_global(revive_runtime_api::immutable_data::GLOBAL_IMMUTABLE_DATA_POINTER)? .value diff --git a/crates/llvm-context/src/polkavm/evm/return.rs b/crates/llvm-context/src/polkavm/evm/return.rs index 9dacd1e..717ce81 100644 --- a/crates/llvm-context/src/polkavm/evm/return.rs +++ b/crates/llvm-context/src/polkavm/evm/return.rs @@ -18,13 +18,11 @@ where match context.code_type() { None => anyhow::bail!("Return is not available if the contract part is undefined"), Some(CodeType::Deploy) => { - if context.has_immutables() { - context.build_call( - >::declaration(context), - Default::default(), - "store_immutable_data", - ); - } + context.build_call( + >::declaration(context), + Default::default(), + "store_immutable_data", + ); } Some(CodeType::Runtime) => {} } diff --git a/crates/solidity/src/test_utils.rs b/crates/solidity/src/test_utils.rs index ef1caff..72d1f88 100644 --- a/crates/solidity/src/test_utils.rs +++ b/crates/solidity/src/test_utils.rs @@ -92,7 +92,7 @@ pub fn build_solidity_with_options( SolcStandardJsonInputSettingsSelection::new_required(), SolcStandardJsonInputSettingsOptimizer::new( solc_optimizer_enabled, - None, + optimizer_settings.middle_end_as_string().chars().last(), &solc_version.default, false, ), @@ -102,16 +102,21 @@ pub fn build_solidity_with_options( let mut output = solc.standard_json(input, None, vec![], None)?; + let debug_config = revive_llvm_context::DebugConfig::new( + None, + optimizer_settings.middle_end_as_string() != "z", + ); + let project = Project::try_from_standard_json_output( &output, sources, libraries, &solc_version, - &DEBUG_CONFIG, + &debug_config, )?; let build: crate::Build = - project.compile(optimizer_settings, false, DEBUG_CONFIG, Default::default())?; + project.compile(optimizer_settings, false, debug_config, Default::default())?; build.write_to_standard_json(&mut output, &solc_version)?; Ok(output)