Skip to content

Commit

Permalink
perf: refactor interpreter internals and cleanup (bluealloy#582)
Browse files Browse the repository at this point in the history
* perf: refactor interpreter internals (take 2)

* perf: cast instruction functions to `fn`

The compiler generates much more favorable assembly if all the
functions are casted to a `fn(_, _)` pointer before calling them.

See <bluealloy#310 (comment)>
for more information.

* chore: remove prelude

* perf: remove stack and memory bound checks on release

* chore: re-add and deprecate `Memory::get_slice`

* readd BLOBHASH

* fix: TSTORE and TLOAD order

* some cleanup

* nits
  • Loading branch information
DaniPopes authored Sep 20, 2023
1 parent 47f121e commit d926728
Show file tree
Hide file tree
Showing 21 changed files with 1,281 additions and 1,540 deletions.
8 changes: 4 additions & 4 deletions crates/interpreter/src/instruction_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ pub enum InstructionResult {

// error codes
OutOfGas = 0x50,
MemoryOOG = 0x51,
MemoryLimitOOG = 0x52,
PrecompileOOG = 0x53,
InvalidOperandOOG = 0x54,
MemoryOOG,
MemoryLimitOOG,
PrecompileOOG,
InvalidOperandOOG,
OpcodeNotFound,
CallNotAllowedInsideStatic,
StateChangeDuringStaticCall,
Expand Down
194 changes: 12 additions & 182 deletions crates/interpreter/src/instructions.rs
Original file line number Diff line number Diff line change
@@ -1,185 +1,15 @@
#[macro_use]
mod macros;

mod arithmetic;
mod bitwise;
mod control;
mod host;
mod host_env;
mod i256;
mod memory;
pub mod macros;

pub mod arithmetic;
pub mod bitwise;
pub mod control;
pub mod host;
pub mod host_env;
pub mod i256;
pub mod memory;
pub mod opcode;
mod stack;
mod system;

use crate::{interpreter::Interpreter, primitives::Spec, Host};
pub use opcode::{OpCode, OPCODE_JUMPMAP};

pub use crate::{return_ok, return_revert, InstructionResult};
pub fn return_stop(interpreter: &mut Interpreter, _host: &mut dyn Host) {
interpreter.instruction_result = InstructionResult::Stop;
}
pub fn return_invalid(interpreter: &mut Interpreter, _host: &mut dyn Host) {
interpreter.instruction_result = InstructionResult::InvalidFEOpcode;
}

pub fn return_not_found(interpreter: &mut Interpreter, _host: &mut dyn Host) {
interpreter.instruction_result = InstructionResult::OpcodeNotFound;
}

#[inline(always)]
pub fn eval<H: Host, S: Spec>(opcode: u8, interp: &mut Interpreter, host: &mut H) {
match opcode {
opcode::STOP => return_stop(interp, host),
opcode::ADD => arithmetic::wrapped_add(interp, host),
opcode::MUL => arithmetic::wrapping_mul(interp, host),
opcode::SUB => arithmetic::wrapping_sub(interp, host),
opcode::DIV => arithmetic::div(interp, host),
opcode::SDIV => arithmetic::sdiv(interp, host),
opcode::MOD => arithmetic::rem(interp, host),
opcode::SMOD => arithmetic::smod(interp, host),
opcode::ADDMOD => arithmetic::addmod(interp, host),
opcode::MULMOD => arithmetic::mulmod(interp, host),
opcode::EXP => arithmetic::eval_exp::<S>(interp, host),
opcode::SIGNEXTEND => arithmetic::signextend(interp, host),
opcode::LT => bitwise::lt(interp, host),
opcode::GT => bitwise::gt(interp, host),
opcode::SLT => bitwise::slt(interp, host),
opcode::SGT => bitwise::sgt(interp, host),
opcode::EQ => bitwise::eq(interp, host),
opcode::ISZERO => bitwise::iszero(interp, host),
opcode::AND => bitwise::bitand(interp, host),
opcode::OR => bitwise::bitor(interp, host),
opcode::XOR => bitwise::bitxor(interp, host),
opcode::NOT => bitwise::not(interp, host),
opcode::BYTE => bitwise::byte(interp, host),
opcode::SHL => bitwise::shl::<S>(interp, host),
opcode::SHR => bitwise::shr::<S>(interp, host),
opcode::SAR => bitwise::sar::<S>(interp, host),
opcode::KECCAK256 => system::calculate_keccak256(interp, host),
opcode::ADDRESS => system::address(interp, host),
opcode::BALANCE => host::balance::<S>(interp, host),
opcode::SELFBALANCE => host::selfbalance::<S>(interp, host),
opcode::BLOBHASH => host_env::blob_hash::<S>(interp, host),
opcode::CODESIZE => system::codesize(interp, host),
opcode::CODECOPY => system::codecopy(interp, host),
opcode::CALLDATALOAD => system::calldataload(interp, host),
opcode::CALLDATASIZE => system::calldatasize(interp, host),
opcode::CALLDATACOPY => system::calldatacopy(interp, host),
opcode::POP => stack::pop(interp, host),
opcode::MLOAD => memory::mload(interp, host),
opcode::MSTORE => memory::mstore(interp, host),
opcode::MSTORE8 => memory::mstore8(interp, host),
opcode::JUMP => control::jump(interp, host),
opcode::JUMPI => control::jumpi(interp, host),
opcode::PC => control::pc(interp, host),
opcode::MSIZE => memory::msize(interp, host),
opcode::JUMPDEST => control::jumpdest(interp, host),
opcode::PUSH0 => stack::push0::<S>(interp, host),
opcode::PUSH1 => stack::push::<1>(interp, host),
opcode::PUSH2 => stack::push::<2>(interp, host),
opcode::PUSH3 => stack::push::<3>(interp, host),
opcode::PUSH4 => stack::push::<4>(interp, host),
opcode::PUSH5 => stack::push::<5>(interp, host),
opcode::PUSH6 => stack::push::<6>(interp, host),
opcode::PUSH7 => stack::push::<7>(interp, host),
opcode::PUSH8 => stack::push::<8>(interp, host),
opcode::PUSH9 => stack::push::<9>(interp, host),
opcode::PUSH10 => stack::push::<10>(interp, host),
opcode::PUSH11 => stack::push::<11>(interp, host),
opcode::PUSH12 => stack::push::<12>(interp, host),
opcode::PUSH13 => stack::push::<13>(interp, host),
opcode::PUSH14 => stack::push::<14>(interp, host),
opcode::PUSH15 => stack::push::<15>(interp, host),
opcode::PUSH16 => stack::push::<16>(interp, host),
opcode::PUSH17 => stack::push::<17>(interp, host),
opcode::PUSH18 => stack::push::<18>(interp, host),
opcode::PUSH19 => stack::push::<19>(interp, host),
opcode::PUSH20 => stack::push::<20>(interp, host),
opcode::PUSH21 => stack::push::<21>(interp, host),
opcode::PUSH22 => stack::push::<22>(interp, host),
opcode::PUSH23 => stack::push::<23>(interp, host),
opcode::PUSH24 => stack::push::<24>(interp, host),
opcode::PUSH25 => stack::push::<25>(interp, host),
opcode::PUSH26 => stack::push::<26>(interp, host),
opcode::PUSH27 => stack::push::<27>(interp, host),
opcode::PUSH28 => stack::push::<28>(interp, host),
opcode::PUSH29 => stack::push::<29>(interp, host),
opcode::PUSH30 => stack::push::<30>(interp, host),
opcode::PUSH31 => stack::push::<31>(interp, host),
opcode::PUSH32 => stack::push::<32>(interp, host),
opcode::DUP1 => stack::dup::<1>(interp, host),
opcode::DUP2 => stack::dup::<2>(interp, host),
opcode::DUP3 => stack::dup::<3>(interp, host),
opcode::DUP4 => stack::dup::<4>(interp, host),
opcode::DUP5 => stack::dup::<5>(interp, host),
opcode::DUP6 => stack::dup::<6>(interp, host),
opcode::DUP7 => stack::dup::<7>(interp, host),
opcode::DUP8 => stack::dup::<8>(interp, host),
opcode::DUP9 => stack::dup::<9>(interp, host),
opcode::DUP10 => stack::dup::<10>(interp, host),
opcode::DUP11 => stack::dup::<11>(interp, host),
opcode::DUP12 => stack::dup::<12>(interp, host),
opcode::DUP13 => stack::dup::<13>(interp, host),
opcode::DUP14 => stack::dup::<14>(interp, host),
opcode::DUP15 => stack::dup::<15>(interp, host),
opcode::DUP16 => stack::dup::<16>(interp, host),

opcode::SWAP1 => stack::swap::<1>(interp, host),
opcode::SWAP2 => stack::swap::<2>(interp, host),
opcode::SWAP3 => stack::swap::<3>(interp, host),
opcode::SWAP4 => stack::swap::<4>(interp, host),
opcode::SWAP5 => stack::swap::<5>(interp, host),
opcode::SWAP6 => stack::swap::<6>(interp, host),
opcode::SWAP7 => stack::swap::<7>(interp, host),
opcode::SWAP8 => stack::swap::<8>(interp, host),
opcode::SWAP9 => stack::swap::<9>(interp, host),
opcode::SWAP10 => stack::swap::<10>(interp, host),
opcode::SWAP11 => stack::swap::<11>(interp, host),
opcode::SWAP12 => stack::swap::<12>(interp, host),
opcode::SWAP13 => stack::swap::<13>(interp, host),
opcode::SWAP14 => stack::swap::<14>(interp, host),
opcode::SWAP15 => stack::swap::<15>(interp, host),
opcode::SWAP16 => stack::swap::<16>(interp, host),
pub mod stack;
pub mod system;

opcode::RETURN => control::ret(interp, host),
opcode::REVERT => control::revert::<S>(interp, host),
opcode::INVALID => return_invalid(interp, host),
opcode::BASEFEE => host_env::basefee::<S>(interp, host),
opcode::ORIGIN => host_env::origin(interp, host),
opcode::CALLER => system::caller(interp, host),
opcode::CALLVALUE => system::callvalue(interp, host),
opcode::GASPRICE => host_env::gasprice(interp, host),
opcode::EXTCODESIZE => host::extcodesize::<S>(interp, host),
opcode::EXTCODEHASH => host::extcodehash::<S>(interp, host),
opcode::EXTCODECOPY => host::extcodecopy::<S>(interp, host),
opcode::RETURNDATASIZE => system::returndatasize::<S>(interp, host),
opcode::RETURNDATACOPY => system::returndatacopy::<S>(interp, host),
opcode::BLOCKHASH => host::blockhash(interp, host),
opcode::COINBASE => host_env::coinbase(interp, host),
opcode::TIMESTAMP => host_env::timestamp(interp, host),
opcode::NUMBER => host_env::number(interp, host),
opcode::DIFFICULTY => host_env::difficulty::<H, S>(interp, host),
opcode::GASLIMIT => host_env::gaslimit(interp, host),
opcode::SLOAD => host::sload::<S>(interp, host),
opcode::SSTORE => host::sstore::<S>(interp, host),
opcode::TSTORE => host::tstore::<H, S>(interp, host),
opcode::TLOAD => host::tload::<H, S>(interp, host),
opcode::GAS => system::gas(interp, host),
opcode::LOG0 => host::log::<0>(interp, host),
opcode::LOG1 => host::log::<1>(interp, host),
opcode::LOG2 => host::log::<2>(interp, host),
opcode::LOG3 => host::log::<3>(interp, host),
opcode::LOG4 => host::log::<4>(interp, host),
opcode::SELFDESTRUCT => host::selfdestruct::<S>(interp, host),
opcode::CREATE => host::create::<false, S>(interp, host), //check
opcode::CREATE2 => host::create::<true, S>(interp, host), //check
opcode::CALL => host::call::<S>(interp, host), //check
opcode::CALLCODE => host::call_code::<S>(interp, host), //check
opcode::DELEGATECALL => host::delegate_call::<S>(interp, host), //check
opcode::STATICCALL => host::static_call::<S>(interp, host), //check
opcode::CHAINID => host_env::chainid::<S>(interp, host),
opcode::MCOPY => memory::mcopy::<S>(interp, host),
_ => return_not_found(interp, host),
}
}
pub use opcode::{Instruction, OpCode, OPCODE_JUMPMAP};
12 changes: 8 additions & 4 deletions crates/interpreter/src/instructions/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ pub fn wrapping_sub(interpreter: &mut Interpreter, _host: &mut dyn Host) {
pub fn div(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::LOW);
pop_top!(interpreter, op1, op2);
*op2 = op1.checked_div(*op2).unwrap_or_default()
if *op2 != U256::ZERO {
*op2 = op1.wrapping_div(*op2);
}
}

pub fn sdiv(interpreter: &mut Interpreter, _host: &mut dyn Host) {
Expand All @@ -38,15 +40,17 @@ pub fn sdiv(interpreter: &mut Interpreter, _host: &mut dyn Host) {
pub fn rem(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::LOW);
pop_top!(interpreter, op1, op2);
*op2 = op1.checked_rem(*op2).unwrap_or_default()
if *op2 != U256::ZERO {
*op2 = op1.wrapping_rem(*op2);
}
}

pub fn smod(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::LOW);
pop_top!(interpreter, op1, op2);
if *op2 != U256::ZERO {
*op2 = i256_mod(op1, *op2)
};
}
}

pub fn addmod(interpreter: &mut Interpreter, _host: &mut dyn Host) {
Expand All @@ -61,7 +65,7 @@ pub fn mulmod(interpreter: &mut Interpreter, _host: &mut dyn Host) {
*op3 = op1.mul_mod(op2, *op3)
}

pub fn eval_exp<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
pub fn exp<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
pop_top!(interpreter, op1, op2);
gas_or_fail!(interpreter, gas::exp_cost::<SPEC>(*op2));
*op2 = op1.pow(*op2);
Expand Down
60 changes: 22 additions & 38 deletions crates/interpreter/src/instructions/bitwise.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
use super::i256::{i256_cmp, i256_sign_compl, two_compl, Sign};
use crate::{
gas,
primitives::SpecId::CONSTANTINOPLE,
primitives::{Spec, U256},
Host, InstructionResult, Interpreter,
};
use core::cmp::Ordering;
use core::ops::{BitAnd, BitOr, BitXor};

pub fn lt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 = if op1.lt(op2) {
U256::from(1)
} else {
U256::ZERO
};
*op2 = U256::from(op1 < *op2);
}

pub fn gt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 = if op1.gt(op2) {
U256::from(1)
} else {
U256::ZERO
};
*op2 = U256::from(op1 > *op2);
}

