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