mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-27 20:57:59 +00:00
b560d72139
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>
191 lines
6.6 KiB
Rust
191 lines
6.6 KiB
Rust
//! The entry function.
|
|
|
|
use inkwell::types::BasicType;
|
|
use revive_common::BIT_LENGTH_ETH_ADDRESS;
|
|
use revive_runtime_api::immutable_data::{
|
|
GLOBAL_IMMUTABLE_DATA_POINTER, GLOBAL_IMMUTABLE_DATA_SIZE,
|
|
};
|
|
use revive_runtime_api::polkavm_imports::CALL_DATA_SIZE;
|
|
use revive_solc_json_interface::PolkaVMDefaultHeapMemorySize;
|
|
|
|
use crate::polkavm::context::address_space::AddressSpace;
|
|
use crate::polkavm::context::function::runtime;
|
|
use crate::polkavm::context::Context;
|
|
use crate::polkavm::WriteLLVM;
|
|
|
|
/// The entry function.
|
|
/// The function is a wrapper managing the runtime and deploy code calling logic.
|
|
/// Is a special runtime function that is only used by the front-end generated code.
|
|
#[derive(Debug, Default)]
|
|
pub struct Entry {}
|
|
|
|
impl Entry {
|
|
/// The call flags argument index.
|
|
pub const ARGUMENT_INDEX_CALL_FLAGS: usize = 0;
|
|
|
|
/// Initializes the global variables.
|
|
/// The pointers are not initialized, because it's not possible to create a null pointer.
|
|
pub fn initialize_globals(context: &mut Context) -> anyhow::Result<()> {
|
|
context.set_global(
|
|
crate::polkavm::GLOBAL_CALLDATA_SIZE,
|
|
context.xlen_type(),
|
|
AddressSpace::Stack,
|
|
context.xlen_type().get_undef(),
|
|
);
|
|
|
|
context.set_global(
|
|
crate::polkavm::GLOBAL_HEAP_SIZE,
|
|
context.xlen_type(),
|
|
AddressSpace::Stack,
|
|
context.xlen_type().const_zero(),
|
|
);
|
|
|
|
let heap_memory_type = context.byte_type().array_type(
|
|
context
|
|
.memory_config
|
|
.heap_size
|
|
.unwrap_or(PolkaVMDefaultHeapMemorySize),
|
|
);
|
|
context.set_global(
|
|
crate::polkavm::GLOBAL_HEAP_MEMORY,
|
|
heap_memory_type,
|
|
AddressSpace::Stack,
|
|
heap_memory_type.const_zero(),
|
|
);
|
|
|
|
let address_type = context.integer_type(BIT_LENGTH_ETH_ADDRESS);
|
|
context.set_global(
|
|
crate::polkavm::GLOBAL_ADDRESS_SPILL_BUFFER,
|
|
address_type,
|
|
AddressSpace::Stack,
|
|
address_type.const_zero(),
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Populate the calldata size global value.
|
|
pub fn load_calldata_size(context: &mut Context) -> anyhow::Result<()> {
|
|
let call_data_size_pointer = context
|
|
.get_global(crate::polkavm::GLOBAL_CALLDATA_SIZE)?
|
|
.value
|
|
.as_pointer_value();
|
|
let call_data_size_value = context
|
|
.build_runtime_call(CALL_DATA_SIZE, &[])
|
|
.expect("the call_data_size syscall method should return a value")
|
|
.into_int_value();
|
|
let call_data_size_value = context.builder().build_int_truncate(
|
|
call_data_size_value,
|
|
context.xlen_type(),
|
|
"call_data_size_truncated",
|
|
)?;
|
|
context
|
|
.builder()
|
|
.build_store(call_data_size_pointer, call_data_size_value)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Calls the deploy code if the first function argument was `1`.
|
|
/// Calls the runtime code otherwise.
|
|
pub fn leave_entry(context: &mut Context) -> anyhow::Result<()> {
|
|
context.set_debug_location(0, 0, None)?;
|
|
|
|
let is_deploy = context
|
|
.current_function()
|
|
.borrow()
|
|
.get_nth_param(Self::ARGUMENT_INDEX_CALL_FLAGS);
|
|
|
|
let deploy_code_call_block = context.append_basic_block("deploy_code_call_block");
|
|
let runtime_code_call_block = context.append_basic_block("runtime_code_call_block");
|
|
|
|
context.build_conditional_branch(
|
|
is_deploy.into_int_value(),
|
|
deploy_code_call_block,
|
|
runtime_code_call_block,
|
|
)?;
|
|
|
|
let deploy_code = context
|
|
.functions
|
|
.get(runtime::FUNCTION_DEPLOY_CODE)
|
|
.cloned()
|
|
.ok_or_else(|| anyhow::anyhow!("Contract deploy code not found"))?;
|
|
let runtime_code = context
|
|
.functions
|
|
.get(runtime::FUNCTION_RUNTIME_CODE)
|
|
.cloned()
|
|
.ok_or_else(|| anyhow::anyhow!("Contract runtime code not found"))?;
|
|
|
|
context.set_basic_block(deploy_code_call_block);
|
|
context.build_call(deploy_code.borrow().declaration, &[], "deploy_code_call");
|
|
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
|
|
|
context.set_basic_block(runtime_code_call_block);
|
|
context.build_call(runtime_code.borrow().declaration, &[], "runtime_code_call");
|
|
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl WriteLLVM for Entry {
|
|
fn declare(&mut self, context: &mut Context) -> anyhow::Result<()> {
|
|
let entry_arguments = vec![context.bool_type().as_basic_type_enum()];
|
|
let entry_function_type = context.function_type(entry_arguments, 0);
|
|
context.add_function(
|
|
runtime::FUNCTION_ENTRY,
|
|
entry_function_type,
|
|
0,
|
|
Some(inkwell::module::Linkage::External),
|
|
None,
|
|
false,
|
|
)?;
|
|
|
|
context.declare_global(
|
|
GLOBAL_IMMUTABLE_DATA_POINTER,
|
|
context.word_type().array_type(0),
|
|
AddressSpace::Stack,
|
|
);
|
|
|
|
context.declare_global(
|
|
GLOBAL_IMMUTABLE_DATA_SIZE,
|
|
context.xlen_type(),
|
|
AddressSpace::Stack,
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Instead of a single entrypoint, the runtime expects two exports: `call ` and `deploy`.
|
|
/// `call` and `deploy` directly call `entry`, signaling a deploy if the first arg is `1`.
|
|
/// 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, false)
|
|
.expect("the entry function should already be declared")
|
|
.borrow()
|
|
.declaration;
|
|
crate::PolkaVMFunction::set_attributes(
|
|
context.llvm(),
|
|
entry,
|
|
&[crate::PolkaVMAttribute::NoReturn],
|
|
true,
|
|
);
|
|
|
|
context.set_current_function(runtime::FUNCTION_ENTRY, None, false)?;
|
|
context.set_basic_block(context.current_function().borrow().entry_block());
|
|
|
|
Self::initialize_globals(context)?;
|
|
Self::load_calldata_size(context)?;
|
|
Self::leave_entry(context)?;
|
|
|
|
context.build_unconditional_branch(context.current_function().borrow().return_block());
|
|
context.set_basic_block(context.current_function().borrow().return_block());
|
|
context.build_unreachable();
|
|
|
|
context.pop_debug_scope();
|
|
|
|
Ok(())
|
|
}
|
|
}
|