pub fn slt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
Expand All @@ -43,36 +33,31 @@ pub fn sgt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
pub fn eq(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 = if op1.eq(op2) {
U256::from(1)
} else {
U256::ZERO
};
*op2 = U256::from(op1 == *op2);
}

pub fn iszero(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1);
*op1 = if *op1 == U256::ZERO {
U256::from(1)
} else {
U256::ZERO
};
*op1 = U256::from(*op1 == U256::ZERO);
}

pub fn bitand(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 = op1.bitand(*op2);
*op2 = op1 & *op2;
}

pub fn bitor(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 = op1.bitor(*op2);
*op2 = op1 | *op2;
}

pub fn bitxor(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 = op1.bitxor(*op2);
*op2 = op1 ^ *op2;
}

pub fn not(interpreter: &mut Interpreter, _host: &mut dyn Host) {
Expand All @@ -84,36 +69,35 @@ pub fn not(interpreter: &mut Interpreter, _host: &mut dyn Host) {
pub fn byte(interpreter: &mut Interpreter, _host: &mut dyn Host) {
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
let mut ret = U256::ZERO;

let o1 = as_usize_saturated!(op1);
if o1 < 32 {
let o2 = &*op2;
ret = (o2 << (8 * o1)) >> (8 * 31);
}

*op2 = ret;
*op2 = if o1 < 32 {
// `31 - o1` because `byte` returns LE, while we want BE
U256::from(op2.byte(31 - o1))
} else {
U256::ZERO
};
}

/// EIP-145: Bitwise shifting instructions in EVM
pub fn shl<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
// EIP-145: Bitwise shifting instructions in EVM
check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
check!(interpreter, CONSTANTINOPLE);
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 <<= as_usize_saturated!(op1);
}

/// EIP-145: Bitwise shifting instructions in EVM
pub fn shr<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
// EIP-145: Bitwise shifting instructions in EVM
check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
check!(interpreter, CONSTANTINOPLE);
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);
*op2 >>= as_usize_saturated!(op1);
}

/// EIP-145: Bitwise shifting instructions in EVM
pub fn sar<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
// EIP-145: Bitwise shifting instructions in EVM
check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
check!(interpreter, CONSTANTINOPLE);
gas!(interpreter, gas::VERYLOW);
pop_top!(interpreter, op1, op2);

Expand Down
Loading

0 comments on commit d926728

Please sign in to comment.