rename target to polkavm

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-05-02 08:47:44 +02:00
parent 9fc24af355
commit 336fc63f1d
112 changed files with 876 additions and 873 deletions
@@ -0,0 +1,244 @@
//! 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.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_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.field_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(())
}
}
@@ -0,0 +1,98 @@
//! The deploy code function.
use std::marker::PhantomData;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// The deploy code function.
/// Is a special function that is only used by the front-end generated code.
#[derive(Debug)]
pub struct DeployCode<B, D>
where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
/// The deploy code AST representation.
inner: B,
/// The `D` phantom data.
_pd: PhantomData<D>,
}
impl<B, D> DeployCode<B, D>
where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
/// A shortcut constructor.
pub fn new(inner: B) -> Self {
Self {
inner,
_pd: PhantomData,
}
}
}
impl<B, D> WriteLLVM<D> for DeployCode<B, D>
where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type =
context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0, false);
context.add_function(
Runtime::FUNCTION_DEPLOY_CODE,
function_type,
0,
Some(inkwell::module::Linkage::External),
)?;
self.inner.declare(context)
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
context.set_current_function(Runtime::FUNCTION_DEPLOY_CODE)?;
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Deploy);
if let Some(vyper) = context.vyper_data.as_ref() {
for index in 0..vyper.immutables_size() / revive_common::BYTE_LENGTH_FIELD {
let offset = (crate::polkavm::r#const::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
as usize)
+ (1 + index) * 2 * revive_common::BYTE_LENGTH_FIELD;
let value = index * revive_common::BYTE_LENGTH_FIELD;
let pointer = Pointer::new_with_offset(
context,
AddressSpace::HeapAuxiliary,
context.field_type(),
context.field_const(offset as u64),
"immutable_index_initializer",
);
context.build_store(pointer, context.field_const(value as u64))?;
}
}
self.inner.into_llvm(context)?;
match context
.basic_block()
.get_last_instruction()
.map(|instruction| instruction.get_opcode())
{
Some(inkwell::values::InstructionOpcode::Br) => {}
Some(inkwell::values::InstructionOpcode::Switch) => {}
_ => context
.build_unconditional_branch(context.current_function().borrow().return_block()),
}
context.set_basic_block(context.current_function().borrow().return_block());
context.build_return(None);
Ok(())
}
}
@@ -0,0 +1,324 @@
//! The `deployer_call` function.
use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::Function;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// The `deployer_call` function.
/// Calls the deployer system contract, which returns the newly deployed contract address or 0.
/// The address is returned in the first 32-byte word of the return data. If it is 0, the 0 is
/// returned. If the entire call has failed, there is also a 0 returned.
#[derive(Debug)]
pub struct DeployerCall {
/// The address space where the calldata is allocated.
/// Solidity uses the ordinary heap. Vyper uses the auxiliary heap.
address_space: AddressSpace,
}
impl DeployerCall {
/// The default function name.
pub const FUNCTION_NAME: &'static str = "__deployer_call";
/// The value argument index.
pub const ARGUMENT_INDEX_VALUE: usize = 0;
/// The input offset argument index.
pub const ARGUMENT_INDEX_INPUT_OFFSET: usize = 1;
/// The input length argument index.
pub const ARGUMENT_INDEX_INPUT_LENGTH: usize = 2;
/// The signature hash argument index.
pub const ARGUMENT_INDEX_SIGNATURE_HASH: usize = 3;
/// The salt argument index.
pub const ARGUMENT_INDEX_SALT: usize = 4;
/// A shortcut constructor.
pub fn new(address_space: AddressSpace) -> Self {
Self { address_space }
}
}
impl<D> WriteLLVM<D> for DeployerCall
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type = context.function_type(
vec![
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
context.field_type().as_basic_type_enum(),
],
1,
false,
);
let function = context.add_function(
Self::FUNCTION_NAME,
function_type,
1,
Some(inkwell::module::Linkage::External),
)?;
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::FUNCTION_NAME)?;
let value = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_VALUE)
.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 signature_hash = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_SIGNATURE_HASH)
.into_int_value();
let salt = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_SALT)
.into_int_value();
let error_block = context.append_basic_block("deployer_call_error_block");
let success_block = context.append_basic_block("deployer_call_success_block");
let value_zero_block = context.append_basic_block("deployer_call_value_zero_block");
let value_non_zero_block = context.append_basic_block("deployer_call_value_non_zero_block");
let value_join_block = context.append_basic_block("deployer_call_value_join_block");
context.set_basic_block(context.current_function().borrow().entry_block());
let _abi_data = crate::polkavm::utils::abi_data(
context,
input_offset,
input_length,
None,
self.address_space,
true,
)?;
let signature_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
input_offset,
"deployer_call_signature_pointer",
);
context.build_store(signature_pointer, signature_hash)?;
let salt_offset = context.builder().build_int_add(
input_offset,
context.field_const(revive_common::BYTE_LENGTH_X32 as u64),
"deployer_call_salt_offset",
)?;
let salt_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
salt_offset,
"deployer_call_salt_pointer",
);
context.build_store(salt_pointer, salt)?;
let arguments_offset_offset = context.builder().build_int_add(
salt_offset,
context.field_const((revive_common::BYTE_LENGTH_FIELD * 2) as u64),
"deployer_call_arguments_offset_offset",
)?;
let arguments_offset_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
arguments_offset_offset,
"deployer_call_arguments_offset_pointer",
);
context.build_store(
arguments_offset_pointer,
context.field_const(
(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE
- (revive_common::BYTE_LENGTH_X32 + revive_common::BYTE_LENGTH_FIELD))
as u64,
),
)?;
let arguments_length_offset = context.builder().build_int_add(
arguments_offset_offset,
context.field_const(revive_common::BYTE_LENGTH_FIELD as u64),
"deployer_call_arguments_length_offset",
)?;
let arguments_length_pointer = Pointer::new_with_offset(
context,
self.address_space,
context.field_type(),
arguments_length_offset,
"deployer_call_arguments_length_pointer",
);
let arguments_length_value = context.builder().build_int_sub(
input_length,
context.field_const(crate::polkavm::DEPLOYER_CALL_HEADER_SIZE as u64),
"deployer_call_arguments_length",
)?;
context.build_store(arguments_length_pointer, arguments_length_value)?;
let result_pointer =
context.build_alloca(context.field_type(), "deployer_call_result_pointer");
context.build_store(result_pointer, context.field_const(0))?;
let deployer_call_result_type = context.structure_type(&[
context
.llvm()
.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
context.bool_type().as_basic_type_enum(),
]);
let deployer_call_result_pointer =
context.build_alloca(deployer_call_result_type, "deployer_call_result_pointer");
context.build_store(
deployer_call_result_pointer,
deployer_call_result_type.const_zero(),
)?;
let is_value_zero = context.builder().build_int_compare(
inkwell::IntPredicate::EQ,
value,
context.field_const(0),
"deployer_call_is_value_zero",
)?;
context.build_conditional_branch(is_value_zero, value_zero_block, value_non_zero_block)?;
context.set_basic_block(value_zero_block);
//let deployer_call_result = context
// .build_call(
// context.llvm_runtime().far_call,
// crate::polkavm::utils::external_call_arguments(
// context,
// abi_data,
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
// vec![],
// None,
// )
// .as_slice(),
// "deployer_call_ordinary",
// )
// .expect("Always returns a value");
//context.build_store(deployer_call_result_pointer, deployer_call_result)?;
context.build_unconditional_branch(value_join_block);
context.set_basic_block(value_non_zero_block);
//let deployer_call_result = context
// .build_call(
// context.llvm_runtime().far_call,
// crate::polkavm::utils::external_call_arguments(
// context,
// abi_data.as_basic_value_enum(),
// context.field_const(zkevm_opcode_defs::ADDRESS_MSG_VALUE.into()),
// vec![
// value,
// context.field_const(zkevm_opcode_defs::ADDRESS_CONTRACT_DEPLOYER.into()),
// context.field_const(u64::from(crate::polkavm::r#const::SYSTEM_CALL_BIT)),
// ],
// None,
// )
// .as_slice(),
// "deployer_call_system",
// )
// .expect("Always returns a value");
//context.build_store(deployer_call_result_pointer, deployer_call_result)?;
context.build_unconditional_branch(value_join_block);
context.set_basic_block(value_join_block);
let result_abi_data_pointer = context.build_gep(
deployer_call_result_pointer,
&[
context.field_const(0),
context
.integer_type(revive_common::BIT_LENGTH_X32)
.const_zero(),
],
context
.llvm()
.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
"deployer_call_result_abi_data_pointer",
);
let result_abi_data =
context.build_load(result_abi_data_pointer, "deployer_call_result_abi_data")?;
let result_status_code_pointer = context.build_gep(
deployer_call_result_pointer,
&[
context.field_const(0),
context
.integer_type(revive_common::BIT_LENGTH_X32)
.const_int(1, false),
],
context.bool_type().as_basic_type_enum(),
"contract_call_external_result_status_code_pointer",
);
let result_status_code_boolean = context
.build_load(
result_status_code_pointer,
"contract_call_external_result_status_code_boolean",
)?
.into_int_value();
context.build_conditional_branch(result_status_code_boolean, success_block, error_block)?;
context.set_basic_block(success_block);
let result_abi_data_pointer = Pointer::new(
context.field_type(),
AddressSpace::Generic,
result_abi_data.into_pointer_value(),
);
let address_or_status_code = context.build_load(
result_abi_data_pointer,
"deployer_call_address_or_status_code",
)?;
context.build_store(result_pointer, address_or_status_code)?;
context.build_unconditional_branch(context.current_function().borrow().return_block());
context.set_basic_block(error_block);
let result_abi_data_pointer = Pointer::new(
context.byte_type(),
AddressSpace::Generic,
result_abi_data.into_pointer_value(),
);
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 result = context.build_load(result_pointer, "deployer_call_result")?;
context.build_return(Some(&result));
Ok(())
}
}
@@ -0,0 +1,276 @@
//! The entry function.
use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
use crate::PolkaVMPointer as Pointer;
/// 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;
/// The number of mandatory arguments.
pub const MANDATORY_ARGUMENTS_COUNT: usize = 2;
/// Reserve 1kb for calldata.
pub const MAX_CALLDATA_SIZE: usize = 1024;
/// Initializes the global variables.
/// The pointers are not initialized, because it's not possible to create a null pointer.
pub fn initialize_globals<D>(context: &mut Context<D>) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let calldata_type = context.array_type(context.byte_type(), Self::MAX_CALLDATA_SIZE);
context.set_global(
crate::polkavm::GLOBAL_CALLDATA_POINTER,
calldata_type,
AddressSpace::Stack,
calldata_type.get_undef(),
);
context.set_global(
crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER,
context.llvm().ptr_type(AddressSpace::Generic.into()),
AddressSpace::Stack,
context.xlen_type().get_undef(),
);
context.build_store(
context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.into(),
context.build_sbrk(context.integer_const(32, 0))?,
)?;
context.set_global(
crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.field_type(),
AddressSpace::Stack,
context.field_undef(),
);
context.set_global(
crate::polkavm::GLOBAL_RETURN_DATA_SIZE,
context.field_type(),
AddressSpace::Stack,
context.field_const(0),
);
context.set_global(
crate::polkavm::GLOBAL_CALL_FLAGS,
context.field_type(),
AddressSpace::Stack,
context.field_const(0),
);
let extra_abi_data_type = context.array_type(
context.field_type().as_basic_type_enum(),
crate::polkavm::EXTRA_ABI_DATA_SIZE,
);
context.set_global(
crate::polkavm::GLOBAL_EXTRA_ABI_DATA,
extra_abi_data_type,
AddressSpace::Stack,
extra_abi_data_type.const_zero(),
);
Ok(())
}
/// Load the calldata via seal `input` and initialize the calldata end
/// and calldata size globals.
pub fn load_calldata<D>(context: &mut Context<D>) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let input_pointer = context
.get_global(crate::polkavm::GLOBAL_CALLDATA_POINTER)?
.value
.as_pointer_value();
let input_pointer_casted = context.builder.build_ptr_to_int(
input_pointer,
context.xlen_type(),
"input_pointer_casted",
)?;
let length_pointer = context.build_alloca(context.xlen_type(), "len_ptr");
let length_pointer_casted = context.builder.build_ptr_to_int(
length_pointer.value,
context.xlen_type(),
"length_pointer_casted",
)?;
context.build_store(
length_pointer,
context.integer_const(32, Self::MAX_CALLDATA_SIZE as u64),
)?;
context.builder().build_call(
context.module().get_function("input").expect("is declared"),
&[input_pointer_casted.into(), length_pointer_casted.into()],
"call_seal_input",
)?;
// Store the calldata size
let calldata_size = context
.build_load(length_pointer, "input_size")?
.into_int_value();
let calldata_size_casted = context.builder().build_int_z_extend(
calldata_size,
context.field_type(),
"zext_input_len",
)?;
context.set_global(
crate::polkavm::GLOBAL_CALLDATA_SIZE,
context.field_type(),
AddressSpace::Stack,
calldata_size_casted,
);
// Store calldata end pointer
let input_pointer = Pointer::new(
input_pointer.get_type(),
AddressSpace::Generic,
input_pointer,
);
let calldata_end_pointer = context.build_gep(
input_pointer,
&[calldata_size_casted],
context
.llvm()
.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
"return_data_abi_initializer",
);
context.write_abi_pointer(
calldata_end_pointer,
crate::polkavm::GLOBAL_RETURN_DATA_POINTER,
);
context.write_abi_pointer(calldata_end_pointer, crate::polkavm::GLOBAL_ACTIVE_POINTER);
Ok(())
}
/// Calls the deploy code if the first function argument was `1`.
/// Calls the runtime code otherwise.
pub fn leave_entry<D>(context: &mut Context<D>) -> anyhow::Result<()>
where
D: Dependency + Clone,
{
let is_deploy = context
.current_function()
.borrow()
.get_nth_param(Self::ARGUMENT_INDEX_CALL_FLAGS);
context.set_global(
crate::polkavm::GLOBAL_CALL_FLAGS,
is_deploy.get_type(),
AddressSpace::Stack,
is_deploy.into_int_value(),
);
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_invoke(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_unconditional_branch(context.current_function().borrow().return_block());
Ok(())
}
}
impl<D> WriteLLVM<D> for Entry
where
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let entry_arguments = vec![context.bool_type().as_basic_type_enum()];
let entry_function_type = context.function_type(entry_arguments, 0, false);
context.add_function(Runtime::FUNCTION_ENTRY, entry_function_type, 0, None)?;
context.declare_extern_function("deploy")?;
context.declare_extern_function("call")?;
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<D>) -> anyhow::Result<()> {
let entry = context
.get_function(Runtime::FUNCTION_ENTRY)
.expect("the entry function should already be declared")
.borrow()
.declaration;
crate::PolkaVMFunction::set_attributes(
context.llvm(),
entry,
vec![crate::PolkaVMAttribute::NoReturn],
true,
);
context.set_current_function("deploy")?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
.build_call(entry, &[context.bool_const(true).into()], "entry_deploy")
.is_none());
context.set_basic_block(context.current_function().borrow().return_block);
context.build_unreachable();
context.set_current_function("call")?;
context.set_basic_block(context.current_function().borrow().entry_block());
assert!(context
.build_call(entry, &[context.bool_const(false).into()], "entry_call")
.is_none());
context.set_basic_block(context.current_function().borrow().return_block);
context.build_unreachable();
context.set_current_function(Runtime::FUNCTION_ENTRY)?;
context.set_basic_block(context.current_function().borrow().entry_block());
Self::initialize_globals(context)?;
Self::load_calldata(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();
Ok(())
}
}
@@ -0,0 +1,90 @@
//! The front-end runtime functions.
pub mod default_call;
pub mod deploy_code;
pub mod deployer_call;
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;
use self::deployer_call::DeployerCall;
/// The front-end runtime functions.
#[derive(Debug, Clone)]
pub struct Runtime {
/// The address space where the calldata is allocated.
/// Solidity uses the ordinary heap. Vyper uses the auxiliary heap.
address_space: AddressSpace,
}
impl Runtime {
/// The main entry function name.
pub const FUNCTION_ENTRY: &'static str = "__entry";
/// The deploy code function name.
pub const FUNCTION_DEPLOY_CODE: &'static str = "__deploy";
/// The runtime code function name.
pub const FUNCTION_RUNTIME_CODE: &'static str = "__runtime";
/// A shortcut constructor.
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()
}
/// Returns the corresponding runtime function.
pub fn deployer_call<'ctx, D>(context: &Context<'ctx, D>) -> FunctionDeclaration<'ctx>
where
D: Dependency + Clone,
{
context
.get_function(DeployerCall::FUNCTION_NAME)
.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)?;
DeployerCall::new(self.address_space).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)?;
DeployerCall::new(self.address_space).into_llvm(context)?;
Ok(())
}
}
@@ -0,0 +1,79 @@
//! The runtime code function.
use std::marker::PhantomData;
use crate::polkavm::context::code_type::CodeType;
use crate::polkavm::context::function::runtime::Runtime;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm::WriteLLVM;
/// The runtime code function.
/// Is a special function that is only used by the front-end generated code.
#[derive(Debug)]
pub struct RuntimeCode<B, D>
where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
/// The runtime code AST representation.
inner: B,
/// The `D` phantom data.
_pd: PhantomData<D>,
}
impl<B, D> RuntimeCode<B, D>
where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
/// A shortcut constructor.
pub fn new(inner: B) -> Self {
Self {
inner,
_pd: PhantomData,
}
}
}
impl<B, D> WriteLLVM<D> for RuntimeCode<B, D>
where
B: WriteLLVM<D>,
D: Dependency + Clone,
{
fn declare(&mut self, context: &mut Context<D>) -> anyhow::Result<()> {
let function_type =
context.function_type::<inkwell::types::BasicTypeEnum>(vec![], 0, false);
context.add_function(
Runtime::FUNCTION_RUNTIME_CODE,
function_type,
0,
Some(inkwell::module::Linkage::External),
)?;
self.inner.declare(context)
}
fn into_llvm(self, context: &mut Context<D>) -> anyhow::Result<()> {
context.set_current_function(Runtime::FUNCTION_RUNTIME_CODE)?;
context.set_basic_block(context.current_function().borrow().entry_block());
context.set_code_type(CodeType::Runtime);
self.inner.into_llvm(context)?;
match context
.basic_block()
.get_last_instruction()
.map(|instruction| instruction.get_opcode())
{
Some(inkwell::values::InstructionOpcode::Br) => {}
Some(inkwell::values::InstructionOpcode::Switch) => {}
_ => context
.build_unconditional_branch(context.current_function().borrow().return_block()),
}
context.set_basic_block(context.current_function().borrow().return_block());
context.build_unreachable();
Ok(())
}
}