revm_interpreter/
table.rs

1#![allow(clippy::wrong_self_convention)]
2
3use crate::{
4    instructions::{control, instruction},
5    interpreter::Interpreter,
6    interpreter_types::InterpreterTypes,
7    Host,
8};
9use std::boxed::Box;
10
11/// EVM opcode function signature.
12pub type Instruction<W, H> = for<'a> fn(&'a mut Interpreter<W>, &'a mut H);
13
14/// Instruction table is list of instruction function pointers mapped to 256 EVM opcodes.
15pub type InstructionTable<W, H> = [Instruction<W, H>; 256];
16
17/// A table of boxed instructions.
18pub type CustomInstructionTable<IT> = [IT; 256];
19
20pub trait CustomInstruction {
21    type Wire: InterpreterTypes;
22    type Host;
23
24    fn exec(&self, interpreter: &mut Interpreter<Self::Wire>, host: &mut Self::Host);
25
26    fn from_base(instruction: Instruction<Self::Wire, Self::Host>) -> Self;
27}
28
29/// Either a plain, static instruction table, or a boxed, dynamic instruction table.
30///
31/// Note that `Plain` variant is about 10-20% faster in Interpreter execution.
32pub enum InstructionTables<
33    W: InterpreterTypes,
34    H: ?Sized,
35    CI: CustomInstruction<Host = H, Wire = W>,
36> {
37    Plain(Box<InstructionTable<W, H>>),
38    Custom(Box<CustomInstructionTable<CI>>),
39}
40
41impl<WIRE, H, CI> InstructionTables<WIRE, H, CI>
42where
43    WIRE: InterpreterTypes,
44    H: Host + ?Sized,
45    CI: CustomInstruction<Host = H, Wire = WIRE>,
46{
47    /// Inserts the instruction into the table with the specified index.
48    #[inline]
49    pub fn insert(&mut self, opcode: u8, instruction: Instruction<WIRE, H>) {
50        match self {
51            Self::Plain(table) => table[opcode as usize] = instruction,
52            Self::Custom(table) => table[opcode as usize] = CI::from_base(instruction),
53        }
54    }
55
56    /// Converts the current instruction table to a boxed variant if it is not already, and returns
57    /// a mutable reference to the boxed table.
58    #[inline]
59    pub fn to_custom(&mut self) -> &mut CustomInstructionTable<CI> {
60        self.to_custom_with(|i| CI::from_base(i))
61    }
62
63    /// Converts the current instruction table to a boxed variant if it is not already with `f`,
64    /// and returns a mutable reference to the boxed table.
65    #[inline]
66    pub fn to_custom_with<F>(&mut self, f: F) -> &mut CustomInstructionTable<CI>
67    where
68        F: FnMut(Instruction<WIRE, H>) -> CI,
69    {
70        match self {
71            Self::Plain(_) => self.to_custom_with_slow(f),
72            Self::Custom(boxed) => boxed,
73        }
74    }
75
76    #[cold]
77    fn to_custom_with_slow<F>(&mut self, f: F) -> &mut CustomInstructionTable<CI>
78    where
79        F: FnMut(Instruction<WIRE, H>) -> CI,
80    {
81        let Self::Plain(table) = self else {
82            unreachable!()
83        };
84        *self = Self::Custom(Box::new(make_custom_instruction_table(table, f)));
85        let Self::Custom(boxed) = self else {
86            unreachable!()
87        };
88        boxed
89    }
90
91    /// Returns a mutable reference to the boxed instruction at the specified index.
92    #[inline]
93    pub fn get_custom(&mut self, opcode: u8) -> &mut CI {
94        &mut self.to_custom()[opcode as usize]
95    }
96
97    /// Inserts a boxed instruction into the table at the specified index.
98    #[inline]
99    pub fn insert_custom(&mut self, opcode: u8, instruction: CI) {
100        *self.get_custom(opcode) = instruction;
101    }
102
103    /// Replaces a boxed instruction into the table at the specified index, returning the previous
104    /// instruction.
105    #[inline]
106    pub fn replace_boxed(&mut self, opcode: u8, instruction: CI) -> CI {
107        core::mem::replace(self.get_custom(opcode), instruction)
108    }
109}
110
111/// Make instruction table.
112#[inline]
113pub const fn make_instruction_table<WIRE: InterpreterTypes, H: Host + ?Sized>(
114) -> InstructionTable<WIRE, H> {
115    let mut table: InstructionTable<WIRE, H> = [control::unknown; 256];
116    let mut i = 0;
117    while i < 256 {
118        table[i] = instruction::<WIRE, H>(i as u8);
119        i += 1;
120    }
121    table
122}
123
124/// Make boxed instruction table that calls `f` closure for every instruction.
125#[inline]
126pub fn make_custom_instruction_table<W, H, FN, CI: CustomInstruction<Wire = W, Host = H>>(
127    table: &InstructionTable<W, H>,
128    mut f: FN,
129) -> CustomInstructionTable<CI>
130where
131    W: InterpreterTypes,
132    H: Host + ?Sized,
133    FN: FnMut(Instruction<W, H>) -> CI,
134{
135    core::array::from_fn(|i| f(table[i]))
136}
137
138// TODO
139// /// Updates a boxed instruction with a new one.
140// #[inline]
141// pub fn update_custom_instruction<W, H, F>(
142//     instruction: &mut impl CustomInstruction<Wire = W, Host = H>,
143//     f: F,
144// ) where
145//     W: InterpreterTypes,
146//     H: Host + ?Sized,
147//     F: Fn(&DynInstruction<W, H>, &mut W, &mut H),
148// {
149//     // Note: This first allocation gets elided by the compiler.
150//     let prev = core::mem::replace(instruction, Box::new(|_, _| {}));
151//     *instruction = Box::new(move |i, h| f(&prev, i, h));
152// }