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_unsigned, DW_TAG_reference_type};
37use irvm::{
38    block::{BlockIdx, DebugOp, DebugVariable, Instruction},
39    common::Location,
40    datalayout::DataLayout,
41    function::Function,
42    module::Module,
43    target_lexicon::Triple,
44    types::{Type, TypeIdx, TypeStorage},
45    value::{ConstValue, Operand},
46};
47
48use itertools::Itertools;
49use llvm_sys::{
50    LLVMIntPredicate, LLVMModule, LLVMOpaqueMetadata, LLVMRealPredicate,
51    core::{self, LLVMDisposeMessage, LLVMDumpModule},
52    debuginfo::{self, LLVMDIFlagPublic, LLVMDWARFEmissionKind},
53    error::LLVMGetErrorMessage,
54    execution_engine::{self, LLVMExecutionEngineRef, LLVMLinkInMCJIT},
55    prelude::{
56        LLVMBasicBlockRef, LLVMBuilderRef, LLVMContextRef, LLVMDIBuilderRef, LLVMMetadataRef,
57        LLVMTypeRef, LLVMValueRef,
58    },
59    target::{
60        LLVM_InitializeAllAsmPrinters, LLVM_InitializeAllTargetInfos, LLVM_InitializeAllTargetMCs,
61        LLVM_InitializeAllTargets, LLVM_InitializeNativeAsmParser, LLVM_InitializeNativeAsmPrinter,
62        LLVM_InitializeNativeDisassembler, LLVM_InitializeNativeTarget,
63    },
64    target_machine::{
65        self, LLVMCodeGenFileType, LLVMCodeGenOptLevel, LLVMCodeModel, LLVMDisposeTargetMachine,
66        LLVMGetHostCPUFeatures, LLVMGetHostCPUName, LLVMRelocMode, LLVMTargetMachineEmitToFile,
67    },
68    transforms::pass_builder::{
69        LLVMCreatePassBuilderOptions, LLVMDisposePassBuilderOptions, LLVMRunPasses,
70    },
71};
72
73#[derive(Debug)]
74pub enum OutputCompilation {
75    File(PathBuf),
76    Engine(*mut LLVMExecutionEngineRef),
77}
78
79/// A possible Error.
80#[derive(Debug, thiserror::Error, Clone)]
81pub enum Error {
82    #[error("llvm error: {:?}", 0)]
83    LLVMError(String),
84    #[error("jit error: {:?}", 0)]
85    JitError(String),
86    #[error(transparent)]
87    NulError(#[from] std::ffi::NulError),
88    #[error("irvm error: {:?}", 0)]
89    IRVMError(#[from] irvm::error::Error),
90}
91
92/// The target LLVM cpu.
93#[derive(Debug, Clone, Default)]
94pub enum TargetCpu {
95    #[default]
96    Host,
97    Name(String),
98}
99
100/// The target LLVM cpu features.
101#[derive(Debug, Clone, Default)]
102pub enum TargetCpuFeatures {
103    #[default]
104    Host,
105    Features(String),
106}
107
108/// The optimization level to use.
109#[derive(Debug, Clone, Default)]
110pub enum OptLevel {
111    None,
112    Less,
113    #[default]
114    Default,
115    Aggressive,
116}
117
118/// The relocation model types supported by LLVM
119#[derive(Debug, Clone, Default)]
120pub enum RelocModel {
121    /// Generated code will assume the default for a particular target architecture.
122    #[default]
123    Default,
124    /// Generated code will exist at static offsets.
125    Static,
126    /// Generated code will be position-independent.
127    Pic,
128    /// Generated code will not be position-independent and may be used in static or dynamic executables but not necessarily a shared library.
129    DynamicNoPic,
130    /// Generated code will be compiled in read-only position independent mode.
131    /// In this mode, all read-only data and functions are at a link-time constant offset from the program counter.
132    /// ROPI is not supported by all target architectures and calling conventions. It is a particular feature of ARM targets, though.
133    /// ROPI may be useful to avoid committing to compile-time constant locations for code in memory.
134    Ropi,
135    /// Generated code will be compiled in read-write position independent mode.
136    ///
137    /// In this mode, all writable data is at a link-time constant offset from the static base register.
138    ///
139    /// RWPI is not supported by all target architectures and calling conventions. It is a particular feature of ARM targets, though.
140    ///
141    /// RWPI may be useful to avoid committing to compile-time constant locations for code in memory
142    Rwpi,
143    /// Combines the ropi and rwpi modes. Generated code will be compiled in both read-only and read-write position independent modes.
144    /// All read-only data appears at a link-time constant offset from the program counter,
145    /// and all writable data appears at a link-time constant offset from the static base register.
146    RopiRwpi,
147}
148
149/// The code model supported by LLVM.
150#[derive(Debug, Clone, Default)]
151pub enum CodeModel {
152    #[default]
153    Default,
154    JitDefault,
155    Tiny,
156    Small,
157    Kernel,
158    Medium,
159    Large,
160}
161
162/// Compile options to generate the object file.
163#[derive(Debug, Clone, Default)]
164pub struct CompileOptions {
165    pub target_cpu: TargetCpu,
166    pub target_cpu_features: TargetCpuFeatures,
167    pub relocation_model: RelocModel,
168    pub code_model: CodeModel,
169    pub opt_level: u8,
170}
171
172/// A compile result from lowering a given Module.
173#[derive(Debug)]
174pub struct CompileResult {
175    context: LLVMContextRef,
176    module: *mut LLVMModule,
177}
178
179/// A prepared JIT engine.
180#[derive(Debug)]
181pub struct JitEngine {
182    context: LLVMContextRef,
183    engine: LLVMExecutionEngineRef,
184}
185
186impl Drop for CompileResult {
187    fn drop(&mut self) {
188        unsafe {
189            core::LLVMDisposeModule(self.module);
190            core::LLVMContextDispose(self.context);
191        }
192    }
193}
194
195impl Drop for JitEngine {
196    fn drop(&mut self) {
197        unsafe {
198            execution_engine::LLVMDisposeExecutionEngine(self.engine);
199            core::LLVMContextDispose(self.context);
200        }
201    }
202}
203
204/// Possible value/types to pass to the JIT engine execute method.
205#[derive(Debug, Clone, Copy, PartialEq)]
206pub enum JitValue {
207    U8(u8),
208    U16(u16),
209    U32(u32),
210    U64(u64),
211    I8(i8),
212    I16(i16),
213    I32(i32),
214    I64(i64),
215    F32(f32),
216    F64(f64),
217    Ptr(*mut c_void),
218    Void,
219}
220
221impl JitEngine {
222    /// Execute the given function.
223    ///
224    /// # Safety
225    ///
226    /// All arguments and return type must match the signature.
227    pub unsafe fn execute(
228        &self,
229        symbol: &str,
230        args: &[JitValue],
231        return_ty: JitValue,
232    ) -> Result<JitValue, Error> {
233        unsafe {
234            let sym = CString::new(symbol)?;
235            let mut out_fn = null_mut();
236            let ok = execution_engine::LLVMFindFunction(self.engine, sym.as_ptr(), &raw mut out_fn);
237
238            if ok != 0 {
239                return Err(Error::LLVMError("Function not found".to_string()));
240            }
241
242            let mut value_args = Vec::new();
243
244            for arg in args {
245                let value = match arg {
246                    JitValue::U8(value) => execution_engine::LLVMCreateGenericValueOfInt(
247                        core::LLVMInt8Type(),
248                        (*value) as _,
249                        0,
250                    ),
251                    JitValue::U16(value) => execution_engine::LLVMCreateGenericValueOfInt(
252                        core::LLVMInt16Type(),
253                        (*value) as _,
254                        0,
255                    ),
256                    JitValue::U32(value) => execution_engine::LLVMCreateGenericValueOfInt(
257                        core::LLVMInt32Type(),
258                        (*value) as _,
259                        0,
260                    ),
261                    JitValue::U64(value) => execution_engine::LLVMCreateGenericValueOfInt(
262                        core::LLVMInt64Type(),
263                        (*value) as _,
264                        0,
265                    ),
266                    JitValue::I8(value) => execution_engine::LLVMCreateGenericValueOfInt(
267                        core::LLVMInt8Type(),
268                        (*value) as _,
269                        1,
270                    ),
271                    JitValue::I16(value) => execution_engine::LLVMCreateGenericValueOfInt(
272                        core::LLVMInt16Type(),
273                        (*value) as _,
274                        1,
275                    ),
276                    JitValue::I32(value) => execution_engine::LLVMCreateGenericValueOfInt(
277                        core::LLVMInt32Type(),
278                        (*value) as _,
279                        1,
280                    ),
281                    JitValue::I64(value) => execution_engine::LLVMCreateGenericValueOfInt(
282                        core::LLVMInt64Type(),
283                        (*value) as _,
284                        1,
285                    ),
286                    JitValue::F32(value) => execution_engine::LLVMCreateGenericValueOfFloat(
287                        core::LLVMFloatType(),
288                        (*value) as _,
289                    ),
290                    JitValue::F64(value) => execution_engine::LLVMCreateGenericValueOfFloat(
291                        core::LLVMDoubleType(),
292                        (*value) as _,
293                    ),
294                    JitValue::Ptr(value) => {
295                        execution_engine::LLVMCreateGenericValueOfPointer(*value)
296                    }
297                    JitValue::Void => {
298                        return Err(Error::JitError(
299                            "can't use void jit value as argument".to_string(),
300                        ));
301                    }
302                };
303                value_args.push(value);
304            }
305
306            let result = execution_engine::LLVMRunFunction(
307                self.engine,
308                out_fn,
309                value_args.len() as _,
310                value_args.as_mut_ptr(),
311            );
312
313            let res = match return_ty {
314                JitValue::U8(_) => {
315                    JitValue::U8(execution_engine::LLVMGenericValueToInt(result, 0) as u8)
316                }
317                JitValue::U16(_) => {
318                    JitValue::U16(execution_engine::LLVMGenericValueToInt(result, 0) as u16)
319                }
320                JitValue::U32(_) => {
321                    JitValue::U32(execution_engine::LLVMGenericValueToInt(result, 0) as u32)
322                }
323                JitValue::U64(_) => {
324                    JitValue::U64(execution_engine::LLVMGenericValueToInt(result, 0) as u64)
325                }
326                JitValue::I8(_) => {
327                    JitValue::I8(execution_engine::LLVMGenericValueToInt(result, 1) as i8)
328                }
329                JitValue::I16(_) => {
330                    JitValue::I16(execution_engine::LLVMGenericValueToInt(result, 1) as i16)
331                }
332                JitValue::I32(_) => {
333                    JitValue::I32(execution_engine::LLVMGenericValueToInt(result, 1) as i32)
334                }
335                JitValue::I64(_) => {
336                    JitValue::I64(execution_engine::LLVMGenericValueToInt(result, 1) as i64)
337                }
338                JitValue::F32(_) => JitValue::F32(execution_engine::LLVMGenericValueToFloat(
339                    core::LLVMFloatType(),
340                    result,
341                ) as f32),
342                JitValue::F64(_) => JitValue::F64(execution_engine::LLVMGenericValueToFloat(
343                    core::LLVMDoubleType(),
344                    result,
345                ) as f64),
346                JitValue::Ptr(_) => {
347                    JitValue::Ptr(execution_engine::LLVMGenericValueToPointer(result))
348                }
349                JitValue::Void => JitValue::Void,
350            };
351
352            for arg in &value_args {
353                execution_engine::LLVMDisposeGenericValue(*arg);
354            }
355            execution_engine::LLVMDisposeGenericValue(result);
356
357            Ok(res)
358        }
359    }
360}
361
362impl CompileResult {
363    pub fn dump(&self) {
364        unsafe {
365            LLVMDumpModule(self.module);
366        }
367    }
368}
369
370/// Lowers the given module to llvm ir.
371pub fn lower_module_to_llvmir(
372    module: &Module,
373    storage: &TypeStorage,
374) -> Result<CompileResult, Error> {
375    unsafe {
376        let ctx = core::LLVMContextCreate();
377        let module_name = CString::new(module.name.clone())?;
378        let llvm_module = core::LLVMModuleCreateWithNameInContext(module_name.as_ptr(), ctx);
379
380        let datalayout_str = CString::new(module.data_layout.to_llvm_string()).unwrap();
381        core::LLVMSetDataLayout(llvm_module, datalayout_str.as_ptr());
382        let triple_str = CString::new(module.target_triple.to_string()).unwrap();
383        core::LLVMSetTarget(llvm_module, triple_str.as_ptr());
384
385        let mut functions: HashMap<_, _> = Default::default();
386        let mut dfunctions: HashMap<_, _> = Default::default();
387        // let mut debug_functions: HashMap<_, _> = Default::default();
388        let builder = core::LLVMCreateBuilderInContext(ctx);
389        let dibuilder = debuginfo::LLVMCreateDIBuilder(llvm_module);
390
391        let compile_unit_file = get_difile_location(dibuilder, &module.location);
392
393        let producer = c"IRVM version 0.1.0";
394        let flags = c"";
395        let splitname = c"";
396        let sysroot = c"";
397        let sdk = c"";
398
399        let compile_unit = debuginfo::LLVMDIBuilderCreateCompileUnit(
400            dibuilder,
401            debuginfo::LLVMDWARFSourceLanguage::LLVMDWARFSourceLanguageC17,
402            compile_unit_file,
403            producer.as_ptr(),
404            producer.count_bytes(),
405            0,
406            flags.as_ptr(),
407            flags.count_bytes(),
408            0,
409            splitname.as_ptr(),
410            splitname.count_bytes(),
411            LLVMDWARFEmissionKind::LLVMDWARFEmissionKindFull,
412            0,
413            0,
414            0,
415            sysroot.as_ptr(),
416            sysroot.count_bytes(),
417            sdk.as_ptr(),
418            sdk.count_bytes(),
419        );
420
421        let debug_module = debuginfo::LLVMDIBuilderCreateModule(
422            dibuilder,
423            compile_unit,
424            module_name.as_ptr(),
425            module.name.len(),
426            c"".as_ptr(),
427            0,
428            c"".as_ptr(),
429            0,
430            c"".as_ptr(),
431            0,
432        );
433
434        for (fun_idx, func) in module.functions().iter() {
435            let name = CString::new(func.name.as_str()).unwrap();
436
437            let ret_ty = if let Some(ret_ty) = func.result_type {
438                lower_type(ctx, storage, ret_ty)
439            } else {
440                core::LLVMVoidTypeInContext(ctx)
441            };
442            let mut params = func
443                .parameters
444                .iter()
445                .map(|x| lower_type(ctx, storage, x.ty))
446                .collect_vec();
447            let fn_ty = core::LLVMFunctionType(ret_ty, params.as_mut_ptr(), params.len() as u32, 0);
448            let fn_ptr = core::LLVMAddFunction(llvm_module, name.as_ptr(), fn_ty);
449            functions.insert(fun_idx.to_idx(), (fn_ptr, fn_ty));
450
451            let mut file = compile_unit_file;
452
453            let mut line = 0;
454            match &func.location {
455                Location::Unknown => {}
456                Location::File(file_location) => {
457                    file = get_difile(dibuilder, &file_location.file);
458                    line = file_location.line;
459                }
460            }
461
462            let mut debug_param_types = Vec::new();
463
464            for param in func.parameters.iter() {
465                let ptr = lower_debug_type(
466                    &module.data_layout,
467                    dibuilder,
468                    storage,
469                    debug_module,
470                    param.ty,
471                );
472                debug_param_types.push(ptr);
473            }
474
475            let debug_func_ty = debuginfo::LLVMDIBuilderCreateSubroutineType(
476                dibuilder,
477                file,
478                debug_param_types.as_mut_ptr(),
479                debug_param_types.len() as u32,
480                0,
481            );
482
483            let di_func = debuginfo::LLVMDIBuilderCreateFunction(
484                dibuilder,
485                debug_module,
486                name.as_ptr(),
487                name.count_bytes(),
488                name.as_ptr(),
489                name.count_bytes(),
490                file,
491                line,
492                debug_func_ty,
493                0,
494                1,
495                line,
496                0,
497                0,
498            );
499            dfunctions.insert(fun_idx.to_idx(), di_func);
500            debuginfo::LLVMSetSubprogram(fn_ptr, di_func);
501        }
502
503        let functions = Rc::new(functions);
504
505        for (fun_idx, func) in module.functions().iter() {
506            let fn_ptr = functions.get(&fun_idx.to_idx()).unwrap().0;
507            let dfunc = *dfunctions.get(&fun_idx.to_idx()).unwrap();
508
509            let mut fn_ctx = FnCtx {
510                ctx,
511                fn_ptr,
512                func: func.clone(),
513                builder,
514                dibuilder,
515                storage,
516                blocks: Default::default(),
517                values: Default::default(),
518                block_args: Default::default(),
519                functions: Rc::clone(&functions),
520                debug_scope: dfunc,
521                datalayout: &module.data_layout,
522            };
523
524            for (id, _) in func.blocks.iter() {
525                add_block(&mut fn_ctx, id, None);
526            }
527
528            for (id, _) in func.blocks.iter() {
529                lower_block(&mut fn_ctx, id)?;
530            }
531
532            debuginfo::LLVMDIBuilderFinalizeSubprogram(dibuilder, dfunc);
533        }
534
535        debuginfo::LLVMDIBuilderFinalize(dibuilder);
536        debuginfo::LLVMDisposeDIBuilder(dibuilder);
537        core::LLVMDisposeBuilder(builder);
538
539        core::LLVMDumpModule(llvm_module);
540
541        let mut out_msg: *mut i8 = null_mut();
542        let ok = llvm_sys::analysis::LLVMVerifyModule(
543            llvm_module,
544            llvm_sys::analysis::LLVMVerifierFailureAction::LLVMReturnStatusAction,
545            &raw mut out_msg,
546        );
547
548        if ok != 0 {
549            let msg = {
550                let msg = CStr::from_ptr(out_msg);
551                msg.to_string_lossy().to_string()
552            };
553
554            if !out_msg.is_null() {
555                core::LLVMDisposeMessage(out_msg);
556            }
557
558            core::LLVMDisposeModule(llvm_module);
559            core::LLVMContextDispose(ctx);
560
561            return Err(Error::LLVMError(msg));
562        }
563
564        if !out_msg.is_null() {
565            core::LLVMDisposeMessage(out_msg);
566        }
567
568        Ok(CompileResult {
569            context: ctx,
570            module: llvm_module,
571        })
572    }
573}
574
575/// Compiles the given llvm compile result to an object or assembly file.
576///
577/// If output assembly is false it will output an object file.
578pub fn compile_object(
579    compile_result: &CompileResult,
580    target_triple: Triple,
581    options: CompileOptions,
582    output_file: &Path,
583    output_assembly: bool,
584) -> Result<(), Error> {
585    unsafe {
586        static INITIALIZED: OnceLock<()> = OnceLock::new();
587        INITIALIZED.get_or_init(|| {
588            LLVM_InitializeAllTargets();
589            LLVM_InitializeAllTargetInfos();
590            LLVM_InitializeAllTargetMCs();
591            LLVM_InitializeAllAsmPrinters();
592        });
593
594        let target_triple = CString::new(target_triple.to_string())?;
595
596        let target_cpu = match &options.target_cpu {
597            TargetCpu::Host => {
598                let cpu = LLVMGetHostCPUName();
599                CString::from(CStr::from_ptr(cpu))
600            }
601            TargetCpu::Name(name) => CString::new(name.as_bytes())?,
602        };
603
604        let target_cpu_features = match &options.target_cpu_features {
605            TargetCpuFeatures::Host => {
606                let cpu = LLVMGetHostCPUFeatures();
607                CString::from(CStr::from_ptr(cpu))
608            }
609            TargetCpuFeatures::Features(name) => CString::new(name.as_bytes())?,
610        };
611
612        let mut out_msg = null_mut();
613
614        let mut target = null_mut();
615
616        let ok = target_machine::LLVMGetTargetFromTriple(
617            target_triple.as_ptr(),
618            &raw mut target,
619            &raw mut out_msg,
620        );
621
622        if ok != 0 {
623            let msg = {
624                let msg = CStr::from_ptr(out_msg);
625                msg.to_string_lossy().to_string()
626            };
627
628            if !out_msg.is_null() {
629                core::LLVMDisposeMessage(out_msg);
630            }
631
632            return Err(Error::LLVMError(msg));
633        }
634
635        if !out_msg.is_null() {
636            core::LLVMDisposeMessage(out_msg);
637        }
638
639        let machine = target_machine::LLVMCreateTargetMachine(
640            target,
641            target_triple.as_ptr(),
642            target_cpu.as_ptr(),
643            target_cpu_features.as_ptr(),
644            match options.opt_level {
645                0 => LLVMCodeGenOptLevel::LLVMCodeGenLevelNone,
646                1 => LLVMCodeGenOptLevel::LLVMCodeGenLevelLess,
647                2 => LLVMCodeGenOptLevel::LLVMCodeGenLevelDefault,
648                _ => LLVMCodeGenOptLevel::LLVMCodeGenLevelAggressive,
649            },
650            match options.relocation_model {
651                RelocModel::Default => LLVMRelocMode::LLVMRelocDefault,
652                RelocModel::Static => LLVMRelocMode::LLVMRelocStatic,
653                RelocModel::Pic => LLVMRelocMode::LLVMRelocPIC,
654                RelocModel::DynamicNoPic => LLVMRelocMode::LLVMRelocDynamicNoPic,
655                RelocModel::Ropi => LLVMRelocMode::LLVMRelocROPI,
656                RelocModel::Rwpi => LLVMRelocMode::LLVMRelocRWPI,
657                RelocModel::RopiRwpi => LLVMRelocMode::LLVMRelocROPI_RWPI,
658            },
659            match options.code_model {
660                CodeModel::Default => LLVMCodeModel::LLVMCodeModelDefault,
661                CodeModel::JitDefault => LLVMCodeModel::LLVMCodeModelJITDefault,
662                CodeModel::Tiny => LLVMCodeModel::LLVMCodeModelTiny,
663                CodeModel::Small => LLVMCodeModel::LLVMCodeModelSmall,
664                CodeModel::Kernel => LLVMCodeModel::LLVMCodeModelKernel,
665                CodeModel::Medium => LLVMCodeModel::LLVMCodeModelMedium,
666                CodeModel::Large => LLVMCodeModel::LLVMCodeModelLarge,
667            },
668        );
669
670        let opts = LLVMCreatePassBuilderOptions();
671
672        let passes = CString::new(format!("default<O{}>", options.opt_level)).unwrap();
673
674        let error = LLVMRunPasses(compile_result.module, passes.as_ptr(), machine, opts);
675
676        if !error.is_null() {
677            let msg_ptr = LLVMGetErrorMessage(error);
678            let msg = {
679                let msg = CStr::from_ptr(msg_ptr);
680                msg.to_string_lossy().to_string()
681            };
682            LLVMDisposeMessage(msg_ptr);
683            LLVMDisposeTargetMachine(machine);
684            return Err(Error::LLVMError(msg));
685        }
686
687        LLVMDisposePassBuilderOptions(opts);
688
689        let mut out_msg: *mut i8 = null_mut();
690        let ok = llvm_sys::analysis::LLVMVerifyModule(
691            compile_result.module,
692            llvm_sys::analysis::LLVMVerifierFailureAction::LLVMReturnStatusAction,
693            &raw mut out_msg,
694        );
695
696        if ok != 0 {
697            let msg = {
698                let msg = CStr::from_ptr(out_msg);
699                msg.to_string_lossy().to_string()
700            };
701
702            if !out_msg.is_null() {
703                core::LLVMDisposeMessage(out_msg);
704            }
705
706            LLVMDisposeTargetMachine(machine);
707
708            return Err(Error::LLVMError(msg));
709        }
710
711        let filename = CString::new(output_file.as_os_str().to_string_lossy().as_bytes()).unwrap();
712
713        let ok = LLVMTargetMachineEmitToFile(
714            machine,
715            compile_result.module,
716            filename.as_ptr().cast_mut(),
717            if output_assembly {
718                LLVMCodeGenFileType::LLVMAssemblyFile
719            } else {
720                LLVMCodeGenFileType::LLVMObjectFile
721            },
722            &raw mut out_msg,
723        );
724
725        LLVMDisposeTargetMachine(machine);
726
727        if ok != 0 {
728            let msg = {
729                let msg = CStr::from_ptr(out_msg);
730                msg.to_string_lossy().to_string()
731            };
732
733            if !out_msg.is_null() {
734                core::LLVMDisposeMessage(out_msg);
735            }
736
737            return Err(Error::LLVMError(msg));
738        }
739
740        Ok(())
741    }
742}
743
744/// Outputs the given compile result to a llvm ir file.
745pub fn output_to_file(compile_result: &CompileResult, output_ll: &Path) -> Result<(), Error> {
746    unsafe {
747        let file = CString::new(&*output_ll.to_string_lossy())?;
748
749        let mut out_msg: *mut i8 = null_mut();
750
751        let ok =
752            core::LLVMPrintModuleToFile(compile_result.module, file.as_ptr(), &raw mut out_msg);
753
754        if ok != 0 {
755            let msg = {
756                let msg = CStr::from_ptr(out_msg);
757                msg.to_string_lossy().to_string()
758            };
759
760            if !out_msg.is_null() {
761                core::LLVMDisposeMessage(out_msg);
762            }
763
764            return Err(Error::LLVMError(msg));
765        }
766
767        if !out_msg.is_null() {
768            core::LLVMDisposeMessage(out_msg);
769        }
770
771        Ok(())
772    }
773}
774
775/// Creates a jit engine for the given compile result.
776pub fn create_jit_engine(result: CompileResult, optlevel: u32) -> Result<JitEngine, Error> {
777    unsafe {
778        let mut engine = null_mut();
779
780        let mut out_msg: *mut i8 = null_mut();
781
782        let result = ManuallyDrop::new(result);
783
784        static INITIALIZED: OnceLock<()> = OnceLock::new();
785        INITIALIZED.get_or_init(|| {
786            LLVM_InitializeNativeTarget();
787            LLVM_InitializeNativeAsmParser();
788            LLVM_InitializeNativeAsmPrinter();
789            LLVM_InitializeNativeDisassembler();
790            LLVMLinkInMCJIT();
791        });
792
793        let ok = execution_engine::LLVMCreateJITCompilerForModule(
794            &raw mut engine,
795            result.module,
796            optlevel,
797            &raw mut out_msg,
798        );
799
800        if ok != 0 {
801            let msg = {
802                let msg = CStr::from_ptr(out_msg);
803                msg.to_string_lossy().to_string()
804            };
805
806            if !out_msg.is_null() {
807                core::LLVMDisposeMessage(out_msg);
808            }
809
810            return Err(Error::LLVMError(msg));
811        }
812
813        let engine = JitEngine {
814            context: result.context,
815            engine,
816        };
817
818        Ok(engine)
819    }
820}
821
822#[derive(Debug)]
823struct FnCtx<'m> {
824    ctx: LLVMContextRef,
825    fn_ptr: LLVMValueRef,
826    func: Function,
827    storage: &'m TypeStorage,
828    builder: LLVMBuilderRef,
829    dibuilder: LLVMDIBuilderRef,
830    debug_scope: LLVMMetadataRef,
831    functions: Rc<HashMap<usize, (LLVMValueRef, LLVMTypeRef)>>,
832    blocks: HashMap<usize, LLVMBasicBlockRef>,
833    // block, inst
834    values: HashMap<(usize, usize), LLVMValueRef>,
835    block_args: HashMap<usize, Vec<LLVMValueRef>>,
836    datalayout: &'m DataLayout,
837}
838
839// Returns the next block to lower.
840fn lower_block(ctx: &mut FnCtx, block_idx: BlockIdx) -> Result<(), Error> {
841    unsafe {
842        let null_name = c"";
843        let block_ptr = *ctx.blocks.get(&block_idx.to_idx()).unwrap();
844        core::LLVMPositionBuilderAtEnd(ctx.builder, block_ptr);
845        add_preds(ctx, block_idx);
846
847        for (inst_idx, (loc, inst)) in ctx.func.blocks[block_idx].instructions().iter() {
848            let loc = set_loc(ctx.ctx, ctx.builder, loc, ctx.debug_scope);
849
850            match inst {
851                Instruction::BinaryOp(binary_op) => match binary_op {
852                    irvm::block::BinaryOp::Add { lhs, rhs, nsw, nuw } => {
853                        let lhs_ptr = lower_operand(ctx, lhs);
854                        let rhs_ptr = lower_operand(ctx, rhs);
855                        let value =
856                            core::LLVMBuildAdd(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
857                        if *nuw {
858                            core::LLVMSetNUW(value, (*nuw) as i32);
859                        }
860                        if *nsw {
861                            core::LLVMSetNSW(value, (*nsw) as i32);
862                        }
863                        ctx.values
864                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
865                    }
866                    irvm::block::BinaryOp::Sub { lhs, rhs, nsw, nuw } => {
867                        let lhs_ptr = lower_operand(ctx, lhs);
868                        let rhs_ptr = lower_operand(ctx, rhs);
869                        let value =
870                            core::LLVMBuildSub(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
871                        if *nuw {
872                            core::LLVMSetNUW(value, (*nuw) as i32);
873                        }
874                        if *nsw {
875                            core::LLVMSetNSW(value, (*nsw) as i32);
876                        }
877                        ctx.values
878                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
879                    }
880                    irvm::block::BinaryOp::Mul { lhs, rhs, nsw, nuw } => {
881                        let lhs_ptr = lower_operand(ctx, lhs);
882                        let rhs_ptr = lower_operand(ctx, rhs);
883                        let value =
884                            core::LLVMBuildMul(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
885                        if *nuw {
886                            core::LLVMSetNUW(value, (*nuw) as i32);
887                        }
888                        if *nsw {
889                            core::LLVMSetNSW(value, (*nsw) as i32);
890                        }
891                        ctx.values
892                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
893                    }
894                    irvm::block::BinaryOp::Div {
895                        lhs,
896                        rhs,
897                        signed,
898                        exact,
899                    } => {
900                        let lhs_ptr = lower_operand(ctx, lhs);
901                        let rhs_ptr = lower_operand(ctx, rhs);
902                        let value = if *signed {
903                            if *exact {
904                                core::LLVMBuildExactSDiv(
905                                    ctx.builder,
906                                    lhs_ptr,
907                                    rhs_ptr,
908                                    null_name.as_ptr(),
909                                )
910                            } else {
911                                core::LLVMBuildSDiv(
912                                    ctx.builder,
913                                    lhs_ptr,
914                                    rhs_ptr,
915                                    null_name.as_ptr(),
916                                )
917                            }
918                        } else if *exact {
919                            core::LLVMBuildExactUDiv(
920                                ctx.builder,
921                                lhs_ptr,
922                                rhs_ptr,
923                                null_name.as_ptr(),
924                            )
925                        } else {
926                            core::LLVMBuildUDiv(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr())
927                        };
928                        ctx.values
929                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
930                    }
931                    irvm::block::BinaryOp::Rem { lhs, rhs, signed } => {
932                        let lhs_ptr = lower_operand(ctx, lhs);
933                        let rhs_ptr = lower_operand(ctx, rhs);
934                        let value = if *signed {
935                            core::LLVMBuildSRem(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr())
936                        } else {
937                            core::LLVMBuildURem(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr())
938                        };
939                        ctx.values
940                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
941                    }
942                    irvm::block::BinaryOp::FAdd { lhs, rhs } => {
943                        let lhs_ptr = lower_operand(ctx, lhs);
944                        let rhs_ptr = lower_operand(ctx, rhs);
945                        let value =
946                            core::LLVMBuildFAdd(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
947                        ctx.values
948                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
949                    }
950                    irvm::block::BinaryOp::FSub { lhs, rhs } => {
951                        let lhs_ptr = lower_operand(ctx, lhs);
952                        let rhs_ptr = lower_operand(ctx, rhs);
953                        let value =
954                            core::LLVMBuildFSub(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
955                        ctx.values
956                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
957                    }
958                    irvm::block::BinaryOp::FMul { lhs, rhs } => {
959                        let lhs_ptr = lower_operand(ctx, lhs);
960                        let rhs_ptr = lower_operand(ctx, rhs);
961                        let value =
962                            core::LLVMBuildFMul(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
963                        ctx.values
964                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
965                    }
966                    irvm::block::BinaryOp::FDiv { lhs, rhs } => {
967                        let lhs_ptr = lower_operand(ctx, lhs);
968                        let rhs_ptr = lower_operand(ctx, rhs);
969                        let value =
970                            core::LLVMBuildFDiv(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
971                        ctx.values
972                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
973                    }
974                    irvm::block::BinaryOp::FRem { lhs, rhs } => {
975                        let lhs_ptr = lower_operand(ctx, lhs);
976                        let rhs_ptr = lower_operand(ctx, rhs);
977                        let value =
978                            core::LLVMBuildFRem(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
979                        ctx.values
980                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
981                    }
982                },
983                Instruction::BitwiseBinaryOp(bitwise_binary_op) => match bitwise_binary_op {
984                    irvm::block::BitwiseBinaryOp::Shl { lhs, rhs } => {
985                        let lhs_ptr = lower_operand(ctx, lhs);
986                        let rhs_ptr = lower_operand(ctx, rhs);
987                        let value =
988                            core::LLVMBuildShl(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
989                        ctx.values
990                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
991                    }
992                    irvm::block::BitwiseBinaryOp::Lshr { lhs, rhs, exact } => {
993                        let lhs_ptr = lower_operand(ctx, lhs);
994                        let rhs_ptr = lower_operand(ctx, rhs);
995                        let value =
996                            core::LLVMBuildLShr(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
997                        core::LLVMSetExact(value, (*exact) as i32);
998                        ctx.values
999                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1000                    }
1001                    irvm::block::BitwiseBinaryOp::Ashr { lhs, rhs, exact } => {
1002                        let lhs_ptr = lower_operand(ctx, lhs);
1003                        let rhs_ptr = lower_operand(ctx, rhs);
1004                        let value =
1005                            core::LLVMBuildAShr(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1006                        core::LLVMSetExact(value, (*exact) as i32);
1007                        ctx.values
1008                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1009                    }
1010                    irvm::block::BitwiseBinaryOp::And { lhs, rhs } => {
1011                        let lhs_ptr = lower_operand(ctx, lhs);
1012                        let rhs_ptr = lower_operand(ctx, rhs);
1013                        let value =
1014                            core::LLVMBuildAnd(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1015                        ctx.values
1016                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1017                    }
1018                    irvm::block::BitwiseBinaryOp::Or { lhs, rhs, disjoint } => {
1019                        let lhs_ptr = lower_operand(ctx, lhs);
1020                        let rhs_ptr = lower_operand(ctx, rhs);
1021                        let value =
1022                            core::LLVMBuildOr(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1023                        core::LLVMSetIsDisjoint(value, (*disjoint) as i32);
1024                        ctx.values
1025                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1026                    }
1027                    irvm::block::BitwiseBinaryOp::Xor { lhs, rhs } => {
1028                        let lhs_ptr = lower_operand(ctx, lhs);
1029                        let rhs_ptr = lower_operand(ctx, rhs);
1030                        let value =
1031                            core::LLVMBuildXor(ctx.builder, lhs_ptr, rhs_ptr, null_name.as_ptr());
1032                        ctx.values
1033                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1034                    }
1035                },
1036                Instruction::VectorOp(vector_op) => match vector_op {
1037                    irvm::block::VectorOp::ExtractElement { vector, idx } => {
1038                        let vector = lower_operand(ctx, vector);
1039                        let idx = lower_operand(ctx, idx);
1040                        let value = core::LLVMBuildExtractElement(
1041                            ctx.builder,
1042                            vector,
1043                            idx,
1044                            null_name.as_ptr(),
1045                        );
1046                        ctx.values
1047                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1048                    }
1049                },
1050                Instruction::MemoryOp(memory_op) => match memory_op {
1051                    irvm::block::MemoryOp::Alloca {
1052                        ty, num_elements, ..
1053                    } => {
1054                        let ty_ptr = lower_type(ctx.ctx, ctx.storage, *ty);
1055                        let value = if *num_elements > 1 {
1056                            let const_val = core::LLVMConstInt(
1057                                core::LLVMInt64TypeInContext(ctx.ctx),
1058                                (*num_elements) as u64,
1059                                0,
1060                            );
1061                            core::LLVMBuildArrayAlloca(
1062                                ctx.builder,
1063                                ty_ptr,
1064                                const_val,
1065                                null_name.as_ptr(),
1066                            )
1067                        } else {
1068                            core::LLVMBuildAlloca(ctx.builder, ty_ptr, null_name.as_ptr())
1069                        };
1070                        ctx.values
1071                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1072                    }
1073                    irvm::block::MemoryOp::Load { ptr, align } => {
1074                        let ptr_val = lower_operand(ctx, ptr);
1075                        let ty_ptr =
1076                            lower_type(ctx.ctx, ctx.storage, ptr.get_inner_type(ctx.storage)?);
1077
1078                        let value =
1079                            core::LLVMBuildLoad2(ctx.builder, ty_ptr, ptr_val, null_name.as_ptr());
1080                        if let Some(align) = align {
1081                            core::LLVMSetAlignment(value, *align / 8);
1082                        }
1083
1084                        ctx.values
1085                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1086                    }
1087                    irvm::block::MemoryOp::Store { value, ptr, align } => {
1088                        let ptr_val = lower_operand(ctx, ptr);
1089                        let value_val = lower_operand(ctx, value);
1090
1091                        let value = core::LLVMBuildStore(ctx.builder, value_val, ptr_val);
1092                        if let Some(align) = align {
1093                            core::LLVMSetAlignment(value, *align / 8);
1094                        }
1095
1096                        ctx.values
1097                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1098                    }
1099                    irvm::block::MemoryOp::GetElementPtr { ptr, indices } => {
1100                        let ptr_val = lower_operand(ctx, ptr);
1101                        let pointee_ty =
1102                            lower_type(ctx.ctx, ctx.storage, ptr.get_inner_type(ctx.storage)?);
1103
1104                        let mut x = Vec::new();
1105
1106                        for index in indices {
1107                            let value = match index {
1108                                irvm::block::GepIndex::Const(value) => core::LLVMConstInt(
1109                                    core::LLVMInt64TypeInContext(ctx.ctx),
1110                                    (*value) as u64,
1111                                    0,
1112                                ),
1113                                irvm::block::GepIndex::Value(operand) => {
1114                                    lower_operand(ctx, operand)
1115                                }
1116                            };
1117
1118                            x.push(value);
1119                        }
1120
1121                        let value = core::LLVMBuildGEP2(
1122                            ctx.builder,
1123                            pointee_ty,
1124                            ptr_val,
1125                            x.as_mut_ptr(),
1126                            x.len() as u32,
1127                            null_name.as_ptr(),
1128                        );
1129
1130                        ctx.values
1131                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1132                    }
1133                },
1134                Instruction::OtherOp(other_op) => match other_op {
1135                    irvm::block::OtherOp::Call(call_op) => {
1136                        let (target_fn_ptr, fn_ty) = match &call_op.fn_target {
1137                            irvm::block::CallableValue::Symbol(id) => {
1138                                *ctx.functions.get(&id.to_idx()).expect("function not found")
1139                            }
1140                            irvm::block::CallableValue::Pointer(operand, fn_ty) => {
1141                                let ptr = lower_operand(ctx, operand);
1142
1143                                let ret_ty = lower_type(ctx.ctx, ctx.storage, fn_ty.return_type);
1144                                let mut params = fn_ty
1145                                    .parameters
1146                                    .iter()
1147                                    .map(|x| lower_type(ctx.ctx, ctx.storage, *x))
1148                                    .collect_vec();
1149                                let fn_ty = core::LLVMFunctionType(
1150                                    ret_ty,
1151                                    params.as_mut_ptr(),
1152                                    params.len() as u32,
1153                                    0,
1154                                );
1155                                (ptr, fn_ty)
1156                            }
1157                        };
1158
1159                        let mut args = call_op
1160                            .params
1161                            .iter()
1162                            .map(|p| lower_operand(ctx, p))
1163                            .collect_vec();
1164
1165                        let value = core::LLVMBuildCall2(
1166                            ctx.builder,
1167                            fn_ty,
1168                            target_fn_ptr,
1169                            args.as_mut_ptr(),
1170                            args.len() as u32,
1171                            null_name.as_ptr(),
1172                        );
1173
1174                        ctx.values
1175                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1176                    }
1177                    irvm::block::OtherOp::Icmp { cond, lhs, rhs } => {
1178                        let lhs_val = lower_operand(ctx, lhs);
1179                        let rhs_val = lower_operand(ctx, rhs);
1180                        let value = core::LLVMBuildICmp(
1181                            ctx.builder,
1182                            match cond {
1183                                irvm::block::IcmpCond::Eq => LLVMIntPredicate::LLVMIntEQ,
1184                                irvm::block::IcmpCond::Ne => LLVMIntPredicate::LLVMIntNE,
1185                                irvm::block::IcmpCond::Ugt => LLVMIntPredicate::LLVMIntUGT,
1186                                irvm::block::IcmpCond::Uge => LLVMIntPredicate::LLVMIntUGE,
1187                                irvm::block::IcmpCond::Ult => LLVMIntPredicate::LLVMIntULT,
1188                                irvm::block::IcmpCond::Ule => LLVMIntPredicate::LLVMIntULE,
1189                                irvm::block::IcmpCond::Sgt => LLVMIntPredicate::LLVMIntSGT,
1190                                irvm::block::IcmpCond::Sge => LLVMIntPredicate::LLVMIntSGE,
1191                                irvm::block::IcmpCond::Slt => LLVMIntPredicate::LLVMIntSLT,
1192                                irvm::block::IcmpCond::Sle => LLVMIntPredicate::LLVMIntSLE,
1193                            },
1194                            lhs_val,
1195                            rhs_val,
1196                            null_name.as_ptr(),
1197                        );
1198                        ctx.values
1199                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1200                    }
1201                    irvm::block::OtherOp::Fcmp { cond, lhs, rhs } => {
1202                        let lhs_val = lower_operand(ctx, lhs);
1203                        let rhs_val = lower_operand(ctx, rhs);
1204                        let value = core::LLVMBuildFCmp(
1205                            ctx.builder,
1206                            match cond {
1207                                irvm::block::FcmpCond::False => {
1208                                    LLVMRealPredicate::LLVMRealPredicateFalse
1209                                }
1210                                irvm::block::FcmpCond::Oeq => LLVMRealPredicate::LLVMRealOEQ,
1211                                irvm::block::FcmpCond::Ogt => LLVMRealPredicate::LLVMRealOGT,
1212                                irvm::block::FcmpCond::Oge => LLVMRealPredicate::LLVMRealOGE,
1213                                irvm::block::FcmpCond::Olt => LLVMRealPredicate::LLVMRealOLT,
1214                                irvm::block::FcmpCond::Ole => LLVMRealPredicate::LLVMRealOLE,
1215                                irvm::block::FcmpCond::One => LLVMRealPredicate::LLVMRealONE,
1216                                irvm::block::FcmpCond::Ord => LLVMRealPredicate::LLVMRealORD,
1217                                irvm::block::FcmpCond::Ueq => LLVMRealPredicate::LLVMRealUEQ,
1218                                irvm::block::FcmpCond::Ugt => LLVMRealPredicate::LLVMRealUGT,
1219                                irvm::block::FcmpCond::Ult => LLVMRealPredicate::LLVMRealULT,
1220                                irvm::block::FcmpCond::Ule => LLVMRealPredicate::LLVMRealULE,
1221                                irvm::block::FcmpCond::Une => LLVMRealPredicate::LLVMRealUNE,
1222                                irvm::block::FcmpCond::Uno => LLVMRealPredicate::LLVMRealUNO,
1223                                irvm::block::FcmpCond::True => {
1224                                    LLVMRealPredicate::LLVMRealPredicateTrue
1225                                }
1226                            },
1227                            lhs_val,
1228                            rhs_val,
1229                            null_name.as_ptr(),
1230                        );
1231                        ctx.values
1232                            .insert((block_idx.to_idx(), inst_idx.to_idx()), value);
1233                    }
1234                },
1235                Instruction::DebugOp(debug_op) => match debug_op {
1236                    DebugOp::Declare { address, variable } => {
1237                        let var = ctx.func.debug_vars.get(*variable).unwrap();
1238                        let address_ptr = lower_operand(ctx, address);
1239                        let var_ptr = lower_debug_var(
1240                            ctx.dibuilder,
1241                            ctx.debug_scope,
1242                            ctx.datalayout,
1243                            var,
1244                            ctx.storage,
1245                        )?;
1246
1247                        let diexpr =
1248                            debuginfo::LLVMDIBuilderCreateExpression(ctx.dibuilder, null_mut(), 0);
1249                        debuginfo::LLVMDIBuilderInsertDeclareRecordAtEnd(
1250                            ctx.dibuilder,
1251                            address_ptr,
1252                            var_ptr,
1253                            diexpr,
1254                            loc,
1255                            block_ptr,
1256                        );
1257                    }
1258                    DebugOp::Value {
1259                        new_value,
1260                        variable,
1261                    } => {
1262                        let var = ctx.func.debug_vars.get(*variable).unwrap();
1263                        let value_ptr = lower_operand(ctx, new_value);
1264                        let var_ptr = lower_debug_var(
1265                            ctx.dibuilder,
1266                            ctx.debug_scope,
1267                            ctx.datalayout,
1268                            var,
1269                            ctx.storage,
1270                        )?;
1271
1272                        let diexpr =
1273                            debuginfo::LLVMDIBuilderCreateExpression(ctx.dibuilder, null_mut(), 0);
1274                        debuginfo::LLVMDIBuilderInsertDbgValueRecordAtEnd(
1275                            ctx.dibuilder,
1276                            value_ptr,
1277                            var_ptr,
1278                            diexpr,
1279                            loc,
1280                            block_ptr,
1281                        );
1282                    }
1283                    DebugOp::Assign { .. } => {
1284                        // TODO: no di assign in llvm sys?
1285                        todo!()
1286                    }
1287                },
1288            }
1289        }
1290
1291        match ctx.func.blocks[block_idx].terminator().clone() {
1292            irvm::block::Terminator::Ret(op) => {
1293                set_loc(ctx.ctx, ctx.builder, &op.0, ctx.debug_scope);
1294                if let Some(op) = op.1 {
1295                    let value = lower_operand(ctx, &op);
1296                    core::LLVMBuildRet(ctx.builder, value);
1297                } else {
1298                    core::LLVMBuildRetVoid(ctx.builder);
1299                }
1300            }
1301            irvm::block::Terminator::Br {
1302                block: jmp_block,
1303                location,
1304                ..
1305            } => {
1306                set_loc(ctx.ctx, ctx.builder, &location, ctx.debug_scope);
1307                let target_block = *ctx.blocks.get(&jmp_block.to_idx()).unwrap();
1308
1309                core::LLVMBuildBr(ctx.builder, target_block);
1310            }
1311            irvm::block::Terminator::CondBr {
1312                then_block: if_block,
1313                else_block: then_block,
1314                cond,
1315                ..
1316            } => {
1317                let cond = lower_operand(ctx, &cond);
1318
1319                let if_block_value = *ctx.blocks.get(&if_block.to_idx()).unwrap();
1320                let then_block_value = *ctx.blocks.get(&then_block.to_idx()).unwrap();
1321
1322                core::LLVMBuildCondBr(ctx.builder, cond, if_block_value, then_block_value);
1323            }
1324        }
1325
1326        Ok(())
1327    }
1328}
1329
1330fn add_block(ctx: &mut FnCtx, block_idx: BlockIdx, name: Option<String>) -> LLVMBasicBlockRef {
1331    unsafe {
1332        let block_name = CString::new(if block_idx.to_idx() == 0 {
1333            "entry".to_string()
1334        } else if let Some(name) = name {
1335            format!("bb{}", name)
1336        } else {
1337            format!("bb{}", block_idx.to_idx())
1338        })
1339        .unwrap();
1340        let block_ptr = core::LLVMAppendBasicBlock(ctx.fn_ptr, block_name.as_ptr());
1341        ctx.blocks.insert(block_idx.to_idx(), block_ptr);
1342        block_ptr
1343    }
1344}
1345
1346fn lower_debug_var(
1347    dibuilder: LLVMDIBuilderRef,
1348    scope: LLVMMetadataRef,
1349    datalayout: &DataLayout,
1350    variable: &DebugVariable,
1351    storage: &TypeStorage,
1352) -> Result<LLVMMetadataRef, Error> {
1353    let name = CString::new(variable.name.clone())?;
1354    let difile = get_difile_location(dibuilder, &variable.location);
1355
1356    let (line, _col) = match &variable.location {
1357        Location::Unknown => (0, 0),
1358        Location::File(file_location) => (file_location.line, file_location.col),
1359    };
1360
1361    let ty_ptr = lower_debug_type(datalayout, dibuilder, storage, scope, variable.ty);
1362    let align = datalayout.get_type_align(storage, variable.ty);
1363
1364    Ok(unsafe {
1365        if let Some(param) = variable.parameter {
1366            debuginfo::LLVMDIBuilderCreateParameterVariable(
1367                dibuilder,
1368                scope,
1369                name.as_ptr(),
1370                name.count_bytes(),
1371                param,
1372                difile,
1373                line,
1374                ty_ptr,
1375                1,
1376                0,
1377            )
1378        } else {
1379            debuginfo::LLVMDIBuilderCreateAutoVariable(
1380                dibuilder,
1381                scope,
1382                name.as_ptr(),
1383                name.count_bytes(),
1384                difile,
1385                line,
1386                ty_ptr,
1387                1,
1388                0,
1389                align,
1390            )
1391        }
1392    })
1393}
1394
1395fn get_difile_location(dibuilder: LLVMDIBuilderRef, location: &Location) -> LLVMMetadataRef {
1396    match location {
1397        Location::Unknown => unsafe {
1398            debuginfo::LLVMDIBuilderCreateFile(
1399                dibuilder,
1400                c"/dev/stdin".as_ptr(),
1401                c"/dev/stdin".count_bytes(),
1402                c"".as_ptr(),
1403                0,
1404            )
1405        },
1406        Location::File(file_location) => get_difile(dibuilder, &file_location.file),
1407    }
1408}
1409
1410fn get_difile(dibuilder: LLVMDIBuilderRef, file: &Path) -> LLVMMetadataRef {
1411    let parent = if let Some(parent) = file.parent() {
1412        CString::new(parent.display().to_string()).unwrap()
1413    } else {
1414        CString::new("").unwrap()
1415    };
1416
1417    let filename = CString::new(file.display().to_string()).unwrap();
1418
1419    unsafe {
1420        debuginfo::LLVMDIBuilderCreateFile(
1421            dibuilder,
1422            filename.as_ptr(),
1423            filename.count_bytes(),
1424            parent.as_ptr(),
1425            parent.count_bytes(),
1426        )
1427    }
1428}
1429
1430fn set_loc(
1431    ctx: LLVMContextRef,
1432    builder: LLVMBuilderRef,
1433    location: &Location,
1434    scope: LLVMMetadataRef,
1435) -> *mut LLVMOpaqueMetadata {
1436    match location {
1437        Location::Unknown => unsafe {
1438            let loc = debuginfo::LLVMDIBuilderCreateDebugLocation(ctx, 0, 0, scope, null_mut());
1439            core::LLVMSetCurrentDebugLocation2(builder, loc);
1440            loc
1441        },
1442        Location::File(file_location) => unsafe {
1443            let loc = debuginfo::LLVMDIBuilderCreateDebugLocation(
1444                ctx,
1445                file_location.line,
1446                file_location.col,
1447                scope,
1448                null_mut(),
1449            );
1450            core::LLVMSetCurrentDebugLocation2(builder, loc);
1451            loc
1452        },
1453    }
1454}
1455
1456fn add_preds(ctx: &mut FnCtx, block_idx: BlockIdx) {
1457    unsafe {
1458        let block_ptr = *ctx.blocks.get(&block_idx.to_idx()).unwrap();
1459        core::LLVMPositionBuilderAtEnd(ctx.builder, block_ptr);
1460
1461        let preds = ctx.func.find_preds_for(block_idx);
1462        let mut block_args = Vec::new();
1463
1464        if !preds.is_empty() {
1465            let operand_len = preds.first().unwrap().1.len();
1466
1467            for i in 0..(operand_len) {
1468                let phy_ty =
1469                    lower_type(ctx.ctx, ctx.storage, preds.first().unwrap().1[i].get_type());
1470                let phi_node = core::LLVMBuildPhi(ctx.builder, phy_ty, c"".as_ptr());
1471                let mut blocks = Vec::new();
1472                let mut values = Vec::new();
1473                for (pred_block_idx, operands) in &preds {
1474                    let pred_ptr = ctx.blocks.get(&pred_block_idx.to_idx()).unwrap();
1475                    let value = lower_operand(ctx, &operands[i]);
1476
1477                    blocks.push(*pred_ptr);
1478                    values.push(value);
1479                }
1480
1481                assert_eq!(values.len(), values.len());
1482
1483                core::LLVMAddIncoming(
1484                    phi_node,
1485                    values.as_mut_ptr().cast(),
1486                    blocks.as_mut_ptr().cast(),
1487                    blocks.len() as u32,
1488                );
1489                block_args.push(phi_node);
1490            }
1491        }
1492
1493        ctx.block_args.insert(block_idx.to_idx(), block_args);
1494    }
1495}
1496
1497fn lower_operand(ctx: &FnCtx, operand: &Operand) -> LLVMValueRef {
1498    unsafe {
1499        match operand {
1500            Operand::Parameter(idx, _ty) => core::LLVMGetParam(ctx.fn_ptr, (*idx) as u32),
1501            Operand::Value(block_idx, index, _) => *ctx
1502                .values
1503                .get(&(block_idx.to_idx(), index.to_idx()))
1504                .unwrap(),
1505            Operand::Constant(const_value, ty) => lower_constant(ctx, const_value, *ty),
1506            Operand::BlockArgument { block_idx, nth, .. } => {
1507                ctx.block_args.get(block_idx).unwrap()[*nth]
1508            }
1509        }
1510    }
1511}
1512
1513fn lower_constant(ctx: &FnCtx, value: &ConstValue, ty: TypeIdx) -> LLVMValueRef {
1514    unsafe {
1515        let ty_ptr = lower_type(ctx.ctx, ctx.storage, ty);
1516
1517        match value {
1518            irvm::value::ConstValue::Int(value) => core::LLVMConstInt(ty_ptr, *value, 0_i32),
1519            irvm::value::ConstValue::Float(value) => core::LLVMConstReal(ty_ptr, *value),
1520            irvm::value::ConstValue::Array(const_values) => {
1521                let mut values = Vec::new();
1522                let array_ty = if let Type::Array(array_ty) = &ctx.storage.get_type_info(ty).ty {
1523                    array_ty
1524                } else {
1525                    panic!("type mismatch")
1526                };
1527
1528                let typtr = lower_type(ctx.ctx, ctx.storage, array_ty.ty);
1529
1530                for value in const_values {
1531                    let ptr = lower_constant(ctx, value, array_ty.ty);
1532                    values.push(ptr);
1533                }
1534
1535                core::LLVMConstArray2(typtr, values.as_mut_ptr(), values.len() as u64)
1536            }
1537            irvm::value::ConstValue::Vector(const_values) => {
1538                let mut values = Vec::new();
1539                let vec_ty = if let Type::Vector(vec_ty) = &ctx.storage.get_type_info(ty).ty {
1540                    vec_ty
1541                } else {
1542                    panic!("type mismatch")
1543                };
1544
1545                for value in const_values {
1546                    let ptr = lower_constant(ctx, value, vec_ty.ty);
1547                    values.push(ptr);
1548                }
1549
1550                core::LLVMConstVector(values.as_mut_ptr(), values.len() as u32)
1551            }
1552            irvm::value::ConstValue::Struct(const_values) => {
1553                let mut const_fields = Vec::new();
1554                let struct_ty = if let Type::Struct(struct_ty) = &ctx.storage.get_type_info(ty).ty {
1555                    &**struct_ty
1556                } else {
1557                    panic!("type mismatch")
1558                };
1559                for (value, field) in const_values.iter().zip(struct_ty.fields.iter()) {
1560                    let ptr = lower_constant(ctx, value, *field);
1561                    const_fields.push(ptr);
1562                }
1563                core::LLVMConstStructInContext(
1564                    ctx.ctx,
1565                    const_fields.as_mut_ptr(),
1566                    const_fields.len() as u32,
1567                    struct_ty.packed as i32,
1568                )
1569            }
1570            irvm::value::ConstValue::NullPtr => core::LLVMConstPointerNull(ty_ptr),
1571            irvm::value::ConstValue::Undef => core::LLVMGetUndef(ty_ptr),
1572            irvm::value::ConstValue::Poison => core::LLVMGetPoison(ty_ptr),
1573        }
1574    }
1575}
1576
1577fn lower_type(ctx: LLVMContextRef, storage: &TypeStorage, ty: TypeIdx) -> LLVMTypeRef {
1578    let tyinfo = storage.get_type_info(ty);
1579    unsafe {
1580        match &tyinfo.ty {
1581            Type::Int(width) => core::LLVMIntTypeInContext(ctx, *width),
1582            Type::Half => core::LLVMHalfTypeInContext(ctx),
1583            Type::BFloat => core::LLVMBFloatTypeInContext(ctx),
1584            Type::Float => core::LLVMFloatTypeInContext(ctx),
1585            Type::Double => core::LLVMDoubleTypeInContext(ctx),
1586            Type::Fp128 => core::LLVMFP128TypeInContext(ctx),
1587            Type::X86Fp80 => core::LLVMX86FP80TypeInContext(ctx),
1588            Type::PpcFp128 => core::LLVMPPCFP128TypeInContext(ctx),
1589            Type::Ptr {
1590                pointee: _,
1591                address_space,
1592            } => core::LLVMPointerTypeInContext(ctx, address_space.unwrap_or(0)),
1593            Type::Vector(vector_type) => {
1594                let inner = lower_type(ctx, storage, vector_type.ty);
1595                core::LLVMVectorType(inner, vector_type.size)
1596            }
1597            Type::Array(array_type) => {
1598                let inner = lower_type(ctx, storage, array_type.ty);
1599                core::LLVMArrayType2(inner, array_type.size)
1600            }
1601            Type::Struct(struct_type) => {
1602                let mut fields = Vec::new();
1603
1604                for field in struct_type.fields.iter() {
1605                    fields.push(lower_type(ctx, storage, *field));
1606                }
1607
1608                if let Some(ident) = &struct_type.ident {
1609                    let name = CString::new(ident.as_str()).unwrap();
1610                    let ptr = core::LLVMStructCreateNamed(ctx, name.as_ptr());
1611
1612                    core::LLVMStructSetBody(
1613                        ptr,
1614                        fields.as_mut_ptr(),
1615                        fields.len() as u32,
1616                        struct_type.packed as i32,
1617                    );
1618
1619                    ptr
1620                } else {
1621                    core::LLVMStructTypeInContext(
1622                        ctx,
1623                        fields.as_mut_ptr(),
1624                        fields.len() as u32,
1625                        struct_type.packed as i32,
1626                    )
1627                }
1628            }
1629        }
1630    }
1631}
1632
1633fn lower_debug_type(
1634    datalayout: &DataLayout,
1635    builder: LLVMDIBuilderRef,
1636    storage: &TypeStorage,
1637    module_scope: LLVMMetadataRef,
1638    type_idx: TypeIdx,
1639) -> LLVMMetadataRef {
1640    let ty = storage.get_type_info(type_idx);
1641
1642    // 1 == address
1643    // 2 = boolean
1644    // 4 = float
1645    // 5 = signed
1646    // 11 = numeric string
1647    // https://dwarfstd.org/doc/DWARF5.pdf#section.7.8
1648
1649    let size_in_bits = datalayout.get_type_size(storage, type_idx);
1650    let align_in_bits = datalayout.get_type_align(storage, type_idx);
1651
1652    if let Some(debug_info) = &ty.debug_info {
1653        let name = CString::new(debug_info.name.clone()).unwrap();
1654        unsafe {
1655            match &ty.ty {
1656                Type::Int(width) => {
1657                    let mut encoding = DW_ATE_unsigned;
1658                    if *width == 1 {
1659                        encoding = DW_ATE_boolean;
1660                    }
1661                    debuginfo::LLVMDIBuilderCreateBasicType(
1662                        builder,
1663                        name.as_ptr(),
1664                        name.count_bytes(),
1665                        size_in_bits as u64,
1666                        encoding.0 as u32,
1667                        LLVMDIFlagPublic,
1668                    )
1669                }
1670                Type::Half
1671                | Type::BFloat
1672                | Type::Float
1673                | Type::Double
1674                | Type::Fp128
1675                | Type::X86Fp80
1676                | Type::PpcFp128 => debuginfo::LLVMDIBuilderCreateBasicType(
1677                    builder,
1678                    name.as_ptr(),
1679                    name.count_bytes(),
1680                    size_in_bits as u64,
1681                    0x4,
1682                    LLVMDIFlagPublic,
1683                ),
1684                Type::Ptr {
1685                    pointee,
1686                    address_space,
1687                } => {
1688                    let pointee_ptr =
1689                        lower_debug_type(datalayout, builder, storage, module_scope, *pointee);
1690
1691                    if debug_info.is_reference {
1692                        debuginfo::LLVMDIBuilderCreateReferenceType(
1693                            builder,
1694                            DW_TAG_reference_type.0 as u32,
1695                            pointee_ptr,
1696                        )
1697                    } else {
1698                        debuginfo::LLVMDIBuilderCreatePointerType(
1699                            builder,
1700                            pointee_ptr,
1701                            size_in_bits as u64,
1702                            align_in_bits,
1703                            address_space.unwrap_or(0),
1704                            name.as_ptr(),
1705                            name.count_bytes(),
1706                        )
1707                    }
1708                }
1709                Type::Vector(vector_type) => {
1710                    let inner_ty_ptr = lower_debug_type(
1711                        datalayout,
1712                        builder,
1713                        storage,
1714                        module_scope,
1715                        vector_type.ty,
1716                    );
1717                    let size = datalayout.get_type_size(storage, type_idx);
1718                    let align = datalayout.get_type_align(storage, type_idx);
1719                    let mut subrange = debuginfo::LLVMDIBuilderGetOrCreateSubrange(
1720                        builder,
1721                        0,
1722                        vector_type.size as i64,
1723                    );
1724                    debuginfo::LLVMDIBuilderCreateVectorType(
1725                        builder,
1726                        size as u64,
1727                        align,
1728                        inner_ty_ptr,
1729                        &raw mut subrange,
1730                        1,
1731                    )
1732                }
1733                Type::Array(array_type) => {
1734                    let inner_ty_ptr =
1735                        lower_debug_type(datalayout, builder, storage, module_scope, array_type.ty);
1736                    let size = datalayout.get_type_size(storage, type_idx);
1737                    let align = datalayout.get_type_align(storage, type_idx);
1738                    let mut subrange = debuginfo::LLVMDIBuilderGetOrCreateSubrange(
1739                        builder,
1740                        0,
1741                        array_type.size as i64,
1742                    );
1743                    debuginfo::LLVMDIBuilderCreateArrayType(
1744                        builder,
1745                        size as u64,
1746                        align,
1747                        inner_ty_ptr,
1748                        &raw mut subrange,
1749                        1,
1750                    )
1751                }
1752                Type::Struct(struct_type) => {
1753                    let mut fields = Vec::with_capacity(struct_type.fields.len());
1754
1755                    let difile = get_difile_location(builder, &debug_info.location);
1756                    let line = debug_info.location.get_line();
1757
1758                    let mut offset = 0;
1759                    let mut cur_align = 8;
1760
1761                    for (i, field) in struct_type.fields.iter().enumerate() {
1762                        let field_align = datalayout.get_type_align(storage, *field);
1763                        cur_align = cur_align.max(field_align);
1764
1765                        if offset % field_align != 0 {
1766                            let padding = (field_align - (offset % field_align)) % field_align;
1767                            offset += padding;
1768                        }
1769
1770                        let field_size = datalayout.get_type_size(storage, *field);
1771
1772                        let mut ty =
1773                            lower_debug_type(datalayout, builder, storage, module_scope, *field);
1774
1775                        if let Some((field_name, location)) = struct_type.debug_field_names.get(i) {
1776                            let name = CString::new(field_name.clone()).unwrap();
1777                            let difile = get_difile_location(builder, location);
1778                            let line = location.get_line().unwrap_or(0);
1779                            let size = datalayout.get_type_size(storage, *field);
1780                            let align = datalayout.get_type_align(storage, *field);
1781                            ty = debuginfo::LLVMDIBuilderCreateMemberType(
1782                                builder,
1783                                module_scope,
1784                                name.as_ptr(),
1785                                name.count_bytes(),
1786                                difile,
1787                                line,
1788                                size as u64,
1789                                align,
1790                                offset as u64,
1791                                0,
1792                                ty,
1793                            );
1794                        }
1795
1796                        offset += field_size;
1797
1798                        fields.push(ty);
1799                    }
1800
1801                    let size = datalayout.get_type_size(storage, type_idx);
1802                    let align = datalayout.get_type_align(storage, type_idx);
1803
1804                    debuginfo::LLVMDIBuilderCreateStructType(
1805                        builder,
1806                        module_scope,
1807                        name.as_ptr(),
1808                        name.count_bytes(),
1809                        difile,
1810                        line.unwrap_or(0),
1811                        size as u64,
1812                        align,
1813                        0,
1814                        null_mut(),
1815                        fields.as_mut_ptr(),
1816                        fields.len() as u32,
1817                        0,
1818                        null_mut(),
1819                        name.as_ptr(),
1820                        name.count_bytes(),
1821                    )
1822                }
1823            }
1824        }
1825    } else {
1826        todo!()
1827    }
1828}