Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/interpreter/src/gas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,17 @@ pub struct MemoryGas {

impl MemoryGas {
/// Creates a new `MemoryGas` instance with zero memory allocation.
#[inline]
pub const fn new() -> Self {
Self {
words_num: 0,
expansion_cost: 0,
}
}

#[inline]
/// Records a new memory length and calculates additional cost if memory is expanded.
/// Returns the additional gas cost required, or None if no expansion is needed.
#[inline]
pub fn record_new_len(&mut self, new_num: usize) -> Option<u64> {
if new_num <= self.words_num {
return None;
Expand Down
4 changes: 2 additions & 2 deletions crates/interpreter/src/instruction_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use super::Instruction;
/// This struct provides access to both the host interface for external state operations
/// and the interpreter state for stack, memory, and gas operations.
pub struct InstructionContext<'a, H: ?Sized, ITy: InterpreterTypes> {
/// Reference to the host interface for accessing external blockchain state.
pub host: &'a mut H,
/// Reference to the interpreter containing execution state (stack, memory, gas, etc).
pub interpreter: &'a mut Interpreter<ITy>,
/// Reference to the host interface for accessing external blockchain state.
pub host: &'a mut H,
}

impl<H: ?Sized, ITy: InterpreterTypes> std::fmt::Debug for InstructionContext<'_, H, ITy> {
Expand Down
2 changes: 1 addition & 1 deletion crates/interpreter/src/instructions/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ pub fn jumpi<WIRE: InterpreterTypes, H: ?Sized>(context: InstructionContext<'_,
}
}

#[inline(always)]
/// Internal helper function for jump operations.
///
/// Validates jump target and performs the actual jump.
#[inline(always)]
fn jump_inner<WIRE: InterpreterTypes>(interpreter: &mut Interpreter<WIRE>, target: U256) {
let target = as_usize_or_fail!(interpreter, target, InstructionResult::InvalidJump);
if !interpreter.bytecode.is_valid_legacy_jump(target) {
Expand Down
37 changes: 26 additions & 11 deletions crates/interpreter/src/instructions/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,15 @@ macro_rules! resize_memory {
$crate::resize_memory!($interpreter, $offset, $len, ())
};
($interpreter:expr, $offset:expr, $len:expr, $ret:expr) => {
let words_num = $crate::interpreter::num_words($offset.saturating_add($len));
match $interpreter.gas.record_memory_expansion(words_num) {
$crate::gas::MemoryExtensionResult::Extended => {
$interpreter.memory.resize(words_num * 32);
}
$crate::gas::MemoryExtensionResult::OutOfGas => {
$interpreter.halt($crate::InstructionResult::MemoryOOG);
return $ret;
}
$crate::gas::MemoryExtensionResult::Same => (), // no action
};
if !$crate::interpreter::resize_memory(
&mut $interpreter.gas,
&mut $interpreter.memory,
$offset,
$len,
) {
$interpreter.halt($crate::InstructionResult::MemoryOOG);
return $ret;
}
};
}

Expand All @@ -124,14 +122,31 @@ macro_rules! popn {
};
}

#[doc(hidden)]
#[macro_export]
macro_rules! _count {
(@count) => { 0 };
(@count $head:tt $($tail:tt)*) => { 1 + _count!(@count $($tail)*) };
($($arg:tt)*) => { _count!(@count $($arg)*) };
}

/// Pops n values from the stack and returns the top value. Fails the instruction if n values can't be popped.
#[macro_export]
macro_rules! popn_top {
([ $($x:ident),* ], $top:ident, $interpreter:expr $(,$ret:expr)? ) => {
/*
let Some(([$( $x ),*], $top)) = $interpreter.stack.popn_top() else {
$interpreter.halt($crate::InstructionResult::StackUnderflow);
return $($ret)?;
};
*/

// Workaround for https://github.com/rust-lang/rust/issues/144329.
if $interpreter.stack.len() < (1 + $crate::_count!($($x)*)) {
$interpreter.halt($crate::InstructionResult::StackUnderflow);
return $($ret)?;
}
let ([$( $x ),*], $top) = unsafe { $interpreter.stack.popn_top().unwrap_unchecked() };
};
}

Expand Down
10 changes: 5 additions & 5 deletions crates/interpreter/src/instructions/stack.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{
gas,
instructions::utility::cast_slice_to_u256,
interpreter_types::{Immediates, InterpreterTypes, Jumps, RuntimeFlag, StackTr},
InstructionResult,
};
Expand Down Expand Up @@ -33,11 +32,12 @@ pub fn push<const N: usize, WIRE: InterpreterTypes, H: ?Sized>(
context: InstructionContext<'_, H, WIRE>,
) {
gas!(context.interpreter, gas::VERYLOW);
push!(context.interpreter, U256::ZERO);
popn_top!([], top, context.interpreter);

let imm = context.interpreter.bytecode.read_slice(N);
cast_slice_to_u256(imm, top);
let slice = context.interpreter.bytecode.read_slice(N);
if !context.interpreter.stack.push_slice(slice) {
context.interpreter.halt(InstructionResult::StackOverflow);
return;
}

// Can ignore return. as relative N jump is safe operation
context.interpreter.bytecode.relative_jump(N as isize);
Expand Down
65 changes: 0 additions & 65 deletions crates/interpreter/src/instructions/utility.rs
Original file line number Diff line number Diff line change
@@ -1,70 +1,5 @@
use primitives::{Address, B256, U256};

/// Pushes an arbitrary length slice of bytes onto the stack, padding the last word with zeros
/// if necessary.
///
/// # Panics
///
/// Panics if slice is longer than 32 bytes.
#[inline]
pub fn cast_slice_to_u256(slice: &[u8], dest: &mut U256) {
if slice.is_empty() {
return;
}
assert!(slice.len() <= 32, "slice too long");

let n_words = slice.len().div_ceil(32);

// SAFETY: Length checked above.
unsafe {
//let dst = self.data.as_mut_ptr().add(self.data.len()).cast::<u64>();
//self.data.set_len(new_len);
let dst = dest.as_limbs_mut().as_mut_ptr();

let mut i = 0;

// Write full words
let words = slice.chunks_exact(32);
let partial_last_word = words.remainder();
for word in words {
// Note: We unroll `U256::from_be_bytes` here to write directly into the buffer,
// instead of creating a 32 byte array on the stack and then copying it over.
for l in word.rchunks_exact(8) {
dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap()));
i += 1;
}
}

if partial_last_word.is_empty() {
return;
}

// Write limbs of partial last word
let limbs = partial_last_word.rchunks_exact(8);
let partial_last_limb = limbs.remainder();
for l in limbs {
dst.add(i).write(u64::from_be_bytes(l.try_into().unwrap()));
i += 1;
}

// Write partial last limb by padding with zeros
if !partial_last_limb.is_empty() {
let mut tmp = [0u8; 8];
tmp[8 - partial_last_limb.len()..].copy_from_slice(partial_last_limb);
dst.add(i).write(u64::from_be_bytes(tmp));
i += 1;
}

debug_assert_eq!(i.div_ceil(4), n_words, "wrote too much");

// Zero out upper bytes of last word
let m = i % 4; // 32 / 8
if m != 0 {
dst.add(i).write_bytes(0, 4 - m);
}
}
}

/// Trait for converting types into U256 values.
pub trait IntoU256 {
/// Converts the implementing type into a U256 value.
Expand Down
11 changes: 10 additions & 1 deletion crates/interpreter/src/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub use ext_bytecode::ExtBytecode;
pub use input::InputsImpl;
pub use return_data::ReturnDataImpl;
pub use runtime_flags::RuntimeFlags;
pub use shared_memory::{num_words, SharedMemory};
pub use shared_memory::{num_words, resize_memory, SharedMemory};
pub use stack::{Stack, STACK_LIMIT};

// imports
Expand Down Expand Up @@ -183,6 +183,13 @@ impl<EXT> InterpreterTypes for EthInterpreter<EXT> {
}

impl<IW: InterpreterTypes> Interpreter<IW> {
/// Performs EVM memory resize.
#[inline]
#[must_use]
pub fn resize_memory(&mut self, offset: usize, len: usize) -> bool {
resize_memory(&mut self.gas, &mut self.memory, offset, len)
}

/// Takes the next action from the control and returns it.
#[inline]
pub fn take_next_action(&mut self) -> InterpreterAction {
Expand All @@ -193,6 +200,8 @@ impl<IW: InterpreterTypes> Interpreter<IW> {
/// Halt the interpreter with the given result.
///
/// This will set the action to [`InterpreterAction::Return`] and set the gas to the current gas.
#[cold]
#[inline(never)]
pub fn halt(&mut self, result: InstructionResult) {
self.bytecode
.set_action(InterpreterAction::new_halt(result, self.gas));
Expand Down
Loading
Loading