mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-14 15:41:04 +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.
124 lines
4.4 KiB
Rust
124 lines
4.4 KiB
Rust
//! Translates the arithmetic operations.
|
|
|
|
use inkwell::values::BasicValue;
|
|
|
|
use crate::polkavm::context::runtime::RuntimeFunction;
|
|
use crate::polkavm::context::Context;
|
|
use crate::polkavm::Dependency;
|
|
use crate::PolkaVMDivisionFunction;
|
|
use crate::PolkaVMRemainderFunction;
|
|
use crate::PolkaVMSignedDivisionFunction;
|
|
use crate::PolkaVMSignedRemainderFunction;
|
|
|
|
/// Translates the arithmetic addition.
|
|
pub fn addition<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
Ok(context
|
|
.builder()
|
|
.build_int_add(operand_1, operand_2, "addition_result")?
|
|
.as_basic_value_enum())
|
|
}
|
|
|
|
/// Translates the arithmetic subtraction.
|
|
pub fn subtraction<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
Ok(context
|
|
.builder()
|
|
.build_int_sub(operand_1, operand_2, "subtraction_result")?
|
|
.as_basic_value_enum())
|
|
}
|
|
|
|
/// Translates the arithmetic multiplication.
|
|
pub fn multiplication<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
Ok(context
|
|
.builder()
|
|
.build_int_mul(operand_1, operand_2, "multiplication_result")?
|
|
.as_basic_value_enum())
|
|
}
|
|
|
|
/// Translates the arithmetic division.
|
|
pub fn division<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
let name = <PolkaVMDivisionFunction as RuntimeFunction<D>>::NAME;
|
|
let declaration = <PolkaVMDivisionFunction as RuntimeFunction<D>>::declaration(context);
|
|
Ok(context
|
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "div")
|
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
|
|
}
|
|
|
|
/// Translates the arithmetic remainder.
|
|
pub fn remainder<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
let name = <PolkaVMRemainderFunction as RuntimeFunction<D>>::NAME;
|
|
let declaration = <PolkaVMRemainderFunction as RuntimeFunction<D>>::declaration(context);
|
|
Ok(context
|
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "rem")
|
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
|
|
}
|
|
|
|
/// Translates the signed arithmetic division.
|
|
/// Two differences between the EVM and LLVM IR:
|
|
/// 1. In case of division by zero, 0 is returned.
|
|
/// 2. In case of overflow, the first argument is returned.
|
|
pub fn division_signed<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
let name = <PolkaVMSignedDivisionFunction as RuntimeFunction<D>>::NAME;
|
|
let declaration = <PolkaVMSignedDivisionFunction as RuntimeFunction<D>>::declaration(context);
|
|
Ok(context
|
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "sdiv")
|
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
|
|
}
|
|
|
|
/// Translates the signed arithmetic remainder.
|
|
pub fn remainder_signed<'ctx, D>(
|
|
context: &mut Context<'ctx, D>,
|
|
operand_1: inkwell::values::IntValue<'ctx>,
|
|
operand_2: inkwell::values::IntValue<'ctx>,
|
|
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
|
where
|
|
D: Dependency + Clone,
|
|
{
|
|
let name = <PolkaVMSignedRemainderFunction as RuntimeFunction<D>>::NAME;
|
|
let declaration = <PolkaVMSignedRemainderFunction as RuntimeFunction<D>>::declaration(context);
|
|
Ok(context
|
|
.build_call(declaration, &[operand_1.into(), operand_2.into()], "srem")
|
|
.unwrap_or_else(|| panic!("revive runtime function {name} should return a value",)))
|
|
}
|