Skip to main content

pliron_llvm/
to_llvm_ir.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! Translate from pliron's LLVM dialect to LLVM-IR
5
6use llvm_sys::{
7    LLVMAtomicOrdering, LLVMAtomicRMWBinOp, LLVMInlineAsmDialect, LLVMIntPredicate, LLVMLinkage,
8    LLVMRealPredicate,
9};
10use pliron::{
11    attribute::{Attribute, attr_cast},
12    basic_block::BasicBlock,
13    builtin::{
14        attr_interfaces::FloatAttr,
15        attributes::{FPDoubleAttr, FPSingleAttr, IntegerAttr, StringAttr},
16        op_interfaces::{
17            AtMostOneRegionInterface, BranchOpInterface, CallOpCallable, CallOpInterface,
18            OneOpdInterface, OneResultInterface, SingleBlockRegionInterface, SymbolOpInterface,
19        },
20        ops::ModuleOp,
21        type_interfaces::FunctionTypeInterface,
22        types::{FP16Type, FP32Type, FP64Type, IntegerType},
23    },
24    common_traits::Named,
25    context::{Context, Ptr},
26    derive::{attr_interface, attr_interface_impl},
27    graph::traversals::region::topological_order,
28    identifier::Identifier,
29    input_err, input_err_noloc, input_error, input_error_noloc,
30    linked_list::ContainsLinkedList,
31    location::Located,
32    op::{Op, op_cast},
33    operation::Operation,
34    printable::Printable,
35    result::Result,
36    std_deps::hash::{FxHashMap, hash_map},
37    r#type::{Type, TypeHandle, Typed, type_cast},
38    utils::apint::APInt,
39    value::{DefiningEntity, Value},
40};
41
42use pliron::derive::{op_interface, op_interface_impl, type_interface, type_interface_impl};
43use thiserror::Error;
44
45use crate::{
46    attributes::{
47        AtomicOrderingAttr, AtomicRmwKindAttr, FCmpPredicateAttr, ICmpPredicateAttr, LinkageAttr,
48    },
49    llvm_sys::core::{
50        LLVMBasicBlock, LLVMBuilder, LLVMContext, LLVMModule, LLVMType, LLVMValue,
51        instruction_iter, llvm_add_case, llvm_add_destination, llvm_add_function,
52        llvm_add_global_in_address_space, llvm_add_incoming, llvm_append_basic_block_in_context,
53        llvm_array_type2, llvm_block_address, llvm_build_add, llvm_build_addrspacecast,
54        llvm_build_and, llvm_build_array_alloca, llvm_build_ashr, llvm_build_atomic_cmpxchg,
55        llvm_build_atomic_rmw, llvm_build_bitcast, llvm_build_br, llvm_build_call2,
56        llvm_build_cond_br, llvm_build_extract_element, llvm_build_extract_value, llvm_build_fadd,
57        llvm_build_fcmp, llvm_build_fdiv, llvm_build_fence, llvm_build_fmul, llvm_build_fneg,
58        llvm_build_fpext, llvm_build_fptosi, llvm_build_fptoui, llvm_build_fptrunc,
59        llvm_build_freeze, llvm_build_frem, llvm_build_fsub, llvm_build_gep2, llvm_build_icmp,
60        llvm_build_indirect_br, llvm_build_insert_element, llvm_build_insert_value,
61        llvm_build_int_to_ptr, llvm_build_load2, llvm_build_lshr, llvm_build_mul, llvm_build_or,
62        llvm_build_phi, llvm_build_ptr_to_int, llvm_build_ret, llvm_build_ret_void,
63        llvm_build_sdiv, llvm_build_select, llvm_build_sext, llvm_build_shl,
64        llvm_build_shuffle_vector, llvm_build_sitofp, llvm_build_srem, llvm_build_store,
65        llvm_build_sub, llvm_build_switch, llvm_build_trunc, llvm_build_udiv, llvm_build_uitofp,
66        llvm_build_unreachable, llvm_build_urem, llvm_build_va_arg, llvm_build_xor,
67        llvm_build_zext, llvm_can_value_use_fast_math_flags, llvm_clear_insertion_position,
68        llvm_const_int, llvm_const_null, llvm_const_real, llvm_const_vector, llvm_delete_global,
69        llvm_double_type_in_context, llvm_float_type_in_context, llvm_function_type,
70        llvm_get_inline_asm, llvm_get_named_function, llvm_get_param,
71        llvm_get_pointer_address_space, llvm_get_poison, llvm_get_sync_scope_id, llvm_get_undef,
72        llvm_half_type_in_context, llvm_int_type_in_context, llvm_is_a, llvm_lookup_intrinsic_id,
73        llvm_pointer_type_in_context, llvm_position_builder_at_end, llvm_replace_all_uses_with,
74        llvm_scalable_vector_type, llvm_set_alignment, llvm_set_atomic_sync_scope_id,
75        llvm_set_fast_math_flags, llvm_set_initializer, llvm_set_linkage, llvm_set_nneg,
76        llvm_set_ordering, llvm_struct_create_named, llvm_struct_set_body,
77        llvm_struct_type_in_context, llvm_type_of, llvm_vector_type, llvm_void_type_in_context,
78    },
79    op_interfaces::{
80        AlignableOpInterface, FastMathFlags, IsDeclaration, LlvmSymbolName, NNegFlag,
81        PointerTypeResult,
82    },
83    ops::{
84        AShrOp, AddOp, AddrSpaceCastOp, AddressOfOp, AllocaOp, AndOp, AtomicCmpxchgOp,
85        AtomicLoadOp, AtomicRmwOp, AtomicStoreOp, BitcastOp, BlockAddressOp, BlockTagOp, BrOp,
86        CallIntrinsicOp, CallOp, CondBrOp, ConstantOp, ExtractElementOp, ExtractValueOp, FAddOp,
87        FCmpOp, FDivOp, FMulOp, FNegOp, FPExtOp, FPToSIOp, FPToUIOp, FPTruncOp, FRemOp, FSubOp,
88        FenceOp, FreezeOp, FuncOp, GetElementPtrOp, GlobalOp, ICmpOp, IndirectBrOp, InlineAsmOp,
89        InsertElementOp, InsertValueOp, IntToPtrOp, LShrOp, LoadOp, MulOp, OrOp, PoisonOp,
90        PtrToIntOp, ReturnOp, SDivOp, SExtOp, SIToFPOp, SRemOp, SelectOp, ShlOp, ShuffleVectorOp,
91        StoreOp, SubOp, SwitchOp, TruncOp, UDivOp, UIToFPOp, URemOp, UndefOp, UnreachableOp,
92        VAArgOp, XorOp, ZExtOp, ZeroOp,
93    },
94    types::{ArrayType, FuncType, PointerType, StructType, VectorType, VoidType},
95};
96
97/// Mapping from pliron entities to LLVM entities.
98pub struct ConversionContext<'a> {
99    // The current LLVMModule being converted to.
100    cur_llvm_module: &'a LLVMModule,
101    // A map from pliron Values to LLVM Values.
102    value_map: FxHashMap<Value, LLVMValue>,
103    // A map from pliron basic blocks to LLVM.
104    block_map: FxHashMap<Ptr<BasicBlock>, LLVMBasicBlock>,
105    // A map from pliron functions to LLVM functions.
106    function_map: FxHashMap<Identifier, LLVMValue>,
107    // A map from pliron globals to LLVM globals.
108    globals_map: FxHashMap<Identifier, LLVMValue>,
109    // A map from `(function symbol, block tag)` to the corresponding LLVM block.
110    block_tags: FxHashMap<(Identifier, u64), LLVMBasicBlock>,
111    // A map from every placeholder we insert to
112    // its corresponding `(function symbol, block tag)`
113    pending_block_address_ops: FxHashMap<LLVMValue, (Identifier, u64)>,
114    // A map from pliron StructTypes to LLVM StructTypes.
115    structs_map: FxHashMap<Identifier, LLVMType>,
116    // Type cache to avoid redundant conversions.
117    type_cache: FxHashMap<TypeHandle, LLVMType>,
118    // The active LLVM builder.
119    builder: LLVMBuilder,
120    // Scratch builder in a scratch function for attempting to evaluate constants.
121    scratch_builder: LLVMBuilder,
122}
123
124impl<'a> ConversionContext<'a> {
125    pub fn new(llvm_ctx: &'a LLVMContext, cur_llvm_module: &'a LLVMModule) -> Self {
126        Self {
127            cur_llvm_module,
128            value_map: FxHashMap::default(),
129            block_map: FxHashMap::default(),
130            function_map: FxHashMap::default(),
131            globals_map: FxHashMap::default(),
132            block_tags: FxHashMap::default(),
133            pending_block_address_ops: FxHashMap::default(),
134            structs_map: FxHashMap::default(),
135            type_cache: FxHashMap::default(),
136            builder: LLVMBuilder::new(llvm_ctx),
137            scratch_builder: LLVMBuilder::new(llvm_ctx),
138        }
139    }
140
141    pub fn clear_per_function_data(&mut self) {
142        self.value_map.clear();
143        self.block_map.clear();
144        llvm_clear_insertion_position(&self.builder);
145    }
146}
147
148#[derive(Error, Debug)]
149pub enum ToLLVMErr {
150    #[error("Type {0} does not have a conversion to LLVM type implemented")]
151    MissingTypeConversion(String),
152    #[error("Operation {0} does not have a conversion to LLVM instruction implemented")]
153    MissingOpConversion(String),
154    #[error("Definition for value {0} not seen yet")]
155    UndefinedValue(String),
156    #[error("Block definition {0} not seen yet")]
157    UndefinedBlock(String),
158    #[error("Number of block args in the source dialect equal the number of PHIs in target IR")]
159    NumBlockArgsNumPhisMismatch,
160    #[error("ConstantOp must have integer or float value")]
161    ConstOpNotIntOrFloat,
162    #[error(
163        "Insert/Extract value instructions must specify exactly one index, an LLVM-C API limitation"
164    )]
165    InsertExtractValueIndices,
166    #[error("GlobalOp Initializer region does not terminate with a return with value")]
167    GlobalOpInitializerRegionBadReturn,
168    #[error("Cannot evaluate value to a constant")]
169    CannotEvaluateToConst,
170    #[error("BlockAddressOp refers to missing block tag {1} in function {0}")]
171    MissingBlockTag(String, u64),
172}
173
174pub fn convert_ipredicate(pred: ICmpPredicateAttr) -> LLVMIntPredicate {
175    match pred {
176        ICmpPredicateAttr::EQ => LLVMIntPredicate::LLVMIntEQ,
177        ICmpPredicateAttr::NE => LLVMIntPredicate::LLVMIntNE,
178        ICmpPredicateAttr::UGT => LLVMIntPredicate::LLVMIntUGT,
179        ICmpPredicateAttr::UGE => LLVMIntPredicate::LLVMIntUGE,
180        ICmpPredicateAttr::ULT => LLVMIntPredicate::LLVMIntULT,
181        ICmpPredicateAttr::ULE => LLVMIntPredicate::LLVMIntULE,
182        ICmpPredicateAttr::SGT => LLVMIntPredicate::LLVMIntSGT,
183        ICmpPredicateAttr::SGE => LLVMIntPredicate::LLVMIntSGE,
184        ICmpPredicateAttr::SLT => LLVMIntPredicate::LLVMIntSLT,
185        ICmpPredicateAttr::SLE => LLVMIntPredicate::LLVMIntSLE,
186    }
187}
188
189pub fn convert_fpredicate(pred: FCmpPredicateAttr) -> LLVMRealPredicate {
190    match pred {
191        FCmpPredicateAttr::False => LLVMRealPredicate::LLVMRealPredicateFalse,
192        FCmpPredicateAttr::OEQ => LLVMRealPredicate::LLVMRealOEQ,
193        FCmpPredicateAttr::OGT => LLVMRealPredicate::LLVMRealOGT,
194        FCmpPredicateAttr::OGE => LLVMRealPredicate::LLVMRealOGE,
195        FCmpPredicateAttr::OLT => LLVMRealPredicate::LLVMRealOLT,
196        FCmpPredicateAttr::OLE => LLVMRealPredicate::LLVMRealOLE,
197        FCmpPredicateAttr::ONE => LLVMRealPredicate::LLVMRealONE,
198        FCmpPredicateAttr::ORD => LLVMRealPredicate::LLVMRealORD,
199        FCmpPredicateAttr::UNO => LLVMRealPredicate::LLVMRealUNO,
200        FCmpPredicateAttr::UEQ => LLVMRealPredicate::LLVMRealUEQ,
201        FCmpPredicateAttr::UGT => LLVMRealPredicate::LLVMRealUGT,
202        FCmpPredicateAttr::UGE => LLVMRealPredicate::LLVMRealUGE,
203        FCmpPredicateAttr::ULT => LLVMRealPredicate::LLVMRealULT,
204        FCmpPredicateAttr::ULE => LLVMRealPredicate::LLVMRealULE,
205        FCmpPredicateAttr::UNE => LLVMRealPredicate::LLVMRealUNE,
206        FCmpPredicateAttr::True => LLVMRealPredicate::LLVMRealPredicateTrue,
207    }
208}
209
210pub fn convert_linkage(linkage: LinkageAttr) -> LLVMLinkage {
211    match linkage {
212        LinkageAttr::ExternalLinkage => LLVMLinkage::LLVMExternalLinkage,
213        LinkageAttr::AvailableExternallyLinkage => LLVMLinkage::LLVMAvailableExternallyLinkage,
214        LinkageAttr::LinkOnceAnyLinkage => LLVMLinkage::LLVMLinkOnceAnyLinkage,
215        LinkageAttr::LinkOnceODRLinkage => LLVMLinkage::LLVMLinkOnceODRLinkage,
216        LinkageAttr::WeakAnyLinkage => LLVMLinkage::LLVMWeakAnyLinkage,
217        LinkageAttr::WeakODRLinkage => LLVMLinkage::LLVMWeakODRLinkage,
218        LinkageAttr::AppendingLinkage => LLVMLinkage::LLVMAppendingLinkage,
219        LinkageAttr::InternalLinkage => LLVMLinkage::LLVMInternalLinkage,
220        LinkageAttr::PrivateLinkage => LLVMLinkage::LLVMPrivateLinkage,
221        LinkageAttr::DLLImportLinkage => LLVMLinkage::LLVMDLLImportLinkage,
222        LinkageAttr::DLLExportLinkage => LLVMLinkage::LLVMDLLExportLinkage,
223        LinkageAttr::ExternalWeakLinkage => LLVMLinkage::LLVMExternalWeakLinkage,
224        LinkageAttr::GhostLinkage => LLVMLinkage::LLVMGhostLinkage,
225        LinkageAttr::CommonLinkage => LLVMLinkage::LLVMCommonLinkage,
226        LinkageAttr::LinkOnceODRAutoHideLinkage => LLVMLinkage::LLVMLinkOnceODRAutoHideLinkage,
227        LinkageAttr::LinkerPrivateLinkage => LLVMLinkage::LLVMLinkerPrivateLinkage,
228        LinkageAttr::LinkerPrivateWeakLinkage => LLVMLinkage::LLVMLinkerPrivateWeakLinkage,
229    }
230}
231
232#[::pliron::linkme::distributed_slice]
233#[linkme(crate = pliron::linkme)]
234pub static TEST: [u64];
235
236/// Convert a float attribute to fp64 (since LLVM's C-API pretty much restricts us to that).
237#[attr_interface]
238trait FloatAttrToFP64: FloatAttr {
239    fn to_fp64(&self) -> f64;
240    fn verify(_attr: &dyn Attribute, _ctx: &Context) -> Result<()>
241    where
242        Self: Sized,
243    {
244        Ok(())
245    }
246}
247
248#[attr_interface_impl]
249impl FloatAttrToFP64 for FPSingleAttr {
250    fn to_fp64(&self) -> f64 {
251        Into::<f32>::into(self.clone()) as f64
252    }
253}
254
255#[attr_interface_impl]
256impl FloatAttrToFP64 for FPDoubleAttr {
257    fn to_fp64(&self) -> f64 {
258        Into::<f64>::into(self.clone())
259    }
260}
261
262/// A type that implements this is convertible to an [LLVMType].
263#[type_interface]
264trait ToLLVMType {
265    /// Convert from pliron [Type] to [LLVMType].
266    fn convert(
267        &self,
268        ctx: &Context,
269        llvm_ctx: &LLVMContext,
270        cctx: &mut ConversionContext,
271    ) -> Result<LLVMType>;
272
273    fn verify(_type: &dyn Type, _ctx: &Context) -> Result<()>
274    where
275        Self: Sized,
276    {
277        Ok(())
278    }
279}
280
281/// An [Op] that implements this is convertible to an [LLVMValue].
282#[op_interface]
283trait ToLLVMValue {
284    /// Convert from pliron [Op] to [LLVMValue].
285    fn convert(
286        &self,
287        ctx: &Context,
288        llvm_ctx: &LLVMContext,
289        cctx: &mut ConversionContext,
290    ) -> Result<LLVMValue>;
291
292    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
293    where
294        Self: Sized,
295    {
296        Ok(())
297    }
298}
299
300#[type_interface_impl]
301impl ToLLVMType for IntegerType {
302    fn convert(
303        &self,
304        _ctx: &Context,
305        llvm_ctx: &LLVMContext,
306        _cctx: &mut ConversionContext,
307    ) -> Result<LLVMType> {
308        Ok(llvm_int_type_in_context(llvm_ctx, self.width()))
309    }
310}
311
312#[type_interface_impl]
313impl ToLLVMType for ArrayType {
314    fn convert(
315        &self,
316        ctx: &Context,
317        llvm_ctx: &LLVMContext,
318        cctx: &mut ConversionContext,
319    ) -> Result<LLVMType> {
320        let elem_ty = convert_type(ctx, llvm_ctx, cctx, self.elem_type())?;
321        Ok(llvm_array_type2(elem_ty, self.size()))
322    }
323}
324
325#[type_interface_impl]
326impl ToLLVMType for FuncType {
327    fn convert(
328        &self,
329        ctx: &Context,
330        llvm_ctx: &LLVMContext,
331        cctx: &mut ConversionContext,
332    ) -> Result<LLVMType> {
333        let args_tys: Vec<_> = self
334            .arg_types()
335            .iter()
336            .map(|ty| convert_type(ctx, llvm_ctx, cctx, *ty))
337            .collect::<Result<_>>()?;
338        let ret_ty = convert_type(ctx, llvm_ctx, cctx, self.result_type())?;
339        Ok(llvm_function_type(ret_ty, &args_tys, self.is_var_arg()))
340    }
341}
342
343#[type_interface_impl]
344impl ToLLVMType for VoidType {
345    fn convert(
346        &self,
347        _ctx: &Context,
348        llvm_ctx: &LLVMContext,
349        _cctx: &mut ConversionContext,
350    ) -> Result<LLVMType> {
351        Ok(llvm_void_type_in_context(llvm_ctx))
352    }
353}
354
355#[type_interface_impl]
356impl ToLLVMType for PointerType {
357    fn convert(
358        &self,
359        _ctx: &Context,
360        llvm_ctx: &LLVMContext,
361        _cctx: &mut ConversionContext,
362    ) -> Result<LLVMType> {
363        Ok(llvm_pointer_type_in_context(llvm_ctx, self.address_space()))
364    }
365}
366
367#[type_interface_impl]
368impl ToLLVMType for StructType {
369    fn convert(
370        &self,
371        ctx: &Context,
372        llvm_ctx: &LLVMContext,
373        cctx: &mut ConversionContext,
374    ) -> Result<LLVMType> {
375        if self.is_opaque() {
376            let name = self.name().expect("Opaqaue struct must have a name");
377            Ok(llvm_struct_create_named(llvm_ctx, name.as_str()))
378        } else {
379            let field_types = self
380                .fields()
381                .map(|fty| convert_type(ctx, llvm_ctx, cctx, fty))
382                .collect::<Result<Vec<_>>>()?;
383            if let Some(name) = self.name() {
384                match cctx.structs_map.entry(name) {
385                    hash_map::Entry::Occupied(entry) => Ok(*entry.get()),
386                    hash_map::Entry::Vacant(entry) => {
387                        let str_ty = llvm_struct_create_named(llvm_ctx, entry.key());
388                        llvm_struct_set_body(str_ty, &field_types, false);
389                        entry.insert(str_ty);
390                        Ok(str_ty)
391                    }
392                }
393            } else {
394                Ok(llvm_struct_type_in_context(llvm_ctx, &field_types, false))
395            }
396        }
397    }
398}
399
400#[type_interface_impl]
401impl ToLLVMType for VectorType {
402    fn convert(
403        &self,
404        ctx: &Context,
405        llvm_ctx: &LLVMContext,
406        cctx: &mut ConversionContext,
407    ) -> Result<LLVMType> {
408        let elem_ty = convert_type(ctx, llvm_ctx, cctx, self.elem_type())?;
409        let num_elems = self.num_elements();
410        if self.is_scalable() {
411            Ok(llvm_scalable_vector_type(elem_ty, num_elems))
412        } else {
413            Ok(llvm_vector_type(elem_ty, num_elems))
414        }
415    }
416}
417
418#[type_interface_impl]
419impl ToLLVMType for FP32Type {
420    fn convert(
421        &self,
422        _ctx: &Context,
423        llvm_ctx: &LLVMContext,
424        _cctx: &mut ConversionContext,
425    ) -> Result<LLVMType> {
426        Ok(llvm_float_type_in_context(llvm_ctx))
427    }
428}
429
430#[type_interface_impl]
431impl ToLLVMType for FP64Type {
432    fn convert(
433        &self,
434        _ctx: &Context,
435        llvm_ctx: &LLVMContext,
436        _cctx: &mut ConversionContext,
437    ) -> Result<LLVMType> {
438        Ok(llvm_double_type_in_context(llvm_ctx))
439    }
440}
441
442#[type_interface_impl]
443impl ToLLVMType for FP16Type {
444    fn convert(
445        &self,
446        _ctx: &Context,
447        llvm_ctx: &LLVMContext,
448        _cctx: &mut ConversionContext,
449    ) -> Result<LLVMType> {
450        Ok(llvm_half_type_in_context(llvm_ctx))
451    }
452}
453
454/// Convert a pliron [Type] to [LLVMType].
455pub fn convert_type(
456    ctx: &Context,
457    llvm_ctx: &LLVMContext,
458    cctx: &mut ConversionContext,
459    ty: TypeHandle,
460) -> Result<LLVMType> {
461    if let Some(cached) = cctx.type_cache.get(&ty) {
462        return Ok(*cached);
463    }
464    if let Some(converter) = type_cast::<dyn ToLLVMType>(&*ty.deref(ctx)) {
465        let llvm_ty = converter.convert(ctx, llvm_ctx, cctx)?;
466        cctx.type_cache.insert(ty, llvm_ty);
467        return Ok(llvm_ty);
468    }
469
470    input_err_noloc!(ToLLVMErr::MissingTypeConversion(
471        ty.deref(ctx).get_type_id().to_string()
472    ))
473}
474
475fn convert_value_operand(
476    cctx: &mut ConversionContext,
477    ctx: &Context,
478    value: &Value,
479) -> Result<LLVMValue> {
480    match cctx.value_map.get(value) {
481        Some(v) => Ok(*v),
482        None => {
483            input_err_noloc!(ToLLVMErr::UndefinedValue(value.unique_name(ctx).into()))
484        }
485    }
486}
487
488fn convert_block_operand(
489    cctx: &mut ConversionContext,
490    ctx: &Context,
491    block: Ptr<BasicBlock>,
492) -> Result<LLVMBasicBlock> {
493    match cctx.block_map.get(&block) {
494        Some(v) => Ok(*v),
495        None => {
496            input_err_noloc!(ToLLVMErr::UndefinedBlock(block.unique_name(ctx).into()))
497        }
498    }
499}
500
501macro_rules! to_llvm_value_int_bin_op {
502    (
503        $op_name:ident, $builder_function:ident
504    ) => {
505        #[pliron::derive::op_interface_impl]
506        impl ToLLVMValue for $op_name {
507            fn convert(
508                &self,
509                ctx: &Context,
510                _llvm_ctx: &LLVMContext,
511                cctx: &mut ConversionContext,
512            ) -> Result<LLVMValue> {
513                let op = self.get_operation().deref(ctx);
514                let (lhs, rhs) = (op.get_operand(0), op.get_operand(1));
515                let lhs = convert_value_operand(cctx, ctx, &lhs)?;
516                let rhs = convert_value_operand(cctx, ctx, &rhs)?;
517                Ok($builder_function(
518                    &cctx.builder,
519                    lhs,
520                    rhs,
521                    &self.get_result(ctx).unique_name(ctx),
522                ))
523            }
524        }
525    };
526}
527
528to_llvm_value_int_bin_op!(AddOp, llvm_build_add);
529to_llvm_value_int_bin_op!(SubOp, llvm_build_sub);
530to_llvm_value_int_bin_op!(MulOp, llvm_build_mul);
531to_llvm_value_int_bin_op!(SDivOp, llvm_build_sdiv);
532to_llvm_value_int_bin_op!(UDivOp, llvm_build_udiv);
533to_llvm_value_int_bin_op!(URemOp, llvm_build_urem);
534to_llvm_value_int_bin_op!(SRemOp, llvm_build_srem);
535to_llvm_value_int_bin_op!(AndOp, llvm_build_and);
536to_llvm_value_int_bin_op!(OrOp, llvm_build_or);
537to_llvm_value_int_bin_op!(XorOp, llvm_build_xor);
538to_llvm_value_int_bin_op!(ShlOp, llvm_build_shl);
539to_llvm_value_int_bin_op!(LShrOp, llvm_build_lshr);
540to_llvm_value_int_bin_op!(AShrOp, llvm_build_ashr);
541
542#[op_interface_impl]
543impl ToLLVMValue for AllocaOp {
544    fn convert(
545        &self,
546        ctx: &Context,
547        llvm_ctx: &LLVMContext,
548        cctx: &mut ConversionContext,
549    ) -> Result<LLVMValue> {
550        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_pointee_type(ctx))?;
551        let size = convert_value_operand(cctx, ctx, &self.get_operand(ctx))?;
552        let alloca_op = llvm_build_array_alloca(
553            &cctx.builder,
554            ty,
555            size,
556            &self.get_result(ctx).unique_name(ctx),
557        );
558        if let Some(alignment) = self.alignment(ctx) {
559            llvm_set_alignment(alloca_op, alignment);
560        }
561        Ok(alloca_op)
562    }
563}
564
565#[op_interface_impl]
566impl ToLLVMValue for BitcastOp {
567    fn convert(
568        &self,
569        ctx: &Context,
570        llvm_ctx: &LLVMContext,
571        cctx: &mut ConversionContext,
572    ) -> Result<LLVMValue> {
573        let arg = convert_value_operand(cctx, ctx, &self.get_operand(ctx))?;
574        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
575        let bitcast_op = llvm_build_bitcast(
576            &cctx.builder,
577            arg,
578            ty,
579            &self.get_result(ctx).unique_name(ctx),
580        );
581        Ok(bitcast_op)
582    }
583}
584
585#[op_interface_impl]
586impl ToLLVMValue for AddrSpaceCastOp {
587    fn convert(
588        &self,
589        ctx: &Context,
590        llvm_ctx: &LLVMContext,
591        cctx: &mut ConversionContext,
592    ) -> Result<LLVMValue> {
593        let arg = convert_value_operand(cctx, ctx, &self.get_operand(ctx))?;
594        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
595        let addrspacecast_op = llvm_build_addrspacecast(
596            &cctx.builder,
597            arg,
598            ty,
599            &self.get_result(ctx).unique_name(ctx),
600        );
601        Ok(addrspacecast_op)
602    }
603}
604
605fn link_succ_operands_with_phis(
606    ctx: &Context,
607    cctx: &mut ConversionContext,
608    source_block: Ptr<BasicBlock>,
609    target_block: LLVMBasicBlock,
610    opds: Vec<Value>,
611) -> Result<()> {
612    let mut phis = vec![];
613    for inst in instruction_iter(target_block) {
614        if !llvm_is_a::phi_node(inst) {
615            break;
616        };
617        phis.push(inst);
618    }
619
620    if phis.len() != opds.len() {
621        return input_err!(
622            source_block.deref(ctx).loc(),
623            ToLLVMErr::NumBlockArgsNumPhisMismatch
624        );
625    }
626
627    let source_block = convert_block_operand(cctx, ctx, source_block)?;
628
629    for (idx, arg) in opds.iter().enumerate() {
630        let arg = convert_value_operand(cctx, ctx, arg)?;
631        llvm_add_incoming(phis[idx], &[arg], &[source_block]);
632    }
633    Ok(())
634}
635
636#[op_interface_impl]
637impl ToLLVMValue for BrOp {
638    fn convert(
639        &self,
640        ctx: &Context,
641        _llvm_ctx: &LLVMContext,
642        cctx: &mut ConversionContext,
643    ) -> Result<LLVMValue> {
644        let op = self.get_operation().deref(ctx);
645        let succ = op.get_successor(0);
646        let succ_llvm = convert_block_operand(cctx, ctx, succ)?;
647        let branch_op = llvm_build_br(&cctx.builder, succ_llvm);
648
649        // Link the arguments we pass to the block with the PHIs there.
650        link_succ_operands_with_phis(
651            ctx,
652            cctx,
653            op.get_parent_block().expect("Unlinked operation"),
654            succ_llvm,
655            self.successor_operands(ctx, 0),
656        )?;
657
658        Ok(branch_op)
659    }
660}
661
662#[op_interface_impl]
663impl ToLLVMValue for CondBrOp {
664    fn convert(
665        &self,
666        ctx: &Context,
667        _llvm_ctx: &LLVMContext,
668        cctx: &mut ConversionContext,
669    ) -> Result<LLVMValue> {
670        let op = self.get_operation().deref(ctx);
671        let (true_succ, false_succ) = (op.get_successor(0), op.get_successor(1));
672        let true_succ_llvm = convert_block_operand(cctx, ctx, true_succ)?;
673        let false_succ_llvm = convert_block_operand(cctx, ctx, false_succ)?;
674        let cond = convert_value_operand(cctx, ctx, &self.get_operand_condition(ctx))?;
675
676        let branch_op = llvm_build_cond_br(&cctx.builder, cond, true_succ_llvm, false_succ_llvm);
677
678        // Link the arguments we pass to the block with the PHIs there.
679        link_succ_operands_with_phis(
680            ctx,
681            cctx,
682            op.get_parent_block().expect("Unlinked operation"),
683            true_succ_llvm,
684            self.successor_operands(ctx, 0),
685        )?;
686        link_succ_operands_with_phis(
687            ctx,
688            cctx,
689            op.get_parent_block().expect("Unlinked operation"),
690            false_succ_llvm,
691            self.successor_operands(ctx, 1),
692        )?;
693
694        Ok(branch_op)
695    }
696}
697
698#[op_interface_impl]
699impl ToLLVMValue for SwitchOp {
700    fn convert(
701        &self,
702        ctx: &Context,
703        llvm_ctx: &LLVMContext,
704        cctx: &mut ConversionContext,
705    ) -> Result<LLVMValue> {
706        let op = self.get_operation().deref(ctx);
707        let cond = convert_value_operand(cctx, ctx, &self.get_operand_condition(ctx))?;
708        let default_succ = convert_block_operand(cctx, ctx, self.default_dest(ctx))?;
709        let switch_op = llvm_build_switch(
710            &cctx.builder,
711            cond,
712            default_succ,
713            self.cases(ctx).len() as u32,
714        );
715
716        // Link the arguments we pass to the block with the PHIs there.
717        link_succ_operands_with_phis(
718            ctx,
719            cctx,
720            op.get_parent_block().expect("Unlinked operation"),
721            default_succ,
722            self.default_dest_operands(ctx),
723        )?;
724        for case in self.cases(ctx) {
725            let succ_llvm = convert_block_operand(cctx, ctx, case.dest)?;
726            link_succ_operands_with_phis(
727                ctx,
728                cctx,
729                op.get_parent_block().expect("Unlinked operation"),
730                succ_llvm,
731                case.dest_opds,
732            )?;
733
734            let int_ty = case.value.get_type();
735            let int_ty_llvm = convert_type(ctx, llvm_ctx, cctx, int_ty.into())?;
736            let ap_int_val: APInt = case.value.clone().into();
737            let case_const_val = llvm_const_int(int_ty_llvm, ap_int_val.to_u64(), false);
738
739            llvm_add_case(switch_op, case_const_val, succ_llvm);
740        }
741
742        Ok(switch_op)
743    }
744}
745
746#[op_interface_impl]
747impl ToLLVMValue for IndirectBrOp {
748    fn convert(
749        &self,
750        ctx: &Context,
751        _llvm_ctx: &LLVMContext,
752        cctx: &mut ConversionContext,
753    ) -> Result<LLVMValue> {
754        let op = self.get_operation().deref(ctx);
755        let addr = convert_value_operand(cctx, ctx, &self.get_operand_address(ctx))?;
756        let dests = self.destinations(ctx);
757        let indirect_br_op = llvm_build_indirect_br(&cctx.builder, addr, dests.len() as u32);
758
759        for dest in dests {
760            let succ_llvm = convert_block_operand(cctx, ctx, dest.dest)?;
761            llvm_add_destination(indirect_br_op, succ_llvm);
762            link_succ_operands_with_phis(
763                ctx,
764                cctx,
765                op.get_parent_block().expect("Unlinked operation"),
766                succ_llvm,
767                dest.dest_opds,
768            )?;
769        }
770
771        Ok(indirect_br_op)
772    }
773}
774
775#[op_interface_impl]
776impl ToLLVMValue for LoadOp {
777    fn convert(
778        &self,
779        ctx: &Context,
780        llvm_ctx: &LLVMContext,
781        cctx: &mut ConversionContext,
782    ) -> Result<LLVMValue> {
783        let pointee_ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
784        let ptr = convert_value_operand(cctx, ctx, &self.get_operand(ctx))?;
785        let load_op = llvm_build_load2(
786            &cctx.builder,
787            pointee_ty,
788            ptr,
789            &self.get_result(ctx).unique_name(ctx),
790        );
791        if let Some(alignment) = self.alignment(ctx) {
792            llvm_set_alignment(load_op, alignment);
793        }
794        Ok(load_op)
795    }
796}
797
798#[op_interface_impl]
799impl ToLLVMValue for StoreOp {
800    fn convert(
801        &self,
802        ctx: &Context,
803        _llvm_ctx: &LLVMContext,
804        cctx: &mut ConversionContext,
805    ) -> Result<LLVMValue> {
806        let value = convert_value_operand(cctx, ctx, &self.get_operand_value(ctx))?;
807        let ptr = convert_value_operand(cctx, ctx, &self.get_operand_address(ctx))?;
808        let store_op = llvm_build_store(&cctx.builder, value, ptr);
809        if let Some(alignment) = self.alignment(ctx) {
810            llvm_set_alignment(store_op, alignment);
811        }
812        Ok(store_op)
813    }
814}
815
816/// Map a pliron [AtomicOrderingAttr] to its LLVM-C counterpart.
817fn convert_atomic_ordering(o: &AtomicOrderingAttr) -> LLVMAtomicOrdering {
818    match o {
819        AtomicOrderingAttr::Monotonic => LLVMAtomicOrdering::LLVMAtomicOrderingMonotonic,
820        AtomicOrderingAttr::Acquire => LLVMAtomicOrdering::LLVMAtomicOrderingAcquire,
821        AtomicOrderingAttr::Release => LLVMAtomicOrdering::LLVMAtomicOrderingRelease,
822        AtomicOrderingAttr::AcqRel => LLVMAtomicOrdering::LLVMAtomicOrderingAcquireRelease,
823        AtomicOrderingAttr::SeqCst => LLVMAtomicOrdering::LLVMAtomicOrderingSequentiallyConsistent,
824    }
825}
826
827/// Map a pliron [AtomicRmwKindAttr] to its LLVM-C counterpart.
828fn convert_rmw_kind(k: &AtomicRmwKindAttr) -> LLVMAtomicRMWBinOp {
829    match k {
830        AtomicRmwKindAttr::Xchg => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpXchg,
831        AtomicRmwKindAttr::Add => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpAdd,
832        AtomicRmwKindAttr::Sub => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpSub,
833        AtomicRmwKindAttr::And => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpAnd,
834        AtomicRmwKindAttr::Nand => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpNand,
835        AtomicRmwKindAttr::Or => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpOr,
836        AtomicRmwKindAttr::Xor => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpXor,
837        AtomicRmwKindAttr::Max => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpMax,
838        AtomicRmwKindAttr::Min => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpMin,
839        AtomicRmwKindAttr::UMax => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpUMax,
840        AtomicRmwKindAttr::UMin => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpUMin,
841        AtomicRmwKindAttr::FAdd => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFAdd,
842        AtomicRmwKindAttr::FSub => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFSub,
843        AtomicRmwKindAttr::FMax => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFMax,
844        AtomicRmwKindAttr::FMin => LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFMin,
845    }
846}
847
848#[op_interface_impl]
849impl ToLLVMValue for AtomicRmwOp {
850    fn convert(
851        &self,
852        ctx: &Context,
853        llvm_ctx: &LLVMContext,
854        cctx: &mut ConversionContext,
855    ) -> Result<LLVMValue> {
856        let (ptr_opd, val_opd) = {
857            let op = self.get_operation().deref(ctx);
858            (op.get_operand(0), op.get_operand(1))
859        };
860        let ptr = convert_value_operand(cctx, ctx, &ptr_opd)?;
861        let val = convert_value_operand(cctx, ctx, &val_opd)?;
862        let kind = convert_rmw_kind(
863            &self
864                .get_attr_llvm_rmw_kind(ctx)
865                .expect("atomicrmw missing rmw kind"),
866        );
867        let ordering = convert_atomic_ordering(
868            &self
869                .get_attr_llvm_rmw_ordering(ctx)
870                .expect("atomicrmw missing ordering"),
871        );
872        let scope = self
873            .get_attr_llvm_rmw_syncscope(ctx)
874            .map(|s| String::from((*s).clone()))
875            .unwrap_or_default();
876        let ssid = llvm_get_sync_scope_id(llvm_ctx, &scope);
877        Ok(llvm_build_atomic_rmw(
878            &cctx.builder,
879            kind,
880            ptr,
881            val,
882            ordering,
883            ssid,
884        ))
885    }
886}
887
888#[op_interface_impl]
889impl ToLLVMValue for AtomicCmpxchgOp {
890    fn convert(
891        &self,
892        ctx: &Context,
893        llvm_ctx: &LLVMContext,
894        cctx: &mut ConversionContext,
895    ) -> Result<LLVMValue> {
896        let (ptr_opd, cmp_opd, new_opd) = {
897            let op = self.get_operation().deref(ctx);
898            (op.get_operand(0), op.get_operand(1), op.get_operand(2))
899        };
900        let ptr = convert_value_operand(cctx, ctx, &ptr_opd)?;
901        let cmp = convert_value_operand(cctx, ctx, &cmp_opd)?;
902        let new = convert_value_operand(cctx, ctx, &new_opd)?;
903        let success = convert_atomic_ordering(
904            &self
905                .get_attr_llvm_cas_success_ordering(ctx)
906                .expect("cmpxchg missing success ordering"),
907        );
908        let failure = convert_atomic_ordering(
909            &self
910                .get_attr_llvm_cas_failure_ordering(ctx)
911                .expect("cmpxchg missing failure ordering"),
912        );
913        let scope = self
914            .get_attr_llvm_cas_syncscope(ctx)
915            .map(|s| String::from((*s).clone()))
916            .unwrap_or_default();
917        let ssid = llvm_get_sync_scope_id(llvm_ctx, &scope);
918        Ok(llvm_build_atomic_cmpxchg(
919            &cctx.builder,
920            ptr,
921            cmp,
922            new,
923            success,
924            failure,
925            ssid,
926        ))
927    }
928}
929
930#[op_interface_impl]
931impl ToLLVMValue for FenceOp {
932    fn convert(
933        &self,
934        ctx: &Context,
935        llvm_ctx: &LLVMContext,
936        cctx: &mut ConversionContext,
937    ) -> Result<LLVMValue> {
938        let ordering = convert_atomic_ordering(
939            &self
940                .get_attr_llvm_fence_ordering(ctx)
941                .expect("fence missing ordering"),
942        );
943        let scope = self
944            .get_attr_llvm_fence_syncscope(ctx)
945            .map(|s| String::from((*s).clone()))
946            .unwrap_or_default();
947        let ssid = llvm_get_sync_scope_id(llvm_ctx, &scope);
948        Ok(llvm_build_fence(&cctx.builder, ordering, ssid, ""))
949    }
950}
951
952#[op_interface_impl]
953impl ToLLVMValue for AtomicLoadOp {
954    fn convert(
955        &self,
956        ctx: &Context,
957        llvm_ctx: &LLVMContext,
958        cctx: &mut ConversionContext,
959    ) -> Result<LLVMValue> {
960        let (ptr_opd, result_val) = {
961            let op = self.get_operation().deref(ctx);
962            (op.get_operand(0), op.get_result(0))
963        };
964        let pointee_ty = convert_type(ctx, llvm_ctx, cctx, result_val.get_type(ctx))?;
965        let ptr = convert_value_operand(cctx, ctx, &ptr_opd)?;
966        let load = llvm_build_load2(&cctx.builder, pointee_ty, ptr, &result_val.unique_name(ctx));
967        let ordering = convert_atomic_ordering(
968            &self
969                .get_attr_llvm_ld_ordering(ctx)
970                .expect("atomic load missing ordering"),
971        );
972        llvm_set_ordering(load, ordering);
973        let scope = self
974            .get_attr_llvm_ld_syncscope(ctx)
975            .map(|s| String::from((*s).clone()))
976            .unwrap_or_default();
977        llvm_set_atomic_sync_scope_id(load, llvm_get_sync_scope_id(llvm_ctx, &scope));
978        if let Some(alignment) = self.alignment(ctx) {
979            llvm_set_alignment(load, alignment);
980        }
981        Ok(load)
982    }
983}
984
985#[op_interface_impl]
986impl ToLLVMValue for AtomicStoreOp {
987    fn convert(
988        &self,
989        ctx: &Context,
990        llvm_ctx: &LLVMContext,
991        cctx: &mut ConversionContext,
992    ) -> Result<LLVMValue> {
993        let (val_opd, ptr_opd) = {
994            let op = self.get_operation().deref(ctx);
995            (op.get_operand(0), op.get_operand(1))
996        };
997        let value = convert_value_operand(cctx, ctx, &val_opd)?;
998        let ptr = convert_value_operand(cctx, ctx, &ptr_opd)?;
999        let store = llvm_build_store(&cctx.builder, value, ptr);
1000        let ordering = convert_atomic_ordering(
1001            &self
1002                .get_attr_llvm_st_ordering(ctx)
1003                .expect("atomic store missing ordering"),
1004        );
1005        llvm_set_ordering(store, ordering);
1006        let scope = self
1007            .get_attr_llvm_st_syncscope(ctx)
1008            .map(|s| String::from((*s).clone()))
1009            .unwrap_or_default();
1010        llvm_set_atomic_sync_scope_id(store, llvm_get_sync_scope_id(llvm_ctx, &scope));
1011        if let Some(alignment) = self.alignment(ctx) {
1012            llvm_set_alignment(store, alignment);
1013        }
1014        Ok(store)
1015    }
1016}
1017
1018#[op_interface_impl]
1019impl ToLLVMValue for InlineAsmOp {
1020    fn convert(
1021        &self,
1022        ctx: &Context,
1023        llvm_ctx: &LLVMContext,
1024        cctx: &mut ConversionContext,
1025    ) -> Result<LLVMValue> {
1026        let (arg_opds, result_val) = {
1027            let op = self.get_operation().deref(ctx);
1028            let n = op.get_num_operands();
1029            let args: Vec<Value> = (0..n).map(|i| op.get_operand(i)).collect();
1030            (args, op.get_result(0))
1031        };
1032        let args: Vec<LLVMValue> = arg_opds
1033            .iter()
1034            .map(|v| convert_value_operand(cctx, ctx, v))
1035            .collect::<Result<_>>()?;
1036        let result_ty = result_val.get_type(ctx);
1037        let result_llvm_ty = convert_type(ctx, llvm_ctx, cctx, result_ty)?;
1038        let arg_types: Vec<LLVMType> = args.iter().map(|a| llvm_type_of(*a)).collect();
1039        let fn_ty = llvm_function_type(result_llvm_ty, &arg_types, false);
1040        let asm = String::from(
1041            (*self
1042                .get_attr_inline_asm_template(ctx)
1043                .expect("inline asm missing template"))
1044            .clone(),
1045        );
1046        let constraints = String::from(
1047            (*self
1048                .get_attr_inline_asm_constraints(ctx)
1049                .expect("inline asm missing constraints"))
1050            .clone(),
1051        );
1052        // `has_side_effects` is set unconditionally: this op does not model a
1053        // side-effects flag, and side-effecting asm is the safe default.
1054        // NOTE: the op's `inline_asm_convergent` attribute is not applied here.
1055        // `convergent` is an LLVM call-site attribute (not part of the inline-asm
1056        // value), so converting to LLVM IR drops the convergent flag.
1057        let asm_val = llvm_get_inline_asm(
1058            fn_ty,
1059            &asm,
1060            &constraints,
1061            true,
1062            false,
1063            LLVMInlineAsmDialect::LLVMInlineAsmDialectATT,
1064            false,
1065        );
1066        let name = if result_ty.deref(ctx).is::<VoidType>() {
1067            String::new()
1068        } else {
1069            result_val.unique_name(ctx).to_string()
1070        };
1071        Ok(llvm_build_call2(
1072            &cctx.builder,
1073            fn_ty,
1074            asm_val,
1075            &args,
1076            &name,
1077        ))
1078    }
1079}
1080
1081#[op_interface_impl]
1082impl ToLLVMValue for ICmpOp {
1083    fn convert(
1084        &self,
1085        ctx: &Context,
1086        _llvm_ctx: &LLVMContext,
1087        cctx: &mut ConversionContext,
1088    ) -> Result<LLVMValue> {
1089        let op = self.get_operation().deref(ctx);
1090        let predicate = convert_ipredicate(self.predicate(ctx));
1091        let lhs = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1092        let rhs = convert_value_operand(cctx, ctx, &op.get_operand(1))?;
1093        let icmp_op = llvm_build_icmp(
1094            &cctx.builder,
1095            predicate,
1096            lhs,
1097            rhs,
1098            &self.get_result(ctx).unique_name(ctx),
1099        );
1100        Ok(icmp_op)
1101    }
1102}
1103
1104#[op_interface_impl]
1105impl ToLLVMValue for ReturnOp {
1106    fn convert(
1107        &self,
1108        ctx: &Context,
1109        _llvm_ctx: &LLVMContext,
1110        cctx: &mut ConversionContext,
1111    ) -> Result<LLVMValue> {
1112        let ret_op = if let Some(retval) = self.retval(ctx) {
1113            let retval = convert_value_operand(cctx, ctx, &retval)?;
1114            llvm_build_ret(&cctx.builder, retval)
1115        } else {
1116            llvm_build_ret_void(&cctx.builder)
1117        };
1118        Ok(ret_op)
1119    }
1120}
1121
1122#[op_interface_impl]
1123impl ToLLVMValue for UnreachableOp {
1124    fn convert(
1125        &self,
1126        _ctx: &Context,
1127        _llvm_ctx: &LLVMContext,
1128        cctx: &mut ConversionContext,
1129    ) -> Result<LLVMValue> {
1130        Ok(llvm_build_unreachable(&cctx.builder))
1131    }
1132}
1133
1134#[op_interface_impl]
1135impl ToLLVMValue for ConstantOp {
1136    fn convert(
1137        &self,
1138        ctx: &Context,
1139        llvm_ctx: &LLVMContext,
1140        cctx: &mut ConversionContext,
1141    ) -> Result<LLVMValue> {
1142        <Self as ToLLVMConstValue>::convert(self, ctx, llvm_ctx, cctx)
1143    }
1144}
1145
1146#[op_interface_impl]
1147impl ToLLVMValue for ZeroOp {
1148    fn convert(
1149        &self,
1150        ctx: &Context,
1151        llvm_ctx: &LLVMContext,
1152        cctx: &mut ConversionContext,
1153    ) -> Result<LLVMValue> {
1154        <Self as ToLLVMConstValue>::convert(self, ctx, llvm_ctx, cctx)
1155    }
1156}
1157
1158#[op_interface_impl]
1159impl ToLLVMValue for IntToPtrOp {
1160    fn convert(
1161        &self,
1162        ctx: &Context,
1163        llvm_ctx: &LLVMContext,
1164        cctx: &mut ConversionContext,
1165    ) -> Result<LLVMValue> {
1166        let op = self.get_operation().deref(ctx);
1167        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1168        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1169        let inttoptr_op = llvm_build_int_to_ptr(
1170            &cctx.builder,
1171            arg,
1172            ty,
1173            &self.get_result(ctx).unique_name(ctx),
1174        );
1175        Ok(inttoptr_op)
1176    }
1177}
1178
1179#[op_interface_impl]
1180impl ToLLVMValue for PtrToIntOp {
1181    fn convert(
1182        &self,
1183        ctx: &Context,
1184        llvm_ctx: &LLVMContext,
1185        cctx: &mut ConversionContext,
1186    ) -> Result<LLVMValue> {
1187        let op = self.get_operation().deref(ctx);
1188        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1189        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1190        let ptrtoint_op = llvm_build_ptr_to_int(
1191            &cctx.builder,
1192            arg,
1193            ty,
1194            &self.get_result(ctx).unique_name(ctx),
1195        );
1196        Ok(ptrtoint_op)
1197    }
1198}
1199
1200#[op_interface_impl]
1201impl ToLLVMValue for UndefOp {
1202    fn convert(
1203        &self,
1204        ctx: &Context,
1205        llvm_ctx: &LLVMContext,
1206        cctx: &mut ConversionContext,
1207    ) -> Result<LLVMValue> {
1208        <Self as ToLLVMConstValue>::convert(self, ctx, llvm_ctx, cctx)
1209    }
1210}
1211
1212#[op_interface_impl]
1213impl ToLLVMValue for PoisonOp {
1214    fn convert(
1215        &self,
1216        ctx: &Context,
1217        llvm_ctx: &LLVMContext,
1218        cctx: &mut ConversionContext,
1219    ) -> Result<LLVMValue> {
1220        <Self as ToLLVMConstValue>::convert(self, ctx, llvm_ctx, cctx)
1221    }
1222}
1223
1224#[op_interface_impl]
1225impl ToLLVMValue for AddressOfOp {
1226    fn convert(
1227        &self,
1228        ctx: &Context,
1229        llvm_ctx: &LLVMContext,
1230        cctx: &mut ConversionContext,
1231    ) -> Result<LLVMValue> {
1232        <Self as ToLLVMConstValue>::convert(self, ctx, llvm_ctx, cctx)
1233    }
1234}
1235
1236#[op_interface_impl]
1237impl ToLLVMValue for BlockAddressOp {
1238    fn convert(
1239        &self,
1240        ctx: &Context,
1241        llvm_ctx: &LLVMContext,
1242        cctx: &mut ConversionContext,
1243    ) -> Result<LLVMValue> {
1244        <Self as ToLLVMConstValue>::convert(self, ctx, llvm_ctx, cctx)
1245    }
1246}
1247
1248#[op_interface_impl]
1249impl ToLLVMValue for BlockTagOp {
1250    fn convert(
1251        &self,
1252        ctx: &Context,
1253        llvm_ctx: &LLVMContext,
1254        cctx: &mut ConversionContext,
1255    ) -> Result<LLVMValue> {
1256        let cur_block = self
1257            .get_operation()
1258            .deref(ctx)
1259            .get_parent_block()
1260            .expect("BlockTagOp must be in a basic block");
1261        let cur_func = cur_block
1262            .deref(ctx)
1263            .get_parent_op(ctx)
1264            .expect("BlockTagOp must be in a basic block of a function");
1265        let cur_func =
1266            Operation::get_op::<FuncOp>(cur_func, ctx).expect("Block's parent op must be FuncOp");
1267        let cur_func_name = cur_func.get_symbol_name(ctx);
1268        let tag = self.get_tag_id(ctx);
1269
1270        let cur_llvm_block = cctx
1271            .block_map
1272            .get(&cur_block)
1273            .expect("Current block must be in block_map");
1274
1275        // Later on, for all llvm.blockaddress that refers to this tag, we use this info.
1276        cctx.block_tags
1277            .insert((cur_func_name, tag), *cur_llvm_block);
1278
1279        // Actual LLVM doesn't need a BlockTagOp.
1280        // Address is taken directly via the llvm.blockaddress instruction.
1281        Ok(llvm_const_null(llvm_pointer_type_in_context(llvm_ctx, 0)))
1282    }
1283}
1284
1285#[op_interface_impl]
1286impl ToLLVMValue for FreezeOp {
1287    fn convert(
1288        &self,
1289        ctx: &Context,
1290        _llvm_ctx: &LLVMContext,
1291        cctx: &mut ConversionContext,
1292    ) -> Result<LLVMValue> {
1293        let op = self.get_operation().deref(ctx);
1294        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1295        let freeze_op =
1296            llvm_build_freeze(&cctx.builder, arg, &self.get_result(ctx).unique_name(ctx));
1297        Ok(freeze_op)
1298    }
1299}
1300
1301#[op_interface_impl]
1302impl ToLLVMValue for CallOp {
1303    fn convert(
1304        &self,
1305        ctx: &Context,
1306        llvm_ctx: &LLVMContext,
1307        cctx: &mut ConversionContext,
1308    ) -> Result<LLVMValue> {
1309        let args: Vec<_> = self
1310            .args(ctx)
1311            .into_iter()
1312            .map(|v| convert_value_operand(cctx, ctx, &v))
1313            .collect::<Result<_>>()?;
1314        let ty = convert_type(ctx, llvm_ctx, cctx, self.callee_type(ctx))?;
1315        let res = self.get_result(ctx);
1316        let name = if res.get_type(ctx).deref(ctx).is::<VoidType>() {
1317            ""
1318        } else {
1319            &res.unique_name(ctx)
1320        };
1321        let callee = match self.callee(ctx) {
1322            CallOpCallable::Direct(callee_sym) => {
1323                *cctx.function_map.get(&callee_sym).ok_or_else(|| {
1324                    input_error_noloc!(ToLLVMErr::UndefinedValue(callee_sym.to_string()))
1325                })?
1326            }
1327            CallOpCallable::Indirect(callee) => convert_value_operand(cctx, ctx, &callee)?,
1328        };
1329        let call_val = llvm_build_call2(&cctx.builder, ty, callee, &args, name);
1330        if let Some(fmf) = self.get_attr_llvm_call_fastmath_flags(ctx)
1331            && llvm_can_value_use_fast_math_flags(call_val)
1332        {
1333            llvm_set_fast_math_flags(call_val, (*fmf).into());
1334        }
1335        Ok(call_val)
1336    }
1337}
1338
1339#[op_interface_impl]
1340impl ToLLVMValue for CallIntrinsicOp {
1341    fn convert(
1342        &self,
1343        ctx: &Context,
1344        llvm_ctx: &LLVMContext,
1345        cctx: &mut ConversionContext,
1346    ) -> Result<LLVMValue> {
1347        let op = self.get_operation().deref(ctx);
1348        let args: Vec<_> = (0..op.get_num_operands())
1349            .map(|i| convert_value_operand(cctx, ctx, &op.get_operand(i)))
1350            .collect::<Result<_>>()?;
1351        let fn_ty = convert_type(
1352            ctx,
1353            llvm_ctx,
1354            cctx,
1355            self.get_attr_llvm_intrinsic_type(ctx)
1356                .unwrap()
1357                .get_type(ctx),
1358        )?;
1359
1360        let intrinsic_name = <StringAttr as Into<String>>::into(
1361            self.get_attr_llvm_intrinsic_name(ctx)
1362                .expect("Intrinsic call does not name the intrinsic to be called")
1363                .clone(),
1364        );
1365
1366        let _intrinsic_id = llvm_lookup_intrinsic_id(&intrinsic_name).ok_or_else(|| {
1367            input_error_noloc!(ToLLVMErr::UndefinedValue(intrinsic_name.to_string()))
1368        })?;
1369
1370        // We just use llvm_add_function instead of llvm_get_intrinsic_declaration here
1371        // because the latter requires that (and I quote from Intrinsics.h::getOrInsertDeclaration):
1372        //   "For a declaration of an overloaded intrinsic, Tys must provide exactly one
1373        //    type for each overloaded type in the intrinsic."
1374        // I don't know how to determine that from just the name and argument types.
1375        let intrinsic_fn = llvm_get_named_function(cctx.cur_llvm_module, &intrinsic_name)
1376            .unwrap_or_else(|| llvm_add_function(cctx.cur_llvm_module, &intrinsic_name, fn_ty));
1377
1378        let res = self.get_result(ctx);
1379        let name = if res.get_type(ctx).deref(ctx).is::<VoidType>() {
1380            ""
1381        } else {
1382            &res.unique_name(ctx)
1383        };
1384
1385        let intrinsic_op = llvm_build_call2(&cctx.builder, fn_ty, intrinsic_fn, &args, name);
1386
1387        if let Some(fmf) = self.get_attr_llvm_intrinsic_fastmath_flags(ctx)
1388            && llvm_can_value_use_fast_math_flags(intrinsic_op)
1389        {
1390            llvm_set_fast_math_flags(intrinsic_op, (*fmf).into());
1391        }
1392
1393        Ok(intrinsic_op)
1394    }
1395}
1396
1397#[op_interface_impl]
1398impl ToLLVMValue for SExtOp {
1399    fn convert(
1400        &self,
1401        ctx: &Context,
1402        llvm_ctx: &LLVMContext,
1403        cctx: &mut ConversionContext,
1404    ) -> Result<LLVMValue> {
1405        let op = self.get_operation().deref(ctx);
1406        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1407        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1408        let sext_op = llvm_build_sext(
1409            &cctx.builder,
1410            arg,
1411            ty,
1412            &self.get_result(ctx).unique_name(ctx),
1413        );
1414        Ok(sext_op)
1415    }
1416}
1417
1418#[op_interface_impl]
1419impl ToLLVMValue for ZExtOp {
1420    fn convert(
1421        &self,
1422        ctx: &Context,
1423        llvm_ctx: &LLVMContext,
1424        cctx: &mut ConversionContext,
1425    ) -> Result<LLVMValue> {
1426        let op = self.get_operation().deref(ctx);
1427        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1428        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1429        let zext_op = llvm_build_zext(
1430            &cctx.builder,
1431            arg,
1432            ty,
1433            &self.get_result(ctx).unique_name(ctx),
1434        );
1435        // The built value may not even be an instruction, but a folded constant.
1436        if llvm_is_a::instruction(zext_op) {
1437            let nneg = self.nneg(ctx);
1438            llvm_set_nneg(zext_op, nneg);
1439        }
1440        Ok(zext_op)
1441    }
1442}
1443
1444#[op_interface_impl]
1445impl ToLLVMValue for TruncOp {
1446    fn convert(
1447        &self,
1448        ctx: &Context,
1449        llvm_ctx: &LLVMContext,
1450        cctx: &mut ConversionContext,
1451    ) -> Result<LLVMValue> {
1452        let op = self.get_operation().deref(ctx);
1453        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1454        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1455        let trunc_op = llvm_build_trunc(
1456            &cctx.builder,
1457            arg,
1458            ty,
1459            &self.get_result(ctx).unique_name(ctx),
1460        );
1461        Ok(trunc_op)
1462    }
1463}
1464
1465#[op_interface_impl]
1466impl ToLLVMValue for GetElementPtrOp {
1467    fn convert(
1468        &self,
1469        ctx: &Context,
1470        llvm_ctx: &LLVMContext,
1471        cctx: &mut ConversionContext,
1472    ) -> Result<LLVMValue> {
1473        let indices = self
1474            .indices(ctx)
1475            .iter()
1476            .map(|v| match v {
1477                crate::ops::GepIndex::Constant(c) => Ok(llvm_const_int(
1478                    llvm_int_type_in_context(llvm_ctx, 32),
1479                    Into::<u64>::into(*c),
1480                    false,
1481                )),
1482                crate::ops::GepIndex::Value(value) => convert_value_operand(cctx, ctx, value),
1483            })
1484            .collect::<Result<Vec<_>>>()?;
1485
1486        let base = convert_value_operand(cctx, ctx, &self.get_operand_src_ptr(ctx))?;
1487
1488        let src_elem_type = convert_type(ctx, llvm_ctx, cctx, self.src_elem_type(ctx))?;
1489        let gep_op = llvm_build_gep2(
1490            &cctx.builder,
1491            src_elem_type,
1492            base,
1493            &indices,
1494            &self.get_result(ctx).unique_name(ctx),
1495        );
1496        Ok(gep_op)
1497    }
1498}
1499
1500#[op_interface_impl]
1501impl ToLLVMValue for InsertValueOp {
1502    fn convert(
1503        &self,
1504        ctx: &Context,
1505        _llvm_ctx: &LLVMContext,
1506        cctx: &mut ConversionContext,
1507    ) -> Result<LLVMValue> {
1508        let op = self.get_operation().deref(ctx);
1509        let base = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1510        let value = convert_value_operand(cctx, ctx, &op.get_operand(1))?;
1511        let indices = self.indices(ctx);
1512        if indices.len() != 1 {
1513            return input_err!(op.loc(), ToLLVMErr::InsertExtractValueIndices);
1514        }
1515        let insert_op = llvm_build_insert_value(
1516            &cctx.builder,
1517            base,
1518            value,
1519            indices[0],
1520            &self.get_result(ctx).unique_name(ctx),
1521        );
1522        Ok(insert_op)
1523    }
1524}
1525
1526#[op_interface_impl]
1527impl ToLLVMValue for ExtractValueOp {
1528    fn convert(
1529        &self,
1530        ctx: &Context,
1531        _llvm_ctx: &LLVMContext,
1532        cctx: &mut ConversionContext,
1533    ) -> Result<LLVMValue> {
1534        let op = self.get_operation().deref(ctx);
1535        let base = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1536        let indices = self.indices(ctx);
1537        if indices.len() != 1 {
1538            return input_err!(op.loc(), ToLLVMErr::InsertExtractValueIndices);
1539        }
1540        let extract_op = llvm_build_extract_value(
1541            &cctx.builder,
1542            base,
1543            indices[0],
1544            &self.get_result(ctx).unique_name(ctx),
1545        );
1546        Ok(extract_op)
1547    }
1548}
1549
1550#[op_interface_impl]
1551impl ToLLVMValue for InsertElementOp {
1552    fn convert(
1553        &self,
1554        ctx: &Context,
1555        _llvm_ctx: &LLVMContext,
1556        cctx: &mut ConversionContext,
1557    ) -> Result<LLVMValue> {
1558        let base = convert_value_operand(cctx, ctx, &self.get_operand_vector(ctx))?;
1559        let value = convert_value_operand(cctx, ctx, &self.get_operand_element(ctx))?;
1560        let index = convert_value_operand(cctx, ctx, &self.get_operand_index(ctx))?;
1561        let insert_op = llvm_build_insert_element(
1562            &cctx.builder,
1563            base,
1564            value,
1565            index,
1566            &self.get_result(ctx).unique_name(ctx),
1567        );
1568        Ok(insert_op)
1569    }
1570}
1571
1572#[op_interface_impl]
1573impl ToLLVMValue for ExtractElementOp {
1574    fn convert(
1575        &self,
1576        ctx: &Context,
1577        _llvm_ctx: &LLVMContext,
1578        cctx: &mut ConversionContext,
1579    ) -> Result<LLVMValue> {
1580        let base = convert_value_operand(cctx, ctx, &self.get_operand_vector(ctx))?;
1581        let index = convert_value_operand(cctx, ctx, &self.get_operand_index(ctx))?;
1582        let extract_op = llvm_build_extract_element(
1583            &cctx.builder,
1584            base,
1585            index,
1586            &self.get_result(ctx).unique_name(ctx),
1587        );
1588        Ok(extract_op)
1589    }
1590}
1591
1592#[op_interface_impl]
1593impl ToLLVMValue for ShuffleVectorOp {
1594    fn convert(
1595        &self,
1596        ctx: &Context,
1597        llvm_ctx: &LLVMContext,
1598        cctx: &mut ConversionContext,
1599    ) -> Result<LLVMValue> {
1600        let mask = &self
1601            .get_attr_llvm_shuffle_vector_mask(ctx)
1602            .expect("ShuffleVectorOp missing mask attribute")
1603            .0;
1604        let op = self.get_operation().deref(ctx);
1605        let vec1 = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1606        let vec2 = convert_value_operand(cctx, ctx, &op.get_operand(1))?;
1607        let int_ty = llvm_int_type_in_context(llvm_ctx, 32);
1608
1609        let mask = mask
1610            .iter()
1611            .map(|&i| llvm_const_int(int_ty, i as u64, true))
1612            .collect::<Vec<LLVMValue>>();
1613        let mask = llvm_const_vector(&mask);
1614
1615        let shuffle_op = llvm_build_shuffle_vector(
1616            &cctx.builder,
1617            vec1,
1618            vec2,
1619            mask,
1620            &self.get_result(ctx).unique_name(ctx),
1621        );
1622        Ok(shuffle_op)
1623    }
1624}
1625
1626#[op_interface_impl]
1627impl ToLLVMValue for SelectOp {
1628    fn convert(
1629        &self,
1630        ctx: &Context,
1631        _llvm_ctx: &LLVMContext,
1632        cctx: &mut ConversionContext,
1633    ) -> Result<LLVMValue> {
1634        let op = self.get_operation().deref(ctx);
1635        let cond = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1636        let true_val = convert_value_operand(cctx, ctx, &op.get_operand(1))?;
1637        let false_val = convert_value_operand(cctx, ctx, &op.get_operand(2))?;
1638        let select_op = llvm_build_select(
1639            &cctx.builder,
1640            cond,
1641            true_val,
1642            false_val,
1643            &self.get_result(ctx).unique_name(ctx),
1644        );
1645        // The built value may not even be an instruction, but a folded constant.
1646        if let Some(fmf) = self.get_attr_llvm_select_fast_math_flags(ctx)
1647            && llvm_can_value_use_fast_math_flags(select_op)
1648        {
1649            llvm_set_fast_math_flags(select_op, (*fmf).into());
1650        }
1651        Ok(select_op)
1652    }
1653}
1654
1655#[op_interface_impl]
1656impl ToLLVMValue for FAddOp {
1657    fn convert(
1658        &self,
1659        ctx: &Context,
1660        _llvm_ctx: &LLVMContext,
1661        cctx: &mut ConversionContext,
1662    ) -> Result<LLVMValue> {
1663        let op = self.get_operation().deref(ctx);
1664        let (lhs, rhs) = (op.get_operand(0), op.get_operand(1));
1665        let lhs = convert_value_operand(cctx, ctx, &lhs)?;
1666        let rhs = convert_value_operand(cctx, ctx, &rhs)?;
1667        let inst = llvm_build_fadd(
1668            &cctx.builder,
1669            lhs,
1670            rhs,
1671            &self.get_result(ctx).unique_name(ctx),
1672        );
1673        // The built value may not even be an instruction, but a folded constant.
1674        if llvm_can_value_use_fast_math_flags(inst) {
1675            let fastmath = self.fast_math_flags(ctx);
1676            llvm_set_fast_math_flags(inst, fastmath.into());
1677        }
1678        Ok(inst)
1679    }
1680}
1681
1682#[op_interface_impl]
1683impl ToLLVMValue for FSubOp {
1684    fn convert(
1685        &self,
1686        ctx: &Context,
1687        _llvm_ctx: &LLVMContext,
1688        cctx: &mut ConversionContext,
1689    ) -> Result<LLVMValue> {
1690        let op = self.get_operation().deref(ctx);
1691        let (lhs, rhs) = (op.get_operand(0), op.get_operand(1));
1692        let lhs = convert_value_operand(cctx, ctx, &lhs)?;
1693        let rhs = convert_value_operand(cctx, ctx, &rhs)?;
1694        let inst = llvm_build_fsub(
1695            &cctx.builder,
1696            lhs,
1697            rhs,
1698            &self.get_result(ctx).unique_name(ctx),
1699        );
1700        // The built value may not even be an instruction, but a folded constant.
1701        if llvm_can_value_use_fast_math_flags(inst) {
1702            let fastmath = self.fast_math_flags(ctx);
1703            llvm_set_fast_math_flags(inst, fastmath.into());
1704        }
1705        Ok(inst)
1706    }
1707}
1708
1709#[op_interface_impl]
1710impl ToLLVMValue for FMulOp {
1711    fn convert(
1712        &self,
1713        ctx: &Context,
1714        _llvm_ctx: &LLVMContext,
1715        cctx: &mut ConversionContext,
1716    ) -> Result<LLVMValue> {
1717        let op = self.get_operation().deref(ctx);
1718        let (lhs, rhs) = (op.get_operand(0), op.get_operand(1));
1719        let lhs = convert_value_operand(cctx, ctx, &lhs)?;
1720        let rhs = convert_value_operand(cctx, ctx, &rhs)?;
1721        let inst = llvm_build_fmul(
1722            &cctx.builder,
1723            lhs,
1724            rhs,
1725            &self.get_result(ctx).unique_name(ctx),
1726        );
1727        // The built value may not even be an instruction, but a folded constant.
1728        if llvm_can_value_use_fast_math_flags(inst) {
1729            let fastmath = self.fast_math_flags(ctx);
1730            llvm_set_fast_math_flags(inst, fastmath.into());
1731        }
1732        Ok(inst)
1733    }
1734}
1735
1736#[op_interface_impl]
1737impl ToLLVMValue for FDivOp {
1738    fn convert(
1739        &self,
1740        ctx: &Context,
1741        _llvm_ctx: &LLVMContext,
1742        cctx: &mut ConversionContext,
1743    ) -> Result<LLVMValue> {
1744        let op = self.get_operation().deref(ctx);
1745        let (lhs, rhs) = (op.get_operand(0), op.get_operand(1));
1746        let lhs = convert_value_operand(cctx, ctx, &lhs)?;
1747        let rhs = convert_value_operand(cctx, ctx, &rhs)?;
1748        let inst = llvm_build_fdiv(
1749            &cctx.builder,
1750            lhs,
1751            rhs,
1752            &self.get_result(ctx).unique_name(ctx),
1753        );
1754        // The built value may not even be an instruction, but a folded constant.
1755        if llvm_can_value_use_fast_math_flags(inst) {
1756            let fastmath = self.fast_math_flags(ctx);
1757            llvm_set_fast_math_flags(inst, fastmath.into());
1758        }
1759        Ok(inst)
1760    }
1761}
1762
1763#[op_interface_impl]
1764impl ToLLVMValue for FRemOp {
1765    fn convert(
1766        &self,
1767        ctx: &Context,
1768        _llvm_ctx: &LLVMContext,
1769        cctx: &mut ConversionContext,
1770    ) -> Result<LLVMValue> {
1771        let op = self.get_operation().deref(ctx);
1772        let (lhs, rhs) = (op.get_operand(0), op.get_operand(1));
1773        let lhs = convert_value_operand(cctx, ctx, &lhs)?;
1774        let rhs = convert_value_operand(cctx, ctx, &rhs)?;
1775        let inst = llvm_build_frem(
1776            &cctx.builder,
1777            lhs,
1778            rhs,
1779            &self.get_result(ctx).unique_name(ctx),
1780        );
1781        // The built value may not even be an instruction, but a folded constant.
1782        if llvm_can_value_use_fast_math_flags(inst) {
1783            let fastmath = self.fast_math_flags(ctx);
1784            llvm_set_fast_math_flags(inst, fastmath.into());
1785        }
1786        Ok(inst)
1787    }
1788}
1789
1790#[op_interface_impl]
1791impl ToLLVMValue for FCmpOp {
1792    fn convert(
1793        &self,
1794        ctx: &Context,
1795        _llvm_ctx: &LLVMContext,
1796        cctx: &mut ConversionContext,
1797    ) -> Result<LLVMValue> {
1798        let op = self.get_operation().deref(ctx);
1799        let predicate = convert_fpredicate(self.predicate(ctx));
1800        let lhs = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1801        let rhs = convert_value_operand(cctx, ctx, &op.get_operand(1))?;
1802        let fcmp_op = llvm_build_fcmp(
1803            &cctx.builder,
1804            predicate,
1805            lhs,
1806            rhs,
1807            &self.get_result(ctx).unique_name(ctx),
1808        );
1809        // The built value may not even be an instruction, but a folded constant.
1810        if llvm_can_value_use_fast_math_flags(fcmp_op) {
1811            let fastmath = self.fast_math_flags(ctx);
1812            llvm_set_fast_math_flags(fcmp_op, fastmath.into());
1813        }
1814        Ok(fcmp_op)
1815    }
1816}
1817
1818#[op_interface_impl]
1819impl ToLLVMValue for FNegOp {
1820    fn convert(
1821        &self,
1822        ctx: &Context,
1823        _llvm_ctx: &LLVMContext,
1824        cctx: &mut ConversionContext,
1825    ) -> Result<LLVMValue> {
1826        let op = self.get_operation().deref(ctx);
1827        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1828        let inst = llvm_build_fneg(&cctx.builder, arg, &self.get_result(ctx).unique_name(ctx));
1829        // The built value may not even be an instruction, but a folded constant.
1830        if llvm_can_value_use_fast_math_flags(inst) {
1831            let fastmath = self.fast_math_flags(ctx);
1832            llvm_set_fast_math_flags(inst, fastmath.into());
1833        }
1834        Ok(inst)
1835    }
1836}
1837
1838#[op_interface_impl]
1839impl ToLLVMValue for FPExtOp {
1840    fn convert(
1841        &self,
1842        ctx: &Context,
1843        llvm_ctx: &LLVMContext,
1844        cctx: &mut ConversionContext,
1845    ) -> Result<LLVMValue> {
1846        let op = self.get_operation().deref(ctx);
1847        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1848        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1849        let fpext_op = llvm_build_fpext(
1850            &cctx.builder,
1851            arg,
1852            ty,
1853            &self.get_result(ctx).unique_name(ctx),
1854        );
1855        // The built value may not even be an instruction, but a folded constant.
1856        if llvm_can_value_use_fast_math_flags(fpext_op) {
1857            let fastmath = self.fast_math_flags(ctx);
1858            llvm_set_fast_math_flags(fpext_op, fastmath.into());
1859        }
1860        Ok(fpext_op)
1861    }
1862}
1863
1864#[op_interface_impl]
1865impl ToLLVMValue for FPTruncOp {
1866    fn convert(
1867        &self,
1868        ctx: &Context,
1869        llvm_ctx: &LLVMContext,
1870        cctx: &mut ConversionContext,
1871    ) -> Result<LLVMValue> {
1872        let op = self.get_operation().deref(ctx);
1873        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1874        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1875        let fptrunc_op = llvm_build_fptrunc(
1876            &cctx.builder,
1877            arg,
1878            ty,
1879            &self.get_result(ctx).unique_name(ctx),
1880        );
1881        // The built value may not even be an instruction, but a folded constant.
1882        if llvm_can_value_use_fast_math_flags(fptrunc_op) {
1883            llvm_set_fast_math_flags(fptrunc_op, self.fast_math_flags(ctx).into());
1884        }
1885        Ok(fptrunc_op)
1886    }
1887}
1888
1889#[op_interface_impl]
1890impl ToLLVMValue for FPToSIOp {
1891    fn convert(
1892        &self,
1893        ctx: &Context,
1894        llvm_ctx: &LLVMContext,
1895        cctx: &mut ConversionContext,
1896    ) -> Result<LLVMValue> {
1897        let op = self.get_operation().deref(ctx);
1898        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1899        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1900        let fptosi_op = llvm_build_fptosi(
1901            &cctx.builder,
1902            arg,
1903            ty,
1904            &self.get_result(ctx).unique_name(ctx),
1905        );
1906        Ok(fptosi_op)
1907    }
1908}
1909
1910#[op_interface_impl]
1911impl ToLLVMValue for SIToFPOp {
1912    fn convert(
1913        &self,
1914        ctx: &Context,
1915        llvm_ctx: &LLVMContext,
1916        cctx: &mut ConversionContext,
1917    ) -> Result<LLVMValue> {
1918        let op = self.get_operation().deref(ctx);
1919        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1920        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1921        let sitofp_op = llvm_build_sitofp(
1922            &cctx.builder,
1923            arg,
1924            ty,
1925            &self.get_result(ctx).unique_name(ctx),
1926        );
1927        Ok(sitofp_op)
1928    }
1929}
1930
1931#[op_interface_impl]
1932impl ToLLVMValue for FPToUIOp {
1933    fn convert(
1934        &self,
1935        ctx: &Context,
1936        llvm_ctx: &LLVMContext,
1937        cctx: &mut ConversionContext,
1938    ) -> Result<LLVMValue> {
1939        let op = self.get_operation().deref(ctx);
1940        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1941        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1942        let fptoui_op = llvm_build_fptoui(
1943            &cctx.builder,
1944            arg,
1945            ty,
1946            &self.get_result(ctx).unique_name(ctx),
1947        );
1948        Ok(fptoui_op)
1949    }
1950}
1951
1952#[op_interface_impl]
1953impl ToLLVMValue for UIToFPOp {
1954    fn convert(
1955        &self,
1956        ctx: &Context,
1957        llvm_ctx: &LLVMContext,
1958        cctx: &mut ConversionContext,
1959    ) -> Result<LLVMValue> {
1960        let op = self.get_operation().deref(ctx);
1961        let arg = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1962        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1963        let uitofp_op = llvm_build_uitofp(
1964            &cctx.builder,
1965            arg,
1966            ty,
1967            &self.get_result(ctx).unique_name(ctx),
1968        );
1969        // The built value may not even be an instruction, but a folded constant.
1970        if llvm_is_a::instruction(uitofp_op) {
1971            let nneg = self.nneg(ctx);
1972            llvm_set_nneg(uitofp_op, nneg);
1973        }
1974        Ok(uitofp_op)
1975    }
1976}
1977
1978#[op_interface_impl]
1979impl ToLLVMValue for VAArgOp {
1980    fn convert(
1981        &self,
1982        ctx: &Context,
1983        llvm_ctx: &LLVMContext,
1984        cctx: &mut ConversionContext,
1985    ) -> Result<LLVMValue> {
1986        let op = self.get_operation().deref(ctx);
1987        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
1988        let opd = convert_value_operand(cctx, ctx, &op.get_operand(0))?;
1989        log::warn!("Generating va_arg instruction: It is poorly supported by LLVM");
1990        let vaarg_op = llvm_build_va_arg(
1991            &cctx.builder,
1992            opd,
1993            ty,
1994            &self.get_result(ctx).unique_name(ctx),
1995        );
1996        Ok(vaarg_op)
1997    }
1998}
1999
2000/// Convert a pliron [BasicBlock] to [LLVMBasicBlock].
2001fn convert_block(
2002    ctx: &Context,
2003    llvm_ctx: &LLVMContext,
2004    cctx: &mut ConversionContext,
2005    block: Ptr<BasicBlock>,
2006) -> Result<()> {
2007    let block_llvm = cctx.block_map[&block];
2008    llvm_position_builder_at_end(&cctx.builder, block_llvm);
2009
2010    for opr in block.deref(ctx).iter(ctx) {
2011        let op = Operation::get_op_dyn(opr, ctx);
2012        let op = op.as_ref();
2013        let Some(op_conv) = op_cast::<dyn ToLLVMValue>(op) else {
2014            let loc = op.loc(ctx);
2015            return input_err!(
2016                loc,
2017                ToLLVMErr::MissingOpConversion(op.get_opid().to_string())
2018            );
2019        };
2020        let op_llvm = op_conv.convert(ctx, llvm_ctx, cctx)?;
2021        let opr_ref = opr.deref(ctx);
2022        // LLVM instructions have at most one result.
2023        if opr_ref.get_num_results() == 1 {
2024            cctx.value_map.insert(opr_ref.get_result(0), op_llvm);
2025        }
2026    }
2027
2028    Ok(())
2029}
2030
2031/// Convert a pliron [FuncOp] to [LLVMValue]
2032fn convert_function(
2033    ctx: &Context,
2034    llvm_ctx: &LLVMContext,
2035    cctx: &mut ConversionContext,
2036    func_op: FuncOp,
2037) -> Result<LLVMValue> {
2038    cctx.clear_per_function_data();
2039    let func_llvm = cctx.function_map[&func_op.get_symbol_name(ctx)];
2040
2041    if let Some(linkage) = func_op.get_attr_llvm_function_linkage(ctx) {
2042        let llvm_linkage: LLVMLinkage = convert_linkage(linkage.clone());
2043        llvm_set_linkage(func_llvm, llvm_linkage);
2044    }
2045
2046    let f_region = func_op.get_region(ctx).expect("Function missing region");
2047
2048    // Map all blocks, staring with entry.
2049    let mut block_iter = f_region.deref(ctx).iter(ctx);
2050    {
2051        let entry = block_iter.next().expect("Missing entry block");
2052        // Map entry block arguments to LLVM function arguments.
2053        for (arg_idx, arg) in entry.deref(ctx).arguments().enumerate() {
2054            cctx.value_map
2055                .insert(arg, llvm_get_param(func_llvm, arg_idx.try_into().unwrap()));
2056        }
2057        let llvm_entry_block = llvm_append_basic_block_in_context(
2058            llvm_ctx,
2059            func_llvm,
2060            &entry.deref(ctx).unique_name(ctx),
2061        );
2062        cctx.block_map.insert(entry, llvm_entry_block);
2063    }
2064    for block in block_iter {
2065        let llvm_block = llvm_append_basic_block_in_context(
2066            llvm_ctx,
2067            func_llvm,
2068            &block.deref(ctx).unique_name(ctx),
2069        );
2070        llvm_position_builder_at_end(&cctx.builder, llvm_block);
2071        for arg in block.deref(ctx).arguments() {
2072            let arg_type = convert_type(ctx, llvm_ctx, cctx, arg.get_type(ctx))?;
2073            let phi = llvm_build_phi(&cctx.builder, arg_type, &arg.unique_name(ctx));
2074            cctx.value_map.insert(arg, phi);
2075        }
2076        cctx.block_map.insert(block, llvm_block);
2077    }
2078
2079    // Convert within every block.
2080    for block in topological_order(ctx, &f_region) {
2081        convert_block(ctx, llvm_ctx, cctx, block)?;
2082    }
2083
2084    Ok(func_llvm)
2085}
2086
2087#[op_interface]
2088trait ToLLVMConstValue {
2089    /// Convert from pliron [Op] to a constant [LLVMValue].
2090    fn convert(
2091        &self,
2092        ctx: &Context,
2093        llvm_ctx: &LLVMContext,
2094        cctx: &mut ConversionContext,
2095    ) -> Result<LLVMValue>;
2096
2097    fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
2098    where
2099        Self: Sized,
2100    {
2101        Ok(())
2102    }
2103}
2104
2105#[op_interface_impl]
2106impl ToLLVMConstValue for ConstantOp {
2107    fn convert(
2108        &self,
2109        ctx: &Context,
2110        llvm_ctx: &LLVMContext,
2111        cctx: &mut ConversionContext,
2112    ) -> Result<LLVMValue> {
2113        let op = self.get_operation().deref(ctx);
2114        let value = self.get_value(ctx);
2115        if let Some(int_val) = value.downcast_ref::<IntegerAttr>() {
2116            let int_ty = int_val.get_type();
2117            let int_ty_llvm = convert_type(ctx, llvm_ctx, cctx, int_ty.into())?;
2118            let ap_int_val: APInt = int_val.clone().into();
2119            let const_val = llvm_const_int(int_ty_llvm, ap_int_val.to_u64(), false);
2120            Ok(const_val)
2121        } else if let Some(float_val) = attr_cast::<dyn FloatAttrToFP64>(&*value) {
2122            let float_ty = float_val.get_type(ctx);
2123            let float_ty_llvm = convert_type(ctx, llvm_ctx, cctx, float_ty)?;
2124            let const_val = llvm_const_real(float_ty_llvm, float_val.to_fp64());
2125            Ok(const_val)
2126        } else {
2127            input_err!(op.loc(), ToLLVMErr::ConstOpNotIntOrFloat)
2128        }
2129    }
2130}
2131
2132#[op_interface_impl]
2133impl ToLLVMConstValue for UndefOp {
2134    fn convert(
2135        &self,
2136        ctx: &Context,
2137        llvm_ctx: &LLVMContext,
2138        cctx: &mut ConversionContext,
2139    ) -> Result<LLVMValue> {
2140        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
2141        Ok(llvm_get_undef(ty))
2142    }
2143}
2144
2145#[op_interface_impl]
2146impl ToLLVMConstValue for PoisonOp {
2147    fn convert(
2148        &self,
2149        ctx: &Context,
2150        llvm_ctx: &LLVMContext,
2151        cctx: &mut ConversionContext,
2152    ) -> Result<LLVMValue> {
2153        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
2154        Ok(llvm_get_poison(ty))
2155    }
2156}
2157
2158#[op_interface_impl]
2159impl ToLLVMConstValue for ZeroOp {
2160    fn convert(
2161        &self,
2162        ctx: &Context,
2163        llvm_ctx: &LLVMContext,
2164        cctx: &mut ConversionContext,
2165    ) -> Result<LLVMValue> {
2166        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
2167        let zero_val = llvm_const_null(ty);
2168        Ok(zero_val)
2169    }
2170}
2171
2172#[op_interface_impl]
2173impl ToLLVMConstValue for AddressOfOp {
2174    fn convert(
2175        &self,
2176        ctx: &Context,
2177        _llvm_ctx: &LLVMContext,
2178        cctx: &mut ConversionContext,
2179    ) -> Result<LLVMValue> {
2180        let sym = self.get_global_name(ctx);
2181        cctx.globals_map
2182            .get(&sym)
2183            .or_else(|| cctx.function_map.get(&sym))
2184            .cloned()
2185            .ok_or_else(|| input_error_noloc!(ToLLVMErr::CannotEvaluateToConst))
2186    }
2187}
2188
2189#[op_interface_impl]
2190impl ToLLVMConstValue for BlockAddressOp {
2191    fn convert(
2192        &self,
2193        ctx: &Context,
2194        llvm_ctx: &LLVMContext,
2195        cctx: &mut ConversionContext,
2196    ) -> Result<LLVMValue> {
2197        let tag = self.get_tag_id(ctx);
2198        let func = self.get_function_name(ctx);
2199
2200        // The target block may not be converted yet (possibly not even its
2201        // function), so emit a placeholder now and patch it in `convert_module`
2202        // once the whole module is converted. The placeholder must be a real
2203        // constant, not an instruction, so it can be used in other constant
2204        // expressions (e.g. a global's initializer). A `GlobalVariable` fits:
2205        // unlike other LLVM constants, which are unique'd, it has per-instance
2206        // identity and so can be RAUW'd. Same trick used by MLIR's LLVM-IR
2207        // translation and LLVM's own bitcode reader for forward-referenced
2208        // block addresses.
2209        let result_ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
2210        let addr_space = llvm_get_pointer_address_space(result_ty);
2211        let placeholder = llvm_add_global_in_address_space(
2212            cctx.cur_llvm_module,
2213            llvm_int_type_in_context(llvm_ctx, 8),
2214            "blockaddress_placeholder",
2215            addr_space,
2216        );
2217
2218        cctx.pending_block_address_ops
2219            .insert(placeholder, (func, tag));
2220        Ok(placeholder)
2221    }
2222}
2223
2224#[op_interface_impl]
2225impl ToLLVMConstValue for InsertValueOp {
2226    fn convert(
2227        &self,
2228        ctx: &Context,
2229        llvm_ctx: &LLVMContext,
2230        cctx: &mut ConversionContext,
2231    ) -> Result<LLVMValue> {
2232        let op = self.get_operation().deref(ctx);
2233        let base = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(0))?;
2234        let value = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(1))?;
2235        let indices = self.indices(ctx);
2236        if indices.len() != 1 {
2237            return input_err!(op.loc(), ToLLVMErr::InsertExtractValueIndices);
2238        }
2239
2240        // LLVM's builder tries to fold this, so we rely on that.
2241        let insert_op = llvm_build_insert_value(
2242            &cctx.scratch_builder,
2243            base,
2244            value,
2245            indices[0],
2246            &self.get_result(ctx).unique_name(ctx),
2247        );
2248        if !llvm_is_a::constant(insert_op) {
2249            return input_err!(op.loc(), ToLLVMErr::CannotEvaluateToConst);
2250        }
2251        Ok(insert_op)
2252    }
2253}
2254
2255#[op_interface_impl]
2256impl ToLLVMConstValue for InsertElementOp {
2257    fn convert(
2258        &self,
2259        ctx: &Context,
2260        llvm_ctx: &LLVMContext,
2261        cctx: &mut ConversionContext,
2262    ) -> Result<LLVMValue> {
2263        let op = self.get_operation().deref(ctx);
2264        let base = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(0))?;
2265        let value = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(1))?;
2266        let index = self.get_operand_index(ctx);
2267        let index = convert_to_llvm_const(ctx, cctx, llvm_ctx, index)?;
2268
2269        // LLVM's builder tries to fold this, so we rely on that.
2270        let insert_op = llvm_build_insert_element(
2271            &cctx.scratch_builder,
2272            base,
2273            value,
2274            index,
2275            &self.get_result(ctx).unique_name(ctx),
2276        );
2277        if !llvm_is_a::constant(insert_op) {
2278            return input_err!(op.loc(), ToLLVMErr::CannotEvaluateToConst);
2279        }
2280        Ok(insert_op)
2281    }
2282}
2283
2284#[op_interface_impl]
2285impl ToLLVMConstValue for TruncOp {
2286    fn convert(
2287        &self,
2288        ctx: &Context,
2289        llvm_ctx: &LLVMContext,
2290        cctx: &mut ConversionContext,
2291    ) -> Result<LLVMValue> {
2292        let op = self.get_operation().deref(ctx);
2293        let arg = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(0))?;
2294        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
2295
2296        // LLVM's builder tries to fold this, so we rely on that.
2297        let trunc_op = llvm_build_trunc(
2298            &cctx.scratch_builder,
2299            arg,
2300            ty,
2301            &self.get_result(ctx).unique_name(ctx),
2302        );
2303        if !llvm_is_a::constant(trunc_op) {
2304            return input_err!(op.loc(), ToLLVMErr::CannotEvaluateToConst);
2305        }
2306        Ok(trunc_op)
2307    }
2308}
2309
2310#[op_interface_impl]
2311impl ToLLVMConstValue for SubOp {
2312    fn convert(
2313        &self,
2314        ctx: &Context,
2315        llvm_ctx: &LLVMContext,
2316        cctx: &mut ConversionContext,
2317    ) -> Result<LLVMValue> {
2318        let op = self.get_operation().deref(ctx);
2319        let lhs = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(0))?;
2320        let rhs = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(1))?;
2321
2322        // LLVM's builder tries to fold this, so we rely on that.
2323        let sub_op = llvm_build_sub(
2324            &cctx.scratch_builder,
2325            lhs,
2326            rhs,
2327            &self.get_result(ctx).unique_name(ctx),
2328        );
2329        if !llvm_is_a::constant(sub_op) {
2330            return input_err!(op.loc(), ToLLVMErr::CannotEvaluateToConst);
2331        }
2332        Ok(sub_op)
2333    }
2334}
2335
2336#[op_interface_impl]
2337impl ToLLVMConstValue for PtrToIntOp {
2338    fn convert(
2339        &self,
2340        ctx: &Context,
2341        llvm_ctx: &LLVMContext,
2342        cctx: &mut ConversionContext,
2343    ) -> Result<LLVMValue> {
2344        let op = self.get_operation().deref(ctx);
2345        let arg = convert_to_llvm_const(ctx, cctx, llvm_ctx, op.get_operand(0))?;
2346        let ty = convert_type(ctx, llvm_ctx, cctx, self.result_type(ctx))?;
2347
2348        // LLVM's builder tries to fold this, so we rely on that.
2349        let ptoi_op = llvm_build_ptr_to_int(
2350            &cctx.scratch_builder,
2351            arg,
2352            ty,
2353            &self.get_result(ctx).unique_name(ctx),
2354        );
2355        if !llvm_is_a::constant(ptoi_op) {
2356            return input_err!(op.loc(), ToLLVMErr::CannotEvaluateToConst);
2357        }
2358        Ok(ptoi_op)
2359    }
2360}
2361
2362fn convert_to_llvm_const(
2363    ctx: &Context,
2364    cctx: &mut ConversionContext,
2365    llvm_ctx: &LLVMContext,
2366    value: Value,
2367) -> Result<LLVMValue> {
2368    match value.defining_entity() {
2369        DefiningEntity::Op(op) => {
2370            let op = Operation::get_op_dyn(op, ctx);
2371            if let Some(const_trans) = op_cast::<dyn ToLLVMConstValue>(op.as_ref()) {
2372                const_trans.convert(ctx, llvm_ctx, cctx)
2373            } else {
2374                input_err!(value.loc(ctx), ToLLVMErr::CannotEvaluateToConst)
2375            }
2376        }
2377        DefiningEntity::Block(_) => {
2378            input_err!(value.loc(ctx), ToLLVMErr::CannotEvaluateToConst)
2379        }
2380    }
2381}
2382
2383fn convert_global_initializer(
2384    ctx: &Context,
2385    llvm_ctx: &LLVMContext,
2386    cctx: &mut ConversionContext,
2387    global_op: GlobalOp,
2388) -> Result<Option<LLVMValue>> {
2389    if let Some(_initializer) = global_op.get_initializer_value(ctx) {
2390        todo!()
2391    }
2392
2393    if let Some(init_block) = global_op.get_initializer_block(ctx) {
2394        let ret =
2395            Operation::get_op::<ReturnOp>(init_block.deref(ctx).get_terminator(ctx).unwrap(), ctx);
2396        let ret = ret.ok_or_else(|| {
2397            input_error!(
2398                global_op.loc(ctx),
2399                ToLLVMErr::GlobalOpInitializerRegionBadReturn
2400            )
2401        })?;
2402        let Some(ret_val) = ret.retval(ctx) else {
2403            return input_err!(
2404                global_op.loc(ctx),
2405                ToLLVMErr::GlobalOpInitializerRegionBadReturn
2406            );
2407        };
2408        let initializer_val = convert_to_llvm_const(ctx, cctx, llvm_ctx, ret_val)?;
2409        return Ok(Some(initializer_val));
2410    }
2411
2412    Ok(None)
2413}
2414
2415/// Convert pliron [ModuleOp] to [LLVMModule].
2416pub fn convert_module(
2417    ctx: &Context,
2418    llvm_ctx: &LLVMContext,
2419    module: ModuleOp,
2420) -> Result<LLVMModule> {
2421    let mod_name = module.get_symbol_name(ctx);
2422    let llvm_module = LLVMModule::new(&mod_name, llvm_ctx);
2423    let cctx = &mut ConversionContext::new(llvm_ctx, &llvm_module);
2424
2425    // Setup the scratch builder for evaluating constants.
2426    // `scratch_module` is freed at the end of this function, when it exits the scope.
2427    let scratch_module = LLVMModule::new("__pliron_scratch_module", llvm_ctx);
2428    let scratch_function = llvm_add_function(
2429        &scratch_module,
2430        "scratch",
2431        llvm_function_type(llvm_void_type_in_context(llvm_ctx), &[], false),
2432    );
2433    let scratch_function_entry =
2434        llvm_append_basic_block_in_context(llvm_ctx, scratch_function, "entry");
2435    llvm_position_builder_at_end(&cctx.scratch_builder, scratch_function_entry);
2436
2437    // Create new functions and map them.
2438    for op in module.get_body(ctx, 0).deref(ctx).iter(ctx) {
2439        if let Some(func_op) = Operation::get_op::<FuncOp>(op, ctx) {
2440            let func_ty = func_op.get_type(ctx).deref(ctx);
2441            let func_ty_to_llvm = type_cast::<dyn ToLLVMType>(&*func_ty).ok_or_else(|| {
2442                input_error_noloc!(ToLLVMErr::MissingTypeConversion(
2443                    func_ty.disp(ctx).to_string()
2444                ))
2445            })?;
2446            let fn_ty_llvm = func_ty_to_llvm.convert(ctx, llvm_ctx, cctx)?;
2447            let name = func_op.get_symbol_name(ctx);
2448            let llvm_name = func_op.llvm_symbol_name(ctx).unwrap_or(name.clone().into());
2449            let func_llvm = llvm_add_function(&llvm_module, &llvm_name, fn_ty_llvm);
2450            cctx.function_map.insert(name, func_llvm);
2451        }
2452        if let Some(global_op) = Operation::get_op::<GlobalOp>(op, ctx) {
2453            let global_ty = global_op.get_type(ctx);
2454            let global_ty_llvm = convert_type(ctx, llvm_ctx, cctx, global_ty)?;
2455            let global_name = global_op.get_symbol_name(ctx);
2456            let llvm_global_name = global_op
2457                .llvm_symbol_name(ctx)
2458                .unwrap_or(global_name.clone().into());
2459            let global_addr_space = global_op.address_space(ctx);
2460            let global_llvm = llvm_add_global_in_address_space(
2461                &llvm_module,
2462                global_ty_llvm,
2463                &llvm_global_name,
2464                global_addr_space,
2465            );
2466            cctx.globals_map.insert(global_name, global_llvm);
2467        }
2468    }
2469
2470    for op in module.get_body(ctx, 0).deref(ctx).iter(ctx) {
2471        if let Some(func_op) = Operation::get_op::<FuncOp>(op, ctx)
2472            && !func_op.is_declaration(ctx)
2473        {
2474            convert_function(ctx, llvm_ctx, cctx, func_op)?;
2475        }
2476        if let Some(global_op) = Operation::get_op::<GlobalOp>(op, ctx) {
2477            let global_name = global_op.get_symbol_name(ctx);
2478            let global_llvm = cctx.globals_map[&global_name];
2479            if !global_op.is_declaration(ctx)
2480                && let Some(initializer) =
2481                    convert_global_initializer(ctx, llvm_ctx, cctx, global_op)?
2482            {
2483                llvm_set_initializer(global_llvm, initializer);
2484            }
2485            if let Some(linkage) = global_op.get_attr_llvm_global_linkage(ctx) {
2486                let llvm_linkage: LLVMLinkage = convert_linkage(linkage.clone());
2487                llvm_set_linkage(global_llvm, llvm_linkage);
2488            }
2489            if let Some(alignment) = global_op.alignment(ctx) {
2490                llvm_set_alignment(global_llvm, alignment);
2491            }
2492        }
2493    }
2494
2495    // Replace all pending block address operations with the actual block addresses.
2496    for (placeholder, (func_name, tag)) in cctx.pending_block_address_ops.iter() {
2497        let function_llvm = cctx
2498            .function_map
2499            .get(func_name)
2500            .ok_or_else(|| input_error_noloc!(ToLLVMErr::CannotEvaluateToConst))?;
2501        let block_llvm = cctx
2502            .block_tags
2503            .get(&(func_name.clone(), *tag))
2504            .ok_or_else(|| {
2505                input_error_noloc!(ToLLVMErr::MissingBlockTag(func_name.to_string(), *tag))
2506            })?;
2507        let block_addr = llvm_block_address(*function_llvm, *block_llvm);
2508        llvm_replace_all_uses_with(*placeholder, block_addr);
2509        llvm_delete_global(*placeholder);
2510    }
2511
2512    Ok(llvm_module)
2513}