mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-16 06:11:08 +00:00
llvm-context: modularize compiler builtin functions (#234)
- Add the revive runtime function interface to minimize boiler plate code. - Outline heavily repeated code into dedicated functions to bring down code size. - The code size tests builds optimized for size. - Function attributes are passed as slices. This significantly brings down the code size for all OpenZeppelin wizard contracts (using all possible features) compiled against OpenZeppelin `v5.0.0` with size optimizations. |contract|| `-Oz` main | `-Oz` PR || `-O3` main | `-O3` PR | |-|-|-|-|-|-|-| |erc1155.sol||100K|67K||114K|147K| |erc20.sol||120K|90K||160K|191K| |erc721.sol||128K|101K||178K|214K| |governor.sol||226K|165K||293K|349K| |rwa.sol||116K|85K||154K|185K| |stable.sol||116K|86K||155K|192K| On the flip side this introduces a heavy penalty for cycle optimized builds. Setting the no-inline attributes for cycle optimized builds helps a lot but heavily penalizes runtime speed (LLVM does not yet inline everything properly - to be investigated later on). Next steps: - Modularize more functions - Refactor the YUL function arguments to use pointers instead of values - Afterwards check if LLVM still has trouble inline-ing properly on O3 or set the no-inline attribute if it does not penalize runtime performance too bad.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
//! Translates the storage operations.
|
||||
|
||||
use crate::polkavm::context::address_space::AddressSpace;
|
||||
use crate::polkavm::context::runtime::RuntimeFunction;
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::PolkaVMLoadStorageWordFunction;
|
||||
use crate::PolkaVMStoreStorageWordFunction;
|
||||
|
||||
/// Translates the storage load.
|
||||
pub fn load<'ctx, D>(
|
||||
@@ -12,10 +14,12 @@ pub fn load<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
|
||||
slot_ptr.address_space = AddressSpace::Storage;
|
||||
context.builder().build_store(slot_ptr.value, position)?;
|
||||
context.build_load(slot_ptr, "storage_load_value")
|
||||
let name = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::NAME;
|
||||
let declaration = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::declaration(context);
|
||||
let arguments = [context.xlen_type().const_zero().into(), position.into()];
|
||||
Ok(context
|
||||
.build_call(declaration, &arguments, "storage_load")
|
||||
.unwrap_or_else(|| panic!("runtime function {name} should return a value")))
|
||||
}
|
||||
|
||||
/// Translates the storage store.
|
||||
@@ -27,10 +31,13 @@ pub fn store<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
|
||||
slot_ptr.address_space = AddressSpace::Storage;
|
||||
context.builder().build_store(slot_ptr.value, position)?;
|
||||
context.build_store(slot_ptr, value)?;
|
||||
let declaration = <PolkaVMStoreStorageWordFunction as RuntimeFunction<D>>::declaration(context);
|
||||
let arguments = [
|
||||
context.xlen_type().const_zero().into(),
|
||||
position.into(),
|
||||
value.into(),
|
||||
];
|
||||
context.build_call(declaration, &arguments, "storage_store");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -42,10 +49,15 @@ pub fn transient_load<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
|
||||
slot_ptr.address_space = AddressSpace::TransientStorage;
|
||||
context.builder().build_store(slot_ptr.value, position)?;
|
||||
context.build_load(slot_ptr, "transient_storage_load_value")
|
||||
let name = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::NAME;
|
||||
let declaration = <PolkaVMLoadStorageWordFunction as RuntimeFunction<D>>::declaration(context);
|
||||
let arguments = [
|
||||
context.xlen_type().const_int(1, false).into(),
|
||||
position.into(),
|
||||
];
|
||||
Ok(context
|
||||
.build_call(declaration, &arguments, "storage_load")
|
||||
.unwrap_or_else(|| panic!("runtime function {name} should return a value")))
|
||||
}
|
||||
|
||||
/// Translates the transient storage store.
|
||||
@@ -57,9 +69,12 @@ pub fn transient_store<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let mut slot_ptr = context.build_alloca_at_entry(context.word_type(), "slot_pointer");
|
||||
slot_ptr.address_space = AddressSpace::TransientStorage;
|
||||
context.builder().build_store(slot_ptr.value, position)?;
|
||||
context.build_store(slot_ptr, value)?;
|
||||
let declaration = <PolkaVMStoreStorageWordFunction as RuntimeFunction<D>>::declaration(context);
|
||||
let arguments = [
|
||||
context.xlen_type().const_int(1, false).into(),
|
||||
position.into(),
|
||||
value.into(),
|
||||
];
|
||||
context.build_call(declaration, &arguments, "storage_store");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user