Skip to content

Conversation

@DaniPopes
Copy link
Collaborator

@DaniPopes DaniPopes commented Jul 24, 2023

This PR focuses on refactoring some parts of the interpreter which lead to, although minor, performance gains.

Closes #310, not fixes, as the current match performs better on debug builds, and performs the same in release builds. This is not shown in the results.

Results

Cachegrind

See #582

Benches

Not entirely sure why the non-generic version performs better, since I would expect the checks to be optimized out at compile time on the generic version.

I have included the patch which can be used to change between the two versions.

Time benches shouldn't be relied upon for these type of small changes, and these ones are also outdated.

Main

36de35b

snailtracer/transact/analysed                                                                           
                        time:   [46.154 ms 46.666 ms 47.183 ms]
snailtracer/eval        time:   [40.100 ms 40.293 ms 40.487 ms]                            


snailtracer/transact/analysed                                                                           
                        time:   [45.875 ms 46.356 ms 47.026 ms]
snailtracer/eval        time:   [40.874 ms 41.289 ms 41.946 ms]                            


snailtracer/transact/analysed                                                                           
                        time:   [46.696 ms 46.993 ms 47.451 ms]
snailtracer/eval        time:   [40.366 ms 40.707 ms 41.054 ms]                            

Generic (patch)

3658fa7

Patch

diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs
index fa93b6a..dbd05be 100644
--- a/crates/interpreter/src/gas/calc.rs
+++ b/crates/interpreter/src/gas/calc.rs
@@ -1,13 +1,13 @@
 use super::constants::*;
 use crate::inner_models::SelfDestructResult;
 use alloc::vec::Vec;
-use primitives::{Bytes, SpecId, SpecId::*, B160, U256};
+use primitives::{Bytes, Spec, SpecId::*, B160, U256};
 
 #[allow(clippy::collapsible_else_if)]
