llvm-context: alloca at the function entry if possible (#283)

Closes  #48

Change the code size test to no longer emit debug info as to get a more
accurate picture.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus
2025-04-15 15:22:24 +02:00
committed by GitHub
parent 431b5a2ce5
commit 5003f3e9ac
11 changed files with 102 additions and 89 deletions
+20 -20
View File
@@ -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)
+8 -8
View File
@@ -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
}
@@ -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;
@@ -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(())
}
+3 -16
View File
@@ -101,9 +101,6 @@ where
solidity_data: Option<SolidityData>,
/// The Yul data.
yul_data: Option<YulData>,
/// 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<Pointer<'ctx>> {
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
}
}
+28 -14
View File
@@ -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`
+16 -16
View File
@@ -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()],
@@ -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(),
@@ -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
@@ -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(
<Store as RuntimeFunction<D>>::declaration(context),
Default::default(),
"store_immutable_data",
);
}
context.build_call(
<Store as RuntimeFunction<D>>::declaration(context),
Default::default(),
"store_immutable_data",
);
}
Some(CodeType::Runtime) => {}
}
+8 -3
View File
@@ -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)