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