Contract calls (#19)

This commit is contained in:
Cyrill Leutwiler
2024-06-01 20:48:20 +02:00
committed by GitHub
parent 532721f3be
commit 1ba806be1f
27 changed files with 540 additions and 929 deletions
@@ -1,57 +1,68 @@
//! Runtime API import and export symbols.
/// The contract deploy export.
pub static CALL: &str = "call";
pub mod exports {
/// The contract deploy export.
pub static CALL: &str = "call";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// The contract call export.
pub static DEPLOY: &str = "deploy";
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
/// All exported symbols.
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
}
pub static ADDRESS: &str = "address";
pub mod imports {
pub static ADDRESS: &str = "address";
pub static BLOCK_NUMBER: &str = "block_number";
pub static BLOCK_NUMBER: &str = "block_number";
pub static CALLER: &str = "caller";
pub static CALL: &str = "seal_call";
pub static CODE_SIZE: &str = "code_size";
pub static CALLER: &str = "caller";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static CODE_SIZE: &str = "code_size";
pub static GET_STORAGE: &str = "get_storage";
pub static DEPOSIT_EVENT: &str = "deposit_event";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static GET_STORAGE: &str = "get_storage";
pub static INPUT: &str = "input";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INSTANTIATE: &str = "instantiate";
pub static INPUT: &str = "input";
pub static NOW: &str = "now";
pub static INSTANTIATE: &str = "instantiate";
pub static RETURN: &str = "seal_return";
pub static NOW: &str = "now";
pub static SET_STORAGE: &str = "set_storage";
pub static RETURN: &str = "seal_return";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
pub static RETURNDATACOPY: &str = "returndatacopy";
/// All imported runtime API symbols..
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 12] = [
ADDRESS,
BLOCK_NUMBER,
CALLER,
DEPOSIT_EVENT,
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
INSTANTIATE,
NOW,
RETURN,
SET_STORAGE,
VALUE_TRANSFERRED,
];
pub static SET_STORAGE: &str = "set_storage";
pub static VALUE_TRANSFERRED: &str = "value_transferred";
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 15] = [
ADDRESS,
BLOCK_NUMBER,
CALL,
CALLER,
CODE_SIZE,
DEPOSIT_EVENT,
GET_STORAGE,
HASH_KECCAK_256,
INPUT,
INSTANTIATE,
NOW,
RETURN,
RETURNDATACOPY,
SET_STORAGE,
VALUE_TRANSFERRED,
];
}
/// PolkaVM __sbrk API symbol to extend the heap memory.
pub static SBRK: &str = "__sbrk";
@@ -11,11 +11,6 @@ use crate::polkavm::context::function::Function;
/// The functions are automatically linked to the LLVM implementations if the signatures match.
#[derive(Debug)]
pub struct LLVMRuntime<'ctx> {
/// The LLVM personality function, used for exception handling.
pub personality: FunctionDeclaration<'ctx>,
/// The LLVM exception throwing function.
pub cxa_throw: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub shl: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
@@ -37,29 +32,6 @@ pub struct LLVMRuntime<'ctx> {
/// The corresponding LLVM runtime function.
pub sha3: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub system_request: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
//pub far_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub far_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub static_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub static_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub delegate_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub delegate_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub mimic_call: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub mimic_call_byref: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub r#return: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
@@ -139,30 +111,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
module: &inkwell::module::Module<'ctx>,
optimizer: &Optimizer,
) -> Self {
let personality = Self::declare(
module,
Self::FUNCTION_PERSONALITY,
llvm.i32_type().fn_type(&[], false),
None,
);
let cxa_throw = Self::declare(
module,
Self::FUNCTION_CXA_THROW,
llvm.void_type().fn_type(
vec![
llvm.ptr_type(AddressSpace::Stack.into())
.as_basic_type_enum()
.into();
3
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_cxa_throw_attributes(llvm, cxa_throw);
let shl = Self::declare(
module,
Self::FUNCTION_SHL,
@@ -288,138 +236,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
false,
);
let system_request = Self::declare(
module,
Self::FUNCTION_SYSTEM_REQUEST,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
llvm.ptr_type(AddressSpace::Stack.into())
.as_basic_type_enum()
.into(),
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, system_request, optimizer);
let external_call_arguments: Vec<inkwell::types::BasicMetadataTypeEnum> = vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into();
crate::polkavm::context::function::runtime::entry::Entry::MANDATORY_ARGUMENTS_COUNT
+ crate::polkavm::EXTRA_ABI_DATA_SIZE
];
let mut mimic_call_arguments = external_call_arguments.clone();
mimic_call_arguments.push(
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
);
let mut external_call_arguments_by_ref: Vec<inkwell::types::BasicMetadataTypeEnum> = vec![
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
];
external_call_arguments_by_ref.extend::<Vec<inkwell::types::BasicMetadataTypeEnum>>(vec![
llvm.custom_width_int_type(
revive_common::BIT_LENGTH_WORD as u32
)
.as_basic_type_enum()
.into();
crate::polkavm::EXTRA_ABI_DATA_SIZE
]);
let mut mimic_call_arguments_by_ref = external_call_arguments_by_ref.clone();
mimic_call_arguments_by_ref.push(
llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32)
.as_basic_type_enum()
.into(),
);
let external_call_result_type = llvm
.struct_type(
&[
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
llvm.bool_type().as_basic_type_enum(),
],
false,
)
.as_basic_type_enum();
//let far_call = Self::declare(
// module,
// Self::FUNCTION_FARCALL,
// external_call_result_type.fn_type(external_call_arguments.as_slice(), false),
// Some(inkwell::module::Linkage::External),
//);
//Function::set_default_attributes(llvm, far_call, optimizer);
let static_call = Self::declare(
module,
Self::FUNCTION_STATICCALL,
external_call_result_type.fn_type(external_call_arguments.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, static_call, optimizer);
let delegate_call = Self::declare(
module,
Self::FUNCTION_DELEGATECALL,
external_call_result_type.fn_type(external_call_arguments.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, delegate_call, optimizer);
let mimic_call = Self::declare(
module,
Self::FUNCTION_MIMICCALL,
external_call_result_type.fn_type(mimic_call_arguments.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, mimic_call, optimizer);
let far_call_byref = Self::declare(
module,
Self::FUNCTION_FARCALL_BYREF,
external_call_result_type.fn_type(external_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, far_call_byref, optimizer);
let static_call_byref = Self::declare(
module,
Self::FUNCTION_STATICCALL_BYREF,
external_call_result_type.fn_type(external_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, static_call_byref, optimizer);
let delegate_call_byref = Self::declare(
module,
Self::FUNCTION_DELEGATECALL_BYREF,
external_call_result_type.fn_type(external_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, delegate_call_byref, optimizer);
let mimic_call_byref = Self::declare(
module,
Self::FUNCTION_MIMICCALL_BYREF,
external_call_result_type.fn_type(mimic_call_arguments_by_ref.as_slice(), false),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, mimic_call_byref, optimizer);
let r#return = Self::declare(
module,
Self::FUNCTION_RETURN,
@@ -454,9 +270,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
Function::set_default_attributes(llvm, revert, optimizer);
Self {
personality,
cxa_throw,
shl,
shr,
sar,
@@ -469,18 +282,6 @@ impl<'ctx> LLVMRuntime<'ctx> {
sha3,
system_request,
//far_call,
static_call,
delegate_call,
mimic_call,
far_call_byref,
static_call_byref,
delegate_call_byref,
mimic_call_byref,
r#return,
revert,
}
@@ -506,42 +307,4 @@ impl<'ctx> LLVMRuntime<'ctx> {
value.set_linkage(inkwell::module::Linkage::External);
FunctionDeclaration::new(value.get_type(), value).into()
}
/// Modifies the external call function with `is_byref` and `is_system` modifiers.
pub fn modify(
&self,
function: FunctionDeclaration<'ctx>,
is_byref: bool,
) -> anyhow::Result<FunctionDeclaration<'ctx>> {
let modified = if
/*function == self.far_call {
match is_byref {
false => self.far_call,
true => self.far_call_byref,
}
} else if */
function == self.static_call {
match is_byref {
false => self.static_call,
true => self.static_call_byref,
}
} else if function == self.delegate_call {
match is_byref {
false => self.delegate_call,
true => self.delegate_call_byref,
}
} else if function == self.mimic_call {
match is_byref {
false => self.mimic_call,
true => self.mimic_call_byref,
}
} else {
anyhow::bail!(
"Cannot modify an external call function `{}`",
function.value.get_name().to_string_lossy()
);
};
Ok(modified)
}
}
@@ -1,244 +0,0 @@
//! The `default_call` function.
use inkwell::types::BasicType;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::function::llvm_runtime::LLVMRuntime;
use crate::polkavm::context::function::Function;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// The `default_call` function.
/// Generates a default contract call, if the `msg.value` is zero.
#[derive(Debug)]
pub struct DefaultCall {
/// The name of the inner function used for the low-level call.
inner_name: String,
/// The function name with the low-level function name as an element.
name: String,
}
#[allow(unused)]
impl DefaultCall {
/// The gas argument index.
pub const ARGUMENT_INDEX_GAS: usize = 0;
/// The address argument index.
pub const ARGUMENT_INDEX_ADDRESS: usize = 1;
/// The input offset argument index.
pub const ARGUMENT_INDEX_INPUT_OFFSET: usize = 2;
/// The input length argument index.
pub const ARGUMENT_INDEX_INPUT_LENGTH: usize = 3;
/// The output offset argument index.
pub const ARGUMENT_INDEX_OUTPUT_OFFSET: usize = 4;
/// The output length argument index.
pub const ARGUMENT_INDEX_OUTPUT_LENGTH: usize = 5;
/// A shortcut constructor.
pub fn new(call_function: FunctionDeclaration) -> Self {
let inner_name = call_function.value.get_name().to_string_lossy().to_string();
let name = Self::name(call_function);
Self { inner_name, name }
}
/// Returns the function name.
pub fn name(call_function: FunctionDeclaration) -> String {
let suffix = match call_function.value.get_name().to_string_lossy() {
name if name == LLVMRuntime::FUNCTION_FARCALL => "far",
name if name == LLVMRuntime::FUNCTION_STATICCALL => "static",
name if name == LLVMRuntime::FUNCTION_DELEGATECALL => "delegate",
name => panic!("Invalid low-level call inner function `{name}`"),
};
format!("__default_{suffix}_call")
}
/// Returns the low-level call function.
fn inner_function<'ctx, D>(&self, context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
{
match self.inner_name.as_str() {
//name if name == LLVMRuntime::FUNCTION_FARCALL => context.llvm_runtime().far_call,
name if name == LLVMRuntime::FUNCTION_STATICCALL => context.llvm_runtime().static_call,
name if name == LLVMRuntime::FUNCTION_DELEGATECALL => {
context.llvm_runtime().delegate_call
}
name => panic!("Invalid low-level call inner function `{name}`"),
}
}
}
impl<D> WriteLLVM<D> for DefaultCall
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type = context.function_type(
vec![
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
context.word_type().as_basic_type_enum(),
],
1,
false,
);
let function = context.add_function(
self.name.as_str(),
function_type,
1,
Some(inkwell::module::Linkage::Private),
)?;
Function::set_frontend_runtime_attributes(
context.llvm,
function.borrow().declaration(),
&context.optimizer,
);
Ok(())
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
context.set_current_function(self.name.as_str())?;
/*
let gas = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_GAS)
.into_int_value();
let address = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_ADDRESS)
.into_int_value();
let input_offset = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_INPUT_OFFSET)
.into_int_value();
let input_length = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_INPUT_LENGTH)
.into_int_value();
let output_offset = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_OUTPUT_OFFSET)
.into_int_value();
let output_length = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_OUTPUT_LENGTH)
.into_int_value();
*/
context.set_basic_block(context.current_function().borrow().entry_block());
let status_code_result_pointer = context.build_alloca(
context.word_type(),
"contract_call_result_status_code_pointer",
);
/*
context.build_store(status_code_result_pointer, context.field_const(0));
let abi_data = crate::polkavm::utils::abi_data(
context,
input_offset,
input_length,
Some(gas),
AddressSpace::Heap,
false,
)?
.into_int_value();
let result = context
.build_call(
self.inner_function(context),
crate::polkavm::utils::external_call_arguments(
context,
abi_data.as_basic_value_enum(),
address,
vec![],
None,
)
.as_slice(),
"contract_call_external",
)
.expect("IntrinsicFunction always returns a flag");
let result_abi_data = context
.builder()
.build_extract_value(
result.into_struct_value(),
0,
"contract_call_external_result_abi_data",
)
.expect("Always exists");
let result_abi_data_pointer = Pointer::new(
context.byte_type(),
AddressSpace::Generic,
result_abi_data.into_pointer_value(),
);
let result_abi_data_casted = result_abi_data_pointer.cast(context.field_type());
let result_status_code_boolean = context
.builder()
.build_extract_value(
result.into_struct_value(),
1,
"contract_call_external_result_status_code_boolean",
)
.expect("Always exists");
let result_status_code = context.builder().build_int_z_extend_or_bit_cast(
result_status_code_boolean.into_int_value(),
context.field_type(),
"contract_call_external_result_status_code",
)?;
context.build_store(status_code_result_pointer, result_status_code);
let source = result_abi_data_casted;
let destination = Pointer::new_with_offset(
context,
AddressSpace::Heap,
context.byte_type(),
output_offset,
"contract_call_destination",
);
context.build_memcpy_return_data(
context.intrinsics().memory_copy_from_generic,
destination,
source,
output_length,
"contract_call_memcpy_from_child",
);
context.write_abi_pointer(
result_abi_data_pointer,
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
);
context.write_abi_data_size(
result_abi_data_pointer,
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
);
*/
context.build_unconditional_branch(context.current_function().borrow().return_block());
context.set_basic_block(context.current_function().borrow().return_block());
let status_code_result =
context.build_load(status_code_result_pointer, "contract_call_status_code")?;
context.build_return(Some(&status_code_result));
Ok(())
}
}
@@ -153,7 +153,7 @@ impl Entry {
context.integer_const(crate::polkavm::XLEN, Self::MAX_CALLDATA_SIZE as u64),
)?;
context.build_runtime_call(
runtime_api::INPUT,
runtime_api::imports::INPUT,
&[input_pointer_casted.into(), length_pointer_casted.into()],
);
@@ -215,11 +215,11 @@ impl Entry {
.ok_or_else(|| anyhow::anyhow!("Contract runtime code not found"))?;
context.set_basic_block(deploy_code_call_block);
context.build_invoke(deploy_code.borrow().declaration, &[], "deploy_code_call");
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_invoke(runtime_code.borrow().declaration, &[], "runtime_code_call");
context.build_call(runtime_code.borrow().declaration, &[], "runtime_code_call");
context.build_unconditional_branch(context.current_function().borrow().return_block());
Ok(())
@@ -235,7 +235,7 @@ where
let entry_function_type = context.function_type(entry_arguments, 0, false);
context.add_function(Runtime::FUNCTION_ENTRY, entry_function_type, 0, None)?;
for symbol in runtime_api::EXPORTS {
for symbol in runtime_api::exports::EXPORTS {
context.declare_extern_function(symbol)?;
}
@@ -258,7 +258,7 @@ where
true,
);
context.set_current_function(runtime_api::DEPLOY)?;
context.set_current_function(runtime_api::exports::DEPLOY)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -268,7 +268,7 @@ where
context.set_basic_block(context.current_function().borrow().return_block);
context.build_unreachable();
context.set_current_function(runtime_api::CALL)?;
context.set_current_function(runtime_api::exports::CALL)?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
@@ -1,17 +1,10 @@
//! The front-end runtime functions.
pub mod default_call;
pub mod deploy_code;
pub mod entry;
pub mod runtime_code;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
use self::default_call::DefaultCall;
/// The front-end runtime functions.
#[derive(Debug, Clone)]
@@ -35,40 +28,4 @@ impl Runtime {
pub fn new(_address_space: AddressSpace) -> Self {
Self { _address_space }
}
/// Returns the corresponding runtime function.
pub fn default_call<'ctx, D>(
context: &Context<'ctx, D>,
call_function: FunctionDeclaration<'ctx>,
) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
{
context
.get_function(DefaultCall::name(call_function).as_str())
.expect("Always exists")
.borrow()
.declaration()
}
}
impl<D> WriteLLVM<D> for Runtime
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
//DefaultCall::new(context.llvm_runtime().far_call).declare(context)?;
DefaultCall::new(context.llvm_runtime().static_call).declare(context)?;
DefaultCall::new(context.llvm_runtime().delegate_call).declare(context)?;
Ok(())
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
//DefaultCall::new(context.llvm_runtime().far_call).into_llvm(context)?;
DefaultCall::new(context.llvm_runtime().static_call).into_llvm(context)?;
DefaultCall::new(context.llvm_runtime().delegate_call).into_llvm(context)?;
Ok(())
}
}
+12 -107
View File
@@ -141,7 +141,7 @@ where
)
.expect("the PolkaVM guest API module should be linkable");
for export in runtime_api::EXPORTS {
for export in runtime_api::exports::EXPORTS {
module
.get_function(export)
.expect("should be declared")
@@ -151,7 +151,7 @@ where
);
}
for import in runtime_api::IMPORTS {
for import in runtime_api::imports::IMPORTS {
module
.get_function(import)
.expect("should be declared")
@@ -433,8 +433,6 @@ where
let entry_block = self.llvm.append_basic_block(value, "entry");
let return_block = self.llvm.append_basic_block(value, "return");
value.set_personality_function(self.llvm_runtime.personality.value);
let r#return = match return_values_length {
0 => FunctionReturn::none(),
1 => {
@@ -705,7 +703,7 @@ where
.build_stack_parameter(revive_common::BIT_LENGTH_WORD, "storage_value_pointer");
self.build_runtime_call(
runtime_api::GET_STORAGE,
runtime_api::imports::GET_STORAGE,
&[
storage_key_pointer_casted.into(),
self.integer_const(crate::polkavm::XLEN, 32).into(),
@@ -809,7 +807,7 @@ where
.build_store(storage_value_pointer.value, storage_value_value)?;
self.build_runtime_call(
runtime_api::SET_STORAGE,
runtime_api::imports::SET_STORAGE,
&[
storage_key_pointer_casted.into(),
self.integer_const(crate::polkavm::XLEN, 32).into(),
@@ -957,106 +955,6 @@ where
call_site_value.try_as_basic_value().left()
}
/// Builds an invoke.
/// Is defaulted to a call if there is no global exception handler.
pub fn build_invoke(
&self,
function: FunctionDeclaration<'ctx>,
arguments: &[inkwell::values::BasicValueEnum<'ctx>],
name: &str,
) -> Option<inkwell::values::BasicValueEnum<'ctx>> {
if !self
.functions
.contains_key(Function::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER)
{
return self.build_call(function, arguments, name);
}
let return_pointer = if let Some(r#type) = function.r#type.get_return_type() {
let pointer = self.build_alloca(r#type, "invoke_return_pointer");
self.build_store(pointer, r#type.const_zero()).unwrap();
Some(pointer)
} else {
None
};
let success_block = self.append_basic_block("invoke_success_block");
let catch_block = self.append_basic_block("invoke_catch_block");
let current_block = self.basic_block();
self.set_basic_block(catch_block);
let landing_pad_type = self.structure_type(&[
self.llvm()
.ptr_type(AddressSpace::Stack.into())
.as_basic_type_enum(),
self.integer_type(revive_common::BIT_LENGTH_X32)
.as_basic_type_enum(),
]);
self.builder
.build_landing_pad(
landing_pad_type,
self.llvm_runtime.personality.value,
&[self
.llvm()
.ptr_type(AddressSpace::Stack.into())
.const_zero()
.as_basic_value_enum()],
false,
"invoke_catch_landing",
)
.unwrap();
crate::polkavm::utils::throw(self);
self.set_basic_block(current_block);
let call_site_value = self
.builder
.build_indirect_invoke(
function.r#type,
function.value.as_global_value().as_pointer_value(),
arguments,
success_block,
catch_block,
name,
)
.unwrap();
self.modify_call_site_value(arguments, call_site_value, function);
self.set_basic_block(success_block);
if let (Some(return_pointer), Some(mut return_value)) =
(return_pointer, call_site_value.try_as_basic_value().left())
{
if let Some(return_type) = function.r#type.get_return_type() {
if return_type.is_pointer_type() {
return_value = self
.builder()
.build_int_to_ptr(
return_value.into_int_value(),
return_type.into_pointer_type(),
format!("{name}_invoke_return_pointer_casted").as_str(),
)
.unwrap()
.as_basic_value_enum();
}
}
self.build_store(return_pointer, return_value).unwrap();
}
return_pointer.map(|pointer| self.build_load(pointer, "invoke_result").unwrap())
}
/// Builds an invoke of local call covered with an exception handler.
/// Yul does not the exception handling, so the user can declare a special handling function
/// called (see constant `ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER`. If the enclosed function
/// panics, the control flow will be transferred to the exception handler.
pub fn build_invoke_near_call_abi(
&self,
_function: FunctionDeclaration<'ctx>,
_arguments: Vec<inkwell::values::BasicValueEnum<'ctx>>,
_name: &str,
) -> Option<inkwell::values::BasicValueEnum<'ctx>> {
unimplemented!()
}
/// Builds a memory copy call.
/// Sets the alignment to `1`, since all non-stack memory pages have such alignment.
pub fn build_memcpy(
&self,
@@ -1132,7 +1030,7 @@ where
)?;
self.build_runtime_call(
runtime_api::RETURN,
runtime_api::imports::RETURN,
&[flags.into(), offset_pointer.into(), length_pointer.into()],
);
self.build_unreachable();
@@ -1396,6 +1294,13 @@ where
self.llvm.custom_width_int_type(crate::polkavm::XLEN as u32)
}
/// Returns the register witdh sized type.
pub fn sentinel_pointer(&self) -> inkwell::values::PointerValue<'ctx> {
self.xlen_type()
.const_all_ones()
.const_to_pointer(self.llvm().ptr_type(Default::default()))
}
/// Returns the runtime value width sized type.
pub fn value_type(&self) -> inkwell::types::IntType<'ctx> {
self.llvm
+93 -67
View File
@@ -3,17 +3,104 @@
use inkwell::values::BasicValue;
use crate::polkavm::context::argument::Argument;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
static STATIC_CALL_FLAG: u32 = 0b0001_0000;
/// Translates a contract call.
/// If the `simulation_address` is specified, the call is substituted with another instruction
/// according to the specification.
///
/// If the `simulation_address` is specified, the call is
/// substituted with another instruction according to the specification.
#[allow(clippy::too_many_arguments)]
pub fn default<'ctx, D>(
pub fn call<'ctx, D>(
context: &mut Context<'ctx, D>,
gas: inkwell::values::IntValue<'ctx>,
address: inkwell::values::IntValue<'ctx>,
value: Option<inkwell::values::IntValue<'ctx>>,
input_offset: inkwell::values::IntValue<'ctx>,
input_length: inkwell::values::IntValue<'ctx>,
output_offset: inkwell::values::IntValue<'ctx>,
output_length: inkwell::values::IntValue<'ctx>,
_constants: Vec<Option<num::BigUint>>,
static_call: bool,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let address_pointer = context.build_alloca(context.word_type(), "address_ptr");
context.build_store(address_pointer, address)?;
let value_pointer = if let Some(value) = value {
let value_pointer = context.build_alloca(context.value_type(), "value");
context.build_store(value_pointer, value)?;
value_pointer.value
} else {
context.sentinel_pointer()
};
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
let gas = context
.builder()
.build_int_truncate(gas, context.integer_type(64), "gas")?;
let flags = if static_call { STATIC_CALL_FLAG } else { 0 };
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
let output_length_pointer = context.get_global(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?;
context.build_store(output_length_pointer.into(), output_length)?;
let argument_pointer = pallet_contracts_pvm_llapi::calling_convention::Spill::new(
context.builder(),
pallet_contracts_pvm_llapi::calling_convention::call(context.llvm()),
"call_arguments",
)?
.next(context.xlen_type().const_int(flags as u64, false))?
.next(address_pointer.value)?
.next(gas)?
.skip()
.next(context.sentinel_pointer())?
.next(value_pointer)?
.next(input_pointer.value)?
.next(input_length)?
.next(output_pointer.value)?
.next(output_length_pointer.value)?
.done();
let name = runtime_api::imports::CALL;
let arguments = context.builder().build_ptr_to_int(
argument_pointer,
context.xlen_type(),
"argument_pointer",
)?;
let success = context
.build_runtime_call(name, &[arguments.into()])
.unwrap_or_else(|| panic!("{name} should return a value"))
.into_int_value();
let is_success = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
success,
context.xlen_type().const_zero(),
"is_success",
)?;
Ok(context
.builder()
.build_int_z_extend(is_success, context.word_type(), "success")?
.as_basic_value_enum())
}
#[allow(clippy::too_many_arguments)]
pub fn delegate_call<'ctx, D>(
_context: &mut Context<'ctx, D>,
_function: FunctionDeclaration<'ctx>,
_gas: inkwell::values::IntValue<'ctx>,
_address: inkwell::values::IntValue<'ctx>,
_value: Option<inkwell::values::IntValue<'ctx>>,
@@ -26,68 +113,7 @@ pub fn default<'ctx, D>(
where
D: Dependency + Clone,
{
todo!();
/*
let ordinary_block = context.append_basic_block("contract_call_ordinary_block");
let join_block = context.append_basic_block("contract_call_join_block");
let result_pointer = context.build_alloca(context.field_type(), "contract_call_result_pointer");
context.build_store(result_pointer, context.field_const(0));
context.builder().build_switch(
address,
ordinary_block,
&[(
context.field_const(zkevm_opcode_defs::ADDRESS_IDENTITY.into()),
identity_block,
)],
)?;
{
context.set_basic_block(identity_block);
let result = identity(context, output_offset, input_offset, output_length)?;
context.build_store(result_pointer, result);
context.build_unconditional_branch(join_block);
}
context.set_basic_block(ordinary_block);
let result = if let Some(value) = value {
default_wrapped(
context,
function,
gas,
value,
address,
input_offset,
input_length,
output_offset,
output_length,
)?
} else {
let function = Runtime::default_call(context, function);
context
.build_call(
function,
&[
gas.as_basic_value_enum(),
address.as_basic_value_enum(),
input_offset.as_basic_value_enum(),
input_length.as_basic_value_enum(),
output_offset.as_basic_value_enum(),
output_length.as_basic_value_enum(),
],
"default_call",
)
.expect("Always exists")
};
context.build_store(result_pointer, result);
context.build_unconditional_branch(join_block);
context.set_basic_block(join_block);
let result = context.build_load(result_pointer, "contract_call_result");
Ok(result)
*/
todo!()
}
/// Translates the Yul `linkersymbol` instruction.
@@ -58,7 +58,7 @@ where
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::BLOCK_NUMBER,
runtime_api::imports::BLOCK_NUMBER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
@@ -83,7 +83,7 @@ where
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::NOW,
runtime_api::imports::NOW,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
@@ -174,7 +174,7 @@ where
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_output");
context.build_runtime_call(
runtime_api::ADDRESS,
runtime_api::imports::ADDRESS,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
@@ -197,7 +197,7 @@ where
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "caller_output");
context.build_runtime_call(
runtime_api::CALLER,
runtime_api::imports::CALLER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
+7 -12
View File
@@ -56,11 +56,7 @@ where
let (address_pointer, address_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_pointer");
let sentinel = context
.xlen_type()
.const_all_ones()
.const_to_pointer(context.llvm().ptr_type(Default::default()));
context.build_store(address_pointer, context.word_const(0))?;
let argument_pointer = pallet_contracts_pvm_llapi::calling_convention::Spill::new(
context.builder(),
@@ -70,14 +66,14 @@ where
.next(code_hash_pointer.value)?
.skip()
.skip()
.next(sentinel)?
.next(context.sentinel_pointer())?
.next(value_pointer.value)?
.next(input_data_pointer.value)?
.next(input_length)?
.next(address_pointer.value)?
.next(address_length_pointer.value)?
.next(sentinel)?
.next(sentinel)?
.next(context.sentinel_pointer())?
.next(context.sentinel_pointer())?
.next(salt_pointer.value)?
.next(
context
@@ -86,14 +82,13 @@ where
)?
.done();
context.builder().build_direct_call(
context.runtime_api_method(runtime_api::INSTANTIATE),
context.build_runtime_call(
runtime_api::imports::INSTANTIATE,
&[context
.builder()
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
.into()],
"create2",
)?;
);
context.build_load_word(
address_pointer,
@@ -19,7 +19,7 @@ where
let output_pointer = context.build_alloca(context.word_type(), "output_pointer");
context.build_runtime_call(
runtime_api::HASH_KECCAK_256,
runtime_api::imports::HASH_KECCAK_256,
&[
input_pointer.to_int(context).into(),
length_casted.into(),
@@ -26,7 +26,7 @@ where
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_VALUE, "value_transferred_output");
context.build_runtime_call(
runtime_api::VALUE_TRANSFERRED,
runtime_api::imports::VALUE_TRANSFERRED,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
+1 -1
View File
@@ -75,7 +75,7 @@ where
]
};
let _ = context.build_runtime_call(runtime_api::DEPOSIT_EVENT, &arguments);
let _ = context.build_runtime_call(runtime_api::imports::DEPOSIT_EVENT, &arguments);
Ok(())
}
@@ -23,8 +23,11 @@ where
"address_pointer",
)?;
let value = context
.build_runtime_call(runtime_api::CODE_SIZE, &[address_pointer_casted.into()])
.unwrap_or_else(|| panic!("{} should return a value", runtime_api::CODE_SIZE))
.build_runtime_call(
runtime_api::imports::CODE_SIZE,
&[address_pointer_casted.into()],
)
.unwrap_or_else(|| panic!("{} should return a value", runtime_api::imports::CODE_SIZE))
.into_int_value();
Ok(context
@@ -4,6 +4,7 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the return data size.
pub fn size<'ctx, D>(
@@ -38,54 +39,16 @@ where
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
let size = context.safe_truncate_int_to_xlen(size)?;
let block_copy = context.append_basic_block("copy_block");
let block_trap = context.append_basic_block("trap_block");
let block_check_out_of_bounds = context.append_basic_block("check_out_of_bounds_block");
let is_overflow = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
source_offset,
context.builder().build_int_sub(
context.xlen_type().const_all_ones(),
size,
"offset_plus_size_max_value",
)?,
"is_returndata_size_out_of_bounds",
let destination_offset = context.builder().build_ptr_to_int(
context.build_heap_gep(destination_offset, size)?.value,
context.xlen_type(),
"destination_offset",
)?;
context.build_conditional_branch(is_overflow, block_trap, block_check_out_of_bounds)?;
context.set_basic_block(block_check_out_of_bounds);
let is_out_of_bounds = context.builder().build_int_compare(
inkwell::IntPredicate::UGT,
context.builder().build_int_add(
source_offset,
context
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
.into_int_value(),
"returndata_end_pointer",
)?,
context
.xlen_type()
.const_int(crate::PolkaVMEntryFunction::MAX_CALLDATA_SIZE as u64, false),
"is_return_data_copy_overflow",
)?;
context.build_conditional_branch(is_out_of_bounds, block_trap, block_copy)?;
context.build_runtime_call(
runtime_api::imports::RETURNDATACOPY,
&[destination_offset.into(), source_offset.into(), size.into()],
);
context.set_basic_block(block_trap);
context.build_call(context.intrinsics().trap, &[], "invalid_returndata_copy");
context.build_unreachable();
context.set_basic_block(block_copy);
context.build_memcpy(
context.build_heap_gep(destination_offset, size)?,
context.build_gep(
context
.get_global(crate::polkavm::GLOBAL_RETURN_DATA_POINTER)?
.into(),
&[context.xlen_type().const_zero(), source_offset],
context.byte_type(),
"source_offset_gep",
),
size,
"return_data_copy_memcpy_from_return_data",
)
Ok(())
}
-18
View File
@@ -3,7 +3,6 @@
use inkwell::values::BasicValue;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::llvm_runtime::LLVMRuntime;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
@@ -40,23 +39,6 @@ where
Ok(result.into_int_value())
}
/// Generates an exception.
pub fn throw<D>(context: &Context<D>)
where
D: Dependency + Clone,
{
context.build_call(
context.llvm_runtime().cxa_throw,
&[context
.llvm()
.ptr_type(AddressSpace::Stack.into())
.get_undef()
.as_basic_value_enum(); 3],
LLVMRuntime::FUNCTION_CXA_THROW,
);
context.build_unreachable();
}
/// Returns the full list of arguments for an external call.
/// Performs the extra ABI data padding and adds the mimic call extra argument.
pub fn external_call_arguments<'ctx, D>(