Skip to content

Commit

Permalink
feat: implement MLOAD/MSTORE in IR (#49)
Browse files Browse the repository at this point in the history
* feat: implement MLOAD/MSTORE in IR

* perf: inline less

* feat: better attributes

* chore: cleanup type printing, add is_compile_time_known

* less inline

* no inline
  • Loading branch information
DaniPopes authored Jul 16, 2024
1 parent 719be6b commit fd02a34
Show file tree
Hide file tree
Showing 11 changed files with 570 additions and 175 deletions.
14 changes: 14 additions & 0 deletions crates/revmc-backend/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,16 @@ pub trait Builder: BackendTypes + TypeMethods {
fn add_comment_to_current_inst(&mut self, comment: &str);

fn fn_param(&mut self, index: usize) -> Self::Value;
fn num_fn_params(&self) -> usize;

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 +323,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 @@ -347,8 +355,12 @@ pub trait Builder: BackendTypes + TypeMethods {
name: &str,
) -> Self::Value;

#[must_use]
fn call(&mut self, function: Self::Function, args: &[Self::Value]) -> Option<Self::Value>;

/// Returns `Some(is_value_compile_time)`, or `None` if unsupported.
fn is_compile_time_known(&mut self, value: Self::Value) -> Option<Self::Value>;

fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value);
fn memcpy_inline(&mut self, dst: Self::Value, src: Self::Value, len: i64) {
let len = self.iconst(self.type_int(64), len);
Expand All @@ -368,6 +380,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
24 changes: 24 additions & 0 deletions crates/revmc-cranelift/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ impl<'a> Builder for EvmCraneliftBuilder<'a> {
self.bcx.block_params(block)[index]
}

fn num_fn_params(&self) -> usize {
self.bcx.func.signature.params.len()
}

fn bool_const(&mut self, value: bool) -> Self::Value {
self.iconst(types::I8, value as i64)
}
Expand All @@ -361,6 +365,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 +613,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 @@ -686,6 +698,10 @@ impl<'a> Builder for EvmCraneliftBuilder<'a> {
self.bcx.inst_results(ins).first().copied()
}

fn is_compile_time_known(&mut self, _value: Self::Value) -> Option<Self::Value> {
None
}

fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value) {
let config = self.module.get().target_config();
self.bcx.call_memcpy(config, dst, src, len)
Expand Down Expand Up @@ -749,6 +765,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
Loading

0 comments on commit fd02a34

Please sign in to comment.