irvm_lower/
llvm.rs

1//! ## LLVM IR lowering
2//!
3//! Lower irvm IR to LLVM IR.
4//!
5//! The main functions to use are [`lower_module_to_llvmir`] which gives you a [`CompileResult`].
6//!
7//! With this result you can either write it to a file (.ll), compile it to an object file
8//! or create a JIT Engine to execute methods (a bit limited currently, useful for testing).
9//!
10//! ```rust,ignore
11//!
12//! // Lower the module.
13//! let result = lower_module_to_llvmir(&mymodule, &my_type_storage)?;
14//!
15//! // Output to a file as a .ll
16//! output_to_file(&result, Path::new("out.ll"))?;
17//!
18//! // Compile to an object file.
19//! compile_object(&result, &mymodule.target_triple, CompileOptions::default(), Path::new("out.o"), false)?;
20//!
21//! // Or create a JIT engine.
22//! let engine = create_jit_engine(result, 3)?;
23//! let res = unsafe { engine.execute("main", &[JitValue::U32(4)], JitValue::U32(0))? };
24//! ```
25
26use std::{
27    collections::HashMap,
28    ffi::{CStr, CString, c_void},
29    mem::ManuallyDrop,
30    path::{Path, PathBuf},
31    ptr::null_mut,
32    rc::Rc,
33    sync::OnceLock,
34};
35
36use gimli::{DW_ATE_boolean, DW_ATE_float, DW_ATE_unsigned, DW_TAG_reference_type};
37use irvm::{
38    block::{
39        AtomicOrdering, AtomicRMWOp, BlockIdx, DebugOp, DebugVariable, FastMathFlags, Instruction,
40        SyncScope,
41    },
42    common::{Linkage, Location},
43    datalayout::DataLayout,
44    function::Function,
45    module::Module,
46    target_lexicon::Triple,
47    types::{Type, TypeIdx, TypeStorage},
48    value::{ConstValue, Operand},
49};
50
51use itertools::Itertools;
52use llvm_sys::{
53    LLVMIntPredicate, LLVMModule, LLVMOpaqueMetadata, LLVMRealPredicate,
54    core::{self, LLVMDisposeMessage, LLVMDumpModule},
55    debuginfo::{self, LLVMDIFlagPublic, LLVMDWARFEmissionKind},
56    error::LLVMGetErrorMessage,
57    execution_engine::{self, LLVMExecutionEngineRef, LLVMLinkInMCJIT},
58    prelude::{
59        LLVMBasicBlockRef, LLVMBuilderRef, LLVMContextRef, LLVMDIBuilderRef, LLVMMetadataRef,
60        LLVMTypeRef, LLVMValueRef,
61    },
62    target::{
63        LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos, LLVM_InitializeAllTargetMCs,
64        LLVM_InitializeAllTargets, LLVM_InitializeNativeAsmParser, LLVM_InitializeNativeAsmPrinter,
65        LLVM_InitializeNativeDisassembler, LLVM_InitializeNativeTarget,
66    },
67    target_machine::{
68        self, LLVMCodeGenFileType, LLVMCodeGenOptLevel, LLVMCodeModel, LLVMDisposeTargetMachine,
69        LLVMGetHostCPUFeatures, LLVMGetHostCPUName, LLVMRelocMode, LLVMTargetMachineEmitToFile,
70    },
71    transforms::pass_builder::{
72        LLVMCreatePassBuilderOptions, LLVMDisposePassBuilderOptions, LLVMRunPasses,
73    },
74};
75
76#[derive(Debug)]
77pub enum OutputCompilation {
78    File(PathBuf),
79    Engine(*mut LLVMExecutionEngineRef),
80}
81
82/// A possible Error.
83#[derive(Debug, thiserror::Error, Clone)]
84pub enum Error {
85    #[error("llvm error: {:?}", 0)]
86    LLVMError(String),
87    #[error("jit error: {:?}", 0)]
88    JitError(String),
89    #[error(transparent)]
90    NulError(#[from] std::ffi::NulError),
91    #[error("irvm error: {:?}", 0)]
92    IRVMError(#[from] irvm::error::Error),
93}
94
95/// The target LLVM cpu.
96#[derive(Debug, Clone, Default)]
97pub enum TargetCpu {
98    #[default]
99    Host,
100    Name(String),
101}
102
103/// The target LLVM cpu features.
104#[derive(Debug, Clone, Default)]
105pub enum TargetCpuFeatures {
106    #[default]
107    Host,
108    Features(String),
109}
110
111/// The optimization level to use.
112#[derive(Debug, Clone, Default)]
113pub enum OptLevel {
114    None,
115    Less,
116    #[default]
117    Default,
118    Aggressive,
119}
120
121/// The relocation model types supported by LLVM
122#[derive(Debug, Clone, Default)]
123pub enum RelocModel {
124    /// Generated code will assume the default for a particular target architecture.
125    #[default]
126    Default,
127    /// Generated code will exist at static offsets.
128    Static,
129    /// Generated code will be position-independent.
130    Pic,
131    /// Generated code will not be position-independent and may be used in static or dynamic executables but not necessarily a shared library.
132    DynamicNoPic,
133    /// Generated code will be compiled in read-only position independent mode.
134    /// In this mode, all read-only data and functions are at a link-time constant offset from the program counter.
135    /// ROPI is not supported by all target architectures and calling conventions. It is a particular feature of ARM targets, though.
136    /// ROPI may be useful to avoid committing to compile-time constant locations for code in memory.
137    Ropi,
138    /// Generated code will be compiled in read-write position independent mode.
139    ///
140    /// In this mode, all writable data is at a link-time constant offset from the static base register.
141    ///
142    /// RWPI is not supported by all target architectures and calling conventions. It is a particular feature of ARM targets, though.
143    ///
144    /// RWPI may be useful to avoid committing to compile-time constant locations for code in memory
145    Rwpi,
146    /// Combines the ropi and rwpi modes. Generated code will be compiled in both read-only and read-write position independent modes.
147    /// All read-only data appears at a link-time constant offset from the program counter,
148    /// and all writable data appears at a link-time constant offset from the static base register.
149    RopiRwpi,
150}
151
152/// The code model supported by LLVM.
153#[derive(Debug, Clone, Default)]
154pub enum CodeModel {
155    #[default]
156    Default,
157    JitDefault,
158    Tiny,
159    Small,
160    Kernel,
161    Medium,
162    Large,
163}
164
165/// Compile options to generate the object file.
166#[derive(Debug, Clone, Default)]
167pub struct CompileOptions {
168    pub target_cpu: TargetCpu,
169    pub target_cpu_features: TargetCpuFeatures,
170    pub relocation_model: RelocModel,
171    pub code_model: CodeModel,
172    pub opt_level: u8,
173}
174
175/// A compile result from lowering a given Module.
176#[derive(Debug)]
177pub struct CompileResult {
178    context: LLVMContextRef,
179    module: *mut LLVMModule,
180}
181
182/// A prepared JIT engine.
183#[derive(Debug)]
184pub struct JitEngine {
185    context: LLVMContextRef,
186    engine: LLVMExecutionEngineRef,
187}
188
189impl Drop for CompileResult {
190    fn drop(&mut self) {
191        unsafe {
192            core::LLVMDisposeModule(self.module);
193            core::LLVMContextDispose(self.context);
194        }
195    }
196}
197
198impl Drop for JitEngine {
199    fn drop(&mut self) {
200        unsafe {
201            execution_engine::LLVMDisposeExecutionEngine(self.engine);
202            core::LLVMContextDispose(self.context);
203        }
204    }
205}
206
207/// Possible value/types to pass to the JIT engine execute method.
208#[derive(Debug, Clone, Copy, PartialEq)]
209pub enum JitValue {
210    U8(u8),
211    U16(u16),
212    U32(u32),
213    U64(u64),
214    I8(i8),
215    I16(i16),
216    I32(i32),
217    I64(i64),
218    F32(f32),
219    F64(f64),
220    Ptr(*mut c_void),
221    Void,
222}
223
224impl JitEngine {
225    /// Execute the given function.
226    ///
227    /// # Safety
228    ///
229    /// All arguments and return type must match the signature.
230    pub unsafe fn execute(
231        &self,
232        symbol: &str,
233        args: &[JitValue],
234        return_ty: JitValue,
235    ) -> Result<JitValue, Error> {
236        unsafe {
237            let sym = CString::new(symbol)?;
238            let mut out_fn = null_mut();
239            let ok = execution_engine::LLVMFindFunction(self.engine, sym.as_ptr(), &raw mut out_fn);
240
241            if ok != 0 {
242                return Err(Error::LLVMError("Function not found".to_string()));
243            }
244
245            let mut value_args = Vec::new();
246
247            for arg in args {
248                let value = match arg {
249                    JitValue::U8(value) => execution_engine::LLVMCreateGenericValueOfInt(
250                        core::LLVMInt8Type(),
251                        (*value) as _,
252                        0,
253                    ),
254                    JitValue::U16(value) => execution_engine::LLVMCreateGenericValueOfInt(
255                        core::LLVMInt16Type(),
256                        (*value) as _,
257                        0,
258                    ),
259                    JitValue::U32(value) => execution_engine::LLVMCreateGenericValueOfInt(
260                        core::LLVMInt32Type(),
261                        (*value) as _,
262                        0,
263                    ),
264                    JitValue::U64(value) => execution_engine::LLVMCreateGenericValueOfInt(
265                        core::LLVMInt64Type(),
266                        (*value) as _,
267                        0,
268                    ),
269                    JitValue::I8(value) => execution_engine::LLVMCreateGenericValueOfInt(
270                        core::LLVMInt8Type(),
271                        (*value) as _,
272                        1,
273                    ),
274                    JitValue::I16(value) => execution_engine::LLVMCreateGenericValueOfInt(
275                        core::LLVMInt16Type(),
276                        (*value) as _,
277                        1,
278                    ),
279                    JitValue::I32(value) => execution_engine::LLVMCreateGenericValueOfInt(
280                        core::LLVMInt32Type(),
281                        (*value) as _,
282                        1,
283                    ),
284                    JitValue::I64(value) => execution_engine::LLVMCreateGenericValueOfInt(
285                        core::LLVMInt64Type(),
286                        (*value) as _,
287                        1,
288                    ),
289                    JitValue::F32(value) => execution_engine::LLVMCreateGenericValueOfFloat(
290                        core::LLVMFloatType(),
291                        (*value) as _,
292                    ),
293                    JitValue::F64(value) => execution_engine::LLVMCreateGenericValueOfFloat(
294                        core::LLVMDoubleType(),
295                        (*value) as _,
296                    ),
297                    JitValue::Ptr(value) => {
298                        execution_engine::LLVMCreateGenericValueOfPointer(*value)
299                    }
300                    JitValue::Void => {
301                        return Err(Error::JitError(
302                            "can't use void jit value as argument".to_string(),
303                        ));
304                    }
305                };
306                value_args.push(value);
307            }
308
309            let result = execution_engine::LLVMRunFunction(
310                self.engine,
311                out_fn,
312                value_args.len() as _,
313                value_args.as_mut_ptr(),
314            );
315
316            let res = match return_ty {
317                JitValue::U8(_) => {
318                    JitValue::U8(execution_engine::LLVMGenericValueToInt(result, 0) as u8)
319                }
320                JitValue::U16(_) => {
321                    JitValue::U16(execution_engine::LLVMGenericValueToInt(result, 0) as u16)
322                }
323                JitValue::U32(_) => {
324                    JitValue::U32(execution_engine::LLVMGenericValueToInt(result, 0) as u32)
325                }
326                JitValue::U64(_) => {
327                    JitValue::U64(execution_engine::LLVMGenericValueToInt(result, 0) as u64)
328                }
329                JitValue::I8(_) => {
330                    JitValue::I8(execution_engine::LLVMGenericValueToInt(result, 1) as i8)
331                }
332                JitValue::I16(_) => {
333                    JitValue::I16(execution_engine::LLVMGenericValueToInt(result, 1) as i16)
334                }
335                JitValue::I32(_) => {
336                    JitValue::I32(execution_engine::LLVMGenericValueToInt(result, 1) as i32)
337                }
338                JitValue::I64(_) => {
339                    JitValue::I64(execution_engine::LLVMGenericValueToInt(result, 1) as i64)
340                }
341                JitValue::F32(_) => JitValue::F32(execution_engine::LLVMGenericValueToFloat(
342                    core::LLVMFloatType(),
343                    result,
344                ) as f32),
345                JitValue::F64(_) => JitValue::F64(execution_engine::LLVMGenericValueToFloat(
346                    core::LLVMDoubleType(),
347                    result,
348                ) as f64),
349                JitValue::Ptr(_) => {
350                    JitValue::Ptr(execution_engine::LLVMGenericValueToPointer(result))
351                }
352                JitValue::Void => JitValue::Void,
353            };
354
355            for arg in &value_args {
356                execution_engine::LLVMDisposeGenericValue(*arg);
357            }
358            execution_engine::LLVMDisposeGenericValue(result);
359
360            Ok(res)
361        }
362    }
363}
364
365impl CompileResult {
366    pub fn dump(&self) {
367        unsafe {
368            LLVMDumpModule(self.module);
369        }
370    }
371}
372
373/// Lowers the given module to llvm ir.
374pub fn lower_module_to_llvmir(
375    module: &Module,
376    storage: &TypeStorage,
377) -> Result<CompileResult, Error> {
378    unsafe {
379        let ctx = core::LLVMContextCreate();
380        let module_name = CString::new(module.name.clone())?;
381        let llvm_module = core::LLVMModuleCreateWithNameInContext(module_name.as_ptr(), ctx);
382
383        let datalayout_str = CString::new(module.data_layout.to_llvm_string()).unwrap();
384        core::LLVMSetDataLayout(llvm_module, datalayout_str.as_ptr());
385        let triple_str = CString::new(module.target_triple.to_string()).unwrap();
386        core::LLVMSetTarget(llvm_module, triple_str.as_ptr());
387
388        let mut functions: HashMap<_, _> = Default::default();
389        let mut dfunctions: HashMap<_, _> = Default::default();
390        // let mut debug_functions: HashMap<_, _> = Default::default();
391        let builder = core::LLVMCreateBuilderInContext(ctx);
392        let dibuilder = debuginfo::LLVMCreateDIBuilder(llvm_module);
393
394        let compile_unit_file = get_difile_location(dibuilder, &module.location);
395
396        let producer = c"IRVM version 0.1.0";
397        let flags = c"";
398        let splitname = c"";
399        let sysroot = c"";
400        let sdk = c"";
401
402        let compile_unit = debuginfo::LLVMDIBuilderCreateCompileUnit(
403            dibuilder,
404            debuginfo::LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC17,
405            compile_unit_file,
406            producer.as_ptr(),
407            producer.count_bytes(),
408            0,
409            flags.as_ptr(),
410            flags.count_bytes(),
411            0,
412            splitname.as_ptr(),
413            splitname.count_bytes(),
414            LLVMDWARFEmissionKind::LLVMDWARFEmissionKindFull,
415            0,
416            0,
417            0,
418            sysroot.as_ptr(),
419            sysroot.count_bytes(),
420            sdk.as_ptr(),
421            sdk.count_bytes(),
422        );
423
424        let debug_module = debuginfo::LLVMDIBuilderCreateModule(
425            dibuilder,
426            compile_unit,
427            module_name.as_ptr(),
428            module.name.len(),
429            c"".as_ptr(),
430            0,
431            c"".as_ptr(),
432            0,
433            c"".as_ptr(),
434            0,
435        );
436
437        // Lower global variables
438        let mut globals: HashMap<usize, LLVMValueRef> = Default::default();
439        for (global_idx, global) in module.globals().iter() {
440            let name = CString::new(global.name.as_str()).unwrap();
441            let ty = lower_type(ctx, storage, global.ty);
442
443            let global_var = core::LLVMAddGlobal(llvm_module, ty, name.as_ptr());
444
445            // Set initializer if present
446            if let Some(init) = &global.initializer {
447                let init_val = lower_global_constant(ctx, storage, init, global.ty);
448                core::LLVMSetInitializer(global_var, init_val);
449            }
450
451            // Set constant flag
452            core::LLVMSetGlobalConstant(global_var, global.is_constant as i32);
453
454            // Set linkage if specified
455            if let Some(linkage) = &global.linkage {
456                core::LLVMSetLinkage(global_var, lower_linkage(linkage));
457            }
458
459            // Set alignment if specified
460            if let Some(align) = global.align {
461                core::LLVMSetAlignment(global_var, align);
462            }
463
464            globals.insert(global_idx.to_idx(), global_var);
465        }
466        let globals = Rc::new(globals);
467
468        for (fun_idx, func) in module.functions().iter() {
469            let name = CString::new(func.name.as_str()).unwrap();
470
471            let ret_ty = if let Some(ret_ty) = func.result_type {
472                lower_type(ctx, storage, ret_ty)
473            } else {
474                core::LLVMVoidTypeInContext(ctx)
475            };
476            let mut params = func
477                .parameters
478                .iter()
479                .map(|x| lower_type(ctx, storage, x.ty))
480                .collect_vec();
481            let fn_ty = core::LLVMFunctionType(ret_ty, params.as_mut_ptr(), params.len() as u32, 0);
482            let fn_ptr = core::LLVMAddFunction(llvm_module, name.as_ptr(), fn_ty);
483            apply_function_attrs(ctx, fn_ptr, &func.attrs);
484            apply_parameter_attrs(ctx, fn_ptr, &func.parameters);
485            apply_return_attrs(ctx, fn_ptr, &func.return_attrs);
486
487            // Set GC name if specified
488            if let Some(gc_name) = &func.gc_name {
489                let gc_cstring = CString::new(gc_name.as_str()).unwrap();
490                core::LLVMSetGC(fn_ptr, gc_cstring.as_ptr());
491            }
492
493            // Set prefix data if specified
494            if let Some((value, ty)) = &func.prefix_data {
495                let llvm_val = lower_global_constant(ctx, storage, value, *ty);
496                core::LLVMSetPrefixData(fn_ptr, llvm_val);
497            }
498
499            // Set prologue data if specified
500            if let Some((value, ty)) = &func.prologue_data {
501                let llvm_val = lower_global_constant(ctx, storage, value, *ty);
502                core::LLVMSetPrologueData(fn_ptr, llvm_val);
503            }
504
505            functions.insert(fun_idx.to_idx(), (fn_ptr, fn_ty));
506
507            let mut file = compile_unit_file;
508
509            let mut line = 0;
510            match &func.location {
511                Location::Unknown => {}
512                Location::File(file_location) => {
513                    file = get_difile(dibuilder, &file_location.file);
514                    line = file_location.line;
515                }
516            }
517
518            let mut debug_param_types = Vec::new();
519
520            for param in func.parameters.iter() {
521                let ptr = lower_debug_type(
522                    &module.data_layout,
523                    dibuilder,
524                    storage,
525                    debug_module,
526                    param.ty,
527                );
528                debug_param_types.push(ptr);
529            }
530
531            let debug_func_ty = debuginfo::LLVMDIBuilderCreateSubroutineType(
532                dibuilder,
533                file,
534                debug_param_types.as_mut_ptr(),
535                debug_param_types.len() as u32,
536                0,
537            );
538
539            let di_func = debuginfo::LLVMDIBuilderCreateFunction(
540                dibuilder,
541                debug_module,
542                name.as_ptr(),
543                name.count_bytes(),
544                name.as_ptr(),
545                name.count_bytes(),
546                file,
547                line,
548                debug_func_ty,
549                0,
550                1,
551                line,
552                0,
553                0,
554            );
555            dfunctions.insert(fun_idx.to_idx(), di_func);
556            debuginfo::LLVMSetSubprogram(fn_ptr, di_func);
557        }
558
559        let functions = Rc::new(functions);
560
561        for (fun_idx, func) in module.functions().iter() {
562            let fn_ptr = functions.get(&fun_idx.to_idx()).unwrap().0;
563            let dfunc = *dfunctions.get(&fun_idx.to_idx()).unwrap();
564
565            let mut fn_ctx = FnCtx {
566                ctx,
567                fn_ptr,
568                func: func.clone(),
569                builder,
570                dibuilder,
571                storage,
572                blocks: Default::default(),
573                values: Default::default(),
574                block_args: Default::default(),
575                functions: Rc::clone(&functions),
576                globals: Rc::clone(&globals),
577                debug_scope: dfunc,
578                datalayout: &module.data_layout,
579            };
580
581            for (id, _) in func.blocks.iter() {
582                add_block(&mut fn_ctx, id, None);
583            }
584
585            for (id, _) in func.blocks.iter() {
586                lower_block(&mut fn_ctx, id)?;
587            }
588
589            debuginfo::LLVMDIBuilderFinalizeSubprogram(dibuilder, dfunc);
590        }
591
592        debuginfo::LLVMDIBuilderFinalize(dibuilder);
593        debuginfo::LLVMDisposeDIBuilder(dibuilder);
594        core::LLVMDisposeBuilder(builder);
595
596        if std::env::var("IRVM_DUMP_IR").is_ok() {
597            core::LLVMDumpModule(llvm_module);
598        }
599
600        let mut out_msg: *mut i8 = null_mut();
601        let ok = llvm_sys::analysis::LLVMVerifyModule(
602            llvm_module,
603            llvm_sys::analysis::LLVMVerifierFailureAction::LLVMReturnStatusAction,
604            &raw mut out_msg,
605        );
606
607        if ok != 0 {
608            let msg = {
609                let msg = CStr::from_ptr(out_msg);
610                msg.to_string_lossy().to_string()
611            };
612
613            if !out_msg.is_null() {
614                core::LLVMDisposeMessage(out_msg);
615            }
616
617            core::LLVMDisposeModule(llvm_module);
618            core::LLVMContextDispose(ctx);
619
620            return Err(Error::LLVMError(msg));
621        }
622
623        if !out_msg.is_null() {
624            core::LLVMDisposeMessage(out_msg);
625        }
626
627        Ok(CompileResult {
628            context: ctx,
629            module: llvm_module,
630        })
631    }
632}
633
634/// Compiles the given llvm compile result to an object or assembly file.
635///
636/// If output assembly is false it will output an object file.
637pub fn compile_object(
638    compile_result: &CompileResult,
639    target_triple: Triple,
640    options: CompileOptions,
641    output_file: &Path,
642    output_assembly: bool,
643) -> Result<(), Error> {
644    unsafe {
645        static INITIALIZED: OnceLock<()> = OnceLock::new();
646        INITIALIZED.get_or_init(|| {
647            LLVM_InitializeAllTargets();
648            LLVM_InitializeAllTargetInfos();
649            LLVM_InitializeAllTargetMCs();
650            LLVM_InitializeAllAsmPrinters();
651        });
652
653        let target_triple = CString::new(target_triple.to_string())?;
654
655        let target_cpu = match &options.target_cpu {
656            TargetCpu::Host => {
657                let cpu = LLVMGetHostCPUName();
658                CString::from(CStr::from_ptr(cpu))
659            }
660            TargetCpu::Name(name) => CString::new(name.as_bytes())?,
661        };
662
663        let target_cpu_features = match &options.target_cpu_features {
664            TargetCpuFeatures::Host => {
665                let cpu = LLVMGetHostCPUFeatures();
666                CString::from(CStr::from_ptr(cpu))
667            }
668            TargetCpuFeatures::Features(name) => CString::new(name.as_bytes())?,
669        };
670
671        let mut out_msg = null_mut();
672
673        let mut target = null_mut();
674
675        let ok = target_machine::LLVMGetTargetFromTriple(
676            target_triple.as_ptr(),
677            &raw mut target,
678            &raw mut out_msg,
679        );
680
681        if ok != 0 {
682            let msg = {
683                let msg = CStr::from_ptr(out_msg);
684                msg.to_string_lossy().to_string()
685            };
686
687            if !out_msg.is_null() {
688                core::LLVMDisposeMessage(out_msg);
689            }
690
691            return Err(Error::LLVMError(msg));
692        }
693
694        if !out_msg.is_null() {
695            core::LLVMDisposeMessage(out_msg);
696        }
697
698        let machine = target_machine::LLVMCreateTargetMachine(
699            target,
700            target_triple.as_ptr(),
701            target_cpu.as_ptr(),
702            target_cpu_features.as_ptr(),
703            match options.opt_level {
704                0 => LLVMCodeGenOptLevel::LLVMCodeGenLevelNone,
705                1 => LLVMCodeGenOptLevel::LLVMCodeGenLevelLess,
706                2 => LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault,
707                _ => LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive,
708            },
709            match options.relocation_model {
710                RelocModel::Default => LLVMRelocMode::LLVMRelocDefault,
711                RelocModel::Static => LLVMRelocMode::LLVMRelocStatic,
712                RelocModel::Pic => LLVMRelocMode::LLVMRelocPIC,
713                RelocModel::DynamicNoPic => LLVMRelocMode::LLVMRelocDynamicNoPic,
714                RelocModel::Ropi => LLVMRelocMode::LLVMRelocROPI,
715                RelocModel::Rwpi => LLVMRelocMode::LLVMRelocRWPI,
716                RelocModel::RopiRwpi => LLVMRelocMode::LLVMRelocROPI_RWPI,
717            },
718            match options.code_model {
719                CodeModel::Default => LLVMCodeModel::LLVMCodeModelDefault,
720                CodeModel::JitDefault => LLVMCodeModel::LLVMCodeModelJITDefault,
721                CodeModel::Tiny => LLVMCodeModel::LLVMCodeModelTiny,
722                CodeModel::Small => LLVMCodeModel::LLVMCodeModelSmall,
723                CodeModel::Kernel => LLVMCodeModel::LLVMCodeModelKernel,
724                CodeModel::Medium => LLVMCodeModel::LLVMCodeModelMedium,
725                CodeModel::Large => LLVMCodeModel::LLVMCodeModelLarge,
726            },
727        );
728
729        let opts = LLVMCreatePassBuilderOptions();
730
731        let passes = CString::new(format!("default<O{}>", options.opt_level)).unwrap();
732
733        let error = LLVMRunPasses(compile_result.module, passes.as_ptr(), machine, opts);
734
735        if !error.is_null() {
736            let msg_ptr = LLVMGetErrorMessage(error);
737            let msg = {
738                let msg = CStr::from_ptr(msg_ptr);
739                msg.to_string_lossy().to_string()
740            };
741            LLVMDisposeMessage(msg_ptr);
742            LLVMDisposeTargetMachine(machine);
743            return Err(Error::LLVMError(msg));
744        }
745
746        LLVMDisposePassBuilderOptions(opts);
747
748        let mut out_msg: *mut i8 = null_mut();
749        let ok = llvm_sys::analysis::LLVMVerifyModule(
750            compile_result.module,
751            llvm_sys::analysis::LLVMVerifierFailureAction::LLVMReturnStatusAction,
752            &raw mut out_msg,
753        );
754
755        if ok != 0 {
756            let msg = {
757                let msg = CStr::from_ptr(out_msg);
758                msg.to_string_lossy().to_string()
759            };
760
761            if !out_msg.is_null() {
762                core::LLVMDisposeMessage(out_msg);
763            }
764
765            LLVMDisposeTargetMachine(machine);
766
767            return Err(Error::LLVMError(msg));
768        }
769
770        let filename = CString::new(output_file.as_os_str().to_string_lossy().as_bytes()).unwrap();
771
772        let ok = LLVMTargetMachineEmitToFile(
773            machine,
774            compile_result.module,
775            filename.as_ptr().cast_mut(),
776            if output_assembly {
777                LLVMCodeGenFileType::LLVMAssemblyFile
778            } else {
779                LLVMCodeGenFileType::LLVMObjectFile
780            },
781            &raw mut out_msg,
782        );
783
784        LLVMDisposeTargetMachine(machine);
785
786        if ok != 0 {
787            let msg = {
788                let msg = CStr::from_ptr(out_msg);
789                msg.to_string_lossy().to_string()
790            };
791
792            if !out_msg.is_null() {
793                core::LLVMDisposeMessage(out_msg);
794            }
795
796            return Err(Error::LLVMError(msg));
797        }
798
799        Ok(())
800    }
801}
802
803/// Outputs the given compile result to a llvm ir file.
804pub fn output_to_file(compile_result: &CompileResult, output_ll: &Path) -> Result<(), Error> {
805    unsafe {
806        let file = CString::new(&*output_ll.to_string_lossy())?;
807
808        let mut out_msg: *mut i8 = null_mut();
809
810        let ok =
811            core::LLVMPrintModuleToFile(compile_result.module, file.as_ptr(), &raw mut out_msg);
812
813        if ok != 0 {
814            let msg = {
815                let msg = CStr::from_ptr(out_msg);
816                msg.to_string_lossy().to_string()
817            };
818
819            if !out_msg.is_null() {
820                core::LLVMDisposeMessage(out_msg);
821            }
822
823            return Err(Error::LLVMError(msg));
824        }
825
826        if !out_msg.is_null() {
827            core::LLVMDisposeMessage(out_msg);
828        }
829
830        Ok(())
831    }
832}
833
834/// Creates a jit engine for the given compile result.
835pub fn create_jit_engine(result: CompileResult, optlevel: u32) -> Result<JitEngine, Error> {
836    unsafe {
837        let mut engine = null_mut();
838
839        let mut out_msg: *mut i8 = null_mut();
840
841        let result = ManuallyDrop::new(result);
842
843        static INITIALIZED: OnceLock<()> = OnceLock::new();
844        INITIALIZED.get_or_init(|| {
845            LLVM_InitializeNativeTarget();
846            LLVM_InitializeNativeAsmParser();
847            LLVM_InitializeNativeAsmPrinter();
848            LLVM_InitializeNativeDisassembler();
849            LLVMLinkInMCJIT();
850        });
851
852        let ok = execution_engine::LLVMCreateJITCompilerForModule(
853            &raw mut engine,
854            result.module,
855            optlevel,
856            &raw mut out_msg,
857        );
858
859        if ok != 0 {
860            let msg = {
861                let msg = CStr::from_ptr(out_msg);
862                msg.to_string_lossy().to_string()
863            };
864
865            if !out_msg.is_null() {
866                core::LLVMDisposeMessage(out_msg);
867            }
868
869            return Err(Error::LLVMError(msg));
870        }
871
872        let engine = JitEngine {
873            context: result.context,
874            engine,
875        };
876
877        Ok(engine)
878    }
879}
880
881#[derive(Debug)]
882struct FnCtx<'m> {
883    ctx: LLVMContextRef,
884    fn_ptr: LLVMValueRef,
885    func: Function,
886    storage: &'m TypeStorage,
887    builder: LLVMBuilderRef,
888    dibuilder: LLVMDIBuilderRef,
889    debug_scope: LLVMMetadataRef,
890    functions: Rc<HashMap<usize, (LLVMValueRef, LLVMTypeRef)>>,
891    globals: Rc<HashMap<usize, LLVMValueRef>>,
892    blocks: HashMap<usize, LLVMBasicBlockRef>,
893    // block, inst
894    values: HashMap<(usize, usize), LLVMValueRef>,
895    block_args: HashMap<usize, Vec<LLVMValueRef>>,
896    datalayout: &'m DataLayout,
897}
898
899// Returns the next block to lower.
900fn lower_block(ctx: &mut FnCtx, block_idx: BlockIdx) -> Result<(), Error> {
901    unsafe {
902        let null_name = c"";
903        let block_ptr = *ctx.blocks.get(&block_idx.to_idx()).unwrap();
904        core::LLVMPositionBuilderAtEnd(ctx.builder, block_ptr);
905        add_preds(ctx, block_idx);
906
907        for (inst_idx, (loc, inst)) in ctx.func.blocks[block_idx].instructions().iter() {
908            let loc = set_loc(ctx.ctx, ctx.builder, loc, ctx.debug_scope);
909
910            match inst {
911                Instruction::BinaryOp(binary_op) => match binary_op {
912                    irvm::block::BinaryOp::Add { lhs, rhs, nsw, nuw } => {
913                        let lhs_ptr = lower_operand(ctx, lhs);
914                        let rhs_ptr = lower_operand(ctx, rhs);
915                        let value =
916                            core::LLVMBuildAdd(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
917                        if *nuw {
918                            core::LLVMSetNUW(value, (*nuw) as i32);
919                        }
920                        if *nsw {
921                            core::LLVMSetNSW(value, (*nsw) as i32);
922                        }
923                        ctx.values
924                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
925                    }
926                    irvm::block::BinaryOp::Sub { lhs, rhs, nsw, nuw } => {
927                        let lhs_ptr = lower_operand(ctx, lhs);
928                        let rhs_ptr = lower_operand(ctx, rhs);
929                        let value =
930                            core::LLVMBuildSub(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
931                        if *nuw {
932                            core::LLVMSetNUW(value, (*nuw) as i32);
933                        }
934                        if *nsw {
935                            core::LLVMSetNSW(value, (*nsw) as i32);
936                        }
937                        ctx.values
938                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
939                    }
940                    irvm::block::BinaryOp::Mul { lhs, rhs, nsw, nuw } => {
941                        let lhs_ptr = lower_operand(ctx, lhs);
942                        let rhs_ptr = lower_operand(ctx, rhs);
943                        let value =
944                            core::LLVMBuildMul(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
945                        if *nuw {
946                            core::LLVMSetNUW(value, (*nuw) as i32);
947                        }
948                        if *nsw {
949                            core::LLVMSetNSW(value, (*nsw) as i32);
950                        }
951                        ctx.values
952                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
953                    }
954                    irvm::block::BinaryOp::Div {
955                        lhs,
956                        rhs,
957                        signed,
958                        exact,
959                    } => {
960                        let lhs_ptr = lower_operand(ctx, lhs);
961                        let rhs_ptr = lower_operand(ctx, rhs);
962                        let value = if *signed {
963                            if *exact {
964                                core::LLVMBuildExactSDiv(
965                                    ctx.builder,
966                                    lhs_ptr,
967                                    rhs_ptr,
968                                    null_name.as_ptr(),
969                                )
970                            } else {
971                                core::LLVMBuildSDiv(
972                                    ctx.builder,
973                                    lhs_ptr,
974                                    rhs_ptr,
975                                    null_name.as_ptr(),
976                                )
977                            }
978                        } else if *exact {
979                            core::LLVMBuildExactUDiv(
980                                ctx.builder,
981                                lhs_ptr,
982                                rhs_ptr,
983                                null_name.as_ptr(),
984                            )
985                        } else {
986                            core::LLVMBuildUDiv(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr())
987                        };
988                        ctx.values
989                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
990                    }
991                    irvm::block::BinaryOp::Rem { lhs, rhs, signed } => {
992                        let lhs_ptr = lower_operand(ctx, lhs);
993                        let rhs_ptr = lower_operand(ctx, rhs);
994                        let value = if *signed {
995                            core::LLVMBuildSRem(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr())
996                        } else {
997                            core::LLVMBuildURem(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr())
998                        };
999                        ctx.values
1000                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1001                    }
1002                    irvm::block::BinaryOp::FAdd { lhs, rhs, flags } => {
1003                        let lhs_ptr = lower_operand(ctx, lhs);
1004                        let rhs_ptr = lower_operand(ctx, rhs);
1005                        let value =
1006                            core::LLVMBuildFAdd(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1007                        apply_fast_math_flags(value, flags);
1008                        ctx.values
1009                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1010                    }
1011                    irvm::block::BinaryOp::FSub { lhs, rhs, flags } => {
1012                        let lhs_ptr = lower_operand(ctx, lhs);
1013                        let rhs_ptr = lower_operand(ctx, rhs);
1014                        let value =
1015                            core::LLVMBuildFSub(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1016                        apply_fast_math_flags(value, flags);
1017                        ctx.values
1018                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1019                    }
1020                    irvm::block::BinaryOp::FMul { lhs, rhs, flags } => {
1021                        let lhs_ptr = lower_operand(ctx, lhs);
1022                        let rhs_ptr = lower_operand(ctx, rhs);
1023                        let value =
1024                            core::LLVMBuildFMul(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1025                        apply_fast_math_flags(value, flags);
1026                        ctx.values
1027                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1028                    }
1029                    irvm::block::BinaryOp::FDiv { lhs, rhs, flags } => {
1030                        let lhs_ptr = lower_operand(ctx, lhs);
1031                        let rhs_ptr = lower_operand(ctx, rhs);
1032                        let value =
1033                            core::LLVMBuildFDiv(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1034                        apply_fast_math_flags(value, flags);
1035                        ctx.values
1036                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1037                    }
1038                    irvm::block::BinaryOp::FRem { lhs, rhs, flags } => {
1039                        let lhs_ptr = lower_operand(ctx, lhs);
1040                        let rhs_ptr = lower_operand(ctx, rhs);
1041                        let value =
1042                            core::LLVMBuildFRem(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1043                        apply_fast_math_flags(value, flags);
1044                        ctx.values
1045                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1046                    }
1047                    irvm::block::BinaryOp::FNeg { value, flags } => {
1048                        let val_ptr = lower_operand(ctx, value);
1049                        let result = core::LLVMBuildFNeg(ctx.builder, val_ptr, null_name.as_ptr());
1050                        apply_fast_math_flags(result, flags);
1051                        ctx.values
1052                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1053                    }
1054                },
1055                Instruction::BitwiseBinaryOp(bitwise_binary_op) => match bitwise_binary_op {
1056                    irvm::block::BitwiseBinaryOp::Shl { lhs, rhs } => {
1057                        let lhs_ptr = lower_operand(ctx, lhs);
1058                        let rhs_ptr = lower_operand(ctx, rhs);
1059                        let value =
1060                            core::LLVMBuildShl(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1061                        ctx.values
1062                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1063                    }
1064                    irvm::block::BitwiseBinaryOp::Lshr { lhs, rhs, exact } => {
1065                        let lhs_ptr = lower_operand(ctx, lhs);
1066                        let rhs_ptr = lower_operand(ctx, rhs);
1067                        let value =
1068                            core::LLVMBuildLShr(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1069                        core::LLVMSetExact(value, (*exact) as i32);
1070                        ctx.values
1071                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1072                    }
1073                    irvm::block::BitwiseBinaryOp::Ashr { lhs, rhs, exact } => {
1074                        let lhs_ptr = lower_operand(ctx, lhs);
1075                        let rhs_ptr = lower_operand(ctx, rhs);
1076                        let value =
1077                            core::LLVMBuildAShr(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1078                        core::LLVMSetExact(value, (*exact) as i32);
1079                        ctx.values
1080                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1081                    }
1082                    irvm::block::BitwiseBinaryOp::And { lhs, rhs } => {
1083                        let lhs_ptr = lower_operand(ctx, lhs);
1084                        let rhs_ptr = lower_operand(ctx, rhs);
1085                        let value =
1086                            core::LLVMBuildAnd(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1087                        ctx.values
1088                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1089                    }
1090                    irvm::block::BitwiseBinaryOp::Or { lhs, rhs, disjoint } => {
1091                        let lhs_ptr = lower_operand(ctx, lhs);
1092                        let rhs_ptr = lower_operand(ctx, rhs);
1093                        let value =
1094                            core::LLVMBuildOr(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1095                        core::LLVMSetIsDisjoint(value, (*disjoint) as i32);
1096                        ctx.values
1097                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1098                    }
1099                    irvm::block::BitwiseBinaryOp::Xor { lhs, rhs } => {
1100                        let lhs_ptr = lower_operand(ctx, lhs);
1101                        let rhs_ptr = lower_operand(ctx, rhs);
1102                        let value =
1103                            core::LLVMBuildXor(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1104                        ctx.values
1105                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1106                    }
1107                },
1108                Instruction::VectorOp(vector_op) => match vector_op {
1109                    irvm::block::VectorOp::ExtractElement { vector, idx } => {
1110                        let vector = lower_operand(ctx, vector);
1111                        let idx = lower_operand(ctx, idx);
1112                        let value = core::LLVMBuildExtractElement(
1113                            ctx.builder,
1114                            vector,
1115                            idx,
1116                            null_name.as_ptr(),
1117                        );
1118                        ctx.values
1119                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1120                    }
1121                    irvm::block::VectorOp::InsertElement {
1122                        vector,
1123                        element,
1124                        idx,
1125                    } => {
1126                        let vec_val = lower_operand(ctx, vector);
1127                        let elem_val = lower_operand(ctx, element);
1128                        let idx_val = lower_operand(ctx, idx);
1129                        let value = core::LLVMBuildInsertElement(
1130                            ctx.builder,
1131                            vec_val,
1132                            elem_val,
1133                            idx_val,
1134                            null_name.as_ptr(),
1135                        );
1136                        ctx.values
1137                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1138                    }
1139                    irvm::block::VectorOp::ShuffleVector { vec1, vec2, mask } => {
1140                        let v1 = lower_operand(ctx, vec1);
1141                        let v2 = lower_operand(ctx, vec2);
1142
1143                        // Create mask as constant vector
1144                        let i32_ty = core::LLVMInt32TypeInContext(ctx.ctx);
1145                        let mut mask_vals: Vec<LLVMValueRef> = mask
1146                            .iter()
1147                            .map(|&m| {
1148                                if m < 0 {
1149                                    core::LLVMGetUndef(i32_ty)
1150                                } else {
1151                                    core::LLVMConstInt(i32_ty, m as u64, 0)
1152                                }
1153                            })
1154                            .collect();
1155                        let mask_val =
1156                            core::LLVMConstVector(mask_vals.as_mut_ptr(), mask_vals.len() as u32);
1157
1158                        let value = core::LLVMBuildShuffleVector(
1159                            ctx.builder,
1160                            v1,
1161                            v2,
1162                            mask_val,
1163                            null_name.as_ptr(),
1164                        );
1165                        ctx.values
1166                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1167                    }
1168                },
1169                Instruction::MemoryOp(memory_op) => match memory_op {
1170                    irvm::block::MemoryOp::Alloca {
1171                        ty, num_elements, ..
1172                    } => {
1173                        let ty_ptr = lower_type(ctx.ctx, ctx.storage, *ty);
1174                        let value = if *num_elements > 1 {
1175                            let const_val = core::LLVMConstInt(
1176                                core::LLVMInt64TypeInContext(ctx.ctx),
1177                                (*num_elements) as u64,
1178                                0,
1179                            );
1180                            core::LLVMBuildArrayAlloca(
1181                                ctx.builder,
1182                                ty_ptr,
1183                                const_val,
1184                                null_name.as_ptr(),
1185                            )
1186                        } else {
1187                            core::LLVMBuildAlloca(ctx.builder, ty_ptr, null_name.as_ptr())
1188                        };
1189                        ctx.values
1190                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1191                    }
1192                    irvm::block::MemoryOp::Load {
1193                        ptr,
1194                        align,
1195                        volatile,
1196                    } => {
1197                        let ptr_val = lower_operand(ctx, ptr);
1198                        let ty_ptr =
1199                            lower_type(ctx.ctx, ctx.storage, ptr.get_inner_type(ctx.storage)?);
1200
1201                        let value =
1202                            core::LLVMBuildLoad2(ctx.builder, ty_ptr, ptr_val, null_name.as_ptr());
1203                        if let Some(align) = align {
1204                            core::LLVMSetAlignment(value, *align / 8);
1205                        }
1206                        if *volatile {
1207                            core::LLVMSetVolatile(value, 1);
1208                        }
1209
1210                        ctx.values
1211                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1212                    }
1213                    irvm::block::MemoryOp::Store {
1214                        value,
1215                        ptr,
1216                        align,
1217                        volatile,
1218                    } => {
1219                        let ptr_val = lower_operand(ctx, ptr);
1220                        let value_val = lower_operand(ctx, value);
1221
1222                        let value = core::LLVMBuildStore(ctx.builder, value_val, ptr_val);
1223                        if let Some(align) = align {
1224                            core::LLVMSetAlignment(value, *align / 8);
1225                        }
1226                        if *volatile {
1227                            core::LLVMSetVolatile(value, 1);
1228                        }
1229
1230                        ctx.values
1231                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1232                    }
1233                    irvm::block::MemoryOp::GetElementPtr { ptr, indices } => {
1234                        let ptr_val = lower_operand(ctx, ptr);
1235                        let pointee_ty =
1236                            lower_type(ctx.ctx, ctx.storage, ptr.get_inner_type(ctx.storage)?);
1237
1238                        let mut x = Vec::new();
1239
1240                        for index in indices {
1241                            let value = match index {
1242                                irvm::block::GepIndex::Const(value) => core::LLVMConstInt(
1243                                    core::LLVMInt64TypeInContext(ctx.ctx),
1244                                    (*value) as u64,
1245                                    0,
1246                                ),
1247                                irvm::block::GepIndex::Value(operand) => {
1248                                    lower_operand(ctx, operand)
1249                                }
1250                            };
1251
1252                            x.push(value);
1253                        }
1254
1255                        let value = core::LLVMBuildGEP2(
1256                            ctx.builder,
1257                            pointee_ty,
1258                            ptr_val,
1259                            x.as_mut_ptr(),
1260                            x.len() as u32,
1261                            null_name.as_ptr(),
1262                        );
1263
1264                        ctx.values
1265                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1266                    }
1267                    irvm::block::MemoryOp::AtomicLoad {
1268                        ptr,
1269                        ordering,
1270                        align,
1271                        sync_scope,
1272                    } => {
1273                        let ptr_val = lower_operand(ctx, ptr);
1274                        let ty_ptr =
1275                            lower_type(ctx.ctx, ctx.storage, ptr.get_inner_type(ctx.storage)?);
1276
1277                        let value =
1278                            core::LLVMBuildLoad2(ctx.builder, ty_ptr, ptr_val, null_name.as_ptr());
1279                        core::LLVMSetOrdering(value, lower_atomic_ordering(ordering));
1280                        core::LLVMSetVolatile(value, 0);
1281                        if let Some(a) = align {
1282                            core::LLVMSetAlignment(value, *a / 8);
1283                        }
1284                        let _ = sync_scope; // TODO: handle sync scope when LLVM-sys supports it
1285                        ctx.values
1286                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1287                    }
1288                    irvm::block::MemoryOp::AtomicStore {
1289                        value,
1290                        ptr,
1291                        ordering,
1292                        align,
1293                        sync_scope,
1294                    } => {
1295                        let ptr_val = lower_operand(ctx, ptr);
1296                        let val = lower_operand(ctx, value);
1297                        let store = core::LLVMBuildStore(ctx.builder, val, ptr_val);
1298                        core::LLVMSetOrdering(store, lower_atomic_ordering(ordering));
1299                        if let Some(a) = align {
1300                            core::LLVMSetAlignment(store, *a / 8);
1301                        }
1302                        let _ = sync_scope; // TODO: handle sync scope when LLVM-sys supports it
1303                        ctx.values
1304                            .insert((block_idx.to_idx(), inst_idx.to_idx()), store);
1305                    }
1306                    irvm::block::MemoryOp::AtomicRMW {
1307                        op,
1308                        ptr,
1309                        value,
1310                        ordering,
1311                        sync_scope,
1312                    } => {
1313                        let ptr_val = lower_operand(ctx, ptr);
1314                        let val = lower_operand(ctx, value);
1315                        let result = core::LLVMBuildAtomicRMW(
1316                            ctx.builder,
1317                            lower_atomic_rmw_op(op),
1318                            ptr_val,
1319                            val,
1320                            lower_atomic_ordering(ordering),
1321                            matches!(sync_scope, SyncScope::SingleThread) as i32,
1322                        );
1323                        ctx.values
1324                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1325                    }
1326                    irvm::block::MemoryOp::CmpXchg {
1327                        ptr,
1328                        cmp,
1329                        new_val,
1330                        success_ordering,
1331                        failure_ordering,
1332                        weak,
1333                        sync_scope,
1334                    } => {
1335                        let ptr_val = lower_operand(ctx, ptr);
1336                        let cmp_val = lower_operand(ctx, cmp);
1337                        let new_val_ptr = lower_operand(ctx, new_val);
1338                        let result = core::LLVMBuildAtomicCmpXchg(
1339                            ctx.builder,
1340                            ptr_val,
1341                            cmp_val,
1342                            new_val_ptr,
1343                            lower_atomic_ordering(success_ordering),
1344                            lower_atomic_ordering(failure_ordering),
1345                            matches!(sync_scope, SyncScope::SingleThread) as i32,
1346                        );
1347                        core::LLVMSetWeak(result, *weak as i32);
1348                        ctx.values
1349                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1350                    }
1351                    irvm::block::MemoryOp::Fence {
1352                        ordering,
1353                        sync_scope,
1354                    } => {
1355                        core::LLVMBuildFence(
1356                            ctx.builder,
1357                            lower_atomic_ordering(ordering),
1358                            matches!(sync_scope, SyncScope::SingleThread) as i32,
1359                            null_name.as_ptr(),
1360                        );
1361                    }
1362                },
1363                Instruction::OtherOp(other_op) => match other_op {
1364                    irvm::block::OtherOp::Call(call_op) => {
1365                        let (target_fn_ptr, fn_ty) = match &call_op.fn_target {
1366                            irvm::block::CallableValue::Symbol(id) => {
1367                                *ctx.functions.get(&id.to_idx()).expect("function not found")
1368                            }
1369                            irvm::block::CallableValue::Pointer(operand, fn_ty) => {
1370                                let ptr = lower_operand(ctx, operand);
1371
1372                                let ret_ty = lower_type(ctx.ctx, ctx.storage, fn_ty.return_type);
1373                                let mut params = fn_ty
1374                                    .parameters
1375                                    .iter()
1376                                    .map(|x| lower_type(ctx.ctx, ctx.storage, *x))
1377                                    .collect_vec();
1378                                let fn_ty = core::LLVMFunctionType(
1379                                    ret_ty,
1380                                    params.as_mut_ptr(),
1381                                    params.len() as u32,
1382                                    0,
1383                                );
1384                                (ptr, fn_ty)
1385                            }
1386                        };
1387
1388                        let mut args = call_op
1389                            .params
1390                            .iter()
1391                            .map(|p| lower_operand(ctx, p))
1392                            .collect_vec();
1393
1394                        let value = core::LLVMBuildCall2(
1395                            ctx.builder,
1396                            fn_ty,
1397                            target_fn_ptr,
1398                            args.as_mut_ptr(),
1399                            args.len() as u32,
1400                            null_name.as_ptr(),
1401                        );
1402
1403                        ctx.values
1404                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1405                    }
1406                    irvm::block::OtherOp::Icmp { cond, lhs, rhs } => {
1407                        let lhs_val = lower_operand(ctx, lhs);
1408                        let rhs_val = lower_operand(ctx, rhs);
1409                        let value = core::LLVMBuildICmp(
1410                            ctx.builder,
1411                            match cond {
1412                                irvm::block::IcmpCond::Eq => LLVMIntPredicate::LLVMIntEQ,
1413                                irvm::block::IcmpCond::Ne => LLVMIntPredicate::LLVMIntNE,
1414                                irvm::block::IcmpCond::Ugt => LLVMIntPredicate::LLVMIntUGT,
1415                                irvm::block::IcmpCond::Uge => LLVMIntPredicate::LLVMIntUGE,
1416                                irvm::block::IcmpCond::Ult => LLVMIntPredicate::LLVMIntULT,
1417                                irvm::block::IcmpCond::Ule => LLVMIntPredicate::LLVMIntULE,
1418                                irvm::block::IcmpCond::Sgt => LLVMIntPredicate::LLVMIntSGT,
1419                                irvm::block::IcmpCond::Sge => LLVMIntPredicate::LLVMIntSGE,
1420                                irvm::block::IcmpCond::Slt => LLVMIntPredicate::LLVMIntSLT,
1421                                irvm::block::IcmpCond::Sle => LLVMIntPredicate::LLVMIntSLE,
1422                            },
1423                            lhs_val,
1424                            rhs_val,
1425                            null_name.as_ptr(),
1426                        );
1427                        ctx.values
1428                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1429                    }
1430                    irvm::block::OtherOp::Fcmp { cond, lhs, rhs } => {
1431                        let lhs_val = lower_operand(ctx, lhs);
1432                        let rhs_val = lower_operand(ctx, rhs);
1433                        let value = core::LLVMBuildFCmp(
1434                            ctx.builder,
1435                            match cond {
1436                                irvm::block::FcmpCond::False => {
1437                                    LLVMRealPredicate::LLVMRealPredicateFalse
1438                                }
1439                                irvm::block::FcmpCond::Oeq => LLVMRealPredicate::LLVMRealOEQ,
1440                                irvm::block::FcmpCond::Ogt => LLVMRealPredicate::LLVMRealOGT,
1441                                irvm::block::FcmpCond::Oge => LLVMRealPredicate::LLVMRealOGE,
1442                                irvm::block::FcmpCond::Olt => LLVMRealPredicate::LLVMRealOLT,
1443                                irvm::block::FcmpCond::Ole => LLVMRealPredicate::LLVMRealOLE,
1444                                irvm::block::FcmpCond::One => LLVMRealPredicate::LLVMRealONE,
1445                                irvm::block::FcmpCond::Ord => LLVMRealPredicate::LLVMRealORD,
1446                                irvm::block::FcmpCond::Ueq => LLVMRealPredicate::LLVMRealUEQ,
1447                                irvm::block::FcmpCond::Ugt => LLVMRealPredicate::LLVMRealUGT,
1448                                irvm::block::FcmpCond::Ult => LLVMRealPredicate::LLVMRealULT,
1449                                irvm::block::FcmpCond::Ule => LLVMRealPredicate::LLVMRealULE,
1450                                irvm::block::FcmpCond::Une => LLVMRealPredicate::LLVMRealUNE,
1451                                irvm::block::FcmpCond::Uno => LLVMRealPredicate::LLVMRealUNO,
1452                                irvm::block::FcmpCond::True => {
1453                                    LLVMRealPredicate::LLVMRealPredicateTrue
1454                                }
1455                            },
1456                            lhs_val,
1457                            rhs_val,
1458                            null_name.as_ptr(),
1459                        );
1460                        ctx.values
1461                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1462                    }
1463                    irvm::block::OtherOp::Select {
1464                        cond,
1465                        true_val,
1466                        false_val,
1467                    } => {
1468                        let cond_val = lower_operand(ctx, cond);
1469                        let true_ptr = lower_operand(ctx, true_val);
1470                        let false_ptr = lower_operand(ctx, false_val);
1471                        let value = core::LLVMBuildSelect(
1472                            ctx.builder,
1473                            cond_val,
1474                            true_ptr,
1475                            false_ptr,
1476                            null_name.as_ptr(),
1477                        );
1478                        ctx.values
1479                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1480                    }
1481                    irvm::block::OtherOp::LandingPad {
1482                        result_ty,
1483                        cleanup,
1484                        clauses,
1485                    } => {
1486                        let ty = lower_type(ctx.ctx, ctx.storage, *result_ty);
1487                        let lp = core::LLVMBuildLandingPad(
1488                            ctx.builder,
1489                            ty,
1490                            null_mut(), // personality function is set on the function
1491                            clauses.len() as u32,
1492                            null_name.as_ptr(),
1493                        );
1494
1495                        if *cleanup {
1496                            core::LLVMSetCleanup(lp, 1);
1497                        }
1498
1499                        for clause in clauses {
1500                            match clause {
1501                                irvm::block::LandingPadClause::Catch(operand) => {
1502                                    let catch_val = lower_operand(ctx, operand);
1503                                    core::LLVMAddClause(lp, catch_val);
1504                                }
1505                                irvm::block::LandingPadClause::Filter(operands) => {
1506                                    // Filter is an array of type infos
1507                                    let i8_ptr_ty = core::LLVMPointerType(
1508                                        core::LLVMInt8TypeInContext(ctx.ctx),
1509                                        0,
1510                                    );
1511                                    let mut filter_vals: Vec<_> =
1512                                        operands.iter().map(|o| lower_operand(ctx, o)).collect();
1513                                    let filter_arr = core::LLVMConstArray2(
1514                                        i8_ptr_ty,
1515                                        filter_vals.as_mut_ptr(),
1516                                        filter_vals.len() as u64,
1517                                    );
1518                                    core::LLVMAddClause(lp, filter_arr);
1519                                }
1520                            }
1521                        }
1522
1523                        ctx.values
1524                            .insert((block_idx.to_idx(), inst_idx.to_idx()), lp);
1525                    }
1526                    irvm::block::OtherOp::Intrinsic(intrinsic) => {
1527                        let value = lower_intrinsic(ctx, intrinsic, block_idx, inst_idx)?;
1528                        if !value.is_null() {
1529                            ctx.values
1530                                .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1531                        }
1532                    }
1533                },
1534                Instruction::DebugOp(debug_op) => match debug_op {
1535                    DebugOp::Declare { address, variable } => {
1536                        let var = ctx.func.debug_vars.get(*variable).unwrap();
1537                        let address_ptr = lower_operand(ctx, address);
1538                        let var_ptr = lower_debug_var(
1539                            ctx.dibuilder,
1540                            ctx.debug_scope,
1541                            ctx.datalayout,
1542                            var,
1543                            ctx.storage,
1544                        )?;
1545
1546                        let diexpr =
1547                            debuginfo::LLVMDIBuilderCreateExpression(ctx.dibuilder, null_mut(), 0);
1548                        debuginfo::LLVMDIBuilderInsertDeclareRecordAtEnd(
1549                            ctx.dibuilder,
1550                            address_ptr,
1551                            var_ptr,
1552                            diexpr,
1553                            loc,
1554                            block_ptr,
1555                        );
1556                    }
1557                    DebugOp::Value {
1558                        new_value,
1559                        variable,
1560                    } => {
1561                        let var = ctx.func.debug_vars.get(*variable).unwrap();
1562                        let value_ptr = lower_operand(ctx, new_value);
1563                        let var_ptr = lower_debug_var(
1564                            ctx.dibuilder,
1565                            ctx.debug_scope,
1566                            ctx.datalayout,
1567                            var,
1568                            ctx.storage,
1569                        )?;
1570
1571                        let diexpr =
1572                            debuginfo::LLVMDIBuilderCreateExpression(ctx.dibuilder, null_mut(), 0);
1573                        debuginfo::LLVMDIBuilderInsertDbgValueRecordAtEnd(
1574                            ctx.dibuilder,
1575                            value_ptr,
1576                            var_ptr,
1577                            diexpr,
1578                            loc,
1579                            block_ptr,
1580                        );
1581                    }
1582                    DebugOp::Assign { .. } => {
1583                        // TODO: no di assign in llvm sys?
1584                        todo!()
1585                    }
1586                },
1587                Instruction::ConversionOp(conv_op) => match conv_op {
1588                    irvm::block::ConversionOp::Trunc { value, target_ty } => {
1589                        let val = lower_operand(ctx, value);
1590                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1591                        let result = core::LLVMBuildTrunc(ctx.builder, val, ty, null_name.as_ptr());
1592                        ctx.values
1593                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1594                    }
1595                    irvm::block::ConversionOp::ZExt { value, target_ty } => {
1596                        let val = lower_operand(ctx, value);
1597                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1598                        let result = core::LLVMBuildZExt(ctx.builder, val, ty, null_name.as_ptr());
1599                        ctx.values
1600                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1601                    }
1602                    irvm::block::ConversionOp::SExt { value, target_ty } => {
1603                        let val = lower_operand(ctx, value);
1604                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1605                        let result = core::LLVMBuildSExt(ctx.builder, val, ty, null_name.as_ptr());
1606                        ctx.values
1607                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1608                    }
1609                    irvm::block::ConversionOp::FPTrunc { value, target_ty } => {
1610                        let val = lower_operand(ctx, value);
1611                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1612                        let result =
1613                            core::LLVMBuildFPTrunc(ctx.builder, val, ty, null_name.as_ptr());
1614                        ctx.values
1615                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1616                    }
1617                    irvm::block::ConversionOp::FPExt { value, target_ty } => {
1618                        let val = lower_operand(ctx, value);
1619                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1620                        let result = core::LLVMBuildFPExt(ctx.builder, val, ty, null_name.as_ptr());
1621                        ctx.values
1622                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1623                    }
1624                    irvm::block::ConversionOp::FPToUI { value, target_ty } => {
1625                        let val = lower_operand(ctx, value);
1626                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1627                        let result =
1628                            core::LLVMBuildFPToUI(ctx.builder, val, ty, null_name.as_ptr());
1629                        ctx.values
1630                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1631                    }
1632                    irvm::block::ConversionOp::FPToSI { value, target_ty } => {
1633                        let val = lower_operand(ctx, value);
1634                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1635                        let result =
1636                            core::LLVMBuildFPToSI(ctx.builder, val, ty, null_name.as_ptr());
1637                        ctx.values
1638                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1639                    }
1640                    irvm::block::ConversionOp::UIToFP { value, target_ty } => {
1641                        let val = lower_operand(ctx, value);
1642                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1643                        let result =
1644                            core::LLVMBuildUIToFP(ctx.builder, val, ty, null_name.as_ptr());
1645                        ctx.values
1646                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1647                    }
1648                    irvm::block::ConversionOp::SIToFP { value, target_ty } => {
1649                        let val = lower_operand(ctx, value);
1650                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1651                        let result =
1652                            core::LLVMBuildSIToFP(ctx.builder, val, ty, null_name.as_ptr());
1653                        ctx.values
1654                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1655                    }
1656                    irvm::block::ConversionOp::PtrToInt { value, target_ty } => {
1657                        let val = lower_operand(ctx, value);
1658                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1659                        let result =
1660                            core::LLVMBuildPtrToInt(ctx.builder, val, ty, null_name.as_ptr());
1661                        ctx.values
1662                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1663                    }
1664                    irvm::block::ConversionOp::IntToPtr { value, target_ty } => {
1665                        let val = lower_operand(ctx, value);
1666                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1667                        let result =
1668                            core::LLVMBuildIntToPtr(ctx.builder, val, ty, null_name.as_ptr());
1669                        ctx.values
1670                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1671                    }
1672                    irvm::block::ConversionOp::Bitcast { value, target_ty } => {
1673                        let val = lower_operand(ctx, value);
1674                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1675                        let result =
1676                            core::LLVMBuildBitCast(ctx.builder, val, ty, null_name.as_ptr());
1677                        ctx.values
1678                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1679                    }
1680                    irvm::block::ConversionOp::AddrSpaceCast { value, target_ty } => {
1681                        let val = lower_operand(ctx, value);
1682                        let ty = lower_type(ctx.ctx, ctx.storage, *target_ty);
1683                        let result =
1684                            core::LLVMBuildAddrSpaceCast(ctx.builder, val, ty, null_name.as_ptr());
1685                        ctx.values
1686                            .insert((block_idx.to_idx(), inst_idx.to_idx()), result);
1687                    }
1688                },
1689                Instruction::AggregateOp(agg_op) => match agg_op {
1690                    irvm::block::AggregateOp::ExtractValue { aggregate, indices } => {
1691                        let agg_val = lower_operand(ctx, aggregate);
1692                        let value = if indices.len() == 1 {
1693                            core::LLVMBuildExtractValue(
1694                                ctx.builder,
1695                                agg_val,
1696                                indices[0],
1697                                null_name.as_ptr(),
1698                            )
1699                        } else {
1700                            // For multiple indices, we need to chain ExtractValue calls
1701                            let mut result = agg_val;
1702                            for &idx in indices {
1703                                result = core::LLVMBuildExtractValue(
1704                                    ctx.builder,
1705                                    result,
1706                                    idx,
1707                                    null_name.as_ptr(),
1708                                );
1709                            }
1710                            result
1711                        };
1712                        ctx.values
1713                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1714                    }
1715                    irvm::block::AggregateOp::InsertValue {
1716                        aggregate,
1717                        element,
1718                        indices,
1719                    } => {
1720                        let agg_val = lower_operand(ctx, aggregate);
1721                        let elem_val = lower_operand(ctx, element);
1722                        let value = if indices.len() == 1 {
1723                            core::LLVMBuildInsertValue(
1724                                ctx.builder,
1725                                agg_val,
1726                                elem_val,
1727                                indices[0],
1728                                null_name.as_ptr(),
1729                            )
1730                        } else {
1731                            // For multiple indices, we need to extract nested aggregates,
1732                            // insert at the innermost, and rebuild
1733                            // For simplicity, this implementation only handles single index
1734                            // A full implementation would need recursive extraction
1735                            core::LLVMBuildInsertValue(
1736                                ctx.builder,
1737                                agg_val,
1738                                elem_val,
1739                                indices[0],
1740                                null_name.as_ptr(),
1741                            )
1742                        };
1743                        ctx.values
1744                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1745                    }
1746                },
1747            }
1748        }
1749
1750        match ctx.func.blocks[block_idx].terminator().clone() {
1751            irvm::block::Terminator::Ret(op) => {
1752                set_loc(ctx.ctx, ctx.builder, &op.0, ctx.debug_scope);
1753                if let Some(op) = op.1 {
1754                    let value = lower_operand(ctx, &op);
1755                    core::LLVMBuildRet(ctx.builder, value);
1756                } else {
1757                    core::LLVMBuildRetVoid(ctx.builder);
1758                }
1759            }
1760            irvm::block::Terminator::Br {
1761                block: jmp_block,
1762                location,
1763                ..
1764            } => {
1765                set_loc(ctx.ctx, ctx.builder, &location, ctx.debug_scope);
1766                let target_block = *ctx.blocks.get(&jmp_block.to_idx()).unwrap();
1767
1768                core::LLVMBuildBr(ctx.builder, target_block);
1769            }
1770            irvm::block::Terminator::CondBr {
1771                then_block: if_block,
1772                else_block: then_block,
1773                cond,
1774                ..
1775            } => {
1776                let cond = lower_operand(ctx, &cond);
1777
1778                let if_block_value = *ctx.blocks.get(&if_block.to_idx()).unwrap();
1779                let then_block_value = *ctx.blocks.get(&then_block.to_idx()).unwrap();
1780
1781                core::LLVMBuildCondBr(ctx.builder, cond, if_block_value, then_block_value);
1782            }
1783            irvm::block::Terminator::Switch {
1784                value,
1785                default_block,
1786                cases,
1787                location,
1788                ..
1789            } => {
1790                set_loc(ctx.ctx, ctx.builder, &location, ctx.debug_scope);
1791                let switch_val = lower_operand(ctx, &value);
1792                let default_bb = *ctx.blocks.get(&default_block.to_idx()).unwrap();
1793
1794                let switch_instr =
1795                    core::LLVMBuildSwitch(ctx.builder, switch_val, default_bb, cases.len() as u32);
1796
1797                let val_ty = lower_type(ctx.ctx, ctx.storage, value.get_type());
1798
1799                for case in cases {
1800                    let case_bb = *ctx.blocks.get(&case.block.to_idx()).unwrap();
1801                    let case_val = core::LLVMConstInt(val_ty, case.value, 0);
1802                    core::LLVMAddCase(switch_instr, case_val, case_bb);
1803                }
1804            }
1805            irvm::block::Terminator::Invoke {
1806                call,
1807                normal_dest,
1808                unwind_dest,
1809                location,
1810                ..
1811            } => {
1812                set_loc(ctx.ctx, ctx.builder, &location, ctx.debug_scope);
1813
1814                let (target_fn_ptr, fn_ty) = match &call.fn_target {
1815                    irvm::block::CallableValue::Symbol(id) => {
1816                        *ctx.functions.get(&id.to_idx()).expect("function not found")
1817                    }
1818                    irvm::block::CallableValue::Pointer(operand, fn_ty) => {
1819                        let ptr = lower_operand(ctx, operand);
1820
1821                        let ret_ty = lower_type(ctx.ctx, ctx.storage, fn_ty.return_type);
1822                        let mut params = fn_ty
1823                            .parameters
1824                            .iter()
1825                            .map(|x| lower_type(ctx.ctx, ctx.storage, *x))
1826                            .collect_vec();
1827                        let fn_ty = core::LLVMFunctionType(
1828                            ret_ty,
1829                            params.as_mut_ptr(),
1830                            params.len() as u32,
1831                            0,
1832                        );
1833                        (ptr, fn_ty)
1834                    }
1835                };
1836
1837                let mut args = call
1838                    .params
1839                    .iter()
1840                    .map(|p| lower_operand(ctx, p))
1841                    .collect_vec();
1842
1843                let normal_bb = *ctx.blocks.get(&normal_dest.to_idx()).unwrap();
1844                let unwind_bb = *ctx.blocks.get(&unwind_dest.to_idx()).unwrap();
1845
1846                core::LLVMBuildInvoke2(
1847                    ctx.builder,
1848                    fn_ty,
1849                    target_fn_ptr,
1850                    args.as_mut_ptr(),
1851                    args.len() as u32,
1852                    normal_bb,
1853                    unwind_bb,
1854                    c"".as_ptr(),
1855                );
1856            }
1857            irvm::block::Terminator::Resume { value, location } => {
1858                set_loc(ctx.ctx, ctx.builder, &location, ctx.debug_scope);
1859                let val = lower_operand(ctx, &value);
1860                core::LLVMBuildResume(ctx.builder, val);
1861            }
1862            irvm::block::Terminator::Unreachable { location } => {
1863                set_loc(ctx.ctx, ctx.builder, &location, ctx.debug_scope);
1864                core::LLVMBuildUnreachable(ctx.builder);
1865            }
1866        }
1867
1868        Ok(())
1869    }
1870}
1871
1872fn add_block(ctx: &mut FnCtx, block_idx: BlockIdx, name: Option<String>) -> LLVMBasicBlockRef {
1873    unsafe {
1874        let block_name = CString::new(if block_idx.to_idx() == 0 {
1875            "entry".to_string()
1876        } else if let Some(name) = name {
1877            format!("bb{name}")
1878        } else {
1879            format!("bb{}", block_idx.to_idx())
1880        })
1881        .unwrap();
1882        let block_ptr = core::LLVMAppendBasicBlock(ctx.fn_ptr, block_name.as_ptr());
1883        ctx.blocks.insert(block_idx.to_idx(), block_ptr);
1884        block_ptr
1885    }
1886}
1887
1888fn lower_debug_var(
1889    dibuilder: LLVMDIBuilderRef,
1890    scope: LLVMMetadataRef,
1891    datalayout: &DataLayout,
1892    variable: &DebugVariable,
1893    storage: &TypeStorage,
1894) -> Result<LLVMMetadataRef, Error> {
1895    let name = CString::new(variable.name.clone())?;
1896    let difile = get_difile_location(dibuilder, &variable.location);
1897
1898    let (line, _col) = match &variable.location {
1899        Location::Unknown => (0, 0),
1900        Location::File(file_location) => (file_location.line, file_location.col),
1901    };
1902
1903    let ty_ptr = lower_debug_type(datalayout, dibuilder, storage, scope, variable.ty);
1904    let align = datalayout.get_type_align(storage, variable.ty);
1905
1906    Ok(unsafe {
1907        if let Some(param) = variable.parameter {
1908            debuginfo::LLVMDIBuilderCreateParameterVariable(
1909                dibuilder,
1910                scope,
1911                name.as_ptr(),
1912                name.count_bytes(),
1913                param,
1914                difile,
1915                line,
1916                ty_ptr,
1917                1,
1918                0,
1919            )
1920        } else {
1921            debuginfo::LLVMDIBuilderCreateAutoVariable(
1922                dibuilder,
1923                scope,
1924                name.as_ptr(),
1925                name.count_bytes(),
1926                difile,
1927                line,
1928                ty_ptr,
1929                1,
1930                0,
1931                align,
1932            )
1933        }
1934    })
1935}
1936
1937fn get_difile_location(dibuilder: LLVMDIBuilderRef, location: &Location) -> LLVMMetadataRef {
1938    match location {
1939        Location::Unknown => unsafe {
1940            debuginfo::LLVMDIBuilderCreateFile(
1941                dibuilder,
1942                c"/dev/stdin".as_ptr(),
1943                c"/dev/stdin".count_bytes(),
1944                c"".as_ptr(),
1945                0,
1946            )
1947        },
1948        Location::File(file_location) => get_difile(dibuilder, &file_location.file),
1949    }
1950}
1951
1952fn get_difile(dibuilder: LLVMDIBuilderRef, file: &Path) -> LLVMMetadataRef {
1953    let parent = if let Some(parent) = file.parent() {
1954        CString::new(parent.display().to_string()).unwrap()
1955    } else {
1956        CString::new("").unwrap()
1957    };
1958
1959    let filename = CString::new(file.display().to_string()).unwrap();
1960
1961    unsafe {
1962        debuginfo::LLVMDIBuilderCreateFile(
1963            dibuilder,
1964            filename.as_ptr(),
1965            filename.count_bytes(),
1966            parent.as_ptr(),
1967            parent.count_bytes(),
1968        )
1969    }
1970}
1971
1972fn set_loc(
1973    ctx: LLVMContextRef,
1974    builder: LLVMBuilderRef,
1975    location: &Location,
1976    scope: LLVMMetadataRef,
1977) -> *mut LLVMOpaqueMetadata {
1978    match location {
1979        Location::Unknown => unsafe {
1980            let loc = debuginfo::LLVMDIBuilderCreateDebugLocation(ctx, 0, 0, scope, null_mut());
1981            core::LLVMSetCurrentDebugLocation2(builder, loc);
1982            loc
1983        },
1984        Location::File(file_location) => unsafe {
1985            let loc = debuginfo::LLVMDIBuilderCreateDebugLocation(
1986                ctx,
1987                file_location.line,
1988                file_location.col,
1989                scope,
1990                null_mut(),
1991            );
1992            core::LLVMSetCurrentDebugLocation2(builder, loc);
1993            loc
1994        },
1995    }
1996}
1997
1998fn add_preds(ctx: &mut FnCtx, block_idx: BlockIdx) {
1999    unsafe {
2000        let block_ptr = *ctx.blocks.get(&block_idx.to_idx()).unwrap();
2001        core::LLVMPositionBuilderAtEnd(ctx.builder, block_ptr);
2002
2003        let preds = ctx.func.find_preds_for(block_idx);
2004        let mut block_args = Vec::new();
2005
2006        if !preds.is_empty() {
2007            let operand_len = preds.first().unwrap().1.len();
2008
2009            for i in 0..(operand_len) {
2010                let phy_ty =
2011                    lower_type(ctx.ctx, ctx.storage, preds.first().unwrap().1[i].get_type());
2012                let phi_node = core::LLVMBuildPhi(ctx.builder, phy_ty, c"".as_ptr());
2013                let mut blocks = Vec::new();
2014                let mut values = Vec::new();
2015                for (pred_block_idx, operands) in &preds {
2016                    let pred_ptr = ctx.blocks.get(&pred_block_idx.to_idx()).unwrap();
2017                    let value = lower_operand(ctx, &operands[i]);
2018
2019                    blocks.push(*pred_ptr);
2020                    values.push(value);
2021                }
2022
2023                assert_eq!(values.len(), values.len());
2024
2025                core::LLVMAddIncoming(
2026                    phi_node,
2027                    values.as_mut_ptr().cast(),
2028                    blocks.as_mut_ptr().cast(),
2029                    blocks.len() as u32,
2030                );
2031                block_args.push(phi_node);
2032            }
2033        }
2034
2035        ctx.block_args.insert(block_idx.to_idx(), block_args);
2036    }
2037}
2038
2039fn lower_operand(ctx: &FnCtx, operand: &Operand) -> LLVMValueRef {
2040    unsafe {
2041        match operand {
2042            Operand::Parameter(idx, _ty) => core::LLVMGetParam(ctx.fn_ptr, (*idx) as u32),
2043            Operand::Value(block_idx, index, _) => *ctx
2044                .values
2045                .get(&(block_idx.to_idx(), index.to_idx()))
2046                .unwrap(),
2047            Operand::Constant(const_value, ty) => lower_constant(ctx, const_value, *ty),
2048            Operand::BlockArgument { block_idx, nth, .. } => {
2049                ctx.block_args.get(block_idx).unwrap()[*nth]
2050            }
2051            Operand::Global(global_idx, _ty) => *ctx
2052                .globals
2053                .get(&global_idx.to_idx())
2054                .expect("global not found"),
2055        }
2056    }
2057}
2058
2059fn lower_constant(ctx: &FnCtx, value: &ConstValue, ty: TypeIdx) -> LLVMValueRef {
2060    unsafe {
2061        let ty_ptr = lower_type(ctx.ctx, ctx.storage, ty);
2062
2063        match value {
2064            irvm::value::ConstValue::Int(value) => core::LLVMConstInt(ty_ptr, *value, 0_i32),
2065            irvm::value::ConstValue::Float(value) => core::LLVMConstReal(ty_ptr, *value),
2066            irvm::value::ConstValue::Array(const_values) => {
2067                let mut values = Vec::new();
2068                let array_ty = if let Type::Array(array_ty) = &ctx.storage.get_type_info(ty).ty {
2069                    array_ty
2070                } else {
2071                    panic!("type mismatch")
2072                };
2073
2074                let typtr = lower_type(ctx.ctx, ctx.storage, array_ty.ty);
2075
2076                for value in const_values {
2077                    let ptr = lower_constant(ctx, value, array_ty.ty);
2078                    values.push(ptr);
2079                }
2080
2081                core::LLVMConstArray2(typtr, values.as_mut_ptr(), values.len() as u64)
2082            }
2083            irvm::value::ConstValue::Vector(const_values) => {
2084                let mut values = Vec::new();
2085                let vec_ty = if let Type::Vector(vec_ty) = &ctx.storage.get_type_info(ty).ty {
2086                    vec_ty
2087                } else {
2088                    panic!("type mismatch")
2089                };
2090
2091                for value in const_values {
2092                    let ptr = lower_constant(ctx, value, vec_ty.ty);
2093                    values.push(ptr);
2094                }
2095
2096                core::LLVMConstVector(values.as_mut_ptr(), values.len() as u32)
2097            }
2098            irvm::value::ConstValue::Struct(const_values) => {
2099                let mut const_fields = Vec::new();
2100                let struct_ty = if let Type::Struct(struct_ty) = &ctx.storage.get_type_info(ty).ty {
2101                    &**struct_ty
2102                } else {
2103                    panic!("type mismatch")
2104                };
2105                for (value, field) in const_values.iter().zip(struct_ty.fields.iter()) {
2106                    let ptr = lower_constant(ctx, value, *field);
2107                    const_fields.push(ptr);
2108                }
2109                core::LLVMConstStructInContext(
2110                    ctx.ctx,
2111                    const_fields.as_mut_ptr(),
2112                    const_fields.len() as u32,
2113                    struct_ty.packed as i32,
2114                )
2115            }
2116            irvm::value::ConstValue::NullPtr => core::LLVMConstPointerNull(ty_ptr),
2117            irvm::value::ConstValue::Undef => core::LLVMGetUndef(ty_ptr),
2118            irvm::value::ConstValue::Poison => core::LLVMGetPoison(ty_ptr),
2119        }
2120    }
2121}
2122
2123fn lower_type(ctx: LLVMContextRef, storage: &TypeStorage, ty: TypeIdx) -> LLVMTypeRef {
2124    let tyinfo = storage.get_type_info(ty);
2125    unsafe {
2126        match &tyinfo.ty {
2127            Type::Int(width) => core::LLVMIntTypeInContext(ctx, *width),
2128            Type::Half => core::LLVMHalfTypeInContext(ctx),
2129            Type::BFloat => core::LLVMBFloatTypeInContext(ctx),
2130            Type::Float => core::LLVMFloatTypeInContext(ctx),
2131            Type::Double => core::LLVMDoubleTypeInContext(ctx),
2132            Type::Fp128 => core::LLVMFP128TypeInContext(ctx),
2133            Type::X86Fp80 => core::LLVMX86FP80TypeInContext(ctx),
2134            Type::PpcFp128 => core::LLVMPPCFP128TypeInContext(ctx),
2135            Type::Ptr {
2136                pointee: _,
2137                address_space,
2138            } => core::LLVMPointerTypeInContext(ctx, address_space.unwrap_or(0)),
2139            Type::Vector(vector_type) => {
2140                let inner = lower_type(ctx, storage, vector_type.ty);
2141                core::LLVMVectorType(inner, vector_type.size)
2142            }
2143            Type::Array(array_type) => {
2144                let inner = lower_type(ctx, storage, array_type.ty);
2145                core::LLVMArrayType2(inner, array_type.size)
2146            }
2147            Type::Struct(struct_type) => {
2148                let mut fields = Vec::new();
2149
2150                for field in struct_type.fields.iter() {
2151                    fields.push(lower_type(ctx, storage, *field));
2152                }
2153
2154                if let Some(ident) = &struct_type.ident {
2155                    let name = CString::new(ident.as_str()).unwrap();
2156                    let ptr = core::LLVMStructCreateNamed(ctx, name.as_ptr());
2157
2158                    core::LLVMStructSetBody(
2159                        ptr,
2160                        fields.as_mut_ptr(),
2161                        fields.len() as u32,
2162                        struct_type.packed as i32,
2163                    );
2164
2165                    ptr
2166                } else {
2167                    core::LLVMStructTypeInContext(
2168                        ctx,
2169                        fields.as_mut_ptr(),
2170                        fields.len() as u32,
2171                        struct_type.packed as i32,
2172                    )
2173                }
2174            }
2175        }
2176    }
2177}
2178
2179fn lower_debug_type(
2180    datalayout: &DataLayout,
2181    builder: LLVMDIBuilderRef,
2182    storage: &TypeStorage,
2183    module_scope: LLVMMetadataRef,
2184    type_idx: TypeIdx,
2185) -> LLVMMetadataRef {
2186    let ty = storage.get_type_info(type_idx);
2187
2188    // 1 == address
2189    // 2 = boolean
2190    // 4 = float
2191    // 5 = signed
2192    // 11 = numeric string
2193    // https://dwarfstd.org/doc/DWARF5.pdf#section.7.8
2194
2195    let size_in_bits = datalayout.get_type_size(storage, type_idx);
2196    let align_in_bits = datalayout.get_type_align(storage, type_idx);
2197
2198    if let Some(debug_info) = &ty.debug_info {
2199        let name = CString::new(debug_info.name.clone()).unwrap();
2200        unsafe {
2201            match &ty.ty {
2202                Type::Int(width) => {
2203                    let mut encoding = DW_ATE_unsigned;
2204                    if *width == 1 {
2205                        encoding = DW_ATE_boolean;
2206                    }
2207                    debuginfo::LLVMDIBuilderCreateBasicType(
2208                        builder,
2209                        name.as_ptr(),
2210                        name.count_bytes(),
2211                        size_in_bits as u64,
2212                        encoding.0 as u32,
2213                        LLVMDIFlagPublic,
2214                    )
2215                }
2216                Type::Half
2217                | Type::BFloat
2218                | Type::Float
2219                | Type::Double
2220                | Type::Fp128
2221                | Type::X86Fp80
2222                | Type::PpcFp128 => debuginfo::LLVMDIBuilderCreateBasicType(
2223                    builder,
2224                    name.as_ptr(),
2225                    name.count_bytes(),
2226                    size_in_bits as u64,
2227                    0x4,
2228                    LLVMDIFlagPublic,
2229                ),
2230                Type::Ptr {
2231                    pointee,
2232                    address_space,
2233                } => {
2234                    let pointee_ptr =
2235                        lower_debug_type(datalayout, builder, storage, module_scope, *pointee);
2236
2237                    if debug_info.is_reference {
2238                        debuginfo::LLVMDIBuilderCreateReferenceType(
2239                            builder,
2240                            DW_TAG_reference_type.0 as u32,
2241                            pointee_ptr,
2242                        )
2243                    } else {
2244                        debuginfo::LLVMDIBuilderCreatePointerType(
2245                            builder,
2246                            pointee_ptr,
2247                            size_in_bits as u64,
2248                            align_in_bits,
2249                            address_space.unwrap_or(0),
2250                            name.as_ptr(),
2251                            name.count_bytes(),
2252                        )
2253                    }
2254                }
2255                Type::Vector(vector_type) => {
2256                    let inner_ty_ptr = lower_debug_type(
2257                        datalayout,
2258                        builder,
2259                        storage,
2260                        module_scope,
2261                        vector_type.ty,
2262                    );
2263                    let size = datalayout.get_type_size(storage, type_idx);
2264                    let align = datalayout.get_type_align(storage, type_idx);
2265                    let mut subrange = debuginfo::LLVMDIBuilderGetOrCreateSubrange(
2266                        builder,
2267                        0,
2268                        vector_type.size as i64,
2269                    );
2270                    debuginfo::LLVMDIBuilderCreateVectorType(
2271                        builder,
2272                        size as u64,
2273                        align,
2274                        inner_ty_ptr,
2275                        &raw mut subrange,
2276                        1,
2277                    )
2278                }
2279                Type::Array(array_type) => {
2280                    let inner_ty_ptr =
2281                        lower_debug_type(datalayout, builder, storage, module_scope, array_type.ty);
2282                    let size = datalayout.get_type_size(storage, type_idx);
2283                    let align = datalayout.get_type_align(storage, type_idx);
2284                    let mut subrange = debuginfo::LLVMDIBuilderGetOrCreateSubrange(
2285                        builder,
2286                        0,
2287                        array_type.size as i64,
2288                    );
2289                    debuginfo::LLVMDIBuilderCreateArrayType(
2290                        builder,
2291                        size as u64,
2292                        align,
2293                        inner_ty_ptr,
2294                        &raw mut subrange,
2295                        1,
2296                    )
2297                }
2298                Type::Struct(struct_type) => {
2299                    let mut fields = Vec::with_capacity(struct_type.fields.len());
2300
2301                    let difile = get_difile_location(builder, &debug_info.location);
2302                    let line = debug_info.location.get_line();
2303
2304                    let mut offset = 0;
2305                    let mut cur_align = 8;
2306
2307                    for (i, field) in struct_type.fields.iter().enumerate() {
2308                        let field_align = datalayout.get_type_align(storage, *field);
2309                        cur_align = cur_align.max(field_align);
2310
2311                        if offset % field_align != 0 {
2312                            let padding = (field_align - (offset % field_align)) % field_align;
2313                            offset += padding;
2314                        }
2315
2316                        let field_size = datalayout.get_type_size(storage, *field);
2317
2318                        let mut ty =
2319                            lower_debug_type(datalayout, builder, storage, module_scope, *field);
2320
2321                        if let Some((field_name, location)) = struct_type.debug_field_names.get(i) {
2322                            let name = CString::new(field_name.clone()).unwrap();
2323                            let difile = get_difile_location(builder, location);
2324                            let line = location.get_line().unwrap_or(0);
2325                            let size = datalayout.get_type_size(storage, *field);
2326                            let align = datalayout.get_type_align(storage, *field);
2327                            ty = debuginfo::LLVMDIBuilderCreateMemberType(
2328                                builder,
2329                                module_scope,
2330                                name.as_ptr(),
2331                                name.count_bytes(),
2332                                difile,
2333                                line,
2334                                size as u64,
2335                                align,
2336                                offset as u64,
2337                                0,
2338                                ty,
2339                            );
2340                        }
2341
2342                        offset += field_size;
2343
2344                        fields.push(ty);
2345                    }
2346
2347                    let size = datalayout.get_type_size(storage, type_idx);
2348                    let align = datalayout.get_type_align(storage, type_idx);
2349
2350                    debuginfo::LLVMDIBuilderCreateStructType(
2351                        builder,
2352                        module_scope,
2353                        name.as_ptr(),
2354                        name.count_bytes(),
2355                        difile,
2356                        line.unwrap_or(0),
2357                        size as u64,
2358                        align,
2359                        0,
2360                        null_mut(),
2361                        fields.as_mut_ptr(),
2362                        fields.len() as u32,
2363                        0,
2364                        null_mut(),
2365                        name.as_ptr(),
2366                        name.count_bytes(),
2367                    )
2368                }
2369            }
2370        }
2371    } else {
2372        // No debug info provided - create anonymous debug types
2373        unsafe {
2374            match &ty.ty {
2375                Type::Int(width) => {
2376                    let name = CString::new(format!("i{}", width)).unwrap();
2377                    let encoding = if *width == 1 {
2378                        DW_ATE_boolean
2379                    } else {
2380                        DW_ATE_unsigned
2381                    };
2382                    debuginfo::LLVMDIBuilderCreateBasicType(
2383                        builder,
2384                        name.as_ptr(),
2385                        name.count_bytes(),
2386                        size_in_bits as u64,
2387                        encoding.0 as u32,
2388                        LLVMDIFlagPublic,
2389                    )
2390                }
2391                Type::Half
2392                | Type::BFloat
2393                | Type::Float
2394                | Type::Double
2395                | Type::Fp128
2396                | Type::X86Fp80
2397                | Type::PpcFp128 => {
2398                    let name = CString::new(format!("f{}", size_in_bits)).unwrap();
2399                    debuginfo::LLVMDIBuilderCreateBasicType(
2400                        builder,
2401                        name.as_ptr(),
2402                        name.count_bytes(),
2403                        size_in_bits as u64,
2404                        DW_ATE_float.0 as u32,
2405                        LLVMDIFlagPublic,
2406                    )
2407                }
2408                Type::Ptr {
2409                    pointee,
2410                    address_space,
2411                } => {
2412                    let pointee_ptr =
2413                        lower_debug_type(datalayout, builder, storage, module_scope, *pointee);
2414                    let name = CString::new("ptr").unwrap();
2415                    debuginfo::LLVMDIBuilderCreatePointerType(
2416                        builder,
2417                        pointee_ptr,
2418                        size_in_bits as u64,
2419                        align_in_bits,
2420                        address_space.unwrap_or(0),
2421                        name.as_ptr(),
2422                        name.count_bytes(),
2423                    )
2424                }
2425                Type::Vector(vector_type) => {
2426                    let inner_ty_ptr = lower_debug_type(
2427                        datalayout,
2428                        builder,
2429                        storage,
2430                        module_scope,
2431                        vector_type.ty,
2432                    );
2433                    let mut subrange = debuginfo::LLVMDIBuilderGetOrCreateSubrange(
2434                        builder,
2435                        0,
2436                        vector_type.size as i64,
2437                    );
2438                    debuginfo::LLVMDIBuilderCreateVectorType(
2439                        builder,
2440                        size_in_bits as u64,
2441                        align_in_bits,
2442                        inner_ty_ptr,
2443                        &raw mut subrange,
2444                        1,
2445                    )
2446                }
2447                Type::Array(array_type) => {
2448                    let inner_ty_ptr =
2449                        lower_debug_type(datalayout, builder, storage, module_scope, array_type.ty);
2450                    let mut subrange = debuginfo::LLVMDIBuilderGetOrCreateSubrange(
2451                        builder,
2452                        0,
2453                        array_type.size as i64,
2454                    );
2455                    debuginfo::LLVMDIBuilderCreateArrayType(
2456                        builder,
2457                        size_in_bits as u64,
2458                        align_in_bits,
2459                        inner_ty_ptr,
2460                        &raw mut subrange,
2461                        1,
2462                    )
2463                }
2464                Type::Struct(struct_type) => {
2465                    let mut fields = Vec::with_capacity(struct_type.fields.len());
2466                    let mut offset = 0;
2467
2468                    for field in struct_type.fields.iter() {
2469                        let field_align = datalayout.get_type_align(storage, *field);
2470                        if offset % field_align != 0 {
2471                            offset += (field_align - (offset % field_align)) % field_align;
2472                        }
2473                        let ty =
2474                            lower_debug_type(datalayout, builder, storage, module_scope, *field);
2475                        let field_size = datalayout.get_type_size(storage, *field);
2476                        offset += field_size;
2477                        fields.push(ty);
2478                    }
2479
2480                    let name = CString::new("struct").unwrap();
2481                    debuginfo::LLVMDIBuilderCreateStructType(
2482                        builder,
2483                        module_scope,
2484                        name.as_ptr(),
2485                        name.count_bytes(),
2486                        null_mut(),
2487                        0,
2488                        size_in_bits as u64,
2489                        align_in_bits,
2490                        0,
2491                        null_mut(),
2492                        fields.as_mut_ptr(),
2493                        fields.len() as u32,
2494                        0,
2495                        null_mut(),
2496                        name.as_ptr(),
2497                        name.count_bytes(),
2498                    )
2499                }
2500            }
2501        }
2502    }
2503}
2504
2505/// Lower a constant value for global variable initialization (doesn't need FnCtx).
2506fn lower_global_constant(
2507    ctx: LLVMContextRef,
2508    storage: &TypeStorage,
2509    value: &ConstValue,
2510    ty: TypeIdx,
2511) -> LLVMValueRef {
2512    unsafe {
2513        let ty_ptr = lower_type(ctx, storage, ty);
2514
2515        match value {
2516            ConstValue::Int(value) => core::LLVMConstInt(ty_ptr, *value, 0_i32),
2517            ConstValue::Float(value) => core::LLVMConstReal(ty_ptr, *value),
2518            ConstValue::Array(const_values) => {
2519                let array_ty = if let Type::Array(array_ty) = &storage.get_type_info(ty).ty {
2520                    array_ty
2521                } else {
2522                    panic!("type mismatch")
2523                };
2524
2525                let typtr = lower_type(ctx, storage, array_ty.ty);
2526                let mut values: Vec<_> = const_values
2527                    .iter()
2528                    .map(|v| lower_global_constant(ctx, storage, v, array_ty.ty))
2529                    .collect();
2530
2531                core::LLVMConstArray2(typtr, values.as_mut_ptr(), values.len() as u64)
2532            }
2533            ConstValue::Vector(const_values) => {
2534                let vec_ty = if let Type::Vector(vec_ty) = &storage.get_type_info(ty).ty {
2535                    vec_ty
2536                } else {
2537                    panic!("type mismatch")
2538                };
2539
2540                let mut values: Vec<_> = const_values
2541                    .iter()
2542                    .map(|v| lower_global_constant(ctx, storage, v, vec_ty.ty))
2543                    .collect();
2544
2545                core::LLVMConstVector(values.as_mut_ptr(), values.len() as u32)
2546            }
2547            ConstValue::Struct(const_values) => {
2548                let struct_ty = if let Type::Struct(struct_ty) = &storage.get_type_info(ty).ty {
2549                    &**struct_ty
2550                } else {
2551                    panic!("type mismatch")
2552                };
2553                let mut const_fields: Vec<_> = const_values
2554                    .iter()
2555                    .zip(struct_ty.fields.iter())
2556                    .map(|(v, field)| lower_global_constant(ctx, storage, v, *field))
2557                    .collect();
2558                core::LLVMConstStructInContext(
2559                    ctx,
2560                    const_fields.as_mut_ptr(),
2561                    const_fields.len() as u32,
2562                    struct_ty.packed as i32,
2563                )
2564            }
2565            ConstValue::NullPtr => core::LLVMConstPointerNull(ty_ptr),
2566            ConstValue::Undef => core::LLVMGetUndef(ty_ptr),
2567            ConstValue::Poison => core::LLVMGetPoison(ty_ptr),
2568        }
2569    }
2570}
2571
2572fn lower_linkage(linkage: &Linkage) -> llvm_sys::LLVMLinkage {
2573    match linkage {
2574        Linkage::Private => llvm_sys::LLVMLinkage::LLVMPrivateLinkage,
2575        Linkage::Internal => llvm_sys::LLVMLinkage::LLVMInternalLinkage,
2576        Linkage::AvailableExternally => llvm_sys::LLVMLinkage::LLVMAvailableExternallyLinkage,
2577        Linkage::LinkOnce => llvm_sys::LLVMLinkage::LLVMLinkOnceAnyLinkage,
2578        Linkage::Weak => llvm_sys::LLVMLinkage::LLVMWeakAnyLinkage,
2579        Linkage::Common => llvm_sys::LLVMLinkage::LLVMCommonLinkage,
2580        Linkage::Appending => llvm_sys::LLVMLinkage::LLVMAppendingLinkage,
2581        Linkage::ExternWeak => llvm_sys::LLVMLinkage::LLVMExternalWeakLinkage,
2582        Linkage::LinkOnceOdr => llvm_sys::LLVMLinkage::LLVMLinkOnceODRLinkage,
2583        Linkage::WeakOdr => llvm_sys::LLVMLinkage::LLVMWeakODRLinkage,
2584        Linkage::External => llvm_sys::LLVMLinkage::LLVMExternalLinkage,
2585    }
2586}
2587
2588fn lower_atomic_ordering(ordering: &AtomicOrdering) -> llvm_sys::LLVMAtomicOrdering {
2589    match ordering {
2590        AtomicOrdering::Unordered => llvm_sys::LLVMAtomicOrdering::LLVMAtomicOrderingUnordered,
2591        AtomicOrdering::Monotonic => llvm_sys::LLVMAtomicOrdering::LLVMAtomicOrderingMonotonic,
2592        AtomicOrdering::Acquire => llvm_sys::LLVMAtomicOrdering::LLVMAtomicOrderingAcquire,
2593        AtomicOrdering::Release => llvm_sys::LLVMAtomicOrdering::LLVMAtomicOrderingRelease,
2594        AtomicOrdering::AcqRel => llvm_sys::LLVMAtomicOrdering::LLVMAtomicOrderingAcquireRelease,
2595        AtomicOrdering::SeqCst => {
2596            llvm_sys::LLVMAtomicOrdering::LLVMAtomicOrderingSequentiallyConsistent
2597        }
2598    }
2599}
2600
2601fn lower_atomic_rmw_op(op: &AtomicRMWOp) -> llvm_sys::LLVMAtomicRMWBinOp {
2602    match op {
2603        AtomicRMWOp::Xchg => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpXchg,
2604        AtomicRMWOp::Add => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpAdd,
2605        AtomicRMWOp::Sub => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpSub,
2606        AtomicRMWOp::And => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpAnd,
2607        AtomicRMWOp::Nand => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpNand,
2608        AtomicRMWOp::Or => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpOr,
2609        AtomicRMWOp::Xor => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpXor,
2610        AtomicRMWOp::Max => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpMax,
2611        AtomicRMWOp::Min => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpMin,
2612        AtomicRMWOp::UMax => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpUMax,
2613        AtomicRMWOp::UMin => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpUMin,
2614        AtomicRMWOp::FAdd => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFAdd,
2615        AtomicRMWOp::FSub => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFSub,
2616        AtomicRMWOp::FMax => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFMax,
2617        AtomicRMWOp::FMin => llvm_sys::LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFMin,
2618    }
2619}
2620
2621fn apply_fast_math_flags(value: LLVMValueRef, flags: &FastMathFlags) {
2622    if !flags.any() {
2623        return;
2624    }
2625
2626    let mut llvm_flags = llvm_sys::LLVMFastMathNone;
2627    if flags.reassoc {
2628        llvm_flags |= llvm_sys::LLVMFastMathAllowReassoc;
2629    }
2630    if flags.nnan {
2631        llvm_flags |= llvm_sys::LLVMFastMathNoNaNs;
2632    }
2633    if flags.ninf {
2634        llvm_flags |= llvm_sys::LLVMFastMathNoInfs;
2635    }
2636    if flags.nsz {
2637        llvm_flags |= llvm_sys::LLVMFastMathNoSignedZeros;
2638    }
2639    if flags.arcp {
2640        llvm_flags |= llvm_sys::LLVMFastMathAllowReciprocal;
2641    }
2642    if flags.contract {
2643        llvm_flags |= llvm_sys::LLVMFastMathAllowContract;
2644    }
2645    if flags.afn {
2646        llvm_flags |= llvm_sys::LLVMFastMathApproxFunc;
2647    }
2648
2649    unsafe {
2650        core::LLVMSetFastMathFlags(value, llvm_flags);
2651    }
2652}
2653
2654/// Apply function-level attributes to an LLVM function.
2655fn apply_function_attrs(
2656    ctx: LLVMContextRef,
2657    fn_ptr: LLVMValueRef,
2658    attrs: &irvm::function::FunctionAttrs,
2659) {
2660    // Function attribute index is -1 (LLVMAttributeFunctionIndex)
2661    const FUNCTION_INDEX: u32 = !0;
2662
2663    unsafe {
2664        // Helper to add a string-named attribute
2665        let add_attr = |name: &[u8]| {
2666            let kind = core::LLVMGetEnumAttributeKindForName(name.as_ptr().cast(), name.len());
2667            if kind != 0 {
2668                let attr = core::LLVMCreateEnumAttribute(ctx, kind, 0);
2669                core::LLVMAddAttributeAtIndex(fn_ptr, FUNCTION_INDEX, attr);
2670            }
2671        };
2672
2673        if attrs.nounwind {
2674            add_attr(b"nounwind");
2675        }
2676        if attrs.noreturn {
2677            add_attr(b"noreturn");
2678        }
2679        if attrs.cold {
2680            add_attr(b"cold");
2681        }
2682        if attrs.hot {
2683            add_attr(b"hot");
2684        }
2685        if attrs.willreturn {
2686            add_attr(b"willreturn");
2687        }
2688        if attrs.nosync {
2689            add_attr(b"nosync");
2690        }
2691        if attrs.nofree {
2692            add_attr(b"nofree");
2693        }
2694        if attrs.norecurse {
2695            add_attr(b"norecurse");
2696        }
2697        if attrs.readnone {
2698            add_attr(b"readnone");
2699        }
2700        if attrs.readonly {
2701            add_attr(b"readonly");
2702        }
2703        if attrs.writeonly {
2704            add_attr(b"writeonly");
2705        }
2706        if attrs.inlinehint {
2707            add_attr(b"inlinehint");
2708        }
2709        if attrs.alwaysinline {
2710            add_attr(b"alwaysinline");
2711        }
2712        if attrs.noinline {
2713            add_attr(b"noinline");
2714        }
2715        if attrs.minsize {
2716            add_attr(b"minsize");
2717        }
2718        if attrs.optsize {
2719            add_attr(b"optsize");
2720        }
2721    }
2722}
2723
2724/// Apply parameter attributes to an LLVM function.
2725fn apply_parameter_attrs(
2726    ctx: LLVMContextRef,
2727    fn_ptr: LLVMValueRef,
2728    params: &[irvm::function::Parameter],
2729) {
2730    unsafe {
2731        for (idx, param) in params.iter().enumerate() {
2732            // Parameter indices start at 1 (0 is return value)
2733            let param_index = (idx + 1) as u32;
2734
2735            // Helper to add a boolean attribute
2736            let add_attr = |name: &[u8]| {
2737                let kind = core::LLVMGetEnumAttributeKindForName(name.as_ptr().cast(), name.len());
2738                if kind != 0 {
2739                    let attr = core::LLVMCreateEnumAttribute(ctx, kind, 0);
2740                    core::LLVMAddAttributeAtIndex(fn_ptr, param_index, attr);
2741                }
2742            };
2743
2744            if param.nocapture {
2745                add_attr(b"nocapture");
2746            }
2747            if param.readonly {
2748                add_attr(b"readonly");
2749            }
2750            if param.writeonly {
2751                add_attr(b"writeonly");
2752            }
2753            if param.noalias {
2754                add_attr(b"noalias");
2755            }
2756            if param.noundef {
2757                add_attr(b"noundef");
2758            }
2759            if param.nonnull {
2760                add_attr(b"nonnull");
2761            }
2762            if param.nofree {
2763                add_attr(b"nofree");
2764            }
2765            if param.nest {
2766                add_attr(b"nest");
2767            }
2768            if param.returned {
2769                add_attr(b"returned");
2770            }
2771            if param.inreg {
2772                add_attr(b"inreg");
2773            }
2774            if param.zeroext {
2775                add_attr(b"zeroext");
2776            }
2777            if param.signext {
2778                add_attr(b"signext");
2779            }
2780
2781            // Int-valued attribute for dereferenceable
2782            if let Some(deref) = param.dereferenceable {
2783                let kind = core::LLVMGetEnumAttributeKindForName(
2784                    b"dereferenceable".as_ptr().cast(),
2785                    b"dereferenceable".len(),
2786                );
2787                if kind != 0 {
2788                    let attr = core::LLVMCreateEnumAttribute(ctx, kind, deref as u64);
2789                    core::LLVMAddAttributeAtIndex(fn_ptr, param_index, attr);
2790                }
2791            }
2792
2793            // Int-valued attribute for alignment
2794            if let Some(align) = param.align {
2795                let kind =
2796                    core::LLVMGetEnumAttributeKindForName(b"align".as_ptr().cast(), b"align".len());
2797                if kind != 0 {
2798                    let attr = core::LLVMCreateEnumAttribute(ctx, kind, align as u64);
2799                    core::LLVMAddAttributeAtIndex(fn_ptr, param_index, attr);
2800                }
2801            }
2802        }
2803    }
2804}
2805
2806/// Apply return value attributes to an LLVM function.
2807fn apply_return_attrs(
2808    ctx: LLVMContextRef,
2809    fn_ptr: LLVMValueRef,
2810    attrs: &irvm::function::ReturnAttrs,
2811) {
2812    // Return attribute index is 0
2813    const RETURN_INDEX: u32 = 0;
2814
2815    unsafe {
2816        let add_attr = |name: &[u8]| {
2817            let kind = core::LLVMGetEnumAttributeKindForName(name.as_ptr().cast(), name.len());
2818            if kind != 0 {
2819                let attr = core::LLVMCreateEnumAttribute(ctx, kind, 0);
2820                core::LLVMAddAttributeAtIndex(fn_ptr, RETURN_INDEX, attr);
2821            }
2822        };
2823
2824        if attrs.noalias {
2825            add_attr(b"noalias");
2826        }
2827        if attrs.noundef {
2828            add_attr(b"noundef");
2829        }
2830        if attrs.nonnull {
2831            add_attr(b"nonnull");
2832        }
2833
2834        // Int-valued attribute for dereferenceable
2835        if let Some(deref) = attrs.dereferenceable {
2836            let kind = core::LLVMGetEnumAttributeKindForName(
2837                b"dereferenceable".as_ptr().cast(),
2838                b"dereferenceable".len(),
2839            );
2840            if kind != 0 {
2841                let attr = core::LLVMCreateEnumAttribute(ctx, kind, deref as u64);
2842                core::LLVMAddAttributeAtIndex(fn_ptr, RETURN_INDEX, attr);
2843            }
2844        }
2845    }
2846}
2847
2848/// Lower an intrinsic call to LLVM IR.
2849unsafe fn lower_intrinsic(
2850    ctx: &FnCtx,
2851    intrinsic: &irvm::block::Intrinsic,
2852    _block_idx: BlockIdx,
2853    _inst_idx: irvm::block::InstIdx,
2854) -> Result<LLVMValueRef, Error> {
2855    use irvm::block::Intrinsic;
2856
2857    unsafe {
2858        let null_name = c"";
2859        let module = core::LLVMGetGlobalParent(ctx.fn_ptr);
2860
2861        match intrinsic {
2862            // Memory intrinsics
2863            Intrinsic::Memcpy {
2864                dest,
2865                src,
2866                len,
2867                is_volatile: _,
2868            } => {
2869                let dest_val = lower_operand(ctx, dest);
2870                let src_val = lower_operand(ctx, src);
2871                let len_val = lower_operand(ctx, len);
2872                core::LLVMBuildMemCpy(
2873                    ctx.builder,
2874                    dest_val,
2875                    1, // dest alignment
2876                    src_val,
2877                    1, // src alignment
2878                    len_val,
2879                );
2880                Ok(null_mut())
2881            }
2882            Intrinsic::Memset {
2883                dest,
2884                val,
2885                len,
2886                is_volatile: _,
2887            } => {
2888                let dest_val = lower_operand(ctx, dest);
2889                let val_val = lower_operand(ctx, val);
2890                let len_val = lower_operand(ctx, len);
2891                core::LLVMBuildMemSet(
2892                    ctx.builder,
2893                    dest_val,
2894                    val_val,
2895                    len_val,
2896                    1, // alignment
2897                );
2898                Ok(null_mut())
2899            }
2900            Intrinsic::Memmove {
2901                dest,
2902                src,
2903                len,
2904                is_volatile: _,
2905            } => {
2906                let dest_val = lower_operand(ctx, dest);
2907                let src_val = lower_operand(ctx, src);
2908                let len_val = lower_operand(ctx, len);
2909                core::LLVMBuildMemMove(
2910                    ctx.builder,
2911                    dest_val,
2912                    1, // dest alignment
2913                    src_val,
2914                    1, // src alignment
2915                    len_val,
2916                );
2917                Ok(null_mut())
2918            }
2919
2920            // Overflow intrinsics
2921            Intrinsic::SaddWithOverflow {
2922                lhs,
2923                rhs,
2924                result_ty: _,
2925            } => {
2926                let lhs_val = lower_operand(ctx, lhs);
2927                let rhs_val = lower_operand(ctx, rhs);
2928                let lhs_ty = lower_type(ctx.ctx, ctx.storage, lhs.get_type());
2929                let intrinsic_name = CString::new(format!(
2930                    "llvm.sadd.with.overflow.i{}",
2931                    core::LLVMGetIntTypeWidth(lhs_ty)
2932                ))
2933                .unwrap();
2934                let intrinsic_id = core::LLVMLookupIntrinsicID(
2935                    intrinsic_name.as_ptr(),
2936                    intrinsic_name.to_bytes().len(),
2937                );
2938                let mut param_types = [lhs_ty];
2939                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
2940                    module,
2941                    intrinsic_id,
2942                    param_types.as_mut_ptr(),
2943                    param_types.len(),
2944                );
2945                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
2946                let mut args = [lhs_val, rhs_val];
2947                let value = core::LLVMBuildCall2(
2948                    ctx.builder,
2949                    fn_ty,
2950                    intrinsic_fn,
2951                    args.as_mut_ptr(),
2952                    args.len() as u32,
2953                    null_name.as_ptr(),
2954                );
2955                Ok(value)
2956            }
2957            Intrinsic::UaddWithOverflow {
2958                lhs,
2959                rhs,
2960                result_ty: _,
2961            } => {
2962                let lhs_val = lower_operand(ctx, lhs);
2963                let rhs_val = lower_operand(ctx, rhs);
2964                let lhs_ty = lower_type(ctx.ctx, ctx.storage, lhs.get_type());
2965                let intrinsic_name = CString::new(format!(
2966                    "llvm.uadd.with.overflow.i{}",
2967                    core::LLVMGetIntTypeWidth(lhs_ty)
2968                ))
2969                .unwrap();
2970                let intrinsic_id = core::LLVMLookupIntrinsicID(
2971                    intrinsic_name.as_ptr(),
2972                    intrinsic_name.to_bytes().len(),
2973                );
2974                let mut param_types = [lhs_ty];
2975                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
2976                    module,
2977                    intrinsic_id,
2978                    param_types.as_mut_ptr(),
2979                    param_types.len(),
2980                );
2981                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
2982                let mut args = [lhs_val, rhs_val];
2983                let value = core::LLVMBuildCall2(
2984                    ctx.builder,
2985                    fn_ty,
2986                    intrinsic_fn,
2987                    args.as_mut_ptr(),
2988                    args.len() as u32,
2989                    null_name.as_ptr(),
2990                );
2991                Ok(value)
2992            }
2993            Intrinsic::SsubWithOverflow {
2994                lhs,
2995                rhs,
2996                result_ty: _,
2997            } => {
2998                let lhs_val = lower_operand(ctx, lhs);
2999                let rhs_val = lower_operand(ctx, rhs);
3000                let lhs_ty = lower_type(ctx.ctx, ctx.storage, lhs.get_type());
3001                let intrinsic_name = CString::new(format!(
3002                    "llvm.ssub.with.overflow.i{}",
3003                    core::LLVMGetIntTypeWidth(lhs_ty)
3004                ))
3005                .unwrap();
3006                let intrinsic_id = core::LLVMLookupIntrinsicID(
3007                    intrinsic_name.as_ptr(),
3008                    intrinsic_name.to_bytes().len(),
3009                );
3010                let mut param_types = [lhs_ty];
3011                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3012                    module,
3013                    intrinsic_id,
3014                    param_types.as_mut_ptr(),
3015                    param_types.len(),
3016                );
3017                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3018                let mut args = [lhs_val, rhs_val];
3019                let value = core::LLVMBuildCall2(
3020                    ctx.builder,
3021                    fn_ty,
3022                    intrinsic_fn,
3023                    args.as_mut_ptr(),
3024                    args.len() as u32,
3025                    null_name.as_ptr(),
3026                );
3027                Ok(value)
3028            }
3029            Intrinsic::UsubWithOverflow {
3030                lhs,
3031                rhs,
3032                result_ty: _,
3033            } => {
3034                let lhs_val = lower_operand(ctx, lhs);
3035                let rhs_val = lower_operand(ctx, rhs);
3036                let lhs_ty = lower_type(ctx.ctx, ctx.storage, lhs.get_type());
3037                let intrinsic_name = CString::new(format!(
3038                    "llvm.usub.with.overflow.i{}",
3039                    core::LLVMGetIntTypeWidth(lhs_ty)
3040                ))
3041                .unwrap();
3042                let intrinsic_id = core::LLVMLookupIntrinsicID(
3043                    intrinsic_name.as_ptr(),
3044                    intrinsic_name.to_bytes().len(),
3045                );
3046                let mut param_types = [lhs_ty];
3047                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3048                    module,
3049                    intrinsic_id,
3050                    param_types.as_mut_ptr(),
3051                    param_types.len(),
3052                );
3053                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3054                let mut args = [lhs_val, rhs_val];
3055                let value = core::LLVMBuildCall2(
3056                    ctx.builder,
3057                    fn_ty,
3058                    intrinsic_fn,
3059                    args.as_mut_ptr(),
3060                    args.len() as u32,
3061                    null_name.as_ptr(),
3062                );
3063                Ok(value)
3064            }
3065            Intrinsic::SmulWithOverflow {
3066                lhs,
3067                rhs,
3068                result_ty: _,
3069            } => {
3070                let lhs_val = lower_operand(ctx, lhs);
3071                let rhs_val = lower_operand(ctx, rhs);
3072                let lhs_ty = lower_type(ctx.ctx, ctx.storage, lhs.get_type());
3073                let intrinsic_name = CString::new(format!(
3074                    "llvm.smul.with.overflow.i{}",
3075                    core::LLVMGetIntTypeWidth(lhs_ty)
3076                ))
3077                .unwrap();
3078                let intrinsic_id = core::LLVMLookupIntrinsicID(
3079                    intrinsic_name.as_ptr(),
3080                    intrinsic_name.to_bytes().len(),
3081                );
3082                let mut param_types = [lhs_ty];
3083                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3084                    module,
3085                    intrinsic_id,
3086                    param_types.as_mut_ptr(),
3087                    param_types.len(),
3088                );
3089                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3090                let mut args = [lhs_val, rhs_val];
3091                let value = core::LLVMBuildCall2(
3092                    ctx.builder,
3093                    fn_ty,
3094                    intrinsic_fn,
3095                    args.as_mut_ptr(),
3096                    args.len() as u32,
3097                    null_name.as_ptr(),
3098                );
3099                Ok(value)
3100            }
3101            Intrinsic::UmulWithOverflow {
3102                lhs,
3103                rhs,
3104                result_ty: _,
3105            } => {
3106                let lhs_val = lower_operand(ctx, lhs);
3107                let rhs_val = lower_operand(ctx, rhs);
3108                let lhs_ty = lower_type(ctx.ctx, ctx.storage, lhs.get_type());
3109                let intrinsic_name = CString::new(format!(
3110                    "llvm.umul.with.overflow.i{}",
3111                    core::LLVMGetIntTypeWidth(lhs_ty)
3112                ))
3113                .unwrap();
3114                let intrinsic_id = core::LLVMLookupIntrinsicID(
3115                    intrinsic_name.as_ptr(),
3116                    intrinsic_name.to_bytes().len(),
3117                );
3118                let mut param_types = [lhs_ty];
3119                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3120                    module,
3121                    intrinsic_id,
3122                    param_types.as_mut_ptr(),
3123                    param_types.len(),
3124                );
3125                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3126                let mut args = [lhs_val, rhs_val];
3127                let value = core::LLVMBuildCall2(
3128                    ctx.builder,
3129                    fn_ty,
3130                    intrinsic_fn,
3131                    args.as_mut_ptr(),
3132                    args.len() as u32,
3133                    null_name.as_ptr(),
3134                );
3135                Ok(value)
3136            }
3137
3138            // Math intrinsics - unary
3139            Intrinsic::Sqrt { value } => {
3140                lower_unary_float_intrinsic(ctx, module, "llvm.sqrt", value)
3141            }
3142            Intrinsic::Sin { value } => lower_unary_float_intrinsic(ctx, module, "llvm.sin", value),
3143            Intrinsic::Cos { value } => lower_unary_float_intrinsic(ctx, module, "llvm.cos", value),
3144            Intrinsic::Exp { value } => lower_unary_float_intrinsic(ctx, module, "llvm.exp", value),
3145            Intrinsic::Exp2 { value } => {
3146                lower_unary_float_intrinsic(ctx, module, "llvm.exp2", value)
3147            }
3148            Intrinsic::Log { value } => lower_unary_float_intrinsic(ctx, module, "llvm.log", value),
3149            Intrinsic::Log2 { value } => {
3150                lower_unary_float_intrinsic(ctx, module, "llvm.log2", value)
3151            }
3152            Intrinsic::Log10 { value } => {
3153                lower_unary_float_intrinsic(ctx, module, "llvm.log10", value)
3154            }
3155            Intrinsic::Fabs { value } => {
3156                lower_unary_float_intrinsic(ctx, module, "llvm.fabs", value)
3157            }
3158            Intrinsic::Floor { value } => {
3159                lower_unary_float_intrinsic(ctx, module, "llvm.floor", value)
3160            }
3161            Intrinsic::Ceil { value } => {
3162                lower_unary_float_intrinsic(ctx, module, "llvm.ceil", value)
3163            }
3164            Intrinsic::Trunc { value } => {
3165                lower_unary_float_intrinsic(ctx, module, "llvm.trunc", value)
3166            }
3167            Intrinsic::Round { value } => {
3168                lower_unary_float_intrinsic(ctx, module, "llvm.round", value)
3169            }
3170
3171            // Math intrinsics - binary
3172            Intrinsic::Pow { base, exp } => {
3173                lower_binary_float_intrinsic(ctx, module, "llvm.pow", base, exp)
3174            }
3175            Intrinsic::Powi { base, exp } => {
3176                // powi takes float and i32 exponent
3177                let base_val = lower_operand(ctx, base);
3178                let exp_val = lower_operand(ctx, exp);
3179                let base_ty = lower_type(ctx.ctx, ctx.storage, base.get_type());
3180                let ty_name = get_float_type_suffix(base_ty);
3181                let intrinsic_name = CString::new(format!("llvm.powi.{}.i32", ty_name)).unwrap();
3182                let intrinsic_id = core::LLVMLookupIntrinsicID(
3183                    intrinsic_name.as_ptr(),
3184                    intrinsic_name.to_bytes().len(),
3185                );
3186                let mut param_types = [base_ty];
3187                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3188                    module,
3189                    intrinsic_id,
3190                    param_types.as_mut_ptr(),
3191                    param_types.len(),
3192                );
3193                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3194                let mut args = [base_val, exp_val];
3195                let value = core::LLVMBuildCall2(
3196                    ctx.builder,
3197                    fn_ty,
3198                    intrinsic_fn,
3199                    args.as_mut_ptr(),
3200                    args.len() as u32,
3201                    c"".as_ptr(),
3202                );
3203                Ok(value)
3204            }
3205            Intrinsic::Copysign { mag, sign } => {
3206                lower_binary_float_intrinsic(ctx, module, "llvm.copysign", mag, sign)
3207            }
3208            Intrinsic::Minnum { a, b } => {
3209                lower_binary_float_intrinsic(ctx, module, "llvm.minnum", a, b)
3210            }
3211            Intrinsic::Maxnum { a, b } => {
3212                lower_binary_float_intrinsic(ctx, module, "llvm.maxnum", a, b)
3213            }
3214
3215            // Math intrinsics - ternary
3216            Intrinsic::Fma { a, b, c } => {
3217                let a_val = lower_operand(ctx, a);
3218                let b_val = lower_operand(ctx, b);
3219                let c_val = lower_operand(ctx, c);
3220                let ty = lower_type(ctx.ctx, ctx.storage, a.get_type());
3221                let ty_name = get_float_type_suffix(ty);
3222                let intrinsic_name = CString::new(format!("llvm.fma.{}", ty_name)).unwrap();
3223                let intrinsic_id = core::LLVMLookupIntrinsicID(
3224                    intrinsic_name.as_ptr(),
3225                    intrinsic_name.to_bytes().len(),
3226                );
3227                let mut param_types = [ty];
3228                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3229                    module,
3230                    intrinsic_id,
3231                    param_types.as_mut_ptr(),
3232                    param_types.len(),
3233                );
3234                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3235                let mut args = [a_val, b_val, c_val];
3236                let value = core::LLVMBuildCall2(
3237                    ctx.builder,
3238                    fn_ty,
3239                    intrinsic_fn,
3240                    args.as_mut_ptr(),
3241                    args.len() as u32,
3242                    c"".as_ptr(),
3243                );
3244                Ok(value)
3245            }
3246
3247            // Bit manipulation intrinsics
3248            Intrinsic::Ctpop { value } => {
3249                lower_unary_int_intrinsic(ctx, module, "llvm.ctpop", value)
3250            }
3251            Intrinsic::Ctlz {
3252                value,
3253                is_zero_poison,
3254            } => {
3255                let val = lower_operand(ctx, value);
3256                let ty = lower_type(ctx.ctx, ctx.storage, value.get_type());
3257                let intrinsic_name =
3258                    CString::new(format!("llvm.ctlz.i{}", core::LLVMGetIntTypeWidth(ty))).unwrap();
3259                let intrinsic_id = core::LLVMLookupIntrinsicID(
3260                    intrinsic_name.as_ptr(),
3261                    intrinsic_name.to_bytes().len(),
3262                );
3263                let mut param_types = [ty];
3264                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3265                    module,
3266                    intrinsic_id,
3267                    param_types.as_mut_ptr(),
3268                    param_types.len(),
3269                );
3270                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3271                let poison_val = core::LLVMConstInt(
3272                    core::LLVMInt1TypeInContext(ctx.ctx),
3273                    *is_zero_poison as u64,
3274                    0,
3275                );
3276                let mut args = [val, poison_val];
3277                let result = core::LLVMBuildCall2(
3278                    ctx.builder,
3279                    fn_ty,
3280                    intrinsic_fn,
3281                    args.as_mut_ptr(),
3282                    args.len() as u32,
3283                    c"".as_ptr(),
3284                );
3285                Ok(result)
3286            }
3287            Intrinsic::Cttz {
3288                value,
3289                is_zero_poison,
3290            } => {
3291                let val = lower_operand(ctx, value);
3292                let ty = lower_type(ctx.ctx, ctx.storage, value.get_type());
3293                let intrinsic_name =
3294                    CString::new(format!("llvm.cttz.i{}", core::LLVMGetIntTypeWidth(ty))).unwrap();
3295                let intrinsic_id = core::LLVMLookupIntrinsicID(
3296                    intrinsic_name.as_ptr(),
3297                    intrinsic_name.to_bytes().len(),
3298                );
3299                let mut param_types = [ty];
3300                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3301                    module,
3302                    intrinsic_id,
3303                    param_types.as_mut_ptr(),
3304                    param_types.len(),
3305                );
3306                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3307                let poison_val = core::LLVMConstInt(
3308                    core::LLVMInt1TypeInContext(ctx.ctx),
3309                    *is_zero_poison as u64,
3310                    0,
3311                );
3312                let mut args = [val, poison_val];
3313                let result = core::LLVMBuildCall2(
3314                    ctx.builder,
3315                    fn_ty,
3316                    intrinsic_fn,
3317                    args.as_mut_ptr(),
3318                    args.len() as u32,
3319                    c"".as_ptr(),
3320                );
3321                Ok(result)
3322            }
3323            Intrinsic::Bitreverse { value } => {
3324                lower_unary_int_intrinsic(ctx, module, "llvm.bitreverse", value)
3325            }
3326            Intrinsic::Bswap { value } => {
3327                lower_unary_int_intrinsic(ctx, module, "llvm.bswap", value)
3328            }
3329            Intrinsic::Fshl { a, b, shift } => {
3330                let a_val = lower_operand(ctx, a);
3331                let b_val = lower_operand(ctx, b);
3332                let shift_val = lower_operand(ctx, shift);
3333                let ty = lower_type(ctx.ctx, ctx.storage, a.get_type());
3334                let intrinsic_name =
3335                    CString::new(format!("llvm.fshl.i{}", core::LLVMGetIntTypeWidth(ty))).unwrap();
3336                let intrinsic_id = core::LLVMLookupIntrinsicID(
3337                    intrinsic_name.as_ptr(),
3338                    intrinsic_name.to_bytes().len(),
3339                );
3340                let mut param_types = [ty];
3341                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3342                    module,
3343                    intrinsic_id,
3344                    param_types.as_mut_ptr(),
3345                    param_types.len(),
3346                );
3347                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3348                let mut args = [a_val, b_val, shift_val];
3349                let result = core::LLVMBuildCall2(
3350                    ctx.builder,
3351                    fn_ty,
3352                    intrinsic_fn,
3353                    args.as_mut_ptr(),
3354                    args.len() as u32,
3355                    c"".as_ptr(),
3356                );
3357                Ok(result)
3358            }
3359            Intrinsic::Fshr { a, b, shift } => {
3360                let a_val = lower_operand(ctx, a);
3361                let b_val = lower_operand(ctx, b);
3362                let shift_val = lower_operand(ctx, shift);
3363                let ty = lower_type(ctx.ctx, ctx.storage, a.get_type());
3364                let intrinsic_name =
3365                    CString::new(format!("llvm.fshr.i{}", core::LLVMGetIntTypeWidth(ty))).unwrap();
3366                let intrinsic_id = core::LLVMLookupIntrinsicID(
3367                    intrinsic_name.as_ptr(),
3368                    intrinsic_name.to_bytes().len(),
3369                );
3370                let mut param_types = [ty];
3371                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3372                    module,
3373                    intrinsic_id,
3374                    param_types.as_mut_ptr(),
3375                    param_types.len(),
3376                );
3377                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3378                let mut args = [a_val, b_val, shift_val];
3379                let result = core::LLVMBuildCall2(
3380                    ctx.builder,
3381                    fn_ty,
3382                    intrinsic_fn,
3383                    args.as_mut_ptr(),
3384                    args.len() as u32,
3385                    c"".as_ptr(),
3386                );
3387                Ok(result)
3388            }
3389
3390            // Other intrinsics
3391            Intrinsic::Expect { value, expected } => {
3392                let val = lower_operand(ctx, value);
3393                let exp = lower_operand(ctx, expected);
3394                let ty = lower_type(ctx.ctx, ctx.storage, value.get_type());
3395                let intrinsic_name =
3396                    CString::new(format!("llvm.expect.i{}", core::LLVMGetIntTypeWidth(ty)))
3397                        .unwrap();
3398                let intrinsic_id = core::LLVMLookupIntrinsicID(
3399                    intrinsic_name.as_ptr(),
3400                    intrinsic_name.to_bytes().len(),
3401                );
3402                let mut param_types = [ty];
3403                let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3404                    module,
3405                    intrinsic_id,
3406                    param_types.as_mut_ptr(),
3407                    param_types.len(),
3408                );
3409                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3410                let mut args = [val, exp];
3411                let result = core::LLVMBuildCall2(
3412                    ctx.builder,
3413                    fn_ty,
3414                    intrinsic_fn,
3415                    args.as_mut_ptr(),
3416                    args.len() as u32,
3417                    c"".as_ptr(),
3418                );
3419                Ok(result)
3420            }
3421            Intrinsic::Assume { cond } => {
3422                let cond_val = lower_operand(ctx, cond);
3423                let intrinsic_name = c"llvm.assume";
3424                let intrinsic_id = core::LLVMLookupIntrinsicID(
3425                    intrinsic_name.as_ptr(),
3426                    intrinsic_name.count_bytes(),
3427                );
3428                let intrinsic_fn =
3429                    core::LLVMGetIntrinsicDeclaration(module, intrinsic_id, null_mut(), 0);
3430                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3431                let mut args = [cond_val];
3432                core::LLVMBuildCall2(
3433                    ctx.builder,
3434                    fn_ty,
3435                    intrinsic_fn,
3436                    args.as_mut_ptr(),
3437                    args.len() as u32,
3438                    c"".as_ptr(),
3439                );
3440                Ok(null_mut())
3441            }
3442            Intrinsic::Trap => {
3443                let intrinsic_name = c"llvm.trap";
3444                let intrinsic_id = core::LLVMLookupIntrinsicID(
3445                    intrinsic_name.as_ptr(),
3446                    intrinsic_name.count_bytes(),
3447                );
3448                let intrinsic_fn =
3449                    core::LLVMGetIntrinsicDeclaration(module, intrinsic_id, null_mut(), 0);
3450                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3451                core::LLVMBuildCall2(
3452                    ctx.builder,
3453                    fn_ty,
3454                    intrinsic_fn,
3455                    null_mut(),
3456                    0,
3457                    c"".as_ptr(),
3458                );
3459                Ok(null_mut())
3460            }
3461            Intrinsic::Debugtrap => {
3462                let intrinsic_name = c"llvm.debugtrap";
3463                let intrinsic_id = core::LLVMLookupIntrinsicID(
3464                    intrinsic_name.as_ptr(),
3465                    intrinsic_name.count_bytes(),
3466                );
3467                let intrinsic_fn =
3468                    core::LLVMGetIntrinsicDeclaration(module, intrinsic_id, null_mut(), 0);
3469                let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3470                core::LLVMBuildCall2(
3471                    ctx.builder,
3472                    fn_ty,
3473                    intrinsic_fn,
3474                    null_mut(),
3475                    0,
3476                    c"".as_ptr(),
3477                );
3478                Ok(null_mut())
3479            }
3480        }
3481    } // end unsafe block
3482}
3483
3484/// Helper to lower unary float intrinsics.
3485unsafe fn lower_unary_float_intrinsic(
3486    ctx: &FnCtx,
3487    module: *mut LLVMModule,
3488    name: &str,
3489    value: &Operand,
3490) -> Result<LLVMValueRef, Error> {
3491    unsafe {
3492        let val = lower_operand(ctx, value);
3493        let ty = lower_type(ctx.ctx, ctx.storage, value.get_type());
3494        let ty_name = get_float_type_suffix(ty);
3495        let intrinsic_name = CString::new(format!("{}.{}", name, ty_name)).unwrap();
3496        let intrinsic_id =
3497            core::LLVMLookupIntrinsicID(intrinsic_name.as_ptr(), intrinsic_name.to_bytes().len());
3498        let mut param_types = [ty];
3499        let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3500            module,
3501            intrinsic_id,
3502            param_types.as_mut_ptr(),
3503            param_types.len(),
3504        );
3505        let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3506        let mut args = [val];
3507        let result = core::LLVMBuildCall2(
3508            ctx.builder,
3509            fn_ty,
3510            intrinsic_fn,
3511            args.as_mut_ptr(),
3512            args.len() as u32,
3513            c"".as_ptr(),
3514        );
3515        Ok(result)
3516    } // end unsafe block
3517}
3518
3519/// Helper to lower binary float intrinsics.
3520unsafe fn lower_binary_float_intrinsic(
3521    ctx: &FnCtx,
3522    module: *mut LLVMModule,
3523    name: &str,
3524    a: &Operand,
3525    b: &Operand,
3526) -> Result<LLVMValueRef, Error> {
3527    unsafe {
3528        let a_val = lower_operand(ctx, a);
3529        let b_val = lower_operand(ctx, b);
3530        let ty = lower_type(ctx.ctx, ctx.storage, a.get_type());
3531        let ty_name = get_float_type_suffix(ty);
3532        let intrinsic_name = CString::new(format!("{}.{}", name, ty_name)).unwrap();
3533        let intrinsic_id =
3534            core::LLVMLookupIntrinsicID(intrinsic_name.as_ptr(), intrinsic_name.to_bytes().len());
3535        let mut param_types = [ty];
3536        let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3537            module,
3538            intrinsic_id,
3539            param_types.as_mut_ptr(),
3540            param_types.len(),
3541        );
3542        let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3543        let mut args = [a_val, b_val];
3544        let result = core::LLVMBuildCall2(
3545            ctx.builder,
3546            fn_ty,
3547            intrinsic_fn,
3548            args.as_mut_ptr(),
3549            args.len() as u32,
3550            c"".as_ptr(),
3551        );
3552        Ok(result)
3553    } // end unsafe block
3554}
3555
3556/// Helper to lower unary integer intrinsics.
3557unsafe fn lower_unary_int_intrinsic(
3558    ctx: &FnCtx,
3559    module: *mut LLVMModule,
3560    name: &str,
3561    value: &Operand,
3562) -> Result<LLVMValueRef, Error> {
3563    unsafe {
3564        let val = lower_operand(ctx, value);
3565        let ty = lower_type(ctx.ctx, ctx.storage, value.get_type());
3566        let intrinsic_name =
3567            CString::new(format!("{}.i{}", name, core::LLVMGetIntTypeWidth(ty))).unwrap();
3568        let intrinsic_id =
3569            core::LLVMLookupIntrinsicID(intrinsic_name.as_ptr(), intrinsic_name.to_bytes().len());
3570        let mut param_types = [ty];
3571        let intrinsic_fn = core::LLVMGetIntrinsicDeclaration(
3572            module,
3573            intrinsic_id,
3574            param_types.as_mut_ptr(),
3575            param_types.len(),
3576        );
3577        let fn_ty = core::LLVMGlobalGetValueType(intrinsic_fn);
3578        let mut args = [val];
3579        let result = core::LLVMBuildCall2(
3580            ctx.builder,
3581            fn_ty,
3582            intrinsic_fn,
3583            args.as_mut_ptr(),
3584            args.len() as u32,
3585            c"".as_ptr(),
3586        );
3587        Ok(result)
3588    } // end unsafe block
3589}
3590
3591/// Get the LLVM type suffix for float types.
3592unsafe fn get_float_type_suffix(ty: LLVMTypeRef) -> &'static str {
3593    unsafe {
3594        match core::LLVMGetTypeKind(ty) {
3595            llvm_sys::LLVMTypeKind::LLVMHalfTypeKind => "f16",
3596            llvm_sys::LLVMTypeKind::LLVMBFloatTypeKind => "bf16",
3597            llvm_sys::LLVMTypeKind::LLVMFloatTypeKind => "f32",
3598            llvm_sys::LLVMTypeKind::LLVMDoubleTypeKind => "f64",
3599            llvm_sys::LLVMTypeKind::LLVMFP128TypeKind => "f128",
3600            _ => "f64", // default to f64
3601        }
3602    } // end unsafe block
3603}