Skip to main content

pliron_llvm/
to_llvm_ir.rs

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