1#[cfg(feature = "jit")]
7use alloc::string::String;
8use alloy_evm::{Database, EvmEnv, EvmFactory};
9use revm::{
10 context::{BlockEnv, DBErrorMarker},
11 context_interface::result::{EVMError, HaltReason},
12 inspector::NoOpInspector,
13 primitives::hardfork::SpecId,
14 Inspector,
15};
16#[cfg(feature = "jit")]
17use revmc::alloy_evm::JitEvmFactory;
18
19#[cfg(feature = "jit")]
20pub use revmc::{
21 runtime::{
22 maybe_run_jit_helper, CompilationEvent, CompilationKind, JitBackend, JitMode,
23 RuntimeConfig, RuntimeStatsSnapshot, RuntimeTuning,
24 },
25 CompileTimings,
26};
27
28#[cfg(feature = "jit")]
29type Inner = JitEvmFactory;
30#[cfg(not(feature = "jit"))]
31type Inner = alloy_evm::EthEvmFactory;
32
33#[derive(Debug)]
45pub struct RethEvmFactory {
46 inner: Inner,
47 #[cfg(feature = "jit")]
48 disabled: JitBackend,
49 #[cfg(feature = "jit")]
50 metrics: RevmcMetrics,
51 #[cfg(feature = "jit")]
52 jit_support: bool,
53}
54
55impl Clone for RethEvmFactory {
56 fn clone(&self) -> Self {
57 Self {
58 #[cfg(feature = "jit")]
59 inner: self.inner.clone(),
60 #[cfg(not(feature = "jit"))]
61 inner: self.inner,
62 #[cfg(feature = "jit")]
63 disabled: self.disabled.clone(),
64 #[cfg(feature = "jit")]
65 metrics: self.metrics.clone(),
66 #[cfg(feature = "jit")]
67 jit_support: self.jit_support,
68 }
69 }
70}
71
72#[allow(clippy::derivable_impls)]
73impl Default for RethEvmFactory {
74 fn default() -> Self {
75 #[cfg(feature = "jit")]
76 {
77 Self::new(JitBackend::disabled())
78 }
79 #[cfg(not(feature = "jit"))]
80 {
81 Self { inner: Default::default() }
82 }
83 }
84}
85
86#[cfg(feature = "jit")]
87impl RethEvmFactory {
88 pub fn new(backend: JitBackend) -> Self {
90 Self::new_with_metrics(backend, RevmcMetrics::default())
91 }
92
93 pub fn new_with_metrics(backend: JitBackend, metrics: RevmcMetrics) -> Self {
95 Self {
96 inner: JitEvmFactory::new(backend),
97 disabled: JitBackend::disabled(),
98 metrics,
99 jit_support: false,
100 }
101 }
102
103 pub fn disabled() -> Self {
105 Self::default()
106 }
107
108 pub const fn backend(&self) -> &JitBackend {
110 self.inner.backend()
111 }
112
113 pub const fn set_jit_support(&mut self, enabled: bool) {
119 self.jit_support = enabled;
120 }
121
122 pub const fn jit_support_enabled(&self) -> bool {
124 self.jit_support
125 }
126
127 fn pause_jit(&self) {
129 let backend = self.inner.backend();
130 let was_paused = backend.is_paused();
131 backend.pause();
132 let is_paused = backend.is_paused();
133 if !was_paused && is_paused {
134 self.metrics.pauses_total.increment(1);
135 }
136 self.metrics.paused.set(is_paused as u8 as f64);
137 }
138
139 fn resume_jit(&self) {
141 let backend = self.inner.backend();
142 let was_paused = backend.is_paused();
143 backend.resume();
144 let is_paused = backend.is_paused();
145 if was_paused && !is_paused {
146 self.metrics.resumes_total.increment(1);
147 }
148 self.metrics.paused.set(is_paused as u8 as f64);
149 }
150}
151
152#[cfg(feature = "jit")]
153impl reth_evm::JitBackend for RethEvmFactory {
154 fn set_enabled(&self, enabled: bool) -> Result<(), String> {
155 self.inner.backend().set_enabled(enabled).map_err(|err| err.to_string())
156 }
157
158 fn pause(&self) {
159 self.pause_jit();
160 }
161
162 fn resume(&self) {
163 self.resume_jit();
164 }
165
166 fn clear(&self) {
167 self.inner.backend().clear_all();
168 }
169}
170
171impl EvmFactory for RethEvmFactory {
172 type Evm<DB: Database, I: Inspector<alloy_evm::eth::EthEvmContext<DB>>> =
173 <Inner as EvmFactory>::Evm<DB, I>;
174 type Context<DB: Database> = <Inner as EvmFactory>::Context<DB>;
175 type Tx = <Inner as EvmFactory>::Tx;
176 type Error<DBError: DBErrorMarker> = EVMError<DBError>;
177 type HaltReason = HaltReason;
178 type Spec = SpecId;
179 type BlockEnv = BlockEnv;
180 type Precompiles = <Inner as EvmFactory>::Precompiles;
181
182 fn create_evm<DB: Database>(&self, db: DB, input: EvmEnv) -> Self::Evm<DB, NoOpInspector> {
183 #[cfg(feature = "jit")]
184 {
185 if self.jit_support {
186 self.inner.create_evm(db, input)
187 } else {
188 JitEvmFactory::new(self.disabled.clone()).create_evm(db, input)
189 }
190 }
191 #[cfg(not(feature = "jit"))]
192 {
193 self.inner.create_evm(db, input)
194 }
195 }
196
197 fn create_evm_with_inspector<DB: Database, I: Inspector<Self::Context<DB>>>(
198 &self,
199 db: DB,
200 input: EvmEnv,
201 inspector: I,
202 ) -> Self::Evm<DB, I> {
203 #[cfg(feature = "jit")]
204 {
205 if self.jit_support {
206 self.inner.create_evm_with_inspector(db, input, inspector)
207 } else {
208 JitEvmFactory::new(self.disabled.clone())
209 .create_evm_with_inspector(db, input, inspector)
210 }
211 }
212 #[cfg(not(feature = "jit"))]
213 {
214 self.inner.create_evm_with_inspector(db, input, inspector)
215 }
216 }
217}
218
219#[cfg(feature = "jit")]
221#[derive(reth_metrics::Metrics, Clone)]
222#[metrics(scope = "revmc.jit")]
223pub struct RevmcMetrics {
224 pub lookup_hits: metrics::Gauge,
226 pub lookup_misses: metrics::Gauge,
228 pub events_queued: metrics::Gauge,
230 pub events_dropped: metrics::Gauge,
232 pub resident_entries: metrics::Gauge,
234 pub jit_code_bytes: metrics::Gauge,
236 pub jit_data_bytes: metrics::Gauge,
238 pub command_queue_len: metrics::Gauge,
240 pub pending_jobs: metrics::Gauge,
242 pub evictions: metrics::Gauge,
244 pub compilations_dispatched: metrics::Gauge,
246 pub compilations_succeeded: metrics::Gauge,
248 pub compilations_failed: metrics::Gauge,
250 pub jit_helper_spawns: metrics::Gauge,
252 pub jit_helper_spawn_failures: metrics::Gauge,
254 pub jit_helper_restarts: metrics::Gauge,
256 pub jit_helper_timeouts: metrics::Gauge,
258 pub jit_helper_disconnects: metrics::Gauge,
260 pub pauses_total: metrics::Counter,
262 pub resumes_total: metrics::Counter,
264 pub paused: metrics::Gauge,
266 pub jit_compilation_duration: metrics::Histogram,
268 pub jit_compilation_duration_last: metrics::Gauge,
270 pub jit_parse_duration: metrics::Histogram,
272 pub jit_translate_duration: metrics::Histogram,
274 pub jit_optimize_duration: metrics::Histogram,
276 pub jit_codegen_duration: metrics::Histogram,
278}
279
280#[cfg(feature = "jit")]
281impl RevmcMetrics {
282 pub fn record(&self, stats: &RuntimeStatsSnapshot) {
284 let RuntimeStatsSnapshot {
285 lookup_hits,
286 lookup_misses,
287 events_dropped,
288 resident_entries,
289 events_queued,
290 command_queue_len,
291 pending_jobs,
292 jit_code_bytes,
293 jit_data_bytes,
294 evictions,
295 compilations_dispatched,
296 compilations_succeeded,
297 compilations_failed,
298 jit_helper_spawns,
299 jit_helper_spawn_failures,
300 jit_helper_restarts,
301 jit_helper_timeouts,
302 jit_helper_disconnects,
303 ..
304 } = *stats;
305 self.lookup_hits.set(lookup_hits as f64);
306 self.lookup_misses.set(lookup_misses as f64);
307 self.events_queued.set(events_queued as f64);
308 self.events_dropped.set(events_dropped as f64);
309 self.resident_entries.set(resident_entries as f64);
310 self.jit_code_bytes.set(jit_code_bytes as f64);
311 self.jit_data_bytes.set(jit_data_bytes as f64);
312 self.command_queue_len.set(command_queue_len as f64);
313 self.pending_jobs.set(pending_jobs as f64);
314 self.evictions.set(evictions as f64);
315 self.compilations_dispatched.set(compilations_dispatched as f64);
316 self.compilations_succeeded.set(compilations_succeeded as f64);
317 self.compilations_failed.set(compilations_failed as f64);
318 self.jit_helper_spawns.set(jit_helper_spawns as f64);
319 self.jit_helper_spawn_failures.set(jit_helper_spawn_failures as f64);
320 self.jit_helper_restarts.set(jit_helper_restarts as f64);
321 self.jit_helper_timeouts.set(jit_helper_timeouts as f64);
322 self.jit_helper_disconnects.set(jit_helper_disconnects as f64);
323 }
324
325 pub fn record_compilation(&self, event: &CompilationEvent) {
327 let duration_secs = event.duration.as_secs_f64();
328 self.jit_compilation_duration.record(duration_secs);
329 self.jit_compilation_duration_last.set(duration_secs);
330 self.jit_parse_duration.record(event.timings.parse.as_secs_f64());
331 self.jit_translate_duration.record(event.timings.translate.as_secs_f64());
332 self.jit_optimize_duration.record(event.timings.optimize.as_secs_f64());
333 self.jit_codegen_duration.record(event.timings.codegen.as_secs_f64());
334 }
335}