Skip to main content

revmc_llvm/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(test), warn(unused_extern_crates))]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4
5#[macro_use]
6extern crate tracing;
7
8use alloy_primitives::map::{FxBuildHasher, HashSet};
9use inkwell::{
10    AddressSpace, IntPredicate,
11    attributes::{Attribute, AttributeLoc},
12    basic_block::BasicBlock,
13    debug_info::{
14        AsDIScope, DICompileUnit, DIFlags, DIFlagsConstants, DISubprogram, DWARFEmissionKind,
15        DWARFSourceLanguage, DebugInfoBuilder,
16    },
17    module::{FlagBehavior, Module},
18    passes::PassBuilderOptions,
19    support::error_handling::install_fatal_error_handler,
20    targets::{
21        CodeModel, FileType, InitializationConfig, RelocMode, Target, TargetMachine, TargetTriple,
22    },
23    types::{
24        AnyType, AnyTypeEnum, BasicType, BasicTypeEnum, FunctionType, IntType, PointerType,
25        StringRadix, VoidType,
26    },
27    values::{
28        AsValueRef, BasicMetadataValueEnum, BasicValue, BasicValueEnum, CallSiteValue,
29        FunctionValue, InstructionValue, PointerValue,
30    },
31};
32use object::{Object, ObjectSymbol};
33use revmc_backend::{
34    Backend, BackendConfig, BackendTypes, Builder, CallConv, IntCC, OptimizationLevel, Result,
35    TailCallKind, TypeMethods, U256, eyre, format_bytes,
36};
37use std::{
38    cell::Cell,
39    ffi::{CStr, CString},
40    fmt::{self, Write},
41    iter,
42    mem::ManuallyDrop,
43    path::Path,
44    sync::{
45        Arc, Once, OnceLock,
46        atomic::{AtomicU64, AtomicUsize, Ordering},
47    },
48};
49
50pub use inkwell::{self, context::Context};
51
52mod cpp;
53
54mod dh;
55pub mod orc;
56
57mod utils;
58pub(crate) use utils::*;
59
60/// Branch weight for non-cold branches.
61const DEFAULT_WEIGHT: u32 = 20000;
62
63/// Current JIT memory usage counters.
64///
65/// Counters reflect live memory: bytes are added on compilation and
66/// subtracted when JIT code is freed.
67#[derive(Clone, Copy, Debug, Default)]
68pub struct JitMemoryUsage {
69    /// Bytes allocated for executable (code) sections.
70    pub code_bytes: usize,
71    /// Bytes allocated for non-executable (data) sections.
72    pub data_bytes: usize,
73}
74
75impl JitMemoryUsage {
76    /// Total bytes (code + data).
77    pub fn total_bytes(&self) -> usize {
78        self.code_bytes + self.data_bytes
79    }
80}
81
82impl fmt::Display for JitMemoryUsage {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(
85            f,
86            "total: {}, code: {}, data: {}",
87            format_bytes(self.total_bytes()),
88            format_bytes(self.code_bytes),
89            format_bytes(self.data_bytes),
90        )
91    }
92}
93
94/// Atomic counters written by the C++ `MemoryUsagePlugin`.
95///
96/// Leaked into a `&'static` reference so the plugin (which lives as long as
97/// the process-global LLJIT) always has valid pointers.
98struct JitMemoryCounters {
99    code_bytes: AtomicUsize,
100    data_bytes: AtomicUsize,
101}
102
103impl JitMemoryCounters {
104    fn get(&self) -> JitMemoryUsage {
105        JitMemoryUsage {
106            code_bytes: self.code_bytes.load(Ordering::Relaxed),
107            data_bytes: self.data_bytes.load(Ordering::Relaxed),
108        }
109    }
110}
111
112/// Returns the current JIT memory usage.
113///
114/// Returns `None` if the JIT has not been initialized yet.
115pub fn jit_memory_usage() -> Option<JitMemoryUsage> {
116    GlobalOrcJit::try_get().map(|g| g.memory_counters.get())
117}
118
119/// Collect garbage on the global JIT.
120pub fn global_gc() {
121    if let Some(orc) = GlobalOrcJit::try_get() {
122        orc.gc();
123    }
124}
125
126type FxHashMap<K, V> = alloy_primitives::map::HashMap<K, V, FxBuildHasher>;
127
128/// The LLVM-based EVM bytecode compiler backend.
129#[derive(Debug)]
130#[must_use]
131pub struct EvmLlvmBackend {
132    cx: &'static Context,
133    _dh: dh::DiagnosticHandlerGuard,
134    bcx: inkwell::builder::Builder<'static>,
135    module: Module<'static>,
136    machine: TargetMachine,
137
138    /// ORC JIT state. `None` in AOT mode.
139    /// Dropped before `_tscx` so the JIT engine is disposed before the context.
140    orc: Option<OrcJitState>,
141    /// ORC thread-safe context that owns the LLVM context (JIT mode only).
142    _tscx: Option<orc::ThreadSafeContext>,
143    /// Non-owning context handle for JIT mode. See [`create_orc_context`].
144    _cx_handle: Option<Box<ManuallyDrop<Context>>>,
145    /// Owned context for AOT mode. `None` in JIT mode.
146    _aot_cx: Option<Box<Context>>,
147
148    /// LLVM debug info builder and compile unit, created lazily when `debug_file` is set.
149    di_state: Option<DiState>,
150
151    ty_void: VoidType<'static>,
152    ty_ptr: PointerType<'static>,
153    ty_i1: IntType<'static>,
154    ty_i8: IntType<'static>,
155    ty_i32: IntType<'static>,
156    ty_i64: IntType<'static>,
157    ty_i256: IntType<'static>,
158    ty_isize: IntType<'static>,
159
160    aot: bool,
161    backend_config: BackendConfig,
162    scratch: String,
163    /// Separate from `function_names` to have always increasing IDs.
164    function_counter: u32,
165    /// Persistent mapping from function ID to symbol name.
166    function_names: FxHashMap<u32, String>,
167}
168
169// Thread-local slot for capturing compiled object buffers from the ObjectTransformLayer.
170// Safe because LLJIT uses InPlaceTaskDispatcher — compilation runs inline on the calling thread.
171thread_local! {
172    static OBJ_CAPTURE: Cell<*mut Option<Vec<u8>>> = const { Cell::new(std::ptr::null_mut()) };
173}
174
175/// RAII guard that arms the thread-local object capture for the duration of a JIT commit.
176struct ScopedObjCapture {
177    prev: *mut Option<Vec<u8>>,
178}
179
180impl ScopedObjCapture {
181    fn install(slot: &mut Option<Vec<u8>>) -> Self {
182        Self { prev: OBJ_CAPTURE.replace(slot as *mut _) }
183    }
184}
185
186impl Drop for ScopedObjCapture {
187    fn drop(&mut self) {
188        OBJ_CAPTURE.set(self.prev);
189    }
190}
191
192fn obj_capture_transform(obj: &[u8]) -> Result<Option<Vec<u8>>, String> {
193    OBJ_CAPTURE.with(|tls| {
194        let ptr = tls.get();
195        if !ptr.is_null() {
196            unsafe { *ptr = Some(obj.to_vec()) };
197        }
198    });
199    Ok(None)
200}
201
202/// Process-global shared LLJIT instance.
203///
204/// ORC/LLJIT is thread-safe and designed to be shared. Individual compilers get
205/// their own [`JITDylib`](orc::JITDylibRef) for symbol isolation.
206///
207/// Builtin function pointers (absolute symbols) are defined once in a shared
208/// `builtins` JITDylib. Each per-compiler JITDylib links against the builtins
209/// JD so compiled code can resolve them without duplicating definitions.
210struct GlobalOrcJit {
211    /// Shared JITDylib containing absolute symbols for builtin functions.
212    /// Added to each per-compiler JITDylib's link order.
213    builtins_jd: orc::JITDylibRef,
214
215    /// Symbols already defined in the builtins JD.
216    builtins_defined: std::sync::Mutex<HashSet<CString>>,
217
218    next_dylib_id: AtomicU64,
219
220    /// Live JIT memory counters, updated by the C++ MemoryUsagePlugin.
221    memory_counters: &'static JitMemoryCounters,
222
223    jit: orc::LLJIT,
224}
225
226impl GlobalOrcJit {
227    fn global() -> &'static OnceLock<Result<Self, String>> {
228        static GLOBAL: OnceLock<Result<GlobalOrcJit, String>> = OnceLock::new();
229        &GLOBAL
230    }
231
232    fn try_get() -> Option<&'static Self> {
233        Self::global().get().and_then(|r| r.as_ref().ok())
234    }
235
236    fn get(
237        debug_support: bool,
238        profiling_support: bool,
239        simple_perf: bool,
240    ) -> Result<&'static Self> {
241        let result = Self::global().get_or_init(|| {
242            init().map_err(|e| e.to_string())?;
243            let jit =
244                orc::LLJIT::builder().concurrent_compiler().build().map_err(|e| e.to_string())?;
245            jit.get_execution_session().set_default_error_reporter();
246            jit.get_obj_transform_layer().set_transform(obj_capture_transform);
247
248            // Register JIT debug info with debuggers and profilers.
249            if debug_support && let Err(e) = jit.enable_debug_support() {
250                warn!("failed to enable JIT debug support: {e}");
251            }
252            if profiling_support && let Err(e) = jit.enable_perf_support() {
253                warn!("failed to enable JIT perf support: {e}");
254            }
255            if simple_perf && let Err(e) = jit.enable_simple_perf() {
256                warn!("failed to enable simple perf map support: {e}");
257            }
258
259            // Track JIT memory usage.
260            let memory_counters: &'static JitMemoryCounters =
261                Box::leak(Box::new(JitMemoryCounters {
262                    code_bytes: AtomicUsize::new(0),
263                    data_bytes: AtomicUsize::new(0),
264                }));
265            orc::cvt(unsafe {
266                cpp::revmc_llvm_lljit_enable_memory_usage(
267                    jit.as_inner(),
268                    &memory_counters.code_bytes,
269                    &memory_counters.data_bytes,
270                )
271            })
272            .map_err(|e| e.to_string())?;
273
274            let builtins_jd = jit.get_execution_session().create_bare_jit_dylib(c"revmc.builtins");
275
276            Ok(Self {
277                jit,
278                builtins_jd,
279                builtins_defined: Default::default(),
280                next_dylib_id: Default::default(),
281                memory_counters,
282            })
283        });
284        match result {
285            Ok(g) => Ok(g),
286            Err(e) => Err(eyre::eyre!("{e}")),
287        }
288    }
289
290    /// Creates a fresh JITDylib for a compiler.
291    fn create_jit_dylib(&self) -> orc::JITDylibRef {
292        let id = self.next_dylib_id.fetch_add(1, Ordering::Relaxed);
293        let name = CString::new(format!("revmc.compiler.{id}")).unwrap();
294        let es = self.jit.get_execution_session();
295        let jd = es.create_bare_jit_dylib(&name);
296        // Link against the builtins JD so compiled code can resolve builtin symbols.
297        jd.add_to_link_order(self.builtins_jd);
298        // Attach a process symbol generator so the JITDylib can resolve libc and other
299        // process-level symbols (e.g. printf, memcpy) that JIT-compiled code may reference.
300        let prefix = self.jit.get_global_prefix();
301        if let Ok(generator) = orc::DefinitionGenerator::for_current_process(prefix) {
302            jd.add_generator(generator);
303        }
304        jd
305    }
306
307    /// Removes a JITDylib from the ExecutionSession, freeing all its resources.
308    fn remove_jit_dylib(&self, jd: orc::JITDylibRef) {
309        let es = self.jit.get_execution_session();
310        if let Err(e) = es.remove_jit_dylib(jd) {
311            error!("failed to remove JITDylib: {e}");
312        }
313        // Reclaim unreferenced interned strings after removing symbols.
314        es.get_symbol_string_pool().clear_dead_entries();
315    }
316
317    /// Defines absolute symbols in the shared builtins JITDylib, skipping any
318    /// that are already defined.
319    fn define_builtins(&self, symbols: &[(CString, usize)]) {
320        if symbols.is_empty() {
321            return;
322        }
323        let mut defined = self.builtins_defined.lock().unwrap();
324        let new_syms: Vec<_> = symbols
325            .iter()
326            .filter(|(name, _)| defined.insert(name.clone()))
327            .map(|(name, addr)| {
328                orc::SymbolMapPair::new(
329                    self.jit.mangle_and_intern(name),
330                    orc::EvaluatedSymbol::new(
331                        *addr as u64,
332                        orc::SymbolFlags::none().exported().callable(),
333                    ),
334                )
335            })
336            .collect();
337        drop(defined);
338        if !new_syms.is_empty()
339            && let Err((e, _)) =
340                self.builtins_jd.define(orc::MaterializationUnit::absolute_symbols(new_syms))
341        {
342            error!("failed to define builtins: {e}");
343        }
344    }
345
346    fn gc(&self) {
347        self.jit.get_execution_session().get_symbol_string_pool().clear_dead_entries();
348    }
349}
350
351/// Shared guard that keeps a JITDylib alive.
352///
353/// The JITDylib will not be cleared or returned to the pool until the last
354/// `JitDylibGuard` is dropped. Runtime callers holding JIT function pointers
355/// must retain a clone of this guard to prevent the code from being freed.
356#[allow(missing_debug_implementations)]
357pub struct JitDylibGuard {
358    global: &'static GlobalOrcJit,
359    jd: orc::JITDylibRef,
360}
361
362impl Drop for JitDylibGuard {
363    fn drop(&mut self) {
364        self.global.remove_jit_dylib(self.jd);
365    }
366}
367
368/// ORC JIT state for the LLVM backend (JIT mode only).
369///
370/// The LLVM context is owned separately (via `tscx`) and persists across JIT resets.
371/// Each compiler gets its own JITDylib in the global LLJIT for symbol isolation.
372struct OrcJitState {
373    /// Reference to the global LLJIT instance.
374    global: &'static GlobalOrcJit,
375    /// Shared guard that owns the JITDylib. The JD is not recycled until all
376    /// `Arc<JitDylibGuard>` holders (including external callers) are dropped.
377    ///
378    /// Declared before `loaded_trackers` so it drops first: `removeJITDylib`
379    /// (called in `JitDylibGuard::drop`) clears the JITDylib internally and
380    /// must run while the tracker handles are still live.
381    jd_guard: Arc<JitDylibGuard>,
382    /// Functions in the current staging module (not yet committed to JIT).
383    staged_functions: FxHashMap<u32, FunctionValue<'static>>,
384    /// Absolute symbols collected during translation, flushed to the global
385    /// builtins JITDylib before commit.
386    pending_symbols: Vec<(CString, usize)>,
387    /// Resource trackers for committed JIT modules, used for code removal.
388    loaded_trackers: Vec<orc::ResourceTracker>,
389    /// Maps committed function ID → index into `loaded_trackers`.
390    committed_functions: FxHashMap<u32, usize>,
391    /// Cached object buffer from the last `commit_staged_module`, captured via
392    /// ObjectTransformLayer.
393    last_compiled_object: Option<Vec<u8>>,
394    /// Counter for throttling `SymbolStringPool::clearDeadEntries()` calls.
395    clear_pool_counter: u32,
396}
397
398impl fmt::Debug for OrcJitState {
399    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400        f.debug_struct("OrcJitState")
401            .field("staged_functions", &self.staged_functions.len())
402            .field("pending_symbols", &self.pending_symbols.len())
403            .field("loaded_trackers", &self.loaded_trackers.len())
404            .field("committed_functions", &self.committed_functions.len())
405            .finish_non_exhaustive()
406    }
407}
408
409impl OrcJitState {
410    fn new(debug_support: bool, profiling_support: bool, simple_perf: bool) -> Result<Self> {
411        let global = GlobalOrcJit::get(debug_support, profiling_support, simple_perf)?;
412        let jd = global.create_jit_dylib();
413        let jd_guard = Arc::new(JitDylibGuard { global, jd });
414        Ok(Self {
415            global,
416            staged_functions: FxHashMap::default(),
417            pending_symbols: Vec::new(),
418            loaded_trackers: Vec::new(),
419            committed_functions: FxHashMap::default(),
420            last_compiled_object: None,
421            jd_guard,
422            clear_pool_counter: 0,
423        })
424    }
425
426    /// Clears all code and symbols from this compiler's JITDylib.
427    fn clear(&mut self) -> Result<()> {
428        self.staged_functions.clear();
429        self.pending_symbols.clear();
430        self.committed_functions.clear();
431        self.last_compiled_object = None;
432        // Clear the JITDylib before dropping resource trackers: `LLVMOrcJITDylibClear` calls
433        // remove on all trackers associated with the dylib and must run while the tracker
434        // handles are still live.
435        self.jd().clear().map_err(error_msg)?;
436        self.loaded_trackers.clear();
437        Ok(())
438    }
439
440    /// Periodically clears dead entries from the global SymbolStringPool.
441    ///
442    /// `clearDeadEntries` is O(pool_size), so we throttle it to avoid
443    /// O(N²) total cost over many compilations.
444    fn maybe_clear_dead_pool_entries(&mut self) {
445        self.clear_pool_counter += 1;
446        if self.clear_pool_counter.is_multiple_of(256) {
447            self.global.jit.get_execution_session().get_symbol_string_pool().clear_dead_entries();
448        }
449    }
450
451    fn jd(&self) -> orc::JITDylibRef {
452        self.jd_guard.jd
453    }
454}
455
456fn link_jit_object_in_dylib(
457    orc: &OrcJitState,
458    jd: orc::JITDylibRef,
459    symbol_name: &CStr,
460    object: &[u8],
461    symbols: &[(CString, usize)],
462) -> Result<(usize, orc::ResourceTracker)> {
463    orc.global.define_builtins(symbols);
464    let tracker = jd.create_resource_tracker();
465    orc.global.jit.add_object_with_rt(symbol_name, object, &tracker).map_err(error_msg)?;
466    let addr = orc.global.jit.lookup_in(jd, symbol_name).map_err(error_msg)?;
467    Ok((addr, tracker))
468}
469
470/// Wraps a module in a [`orc::ThreadSafeModule`] for transfer to LLJIT.
471///
472/// Uses a raw pointer cast to work around `Module<'ctx>` invariance — the module's
473/// context is genuinely owned by `tscx`.
474fn create_thread_safe_module(
475    tscx: &orc::ThreadSafeContext,
476    module: Module<'static>,
477) -> orc::ThreadSafeModule {
478    let module = std::mem::ManuallyDrop::new(module);
479    // SAFETY: The module was created in the context owned by `tscx`.
480    unsafe { orc::ThreadSafeModule::create_in_context(Module::new(module.as_mut_ptr()), tscx) }
481}
482
483/// Creates an ORC-owned LLVM context, returning a `&'static` reference to it.
484///
485/// In JIT mode, ORC owns the context via a [`orc::ThreadSafeContext`] so that modules can be
486/// safely transferred to the JIT. A non-owning handle ([`ManuallyDrop<Context>`]) is
487/// heap-allocated to provide a stable address for the `&'static` reference.
488fn create_orc_context() -> (&'static Context, orc::ThreadSafeContext, Box<ManuallyDrop<Context>>) {
489    let cx = Context::create();
490    let raw = cx.raw();
491    let tscx = orc::ThreadSafeContext::from_context(cx);
492    // SAFETY: The TSC now owns the context. `from_context` uses `ManuallyDrop` internally,
493    // so the LLVM context is still valid — ownership was just transferred to the TSC.
494    let cx_handle = Box::new(ManuallyDrop::new(unsafe { Context::new(raw) }));
495    // SAFETY: The Box provides a stable heap address. The context is valid as long as
496    // the TSC lives.
497    let cx: &'static Context = unsafe { &*(&**cx_handle as *const Context) };
498    (cx, tscx, cx_handle)
499}
500
501/// LLVM debug info state for a module.
502struct DiState {
503    dibuilder: DebugInfoBuilder<'static>,
504    compile_unit: DICompileUnit<'static>,
505    finalized: bool,
506}
507
508impl fmt::Debug for DiState {
509    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510        f.debug_struct("DiState").field("finalized", &self.finalized).finish()
511    }
512}
513
514// SAFETY: In JIT mode the LLVM context is owned by an ORC ThreadSafeContext.
515// In AOT mode the context is owned by `_aot_cx` (a Box<Context>).
516// Both travel with the backend, so no thread-local or shared state is referenced.
517unsafe impl Send for EvmLlvmBackend {}
518
519impl EvmLlvmBackend {
520    /// Creates a new LLVM backend for the host machine.
521    #[instrument(name = "new_llvm_backend", level = "debug", skip_all)]
522    pub fn new(aot: bool) -> Result<Self> {
523        let config = BackendConfig::default();
524        init()?;
525
526        let target_info = TargetInfo::new();
527        let target = &target_info.target;
528        let machine = target
529            .create_target_machine(
530                &target_info.triple,
531                &target_info.cpu,
532                &target_info.features,
533                convert_opt_level(config.opt_level),
534                if aot { RelocMode::PIC } else { RelocMode::Static },
535                CodeModel::Default,
536            )
537            .ok_or_else(|| eyre::eyre!("failed to create target machine"))?;
538
539        // In JIT mode, ORC owns the context via a ThreadSafeContext so that modules can be
540        // safely transferred to the JIT without double-ownership issues with the TLS context.
541        // In AOT mode, we use an owned Box<Context> so the backend is safely Send.
542        let (cx, tscx, cx_handle, aot_cx) = if aot {
543            let aot_cx = Box::new(Context::create());
544            // SAFETY: The Box provides a stable heap address. The context is valid as long as
545            // `_aot_cx` lives, and it is dropped after all LLVM objects due to field ordering.
546            let cx: &'static Context = unsafe { &*(&*aot_cx as *const Context) };
547            (cx, None, None, Some(aot_cx))
548        } else {
549            if !target.has_jit() {
550                return Err(eyre::eyre!("target {:?} does not support JIT", target.get_name()));
551            }
552            if !target.has_target_machine() {
553                return Err(eyre::eyre!(
554                    "target {:?} does not have target machine",
555                    target.get_name()
556                ));
557            }
558
559            let (cx, tscx, cx_handle) = create_orc_context();
560            (cx, Some(tscx), Some(cx_handle), None)
561        };
562
563        let module = create_module(cx, &machine, aot)?;
564        let bcx = cx.create_builder();
565
566        let ty_void = cx.void_type();
567        let ty_i1 = cx.bool_type();
568        let ty_i8 = cx.i8_type();
569        let ty_i32 = cx.i32_type();
570        let ty_i64 = cx.i64_type();
571        let ty_i256 =
572            cx.custom_width_int_type(std::num::NonZeroU32::new(256).unwrap()).expect("i256");
573        let ty_isize = cx.ptr_sized_int_type(&machine.get_target_data(), None);
574        let ty_ptr = cx.ptr_type(AddressSpace::default());
575        Ok(Self {
576            cx,
577            _dh: dh::DiagnosticHandlerGuard::new(cx),
578            bcx,
579            module,
580            machine,
581            orc: None,
582            _tscx: tscx,
583            _cx_handle: cx_handle,
584            _aot_cx: aot_cx,
585            ty_void,
586            ty_i1,
587            ty_i8,
588            ty_i32,
589            ty_i64,
590            ty_i256,
591            ty_isize,
592            ty_ptr,
593            aot,
594            backend_config: config,
595            scratch: String::new(),
596            function_counter: 0,
597            function_names: FxHashMap::default(),
598            di_state: None,
599        })
600    }
601
602    /// Returns the LLVM context.
603    #[inline]
604    pub fn cx(&self) -> &Context {
605        self.cx
606    }
607
608    #[inline]
609    fn module(&self) -> &Module<'static> {
610        &self.module
611    }
612
613    fn ensure_orc(&mut self) -> Result<&mut OrcJitState> {
614        if self.orc.is_none() {
615            self.orc = Some(OrcJitState::new(
616                self.backend_config.debug_support,
617                self.backend_config.profiling_support,
618                self.backend_config.simple_perf,
619            )?);
620        }
621        Ok(self.orc.as_mut().unwrap())
622    }
623
624    fn fn_type(
625        &self,
626        ret: Option<BasicTypeEnum<'static>>,
627        params: &[BasicTypeEnum<'static>],
628    ) -> FunctionType<'static> {
629        let params = params.iter().copied().map(Into::into).collect::<Vec<_>>();
630        match ret {
631            Some(ret) => ret.fn_type(&params, false),
632            None => self.ty_void.fn_type(&params, false),
633        }
634    }
635
636    /// Returns the given name if IR output is being dumped, otherwise an empty string.
637    /// LLVM skips internal name processing for empty names, avoiding overhead when names
638    /// are not needed for readability.
639    #[inline]
640    fn name<'a>(&self, name: &'a str) -> &'a str {
641        if self.backend_config.is_dumping { name } else { "" }
642    }
643
644    fn id_to_name(&self, id: u32) -> &str {
645        &self.function_names[&id]
646    }
647
648    /// Lazily initializes the debug info builder and compile unit for the module.
649    fn ensure_di_state(&mut self) {
650        if self.di_state.is_some() {
651            return;
652        }
653        let Some(debug_file) = &self.backend_config.debug_file else { return };
654
655        let filename =
656            debug_file.file_name().map(|f| f.to_string_lossy()).unwrap_or_default().into_owned();
657        let directory =
658            debug_file.parent().map(|p| p.to_string_lossy()).unwrap_or_default().into_owned();
659
660        // Add required module flags for debug info.
661        self.module().add_basic_value_flag(
662            "Debug Info Version",
663            FlagBehavior::Warning,
664            self.ty_i32.const_int(inkwell::debug_info::debug_metadata_version() as u64, false),
665        );
666        self.module().add_basic_value_flag(
667            "Dwarf Version",
668            FlagBehavior::Warning,
669            self.ty_i32.const_int(5, false),
670        );
671
672        let opt_level = self.backend_config.opt_level;
673        let is_optimized = opt_level != OptimizationLevel::None;
674        let mut flags = Vec::new();
675        flags.push(match opt_level {
676            OptimizationLevel::None => "-O0",
677            OptimizationLevel::Less => "-O1",
678            OptimizationLevel::Default => "-O2",
679            OptimizationLevel::Aggressive => "-O3",
680        });
681        flags.push(if self.aot { "--aot" } else { "--jit" });
682        let flags = flags.join(" ");
683
684        let (dibuilder, compile_unit) = self.module().create_debug_info_builder(
685            true,
686            DWARFSourceLanguage::C,
687            &filename,
688            &directory,
689            "revmc",
690            is_optimized,
691            &flags,
692            0,
693            "",
694            DWARFEmissionKind::Full,
695            0,
696            false,
697            false,
698            "",
699            "",
700        );
701
702        self.di_state = Some(DiState { dibuilder, compile_unit, finalized: false });
703    }
704
705    /// Commits the current staging module to the ORC JIT if there are pending functions.
706    fn commit_staged_module(&mut self) -> Result<()> {
707        if self.aot || self.orc.as_ref().is_none_or(|o| o.staged_functions.is_empty()) {
708            return Ok(());
709        }
710
711        self.di_state = None;
712
713        let new_module = create_module(self.cx, &self.machine, self.aot)?;
714        let old_module = std::mem::replace(&mut self.module, new_module);
715
716        let tscx = self._tscx.as_ref().expect("missing ThreadSafeContext");
717        let orc = self.orc.as_mut().unwrap();
718
719        // Flush pending absolute symbols to the shared builtins JITDylib.
720        let pending = &mut orc.pending_symbols;
721        if !pending.is_empty() {
722            orc.global.define_builtins(pending);
723            pending.clear();
724        }
725
726        let tracker = orc.jd().create_resource_tracker();
727
728        let tsm = create_thread_safe_module(tscx, old_module);
729        orc.global.jit.add_module_with_rt(tsm, &tracker).map_err(error_msg)?;
730
731        let tracker_idx = orc.loaded_trackers.len();
732        for &id in orc.staged_functions.keys() {
733            orc.committed_functions.insert(id, tracker_idx);
734        }
735        orc.loaded_trackers.push(tracker);
736        orc.staged_functions.clear();
737        Ok(())
738    }
739
740    /// Clears all code from this compiler's JITDylib, freeing JIT-compiled code.
741    /// The LLVM context and global LLJIT are reused across resets.
742    fn reset_jit(&mut self) -> Result<()> {
743        self.function_names.clear();
744        self.di_state = None;
745
746        if let Some(orc) = &mut self.orc {
747            orc.clear()?;
748        }
749
750        self.module = create_module(self.cx, &self.machine, self.aot)?;
751
752        Ok(())
753    }
754
755    /// Returns pending absolute symbols collected while translating the current JIT module.
756    pub fn pending_symbol_names(&self) -> Vec<CString> {
757        self.orc
758            .as_ref()
759            .map(|orc| orc.pending_symbols.iter().map(|(name, _)| name.clone()).collect())
760            .unwrap_or_default()
761    }
762
763    /// Links a relocatable object into this backend's JITDylib and returns the function address
764    /// and resource tracker that owns the linked code.
765    pub fn link_jit_object(
766        &mut self,
767        symbol_name: &CStr,
768        object: &[u8],
769        symbols: &[(CString, usize)],
770    ) -> Result<(usize, orc::ResourceTracker)> {
771        let orc = self.ensure_orc()?;
772        let (addr, tracker) =
773            link_jit_object_in_dylib(orc, orc.jd(), symbol_name, object, symbols)?;
774        orc.loaded_trackers.push(tracker);
775        Ok((addr, orc.loaded_trackers.pop().unwrap()))
776    }
777
778    /// Links a relocatable object into a fresh JITDylib and returns the function address,
779    /// resource tracker, and guard for the JITDylib that owns the linked code.
780    pub fn link_jit_object_in_fresh_dylib(
781        &mut self,
782        symbol_name: &CStr,
783        object: &[u8],
784        symbols: &[(CString, usize)],
785    ) -> Result<(usize, orc::ResourceTracker, Arc<JitDylibGuard>)> {
786        let orc = self.ensure_orc()?;
787        let jd = orc.global.create_jit_dylib();
788        let jd_guard = Arc::new(JitDylibGuard { global: orc.global, jd });
789        let (addr, tracker) = link_jit_object_in_dylib(orc, jd, symbol_name, object, symbols)?;
790        Ok((addr, tracker, jd_guard))
791    }
792
793    /// Pops and returns the [`ResourceTracker`](orc::ResourceTracker) for the last committed
794    /// JIT module.
795    ///
796    /// Each call to [`jit_function`](revmc_backend::Backend::jit_function) commits a module
797    /// with its own tracker. The caller takes ownership and is responsible for calling
798    /// `tracker.remove()` to free the machine code when it is no longer needed.
799    ///
800    /// The caller must also hold a [`JitDylibGuard`] to prevent the owning JITDylib from
801    /// being recycled before the tracker is removed.
802    ///
803    /// Returns `None` in AOT mode or if no modules have been committed.
804    pub fn take_last_resource_tracker(&mut self) -> Option<orc::ResourceTracker> {
805        let orc = self.orc.as_mut()?;
806        let tracker = orc.loaded_trackers.pop()?;
807        let tracker_idx = orc.loaded_trackers.len();
808        orc.committed_functions.retain(|_, idx| *idx != tracker_idx);
809        Some(tracker)
810    }
811
812    /// Returns a shared handle that keeps this backend's JITDylib alive.
813    ///
814    /// The JITDylib will not be cleared or recycled until all `JitDylibGuard` handles
815    /// (and the backend itself) are dropped. Callers holding JIT function pointers must
816    /// retain a guard to prevent the backing code from being freed.
817    pub fn jit_dylib_guard(&self) -> Arc<JitDylibGuard> {
818        Arc::clone(&self.orc.as_ref().expect("jit_dylib_guard called in AOT mode").jd_guard)
819    }
820}
821
822impl BackendTypes for EvmLlvmBackend {
823    type Type = BasicTypeEnum<'static>;
824    type Value = BasicValueEnum<'static>;
825    type StackSlot = PointerValue<'static>;
826    type BasicBlock = BasicBlock<'static>;
827    type Function = FunctionValue<'static>;
828}
829
830impl TypeMethods for EvmLlvmBackend {
831    fn type_ptr(&self) -> Self::Type {
832        self.ty_ptr.into()
833    }
834
835    fn type_ptr_sized_int(&self) -> Self::Type {
836        self.ty_isize.into()
837    }
838
839    fn type_int(&self, bits: u32) -> Self::Type {
840        match bits {
841            1 => self.ty_i1,
842            8 => self.ty_i8,
843            16 => self.cx.i16_type(),
844            32 => self.ty_i32,
845            64 => self.ty_i64,
846            128 => self.cx.i128_type(),
847            256 => self.ty_i256,
848            bits => self
849                .cx
850                .custom_width_int_type(std::num::NonZeroU32::new(bits).unwrap())
851                .expect("custom int type"),
852        }
853        .into()
854    }
855
856    fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type {
857        ty.array_type(size).into()
858    }
859
860    fn type_bit_width(&self, ty: Self::Type) -> u32 {
861        ty.into_int_type().get_bit_width()
862    }
863}
864
865impl Backend for EvmLlvmBackend {
866    type Builder<'a>
867        = EvmLlvmBuilder<'a>
868    where
869        Self: 'a;
870    type FuncId = u32;
871
872    fn ir_extension(&self) -> &'static str {
873        "ll"
874    }
875
876    fn set_module_name(&mut self, name: &str) {
877        self.module().set_name(name);
878    }
879
880    fn config(&self) -> &BackendConfig {
881        &self.backend_config
882    }
883
884    fn apply_config(&mut self, config: BackendConfig) {
885        if self.backend_config.is_dumping != config.is_dumping {
886            self.machine.set_asm_verbosity(config.is_dumping);
887        }
888        if self.backend_config.opt_level != config.opt_level {
889            unsafe {
890                cpp::revmc_llvm_target_machine_set_opt_level(
891                    self.machine.as_mut_ptr(),
892                    convert_opt_level(config.opt_level).into(),
893                );
894            }
895        }
896        self.backend_config = config;
897    }
898
899    fn finalize_debug_info(&mut self) -> Result<()> {
900        if let Some(di) = &mut self.di_state
901            && !di.finalized
902        {
903            di.dibuilder.finalize();
904            di.finalized = true;
905        }
906        Ok(())
907    }
908
909    fn is_aot(&self) -> bool {
910        self.aot
911    }
912
913    fn function_name_is_unique(&self, name: &str) -> bool {
914        self.module().get_function(name).is_none()
915    }
916
917    fn dump_ir(&mut self, path: &Path) -> Result<()> {
918        self.module().print_to_file(path).map_err(error_msg)
919    }
920
921    fn dump_disasm(&mut self, path: &Path) -> Result<()> {
922        self.machine.write_to_file(self.module(), FileType::Assembly, path).map_err(error_msg)
923    }
924
925    fn build_function(
926        &mut self,
927        name: &str,
928        ret: Option<Self::Type>,
929        params: &[Self::Type],
930        param_names: &[&str],
931        linkage: revmc_backend::Linkage,
932    ) -> Result<(Self::Builder<'_>, Self::FuncId)> {
933        if !self.aot {
934            self.ensure_orc()?;
935        }
936        let (id, function) = if let Some((&id, _fname)) =
937            self.function_names.iter().find(|(_k, fname)| fname.as_str() == name)
938            && let Some(orc) = &self.orc
939            && let Some(function) = orc.staged_functions.get(&id).copied()
940            && let Some(function2) = self.module().get_function(name)
941            && function == function2
942        {
943            self.bcx.position_at_end(function.get_first_basic_block().unwrap());
944            (id, function)
945        } else {
946            let fn_type = self.fn_type(ret, params);
947            let function =
948                self.module().add_function(name, fn_type, Some(convert_linkage(linkage)));
949            cpp::set_dso_local(function);
950            if self.backend_config.is_dumping {
951                for (i, &name) in param_names.iter().enumerate() {
952                    function.get_nth_param(i as u32).expect(name).set_name(self.name(name));
953                }
954            }
955
956            let entry = self.cx.append_basic_block(function, self.name("entry"));
957            self.bcx.position_at_end(entry);
958
959            let id = self.function_counter;
960            self.function_counter += 1;
961            self.function_names.insert(id, name.to_string());
962            if let Some(orc) = &mut self.orc {
963                orc.staged_functions.insert(id, function);
964            }
965            (id, function)
966        };
967
968        // Attach debug info subprogram if debug is active.
969        self.ensure_di_state();
970        let debug_scope = if let Some(di) = &self.di_state {
971            let file = di.compile_unit.get_file();
972            let subroutine_type =
973                di.dibuilder.create_subroutine_type(file, None, &[], DIFlags::PUBLIC);
974            let subprogram = di.dibuilder.create_function(
975                di.compile_unit.as_debug_info_scope(),
976                name,
977                None,
978                file,
979                0,
980                subroutine_type,
981                true,
982                true,
983                0,
984                DIFlags::PUBLIC,
985                self.backend_config.opt_level != OptimizationLevel::None,
986            );
987            function.set_subprogram(subprogram);
988            Some(subprogram)
989        } else {
990            None
991        };
992
993        let builder = EvmLlvmBuilder { backend: self, function, debug_scope };
994        Ok((builder, id))
995    }
996
997    fn verify_module(&mut self) -> Result<()> {
998        self.module().verify().map_err(error_msg)
999    }
1000
1001    fn optimize_module(&mut self) -> Result<()> {
1002        // We use a custom pipeline instead of `default<O3>` because GVN is extremely slow on
1003        // the huge single-function modules that EVM compilation produces. Replacing GVN with
1004        // `early-cse` + `sccp` achieves equivalent or better code size and runtime performance
1005        // at ~5x faster compile time.
1006        //
1007        // The standard `default<O1>` is also slow (~730ms on snailtracer) because the loop
1008        // analysis infrastructure (LoopInfo, DominatorTree, MemorySSA, LCSSA) is expensive to
1009        // compute on functions with thousands of basic blocks, even though the loop passes
1010        // themselves do nothing useful — EVM has no natural loops to optimize.
1011        //
1012        // LICM (Loop Invariant Code Motion) helps tight EVM loops by hoisting gas counter and
1013        // stack slot loads/stores into registers. However, the loop analysis infrastructure is
1014        // quadratic on large functions — e.g. +430ms on snailtracer (7770 BBs) vs +0ms on
1015        // fibonacci (45 BBs). We skip it for functions with >4000 basic blocks.
1016        //
1017        // Can be overridden with `REVMC_PASSES` env var for experimentation.
1018        // From `opt --help`, `-passes`.
1019
1020        static PASSES_OVERRIDE: std::sync::OnceLock<Option<String>> = std::sync::OnceLock::new();
1021        static PASSES: std::sync::OnceLock<String> = std::sync::OnceLock::new();
1022        static PASSES_WITH_LICM: std::sync::OnceLock<String> = std::sync::OnceLock::new();
1023
1024        let passes = PASSES_OVERRIDE.get_or_init(|| std::env::var("REVMC_PASSES").ok());
1025        let passes = passes.as_deref().unwrap_or_else(|| match self.backend_config.opt_level {
1026            OptimizationLevel::None => "default<O0>",
1027            OptimizationLevel::Less | OptimizationLevel::Default => {
1028                let total_bbs: u32 =
1029                    self.module.get_functions().map(|f| f.count_basic_blocks()).sum();
1030                let with_licm = total_bbs <= 4000;
1031                let passes = if with_licm { &PASSES_WITH_LICM } else { &PASSES };
1032                passes.get_or_init(|| build_pass_pipeline(with_licm))
1033            }
1034            OptimizationLevel::Aggressive => "default<O3>",
1035        });
1036        let opts = PassBuilderOptions::create();
1037        self.module().run_passes(passes, &self.machine, opts).map_err(error_msg)
1038    }
1039
1040    fn write_object<W: std::io::Write>(&mut self, mut w: W) -> Result<()> {
1041        let buffer = self
1042            .machine
1043            .write_to_memory_buffer(self.module(), FileType::Object)
1044            .map_err(error_msg)?;
1045        w.write_all(buffer.as_slice())?;
1046        Ok(())
1047    }
1048
1049    fn jit_function(&mut self, id: Self::FuncId) -> Result<usize> {
1050        self.ensure_orc()?;
1051        self.commit_staged_module()?;
1052        let name_str = self.id_to_name(id);
1053        let name = CString::new(name_str).unwrap();
1054        let orc = self.orc.as_mut().unwrap();
1055        // Capture the compiled object buffer during lookup. LLJIT compiles lazily:
1056        // add_module_with_rt just registers the module, actual compilation happens
1057        // in lookup_in when the symbol is first requested.
1058        let mut captured = None;
1059        let _guard = ScopedObjCapture::install(&mut captured);
1060        let addr = orc.global.jit.lookup_in(orc.jd(), &name).map_err(error_msg)?;
1061        drop(_guard);
1062        if captured.is_some() {
1063            orc.last_compiled_object = captured;
1064        }
1065        Ok(addr)
1066    }
1067
1068    fn function_name(&self, id: Self::FuncId) -> Option<&str> {
1069        self.function_names.get(&id).map(|s| s.as_str())
1070    }
1071
1072    fn function_sizes(&self) -> Vec<(String, usize)> {
1073        let Some(orc) = &self.orc else { return Vec::new() };
1074        let Some(data) = orc.last_compiled_object.as_deref() else { return Vec::new() };
1075        let Ok(obj) = object::File::parse(data) else { return Vec::new() };
1076
1077        let mut result: Vec<_> = obj
1078            .symbols()
1079            .filter(|sym| sym.is_definition())
1080            .filter_map(|sym| {
1081                let name = sym.name().ok()?;
1082                self.function_names.values().any(|n| n == name).then_some(())?;
1083                Some((name.to_string(), sym.size() as usize))
1084            })
1085            .collect();
1086        result.sort_by_key(|(_, size)| std::cmp::Reverse(*size));
1087        result
1088    }
1089
1090    fn clear_ir(&mut self) -> Result<()> {
1091        self.di_state = None;
1092        self.function_names.clear();
1093        self.module = create_module(self.cx, &self.machine, self.aot)?;
1094        if let Some(orc) = &mut self.orc {
1095            orc.staged_functions.clear();
1096            orc.pending_symbols.clear();
1097            orc.maybe_clear_dead_pool_entries();
1098        }
1099        Ok(())
1100    }
1101
1102    unsafe fn free_function(&mut self, id: Self::FuncId) -> Result<()> {
1103        if let Some(orc) = &mut self.orc {
1104            // Remove from staging if not yet committed.
1105            if let Some(function) = orc.staged_functions.remove(&id) {
1106                unsafe { function.delete() };
1107            }
1108
1109            // Remove from committed trackers.
1110            if let Some(tracker_idx) = orc.committed_functions.remove(&id) {
1111                orc.loaded_trackers[tracker_idx].remove().map_err(error_msg)?;
1112                // Also remove co-committed functions since removing the
1113                // tracker frees all symbols in that module.
1114                orc.committed_functions.retain(|co_id, idx| {
1115                    if *idx == tracker_idx {
1116                        self.function_names.remove(co_id);
1117                        false
1118                    } else {
1119                        true
1120                    }
1121                });
1122                // Note: the tracker slot becomes dead but indices of later
1123                // trackers are unchanged, keeping the map consistent.
1124            }
1125        }
1126        self.function_names.remove(&id);
1127        Ok(())
1128    }
1129
1130    unsafe fn free_all_functions(&mut self) -> Result<()> {
1131        self.reset_jit()
1132    }
1133}
1134
1135/// Cached target information for the host machine.
1136#[derive(Debug)]
1137struct TargetInfo {
1138    triple: TargetTriple,
1139    target: Target,
1140    cpu: String,
1141    features: String,
1142}
1143
1144// SAFETY: No mutability is exposed and `TargetTriple` is an owned string.
1145unsafe impl std::marker::Send for TargetInfo {}
1146unsafe impl std::marker::Sync for TargetInfo {}
1147
1148impl Clone for TargetInfo {
1149    fn clone(&self) -> Self {
1150        let triple = TargetTriple::create(self.triple.as_str().to_str().unwrap());
1151        Self {
1152            target: Target::from_triple(&triple).unwrap(),
1153            triple,
1154            cpu: self.cpu.clone(),
1155            features: self.features.clone(),
1156        }
1157    }
1158}
1159
1160impl TargetInfo {
1161    fn new() -> &'static Self {
1162        static HOST_TARGET_INFO: OnceLock<TargetInfo> = OnceLock::new();
1163        HOST_TARGET_INFO.get_or_init(|| {
1164            let triple = TargetMachine::get_default_triple();
1165            let target = Target::from_triple(&triple).unwrap();
1166            let cpu = TargetMachine::get_host_cpu_name().to_string_lossy().into_owned();
1167            let features = TargetMachine::get_host_cpu_features().to_string_lossy().into_owned();
1168            Self { target, triple, cpu, features }
1169        })
1170    }
1171}
1172
1173/// The LLVM-based EVM bytecode compiler function builder.
1174#[derive(Debug)]
1175#[must_use]
1176pub struct EvmLlvmBuilder<'a> {
1177    backend: &'a mut EvmLlvmBackend,
1178    function: FunctionValue<'static>,
1179    debug_scope: Option<DISubprogram<'static>>,
1180}
1181
1182impl std::ops::Deref for EvmLlvmBuilder<'_> {
1183    type Target = EvmLlvmBackend;
1184
1185    #[inline]
1186    fn deref(&self) -> &Self::Target {
1187        self.backend
1188    }
1189}
1190
1191impl std::ops::DerefMut for EvmLlvmBuilder<'_> {
1192    #[inline]
1193    fn deref_mut(&mut self) -> &mut Self::Target {
1194        self.backend
1195    }
1196}
1197
1198impl EvmLlvmBuilder<'_> {
1199    #[allow(dead_code)]
1200    fn extract_value(
1201        &mut self,
1202        value: BasicValueEnum<'static>,
1203        index: u32,
1204        name: &str,
1205    ) -> BasicValueEnum<'static> {
1206        self.bcx.build_extract_value(value.into_struct_value(), index, self.name(name)).unwrap()
1207    }
1208
1209    fn memcpy_inner(
1210        &mut self,
1211        dst: BasicValueEnum<'static>,
1212        src: BasicValueEnum<'static>,
1213        len: BasicValueEnum<'static>,
1214        inline: bool,
1215    ) {
1216        let dst = dst.into_pointer_value();
1217        let src = src.into_pointer_value();
1218        let len = len.into_int_value();
1219        let volatile = self.bool_const(false);
1220        let name = format!(
1221            "llvm.memcpy{}.p0.p0.{}",
1222            if inline { ".inline" } else { "" },
1223            fmt_ty(len.get_type().into()),
1224        );
1225        let memcpy = self.get_or_add_function(&name, |this| {
1226            this.ty_void.fn_type(
1227                &[this.ty_ptr.into(), this.ty_ptr.into(), this.ty_i64.into(), this.ty_i1.into()],
1228                false,
1229            )
1230        });
1231        self.bcx
1232            .build_call(memcpy, &[dst.into(), src.into(), len.into(), volatile.into()], "")
1233            .unwrap();
1234    }
1235
1236    #[allow(dead_code)]
1237    fn call_overflow_function(
1238        &mut self,
1239        name: &str,
1240        lhs: BasicValueEnum<'static>,
1241        rhs: BasicValueEnum<'static>,
1242    ) -> (BasicValueEnum<'static>, BasicValueEnum<'static>) {
1243        let f = self.get_overflow_function(name, lhs.get_type());
1244        let result = self.call(f, &[lhs, rhs]).unwrap();
1245        (self.extract_value(result, 0, "result"), self.extract_value(result, 1, "overflow"))
1246    }
1247
1248    #[allow(dead_code)]
1249    fn get_overflow_function(
1250        &mut self,
1251        name: &str,
1252        ty: BasicTypeEnum<'static>,
1253    ) -> FunctionValue<'static> {
1254        let name = format!("llvm.{name}.with.overflow.{}", fmt_ty(ty));
1255        self.get_or_add_function(&name, |this| {
1256            this.fn_type(
1257                Some(this.cx.struct_type(&[ty, this.ty_i1.into()], false).into()),
1258                &[ty, ty],
1259            )
1260        })
1261    }
1262
1263    fn get_sat_function(
1264        &mut self,
1265        name: &str,
1266        ty: BasicTypeEnum<'static>,
1267    ) -> FunctionValue<'static> {
1268        let name = format!("llvm.{name}.sat.{}", fmt_ty(ty));
1269        self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]))
1270    }
1271
1272    fn get_or_add_function(
1273        &mut self,
1274        name: &str,
1275        mk_ty: impl FnOnce(&mut Self) -> FunctionType<'static>,
1276    ) -> FunctionValue<'static> {
1277        match self.module().get_function(name) {
1278            Some(function) => function,
1279            None => {
1280                let ty = mk_ty(self);
1281                self.module().add_function(name, ty, None)
1282            }
1283        }
1284    }
1285
1286    fn set_branch_weights(
1287        &self,
1288        inst: InstructionValue<'static>,
1289        weights: impl IntoIterator<Item = u32>,
1290    ) {
1291        let weights = weights.into_iter();
1292        let mut values = Vec::<BasicMetadataValueEnum<'static>>::with_capacity(
1293            1 + weights.size_hint().1.unwrap(),
1294        );
1295        values.push(self.cx.metadata_string("branch_weights").into());
1296        for weight in weights {
1297            values.push(self.ty_i32.const_int(weight as u64, false).into());
1298        }
1299        let metadata = self.cx.metadata_node(&values);
1300        let kind_id = self.cx.get_kind_id("prof");
1301        inst.set_metadata(metadata, kind_id).unwrap();
1302    }
1303
1304    fn assume_inner(&mut self, cond: BasicValueEnum<'static>) -> CallSiteValue<'static> {
1305        let function = self.get_or_add_function("llvm.assume", |this| {
1306            this.ty_void.fn_type(&[this.ty_i1.into()], false)
1307        });
1308        self.bcx.build_call(function, &[cond.into()], "").unwrap()
1309    }
1310}
1311
1312impl BackendTypes for EvmLlvmBuilder<'_> {
1313    type Type = <EvmLlvmBackend as BackendTypes>::Type;
1314    type Value = <EvmLlvmBackend as BackendTypes>::Value;
1315    type StackSlot = <EvmLlvmBackend as BackendTypes>::StackSlot;
1316    type BasicBlock = <EvmLlvmBackend as BackendTypes>::BasicBlock;
1317    type Function = <EvmLlvmBackend as BackendTypes>::Function;
1318}
1319
1320impl TypeMethods for EvmLlvmBuilder<'_> {
1321    fn type_ptr(&self) -> Self::Type {
1322        self.backend.type_ptr()
1323    }
1324
1325    fn type_ptr_sized_int(&self) -> Self::Type {
1326        self.backend.type_ptr_sized_int()
1327    }
1328
1329    fn type_int(&self, bits: u32) -> Self::Type {
1330        self.backend.type_int(bits)
1331    }
1332
1333    fn type_array(&self, ty: Self::Type, size: u32) -> Self::Type {
1334        self.backend.type_array(ty, size)
1335    }
1336
1337    fn type_bit_width(&self, ty: Self::Type) -> u32 {
1338        self.backend.type_bit_width(ty)
1339    }
1340}
1341
1342impl Builder for EvmLlvmBuilder<'_> {
1343    fn create_block(&mut self, name: &str) -> Self::BasicBlock {
1344        self.cx.append_basic_block(self.function, self.name(name))
1345    }
1346
1347    fn create_block_after(&mut self, after: Self::BasicBlock, name: &str) -> Self::BasicBlock {
1348        self.cx.insert_basic_block_after(after, self.name(name))
1349    }
1350
1351    fn switch_to_block(&mut self, block: Self::BasicBlock) {
1352        self.bcx.position_at_end(block);
1353    }
1354
1355    fn seal_block(&mut self, block: Self::BasicBlock) {
1356        let _ = block;
1357        // Nothing to do.
1358    }
1359
1360    fn seal_all_blocks(&mut self) {
1361        // Nothing to do.
1362    }
1363
1364    fn set_current_block_cold(&mut self) {
1365        let true_ = self.bool_const(true);
1366        let callsite = self.assume_inner(true_);
1367        let cold = self.cx.create_enum_attribute(Attribute::get_named_enum_kind_id("cold"), 0);
1368        callsite.add_attribute(AttributeLoc::Function, cold);
1369    }
1370
1371    fn current_block(&mut self) -> Option<Self::BasicBlock> {
1372        self.bcx.get_insert_block()
1373    }
1374
1375    fn block_addr(&mut self, block: Self::BasicBlock) -> Option<Self::Value> {
1376        unsafe { block.get_address().map(Into::into) }
1377    }
1378
1379    fn add_comment_to_current_inst(&mut self, comment: &str) {
1380        if !self.backend_config.is_dumping {
1381            return;
1382        }
1383        let Some(block) = self.current_block() else { return };
1384        let Some(ins) = block.get_last_instruction() else { return };
1385        let metadata = self.cx.metadata_string(comment);
1386        let metadata = self.cx.metadata_node(&[metadata.into()]);
1387        ins.set_metadata(metadata, self.cx.get_kind_id("annotation")).unwrap();
1388    }
1389
1390    fn set_debug_location(&mut self, line: u32, col: u32) {
1391        let Some(scope) = self.debug_scope else { return };
1392        let Some(di) = &self.di_state else { return };
1393        let loc = di.dibuilder.create_debug_location(
1394            self.cx,
1395            line,
1396            col,
1397            scope.as_debug_info_scope(),
1398            None,
1399        );
1400        self.bcx.set_current_debug_location(loc);
1401    }
1402
1403    fn clear_debug_location(&mut self) {
1404        if self.debug_scope.is_some() {
1405            self.bcx.unset_current_debug_location();
1406        }
1407    }
1408
1409    fn fn_param(&mut self, index: usize) -> Self::Value {
1410        self.function.get_nth_param(index as _).unwrap()
1411    }
1412
1413    fn num_fn_params(&self) -> usize {
1414        self.function.count_params() as usize
1415    }
1416
1417    fn bool_const(&mut self, value: bool) -> Self::Value {
1418        self.ty_i1.const_int(value as u64, false).into()
1419    }
1420
1421    fn iconst(&mut self, ty: Self::Type, value: i64) -> Self::Value {
1422        ty.into_int_type().const_int(value as u64, value.is_negative()).into()
1423    }
1424
1425    fn uconst(&mut self, ty: Self::Type, value: u64) -> Self::Value {
1426        ty.into_int_type().const_int(value, false).into()
1427    }
1428
1429    fn iconst_256(&mut self, value: impl TryInto<U256>) -> Self::Value {
1430        let value = value.try_into().ok().expect("invalid U256 value");
1431        if let Ok(low) = value.try_into() {
1432            return self.ty_i256.const_int(low, false).into();
1433        }
1434
1435        self.scratch.clear();
1436        write!(self.scratch, "{value:x}").unwrap();
1437        self.ty_i256.const_int_from_string(&self.scratch, StringRadix::Hexadecimal).unwrap().into()
1438    }
1439
1440    fn str_const(&mut self, value: &str) -> Self::Value {
1441        self.bcx.build_global_string_ptr(value, "").unwrap().as_pointer_value().into()
1442    }
1443
1444    fn nullptr(&mut self) -> Self::Value {
1445        self.ty_ptr.const_null().into()
1446    }
1447
1448    fn new_stack_slot_raw(&mut self, ty: Self::Type, name: &str) -> Self::StackSlot {
1449        // let ty = self.ty_i8.array_type(size);
1450        // let ptr = self.bcx.build_alloca(ty, self.name(name)).unwrap();
1451        // ptr.as_instruction().unwrap().set_alignment(align).unwrap();
1452        // ptr
1453        self.bcx.build_alloca(ty, self.name(name)).unwrap()
1454    }
1455
1456    fn stack_load(&mut self, ty: Self::Type, slot: Self::StackSlot, name: &str) -> Self::Value {
1457        self.load(ty, slot.into(), name)
1458    }
1459
1460    fn stack_store(&mut self, value: Self::Value, slot: Self::StackSlot) {
1461        self.store(value, slot.into())
1462    }
1463
1464    fn stack_addr(&mut self, ty: Self::Type, slot: Self::StackSlot) -> Self::Value {
1465        let _ = ty;
1466        slot.into()
1467    }
1468
1469    fn load(&mut self, ty: Self::Type, ptr: Self::Value, name: &str) -> Self::Value {
1470        let value = self.bcx.build_load(ty, ptr.into_pointer_value(), self.name(name)).unwrap();
1471        if ty == self.ty_i256.into() {
1472            self.current_block().unwrap().get_last_instruction().unwrap().set_alignment(8).unwrap();
1473        }
1474        value
1475    }
1476
1477    fn load_aligned(
1478        &mut self,
1479        ty: Self::Type,
1480        ptr: Self::Value,
1481        align: usize,
1482        name: &str,
1483    ) -> Self::Value {
1484        let value = self.bcx.build_load(ty, ptr.into_pointer_value(), self.name(name)).unwrap();
1485        self.current_block()
1486            .unwrap()
1487            .get_last_instruction()
1488            .unwrap()
1489            .set_alignment(align as u32)
1490            .unwrap();
1491        value
1492    }
1493
1494    fn store(&mut self, value: Self::Value, ptr: Self::Value) {
1495        let inst = self.bcx.build_store(ptr.into_pointer_value(), value).unwrap();
1496        if value.get_type() == self.ty_i256.into() {
1497            inst.set_alignment(8).unwrap();
1498        }
1499    }
1500
1501    fn store_aligned(&mut self, value: Self::Value, ptr: Self::Value, align: usize) {
1502        let inst = self.bcx.build_store(ptr.into_pointer_value(), value).unwrap();
1503        inst.set_alignment(align as u32).unwrap();
1504    }
1505
1506    fn nop(&mut self) {
1507        // LLVM doesn't have a NOP instruction.
1508    }
1509
1510    fn ret(&mut self, values: &[Self::Value]) {
1511        match values {
1512            [] => self.bcx.build_return(None),
1513            [value] => self.bcx.build_return(Some(value)),
1514            values => self.bcx.build_aggregate_return(values),
1515        }
1516        .unwrap();
1517    }
1518
1519    fn assume(&mut self, cond: Self::Value) {
1520        self.assume_inner(cond);
1521    }
1522
1523    fn icmp(&mut self, cond: IntCC, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1524        self.bcx
1525            .build_int_compare(convert_intcc(cond), lhs.into_int_value(), rhs.into_int_value(), "")
1526            .unwrap()
1527            .into()
1528    }
1529
1530    fn icmp_imm(&mut self, cond: IntCC, lhs: Self::Value, rhs: i64) -> Self::Value {
1531        let rhs = self.iconst(lhs.get_type(), rhs);
1532        self.icmp(cond, lhs, rhs)
1533    }
1534
1535    fn is_null(&mut self, ptr: Self::Value) -> Self::Value {
1536        self.bcx.build_is_null(ptr.into_pointer_value(), "").unwrap().into()
1537    }
1538
1539    fn is_not_null(&mut self, ptr: Self::Value) -> Self::Value {
1540        self.bcx.build_is_not_null(ptr.into_pointer_value(), "").unwrap().into()
1541    }
1542
1543    fn br(&mut self, dest: Self::BasicBlock) {
1544        self.bcx.build_unconditional_branch(dest).unwrap();
1545    }
1546
1547    fn brif(
1548        &mut self,
1549        cond: Self::Value,
1550        then_block: Self::BasicBlock,
1551        else_block: Self::BasicBlock,
1552    ) {
1553        self.bcx.build_conditional_branch(cond.into_int_value(), then_block, else_block).unwrap();
1554    }
1555
1556    fn brif_cold(
1557        &mut self,
1558        cond: Self::Value,
1559        then_block: Self::BasicBlock,
1560        else_block: Self::BasicBlock,
1561        then_is_cold: bool,
1562    ) {
1563        let inst = self
1564            .bcx
1565            .build_conditional_branch(cond.into_int_value(), then_block, else_block)
1566            .unwrap();
1567        let weights = if then_is_cold { [1, DEFAULT_WEIGHT] } else { [DEFAULT_WEIGHT, 1] };
1568        self.set_branch_weights(inst, weights);
1569    }
1570
1571    fn switch(
1572        &mut self,
1573        index: Self::Value,
1574        default: Self::BasicBlock,
1575        targets: &[(u64, Self::BasicBlock)],
1576        default_is_cold: bool,
1577    ) {
1578        let ty = index.get_type().into_int_type();
1579        let targets =
1580            targets.iter().map(|(v, b)| (ty.const_int(*v, false), *b)).collect::<Vec<_>>();
1581        let inst = self.bcx.build_switch(index.into_int_value(), default, &targets).unwrap();
1582        if default_is_cold {
1583            let weights = iter::once(1).chain(iter::repeat_n(DEFAULT_WEIGHT, targets.len()));
1584            self.set_branch_weights(inst, weights);
1585        }
1586    }
1587
1588    fn br_indirect(&mut self, address: Self::Value, destinations: &[Self::BasicBlock]) {
1589        let _ = self.bcx.build_indirect_branch(address, destinations).unwrap();
1590    }
1591
1592    fn phi(&mut self, ty: Self::Type, incoming: &[(Self::Value, Self::BasicBlock)]) -> Self::Value {
1593        let incoming = incoming
1594            .iter()
1595            .map(|(value, block)| (value as &dyn BasicValue<'_>, *block))
1596            .collect::<Vec<_>>();
1597        let phi = self.bcx.build_phi(ty, "").unwrap();
1598        phi.add_incoming(&incoming);
1599        phi.as_basic_value()
1600    }
1601
1602    fn select(
1603        &mut self,
1604        cond: Self::Value,
1605        then_value: Self::Value,
1606        else_value: Self::Value,
1607    ) -> Self::Value {
1608        self.bcx.build_select(cond.into_int_value(), then_value, else_value, "").unwrap()
1609    }
1610
1611    fn iadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1612        self.bcx.build_int_add(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1613    }
1614
1615    fn isub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1616        self.bcx.build_int_sub(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1617    }
1618
1619    fn imul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1620        self.bcx.build_int_mul(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1621    }
1622
1623    fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1624        self.bcx
1625            .build_int_unsigned_div(lhs.into_int_value(), rhs.into_int_value(), "")
1626            .unwrap()
1627            .into()
1628    }
1629
1630    fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1631        self.bcx
1632            .build_int_signed_div(lhs.into_int_value(), rhs.into_int_value(), "")
1633            .unwrap()
1634            .into()
1635    }
1636
1637    fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1638        self.bcx
1639            .build_int_unsigned_rem(lhs.into_int_value(), rhs.into_int_value(), "")
1640            .unwrap()
1641            .into()
1642    }
1643
1644    fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1645        self.bcx
1646            .build_int_signed_rem(lhs.into_int_value(), rhs.into_int_value(), "")
1647            .unwrap()
1648            .into()
1649    }
1650
1651    fn iadd_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1652        let rhs = self.iconst(lhs.get_type(), rhs);
1653        self.iadd(lhs, rhs)
1654    }
1655
1656    fn isub_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1657        let rhs = self.iconst(lhs.get_type(), rhs);
1658        self.isub(lhs, rhs)
1659    }
1660
1661    fn imul_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1662        let rhs = self.iconst(lhs.get_type(), rhs);
1663        self.imul(lhs, rhs)
1664    }
1665
1666    // - [Avoid using arithmetic intrinsics](https://llvm.org/docs/Frontend/PerformanceTips.html)
1667    // - [Don't use usub.with.overflow intrinsic](https://github.com/rust-lang/rust/pull/103299)
1668    // - [for unsigned add overflow the recommended pattern is x + y < x](https://github.com/rust-lang/rust/pull/124114#issuecomment-2066173305)
1669    fn uadd_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
1670        let result = self.iadd(lhs, rhs);
1671        let overflow = self.icmp(IntCC::UnsignedLessThan, result, rhs);
1672        (result, overflow)
1673    }
1674
1675    fn usub_overflow(&mut self, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
1676        let result = self.isub(lhs, rhs);
1677        let overflow = self.icmp(IntCC::UnsignedLessThan, lhs, rhs);
1678        (result, overflow)
1679    }
1680
1681    fn uadd_sat(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1682        let f = self.get_sat_function("uadd", lhs.get_type());
1683        self.call(f, &[lhs, rhs]).unwrap()
1684    }
1685
1686    fn umax(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1687        let ty = lhs.get_type();
1688        let name = format!("llvm.umax.{}", fmt_ty(ty));
1689        let max = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]));
1690        self.call(max, &[lhs, rhs]).unwrap()
1691    }
1692
1693    fn umin(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1694        let ty = lhs.get_type();
1695        let name = format!("llvm.umin.{}", fmt_ty(ty));
1696        let min = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, ty]));
1697        self.call(min, &[lhs, rhs]).unwrap()
1698    }
1699
1700    fn bswap(&mut self, value: Self::Value) -> Self::Value {
1701        let ty = value.get_type();
1702        let name = format!("llvm.bswap.{}", fmt_ty(ty));
1703        let bswap = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty]));
1704        self.call(bswap, &[value]).unwrap()
1705    }
1706
1707    fn bitor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1708        self.bcx.build_or(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1709    }
1710
1711    fn bitand(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1712        self.bcx.build_and(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1713    }
1714
1715    fn bitxor(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1716        self.bcx.build_xor(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1717    }
1718
1719    fn bitnot(&mut self, value: Self::Value) -> Self::Value {
1720        self.bcx.build_not(value.into_int_value(), "").unwrap().into()
1721    }
1722
1723    fn clz(&mut self, value: Self::Value) -> Self::Value {
1724        let ty = value.get_type();
1725        let i1_ty = self.type_int(1);
1726        let name = format!("llvm.ctlz.{}", fmt_ty(ty));
1727        let ctlz = self.get_or_add_function(&name, |this| this.fn_type(Some(ty), &[ty, i1_ty]));
1728        let is_poison_on_zero = self.bool_const(false);
1729        self.call(ctlz, &[value, is_poison_on_zero]).unwrap()
1730    }
1731
1732    fn bitor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1733        let rhs = self.iconst(lhs.get_type(), rhs);
1734        self.bitor(lhs, rhs)
1735    }
1736
1737    fn bitand_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1738        let rhs = self.iconst(lhs.get_type(), rhs);
1739        self.bitand(lhs, rhs)
1740    }
1741
1742    fn bitxor_imm(&mut self, lhs: Self::Value, rhs: i64) -> Self::Value {
1743        let rhs = self.iconst(lhs.get_type(), rhs);
1744        self.bitxor(lhs, rhs)
1745    }
1746
1747    fn ishl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1748        self.bcx.build_left_shift(lhs.into_int_value(), rhs.into_int_value(), "").unwrap().into()
1749    }
1750
1751    fn ushr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1752        self.bcx
1753            .build_right_shift(lhs.into_int_value(), rhs.into_int_value(), false, "")
1754            .unwrap()
1755            .into()
1756    }
1757
1758    fn sshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value {
1759        self.bcx
1760            .build_right_shift(lhs.into_int_value(), rhs.into_int_value(), true, "")
1761            .unwrap()
1762            .into()
1763    }
1764
1765    fn zext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value {
1766        self.bcx.build_int_z_extend(value.into_int_value(), ty.into_int_type(), "").unwrap().into()
1767    }
1768
1769    fn sext(&mut self, ty: Self::Type, value: Self::Value) -> Self::Value {
1770        self.bcx.build_int_s_extend(value.into_int_value(), ty.into_int_type(), "").unwrap().into()
1771    }
1772
1773    fn ireduce(&mut self, to: Self::Type, value: Self::Value) -> Self::Value {
1774        self.bcx.build_int_truncate(value.into_int_value(), to.into_int_type(), "").unwrap().into()
1775    }
1776
1777    fn inttoptr(&mut self, value: Self::Value, ty: Self::Type) -> Self::Value {
1778        self.bcx
1779            .build_int_to_ptr(value.into_int_value(), ty.into_pointer_type(), "")
1780            .unwrap()
1781            .into()
1782    }
1783
1784    fn gep(
1785        &mut self,
1786        elem_ty: Self::Type,
1787        ptr: Self::Value,
1788        indexes: &[Self::Value],
1789        name: &str,
1790    ) -> Self::Value {
1791        let indexes = indexes.iter().map(|idx| idx.into_int_value()).collect::<Vec<_>>();
1792        unsafe {
1793            self.bcx.build_in_bounds_gep(
1794                elem_ty,
1795                ptr.into_pointer_value(),
1796                &indexes,
1797                self.name(name),
1798            )
1799        }
1800        .unwrap()
1801        .into()
1802    }
1803
1804    fn tail_call(
1805        &mut self,
1806        function: Self::Function,
1807        args: &[Self::Value],
1808        tail_call: TailCallKind,
1809    ) -> Option<Self::Value> {
1810        let args = args.iter().copied().map(Into::into).collect::<Vec<_>>();
1811        let callsite = self.bcx.build_call(function, &args, "").unwrap();
1812        if let Some(call_conv) = function_call_conv(function) {
1813            unsafe {
1814                inkwell::llvm_sys::core::LLVMSetInstructionCallConv(
1815                    callsite.as_value_ref(),
1816                    call_conv as _,
1817                );
1818            }
1819        }
1820        if tail_call != TailCallKind::None {
1821            callsite.set_tail_call_kind(convert_tail_call_kind(tail_call));
1822        }
1823        callsite.try_as_basic_value().basic()
1824    }
1825
1826    fn is_compile_time_known(&mut self, value: Self::Value) -> Option<Self::Value> {
1827        let ty = value.get_type();
1828        let name = format!("llvm.is.constant.{}", fmt_ty(ty));
1829        let f =
1830            self.get_or_add_function(&name, |this| this.fn_type(Some(this.ty_i1.into()), &[ty]));
1831        Some(self.call(f, &[value]).unwrap())
1832    }
1833
1834    fn memcpy(&mut self, dst: Self::Value, src: Self::Value, len: Self::Value) {
1835        self.memcpy_inner(dst, src, len, false);
1836    }
1837
1838    fn memcpy_inline(&mut self, dst: Self::Value, src: Self::Value, len: i64) {
1839        let len = self.iconst(self.ty_i64.into(), len);
1840        self.memcpy_inner(dst, src, len, true);
1841    }
1842
1843    fn unreachable(&mut self) {
1844        self.bcx.build_unreachable().unwrap();
1845    }
1846
1847    fn get_or_build_function(
1848        &mut self,
1849        name: &str,
1850        params: &[Self::Type],
1851        ret: Option<Self::Type>,
1852        linkage: revmc_backend::Linkage,
1853        build: impl FnOnce(&mut Self),
1854    ) -> Self::Function {
1855        if let Some(function) = self.module().get_function(name) {
1856            return function;
1857        }
1858
1859        let before = self.current_block();
1860
1861        let func_ty = self.fn_type(ret, params);
1862        let function = self.module().add_function(name, func_ty, Some(convert_linkage(linkage)));
1863        cpp::set_dso_local(function);
1864        let prev_function = std::mem::replace(&mut self.function, function);
1865
1866        let entry = self.cx.append_basic_block(function, self.name("entry"));
1867        self.bcx.position_at_end(entry);
1868        build(self);
1869        if let Some(before) = before {
1870            self.bcx.position_at_end(before);
1871        }
1872
1873        self.function = prev_function;
1874
1875        function
1876    }
1877
1878    fn get_function(&mut self, name: &str) -> Option<Self::Function> {
1879        self.module().get_function(name)
1880    }
1881
1882    fn get_printf_function(&mut self) -> Self::Function {
1883        let name = "printf";
1884        if let Some(function) = self.module().get_function(name) {
1885            return function;
1886        }
1887
1888        let ty = self.cx.void_type().fn_type(&[self.ty_ptr.into()], true);
1889        let function =
1890            self.module().add_function(name, ty, Some(inkwell::module::Linkage::External));
1891        cpp::set_dso_local(function);
1892        function
1893    }
1894
1895    fn add_function(
1896        &mut self,
1897        name: &str,
1898        params: &[Self::Type],
1899        ret: Option<Self::Type>,
1900        address: Option<usize>,
1901        linkage: revmc_backend::Linkage,
1902        call_conv: CallConv,
1903    ) -> Self::Function {
1904        let func_ty = self.fn_type(ret, params);
1905        let function = self.module().add_function(name, func_ty, Some(convert_linkage(linkage)));
1906        set_function_call_conv(function, call_conv);
1907        cpp::set_dso_local(function);
1908        if let Some(address) = address
1909            && let Some(orc) = &mut self.orc
1910        {
1911            orc.pending_symbols.push((CString::new(name).unwrap(), address));
1912        }
1913        function
1914    }
1915
1916    fn add_function_stub(
1917        &mut self,
1918        function: Self::Function,
1919        call_conv: CallConv,
1920    ) -> Self::Function {
1921        let name = function.get_name().to_string_lossy();
1922        let stub_name = format!("{name}.stub");
1923        if let Some(stub) = self.module().get_function(&stub_name) {
1924            return stub;
1925        }
1926
1927        let func_ty = function.get_type();
1928        let stub = self.module().add_function(
1929            &stub_name,
1930            func_ty,
1931            Some(inkwell::module::Linkage::Internal),
1932        );
1933        set_function_call_conv(stub, call_conv);
1934        cpp::set_dso_local(stub);
1935        self.add_function_attribute(
1936            Some(stub),
1937            revmc_backend::Attribute::NoInline,
1938            revmc_backend::FunctionAttributeLocation::Function,
1939        );
1940
1941        let before = self.bcx.get_insert_block();
1942        let debug_location = self.debug_scope.and_then(|_| self.bcx.get_current_debug_location());
1943        self.bcx.unset_current_debug_location();
1944
1945        let entry = self.cx.append_basic_block(stub, self.name("stub"));
1946        self.bcx.position_at_end(entry);
1947        let args =
1948            (0..stub.count_params()).map(|i| stub.get_nth_param(i).unwrap()).collect::<Vec<_>>();
1949        if let Some(value) = self.call(function, &args) {
1950            self.bcx.build_return(Some(&value)).unwrap();
1951        } else {
1952            self.bcx.build_return(None).unwrap();
1953        }
1954
1955        if let Some(before) = before {
1956            self.bcx.position_at_end(before);
1957        }
1958        if let Some(debug_location) = debug_location {
1959            self.bcx.set_current_debug_location(debug_location);
1960        }
1961
1962        stub
1963    }
1964
1965    fn add_function_attribute(
1966        &mut self,
1967        function: Option<Self::Function>,
1968        attribute: revmc_backend::Attribute,
1969        loc: revmc_backend::FunctionAttributeLocation,
1970    ) {
1971        let func = function.unwrap_or(self.function);
1972        let loc = convert_attribute_loc(loc);
1973        let attr = convert_attribute(self, attribute);
1974        func.add_attribute(loc, attr);
1975    }
1976}
1977
1978/// Builds the LLVM pass pipeline string. See [`EvmLlvmBackend::optimize_module`].
1979fn build_pass_pipeline(with_licm: bool) -> String {
1980    let mut passes = String::from("module(globalopt,cgscc(inline),globalopt,function(");
1981    let function_passes: &[&str] = &[
1982        "simplifycfg",
1983        "sroa",
1984        "early-cse",
1985        "jump-threading",
1986        "correlated-propagation",
1987        "simplifycfg",
1988        "instcombine<no-verify-fixpoint>",
1989    ];
1990    let licm_passes: &[&str] =
1991        &["loop-mssa(licm,loop-rotate,licm)", "simplifycfg", "instcombine<no-verify-fixpoint>"];
1992    let post_passes: &[&str] = &[
1993        "sroa",
1994        "early-cse",
1995        "sccp",
1996        "instcombine<no-verify-fixpoint>",
1997        "adce",
1998        "dse",
1999        "simplifycfg",
2000    ];
2001
2002    let iter =
2003        function_passes.iter().chain(if with_licm { licm_passes } else { &[] }).chain(post_passes);
2004    for (i, pass) in iter.enumerate() {
2005        if i > 0 {
2006            passes.push(',');
2007        }
2008        passes.push_str(pass);
2009    }
2010    passes.push_str("),globaldce)");
2011    passes
2012}
2013
2014fn init() -> Result<()> {
2015    let mut init_result = Ok(());
2016    static INIT: Once = Once::new();
2017    INIT.call_once(|| init_result = init_());
2018    init_result
2019}
2020
2021fn init_() -> Result<()> {
2022    // TODO: This also reports "PLEASE submit a bug report to..." when the segfault is
2023    // outside of LLVM.
2024    // enable_llvm_pretty_stack_trace();
2025
2026    extern "C" fn report_fatal_error(msg: *const std::ffi::c_char) {
2027        let msg_cstr = unsafe { std::ffi::CStr::from_ptr(msg) };
2028        let msg = msg_cstr.to_string_lossy();
2029        error!(target: "llvm", "LLVM fatal error: {msg}");
2030    }
2031
2032    unsafe {
2033        install_fatal_error_handler(report_fatal_error);
2034    }
2035
2036    // Collect extra LLVM args from `REVMC_LLVM_ARGS` env var (space-separated).
2037    let extra: Vec<CString> = std::env::var("REVMC_LLVM_ARGS")
2038        .ok()
2039        .iter()
2040        .flat_map(|s| s.split_whitespace().filter_map(|s| CString::new(s).ok()).collect::<Vec<_>>())
2041        .collect();
2042    if !extra.is_empty() {
2043        debug!(extra = ?&extra, "passing extra LLVM args");
2044    }
2045
2046    // The first arg is only used in `-help` output AFAICT.
2047    let mut args = vec![
2048        c"revmc-llvm".as_ptr(),
2049        c"-x86-asm-syntax=intel".as_ptr(),
2050        // CodeGenPrepare's memory optimization has a pathological interaction with our IR shape:
2051        // repeated stores to the same `gas_remaining` pointer in a huge function cause
2052        // MemoryDependenceResults::getNonLocalPointerDependency to do superlinear work.
2053        // Mark functions with ≥1000 BBs as "huge" to skip the expensive CGP memory opts,
2054        // which produce identical codegen at that scale. Small functions still benefit from
2055        // CGP's address sinking and branch optimizations.
2056        c"--cgpp-huge-func=1000".as_ptr(),
2057        // Greedy register allocation's global live range splitting is superlinear on large
2058        // functions with many long-lived values. Treat smaller live ranges as huge so LLVM uses
2059        // the cheaper splitting path; this preserves code size on snailtracer while cutting llc
2060        // time by ~3x.
2061        c"--huge-size-for-split=64".as_ptr(),
2062    ];
2063    args.extend(extra.iter().map(|s| s.as_ptr()));
2064    unsafe {
2065        inkwell::llvm_sys::support::LLVMParseCommandLineOptions(
2066            args.len() as i32,
2067            args.as_ptr(),
2068            std::ptr::null(),
2069        )
2070    }
2071
2072    let config = InitializationConfig {
2073        asm_parser: false,
2074        asm_printer: true,
2075        base: true,
2076        disassembler: true,
2077        info: true,
2078        machine_code: true,
2079    };
2080    Target::initialize_all(&config);
2081
2082    Ok(())
2083}
2084
2085fn create_module<'ctx>(
2086    cx: &'ctx Context,
2087    machine: &TargetMachine,
2088    aot: bool,
2089) -> Result<Module<'ctx>> {
2090    let module_name = "evm";
2091    let module = cx.create_module(module_name);
2092    module.set_source_file_name(module_name);
2093    module.set_data_layout(&machine.get_target_data().get_data_layout());
2094    module.set_triple(&machine.get_triple());
2095    if aot {
2096        module.add_basic_value_flag(
2097            "PIC Level",
2098            FlagBehavior::Error, // TODO: Min
2099            cx.i32_type().const_int(2, false),
2100        );
2101        module.add_basic_value_flag(
2102            "RtLibUseGOT",
2103            FlagBehavior::Warning,
2104            cx.i32_type().const_int(1, false),
2105        );
2106    }
2107    Ok(module)
2108}
2109
2110fn convert_intcc(cond: IntCC) -> IntPredicate {
2111    match cond {
2112        IntCC::Equal => IntPredicate::EQ,
2113        IntCC::NotEqual => IntPredicate::NE,
2114        IntCC::SignedLessThan => IntPredicate::SLT,
2115        IntCC::SignedGreaterThanOrEqual => IntPredicate::SGE,
2116        IntCC::SignedGreaterThan => IntPredicate::SGT,
2117        IntCC::SignedLessThanOrEqual => IntPredicate::SLE,
2118        IntCC::UnsignedLessThan => IntPredicate::ULT,
2119        IntCC::UnsignedGreaterThanOrEqual => IntPredicate::UGE,
2120        IntCC::UnsignedGreaterThan => IntPredicate::UGT,
2121        IntCC::UnsignedLessThanOrEqual => IntPredicate::ULE,
2122    }
2123}
2124
2125fn convert_opt_level(level: OptimizationLevel) -> inkwell::OptimizationLevel {
2126    match level {
2127        OptimizationLevel::None => inkwell::OptimizationLevel::None,
2128        OptimizationLevel::Less => inkwell::OptimizationLevel::Less,
2129        OptimizationLevel::Default => inkwell::OptimizationLevel::Default,
2130        OptimizationLevel::Aggressive => inkwell::OptimizationLevel::Aggressive,
2131    }
2132}
2133
2134fn convert_attribute(bcx: &EvmLlvmBuilder<'_>, attr: revmc_backend::Attribute) -> Attribute {
2135    use revmc_backend::Attribute as OurAttr;
2136
2137    enum AttrValue<'a> {
2138        String(&'a str),
2139        Enum(u64),
2140        Type(AnyTypeEnum<'a>),
2141    }
2142
2143    let cpu;
2144    let (key, value) = match attr {
2145        OurAttr::WillReturn => ("willreturn", AttrValue::Enum(0)),
2146        OurAttr::NoReturn => ("noreturn", AttrValue::Enum(0)),
2147        OurAttr::NoFree => ("nofree", AttrValue::Enum(0)),
2148        OurAttr::NoRecurse => ("norecurse", AttrValue::Enum(0)),
2149        OurAttr::NoSync => ("nosync", AttrValue::Enum(0)),
2150        OurAttr::NoUnwind => ("nounwind", AttrValue::Enum(0)),
2151        OurAttr::NonLazyBind => ("nonlazybind", AttrValue::Enum(0)),
2152        OurAttr::UWTable => ("uwtable", AttrValue::Enum(2)),
2153        OurAttr::AllFramePointers => ("frame-pointer", AttrValue::String("all")),
2154        OurAttr::NativeTargetCpu => (
2155            "target-cpu",
2156            AttrValue::String({
2157                cpu = bcx.machine.get_cpu();
2158                cpu.to_str().unwrap()
2159            }),
2160        ),
2161        OurAttr::Cold => ("cold", AttrValue::Enum(0)),
2162        OurAttr::Hot => ("hot", AttrValue::Enum(0)),
2163        OurAttr::HintInline => ("inlinehint", AttrValue::Enum(0)),
2164        OurAttr::AlwaysInline => ("alwaysinline", AttrValue::Enum(0)),
2165        OurAttr::NoInline => ("noinline", AttrValue::Enum(0)),
2166        OurAttr::Speculatable => ("speculatable", AttrValue::Enum(0)),
2167
2168        OurAttr::NoAlias => ("noalias", AttrValue::Enum(0)),
2169        OurAttr::NoCapture => ("captures", AttrValue::Enum(0)), // captures(none) - no capture
2170        OurAttr::NoUndef => ("noundef", AttrValue::Enum(0)),
2171        OurAttr::Align(n) => ("align", AttrValue::Enum(n)),
2172        OurAttr::NonNull => ("nonnull", AttrValue::Enum(0)),
2173        OurAttr::Dereferenceable(n) => ("dereferenceable", AttrValue::Enum(n)),
2174        OurAttr::SRet(n) => {
2175            ("sret", AttrValue::Type(bcx.type_array(bcx.ty_i8.into(), n as _).as_any_type_enum()))
2176        }
2177        OurAttr::ReadNone => ("readnone", AttrValue::Enum(0)),
2178        OurAttr::ReadOnly => ("readonly", AttrValue::Enum(0)),
2179        OurAttr::WriteOnly => ("writeonly", AttrValue::Enum(0)),
2180        OurAttr::Writable => ("writable", AttrValue::Enum(0)),
2181        // memory(argmem: readwrite) = ModRef(3) << ArgMem(0) = 3.
2182        OurAttr::ArgMemOnly => ("memory", AttrValue::Enum(3)),
2183        OurAttr::DeadOnReturn => ("dead_on_return", AttrValue::Enum(0)),
2184
2185        OurAttr::Initializes(size) => {
2186            return cpp::create_initializes_attr(bcx.cx, 0, size as i64);
2187        }
2188
2189        attr => unimplemented!("llvm attribute conversion: {attr:?}"),
2190    };
2191    match value {
2192        AttrValue::String(value) => bcx.cx.create_string_attribute(key, value),
2193        AttrValue::Enum(value) => {
2194            let id = Attribute::get_named_enum_kind_id(key);
2195            bcx.cx.create_enum_attribute(id, value)
2196        }
2197        AttrValue::Type(ty) => {
2198            let id = Attribute::get_named_enum_kind_id(key);
2199            bcx.cx.create_type_attribute(id, ty)
2200        }
2201    }
2202}
2203
2204fn convert_attribute_loc(loc: revmc_backend::FunctionAttributeLocation) -> AttributeLoc {
2205    match loc {
2206        revmc_backend::FunctionAttributeLocation::Return => AttributeLoc::Return,
2207        revmc_backend::FunctionAttributeLocation::Param(i) => AttributeLoc::Param(i),
2208        revmc_backend::FunctionAttributeLocation::Function => AttributeLoc::Function,
2209    }
2210}
2211
2212fn function_call_conv(function: FunctionValue<'_>) -> Option<u32> {
2213    let call_conv =
2214        unsafe { inkwell::llvm_sys::core::LLVMGetFunctionCallConv(function.as_value_ref()) };
2215    (call_conv != inkwell::llvm_sys::LLVMCallConv::LLVMCCallConv as u32).then_some(call_conv)
2216}
2217
2218fn set_function_call_conv(function: FunctionValue<'_>, call_conv: CallConv) {
2219    let Some(call_conv) = convert_call_conv(call_conv) else { return };
2220    unsafe {
2221        inkwell::llvm_sys::core::LLVMSetFunctionCallConv(function.as_value_ref(), call_conv as _);
2222    }
2223}
2224
2225fn convert_call_conv(call_conv: CallConv) -> Option<inkwell::llvm_sys::LLVMCallConv> {
2226    match call_conv {
2227        CallConv::Default => None,
2228        CallConv::Cold => Some(inkwell::llvm_sys::LLVMCallConv::LLVMPreserveMostCallConv),
2229    }
2230}
2231
2232fn convert_linkage(linkage: revmc_backend::Linkage) -> inkwell::module::Linkage {
2233    match linkage {
2234        revmc_backend::Linkage::Public => inkwell::module::Linkage::External,
2235        revmc_backend::Linkage::Import => inkwell::module::Linkage::External,
2236        revmc_backend::Linkage::Private => inkwell::module::Linkage::Private,
2237    }
2238}
2239
2240fn convert_tail_call_kind(kind: TailCallKind) -> inkwell::llvm_sys::LLVMTailCallKind {
2241    match kind {
2242        TailCallKind::None => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindNone,
2243        TailCallKind::Tail => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindTail,
2244        TailCallKind::MustTail => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindMustTail,
2245        TailCallKind::NoTail => inkwell::llvm_sys::LLVMTailCallKind::LLVMTailCallKindNoTail,
2246    }
2247}
2248
2249// No `#[track_caller]` because `map_err` doesn't propagate it.
2250fn error_msg(msg: inkwell::support::LLVMString) -> revmc_backend::Error {
2251    revmc_backend::Error::msg(msg.to_string_lossy().trim_end().to_string())
2252}
2253
2254fn fmt_ty(ty: BasicTypeEnum<'_>) -> impl std::fmt::Display {
2255    ty.print_to_string().to_str().unwrap().trim_matches('"').to_string()
2256}