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