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,34 @@
//! The LLVM IR generator function block key.
use crate::polkavm::context::code_type::CodeType;
/// The LLVM IR generator function block key.
/// Is only relevant to the EVM legacy assembly.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Key {
/// The block code type.
pub code_type: CodeType,
/// The block tag.
pub tag: num::BigUint,
}
impl Key {
/// A shortcut constructor.
pub fn new(code_type: CodeType, tag: num::BigUint) -> Self {
Self { code_type, tag }
}
}
impl std::fmt::Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}_{}",
match self.code_type {
CodeType::Deploy => "dt",
CodeType::Runtime => "rt",
},
self.tag
)
}
}
@@ -0,0 +1,18 @@
//! The LLVM function block EVM legacy assembly data.
pub mod key;
/// The LLVM function block EVM legacy assembly data.
/// Describes some data that is only relevant to the EVM legacy assembly.
#[derive(Debug, Clone)]
pub struct EVMLAData {
/// The initial hashes of the allowed stack states.
pub stack_hashes: Vec<md5::Digest>,
}
impl EVMLAData {
/// A shortcut constructor.
pub fn new(stack_hashes: Vec<md5::Digest>) -> Self {
Self { stack_hashes }
}
}
@@ -0,0 +1,52 @@
//! The LLVM IR generator function block.
pub mod evmla_data;
use self::evmla_data::EVMLAData;
/// The LLVM IR generator function block.
#[derive(Debug, Clone)]
pub struct Block<'ctx> {
/// The inner block.
inner: inkwell::basic_block::BasicBlock<'ctx>,
/// The EVM legacy assembly compiler data.
evmla_data: Option<EVMLAData>,
}
impl<'ctx> Block<'ctx> {
/// A shortcut constructor.
pub fn new(inner: inkwell::basic_block::BasicBlock<'ctx>) -> Self {
Self {
inner,
evmla_data: None,
}
}
/// Sets the EVM legacy assembly data.
pub fn set_evmla_data(&mut self, data: EVMLAData) {
self.evmla_data = Some(data);
}
/// The LLVM object reference.
pub fn inner(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.inner
}
/// Returns the EVM data reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evm(&self) -> &EVMLAData {
self.evmla_data
.as_ref()
.expect("The EVM data must have been initialized")
}
/// Returns the EVM data mutable reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evm_mut(&mut self) -> &mut EVMLAData {
self.evmla_data
.as_mut()
.expect("The EVM data must have been initialized")
}
}
@@ -0,0 +1,20 @@
//! The LLVM function declaration.
/// The LLVM function declaration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Declaration<'ctx> {
/// The function type.
pub r#type: inkwell::types::FunctionType<'ctx>,
/// The function value.
pub value: inkwell::values::FunctionValue<'ctx>,
}
impl<'ctx> Declaration<'ctx> {
/// A shortcut constructor.
pub fn new(
r#type: inkwell::types::FunctionType<'ctx>,
value: inkwell::values::FunctionValue<'ctx>,
) -> Self {
Self { r#type, value }
}
}
@@ -0,0 +1,74 @@
//! The LLVM function EVM legacy assembly data.
use std::collections::BTreeMap;
use crate::polkavm::context::function::block::evmla_data::key::Key as BlockKey;
use crate::polkavm::context::function::block::Block;
/// The LLVM function EVM legacy assembly data.
/// Describes some data that is only relevant to the EVM legacy assembly.
#[derive(Debug)]
pub struct EVMLAData<'ctx> {
/// The ordinary blocks with numeric tags.
/// Is only used by the Solidity EVM compiler.
pub blocks: BTreeMap<BlockKey, Vec<Block<'ctx>>>,
/// The function stack size.
pub stack_size: usize,
}
impl<'ctx> EVMLAData<'ctx> {
/// A shortcut constructor.
pub fn new(stack_size: usize) -> Self {
Self {
blocks: BTreeMap::new(),
stack_size,
}
}
/// Inserts a function block.
pub fn insert_block(&mut self, key: BlockKey, block: Block<'ctx>) {
if let Some(blocks) = self.blocks.get_mut(&key) {
blocks.push(block);
} else {
self.blocks.insert(key, vec![block]);
}
}
/// Returns the block with the specified tag and initial stack pattern.
/// If there is only one block, it is returned unconditionally.
pub fn find_block(
&self,
key: &BlockKey,
stack_hash: &md5::Digest,
) -> anyhow::Result<Block<'ctx>> {
if self
.blocks
.get(key)
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))?
.len()
== 1
{
return self
.blocks
.get(key)
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))?
.first()
.cloned()
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key));
}
self.blocks
.get(key)
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))?
.iter()
.find(|block| {
block
.evm()
.stack_hashes
.iter()
.any(|hash| hash == stack_hash)
})
.cloned()
.ok_or_else(|| anyhow::anyhow!("Undeclared function block {}", key))
}
}
@@ -0,0 +1,138 @@
//! The LLVM intrinsic functions.
use inkwell::types::BasicType;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
/// The LLVM intrinsic functions, implemented in the LLVM back-end.
/// Most of them are translated directly into bytecode instructions.
#[derive(Debug)]
pub struct Intrinsics<'ctx> {
/// The trap.
pub trap: FunctionDeclaration<'ctx>,
/// The memory copy within the heap.
pub memory_copy: FunctionDeclaration<'ctx>,
/// The memory copy from a generic page.
pub memory_copy_from_generic: FunctionDeclaration<'ctx>,
/// Performs endianness swaps on i256 values
pub byte_swap: FunctionDeclaration<'ctx>,
}
impl<'ctx> Intrinsics<'ctx> {
/// The corresponding intrinsic function name.
pub const FUNCTION_TRAP: &'static str = "llvm.trap";
/// The corresponding intrinsic function name.
pub const FUNCTION_MEMORY_COPY: &'static str = "llvm.memcpy.p1.p1.i256";
/// The corresponding intrinsic function name.
pub const FUNCTION_MEMORY_COPY_FROM_GENERIC: &'static str = "llvm.memcpy.p3.p1.i256";
/// The corresponding intrinsic function name.
pub const FUNCTION_BYTE_SWAP: &'static str = "llvm.bswap.i256";
/// A shortcut constructor.
pub fn new(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
) -> Self {
let void_type = llvm.void_type();
let bool_type = llvm.bool_type();
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32);
let _stack_field_pointer_type = llvm.ptr_type(AddressSpace::Stack.into());
let heap_field_pointer_type = llvm.ptr_type(AddressSpace::Heap.into());
let generic_byte_pointer_type = llvm.ptr_type(AddressSpace::Generic.into());
let trap = Self::declare(
llvm,
module,
Self::FUNCTION_TRAP,
void_type.fn_type(&[], false),
);
let memory_copy = Self::declare(
llvm,
module,
Self::FUNCTION_MEMORY_COPY,
void_type.fn_type(
&[
heap_field_pointer_type.as_basic_type_enum().into(),
heap_field_pointer_type.as_basic_type_enum().into(),
field_type.as_basic_type_enum().into(),
bool_type.as_basic_type_enum().into(),
],
false,
),
);
let memory_copy_from_generic = Self::declare(
llvm,
module,
Self::FUNCTION_MEMORY_COPY_FROM_GENERIC,
void_type.fn_type(
&[
heap_field_pointer_type.as_basic_type_enum().into(),
generic_byte_pointer_type.as_basic_type_enum().into(),
field_type.as_basic_type_enum().into(),
bool_type.as_basic_type_enum().into(),
],
false,
),
);
let byte_swap = Self::declare(
llvm,
module,
Self::FUNCTION_BYTE_SWAP,
field_type.fn_type(&[field_type.as_basic_type_enum().into()], false),
);
Self {
trap,
memory_copy,
memory_copy_from_generic,
byte_swap,
}
}
/// Finds the specified LLVM intrinsic function in the target and returns its declaration.
pub fn declare(
llvm: &'ctx inkwell::context::Context,
module: &inkwell::module::Module<'ctx>,
name: &str,
r#type: inkwell::types::FunctionType<'ctx>,
) -> FunctionDeclaration<'ctx> {
let intrinsic = inkwell::intrinsics::Intrinsic::find(name)
.unwrap_or_else(|| panic!("Intrinsic function `{name}` does not exist"));
let argument_types = Self::argument_types(llvm, name);
let value = intrinsic
.get_declaration(module, argument_types.as_slice())
.unwrap_or_else(|| panic!("Intrinsic function `{name}` declaration error"));
FunctionDeclaration::new(r#type, value)
}
/// Returns the LLVM types for selecting via the signature.
pub fn argument_types(
llvm: &'ctx inkwell::context::Context,
name: &str,
) -> Vec<inkwell::types::BasicTypeEnum<'ctx>> {
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32);
match name {
name if name == Self::FUNCTION_MEMORY_COPY => vec![
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
field_type.as_basic_type_enum(),
],
name if name == Self::FUNCTION_MEMORY_COPY_FROM_GENERIC => vec![
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
field_type.as_basic_type_enum(),
],
name if name == Self::FUNCTION_BYTE_SWAP => vec![field_type.as_basic_type_enum()],
_ => vec![],
}
}
}
@@ -0,0 +1,649 @@
//! The LLVM runtime functions.
use inkwell::types::BasicType;
use crate::optimizer::Optimizer;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::function::declaration::Declaration as FunctionDeclaration;
use crate::polkavm::context::function::Function;
/// The runtime functions, implemented on the LLVM side.
/// 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 div: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub sdiv: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub r#mod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub smod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub shl: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub shr: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub sar: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub byte: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub add_mod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub mul_mod: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub exp: FunctionDeclaration<'ctx>,
/// The corresponding LLVM runtime function.
pub sign_extend: FunctionDeclaration<'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.
pub revert: FunctionDeclaration<'ctx>,
}
impl<'ctx> LLVMRuntime<'ctx> {
/// The LLVM personality function name.
pub const FUNCTION_PERSONALITY: &'static str = "__personality";
/// The LLVM exception throwing function name.
pub const FUNCTION_CXA_THROW: &'static str = "__cxa_throw";
/// The corresponding runtime function name.
pub const FUNCTION_DIV: &'static str = "__div";
/// The corresponding runtime function name.
pub const FUNCTION_SDIV: &'static str = "__sdiv";
/// The corresponding runtime function name.
pub const FUNCTION_MOD: &'static str = "__mod";
/// The corresponding runtime function name.
pub const FUNCTION_SMOD: &'static str = "__smod";
/// The corresponding runtime function name.
pub const FUNCTION_SHL: &'static str = "__shl";
/// The corresponding runtime function name.
pub const FUNCTION_SHR: &'static str = "__shr";
/// The corresponding runtime function name.
pub const FUNCTION_SAR: &'static str = "__sar";
/// The corresponding runtime function name.
pub const FUNCTION_BYTE: &'static str = "__byte";
/// The corresponding runtime function name.
pub const FUNCTION_ADDMOD: &'static str = "__addmod";
/// The corresponding runtime function name.
pub const FUNCTION_MULMOD: &'static str = "__mulmod";
/// The corresponding runtime function name.
pub const FUNCTION_EXP: &'static str = "__exp";
/// The corresponding runtime function name.
pub const FUNCTION_SIGNEXTEND: &'static str = "__signextend";
/// The corresponding runtime function name.
pub const FUNCTION_SHA3: &'static str = "__sha3";
/// The corresponding runtime function name.
pub const FUNCTION_SYSTEM_REQUEST: &'static str = "__system_request";
/// The corresponding runtime function name.
pub const FUNCTION_FARCALL: &'static str = "__farcall";
/// The corresponding runtime function name.
pub const FUNCTION_STATICCALL: &'static str = "__staticcall";
/// The corresponding runtime function name.
pub const FUNCTION_DELEGATECALL: &'static str = "__delegatecall";
/// The corresponding runtime function name.
pub const FUNCTION_MIMICCALL: &'static str = "__mimiccall";
/// The corresponding runtime function name.
pub const FUNCTION_FARCALL_BYREF: &'static str = "__farcall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_STATICCALL_BYREF: &'static str = "__staticcall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_DELEGATECALL_BYREF: &'static str = "__delegatecall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_MIMICCALL_BYREF: &'static str = "__mimiccall_byref";
/// The corresponding runtime function name.
pub const FUNCTION_RETURN: &'static str = "__return";
/// The corresponding runtime function name.
pub const FUNCTION_REVERT: &'static str = "__revert";
/// A shortcut constructor.
pub fn new(
llvm: &'ctx inkwell::context::Context,
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 div = Self::declare(
module,
Self::FUNCTION_DIV,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, div, optimizer);
Function::set_pure_function_attributes(llvm, div);
let r#mod = Self::declare(
module,
Self::FUNCTION_MOD,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, r#mod, optimizer);
Function::set_pure_function_attributes(llvm, r#mod);
let sdiv = Self::declare(
module,
Self::FUNCTION_SDIV,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, sdiv, optimizer);
Function::set_pure_function_attributes(llvm, sdiv);
let smod = Self::declare(
module,
Self::FUNCTION_SMOD,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, smod, optimizer);
Function::set_pure_function_attributes(llvm, smod);
let shl = Self::declare(
module,
Self::FUNCTION_SHL,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, shl, optimizer);
Function::set_pure_function_attributes(llvm, shl);
let shr = Self::declare(
module,
Self::FUNCTION_SHR,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, shr, optimizer);
Function::set_pure_function_attributes(llvm, shr);
let sar = Self::declare(
module,
Self::FUNCTION_SAR,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, sar, optimizer);
Function::set_pure_function_attributes(llvm, sar);
let byte = Self::declare(
module,
Self::FUNCTION_BYTE,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
2
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, byte, optimizer);
Function::set_pure_function_attributes(llvm, byte);
let add_mod =
Self::define(module, Self::FUNCTION_ADDMOD).expect("should be declared in stdlib");
Function::set_default_attributes(llvm, add_mod, optimizer);
Function::set_pure_function_attributes(llvm, add_mod);
let mul_mod =
Self::define(module, Self::FUNCTION_MULMOD).expect("should be declared in stdlib");
Function::set_default_attributes(llvm, mul_mod, optimizer);
Function::set_pure_function_attributes(llvm, mul_mod);
let exp = Self::define(module, Self::FUNCTION_EXP).expect("should be declared in stdlib");
Function::set_default_attributes(llvm, exp, optimizer);
Function::set_pure_function_attributes(llvm, exp);
let sign_extend =
Self::define(module, Self::FUNCTION_SIGNEXTEND).expect("should be declared in stdlib");
Function::set_default_attributes(llvm, sign_extend, optimizer);
Function::set_pure_function_attributes(llvm, sign_extend);
let sha3 = Self::declare(
module,
Self::FUNCTION_SHA3,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_BOOLEAN as u32)
.as_basic_type_enum()
.into(),
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, sha3, optimizer);
Function::set_attributes(
llvm,
sha3,
//vec![Attribute::ArgMemOnly, Attribute::ReadOnly],
vec![],
false,
);
let system_request = Self::declare(
module,
Self::FUNCTION_SYSTEM_REQUEST,
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into(),
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD 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_FIELD 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_FIELD 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_FIELD 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_FIELD 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_FIELD 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,
llvm.void_type().fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
3
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, r#return, optimizer);
let revert = Self::declare(
module,
Self::FUNCTION_REVERT,
llvm.void_type().fn_type(
vec![
llvm.custom_width_int_type(revive_common::BIT_LENGTH_FIELD as u32)
.as_basic_type_enum()
.into();
3
]
.as_slice(),
false,
),
Some(inkwell::module::Linkage::External),
);
Function::set_default_attributes(llvm, revert, optimizer);
Self {
personality,
cxa_throw,
div,
sdiv,
r#mod,
smod,
shl,
shr,
sar,
byte,
add_mod,
mul_mod,
exp,
sign_extend,
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,
}
}
/// Declares an LLVM runtime function in the `module`,
pub fn declare(
module: &inkwell::module::Module<'ctx>,
name: &str,
r#type: inkwell::types::FunctionType<'ctx>,
linkage: Option<inkwell::module::Linkage>,
) -> FunctionDeclaration<'ctx> {
let value = module.add_function(name, r#type, linkage);
FunctionDeclaration::new(r#type, value)
}
/// Create the function definition from an existing symbol.
pub fn define(
module: &inkwell::module::Module<'ctx>,
name: &str,
) -> Option<FunctionDeclaration<'ctx>> {
let value = module.get_function(name)?;
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)
}
}
@@ -0,0 +1,378 @@
//! The LLVM IR generator function.
pub mod block;
pub mod declaration;
pub mod evmla_data;
pub mod intrinsics;
pub mod llvm_runtime;
pub mod r#return;
pub mod runtime;
pub mod vyper_data;
pub mod yul_data;
use std::collections::HashMap;
use crate::optimizer::settings::size_level::SizeLevel;
use crate::optimizer::Optimizer;
use crate::polkavm::context::attribute::Attribute;
use crate::polkavm::context::pointer::Pointer;
use self::declaration::Declaration;
use self::evmla_data::EVMLAData;
use self::r#return::Return;
use self::runtime::Runtime;
use self::vyper_data::VyperData;
use self::yul_data::YulData;
/// The LLVM IR generator function.
#[derive(Debug)]
pub struct Function<'ctx> {
/// The high-level source code name.
name: String,
/// The LLVM function declaration.
declaration: Declaration<'ctx>,
/// The stack representation.
stack: HashMap<String, Pointer<'ctx>>,
/// The return value entity.
r#return: Return<'ctx>,
/// The entry block. Each LLVM IR functions must have an entry block.
entry_block: inkwell::basic_block::BasicBlock<'ctx>,
/// The return/leave block. LLVM IR functions may have multiple returning blocks, but it is
/// more reasonable to have a single returning block and other high-level language returns
/// jumping to it. This way it is easier to implement some additional checks and clean-ups
/// before the returning.
return_block: inkwell::basic_block::BasicBlock<'ctx>,
/// The Yul compiler data.
yul_data: Option<YulData>,
/// The EVM legacy assembly compiler data.
evmla_data: Option<EVMLAData<'ctx>>,
/// The Vyper data.
vyper_data: Option<VyperData>,
}
impl<'ctx> Function<'ctx> {
/// The near call ABI function prefix.
pub const ZKSYNC_NEAR_CALL_ABI_PREFIX: &'static str = "ZKSYNC_NEAR_CALL";
/// The near call ABI exception handler name.
pub const ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER: &'static str = "ZKSYNC_CATCH_NEAR_CALL";
/// The stack hashmap default capacity.
const STACK_HASHMAP_INITIAL_CAPACITY: usize = 64;
/// A shortcut constructor.
pub fn new(
name: String,
declaration: Declaration<'ctx>,
r#return: Return<'ctx>,
entry_block: inkwell::basic_block::BasicBlock<'ctx>,
return_block: inkwell::basic_block::BasicBlock<'ctx>,
) -> Self {
Self {
name,
declaration,
stack: HashMap::with_capacity(Self::STACK_HASHMAP_INITIAL_CAPACITY),
r#return,
entry_block,
return_block,
yul_data: None,
evmla_data: None,
vyper_data: None,
}
}
/// Returns the function name reference.
pub fn name(&self) -> &str {
self.name.as_str()
}
/// Checks whether the function is defined outside of the front-end.
pub fn is_name_external(name: &str) -> bool {
name.starts_with("llvm.")
|| (name.starts_with("__")
&& name != Runtime::FUNCTION_ENTRY
&& name != Runtime::FUNCTION_DEPLOY_CODE
&& name != Runtime::FUNCTION_RUNTIME_CODE)
}
/// Checks whether the function is related to the near call ABI.
pub fn is_near_call_abi(name: &str) -> bool {
name.starts_with(Self::ZKSYNC_NEAR_CALL_ABI_PREFIX)
|| name == Self::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER
}
/// Returns the LLVM function declaration.
pub fn declaration(&self) -> Declaration<'ctx> {
self.declaration
}
/// Returns the N-th parameter of the function.
pub fn get_nth_param(&self, index: usize) -> inkwell::values::BasicValueEnum<'ctx> {
self.declaration()
.value
.get_nth_param(index as u32)
.expect("Always exists")
}
/// Sets the memory writer function attributes.
pub fn set_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
attributes: Vec<Attribute>,
force: bool,
) {
for attribute_kind in attributes.into_iter() {
match attribute_kind {
Attribute::Memory => todo!("`memory` attributes are not yet implemented"),
attribute_kind @ Attribute::AlwaysInline if force => {
let is_optimize_none_set = declaration
.value
.get_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
Attribute::OptimizeNone as u32,
)
.is_some();
if !is_optimize_none_set {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
Attribute::NoInline as u32,
);
declaration.value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(attribute_kind as u32, 0),
);
}
}
attribute_kind @ Attribute::NoInline if force => {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
Attribute::AlwaysInline as u32,
);
declaration.value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(attribute_kind as u32, 0),
);
}
attribute_kind => declaration.value.add_attribute(
inkwell::attributes::AttributeLoc::Function,
llvm.create_enum_attribute(attribute_kind as u32, 0),
),
}
}
}
/// Remove specified attributes existing on the given declaration.
pub fn remove_attributes(declaration: Declaration, attributes: &[Attribute]) {
for attribute in attributes.iter().filter(|attribute| {
declaration
.value
.get_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
**attribute as u32,
)
.is_some()
}) {
declaration.value.remove_enum_attribute(
inkwell::attributes::AttributeLoc::Function,
*attribute as u32,
);
}
}
/// Sets the default attributes.
/// The attributes only affect the LLVM optimizations.
pub fn set_default_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
optimizer: &Optimizer,
) {
if optimizer.settings().level_middle_end == inkwell::OptimizationLevel::None {
Self::remove_attributes(
declaration,
&[Attribute::OptimizeForSize, Attribute::AlwaysInline],
);
Self::set_attributes(
llvm,
declaration,
vec![Attribute::OptimizeNone, Attribute::NoInline],
false,
);
} else if optimizer.settings().level_middle_end_size == SizeLevel::Z {
Self::set_attributes(
llvm,
declaration,
vec![Attribute::OptimizeForSize, Attribute::MinSize],
false,
);
}
Self::set_attributes(llvm, declaration, vec![Attribute::NoFree], false);
}
/// Sets the front-end runtime attributes.
pub fn set_frontend_runtime_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
optimizer: &Optimizer,
) {
if optimizer.settings().level_middle_end_size == SizeLevel::Z {
Self::set_attributes(llvm, declaration, vec![Attribute::NoInline], false);
}
}
/// Sets the exception handler attributes.
pub fn set_exception_handler_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
) {
Self::set_attributes(llvm, declaration, vec![Attribute::NoInline], false);
}
/// Sets the CXA-throw attributes.
pub fn set_cxa_throw_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
) {
Self::set_attributes(llvm, declaration, vec![Attribute::NoProfile], false);
}
/// Sets the pure function attributes.
pub fn set_pure_function_attributes(
llvm: &'ctx inkwell::context::Context,
declaration: Declaration<'ctx>,
) {
Self::set_attributes(
llvm,
declaration,
vec![
Attribute::MustProgress,
Attribute::NoUnwind,
Attribute::WillReturn,
],
false,
);
}
/// Saves the pointer to a stack variable, returning the pointer to the shadowed variable,
/// if it exists.
pub fn insert_stack_pointer(
&mut self,
name: String,
pointer: Pointer<'ctx>,
) -> Option<Pointer<'ctx>> {
self.stack.insert(name, pointer)
}
/// Gets the pointer to a stack variable.
pub fn get_stack_pointer(&self, name: &str) -> Option<Pointer<'ctx>> {
self.stack.get(name).copied()
}
/// Removes the pointer to a stack variable.
pub fn remove_stack_pointer(&mut self, name: &str) {
self.stack.remove(name);
}
/// Returns the return entity representation.
pub fn r#return(&self) -> Return<'ctx> {
self.r#return
}
/// Returns the pointer to the function return value.
/// # Panics
/// If the pointer has not been set yet.
pub fn return_pointer(&self) -> Option<Pointer<'ctx>> {
self.r#return.return_pointer()
}
/// Returns the return data size in bytes, based on the default stack alignment.
/// # Panics
/// If the pointer has not been set yet.
pub fn return_data_size(&self) -> usize {
self.r#return.return_data_size()
}
/// Returns the function entry block.
pub fn entry_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.entry_block
}
/// Returns the function return block.
pub fn return_block(&self) -> inkwell::basic_block::BasicBlock<'ctx> {
self.return_block
}
/// Sets the EVM legacy assembly data.
pub fn set_evmla_data(&mut self, data: EVMLAData<'ctx>) {
self.evmla_data = Some(data);
}
/// Returns the EVM legacy assembly data reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evmla(&self) -> &EVMLAData<'ctx> {
self.evmla_data
.as_ref()
.expect("The EVM data must have been initialized")
}
/// Returns the EVM legacy assembly data mutable reference.
/// # Panics
/// If the EVM data has not been initialized.
pub fn evmla_mut(&mut self) -> &mut EVMLAData<'ctx> {
self.evmla_data
.as_mut()
.expect("The EVM data must have been initialized")
}
/// Sets the Vyper data.
pub fn set_vyper_data(&mut self, data: VyperData) {
self.vyper_data = Some(data);
}
/// Returns the Vyper data reference.
/// # Panics
/// If the Vyper data has not been initialized.
pub fn vyper(&self) -> &VyperData {
self.vyper_data
.as_ref()
.expect("The Vyper data must have been initialized")
}
/// Returns the Vyper data mutable reference.
/// # Panics
/// If the Vyper data has not been initialized.
pub fn vyper_mut(&mut self) -> &mut VyperData {
self.vyper_data
.as_mut()
.expect("The Vyper data must have been initialized")
}
/// Sets the Yul data.
pub fn set_yul_data(&mut self, data: YulData) {
self.yul_data = Some(data);
}
/// Returns the Yul data reference.
/// # Panics
/// If the Yul data has not been initialized.
pub fn yul(&self) -> &YulData {
self.yul_data
.as_ref()
.expect("The Yul data must have been initialized")
}
/// Returns the Yul data mutable reference.
/// # Panics
/// If the Yul data has not been initialized.
pub fn yul_mut(&mut self) -> &mut YulData {
self.yul_data
.as_mut()
.expect("The Yul data must have been initialized")
}
}
@@ -0,0 +1,59 @@
//! The LLVM IR generator function return entity.
use crate::polkavm::context::pointer::Pointer;
/// The LLVM IR generator function return entity.
#[derive(Debug, Clone, Copy)]
pub enum Return<'ctx> {
/// The function does not return a value.
None,
/// The function returns a primitive value.
Primitive {
/// The primitive value pointer allocated at the function entry.
pointer: Pointer<'ctx>,
},
/// The function returns a compound value.
/// In this case, the return pointer is allocated on the stack by the callee.
Compound {
/// The structure pointer allocated at the function entry.
pointer: Pointer<'ctx>,
/// The function return type size.
size: usize,
},
}
impl<'ctx> Return<'ctx> {
/// A shortcut constructor.
pub fn none() -> Self {
Self::None
}
/// A shortcut constructor.
pub fn primitive(pointer: Pointer<'ctx>) -> Self {
Self::Primitive { pointer }
}
/// A shortcut constructor.
pub fn compound(pointer: Pointer<'ctx>, size: usize) -> Self {
Self::Compound { pointer, size }
}
/// Returns the pointer to the function return value.
pub fn return_pointer(&self) -> Option<Pointer<'ctx>> {
match self {
Return::None => None,
Return::Primitive { pointer } => Some(pointer.to_owned()),
Return::Compound { pointer, .. } => Some(pointer.to_owned()),
}
}
/// Returns the return data size in bytes, based on the default stack alignment.
pub fn return_data_size(&self) -> usize {
revive_common::BYTE_LENGTH_FIELD
* match self {
Self::None => 0,
Self::Primitive { .. } => 1,
Self::Compound { size, .. } => *size,
}
}
}
@@ -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(())
}
}
@@ -0,0 +1,41 @@
//! The LLVM function Vyper data.
use std::collections::HashMap;
/// The LLVM function Vyper data.
/// Describes some data that is only relevant to Vyper.
#[derive(Debug)]
pub struct VyperData {
/// The block-local variables. They are still allocated at the beginning of the function,
/// but their parent block must be known in order to pass the implicit arguments thereto.
/// Is only used by the Vyper LLL IR compiler.
label_arguments: HashMap<String, Vec<String>>,
}
impl Default for VyperData {
fn default() -> Self {
Self {
label_arguments: HashMap::with_capacity(Self::LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY),
}
}
}
impl VyperData {
/// The label arguments hashmap default capacity.
const LABEL_ARGUMENTS_HASHMAP_INITIAL_CAPACITY: usize = 16;
/// A shortcut constructor.
pub fn new() -> Self {
Self::default()
}
/// Returns the list of a Vyper label arguments.
pub fn label_arguments(&self, label_name: &str) -> Option<Vec<String>> {
self.label_arguments.get(label_name).cloned()
}
/// Inserts arguments for the specified label.
pub fn insert_label_arguments(&mut self, label_name: String, arguments: Vec<String>) {
self.label_arguments.insert(label_name, arguments);
}
}
@@ -0,0 +1,42 @@
//! The LLVM function Yul data.
use std::collections::HashMap;
use num::BigUint;
/// The LLVM function Yul data.
/// Describes some data that is only relevant to Yul.
#[derive(Debug)]
pub struct YulData {
/// The constants saved to variables. Used for peculiar cases like call simulation.
/// It is a partial implementation of the constant propagation.
constants: HashMap<String, BigUint>,
}
impl Default for YulData {
fn default() -> Self {
Self {
constants: HashMap::with_capacity(Self::CONSTANTS_HASHMAP_INITIAL_CAPACITY),
}
}
}
impl YulData {
/// The constants hashmap default capacity.
const CONSTANTS_HASHMAP_INITIAL_CAPACITY: usize = 16;
/// A shortcut constructor.
pub fn new() -> Self {
Self::default()
}
/// Returns a constant if it has been saved.
pub fn get_constant(&self, name: &str) -> Option<BigUint> {
self.constants.get(name).cloned()
}
/// Saves a constant detected with the partial constant propagation.
pub fn insert_constant(&mut self, name: String, value: BigUint) {
self.constants.insert(name, value);
}
}