disable sbrk and emulate EVM linear memory internally (#76)

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
Cyrill Leutwiler
2024-10-14 15:20:00 +02:00
committed by GitHub
parent 92a18460e4
commit f0f344a139
12 changed files with 115 additions and 122 deletions
@@ -13,6 +13,10 @@ pub mod exports {
}
pub mod imports {
pub static SBRK: &str = "__sbrk_internal";
pub static MEMORY_SIZE: &str = "__msize";
pub static ADDRESS: &str = "address";
pub static BALANCE: &str = "balance";
@@ -57,7 +61,9 @@ pub mod imports {
/// All imported runtime API symbols.
/// Useful for configuring common attributes and linkage.
pub static IMPORTS: [&str; 21] = [
pub static IMPORTS: [&str; 23] = [
SBRK,
MEMORY_SIZE,
ADDRESS,
BALANCE,
BALANCE_OF,
@@ -81,6 +87,3 @@ pub mod imports {
VALUE_TRANSFERRED,
];
}
/// PolkaVM __sbrk API symbol to extend the heap memory.
pub static SBRK: &str = "__sbrk";
@@ -61,7 +61,10 @@ impl Entry {
context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.into(),
context.build_sbrk(context.integer_const(crate::polkavm::XLEN, 0))?,
context.build_sbrk(
context.xlen_type().const_zero(),
context.xlen_type().const_zero(),
)?,
)?;
context.set_global(
+29 -38
View File
@@ -1088,16 +1088,20 @@ where
Ok(truncated)
}
/// Build a call to PolkaVM `sbrk` for extending the heap by `size`.
/// Build a call to PolkaVM `sbrk` for extending the heap from offset by `size`.
/// The allocation is aligned to 32 bytes.
///
/// This emulates the EVM linear memory until the runtime supports metered memory.
pub fn build_sbrk(
&self,
offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
Ok(self
.builder()
.build_call(
self.runtime_api_method(runtime_api::SBRK),
&[size.into()],
self.runtime_api_method(runtime_api::imports::SBRK),
&[offset.into(), size.into()],
"call_sbrk",
)?
.try_as_basic_value()
@@ -1106,14 +1110,29 @@ where
.into_pointer_value())
}
/// Call PolkaVM `sbrk` for extending the heap by `size`,
/// Build a call to PolkaVM `msize` for querying the linear memory size.
pub fn build_msize(&self) -> anyhow::Result<inkwell::values::IntValue<'ctx>> {
Ok(self
.builder()
.build_call(
self.runtime_api_method(runtime_api::imports::MEMORY_SIZE),
&[],
"call_msize",
)?
.try_as_basic_value()
.left()
.expect("sbrk returns an int")
.into_int_value())
}
/// Call PolkaVM `sbrk` for extending the heap by `offset` + `size`,
/// trapping the contract if the call failed.
/// Returns the end of memory pointer.
pub fn build_heap_alloc(
&self,
offset: inkwell::values::IntValue<'ctx>,
size: inkwell::values::IntValue<'ctx>,
) -> anyhow::Result<inkwell::values::PointerValue<'ctx>> {
let end_of_memory = self.build_sbrk(size)?;
) -> anyhow::Result<()> {
let end_of_memory = self.build_sbrk(offset, size)?;
let return_is_nil = self.builder().build_int_compare(
inkwell::IntPredicate::EQ,
end_of_memory,
@@ -1131,7 +1150,7 @@ where
self.set_basic_block(continue_block);
Ok(end_of_memory)
Ok(())
}
/// Returns a pointer to `offset` into the heap, allocating
@@ -1146,40 +1165,12 @@ where
assert_eq!(offset.get_type(), self.xlen_type());
assert_eq!(length.get_type(), self.xlen_type());
self.build_heap_alloc(offset, length)?;
let heap_start = self
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let heap_end = self.build_sbrk(self.integer_const(crate::polkavm::XLEN, 0))?;
let value_end = self.build_gep(
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
&[self.builder().build_int_nuw_add(offset, length, "end")?],
self.byte_type(),
"heap_end_gep",
);
let is_out_of_bounds = self.builder().build_int_compare(
inkwell::IntPredicate::UGT,
value_end.value,
heap_end,
"is_value_overflowing_heap",
)?;
let out_of_bounds_block = self.append_basic_block("heap_offset_out_of_bounds");
let heap_offset_block = self.append_basic_block("build_heap_pointer");
self.build_conditional_branch(is_out_of_bounds, out_of_bounds_block, heap_offset_block)?;
self.set_basic_block(out_of_bounds_block);
let size = self.builder().build_int_nuw_sub(
self.builder()
.build_ptr_to_int(value_end.value, self.xlen_type(), "value_end")?,
self.builder()
.build_ptr_to_int(heap_end, self.xlen_type(), "heap_end")?,
"heap_alloc_size",
)?;
self.build_heap_alloc(size)?;
self.build_unconditional_branch(heap_offset_block);
self.set_basic_block(heap_offset_block);
Ok(self.build_gep(
Pointer::new(self.byte_type(), AddressSpace::Stack, heap_start),
&[offset],
@@ -107,33 +107,6 @@ where
Ok(context.word_const(0).as_basic_value_enum())
}
/// Translates the `msize` instruction.
pub fn msize<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
let heap_end = context.build_sbrk(context.xlen_type().const_zero())?;
let heap_start = context
.get_global(crate::polkavm::GLOBAL_HEAP_MEMORY_POINTER)?
.value
.as_pointer_value();
let heap_size = context.builder().build_int_nuw_sub(
context
.builder()
.build_ptr_to_int(heap_end, context.xlen_type(), "heap_end")?,
context
.builder()
.build_ptr_to_int(heap_start, context.xlen_type(), "heap_start")?,
"heap_size",
)?;
Ok(context
.builder()
.build_int_z_extend(heap_size, context.word_type(), "heap_size_extended")?
.as_basic_value_enum())
}
/// Translates the `address` instruction.
pub fn address<'ctx, D>(
context: &mut Context<'ctx, D>,
@@ -1,10 +1,29 @@
//! Translates the heap memory operations.
use inkwell::values::BasicValue;
use crate::polkavm::context::address_space::AddressSpace;
use crate::polkavm::context::pointer::Pointer;
use crate::polkavm::context::Context;
use crate::polkavm::Dependency;
/// Translates the `msize` instruction.
pub fn msize<'ctx, D>(
context: &mut Context<'ctx, D>,
) -> anyhow::Result<inkwell::values::BasicValueEnum<'ctx>>
where
D: Dependency + Clone,
{
Ok(context
.builder()
.build_int_z_extend(
context.build_msize()?,
context.word_type(),
"heap_size_extended",
)?
.as_basic_value_enum())
}
/// Translates the `mload` instruction.
/// Uses the main heap.
pub fn load<'ctx, D>(