mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-28 15:28:00 +00:00
Emerge Yul recompiler (#1)
Provide a modified (and incomplete) version of ZKSync zksolc that can compile the most basic contracts
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
//!
|
||||
//! Translates the arithmetic operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the arithmetic addition.
|
||||
///
|
||||
pub fn addition<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_add(operand_1, operand_2, "addition_result")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the arithmetic subtraction.
|
||||
///
|
||||
pub fn subtraction<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_sub(operand_1, operand_2, "subtraction_result")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the arithmetic multiplication.
|
||||
///
|
||||
pub fn multiplication<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_mul(operand_1, operand_2, "multiplication_result")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the arithmetic division.
|
||||
///
|
||||
pub fn division<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_unsigned_div(operand_1, operand_2, "udiv")?
|
||||
.into())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the arithmetic remainder.
|
||||
///
|
||||
pub fn remainder<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().r#mod,
|
||||
&[
|
||||
operand_1.as_basic_value_enum(),
|
||||
operand_2.as_basic_value_enum(),
|
||||
],
|
||||
"add_mod_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the signed arithmetic division.
|
||||
///
|
||||
/// Two differences between the EVM and LLVM IR:
|
||||
/// 1. In case of division by zero, 0 is returned.
|
||||
/// 2. In case of overflow, the first argument is returned.
|
||||
///
|
||||
pub fn division_signed<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().sdiv,
|
||||
&[
|
||||
operand_1.as_basic_value_enum(),
|
||||
operand_2.as_basic_value_enum(),
|
||||
],
|
||||
"add_mod_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the signed arithmetic remainder.
|
||||
///
|
||||
pub fn remainder_signed<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().smod,
|
||||
&[
|
||||
operand_1.as_basic_value_enum(),
|
||||
operand_2.as_basic_value_enum(),
|
||||
],
|
||||
"add_mod_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
//!
|
||||
//! Translates the bitwise operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the bitwise OR.
|
||||
///
|
||||
pub fn or<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_or(operand_1, operand_2, "or_result")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the bitwise XOR.
|
||||
///
|
||||
pub fn xor<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_xor(operand_1, operand_2, "xor_result")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the bitwise AND.
|
||||
///
|
||||
pub fn and<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_and(operand_1, operand_2, "and_result")?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the bitwise shift left.
|
||||
///
|
||||
pub fn shift_left<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
shift: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let overflow_block = context.append_basic_block("shift_left_overflow");
|
||||
let non_overflow_block = context.append_basic_block("shift_left_non_overflow");
|
||||
let join_block = context.append_basic_block("shift_left_join");
|
||||
|
||||
let result_pointer = context.build_alloca(context.field_type(), "shift_left_result_pointer");
|
||||
let condition_is_overflow = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
shift,
|
||||
context.field_const((era_compiler_common::BIT_LENGTH_FIELD - 1) as u64),
|
||||
"shift_left_is_overflow",
|
||||
)?;
|
||||
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
|
||||
|
||||
context.set_basic_block(overflow_block);
|
||||
context.build_store(result_pointer, context.field_const(0))?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(non_overflow_block);
|
||||
let value =
|
||||
context
|
||||
.builder()
|
||||
.build_left_shift(value, shift, "shift_left_non_overflow_result")?;
|
||||
context.build_store(result_pointer, value)?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
context.build_load(result_pointer, "shift_left_result")
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the bitwise shift right.
|
||||
///
|
||||
pub fn shift_right<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
shift: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let overflow_block = context.append_basic_block("shift_right_overflow");
|
||||
let non_overflow_block = context.append_basic_block("shift_right_non_overflow");
|
||||
let join_block = context.append_basic_block("shift_right_join");
|
||||
|
||||
let result_pointer = context.build_alloca(context.field_type(), "shift_right_result_pointer");
|
||||
let condition_is_overflow = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
shift,
|
||||
context.field_const((era_compiler_common::BIT_LENGTH_FIELD - 1) as u64),
|
||||
"shift_right_is_overflow",
|
||||
)?;
|
||||
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
|
||||
|
||||
context.set_basic_block(overflow_block);
|
||||
context.build_store(result_pointer, context.field_const(0))?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(non_overflow_block);
|
||||
let value = context.builder().build_right_shift(
|
||||
value,
|
||||
shift,
|
||||
false,
|
||||
"shift_right_non_overflow_result",
|
||||
)?;
|
||||
context.build_store(result_pointer, value)?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
context.build_load(result_pointer, "shift_right_result")
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the arithmetic bitwise shift right.
|
||||
///
|
||||
pub fn shift_right_arithmetic<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
shift: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let overflow_block = context.append_basic_block("shift_right_arithmetic_overflow");
|
||||
let overflow_positive_block =
|
||||
context.append_basic_block("shift_right_arithmetic_overflow_positive");
|
||||
let overflow_negative_block =
|
||||
context.append_basic_block("shift_right_arithmetic_overflow_negative");
|
||||
let non_overflow_block = context.append_basic_block("shift_right_arithmetic_non_overflow");
|
||||
let join_block = context.append_basic_block("shift_right_arithmetic_join");
|
||||
|
||||
let result_pointer = context.build_alloca(
|
||||
context.field_type(),
|
||||
"shift_right_arithmetic_result_pointer",
|
||||
);
|
||||
let condition_is_overflow = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
shift,
|
||||
context.field_const((era_compiler_common::BIT_LENGTH_FIELD - 1) as u64),
|
||||
"shift_right_arithmetic_is_overflow",
|
||||
)?;
|
||||
context.build_conditional_branch(condition_is_overflow, overflow_block, non_overflow_block)?;
|
||||
|
||||
context.set_basic_block(overflow_block);
|
||||
let sign_bit = context.builder().build_right_shift(
|
||||
value,
|
||||
context.field_const((era_compiler_common::BIT_LENGTH_FIELD - 1) as u64),
|
||||
false,
|
||||
"shift_right_arithmetic_sign_bit",
|
||||
)?;
|
||||
let condition_is_negative = context.builder().build_int_truncate_or_bit_cast(
|
||||
sign_bit,
|
||||
context.bool_type(),
|
||||
"shift_right_arithmetic_sign_bit_truncated",
|
||||
)?;
|
||||
context.build_conditional_branch(
|
||||
condition_is_negative,
|
||||
overflow_negative_block,
|
||||
overflow_positive_block,
|
||||
)?;
|
||||
|
||||
context.set_basic_block(overflow_positive_block);
|
||||
context.build_store(result_pointer, context.field_const(0))?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(overflow_negative_block);
|
||||
context.build_store(result_pointer, context.field_type().const_all_ones())?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(non_overflow_block);
|
||||
let value = context.builder().build_right_shift(
|
||||
value,
|
||||
shift,
|
||||
true,
|
||||
"shift_right_arithmetic_non_overflow_result",
|
||||
)?;
|
||||
context.build_store(result_pointer, value)?;
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
context.build_load(result_pointer, "shift_right_arithmetic_result")
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `byte` instruction.
|
||||
///
|
||||
pub fn byte<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().byte,
|
||||
&[
|
||||
operand_1.as_basic_value_enum(),
|
||||
operand_2.as_basic_value_enum(),
|
||||
],
|
||||
"byte_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
@@ -0,0 +1,756 @@
|
||||
//!
|
||||
//! Translates a contract call.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::argument::Argument;
|
||||
use crate::eravm::context::function::declaration::Declaration as FunctionDeclaration;
|
||||
use crate::eravm::context::function::runtime::Runtime;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates a contract call.
|
||||
///
|
||||
/// If the `simulation_address` is specified, the call is substituted with another instruction
|
||||
/// according to the specification.
|
||||
///
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn default<'ctx, D>(
|
||||
_context: &mut Context<'ctx, D>,
|
||||
_function: FunctionDeclaration<'ctx>,
|
||||
_gas: inkwell::values::IntValue<'ctx>,
|
||||
_address: inkwell::values::IntValue<'ctx>,
|
||||
_value: Option<inkwell::values::IntValue<'ctx>>,
|
||||
_input_offset: inkwell::values::IntValue<'ctx>,
|
||||
_input_length: inkwell::values::IntValue<'ctx>,
|
||||
_output_offset: inkwell::values::IntValue<'ctx>,
|
||||
_output_length: inkwell::values::IntValue<'ctx>,
|
||||
_constants: Vec<Option<num::BigUint>>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
todo!();
|
||||
|
||||
/*
|
||||
if context.is_system_mode() {
|
||||
let simulation_address = constants
|
||||
.get_mut(1)
|
||||
.and_then(|option| option.take())
|
||||
.and_then(|value| value.to_u16());
|
||||
|
||||
match simulation_address {
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_TO_L1) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().far_call,
|
||||
function,
|
||||
"to_l1",
|
||||
)?;
|
||||
|
||||
let is_first = gas;
|
||||
let in_0 = value.expect("Always exists");
|
||||
let in_1 = input_offset;
|
||||
|
||||
return crate::eravm::extensions::general::to_l1(context, is_first, in_0, in_1);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_CODE_ADDRESS) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"code_address",
|
||||
)?;
|
||||
|
||||
return crate::eravm::extensions::general::code_source(context);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_PRECOMPILE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"precompile",
|
||||
)?;
|
||||
|
||||
let in_0 = gas;
|
||||
let gas_left = input_offset;
|
||||
|
||||
return crate::eravm::extensions::general::precompile(context, in_0, gas_left);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_META) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"meta",
|
||||
)?;
|
||||
|
||||
return crate::eravm::extensions::general::meta(context);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_MIMIC_CALL) => {
|
||||
let address = gas;
|
||||
let abi_data = input_offset;
|
||||
let mimic = input_length;
|
||||
|
||||
return crate::eravm::extensions::call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call,
|
||||
address,
|
||||
mimic,
|
||||
abi_data.as_basic_value_enum(),
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_SYSTEM_MIMIC_CALL) => {
|
||||
let address = gas;
|
||||
let abi_data = input_offset;
|
||||
let mimic = input_length;
|
||||
let extra_value_1 = output_offset;
|
||||
let extra_value_2 = output_length;
|
||||
|
||||
return crate::eravm::extensions::call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call,
|
||||
address,
|
||||
mimic,
|
||||
abi_data.as_basic_value_enum(),
|
||||
vec![extra_value_1, extra_value_2],
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_MIMIC_CALL_BYREF) => {
|
||||
let address = gas;
|
||||
let mimic = input_length;
|
||||
let abi_data = context.get_global_value(crate::eravm::GLOBAL_ACTIVE_POINTER)?;
|
||||
|
||||
return crate::eravm::extensions::call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call_byref,
|
||||
address,
|
||||
mimic,
|
||||
abi_data.as_basic_value_enum(),
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_SYSTEM_MIMIC_CALL_BYREF) => {
|
||||
let address = gas;
|
||||
let mimic = input_length;
|
||||
let abi_data = context.get_global_value(crate::eravm::GLOBAL_ACTIVE_POINTER)?;
|
||||
let extra_value_1 = output_offset;
|
||||
let extra_value_2 = output_length;
|
||||
|
||||
return crate::eravm::extensions::call::mimic(
|
||||
context,
|
||||
context.llvm_runtime().mimic_call_byref,
|
||||
address,
|
||||
mimic,
|
||||
abi_data,
|
||||
vec![extra_value_1, extra_value_2],
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_RAW_FAR_CALL) => {
|
||||
let address = gas;
|
||||
let abi_data = input_length;
|
||||
|
||||
return crate::eravm::extensions::call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().modify(function, false)?,
|
||||
address,
|
||||
abi_data.as_basic_value_enum(),
|
||||
output_offset,
|
||||
output_length,
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_RAW_FAR_CALL_BYREF) => {
|
||||
let address = gas;
|
||||
let abi_data = context.get_global_value(crate::eravm::GLOBAL_ACTIVE_POINTER)?;
|
||||
|
||||
return crate::eravm::extensions::call::raw_far(
|
||||
context,
|
||||
context.llvm_runtime().modify(function, true)?,
|
||||
address,
|
||||
abi_data,
|
||||
output_offset,
|
||||
output_length,
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_SYSTEM_CALL) => {
|
||||
let address = gas;
|
||||
let abi_data = input_length;
|
||||
let extra_value_1 = value.expect("Always exists");
|
||||
let extra_value_2 = input_offset;
|
||||
let extra_value_3 = output_offset;
|
||||
let extra_value_4 = output_length;
|
||||
|
||||
return crate::eravm::extensions::call::system(
|
||||
context,
|
||||
context.llvm_runtime().modify(function, false)?,
|
||||
address,
|
||||
abi_data.as_basic_value_enum(),
|
||||
context.field_const(0),
|
||||
context.field_const(0),
|
||||
vec![extra_value_1, extra_value_2, extra_value_3, extra_value_4],
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_SYSTEM_CALL_BYREF) => {
|
||||
let address = gas;
|
||||
let abi_data = context.get_global_value(crate::eravm::GLOBAL_ACTIVE_POINTER)?;
|
||||
let extra_value_1 = value.expect("Always exists");
|
||||
let extra_value_2 = input_offset;
|
||||
let extra_value_3 = output_offset;
|
||||
let extra_value_4 = output_length;
|
||||
|
||||
return crate::eravm::extensions::call::system(
|
||||
context,
|
||||
context.llvm_runtime().modify(function, true)?,
|
||||
address,
|
||||
abi_data,
|
||||
context.field_const(0),
|
||||
context.field_const(0),
|
||||
vec![extra_value_1, extra_value_2, extra_value_3, extra_value_4],
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_SET_CONTEXT_VALUE_CALL) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().far_call,
|
||||
function,
|
||||
"set_context_value",
|
||||
)?;
|
||||
|
||||
let value = value.expect("Always exists");
|
||||
|
||||
return crate::eravm::extensions::general::set_context_value(context, value);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_SET_PUBDATA_PRICE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().far_call,
|
||||
function,
|
||||
"set_pubdata_price",
|
||||
)?;
|
||||
|
||||
let price = gas;
|
||||
|
||||
return crate::eravm::extensions::general::set_pubdata_price(context, price);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_INCREMENT_TX_COUNTER) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().far_call,
|
||||
function,
|
||||
"increment_tx_counter",
|
||||
)?;
|
||||
|
||||
return crate::eravm::extensions::general::increment_tx_counter(context);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_GET_GLOBAL_PTR_CALLDATA) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"get_global_ptr_calldata",
|
||||
)?;
|
||||
|
||||
let pointer = context.get_global_value(crate::eravm::GLOBAL_CALLDATA_POINTER)?;
|
||||
let value = context.builder().build_ptr_to_int(
|
||||
pointer.into_pointer_value(),
|
||||
context.field_type(),
|
||||
"calldata_abi_integer",
|
||||
)?;
|
||||
return Ok(value.as_basic_value_enum());
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_GET_GLOBAL_CALL_FLAGS) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"get_global_call_flags",
|
||||
)?;
|
||||
|
||||
return context.get_global_value(crate::eravm::GLOBAL_CALL_FLAGS);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_GET_GLOBAL_PTR_RETURN_DATA) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"get_global_ptr_return_data",
|
||||
)?;
|
||||
|
||||
let pointer = context.get_global_value(crate::eravm::GLOBAL_RETURN_DATA_POINTER)?;
|
||||
let value = context.builder().build_ptr_to_int(
|
||||
pointer.into_pointer_value(),
|
||||
context.field_type(),
|
||||
"return_data_abi_integer",
|
||||
)?;
|
||||
return Ok(value.as_basic_value_enum());
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_EVENT_INITIALIZE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().far_call,
|
||||
function,
|
||||
"event_initialize",
|
||||
)?;
|
||||
|
||||
let operand_1 = gas;
|
||||
let operand_2 = value.expect("Always exists");
|
||||
|
||||
return crate::eravm::extensions::general::event(
|
||||
context, operand_1, operand_2, true,
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_EVENT_WRITE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().far_call,
|
||||
function,
|
||||
"event_initialize",
|
||||
)?;
|
||||
|
||||
let operand_1 = gas;
|
||||
let operand_2 = value.expect("Always exists");
|
||||
|
||||
return crate::eravm::extensions::general::event(
|
||||
context, operand_1, operand_2, false,
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_LOAD_CALLDATA) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_load_calldata",
|
||||
)?;
|
||||
|
||||
return crate::eravm::extensions::abi::calldata_ptr_to_active(context);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_LOAD_RETURN_DATA) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_load_return_data",
|
||||
)?;
|
||||
|
||||
return crate::eravm::extensions::abi::return_data_ptr_to_active(context);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_ADD) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_add",
|
||||
)?;
|
||||
|
||||
let offset = gas;
|
||||
|
||||
return crate::eravm::extensions::abi::active_ptr_add_assign(context, offset);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_SHRINK) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_shrink",
|
||||
)?;
|
||||
|
||||
let offset = gas;
|
||||
|
||||
return crate::eravm::extensions::abi::active_ptr_shrink_assign(context, offset);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_PACK) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_pack",
|
||||
)?;
|
||||
|
||||
let data = gas;
|
||||
|
||||
return crate::eravm::extensions::abi::active_ptr_pack_assign(context, data);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_MULTIPLICATION_HIGH_REGISTER) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"multiplication_high_register",
|
||||
)?;
|
||||
|
||||
let operand_1 = gas;
|
||||
let operand_2 = input_offset;
|
||||
|
||||
return crate::eravm::extensions::math::multiplication_512(
|
||||
context, operand_1, operand_2,
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_GET_GLOBAL_EXTRA_ABI_DATA) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"get_global_extra_abi_data",
|
||||
)?;
|
||||
|
||||
let index = gas;
|
||||
|
||||
return crate::eravm::extensions::abi::get_extra_abi_data(context, index);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_DATA_LOAD) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_data_load",
|
||||
)?;
|
||||
|
||||
let offset = gas;
|
||||
|
||||
return crate::eravm::extensions::abi::active_ptr_data_load(context, offset);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_DATA_SIZE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_data_size",
|
||||
)?;
|
||||
|
||||
return crate::eravm::extensions::abi::active_ptr_data_size(context);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_ACTIVE_PTR_DATA_COPY) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"active_ptr_data_copy",
|
||||
)?;
|
||||
|
||||
let destination_offset = gas;
|
||||
let source_offset = input_offset;
|
||||
let size = input_length;
|
||||
|
||||
return crate::eravm::extensions::abi::active_ptr_data_copy(
|
||||
context,
|
||||
destination_offset,
|
||||
source_offset,
|
||||
size,
|
||||
);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_CONST_ARRAY_DECLARE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"const_array_declare",
|
||||
)?;
|
||||
|
||||
let index = constants
|
||||
.get_mut(0)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index is missing"))?
|
||||
.to_u8()
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index must fit into 8 bits"))?;
|
||||
let size = constants
|
||||
.get_mut(2)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array size is missing"))?
|
||||
.to_u16()
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array size must fit into 16 bits"))?;
|
||||
|
||||
return crate::eravm::extensions::const_array::declare(context, index, size);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_CONST_ARRAY_SET) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"const_array_set",
|
||||
)?;
|
||||
|
||||
let index = constants
|
||||
.get_mut(0)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index is missing"))?
|
||||
.to_u8()
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index must fit into 8 bits"))?;
|
||||
let offset = constants
|
||||
.get_mut(2)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array offset is missing"))?
|
||||
.to_u16()
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array offset must fit into 16 bits"))?;
|
||||
let value = constants
|
||||
.get_mut(4)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array assigned value is missing"))?;
|
||||
|
||||
return crate::eravm::extensions::const_array::set(context, index, offset, value);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_CONST_ARRAY_FINALIZE) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"const_array_finalize",
|
||||
)?;
|
||||
|
||||
let index = constants
|
||||
.get_mut(0)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index is missing"))?
|
||||
.to_u8()
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index must fit into 8 bits"))?;
|
||||
|
||||
return crate::eravm::extensions::const_array::finalize(context, index);
|
||||
}
|
||||
Some(era_compiler_common::ERAVM_ADDRESS_CONST_ARRAY_GET) => {
|
||||
crate::eravm::extensions::call::validate_call_type(
|
||||
context.llvm_runtime().static_call,
|
||||
function,
|
||||
"const_array_get",
|
||||
)?;
|
||||
|
||||
let index = constants
|
||||
.get_mut(0)
|
||||
.and_then(|option| option.take())
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index is missing"))?
|
||||
.to_u8()
|
||||
.ok_or_else(|| anyhow::anyhow!("Const array index must fit into 8 bits"))?;
|
||||
let offset = input_offset;
|
||||
|
||||
return crate::eravm::extensions::const_array::get(context, index, offset);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let identity_block = context.append_basic_block("contract_call_identity_block");
|
||||
let ordinary_block = context.append_basic_block("contract_call_ordinary_block");
|
||||
let join_block = context.append_basic_block("contract_call_join_block");
|
||||
|
||||
let result_pointer = context.build_alloca(context.field_type(), "contract_call_result_pointer");
|
||||
context.build_store(result_pointer, context.field_const(0));
|
||||
|
||||
context.builder().build_switch(
|
||||
address,
|
||||
ordinary_block,
|
||||
&[(
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_IDENTITY.into()),
|
||||
identity_block,
|
||||
)],
|
||||
)?;
|
||||
|
||||
{
|
||||
context.set_basic_block(identity_block);
|
||||
let result = identity(context, output_offset, input_offset, output_length)?;
|
||||
context.build_store(result_pointer, result);
|
||||
context.build_unconditional_branch(join_block);
|
||||
}
|
||||
|
||||
context.set_basic_block(ordinary_block);
|
||||
let result = if let Some(value) = value {
|
||||
default_wrapped(
|
||||
context,
|
||||
function,
|
||||
gas,
|
||||
value,
|
||||
address,
|
||||
input_offset,
|
||||
input_length,
|
||||
output_offset,
|
||||
output_length,
|
||||
)?
|
||||
} else {
|
||||
let function = Runtime::default_call(context, function);
|
||||
context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
gas.as_basic_value_enum(),
|
||||
address.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
output_offset.as_basic_value_enum(),
|
||||
output_length.as_basic_value_enum(),
|
||||
],
|
||||
"default_call",
|
||||
)
|
||||
.expect("Always exists")
|
||||
};
|
||||
context.build_store(result_pointer, result);
|
||||
context.build_unconditional_branch(join_block);
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
let result = context.build_load(result_pointer, "contract_call_result");
|
||||
Ok(result)
|
||||
*/
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the Yul `linkersymbol` instruction.
|
||||
///
|
||||
pub fn linker_symbol<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
mut arguments: [Argument<'ctx>; 1],
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let path = arguments[0]
|
||||
.original
|
||||
.take()
|
||||
.ok_or_else(|| anyhow::anyhow!("Linker symbol literal is missing"))?;
|
||||
|
||||
Ok(context
|
||||
.resolve_library(path.as_str())?
|
||||
.as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Generates a custom request to a system contract.
|
||||
///
|
||||
pub fn request<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
address: inkwell::values::IntValue<'ctx>,
|
||||
signature: &'static str,
|
||||
arguments: Vec<inkwell::values::IntValue<'ctx>>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash = crate::eravm::utils::keccak256(signature.as_bytes());
|
||||
let signature_value = context.field_const_str_hex(signature_hash.as_str());
|
||||
|
||||
let calldata_size = context.field_const(
|
||||
(era_compiler_common::BYTE_LENGTH_X32
|
||||
+ (era_compiler_common::BYTE_LENGTH_FIELD * arguments.len())) as u64,
|
||||
);
|
||||
|
||||
let calldata_array_pointer = context.build_alloca(
|
||||
context.array_type(context.field_type(), arguments.len()),
|
||||
"system_request_calldata_array_pointer",
|
||||
);
|
||||
for (index, argument) in arguments.into_iter().enumerate() {
|
||||
let argument_pointer = context.build_gep(
|
||||
calldata_array_pointer,
|
||||
&[context.field_const(0), context.field_const(index as u64)],
|
||||
context.field_type(),
|
||||
"system_request_calldata_array_pointer",
|
||||
);
|
||||
context.build_store(argument_pointer, argument)?;
|
||||
}
|
||||
Ok(context
|
||||
.build_invoke(
|
||||
context.llvm_runtime().system_request,
|
||||
&[
|
||||
address.as_basic_value_enum(),
|
||||
signature_value.as_basic_value_enum(),
|
||||
calldata_size.as_basic_value_enum(),
|
||||
calldata_array_pointer.value.as_basic_value_enum(),
|
||||
],
|
||||
"system_request_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
|
||||
///
|
||||
/// The default call wrapper, which redirects the call to the `msg.value` simulator if `msg.value`
|
||||
/// is not zero.
|
||||
///
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn _default_wrapped<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
function: FunctionDeclaration<'ctx>,
|
||||
gas: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
address: inkwell::values::IntValue<'ctx>,
|
||||
input_offset: inkwell::values::IntValue<'ctx>,
|
||||
input_length: inkwell::values::IntValue<'ctx>,
|
||||
output_offset: inkwell::values::IntValue<'ctx>,
|
||||
output_length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let value_zero_block = context.append_basic_block("contract_call_value_zero_block");
|
||||
let value_non_zero_block = context.append_basic_block("contract_call_value_non_zero_block");
|
||||
let value_join_block = context.append_basic_block("contract_call_value_join_block");
|
||||
|
||||
let result_pointer =
|
||||
context.build_alloca(context.field_type(), "contract_call_address_result_pointer");
|
||||
context.build_store(result_pointer, context.field_const(0))?;
|
||||
let is_value_zero = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::EQ,
|
||||
value,
|
||||
context.field_const(0),
|
||||
"contract_call_is_value_zero",
|
||||
)?;
|
||||
context.build_conditional_branch(is_value_zero, value_zero_block, value_non_zero_block)?;
|
||||
|
||||
context.set_basic_block(value_non_zero_block);
|
||||
let abi_data = crate::eravm::utils::abi_data(
|
||||
context,
|
||||
input_offset,
|
||||
input_length,
|
||||
Some(gas),
|
||||
AddressSpace::Heap,
|
||||
true,
|
||||
)?;
|
||||
let result = crate::eravm::extensions::call::system(
|
||||
context,
|
||||
context.llvm_runtime().modify(function, false)?,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_MSG_VALUE.into()),
|
||||
abi_data,
|
||||
output_offset,
|
||||
output_length,
|
||||
vec![
|
||||
value,
|
||||
address,
|
||||
context.field_const(u64::from(crate::eravm::r#const::NO_SYSTEM_CALL_BIT)),
|
||||
],
|
||||
)?;
|
||||
context.build_store(result_pointer, result)?;
|
||||
context.build_unconditional_branch(value_join_block);
|
||||
|
||||
context.set_basic_block(value_zero_block);
|
||||
let function = Runtime::default_call(context, function);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
gas.as_basic_value_enum(),
|
||||
address.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
output_offset.as_basic_value_enum(),
|
||||
output_length.as_basic_value_enum(),
|
||||
],
|
||||
"default_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
context.build_store(result_pointer, result)?;
|
||||
context.build_unconditional_branch(value_join_block);
|
||||
|
||||
context.set_basic_block(value_join_block);
|
||||
context.build_load(result_pointer, "contract_call_address_result")
|
||||
}
|
||||
|
||||
///
|
||||
/// Generates a memory copy loop repeating the behavior of the EVM `Identity` precompile.
|
||||
///
|
||||
fn _identity<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
destination: inkwell::values::IntValue<'ctx>,
|
||||
source: inkwell::values::IntValue<'ctx>,
|
||||
size: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let destination = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
destination,
|
||||
"contract_call_identity_destination",
|
||||
);
|
||||
let source = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
source,
|
||||
"contract_call_identity_source",
|
||||
);
|
||||
|
||||
context.build_memcpy(
|
||||
context.intrinsics().memory_copy,
|
||||
destination,
|
||||
source,
|
||||
size,
|
||||
"contract_call_memcpy_to_child",
|
||||
)?;
|
||||
|
||||
Ok(context.field_const(1).as_basic_value_enum())
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
//!
|
||||
//! Translates the calldata instructions.
|
||||
//!
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
use inkwell::types::BasicType;
|
||||
|
||||
///
|
||||
/// Translates the calldata load.
|
||||
///
|
||||
pub fn load<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let calldata_pointer = context
|
||||
.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?
|
||||
.value
|
||||
.as_pointer_value();
|
||||
let offset = context.build_gep(
|
||||
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
|
||||
&[offset],
|
||||
context.field_type().as_basic_type_enum(),
|
||||
"calldata_pointer_with_offset",
|
||||
);
|
||||
context
|
||||
.build_load(offset, "calldata_value")
|
||||
.map(|value| context.build_byte_swap(value))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the calldata size.
|
||||
///
|
||||
pub fn size<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let value = context.get_global_value(crate::eravm::GLOBAL_CALLDATA_SIZE)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the calldata copy.
|
||||
///
|
||||
pub fn copy<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
destination_offset: inkwell::values::IntValue<'ctx>,
|
||||
source_offset: inkwell::values::IntValue<'ctx>,
|
||||
size: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
// TODO: Untested
|
||||
let destination = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
destination_offset,
|
||||
"calldata_copy_destination_pointer",
|
||||
);
|
||||
let calldata_pointer = context
|
||||
.get_global(crate::eravm::GLOBAL_CALLDATA_POINTER)?
|
||||
.value
|
||||
.as_pointer_value();
|
||||
let source = context.build_gep(
|
||||
Pointer::new(context.byte_type(), AddressSpace::Stack, calldata_pointer),
|
||||
&[source_offset],
|
||||
context.field_type().as_basic_type_enum(),
|
||||
"calldata_pointer_with_offset",
|
||||
);
|
||||
|
||||
context.build_memcpy(
|
||||
context.intrinsics().memory_copy_from_generic,
|
||||
destination,
|
||||
source,
|
||||
size,
|
||||
"calldata_copy_memcpy_from_child",
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//!
|
||||
//! Translates the comparison operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the comparison operations.
|
||||
///
|
||||
/// There is not difference between the EVM and LLVM IR behaviors.
|
||||
///
|
||||
pub fn compare<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
operation: inkwell::IntPredicate,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let result = context.builder().build_int_compare(
|
||||
operation,
|
||||
operand_1,
|
||||
operand_2,
|
||||
"comparison_result",
|
||||
)?;
|
||||
let result = context.builder().build_int_z_extend_or_bit_cast(
|
||||
result,
|
||||
context.field_type(),
|
||||
"comparison_result_extended",
|
||||
)?;
|
||||
Ok(result.as_basic_value_enum())
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
//!
|
||||
//! Translates the context getter instructions.
|
||||
//!
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `gas_limit` instruction.
|
||||
///
|
||||
pub fn gas_limit<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"blockGasLimit()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `gas_price` instruction.
|
||||
///
|
||||
pub fn gas_price<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"gasPrice()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `tx.origin` instruction.
|
||||
///
|
||||
pub fn origin<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"origin()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `chain_id` instruction.
|
||||
///
|
||||
pub fn chain_id<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"chainId()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `block_number` instruction.
|
||||
///
|
||||
pub fn block_number<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"getBlockNumber()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `block_timestamp` instruction.
|
||||
///
|
||||
pub fn block_timestamp<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"getBlockTimestamp()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `block_hash` instruction.
|
||||
///
|
||||
pub fn block_hash<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
index: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"getBlockHashEVM(uint256)",
|
||||
vec![index],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `difficulty` instruction.
|
||||
///
|
||||
pub fn difficulty<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"difficulty()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `coinbase` instruction.
|
||||
///
|
||||
pub fn coinbase<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"coinbase()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `basefee` instruction.
|
||||
///
|
||||
pub fn basefee<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_SYSTEM_CONTEXT.into()),
|
||||
"baseFee()",
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `msize` instruction.
|
||||
///
|
||||
pub fn msize<'ctx, D>(
|
||||
_context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
todo!()
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//!
|
||||
//! Translates the contract creation instructions.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
use num::Zero;
|
||||
|
||||
use crate::eravm::context::argument::Argument;
|
||||
use crate::eravm::context::code_type::CodeType;
|
||||
use crate::eravm::context::function::runtime::Runtime;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the contract `create` instruction.
|
||||
///
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
///
|
||||
pub fn create<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
input_offset: inkwell::values::IntValue<'ctx>,
|
||||
input_length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash_string =
|
||||
crate::eravm::utils::keccak256(crate::eravm::DEPLOYER_SIGNATURE_CREATE.as_bytes());
|
||||
let signature_hash = context.field_const_str_hex(signature_hash_string.as_str());
|
||||
|
||||
let salt = context.field_const(0);
|
||||
|
||||
let function = Runtime::deployer_call(context);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
value.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
signature_hash.as_basic_value_enum(),
|
||||
salt.as_basic_value_enum(),
|
||||
],
|
||||
"create_deployer_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the contract `create2` instruction.
|
||||
///
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
///
|
||||
pub fn create2<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
input_offset: inkwell::values::IntValue<'ctx>,
|
||||
input_length: inkwell::values::IntValue<'ctx>,
|
||||
salt: Option<inkwell::values::IntValue<'ctx>>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let signature_hash_string =
|
||||
crate::eravm::utils::keccak256(crate::eravm::DEPLOYER_SIGNATURE_CREATE2.as_bytes());
|
||||
let signature_hash = context.field_const_str_hex(signature_hash_string.as_str());
|
||||
|
||||
let salt = salt.unwrap_or_else(|| context.field_const(0));
|
||||
|
||||
let function = Runtime::deployer_call(context);
|
||||
let result = context
|
||||
.build_call(
|
||||
function,
|
||||
&[
|
||||
value.as_basic_value_enum(),
|
||||
input_offset.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
signature_hash.as_basic_value_enum(),
|
||||
salt.as_basic_value_enum(),
|
||||
],
|
||||
"create2_deployer_call",
|
||||
)
|
||||
.expect("Always exists");
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the contract hash instruction, which is actually used to set the hash of the contract
|
||||
/// being created, or other related auxiliary data.
|
||||
///
|
||||
/// Represents `dataoffset` in Yul and `PUSH [$]` in the EVM legacy assembly.
|
||||
///
|
||||
pub fn contract_hash<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
identifier: String,
|
||||
) -> anyhow::Result<Argument<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let code_type = context
|
||||
.code_type()
|
||||
.ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?;
|
||||
|
||||
let parent = context.module().get_name().to_str().expect("Always valid");
|
||||
|
||||
let contract_path =
|
||||
context
|
||||
.resolve_path(identifier.as_str())
|
||||
.map_err(|error| match code_type {
|
||||
CodeType::Runtime if identifier.ends_with("_deployed") => {
|
||||
anyhow::anyhow!("type({}).runtimeCode is not supported", identifier)
|
||||
}
|
||||
_ => error,
|
||||
})?;
|
||||
if contract_path.as_str() == parent {
|
||||
return Ok(Argument::new_with_constant(
|
||||
context.field_const(0).as_basic_value_enum(),
|
||||
num::BigUint::zero(),
|
||||
));
|
||||
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
|
||||
anyhow::bail!("type({}).runtimeCode is not supported", identifier);
|
||||
}
|
||||
|
||||
let hash_string = context.compile_dependency(identifier.as_str())?;
|
||||
let hash_value = context
|
||||
.field_const_str_hex(hash_string.as_str())
|
||||
.as_basic_value_enum();
|
||||
Ok(Argument::new_with_original(hash_value, hash_string))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the deployer call header size instruction, Usually, the header consists of:
|
||||
/// - the deployer contract method signature
|
||||
/// - the salt if the call is `create2`, or zero if the call is `create1`
|
||||
/// - the hash of the bytecode of the contract whose instance is being created
|
||||
/// - the offset of the constructor arguments
|
||||
/// - the length of the constructor arguments
|
||||
///
|
||||
/// If the call is `create1`, the space for the salt is still allocated, because the memory for the
|
||||
/// header is allocated by the Yul or EVM legacy assembly before it is known which version of
|
||||
/// `create` is going to be used.
|
||||
///
|
||||
/// Represents `datasize` in Yul and `PUSH #[$]` in the EVM legacy assembly.
|
||||
///
|
||||
pub fn header_size<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
identifier: String,
|
||||
) -> anyhow::Result<Argument<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let code_type = context
|
||||
.code_type()
|
||||
.ok_or_else(|| anyhow::anyhow!("The contract code part type is undefined"))?;
|
||||
|
||||
let parent = context.module().get_name().to_str().expect("Always valid");
|
||||
|
||||
let contract_path =
|
||||
context
|
||||
.resolve_path(identifier.as_str())
|
||||
.map_err(|error| match code_type {
|
||||
CodeType::Runtime if identifier.ends_with("_deployed") => {
|
||||
anyhow::anyhow!("type({}).runtimeCode is not supported", identifier)
|
||||
}
|
||||
_ => error,
|
||||
})?;
|
||||
if contract_path.as_str() == parent {
|
||||
return Ok(Argument::new_with_constant(
|
||||
context.field_const(0).as_basic_value_enum(),
|
||||
num::BigUint::zero(),
|
||||
));
|
||||
} else if identifier.ends_with("_deployed") && code_type == CodeType::Runtime {
|
||||
anyhow::bail!("type({}).runtimeCode is not supported", identifier);
|
||||
}
|
||||
|
||||
let size_bigint = num::BigUint::from(crate::eravm::DEPLOYER_CALL_HEADER_SIZE);
|
||||
let size_value = context
|
||||
.field_const(crate::eravm::DEPLOYER_CALL_HEADER_SIZE as u64)
|
||||
.as_basic_value_enum();
|
||||
Ok(Argument::new_with_constant(size_value, size_bigint))
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//!
|
||||
//! Translates the cryptographic operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::function::Function as EraVMFunction;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `sha3` instruction.
|
||||
///
|
||||
pub fn sha3<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(offset.into())
|
||||
/*
|
||||
let offset_pointer = context.builder().build_int_to_ptr(
|
||||
offset,
|
||||
context.byte_type().ptr_type(AddressSpace::Heap.into()),
|
||||
"sha3_offset_pointer",
|
||||
)?;
|
||||
|
||||
Ok(context
|
||||
.build_invoke(
|
||||
context.llvm_runtime().sha3,
|
||||
&[
|
||||
offset_pointer.as_basic_value_enum(),
|
||||
length.as_basic_value_enum(),
|
||||
context
|
||||
.bool_const(
|
||||
context
|
||||
.get_function(EraVMFunction::ZKSYNC_NEAR_CALL_ABI_EXCEPTION_HANDLER)
|
||||
.is_some(),
|
||||
)
|
||||
.as_basic_value_enum(),
|
||||
],
|
||||
"sha3_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
*/
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//!
|
||||
//! Translates the value and balance operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `gas` instruction.
|
||||
///
|
||||
pub fn gas<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context.integer_const(256, 0).as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `value` instruction.
|
||||
///
|
||||
pub fn value<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context.integer_const(256, 0).as_basic_value_enum())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `balance` instructions.
|
||||
///
|
||||
pub fn balance<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
address: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_ETH_TOKEN.into()),
|
||||
"balanceOf(uint256)",
|
||||
vec![address],
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
//!
|
||||
//! Translates a log or event call.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates a log or event call.
|
||||
///
|
||||
/// The decoding logic is implemented in a system contract, which is called from here.
|
||||
///
|
||||
/// There are several cases of the translation for the sake of efficiency, since the front-end
|
||||
/// emits topics and values sequentially by one, but the LLVM intrinsic and bytecode instruction
|
||||
/// accept two at once.
|
||||
///
|
||||
pub fn log<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
input_offset: inkwell::values::IntValue<'ctx>,
|
||||
input_length: inkwell::values::IntValue<'ctx>,
|
||||
topics: Vec<inkwell::values::IntValue<'ctx>>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
/*
|
||||
let failure_block = context.append_basic_block("event_failure_block");
|
||||
let join_block = context.append_basic_block("event_join_block");
|
||||
|
||||
let gas = crate::eravm::evm::ether_gas::gas(context)?.into_int_value();
|
||||
let abi_data = crate::eravm::utils::abi_data(
|
||||
context,
|
||||
input_offset,
|
||||
input_length,
|
||||
Some(gas),
|
||||
AddressSpace::Heap,
|
||||
true,
|
||||
)?;
|
||||
let mut extra_abi_data = Vec::with_capacity(1 + topics.len());
|
||||
extra_abi_data.push(context.field_const(topics.len() as u64));
|
||||
extra_abi_data.extend(topics);
|
||||
|
||||
let result = context
|
||||
.build_call(
|
||||
context.llvm_runtime().far_call,
|
||||
crate::eravm::utils::external_call_arguments(
|
||||
context,
|
||||
abi_data.as_basic_value_enum(),
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_EVENT_WRITER as u64),
|
||||
extra_abi_data,
|
||||
None,
|
||||
)
|
||||
.as_slice(),
|
||||
"event_writer_call_external",
|
||||
)
|
||||
.expect("Always returns a value");
|
||||
|
||||
let result_status_code_boolean = context
|
||||
.builder()
|
||||
.build_extract_value(
|
||||
result.into_struct_value(),
|
||||
1,
|
||||
"event_writer_external_result_status_code_boolean",
|
||||
)
|
||||
.expect("Always exists");
|
||||
context.build_conditional_branch(
|
||||
result_status_code_boolean.into_int_value(),
|
||||
join_block,
|
||||
failure_block,
|
||||
)?;
|
||||
|
||||
context.set_basic_block(failure_block);
|
||||
crate::eravm::evm::r#return::revert(context, context.field_const(0), context.field_const(0))?;
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//!
|
||||
//! Translates the external code operations.
|
||||
//!
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `extcodesize` instruction.
|
||||
///
|
||||
pub fn size<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
address: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into()),
|
||||
"getCodeSize(uint256)",
|
||||
vec![address],
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `extcodehash` instruction.
|
||||
///
|
||||
pub fn hash<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
address: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::call::request(
|
||||
context,
|
||||
context.field_const(zkevm_opcode_defs::ADDRESS_ACCOUNT_CODE_STORAGE.into()),
|
||||
"getCodeHash(uint256)",
|
||||
vec![address],
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//!
|
||||
//! Translates the contract immutable operations.
|
||||
//!
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::code_type::CodeType;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the contract immutable load.
|
||||
///
|
||||
/// In the deploy code the values are read from the auxiliary heap.
|
||||
/// In the runtime code they are requested from the system contract.
|
||||
///
|
||||
pub fn load<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
index: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.code_type() {
|
||||
None => {
|
||||
anyhow::bail!("Immutables are not available if the contract part is undefined");
|
||||
}
|
||||
Some(CodeType::Deploy) => {
|
||||
let index_double = context.builder().build_int_mul(
|
||||
index,
|
||||
context.field_const(2),
|
||||
"immutable_load_index_double",
|
||||
)?;
|
||||
let offset_absolute = context.builder().build_int_add(
|
||||
index_double,
|
||||
context.field_const(
|
||||
crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
|
||||
+ (3 * era_compiler_common::BYTE_LENGTH_FIELD) as u64,
|
||||
),
|
||||
"immutable_offset_absolute",
|
||||
)?;
|
||||
let immutable_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.field_type(),
|
||||
offset_absolute,
|
||||
"immutable_pointer",
|
||||
);
|
||||
context.build_load(immutable_pointer, "immutable_value")
|
||||
}
|
||||
Some(CodeType::Runtime) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the contract immutable store.
|
||||
///
|
||||
/// In the deploy code the values are written to the auxiliary heap at the predefined offset,
|
||||
/// being prepared for returning to the system contract for saving.
|
||||
///
|
||||
/// Ignored in the runtime code.
|
||||
///
|
||||
pub fn store<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
index: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.code_type() {
|
||||
None => {
|
||||
anyhow::bail!("Immutables are not available if the contract part is undefined");
|
||||
}
|
||||
Some(CodeType::Deploy) => {
|
||||
let index_double = context.builder().build_int_mul(
|
||||
index,
|
||||
context.field_const(2),
|
||||
"immutable_load_index_double",
|
||||
)?;
|
||||
let index_offset_absolute = context.builder().build_int_add(
|
||||
index_double,
|
||||
context.field_const(
|
||||
crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
|
||||
+ (2 * era_compiler_common::BYTE_LENGTH_FIELD) as u64,
|
||||
),
|
||||
"index_offset_absolute",
|
||||
)?;
|
||||
let index_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.field_type(),
|
||||
index_offset_absolute,
|
||||
"immutable_index_pointer",
|
||||
);
|
||||
context.build_store(index_offset_pointer, index)?;
|
||||
|
||||
let value_offset_absolute = context.builder().build_int_add(
|
||||
index_offset_absolute,
|
||||
context.field_const(era_compiler_common::BYTE_LENGTH_FIELD as u64),
|
||||
"value_offset_absolute",
|
||||
)?;
|
||||
let value_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.field_type(),
|
||||
value_offset_absolute,
|
||||
"immutable_value_pointer",
|
||||
);
|
||||
context.build_store(value_offset_pointer, value)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Some(CodeType::Runtime) => {
|
||||
anyhow::bail!("Immutable writes are not available in the runtime code");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
//!
|
||||
//! Translates the mathematical operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `addmod` instruction.
|
||||
///
|
||||
pub fn add_mod<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
modulo: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().add_mod,
|
||||
&[
|
||||
operand_1.as_basic_value_enum(),
|
||||
operand_2.as_basic_value_enum(),
|
||||
modulo.as_basic_value_enum(),
|
||||
],
|
||||
"add_mod_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `mulmod` instruction.
|
||||
///
|
||||
pub fn mul_mod<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
operand_1: inkwell::values::IntValue<'ctx>,
|
||||
operand_2: inkwell::values::IntValue<'ctx>,
|
||||
modulo: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().mul_mod,
|
||||
&[
|
||||
operand_1.as_basic_value_enum(),
|
||||
operand_2.as_basic_value_enum(),
|
||||
modulo.as_basic_value_enum(),
|
||||
],
|
||||
"mul_mod_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `exp` instruction.
|
||||
///
|
||||
pub fn exponent<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
exponent: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().exp,
|
||||
&[value.as_basic_value_enum(), exponent.as_basic_value_enum()],
|
||||
"exp_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `signextend` instruction.
|
||||
///
|
||||
pub fn sign_extend<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
bytes: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
Ok(context
|
||||
.build_call(
|
||||
context.llvm_runtime().sign_extend,
|
||||
&[bytes.as_basic_value_enum(), value.as_basic_value_enum()],
|
||||
"sign_extend_call",
|
||||
)
|
||||
.expect("Always exists"))
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
//!
|
||||
//! Translates the heap memory operations.
|
||||
//!
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `mload` instruction.
|
||||
///
|
||||
/// Uses the main heap.
|
||||
///
|
||||
pub fn load<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.field_type(),
|
||||
offset,
|
||||
"memory_load_pointer",
|
||||
);
|
||||
context.build_load(pointer, "memory_load_result")
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `mstore` instruction.
|
||||
///
|
||||
/// Uses the main heap.
|
||||
///
|
||||
pub fn store<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.field_type(),
|
||||
offset,
|
||||
"memory_store_pointer",
|
||||
);
|
||||
context.build_store(pointer, value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `mstore8` instruction.
|
||||
///
|
||||
/// Uses the main heap.
|
||||
///
|
||||
pub fn store_byte<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
offset,
|
||||
"mstore8_offset_pointer",
|
||||
);
|
||||
context.build_call(
|
||||
context.llvm_runtime().mstore8,
|
||||
&[
|
||||
offset_pointer.value.as_basic_value_enum(),
|
||||
value.as_basic_value_enum(),
|
||||
],
|
||||
"mstore8_call",
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
//!
|
||||
//! The EVM instructions translation utils.
|
||||
//!
|
||||
|
||||
pub mod arithmetic;
|
||||
pub mod bitwise;
|
||||
pub mod call;
|
||||
pub mod calldata;
|
||||
pub mod comparison;
|
||||
pub mod context;
|
||||
pub mod create;
|
||||
pub mod crypto;
|
||||
pub mod ether_gas;
|
||||
pub mod event;
|
||||
pub mod ext_code;
|
||||
pub mod immutable;
|
||||
pub mod math;
|
||||
pub mod memory;
|
||||
pub mod r#return;
|
||||
pub mod return_data;
|
||||
pub mod storage;
|
||||
@@ -0,0 +1,129 @@
|
||||
//!
|
||||
//! Translates the transaction return operations.
|
||||
//!
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::code_type::CodeType;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the `return` instruction.
|
||||
///
|
||||
/// Unlike in EVM, zkSync constructors return the array of contract immutables.
|
||||
///
|
||||
pub fn r#return<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.code_type() {
|
||||
None => {
|
||||
anyhow::bail!("Return is not available if the contract part is undefined");
|
||||
}
|
||||
Some(CodeType::Deploy) => {
|
||||
let immutables_offset_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.field_type(),
|
||||
context.field_const(crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA),
|
||||
"immutables_offset_pointer",
|
||||
);
|
||||
context.build_store(
|
||||
immutables_offset_pointer,
|
||||
context.field_const(era_compiler_common::BYTE_LENGTH_FIELD as u64),
|
||||
)?;
|
||||
|
||||
let immutables_number_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::HeapAuxiliary,
|
||||
context.field_type(),
|
||||
context.field_const(
|
||||
crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA
|
||||
+ (era_compiler_common::BYTE_LENGTH_FIELD as u64),
|
||||
),
|
||||
"immutables_number_pointer",
|
||||
);
|
||||
let immutable_values_size = context.immutables_size()?;
|
||||
context.build_store(
|
||||
immutables_number_pointer,
|
||||
context.field_const(
|
||||
(immutable_values_size / era_compiler_common::BYTE_LENGTH_FIELD) as u64,
|
||||
),
|
||||
)?;
|
||||
let immutables_size = context.builder().build_int_mul(
|
||||
context.field_const(immutable_values_size as u64),
|
||||
context.field_const(2),
|
||||
"immutables_size",
|
||||
)?;
|
||||
let return_data_length = context.builder().build_int_add(
|
||||
immutables_size,
|
||||
context.field_const((era_compiler_common::BYTE_LENGTH_FIELD * 2) as u64),
|
||||
"return_data_length",
|
||||
)?;
|
||||
|
||||
context.build_exit(
|
||||
context.integer_const(32, 0),
|
||||
context.field_const(crate::eravm::HEAP_AUX_OFFSET_CONSTRUCTOR_RETURN_DATA),
|
||||
return_data_length,
|
||||
)?;
|
||||
}
|
||||
Some(CodeType::Runtime) => {
|
||||
context.build_exit(context.integer_const(32, 0), offset, length)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `revert` instruction.
|
||||
///
|
||||
pub fn revert<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
offset: inkwell::values::IntValue<'ctx>,
|
||||
length: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
context.build_exit(context.integer_const(32, 1), offset, length)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `stop` instruction.
|
||||
///
|
||||
/// Is the same as `return(0, 0)`.
|
||||
///
|
||||
pub fn stop<D>(context: &mut Context<D>) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
r#return(
|
||||
context,
|
||||
context.integer_const(32, 0),
|
||||
context.integer_const(32, 0),
|
||||
)
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the `invalid` instruction.
|
||||
///
|
||||
/// Burns all gas using an out-of-bounds memory store, causing a panic.
|
||||
///
|
||||
pub fn invalid<D>(context: &mut Context<D>) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
crate::eravm::evm::memory::store(
|
||||
context,
|
||||
context.field_type().const_all_ones(),
|
||||
context.field_const(0),
|
||||
)?;
|
||||
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
//!
|
||||
//! Translates the return data instructions.
|
||||
//!
|
||||
|
||||
use inkwell::types::BasicType;
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the return data size.
|
||||
///
|
||||
pub fn size<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
match context.get_global_value(crate::eravm::GLOBAL_RETURN_DATA_SIZE) {
|
||||
Ok(global) => Ok(global),
|
||||
Err(_error) => Ok(context.field_const(0).as_basic_value_enum()),
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the return data copy.
|
||||
///
|
||||
pub fn copy<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
destination_offset: inkwell::values::IntValue<'ctx>,
|
||||
source_offset: inkwell::values::IntValue<'ctx>,
|
||||
size: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let error_block = context.append_basic_block("return_data_copy_error_block");
|
||||
let join_block = context.append_basic_block("return_data_copy_join_block");
|
||||
|
||||
let return_data_size = self::size(context)?.into_int_value();
|
||||
let copy_slice_end =
|
||||
context
|
||||
.builder()
|
||||
.build_int_add(source_offset, size, "return_data_copy_slice_end")?;
|
||||
let is_copy_out_of_bounds = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
copy_slice_end,
|
||||
return_data_size,
|
||||
"return_data_copy_is_out_of_bounds",
|
||||
)?;
|
||||
context.build_conditional_branch(is_copy_out_of_bounds, error_block, join_block)?;
|
||||
|
||||
context.set_basic_block(error_block);
|
||||
crate::eravm::evm::r#return::revert(context, context.field_const(0), context.field_const(0))?;
|
||||
|
||||
context.set_basic_block(join_block);
|
||||
let destination = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Heap,
|
||||
context.byte_type(),
|
||||
destination_offset,
|
||||
"return_data_copy_destination_pointer",
|
||||
);
|
||||
|
||||
let return_data_pointer_global =
|
||||
context.get_global(crate::eravm::GLOBAL_RETURN_DATA_POINTER)?;
|
||||
let return_data_pointer_pointer = return_data_pointer_global.into();
|
||||
let return_data_pointer =
|
||||
context.build_load(return_data_pointer_pointer, "return_data_pointer")?;
|
||||
let source = context.build_gep(
|
||||
Pointer::new(
|
||||
context.byte_type(),
|
||||
return_data_pointer_pointer.address_space,
|
||||
return_data_pointer.into_pointer_value(),
|
||||
),
|
||||
&[source_offset],
|
||||
context.byte_type().as_basic_type_enum(),
|
||||
"return_data_source_pointer",
|
||||
);
|
||||
|
||||
context.build_memcpy(
|
||||
context.intrinsics().memory_copy_from_generic,
|
||||
destination,
|
||||
source,
|
||||
size,
|
||||
"return_data_copy_memcpy_from_return_data",
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//!
|
||||
//! Translates the storage operations.
|
||||
//!
|
||||
|
||||
use crate::eravm::context::address_space::AddressSpace;
|
||||
use crate::eravm::context::pointer::Pointer;
|
||||
use crate::eravm::context::Context;
|
||||
use crate::eravm::Dependency;
|
||||
|
||||
///
|
||||
/// Translates the storage load.
|
||||
///
|
||||
pub fn load<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
position: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let position_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Storage,
|
||||
context.field_type(),
|
||||
position,
|
||||
"storage_load_position_pointer",
|
||||
);
|
||||
context.build_load(position_pointer, "storage_load_value")
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the storage store.
|
||||
///
|
||||
pub fn store<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
position: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let position_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::Storage,
|
||||
context.field_type(),
|
||||
position,
|
||||
"storage_store_position_pointer",
|
||||
);
|
||||
context.build_store(position_pointer, value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the transient storage load.
|
||||
///
|
||||
pub fn transient_load<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
position: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let position_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::TransientStorage,
|
||||
context.field_type(),
|
||||
position,
|
||||
"transient_storage_load_position_pointer",
|
||||
);
|
||||
context.build_load(position_pointer, "transient_storage_load_value")
|
||||
}
|
||||
|
||||
///
|
||||
/// Translates the transient storage store.
|
||||
///
|
||||
pub fn transient_store<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
position: inkwell::values::IntValue<'ctx>,
|
||||
value: inkwell::values::IntValue<'ctx>,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let position_pointer = Pointer::new_with_offset(
|
||||
context,
|
||||
AddressSpace::TransientStorage,
|
||||
context.field_type(),
|
||||
position,
|
||||
"transient_storage_store_position_pointer",
|
||||
);
|
||||
context.build_store(position_pointer, value)?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user