implement block.number and block.timestamp

Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2024-05-07 18:03:17 +02:00
parent a7318f2ef6
commit 95ff85c6d1
20 changed files with 295 additions and 83 deletions
+7 -1
View File
@@ -43,7 +43,13 @@ pub static GLOBAL_CONST_ARRAY_PREFIX: &str = "const_array_";
pub static GLOBAL_VERBATIM_GETTER_PREFIX: &str = "get_global::";
/// The static word size.
pub static GLOBAL_WORD_SIZE: &str = "word_size";
pub static GLOBAL_I256_SIZE: &str = "i256_size";
/// The static value size.
pub static GLOBAL_I128_SIZE: &str = "i128_size";
/// The static i64 size.
pub static GLOBAL_I64_SIZE: &str = "i64_size";
/// The external call data offset in the auxiliary heap.
pub const HEAP_AUX_OFFSET_EXTERNAL_CALL: u64 = 0;
@@ -10,12 +10,16 @@ pub static DEPLOY: &str = "deploy";
/// Useful for configuring common attributes and linkage.
pub static EXPORTS: [&str; 2] = [CALL, DEPLOY];
pub static BLOCK_NUMBER: &str = "block_number";
pub static GET_STORAGE: &str = "get_storage";
pub static HASH_KECCAK_256: &str = "hash_keccak_256";
pub static INPUT: &str = "input";
pub static NOW: &str = "now";
pub static RETURN: &str = "seal_return";
pub static SET_STORAGE: &str = "set_storage";
@@ -39,7 +39,7 @@ impl<'ctx> Intrinsics<'ctx> {
) -> Self {
let void_type = llvm.void_type();
let bool_type = llvm.bool_type();
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
let _stack_field_pointer_type = llvm.ptr_type(AddressSpace::Stack.into());
let heap_field_pointer_type = llvm.ptr_type(AddressSpace::Heap.into());
let generic_byte_pointer_type = llvm.ptr_type(AddressSpace::Generic.into());
@@ -58,7 +58,7 @@ impl<'ctx> Intrinsics<'ctx> {
&[
heap_field_pointer_type.as_basic_type_enum().into(),
heap_field_pointer_type.as_basic_type_enum().into(),
field_type.as_basic_type_enum().into(),
word_type.as_basic_type_enum().into(),
bool_type.as_basic_type_enum().into(),
],
false,
@@ -72,7 +72,7 @@ impl<'ctx> Intrinsics<'ctx> {
&[
heap_field_pointer_type.as_basic_type_enum().into(),
generic_byte_pointer_type.as_basic_type_enum().into(),
field_type.as_basic_type_enum().into(),
word_type.as_basic_type_enum().into(),
bool_type.as_basic_type_enum().into(),
],
false,
@@ -82,7 +82,7 @@ impl<'ctx> Intrinsics<'ctx> {
llvm,
module,
Self::FUNCTION_BYTE_SWAP,
field_type.fn_type(&[field_type.as_basic_type_enum().into()], false),
word_type.fn_type(&[word_type.as_basic_type_enum().into()], false),
);
Self {
@@ -114,7 +114,7 @@ impl<'ctx> Intrinsics<'ctx> {
llvm: &'ctx inkwell::context::Context,
name: &str,
) -> Vec<inkwell::types::BasicTypeEnum<'ctx>> {
let field_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
let word_type = llvm.custom_width_int_type(revive_common::BIT_LENGTH_WORD as u32);
match name {
name if name == Self::FUNCTION_MEMORY_COPY => vec![
@@ -122,16 +122,16 @@ impl<'ctx> Intrinsics<'ctx> {
.as_basic_type_enum(),
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
field_type.as_basic_type_enum(),
word_type.as_basic_type_enum(),
],
name if name == Self::FUNCTION_MEMORY_COPY_FROM_GENERIC => vec![
llvm.ptr_type(AddressSpace::Heap.into())
.as_basic_type_enum(),
llvm.ptr_type(AddressSpace::Generic.into())
.as_basic_type_enum(),
field_type.as_basic_type_enum(),
word_type.as_basic_type_enum(),
],
name if name == Self::FUNCTION_BYTE_SWAP => vec![field_type.as_basic_type_enum()],
name if name == Self::FUNCTION_BYTE_SWAP => vec![word_type.as_basic_type_enum()],
_ => vec![],
}
}
@@ -85,10 +85,30 @@ impl Entry {
);
context.set_global(
crate::polkavm::GLOBAL_WORD_SIZE,
crate::polkavm::GLOBAL_I256_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_WORD as u64),
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 4,
),
);
context.set_global(
crate::polkavm::GLOBAL_I128_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_X64 as u64 * 2,
),
);
context.set_global(
crate::polkavm::GLOBAL_I64_SIZE,
context.xlen_type(),
AddressSpace::Stack,
context.integer_const(crate::polkavm::XLEN, revive_common::BYTE_LENGTH_X64 as u64),
);
Ok(())
+54 -17
View File
@@ -610,16 +610,45 @@ where
Pointer::new(r#type, AddressSpace::Stack, pointer)
}
/// Allocates a word-sized byte buffer on the stack.
/// Allocate an int of size `bit_length` on the stack.
/// Returns the allocation pointer and the length pointer.
pub fn build_stack_word_parameter(&self, name: &str) -> (Pointer<'ctx>, Pointer<'ctx>) {
let buffer_pointer = self.build_alloca(self.word_type(), name);
let length_pointer = self
.get_global(GLOBAL_WORD_SIZE)
.expect("should be declared");
///
/// Useful helper for passing runtime API parameters on the stack.
pub fn build_stack_parameter(
&self,
bit_length: usize,
name: &str,
) -> (Pointer<'ctx>, Pointer<'ctx>) {
let buffer_pointer = self.build_alloca(self.integer_type(bit_length), name);
let symbol = match bit_length {
256 => GLOBAL_I256_SIZE,
128 => GLOBAL_I128_SIZE,
64 => GLOBAL_I64_SIZE,
_ => unreachable!("invalid stack parameter bit width: {bit_length}"),
};
let length_pointer = self.get_global(symbol).expect("should be declared");
(buffer_pointer, length_pointer.into())
}
/// Load the integer at given pointer and zero extend it to the VM word size.
pub fn build_load_word(
&self,
pointer: Pointer<'ctx>,
bit_length: usize,
name: &str,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
let value = self.build_load(
pointer.cast(self.integer_type(bit_length)),
&format!("load_{name}"),
)?;
let value_extended = self.builder().build_int_z_extend(
value.into_int_value(),
self.word_type(),
&format!("zext_{name}"),
)?;
Ok(value_extended.as_basic_value_enum())
}
/// Builds a stack load instruction.
/// Sets the alignment to 256 bits for the stack and 1 bit for the heap, parent, and child.
pub fn build_load(
@@ -651,7 +680,7 @@ where
.set_alignment(revive_common::BYTE_LENGTH_BYTE as u32)
.expect("Alignment is valid");
Ok(self.build_byte_swap(value))
self.build_byte_swap(value)
}
AddressSpace::TransientStorage => todo!(),
AddressSpace::Storage => {
@@ -670,8 +699,8 @@ where
self.builder()
.build_store(storage_key_pointer.value, storage_key_value)?;
let (storage_value_pointer, storage_value_length_pointer) =
self.build_stack_word_parameter("storage_value_pointer");
let (storage_value_pointer, storage_value_length_pointer) = self
.build_stack_parameter(revive_common::BIT_LENGTH_WORD, "storage_value_pointer");
self.build_runtime_call(
runtime_api::GET_STORAGE,
@@ -688,13 +717,13 @@ where
// If a key doesn't exist the "zero" value is returned.
self.build_load(storage_value_pointer, "storage_value_load")
.map(|value| self.build_byte_swap(value))
.and_then(|value| self.build_byte_swap(value))
}
AddressSpace::Code | AddressSpace::HeapAuxiliary => todo!(),
AddressSpace::Generic => Ok(self.build_byte_swap(self.build_load(
pointer.address_space_cast(self, AddressSpace::Stack, &format!("{}_cast", name))?,
name,
)?)),
)?)?),
AddressSpace::Stack => {
let value = self
.builder()
@@ -735,7 +764,7 @@ where
let value = value.as_basic_value_enum();
let value = match value.get_type().into_int_type().get_bit_width() as usize {
revive_common::BIT_LENGTH_WORD => self.build_byte_swap(value),
revive_common::BIT_LENGTH_WORD => self.build_byte_swap(value)?,
revive_common::BIT_LENGTH_BYTE => value,
_ => unreachable!("Only word and byte sized values can be stored on EVM heap"),
};
@@ -756,7 +785,7 @@ where
self.build_alloca(storage_key_value.get_type(), "storage_key");
let storage_value_value = self
.build_byte_swap(value.as_basic_value_enum())
.build_byte_swap(value.as_basic_value_enum())?
.into_int_value();
let storage_value_pointer =
self.build_alloca(storage_value_value.get_type(), "storage_value");
@@ -790,7 +819,7 @@ where
AddressSpace::Code | AddressSpace::HeapAuxiliary => {}
AddressSpace::Generic => self.build_store(
pointer.address_space_cast(self, AddressSpace::Stack, "cast")?,
self.build_byte_swap(value.as_basic_value_enum()),
self.build_byte_swap(value.as_basic_value_enum())?,
)?,
AddressSpace::Stack => {
let instruction = self.builder.build_store(pointer.value, value).unwrap();
@@ -807,9 +836,17 @@ where
pub fn build_byte_swap(
&self,
value: inkwell::values::BasicValueEnum<'ctx>,
) -> inkwell::values::BasicValueEnum<'ctx> {
self.build_call(self.intrinsics().byte_swap, &[value], "call_byte_swap")
.expect("byte_swap should return a value")
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>> {
Ok(self
.builder()
.build_call(
self.intrinsics().byte_swap.value,
&[value.into()],
"call_byte_swap",
)?
.try_as_basic_value()
.left()
.unwrap())
}
/// Builds a GEP instruction.
@@ -26,7 +26,7 @@ where
);
context
.build_load(offset, "calldata_value")
.map(|value| context.build_byte_swap(value))
.and_then(|value| context.build_byte_swap(value))
}
/// Translates the calldata size.
+35 -4
View File
@@ -4,6 +4,7 @@ use inkwell::values::BasicValue;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
use crate::polkavm_const::runtime_api;
/// Translates the `gas_limit` instruction.
pub fn gas_limit<'ctx, D>(
@@ -47,22 +48,52 @@ where
/// Translates the `block_number` instruction.
pub fn block_number<'ctx, D>(
_context: &mut Context<'ctx, D>,
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let (output_pointer, output_length_pointer) = context.build_stack_parameter(
revive_common::BIT_LENGTH_BLOCK_NUMBER,
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::BLOCK_NUMBER,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_BLOCK_NUMBER,
"block_number",
)
}
/// Translates the `block_timestamp` instruction.
pub fn block_timestamp<'ctx, D>(
_context: &mut Context<'ctx, D>,
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
todo!()
let (output_pointer, output_length_pointer) = context.build_stack_parameter(
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
"block_timestamp_output",
);
context.build_runtime_call(
runtime_api::NOW,
&[
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_BLOCK_TIMESTAMP,
"block_timestamp",
)
}
/// Translates the `block_hash` instruction.
@@ -27,5 +27,5 @@ where
],
);
Ok(context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?))
context.build_byte_swap(context.build_load(output_pointer, "sha3_output")?)
}
@@ -23,42 +23,20 @@ pub fn value<'ctx, D>(
where
D: Dependency + Clone,
{
let output_pointer = context.build_alloca(context.value_type(), "output_pointer");
let output_pointer_casted = context.builder().build_ptr_to_int(
output_pointer.value,
context.xlen_type(),
"output_pointer_casted",
)?;
let output_length_pointer = context.build_alloca(context.xlen_type(), "output_len_pointer");
let output_length_pointer_casted = context.builder().build_ptr_to_int(
output_length_pointer.value,
context.xlen_type(),
"output_pointer_casted",
)?;
context.build_store(
output_length_pointer,
context.integer_const(
crate::polkavm::XLEN,
revive_common::BYTE_LENGTH_VALUE as u64,
),
)?;
let (output_pointer, output_length_pointer) =
context.build_stack_parameter(revive_common::BIT_LENGTH_VALUE, "value_transferred_output");
context.build_runtime_call(
runtime_api::VALUE_TRANSFERRED,
&[
output_pointer_casted.into(),
output_length_pointer_casted.into(),
output_pointer.to_int(context).into(),
output_length_pointer.to_int(context).into(),
],
);
let value = context.build_load(output_pointer, "transferred_value")?;
let value_extended = context.builder().build_int_z_extend(
value.into_int_value(),
context.word_type(),
"transferred_value_extended",
)?;
Ok(value_extended.as_basic_value_enum())
context.build_load_word(
output_pointer,
revive_common::BIT_LENGTH_VALUE,
"value_transferred",
)
}
/// Translates the `balance` instructions.