-pub fn sstore_refund(original: U256, current: U256, new: U256, spec: SpecId) -> i64 {
-    if SpecId::enabled(spec, ISTANBUL) {
+pub fn sstore_refund<SPEC: Spec>(original: U256, current: U256, new: U256) -> i64 {
+    if SPEC::enabled(ISTANBUL) {
         // EIP-3529: Reduction in refunds
-        let sstore_clears_schedule = if SpecId::enabled(spec, LONDON) {
+        let sstore_clears_schedule = if SPEC::enabled(LONDON) {
             (SSTORE_RESET - COLD_SLOAD_COST + ACCESS_LIST_STORAGE_KEY) as i64
         } else {
             REFUND_SSTORE_CLEARS
@@ -29,10 +29,10 @@ pub fn sstore_refund(original: U256, current: U256, new: U256, spec: SpecId) ->
                 }
 
                 if original == new {
-                    let (gas_sstore_reset, gas_sload) = if SpecId::enabled(spec, BERLIN) {
+                    let (gas_sstore_reset, gas_sload) = if SPEC::enabled(BERLIN) {
                         (SSTORE_RESET - COLD_SLOAD_COST, WARM_STORAGE_READ_COST)
                     } else {
-                        (SSTORE_RESET, sload_cost(false, spec))
+                        (SSTORE_RESET, sload_cost::<SPEC>(false))
                     };
                     if original == U256::ZERO {
                         refund += (SSTORE_SET - gas_sload) as i64;
@@ -83,12 +83,12 @@ fn log2floor(value: U256) -> u64 {
     l
 }
 
-pub fn exp_cost(power: U256, spec: SpecId) -> Option<u64> {
+pub fn exp_cost<SPEC: Spec>(power: U256) -> Option<u64> {
     if power == U256::ZERO {
         Some(EXP)
     } else {
         // EIP-160: EXP cost increase
-        let gas_byte = U256::from(if SpecId::enabled(spec, SPURIOUS_DRAGON) {
+        let gas_byte = U256::from(if SPEC::enabled(SPURIOUS_DRAGON) {
             50u64
         } else {
             10
@@ -106,17 +106,17 @@ pub fn verylowcopy_cost(len: u64) -> Option<u64> {
     VERYLOW.checked_add(COPY.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?)
 }
 
-pub fn extcodecopy_cost(len: u64, is_cold: bool, spec: SpecId) -> Option<u64> {
+pub fn extcodecopy_cost<SPEC: Spec>(len: u64, is_cold: bool) -> Option<u64> {
     let wordd = len / 32;
     let wordr = len % 32;
 
-    let base_gas: u64 = if SpecId::enabled(spec, BERLIN) {
+    let base_gas: u64 = if SPEC::enabled(BERLIN) {
         if is_cold {
             COLD_ACCOUNT_ACCESS_COST
         } else {
             WARM_STORAGE_READ_COST
         }
-    } else if SpecId::enabled(spec, TANGERINE) {
+    } else if SPEC::enabled(TANGERINE) {
         700
     } else {
         20
@@ -124,14 +124,14 @@ pub fn extcodecopy_cost(len: u64, is_cold: bool, spec: SpecId) -> Option<u64> {
     base_gas.checked_add(COPY.checked_mul(if wordr == 0 { wordd } else { wordd + 1 })?)
 }
 
-pub fn account_access_gas(is_cold: bool, spec: SpecId) -> u64 {
-    if SpecId::enabled(spec, BERLIN) {
+pub fn account_access_gas<SPEC: Spec>(is_cold: bool) -> u64 {
+    if SPEC::enabled(BERLIN) {
         if is_cold {
             COLD_ACCOUNT_ACCESS_COST
         } else {
             WARM_STORAGE_READ_COST
         }
-    } else if SpecId::enabled(spec, ISTANBUL) {
+    } else if SPEC::enabled(ISTANBUL) {
         700
     } else {
         20
@@ -160,17 +160,17 @@ pub fn initcode_cost(len: u64) -> u64 {
     INITCODE_WORD_COST * if wordr == 0 { wordd } else { wordd + 1 }
 }
 
-pub fn sload_cost(is_cold: bool, spec: SpecId) -> u64 {
-    if SpecId::enabled(spec, BERLIN) {
+pub fn sload_cost<SPEC: Spec>(is_cold: bool) -> u64 {
+    if SPEC::enabled(BERLIN) {
         if is_cold {
             COLD_SLOAD_COST
         } else {
             WARM_STORAGE_READ_COST
         }
-    } else if SpecId::enabled(spec, ISTANBUL) {
+    } else if SPEC::enabled(ISTANBUL) {
         // EIP-1884: Repricing for trie-size-dependent opcodes
         800
-    } else if SpecId::enabled(spec, TANGERINE) {
+    } else if SPEC::enabled(TANGERINE) {
         // EIP-150: Gas cost changes for IO-heavy operations
         200
     } else {
@@ -179,24 +179,23 @@ pub fn sload_cost(is_cold: bool, spec: SpecId) -> u64 {
 }
 
 #[allow(clippy::collapsible_else_if)]
-pub fn sstore_cost(
+pub fn sstore_cost<SPEC: Spec>(
     original: U256,
     current: U256,
     new: U256,
     gas: u64,
     is_cold: bool,
-    spec: SpecId,
 ) -> Option<u64> {
     // TODO untangle this mess and make it more elegant
-    let (gas_sload, gas_sstore_reset) = if SpecId::enabled(spec, BERLIN) {
+    let (gas_sload, gas_sstore_reset) = if SPEC::enabled(BERLIN) {
         (WARM_STORAGE_READ_COST, SSTORE_RESET - COLD_SLOAD_COST)
     } else {
-        (sload_cost(is_cold, spec), SSTORE_RESET)
+        (sload_cost::<SPEC>(is_cold), SSTORE_RESET)
     };
 
     // https://eips.ethereum.org/EIPS/eip-2200
     // It’s a combined version of EIP-1283 and EIP-1706
-    let gas_cost = if SpecId::enabled(spec, ISTANBUL) {
+    let gas_cost = if SPEC::enabled(ISTANBUL) {
         // EIP-1706
         if gas <= CALL_STIPEND {
             return None;
@@ -224,58 +223,53 @@ pub fn sstore_cost(
         }
     };
     // In EIP-2929 we charge extra if the slot has not been used yet in this transaction
-    if SpecId::enabled(spec, BERLIN) && is_cold {
+    if SPEC::enabled(BERLIN) && is_cold {
         Some(gas_cost + COLD_SLOAD_COST)
     } else {
         Some(gas_cost)
     }
 }
 
-pub fn selfdestruct_cost(res: SelfDestructResult, spec: SpecId) -> u64 {
+pub fn selfdestruct_cost<SPEC: Spec>(res: SelfDestructResult) -> u64 {
     // EIP-161: State trie clearing (invariant-preserving alternative)
-    let should_charge_topup = if SpecId::enabled(spec, SPURIOUS_DRAGON) {
+    let should_charge_topup = if SPEC::enabled(SPURIOUS_DRAGON) {
         res.had_value && !res.target_exists
     } else {
         !res.target_exists
     };
 
-    let selfdestruct_gas_topup = if SpecId::enabled(spec, TANGERINE) && should_charge_topup {
+    let selfdestruct_gas_topup = if SPEC::enabled(TANGERINE) && should_charge_topup {
         //EIP-150: Gas cost changes for IO-heavy operations
         25000
     } else {
         0
     };
 
-    let selfdestruct_gas = if SpecId::enabled(spec, TANGERINE) {
-        5000
-    } else {
-        0
-    }; //EIP-150: Gas cost changes for IO-heavy operations
+    let selfdestruct_gas = if SPEC::enabled(TANGERINE) { 5000 } else { 0 }; //EIP-150: Gas cost changes for IO-heavy operations
 
     let mut gas = selfdestruct_gas + selfdestruct_gas_topup;
-    if SpecId::enabled(spec, BERLIN) && res.is_cold {
+    if SPEC::enabled(BERLIN) && res.is_cold {
         gas += COLD_ACCOUNT_ACCESS_COST
     }
     gas
 }
 
-pub fn call_cost(
+pub fn call_cost<SPEC: Spec>(
     value: U256,
     is_new: bool,
     is_cold: bool,
     is_call_or_callcode: bool,
     is_call_or_staticcall: bool,
-    spec: SpecId,
 ) -> u64 {
     let transfers_value = value != U256::default();
 
-    let call_gas = if SpecId::enabled(spec, BERLIN) {
+    let call_gas = if SPEC::enabled(BERLIN) {
         if is_cold {
             COLD_ACCOUNT_ACCESS_COST
         } else {
             WARM_STORAGE_READ_COST
         }
-    } else if SpecId::enabled(spec, TANGERINE) {
+    } else if SPEC::enabled(TANGERINE) {
         // EIP-150: Gas cost changes for IO-heavy operations
         700
     } else {
@@ -284,11 +278,11 @@ pub fn call_cost(
 
     call_gas
         + xfer_cost(is_call_or_callcode, transfers_value)
-        + new_cost(is_call_or_staticcall, is_new, transfers_value, spec)
+        + new_cost::<SPEC>(is_call_or_staticcall, is_new, transfers_value)
 }
 
-pub fn hot_cold_cost(is_cold: bool, regular_value: u64, spec: SpecId) -> u64 {
-    if SpecId::enabled(spec, BERLIN) {
+pub fn hot_cold_cost<SPEC: Spec>(is_cold: bool, regular_value: u64) -> u64 {
+    if SPEC::enabled(BERLIN) {
         if is_cold {
             COLD_ACCOUNT_ACCESS_COST
         } else {
@@ -307,10 +301,10 @@ fn xfer_cost(is_call_or_callcode: bool, transfers_value: bool) -> u64 {
     }
 }
 
-fn new_cost(is_call_or_staticcall: bool, is_new: bool, transfers_value: bool, spec: SpecId) -> u64 {
+fn new_cost<SPEC: Spec>(is_call_or_staticcall: bool, is_new: bool, transfers_value: bool) -> u64 {
     if is_call_or_staticcall {
         // EIP-161: State trie clearing (invariant-preserving alternative)
-        if SpecId::enabled(spec, SPURIOUS_DRAGON) {
+        if SPEC::enabled(SPURIOUS_DRAGON) {
             if transfers_value && is_new {
                 NEWACCOUNT
             } else {
@@ -335,11 +329,10 @@ pub fn memory_gas(a: usize) -> u64 {
 
 /// Initial gas that is deducted for transaction to be included.
 /// Initial gas contains initial stipend gas, gas for access list and input data.
-pub fn initial_tx_gas(
+pub fn initial_tx_gas<SPEC: Spec>(
     input: &Bytes,
     is_create: bool,
     access_list: &[(B160, Vec<U256>)],
-    spec: SpecId,
 ) -> u64 {
     let mut initial_gas = 0;
     let zero_data_len = input.iter().filter(|v| **v == 0).count() as u64;
@@ -348,15 +341,10 @@ pub fn initial_tx_gas(
     // initdate stipend
     initial_gas += zero_data_len * TRANSACTION_ZERO_DATA;
     // EIP-2028: Transaction data gas cost reduction
-    initial_gas += non_zero_data_len
-        * if SpecId::enabled(spec, ISTANBUL) {
-            16
-        } else {
-            68
-        };
+    initial_gas += non_zero_data_len * if SPEC::enabled(ISTANBUL) { 16 } else { 68 };
 
     // get number of access list account and storages.
-    if SpecId::enabled(spec, BERLIN) {
+    if SPEC::enabled(BERLIN) {
         let accessed_slots = access_list
             .iter()
             .fold(0, |slot_count, (_, slots)| slot_count + slots.len() as u64);
@@ -366,7 +354,7 @@ pub fn initial_tx_gas(
 
     // base stipend
     initial_gas += if is_create {
-        if SpecId::enabled(spec, HOMESTEAD) {
+        if SPEC::enabled(HOMESTEAD) {
             // EIP-2: Homestead Hard-fork Changes
             53000
         } else {
@@ -378,7 +366,7 @@ pub fn initial_tx_gas(
 
     // EIP-3860: Limit and meter initcode
     // Initcode stipend for bytecode analysis
-    if SpecId::enabled(spec, SHANGHAI) && is_create {
+    if SPEC::enabled(SHANGHAI) && is_create {
         initial_gas += initcode_cost(input.len() as u64)
     }
 
diff --git a/crates/interpreter/src/instructions/arithmetic.rs b/crates/interpreter/src/instructions/arithmetic.rs
index 8fa09ae..a93e1f9 100644
--- a/crates/interpreter/src/instructions/arithmetic.rs
+++ b/crates/interpreter/src/instructions/arithmetic.rs
@@ -1,25 +1,25 @@
 use super::i256::{i256_div, i256_mod};
 use super::prelude::*;
 
-pub(super) fn wrapped_add(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn wrapped_add(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = op1.wrapping_add(*op2);
 }
 
-pub(super) fn wrapping_mul(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn wrapping_mul(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::LOW);
     pop_top!(interpreter, op1, op2);
     *op2 = op1.wrapping_mul(*op2);
 }
 
-pub(super) fn wrapping_sub(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn wrapping_sub(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = op1.wrapping_sub(*op2);
 }
 
-pub(super) fn div(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn div(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::LOW);
     pop_top!(interpreter, op1, op2);
     if *op2 != U256::ZERO {
@@ -27,13 +27,13 @@ pub(super) fn div(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: Sp
     }
 }
 
-pub(super) fn sdiv(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn sdiv(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::LOW);
     pop_top!(interpreter, op1, op2);
     *op2 = i256_div(op1, *op2);
 }
 
-pub(super) fn rem(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn rem(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::LOW);
     pop_top!(interpreter, op1, op2);
     if *op2 != U256::ZERO {
@@ -41,7 +41,7 @@ pub(super) fn rem(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: Sp
     }
 }
 
-pub(super) fn smod(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn smod(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::LOW);
     pop_top!(interpreter, op1, op2);
     if *op2 != U256::ZERO {
@@ -49,21 +49,21 @@ pub(super) fn smod(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: S
     }
 }
 
-pub(super) fn addmod(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn addmod(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::MID);
     pop_top!(interpreter, op1, op2, op3);
     *op3 = op1.add_mod(op2, *op3)
 }
 
-pub(super) fn mulmod(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn mulmod(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::MID);
     pop_top!(interpreter, op1, op2, op3);
     *op3 = op1.mul_mod(op2, *op3)
 }
 
-pub(super) fn exp(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
+pub(super) fn exp<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     pop_top!(interpreter, op1, op2);
-    gas_or_fail!(interpreter, gas::exp_cost(*op2, spec));
+    gas_or_fail!(interpreter, gas::exp_cost::<SPEC>(*op2));
     *op2 = op1.pow(*op2);
 }
 
@@ -82,7 +82,7 @@ pub(super) fn exp(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: Spe
 /// `y | !mask` where `|` is the bitwise `OR` and `!` is bitwise negation. Similarly, if
 /// `b == 0` then the yellow paper says the output should start with all zeros, then end with
 /// bits from `b`; this is equal to `y & mask` where `&` is bitwise `AND`.
-pub(super) fn signextend(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn signextend(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::LOW);
     pop_top!(interpreter, op1, op2);
     if op1 < U256::from(32) {
diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs
index 6e09945..700a38b 100644
--- a/crates/interpreter/src/instructions/bitwise.rs
+++ b/crates/interpreter/src/instructions/bitwise.rs
@@ -6,67 +6,67 @@ const fn btou256(b: bool) -> U256 {
     U256::from_limbs([b as u64, 0, 0, 0])
 }
 
-pub(super) fn lt(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn lt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = btou256(op1 < *op2);
 }
 
-pub(super) fn gt(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn gt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = btou256(op1 > *op2);
 }
 
-pub(super) fn slt(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn slt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = btou256(i256_cmp(op1, *op2) == Ordering::Less);
 }
 
-pub(super) fn sgt(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn sgt(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = btou256(i256_cmp(op1, *op2) == Ordering::Greater);
 }
 
-pub(super) fn eq(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn eq(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = btou256(op1 == *op2);
 }
 
-pub(super) fn iszero(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn iszero(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1);
     *op1 = btou256(*op1 == U256::ZERO);
 }
 
-pub(super) fn bitand(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn bitand(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = op1 & *op2;
 }
 
-pub(super) fn bitor(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn bitor(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = op1 | *op2;
 }
 
-pub(super) fn bitxor(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn bitxor(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 = op1 ^ *op2;
 }
 
-pub(super) fn not(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn not(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1);
     *op1 = !*op1;
 }
 
-pub(super) fn byte(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn byte(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
 
@@ -89,24 +89,24 @@ pub(super) fn byte(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: S
 }
 
 // EIP-145: Bitwise shifting instructions in EVM
-pub(super) fn shl(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, CONSTANTINOPLE));
+pub(super) fn shl<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 <<= as_usize_saturated!(op1);
 }
 
 // EIP-145: Bitwise shifting instructions in EVM
-pub(super) fn shr(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, CONSTANTINOPLE));
+pub(super) fn shr<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
     *op2 >>= as_usize_saturated!(op1);
 }
 
 // EIP-145: Bitwise shifting instructions in EVM
-pub(super) fn sar(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, CONSTANTINOPLE));
+pub(super) fn sar<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
     gas!(interpreter, gas::VERYLOW);
     pop_top!(interpreter, op1, op2);
 
diff --git a/crates/interpreter/src/instructions/control.rs b/crates/interpreter/src/instructions/control.rs
index 3a2b18c..669b97e 100644
--- a/crates/interpreter/src/instructions/control.rs
+++ b/crates/interpreter/src/instructions/control.rs
@@ -1,6 +1,6 @@
 use super::prelude::*;
 
-pub(super) fn jump(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn jump(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::MID);
     pop!(interpreter, dest);
     let dest = as_usize_or_fail!(interpreter, dest, InstructionResult::InvalidJump);
@@ -14,7 +14,7 @@ pub(super) fn jump(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: S
     }
 }
 
-pub(super) fn jumpi(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn jumpi(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::HIGH);
     pop!(interpreter, dest, value);
     if value != U256::ZERO {
@@ -30,16 +30,16 @@ pub(super) fn jumpi(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec:
     }
 }
 
-pub(super) fn jumpdest(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn jumpdest(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::JUMPDEST);
 }
 
-pub(super) fn pc(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn pc(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, U256::from(interpreter.program_counter() - 1));
 }
 
-pub(super) fn ret(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn ret(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     // zero gas cost
     // gas!(interpreter, gas::ZERO);
     pop!(interpreter, start, len);
@@ -55,10 +55,10 @@ pub(super) fn ret(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: Sp
 }
 
 // EIP-140: REVERT instruction
-pub(super) fn revert(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
+pub(super) fn revert<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     // zero gas cost
     // gas!(interpreter, gas::ZERO);
-    check!(interpreter, SpecId::enabled(spec, BYZANTIUM));
+    check!(interpreter, SPEC::enabled(BYZANTIUM));
     pop!(interpreter, start, len);
     let len = as_usize_or_fail!(interpreter, len);
     if len == 0 {
@@ -71,14 +71,14 @@ pub(super) fn revert(interpreter: &mut Interpreter, _host: &mut dyn Host, spec:
     interpreter.instruction_result = InstructionResult::Revert;
 }
 
-pub(super) fn stop(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn stop(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     interpreter.instruction_result = InstructionResult::Stop;
 }
 
-pub(super) fn invalid(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn invalid(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     interpreter.instruction_result = InstructionResult::InvalidFEOpcode;
 }
 
-pub(super) fn not_found(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn not_found(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     interpreter.instruction_result = InstructionResult::OpcodeNotFound;
 }
diff --git a/crates/interpreter/src/instructions/host.rs b/crates/interpreter/src/instructions/host.rs
index d3f53d4..7b5da9a 100644
--- a/crates/interpreter/src/instructions/host.rs
+++ b/crates/interpreter/src/instructions/host.rs
@@ -10,7 +10,7 @@ use crate::{
 };
 use core::cmp::min;
 
-pub(super) fn balance(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn balance<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     pop_address!(interpreter, address);
     let Some((balance, is_cold)) = host.balance(address) else {
         interpreter.instruction_result = InstructionResult::FatalExternalError;
@@ -18,10 +18,10 @@ pub(super) fn balance(interpreter: &mut Interpreter, host: &mut dyn Host, spec:
     };
     gas!(
         interpreter,
-        if SpecId::enabled(spec, ISTANBUL) {
+        if SPEC::enabled(ISTANBUL) {
             // EIP-1884: Repricing for trie-size-dependent opcodes
-            gas::account_access_gas(is_cold, spec)
-        } else if SpecId::enabled(spec, TANGERINE) {
+            gas::account_access_gas::<SPEC>(is_cold)
+        } else if SPEC::enabled(TANGERINE) {
             400
         } else {
             20
@@ -31,8 +31,8 @@ pub(super) fn balance(interpreter: &mut Interpreter, host: &mut dyn Host, spec:
 }
 
 // EIP-1884: Repricing for trie-size-dependent opcodes
-pub(super) fn selfbalance(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, ISTANBUL));
+pub(super) fn selfbalance<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(ISTANBUL));
     gas!(interpreter, gas::LOW);
     let Some((balance, _)) = host.balance(interpreter.contract.address) else {
         interpreter.instruction_result = InstructionResult::FatalExternalError;
@@ -41,13 +41,13 @@ pub(super) fn selfbalance(interpreter: &mut Interpreter, host: &mut dyn Host, sp
     push!(interpreter, balance);
 }
 
-pub(super) fn extcodesize(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn extcodesize<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     pop_address!(interpreter, address);
     let Some((code, is_cold)) = host.code(address) else {
         interpreter.instruction_result = InstructionResult::FatalExternalError;
         return;
     };
-    if SpecId::enabled(spec, BERLIN) {
+    if SPEC::enabled(BERLIN) {
         gas!(
             interpreter,
             if is_cold {
@@ -56,7 +56,7 @@ pub(super) fn extcodesize(interpreter: &mut Interpreter, host: &mut dyn Host, sp
                 WARM_STORAGE_READ_COST
             }
         );
-    } else if SpecId::enabled(spec, TANGERINE) {
+    } else if SPEC::enabled(TANGERINE) {
         gas!(interpreter, 700);
     } else {
         gas!(interpreter, 20);
@@ -66,14 +66,14 @@ pub(super) fn extcodesize(interpreter: &mut Interpreter, host: &mut dyn Host, sp
 }
 
 // EIP-1052: EXTCODEHASH opcode
-pub(super) fn extcodehash(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, CONSTANTINOPLE));
+pub(super) fn extcodehash<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(CONSTANTINOPLE));
     pop_address!(interpreter, address);
     let Some((code_hash, is_cold)) = host.code_hash(address) else {
         interpreter.instruction_result = InstructionResult::FatalExternalError;
         return;
     };
-    if SpecId::enabled(spec, BERLIN) {
+    if SPEC::enabled(BERLIN) {
         gas!(
             interpreter,
             if is_cold {
@@ -82,7 +82,7 @@ pub(super) fn extcodehash(interpreter: &mut Interpreter, host: &mut dyn Host, sp
                 WARM_STORAGE_READ_COST
             }
         );
-    } else if SpecId::enabled(spec, ISTANBUL) {
+    } else if SPEC::enabled(ISTANBUL) {
         gas!(interpreter, 700);
     } else {
         gas!(interpreter, 400);
@@ -90,7 +90,7 @@ pub(super) fn extcodehash(interpreter: &mut Interpreter, host: &mut dyn Host, sp
     push_b256!(interpreter, code_hash);
 }
 
-pub(super) fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn extcodecopy<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     pop_address!(interpreter, address);
     pop!(interpreter, memory_offset, code_offset, len_u256);
 
@@ -102,7 +102,7 @@ pub(super) fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Host, sp
     let len = as_usize_or_fail!(interpreter, len_u256);
     gas_or_fail!(
         interpreter,
-        gas::extcodecopy_cost(len as u64, is_cold, spec)
+        gas::extcodecopy_cost::<SPEC>(len as u64, is_cold)
     );
     if len == 0 {
         return;
@@ -117,7 +117,7 @@ pub(super) fn extcodecopy(interpreter: &mut Interpreter, host: &mut dyn Host, sp
         .set_data(memory_offset, code_offset, len, code.bytes());
 }
 
-pub(super) fn blockhash(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn blockhash(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BLOCKHASH);
     pop_top!(interpreter, number);
 
@@ -136,18 +136,18 @@ pub(super) fn blockhash(interpreter: &mut Interpreter, host: &mut dyn Host, _spe
     *number = U256::ZERO;
 }
 
-pub(super) fn sload(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn sload<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     pop!(interpreter, index);
 
     let Some((value, is_cold)) = host.sload(interpreter.contract.address, index) else {
         interpreter.instruction_result = InstructionResult::FatalExternalError;
         return;
     };
-    gas!(interpreter, gas::sload_cost(is_cold, spec));
+    gas!(interpreter, gas::sload_cost::<SPEC>(is_cold));
     push!(interpreter, value);
 }
 
-pub(super) fn sstore(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn sstore<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     check_staticcall!(interpreter);
 
     pop!(interpreter, index, value);
@@ -159,16 +159,12 @@ pub(super) fn sstore(interpreter: &mut Interpreter, host: &mut dyn Host, spec: S
     };
     gas_or_fail!(interpreter, {
         let remaining_gas = interpreter.gas.remaining();
-        gas::sstore_cost(original, old, new, remaining_gas, is_cold, spec)
+        gas::sstore_cost::<SPEC>(original, old, new, remaining_gas, is_cold)
     });
-    refund!(interpreter, gas::sstore_refund(original, old, new, spec));
+    refund!(interpreter, gas::sstore_refund::<SPEC>(original, old, new));
 }
 
-pub(super) fn log<const N: usize>(
-    interpreter: &mut Interpreter,
-    host: &mut dyn Host,
-    _spec: SpecId,
-) {
+pub(super) fn log<const N: usize>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     check_staticcall!(interpreter);
 
     pop!(interpreter, offset, len);
@@ -197,7 +193,7 @@ pub(super) fn log<const N: usize>(
     host.log(interpreter.contract.address, topics.to_vec(), data);
 }
 
-pub(super) fn selfdestruct(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn selfdestruct<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     check_staticcall!(interpreter);
     pop_address!(interpreter, target);
 
@@ -207,24 +203,23 @@ pub(super) fn selfdestruct(interpreter: &mut Interpreter, host: &mut dyn Host, s
     };
 
     // EIP-3529: Reduction in refunds
-    if !SpecId::enabled(spec, LONDON) && !res.previously_destroyed {
+    if !SPEC::enabled(LONDON) && !res.previously_destroyed {
         refund!(interpreter, gas::SELFDESTRUCT)
     }
-    gas!(interpreter, gas::selfdestruct_cost(res, spec));
+    gas!(interpreter, gas::selfdestruct_cost::<SPEC>(res));
 
     interpreter.instruction_result = InstructionResult::SelfDestruct;
 }
 
-pub(super) fn prepare_create_inputs<const IS_CREATE2: bool>(
+pub(super) fn prepare_create_inputs<const IS_CREATE2: bool, SPEC: Spec>(
     interpreter: &mut Interpreter,
     create_inputs: &mut Option<Box<CreateInputs>>,
-    spec: SpecId,
 ) {
     check_staticcall!(interpreter);
 
     // EIP-1014: Skinny CREATE2
     if IS_CREATE2 {
-        check!(interpreter, SpecId::enabled(spec, PETERSBURG));
+        check!(interpreter, SPEC::enabled(PETERSBURG));
     }
 
     interpreter.return_data_buffer = Bytes::new();
@@ -236,7 +231,7 @@ pub(super) fn prepare_create_inputs<const IS_CREATE2: bool>(
         Bytes::new()
     } else {
         // EIP-3860: Limit and meter initcode
-        if SpecId::enabled(spec, SHANGHAI) {
+        if SPEC::enabled(SHANGHAI) {
             if len > MAX_INITCODE_SIZE {
                 interpreter.instruction_result = InstructionResult::CreateInitcodeSizeLimit;
                 return;
@@ -261,7 +256,7 @@ pub(super) fn prepare_create_inputs<const IS_CREATE2: bool>(
     let mut gas_limit = interpreter.gas().remaining();
 
     // EIP-150: Gas cost changes for IO-heavy operations
-    if SpecId::enabled(spec, TANGERINE) {
+    if SPEC::enabled(TANGERINE) {
         // take remaining gas and deduce l64 part of it.
         gas_limit -= gas_limit / 64
     }
@@ -276,13 +271,12 @@ pub(super) fn prepare_create_inputs<const IS_CREATE2: bool>(
     }));
 }
 
-pub(super) fn create<const IS_CREATE2: bool>(
+pub(super) fn create<const IS_CREATE2: bool, SPEC: Spec>(
     interpreter: &mut Interpreter,
     host: &mut dyn Host,
-    spec: SpecId,
 ) {
     let mut create_input: Option<Box<CreateInputs>> = None;
-    prepare_create_inputs::<IS_CREATE2>(interpreter, &mut create_input, spec);
+    prepare_create_inputs::<IS_CREATE2, SPEC>(interpreter, &mut create_input);
 
     let Some(mut create_input) = create_input else {
         return;
@@ -320,30 +314,29 @@ pub(super) fn create<const IS_CREATE2: bool>(
     }
 }
 
-pub(super) fn call(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    call_inner(CallScheme::Call, interpreter, host, spec);
+pub(super) fn call<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    call_inner::<SPEC>(CallScheme::Call, interpreter, host);
 }
 
-pub(super) fn call_code(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    call_inner(CallScheme::CallCode, interpreter, host, spec);
+pub(super) fn call_code<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    call_inner::<SPEC>(CallScheme::CallCode, interpreter, host);
 }
 
-pub(super) fn delegate_call(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    call_inner(CallScheme::DelegateCall, interpreter, host, spec);
+pub(super) fn delegate_call<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    call_inner::<SPEC>(CallScheme::DelegateCall, interpreter, host);
 }
 
-pub(super) fn static_call(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    call_inner(CallScheme::StaticCall, interpreter, host, spec);
+pub(super) fn static_call<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    call_inner::<SPEC>(CallScheme::StaticCall, interpreter, host);
 }
 
-fn prepare_call_inputs(
+fn prepare_call_inputs<SPEC: Spec>(
     interpreter: &mut Interpreter,
     scheme: CallScheme,
     host: &mut dyn Host,
     result_len: &mut usize,
     result_offset: &mut usize,
     result_call_inputs: &mut Option<Box<CallInputs>>,
-    spec: SpecId,
 ) {
     pop!(interpreter, local_gas_limit);
     pop_address!(interpreter, to);
@@ -439,18 +432,17 @@ fn prepare_call_inputs(
 
     gas!(
         interpreter,
-        gas::call_cost(
+        gas::call_cost::<SPEC>(
             value,
             is_new,
             is_cold,
             matches!(scheme, CallScheme::Call | CallScheme::CallCode),
             matches!(scheme, CallScheme::Call | CallScheme::StaticCall),
-            spec
         )
     );
 
     // take l64 part of gas_limit
-    let mut gas_limit = if SpecId::enabled(spec, TANGERINE) {
+    let mut gas_limit = if SPEC::enabled(TANGERINE) {
         //EIP-150: Gas cost changes for IO-heavy operations
         let gas = interpreter.gas().remaining();
         min(gas - gas / 64, local_gas_limit)
@@ -476,17 +468,16 @@ fn prepare_call_inputs(
     }));
 }
 
-pub(super) fn call_inner(
+pub(super) fn call_inner<SPEC: Spec>(
     scheme: CallScheme,
     interpreter: &mut Interpreter,
     host: &mut dyn Host,
-    spec: SpecId,
 ) {
     match scheme {
         // EIP-7: DELEGATECALL
-        CallScheme::DelegateCall => check!(interpreter, SpecId::enabled(spec, HOMESTEAD)),
+        CallScheme::DelegateCall => check!(interpreter, SPEC::enabled(HOMESTEAD)),
         // EIP-214: New opcode STATICCALL
-        CallScheme::StaticCall => check!(interpreter, SpecId::enabled(spec, BYZANTIUM)),
+        CallScheme::StaticCall => check!(interpreter, SPEC::enabled(BYZANTIUM)),
         _ => (),
     }
     interpreter.return_data_buffer = Bytes::new();
@@ -494,14 +485,13 @@ pub(super) fn call_inner(
     let mut out_offset: usize = 0;
     let mut out_len: usize = 0;
     let mut call_input: Option<Box<CallInputs>> = None;
-    prepare_call_inputs(
+    prepare_call_inputs::<SPEC>(
         interpreter,
         scheme,
         host,
         &mut out_len,
         &mut out_offset,
         &mut call_input,
-        spec,
     );
 
     let Some(mut call_input) = call_input else {
diff --git a/crates/interpreter/src/instructions/host_env.rs b/crates/interpreter/src/instructions/host_env.rs
index 1247e86..e681ecd 100644
--- a/crates/interpreter/src/instructions/host_env.rs
+++ b/crates/interpreter/src/instructions/host_env.rs
@@ -1,54 +1,54 @@
 use super::prelude::*;
 
 // EIP-1344: ChainID opcode
-pub(super) fn chainid(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, ISTANBUL));
+pub(super) fn chainid<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(ISTANBUL));
     gas!(interpreter, gas::BASE);
     push!(interpreter, host.env().cfg.chain_id);
 }
 
-pub(super) fn coinbase(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn coinbase(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push_b256!(interpreter, host.env().block.coinbase.into());
 }
 
-pub(super) fn timestamp(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn timestamp(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, host.env().block.timestamp);
 }
 
-pub(super) fn number(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn number(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, host.env().block.number);
 }
 
-pub(super) fn difficulty(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+pub(super) fn difficulty<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
-    if SpecId::enabled(spec, MERGE) {
+    if SPEC::enabled(MERGE) {
         push_b256!(interpreter, host.env().block.prevrandao.unwrap());
     } else {
         push!(interpreter, host.env().block.difficulty);
     }
 }
 
-pub(super) fn gaslimit(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn gaslimit(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, host.env().block.gas_limit);
 }
 
-pub(super) fn gasprice(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn gasprice(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, host.env().effective_gas_price());
 }
 
 // EIP-3198: BASEFEE opcode
-pub(super) fn basefee(interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, LONDON));
+pub(super) fn basefee<SPEC: Spec>(interpreter: &mut Interpreter, host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(LONDON));
     gas!(interpreter, gas::BASE);
     push!(interpreter, host.env().block.basefee);
 }
 
-pub(super) fn origin(interpreter: &mut Interpreter, host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn origin(interpreter: &mut Interpreter, host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push_b256!(interpreter, host.env().tx.caller.into());
 }
diff --git a/crates/interpreter/src/instructions/memory.rs b/crates/interpreter/src/instructions/memory.rs
index 88d453d..e6e3166 100644
--- a/crates/interpreter/src/instructions/memory.rs
+++ b/crates/interpreter/src/instructions/memory.rs
@@ -1,6 +1,6 @@
 use super::prelude::*;
 
-pub(super) fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop!(interpreter, index);
     let index = as_usize_or_fail!(interpreter, index);
@@ -11,7 +11,7 @@ pub(super) fn mload(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec:
     );
 }
 
-pub(super) fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop!(interpreter, index, value);
     let index = as_usize_or_fail!(interpreter, index);
@@ -19,7 +19,7 @@ pub(super) fn mstore(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec:
     interpreter.memory.set_u256(index, value);
 }
 
-pub(super) fn mstore8(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn mstore8(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop!(interpreter, index, value);
     let index = as_usize_or_fail!(interpreter, index);
@@ -29,14 +29,14 @@ pub(super) fn mstore8(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec
     unsafe { interpreter.memory.set_byte(index, value) }
 }
 
-pub(super) fn msize(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn msize(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, U256::from(interpreter.memory.effective_len()));
 }
 
 // EIP-5656: MCOPY - Memory copying instruction
-pub(super) fn mcopy(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, CANCUN));
+pub(super) fn mcopy<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(CANCUN));
     pop!(interpreter, dst, src, len);
 
     // into usize or fail
diff --git a/crates/interpreter/src/instructions/mod.rs b/crates/interpreter/src/instructions/mod.rs
index 4882c14..5928827 100644
--- a/crates/interpreter/src/instructions/mod.rs
+++ b/crates/interpreter/src/instructions/mod.rs
@@ -16,7 +16,7 @@ mod prelude {
     pub(super) use crate::{gas, Host, InstructionResult, Interpreter};
     pub(super) use core::cmp::Ordering;
     pub(super) use primitives::{
-        keccak256, Bytes, SpecId, SpecId::*, B160, B256, KECCAK_EMPTY, U256,
+        keccak256, Bytes, Spec, SpecId, SpecId::*, B160, B256, KECCAK_EMPTY, U256,
     };
 }
 
diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs
index c7eba1e..a836219 100644
--- a/crates/interpreter/src/instructions/opcode.rs
+++ b/crates/interpreter/src/instructions/opcode.rs
@@ -25,7 +25,7 @@ macro_rules! opcodes {
 
         /// Evaluates the opcode.
         #[inline(always)]
-        pub(crate) fn eval(opcode: u8, interpreter: &mut Interpreter, host: &mut dyn Host, spec: SpecId) {
+        pub(crate) fn eval<SPEC: Spec>(opcode: u8, interpreter: &mut Interpreter, host: &mut dyn Host) {
             // type Instruction = fn(&mut Interpreter, &mut dyn Host, SpecId);
             // const INSTRUCTIONS: [Instruction; 256] = {
             //     #[allow(unused_mut)]
@@ -38,8 +38,8 @@ macro_rules! opcodes {
             // INSTRUCTIONS[opcode as usize](interpreter, host, spec);
 
             match opcode {
-                $($name => $f(interpreter, host, spec),)*
-                _ => control::not_found(interpreter, host, spec),
+                $($name => $f(interpreter, host),)*
+                _ => control::not_found(interpreter, host),
             }
         }
     };
@@ -57,7 +57,7 @@ opcodes! {
     0x07 => SMOD       => arithmetic::smod,
     0x08 => ADDMOD     => arithmetic::addmod,
     0x09 => MULMOD     => arithmetic::mulmod,
-    0x0A => EXP        => arithmetic::exp,
+    0x0A => EXP        => arithmetic::exp::<SPEC>,
     0x0B => SIGNEXTEND => arithmetic::signextend,
 
     0x10 => LT     => bitwise::lt,
@@ -71,14 +71,14 @@ opcodes! {
     0x18 => XOR    => bitwise::bitxor,
     0x19 => NOT    => bitwise::not,
     0x1A => BYTE   => bitwise::byte,
-    0x1B => SHL    => bitwise::shl,
-    0x1C => SHR    => bitwise::shr,
-    0x1D => SAR    => bitwise::sar,
+    0x1B => SHL    => bitwise::shl::<SPEC>,
+    0x1C => SHR    => bitwise::shr::<SPEC>,
+    0x1D => SAR    => bitwise::sar::<SPEC>,
 
     0x20 => KECCAK256 => system::calculate_keccak256,
 
     0x30 => ADDRESS   => system::address,
-    0x31 => BALANCE   => host::balance,
+    0x31 => BALANCE   => host::balance::<SPEC>,
     0x32 => ORIGIN    => host_env::origin,
     0x33 => CALLER    => system::caller,
     0x34 => CALLVALUE => system::callvalue,
@@ -90,36 +90,36 @@ opcodes! {
     0x39 => CODECOPY     => system::codecopy,
 
     0x3A => GASPRICE       => host_env::gasprice,
-    0x3B => EXTCODESIZE    => host::extcodesize,
-    0x3C => EXTCODECOPY    => host::extcodecopy,
-    0x3D => RETURNDATASIZE => system::returndatasize,
-    0x3E => RETURNDATACOPY => system::returndatacopy,
-    0x3F => EXTCODEHASH    => host::extcodehash,
+    0x3B => EXTCODESIZE    => host::extcodesize::<SPEC>,
+    0x3C => EXTCODECOPY    => host::extcodecopy::<SPEC>,
+    0x3D => RETURNDATASIZE => system::returndatasize::<SPEC>,
+    0x3E => RETURNDATACOPY => system::returndatacopy::<SPEC>,
+    0x3F => EXTCODEHASH    => host::extcodehash::<SPEC>,
     0x40 => BLOCKHASH      => host::blockhash,
     0x41 => COINBASE       => host_env::coinbase,
     0x42 => TIMESTAMP      => host_env::timestamp,
     0x43 => NUMBER         => host_env::number,
-    0x44 => DIFFICULTY     => host_env::difficulty,
+    0x44 => DIFFICULTY     => host_env::difficulty::<SPEC>,
     0x45 => GASLIMIT       => host_env::gaslimit,
-    0x46 => CHAINID        => host_env::chainid,
-    0x47 => SELFBALANCE    => host::selfbalance,
-    0x48 => BASEFEE        => host_env::basefee,
+    0x46 => CHAINID        => host_env::chainid::<SPEC>,
+    0x47 => SELFBALANCE    => host::selfbalance::<SPEC>,
+    0x48 => BASEFEE        => host_env::basefee::<SPEC>,
 
     0x50 => POP      => stack::pop,
     0x51 => MLOAD    => memory::mload,
     0x52 => MSTORE   => memory::mstore,
     0x53 => MSTORE8  => memory::mstore8,
-    0x54 => SLOAD    => host::sload,
-    0x55 => SSTORE   => host::sstore,
+    0x54 => SLOAD    => host::sload::<SPEC>,
+    0x55 => SSTORE   => host::sstore::<SPEC>,
     0x56 => JUMP     => control::jump,
     0x57 => JUMPI    => control::jumpi,
     0x58 => PC       => control::pc,
     0x59 => MSIZE    => memory::msize,
     0x5A => GAS      => system::gas,
     0x5B => JUMPDEST => control::jumpdest,
-    0x5E => MCOPY    => memory::mcopy,
+    0x5E => MCOPY    => memory::mcopy::<SPEC>,
 
-    0x5F => PUSH0  => stack::push0,
+    0x5F => PUSH0  => stack::push0::<SPEC>,
     0x60 => PUSH1  => stack::push::<1>,
     0x61 => PUSH2  => stack::push::<2>,
     0x62 => PUSH3  => stack::push::<3>,
@@ -193,16 +193,16 @@ opcodes! {
     0xA3 => LOG3 => host::log::<3>,
     0xA4 => LOG4 => host::log::<4>,
 
-    0xF0 => CREATE       => host::create::<false>,
-    0xF1 => CALL         => host::call,
-    0xF2 => CALLCODE     => host::call_code,
+    0xF0 => CREATE       => host::create::<false, SPEC>,
+    0xF1 => CALL         => host::call::<SPEC>,
+    0xF2 => CALLCODE     => host::call_code::<SPEC>,
     0xF3 => RETURN       => control::ret,
-    0xF4 => DELEGATECALL => host::delegate_call,
-    0xF5 => CREATE2      => host::create::<true>,
-    0xFA => STATICCALL   => host::static_call,
-    0xFD => REVERT       => control::revert,
+    0xF4 => DELEGATECALL => host::delegate_call::<SPEC>,
+    0xF5 => CREATE2      => host::create::<true, SPEC>,
+    0xFA => STATICCALL   => host::static_call::<SPEC>,
+    0xFD => REVERT       => control::revert::<SPEC>,
     0xFE => INVALID      => control::invalid,
-    0xFF => SELFDESTRUCT => host::selfdestruct,
+    0xFF => SELFDESTRUCT => host::selfdestruct::<SPEC>,
 }
 
 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
diff --git a/crates/interpreter/src/instructions/stack.rs b/crates/interpreter/src/instructions/stack.rs
index d73285b..aacfebd 100644
--- a/crates/interpreter/src/instructions/stack.rs
+++ b/crates/interpreter/src/instructions/stack.rs
@@ -1,6 +1,6 @@
 use super::prelude::*;
 
-pub(super) fn pop(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn pop(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     if let Some(ret) = interpreter.stack.reduce_one() {
         interpreter.instruction_result = ret;
@@ -10,19 +10,15 @@ pub(super) fn pop(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: Sp
 /// EIP-3855: PUSH0 instruction
 ///
 /// Introduce a new instruction which pushes the constant value 0 onto the stack.
-pub(super) fn push0(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, SHANGHAI));
+pub(super) fn push0<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(SHANGHAI));
     gas!(interpreter, gas::BASE);
     if let Err(result) = interpreter.stack.push(U256::ZERO) {
         interpreter.instruction_result = result;
     }
 }
 
-pub(super) fn push<const N: usize>(
-    interpreter: &mut Interpreter,
-    _host: &mut dyn Host,
-    _spec: SpecId,
-) {
+pub(super) fn push<const N: usize>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     let start = interpreter.instruction_pointer;
     // Safety: In Analysis we appended needed bytes for bytecode so that we are safe to just add without
@@ -37,22 +33,14 @@ pub(super) fn push<const N: usize>(
     interpreter.instruction_pointer = unsafe { start.add(N) };
 }
 
-pub(super) fn dup<const N: usize>(
-    interpreter: &mut Interpreter,
-    _host: &mut dyn Host,
-    _spec: SpecId,
-) {
+pub(super) fn dup<const N: usize>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     if let Some(ret) = interpreter.stack.dup::<N>() {
         interpreter.instruction_result = ret;
     }
 }
 
-pub(super) fn swap<const N: usize>(
-    interpreter: &mut Interpreter,
-    _host: &mut dyn Host,
-    _spec: SpecId,
-) {
+pub(super) fn swap<const N: usize>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     if let Some(ret) = interpreter.stack.swap::<N>() {
         interpreter.instruction_result = ret;
diff --git a/crates/interpreter/src/instructions/system.rs b/crates/interpreter/src/instructions/system.rs
index fa1ac9c..180b8f6 100644
--- a/crates/interpreter/src/instructions/system.rs
+++ b/crates/interpreter/src/instructions/system.rs
@@ -1,11 +1,7 @@
 use super::prelude::*;
 use core::cmp::min;
 
-pub(super) fn calculate_keccak256(
-    interpreter: &mut Interpreter,
-    _host: &mut dyn Host,
-    _spec: SpecId,
-) {
+pub(super) fn calculate_keccak256(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     pop!(interpreter, from, len);
     let len = as_usize_or_fail!(interpreter, len);
     gas_or_fail!(interpreter, gas::keccak256_cost(len as u64));
@@ -20,22 +16,22 @@ pub(super) fn calculate_keccak256(
     push_b256!(interpreter, hash);
 }
 
-pub(super) fn address(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn address(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push_b256!(interpreter, B256::from(interpreter.contract.address));
 }
 
-pub(super) fn caller(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn caller(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push_b256!(interpreter, B256::from(interpreter.contract.caller));
 }
 
-pub(super) fn codesize(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn codesize(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, U256::from(interpreter.contract.bytecode.len()));
 }
 
-pub(super) fn codecopy(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn codecopy(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     pop!(interpreter, memory_offset, code_offset, len);
     let len = as_usize_or_fail!(interpreter, len);
     gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64));
@@ -55,7 +51,7 @@ pub(super) fn codecopy(interpreter: &mut Interpreter, _host: &mut dyn Host, _spe
     );
 }
 
-pub(super) fn calldataload(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn calldataload(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::VERYLOW);
     pop!(interpreter, index);
     let index = as_usize_saturated!(index);
@@ -72,17 +68,17 @@ pub(super) fn calldataload(interpreter: &mut Interpreter, _host: &mut dyn Host,
     push_b256!(interpreter, load);
 }
 
-pub(super) fn calldatasize(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn calldatasize(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, U256::from(interpreter.contract.input.len()));
 }
 
-pub(super) fn callvalue(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn callvalue(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, interpreter.contract.value);
 }
 
-pub(super) fn calldatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn calldatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     pop!(interpreter, memory_offset, data_offset, len);
     let len = as_usize_or_fail!(interpreter, len);
     gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64));
@@ -100,9 +96,9 @@ pub(super) fn calldatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host,
 }
 
 // EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
-pub(super) fn returndatasize(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
+pub(super) fn returndatasize<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
-    check!(interpreter, SpecId::enabled(spec, BYZANTIUM));
+    check!(interpreter, SPEC::enabled(BYZANTIUM));
     push!(
         interpreter,
         U256::from(interpreter.return_data_buffer.len())
@@ -110,8 +106,8 @@ pub(super) fn returndatasize(interpreter: &mut Interpreter, _host: &mut dyn Host
 }
 
 // EIP-211: New opcodes: RETURNDATASIZE and RETURNDATACOPY
-pub(super) fn returndatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host, spec: SpecId) {
-    check!(interpreter, SpecId::enabled(spec, BYZANTIUM));
+pub(super) fn returndatacopy<SPEC: Spec>(interpreter: &mut Interpreter, _host: &mut dyn Host) {
+    check!(interpreter, SPEC::enabled(BYZANTIUM));
     pop!(interpreter, memory_offset, offset, len);
     let len = as_usize_or_fail!(interpreter, len);
     gas_or_fail!(interpreter, gas::verylowcopy_cost(len as u64));
@@ -131,7 +127,7 @@ pub(super) fn returndatacopy(interpreter: &mut Interpreter, _host: &mut dyn Host
     }
 }
 
-pub(super) fn gas(interpreter: &mut Interpreter, _host: &mut dyn Host, _spec: SpecId) {
+pub(super) fn gas(interpreter: &mut Interpreter, _host: &mut dyn Host) {
     gas!(interpreter, gas::BASE);
     push!(interpreter, U256::from(interpreter.gas.remaining()));
 }
diff --git a/crates/interpreter/src/interpreter/mod.rs b/crates/interpreter/src/interpreter/mod.rs
index a8214f6..2f824f3 100644
--- a/crates/interpreter/src/interpreter/mod.rs
+++ b/crates/interpreter/src/interpreter/mod.rs
@@ -8,7 +8,7 @@ pub use contract::Contract;
 pub use memory::Memory;
 pub use stack::{Stack, STACK_LIMIT};
 
-use crate::primitives::{Bytes, Spec, SpecId};
+use crate::primitives::{Bytes, Spec};
 use crate::{alloc::boxed::Box, opcode::eval, Gas, Host, InstructionResult};
 use core::ops::Range;
 
@@ -125,20 +125,20 @@ impl Interpreter {
 
     /// Execute next instruction
     #[inline(always)]
-    pub fn step(&mut self, host: &mut dyn Host, spec: SpecId) {
+    pub fn step<SPEC: Spec>(&mut self, host: &mut dyn Host) {
         // step.
         let opcode = unsafe { *self.instruction_pointer };
         // Safety: In analysis we are doing padding of bytecode so that we are sure that last
         // byte instruction is STOP so we are safe to just increment program_counter bcs on last instruction
         // it will do noop and just stop execution of this contract
         self.instruction_pointer = unsafe { self.instruction_pointer.offset(1) };
-        eval(opcode, self, host, spec);
+        eval::<SPEC>(opcode, self, host);
     }
 
     /// loop steps until we are finished with execution
     pub fn run<H: Host, SPEC: Spec>(&mut self, host: &mut H) -> InstructionResult {
         while self.instruction_result == InstructionResult::Continue {
-            self.step(host, SPEC::SPEC_ID);
+            self.step::<SPEC>(host);
         }
         self.instruction_result
     }
@@ -151,7 +151,7 @@ impl Interpreter {
             if ret != InstructionResult::Continue {
                 return ret;
             }
-            self.step(host, SPEC::SPEC_ID);
+            self.step::<SPEC>(host);
 
             // step ends
             let ret = host.step_end(self, self.instruction_result);
diff --git a/crates/revm/src/evm_impl.rs b/crates/revm/src/evm_impl.rs
index d1e40e7..b76baac 100644
--- a/crates/revm/src/evm_impl.rs
+++ b/crates/revm/src/evm_impl.rs
@@ -98,7 +98,7 @@ impl<'a, GSPEC: Spec, DB: Database, const INSPECT: bool> Transact<DB::Error>
         let effective_gas_price = env.effective_gas_price();
 
         let initial_gas_spend =
-            initial_tx_gas(&tx_data, tx_is_create, &env.tx.access_list, GSPEC::SPEC_ID);
+            initial_tx_gas::<GSPEC>(&tx_data, tx_is_create, &env.tx.access_list);
 
         // Additonal check to see if limit is big enought to cover initial gas.
         if env.tx.gas_limit < initial_gas_spend {

snailtracer/transact/analysed
                        time:   [44.178 ms 44.821 ms 45.376 ms]
snailtracer/eval        time:   [39.675 ms 39.893 ms 40.136 ms]


snailtracer/transact/analysed
                        time:   [45.129 ms 45.361 ms 45.728 ms]
snailtracer/eval        time:   [40.730 ms 40.839 ms 40.963 ms]


snailtracer/transact/analysed
                        time:   [44.938 ms 45.281 ms 45.617 ms]
snailtracer/eval        time:   [39.618 ms 39.820 ms 40.028 ms]

Non generic

3658fa7

snailtracer/transact/analysed                                         
                        time:   [40.656 ms 40.801 ms 40.935 ms]
snailtracer/eval        time:   [39.310 ms 39.482 ms 39.644 ms]


snailtracer/transact/analysed
                        time:   [40.673 ms 41.287 ms 41.683 ms]
snailtracer/eval        time:   [40.557 ms 41.455 ms 42.432 ms]


snailtracer/transact/analysed
                        time:   [40.800 ms 41.064 ms 41.357 ms]
snailtracer/eval        time:   [38.785 ms 39.001 ms 39.361 ms]

@DaniPopes DaniPopes force-pushed the static-interpreter branch 3 times, most recently from 2dd9e0c to 3658fa7 Compare July 25, 2023 02:06
@DaniPopes DaniPopes changed the title feat(interpreter): dispatch using static function array refactor(interpreter): opcodes, benches Jul 25, 2023
@DaniPopes DaniPopes marked this pull request as ready for review July 25, 2023 02:30
Copy link
Collaborator Author

@DaniPopes DaniPopes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General changes:

  • restrict visibility of items that are not exported to emphasize that they are private
  • omit error from as_usize_or_fail! macro calls when it's the default (InvalidOperandOOG)
  • rewrote benches from revm-test to criterion, and moved them to crates/revm/benches/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes:

  • cmp opcodes are now branchless
  • byte indexes into the slice instead of performing shifts
  • sar uses U256::MAX instead of computing it at runtime

Copy link
Collaborator Author

@DaniPopes DaniPopes Jul 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes:

  • Display should not be gated to std
  • avoid using manual implementations (like swap and pop) where possible by deferring to the standard library. This produces the same instructions while being safer

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes:

  • declare opcode constants, names (OPCODE_JUMPMAP) and the eval function in one macro call

@DaniPopes DaniPopes force-pushed the static-interpreter branch from 66c08a0 to c771cc1 Compare July 25, 2023 03:29
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes:

  • change is_none() + unwrap to use let else

@DaniPopes DaniPopes force-pushed the static-interpreter branch 2 times, most recently from 1a0b3d3 to b937b87 Compare July 26, 2023 20:13
@DaniPopes DaniPopes force-pushed the static-interpreter branch from b937b87 to d70c87b Compare July 29, 2023 11:11
`push_b256!` will convert the big-endian `[u8; 32]` to the U256 `[u64; 4]` at
runtime, so use `push!` to avoid doing that where possible.
@DaniPopes DaniPopes force-pushed the static-interpreter branch from f6f3fba to d89e56d Compare July 29, 2023 14:00
@DaniPopes DaniPopes changed the title refactor(interpreter): opcodes, benches refactor/perf: refactor some interpreter internals, rewrite benches Jul 29, 2023
@DaniPopes DaniPopes force-pushed the static-interpreter branch from d89e56d to c657758 Compare July 29, 2023 16:44
@DaniPopes DaniPopes force-pushed the static-interpreter branch from 4f5e0ba to 2a06916 Compare July 30, 2023 20:43
Copy link
Member

@rakita rakita left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DaniPopes you made too many changes here to be accepted as it is, and I don't agree with some of them

@DaniPopes DaniPopes force-pushed the static-interpreter branch from 2a06916 to c536272 Compare July 31, 2023 10:08
@DaniPopes
Copy link
Collaborator Author

@rakita Sure makes sense, what do you want me to split off / change?

@DaniPopes DaniPopes changed the title refactor/perf: refactor some interpreter internals, rewrite benches perf: refactor interpreter internals Aug 3, 2023
@DaniPopes DaniPopes marked this pull request as draft August 3, 2023 15:30
@DaniPopes
Copy link
Collaborator Author

Superseded by #579, #581, and mainly #582.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New dispatch strategy: Add array of instruction handles

2 participants