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:
xermicus
2025-02-25 16:47:01 +01:00
committed by GitHub
parent 7ffe64ed7c
commit a07968205b
32 changed files with 1444 additions and 655 deletions
@@ -672,7 +672,7 @@ impl FunctionCall {
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
vec![],
[],
)
.map(|_| None)
}
@@ -682,10 +682,7 @@ impl FunctionCall {
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
arguments[2..]
.iter()
.map(|argument| argument.into_int_value())
.collect(),
[arguments[2]],
)
.map(|_| None)
}
@@ -695,10 +692,7 @@ impl FunctionCall {
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
arguments[2..]
.iter()
.map(|argument| argument.into_int_value())
.collect(),
[arguments[2], arguments[3]],
)
.map(|_| None)
}
@@ -708,10 +702,7 @@ impl FunctionCall {
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
arguments[2..]
.iter()
.map(|argument| argument.into_int_value())
.collect(),
[arguments[2], arguments[3], arguments[4]],
)
.map(|_| None)
}
@@ -721,10 +712,7 @@ impl FunctionCall {
context,
arguments[0].into_int_value(),
arguments[1].into_int_value(),
arguments[2..]
.iter()
.map(|argument| argument.into_int_value())
.collect(),
[arguments[2], arguments[3], arguments[4], arguments[5]],
)
.map(|_| None)
}
@@ -216,7 +216,7 @@ where
revive_llvm_context::PolkaVMFunction::set_attributes(
context.llvm(),
function.borrow().declaration(),
self.attributes.clone().into_iter().collect(),
&self.attributes.clone().into_iter().collect::<Vec<_>>(),
true,
);
function
@@ -185,7 +185,28 @@ where
&mut self,
context: &mut revive_llvm_context::PolkaVMContext<D>,
) -> anyhow::Result<()> {
revive_llvm_context::PolkaVMImmutableDataLoadFunction.declare(context)?;
revive_llvm_context::PolkaVMLoadImmutableDataFunction.declare(context)?;
revive_llvm_context::PolkaVMStoreImmutableDataFunction.declare(context)?;
revive_llvm_context::PolkaVMLoadHeapWordFunction.declare(context)?;
revive_llvm_context::PolkaVMStoreHeapWordFunction.declare(context)?;
revive_llvm_context::PolkaVMLoadStorageWordFunction.declare(context)?;
revive_llvm_context::PolkaVMStoreStorageWordFunction.declare(context)?;
revive_llvm_context::PolkaVMWordToPointerFunction.declare(context)?;
revive_llvm_context::PolkaVMExitFunction.declare(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<0>.declare(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<1>.declare(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<2>.declare(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<3>.declare(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<4>.declare(context)?;
revive_llvm_context::PolkaVMDivisionFunction.declare(context)?;
revive_llvm_context::PolkaVMSignedDivisionFunction.declare(context)?;
revive_llvm_context::PolkaVMRemainderFunction.declare(context)?;
revive_llvm_context::PolkaVMSignedRemainderFunction.declare(context)?;
let mut entry = revive_llvm_context::PolkaVMEntryFunction::default();
entry.declare(context)?;
@@ -202,7 +223,6 @@ where
revive_llvm_context::PolkaVMFunctionDeployCode,
revive_llvm_context::PolkaVMFunctionRuntimeCode,
revive_llvm_context::PolkaVMFunctionEntry,
revive_llvm_context::PolkaVMFunctionImmutableDataLoad,
]
.into_iter()
{
@@ -215,6 +235,28 @@ where
entry.into_llvm(context)?;
revive_llvm_context::PolkaVMLoadImmutableDataFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMStoreImmutableDataFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMLoadHeapWordFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMStoreHeapWordFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMLoadStorageWordFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMStoreStorageWordFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMWordToPointerFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMExitFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<0>.into_llvm(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<1>.into_llvm(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<2>.into_llvm(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<3>.into_llvm(context)?;
revive_llvm_context::PolkaVMEventLogFunction::<4>.into_llvm(context)?;
revive_llvm_context::PolkaVMDivisionFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSignedDivisionFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMRemainderFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMSignedRemainderFunction.into_llvm(context)?;
Ok(())
}
@@ -230,7 +272,6 @@ where
}
if self.identifier.ends_with("_deployed") {
revive_llvm_context::PolkaVMImmutableDataLoadFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(self.code).into_llvm(context)?;
} else {
revive_llvm_context::PolkaVMDeployCodeFunction::new(self.code).into_llvm(context)?;