mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-16 22:31:01 +00:00
Updated call semantics (#56)
- Update pallet-revive dependency - Implement calls according to pallet-revive call semantics - Switch to the new return data API in pallet revive and get rid of return data buffer - Remove a bunch of resulting dead code
This commit is contained in:
@@ -7,16 +7,14 @@ use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm_const::runtime_api;
|
||||
|
||||
static STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
||||
const STATIC_CALL_FLAG: u32 = 0b0001_0000;
|
||||
const REENTRANT_CALL_FLAG: u32 = 0b0000_1000;
|
||||
|
||||
/// 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 call<'ctx, D>(
|
||||
context: &mut Context<'ctx, D>,
|
||||
gas: inkwell::values::IntValue<'ctx>,
|
||||
_gas: inkwell::values::IntValue<'ctx>,
|
||||
address: inkwell::values::IntValue<'ctx>,
|
||||
value: Option<inkwell::values::IntValue<'ctx>>,
|
||||
input_offset: inkwell::values::IntValue<'ctx>,
|
||||
@@ -29,59 +27,71 @@ pub fn call<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let address_pointer = context.build_alloca(context.word_type(), "address_ptr");
|
||||
context.build_store(address_pointer, address)?;
|
||||
let address_type = context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS);
|
||||
let address_pointer = context.build_alloca_at_entry(address_type, "address_pointer");
|
||||
let address_truncated =
|
||||
context
|
||||
.builder()
|
||||
.build_int_truncate(address, address_type, "address_truncated")?;
|
||||
let address_swapped = context.build_byte_swap(address_truncated.into())?;
|
||||
context.build_store(address_pointer, address_swapped)?;
|
||||
|
||||
let value_pointer = if let Some(value) = value {
|
||||
let value_pointer = context.build_alloca(context.value_type(), "value");
|
||||
context.build_store(value_pointer, value)?;
|
||||
value_pointer
|
||||
} else {
|
||||
context.sentinel_pointer()
|
||||
};
|
||||
let value = value.unwrap_or_else(|| context.word_const(0));
|
||||
let value_pointer = context.build_alloca_at_entry(context.word_type(), "value_pointer");
|
||||
context.build_store(value_pointer, value)?;
|
||||
|
||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||
let output_offset = context.safe_truncate_int_to_xlen(output_offset)?;
|
||||
let output_length = context.safe_truncate_int_to_xlen(output_length)?;
|
||||
|
||||
let gas = context
|
||||
// TODO: What to supply here? Is there a weight to gas?
|
||||
let _gas = context
|
||||
.builder()
|
||||
.build_int_truncate(gas, context.integer_type(64), "gas")?;
|
||||
|
||||
let flags = if static_call { STATIC_CALL_FLAG } else { 0 };
|
||||
.build_int_truncate(_gas, context.integer_type(64), "gas")?;
|
||||
|
||||
let input_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||
let output_pointer = context.build_heap_gep(output_offset, output_length)?;
|
||||
|
||||
let output_length_pointer = context.get_global(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?;
|
||||
context.build_store(output_length_pointer.into(), output_length)?;
|
||||
let output_length_pointer = context.build_alloca_at_entry(context.xlen_type(), "output_length");
|
||||
context.build_store(output_length_pointer, output_length)?;
|
||||
|
||||
let argument_pointer = revive_runtime_api::calling_convention::Spill::new(
|
||||
let flags = if static_call {
|
||||
REENTRANT_CALL_FLAG | STATIC_CALL_FLAG
|
||||
} else {
|
||||
REENTRANT_CALL_FLAG
|
||||
};
|
||||
let flags = context.xlen_type().const_int(flags as u64, false);
|
||||
|
||||
let argument_type = revive_runtime_api::calling_convention::call(context.llvm());
|
||||
let argument_pointer = context.build_alloca_at_entry(argument_type, "call_arguments");
|
||||
let arguments = &[
|
||||
flags.as_basic_value_enum(),
|
||||
address_pointer.value.as_basic_value_enum(),
|
||||
context.integer_const(64, 0).as_basic_value_enum(),
|
||||
context.integer_const(64, 0).as_basic_value_enum(),
|
||||
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||
value_pointer.value.as_basic_value_enum(),
|
||||
input_pointer.value.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
output_pointer.value.as_basic_value_enum(),
|
||||
output_length_pointer.value.as_basic_value_enum(),
|
||||
];
|
||||
revive_runtime_api::calling_convention::spill(
|
||||
context.builder(),
|
||||
revive_runtime_api::calling_convention::call(context.llvm()),
|
||||
"call_arguments",
|
||||
)?
|
||||
.next(context.xlen_type().const_int(flags as u64, false))?
|
||||
.next(address_pointer.value)?
|
||||
.next(gas)?
|
||||
.skip()
|
||||
.next(context.sentinel_pointer().value)?
|
||||
.next(value_pointer.value)?
|
||||
.next(input_pointer.value)?
|
||||
.next(input_length)?
|
||||
.next(output_pointer.value)?
|
||||
.next(output_length_pointer.value)?
|
||||
.done();
|
||||
argument_pointer.value,
|
||||
argument_type,
|
||||
arguments,
|
||||
)?;
|
||||
|
||||
let name = runtime_api::imports::CALL;
|
||||
let arguments = context.builder().build_ptr_to_int(
|
||||
argument_pointer,
|
||||
let argument_pointer = context.builder().build_ptr_to_int(
|
||||
argument_pointer.value,
|
||||
context.xlen_type(),
|
||||
"argument_pointer",
|
||||
"call_argument_pointer",
|
||||
)?;
|
||||
let success = context
|
||||
.build_runtime_call(name, &[arguments.into()])
|
||||
.build_runtime_call(name, &[argument_pointer.into()])
|
||||
.unwrap_or_else(|| panic!("{name} should return a value"))
|
||||
.into_int_value();
|
||||
|
||||
|
||||
@@ -149,11 +149,7 @@ where
|
||||
runtime_api::imports::ADDRESS,
|
||||
&[pointer.to_int(context).into()],
|
||||
);
|
||||
let value = context.build_byte_swap(context.build_load(pointer, "address")?)?;
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(value.into_int_value(), context.word_type(), "address_zext")?
|
||||
.into())
|
||||
context.build_load_address(pointer)
|
||||
}
|
||||
|
||||
/// Translates the `caller` instruction.
|
||||
@@ -163,5 +159,13 @@ pub fn caller<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
context.build_runtime_call_to_getter(runtime_api::imports::CALLER)
|
||||
let pointer = context.build_alloca_at_entry(
|
||||
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
|
||||
"address_output",
|
||||
);
|
||||
context.build_runtime_call(
|
||||
runtime_api::imports::CALLER,
|
||||
&[pointer.to_int(context).into()],
|
||||
);
|
||||
context.build_load_address(pointer)
|
||||
}
|
||||
|
||||
@@ -9,26 +9,14 @@ use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm_const::runtime_api;
|
||||
|
||||
/// Translates the contract `create` instruction.
|
||||
/// The instruction is simulated by a call to a system contract.
|
||||
/// Translates the contract `create` and `create2` instruction.
|
||||
///
|
||||
/// A `salt` value of `None` is equivalent to `create1`.
|
||||
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,
|
||||
{
|
||||
self::create2(context, value, input_offset, input_length, None)
|
||||
}
|
||||
|
||||
/// Translates the contract `create2` instruction.
|
||||
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
|
||||
@@ -37,9 +25,6 @@ where
|
||||
let input_offset = context.safe_truncate_int_to_xlen(input_offset)?;
|
||||
let input_length = context.safe_truncate_int_to_xlen(input_length)?;
|
||||
|
||||
let value_pointer = context.build_alloca(context.value_type(), "value");
|
||||
context.build_store(value_pointer, value)?;
|
||||
|
||||
let code_hash_pointer = context.build_heap_gep(input_offset, input_length)?;
|
||||
|
||||
let input_data_pointer = context.build_gep(
|
||||
@@ -48,53 +33,68 @@ where
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false)],
|
||||
context.byte_type(),
|
||||
"value_ptr_parameter_offset",
|
||||
"input_ptr_parameter_offset",
|
||||
);
|
||||
|
||||
let salt_pointer = context.build_alloca(context.word_type(), "salt");
|
||||
context.build_store(salt_pointer, salt.unwrap_or_else(|| context.word_const(0)))?;
|
||||
let value_pointer = context.build_alloca_at_entry(context.value_type(), "transferred_value");
|
||||
context.build_store(value_pointer, value)?;
|
||||
|
||||
let (address_pointer, address_length_pointer) =
|
||||
context.build_stack_parameter(revive_common::BIT_LENGTH_ETH_ADDRESS, "address_pointer");
|
||||
let salt_pointer = match salt {
|
||||
Some(salt) => {
|
||||
let salt_pointer = context.build_alloca_at_entry(context.word_type(), "salt_pointer");
|
||||
context.build_store(salt_pointer, salt)?;
|
||||
salt_pointer
|
||||
}
|
||||
None => context.sentinel_pointer(),
|
||||
};
|
||||
|
||||
let address_pointer = context.build_alloca_at_entry(
|
||||
context.integer_type(revive_common::BIT_LENGTH_ETH_ADDRESS),
|
||||
"address_pointer",
|
||||
);
|
||||
context.build_store(address_pointer, context.word_const(0))?;
|
||||
|
||||
let argument_pointer = revive_runtime_api::calling_convention::Spill::new(
|
||||
let argument_type = revive_runtime_api::calling_convention::instantiate(context.llvm());
|
||||
let argument_pointer = context.build_alloca_at_entry(argument_type, "instantiate_arguments");
|
||||
let arguments = &[
|
||||
code_hash_pointer.value.as_basic_value_enum(),
|
||||
context.integer_const(64, 0).as_basic_value_enum(),
|
||||
context.integer_const(64, 0).as_basic_value_enum(),
|
||||
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||
value_pointer.value.as_basic_value_enum(),
|
||||
input_data_pointer.value.as_basic_value_enum(),
|
||||
input_length.as_basic_value_enum(),
|
||||
address_pointer.value.as_basic_value_enum(),
|
||||
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||
context.sentinel_pointer().value.as_basic_value_enum(),
|
||||
salt_pointer.value.as_basic_value_enum(),
|
||||
];
|
||||
revive_runtime_api::calling_convention::spill(
|
||||
context.builder(),
|
||||
revive_runtime_api::calling_convention::instantiate(context.llvm()),
|
||||
"create2_arguments",
|
||||
)?
|
||||
.next(code_hash_pointer.value)?
|
||||
.skip()
|
||||
.skip()
|
||||
.next(context.sentinel_pointer().value)?
|
||||
.next(value_pointer.value)?
|
||||
.next(input_data_pointer.value)?
|
||||
.next(input_length)?
|
||||
.next(address_pointer.value)?
|
||||
.next(address_length_pointer.value)?
|
||||
.next(context.sentinel_pointer().value)?
|
||||
.next(context.sentinel_pointer().value)?
|
||||
.next(salt_pointer.value)?
|
||||
.next(
|
||||
context
|
||||
.xlen_type()
|
||||
.const_int(revive_common::BYTE_LENGTH_WORD as u64, false),
|
||||
)?
|
||||
.done();
|
||||
argument_pointer.value,
|
||||
argument_type,
|
||||
arguments,
|
||||
)?;
|
||||
|
||||
let argument_pointer = context.builder().build_ptr_to_int(
|
||||
argument_pointer.value,
|
||||
context.xlen_type(),
|
||||
"instantiate_argument_pointer",
|
||||
)?;
|
||||
context.build_runtime_call(
|
||||
runtime_api::imports::INSTANTIATE,
|
||||
&[context
|
||||
.builder()
|
||||
.build_ptr_to_int(argument_pointer, context.xlen_type(), "argument_pointer")?
|
||||
.into()],
|
||||
&[argument_pointer.into()],
|
||||
);
|
||||
|
||||
context.build_load_word(
|
||||
address_pointer,
|
||||
revive_common::BIT_LENGTH_ETH_ADDRESS,
|
||||
"address",
|
||||
)
|
||||
let address = context.build_byte_swap(context.build_load(address_pointer, "address")?)?;
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(
|
||||
address.into_int_value(),
|
||||
context.word_type(),
|
||||
"address_zext",
|
||||
)?
|
||||
.into())
|
||||
}
|
||||
|
||||
/// Translates the contract hash instruction, which is actually used to set the hash of the contract
|
||||
|
||||
@@ -29,11 +29,7 @@ where
|
||||
runtime_api::imports::VALUE_TRANSFERRED,
|
||||
&[output_pointer.to_int(context).into()],
|
||||
);
|
||||
context.build_load_word(
|
||||
output_pointer,
|
||||
revive_common::BIT_LENGTH_VALUE,
|
||||
"value_transferred",
|
||||
)
|
||||
context.build_load(output_pointer, "value_transferred")
|
||||
}
|
||||
|
||||
/// Translates the `balance` instructions.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! Translates the return data instructions.
|
||||
|
||||
use inkwell::values::BasicValue;
|
||||
|
||||
use crate::polkavm::context::Context;
|
||||
use crate::polkavm::Dependency;
|
||||
use crate::polkavm_const::runtime_api;
|
||||
@@ -13,13 +11,17 @@ pub fn size<'ctx, D>(
|
||||
where
|
||||
D: Dependency + Clone,
|
||||
{
|
||||
let value = context
|
||||
.get_global_value(crate::polkavm::GLOBAL_RETURN_DATA_SIZE)?
|
||||
.into_int_value();
|
||||
Ok(context
|
||||
.builder()
|
||||
.build_int_z_extend(value, context.word_type(), "calldatasize_extended")?
|
||||
.as_basic_value_enum())
|
||||
let output_pointer = context.build_alloca_at_entry(context.word_type(), "return_data_size");
|
||||
let output_pointer_parameter = context.builder().build_ptr_to_int(
|
||||
output_pointer.value,
|
||||
context.xlen_type(),
|
||||
"return_data_copy_output_pointer",
|
||||
)?;
|
||||
context.build_runtime_call(
|
||||
runtime_api::imports::RETURNDATASIZE,
|
||||
&[output_pointer_parameter.into()],
|
||||
);
|
||||
context.build_load(output_pointer, "return_data_size_load")
|
||||
}
|
||||
|
||||
/// Translates the return data copy, trapping if
|
||||
@@ -39,16 +41,49 @@ where
|
||||
let destination_offset = context.safe_truncate_int_to_xlen(destination_offset)?;
|
||||
let size = context.safe_truncate_int_to_xlen(size)?;
|
||||
|
||||
let destination_offset = context.builder().build_ptr_to_int(
|
||||
let output_pointer = context.builder().build_ptr_to_int(
|
||||
context.build_heap_gep(destination_offset, size)?.value,
|
||||
context.xlen_type(),
|
||||
"destination_offset",
|
||||
"return_data_copy_output_pointer",
|
||||
)?;
|
||||
|
||||
let output_length_pointer = context.build_alloca_at_entry(
|
||||
context.xlen_type(),
|
||||
"return_data_copy_output_length_pointer",
|
||||
);
|
||||
context.build_store(output_length_pointer, size)?;
|
||||
let output_length_pointer_int = context.builder().build_ptr_to_int(
|
||||
output_length_pointer.value,
|
||||
context.xlen_type(),
|
||||
"return_data_copy_output_length_pointer_int",
|
||||
)?;
|
||||
|
||||
context.build_runtime_call(
|
||||
runtime_api::imports::RETURNDATACOPY,
|
||||
&[destination_offset.into(), source_offset.into(), size.into()],
|
||||
&[
|
||||
output_pointer.into(),
|
||||
output_length_pointer_int.into(),
|
||||
source_offset.into(),
|
||||
],
|
||||
);
|
||||
|
||||
// Trap on OOB (will be different in EOF code)
|
||||
let overflow_block = context.append_basic_block("return_data_overflow");
|
||||
let non_overflow_block = context.append_basic_block("return_data_non_overflow");
|
||||
let is_overflow = context.builder().build_int_compare(
|
||||
inkwell::IntPredicate::UGT,
|
||||
size,
|
||||
context
|
||||
.build_load(output_length_pointer, "bytes_written")?
|
||||
.into_int_value(),
|
||||
"is_overflow",
|
||||
)?;
|
||||
context.build_conditional_branch(is_overflow, overflow_block, non_overflow_block)?;
|
||||
|
||||
context.set_basic_block(overflow_block);
|
||||
context.build_call(context.intrinsics().trap, &[], "invalid_trap");
|
||||
context.build_unreachable();
|
||||
|
||||
context.set_basic_block(non_overflow_block);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user