1#[cfg(feature = "jit")]
2use alloc::string::String;
3use alloc::sync::Arc;
4use alloy_consensus::Header;
5use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks};
6#[cfg(feature = "std")]
7use reth_evm::precompile_cache::{
8 CachedPrecompileProvider, PrecompileCacheMap, PrecompileCachePolicy,
9};
10
11#[cfg(feature = "jit")]
12pub use evm2_jit::{
13 runtime::{
14 maybe_run_jit_helper, CompilationEvent, CompilationKind, JitBackend, JitMode,
15 RuntimeConfig, RuntimeStatsSnapshot, RuntimeTuning,
16 },
17 CompileTimings,
18};
19
20use crate::{
21 executor::{EthBigBlockExecutor, EthBigBlockPlan},
22 EthBlockExecutionCtx, EthBlockExecutor, EthEvmEnv, RethReceiptBuilder,
23};
24use reth_ethereum_primitives::{Receipt, TransactionSigned};
25use reth_evm::ReceiptBuilder;
26
27pub trait EvmFactory: Clone + core::fmt::Debug + Send + Sync + Unpin + 'static
31where
32 <Self::Types as evm2::EvmTypesHost>::BlockEnvExt: Send + Sync,
33 <Self::Types as evm2::EvmTypesHost>::EvmExt: Default,
34 <Self::Types as evm2::EvmTypesHost>::TxResultExt: Send + Sync,
35{
36 type Types: evm2::EvmTypes + evm2::EvmTypesHost<SpecId = Self::SpecId>;
38
39 type SpecId: Copy
41 + Into<evm2::SpecId>
42 + Eq
43 + core::hash::Hash
44 + core::fmt::Debug
45 + Send
46 + Sync
47 + Clone
48 + 'static;
49
50 fn spec_id(&self, spec: evm2::SpecId) -> Self::SpecId;
52
53 fn block_env(&self, block: evm2::env::BlockEnv) -> evm2::env::BlockEnv<Self::Types> {
55 block.with_ext(Default::default())
56 }
57
58 fn version(&self, spec: evm2::SpecId, chain_id: u64) -> evm2::Version {
60 let mut version = evm2::Version::new(spec);
61 version.chain_id = chain_id;
62 version
63 }
64
65 fn execution_config(
67 &self,
68 spec: Self::SpecId,
69 version: evm2::Version,
70 ) -> evm2::ExecutionConfig<Self::Types> {
71 evm2::ExecutionConfig::for_spec_and_version(spec, version)
72 }
73
74 fn tx_registry(
76 &self,
77 spec: Self::SpecId,
78 ) -> evm2::registry::TxRegistry<Self::Types, evm2::TxResult<Self::Types>>;
79
80 fn precompiles(&self, spec: Self::SpecId) -> evm2::Precompiles<Self::Types> {
82 evm2::Precompiles::base(spec.into())
83 }
84
85 fn configure_evm(&self, _evm: &mut evm2::Evm<'_, Self::Types>) {}
87
88 #[cfg(feature = "std")]
90 fn precompile_cache_policy(&self, _spec: Self::SpecId) -> PrecompileCachePolicy {
91 PrecompileCachePolicy::Disabled
92 }
93}
94
95#[derive(Debug)]
97pub struct EthBlockExecutorFactory<R = RethReceiptBuilder, C = ChainSpec, F = RethEvmFactory>
98where
99 F: EvmFactory,
100{
101 receipt_builder: R,
103 chain_spec: Arc<C>,
105 evm_factory: F,
107}
108
109#[derive(Debug, Clone)]
111pub struct EthBigBlockExecutorFactory<C = ChainSpec, F = RethEvmFactory>
112where
113 F: EvmFactory,
114{
115 inner: EthBlockExecutorFactory<RethReceiptBuilder, C, F>,
116}
117
118impl<C, F: EvmFactory> EthBigBlockExecutorFactory<C, F> {
119 pub const fn new(inner: EthBlockExecutorFactory<RethReceiptBuilder, C, F>) -> Self {
121 Self { inner }
122 }
123
124 pub const fn inner(&self) -> &EthBlockExecutorFactory<RethReceiptBuilder, C, F> {
126 &self.inner
127 }
128}
129
130impl<C, F> reth_evm::BlockExecutorFactory for EthBigBlockExecutorFactory<C, F>
131where
132 C: EthChainSpec<Header = Header> + EthereumHardforks,
133 F: EvmFactory,
134 F::Types: evm2::EvmTypes<Tx = evm2::ethereum::TxEnvelope>,
135{
136 type EvmFactory = F;
137 type EvmTypes = F::Types;
138 type Transaction = TransactionSigned;
139 type Receipt = Receipt;
140 type Evm<'a> = evm2::Evm<'a, F::Types>;
141 type EvmEnv = EthEvmEnv<F::Types>;
142 type ExecutionCtx<'a>
143 = EthBigBlockPlan<'a, F::Types>
144 where
145 Self: 'a;
146 type Executor<'a>
147 = EthBigBlockExecutor<'a, C, F>
148 where
149 Self: 'a;
150
151 fn create_executor<'a>(
152 &'a self,
153 evm: Self::Evm<'a>,
154 ctx: Self::ExecutionCtx<'a>,
155 ) -> Self::Executor<'a>
156 where
157 Self: 'a,
158 {
159 let executor = EthBlockExecutor::new(
160 evm,
161 ctx.segments[0].ctx.clone(),
162 self.inner.chain_spec().as_ref(),
163 self.inner.receipt_builder(),
164 );
165 EthBigBlockExecutor::new(executor, &self.inner, self.inner.chain_spec().clone(), ctx)
166 }
167
168 fn evm_factory(&self) -> &Self::EvmFactory {
169 self.inner.evm_factory()
170 }
171
172 fn evm_with_env<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
173 where
174 DB: evm2::evm::DynDatabase + 'a,
175 {
176 self.inner.evm_with_env(db, evm_env)
177 }
178}
179
180impl<R: Clone, C, F: EvmFactory> Clone for EthBlockExecutorFactory<R, C, F> {
181 fn clone(&self) -> Self {
182 Self {
183 receipt_builder: self.receipt_builder.clone(),
184 chain_spec: self.chain_spec.clone(),
185 evm_factory: self.evm_factory.clone(),
186 }
187 }
188}
189
190impl<C> EthBlockExecutorFactory<RethReceiptBuilder, C> {
191 pub fn new(chain_spec: Arc<C>) -> Self {
193 Self::new_with_evm_factory(chain_spec, RethEvmFactory::default())
194 }
195}
196
197impl<C, F: EvmFactory> EthBlockExecutorFactory<RethReceiptBuilder, C, F> {
198 pub const fn new_with_evm_factory(chain_spec: Arc<C>, evm_factory: F) -> Self {
200 Self::new_with_receipt_builder(RethReceiptBuilder, chain_spec, evm_factory)
201 }
202}
203
204impl<R, C, F: EvmFactory> EthBlockExecutorFactory<R, C, F> {
205 pub const fn new_with_receipt_builder(
207 receipt_builder: R,
208 chain_spec: Arc<C>,
209 evm_factory: F,
210 ) -> Self {
211 Self { receipt_builder, chain_spec, evm_factory }
212 }
213
214 pub const fn chain_spec(&self) -> &Arc<C> {
216 &self.chain_spec
217 }
218
219 pub const fn receipt_builder(&self) -> &R {
221 &self.receipt_builder
222 }
223
224 pub const fn evm_factory(&self) -> &F {
226 &self.evm_factory
227 }
228
229 #[cfg(feature = "jit")]
231 pub(crate) const fn evm_factory_mut(&mut self) -> &mut F {
232 &mut self.evm_factory
233 }
234
235 pub(crate) fn build_evm_with_env<'a, DB>(
237 &self,
238 db: DB,
239 env: EthEvmEnv<F::Types>,
240 ) -> evm2::Evm<'a, F::Types>
241 where
242 C: EthChainSpec<Header = Header>,
243 DB: evm2::evm::DynDatabase + 'a,
244 F: 'static,
245 {
246 let precompiles = self.evm_factory.precompiles(env.spec);
247 self.build_evm(db, env, precompiles)
248 }
249
250 #[cfg(feature = "std")]
255 pub(crate) fn build_evm_with_precompile_cache<'a, DB>(
256 &self,
257 db: DB,
258 env: EthEvmEnv<F::Types>,
259 cache_map: PrecompileCacheMap<F::SpecId>,
260 metrics_enabled: bool,
261 ) -> evm2::Evm<'a, F::Types>
262 where
263 C: EthChainSpec<Header = Header>,
264 DB: evm2::evm::DynDatabase + 'a,
265 F: 'static,
266 {
267 let spec = env.spec;
268 let precompiles = CachedPrecompileProvider::new(
269 self.evm_factory.precompiles(spec),
270 cache_map,
271 spec,
272 self.evm_factory.precompile_cache_policy(spec),
273 metrics_enabled,
274 );
275 self.build_evm(db, env, precompiles)
276 }
277
278 fn build_evm<'a, DB>(
279 &self,
280 db: DB,
281 env: EthEvmEnv<F::Types>,
282 precompiles: impl evm2::evm::precompile::PrecompileProvider<F::Types> + 'a,
283 ) -> evm2::Evm<'a, F::Types>
284 where
285 DB: evm2::evm::DynDatabase + 'a,
286 {
287 let spec = env.spec;
288 let config = self.evm_factory.execution_config(spec, env.version);
289 let registry = self.evm_factory.tx_registry(spec);
290
291 let mut evm = evm2::Evm::<F::Types>::new_with_execution_config(
292 config,
293 spec,
294 env.block,
295 registry,
296 db,
297 precompiles,
298 );
299 self.evm_factory.configure_evm(&mut evm);
300 evm
301 }
302
303 pub(crate) fn reconfigure_evm<'a>(
309 &'a self,
310 evm: &mut evm2::Evm<'a, F::Types>,
311 env: &EthEvmEnv<F::Types>,
312 ) {
313 let spec = env.spec;
314 let config = self.evm_factory.execution_config(spec, env.version);
315 if evm.config_spec_id() == spec {
316 evm.set_block_and_execution_config_within_spec(env.block, config);
317 return
318 }
319
320 let registry = self.evm_factory.tx_registry(spec);
321 let precompiles = self.evm_factory.precompiles(spec);
322
323 evm.set_block_and_execution_config(env.block, config, spec, registry, precompiles);
324 self.evm_factory.configure_evm(evm);
325 }
326}
327
328impl<R, C, F> reth_evm::BlockExecutorFactory for EthBlockExecutorFactory<R, C, F>
329where
330 C: EthChainSpec<Header = Header> + EthereumHardforks,
331 F: EvmFactory,
332 R: ReceiptBuilder,
333
334 <F::Types as evm2::EvmTypesHost>::Tx: alloy_eips::eip2718::Typed2718,
335{
336 type EvmFactory = F;
337 type EvmTypes = F::Types;
338 type Transaction = R::Transaction;
339 type Receipt = R::Receipt;
340 type Evm<'a> = evm2::Evm<'a, F::Types>;
341 type EvmEnv = EthEvmEnv<F::Types>;
342 type ExecutionCtx<'a>
343 = EthBlockExecutionCtx<'a>
344 where
345 Self: 'a;
346 type Executor<'a>
347 = EthBlockExecutor<'a, F::Types, &'a R>
348 where
349 Self: 'a;
350
351 fn create_executor<'a>(
352 &'a self,
353 evm: Self::Evm<'a>,
354 ctx: Self::ExecutionCtx<'a>,
355 ) -> Self::Executor<'a>
356 where
357 Self: 'a,
358 {
359 EthBlockExecutor::new(evm, ctx, self.chain_spec.as_ref(), &self.receipt_builder)
360 }
361
362 fn evm_factory(&self) -> &Self::EvmFactory {
363 Self::evm_factory(self)
364 }
365
366 fn evm_with_env<'a, DB>(&self, db: DB, evm_env: Self::EvmEnv) -> Self::Evm<'a>
367 where
368 DB: evm2::evm::DynDatabase + 'a,
369 {
370 self.build_evm_with_env(db, evm_env)
371 }
372}
373
374#[derive(Debug, Clone)]
380pub struct RethEvmFactory {
381 #[cfg(feature = "jit")]
382 backend: JitBackend,
383 #[cfg(feature = "jit")]
384 metrics: JitMetrics,
385 #[cfg(feature = "jit")]
386 jit_support: bool,
387}
388
389impl Default for RethEvmFactory {
390 fn default() -> Self {
391 Self::disabled()
392 }
393}
394
395impl RethEvmFactory {
396 #[cfg_attr(not(feature = "jit"), allow(clippy::missing_const_for_fn))]
398 pub fn disabled() -> Self {
399 #[cfg(feature = "jit")]
400 {
401 Self::new(JitBackend::disabled())
402 }
403 #[cfg(not(feature = "jit"))]
404 {
405 Self {}
406 }
407 }
408}
409
410impl EvmFactory for RethEvmFactory {
411 type Types = evm2::BaseEvmTypes;
412 type SpecId = evm2::SpecId;
413
414 fn spec_id(&self, spec: evm2::SpecId) -> evm2::SpecId {
415 spec
416 }
417
418 #[cfg(feature = "std")]
419 fn precompile_cache_policy(&self, _spec: Self::SpecId) -> PrecompileCachePolicy {
420 PrecompileCachePolicy::All
421 }
422
423 fn tx_registry(
424 &self,
425 spec: Self::SpecId,
426 ) -> evm2::registry::TxRegistry<Self::Types, evm2::TxResult<Self::Types>> {
427 evm2::ethereum::ethereum_tx_registry(spec)
428 }
429
430 fn configure_evm(&self, evm: &mut evm2::Evm<'_, Self::Types>) {
431 #[cfg(feature = "jit")]
432 self.configure_evm(evm);
433
434 #[cfg(not(feature = "jit"))]
435 let _ = evm;
436 }
437}
438
439#[cfg(feature = "jit")]
440impl RethEvmFactory {
441 pub fn new(backend: JitBackend) -> Self {
443 Self::new_with_metrics(backend, JitMetrics::default())
444 }
445
446 pub const fn new_with_metrics(backend: JitBackend, metrics: JitMetrics) -> Self {
448 Self { backend, metrics, jit_support: false }
449 }
450
451 pub const fn backend(&self) -> &JitBackend {
453 &self.backend
454 }
455
456 pub const fn set_jit_support(&mut self, enabled: bool) {
458 self.jit_support = enabled;
459 }
460
461 pub const fn jit_support_enabled(&self) -> bool {
463 self.jit_support
464 }
465
466 fn configure_evm(&self, evm: &mut evm2::Evm<'_, evm2::BaseEvmTypes>) {
468 if self.jit_support_enabled() {
469 evm.set_interpreter_runner(evm2_jit::evm2_evm::JitInterpreterRunner::new(
470 self.backend.clone(),
471 ));
472 }
473 }
474
475 fn pause_jit(&self) {
477 let was_paused = self.backend.is_paused();
478 self.backend.pause();
479 let is_paused = self.backend.is_paused();
480 if !was_paused && is_paused {
481 self.metrics.pauses_total.increment(1);
482 }
483 self.metrics.paused.set(is_paused as u8 as f64);
484 }
485
486 fn resume_jit(&self) {
488 let was_paused = self.backend.is_paused();
489 self.backend.resume();
490 let is_paused = self.backend.is_paused();
491 if was_paused && !is_paused {
492 self.metrics.resumes_total.increment(1);
493 }
494 self.metrics.paused.set(is_paused as u8 as f64);
495 }
496}
497
498#[cfg(feature = "jit")]
499impl reth_evm::JitBackend for RethEvmFactory {
500 fn set_enabled(&self, enabled: bool) -> Result<(), String> {
501 self.backend.set_enabled(enabled).map_err(|err| err.to_string())
502 }
503
504 fn pause(&self) {
505 self.pause_jit();
506 }
507
508 fn resume(&self) {
509 self.resume_jit();
510 }
511
512 fn clear(&self) {
513 self.backend.clear_all();
514 }
515}
516
517#[cfg(feature = "jit")]
519#[derive(reth_metrics::Metrics, Clone)]
520#[metrics(scope = "evm2.jit")]
521pub struct JitMetrics {
522 pub lookup_hits: metrics::Gauge,
524 pub lookup_misses: metrics::Gauge,
526 pub events_queued: metrics::Gauge,
528 pub events_dropped: metrics::Gauge,
530 pub commands_dropped: metrics::Gauge,
532 pub resident_entries: metrics::Gauge,
534 pub jit_code_bytes: metrics::Gauge,
536 pub jit_data_bytes: metrics::Gauge,
538 pub command_queue_len: metrics::Gauge,
540 pub pending_jobs: metrics::Gauge,
542 pub evictions: metrics::Gauge,
544 pub compilations_dispatched: metrics::Gauge,
546 pub compilations_succeeded: metrics::Gauge,
548 pub compilations_failed: metrics::Gauge,
550 pub compilations_discarded: metrics::Gauge,
552 pub interpreter_fallbacks: metrics::Gauge,
554 pub jit_helper_spawns: metrics::Gauge,
556 pub jit_helper_spawn_failures: metrics::Gauge,
558 pub jit_helper_restarts: metrics::Gauge,
560 pub jit_helper_timeouts: metrics::Gauge,
562 pub jit_helper_disconnects: metrics::Gauge,
564 pub jit_helper_pause_requests: metrics::Gauge,
566 pub jit_helper_pause_acknowledgements: metrics::Gauge,
568 pub jit_helper_pause_failures: metrics::Gauge,
570 pub jit_helper_pause_timeouts: metrics::Gauge,
572 pub jit_helper_resume_requests: metrics::Gauge,
574 pub jit_helper_resume_failures: metrics::Gauge,
576 pub pauses_total: metrics::Counter,
578 pub resumes_total: metrics::Counter,
580 pub paused: metrics::Gauge,
582 pub jit_compilation_duration: metrics::Histogram,
584 pub jit_compilation_duration_last: metrics::Gauge,
586 pub jit_parse_duration: metrics::Histogram,
588 pub jit_translate_duration: metrics::Histogram,
590 pub jit_optimize_duration: metrics::Histogram,
592 pub jit_codegen_duration: metrics::Histogram,
594}
595
596#[cfg(feature = "jit")]
597impl JitMetrics {
598 pub fn record(&self, stats: &RuntimeStatsSnapshot) {
600 let RuntimeStatsSnapshot {
601 lookup_hits,
602 lookup_misses,
603 events_dropped,
604 commands_dropped,
605 resident_entries,
606 events_queued,
607 command_queue_len,
608 pending_jobs,
609 jit_code_bytes,
610 jit_data_bytes,
611 evictions,
612 compilations_dispatched,
613 compilations_succeeded,
614 compilations_failed,
615 compilations_discarded,
616 interpreter_fallbacks,
617 jit_helper_spawns,
618 jit_helper_spawn_failures,
619 jit_helper_restarts,
620 jit_helper_timeouts,
621 jit_helper_disconnects,
622 jit_helper_pause_requests,
623 jit_helper_pause_acknowledgements,
624 jit_helper_pause_failures,
625 jit_helper_pause_timeouts,
626 jit_helper_resume_requests,
627 jit_helper_resume_failures,
628 } = *stats;
629 self.lookup_hits.set(lookup_hits as f64);
630 self.lookup_misses.set(lookup_misses as f64);
631 self.events_queued.set(events_queued as f64);
632 self.events_dropped.set(events_dropped as f64);
633 self.commands_dropped.set(commands_dropped as f64);
634 self.resident_entries.set(resident_entries as f64);
635 self.jit_code_bytes.set(jit_code_bytes as f64);
636 self.jit_data_bytes.set(jit_data_bytes as f64);
637 self.command_queue_len.set(command_queue_len as f64);
638 self.pending_jobs.set(pending_jobs as f64);
639 self.evictions.set(evictions as f64);
640 self.compilations_dispatched.set(compilations_dispatched as f64);
641 self.compilations_succeeded.set(compilations_succeeded as f64);
642 self.compilations_failed.set(compilations_failed as f64);
643 self.compilations_discarded.set(compilations_discarded as f64);
644 self.interpreter_fallbacks.set(interpreter_fallbacks as f64);
645 self.jit_helper_spawns.set(jit_helper_spawns as f64);
646 self.jit_helper_spawn_failures.set(jit_helper_spawn_failures as f64);
647 self.jit_helper_restarts.set(jit_helper_restarts as f64);
648 self.jit_helper_timeouts.set(jit_helper_timeouts as f64);
649 self.jit_helper_disconnects.set(jit_helper_disconnects as f64);
650 self.jit_helper_pause_requests.set(jit_helper_pause_requests as f64);
651 self.jit_helper_pause_acknowledgements.set(jit_helper_pause_acknowledgements as f64);
652 self.jit_helper_pause_failures.set(jit_helper_pause_failures as f64);
653 self.jit_helper_pause_timeouts.set(jit_helper_pause_timeouts as f64);
654 self.jit_helper_resume_requests.set(jit_helper_resume_requests as f64);
655 self.jit_helper_resume_failures.set(jit_helper_resume_failures as f64);
656 }
657
658 pub fn record_compilation(&self, event: &CompilationEvent) {
660 let duration_secs = event.duration.as_secs_f64();
661 self.jit_compilation_duration.record(duration_secs);
662 self.jit_compilation_duration_last.set(duration_secs);
663 self.jit_parse_duration.record(event.timings.parse.as_secs_f64());
664 self.jit_translate_duration.record(event.timings.translate.as_secs_f64());
665 self.jit_optimize_duration.record(event.timings.optimize.as_secs_f64());
666 self.jit_codegen_duration.record(event.timings.codegen.as_secs_f64());
667 }
668}
669
670#[cfg(all(test, feature = "std"))]
671mod tests {
672 use super::*;
673 use alloy_primitives::U256;
674 use core::any::TypeId;
675 use evm2::{
676 evm::{EmptyDB, NonStaticAny},
677 BaseEvmTypes, SpecId,
678 };
679
680 #[test]
681 fn reconfigure_keeps_precompile_cache_within_spec() {
682 let factory = EthBlockExecutorFactory::new(Arc::new(ChainSpec::default()));
683 let has_cache = |evm: &evm2::Evm<'_, BaseEvmTypes>| {
684 let precompiles: &dyn NonStaticAny = &**evm.precompiles();
685 precompiles.type_id() == TypeId::of::<CachedPrecompileProvider<BaseEvmTypes, SpecId>>()
686 };
687 let env = |spec, number: u64| {
688 let mut env = EthEvmEnv::<BaseEvmTypes>::new(spec, Default::default(), 1);
689 env.block.number = U256::from(number);
690 env
691 };
692
693 let mut evm = factory.build_evm_with_precompile_cache(
694 EmptyDB::default(),
695 env(SpecId::OSAKA, 1),
696 PrecompileCacheMap::default(),
697 false,
698 );
699 factory.reconfigure_evm(&mut evm, &env(SpecId::OSAKA, 2));
700 assert!(has_cache(&evm));
701 assert_eq!(evm.block().number, U256::from(2));
702
703 factory.reconfigure_evm(&mut evm, &env(SpecId::AMSTERDAM, 3));
704 assert!(!has_cache(&evm));
705 assert_eq!(evm.config_spec_id(), SpecId::AMSTERDAM);
706 }
707}