Prevent frontend function confusion (#383)

Function symbols can clash as we compile multiple YUL `object`
definition into the same `LLVM` module.
- Disambiguate via unique function symbols based its location (runtime
or deploy code).
- Use `LinkOnceODR` linkage for compiler builtin helpers.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus
2025-09-29 23:05:57 +02:00
committed by GitHub
parent 6858cb9a61
commit b560d72139
12 changed files with 103 additions and 58 deletions
+8 -8
View File
@@ -1,10 +1,10 @@
{
"Baseline": 960,
"Computation": 2356,
"DivisionArithmetics": 8964,
"ERC20": 17143,
"Events": 1680,
"FibonacciIterative": 1502,
"Flipper": 2137,
"SHA1": 7740
"Baseline": 932,
"Computation": 2313,
"DivisionArithmetics": 8959,
"ERC20": 16993,
"Events": 1692,
"FibonacciIterative": 1474,
"Flipper": 2098,
"SHA1": 7751
}
@@ -1,6 +1,5 @@
//! The deploy code function.
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime;
use crate::polkavm::context::Context;
use crate::polkavm::WriteLLVM;
@@ -38,16 +37,16 @@ where
0,
Some(inkwell::module::Linkage::Private),
None,
false,
)?;
self.inner.declare(context)
}
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE, None)?;
context.set_current_function(runtime::FUNCTION_DEPLOY_CODE, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Deploy);
self.inner.into_llvm(context)?;
context.set_debug_location(0, 0, None)?;
@@ -138,6 +138,7 @@ impl WriteLLVM for Entry {
0,
Some(inkwell::module::Linkage::External),
None,
false,
)?;
context.declare_global(
@@ -160,7 +161,7 @@ impl WriteLLVM for Entry {
/// The `entry` function loads calldata, sets globals and calls the runtime or deploy code.
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
let entry = context
.get_function(runtime::FUNCTION_ENTRY)
.get_function(runtime::FUNCTION_ENTRY, false)
.expect("the entry function should already be declared")
.borrow()
.declaration;
@@ -171,7 +172,7 @@ impl WriteLLVM for Entry {
true,
);
context.set_current_function(runtime::FUNCTION_ENTRY, None)?;
context.set_current_function(runtime::FUNCTION_ENTRY, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block());
Self::initialize_globals(context)?;
@@ -1,6 +1,5 @@
//! The runtime code function.
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime;
use crate::polkavm::context::Context;
use crate::polkavm::WriteLLVM;
@@ -38,16 +37,16 @@ where
0,
Some(inkwell::module::Linkage::Private),
None,
false,
)?;
self.inner.declare(context)
}
fn into_llvm(self, context: &mut Context) -> anyhow::Result<()> {
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE, None)?;
context.set_current_function(runtime::FUNCTION_RUNTIME_CODE, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Runtime);
self.inner.into_llvm(context)?;
context.set_debug_location(0, 0, None)?;
+29 -6
View File
@@ -426,8 +426,15 @@ impl<'ctx> Context<'ctx> {
return_values_length: usize,
linkage: Option<inkwell::module::Linkage>,
location: Option<(u32, u32)>,
is_frontend: bool,
) -> anyhow::Result<Rc<RefCell<Function<'ctx>>>> {
let value = self.module().add_function(name, r#type, linkage);
assert!(
self.get_function(name, is_frontend).is_none(),
"ICE: function '{name}' declared subsequentally"
);
let name = self.internal_function_name(name, is_frontend);
let value = self.module().add_function(&name, r#type, linkage);
if self.debug_info().is_some() {
self.builder().unset_current_debug_location();
@@ -468,7 +475,7 @@ impl<'ctx> Context<'ctx> {
};
let function = Function::new(
name.to_owned(),
name.clone(),
FunctionDeclaration::new(r#type, value),
r#return,
entry_block,
@@ -476,7 +483,7 @@ impl<'ctx> Context<'ctx> {
);
Function::set_default_attributes(self.llvm, function.declaration(), &self.optimizer);
let function = Rc::new(RefCell::new(function));
self.functions.insert(name.to_string(), function.clone());
self.functions.insert(name, function.clone());
self.pop_debug_scope();
@@ -484,8 +491,14 @@ impl<'ctx> Context<'ctx> {
}
/// Returns a shared reference to the specified function.
pub fn get_function(&self, name: &str) -> Option<Rc<RefCell<Function<'ctx>>>> {
self.functions.get(name).cloned()
pub fn get_function(
&self,
name: &str,
is_frontend: bool,
) -> Option<Rc<RefCell<Function<'ctx>>>> {
self.functions
.get(&self.internal_function_name(name, is_frontend))
.cloned()
}
/// Returns a shared reference to the current active function.
@@ -501,8 +514,9 @@ impl<'ctx> Context<'ctx> {
&mut self,
name: &str,
location: Option<(u32, u32)>,
frontend: bool,
) -> anyhow::Result<()> {
let function = self.functions.get(name).cloned().ok_or_else(|| {
let function = self.get_function(name, frontend).ok_or_else(|| {
anyhow::anyhow!("Failed to activate an undeclared function `{}`", name)
})?;
self.current_function = Some(function);
@@ -1371,4 +1385,13 @@ impl<'ctx> Context<'ctx> {
false,
)
}
/// Returns the internal function name.
fn internal_function_name(&self, name: &str, is_frontend: bool) -> String {
if is_frontend {
format!("{name}_{}", self.code_type().unwrap())
} else {
name.to_string()
}
}
}
@@ -30,8 +30,9 @@ pub trait RuntimeFunction {
Self::NAME,
Self::r#type(context),
0,
Some(inkwell::module::Linkage::External), // TODO: `Private` emits unrelocated AUIPC?
Some(inkwell::module::Linkage::LinkOnceODR),
None,
false,
)?;
let mut attributes = Self::ATTRIBUTES.to_vec();
@@ -45,6 +46,12 @@ pub trait RuntimeFunction {
&attributes,
true,
);
let function = function.borrow().declaration().function_value();
let comdat = context
.module()
.get_or_insert_comdat(&format!("{}_comdat", Self::NAME));
comdat.set_selection_kind(inkwell::comdat::ComdatSelectionKind::NoDuplicates);
function.as_global_value().set_comdat(comdat);
Ok(())
}
@@ -52,7 +59,7 @@ pub trait RuntimeFunction {
/// Get the function declaration.
fn declaration<'ctx>(context: &Context<'ctx>) -> Declaration<'ctx> {
context
.get_function(Self::NAME)
.get_function(Self::NAME, false)
.unwrap_or_else(|| panic!("runtime function {} should be declared", Self::NAME))
.borrow()
.declaration()
@@ -60,7 +67,7 @@ pub trait RuntimeFunction {
/// Emit the function.
fn emit(&self, context: &mut Context) -> anyhow::Result<()> {
context.set_current_function(Self::NAME, None)?;
context.set_current_function(Self::NAME, None, false)?;
context.set_basic_block(context.current_function().borrow().entry_block());
let return_value = self.emit_body(context)?;
@@ -99,7 +106,7 @@ pub trait RuntimeFunction {
) -> inkwell::values::BasicValueEnum<'ctx> {
let name = Self::NAME;
context
.get_function(name)
.get_function(name, false)
.unwrap_or_else(|| panic!("runtime function {name} should be declared"))
.borrow()
.declaration()
@@ -38,6 +38,7 @@ pub fn check_attribute_null_pointer_is_invalid() {
1,
Some(inkwell::module::Linkage::External),
None,
false,
)
.expect("Failed to add function");
assert!(!function
@@ -62,6 +63,7 @@ pub fn check_attribute_optimize_for_size_mode_3() {
1,
Some(inkwell::module::Linkage::External),
None,
false,
)
.expect("Failed to add function");
assert!(!function
@@ -86,6 +88,7 @@ pub fn check_attribute_optimize_for_size_mode_z() {
1,
Some(inkwell::module::Linkage::External),
None,
false,
)
.expect("Failed to add function");
assert!(function
@@ -110,6 +113,7 @@ pub fn check_attribute_min_size_mode_3() {
1,
Some(inkwell::module::Linkage::External),
None,
false,
)
.expect("Failed to add function");
assert!(!function
@@ -134,6 +138,7 @@ pub fn check_attribute_min_size_mode_z() {
1,
Some(inkwell::module::Linkage::External),
None,
false,
)
.expect("Failed to add function");
assert!(function
@@ -210,7 +210,7 @@ pub fn load<'ctx>(
let name = <Load as RuntimeFunction>::NAME;
context.build_call(
context
.get_function(name)
.get_function(name, false)
.expect("is always declared for runtime code")
.borrow()
.declaration(),
+1
View File
@@ -159,6 +159,7 @@ impl PolkaVMWriteLLVM for Block {
context.set_current_function(
current_function.as_str(),
Some((self.location.line, self.location.column)),
false,
)?;
if let Some(debug_info) = context.debug_info() {
@@ -135,7 +135,7 @@ impl FunctionCall {
values.push(value);
}
values.reverse();
let function = context.get_function(name.as_str()).ok_or_else(|| {
let function = context.get_function(name.as_str(), true).ok_or_else(|| {
anyhow::anyhow!("{} Undeclared function `{}`", location, name)
})?;
@@ -215,8 +215,9 @@ impl PolkaVMWriteLLVM for FunctionDefinition {
self.identifier.as_str(),
function_type,
self.result.len(),
Some(inkwell::module::Linkage::Private),
Some(inkwell::module::Linkage::External),
Some((self.location.line, self.location.column)),
true,
)?;
PolkaVMFunction::set_attributes(
context.llvm(),
@@ -235,6 +236,7 @@ impl PolkaVMWriteLLVM for FunctionDefinition {
context.set_current_function(
self.identifier.as_str(),
Some((self.location.line, self.location.column)),
true,
)?;
context.set_basic_block(context.current_function().borrow().entry_block());
+37 -29
View File
@@ -4,6 +4,7 @@ use std::collections::BTreeSet;
use std::collections::HashSet;
use inkwell::debug_info::AsDIScope;
use revive_llvm_context::PolkaVMCodeType;
use serde::Deserialize;
use serde::Serialize;
@@ -184,6 +185,12 @@ impl Object {
impl PolkaVMWriteLLVM for Object {
fn declare(&mut self, context: &mut PolkaVMContext) -> anyhow::Result<()> {
if self.identifier.ends_with("_deployed") {
context.set_code_type(PolkaVMCodeType::Runtime);
} else {
context.set_code_type(PolkaVMCodeType::Deploy);
}
revive_llvm_context::PolkaVMLoadImmutableDataFunction.declare(context)?;
revive_llvm_context::PolkaVMStoreImmutableDataFunction.declare(context)?;
@@ -230,40 +237,12 @@ impl PolkaVMWriteLLVM for Object {
.into_iter()
{
context
.get_function(name)
.get_function(name, false)
.expect("Always exists")
.borrow_mut()
.set_yul_data(revive_llvm_context::PolkaVMFunctionYulData::default());
}
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::PolkaVMLoadTransientStorageWordFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMStoreTransientStorageWordFunction.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)?;
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
Ok(())
}
@@ -280,8 +259,37 @@ impl PolkaVMWriteLLVM for Object {
context.set_debug_location(self.location.line, self.location.column, None)?;
if self.identifier.ends_with("_deployed") {
context.set_code_type(PolkaVMCodeType::Runtime);
revive_llvm_context::PolkaVMRuntimeCodeFunction::new(self.code).into_llvm(context)?;
} else {
context.set_code_type(PolkaVMCodeType::Deploy);
revive_llvm_context::PolkaVMEntryFunction::default().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::PolkaVMLoadTransientStorageWordFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMStoreTransientStorageWordFunction.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)?;
revive_llvm_context::PolkaVMSbrkFunction.into_llvm(context)?;
revive_llvm_context::PolkaVMDeployCodeFunction::new(self.code).into_llvm(context)?;
}