mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-29 08:57:56 +00:00
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user