mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-21 12:41:02 +00:00
a07968205b
- 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.
114 lines
3.7 KiB
Rust
114 lines
3.7 KiB
Rust
//! The revive compiler runtime function interface definition.
|
|
//!
|
|
//! Common routines should not be inlined but extracted into smaller functions.
|
|
//! This benefits contract code size.
|
|
|
|
use crate::optimizer::settings::size_level::SizeLevel;
|
|
use crate::polkavm::context::function::declaration::Declaration;
|
|
use crate::polkavm::context::function::Function;
|
|
use crate::polkavm::context::Attribute;
|
|
use crate::polkavm::context::Context;
|
|
use crate::polkavm::Dependency;
|
|
|
|
/// The revive runtime function interface simplifies declaring runtime functions
|
|
/// and code emitting by providing helpful default implementations.
|
|
pub trait RuntimeFunction<D>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
/// The function name.
|
|
const NAME: &'static str;
|
|
|
|
const ATTRIBUTES: &'static [Attribute] = &[
|
|
Attribute::NoFree,
|
|
Attribute::NoRecurse,
|
|
Attribute::WillReturn,
|
|
];
|
|
|
|
/// The function type.
|
|
fn r#type<'ctx>(context: &Context<'ctx, D>) -> inkwell::types::FunctionType<'ctx>;
|
|
|
|
/// Declare the function.
|
|
fn declare(&self, context: &mut Context<D>) -> anyhow::Result<()> {
|
|
let function = context.add_function(
|
|
Self::NAME,
|
|
Self::r#type(context),
|
|
0,
|
|
Some(inkwell::module::Linkage::External),
|
|
)?;
|
|
|
|
let mut attributes = Self::ATTRIBUTES.to_vec();
|
|
attributes.extend_from_slice(match context.optimizer_settings().level_middle_end_size {
|
|
SizeLevel::Zero => &[],
|
|
_ => &[Attribute::OptimizeForSize, Attribute::MinSize],
|
|
});
|
|
Function::set_attributes(
|
|
context.llvm(),
|
|
function.borrow().declaration(),
|
|
&attributes,
|
|
true,
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Get the function declaration.
|
|
fn declaration<'ctx>(context: &Context<'ctx, D>) -> Declaration<'ctx> {
|
|
context
|
|
.get_function(Self::NAME)
|
|
.unwrap_or_else(|| panic!("runtime function {} should be declared", Self::NAME))
|
|
.borrow()
|
|
.declaration()
|
|
}
|
|
|
|
/// Emit the function.
|
|
fn emit(&self, context: &mut Context<D>) -> anyhow::Result<()> {
|
|
context.set_current_function(Self::NAME, None)?;
|
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
|
|
|
let return_value = self.emit_body(context)?;
|
|
self.emit_epilogue(context, return_value);
|
|
|
|
context.pop_debug_scope();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Emit the function body.
|
|
fn emit_body<'ctx>(
|
|
&self,
|
|
context: &mut Context<'ctx, D>,
|
|
) -> anyhow::Result<Option<inkwell::values::BasicValueEnum<'ctx>>>;
|
|
|
|
/// Emit the function return instructions.
|
|
fn emit_epilogue<'ctx>(
|
|
&self,
|
|
context: &mut Context<'ctx, D>,
|
|
return_value: Option<inkwell::values::BasicValueEnum<'ctx>>,
|
|
) {
|
|
let return_block = context.current_function().borrow().return_block();
|
|
context.build_unconditional_branch(return_block);
|
|
context.set_basic_block(return_block);
|
|
match return_value {
|
|
Some(value) => context.build_return(Some(&value)),
|
|
None => context.build_return(None),
|
|
}
|
|
}
|
|
|
|
/// Get the nth function paramater.
|
|
fn paramater<'ctx>(
|
|
context: &Context<'ctx, D>,
|
|
index: usize,
|
|
) -> inkwell::values::BasicValueEnum<'ctx> {
|
|
let name = Self::NAME;
|
|
context
|
|
.get_function(name)
|
|
.unwrap_or_else(|| panic!("runtime function {name} should be declared"))
|
|
.borrow()
|
|
.declaration()
|
|
.function_value()
|
|
.get_nth_param(index as u32)
|
|
.unwrap_or_else(|| panic!("runtime function {name} should have parameter #{index}"))
|
|
}
|
|
}
|