Skip to main content

pliron_llvm/
from_llvm_ir.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! Translate from LLVM-IR to pliron's LLVM dialect
5
6use core::num::NonZero;
7
8use llvm_sys::{
9    LLVMAtomicOrdering, LLVMAtomicRMWBinOp, LLVMIntPredicate, LLVMLinkage, LLVMOpcode,
10    LLVMRealPredicate, LLVMTypeKind, LLVMValueKind,
11};
12use pliron::{
13    attribute::Attribute,
14    basic_block::BasicBlock,
15    builtin::{
16        attr_interfaces::TypedAttrInterface,
17        attributes::{
18            FPBFloatAttr, FPDoubleAttr, FPHalfAttr, FPSingleAttr, IntegerAttr, StringAttr,
19        },
20        op_interfaces::{
21            AtMostOneRegionInterface, CallOpCallable, OneResultInterface,
22            SingleBlockRegionInterface,
23        },
24        ops::ModuleOp,
25        types::{BF16Type, FP16Type, FP32Type, FP64Type, IntegerType, Signedness},
26    },
27    context::{Context, Ptr},
28    identifier::{self, Identifier},
29    input_err_noloc, input_error_noloc,
30    irbuild::{
31        inserter::{IRInserter, Inserter},
32        listener::DummyListener,
33    },
34    linked_list::ContainsLinkedList,
35    op::Op,
36    operation::Operation,
37    result::Result,
38    r#type::{TypeHandle, TypedHandle},
39    utils::{
40        apfloat::{f64_to_bfloat, f64_to_half},
41        apint::APInt,
42        table::{HMap, HSet, IMap},
43    },
44    value::Value,
45};
46use thiserror::Error;
47
48use crate::{
49    attributes::{
50        AggregateAttr, AtomicOrderingAttr, AtomicRmwKindAttr, BytesAttr, FCmpPredicateAttr,
51        FastmathFlagsAttr, ICmpPredicateAttr, IntegerOverflowFlagsAttr, LinkageAttr, PoisonAttr,
52        SplatAttr, SymbolAddrAttr, SyncScopeAttr, UndefAttr, ZeroAttr,
53    },
54    llvm_attrs_conversions::from_llvm_ir::convert_llvm_attributes,
55    llvm_sys::core::{
56        LLVM_ATTRIBUTE_FUNCTION_INDEX, LLVMBasicBlock, LLVMModule, LLVMType, LLVMValue,
57        basic_block_iter, function_iter, global_iter, incoming_iter, instruction_iter,
58        llvm_can_value_use_fast_math_flags, llvm_const_int_get_zext_value,
59        llvm_const_real_get_double, llvm_count_struct_element_types, llvm_get_aggregate_element,
60        llvm_get_alignment, llvm_get_allocated_type, llvm_get_array_length2, llvm_get_as_string,
61        llvm_get_atomic_rmw_bin_op, llvm_get_atomic_sync_scope_id, llvm_get_attributes_at_index,
62        llvm_get_basic_block_name, llvm_get_basic_block_terminator,
63        llvm_get_block_address_basic_block, llvm_get_block_address_function,
64        llvm_get_call_site_attributes, llvm_get_called_function_type, llvm_get_called_value,
65        llvm_get_cmpxchg_failure_ordering, llvm_get_cmpxchg_success_ordering,
66        llvm_get_const_opcode, llvm_get_element_type, llvm_get_fast_math_flags,
67        llvm_get_fcmp_predicate, llvm_get_gep_no_wrap_flags, llvm_get_gep_source_element_type,
68        llvm_get_icmp_predicate, llvm_get_indices, llvm_get_initializer,
69        llvm_get_inline_asm_asm_string, llvm_get_inline_asm_constraint_string,
70        llvm_get_inline_asm_has_side_effects, llvm_get_instruction_opcode,
71        llvm_get_instruction_parent, llvm_get_int_type_width, llvm_get_linkage,
72        llvm_get_mask_value, llvm_get_module_identifier, llvm_get_nneg, llvm_get_nsw,
73        llvm_get_num_arg_operands, llvm_get_num_mask_elements, llvm_get_num_operands, llvm_get_nuw,
74        llvm_get_operand, llvm_get_ordering, llvm_get_param_types, llvm_get_pointer_address_space,
75        llvm_get_return_type, llvm_get_struct_element_types, llvm_get_struct_name,
76        llvm_get_switch_case_value, llvm_get_type_kind, llvm_get_value_kind, llvm_get_value_name,
77        llvm_get_vector_size, llvm_get_volatile, llvm_global_get_value_type,
78        llvm_instruction_get_all_metadata_other_than_debug_loc, llvm_is_a, llvm_is_declaration,
79        llvm_is_function_type_var_arg, llvm_is_global_constant, llvm_is_opaque_struct,
80        llvm_is_packed_struct, llvm_lookup_intrinsic_id, llvm_print_type_to_string,
81        llvm_print_value_to_string, llvm_type_of, llvm_value_as_basic_block,
82        llvm_value_is_basic_block, param_iter,
83    },
84    metadata_conversions::from_llvm_ir::{
85        MD_KIND_DBG, MdConversionContext, convert_global_object_metadata,
86        convert_instruction_metadata, convert_module_metadata, md_kind_name,
87    },
88    op_interfaces::{
89        AlignableOpInterface, BinArithOp, CastOpInterface, CastOpWithNNegInterface, FastMathFlags,
90        FloatBinArithOpWithFastMathFlags, IntBinArithOpWithOverflowFlag, LlvmSymbolName,
91        VolatilityOpInterface,
92    },
93    ops::{
94        AShrOp, AddOp, AddrSpaceCastOp, AddressOfOp, AllocaOp, AndOp, AtomicCmpxchgOp,
95        AtomicLoadOp, AtomicRmwOp, AtomicStoreOp, BitcastOp, BlockAddressOp, BlockTagOp, BrOp,
96        CallIntrinsicOp, CallOp, CondBrOp, ConstantOp, ExtractElementOp, ExtractValueOp, FAddOp,
97        FCmpOp, FDivOp, FMulOp, FNegOp, FPExtOp, FPToSIOp, FPToUIOp, FPTruncOp, FRemOp, FSubOp,
98        FenceOp, FreezeOp, FuncOp, GepIndex, GetElementPtrOp, GlobalOp, ICmpOp, IndirectBrOp,
99        InlineAsmOp, InsertElementOp, InsertValueOp, IntToPtrOp, LShrOp, LoadOp, MulOp, OrOp,
100        PoisonOp, PtrToIntOp, ReturnOp, SDivOp, SExtOp, SIToFPOp, SRemOp, SelectOp, ShlOp,
101        ShuffleVectorOp, StoreOp, SubOp, SwitchCase, SwitchOp, TruncOp, UDivOp, UIToFPOp, URemOp,
102        UndefOp, UnreachableOp, VAArgOp, XorOp, ZExtOp, ZeroOp,
103    },
104    types::{
105        ArrayType, FuncType, PointerType, StructErr, StructLayout, StructType, VectorType,
106        VectorTypeKind, VoidType,
107    },
108};
109
110/// Try to build an attribute for the LLVM constant `val`.
111pub(crate) fn const_llvm_value_to_attr(
112    ctx: &Context,
113    cctx: &mut ConversionContext,
114    val: LLVMValue,
115) -> Result<Option<Box<dyn TypedAttrInterface>>> {
116    let ll_ty = llvm_type_of(val);
117    let ty = convert_type(ctx, cctx, ll_ty)?;
118
119    match llvm_get_value_kind(val) {
120        // Zero / Undef / Poison carry no data beyond their type.
121        LLVMValueKind::LLVMConstantPointerNullValueKind
122        | LLVMValueKind::LLVMConstantAggregateZeroValueKind => Ok(Some(Box::new(ZeroAttr(ty)))),
123        LLVMValueKind::LLVMUndefValueValueKind => Ok(Some(Box::new(UndefAttr(ty)))),
124        LLVMValueKind::LLVMPoisonValueKind => Ok(Some(Box::new(PoisonAttr(ty)))),
125
126        // The address of a global or a function, if we know it.
127        LLVMValueKind::LLVMGlobalVariableValueKind | LLVMValueKind::LLVMFunctionValueKind => {
128            let Some(name) = cctx.symbol_name(val) else {
129                return Ok(None);
130            };
131            let ptr_ty = TypedHandle::<PointerType>::from_handle(ty, ctx)?;
132            Ok(Some(Box::new(SymbolAddrAttr::new(name, ptr_ty))))
133        }
134
135        // A scalar constant, or a splat of one.
136        LLVMValueKind::LLVMConstantIntValueKind | LLVMValueKind::LLVMConstantFPValueKind => {
137            const_llvm_scalar_to_attr(ctx, val, ty)
138        }
139
140        LLVMValueKind::LLVMConstantArrayValueKind
141        | LLVMValueKind::LLVMConstantDataArrayValueKind
142        | LLVMValueKind::LLVMConstantStructValueKind
143        | LLVMValueKind::LLVMConstantVectorValueKind
144        | LLVMValueKind::LLVMConstantDataVectorValueKind => {
145            if let Some(bytes) = llvm_get_as_string(val) {
146                return Ok(Some(Box::new(BytesAttr::new(bytes))));
147            }
148            const_llvm_aggregate_to_attr(ctx, cctx, val, ll_ty, ty)
149        }
150
151        _ => Ok(None),
152    }
153}
154
155/// Build the attribute for `val`, an LLVM `ConstantInt` or `ConstantFP` of pliron type `ty`
156fn const_llvm_scalar_to_attr(
157    ctx: &Context,
158    val: LLVMValue,
159    ty: TypeHandle,
160) -> Result<Option<Box<dyn TypedAttrInterface>>> {
161    let ty_obj = ty.deref(ctx);
162
163    if let Some(vector_ty) = ty_obj.downcast_ref::<VectorType>() {
164        let element = const_llvm_scalar_to_attr(ctx, val, vector_ty.elem_type())?;
165        let vector_ty = TypedHandle::<VectorType>::from_handle(ty, ctx)?;
166        return Ok(element.map(|element| {
167            Box::new(SplatAttr::new(element, vector_ty)) as Box<dyn TypedAttrInterface>
168        }));
169    }
170
171    if let Some(int_ty) = ty_obj.downcast_ref::<IntegerType>() {
172        if !llvm_is_a::constant_int(val) {
173            return Ok(None);
174        }
175        let width = int_ty.width() as usize;
176        if width == 0 {
177            return input_err_noloc!(ConversionErr::ZeroWidthIntConst);
178        }
179        if width > 64 {
180            return input_err_noloc!(ConversionErr::IntConstTooWide(width));
181        }
182        return Ok(Some(Box::new(IntegerAttr::new(
183            TypedHandle::<IntegerType>::from_handle(ty, ctx)?,
184            APInt::from_u64(
185                llvm_const_int_get_zext_value(val),
186                NonZero::new(width).unwrap(),
187            ),
188        ))));
189    }
190
191    if !llvm_is_a::constant_fp(val) {
192        return Ok(None);
193    }
194    // LLVM's C-API pretty much restricts us to f64 for floating point constants.
195    let (fp64, lost_info) = llvm_const_real_get_double(val);
196    assert!(!lost_info, "Lost information when converting FP constant");
197    if ty_obj.is::<FP16Type>() {
198        Ok(Some(Box::new(FPHalfAttr(f64_to_half(fp64)))))
199    } else if ty_obj.is::<BF16Type>() {
200        Ok(Some(Box::new(FPBFloatAttr(f64_to_bfloat(fp64)))))
201    } else if ty_obj.is::<FP32Type>() {
202        Ok(Some(Box::new(FPSingleAttr::from(fp64 as f32))))
203    } else if ty_obj.is::<FP64Type>() {
204        Ok(Some(Box::new(FPDoubleAttr::from(fp64))))
205    } else {
206        Ok(None)
207    }
208}
209
210/// Try to build an [AggregateAttr] for the LLVM constant aggregate `val`,
211/// of [LLVMType] `ll_ty` and pliron [TypeHandle] `ty`.
212fn const_llvm_aggregate_to_attr(
213    ctx: &Context,
214    cctx: &mut ConversionContext,
215    val: LLVMValue,
216    ll_ty: LLVMType,
217    ty: TypeHandle,
218) -> Result<Option<Box<dyn TypedAttrInterface>>> {
219    let num_elements = match llvm_get_type_kind(ll_ty) {
220        LLVMTypeKind::LLVMArrayTypeKind => llvm_get_array_length2(ll_ty),
221        LLVMTypeKind::LLVMStructTypeKind => llvm_count_struct_element_types(ll_ty).into(),
222        LLVMTypeKind::LLVMVectorTypeKind => llvm_get_vector_size(ll_ty).into(),
223        _ => {
224            return input_err_noloc!(ConversionErr::UnsizedAggregate(
225                llvm_print_type_to_string(ll_ty).unwrap_or_default()
226            ));
227        }
228    };
229
230    let mut elements = Vec::with_capacity(num_elements as usize);
231    for idx in 0..num_elements {
232        let element = llvm_get_aggregate_element(val, idx.try_into().unwrap())
233            .ok_or_else(|| input_error_noloc!(ConversionErr::MissingAggregateElement(idx)))?;
234        let Some(element) = const_llvm_value_to_attr(ctx, cctx, element)? else {
235            return Ok(None);
236        };
237        elements.push(element);
238    }
239
240    // A vector whose elements are all equal is a splat.
241    if llvm_get_type_kind(ll_ty) == LLVMTypeKind::LLVMVectorTypeKind
242        && let Some(first) = elements.first()
243        && elements.iter().all(|element| element == first)
244    {
245        let vector_ty = TypedHandle::<VectorType>::from_handle(ty, ctx)?;
246        return Ok(Some(Box::new(SplatAttr::new(first.clone(), vector_ty))));
247    }
248
249    Ok(Some(Box::new(AggregateAttr::new(elements, ty))))
250}
251
252pub(crate) fn convert_type(
253    ctx: &Context,
254    cctx: &mut ConversionContext,
255    ty: LLVMType,
256) -> Result<TypeHandle> {
257    if let Some(cached) = cctx.type_cache.get(&ty) {
258        return Ok(*cached);
259    }
260    let kind = llvm_get_type_kind(ty);
261    let converted_ty: Result<TypeHandle> = match kind {
262        LLVMTypeKind::LLVMArrayTypeKind => {
263            let (element_ty, len) = (llvm_get_element_type(ty), llvm_get_array_length2(ty));
264            let elem = convert_type(ctx, cctx, element_ty)?;
265            Ok(ArrayType::get(ctx, elem, len).into())
266        }
267        LLVMTypeKind::LLVMFunctionTypeKind => {
268            let return_type = convert_type(ctx, cctx, llvm_get_return_type(ty))?;
269            let param_types = llvm_get_param_types(ty)
270                .into_iter()
271                .map(|ty| convert_type(ctx, cctx, ty))
272                .collect::<Result<_>>()?;
273            let is_var_arg = llvm_is_function_type_var_arg(ty);
274            Ok(FuncType::get(ctx, return_type, param_types, is_var_arg).into())
275        }
276        LLVMTypeKind::LLVMIntegerTypeKind => {
277            let bit_width = llvm_get_int_type_width(ty);
278            Ok(IntegerType::get(ctx, bit_width, Signedness::Signless).into())
279        }
280        LLVMTypeKind::LLVMPointerTypeKind => {
281            Ok(PointerType::get(ctx, llvm_get_pointer_address_space(ty)).into())
282        }
283        LLVMTypeKind::LLVMStructTypeKind => {
284            let name_opt: Option<Identifier> =
285                llvm_get_struct_name(ty).map(|str| cctx.id_legaliser.legalise(&str));
286            if llvm_is_opaque_struct(ty) {
287                // Opaque structs must be named.
288                let Some(name) = name_opt else {
289                    return input_err_noloc!(StructErr::OpaqueAndAnonymousErr);
290                };
291                Ok(StructType::get_named(ctx, name, None)?.into())
292            } else {
293                let layout: StructLayout = llvm_is_packed_struct(ty).into();
294                let field_types = llvm_get_struct_element_types(ty)
295                    .into_iter()
296                    .map(|ty| convert_type(ctx, cctx, ty))
297                    .collect::<Result<_>>()?;
298                if let Some(name) = name_opt {
299                    Ok(StructType::get_named(ctx, name, Some((field_types, layout)))?.into())
300                } else {
301                    Ok(StructType::get_unnamed(ctx, (field_types, layout)).into())
302                }
303            }
304        }
305        LLVMTypeKind::LLVMVoidTypeKind => Ok(VoidType::get(ctx).into()),
306        LLVMTypeKind::LLVMFloatTypeKind => Ok(FP32Type::get(ctx).into()),
307        LLVMTypeKind::LLVMDoubleTypeKind => Ok(FP64Type::get(ctx).into()),
308        LLVMTypeKind::LLVMVectorTypeKind | LLVMTypeKind::LLVMScalableVectorTypeKind => {
309            let element_ty = llvm_get_element_type(ty);
310            let elem_ty = convert_type(ctx, cctx, element_ty)?;
311            let num_elements = llvm_get_vector_size(ty);
312            let kind = if matches!(kind, LLVMTypeKind::LLVMScalableVectorTypeKind) {
313                VectorTypeKind::Scalable
314            } else {
315                VectorTypeKind::Fixed
316            };
317            Ok(VectorType::get(ctx, elem_ty, num_elements, kind).into())
318        }
319        LLVMTypeKind::LLVMHalfTypeKind => Ok(FP16Type::get(ctx).into()),
320        LLVMTypeKind::LLVMBFloatTypeKind => Ok(BF16Type::get(ctx).into()),
321        LLVMTypeKind::LLVMX86_FP80TypeKind => todo!(),
322        LLVMTypeKind::LLVMFP128TypeKind => todo!(),
323        LLVMTypeKind::LLVMPPC_FP128TypeKind => todo!(),
324        LLVMTypeKind::LLVMLabelTypeKind => todo!(),
325        LLVMTypeKind::LLVMMetadataTypeKind => todo!(),
326        LLVMTypeKind::LLVMTokenTypeKind => todo!(),
327        LLVMTypeKind::LLVMX86_AMXTypeKind => todo!(),
328        LLVMTypeKind::LLVMTargetExtTypeKind => todo!(),
329        LLVMTypeKind::LLVMByteTypeKind => todo!(),
330    };
331
332    let converted_ty = converted_ty?;
333    cctx.type_cache.insert(ty, converted_ty);
334    Ok(converted_ty)
335}
336
337pub fn convert_ipredicate(ipred: LLVMIntPredicate) -> ICmpPredicateAttr {
338    match ipred {
339        LLVMIntPredicate::LLVMIntEQ => ICmpPredicateAttr::EQ,
340        LLVMIntPredicate::LLVMIntNE => ICmpPredicateAttr::NE,
341        LLVMIntPredicate::LLVMIntUGT => ICmpPredicateAttr::UGT,
342        LLVMIntPredicate::LLVMIntUGE => ICmpPredicateAttr::UGE,
343        LLVMIntPredicate::LLVMIntULT => ICmpPredicateAttr::ULT,
344        LLVMIntPredicate::LLVMIntULE => ICmpPredicateAttr::ULE,
345        LLVMIntPredicate::LLVMIntSGT => ICmpPredicateAttr::SGT,
346        LLVMIntPredicate::LLVMIntSGE => ICmpPredicateAttr::SGE,
347        LLVMIntPredicate::LLVMIntSLT => ICmpPredicateAttr::SLT,
348        LLVMIntPredicate::LLVMIntSLE => ICmpPredicateAttr::SLE,
349    }
350}
351
352pub fn convert_fpredicate(fpred: LLVMRealPredicate) -> FCmpPredicateAttr {
353    match fpred {
354        LLVMRealPredicate::LLVMRealPredicateFalse => FCmpPredicateAttr::False,
355        LLVMRealPredicate::LLVMRealOEQ => FCmpPredicateAttr::OEQ,
356        LLVMRealPredicate::LLVMRealOGT => FCmpPredicateAttr::OGT,
357        LLVMRealPredicate::LLVMRealOGE => FCmpPredicateAttr::OGE,
358        LLVMRealPredicate::LLVMRealOLT => FCmpPredicateAttr::OLT,
359        LLVMRealPredicate::LLVMRealOLE => FCmpPredicateAttr::OLE,
360        LLVMRealPredicate::LLVMRealONE => FCmpPredicateAttr::ONE,
361        LLVMRealPredicate::LLVMRealORD => FCmpPredicateAttr::ORD,
362        LLVMRealPredicate::LLVMRealUNO => FCmpPredicateAttr::UNO,
363        LLVMRealPredicate::LLVMRealUEQ => FCmpPredicateAttr::UEQ,
364        LLVMRealPredicate::LLVMRealUGT => FCmpPredicateAttr::UGT,
365        LLVMRealPredicate::LLVMRealUGE => FCmpPredicateAttr::UGE,
366        LLVMRealPredicate::LLVMRealULT => FCmpPredicateAttr::ULT,
367        LLVMRealPredicate::LLVMRealULE => FCmpPredicateAttr::ULE,
368        LLVMRealPredicate::LLVMRealUNE => FCmpPredicateAttr::UNE,
369        LLVMRealPredicate::LLVMRealPredicateTrue => FCmpPredicateAttr::True,
370    }
371}
372
373pub fn convert_linkage(linkage: LLVMLinkage) -> LinkageAttr {
374    match linkage {
375        LLVMLinkage::LLVMExternalLinkage => LinkageAttr::ExternalLinkage,
376        LLVMLinkage::LLVMAvailableExternallyLinkage => LinkageAttr::AvailableExternallyLinkage,
377        LLVMLinkage::LLVMLinkOnceAnyLinkage => LinkageAttr::LinkOnceAnyLinkage,
378        LLVMLinkage::LLVMLinkOnceODRLinkage => LinkageAttr::LinkOnceODRLinkage,
379        LLVMLinkage::LLVMWeakAnyLinkage => LinkageAttr::WeakAnyLinkage,
380        LLVMLinkage::LLVMWeakODRLinkage => LinkageAttr::WeakODRLinkage,
381        LLVMLinkage::LLVMLinkOnceODRAutoHideLinkage => LinkageAttr::LinkOnceODRAutoHideLinkage,
382        LLVMLinkage::LLVMCommonLinkage => LinkageAttr::CommonLinkage,
383        LLVMLinkage::LLVMAppendingLinkage => LinkageAttr::AppendingLinkage,
384        LLVMLinkage::LLVMInternalLinkage => LinkageAttr::InternalLinkage,
385        LLVMLinkage::LLVMPrivateLinkage => LinkageAttr::PrivateLinkage,
386        LLVMLinkage::LLVMDLLImportLinkage => LinkageAttr::DLLImportLinkage,
387        LLVMLinkage::LLVMDLLExportLinkage => LinkageAttr::DLLExportLinkage,
388        LLVMLinkage::LLVMExternalWeakLinkage => LinkageAttr::ExternalWeakLinkage,
389        LLVMLinkage::LLVMLinkerPrivateLinkage => LinkageAttr::LinkerPrivateLinkage,
390        LLVMLinkage::LLVMLinkerPrivateWeakLinkage => LinkageAttr::LinkerPrivateWeakLinkage,
391        LLVMLinkage::LLVMGhostLinkage => LinkageAttr::GhostLinkage,
392    }
393}
394
395/// Mapping from LLVM entities to pliron entities.
396#[derive(Default)]
397pub(crate) struct ConversionContext {
398    /// A map from LLVM's Values to pliron's Values.
399    value_map: HMap<LLVMValue, Value>,
400    /// A map from LLVM's basic blocks to plirons'.
401    block_map: HMap<LLVMBasicBlock, Ptr<BasicBlock>>,
402    /// Cache already converted types.
403    type_cache: HMap<LLVMType, TypeHandle>,
404    /// Tag addressed to a (function, block) pair.
405    block_tag_map: IMap<(LLVMValue, LLVMBasicBlock), u64>,
406    /// Next block tag id to assign.
407    /// This is unique across all functions in the module for simplicity.
408    block_tag_counter: u64,
409    /// Insertion point for constants in the entry block.
410    constants_inserter: Option<IRInserter<DummyListener>>,
411    /// Identifier legaliser
412    id_legaliser: identifier::Legaliser,
413    /// Names of the globals and functions of the module.
414    symbol_names: HMap<LLVMValue, Identifier>,
415    /// State for converting the module's metadata.
416    pub(crate) md: MdConversionContext,
417}
418
419impl ConversionContext {
420    /// The pliron symbol name for an LLVM global object, if the module has one.
421    pub(crate) fn symbol_name(&self, val: LLVMValue) -> Option<Identifier> {
422        self.symbol_names.get(&val).cloned()
423    }
424
425    /// The pliron symbol name for an LLVM global object.
426    /// It is legalised and remembered when seen first.
427    pub(crate) fn legalized_symbol_name(&mut self, val: LLVMValue) -> Identifier {
428        if let Some(name) = self.symbol_names.get(&val) {
429            return name.clone();
430        }
431        let llvm_name = llvm_get_value_name(val).unwrap_or_default();
432        let name = self.id_legaliser.legalise(&llvm_name);
433        self.symbol_names.insert(val, name.clone());
434        name
435    }
436
437    /// Reset the value and block maps and initialize
438    /// constants inserter to the start of the entry block.
439    /// Identifier::Legaliser remains unmodified.
440    fn reset_for_region(&mut self, entry_block: Ptr<BasicBlock>) {
441        self.constants_inserter = Some(IRInserter::new_at_block_start(entry_block));
442        // The same LLVM constant values map to different pliron Values in different regions.
443        // So we clear the value map for each region.
444        self.value_map.clear();
445    }
446}
447
448/// Get the successors of an LLVM block.
449fn successors(block: LLVMBasicBlock) -> Vec<LLVMBasicBlock> {
450    let Some(term) = llvm_get_basic_block_terminator(block) else {
451        return vec![];
452    };
453
454    match llvm_get_instruction_opcode(term) {
455        LLVMOpcode::LLVMUncondBr => {
456            assert!(llvm_get_num_operands(term) == 1);
457            vec![llvm_value_as_basic_block(llvm_get_operand(term, 0))]
458        }
459        LLVMOpcode::LLVMCondBr => {
460            // Operands are ordered condition, true destination, false destination.
461            assert!(llvm_get_num_operands(term) == 3);
462            vec![
463                llvm_value_as_basic_block(llvm_get_operand(term, 1)),
464                llvm_value_as_basic_block(llvm_get_operand(term, 2)),
465            ]
466        }
467        LLVMOpcode::LLVMSwitch => {
468            // The first two operands are the condition value and the default destination.
469            // After that there are pairs of case value and destination.
470            let num_cases = (llvm_get_num_operands(term) - 2) / 2;
471            let mut succs = vec![llvm_value_as_basic_block(llvm_get_operand(term, 1))];
472            for i in 0..num_cases {
473                succs.push(llvm_value_as_basic_block(llvm_get_operand(
474                    term,
475                    2 + (2 * i) + 1,
476                )));
477            }
478            succs
479        }
480        LLVMOpcode::LLVMIndirectBr => {
481            // Operand 0 is the address; the rest are the possible destinations.
482            (1..llvm_get_num_operands(term))
483                .map(|i| llvm_value_as_basic_block(llvm_get_operand(term, i)))
484                .collect()
485        }
486        LLVMOpcode::LLVMRet | LLVMOpcode::LLVMUnreachable => {
487            // No successors.
488            vec![]
489        }
490        _ => {
491            todo!(
492                "Unsupported instruction: {}",
493                llvm_print_value_to_string(term).unwrap_or_default()
494            )
495        }
496    }
497}
498
499/// Return RPO ordering of blocks in an LLVM function.
500fn rpo(function: LLVMValue) -> Vec<LLVMBasicBlock> {
501    let visited = &mut HSet::<LLVMBasicBlock>::default();
502    let mut po = Vec::<LLVMBasicBlock>::new();
503    let mut revpo = Vec::<LLVMBasicBlock>::new();
504
505    fn walk(
506        block: LLVMBasicBlock,
507        visited: &mut HSet<LLVMBasicBlock>,
508        po: &mut Vec<LLVMBasicBlock>,
509    ) {
510        if !visited.insert(block) {
511            // block already visited.
512            return;
513        }
514        // Visit successors before visiting self.
515        for succ in successors(block).into_iter() {
516            walk(succ, visited, po);
517        }
518        // Visit self.
519        po.push(block);
520    }
521
522    // Walk every block (not just entry) since we may have unreachable blocks.
523    for block in basic_block_iter(function) {
524        if visited.contains(&block) {
525            continue;
526        }
527        walk(block, visited, &mut po);
528        // We collect the RPO for this connected component right now
529        // so that the entry block of the function comes first in the final ordering.
530        revpo.extend(po.drain(..).rev());
531    }
532
533    revpo
534}
535
536/// Conversion errors
537#[derive(Error, Debug)]
538pub enum ConversionErr {
539    #[error("Unable to get operand with idx {0}")]
540    OpdMissing(usize),
541    #[error("Unable to get successor with idx {0}")]
542    SuccMissing(usize),
543    #[error("PHI node must have argument from predecessor block \"^{0}\"")]
544    PhiArgMissing(String),
545    #[error("Definition for value \"{0}\" not seen yet")]
546    UndefinedValue(String),
547    #[error("Block definition \"^{0}\" not seen yet")]
548    UndefinedBlock(String),
549    #[error("Integer constant has bit-width 0")]
550    ZeroWidthIntConst,
551    #[error("Integer constant has bit-width {0}, exceeding the 64-bit limit of the LLVM-C API")]
552    IntConstTooWide(usize),
553    #[error("Switch case value is not an integer constant")]
554    SwitchCaseNonIntConst,
555    #[error("Constant {0} has no attribute to represent it")]
556    ConstWithoutAttr(String),
557    #[error("Constant aggregate does not have an element at index {0}")]
558    MissingAggregateElement(u64),
559    #[error("Constant aggregate is of type {0}, which has no statically known size")]
560    UnsizedAggregate(String),
561}
562
563/// If a value is a ConstantOp with integer type, return the value.
564fn get_const_op_as_int(ctx: &Context, val: Value) -> Option<IntegerAttr> {
565    let defining_op = val.defining_op()?;
566
567    Operation::get_op::<ConstantOp>(defining_op, ctx).and_then(|const_op| {
568        let value = const_op.get_value(ctx);
569        (&*value as &dyn Attribute)
570            .downcast_ref::<IntegerAttr>()
571            .cloned()
572    })
573}
574
575/// If a value is a ConstantOp with a 32-bit integer type, return the value.
576fn get_const_op_as_u32(ctx: &Context, val: Value) -> Option<u32> {
577    get_const_op_as_int(ctx, val).and_then(|int_attr| {
578        let int_ty = int_attr.get_type().deref(ctx);
579        // LLVM integers are signless.
580        (int_ty.is_signless() && int_ty.width() == 32).then(|| int_attr.value().to_u32())
581    })
582}
583
584/// Try to convert the constant `val` into an attribute and materialize it as a [ConstantOp].
585fn const_llvm_value_to_const_op(
586    ctx: &mut Context,
587    cctx: &mut ConversionContext,
588    val: LLVMValue,
589) -> Result<bool> {
590    let Some(attr) = const_llvm_value_to_attr(ctx, cctx, val)? else {
591        return Ok(false);
592    };
593    let const_op = ConstantOp::new(ctx, attr);
594    cctx.constants_inserter
595        .as_mut()
596        .unwrap()
597        .append_operation(ctx, const_op.get_operation());
598    cctx.value_map.insert(val, const_op.get_result(ctx));
599    Ok(true)
600}
601
602/// Checks if a constant has been processed already, and if not
603/// converts it and puts it in the [ConversionContext::value_map].
604fn process_constant(ctx: &mut Context, cctx: &mut ConversionContext, val: LLVMValue) -> Result<()> {
605    if cctx.value_map.contains_key(&val) {
606        return Ok(());
607    }
608    let ll_ty = llvm_type_of(val);
609    let ty = convert_type(ctx, cctx, ll_ty)?;
610
611    // Insert a new constant instruction in the entry block.
612    fn insert_const_inst(ctx: &mut Context, cctx: &mut ConversionContext, op: Ptr<Operation>) {
613        cctx.constants_inserter
614            .as_mut()
615            .unwrap()
616            .append_operation(ctx, op);
617    }
618
619    match llvm_get_value_kind(val) {
620        LLVMValueKind::LLVMUndefValueValueKind => {
621            let undef_op = UndefOp::new(ctx, ty);
622            insert_const_inst(ctx, cctx, undef_op.get_operation());
623            cctx.value_map.insert(val, undef_op.get_result(ctx));
624        }
625        LLVMValueKind::LLVMPoisonValueKind => {
626            let poison_op = PoisonOp::new(ctx, ty);
627            insert_const_inst(ctx, cctx, poison_op.get_operation());
628            cctx.value_map.insert(val, poison_op.get_result(ctx));
629        }
630        LLVMValueKind::LLVMConstantPointerNullValueKind => {
631            let null_op = ZeroOp::new(ctx, ty);
632            insert_const_inst(ctx, cctx, null_op.get_operation());
633            cctx.value_map.insert(val, null_op.get_result(ctx));
634        }
635        LLVMValueKind::LLVMConstantIntValueKind | LLVMValueKind::LLVMConstantFPValueKind => {
636            if !const_llvm_value_to_const_op(ctx, cctx, val)? {
637                return input_err_noloc!(ConversionErr::ConstWithoutAttr(
638                    llvm_print_value_to_string(val).unwrap_or_default()
639                ));
640            }
641        }
642        LLVMValueKind::LLVMConstantArrayValueKind
643        | LLVMValueKind::LLVMConstantDataArrayValueKind => {
644            if const_llvm_value_to_const_op(ctx, cctx, val)? {
645                return Ok(());
646            }
647            fn get_operand(val: LLVMValue, index: u32) -> Result<LLVMValue> {
648                if matches!(
649                    llvm_get_value_kind(val),
650                    LLVMValueKind::LLVMConstantDataArrayValueKind
651                ) {
652                    llvm_get_aggregate_element(val, index).ok_or_else(|| {
653                        input_error_noloc!("LLVMConstantDataArrayValueKind does not have an element at index {index}")
654                    })
655                } else {
656                    Ok(llvm_get_operand(val, index))
657                }
658            }
659            let mut field_vals = vec![];
660            let num_elements = llvm_get_array_length2(ll_ty);
661            for i in 0..num_elements {
662                let field_val = get_operand(val, i.try_into().unwrap())?;
663                process_constant(ctx, cctx, field_val)?;
664                let Some(m_val) = cctx.value_map.get(&field_val) else {
665                    panic!("We just processed this constant, it must be in the map");
666                };
667                field_vals.push(*m_val);
668            }
669            // Starting with an Undef value, we insert elements, for each field.
670            let undef_op = UndefOp::new(ctx, ty);
671            insert_const_inst(ctx, cctx, undef_op.get_operation());
672            let (ctx, const_array) = field_vals.iter().enumerate().try_fold(
673                (ctx, undef_op.get_operation()),
674                |(ctx, acc), (field_idx, field_val)| -> Result<_> {
675                    let acc_val = acc.deref(ctx).get_result(0);
676                    let insert_op = InsertValueOp::new(
677                        ctx,
678                        acc_val,
679                        *field_val,
680                        vec![field_idx.try_into().unwrap()],
681                    )
682                    .get_operation();
683                    insert_const_inst(ctx, cctx, insert_op);
684                    Ok((ctx, insert_op))
685                },
686            )?;
687
688            cctx.value_map
689                .insert(val, const_array.deref(ctx).get_result(0));
690        }
691        LLVMValueKind::LLVMConstantStructValueKind => {
692            if const_llvm_value_to_const_op(ctx, cctx, val)? {
693                return Ok(());
694            }
695            let mut field_vals = vec![];
696            let num_fields = llvm_count_struct_element_types(ll_ty);
697            for i in 0..num_fields {
698                let field_val = llvm_get_operand(val, i);
699                process_constant(ctx, cctx, field_val)?;
700                let Some(m_val) = cctx.value_map.get(&field_val) else {
701                    panic!("We just processed this constant, it must be in the map");
702                };
703                field_vals.push(*m_val);
704            }
705            // Starting with an Undef value, we insert elements, for each field.
706            let undef_op = UndefOp::new(ctx, ty);
707            insert_const_inst(ctx, cctx, undef_op.get_operation());
708            let (ctx, const_struct) = field_vals.iter().enumerate().try_fold(
709                (ctx, undef_op.get_operation()),
710                |(ctx, acc), (field_idx, field_val)| -> Result<_> {
711                    let acc_val = acc.deref(ctx).get_result(0);
712                    let insert_op = InsertValueOp::new(
713                        ctx,
714                        acc_val,
715                        *field_val,
716                        vec![field_idx.try_into().unwrap()],
717                    )
718                    .get_operation();
719                    insert_const_inst(ctx, cctx, insert_op);
720                    Ok((ctx, insert_op))
721                },
722            )?;
723
724            cctx.value_map
725                .insert(val, const_struct.deref(ctx).get_result(0));
726        }
727        LLVMValueKind::LLVMConstantExprValueKind => {
728            let opcode = llvm_get_const_opcode(val);
729            match opcode {
730                LLVMOpcode::LLVMBitCast => {
731                    let opd = llvm_get_operand(val, 0);
732                    process_constant(ctx, cctx, opd)?;
733                    let Some(m_val) = cctx.value_map.get(&opd) else {
734                        panic!("We just processed this constant, it must be in the map");
735                    };
736                    let cast_op = BitcastOp::new(ctx, *m_val, ty);
737                    insert_const_inst(ctx, cctx, cast_op.get_operation());
738                    cctx.value_map.insert(val, cast_op.get_result(ctx));
739                }
740                LLVMOpcode::LLVMIntToPtr => {
741                    let opd = llvm_get_operand(val, 0);
742                    process_constant(ctx, cctx, opd)?;
743                    let Some(m_val) = cctx.value_map.get(&opd) else {
744                        panic!("We just processed this constant, it must be in the map");
745                    };
746                    let cast_op = IntToPtrOp::new(ctx, *m_val, ty);
747                    insert_const_inst(ctx, cctx, cast_op.get_operation());
748                    cctx.value_map.insert(val, cast_op.get_result(ctx));
749                }
750                LLVMOpcode::LLVMPtrToInt => {
751                    let opd = llvm_get_operand(val, 0);
752                    process_constant(ctx, cctx, opd)?;
753                    let Some(m_val) = cctx.value_map.get(&opd) else {
754                        panic!("We just processed this constant, it must be in the map");
755                    };
756                    let cast_op = PtrToIntOp::new(ctx, *m_val, ty);
757                    insert_const_inst(ctx, cctx, cast_op.get_operation());
758                    cctx.value_map.insert(val, cast_op.get_result(ctx));
759                }
760                LLVMOpcode::LLVMTrunc => {
761                    let opd = llvm_get_operand(val, 0);
762                    process_constant(ctx, cctx, opd)?;
763                    let Some(m_val) = cctx.value_map.get(&opd).cloned() else {
764                        panic!("We just processed this constant, it must be in the map");
765                    };
766                    let trunc_op = TruncOp::new(ctx, m_val, ty);
767                    insert_const_inst(ctx, cctx, trunc_op.get_operation());
768                    cctx.value_map.insert(val, trunc_op.get_result(ctx));
769                }
770                LLVMOpcode::LLVMGetElementPtr => {
771                    let base = llvm_get_operand(val, 0);
772                    process_constant(ctx, cctx, base)?;
773                    let Some(m_base) = cctx.value_map.get(&base).cloned() else {
774                        panic!("We just processed this constant, it must be in the map");
775                    };
776                    let mut indices = vec![];
777                    for i in 1..llvm_get_num_operands(val) {
778                        let opd = llvm_get_operand(val, i);
779                        process_constant(ctx, cctx, opd)?;
780                        let Some(m_val) = cctx.value_map.get(&opd) else {
781                            panic!("We just processed this constant, it must be in the map");
782                        };
783                        if let Some(c) = get_const_op_as_u32(ctx, *m_val) {
784                            indices.push(GepIndex::Constant(c));
785                        } else {
786                            indices.push(GepIndex::Value(*m_val));
787                        }
788                    }
789                    let src_elm_type =
790                        convert_type(ctx, cctx, llvm_get_gep_source_element_type(val))?;
791                    let gep_op = GetElementPtrOp::new_with_no_wrap_flags(
792                        ctx,
793                        m_base,
794                        indices,
795                        src_elm_type,
796                        llvm_get_gep_no_wrap_flags(val),
797                    );
798                    insert_const_inst(ctx, cctx, gep_op.get_operation());
799                    cctx.value_map.insert(val, gep_op.get_result(ctx));
800                }
801                LLVMOpcode::LLVMAdd | LLVMOpcode::LLVMSub => {
802                    let (lhs, rhs) = (llvm_get_operand(val, 0), llvm_get_operand(val, 1));
803                    process_constant(ctx, cctx, lhs)?;
804                    process_constant(ctx, cctx, rhs)?;
805                    let Some(m_lhs) = cctx.value_map.get(&lhs).cloned() else {
806                        panic!("We just processed this constant, it must be in the map");
807                    };
808                    let Some(m_rhs) = cctx.value_map.get(&rhs).cloned() else {
809                        panic!("We just processed this constant, it must be in the map");
810                    };
811                    // `Instruction *ConstantExpr::getAsInstruction() const ` just sets no flags.
812                    let flags = IntegerOverflowFlagsAttr::default();
813                    let (op, res_val) = if opcode == LLVMOpcode::LLVMAdd {
814                        let op = AddOp::new(ctx, m_lhs, m_rhs);
815                        op.set_integer_overflow_flag(ctx, flags);
816                        (op.get_operation(), op.get_result(ctx))
817                    } else {
818                        let op = SubOp::new(ctx, m_lhs, m_rhs);
819                        op.set_integer_overflow_flag(ctx, flags);
820                        (op.get_operation(), op.get_result(ctx))
821                    };
822                    insert_const_inst(ctx, cctx, op);
823                    cctx.value_map.insert(val, res_val);
824                }
825                _ => {
826                    todo!("Unsupported constant expression opcode: {:?}", opcode)
827                }
828            }
829        }
830        LLVMValueKind::LLVMGlobalVariableValueKind => {
831            let global_name = llvm_get_value_name(val).unwrap_or_default();
832            let global_name = cctx.id_legaliser.legalise(&global_name);
833            let global_op = AddressOfOp::new(
834                ctx,
835                global_name,
836                llvm_get_pointer_address_space(llvm_type_of(val)),
837            );
838            insert_const_inst(ctx, cctx, global_op.get_operation());
839            cctx.value_map.insert(val, global_op.get_result(ctx));
840        }
841        LLVMValueKind::LLVMFunctionValueKind => {
842            let fn_name = llvm_get_value_name(val).unwrap_or_default();
843            let fn_name = cctx.id_legaliser.legalise(&fn_name);
844            let func_op = AddressOfOp::new(
845                ctx,
846                fn_name,
847                llvm_get_pointer_address_space(llvm_type_of(val)),
848            );
849            insert_const_inst(ctx, cctx, func_op.get_operation());
850            cctx.value_map.insert(val, func_op.get_result(ctx));
851        }
852        LLVMValueKind::LLVMConstantAggregateZeroValueKind => {
853            let zero_op = ZeroOp::new(ctx, ty);
854            insert_const_inst(ctx, cctx, zero_op.get_operation());
855            cctx.value_map.insert(val, zero_op.get_result(ctx));
856        }
857        LLVMValueKind::LLVMConstantVectorValueKind
858        | LLVMValueKind::LLVMConstantDataVectorValueKind => {
859            if const_llvm_value_to_const_op(ctx, cctx, val)? {
860                return Ok(());
861            }
862            let num_elements = llvm_get_vector_size(ll_ty);
863            let mut element_vals = vec![];
864            for i in 0..num_elements {
865                let element_val = llvm_get_aggregate_element(val, i)
866                    .expect("Constant vector must have element at index");
867                process_constant(ctx, cctx, element_val)?;
868                let Some(m_val) = cctx.value_map.get(&element_val) else {
869                    panic!("We just processed this constant, it must be in the map");
870                };
871                element_vals.push(*m_val);
872            }
873            // Starting with an Undef value, we insert elements, for each element.
874            let undef_op = UndefOp::new(ctx, ty);
875            let ty_int32 = TypedHandle::<IntegerType>::from_handle(
876                IntegerType::get(ctx, 32, Signedness::Signless).into(),
877                ctx,
878            )?;
879            insert_const_inst(ctx, cctx, undef_op.get_operation());
880            let const_vector = element_vals.iter().enumerate().try_fold(
881                undef_op.get_operation(),
882                |acc, (elem_idx, elem_val)| -> Result<_> {
883                    let acc_val = acc.deref(ctx).get_result(0);
884                    // The index needs to be a [Value], so we create a ConstantOp for it.
885                    let idx_attr = IntegerAttr::new(
886                        ty_int32,
887                        APInt::from_u64(elem_idx.try_into().unwrap(), NonZero::new(32).unwrap()),
888                    );
889                    let idx_const_op = ConstantOp::new(ctx, Box::new(idx_attr)).get_operation();
890                    insert_const_inst(ctx, cctx, idx_const_op);
891                    let idx_val = idx_const_op.deref(ctx).get_result(0);
892                    let insert_op =
893                        InsertElementOp::new(ctx, acc_val, *elem_val, idx_val).get_operation();
894                    insert_const_inst(ctx, cctx, insert_op);
895                    Ok(insert_op)
896                },
897            )?;
898            cctx.value_map
899                .insert(val, const_vector.deref(ctx).get_result(0));
900        }
901        LLVMValueKind::LLVMBlockAddressValueKind => {
902            let function = llvm_get_block_address_function(val);
903            let block = llvm_get_block_address_basic_block(val);
904            let fn_name = llvm_get_value_name(function).unwrap_or_default();
905            let fn_name = cctx.id_legaliser.legalise(&fn_name);
906            let tag = *cctx
907                .block_tag_map
908                .entry((function, block))
909                .or_insert_with(|| {
910                    let tag = cctx.block_tag_counter;
911                    cctx.block_tag_counter += 1;
912                    tag
913                });
914            let block_addr_op = BlockAddressOp::new(
915                ctx,
916                fn_name,
917                tag,
918                llvm_get_pointer_address_space(llvm_type_of(val)),
919            );
920            insert_const_inst(ctx, cctx, block_addr_op.get_operation());
921            cctx.value_map.insert(val, block_addr_op.get_result(ctx));
922        }
923        LLVMValueKind::LLVMArgumentValueKind => todo!(),
924        LLVMValueKind::LLVMBasicBlockValueKind => todo!(),
925        LLVMValueKind::LLVMMemoryUseValueKind => todo!(),
926        LLVMValueKind::LLVMMemoryDefValueKind => todo!(),
927        LLVMValueKind::LLVMMemoryPhiValueKind => todo!(),
928        LLVMValueKind::LLVMGlobalAliasValueKind => todo!(),
929        LLVMValueKind::LLVMGlobalIFuncValueKind => todo!(),
930        LLVMValueKind::LLVMConstantTokenNoneValueKind => todo!(),
931        LLVMValueKind::LLVMMetadataAsValueValueKind => todo!(),
932        LLVMValueKind::LLVMInlineAsmValueKind => todo!(),
933        LLVMValueKind::LLVMInstructionValueKind => todo!(),
934        LLVMValueKind::LLVMConstantTargetNoneValueKind => todo!(),
935        LLVMValueKind::LLVMConstantPtrAuthValueKind => todo!(),
936        LLVMValueKind::LLVMConstantByteValueKind => todo!(),
937    }
938    Ok(())
939}
940
941fn convert_operands(
942    ctx: &mut Context,
943    cctx: &mut ConversionContext,
944    operands: &[LLVMValue],
945) -> Result<(Vec<Value>, Vec<Ptr<BasicBlock>>)> {
946    let mut opds = vec![];
947    let mut succs = vec![];
948
949    for opd in operands.iter().cloned() {
950        if !llvm_value_is_basic_block(opd) {
951            process_constant(ctx, cctx, opd)?;
952            if let Some(m_val) = cctx.value_map.get(&opd) {
953                opds.push(*m_val);
954            } else {
955                return input_err_noloc!(ConversionErr::UndefinedValue(
956                    llvm_get_value_name(opd).unwrap_or_default()
957                ));
958            }
959        } else {
960            let block = llvm_value_as_basic_block(opd);
961            let Some(m_block) = cctx.block_map.get(&block) else {
962                return input_err_noloc!(ConversionErr::UndefinedBlock(
963                    llvm_get_basic_block_name(block).unwrap_or_default()
964                ));
965            };
966            succs.push(*m_block);
967        }
968    }
969    Ok((opds, succs))
970}
971
972fn get_operand<T: Clone>(opds: &[T], idx: usize) -> Result<T> {
973    opds.get(idx)
974        .ok_or_else(|| input_error_noloc!(ConversionErr::OpdMissing(idx)))
975        .cloned()
976}
977
978/// Compute the arguments to be passed when branching from `src` to `dest`.
979fn convert_branch_args(
980    ctx: &mut Context,
981    cctx: &mut ConversionContext,
982    src_block: LLVMBasicBlock,
983    dst_block: LLVMBasicBlock,
984) -> Result<Vec<Value>> {
985    let mut args = vec![];
986    for inst in instruction_iter(dst_block) {
987        if llvm_is_a::phi_node(inst) {
988            let Some((incoming_val, _)) =
989                incoming_iter(inst).find(|(_, block)| *block == src_block)
990            else {
991                return input_err_noloc!(ConversionErr::PhiArgMissing(
992                    llvm_get_basic_block_name(src_block).unwrap_or_default()
993                ));
994            };
995            process_constant(ctx, cctx, incoming_val)?;
996            let Some(m_incoming_val) = cctx.value_map.get(&incoming_val) else {
997                return input_err_noloc!(ConversionErr::UndefinedValue(
998                    llvm_get_value_name(incoming_val).unwrap_or_default()
999                ));
1000            };
1001            args.push(*m_incoming_val)
1002        } else {
1003            // PHIs are at the start of the block.
1004            break;
1005        }
1006    }
1007    Ok(args)
1008}
1009
1010/// Map an LLVM-C [LLVMAtomicOrdering] to a pliron [AtomicOrderingAttr].
1011fn convert_ordering_from_llvm(o: LLVMAtomicOrdering) -> AtomicOrderingAttr {
1012    match o {
1013        LLVMAtomicOrdering::LLVMAtomicOrderingMonotonic => AtomicOrderingAttr::Monotonic,
1014        LLVMAtomicOrdering::LLVMAtomicOrderingAcquire => AtomicOrderingAttr::Acquire,
1015        LLVMAtomicOrdering::LLVMAtomicOrderingRelease => AtomicOrderingAttr::Release,
1016        LLVMAtomicOrdering::LLVMAtomicOrderingAcquireRelease => AtomicOrderingAttr::AcqRel,
1017        LLVMAtomicOrdering::LLVMAtomicOrderingSequentiallyConsistent => AtomicOrderingAttr::SeqCst,
1018        other => panic!("unsupported atomic ordering from LLVM: {other:?}"),
1019    }
1020}
1021
1022/// Map an LLVM-C [LLVMAtomicRMWBinOp] to a pliron [AtomicRmwKindAttr].
1023fn convert_rmw_kind_from_llvm(k: LLVMAtomicRMWBinOp) -> AtomicRmwKindAttr {
1024    match k {
1025        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpXchg => AtomicRmwKindAttr::Xchg,
1026        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpAdd => AtomicRmwKindAttr::Add,
1027        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpSub => AtomicRmwKindAttr::Sub,
1028        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpAnd => AtomicRmwKindAttr::And,
1029        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpNand => AtomicRmwKindAttr::Nand,
1030        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpOr => AtomicRmwKindAttr::Or,
1031        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpXor => AtomicRmwKindAttr::Xor,
1032        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpMax => AtomicRmwKindAttr::Max,
1033        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpMin => AtomicRmwKindAttr::Min,
1034        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpUMax => AtomicRmwKindAttr::UMax,
1035        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpUMin => AtomicRmwKindAttr::UMin,
1036        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFAdd => AtomicRmwKindAttr::FAdd,
1037        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFSub => AtomicRmwKindAttr::FSub,
1038        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFMax => AtomicRmwKindAttr::FMax,
1039        LLVMAtomicRMWBinOp::LLVMAtomicRMWBinOpFMin => AtomicRmwKindAttr::FMin,
1040        other => panic!("unsupported atomicrmw binop from LLVM: {other:?}"),
1041    }
1042}
1043
1044/// Build [SyncScopeAttr] of an atomic instruction.
1045fn syncscope_from_llvm(inst: LLVMValue) -> SyncScopeAttr {
1046    /// LLVM-C provides no function to get a SyncScope name from its SyncScope ID.
1047    /// So extract the sync scope name from the instruction's printed form,
1048    /// which spells a named scope as `syncscope("<name>")`. The printed name is
1049    /// escaped (so a `"` in it cannot be mistaken for the closing quote).
1050    /// TODO: Unescape it and support names with escapes.
1051    fn syncscope_name(inst: LLVMValue) -> Option<String> {
1052        let printed = llvm_print_value_to_string(inst)?;
1053        let (_, rest) = printed.split_once("syncscope(\"")?;
1054        let (name, _) = rest.split_once('"')?;
1055        Some(name.to_string())
1056    }
1057
1058    match llvm_get_atomic_sync_scope_id(inst) {
1059        // LLVM's built-in scope ids: `SyncScope::SingleThread` and `SyncScope::System`.
1060        // Every other id is a named scope, unique only within the `LLVMContext`.
1061        0 => SyncScopeAttr::SingleThread,
1062        1 => SyncScopeAttr::System,
1063        _ => syncscope_name(inst)
1064            .map(|name| SyncScopeAttr::NamedScope(StringAttr::new(name)))
1065            .unwrap_or_else(|| {
1066                log::warn!("Could not recover sync scope name; falling back to the system scope");
1067                SyncScopeAttr::System
1068            }),
1069    }
1070}
1071
1072fn convert_call(
1073    ctx: &mut Context,
1074    cctx: &mut ConversionContext,
1075    inst: LLVMValue,
1076) -> Result<Ptr<Operation>> {
1077    let llvm_operands: Vec<_> = (0..llvm_get_num_arg_operands(inst))
1078        .map(|opd_idx| llvm_get_operand(inst, opd_idx))
1079        .collect();
1080    let (args, _) = convert_operands(ctx, cctx, &llvm_operands)?;
1081
1082    let callee = llvm_get_called_value(inst);
1083
1084    // Gather LLVM attributes at this call site's function index.
1085    let call_attrs = convert_llvm_attributes(
1086        ctx,
1087        cctx,
1088        llvm_get_call_site_attributes(inst, LLVM_ATTRIBUTE_FUNCTION_INDEX),
1089    )?;
1090
1091    if llvm_get_value_kind(callee) == LLVMValueKind::LLVMInlineAsmValueKind {
1092        let asm = llvm_get_inline_asm_asm_string(callee);
1093        let constraints = llvm_get_inline_asm_constraint_string(callee);
1094        let side_effects = llvm_get_inline_asm_has_side_effects(callee);
1095        let result_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1096        let op = InlineAsmOp::new(ctx, result_ty, args, &asm, &constraints, side_effects);
1097        if !call_attrs.is_empty() {
1098            op.set_attr_llvm_inline_asm_attrs(ctx, call_attrs);
1099        }
1100        return Ok(op.get_operation());
1101    }
1102
1103    enum Callee {
1104        FnCall(CallOpCallable),
1105        IntrinsicCall(String),
1106    }
1107    let callee = if llvm_is_a::function(callee) {
1108        let llvm_fn_name =
1109            llvm_get_value_name(callee).expect("Unable to obtain valid function name");
1110        if llvm_lookup_intrinsic_id(&llvm_fn_name).is_some() {
1111            Callee::IntrinsicCall(llvm_fn_name)
1112        } else {
1113            let fn_name = cctx.id_legaliser.legalise(&llvm_fn_name);
1114            Callee::FnCall(CallOpCallable::Direct(fn_name))
1115        }
1116    } else {
1117        let (callee_converted, _) = convert_operands(ctx, cctx, &[callee])?;
1118        Callee::FnCall(CallOpCallable::Indirect(callee_converted[0]))
1119    };
1120
1121    let callee_ty = llvm_get_called_function_type(inst);
1122    let callee_ty: TypedHandle<FuncType> =
1123        convert_type(ctx, cctx, callee_ty).and_then(|ty| TypedHandle::from_handle(ty, ctx))?;
1124
1125    let fmf: Option<FastmathFlagsAttr> = if llvm_can_value_use_fast_math_flags(inst) {
1126        // Not all calls can have fast-math flags.
1127        let fmf = llvm_get_fast_math_flags(inst);
1128        (!fmf.is_empty()).then_some(fmf.into())
1129    } else {
1130        None
1131    };
1132
1133    let op = match callee {
1134        Callee::FnCall(callable) => {
1135            let op = CallOp::new(ctx, callable, callee_ty, args);
1136            if let Some(fmf) = fmf {
1137                op.set_attr_llvm_call_fastmath_flags(ctx, fmf);
1138            }
1139            if !call_attrs.is_empty() {
1140                op.set_attr_llvm_call_attrs(ctx, call_attrs);
1141            }
1142            op.get_operation()
1143        }
1144        Callee::IntrinsicCall(name) => {
1145            let op = CallIntrinsicOp::new(ctx, name.into(), callee_ty, args);
1146            if let Some(fmf) = fmf {
1147                op.set_attr_llvm_intrinsic_fastmath_flags(ctx, fmf);
1148            }
1149            if !call_attrs.is_empty() {
1150                op.set_attr_llvm_intrinsic_attrs(ctx, call_attrs);
1151            }
1152            op.get_operation()
1153        }
1154    };
1155
1156    Ok(op)
1157}
1158
1159fn convert_instruction(
1160    ctx: &mut Context,
1161    cctx: &mut ConversionContext,
1162    inst: LLVMValue,
1163) -> Result<Ptr<Operation>> {
1164    if llvm_is_a::call_inst(inst) {
1165        return convert_call(ctx, cctx, inst);
1166    }
1167
1168    fn get_integer_overflow_flag(inst: LLVMValue) -> IntegerOverflowFlagsAttr {
1169        let mut flags = IntegerOverflowFlagsAttr::default();
1170        if llvm_get_nsw(inst) {
1171            flags.nsw = true;
1172        }
1173        if llvm_get_nuw(inst) {
1174            flags.nuw = true;
1175        }
1176        flags
1177    }
1178
1179    let llvm_operands: Vec<_> = (0..llvm_get_num_operands(inst))
1180        .map(|opd_idx| llvm_get_operand(inst, opd_idx))
1181        .collect();
1182
1183    let (ref opds, ref succs) = convert_operands(ctx, cctx, &llvm_operands)?;
1184    match llvm_get_instruction_opcode(inst) {
1185        LLVMOpcode::LLVMAdd => {
1186            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1187            Ok(
1188                AddOp::new_with_overflow_flag(ctx, lhs, rhs, get_integer_overflow_flag(inst))
1189                    .get_operation(),
1190            )
1191        }
1192        LLVMOpcode::LLVMAddrSpaceCast => {
1193            let arg = get_operand(opds, 0)?;
1194            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1195            Ok(AddrSpaceCastOp::new(ctx, arg, res_ty).get_operation())
1196        }
1197        LLVMOpcode::LLVMAlloca => {
1198            let elem_type = convert_type(ctx, cctx, llvm_get_allocated_type(inst))?;
1199            let size = get_operand(opds, 0)?;
1200            let address_space = llvm_get_pointer_address_space(llvm_type_of(inst));
1201            let op = AllocaOp::new(ctx, elem_type, size, address_space);
1202            let alignment = llvm_get_alignment(inst);
1203            if alignment != 0 {
1204                op.set_alignment(ctx, alignment);
1205            }
1206            Ok(op.get_operation())
1207        }
1208        LLVMOpcode::LLVMAnd => {
1209            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1210            Ok(AndOp::new(ctx, lhs, rhs).get_operation())
1211        }
1212        LLVMOpcode::LLVMAShr => {
1213            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1214            Ok(AShrOp::new(ctx, lhs, rhs).get_operation())
1215        }
1216        LLVMOpcode::LLVMAtomicCmpXchg => {
1217            let (ptr, cmp, new) = (
1218                get_operand(opds, 0)?,
1219                get_operand(opds, 1)?,
1220                get_operand(opds, 2)?,
1221            );
1222            let success = convert_ordering_from_llvm(llvm_get_cmpxchg_success_ordering(inst));
1223            let failure = convert_ordering_from_llvm(llvm_get_cmpxchg_failure_ordering(inst));
1224            let syncscope = syncscope_from_llvm(inst);
1225            Ok(
1226                AtomicCmpxchgOp::new(ctx, ptr, cmp, new, success, failure, syncscope)
1227                    .get_operation(),
1228            )
1229        }
1230        LLVMOpcode::LLVMAtomicRMW => {
1231            let (ptr, val) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1232            let kind = convert_rmw_kind_from_llvm(llvm_get_atomic_rmw_bin_op(inst));
1233            let ordering = convert_ordering_from_llvm(llvm_get_ordering(inst));
1234            let syncscope = syncscope_from_llvm(inst);
1235            Ok(AtomicRmwOp::new(ctx, ptr, val, kind, ordering, syncscope).get_operation())
1236        }
1237        LLVMOpcode::LLVMBitCast => {
1238            let arg = get_operand(opds, 0)?;
1239            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1240            Ok(BitcastOp::new(ctx, arg, res_ty).get_operation())
1241        }
1242        LLVMOpcode::LLVMUncondBr => {
1243            let dest_opds = convert_branch_args(
1244                ctx,
1245                cctx,
1246                llvm_get_instruction_parent(inst).unwrap(),
1247                llvm_value_as_basic_block(llvm_get_operand(inst, 0)),
1248            )?;
1249            Ok(BrOp::new(ctx, get_operand(succs, 0)?, dest_opds).get_operation())
1250        }
1251        LLVMOpcode::LLVMCondBr => {
1252            assert!(
1253                opds.len() == 1,
1254                "Conditional branch must have one condition"
1255            );
1256            assert!(
1257                succs.len() == 2,
1258                "Conditional branch must have two successors"
1259            );
1260            let true_dest_opds = convert_branch_args(
1261                ctx,
1262                cctx,
1263                llvm_get_instruction_parent(inst).unwrap(),
1264                llvm_value_as_basic_block(llvm_get_operand(inst, 1)),
1265            )?;
1266            let false_dest_opds = convert_branch_args(
1267                ctx,
1268                cctx,
1269                llvm_get_instruction_parent(inst).unwrap(),
1270                llvm_value_as_basic_block(llvm_get_operand(inst, 2)),
1271            )?;
1272            Ok(CondBrOp::new(
1273                ctx,
1274                get_operand(opds, 0)?,
1275                get_operand(succs, 0)?,
1276                true_dest_opds,
1277                get_operand(succs, 1)?,
1278                false_dest_opds,
1279            )
1280            .get_operation())
1281        }
1282        LLVMOpcode::LLVMCall => {
1283            unreachable!("Should've already been processed separately")
1284        }
1285        LLVMOpcode::LLVMCallBr => todo!(),
1286        LLVMOpcode::LLVMCatchPad => todo!(),
1287        LLVMOpcode::LLVMCatchRet => todo!(),
1288        LLVMOpcode::LLVMCatchSwitch => todo!(),
1289        LLVMOpcode::LLVMCleanupPad => todo!(),
1290        LLVMOpcode::LLVMCleanupRet => todo!(),
1291        LLVMOpcode::LLVMFNeg => {
1292            let arg = get_operand(opds, 0)?;
1293            Ok(
1294                FNegOp::new_with_fast_math_flags(ctx, arg, llvm_get_fast_math_flags(inst).into())
1295                    .get_operation(),
1296            )
1297        }
1298        LLVMOpcode::LLVMFAdd => {
1299            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1300            Ok(FAddOp::new_with_fast_math_flags(
1301                ctx,
1302                lhs,
1303                rhs,
1304                llvm_get_fast_math_flags(inst).into(),
1305            )
1306            .get_operation())
1307        }
1308        LLVMOpcode::LLVMFCmp => {
1309            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1310            let pred = convert_fpredicate(llvm_get_fcmp_predicate(inst));
1311            let fastmath = llvm_get_fast_math_flags(inst);
1312            let op = FCmpOp::new(ctx, pred, lhs, rhs);
1313            op.set_fast_math_flags(ctx, fastmath.into());
1314            Ok(op.get_operation())
1315        }
1316        LLVMOpcode::LLVMFDiv => {
1317            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1318            Ok(FDivOp::new_with_fast_math_flags(
1319                ctx,
1320                lhs,
1321                rhs,
1322                llvm_get_fast_math_flags(inst).into(),
1323            )
1324            .get_operation())
1325        }
1326        LLVMOpcode::LLVMFMul => {
1327            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1328            Ok(FMulOp::new_with_fast_math_flags(
1329                ctx,
1330                lhs,
1331                rhs,
1332                llvm_get_fast_math_flags(inst).into(),
1333            )
1334            .get_operation())
1335        }
1336        LLVMOpcode::LLVMFRem => {
1337            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1338            Ok(FRemOp::new_with_fast_math_flags(
1339                ctx,
1340                lhs,
1341                rhs,
1342                llvm_get_fast_math_flags(inst).into(),
1343            )
1344            .get_operation())
1345        }
1346        LLVMOpcode::LLVMFSub => {
1347            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1348            Ok(FSubOp::new_with_fast_math_flags(
1349                ctx,
1350                lhs,
1351                rhs,
1352                llvm_get_fast_math_flags(inst).into(),
1353            )
1354            .get_operation())
1355        }
1356        LLVMOpcode::LLVMFPExt => {
1357            let arg = get_operand(opds, 0)?;
1358            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1359            let op = FPExtOp::new(ctx, arg, res_ty);
1360            op.set_fast_math_flags(ctx, llvm_get_fast_math_flags(inst).into());
1361            Ok(op.get_operation())
1362        }
1363        LLVMOpcode::LLVMFPTrunc => {
1364            let arg = get_operand(opds, 0)?;
1365            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1366            let op = FPTruncOp::new(ctx, arg, res_ty);
1367            op.set_fast_math_flags(ctx, llvm_get_fast_math_flags(inst).into());
1368            Ok(op.get_operation())
1369        }
1370        LLVMOpcode::LLVMFPToSI => {
1371            let arg = get_operand(opds, 0)?;
1372            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1373            Ok(FPToSIOp::new(ctx, arg, res_ty).get_operation())
1374        }
1375        LLVMOpcode::LLVMFPToUI => {
1376            let arg = get_operand(opds, 0)?;
1377            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1378            Ok(FPToUIOp::new(ctx, arg, res_ty).get_operation())
1379        }
1380        LLVMOpcode::LLVMSIToFP => {
1381            let arg = get_operand(opds, 0)?;
1382            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1383            Ok(SIToFPOp::new(ctx, arg, res_ty).get_operation())
1384        }
1385        LLVMOpcode::LLVMUIToFP => {
1386            let arg = get_operand(opds, 0)?;
1387            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1388            let nneg = llvm_get_nneg(inst);
1389            Ok(UIToFPOp::new_with_nneg(ctx, arg, res_ty, nneg).get_operation())
1390        }
1391        LLVMOpcode::LLVMFence => {
1392            let ordering = convert_ordering_from_llvm(llvm_get_ordering(inst));
1393            let syncscope = syncscope_from_llvm(inst);
1394            Ok(FenceOp::new(ctx, ordering, syncscope).get_operation())
1395        }
1396        LLVMOpcode::LLVMFreeze => {
1397            let arg = get_operand(opds, 0)?;
1398            Ok(FreezeOp::new(ctx, arg).get_operation())
1399        }
1400        LLVMOpcode::LLVMGetElementPtr => {
1401            let mut opds = opds.iter();
1402            let base = opds
1403                .next()
1404                .ok_or_else(|| input_error_noloc!(ConversionErr::OpdMissing(0)))?;
1405            let indices = opds
1406                .map(|v| {
1407                    if let Some(c) = get_const_op_as_u32(ctx, *v) {
1408                        GepIndex::Constant(c)
1409                    } else {
1410                        GepIndex::Value(*v)
1411                    }
1412                })
1413                .collect::<Vec<_>>();
1414            let src_elm_type = convert_type(ctx, cctx, llvm_get_gep_source_element_type(inst))?;
1415            Ok(GetElementPtrOp::new_with_no_wrap_flags(
1416                ctx,
1417                *base,
1418                indices,
1419                src_elm_type,
1420                llvm_get_gep_no_wrap_flags(inst),
1421            )
1422            .get_operation())
1423        }
1424        LLVMOpcode::LLVMICmp => {
1425            let pred = convert_ipredicate(llvm_get_icmp_predicate(inst));
1426            Ok(
1427                ICmpOp::new(ctx, pred, get_operand(opds, 0)?, get_operand(opds, 1)?)
1428                    .get_operation(),
1429            )
1430        }
1431        LLVMOpcode::LLVMIndirectBr => {
1432            let addr = get_operand(opds, 0)?;
1433            let src_block = llvm_get_instruction_parent(inst).unwrap();
1434            let dests = succs
1435                .iter()
1436                .enumerate()
1437                .map(|(i, dest)| {
1438                    // Operand 0 is the address, so destinations start at operand 1.
1439                    let llvm_dest =
1440                        llvm_value_as_basic_block(llvm_get_operand(inst, (i + 1) as u32));
1441                    let dest_opds = convert_branch_args(ctx, cctx, src_block, llvm_dest)?;
1442                    Ok((*dest, dest_opds))
1443                })
1444                .collect::<Result<Vec<_>>>()?;
1445            Ok(IndirectBrOp::new(ctx, addr, dests).get_operation())
1446        }
1447        LLVMOpcode::LLVMInsertElement => {
1448            let vector = get_operand(opds, 0)?;
1449            let element = get_operand(opds, 1)?;
1450            let index = get_operand(opds, 2)?;
1451            Ok(InsertElementOp::new(ctx, vector, element, index).get_operation())
1452        }
1453        LLVMOpcode::LLVMExtractElement => {
1454            let vector = get_operand(opds, 0)?;
1455            let index = get_operand(opds, 1)?;
1456            Ok(ExtractElementOp::new(ctx, vector, index).get_operation())
1457        }
1458        LLVMOpcode::LLVMShuffleVector => {
1459            let vec1 = get_operand(opds, 0)?;
1460            let vec2 = get_operand(opds, 1)?;
1461            let num_mask_elems = llvm_get_num_mask_elements(inst);
1462            let mask = (0..num_mask_elems)
1463                .map(|i| llvm_get_mask_value(inst, i))
1464                .collect();
1465            Ok(ShuffleVectorOp::new(ctx, vec1, vec2, mask).get_operation())
1466        }
1467        LLVMOpcode::LLVMInsertValue => {
1468            let (aggr, val) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1469            let indices = llvm_get_indices(inst);
1470            Ok(InsertValueOp::new(ctx, aggr, val, indices).get_operation())
1471        }
1472        LLVMOpcode::LLVMExtractValue => {
1473            let aggr = get_operand(opds, 0)?;
1474            let indices = llvm_get_indices(inst);
1475            Ok(ExtractValueOp::new(ctx, aggr, indices)?.get_operation())
1476        }
1477        LLVMOpcode::LLVMIntToPtr => {
1478            let arg = get_operand(opds, 0)?;
1479            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1480            Ok(IntToPtrOp::new(ctx, arg, res_ty).get_operation())
1481        }
1482        LLVMOpcode::LLVMInvoke => todo!(),
1483        LLVMOpcode::LLVMLandingPad => todo!(),
1484        LLVMOpcode::LLVMLoad => {
1485            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1486            let ptr = get_operand(opds, 0)?;
1487            let alignment = llvm_get_alignment(inst);
1488            let ordering = llvm_get_ordering(inst);
1489            let is_volatile = llvm_get_volatile(inst);
1490            if matches!(ordering, LLVMAtomicOrdering::LLVMAtomicOrderingNotAtomic) {
1491                let load_op = LoadOp::new(ctx, ptr, res_ty);
1492                if is_volatile {
1493                    load_op.set_volatile(ctx, true);
1494                }
1495                if alignment != 0 {
1496                    load_op.set_alignment(ctx, alignment);
1497                }
1498                Ok(load_op.get_operation())
1499            } else {
1500                let load_op = AtomicLoadOp::new(
1501                    ctx,
1502                    ptr,
1503                    res_ty,
1504                    convert_ordering_from_llvm(ordering),
1505                    syncscope_from_llvm(inst),
1506                );
1507                if alignment != 0 {
1508                    load_op.set_alignment(ctx, alignment);
1509                }
1510                Ok(load_op.get_operation())
1511            }
1512        }
1513        LLVMOpcode::LLVMLShr => {
1514            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1515            Ok(LShrOp::new(ctx, lhs, rhs).get_operation())
1516        }
1517        LLVMOpcode::LLVMMul => {
1518            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1519            Ok(
1520                MulOp::new_with_overflow_flag(ctx, lhs, rhs, get_integer_overflow_flag(inst))
1521                    .get_operation(),
1522            )
1523        }
1524        LLVMOpcode::LLVMOr => {
1525            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1526            Ok(OrOp::new(ctx, lhs, rhs).get_operation())
1527        }
1528        LLVMOpcode::LLVMPHI => {
1529            unreachable!("PHI nodes must already be handled")
1530        }
1531        LLVMOpcode::LLVMPtrToInt => {
1532            let arg = get_operand(opds, 0)?;
1533            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1534            Ok(PtrToIntOp::new(ctx, arg, res_ty).get_operation())
1535        }
1536        LLVMOpcode::LLVMPtrToAddr => {
1537            unimplemented!("LLVM-C does not have LLVMBuildPtrToAddr yet")
1538        }
1539        LLVMOpcode::LLVMResume => todo!(),
1540        LLVMOpcode::LLVMRet => {
1541            let retval = if llvm_get_num_operands(inst) == 1 {
1542                Some(get_operand(opds, 0)?)
1543            } else {
1544                None
1545            };
1546            Ok(ReturnOp::new(ctx, retval).get_operation())
1547        }
1548        LLVMOpcode::LLVMSDiv => {
1549            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1550            Ok(SDivOp::new(ctx, lhs, rhs).get_operation())
1551        }
1552        LLVMOpcode::LLVMSelect => {
1553            let (cond, true_val, false_val) = (
1554                get_operand(opds, 0)?,
1555                get_operand(opds, 1)?,
1556                get_operand(opds, 2)?,
1557            );
1558            let op = SelectOp::new(ctx, cond, true_val, false_val);
1559            // Only float-typed selects can carry fast-math flags.
1560            if llvm_can_value_use_fast_math_flags(inst) {
1561                let fmf = llvm_get_fast_math_flags(inst);
1562                if !fmf.is_empty() {
1563                    op.set_attr_llvm_select_fast_math_flags(ctx, fmf.into());
1564                }
1565            }
1566            Ok(op.get_operation())
1567        }
1568        LLVMOpcode::LLVMSExt => {
1569            let arg = get_operand(opds, 0)?;
1570            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1571            Ok(SExtOp::new(ctx, arg, res_ty).get_operation())
1572        }
1573        LLVMOpcode::LLVMZExt => {
1574            let arg = get_operand(opds, 0)?;
1575            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1576            let nneg = llvm_get_nneg(inst);
1577            Ok(ZExtOp::new_with_nneg(ctx, arg, res_ty, nneg).get_operation())
1578        }
1579        LLVMOpcode::LLVMTrunc => {
1580            let arg = get_operand(opds, 0)?;
1581            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1582            Ok(TruncOp::new(ctx, arg, res_ty).get_operation())
1583        }
1584        LLVMOpcode::LLVMShl => {
1585            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1586            Ok(
1587                ShlOp::new_with_overflow_flag(ctx, lhs, rhs, get_integer_overflow_flag(inst))
1588                    .get_operation(),
1589            )
1590        }
1591        LLVMOpcode::LLVMSRem => {
1592            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1593            Ok(SRemOp::new(ctx, lhs, rhs).get_operation())
1594        }
1595        LLVMOpcode::LLVMStore => {
1596            let (value_opd, ptr_opd) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1597            let alignment = llvm_get_alignment(inst);
1598            let ordering = llvm_get_ordering(inst);
1599            let is_volatile = llvm_get_volatile(inst);
1600            if matches!(ordering, LLVMAtomicOrdering::LLVMAtomicOrderingNotAtomic) {
1601                let store_op = StoreOp::new(ctx, value_opd, ptr_opd);
1602                if is_volatile {
1603                    store_op.set_volatile(ctx, true);
1604                }
1605                if alignment != 0 {
1606                    store_op.set_alignment(ctx, alignment);
1607                }
1608                Ok(store_op.get_operation())
1609            } else {
1610                let store_op = AtomicStoreOp::new(
1611                    ctx,
1612                    value_opd,
1613                    ptr_opd,
1614                    convert_ordering_from_llvm(ordering),
1615                    syncscope_from_llvm(inst),
1616                );
1617                if alignment != 0 {
1618                    store_op.set_alignment(ctx, alignment);
1619                }
1620                Ok(store_op.get_operation())
1621            }
1622        }
1623        LLVMOpcode::LLVMSub => {
1624            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1625            Ok(
1626                SubOp::new_with_overflow_flag(ctx, lhs, rhs, get_integer_overflow_flag(inst))
1627                    .get_operation(),
1628            )
1629        }
1630        LLVMOpcode::LLVMSwitch => {
1631            let cond = get_operand(opds, 0)?;
1632            let default_dest = succs
1633                .first()
1634                .ok_or_else(|| input_error_noloc!(ConversionErr::SuccMissing(0)))?;
1635            let num_cases = succs.len() - 1;
1636            // Case values aren't stored as operands, but we can get them using `llvm_get_switch_case_value`.
1637            let mut case_values = vec![];
1638            // Case 0 is the default destination, so we start with 1.
1639            for case_idx in 1..=num_cases {
1640                let case_value = llvm_get_switch_case_value(inst, case_idx.try_into().unwrap());
1641                process_constant(ctx, cctx, case_value)?;
1642                let Some(case_value) = cctx.value_map.get(&case_value) else {
1643                    return input_err_noloc!(ConversionErr::SwitchCaseNonIntConst);
1644                };
1645                case_values.push(*case_value);
1646            }
1647            let cases = case_values
1648                .iter()
1649                // Skip the first successor which is the default destination
1650                .zip(succs.iter().skip(1))
1651                .enumerate()
1652                .map(|(case_idx, (case_val, dest_block))| {
1653                    let case_val = get_const_op_as_int(ctx, *case_val).ok_or_else(|| {
1654                        input_error_noloc!("Switch case value must be a constant integer")
1655                    })?;
1656                    let case_idx: u32 = case_idx.try_into().unwrap();
1657                    // Operand 0 is the condition and operand 1 is the default destination,
1658                    // so case destinations start at operand index 2.
1659                    let llvm_dest = llvm_value_as_basic_block(llvm_get_operand(inst, 2 + case_idx));
1660                    assert!(
1661                        cctx.block_map.get(&llvm_dest).unwrap() == dest_block,
1662                        "Switch case destination block does not match the expected block"
1663                    );
1664                    let case_args = convert_branch_args(
1665                        ctx,
1666                        cctx,
1667                        llvm_get_instruction_parent(inst).unwrap(),
1668                        llvm_dest,
1669                    )?;
1670                    Ok(SwitchCase {
1671                        value: case_val,
1672                        dest: *dest_block,
1673                        dest_opds: case_args,
1674                    })
1675                })
1676                .collect::<Result<Vec<_>>>()?;
1677            let default_dest_args = convert_branch_args(
1678                ctx,
1679                cctx,
1680                llvm_get_instruction_parent(inst).unwrap(),
1681                llvm_value_as_basic_block(llvm_get_operand(inst, 1)),
1682            )?;
1683            Ok(SwitchOp::new(ctx, cond, *default_dest, default_dest_args, cases).get_operation())
1684        }
1685        LLVMOpcode::LLVMUDiv => {
1686            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1687            Ok(UDivOp::new(ctx, lhs, rhs).get_operation())
1688        }
1689        LLVMOpcode::LLVMUnreachable => Ok(UnreachableOp::new(ctx).get_operation()),
1690        LLVMOpcode::LLVMURem => {
1691            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1692            Ok(URemOp::new(ctx, lhs, rhs).get_operation())
1693        }
1694        LLVMOpcode::LLVMVAArg => {
1695            let arg = get_operand(opds, 0)?;
1696            let res_ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1697            Ok(VAArgOp::new(ctx, arg, res_ty).get_operation())
1698        }
1699        LLVMOpcode::LLVMUserOp1 => todo!(),
1700        LLVMOpcode::LLVMUserOp2 => todo!(),
1701
1702        LLVMOpcode::LLVMXor => {
1703            let (lhs, rhs) = (get_operand(opds, 0)?, get_operand(opds, 1)?);
1704            Ok(XorOp::new(ctx, lhs, rhs).get_operation())
1705        }
1706    }
1707}
1708
1709/// Convert [LLVMBasicBlock] to pliron's [BasicBlock].
1710fn convert_block(
1711    ctx: &mut Context,
1712    cctx: &mut ConversionContext,
1713    module: &LLVMModule,
1714    block: LLVMBasicBlock,
1715    m_block: Ptr<BasicBlock>,
1716) -> Result<()> {
1717    let mut inserter = IRInserter::<DummyListener>::new_at_block_end(m_block);
1718    for inst in instruction_iter(block) {
1719        if llvm_get_instruction_opcode(inst) == LLVMOpcode::LLVMPHI {
1720            // We drop metadata attached to PHIs. TODO: Attach them to the block's [AttributeDict].
1721            for (kind_id, _) in llvm_instruction_get_all_metadata_other_than_debug_loc(inst) {
1722                let kind = md_kind_name(cctx, module, kind_id)?;
1723                if kind != MD_KIND_DBG {
1724                    log::warn!("Dropping metadata \"{kind}\" attached to a PHI");
1725                }
1726            }
1727            let ty = convert_type(ctx, cctx, llvm_type_of(inst))?;
1728            let arg_idx = BasicBlock::push_argument(m_block, ctx, ty);
1729            cctx.value_map
1730                .insert(inst, m_block.deref(ctx).get_argument(arg_idx));
1731        } else {
1732            let m_inst = convert_instruction(ctx, cctx, inst)?;
1733            inserter.insert_operation(ctx, m_inst);
1734            convert_instruction_metadata(ctx, cctx, module, inst, m_inst)?;
1735            let m_inst_result = m_inst.deref(ctx).results().next();
1736            // LLVM instructions have at most one result.
1737            if let Some(result) = m_inst_result {
1738                let res_name = llvm_get_value_name(inst)
1739                    .filter(|name| !name.is_empty())
1740                    .map(|name| cctx.id_legaliser.legalise(&name));
1741                result.set_name(ctx, res_name);
1742                cctx.value_map.insert(inst, result);
1743            }
1744        }
1745    }
1746    Ok(())
1747}
1748
1749fn convert_function(
1750    ctx: &mut Context,
1751    cctx: &mut ConversionContext,
1752    module: &LLVMModule,
1753    function: LLVMValue,
1754) -> Result<FuncOp> {
1755    assert!(llvm_is_a::function(function));
1756
1757    let llvm_name = llvm_get_value_name(function).expect("Expected function to have a name");
1758    let name = cctx.legalized_symbol_name(function);
1759    let fn_ty = convert_type(ctx, cctx, llvm_global_get_value_type(function))?;
1760    let fn_ty = TypedHandle::from_handle(fn_ty, ctx)?;
1761    // Create a new FuncOp.
1762    let m_func = FuncOp::new(ctx, name.clone(), fn_ty);
1763
1764    let linkage = convert_linkage(llvm_get_linkage(function));
1765    m_func.set_attr_llvm_function_linkage(ctx, linkage);
1766
1767    let attrs = convert_llvm_attributes(
1768        ctx,
1769        cctx,
1770        llvm_get_attributes_at_index(function, LLVM_ATTRIBUTE_FUNCTION_INDEX),
1771    )?;
1772    if !attrs.is_empty() {
1773        m_func.set_attr_llvm_func_attrs(ctx, attrs);
1774    }
1775
1776    if llvm_name != <Identifier as Into<String>>::into(name) {
1777        m_func.set_llvm_symbol_name(ctx, llvm_name);
1778    }
1779
1780    convert_global_object_metadata(ctx, cctx, module, function, m_func.get_operation())?;
1781
1782    // If function is just a declaration, we have nothing more to do.
1783    if llvm_is_declaration(function) {
1784        return Ok(m_func);
1785    }
1786
1787    let m_entry_block = m_func.get_or_create_entry_block(ctx);
1788    let m_func_reg = m_func.get_region(ctx).unwrap();
1789    cctx.reset_for_region(m_entry_block);
1790
1791    let blocks = rpo(function);
1792    // Map entry block
1793    let mut blocks_iter = blocks.iter();
1794    let Some(entry) = blocks_iter.next() else {
1795        return Ok(m_func);
1796    };
1797
1798    cctx.block_map.insert(*entry, m_entry_block);
1799    {
1800        let val_map = &mut cctx.value_map;
1801        let m_entry_block_ref = m_entry_block.deref(ctx);
1802        // Map function args to entry block args.
1803        for (arg_idx, arg) in param_iter(function).enumerate() {
1804            val_map.insert(arg, m_entry_block_ref.get_argument(arg_idx));
1805        }
1806    }
1807
1808    // Create, place and map rest of the blocks.
1809    for block in blocks_iter {
1810        let label = llvm_get_basic_block_name(*block)
1811            .filter(|name| !name.is_empty())
1812            .map(|name| cctx.id_legaliser.legalise(&name));
1813        let m_block = BasicBlock::new(ctx, label, vec![]);
1814        m_block.insert_at_back(m_func_reg, ctx);
1815        cctx.block_map.insert(*block, m_block);
1816    }
1817
1818    // Finally, convert all blocks
1819    for block in blocks {
1820        let m_block = *cctx
1821            .block_map
1822            .get(&block)
1823            .expect("We have an unmapped block !");
1824        convert_block(ctx, cctx, module, block, m_block)?;
1825    }
1826
1827    Ok(m_func)
1828}
1829
1830fn convert_global(
1831    ctx: &mut Context,
1832    cctx: &mut ConversionContext,
1833    module: &LLVMModule,
1834    global: LLVMValue,
1835) -> Result<GlobalOp> {
1836    let llvm_name = llvm_get_value_name(global).unwrap_or_default();
1837    let name = cctx.legalized_symbol_name(global);
1838
1839    let ty = convert_type(
1840        ctx,
1841        &mut ConversionContext::default(),
1842        llvm_global_get_value_type(global),
1843    )?;
1844
1845    let op = GlobalOp::new(ctx, name.clone(), ty);
1846
1847    let addr_space = llvm_get_pointer_address_space(llvm_type_of(global));
1848    if addr_space != 0 {
1849        op.set_address_space(ctx, addr_space);
1850    }
1851
1852    if <Identifier as Into<String>>::into(name) != llvm_name {
1853        op.set_llvm_symbol_name(ctx, llvm_name);
1854    }
1855
1856    let linkage = convert_linkage(llvm_get_linkage(global));
1857    op.set_attr_llvm_global_linkage(ctx, linkage);
1858
1859    let alignment = llvm_get_alignment(global);
1860    if alignment != 0 {
1861        op.set_alignment(ctx, alignment);
1862    }
1863
1864    if llvm_is_global_constant(global) {
1865        op.set_constant(ctx, true);
1866    }
1867
1868    if let Some(init) = llvm_get_initializer(global) {
1869        assert!(!llvm_is_declaration(global));
1870
1871        // We prefer to have an attribute based initializer than a region based one.
1872        let init_attr = const_llvm_value_to_attr(ctx, cctx, init)?;
1873        if let Some(attr) = init_attr {
1874            op.set_initializer_value(ctx, attr);
1875        } else {
1876            let init_region = op.add_initializer_region(ctx);
1877            let entry_block = init_region.deref(ctx).iter(ctx).next().unwrap();
1878            cctx.reset_for_region(entry_block);
1879
1880            // Convert the initializer.
1881            process_constant(ctx, cctx, init)?;
1882            let Some(m_val) = cctx.value_map.get(&init) else {
1883                panic!("We just processed this constant, it must be in the map");
1884            };
1885
1886            let return_op = ReturnOp::new(ctx, Some(*m_val));
1887            return_op.get_operation().insert_at_back(entry_block, ctx);
1888        }
1889    }
1890
1891    convert_global_object_metadata(ctx, cctx, module, global, op.get_operation())?;
1892
1893    Ok(op)
1894}
1895
1896/// Convert [LLVMModule] to [ModuleOp].
1897pub fn convert_module(ctx: &mut Context, module: &LLVMModule) -> Result<ModuleOp> {
1898    let cctx = &mut ConversionContext::default();
1899
1900    let module_name = llvm_get_module_identifier(module).unwrap_or_default();
1901    let module_name = cctx.id_legaliser.legalise(&module_name);
1902
1903    let m = ModuleOp::new(ctx, module_name);
1904
1905    let data_layout = module.data_layout();
1906    if !data_layout.is_empty() {
1907        crate::attributes::set_data_layout(ctx, m, data_layout);
1908    }
1909    let target_triple = module.target_triple();
1910    if !target_triple.is_empty() {
1911        crate::attributes::set_target_triple(ctx, m, target_triple);
1912    }
1913
1914    {
1915        // Note down every global and function name up front
1916        // to handle forwarded references for metadata conversion.
1917        for gv in global_iter(module) {
1918            cctx.legalized_symbol_name(gv);
1919        }
1920        for fun in function_iter(module) {
1921            let llvm_name = llvm_get_value_name(fun).expect("Expected function to have a name");
1922            if llvm_lookup_intrinsic_id(&llvm_name).is_some() {
1923                // Skip LLVM intrinsics.
1924                continue;
1925            }
1926            cctx.legalized_symbol_name(fun);
1927        }
1928    }
1929
1930    // Convert globals.
1931    for gv in global_iter(module) {
1932        let m_gv = convert_global(ctx, cctx, module, gv)?;
1933        m.append_operation(ctx, m_gv.get_operation(), 0);
1934    }
1935
1936    // Convert functions.
1937    let mut func_map = HMap::default();
1938    for fun in function_iter(module) {
1939        let llvm_name = llvm_get_value_name(fun).expect("Expected function to have a name");
1940        if llvm_lookup_intrinsic_id(&llvm_name).is_some() {
1941            // Skip LLVM intrinsics.
1942            continue;
1943        }
1944        let m_fun = convert_function(ctx, cctx, module, fun)?;
1945        m.append_operation(ctx, m_fun.get_operation(), 0);
1946        func_map.insert(fun, m_fun);
1947    }
1948
1949    // Fill the metadata table now, after the globals and functions have been converted.
1950    convert_module_metadata(ctx, cctx, module, m)?;
1951
1952    // We need to insert [BlockTagOp]s for blocks with their address taken.
1953    for ((func, block), tag) in &cctx.block_tag_map {
1954        if llvm_is_declaration(*func) {
1955            // Skip blocks in function declarations.
1956            continue;
1957        }
1958        let m_block = cctx
1959            .block_map
1960            .get(block)
1961            .expect("We should have converted this block");
1962        let tag_op = BlockTagOp::new(ctx, *tag);
1963        tag_op.get_operation().insert_at_front(*m_block, ctx);
1964    }
1965
1966    Ok(m)
1967}