Skip to main content

revmc_context/
jit_evm.rs

1//! Generic `revm` JIT EVM.
2//!
3//! Provides [`JitEvm`] which wraps any [`EvmTr`]-based EVM and overrides
4//! `frame_run` to dispatch to JIT-compiled functions by code hash,
5//! falling back to the interpreter for unknown contracts.
6
7use revm_context_interface::{
8    ContextSetters, ContextTr,
9    journaled_state::JournalTr,
10    result::{EVMError, HaltReason, InvalidTransaction, ResultAndState},
11};
12use revm_handler::{
13    EthFrame, EvmTr, ExecuteEvm, FrameInitOrResult, FrameTr, Handler, ItemOrResult, MainnetHandler,
14    PrecompileProvider, evm::ContextDbError,
15};
16use revm_inspector::{
17    InspectCommitEvm, InspectEvm, Inspector, InspectorEvmTr, InspectorFrame, InspectorHandler,
18    JournalExt,
19};
20use revm_interpreter::{InstructionResult, InterpreterAction, InterpreterResult, InterpreterTypes};
21use revm_primitives::{B256, map::B256Map};
22use revm_state::EvmState;
23
24use crate::{EvmCompilerFn, RawEvmCompilerFn};
25
26/// Wrapper around any [`EvmTr`] that overrides [`EvmTr::frame_run`] to dispatch
27/// to JIT-compiled functions by code hash, falling back to the interpreter.
28///
29/// The `F` parameter is a lookup function `(B256, &[u8]) -> Option<EvmCompilerFn>` called
30/// when a code hash is not found in the precompiled map. Return `None` to fall
31/// back to the interpreter; return `Some(f)` to execute `f` (e.g. compile on the fly).
32#[allow(missing_debug_implementations)]
33pub struct JitEvm<EVM, F = fn(B256, &[u8]) -> Option<EvmCompilerFn>> {
34    inner: EVM,
35    functions: B256Map<RawEvmCompilerFn>,
36    on_miss: F,
37}
38
39fn no_miss(_: B256, _: &[u8]) -> Option<EvmCompilerFn> {
40    None
41}
42
43impl<EVM> JitEvm<EVM> {
44    /// Create a new JIT EVM wrapper that falls back to the interpreter on miss.
45    pub fn new(inner: EVM, functions: B256Map<RawEvmCompilerFn>) -> Self {
46        Self { inner, functions, on_miss: no_miss }
47    }
48}
49
50impl<EVM, F> JitEvm<EVM, F> {
51    /// Create a new JIT EVM wrapper with a custom miss handler.
52    pub fn with_on_miss(inner: EVM, functions: B256Map<RawEvmCompilerFn>, on_miss: F) -> Self {
53        Self { inner, functions, on_miss }
54    }
55
56    /// Consumes the wrapper and returns the inner EVM.
57    pub fn into_inner(self) -> EVM {
58        self.inner
59    }
60}
61
62impl<EVM, F> core::ops::Deref for JitEvm<EVM, F> {
63    type Target = EVM;
64    fn deref(&self) -> &EVM {
65        &self.inner
66    }
67}
68
69impl<EVM, F> core::ops::DerefMut for JitEvm<EVM, F> {
70    fn deref_mut(&mut self) -> &mut EVM {
71        &mut self.inner
72    }
73}
74
75impl<EVM, F> EvmTr for JitEvm<EVM, F>
76where
77    EVM: EvmTr<
78            Frame = EthFrame,
79            Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
80        >,
81    F: FnMut(B256, &[u8]) -> Option<EvmCompilerFn>,
82{
83    type Context = EVM::Context;
84    type Instructions = EVM::Instructions;
85    type Precompiles = EVM::Precompiles;
86    type Frame = EVM::Frame;
87
88    fn all(
89        &self,
90    ) -> (
91        &Self::Context,
92        &Self::Instructions,
93        &Self::Precompiles,
94        &revm_context_interface::FrameStack<Self::Frame>,
95    ) {
96        self.inner.all()
97    }
98
99    fn all_mut(
100        &mut self,
101    ) -> (
102        &mut Self::Context,
103        &mut Self::Instructions,
104        &mut Self::Precompiles,
105        &mut revm_context_interface::FrameStack<Self::Frame>,
106    ) {
107        self.inner.all_mut()
108    }
109
110    fn frame_init(
111        &mut self,
112        frame_input: <Self::Frame as FrameTr>::FrameInit,
113    ) -> Result<
114        ItemOrResult<&mut Self::Frame, <Self::Frame as FrameTr>::FrameResult>,
115        ContextDbError<Self::Context>,
116    > {
117        self.inner.frame_init(frame_input)
118    }
119
120    fn frame_run(
121        &mut self,
122    ) -> Result<FrameInitOrResult<Self::Frame>, ContextDbError<Self::Context>> {
123        let frame = self.inner.frame_stack().get();
124        let code_hash = frame.interpreter.bytecode.get_or_calculate_hash();
125
126        let f = if let Some(&raw_fn) = self.functions.get(&code_hash) {
127            Some(EvmCompilerFn::new(raw_fn))
128        } else {
129            let code = frame.interpreter.bytecode.original_bytes();
130            (self.on_miss)(code_hash, &code)
131        };
132
133        if let Some(f) = f {
134            let (ctx, _, _, frame_stack) = self.inner.all_mut();
135            let frame = frame_stack.get();
136            let action = unsafe { f.call_with_interpreter(&mut frame.interpreter, ctx) };
137            Ok(frame.process_next_action::<_, ContextDbError<Self::Context>>(ctx, action).inspect(
138                |i| {
139                    if i.is_result() {
140                        frame.set_finished(true);
141                    }
142                },
143            )?)
144        } else {
145            self.inner.frame_run()
146        }
147    }
148
149    fn frame_return_result(
150        &mut self,
151        result: <Self::Frame as FrameTr>::FrameResult,
152    ) -> Result<Option<<Self::Frame as FrameTr>::FrameResult>, ContextDbError<Self::Context>> {
153        self.inner.frame_return_result(result)
154    }
155}
156
157impl<EVM, F> ExecuteEvm for JitEvm<EVM, F>
158where
159    EVM: EvmTr<
160            Frame = EthFrame,
161            Context: ContextTr<Journal: JournalTr<State = EvmState>> + ContextSetters,
162            Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
163        >,
164    F: FnMut(B256, &[u8]) -> Option<EvmCompilerFn>,
165{
166    type ExecutionResult = revm_context_interface::result::ExecutionResult<HaltReason>;
167    type State = EvmState;
168    type Error = EVMError<
169        <<EVM::Context as ContextTr>::Db as revm_context_interface::Database>::Error,
170        InvalidTransaction,
171    >;
172    type Tx = <EVM::Context as ContextTr>::Tx;
173    type Block = <EVM::Context as ContextTr>::Block;
174
175    fn transact_one(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
176        self.ctx_mut().set_tx(tx);
177        MainnetHandler::default().run(self)
178    }
179
180    fn finalize(&mut self) -> Self::State {
181        self.ctx_mut().journal_mut().finalize()
182    }
183
184    fn set_block(&mut self, block: Self::Block) {
185        self.ctx_mut().set_block(block);
186    }
187
188    fn replay(&mut self) -> Result<ResultAndState<HaltReason>, Self::Error> {
189        MainnetHandler::default().run(self).map(|result| {
190            let state = self.ctx_mut().journal_mut().finalize();
191            ResultAndState::new(result, state)
192        })
193    }
194}
195
196impl<EVM, F> InspectorEvmTr for JitEvm<EVM, F>
197where
198    EVM: EvmTr<
199            Frame = EthFrame,
200            Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
201        > + InspectorEvmTr,
202    F: FnMut(B256, &[u8]) -> Option<EvmCompilerFn>,
203{
204    type Inspector = <EVM as InspectorEvmTr>::Inspector;
205
206    #[inline]
207    fn all_inspector(
208        &self,
209    ) -> (
210        &Self::Context,
211        &Self::Instructions,
212        &Self::Precompiles,
213        &revm_context_interface::FrameStack<Self::Frame>,
214        &Self::Inspector,
215    ) {
216        self.inner.all_inspector()
217    }
218
219    #[inline]
220    fn all_mut_inspector(
221        &mut self,
222    ) -> (
223        &mut Self::Context,
224        &mut Self::Instructions,
225        &mut Self::Precompiles,
226        &mut revm_context_interface::FrameStack<Self::Frame>,
227        &mut Self::Inspector,
228    ) {
229        self.inner.all_mut_inspector()
230    }
231
232    #[inline]
233    fn inspect_frame_run(
234        &mut self,
235    ) -> Result<FrameInitOrResult<Self::Frame>, ContextDbError<Self::Context>> {
236        let frame = self.inner.frame_stack().get();
237        let code_hash = frame.interpreter.bytecode.get_or_calculate_hash();
238
239        let f = if let Some(&raw_fn) = self.functions.get(&code_hash) {
240            Some(EvmCompilerFn::new(raw_fn))
241        } else {
242            let code = frame.interpreter.bytecode.original_bytes();
243            (self.on_miss)(code_hash, &code)
244        };
245
246        let Some(f) = f else {
247            return self.inner.inspect_frame_run();
248        };
249
250        // Set up the on_log callback to forward logs to the inspector during JIT execution.
251        //
252        // SAFETY: We dereference raw pointers to the inspector and context inside the closure.
253        // This creates aliasing &mut references with `ecx.host` (which borrows the context),
254        // but the inspector's `log()` implementation only accesses the inspector itself,
255        // not the journaled state already borrowed by the host.
256        let (ctx, inspector) = self.ctx_inspector();
257        let ctx_ptr: *mut EVM::Context = ctx;
258        let inspector_ptr: *mut <EVM as InspectorEvmTr>::Inspector = inspector;
259        let mut on_log = move |log: &revm_primitives::Log| unsafe {
260            (*inspector_ptr).log(&mut *ctx_ptr, log.clone());
261        };
262
263        let (ctx, _, _, frame_stack) = self.inner.all_mut();
264        let frame = frame_stack.get();
265        let action = unsafe {
266            f.call_with_interpreter_with(&mut frame.interpreter, ctx, |ecx| {
267                // SAFETY: `on_log` lives on the stack and outlives the JIT call.
268                // The closure captures raw pointers whose types may not be
269                // `'static`, so we erase the lifetime via pointer cast.
270                ecx.on_log = Some(core::mem::transmute::<
271                    &mut dyn FnMut(&revm_primitives::Log),
272                    &mut (dyn FnMut(&revm_primitives::Log) + '_),
273                >(&mut on_log));
274            })
275        };
276
277        // Handle selfdestruct.
278        let (ctx, inspector) = self.ctx_inspector();
279        if let InterpreterAction::Return(result) = &action
280            && result.result == InstructionResult::SelfDestruct
281        {
282            inspect_selfdestruct(ctx, inspector);
283        }
284
285        let (ctx, _, _, frame_stack) = self.inner.all_mut();
286        let frame = frame_stack.get();
287        let mut result = frame.process_next_action::<_, ContextDbError<Self::Context>>(ctx, action);
288
289        if let Ok(ItemOrResult::Result(frame_result)) = &mut result {
290            let (ctx, inspector, frame) = self.ctx_inspector_frame();
291            if let Some(frame) = frame.eth_frame() {
292                revm_inspector::handler::frame_end(ctx, inspector, &frame.input, frame_result);
293                frame.set_finished(true);
294            }
295        }
296        result
297    }
298}
299
300impl<EVM, F> InspectEvm for JitEvm<EVM, F>
301where
302    EVM: EvmTr<
303            Frame = EthFrame,
304            Context: ContextTr<Journal: JournalTr<State = EvmState> + JournalExt> + ContextSetters,
305            Precompiles: PrecompileProvider<EVM::Context, Output = InterpreterResult>,
306        > + InspectorEvmTr,
307    F: FnMut(B256, &[u8]) -> Option<EvmCompilerFn>,
308{
309    type Inspector = <EVM as InspectorEvmTr>::Inspector;
310
311    #[inline]
312    fn set_inspector(&mut self, inspector: Self::Inspector) {
313        *self.inner.inspector() = inspector;
314    }
315
316    #[inline]
317    fn inspect_one_tx(&mut self, tx: Self::Tx) -> Result<Self::ExecutionResult, Self::Error> {
318        self.ctx_mut().set_tx(tx);
319        MainnetHandler::default().inspect_run(self)
320    }
321}
322
323impl<EVM, F> InspectCommitEvm for JitEvm<EVM, F> where
324    Self: InspectEvm + revm_handler::ExecuteCommitEvm
325{
326}
327
328#[inline(never)]
329#[cold]
330fn inspect_selfdestruct<CTX, IT>(context: &mut CTX, inspector: &mut impl Inspector<CTX, IT>)
331where
332    CTX: ContextTr<Journal: JournalExt> + revm_context_interface::Host,
333    IT: InterpreterTypes,
334{
335    use revm_context::JournalEntry;
336
337    if let Some(
338        JournalEntry::AccountDestroyed {
339            address: contract, target: to, had_balance: balance, ..
340        }
341        | JournalEntry::BalanceTransfer { from: contract, to, balance, .. },
342    ) = context.journal_mut().journal().last()
343    {
344        inspector.selfdestruct(*contract, *to, *balance);
345    }
346}