Skip to content

Commit

Permalink
feat: implement MLOAD/MSTORE in IR
Browse files Browse the repository at this point in the history
  • Loading branch information
DaniPopes committed Jul 11, 2024
1 parent 719be6b commit 5485d4d
Show file tree
Hide file tree
Showing 11 changed files with 415 additions and 128 deletions.
9 changes: 9 additions & 0 deletions crates/revmc-backend/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,13 @@ pub trait Builder: BackendTypes + TypeMethods {
fn fn_param(&mut self, index: usize) -> Self::Value;

fn bool_const(&mut self, value: bool) -> Self::Value;
/// Sign-extends negative values to `ty`.
fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value;
fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value;
fn iconst_256(&mut self, value: U256) -> Self::Value;
fn cstr_const(&mut self, value: &std::ffi::CStr) -> Self::Value {
self.str_const(value.to_str().unwrap())
}
fn str_const(&mut self, value: &str) -> Self::Value;

fn new_stack_slot(&mut self, ty: Self::Type, name: &str) -> Pointer<Self> {
Expand Down Expand Up @@ -317,6 +322,8 @@ pub trait Builder: BackendTypes + TypeMethods {
fn uadd_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);
fn usub_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value);

fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;

fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn umin(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
fn bswap(&mut self, value: Self::Value) -> Self::Value;
Expand Down Expand Up @@ -368,6 +375,8 @@ pub trait Builder: BackendTypes + TypeMethods {

fn get_function(&mut self, name: &str) -> Option<Self::Function>;

fn get_printf_function(&mut self) -> Self::Function;

/// Adds a function to the module that's located at `address`.
///
/// If `address` is `None`, the function must be built.
Expand Down
6 changes: 3 additions & 3 deletions crates/revmc-builtins/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ macro_rules! builtins {
const PANIC: u8 = 0;
const LOG: u8 = LOG0;
const DORETURN: u8 = RETURN;
const RESIZEMEMORY: u8 = 0;

match self {
$(Self::$ident => [<$ident:upper>]),*
Expand Down Expand Up @@ -231,9 +232,6 @@ builtins! {
SelfBalance = __revmc_builtin_self_balance(@[ecx] ptr, @[sp] ptr) Some(u8),
BlobHash = __revmc_builtin_blob_hash(@[ecx] ptr, @[sp] ptr) None,
BlobBaseFee = __revmc_builtin_blob_base_fee(@[ecx] ptr, @[sp] ptr) None,
Mload = __revmc_builtin_mload(@[ecx] ptr, @[sp] ptr) Some(u8),
Mstore = __revmc_builtin_mstore(@[ecx] ptr, @[sp] ptr) Some(u8),
Mstore8 = __revmc_builtin_mstore8(@[ecx] ptr, @[sp] ptr) Some(u8),
Sload = __revmc_builtin_sload(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
Sstore = __revmc_builtin_sstore(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
Msize = __revmc_builtin_msize(@[ecx] ptr) Some(usize),
Expand All @@ -246,4 +244,6 @@ builtins! {
Call = __revmc_builtin_call(@[ecx] ptr, @[sp_dyn] ptr, u8, u8) Some(u8),
DoReturn = __revmc_builtin_do_return(@[ecx] ptr, @[sp] ptr, u8) Some(u8),
SelfDestruct = __revmc_builtin_selfdestruct(@[ecx] ptr, @[sp] ptr, u8) Some(u8),

ResizeMemory = __revmc_builtin_resize_memory(@[ecx] ptr, usize) Some(u8),
}
59 changes: 17 additions & 42 deletions crates/revmc-builtins/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ pub unsafe extern "C" fn __revmc_builtin_keccak256(
} else {
gas_opt!(ecx, gas::dyn_keccak256_cost(len as u64));
let offset = try_into_usize!(offset);
resize_memory!(ecx, offset, len);
ensure_memory!(ecx, offset, len);
let data = ecx.memory.slice(offset, len);
revm_primitives::keccak256(data).0
});
Expand Down Expand Up @@ -208,7 +208,7 @@ pub unsafe extern "C" fn __revmc_builtin_extcodecopy(
let memory_offset = try_into_usize!(memory_offset);
let code_offset = code_offset.to_u256();
let code_offset = as_usize_saturated!(code_offset).min(code.len());
resize_memory!(ecx, memory_offset, len);
ensure_memory!(ecx, memory_offset, len);
ecx.memory.set_data(memory_offset, code_offset, len, &code);
}
InstructionResult::Continue
Expand All @@ -229,7 +229,7 @@ pub unsafe extern "C" fn __revmc_builtin_returndatacopy(
}
if len != 0 {
let memory_offset = try_into_usize!(memory_offset);
resize_memory!(ecx, memory_offset, len);
ensure_memory!(ecx, memory_offset, len);
ecx.memory.set(memory_offset, &ecx.return_data[data_offset..data_end]);
}
InstructionResult::Continue
Expand Down Expand Up @@ -317,39 +317,6 @@ pub unsafe extern "C" fn __revmc_builtin_blob_base_fee(
*slot = ecx.host.env().block.get_blob_gasprice().unwrap_or_default().into();
}

#[no_mangle]
pub unsafe extern "C" fn __revmc_builtin_mload(
ecx: &mut EvmContext<'_>,
offset_ptr: &mut EvmWord,
) -> InstructionResult {
let offset = try_into_usize!(offset_ptr);
resize_memory!(ecx, offset, 32);
*offset_ptr = EvmWord::from_be_bytes(ecx.memory.get_word(offset).0);
InstructionResult::Continue
}

#[no_mangle]
pub unsafe extern "C" fn __revmc_builtin_mstore(
ecx: &mut EvmContext<'_>,
rev![offset, value]: &mut [EvmWord; 2],
) -> InstructionResult {
let offset = try_into_usize!(offset);
resize_memory!(ecx, offset, 32);
ecx.memory.set(offset, &value.to_be_bytes());
InstructionResult::Continue
}

#[no_mangle]
pub unsafe extern "C" fn __revmc_builtin_mstore8(
ecx: &mut EvmContext<'_>,
rev![offset, value]: &mut [EvmWord; 2],
) -> InstructionResult {
let offset = try_into_usize!(offset);
resize_memory!(ecx, offset, 1);
ecx.memory.set_byte(offset, value.to_u256().byte(0));
InstructionResult::Continue
}

#[no_mangle]
pub unsafe extern "C" fn __revmc_builtin_sload(
ecx: &mut EvmContext<'_>,
Expand Down Expand Up @@ -405,7 +372,7 @@ pub unsafe extern "C" fn __revmc_builtin_mcopy(
if len != 0 {
let dst = try_into_usize!(dst);
let src = try_into_usize!(src);
resize_memory!(ecx, dst.max(src), len);
ensure_memory!(ecx, dst.max(src), len);
ecx.memory.copy(dst, src, len);
}
InstructionResult::Continue
Expand All @@ -424,7 +391,7 @@ pub unsafe extern "C" fn __revmc_builtin_log(
gas_opt!(ecx, gas::dyn_log_cost(len as u64));
let data = if len != 0 {
let offset = try_into_usize!(offset);
resize_memory!(ecx, offset, len);
ensure_memory!(ecx, offset, len);
Bytes::copy_from_slice(ecx.memory.slice(offset, len))
} else {
Bytes::new()
Expand Down Expand Up @@ -476,7 +443,7 @@ pub unsafe extern "C" fn __revmc_builtin_create(
}

let code_offset = try_into_usize!(code_offset);
resize_memory!(ecx, code_offset, len);
ensure_memory!(ecx, code_offset, len);
Bytes::copy_from_slice(ecx.memory.slice(code_offset, len))
} else {
Bytes::new()
Expand Down Expand Up @@ -550,7 +517,7 @@ pub unsafe extern "C" fn __revmc_builtin_call(
let in_len = try_into_usize!(in_len);
let input = if in_len != 0 {
let in_offset = try_into_usize!(in_offset);
resize_memory!(ecx, in_offset, in_len);
ensure_memory!(ecx, in_offset, in_len);
Bytes::copy_from_slice(ecx.memory.slice(in_offset, in_len))
} else {
Bytes::new()
Expand All @@ -559,7 +526,7 @@ pub unsafe extern "C" fn __revmc_builtin_call(
let out_len = try_into_usize!(out_len);
let out_offset = if out_len != 0 {
let out_offset = try_into_usize!(out_offset);
resize_memory!(ecx, out_offset, out_len);
ensure_memory!(ecx, out_offset, out_len);
out_offset
} else {
usize::MAX // unrealistic value so we are sure it is not used
Expand Down Expand Up @@ -629,7 +596,7 @@ pub unsafe extern "C" fn __revmc_builtin_do_return(
let len = try_into_usize!(len);
let output = if len != 0 {
let offset = try_into_usize!(offset);
resize_memory!(ecx, offset, len);
ensure_memory!(ecx, offset, len);
ecx.memory.slice(offset, len).to_vec().into()
} else {
Bytes::new()
Expand All @@ -655,3 +622,11 @@ pub unsafe extern "C" fn __revmc_builtin_selfdestruct(

InstructionResult::Continue
}

#[no_mangle]
pub unsafe extern "C" fn __revmc_builtin_resize_memory(
ecx: &mut EvmContext<'_>,
new_size: usize,
) -> InstructionResult {
resize_memory(ecx, new_size)
}
4 changes: 2 additions & 2 deletions crates/revmc-builtins/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ macro_rules! gas_opt {
};
}

macro_rules! resize_memory {
macro_rules! ensure_memory {
($ecx:expr, $offset:expr, $len:expr) => {
match resize_memory($ecx, $offset, $len) {
match ensure_memory($ecx, $offset, $len) {
InstructionResult::Continue => {}
ir => return ir,
}
Expand Down
21 changes: 13 additions & 8 deletions crates/revmc-builtins/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@ pub(crate) unsafe fn read_words_rev<'a, const N: usize>(sp: *mut EvmWord) -> &'a
}

#[inline]
pub(crate) fn resize_memory(
pub(crate) fn ensure_memory(
ecx: &mut EvmContext<'_>,
offset: usize,
len: usize,
) -> InstructionResult {
let size = offset.saturating_add(len);
if size > ecx.memory.len() {
// TODO: Memory limit
if !revm_interpreter::interpreter::resize_memory(ecx.memory, ecx.gas, size) {
return InstructionResult::MemoryOOG;
}
let new_size = offset.saturating_add(len);
if new_size > ecx.memory.len() {
return resize_memory(ecx, new_size);
}
InstructionResult::Continue
}

pub(crate) fn resize_memory(ecx: &mut EvmContext<'_>, new_size: usize) -> InstructionResult {
// TODO: Memory limit
if !revm_interpreter::interpreter::resize_memory(ecx.memory, ecx.gas, new_size) {
return InstructionResult::MemoryOOG;
}
InstructionResult::Continue
}
Expand All @@ -42,7 +47,7 @@ pub(crate) unsafe fn copy_operation(
if len != 0 {
gas_opt!(ecx, gas::dyn_verylowcopy_cost(len as u64));
let memory_offset = try_into_usize!(memory_offset);
resize_memory!(ecx, memory_offset, len);
ensure_memory!(ecx, memory_offset, len);
let data_offset = data_offset.to_u256();
let data_offset = as_usize_saturated!(data_offset);
ecx.memory.set_data(memory_offset, data_offset, len, data);
Expand Down
16 changes: 16 additions & 0 deletions crates/revmc-cranelift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ impl<'a> Builder for EvmCraneliftBuilder<'a> {
self.bcx.ins().iconst(ty, value)
}

fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value {
self.iconst(ty, value as i64)
}

fn iconst_256(&mut self, value: U256) -> Self::Value {
let _ = value;
todo!("no i256 :(")
Expand Down Expand Up @@ -605,6 +609,10 @@ impl<'a> Builder for EvmCraneliftBuilder<'a> {
self.bcx.ins().usub_overflow(lhs, rhs)
}

fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
self.bcx.ins().uadd_sat(lhs, rhs)
}

fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
self.bcx.ins().umax(lhs, rhs)
}
Expand Down Expand Up @@ -749,6 +757,14 @@ impl<'a> Builder for EvmCraneliftBuilder<'a> {
.map(|id| self.module.get_mut().declare_func_in_func(id, self.bcx.func))
}

fn get_printf_function(&mut self) -> Self::Function {
if let Some(f) = self.get_function("printf") {
return f;
}

unimplemented!()
}

fn add_function(
&mut self,
name: &str,
Expand Down
26 changes: 25 additions & 1 deletion crates/revmc-llvm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,12 @@ impl<'a, 'ctx> EvmLlvmBuilder<'a, 'ctx> {
})
}

fn get_sat_function(&mut self, name: &str, ty: BasicTypeEnum<'ctx>) -> FunctionValue<'ctx> {
let bits = ty.into_int_type().get_bit_width();
let name = format!("llvm.{name}.sat.i{bits}");
self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]))
}

fn get_or_add_function(
&mut self,
name: &str,
Expand Down Expand Up @@ -626,10 +632,13 @@ impl<'a, 'ctx> Builder for EvmLlvmBuilder<'a, 'ctx> {
}

fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value {
// TODO: sign extend?
ty.into_int_type().const_int(value as u64, value.is_negative()).into()
}

fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value {
ty.into_int_type().const_int(value, false).into()
}

fn iconst_256(&mut self, value: U256) -> Self::Value {
if value == U256::ZERO {
return self.ty_i256.const_zero().into();
Expand Down Expand Up @@ -869,6 +878,11 @@ impl<'a, 'ctx> Builder for EvmLlvmBuilder<'a, 'ctx> {
(result, overflow)
}

fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
let f = self.get_sat_function("uadd", lhs.get_type());
self.call(f, &[lhs, rhs]).unwrap()
}

fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
let ty = lhs.get_type();
let bits = ty.into_int_type().get_bit_width();
Expand Down Expand Up @@ -1021,6 +1035,16 @@ impl<'a, 'ctx> Builder for EvmLlvmBuilder<'a, 'ctx> {
self.module.get_function(name)
}

fn get_printf_function(&mut self) -> Self::Function {
let name = "printf";
if let Some(function) = self.module.get_function(name) {
return function;
}

let ty = self.cx.void_type().fn_type(&[self.ty_ptr.into()], true);
self.module.add_function(name, ty, Some(inkwell::module::Linkage::External))
}

fn add_function(
&mut self,
name: &str,
Expand Down
4 changes: 1 addition & 3 deletions crates/revmc/src/compiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ use std::{
// emitted.
// Use this when `stack` is passed in arguments.

// TODO: Implement memory instructions in IR rather than as builtins.

// TODO: Expose target configuration, and get rid of `cfg!(target_endian)` calls.
// TODO: Get rid of `cfg!(target_endian)` calls.

// TODO: Test on big-endian hardware.
// It probably doesn't work when loading Rust U256 into native endianness.
Expand Down
Loading

0 comments on commit 5485d4d

Please sign in to comment.