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