Skip to main content

pliron_llvm/
ops.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! [Op]s defined in the LLVM dialect
5
6use core::num::NonZero;
7
8use pliron::{
9    arg_err_noloc,
10    attribute::{AttrObj, AttributeDict, attr_cast, attr_impls},
11    basic_block::BasicBlock,
12    builtin::{
13        attr_interfaces::{FloatAttr, TypedAttrInterface},
14        attributes::{BoolAttr, IdentifierAttr, IntegerAttr, StringAttr, TypeAttr},
15        op_interfaces::{
16            self, ATTR_KEY_SYM_NAME, AtMostNRegionsInterface, AtMostOneRegionInterface,
17            BranchOpInterface, CallOpCallable, CallOpInterface, IsTerminatorInterface,
18            IsolatedFromAboveInterface, NOpdsInterface, NResultsInterface, NSuccsInterface,
19            OneOpdInterface, OneResultInterface, OneSuccInterface, OperandSegmentInterface,
20            OptionalOpdInterface, SameOperandsAndResultType, SameOperandsType, SameResultsType,
21            SingleBlockRegionInterface, SymbolOpInterface, SymbolUserOpInterface,
22        },
23        type_interfaces::{FloatTypeInterface, FunctionTypeInterface},
24        types::{IntegerType, Signedness},
25    },
26    common_traits::{Named, Verify},
27    context::{Context, Ptr},
28    graph::walkers::{self, IRNode, WALKCONFIG_PREORDER_FORWARD},
29    identifier::Identifier,
30    indented_block, input_err,
31    irfmt::{
32        self,
33        parsers::{
34            attr_parser, block_opd_parser, delimited_list_parser, process_parsed_ssa_defs, spaced,
35            ssa_opd_parser, type_parser,
36        },
37        printers::{iter_with_sep, list_with_sep, op::typed_symb_op_header},
38    },
39    linked_list::ContainsLinkedList,
40    location::{Located, Location},
41    op::{Op, OpObj},
42    operation::Operation,
43    parsable::{IntoParseResult, Parsable, ParseResult, StateStream},
44    printable::{self, Printable, indented_nl},
45    region::Region,
46    result::{Error, ErrorKind, Result},
47    symbol_table::SymbolTableCollection,
48    r#type::{TypeHandle, TypedHandle, type_cast, type_impls},
49    utils::{apint::APInt, const_bound_n::I, vec_exns::VecExtns},
50    value::Value,
51    verify_err, verify_error,
52};
53
54use crate::{
55    attributes::{
56        AddressSpaceAttr, AlignmentAttr, AtomicOrderingAttr, AtomicRmwKindAttr, CaseValuesAttr,
57        FCmpPredicateAttr, FastmathFlagsAttr, InsertExtractValueIndicesAttr, LinkageAttr,
58        ShuffleVectorMaskAttr,
59    },
60    op_interfaces::{
61        AlignableOpInterface, BinArithOp, CastOpInterface, CastOpWithNNegInterface, FastMathFlags,
62        FloatBinArithOp, FloatBinArithOpWithFastMathFlags, IntBinArithOp,
63        IntBinArithOpWithOverflowFlag, IsDeclaration, LlvmSymbolName, NNegFlag, PointerTypeResult,
64    },
65    ops::{
66        func_op_attr_names::ATTR_KEY_LLVM_FUNC_TYPE,
67        global_op_attr_names::{ATTR_KEY_GLOBAL_INITIALIZER, ATTR_KEY_LLVM_GLOBAL_TYPE},
68    },
69    types::{ArrayType, FuncType, StructType, VectorType},
70};
71
72#[cfg(feature = "llvm-sys")]
73use crate::llvm_sys::core::{llvm_get_undef_mask_elem, llvm_lookup_intrinsic_id};
74
75use pliron::combine::{
76    self, between, optional,
77    parser::{Parser, char::spaces},
78    token,
79};
80
81use pliron::derive::{op_interface_impl, pliron_op};
82use thiserror::Error;
83
84use super::{
85    attributes::{GepIndexAttr, GepIndicesAttr, ICmpPredicateAttr},
86    types::PointerType,
87};
88
89/// Equivalent to LLVM's return opcode.
90///
91/// Operands:
92///
93/// | operand | description |
94/// |-----|-------|
95/// | `arg` | any type |
96#[pliron_op(
97    name = "llvm.return",
98    format = "operands(CharSpace(`,`))",
99    interfaces = [IsTerminatorInterface, NResultsInterface<0>, OptionalOpdInterface],
100)]
101pub struct ReturnOp;
102impl ReturnOp {
103    /// Create a new [ReturnOp]
104    pub fn new(ctx: &mut Context, value: Option<Value>) -> Self {
105        let op = Operation::new(
106            ctx,
107            Self::get_concrete_op_info(),
108            vec![],
109            value.into_iter().collect(),
110            vec![],
111            0,
112        );
113        ReturnOp { op }
114    }
115
116    /// Get the returned value, if it exists.
117    pub fn retval(&self, ctx: &Context) -> Option<Value> {
118        self.get_operand_opt(ctx)
119    }
120}
121
122#[derive(Error, Debug)]
123enum ReturnOpVerifyErr {
124    #[error("ReturnOp must have no operands in a void function")]
125    VoidWithOperand,
126    #[error("ReturnOp must have exactly one operand in a non-void function")]
127    NonVoidArity,
128    #[error("ReturnOp operand type does not match the function's result type")]
129    ResultTypeMismatch,
130}
131
132impl Verify for ReturnOp {
133    fn verify(&self, ctx: &Context) -> Result<()> {
134        use pliron::r#type::Typed;
135        // Signature coupling is only enforced when the return is inside a FuncOp.
136        let Some(parent_op) = self.get_operation().deref(ctx).get_parent_op(ctx) else {
137            return Ok(());
138        };
139        let Some(func_op) = Operation::get_op::<FuncOp>(parent_op, ctx) else {
140            return Ok(());
141        };
142        let func_ty = func_op.get_type(ctx);
143        let res_ty = func_ty.deref(ctx).result_type();
144        let num_operands = self.get_operation().deref(ctx).get_num_operands();
145        if res_ty.deref(ctx).is::<crate::types::VoidType>() {
146            if num_operands != 0 {
147                verify_err!(self.loc(ctx), ReturnOpVerifyErr::VoidWithOperand)?
148            }
149        } else if num_operands != 1 {
150            verify_err!(self.loc(ctx), ReturnOpVerifyErr::NonVoidArity)?
151        } else {
152            let ret_ty = self.get_operation().deref(ctx).get_operand(0).get_type(ctx);
153            if ret_ty != res_ty {
154                verify_err!(self.loc(ctx), ReturnOpVerifyErr::ResultTypeMismatch)?
155            }
156        }
157        Ok(())
158    }
159}
160
161/// Equivalent to LLVM's unreachable opcode.
162/// No operands or results.
163#[pliron_op(
164    name = "llvm.unreachable",
165    format,
166    interfaces = [IsTerminatorInterface, NOpdsInterface<0>, NResultsInterface<0>],
167    verifier = "succ"
168)]
169pub struct UnreachableOp;
170
171impl UnreachableOp {
172    /// Create a new [UnreachableOp]
173    pub fn new(ctx: &mut Context) -> Self {
174        let op = Operation::new(ctx, Self::get_concrete_op_info(), vec![], vec![], vec![], 0);
175        UnreachableOp { op }
176    }
177}
178
179macro_rules! new_int_bin_op_with_format {
180    (   $(#[$outer:meta])*
181        $op_name:ident, $op_id:literal, $format:literal
182    ) => {
183        $(#[$outer])*
184        /// ### Operands:
185        ///
186        /// | operand | description |
187        /// |-----|-------|
188        /// | `lhs` | Signless integer |
189        /// | `rhs` | Signless integer |
190        ///
191        /// ### Result(s):
192        ///
193        /// | result | description |
194        /// |-----|-------|
195        /// | `res` | Signless integer |
196        #[pliron_op(
197            name = $op_id,
198            format = $format,
199            interfaces = [
200                OneResultInterface, SameOperandsType, SameResultsType,
201                SameOperandsAndResultType, BinArithOp, IntBinArithOp, NOpdsInterface<2>
202            ],
203            verifier = "succ"
204        )]
205        pub struct $op_name;
206    }
207}
208
209macro_rules! new_int_bin_op {
210    (   $(#[$outer:meta])*
211        $op_name:ident, $op_id:literal
212    ) => {
213        new_int_bin_op_with_format!(
214            $(#[$outer])*
215            $op_name,
216            $op_id,
217            "$0 `, ` $1 ` : ` type($0)"
218        );
219    }
220}
221
222macro_rules! new_int_bin_op_with_overflow {
223    (   $(#[$outer:meta])*
224        $op_name:ident, $op_id:literal
225    ) => {
226        new_int_bin_op_with_format!(
227            $(#[$outer])*
228            /// ### Attributes:
229            ///
230            /// | key | value | via Interface |
231            /// |-----|-------| --------------
232            /// | [ATTR_KEY_INTEGER_OVERFLOW_FLAGS](super::op_interfaces::ATTR_KEY_INTEGER_OVERFLOW_FLAGS) | [IntegerOverflowFlagsAttr](super::attributes::IntegerOverflowFlagsAttr) | [IntBinArithOpWithOverflowFlag] |
233            $op_name,
234            $op_id,
235            "$0 `, ` $1 ` <` attr($llvm_integer_overflow_flags, `super::attributes::IntegerOverflowFlagsAttr`) `>` `: ` type($0)"
236        );
237        #[pliron::derive::op_interface_impl]
238        impl IntBinArithOpWithOverflowFlag for $op_name {}
239    }
240}
241
242new_int_bin_op_with_overflow!(
243    /// Equivalent to LLVM's Add opcode.
244    AddOp,
245    "llvm.add"
246);
247
248new_int_bin_op_with_overflow!(
249    /// Equivalent to LLVM's Sub opcode.
250    SubOp,
251    "llvm.sub"
252);
253
254new_int_bin_op_with_overflow!(
255    /// Equivalent to LLVM's Mul opcode.
256    MulOp,
257    "llvm.mul"
258);
259
260new_int_bin_op_with_overflow!(
261    /// Equivalent to LLVM's Shl opcode.
262    ShlOp,
263    "llvm.shl"
264);
265
266new_int_bin_op!(
267    /// Equivalent to LLVM's UDiv opcode.
268    UDivOp,
269    "llvm.udiv"
270);
271
272new_int_bin_op!(
273    /// Equivalent to LLVM's SDiv opcode.
274    SDivOp,
275    "llvm.sdiv"
276);
277
278new_int_bin_op!(
279    /// Equivalent to LLVM's URem opcode.
280    URemOp,
281    "llvm.urem"
282);
283
284new_int_bin_op!(
285    /// Equivalent to LLVM's SRem opcode.
286    SRemOp,
287    "llvm.srem"
288);
289
290new_int_bin_op!(
291    /// Equivalent to LLVM's And opcode.
292    AndOp,
293    "llvm.and"
294);
295
296new_int_bin_op!(
297    /// Equivalent to LLVM's Or opcode.
298    OrOp,
299    "llvm.or"
300);
301
302new_int_bin_op!(
303    /// Equivalent to LLVM's Xor opcode.
304    XorOp,
305    "llvm.xor"
306);
307
308new_int_bin_op!(
309    /// Equivalent to LLVM's LShr opcode.
310    LShrOp,
311    "llvm.lshr"
312);
313
314new_int_bin_op!(
315    /// Equivalent to LLVM's AShr opcode.
316    AShrOp,
317    "llvm.ashr"
318);
319
320#[derive(Error, Debug)]
321pub enum ICmpOpVerifyErr {
322    #[error("Result must be (possibly vector of) 1-bit integer (bool)")]
323    ResultNotBool,
324    #[error("Operand must be (possibly vector of) integer or pointer types")]
325    IncorrectOperandsType,
326    #[error("Missing or incorrect predicate attribute")]
327    PredAttrErr,
328    #[error("Vector operand and result types must have the same number of elements")]
329    MismatchedVectorNumElements,
330}
331
332/// Equivalent to LLVM's ICmp opcode.
333/// ### Operand(s):
334/// | operand | description |
335/// |-----|-------|
336/// | `lhs` | Signless integer or pointer |
337/// | `rhs` | Signless integer or pointer |
338///
339/// ### Result(s):
340///
341/// | result | description |
342/// |-----|-------|
343/// | `res` | 1-bit signless integer |
344#[pliron_op(
345    name = "llvm.icmp",
346    format = "$0 ` <` attr($icmp_predicate, $ICmpPredicateAttr) `> ` $1 ` : ` type($0)",
347    interfaces = [SameOperandsType, OneResultInterface, NOpdsInterface<2>],
348    attributes = (icmp_predicate: ICmpPredicateAttr)
349)]
350pub struct ICmpOp;
351
352impl ICmpOp {
353    /// Create a new [ICmpOp]
354    pub fn new(ctx: &mut Context, pred: ICmpPredicateAttr, lhs: Value, rhs: Value) -> Self {
355        use pliron::r#type::Typed;
356
357        // Determine the result type.
358        let bool_ty = IntegerType::get(ctx, 1, Signedness::Signless);
359        let opd_type = lhs.get_type(ctx);
360        let vec_details = opd_type
361            .deref(ctx)
362            .downcast_ref::<VectorType>()
363            .map(|vec_ty| (vec_ty.num_elements(), vec_ty.kind()));
364        let res_ty = if let Some((num_elements, kind)) = vec_details {
365            VectorType::get(ctx, bool_ty.into(), num_elements, kind).into()
366        } else {
367            bool_ty.into()
368        };
369
370        let op = Operation::new(
371            ctx,
372            Self::get_concrete_op_info(),
373            vec![res_ty],
374            vec![lhs, rhs],
375            vec![],
376            0,
377        );
378        let op = ICmpOp { op };
379        op.set_attr_icmp_predicate(ctx, pred);
380        op
381    }
382
383    /// Get the predicate
384    pub fn predicate(&self, ctx: &Context) -> ICmpPredicateAttr {
385        self.get_attr_icmp_predicate(ctx)
386            .expect("ICmpOp missing or incorrect predicate attribute type")
387            .clone()
388    }
389}
390
391impl Verify for ICmpOp {
392    fn verify(&self, ctx: &Context) -> Result<()> {
393        let loc = self.loc(ctx);
394
395        if self.get_attr_icmp_predicate(ctx).is_none() {
396            verify_err!(loc.clone(), ICmpOpVerifyErr::PredAttrErr)?
397        }
398
399        let mut res_ty = self.result_type(ctx);
400        let mut vec_num_elements = None;
401        if let Some(vec_ty) = res_ty.deref(ctx).downcast_ref::<VectorType>() {
402            res_ty = vec_ty.elem_type();
403            vec_num_elements = Some(vec_ty.num_elements());
404        }
405        let res_ty = res_ty.deref(ctx);
406        let Some(res_ty) = res_ty.downcast_ref::<IntegerType>() else {
407            return verify_err!(loc, ICmpOpVerifyErr::ResultNotBool);
408        };
409        if res_ty.width() != 1 {
410            return verify_err!(loc, ICmpOpVerifyErr::ResultNotBool);
411        }
412
413        let mut opd_ty = self.operand_type_i(ctx, I::<0>.into());
414        if let Some(vec_ty) = opd_ty.deref(ctx).downcast_ref::<VectorType>() {
415            opd_ty = vec_ty.elem_type();
416            // Ensure that the number of elements matches the result type's number of elements.
417            if vec_num_elements.is_none_or(|num_elements| vec_ty.num_elements() != num_elements) {
418                return verify_err!(loc, ICmpOpVerifyErr::MismatchedVectorNumElements);
419            }
420        } else if vec_num_elements.is_some() {
421            return verify_err!(loc, ICmpOpVerifyErr::MismatchedVectorNumElements);
422        }
423        let opd_ty = opd_ty.deref(ctx);
424        if !(opd_ty.is::<IntegerType>() || opd_ty.is::<PointerType>()) {
425            return verify_err!(loc, ICmpOpVerifyErr::IncorrectOperandsType);
426        }
427
428        Ok(())
429    }
430}
431
432#[derive(Error, Debug)]
433pub enum AllocaOpVerifyErr {
434    #[error("Operand must be a signless integer")]
435    OperandType,
436    #[error("Missing or incorrect type of attribute for element type")]
437    ElemTypeAttr,
438}
439
440/// Equivalent to LLVM's Alloca opcode.
441/// ### Operands
442/// | operand | description |
443/// |-----|-------|
444/// | `array_size` | Signless integer |
445///
446/// ### Result(s):
447///
448/// | result | description |
449/// |-----|-------|
450/// | `res` | [PointerType] |
451#[pliron_op(
452    name = "llvm.alloca",
453    format = "`[` attr($alloca_element_type, $TypeAttr) ` x ` $0 `]` ` ` \
454    opt_attr($llvm_alignment, $AlignmentAttr, label($align), delimiters(`[`, `]`)) \
455    ` : ` type($0)",
456    interfaces = [
457        OneResultInterface,
458        OneOpdInterface,
459        AlignableOpInterface,
460    ],
461    operands = (array_size: IntegerType),
462    results = (_: PointerType),
463    attributes = (alloca_element_type: TypeAttr)
464)]
465pub struct AllocaOp;
466impl Verify for AllocaOp {
467    fn verify(&self, ctx: &Context) -> Result<()> {
468        let loc = self.loc(ctx);
469        // Ensure correctness of element type.
470        if self.get_attr_alloca_element_type(ctx).is_none() {
471            verify_err!(loc, AllocaOpVerifyErr::ElemTypeAttr)?
472        }
473        Ok(())
474    }
475}
476
477#[op_interface_impl]
478impl PointerTypeResult for AllocaOp {
479    fn result_pointee_type(&self, ctx: &Context) -> TypeHandle {
480        self.get_attr_alloca_element_type(ctx)
481            .expect("AllocaOp missing or incorrect type for elem_type attribute")
482            .get_type(ctx)
483    }
484}
485
486impl AllocaOp {
487    /// Create a new [AllocaOp]
488    pub fn new(ctx: &mut Context, elem_type: TypeHandle, size: Value) -> Self {
489        // `alloca` yields a pointer in the default (stack) address space.
490        let ptr_ty = PointerType::get(ctx, 0).into();
491        let op = Operation::new(
492            ctx,
493            Self::get_concrete_op_info(),
494            vec![ptr_ty],
495            vec![size],
496            vec![],
497            0,
498        );
499        let op = AllocaOp { op };
500        op.set_attr_alloca_element_type(ctx, TypeAttr::new(elem_type));
501        op
502    }
503}
504
505/// Equivalent to LLVM's Bitcast opcode.
506/// ### Operands
507/// | operand | description |
508/// |-----|-------|
509/// | `arg` | non-aggregate LLVM type |
510///
511/// ### Result(s):
512///
513/// | result | description |
514/// |-----|-------|
515/// | `res` | non-aggregate LLVM type |
516#[pliron_op(
517    name = "llvm.bitcast",
518    format = "$0 ` to ` type($0)",
519    interfaces = [
520        OneResultInterface,
521        OneOpdInterface,
522        CastOpInterface
523    ],
524    verifier = "succ"
525)]
526pub struct BitcastOp;
527
528#[derive(Error, Debug)]
529pub enum IntToPtrOpErr {
530    #[error("Operand must be a signless integer")]
531    OperandTypeErr,
532    #[error("Result must be a pointer type")]
533    ResultTypeErr,
534}
535
536/// Equivalent to LLVM's IntToPtr opcode.
537/// ### Operands
538/// | operand | description |
539/// |-----|-------|
540/// | `arg` | Signless integer |
541////
542/// ### Result(s):
543///
544/// | result | description |
545/// |-----|-------|
546/// | `res` | [PointerType] |
547///
548#[pliron_op(
549    name = "llvm.inttoptr",
550    format = "$0 ` to ` type($0)",
551    interfaces = [
552        OneResultInterface,
553        OneOpdInterface,
554        CastOpInterface,
555     ],
556      operands = (arg: IntegerType),
557    results = (_: PointerType),
558     verifier = "succ"
559)]
560pub struct IntToPtrOp;
561
562#[derive(Error, Debug)]
563pub enum PtrToIntOpErr {
564    #[error("Operand must be a pointer type")]
565    OperandTypeErr,
566    #[error("Result must be a signless integer type")]
567    ResultTypeErr,
568}
569
570/// Equivalent to LLVM's PtrToInt opcode.
571/// ### Operands
572/// | operand | description |
573/// |-----|-------|
574/// | `arg` | [PointerType] |
575///
576/// ### Result(s):
577/// | result | description |
578/// |-----|-------|
579/// | `res` | Signless integer |
580#[pliron_op(
581    name = "llvm.ptrtoint",
582    format = "$0 ` to ` type($0)",
583    interfaces = [
584        OneResultInterface,
585        OneOpdInterface,
586        CastOpInterface,
587    ],
588    operands = (arg: PointerType),
589    results = (_: IntegerType),
590    verifier = "succ"
591)]
592pub struct PtrToIntOp;
593
594/// Equivalent to LLVM's AddrSpaceCast opcode: casts a pointer to a pointer in a
595/// different address space.
596/// ### Operands
597/// | operand | description |
598/// |-----|-------|
599/// | `arg` | [PointerType] |
600///
601/// ### Result(s):
602/// | result | description |
603/// |-----|-------|
604/// | `res` | [PointerType] |
605#[pliron_op(
606    name = "llvm.addrspacecast",
607    format = "$0 ` to ` type($0)",
608    interfaces = [
609        OneResultInterface,
610        OneOpdInterface,
611        CastOpInterface,
612    ],
613    operands = (arg: PointerType),
614    results = (_: PointerType),
615    verifier = "succ"
616)]
617pub struct AddrSpaceCastOp;
618
619/// Equivalent to LLVM's Unconditional Branch.
620/// ### Operands
621/// | operand | description |
622/// |-----|-------|
623/// | `dest_opds` | Any number of operands with any LLVM type |
624///
625/// ### Successors:
626///
627/// | Successor | description |
628/// |-----|-------|
629/// | `dest` | Any successor |
630#[pliron_op(
631    name = "llvm.br",
632    format = "succ($0) `(` operands(CharSpace(`,`)) `)`",
633    interfaces = [
634        IsTerminatorInterface,
635        NResultsInterface<0>,
636        NSuccsInterface<1>,
637        OneSuccInterface
638    ],
639    verifier = "succ"
640)]
641pub struct BrOp;
642
643#[op_interface_impl]
644impl BranchOpInterface for BrOp {
645    fn successor_operands(&self, ctx: &Context, succ_idx: usize) -> Vec<Value> {
646        assert!(succ_idx == 0, "BrOp has exactly one successor");
647        self.get_operation().deref(ctx).operands().collect()
648    }
649
650    fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
651        assert!(succ_idx == 0, "BrOp has exactly one successor");
652        Operation::push_operand(self.get_operation(), ctx, operand)
653    }
654
655    fn remove_successor_operand(
656        &self,
657        ctx: &mut Context,
658        succ_idx: usize,
659        opd_idx: usize,
660    ) -> Value {
661        assert!(succ_idx == 0, "BrOp has exactly one successor");
662        Operation::remove_operand(self.get_operation(), ctx, opd_idx)
663    }
664}
665
666impl BrOp {
667    /// Create anew [BrOp].
668    pub fn new(ctx: &mut Context, dest: Ptr<BasicBlock>, dest_opds: Vec<Value>) -> Self {
669        BrOp {
670            op: Operation::new(
671                ctx,
672                Self::get_concrete_op_info(),
673                vec![],
674                dest_opds,
675                vec![dest],
676                0,
677            ),
678        }
679    }
680}
681
682/// Equivalent to LLVM's Conditional Branch.
683/// ### Operands
684/// | operand | description |
685/// |-----|-------|
686/// | `condition` | 1-bit signless integer |
687/// | `true_dest_opds` | Any number of operands with any LLVM type |
688/// | `false_dest_opds` | Any number of operands with any LLVM type |
689///
690/// ### Successors:
691///
692/// | Successor | description |
693/// |-----|-------|
694/// | `true_dest` | Any successor |
695/// | `false_dest` | Any successor |
696#[pliron_op(
697    name = "llvm.cond_br",
698    interfaces = [IsTerminatorInterface, NResultsInterface<0>, NSuccsInterface<2>],
699    operands = (condition, true_dest_opds, false_dest_opds),
700)]
701pub struct CondBrOp;
702impl CondBrOp {
703    /// Create a new [CondBrOp].
704    pub fn new(
705        ctx: &mut Context,
706        condition: Value,
707        true_dest: Ptr<BasicBlock>,
708        true_dest_opds: Vec<Value>,
709        false_dest: Ptr<BasicBlock>,
710        false_dest_opds: Vec<Value>,
711    ) -> Self {
712        let (operands, segment_sizes) =
713            Self::compute_segment_sizes(vec![vec![condition], true_dest_opds, false_dest_opds]);
714
715        let op = CondBrOp {
716            op: Operation::new(
717                ctx,
718                Self::get_concrete_op_info(),
719                vec![],
720                operands,
721                vec![true_dest, false_dest],
722                0,
723            ),
724        };
725
726        // Set the operand segment sizes attribute.
727        op.set_operand_segment_sizes(ctx, segment_sizes);
728        op
729    }
730}
731
732#[derive(Error, Debug)]
733enum CondBrOpVerifyErr {
734    #[error("Condition operand must be a 1-bit signless integer (i1) or vector of i1")]
735    IncorrectConditionType,
736}
737
738impl Verify for CondBrOp {
739    fn verify(&self, ctx: &Context) -> Result<()> {
740        use pliron::r#type::Typed;
741        // Ensure that the condition is a 1-bit signless integer
742        let condition_ty = self.get_operand_condition(ctx).get_type(ctx);
743        let condition_ty = condition_ty.deref(ctx);
744        let condition_int_ty = condition_ty.downcast_ref::<IntegerType>().ok_or_else(|| {
745            verify_error!(self.loc(ctx), CondBrOpVerifyErr::IncorrectConditionType)
746        })?;
747        if condition_int_ty.width() != 1 || condition_int_ty.signedness() != Signedness::Signless {
748            verify_err!(self.loc(ctx), CondBrOpVerifyErr::IncorrectConditionType)?
749        }
750        Ok(())
751    }
752}
753
754#[op_interface_impl]
755impl OperandSegmentInterface for CondBrOp {}
756
757impl Printable for CondBrOp {
758    fn fmt(
759        &self,
760        ctx: &Context,
761        _state: &pliron::printable::State,
762        f: &mut core::fmt::Formatter<'_>,
763    ) -> core::fmt::Result {
764        let op = self.get_operation().deref(ctx);
765        let condition = op.get_operand(0);
766        let true_dest_opds = self.successor_operands(ctx, 0);
767        let false_dest_opds = self.successor_operands(ctx, 1);
768        let res = write!(
769            f,
770            "{} if {} ^{}({}) else ^{}({})",
771            Self::get_opid_static(),
772            condition.disp(ctx),
773            op.get_successor(0).deref(ctx).unique_name(ctx),
774            iter_with_sep(
775                true_dest_opds.iter(),
776                pliron::printable::ListSeparator::CharSpace(',')
777            )
778            .disp(ctx),
779            op.get_successor(1).deref(ctx).unique_name(ctx),
780            iter_with_sep(
781                false_dest_opds.iter(),
782                pliron::printable::ListSeparator::CharSpace(',')
783            )
784            .disp(ctx),
785        );
786        res
787    }
788}
789
790impl Parsable for CondBrOp {
791    type Arg = Vec<(Identifier, Location)>;
792    type Parsed = OpObj;
793    fn parse<'a>(
794        state_stream: &mut StateStream<'a>,
795        results: Self::Arg,
796    ) -> ParseResult<'a, Self::Parsed> {
797        if !results.is_empty() {
798            input_err!(
799                state_stream.loc(),
800                op_interfaces::NResultsVerifyErr(0, results.len())
801            )?
802        }
803
804        // Parse the condition operand.
805        let r#if = irfmt::parsers::spaced::<StateStream, _>(combine::parser::char::string("if"));
806
807        let condition = ssa_opd_parser();
808
809        let true_operands = delimited_list_parser('(', ')', ',', ssa_opd_parser());
810
811        let r_else =
812            irfmt::parsers::spaced::<StateStream, _>(combine::parser::char::string("else"));
813
814        let false_operands = delimited_list_parser('(', ')', ',', ssa_opd_parser());
815
816        let final_parser = r#if
817            .with(spaced(condition))
818            .and(spaced(block_opd_parser()))
819            .and(true_operands)
820            .and(spaced(r_else).with(spaced(block_opd_parser()).and(false_operands)));
821
822        final_parser
823            .then(
824                move |(((condition, true_dest), true_dest_opds), (false_dest, false_dest_opds))| {
825                    let results = results.clone();
826                    combine::parser(move |parsable_state: &mut StateStream<'a>| {
827                        let ctx = &mut parsable_state.state.ctx;
828                        let op = CondBrOp::new(
829                            ctx,
830                            condition,
831                            true_dest,
832                            true_dest_opds.clone(),
833                            false_dest,
834                            false_dest_opds.clone(),
835                        );
836
837                        process_parsed_ssa_defs(parsable_state, &results, op.get_operation())?;
838                        Ok(OpObj::new(op)).into_parse_result()
839                    })
840                },
841            )
842            .parse_stream(state_stream)
843            .into()
844    }
845}
846
847#[op_interface_impl]
848impl BranchOpInterface for CondBrOp {
849    fn successor_operands(&self, ctx: &Context, succ_idx: usize) -> Vec<Value> {
850        assert!(
851            succ_idx == 0 || succ_idx == 1,
852            "CondBrOp has exactly two successors"
853        );
854
855        // Skip the first segment, which is the condition.
856        self.get_segment(ctx, succ_idx + 1)
857    }
858
859    fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
860        // The successor operands start at segment 1, since segment 0 is the condition operand.
861        self.push_to_segment(ctx, succ_idx + 1, operand)
862    }
863
864    fn remove_successor_operand(
865        &self,
866        ctx: &mut Context,
867        succ_idx: usize,
868        opd_idx: usize,
869    ) -> Value {
870        // The successor operands start at segment 1, since segment 0 is the condition operand.
871        self.remove_from_segment(ctx, succ_idx + 1, opd_idx)
872    }
873}
874
875/// Equivalent to LLVM's Switch opcode.
876///
877/// ### Operands
878/// | operand | description |
879/// |-----|-------|
880/// | `condition` | integer (width matches the case values) |
881/// | `default_dest_opds` | variadic of any type |
882/// | `case_dest_opds` | variadic of any type |
883///
884/// ### Successors:
885/// | Successor | description |
886/// |-----|-------|
887/// | `default_dest` | any successor |
888/// | `case_dests` | any successor(s) |
889#[pliron_op(
890    name = "llvm.switch",
891    interfaces = [IsTerminatorInterface, NResultsInterface<0>],
892    operands = (condition, default_dest_opds, case_dest_opds),
893    attributes = (switch_case_values: CaseValuesAttr)
894)]
895pub struct SwitchOp;
896
897/// One case of a switch statement.
898#[derive(Clone)]
899pub struct SwitchCase {
900    /// The value being matched against.
901    pub value: IntegerAttr,
902    /// The destination block to jump to if this case is taken.
903    pub dest: Ptr<BasicBlock>,
904    /// The operands to pass to the destination block.
905    pub dest_opds: Vec<Value>,
906}
907
908impl Printable for SwitchCase {
909    fn fmt(
910        &self,
911        ctx: &Context,
912        _state: &pliron::printable::State,
913        f: &mut core::fmt::Formatter<'_>,
914    ) -> core::fmt::Result {
915        write!(
916            f,
917            "{{ {}: ^{}({}) }}",
918            self.value.disp(ctx),
919            self.dest.deref(ctx).unique_name(ctx),
920            list_with_sep(
921                &self.dest_opds,
922                pliron::printable::ListSeparator::CharSpace(',')
923            )
924            .disp(ctx)
925        )
926    }
927}
928
929impl Parsable for SwitchCase {
930    type Arg = ();
931    type Parsed = Self;
932
933    fn parse<'a>(
934        state_stream: &mut StateStream<'a>,
935        _arg: Self::Arg,
936    ) -> ParseResult<'a, Self::Parsed> {
937        let mut parser = between(
938            token('{'),
939            token('}'),
940            (
941                spaced(IntegerAttr::parser(())),
942                spaced(token(':')),
943                spaced(block_opd_parser()),
944                delimited_list_parser('(', ')', ',', ssa_opd_parser()),
945                spaces(),
946            ),
947        );
948
949        let ((value, _colon, dest, dest_opds, _spaces), _) =
950            parser.parse_stream(state_stream).into_result()?;
951
952        Ok(SwitchCase {
953            value,
954            dest,
955            dest_opds,
956        })
957        .into_parse_result()
958    }
959}
960
961impl Printable for SwitchOp {
962    fn fmt(
963        &self,
964        ctx: &Context,
965        state: &pliron::printable::State,
966        f: &mut core::fmt::Formatter<'_>,
967    ) -> core::fmt::Result {
968        let op = self.get_operation().deref(ctx);
969        let condition = op.get_operand(0);
970
971        let default_successor = op
972            .successors()
973            .next()
974            .expect("SwitchOp must have at least one successor");
975        let num_total_successors = op.get_num_successors();
976
977        write!(
978            f,
979            "{} {}, ^{}({})",
980            Self::get_opid_static(),
981            condition.disp(ctx),
982            default_successor.unique_name(ctx).disp(ctx),
983            iter_with_sep(
984                self.successor_operands(ctx, 0).iter(),
985                pliron::printable::ListSeparator::CharSpace(',')
986            )
987            .disp(ctx),
988        )?;
989
990        if num_total_successors < 2 {
991            writeln!(f, "[]")?;
992            return Ok(());
993        }
994
995        let cases = self.cases(ctx);
996
997        write!(f, "{}[", indented_nl(state))?;
998        indented_block!(state, {
999            write!(f, "{}", indented_nl(state))?;
1000            list_with_sep(&cases, pliron::printable::ListSeparator::CharNewline(','))
1001                .fmt(ctx, state, f)?;
1002        });
1003        write!(f, "{}]", indented_nl(state))?;
1004
1005        Ok(())
1006    }
1007}
1008
1009impl Parsable for SwitchOp {
1010    type Arg = Vec<(Identifier, Location)>;
1011    type Parsed = OpObj;
1012
1013    fn parse<'a>(
1014        state_stream: &mut StateStream<'a>,
1015        arg: Self::Arg,
1016    ) -> ParseResult<'a, Self::Parsed> {
1017        if !arg.is_empty() {
1018            input_err!(
1019                state_stream.loc(),
1020                op_interfaces::NResultsVerifyErr(0, arg.len())
1021            )?
1022        }
1023
1024        // Parse the condition operand.
1025        let condition = ssa_opd_parser().skip(spaced(token(',')));
1026        let default_successor = block_opd_parser();
1027        let default_operands = delimited_list_parser('(', ')', ',', ssa_opd_parser());
1028        let cases = delimited_list_parser('[', ']', ',', SwitchCase::parser(()));
1029
1030        let final_parser = spaced(condition)
1031            .and(default_successor)
1032            .skip(spaces())
1033            .and(default_operands)
1034            .skip(spaces())
1035            .and(cases);
1036
1037        final_parser
1038            .then(
1039                move |(((condition, default_dest), default_dest_opds), cases)| {
1040                    let results = arg.clone();
1041                    combine::parser(move |parsable_state: &mut StateStream<'a>| {
1042                        let ctx = &mut parsable_state.state.ctx;
1043                        let op = SwitchOp::new(
1044                            ctx,
1045                            condition,
1046                            default_dest,
1047                            default_dest_opds.clone(),
1048                            cases.clone(),
1049                        );
1050
1051                        process_parsed_ssa_defs(parsable_state, &results, op.get_operation())?;
1052                        Ok(OpObj::new(op)).into_parse_result()
1053                    })
1054                },
1055            )
1056            .parse_stream(state_stream)
1057            .into()
1058    }
1059}
1060
1061impl SwitchOp {
1062    /// Create a new [SwitchOp].
1063    pub fn new(
1064        ctx: &mut Context,
1065        condition: Value,
1066        default_dest: Ptr<BasicBlock>,
1067        default_dest_opds: Vec<Value>,
1068        cases: Vec<SwitchCase>,
1069    ) -> Self {
1070        let case_values: Vec<IntegerAttr> = cases.iter().map(|case| case.value.clone()).collect();
1071
1072        let case_operands = cases
1073            .iter()
1074            .map(|case| case.dest_opds.clone())
1075            .collect::<Vec<_>>();
1076
1077        let mut operand_segments = vec![vec![condition], default_dest_opds];
1078        operand_segments.extend(case_operands);
1079        let (operands, segment_sizes) = Self::compute_segment_sizes(operand_segments);
1080
1081        let case_dests = cases.iter().map(|case| case.dest);
1082        let successors = vec![default_dest].into_iter().chain(case_dests).collect();
1083        let op = SwitchOp {
1084            op: Operation::new(
1085                ctx,
1086                Self::get_concrete_op_info(),
1087                vec![],
1088                operands,
1089                successors,
1090                0,
1091            ),
1092        };
1093
1094        // Set the operand segment sizes attribute.
1095        op.set_operand_segment_sizes(ctx, segment_sizes);
1096        // Set the case values
1097        op.set_attr_switch_case_values(ctx, CaseValuesAttr(case_values));
1098        op
1099    }
1100
1101    /// Get the cases of this switch operation.
1102    /// (The default case cannot be / isn't included here).
1103    pub fn cases(&self, ctx: &Context) -> Vec<SwitchCase> {
1104        let case_values = &*self
1105            .get_attr_switch_case_values(ctx)
1106            .expect("SwitchOp missing or incorrect case values attribute");
1107
1108        let op = self.get_operation().deref(ctx);
1109        // Skip the first one, which is the default successor.
1110        let successors = op.successors().skip(1);
1111
1112        successors
1113            .zip(case_values.0.iter())
1114            .enumerate()
1115            .map(|(i, (dest, value))| {
1116                // i+1 here because the first successor is the default destination.
1117                let dest_opds = self.successor_operands(ctx, i + 1);
1118                SwitchCase {
1119                    value: value.clone(),
1120                    dest,
1121                    dest_opds,
1122                }
1123            })
1124            .collect()
1125    }
1126
1127    /// Get the default destination of this switch operation.
1128    pub fn default_dest(&self, ctx: &Context) -> Ptr<BasicBlock> {
1129        self.get_operation().deref(ctx).get_successor(0)
1130    }
1131
1132    /// Get the operands to pass to the default destination.
1133    pub fn default_dest_operands(&self, ctx: &Context) -> Vec<Value> {
1134        self.successor_operands(ctx, 0)
1135    }
1136}
1137
1138#[op_interface_impl]
1139impl BranchOpInterface for SwitchOp {
1140    fn successor_operands(&self, ctx: &Context, succ_idx: usize) -> Vec<Value> {
1141        // Skip the first segment, which is the condition.
1142        self.get_segment(ctx, succ_idx + 1)
1143    }
1144
1145    fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
1146        // The successor operands start at segment 1, since segment 0 is the condition operand.
1147        self.push_to_segment(ctx, succ_idx + 1, operand)
1148    }
1149
1150    fn remove_successor_operand(
1151        &self,
1152        ctx: &mut Context,
1153        succ_idx: usize,
1154        opd_idx: usize,
1155    ) -> Value {
1156        // The successor operands start at segment 1, since segment 0 is the condition operand.
1157        self.remove_from_segment(ctx, succ_idx + 1, opd_idx)
1158    }
1159}
1160
1161#[op_interface_impl]
1162impl OperandSegmentInterface for SwitchOp {}
1163
1164#[derive(Error, Debug)]
1165pub enum SwitchOpVerifyErr {
1166    #[error("SwitchOp has no or incorrect case values attribute")]
1167    CaseValuesAttrErr,
1168    #[error("SwitchOp has no or incorrect default destination")]
1169    DefaultDestErr,
1170    #[error("SwitchOp has no condition operand or is not an integer")]
1171    ConditionErr,
1172}
1173
1174impl Verify for SwitchOp {
1175    fn verify(&self, ctx: &Context) -> Result<()> {
1176        let loc = self.loc(ctx);
1177
1178        let Some(case_values) = self.get_attr_switch_case_values(ctx) else {
1179            verify_err!(loc.clone(), SwitchOpVerifyErr::CaseValuesAttrErr)?
1180        };
1181
1182        let op = &*self.get_operation().deref(ctx);
1183
1184        if op.get_num_successors() < 1 {
1185            verify_err!(loc.clone(), SwitchOpVerifyErr::DefaultDestErr)?;
1186        }
1187
1188        if op.get_num_operands() < 1 {
1189            verify_err!(loc.clone(), SwitchOpVerifyErr::ConditionErr)?;
1190        }
1191
1192        let condition_ty = pliron::r#type::Typed::get_type(&op.get_operand(0), ctx);
1193        let condition_ty = TypedHandle::<IntegerType>::from_handle(condition_ty, ctx)?;
1194
1195        if let Some(case_value) = case_values.0.first() {
1196            // Ensure that the case value type matches the condition type.
1197            if case_value.get_type() != condition_ty {
1198                verify_err!(loc, SwitchOpVerifyErr::ConditionErr)?;
1199            }
1200        }
1201
1202        Ok(())
1203    }
1204}
1205
1206/// One destination of an [IndirectBrOp].
1207#[derive(Clone)]
1208pub struct IndirectBrDest {
1209    /// The destination block to jump to.
1210    pub dest: Ptr<BasicBlock>,
1211    /// The operands to pass to the destination block.
1212    pub dest_opds: Vec<Value>,
1213}
1214
1215impl Printable for IndirectBrDest {
1216    fn fmt(
1217        &self,
1218        ctx: &Context,
1219        _state: &pliron::printable::State,
1220        f: &mut core::fmt::Formatter<'_>,
1221    ) -> core::fmt::Result {
1222        write!(
1223            f,
1224            "^{}({})",
1225            self.dest.deref(ctx).unique_name(ctx),
1226            list_with_sep(
1227                &self.dest_opds,
1228                pliron::printable::ListSeparator::CharSpace(',')
1229            )
1230            .disp(ctx)
1231        )
1232    }
1233}
1234
1235impl Parsable for IndirectBrDest {
1236    type Arg = ();
1237    type Parsed = Self;
1238
1239    fn parse<'a>(
1240        state_stream: &mut StateStream<'a>,
1241        _arg: Self::Arg,
1242    ) -> ParseResult<'a, Self::Parsed> {
1243        let mut parser = (
1244            block_opd_parser(),
1245            delimited_list_parser('(', ')', ',', ssa_opd_parser()),
1246        );
1247
1248        let ((dest, dest_opds), _) = parser.parse_stream(state_stream).into_result()?;
1249
1250        Ok(IndirectBrDest { dest, dest_opds }).into_parse_result()
1251    }
1252}
1253
1254/// Equivalent to LLVM's IndirectBr opcode.
1255///
1256/// ### Operands
1257/// | operand | description |
1258/// |-----|-------|
1259/// | `address` | [PointerType] |
1260/// | `dest_opds` | variadic of any type, one segment per destination |
1261///
1262/// ### Successors:
1263/// | Successor | description |
1264/// |-----|-------|
1265/// | `dests` | one or more successor(s) |
1266#[pliron_op(
1267    name = "llvm.indirectbr",
1268    interfaces = [IsTerminatorInterface, NResultsInterface<0>],
1269    operands = (address: PointerType, dest_opds),
1270)]
1271pub struct IndirectBrOp;
1272
1273impl Printable for IndirectBrOp {
1274    fn fmt(
1275        &self,
1276        ctx: &Context,
1277        state: &pliron::printable::State,
1278        f: &mut core::fmt::Formatter<'_>,
1279    ) -> core::fmt::Result {
1280        let op = self.get_operation().deref(ctx);
1281        let address = op.get_operand(0);
1282        let dests = self.destinations(ctx);
1283
1284        write!(f, "{} {} [", Self::get_opid_static(), address.disp(ctx))?;
1285        indented_block!(state, {
1286            write!(f, "{}", indented_nl(state))?;
1287            list_with_sep(&dests, pliron::printable::ListSeparator::CharNewline(','))
1288                .fmt(ctx, state, f)?;
1289        });
1290        write!(f, "{}]", indented_nl(state))?;
1291
1292        Ok(())
1293    }
1294}
1295
1296impl Parsable for IndirectBrOp {
1297    type Arg = Vec<(Identifier, Location)>;
1298    type Parsed = OpObj;
1299
1300    fn parse<'a>(
1301        state_stream: &mut StateStream<'a>,
1302        arg: Self::Arg,
1303    ) -> ParseResult<'a, Self::Parsed> {
1304        if !arg.is_empty() {
1305            input_err!(
1306                state_stream.loc(),
1307                op_interfaces::NResultsVerifyErr(0, arg.len())
1308            )?
1309        }
1310
1311        let dests = delimited_list_parser('[', ']', ',', IndirectBrDest::parser(()));
1312
1313        let final_parser = spaced(ssa_opd_parser()).and(dests);
1314
1315        final_parser
1316            .then(move |(address, dests)| {
1317                let results = arg.clone();
1318                combine::parser(move |parsable_state: &mut StateStream<'a>| {
1319                    let ctx = &mut parsable_state.state.ctx;
1320                    let op = IndirectBrOp::new(
1321                        ctx,
1322                        address,
1323                        dests
1324                            .iter()
1325                            .map(|d| (d.dest, d.dest_opds.clone()))
1326                            .collect(),
1327                    );
1328
1329                    process_parsed_ssa_defs(parsable_state, &results, op.get_operation())?;
1330                    Ok(OpObj::new(op)).into_parse_result()
1331                })
1332            })
1333            .parse_stream(state_stream)
1334            .into()
1335    }
1336}
1337
1338impl IndirectBrOp {
1339    /// Create a new [IndirectBrOp].
1340    pub fn new(
1341        ctx: &mut Context,
1342        address: Value,
1343        dests: Vec<(Ptr<BasicBlock>, Vec<Value>)>,
1344    ) -> Self {
1345        let mut operand_segments = vec![vec![address]];
1346        operand_segments.extend(dests.iter().map(|(_, dest_opds)| dest_opds.clone()));
1347        let (operands, segment_sizes) = Self::compute_segment_sizes(operand_segments);
1348
1349        let successors = dests.iter().map(|(dest, _)| *dest).collect();
1350        let op = IndirectBrOp {
1351            op: Operation::new(
1352                ctx,
1353                Self::get_concrete_op_info(),
1354                vec![],
1355                operands,
1356                successors,
1357                0,
1358            ),
1359        };
1360
1361        // Set the operand segment sizes attribute.
1362        op.set_operand_segment_sizes(ctx, segment_sizes);
1363        op
1364    }
1365
1366    /// Get the destinations of this indirectbr operation, along with the operands
1367    /// passed to each.
1368    pub fn destinations(&self, ctx: &Context) -> Vec<IndirectBrDest> {
1369        let op = self.get_operation().deref(ctx);
1370        op.successors()
1371            .enumerate()
1372            .map(|(i, dest)| IndirectBrDest {
1373                dest,
1374                dest_opds: self.successor_operands(ctx, i),
1375            })
1376            .collect()
1377    }
1378}
1379
1380#[op_interface_impl]
1381impl BranchOpInterface for IndirectBrOp {
1382    fn successor_operands(&self, ctx: &Context, succ_idx: usize) -> Vec<Value> {
1383        // Skip the first segment, which is the address.
1384        self.get_segment(ctx, succ_idx + 1)
1385    }
1386
1387    fn add_successor_operand(&self, ctx: &mut Context, succ_idx: usize, operand: Value) -> usize {
1388        // The successor operands start at segment 1, since segment 0 is the address operand.
1389        self.push_to_segment(ctx, succ_idx + 1, operand)
1390    }
1391
1392    fn remove_successor_operand(
1393        &self,
1394        ctx: &mut Context,
1395        succ_idx: usize,
1396        opd_idx: usize,
1397    ) -> Value {
1398        // The successor operands start at segment 1, since segment 0 is the address operand.
1399        self.remove_from_segment(ctx, succ_idx + 1, opd_idx)
1400    }
1401}
1402
1403#[op_interface_impl]
1404impl OperandSegmentInterface for IndirectBrOp {}
1405
1406#[derive(Error, Debug)]
1407pub enum IndirectBrOpVerifyErr {
1408    #[error("IndirectBrOp must have at least one destination")]
1409    NoDestinations,
1410}
1411
1412impl Verify for IndirectBrOp {
1413    fn verify(&self, ctx: &Context) -> Result<()> {
1414        let loc = self.loc(ctx);
1415        let op = &*self.get_operation().deref(ctx);
1416
1417        if op.get_num_successors() < 1 {
1418            verify_err!(loc, IndirectBrOpVerifyErr::NoDestinations)?;
1419        }
1420
1421        Ok(())
1422    }
1423}
1424
1425/// A way to express whether a GEP index is a constant or an SSA value
1426#[derive(Clone)]
1427pub enum GepIndex {
1428    Constant(u32),
1429    Value(Value),
1430}
1431
1432impl Printable for GepIndex {
1433    fn fmt(
1434        &self,
1435        ctx: &Context,
1436        _state: &pliron::printable::State,
1437        f: &mut core::fmt::Formatter<'_>,
1438    ) -> core::fmt::Result {
1439        match self {
1440            GepIndex::Constant(c) => write!(f, "{c}"),
1441            GepIndex::Value(v) => write!(f, "{}", v.disp(ctx)),
1442        }
1443    }
1444}
1445
1446#[derive(Error, Debug)]
1447pub enum GetElementPtrOpErr {
1448    #[error("GetElementPtrOp has no or incorrect indices attribute")]
1449    IndicesAttrErr,
1450    #[error("The indices on this GEP are invalid for its source element type")]
1451    IndicesErr,
1452}
1453
1454/// Equivalent to LLVM's GetElementPtr.
1455/// ### Operands
1456/// | operand | description |
1457/// |-----|-------|
1458/// | `base` | LLVM pointer type |
1459/// | `dynamicIndices` | Any number of signless integers |
1460///
1461/// ### Result(s):
1462///
1463/// | result | description |
1464/// |-----|-------|
1465/// | `res` | LLVM pointer type |
1466#[pliron_op(
1467    name = "llvm.gep",
1468    format = "`<` attr($gep_src_elem_type, $TypeAttr) `>` ` (` operands(CharSpace(`,`)) `)` attr($gep_indices, $GepIndicesAttr) ` : ` type($0)",
1469    interfaces = [OneResultInterface],
1470    operands = (src_ptr, dynamic_indices),
1471    results = (_: PointerType),
1472    attributes = (gep_src_elem_type: TypeAttr, gep_indices: GepIndicesAttr)
1473)]
1474pub struct GetElementPtrOp;
1475
1476#[op_interface_impl]
1477impl PointerTypeResult for GetElementPtrOp {
1478    fn result_pointee_type(&self, ctx: &Context) -> TypeHandle {
1479        Self::indexed_type(ctx, self.src_elem_type(ctx), &self.indices(ctx))
1480            .expect("Invalid indices for GEP")
1481    }
1482}
1483
1484impl Verify for GetElementPtrOp {
1485    fn verify(&self, ctx: &Context) -> Result<()> {
1486        let loc = self.loc(ctx);
1487        // Ensure that we have the indices as an attribute.
1488        if self.get_attr_gep_indices(ctx).is_none() {
1489            verify_err!(loc, GetElementPtrOpErr::IndicesAttrErr)?
1490        }
1491
1492        if let Err(e @ Error { .. }) =
1493            Self::indexed_type(ctx, self.src_elem_type(ctx), &self.indices(ctx))
1494        {
1495            return Err(Error {
1496                kind: ErrorKind::VerificationFailed,
1497                // We reset the error origin to be from here
1498                backtrace: pliron::std_deps::backtrace::Backtrace::capture(),
1499                ..e
1500            });
1501        }
1502
1503        Ok(())
1504    }
1505}
1506
1507impl GetElementPtrOp {
1508    /// Create a new [GetElementPtrOp]
1509    pub fn new(
1510        ctx: &mut Context,
1511        base: Value,
1512        indices: Vec<GepIndex>,
1513        src_elem_type: TypeHandle,
1514    ) -> Self {
1515        use pliron::r#type::Typed;
1516
1517        // A GEP result inherits the address space of its base pointer.
1518        let addr_space = {
1519            let base_ty = base.get_type(ctx);
1520            base_ty
1521                .deref(ctx)
1522                .downcast_ref::<PointerType>()
1523                .map_or(0, PointerType::address_space)
1524        };
1525        let result_type = PointerType::get(ctx, addr_space).into();
1526        let mut attr: Vec<GepIndexAttr> = Vec::new();
1527        let mut opds: Vec<Value> = vec![base];
1528        for idx in indices {
1529            match idx {
1530                GepIndex::Constant(c) => {
1531                    attr.push(GepIndexAttr::Constant(c));
1532                }
1533                GepIndex::Value(v) => {
1534                    attr.push(GepIndexAttr::OperandIdx(opds.push_back(v)));
1535                }
1536            }
1537        }
1538        let op = Operation::new(
1539            ctx,
1540            Self::get_concrete_op_info(),
1541            vec![result_type],
1542            opds,
1543            vec![],
1544            0,
1545        );
1546        let src_elem_type = TypeAttr::new(src_elem_type);
1547        let op = GetElementPtrOp { op };
1548
1549        op.set_attr_gep_indices(ctx, GepIndicesAttr(attr));
1550        op.set_attr_gep_src_elem_type(ctx, src_elem_type);
1551        op
1552    }
1553
1554    /// Get the source pointer's element type.
1555    pub fn src_elem_type(&self, ctx: &Context) -> TypeHandle {
1556        self.get_attr_gep_src_elem_type(ctx)
1557            .expect("GetElementPtrOp missing or has incorrect src_elem_type attribute type")
1558            .get_type(ctx)
1559    }
1560
1561    /// Get the indices of this GEP.
1562    pub fn indices(&self, ctx: &Context) -> Vec<GepIndex> {
1563        let op = &*self.op.deref(ctx);
1564        self.get_attr_gep_indices(ctx)
1565            .unwrap()
1566            .0
1567            .iter()
1568            .map(|index| match index {
1569                GepIndexAttr::Constant(c) => GepIndex::Constant(*c),
1570                GepIndexAttr::OperandIdx(i) => GepIndex::Value(op.get_operand(*i)),
1571            })
1572            .collect()
1573    }
1574
1575    /// Returns the result element type of a GEP with the given source element type and indexes.
1576    /// See [getIndexedType](https://llvm.org/doxygen/classllvm_1_1GetElementPtrInst.html#a99d4bfe49182f8d80abb1960f2c12d46)
1577    pub fn indexed_type(
1578        ctx: &Context,
1579        src_elem_type: TypeHandle,
1580        indices: &[GepIndex],
1581    ) -> Result<TypeHandle> {
1582        fn indexed_type_inner(
1583            ctx: &Context,
1584            src_elem_type: TypeHandle,
1585            mut idx_itr: impl Iterator<Item = GepIndex>,
1586        ) -> Result<TypeHandle> {
1587            let Some(idx) = idx_itr.next() else {
1588                return Ok(src_elem_type);
1589            };
1590            let src_elem_type = &*src_elem_type.deref(ctx);
1591            if let Some(st) = src_elem_type.downcast_ref::<StructType>() {
1592                let GepIndex::Constant(i) = idx else {
1593                    return arg_err_noloc!(GetElementPtrOpErr::IndicesErr);
1594                };
1595                if st.is_opaque() || i as usize >= st.num_fields() {
1596                    return arg_err_noloc!(GetElementPtrOpErr::IndicesErr);
1597                }
1598                indexed_type_inner(ctx, st.field_type(i as usize), idx_itr)
1599            } else if let Some(at) = src_elem_type.downcast_ref::<ArrayType>() {
1600                indexed_type_inner(ctx, at.elem_type(), idx_itr)
1601            } else {
1602                arg_err_noloc!(GetElementPtrOpErr::IndicesErr)
1603            }
1604        }
1605        // The first index is for the base (source) pointer. Skip that.
1606        indexed_type_inner(ctx, src_elem_type, indices.iter().skip(1).cloned())
1607    }
1608}
1609
1610#[derive(Error, Debug)]
1611pub enum LoadOpVerifyErr {
1612    #[error("Load operand must be a pointer")]
1613    OperandTypeErr,
1614}
1615
1616/// Equivalent to LLVM's Load opcode.
1617/// ### Operands
1618/// | operand | description |
1619/// |-----|-------|
1620/// | `addr` | [PointerType] |
1621///
1622/// ### Result(s):
1623///
1624/// | result | description |
1625/// |-----|-------|
1626/// | `res` | sized LLVM type |
1627#[pliron_op(
1628    name = "llvm.load",
1629    format = "$0 ` ` opt_attr($llvm_alignment, $AlignmentAttr, label($align), delimiters(`[`, `]`)) ` : ` type($0)",
1630    interfaces = [
1631        OneResultInterface,
1632        OneOpdInterface,
1633        AlignableOpInterface,
1634    ],
1635    operands = (address: PointerType),
1636    verifier = "succ"
1637)]
1638pub struct LoadOp;
1639impl LoadOp {
1640    /// Create a new [LoadOp]
1641    pub fn new(ctx: &mut Context, ptr: Value, res_ty: TypeHandle) -> Self {
1642        LoadOp {
1643            op: Operation::new(
1644                ctx,
1645                Self::get_concrete_op_info(),
1646                vec![res_ty],
1647                vec![ptr],
1648                vec![],
1649                0,
1650            ),
1651        }
1652    }
1653}
1654
1655#[derive(Error, Debug)]
1656pub enum StoreOpVerifyErr {
1657    #[error("Store operand must have two operands")]
1658    NumOpdsErr,
1659    #[error("Store operand must have a pointer as its second argument")]
1660    AddrOpdTypeErr,
1661}
1662
1663/// Equivalent to LLVM's Store opcode.
1664/// ### Operands
1665/// | operand | description |
1666/// |-----|-------|
1667/// | `addr` | [PointerType] |
1668/// | `value` | Sized type |
1669#[pliron_op(
1670    name = "llvm.store",
1671    format = "`*` $1 ` <- ` $0 ` ` opt_attr($llvm_alignment, $AlignmentAttr, label($align), delimiters(`[`, `]`))",
1672    interfaces = [
1673        NResultsInterface<0>,
1674        AlignableOpInterface,
1675        NOpdsInterface<2>
1676    ],
1677    operands = (value, address: PointerType),
1678    verifier = "succ"
1679)]
1680pub struct StoreOp;
1681impl StoreOp {
1682    /// Create a new [StoreOp]
1683    pub fn new(ctx: &mut Context, value: Value, ptr: Value) -> Self {
1684        StoreOp {
1685            op: Operation::new(
1686                ctx,
1687                Self::get_concrete_op_info(),
1688                vec![],
1689                vec![value, ptr],
1690                vec![],
1691                0,
1692            ),
1693        }
1694    }
1695}
1696
1697/// Equivalent to LLVM's `atomicrmw`: atomically applies `kind` to the value at
1698/// a pointer and returns the old value.
1699///
1700/// ### Operands
1701/// | operand | description |
1702/// |-----|-------|
1703/// | `ptr` | [PointerType] |
1704/// | `val` | value to combine with the one in memory |
1705///
1706/// ### Result(s):
1707/// | result | description |
1708/// |-----|-------|
1709/// | `res` | the old value (same type as `val`) |
1710#[pliron_op(
1711    name = "llvm.atomicrmw",
1712    format = "attr($llvm_rmw_kind, $AtomicRmwKindAttr) ` ` $0 `, ` $1 ` ` opt_attr($llvm_rmw_syncscope, $StringAttr, label($syncscope)) attr($llvm_rmw_ordering, $AtomicOrderingAttr) ` : ` type($0)",
1713    interfaces = [
1714        OneResultInterface,
1715        NOpdsInterface<2>,
1716    ],
1717    operands = (ptr: PointerType, val),
1718    attributes = (
1719        llvm_rmw_kind: AtomicRmwKindAttr,
1720        llvm_rmw_ordering: AtomicOrderingAttr,
1721        llvm_rmw_syncscope: StringAttr
1722    ),
1723    verifier = "succ"
1724)]
1725pub struct AtomicRmwOp;
1726
1727impl AtomicRmwOp {
1728    /// Create a new [AtomicRmwOp]. `syncscope` is `None` for the system scope.
1729    pub fn new(
1730        ctx: &mut Context,
1731        ptr: Value,
1732        val: Value,
1733        kind: AtomicRmwKindAttr,
1734        ordering: AtomicOrderingAttr,
1735        syncscope: Option<String>,
1736    ) -> Self {
1737        use pliron::r#type::Typed;
1738        let res_ty = val.get_type(ctx);
1739        let op = Operation::new(
1740            ctx,
1741            Self::get_concrete_op_info(),
1742            vec![res_ty],
1743            vec![ptr, val],
1744            vec![],
1745            0,
1746        );
1747        let op = AtomicRmwOp { op };
1748        op.set_attr_llvm_rmw_kind(ctx, kind);
1749        op.set_attr_llvm_rmw_ordering(ctx, ordering);
1750        if let Some(scope) = syncscope {
1751            op.set_attr_llvm_rmw_syncscope(ctx, StringAttr::new(scope));
1752        }
1753        op
1754    }
1755}
1756
1757/// Equivalent to LLVM's `cmpxchg`: atomically compares the value at a pointer
1758/// with `cmp` and, if equal, stores `new`. Returns a `{ value, i1 }` pair of the
1759/// loaded value and whether the swap succeeded.
1760///
1761/// ### Operands
1762/// | operand | description |
1763/// |-----|-------|
1764/// | `ptr` | [PointerType] |
1765/// | `cmp` | expected value |
1766/// | `new` | value to store on success |
1767///
1768/// ### Result(s):
1769/// | result | description |
1770/// |-----|-------|
1771/// | `res` | `{ value, i1 }` (loaded value, success flag) |
1772#[pliron_op(
1773    name = "llvm.cmpxchg",
1774    format = "$0 `, ` $1 `, ` $2 ` ` opt_attr($llvm_cas_syncscope, $StringAttr, label($syncscope)) attr($llvm_cas_success_ordering, $AtomicOrderingAttr) ` ` attr($llvm_cas_failure_ordering, $AtomicOrderingAttr) ` : ` type($0)",
1775    interfaces = [
1776        OneResultInterface,
1777        NOpdsInterface<3>,
1778    ],
1779    operands = (ptr: PointerType, cmp, new_val),
1780    attributes = (
1781        llvm_cas_success_ordering: AtomicOrderingAttr,
1782        llvm_cas_failure_ordering: AtomicOrderingAttr,
1783        llvm_cas_syncscope: StringAttr
1784    ),
1785    verifier = "succ"
1786)]
1787pub struct AtomicCmpxchgOp;
1788
1789impl AtomicCmpxchgOp {
1790    /// Create a new [AtomicCmpxchgOp]. `syncscope` is `None` for the system scope.
1791    pub fn new(
1792        ctx: &mut Context,
1793        ptr: Value,
1794        cmp: Value,
1795        new_val: Value,
1796        success_ordering: AtomicOrderingAttr,
1797        failure_ordering: AtomicOrderingAttr,
1798        syncscope: Option<String>,
1799    ) -> Self {
1800        use pliron::r#type::Typed;
1801        let val_ty = cmp.get_type(ctx);
1802        let bool_ty = IntegerType::get(ctx, 1, Signedness::Signless);
1803        let res_ty = StructType::get_unnamed(ctx, vec![val_ty, bool_ty.into()]).into();
1804        let op = Operation::new(
1805            ctx,
1806            Self::get_concrete_op_info(),
1807            vec![res_ty],
1808            vec![ptr, cmp, new_val],
1809            vec![],
1810            0,
1811        );
1812        let op = AtomicCmpxchgOp { op };
1813        op.set_attr_llvm_cas_success_ordering(ctx, success_ordering);
1814        op.set_attr_llvm_cas_failure_ordering(ctx, failure_ordering);
1815        if let Some(scope) = syncscope {
1816            op.set_attr_llvm_cas_syncscope(ctx, StringAttr::new(scope));
1817        }
1818        op
1819    }
1820}
1821
1822/// Equivalent to LLVM's `fence`: orders memory accesses. Has no operands or
1823/// results.
1824#[pliron_op(
1825    name = "llvm.fence",
1826    format = "opt_attr($llvm_fence_syncscope, $StringAttr, label($syncscope)) attr($llvm_fence_ordering, $AtomicOrderingAttr)",
1827    interfaces = [NResultsInterface<0>, NOpdsInterface<0>],
1828    attributes = (llvm_fence_ordering: AtomicOrderingAttr, llvm_fence_syncscope: StringAttr),
1829    verifier = "succ"
1830)]
1831pub struct FenceOp;
1832
1833impl FenceOp {
1834    /// Create a new [FenceOp]. `syncscope` is `None` for the system scope.
1835    pub fn new(ctx: &mut Context, ordering: AtomicOrderingAttr, syncscope: Option<String>) -> Self {
1836        let op = Operation::new(ctx, Self::get_concrete_op_info(), vec![], vec![], vec![], 0);
1837        let op = FenceOp { op };
1838        op.set_attr_llvm_fence_ordering(ctx, ordering);
1839        if let Some(scope) = syncscope {
1840            op.set_attr_llvm_fence_syncscope(ctx, StringAttr::new(scope));
1841        }
1842        op
1843    }
1844}
1845
1846/// Equivalent to LLVM's atomic `load`.
1847///
1848/// ### Operands
1849/// | operand | description |
1850/// |-----|-------|
1851/// | `ptr` | [PointerType] |
1852///
1853/// ### Result(s):
1854/// | result | description |
1855/// |-----|-------|
1856/// | `res` | the loaded value |
1857#[pliron_op(
1858    name = "llvm.atomic_load",
1859    format = "$0 ` ` opt_attr($llvm_alignment, $AlignmentAttr, label($align), delimiters(`[`, `]`)) ` ` opt_attr($llvm_ld_syncscope, $StringAttr, label($syncscope)) attr($llvm_ld_ordering, $AtomicOrderingAttr) ` : ` type($0)",
1860    interfaces = [
1861        OneResultInterface,
1862        OneOpdInterface,
1863        AlignableOpInterface,
1864    ],
1865    operands = (ptr: PointerType),
1866    attributes = (llvm_ld_ordering: AtomicOrderingAttr, llvm_ld_syncscope: StringAttr),
1867    verifier = "succ"
1868)]
1869pub struct AtomicLoadOp;
1870
1871impl AtomicLoadOp {
1872    /// Create a new [AtomicLoadOp]. `syncscope` is `None` for the system scope.
1873    pub fn new(
1874        ctx: &mut Context,
1875        ptr: Value,
1876        res_ty: TypeHandle,
1877        ordering: AtomicOrderingAttr,
1878        syncscope: Option<String>,
1879    ) -> Self {
1880        let op = Operation::new(
1881            ctx,
1882            Self::get_concrete_op_info(),
1883            vec![res_ty],
1884            vec![ptr],
1885            vec![],
1886            0,
1887        );
1888        let op = AtomicLoadOp { op };
1889        op.set_attr_llvm_ld_ordering(ctx, ordering);
1890        if let Some(scope) = syncscope {
1891            op.set_attr_llvm_ld_syncscope(ctx, StringAttr::new(scope));
1892        }
1893        op
1894    }
1895}
1896
1897/// Equivalent to LLVM's atomic `store`.
1898///
1899/// ### Operands
1900/// | operand | description |
1901/// |-----|-------|
1902/// | `val` | value to store |
1903/// | `ptr` | [PointerType] |
1904#[pliron_op(
1905    name = "llvm.atomic_store",
1906    format = "`*` $1 ` <- ` $0 ` ` opt_attr($llvm_alignment, $AlignmentAttr, label($align), delimiters(`[`, `]`)) ` ` opt_attr($llvm_st_syncscope, $StringAttr, label($syncscope)) attr($llvm_st_ordering, $AtomicOrderingAttr)",
1907    interfaces = [
1908        NResultsInterface<0>,
1909        AlignableOpInterface,
1910        NOpdsInterface<2>
1911    ],
1912    operands = (value, ptr: PointerType),
1913    attributes = (llvm_st_ordering: AtomicOrderingAttr, llvm_st_syncscope: StringAttr),
1914    verifier = "succ"
1915)]
1916pub struct AtomicStoreOp;
1917
1918impl AtomicStoreOp {
1919    /// Create a new [AtomicStoreOp]. `syncscope` is `None` for the system scope.
1920    pub fn new(
1921        ctx: &mut Context,
1922        value: Value,
1923        ptr: Value,
1924        ordering: AtomicOrderingAttr,
1925        syncscope: Option<String>,
1926    ) -> Self {
1927        let op = Operation::new(
1928            ctx,
1929            Self::get_concrete_op_info(),
1930            vec![],
1931            vec![value, ptr],
1932            vec![],
1933            0,
1934        );
1935        let op = AtomicStoreOp { op };
1936        op.set_attr_llvm_st_ordering(ctx, ordering);
1937        if let Some(scope) = syncscope {
1938            op.set_attr_llvm_st_syncscope(ctx, StringAttr::new(scope));
1939        }
1940        op
1941    }
1942}
1943
1944/// Equivalent to LLVM's inline assembly call. The template and constraint
1945/// strings follow LLVM's inline-asm syntax; `convergent` marks asm that must
1946/// not be reordered across divergent control flow (e.g. warp-synchronous PTX).
1947///
1948/// ### Operands
1949/// | operand | description |
1950/// |-----|-------|
1951/// | `inputs` | input operands to the inline asm |
1952///
1953/// ### Result(s):
1954/// | result | description |
1955/// |-----|-------|
1956/// | `res` | the asm result (a void type when there is none) |
1957#[pliron_op(
1958    name = "llvm.inline_asm",
1959    format = "attr($inline_asm_template, $StringAttr) `, ` attr($inline_asm_constraints, $StringAttr) ` convergent = ` attr($inline_asm_convergent, $BoolAttr) ` (` operands(CharSpace(`,`)) `) : ` type($0)",
1960    interfaces = [OneResultInterface],
1961    attributes = (
1962        inline_asm_template: StringAttr,
1963        inline_asm_constraints: StringAttr,
1964        inline_asm_convergent: BoolAttr
1965    ),
1966    verifier = "succ"
1967)]
1968pub struct InlineAsmOp;
1969
1970impl InlineAsmOp {
1971    /// Create a new [InlineAsmOp]. Use a void result type for asm with no
1972    /// result value.
1973    pub fn new(
1974        ctx: &mut Context,
1975        result_ty: TypeHandle,
1976        inputs: Vec<Value>,
1977        asm_template: &str,
1978        constraints: &str,
1979        convergent: bool,
1980    ) -> Self {
1981        let op = Operation::new(
1982            ctx,
1983            Self::get_concrete_op_info(),
1984            vec![result_ty],
1985            inputs,
1986            vec![],
1987            0,
1988        );
1989        let op = InlineAsmOp { op };
1990        op.set_attr_inline_asm_template(ctx, StringAttr::new(asm_template.to_string()));
1991        op.set_attr_inline_asm_constraints(ctx, StringAttr::new(constraints.to_string()));
1992        op.set_attr_inline_asm_convergent(ctx, BoolAttr::new(convergent));
1993        op
1994    }
1995}
1996
1997/// Equivalent to LLVM's Store opcode.
1998///
1999/// ### Operands
2000/// | operand | description |
2001/// |-----|-------|
2002/// | `callee_operands` | Optional function pointer followed by any number of parameters |
2003///
2004/// ### Result(s):
2005///
2006/// | result | description |
2007/// |-----|-------|
2008/// | `res` | LLVM type |
2009#[pliron_op(
2010    name = "llvm.call",
2011    interfaces = [OneResultInterface],
2012    attributes = (llvm_call_callee: IdentifierAttr, llvm_call_fastmath_flags: FastmathFlagsAttr)
2013)]
2014pub struct CallOp;
2015
2016impl CallOp {
2017    /// Get a new [CallOp].
2018    pub fn new(
2019        ctx: &mut Context,
2020        callee: CallOpCallable,
2021        callee_ty: TypedHandle<FuncType>,
2022        mut args: Vec<Value>,
2023    ) -> Self {
2024        let res_ty = callee_ty.deref(ctx).result_type();
2025        let op = match callee {
2026            CallOpCallable::Direct(cval) => {
2027                let op = Operation::new(
2028                    ctx,
2029                    Self::get_concrete_op_info(),
2030                    vec![res_ty],
2031                    args,
2032                    vec![],
2033                    0,
2034                );
2035                let op = CallOp { op };
2036                op.set_attr_llvm_call_callee(ctx, IdentifierAttr::new(cval));
2037                op
2038            }
2039            CallOpCallable::Indirect(csym) => {
2040                args.insert(0, csym);
2041                let op = Operation::new(
2042                    ctx,
2043                    Self::get_concrete_op_info(),
2044                    vec![res_ty],
2045                    args,
2046                    vec![],
2047                    0,
2048                );
2049                CallOp { op }
2050            }
2051        };
2052        op.set_callee_type(ctx, callee_ty.into());
2053        op
2054    }
2055}
2056
2057#[derive(Error, Debug)]
2058pub enum SymbolUserOpVerifyErr {
2059    #[error("Symbol {0} not found")]
2060    SymbolNotFound(String),
2061    #[error("Function {0} should have been llvm.func type")]
2062    NotLlvmFunc(String),
2063    #[error("AddressOf Op can only refer to a function or a global variable")]
2064    AddressOfInvalidReference,
2065    #[error("Function call has incorrect type: {0}")]
2066    FuncTypeErr(String),
2067}
2068
2069#[op_interface_impl]
2070impl SymbolUserOpInterface for CallOp {
2071    fn verify_symbol_uses(
2072        &self,
2073        ctx: &Context,
2074        symbol_tables: &mut SymbolTableCollection,
2075    ) -> Result<()> {
2076        match self.callee(ctx) {
2077            CallOpCallable::Direct(callee_sym) => {
2078                let Some(callee) = symbol_tables.lookup_symbol_in_nearest_table(
2079                    ctx,
2080                    self.get_operation(),
2081                    &callee_sym,
2082                ) else {
2083                    return verify_err!(
2084                        self.loc(ctx),
2085                        SymbolUserOpVerifyErr::SymbolNotFound(callee_sym.to_string())
2086                    );
2087                };
2088                let Some(func_op) = (&*callee as &dyn Op).downcast_ref::<FuncOp>() else {
2089                    return verify_err!(
2090                        self.loc(ctx),
2091                        SymbolUserOpVerifyErr::NotLlvmFunc(callee_sym.to_string())
2092                    );
2093                };
2094                let func_op_ty = func_op.get_type(ctx);
2095
2096                if func_op_ty.to_handle() != self.callee_type(ctx) {
2097                    return verify_err!(
2098                        self.loc(ctx),
2099                        SymbolUserOpVerifyErr::FuncTypeErr(format!(
2100                            "expected {}, got {}",
2101                            func_op_ty.disp(ctx),
2102                            self.callee_type(ctx).disp(ctx)
2103                        ))
2104                    );
2105                }
2106            }
2107            CallOpCallable::Indirect(pointer) => {
2108                use pliron::r#type::Typed;
2109                if !pointer.get_type(ctx).deref(ctx).is::<PointerType>() {
2110                    return verify_err!(
2111                        self.loc(ctx),
2112                        SymbolUserOpVerifyErr::FuncTypeErr("Callee must be a pointer".to_string())
2113                    );
2114                }
2115            }
2116        }
2117        Ok(())
2118    }
2119
2120    fn used_symbols(&self, ctx: &Context) -> Vec<Identifier> {
2121        match self.callee(ctx) {
2122            CallOpCallable::Direct(identifier) => vec![identifier],
2123            CallOpCallable::Indirect(_) => vec![],
2124        }
2125    }
2126}
2127
2128#[op_interface_impl]
2129impl CallOpInterface for CallOp {
2130    fn callee(&self, ctx: &Context) -> CallOpCallable {
2131        let op = self.op.deref(ctx);
2132        if let Some(callee_sym) = self.get_attr_llvm_call_callee(ctx) {
2133            CallOpCallable::Direct(callee_sym.clone().into())
2134        } else {
2135            assert!(
2136                op.get_num_operands() > 0,
2137                "Indirect call must have function pointer operand"
2138            );
2139            CallOpCallable::Indirect(op.get_operand(0))
2140        }
2141    }
2142
2143    fn args(&self, ctx: &Context) -> Vec<Value> {
2144        let op = self.op.deref(ctx);
2145        // If this is an indirect call, the first operand is the callee value.
2146        let skip = if matches!(self.callee(ctx), CallOpCallable::Direct(_)) {
2147            0
2148        } else {
2149            1
2150        };
2151        op.operands().skip(skip).collect()
2152    }
2153}
2154
2155impl Printable for CallOp {
2156    fn fmt(
2157        &self,
2158        ctx: &Context,
2159        _state: &pliron::printable::State,
2160        f: &mut core::fmt::Formatter<'_>,
2161    ) -> core::fmt::Result {
2162        let callee = self.callee(ctx);
2163        write!(
2164            f,
2165            "{} = {} ",
2166            self.get_result(ctx).disp(ctx),
2167            self.get_opid()
2168        )?;
2169        match callee {
2170            CallOpCallable::Direct(callee_sym) => {
2171                write!(f, "@{callee_sym}")?;
2172            }
2173            CallOpCallable::Indirect(callee_val) => {
2174                write!(f, "{}", callee_val.disp(ctx))?;
2175            }
2176        }
2177
2178        if let Some(fmf) = self.get_attr_llvm_call_fastmath_flags(ctx)
2179            && *fmf != FastmathFlagsAttr::default()
2180        {
2181            write!(f, " {}", fmf.disp(ctx))?;
2182        }
2183
2184        let args = self.args(ctx);
2185        let ty = self.callee_type(ctx);
2186        write!(
2187            f,
2188            " ({}) : {}",
2189            list_with_sep(&args, pliron::printable::ListSeparator::CharSpace(',')).disp(ctx),
2190            ty.disp(ctx)
2191        )?;
2192        Ok(())
2193    }
2194}
2195
2196impl Parsable for CallOp {
2197    type Arg = Vec<(Identifier, Location)>;
2198    type Parsed = OpObj;
2199
2200    fn parse<'a>(
2201        state_stream: &mut StateStream<'a>,
2202        results: Self::Arg,
2203    ) -> ParseResult<'a, Self::Parsed> {
2204        let direct_callee = combine::token('@')
2205            .with(Identifier::parser(()))
2206            .map(CallOpCallable::Direct);
2207        let indirect_callee = ssa_opd_parser().map(CallOpCallable::Indirect);
2208        let callee_parser = direct_callee.or(indirect_callee);
2209        let fastmath_flags_parser = optional(FastmathFlagsAttr::parser(()));
2210        let args_parser = delimited_list_parser('(', ')', ',', ssa_opd_parser());
2211        let ty_parser = spaced(combine::token(':')).with(TypedHandle::<FuncType>::parser(()));
2212
2213        let mut final_parser = spaced(callee_parser)
2214            .and(spaced(fastmath_flags_parser))
2215            .and(spaced(args_parser))
2216            .and(ty_parser)
2217            .then(move |(((callee, fastmath_flags), args), ty)| {
2218                let results = results.clone();
2219                combine::parser(move |parsable_state: &mut StateStream<'a>| {
2220                    let ctx = &mut parsable_state.state.ctx;
2221                    let op = CallOp::new(ctx, callee.clone(), ty, args.clone());
2222                    if let Some(fmf) = &fastmath_flags {
2223                        op.set_attr_llvm_call_fastmath_flags(ctx, *fmf);
2224                    }
2225                    process_parsed_ssa_defs(parsable_state, &results, op.get_operation())?;
2226                    Ok(OpObj::new(op)).into_parse_result()
2227                })
2228            });
2229
2230        final_parser.parse_stream(state_stream).into_result()
2231    }
2232}
2233
2234impl Verify for CallOp {
2235    fn verify(&self, ctx: &Context) -> Result<()> {
2236        // Check that the argument and result types match the callee type.
2237        let callee_ty = &*self.callee_type(ctx).deref(ctx);
2238        let Some(callee_ty) = callee_ty.downcast_ref::<FuncType>() else {
2239            return verify_err!(
2240                self.loc(ctx),
2241                SymbolUserOpVerifyErr::FuncTypeErr("Callee is not a function".to_string())
2242            );
2243        };
2244        // Check the function type against the arguments.
2245        let args = self.args(ctx);
2246        let expected_args = callee_ty.arg_types();
2247        if !callee_ty.is_var_arg() && args.len() != expected_args.len() {
2248            return verify_err!(
2249                self.loc(ctx),
2250                SymbolUserOpVerifyErr::FuncTypeErr("argument count mismatch.".to_string())
2251            );
2252        }
2253        use pliron::r#type::Typed;
2254        for (arg_idx, (arg, expected_arg)) in args.iter().zip(expected_args.iter()).enumerate() {
2255            if arg.get_type(ctx) != *expected_arg {
2256                return verify_err!(
2257                    self.loc(ctx),
2258                    SymbolUserOpVerifyErr::FuncTypeErr(format!(
2259                        "argument {} type mismatch: expected {}, got {}",
2260                        arg_idx,
2261                        expected_arg.disp(ctx),
2262                        arg.get_type(ctx).disp(ctx)
2263                    ))
2264                );
2265            }
2266        }
2267
2268        if callee_ty.result_type() != self.result_type(ctx) {
2269            return verify_err!(
2270                self.loc(ctx),
2271                SymbolUserOpVerifyErr::FuncTypeErr(format!(
2272                    "result type mismatch: expected {}, got {}",
2273                    callee_ty.result_type().disp(ctx),
2274                    self.result_type(ctx).disp(ctx)
2275                ))
2276            );
2277        }
2278
2279        Ok(())
2280    }
2281}
2282
2283/// Constant value operation for the LLVM dialect.
2284/// Similar to MLIR's [llvm.mlir.constant](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirconstant-llvmconstantop).
2285///
2286/// This operation represents a constant value that must be an integer or float attribute.
2287/// Unlike `builtin.constant` which supports any attribute type, `llvm.constant` enforces
2288/// that the value must be either an integer or float.
2289///
2290/// ### Results:
2291///
2292/// | result | description |
2293/// |-----|-------|
2294/// | `result` | integer or float type |
2295#[pliron_op(
2296    name = "llvm.constant",
2297    format = "`<` $llvm_constant_value `>` ` : ` type($0)",
2298    interfaces = [NOpdsInterface<0>, OneResultInterface],
2299    attributes = (llvm_constant_value),
2300)]
2301pub struct ConstantOp;
2302
2303impl ConstantOp {
2304    /// Get the constant value that this Op defines.
2305    pub fn get_value(&self, ctx: &Context) -> AttrObj {
2306        self.get_attr_llvm_constant_value(ctx).unwrap().clone()
2307    }
2308
2309    /// Create a new [ConstantOp].
2310    pub fn new(ctx: &mut Context, value: AttrObj) -> Self {
2311        let result_type = attr_cast::<dyn TypedAttrInterface>(&*value)
2312            .expect("ConstantOp const value must provide TypedAttrInterface")
2313            .get_type(ctx);
2314        let op = Operation::new(
2315            ctx,
2316            Self::get_concrete_op_info(),
2317            vec![result_type],
2318            vec![],
2319            vec![],
2320            0,
2321        );
2322        let op = ConstantOp { op };
2323        op.set_attr_llvm_constant_value(ctx, value);
2324        op
2325    }
2326}
2327
2328#[derive(Error, Debug)]
2329#[error("{}: Unexpected type", ConstantOp::get_opid_static())]
2330pub enum ConstantOpVerifyErr {
2331    #[error("ConstantOp must have either an integer or a float value")]
2332    InvalidValue,
2333}
2334
2335impl Verify for ConstantOp {
2336    fn verify(&self, ctx: &Context) -> Result<()> {
2337        let loc = self.loc(ctx);
2338        let value = self.get_value(ctx);
2339        if !(value.is::<IntegerAttr>() || attr_impls::<dyn FloatAttr>(&*value)) {
2340            verify_err!(loc, ConstantOpVerifyErr::InvalidValue)?;
2341        }
2342        Ok(())
2343    }
2344}
2345
2346/// Undefined value of a type.
2347/// See MLIR's [llvm.mlir.undef](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirundef-llvmundefop).
2348///
2349/// ### Results:
2350/// | result | description |
2351/// |-----|-------|
2352/// | `result` | any type |
2353#[pliron_op(
2354    name = "llvm.undef",
2355    format = "`: ` type($0)",
2356    interfaces = [OneResultInterface, NOpdsInterface<0>],
2357    verifier = "succ"
2358)]
2359pub struct UndefOp;
2360
2361impl UndefOp {
2362    /// Create a new [UndefOp].
2363    pub fn new(ctx: &mut Context, result_ty: TypeHandle) -> Self {
2364        let op = Operation::new(
2365            ctx,
2366            Self::get_concrete_op_info(),
2367            vec![result_ty],
2368            vec![],
2369            vec![],
2370            0,
2371        );
2372        UndefOp { op }
2373    }
2374}
2375
2376/// Poison value of a type.
2377/// See MLIR's [llvm.mlir.poison](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirpoison-llvmpoisonop).
2378///
2379/// ### Results:
2380/// | result | description |
2381/// |-----|-------|
2382/// | `result` | any type |
2383#[pliron_op(
2384    name = "llvm.poison",
2385    format = "`: ` type($0)",
2386    interfaces = [OneResultInterface],
2387    verifier = "succ"
2388)]
2389pub struct PoisonOp;
2390
2391impl PoisonOp {
2392    /// Create a new [PoisonOp].
2393    pub fn new(ctx: &mut Context, result_ty: TypeHandle) -> Self {
2394        let op = Operation::new(
2395            ctx,
2396            Self::get_concrete_op_info(),
2397            vec![result_ty],
2398            vec![],
2399            vec![],
2400            0,
2401        );
2402        PoisonOp { op }
2403    }
2404}
2405
2406/// Freeze value of a type.
2407/// See MLIR's [llvm.mlir.freeze](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmfreeze-llvmfreezeop).
2408///
2409/// ### Results:
2410/// | result | description |
2411/// |-----|-------|
2412/// | `result` | any type |
2413///
2414/// ### Operands:
2415/// | operand | description |
2416/// |-----|-------|
2417/// | `value` | any type |
2418#[pliron_op(
2419    name = "llvm.freeze",
2420    format = "$0 ` : ` type($0)",
2421    interfaces = [OneOpdInterface, OneResultInterface],
2422    verifier = "succ"
2423)]
2424pub struct FreezeOp;
2425
2426impl FreezeOp {
2427    /// Create a new [FreezeOp].
2428    pub fn new(ctx: &mut Context, value: Value) -> Self {
2429        use pliron::r#type::Typed;
2430        let result_ty = value.get_type(ctx);
2431        let op = Operation::new(
2432            ctx,
2433            Self::get_concrete_op_info(),
2434            vec![result_ty],
2435            vec![value],
2436            vec![],
2437            0,
2438        );
2439        FreezeOp { op }
2440    }
2441}
2442
2443/// Same as MLIR's LLVM dialect [ZeroOp](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirzero-llvmzeroop)
2444/// It creates a zero-initialized value of the specified LLVM IR dialect type.
2445/// Results:
2446///
2447/// | result | description |
2448/// |-----|-------|
2449/// | `result` | any type |
2450#[pliron_op(
2451    name = "llvm.zero",
2452    format = "`: ` type($0)",
2453    interfaces = [NOpdsInterface<0>, OneResultInterface],
2454    verifier = "succ"
2455)]
2456pub struct ZeroOp;
2457
2458impl ZeroOp {
2459    /// Create a new [ZeroOp].
2460    pub fn new(ctx: &mut Context, result_ty: TypeHandle) -> Self {
2461        let op = Operation::new(
2462            ctx,
2463            Self::get_concrete_op_info(),
2464            vec![result_ty],
2465            vec![],
2466            vec![],
2467            0,
2468        );
2469        ZeroOp { op }
2470    }
2471}
2472
2473#[derive(Error, Debug)]
2474pub enum GlobalOpVerifyErr {
2475    #[error("GlobalOp must have a type")]
2476    MissingType,
2477}
2478
2479/// Same as MLIR's LLVM dialect [GlobalOp](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmlirglobal-llvmglobalop)
2480/// It creates a global variable of the specified LLVM IR dialect type.
2481/// An initializer can be specified either as an attribute or in the
2482/// operation's initializer region, ending with a return.
2483#[pliron_op(
2484    name = "llvm.global",
2485    interfaces = [
2486        IsolatedFromAboveInterface,
2487        NOpdsInterface<0>,
2488        NResultsInterface<0>,
2489        SymbolOpInterface,
2490        SingleBlockRegionInterface,
2491        LlvmSymbolName,
2492        AlignableOpInterface
2493    ],
2494    attributes = (
2495        llvm_global_type: TypeAttr,
2496        global_initializer,
2497        llvm_global_linkage: LinkageAttr,
2498        llvm_global_addrspace: AddressSpaceAttr
2499    )
2500)]
2501pub struct GlobalOp;
2502
2503impl GlobalOp {
2504    /// Create a new [GlobalOp]. An initializer region can be added later if needed.
2505    pub fn new(ctx: &mut Context, name: Identifier, ty: TypeHandle) -> Self {
2506        let op = Operation::new(ctx, Self::get_concrete_op_info(), vec![], vec![], vec![], 0);
2507        let op = GlobalOp { op };
2508        op.set_symbol_name(ctx, name);
2509        op.set_attr_llvm_global_type(ctx, TypeAttr::new(ty));
2510        op
2511    }
2512
2513    /// Get the address space of this global (0 if unset).
2514    pub fn address_space(&self, ctx: &Context) -> u32 {
2515        self.get_attr_llvm_global_addrspace(ctx)
2516            .map_or(0, |attr| attr.0)
2517    }
2518
2519    /// Set the address space of this global.
2520    pub fn set_address_space(&self, ctx: &mut Context, addr_space: u32) {
2521        self.set_attr_llvm_global_addrspace(ctx, AddressSpaceAttr(addr_space));
2522    }
2523}
2524
2525impl pliron::r#type::Typed for GlobalOp {
2526    fn get_type(&self, ctx: &Context) -> TypeHandle {
2527        pliron::r#type::Typed::get_type(
2528            &*self
2529                .get_attr_llvm_global_type(ctx)
2530                .expect("GlobalOp missing or has incorrect type attribute"),
2531            ctx,
2532        )
2533    }
2534}
2535
2536impl GlobalOp {
2537    /// Get the initializer value of this global variable.
2538    pub fn get_initializer_value(&self, ctx: &Context) -> Option<AttrObj> {
2539        self.get_attr_global_initializer(ctx).map(|v| v.clone())
2540    }
2541
2542    /// Get the initializer region's block of this global variable.
2543    /// This is a block that ends with a return operation.
2544    /// The return operation must have the same type as the global variable.
2545    pub fn get_initializer_block(&self, ctx: &Context) -> Option<Ptr<BasicBlock>> {
2546        (self.op.deref(ctx).num_regions() > 0).then(|| self.get_body(ctx, 0))
2547    }
2548
2549    /// Get the initializer region of this global variable.
2550    pub fn get_initializer_region(&self, ctx: &Context) -> Option<Ptr<Region>> {
2551        (self.op.deref(ctx).num_regions() > 0)
2552            .then(|| self.get_operation().deref(ctx).get_region(0))
2553    }
2554
2555    /// Set a simple initializer value for this global variable.
2556    pub fn set_initializer_value(&self, ctx: &Context, value: AttrObj) {
2557        self.set_attr_global_initializer(ctx, value);
2558    }
2559
2560    /// Add an initializer region (with an entry block) for this global variable.
2561    /// There shouldn't already be one.
2562    pub fn add_initializer_region(&self, ctx: &mut Context) -> Ptr<Region> {
2563        assert!(
2564            self.get_initializer_value(ctx).is_none(),
2565            "Attempt to create an initializer region when there already is an initializer value"
2566        );
2567        let region = Operation::add_region(self.get_operation(), ctx);
2568        let entry = BasicBlock::new(ctx, Some("entry".try_into().unwrap()), vec![]);
2569        entry.insert_at_front(region, ctx);
2570
2571        region
2572    }
2573}
2574
2575impl IsDeclaration for GlobalOp {
2576    fn is_declaration(&self, ctx: &Context) -> bool {
2577        self.get_initializer_value(ctx).is_none() && self.get_initializer_region(ctx).is_none()
2578    }
2579}
2580
2581impl Verify for GlobalOp {
2582    fn verify(&self, ctx: &Context) -> Result<()> {
2583        let loc = self.loc(ctx);
2584
2585        // The name must be set. That is checked by the SymbolOpInterface.
2586        // So we check that other attributes are set. Start with type.
2587        if self.get_attr_llvm_global_type(ctx).is_none() {
2588            return verify_err!(loc, GlobalOpVerifyErr::MissingType);
2589        }
2590
2591        // Check that there is at most one initializer
2592        if self.get_initializer_value(ctx).is_some() && self.get_initializer_region(ctx).is_some() {
2593            return verify_err!(loc, GlobalOpVerifyErr::MissingType);
2594        }
2595
2596        Ok(())
2597    }
2598}
2599
2600impl Printable for GlobalOp {
2601    fn fmt(
2602        &self,
2603        ctx: &Context,
2604        state: &pliron::printable::State,
2605        f: &mut core::fmt::Formatter<'_>,
2606    ) -> core::fmt::Result {
2607        write!(
2608            f,
2609            "{} @{} : {}",
2610            self.get_opid(),
2611            self.get_symbol_name(ctx),
2612            <Self as pliron::r#type::Typed>::get_type(self, ctx).disp(ctx)
2613        )?;
2614
2615        // Print attributes except for type, initializer and symbol name.
2616        let mut attributes_to_print_separately =
2617            self.op.deref(ctx).attributes.clone_skip_outlined();
2618        attributes_to_print_separately.0.retain(|key, _| {
2619            key != &*ATTR_KEY_LLVM_GLOBAL_TYPE
2620                && key != &*ATTR_KEY_SYM_NAME
2621                && key != &*ATTR_KEY_GLOBAL_INITIALIZER
2622        });
2623        indented_block!(state, {
2624            write!(
2625                f,
2626                "{}{}",
2627                indented_nl(state),
2628                attributes_to_print_separately.disp(ctx)
2629            )?;
2630        });
2631
2632        if let Some(init_value) = self.get_initializer_value(ctx) {
2633            write!(f, " = {}", init_value.disp(ctx))?;
2634        }
2635
2636        if let Some(init_region) = self.get_initializer_region(ctx) {
2637            write!(f, " = {}", init_region.print(ctx, state))?;
2638        }
2639
2640        Ok(())
2641    }
2642}
2643
2644impl Parsable for GlobalOp {
2645    type Arg = Vec<(Identifier, Location)>;
2646    type Parsed = OpObj;
2647    fn parse<'a>(
2648        state_stream: &mut StateStream<'a>,
2649        results: Self::Arg,
2650    ) -> ParseResult<'a, Self::Parsed> {
2651        let loc = state_stream.loc();
2652        if !results.is_empty() {
2653            input_err!(loc, "GlobalOp must cannot have results")?;
2654        }
2655        let name_parser = combine::token('@').with(Identifier::parser(()));
2656        let type_parser = type_parser();
2657        let attr_dict_parser = AttributeDict::parser(());
2658
2659        let mut parser = name_parser
2660            .skip(spaced(combine::token(':')))
2661            .and(type_parser)
2662            .and(spaced(attr_dict_parser));
2663
2664        let (((name, ty), attr_dict), _) = parser.parse_stream(state_stream).into_result()?;
2665        let op = GlobalOp::new(state_stream.state.ctx, name, ty);
2666        op.get_operation()
2667            .deref_mut(state_stream.state.ctx)
2668            .attributes
2669            .0
2670            .extend(attr_dict.0);
2671
2672        enum Initializer {
2673            Value(AttrObj),
2674            Region(Ptr<Region>),
2675        }
2676        // Parse optional initializer value or region.
2677        let initializer_parser = combine::token('=').skip(spaces()).with(
2678            attr_parser()
2679                .map(Initializer::Value)
2680                .or(Region::parser(op.get_operation()).map(Initializer::Region)),
2681        );
2682
2683        let initializer = spaces()
2684            .with(combine::optional(initializer_parser))
2685            .parse_stream(state_stream)
2686            .into_result()?;
2687
2688        if let Some(initializer) = initializer.0 {
2689            match initializer {
2690                Initializer::Value(v) => op.set_initializer_value(state_stream.state.ctx, v),
2691                Initializer::Region(_r) => {
2692                    // Nothing to do since the region is already added to the operation during parsing.
2693                }
2694            }
2695        }
2696
2697        Ok(OpObj::new(op)).into_parse_result()
2698    }
2699}
2700
2701/// Same as MLIR's LLVM dialect [AddressOfOp](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmmliraddressof-llvmaddressofop).
2702/// Creates an SSA value containing a pointer to a global value (function, variable etc).
2703///
2704/// ### Results:
2705///
2706/// | result | description |
2707/// |-----|-------|
2708/// | `result` | LLVM pointer type |
2709///
2710#[pliron_op(
2711    name = "llvm.addressof",
2712    format = "`@` attr($global_name, $IdentifierAttr) ` : ` type($0)",
2713    interfaces = [OneResultInterface, NOpdsInterface<0>],
2714    results = (_: PointerType),
2715    attributes = (global_name: IdentifierAttr),
2716)]
2717pub struct AddressOfOp;
2718
2719#[derive(Error, Debug)]
2720enum AddressOfOpVerifyErr {
2721    #[error("AddressOfOp is missing its `global_name` attribute")]
2722    MissingGlobalName,
2723}
2724
2725impl Verify for AddressOfOp {
2726    fn verify(&self, ctx: &Context) -> Result<()> {
2727        if self.get_attr_global_name(ctx).is_none() {
2728            verify_err!(self.loc(ctx), AddressOfOpVerifyErr::MissingGlobalName)?
2729        }
2730        Ok(())
2731    }
2732}
2733
2734impl AddressOfOp {
2735    /// Create a new [AddressOfOp].
2736    pub fn new(ctx: &mut Context, global_name: Identifier, address_space: u32) -> Self {
2737        let result_type = PointerType::get(ctx, address_space).into();
2738        let op = Operation::new(
2739            ctx,
2740            Self::get_concrete_op_info(),
2741            vec![result_type],
2742            vec![],
2743            vec![],
2744            0,
2745        );
2746        let op = AddressOfOp { op };
2747        op.set_attr_global_name(ctx, IdentifierAttr::new(global_name));
2748        op
2749    }
2750
2751    /// Get the global name that this refers to.
2752    pub fn get_global_name(&self, ctx: &Context) -> Identifier {
2753        self.get_attr_global_name(ctx)
2754            .expect("AddressOfOp missing or has incorrect global_name attribute type")
2755            .clone()
2756            .into()
2757    }
2758
2759    /// If this operation referes to a global, get it.
2760    pub fn get_global(
2761        &self,
2762        ctx: &Context,
2763        symbol_tables: &mut SymbolTableCollection,
2764    ) -> Option<GlobalOp> {
2765        let global_name = self.get_global_name(ctx);
2766        symbol_tables
2767            .lookup_symbol_in_nearest_table(ctx, self.get_operation(), &global_name)
2768            .and_then(|sym_op| {
2769                (sym_op as Box<dyn Op>)
2770                    .downcast::<GlobalOp>()
2771                    .map(|op| *op)
2772                    .ok()
2773            })
2774    }
2775
2776    /// If this operation refers to a function, get it.
2777    pub fn get_function(
2778        &self,
2779        ctx: &Context,
2780        symbol_tables: &mut SymbolTableCollection,
2781    ) -> Option<FuncOp> {
2782        let global_name = self.get_global_name(ctx);
2783        symbol_tables
2784            .lookup_symbol_in_nearest_table(ctx, self.get_operation(), &global_name)
2785            .and_then(|sym_op| {
2786                (sym_op as Box<dyn Op>)
2787                    .downcast::<FuncOp>()
2788                    .map(|op| *op)
2789                    .ok()
2790            })
2791    }
2792}
2793
2794#[op_interface_impl]
2795impl SymbolUserOpInterface for AddressOfOp {
2796    fn used_symbols(&self, ctx: &Context) -> Vec<Identifier> {
2797        vec![self.get_global_name(ctx)]
2798    }
2799
2800    fn verify_symbol_uses(
2801        &self,
2802        ctx: &Context,
2803        symbol_tables: &mut SymbolTableCollection,
2804    ) -> Result<()> {
2805        let loc = self.loc(ctx);
2806        let global_name = self.get_global_name(ctx);
2807        let Some(symbol) =
2808            symbol_tables.lookup_symbol_in_nearest_table(ctx, self.get_operation(), &global_name)
2809        else {
2810            return verify_err!(
2811                loc,
2812                SymbolUserOpVerifyErr::SymbolNotFound(global_name.to_string())
2813            );
2814        };
2815
2816        // Symbol can only be a FuncOp or a GlobalOp
2817        let is_global = (&*symbol as &dyn Op).is::<GlobalOp>();
2818        let is_func = (&*symbol as &dyn Op).is::<FuncOp>();
2819        if !is_global && !is_func {
2820            return verify_err!(loc, SymbolUserOpVerifyErr::AddressOfInvalidReference);
2821        }
2822
2823        Ok(())
2824    }
2825}
2826
2827/// Similar to MLIR's LLVM dialect `llvm.blocktag`.
2828/// Marks a basic block with a tag so `llvm.blockaddress` can refer to it.
2829/// A given function should have at most one llvm.blocktag operation with a given tag.
2830#[pliron_op(
2831    name = "llvm.blocktag",
2832    format = "`<id = ` attr($llvm_block_tag_id, $IntegerAttr) `>`",
2833    interfaces = [NResultsInterface<0>, NOpdsInterface<0>],
2834    attributes = (llvm_block_tag_id: IntegerAttr),
2835)]
2836pub struct BlockTagOp;
2837
2838#[derive(Error, Debug)]
2839enum BlockAddressTagVerifyErr {
2840    #[error("Block address tag attribute missing")]
2841    MissingTagAttribute,
2842    #[error("Block address function name attribute missing")]
2843    MissingFunctionNameAttribute,
2844    #[error("Block address tag = {0} not found in function {1}")]
2845    BlockAddressTagNotFound(u64, String),
2846}
2847
2848impl Verify for BlockTagOp {
2849    fn verify(&self, ctx: &Context) -> Result<()> {
2850        if self.get_attr_llvm_block_tag_id(ctx).is_none() {
2851            return verify_err!(self.loc(ctx), BlockAddressTagVerifyErr::MissingTagAttribute);
2852        }
2853        Ok(())
2854    }
2855}
2856
2857impl BlockTagOp {
2858    pub fn new(ctx: &mut Context, tag: u64) -> Self {
2859        let op = Operation::new(ctx, Self::get_concrete_op_info(), vec![], vec![], vec![], 0);
2860        let op = Self { op };
2861        let tag_ty = IntegerType::get(ctx, 64, Signedness::Signless);
2862        op.set_attr_llvm_block_tag_id(
2863            ctx,
2864            IntegerAttr::new(tag_ty, APInt::from_u64(tag, NonZero::new(64).unwrap())),
2865        );
2866        op
2867    }
2868
2869    pub fn get_tag_id(&self, ctx: &Context) -> u64 {
2870        self.get_attr_llvm_block_tag_id(ctx)
2871            .expect("BlockTagOp missing or has incorrect tag attribute type")
2872            .value()
2873            .to_u64()
2874    }
2875}
2876
2877/// Similar to MLIR's LLVM dialect `llvm.blockaddress`.
2878/// Creates an SSA value containing the address of a tagged block in a function.
2879#[pliron_op(
2880    name = "llvm.blockaddress",
2881    format = "`<function = @` attr($llvm_block_address_function, $IdentifierAttr) `, tag = ` attr($llvm_block_address_tag, $IntegerAttr) `> : ` type($0)",
2882    interfaces = [OneResultInterface, NOpdsInterface<0>],
2883    results = (_: PointerType),
2884    attributes = (llvm_block_address_function: IdentifierAttr, llvm_block_address_tag: IntegerAttr),
2885)]
2886pub struct BlockAddressOp;
2887
2888impl Verify for BlockAddressOp {
2889    fn verify(&self, ctx: &Context) -> Result<()> {
2890        if self.get_attr_llvm_block_address_function(ctx).is_none() {
2891            return verify_err!(
2892                self.loc(ctx),
2893                BlockAddressTagVerifyErr::MissingFunctionNameAttribute
2894            );
2895        }
2896        if self.get_attr_llvm_block_address_tag(ctx).is_none() {
2897            return verify_err!(self.loc(ctx), BlockAddressTagVerifyErr::MissingTagAttribute);
2898        }
2899        Ok(())
2900    }
2901}
2902
2903impl BlockAddressOp {
2904    /// Create a new [BlockAddressOp] referring to a specific block (function_name, tag).
2905    pub fn new(ctx: &mut Context, function_name: Identifier, tag: u64, address_space: u32) -> Self {
2906        let result_type = PointerType::get(ctx, address_space).into();
2907        let op = Operation::new(
2908            ctx,
2909            Self::get_concrete_op_info(),
2910            vec![result_type],
2911            vec![],
2912            vec![],
2913            0,
2914        );
2915        let op = Self { op };
2916        let tag_ty = IntegerType::get(ctx, 64, Signedness::Signless);
2917        op.set_attr_llvm_block_address_function(ctx, IdentifierAttr::new(function_name));
2918        op.set_attr_llvm_block_address_tag(
2919            ctx,
2920            IntegerAttr::new(tag_ty, APInt::from_u64(tag, NonZero::new(64).unwrap())),
2921        );
2922        op
2923    }
2924
2925    /// Get the function name of the block that this address refers to.
2926    pub fn get_function_name(&self, ctx: &Context) -> Identifier {
2927        self.get_attr_llvm_block_address_function(ctx)
2928            .expect("BlockAddressOp missing or has incorrect function_name attribute type")
2929            .clone()
2930            .into()
2931    }
2932
2933    /// Get the tag of the block that this address refers to.
2934    pub fn get_tag_id(&self, ctx: &Context) -> u64 {
2935        self.get_attr_llvm_block_address_tag(ctx)
2936            .expect("BlockAddressOp missing or has incorrect tag attribute type")
2937            .value()
2938            .to_u64()
2939    }
2940
2941    /// Get the [BlockTagOp] that this refers to.
2942    /// For a well-formed program, this should always return `Some(BlockTagOp)`.
2943    pub fn get_block_tag_op(
2944        &self,
2945        ctx: &Context,
2946        symbol_tables: &mut SymbolTableCollection,
2947    ) -> Option<BlockTagOp> {
2948        let function_name = self.get_function_name(ctx);
2949        let tag_id = self.get_tag_id(ctx);
2950        let symbol = symbol_tables.lookup_symbol_in_nearest_table(
2951            ctx,
2952            self.get_operation(),
2953            &function_name,
2954        )?;
2955
2956        let func_op = symbol.as_any().downcast_ref::<FuncOp>()?;
2957
2958        // Search the function's blocks for a `BlockTagOp` with the same tag.
2959        walkers::interruptible::immutable::walk_op(
2960            ctx,
2961            &mut tag_id.clone(),
2962            &WALKCONFIG_PREORDER_FORWARD,
2963            func_op.get_operation(),
2964            |ctx, tag_id, irnode| {
2965                let IRNode::Operation(op) = irnode else {
2966                    return walkers::interruptible::walk_advance();
2967                };
2968                if let Some(block_tag_op) = Operation::get_op::<BlockTagOp>(op, ctx)
2969                    && block_tag_op.get_tag_id(ctx) == *tag_id
2970                {
2971                    return walkers::interruptible::walk_break(block_tag_op);
2972                }
2973                walkers::interruptible::walk_advance()
2974            },
2975        )
2976        .break_value()
2977    }
2978}
2979
2980#[op_interface_impl]
2981impl SymbolUserOpInterface for BlockAddressOp {
2982    fn used_symbols(&self, ctx: &Context) -> Vec<Identifier> {
2983        vec![self.get_function_name(ctx)]
2984    }
2985
2986    fn verify_symbol_uses(
2987        &self,
2988        ctx: &Context,
2989        symbol_tables: &mut SymbolTableCollection,
2990    ) -> Result<()> {
2991        let loc = self.loc(ctx);
2992        let function_name = self.get_function_name(ctx);
2993        let tag = self.get_tag_id(ctx);
2994        if self.get_block_tag_op(ctx, symbol_tables).is_none() {
2995            return verify_err!(
2996                loc,
2997                BlockAddressTagVerifyErr::BlockAddressTagNotFound(tag, function_name.to_string())
2998            );
2999        }
3000
3001        Ok(())
3002    }
3003}
3004
3005#[derive(Error, Debug)]
3006enum IntCastVerifyErr {
3007    #[error("Result must be an integer")]
3008    ResultTypeErr,
3009    #[error("Operand must be an integer")]
3010    OperandTypeErr,
3011    #[error("Result type must be larger than operand type")]
3012    ResultTypeSmallerThanOperand,
3013    #[error("Result type must be smaller than operand type")]
3014    ResultTypeLargerThanOperand,
3015    #[error("Result type must be equal to operand type")]
3016    ResultTypeEqualToOperand,
3017}
3018
3019/// Ensure that the integer cast operation is valid.
3020/// This checks that the result type is an integer and that the operand type is also an integer.
3021/// It also checks that the result type is larger or smaller than the operand type (`cmp` operand).
3022fn integer_cast_verify(op: &Operation, ctx: &Context, cmp: ICmpPredicateAttr) -> Result<()> {
3023    use pliron::r#type::Typed;
3024
3025    let loc = op.loc();
3026    let mut res_ty = op.get_type(0).deref(ctx);
3027    let mut opd_ty = op.get_operand(0).get_type(ctx).deref(ctx);
3028
3029    if let Some(vec_res_ty) = res_ty.downcast_ref::<VectorType>() {
3030        res_ty = vec_res_ty.elem_type().deref(ctx);
3031    }
3032    if let Some(vec_opd_ty) = opd_ty.downcast_ref::<VectorType>() {
3033        opd_ty = vec_opd_ty.elem_type().deref(ctx);
3034    }
3035
3036    let Some(res_ty) = res_ty.downcast_ref::<IntegerType>() else {
3037        return verify_err!(loc, IntCastVerifyErr::ResultTypeErr);
3038    };
3039    let Some(opd_ty) = opd_ty.downcast_ref::<IntegerType>() else {
3040        return verify_err!(loc, IntCastVerifyErr::OperandTypeErr);
3041    };
3042
3043    match cmp {
3044        ICmpPredicateAttr::SLT | ICmpPredicateAttr::ULT => {
3045            if res_ty.width() >= opd_ty.width() {
3046                return verify_err!(loc, IntCastVerifyErr::ResultTypeLargerThanOperand);
3047            }
3048        }
3049        ICmpPredicateAttr::SGT | ICmpPredicateAttr::UGT => {
3050            if res_ty.width() <= opd_ty.width() {
3051                return verify_err!(loc, IntCastVerifyErr::ResultTypeSmallerThanOperand);
3052            }
3053        }
3054        ICmpPredicateAttr::SLE | ICmpPredicateAttr::ULE => {
3055            if res_ty.width() > opd_ty.width() {
3056                return verify_err!(loc, IntCastVerifyErr::ResultTypeLargerThanOperand);
3057            }
3058        }
3059        ICmpPredicateAttr::SGE | ICmpPredicateAttr::UGE => {
3060            if res_ty.width() < opd_ty.width() {
3061                return verify_err!(loc, IntCastVerifyErr::ResultTypeSmallerThanOperand);
3062            }
3063        }
3064        ICmpPredicateAttr::EQ | ICmpPredicateAttr::NE => {
3065            if res_ty.width() != opd_ty.width() {
3066                return verify_err!(loc, IntCastVerifyErr::ResultTypeEqualToOperand);
3067            }
3068        }
3069    }
3070    Ok(())
3071}
3072
3073/// Equivalent to LLVM's sext opcode.
3074/// ### Operands
3075/// | operand | description |
3076/// |-----|-------|
3077/// | `arg` | Signless integer |
3078/// ### Result(s):
3079/// | result | description |
3080/// |-----|-------|
3081/// | `res` | Signless integer |
3082#[pliron_op(
3083    name = "llvm.sext",
3084    format = "$0 ` to ` type($0)",
3085    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface]
3086)]
3087pub struct SExtOp;
3088impl Verify for SExtOp {
3089    fn verify(&self, ctx: &Context) -> Result<()> {
3090        integer_cast_verify(
3091            &self.get_operation().deref(ctx),
3092            ctx,
3093            ICmpPredicateAttr::SGT,
3094        )
3095    }
3096}
3097
3098/// Equivalent to LLVM's zext opcode.
3099/// ### Operands
3100/// | operand | description |
3101/// |-----|-------|
3102/// | `arg` | Signless integer |
3103/// ### Result(s):
3104/// | result | description |
3105/// |-----|-------|
3106/// | `res` | Signless integer |
3107#[pliron_op(
3108    name = "llvm.zext",
3109    format = "`<nneg=` attr($llvm_nneg_flag, `pliron::builtin::attributes::BoolAttr`) `> ` $0 ` to ` type($0)",
3110    interfaces = [
3111        CastOpInterface,
3112        OneResultInterface,
3113        OneOpdInterface,
3114        NNegFlag,
3115        CastOpWithNNegInterface,
3116    ]
3117)]
3118pub struct ZExtOp;
3119
3120impl Verify for ZExtOp {
3121    fn verify(&self, ctx: &Context) -> Result<()> {
3122        integer_cast_verify(
3123            &self.get_operation().deref(ctx),
3124            ctx,
3125            ICmpPredicateAttr::UGT,
3126        )
3127    }
3128}
3129
3130/// Equivalent to LLVM's FPExt opcode.
3131///
3132/// ### Operands
3133/// | operand | description |
3134/// |-----|-------|
3135/// | `arg` | Floating-point number |
3136///
3137/// ### Result(s):
3138/// | result | description |
3139/// |-----|-------|
3140/// | `res` | Floating-point number |
3141#[pliron_op(
3142    name = "llvm.fpext",
3143    format = "attr($llvm_fast_math_flags, $FastmathFlagsAttr) ` ` $0 ` to ` type($0)",
3144    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface, FastMathFlags]
3145)]
3146pub struct FPExtOp;
3147
3148impl Verify for FPExtOp {
3149    fn verify(&self, ctx: &Context) -> Result<()> {
3150        // Check operand type to be a float
3151        let opd_ty = OneOpdInterface::operand_type(self, ctx).deref(ctx);
3152        let Some(opd_float_ty) = type_cast::<dyn FloatTypeInterface>(&*opd_ty) else {
3153            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3154        };
3155        let res_ty = OneResultInterface::result_type(self, ctx).deref(ctx);
3156        let Some(res_float_ty) = type_cast::<dyn FloatTypeInterface>(&*res_ty) else {
3157            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3158        };
3159
3160        let opd_size = opd_float_ty.get_semantics().bits;
3161        let res_size = res_float_ty.get_semantics().bits;
3162        if res_size <= opd_size {
3163            return verify_err!(
3164                self.loc(ctx),
3165                FloatCastVerifyErr::ResultTypeSmallerThanOperand
3166            );
3167        }
3168        Ok(())
3169    }
3170}
3171
3172#[derive(Error, Debug)]
3173pub enum FloatCastVerifyErr {
3174    #[error("Incorrect operand type")]
3175    OperandTypeErr,
3176    #[error("Incorrect result type")]
3177    ResultTypeErr,
3178    #[error("Operand and result must both be scalars or vectors with matching shape")]
3179    MismatchedVectorShape,
3180    #[error("Result type must be bigger than the operand type")]
3181    ResultTypeSmallerThanOperand,
3182    #[error("Operand type must be bigger than the result type")]
3183    OperandTypeSmallerThanResult,
3184}
3185
3186/// Equivalent to LLVM's trunc opcode.
3187/// ### Operands
3188/// | operand | description |
3189/// |-----|-------|
3190/// | `arg` | Signless integer |
3191/// ### Result(s):
3192/// | result | description |
3193/// |-----|-------|
3194/// | `res` | Signless integer |
3195#[pliron_op(
3196    name = "llvm.trunc",
3197    format = "$0 ` to ` type($0)",
3198    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface]
3199)]
3200pub struct TruncOp;
3201
3202impl Verify for TruncOp {
3203    fn verify(&self, ctx: &Context) -> Result<()> {
3204        integer_cast_verify(
3205            &self.get_operation().deref(ctx),
3206            ctx,
3207            ICmpPredicateAttr::ULT,
3208        )
3209    }
3210}
3211
3212/// Equivalent to LLVM's FPTrunc opcode.
3213/// ### Operands
3214/// | operand | description |
3215/// |-----|-------|
3216/// | `arg` | Floating-point number |
3217/// ### Result(s):
3218/// | result | description |
3219/// |-----|-------|
3220/// | `res` | Floating-point number |
3221#[pliron_op(
3222    name = "llvm.fptrunc",
3223    format = "attr($llvm_fast_math_flags, $FastmathFlagsAttr) ` ` $0 ` to ` type($0)",
3224    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface, FastMathFlags]
3225)]
3226pub struct FPTruncOp;
3227
3228impl Verify for FPTruncOp {
3229    fn verify(&self, ctx: &Context) -> Result<()> {
3230        // Check operand type to be a float
3231        let opd_ty = OneOpdInterface::operand_type(self, ctx).deref(ctx);
3232        let Some(opd_float_ty) = type_cast::<dyn FloatTypeInterface>(&*opd_ty) else {
3233            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3234        };
3235        let res_ty = OneResultInterface::result_type(self, ctx).deref(ctx);
3236        let Some(res_float_ty) = type_cast::<dyn FloatTypeInterface>(&*res_ty) else {
3237            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3238        };
3239
3240        let opd_size = opd_float_ty.get_semantics().bits;
3241        let res_size = res_float_ty.get_semantics().bits;
3242        if opd_size <= res_size {
3243            return verify_err!(
3244                self.loc(ctx),
3245                FloatCastVerifyErr::OperandTypeSmallerThanResult
3246            );
3247        }
3248        Ok(())
3249    }
3250}
3251
3252fn cast_element_types(
3253    opd_ty: TypeHandle,
3254    res_ty: TypeHandle,
3255    ctx: &Context,
3256    loc: Location,
3257) -> Result<(TypeHandle, TypeHandle)> {
3258    let mut opd_elem_ty = opd_ty;
3259    let mut res_elem_ty = res_ty;
3260    let mut opd_vec_shape = None;
3261    let mut res_vec_shape = None;
3262
3263    if let Some(vec_ty) = opd_ty.deref(ctx).downcast_ref::<VectorType>() {
3264        opd_elem_ty = vec_ty.elem_type();
3265        opd_vec_shape = Some((vec_ty.num_elements(), vec_ty.kind()));
3266    }
3267    if let Some(vec_ty) = res_ty.deref(ctx).downcast_ref::<VectorType>() {
3268        res_elem_ty = vec_ty.elem_type();
3269        res_vec_shape = Some((vec_ty.num_elements(), vec_ty.kind()));
3270    }
3271
3272    if opd_vec_shape != res_vec_shape {
3273        return verify_err!(loc, FloatCastVerifyErr::MismatchedVectorShape);
3274    }
3275
3276    Ok((opd_elem_ty, res_elem_ty))
3277}
3278
3279/// Equivalent to LLVM's FPToSI opcode.
3280///
3281/// ### Operands
3282/// | operand | description |
3283/// |-----|-------|
3284/// | `arg` | Floating-point number |
3285///
3286/// ### Result(s):
3287/// | result | description |
3288/// |-----|-------|
3289/// | `res` | Signed integer |
3290#[pliron_op(
3291    name = "llvm.fptosi",
3292    format = "$0 ` to ` type($0)",
3293    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface]
3294)]
3295pub struct FPToSIOp;
3296
3297impl Verify for FPToSIOp {
3298    fn verify(&self, ctx: &Context) -> Result<()> {
3299        // Check that the operand is a float and the result is an integer
3300        let (opd_ty, res_ty) = cast_element_types(
3301            OneOpdInterface::operand_type(self, ctx),
3302            OneResultInterface::result_type(self, ctx),
3303            ctx,
3304            self.loc(ctx),
3305        )?;
3306        let opd_ty = opd_ty.deref(ctx);
3307        if !type_impls::<dyn FloatTypeInterface>(&*opd_ty) {
3308            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3309        };
3310        let res_ty = res_ty.deref(ctx);
3311        let Some(res_int_ty) = res_ty.downcast_ref::<IntegerType>() else {
3312            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3313        };
3314        if !res_int_ty.is_signless() {
3315            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3316        }
3317        Ok(())
3318    }
3319}
3320
3321/// Equivalent to LLVM's FPToUI opcode.
3322///
3323/// ### Operands
3324/// | operand | description |
3325/// |-----|-------|
3326/// | `arg` | Floating-point number |
3327///
3328/// ### Result(s):
3329/// | result | description |
3330/// |-----|-------|
3331/// | `res` | Unsigned integer |
3332#[pliron_op(
3333    name = "llvm.fptoui",
3334    format = "$0 ` to ` type($0)",
3335    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface]
3336)]
3337pub struct FPToUIOp;
3338
3339impl Verify for FPToUIOp {
3340    fn verify(&self, ctx: &Context) -> Result<()> {
3341        // Check that the operand is a float and the result is an integer
3342        let (opd_ty, res_ty) = cast_element_types(
3343            OneOpdInterface::operand_type(self, ctx),
3344            OneResultInterface::result_type(self, ctx),
3345            ctx,
3346            self.loc(ctx),
3347        )?;
3348        let opd_ty = opd_ty.deref(ctx);
3349        if !type_impls::<dyn FloatTypeInterface>(&*opd_ty) {
3350            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3351        };
3352        let res_ty = res_ty.deref(ctx);
3353        let Some(res_int_ty) = res_ty.downcast_ref::<IntegerType>() else {
3354            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3355        };
3356        if !res_int_ty.is_signless() {
3357            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3358        }
3359        Ok(())
3360    }
3361}
3362
3363/// Equivalent to LLVM's SIToFP opcode.
3364///
3365/// ### Operands
3366/// | operand | description |
3367/// |-----|-------|
3368/// | `arg` | Signed integer |
3369///
3370/// ### Result(s):
3371/// | result | description |
3372/// |-----|-------|
3373/// | `res` | Floating-point number |
3374#[pliron_op(
3375    name = "llvm.sitofp",
3376    format = "$0 ` to ` type($0)",
3377    interfaces = [CastOpInterface, OneResultInterface, OneOpdInterface]
3378)]
3379pub struct SIToFPOp;
3380
3381impl Verify for SIToFPOp {
3382    fn verify(&self, ctx: &Context) -> Result<()> {
3383        // Check that the operand is an integer and the result is a float
3384        let (opd_ty, res_ty) = cast_element_types(
3385            OneOpdInterface::operand_type(self, ctx),
3386            OneResultInterface::result_type(self, ctx),
3387            ctx,
3388            self.loc(ctx),
3389        )?;
3390        let opd_ty = opd_ty.deref(ctx);
3391        let Some(opd_ty_int) = opd_ty.downcast_ref::<IntegerType>() else {
3392            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3393        };
3394        if !opd_ty_int.is_signless() {
3395            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3396        }
3397        let res_ty = res_ty.deref(ctx);
3398        if !type_impls::<dyn FloatTypeInterface>(&*res_ty) {
3399            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3400        }
3401        Ok(())
3402    }
3403}
3404
3405/// Equivalent to LLVM's UIToFP opcode.
3406///
3407/// ### Operands
3408/// | operand | description |
3409/// |-----|-------|
3410/// | `arg` | Unsigned integer |
3411///
3412/// ### Result(s):
3413/// | result | description |
3414/// |-----|-------|
3415/// | `res` | Floating-point number |
3416#[pliron_op(
3417    name = "llvm.uitofp",
3418    format = "`<nneg=` attr($llvm_nneg_flag, `pliron::builtin::attributes::BoolAttr`) `> `$0 ` to ` type($0)",
3419    interfaces = [
3420        CastOpInterface,
3421        OneResultInterface,
3422        OneOpdInterface,
3423        CastOpWithNNegInterface,
3424        NNegFlag,
3425    ]
3426)]
3427pub struct UIToFPOp;
3428
3429impl Verify for UIToFPOp {
3430    fn verify(&self, ctx: &Context) -> Result<()> {
3431        // Check that the operand is an integer and the result is a float
3432        let (opd_ty, res_ty) = cast_element_types(
3433            OneOpdInterface::operand_type(self, ctx),
3434            OneResultInterface::result_type(self, ctx),
3435            ctx,
3436            self.loc(ctx),
3437        )?;
3438        let opd_ty = opd_ty.deref(ctx);
3439        let Some(opd_ty_int) = opd_ty.downcast_ref::<IntegerType>() else {
3440            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3441        };
3442        if !opd_ty_int.is_signless() {
3443            return verify_err!(self.loc(ctx), FloatCastVerifyErr::OperandTypeErr);
3444        }
3445        let res_ty = res_ty.deref(ctx);
3446        if !type_impls::<dyn FloatTypeInterface>(&*res_ty) {
3447            return verify_err!(self.loc(ctx), FloatCastVerifyErr::ResultTypeErr);
3448        }
3449        Ok(())
3450    }
3451}
3452
3453/// Equivalent to LLVM's InsertValue opcode.
3454///
3455/// ### Operands
3456/// | operand | description |
3457/// |-----|-------|
3458/// | `aggregate` | LLVM aggregate type |
3459/// | `value` | LLVM type |
3460///
3461/// ### Result(s):
3462/// | result | description |
3463/// |-----|-------|
3464/// | `res` | LLVM aggregate type |
3465#[pliron_op(
3466    name = "llvm.insert_value",
3467    format = "$0 attr($insert_value_indices, $InsertExtractValueIndicesAttr) `, ` $1 ` : ` type($0)",
3468    interfaces = [OneResultInterface, NOpdsInterface<2>],
3469    attributes = (insert_value_indices: InsertExtractValueIndicesAttr)
3470)]
3471pub struct InsertValueOp;
3472
3473impl InsertValueOp {
3474    /// Create a new [InsertValueOp].
3475    /// `aggregate` is the aggregate type and `value` is the value to insert.
3476    /// `indices` is the list of indices to insert the value at.
3477    /// The `indices` must be valid for the given `aggregate` type.
3478    pub fn new(ctx: &mut Context, aggregate: Value, value: Value, indices: Vec<u32>) -> Self {
3479        use pliron::r#type::Typed;
3480
3481        let result_type = aggregate.get_type(ctx);
3482        let op = Operation::new(
3483            ctx,
3484            Self::get_concrete_op_info(),
3485            vec![result_type],
3486            vec![aggregate, value],
3487            vec![],
3488            0,
3489        );
3490        let op = InsertValueOp { op };
3491        op.set_attr_insert_value_indices(ctx, InsertExtractValueIndicesAttr(indices));
3492        op
3493    }
3494
3495    /// Get the indices for inserting value into aggregate.
3496    pub fn indices(&self, ctx: &Context) -> Vec<u32> {
3497        self.get_attr_insert_value_indices(ctx).unwrap().clone().0
3498    }
3499}
3500
3501impl Verify for InsertValueOp {
3502    fn verify(&self, ctx: &Context) -> Result<()> {
3503        let loc = self.loc(ctx);
3504        // Ensure that we have the indices as an attribute.
3505        if self.get_attr_insert_value_indices(ctx).is_none() {
3506            verify_err!(loc.clone(), InsertExtractValueErr::IndicesAttrErr)?
3507        }
3508
3509        use pliron::r#type::Typed;
3510
3511        // Check that the value we are inserting is of the correct type.
3512        let aggr_type = self.get_operation().deref(ctx).get_operand(0).get_type(ctx);
3513        let indices = self.indices(ctx);
3514        match ExtractValueOp::indexed_type(ctx, aggr_type, &indices) {
3515            Err(e @ Error { .. }) => {
3516                // We reset the error type and error origin to be from here
3517                return Err(Error {
3518                    kind: ErrorKind::VerificationFailed,
3519                    backtrace: pliron::std_deps::backtrace::Backtrace::capture(),
3520                    ..e
3521                });
3522            }
3523            Ok(indexed_type) => {
3524                if indexed_type != self.get_operation().deref(ctx).get_operand(1).get_type(ctx) {
3525                    return verify_err!(loc, InsertExtractValueErr::ValueTypeErr);
3526                }
3527            }
3528        }
3529
3530        Ok(())
3531    }
3532}
3533
3534/// Equivalent to LLVM's ExtractValue opcode.
3535///
3536/// ### Operands
3537/// | operand | description |
3538/// |-----|-------|
3539/// | `aggregate` | LLVM aggregate type |
3540///
3541/// ### Result(s):
3542/// | result | description |
3543/// |-----|-------|
3544/// | `res` | LLVM type |
3545#[pliron_op(
3546    name = "llvm.extract_value",
3547    format = "$0 attr($extract_value_indices, $InsertExtractValueIndicesAttr) ` : ` type($0)",
3548    interfaces = [OneResultInterface, OneOpdInterface],
3549    attributes = (extract_value_indices: InsertExtractValueIndicesAttr)
3550)]
3551pub struct ExtractValueOp;
3552
3553impl Verify for ExtractValueOp {
3554    fn verify(&self, ctx: &Context) -> Result<()> {
3555        let loc = self.loc(ctx);
3556        // Ensure that we have the indices as an attribute.
3557        if self.get_attr_extract_value_indices(ctx).is_none() {
3558            verify_err!(loc.clone(), InsertExtractValueErr::IndicesAttrErr)?
3559        }
3560
3561        use pliron::r#type::Typed;
3562        // Check that the result type matches the indexed type
3563        let aggr_type = self.get_operation().deref(ctx).get_operand(0).get_type(ctx);
3564        let indices = self.indices(ctx);
3565        match Self::indexed_type(ctx, aggr_type, &indices) {
3566            Err(e @ Error { .. }) => {
3567                // We reset the error type and error origin to be from here
3568                return Err(Error {
3569                    kind: ErrorKind::VerificationFailed,
3570                    backtrace: pliron::std_deps::backtrace::Backtrace::capture(),
3571                    ..e
3572                });
3573            }
3574            Ok(indexed_type) => {
3575                if indexed_type != self.get_operation().deref(ctx).get_type(0) {
3576                    return verify_err!(loc, InsertExtractValueErr::ValueTypeErr);
3577                }
3578            }
3579        }
3580
3581        Ok(())
3582    }
3583}
3584
3585impl ExtractValueOp {
3586    /// Create a new [ExtractValueOp].
3587    /// `aggregate` is the aggregate type and `indices` is the list of indices to extract the value from.
3588    /// The `indices` must be valid for the given `aggregate` type.
3589    /// The result type of the operation is the type of the value at the given indices.
3590    pub fn new(ctx: &mut Context, aggregate: Value, indices: Vec<u32>) -> Result<Self> {
3591        use pliron::r#type::Typed;
3592        let result_type = Self::indexed_type(ctx, aggregate.get_type(ctx), &indices)?;
3593        let op = Operation::new(
3594            ctx,
3595            Self::get_concrete_op_info(),
3596            vec![result_type],
3597            vec![aggregate],
3598            vec![],
3599            0,
3600        );
3601        let op = ExtractValueOp { op };
3602        op.set_attr_extract_value_indices(ctx, InsertExtractValueIndicesAttr(indices));
3603        Ok(op)
3604    }
3605
3606    /// Get the indices for extracting value from aggregate.
3607    pub fn indices(&self, ctx: &Context) -> Vec<u32> {
3608        self.get_attr_extract_value_indices(ctx).unwrap().clone().0
3609    }
3610
3611    /// Returns the type of the value at the given indices in the given aggregate type.
3612    pub fn indexed_type(
3613        ctx: &Context,
3614        aggr_type: TypeHandle,
3615        indices: &[u32],
3616    ) -> Result<TypeHandle> {
3617        fn indexed_type_inner(
3618            ctx: &Context,
3619            aggr_type: TypeHandle,
3620            mut idx_itr: impl Iterator<Item = u32>,
3621        ) -> Result<TypeHandle> {
3622            let Some(idx) = idx_itr.next() else {
3623                return Ok(aggr_type);
3624            };
3625            let aggr_type = &*aggr_type.deref(ctx);
3626            if let Some(st) = aggr_type.downcast_ref::<StructType>() {
3627                if st.is_opaque() || idx as usize >= st.num_fields() {
3628                    return arg_err_noloc!(InsertExtractValueErr::InvalidIndicesErr);
3629                }
3630                indexed_type_inner(ctx, st.field_type(idx as usize), idx_itr)
3631            } else if let Some(at) = aggr_type.downcast_ref::<ArrayType>() {
3632                if idx as u64 >= at.size() {
3633                    return arg_err_noloc!(InsertExtractValueErr::InvalidIndicesErr);
3634                }
3635                indexed_type_inner(ctx, at.elem_type(), idx_itr)
3636            } else {
3637                arg_err_noloc!(InsertExtractValueErr::InvalidIndicesErr)
3638            }
3639        }
3640        indexed_type_inner(ctx, aggr_type, indices.iter().cloned())
3641    }
3642}
3643
3644#[derive(Error, Debug)]
3645pub enum InsertExtractValueErr {
3646    #[error("Insert/Extract value instruction has no or incorrect indices attribute")]
3647    IndicesAttrErr,
3648    #[error("Invalid indices on insert/extract value instruction")]
3649    InvalidIndicesErr,
3650    #[error("Value being inserted / extracted does not match the type of the indexed aggregate")]
3651    ValueTypeErr,
3652}
3653
3654/// Equivalent to LLVM's InsertElement opcode.
3655///
3656//// ### Operands
3657/// | operand | description |
3658/// |-----|-------|
3659/// | `vector` | LLVM vector type |
3660/// | `element` | LLVM type |
3661/// | `index` | u32 |
3662///
3663/// /// ### Result(s):
3664/// | result | description |
3665/// |-----|-------|
3666/// | `res` | LLVM vector type |
3667#[pliron_op(
3668    name = "llvm.insert_element",
3669    format = "$0 `, ` $1 `, ` $2 ` : ` type($0)",
3670    interfaces = [OneResultInterface, NOpdsInterface<3>],
3671    operands = (vector, element, index)
3672)]
3673pub struct InsertElementOp;
3674impl Verify for InsertElementOp {
3675    fn verify(&self, ctx: &Context) -> Result<()> {
3676        use pliron::r#type::Typed;
3677
3678        let loc = self.loc(ctx);
3679        let op = &*self.op.deref(ctx);
3680        let vector_ty = op.get_operand(0).get_type(ctx);
3681        let element_ty = op.get_operand(1).get_type(ctx);
3682        let index_ty = op.get_operand(2).get_type(ctx);
3683
3684        let vector_ty = vector_ty.deref(ctx);
3685        let vector_ty = vector_ty.downcast_ref::<VectorType>();
3686        if vector_ty.is_none_or(|ty| ty.elem_type() != element_ty) {
3687            return verify_err!(loc, InsertExtractElementOpVerifyErr::ElementTypeErr);
3688        }
3689
3690        if !index_ty.deref(ctx).is::<IntegerType>() {
3691            return verify_err!(loc, InsertExtractElementOpVerifyErr::IndexTypeErr);
3692        }
3693
3694        Ok(())
3695    }
3696}
3697
3698impl InsertElementOp {
3699    /// Create a new [InsertElementOp].
3700    pub fn new(ctx: &mut Context, vector: Value, element: Value, index: Value) -> Self {
3701        use pliron::r#type::Typed;
3702
3703        let result_type = vector.get_type(ctx);
3704        let op = Operation::new(
3705            ctx,
3706            Self::get_concrete_op_info(),
3707            vec![result_type],
3708            vec![vector, element, index],
3709            vec![],
3710            0,
3711        );
3712        InsertElementOp { op }
3713    }
3714
3715    /// Get the vector type of the InsertElementOp.
3716    pub fn vector_type(&self, ctx: &Context) -> TypedHandle<VectorType> {
3717        let ty = self.get_operation().deref(ctx).get_type(0);
3718        TypedHandle::<VectorType>::from_handle(ty, ctx)
3719            .expect("InsertElementOp result type is not a VectorType")
3720    }
3721}
3722
3723#[derive(Error, Debug)]
3724pub enum InsertExtractElementOpVerifyErr {
3725    #[error("Element type must match vector element type")]
3726    ElementTypeErr,
3727    #[error("Index type must be signless integer")]
3728    IndexTypeErr,
3729}
3730
3731/// ExtractElementOp
3732/// Equivalent to LLVM's ExtractElement opcode.
3733/// /// ### Operands
3734/// | operand | description |
3735/// |-----|-------|
3736/// | `vector` | LLVM vector type |
3737/// | `index` | u32 |
3738/// /// ### Result(s):
3739/// | result | description |
3740/// |-----|-------|
3741/// | `res` | LLVM type |
3742#[pliron_op(
3743    name = "llvm.extract_element",
3744    format = "$0 `, ` $1 ` : ` type($0)",
3745    interfaces = [OneResultInterface, NOpdsInterface<2>],
3746    operands = (vector, index)
3747)]
3748pub struct ExtractElementOp;
3749
3750impl Verify for ExtractElementOp {
3751    fn verify(&self, ctx: &Context) -> Result<()> {
3752        use pliron::r#type::Typed;
3753        let loc = self.loc(ctx);
3754        let op = &*self.op.deref(ctx);
3755        let vector_ty = op.get_operand(0).get_type(ctx);
3756        let index_ty = op.get_operand(1).get_type(ctx);
3757        let vector_ty = vector_ty.deref(ctx);
3758        let vector_ty = vector_ty.downcast_ref::<VectorType>();
3759        if vector_ty.is_none_or(|ty| ty.elem_type() != op.get_type(0)) {
3760            return verify_err!(loc, InsertExtractElementOpVerifyErr::ElementTypeErr);
3761        }
3762        if !index_ty.deref(ctx).is::<IntegerType>() {
3763            return verify_err!(loc, InsertExtractElementOpVerifyErr::IndexTypeErr);
3764        }
3765        Ok(())
3766    }
3767}
3768
3769impl ExtractElementOp {
3770    /// Create a new [ExtractElementOp].
3771    pub fn new(ctx: &mut Context, vector: Value, index: Value) -> Self {
3772        use pliron::r#type::Typed;
3773
3774        let result_type = vector
3775            .get_type(ctx)
3776            .deref(ctx)
3777            .downcast_ref::<VectorType>()
3778            .expect("ExtractElementOp vector operand must be a vector type")
3779            .elem_type();
3780
3781        let op = Operation::new(
3782            ctx,
3783            Self::get_concrete_op_info(),
3784            vec![result_type],
3785            vec![vector, index],
3786            vec![],
3787            0,
3788        );
3789        ExtractElementOp { op }
3790    }
3791
3792    /// Get the vector type of the ExtractElementOp.
3793    pub fn vector_type(&self, ctx: &Context) -> TypedHandle<VectorType> {
3794        use pliron::r#type::Typed;
3795        let ty = self.get_operand_vector(ctx).get_type(ctx);
3796        TypedHandle::<VectorType>::from_handle(ty, ctx)
3797            .expect("ExtractElementOp vector operand type is not a VectorType")
3798    }
3799}
3800
3801/// ShuffleVectorOp
3802/// Equivalent to LLVM's ShuffleVector opcode.
3803///
3804/// ### Operands
3805/// | operand | description |
3806/// |-----|-------|
3807/// | `vector1` | LLVM vector type |
3808/// | `vector2` | LLVM vector type |
3809/// | `mask` | LLVM vector type |
3810///
3811/// ### Result(s):
3812/// | result | description |
3813/// |-----|-------|
3814/// | `res` | LLVM vector type |
3815#[pliron_op(
3816    name = "llvm.shuffle_vector",
3817    format = "$0 `, ` $1 `, ` attr($llvm_shuffle_vector_mask, $ShuffleVectorMaskAttr) ` : ` type($0)",
3818    interfaces = [OneResultInterface, NOpdsInterface<2>],
3819    attributes = (llvm_shuffle_vector_mask: ShuffleVectorMaskAttr)
3820)]
3821pub struct ShuffleVectorOp;
3822impl Verify for ShuffleVectorOp {
3823    fn verify(&self, ctx: &Context) -> Result<()> {
3824        use pliron::r#type::Typed;
3825
3826        let loc = self.loc(ctx);
3827        let op = &*self.op.deref(ctx);
3828        let vector1_ty = op.get_operand(0).get_type(ctx);
3829        let vector2_ty = op.get_operand(1).get_type(ctx);
3830
3831        let vector1_ty = vector1_ty.deref(ctx);
3832        let vector1_ty = vector1_ty.downcast_ref::<VectorType>();
3833        let vector2_ty = vector2_ty.deref(ctx);
3834        let vector2_ty = vector2_ty.downcast_ref::<VectorType>();
3835
3836        let (Some(v1_ty), Some(v2_ty)) = (vector1_ty, vector2_ty) else {
3837            return verify_err!(loc, ShuffleVectorOpVerifyErr::OperandsTypeErr);
3838        };
3839
3840        if v1_ty != v2_ty {
3841            return verify_err!(loc, ShuffleVectorOpVerifyErr::OperandsTypeErr);
3842        }
3843
3844        let res_ty = op.get_type(0).deref(ctx);
3845        let res_ty = res_ty.downcast_ref::<VectorType>();
3846        let Some(res_ty) = res_ty else {
3847            return verify_err!(loc, ShuffleVectorOpVerifyErr::ResultTypeErr);
3848        };
3849
3850        if res_ty.elem_type() != v1_ty.elem_type()
3851            || res_ty.num_elements() as usize
3852                != self.get_attr_llvm_shuffle_vector_mask(ctx).unwrap().0.len()
3853        {
3854            return verify_err!(loc, ShuffleVectorOpVerifyErr::ResultTypeErr);
3855        }
3856
3857        Ok(())
3858    }
3859}
3860
3861/// The undef mask element used in ShuffleVectorOp masks.
3862#[cfg(feature = "llvm-sys")]
3863pub static SHUFFLE_VECTOR_UNDEF_MASK_ELEM: std::sync::LazyLock<i32> =
3864    std::sync::LazyLock::new(llvm_get_undef_mask_elem);
3865#[cfg(not(feature = "llvm-sys"))]
3866pub static SHUFFLE_VECTOR_UNDEF_MASK_ELEM: i32 = -1;
3867
3868impl ShuffleVectorOp {
3869    /// Create a new [ShuffleVectorOp].
3870    pub fn new(ctx: &mut Context, vector1: Value, vector2: Value, mask: Vec<i32>) -> Self {
3871        use pliron::r#type::Typed;
3872
3873        let (elem_ty, kind) = {
3874            let vector1_ty = vector1.get_type(ctx).deref(ctx);
3875            let opd_vec_ty = vector1_ty
3876                .downcast_ref::<VectorType>()
3877                .expect("ShuffleVectorOp vector1 operand must be a vector type");
3878            (opd_vec_ty.elem_type(), opd_vec_ty.kind())
3879        };
3880
3881        let result_type = VectorType::get(
3882            ctx,
3883            elem_ty,
3884            mask.len()
3885                .try_into()
3886                .expect("ShuffleVectorOp mask length too large"),
3887            kind,
3888        );
3889        let op = Operation::new(
3890            ctx,
3891            Self::get_concrete_op_info(),
3892            vec![result_type.into()],
3893            vec![vector1, vector2],
3894            vec![],
3895            0,
3896        );
3897
3898        let mask_attr = ShuffleVectorMaskAttr(mask);
3899        let op = ShuffleVectorOp { op };
3900        op.set_attr_llvm_shuffle_vector_mask(ctx, mask_attr);
3901        op
3902    }
3903}
3904
3905#[derive(Error, Debug)]
3906pub enum ShuffleVectorOpVerifyErr {
3907    #[error("Both operands must be equivalent vector types")]
3908    OperandsTypeErr,
3909    #[error("Result type must be a vector type with correct element type and size")]
3910    ResultTypeErr,
3911}
3912
3913/// Equivalent to LLVM's Select opcode.
3914///
3915/// ### Operands
3916/// | operand | description |
3917/// |-----|-------|
3918/// | `condition` | i1 |
3919/// | `true_dest` | any type |
3920/// | `false_dest` | any type |
3921///
3922/// ### Result(s):
3923/// | result | description |
3924/// |-----|-------|
3925/// | `res` | any type |
3926#[pliron_op(
3927    name = "llvm.select",
3928    format = "opt_attr($llvm_select_fast_math_flags, $FastmathFlagsAttr) ` ` $0 ` ? ` $1 ` : ` $2 ` : ` type($0)",
3929    interfaces = [OneResultInterface, NOpdsInterface<3>],
3930    attributes = (llvm_select_fast_math_flags: FastmathFlagsAttr),
3931)]
3932pub struct SelectOp;
3933
3934impl SelectOp {
3935    /// Create a new [SelectOp].
3936    pub fn new(ctx: &mut Context, cond: Value, true_val: Value, false_val: Value) -> Self {
3937        use pliron::r#type::Typed;
3938
3939        let result_type = true_val.get_type(ctx);
3940        let op = Operation::new(
3941            ctx,
3942            Self::get_concrete_op_info(),
3943            vec![result_type],
3944            vec![cond, true_val, false_val],
3945            vec![],
3946            0,
3947        );
3948        Self { op }
3949    }
3950
3951    /// Create a new [SelectOp] with fast-math flags set.
3952    pub fn new_with_fast_math_flags(
3953        ctx: &mut Context,
3954        cond: Value,
3955        true_val: Value,
3956        false_val: Value,
3957        fast_math_flags: FastmathFlagsAttr,
3958    ) -> Self {
3959        let op = Self::new(ctx, cond, true_val, false_val);
3960        op.set_attr_llvm_select_fast_math_flags(ctx, fast_math_flags);
3961        op
3962    }
3963}
3964
3965impl Verify for SelectOp {
3966    fn verify(&self, ctx: &Context) -> Result<()> {
3967        use pliron::r#type::Typed;
3968
3969        let loc = self.loc(ctx);
3970        let op = &*self.op.deref(ctx);
3971        let ty = op.get_type(0);
3972        let cond_ty = op.get_operand(0).get_type(ctx);
3973        let true_ty = op.get_operand(1).get_type(ctx);
3974        let false_ty = op.get_operand(2).get_type(ctx);
3975        if ty != true_ty || ty != false_ty {
3976            return verify_err!(loc, SelectOpVerifyErr::ResultTypeErr);
3977        }
3978
3979        let mut cond_ty = cond_ty.deref(ctx);
3980        if let Some(vec_ty) = cond_ty.downcast_ref::<VectorType>() {
3981            if let Some(opd_vec_ty) = ty.deref(ctx).downcast_ref::<VectorType>()
3982                && vec_ty.num_elements() == opd_vec_ty.num_elements()
3983            {
3984                // We're good, both the condition and operand are vectors of the same length
3985            } else {
3986                return verify_err!(loc, SelectOpVerifyErr::ConditionTypeErr);
3987            }
3988            cond_ty = vec_ty.elem_type().deref(ctx);
3989        }
3990
3991        let cond_ty = cond_ty.downcast_ref::<IntegerType>();
3992        if cond_ty.is_none_or(|ty| ty.width() != 1) {
3993            return verify_err!(loc, SelectOpVerifyErr::ConditionTypeErr);
3994        }
3995
3996        // LLVM permits fast-math flags on select only when the result type is
3997        // a floating-point scalar or vector.
3998        if let Some(fmf) = self.get_attr_llvm_select_fast_math_flags(ctx)
3999            && *fmf != FastmathFlagsAttr::default()
4000        {
4001            let mut res_ty = ty;
4002            if let Some(vec_ty) = res_ty.deref(ctx).downcast_ref::<VectorType>() {
4003                res_ty = vec_ty.elem_type();
4004            }
4005            if type_cast::<dyn FloatTypeInterface>(&*res_ty.deref(ctx)).is_none() {
4006                return verify_err!(loc, SelectOpVerifyErr::FastMathFlagsOnNonFloatErr);
4007            }
4008        }
4009        Ok(())
4010    }
4011}
4012
4013#[derive(Error, Debug)]
4014pub enum SelectOpVerifyErr {
4015    #[error("Result must be the same as the true and false destination types")]
4016    ResultTypeErr,
4017    #[error("Condition must be an i1 or a vector of i1 equal in length to the operand vectors")]
4018    ConditionTypeErr,
4019    #[error("Fast-math flags are only allowed on selects of floating-point type")]
4020    FastMathFlagsOnNonFloatErr,
4021}
4022
4023/// Floating-point negation
4024/// Equivalent to LLVM's `fneg` instruction.
4025///
4026/// Operands:
4027/// | operand | description |
4028/// |-----|-------|
4029/// | `arg` | float |
4030///
4031/// Result(s):
4032/// | result | description |
4033/// |-----|-------|
4034/// | `res` | float |
4035#[pliron_op(
4036    name = "llvm.fneg",
4037    format = "attr($llvm_fast_math_flags, $FastmathFlagsAttr) ` ` $0 ` : ` type($0)",
4038    interfaces = [
4039        OneResultInterface,
4040        OneOpdInterface,
4041        SameResultsType,
4042        SameOperandsType,
4043        SameOperandsAndResultType,
4044        FastMathFlags,
4045    ]
4046)]
4047pub struct FNegOp;
4048
4049impl Verify for FNegOp {
4050    fn verify(&self, ctx: &Context) -> Result<()> {
4051        use pliron::r#type::Typed;
4052
4053        let loc = self.loc(ctx);
4054        let op = &*self.op.deref(ctx);
4055        let arg_ty = op.get_operand(0).get_type(ctx);
4056        if !type_impls::<dyn FloatTypeInterface>(&*arg_ty.deref(ctx)) {
4057            return verify_err!(loc, FNegOpVerifyErr::ArgumentMustBeFloat);
4058        }
4059        Ok(())
4060    }
4061}
4062
4063impl FNegOp {
4064    /// Create a new [FNegOp].
4065    pub fn new_with_fast_math_flags(
4066        ctx: &mut Context,
4067        arg: Value,
4068        fast_math_flags: FastmathFlagsAttr,
4069    ) -> Self {
4070        use pliron::r#type::Typed;
4071        let op = Operation::new(
4072            ctx,
4073            Self::get_concrete_op_info(),
4074            vec![arg.get_type(ctx)],
4075            vec![arg],
4076            vec![],
4077            0,
4078        );
4079        let op = FNegOp { op };
4080        op.set_fast_math_flags(ctx, fast_math_flags);
4081        op
4082    }
4083}
4084
4085#[derive(Error, Debug)]
4086pub enum FNegOpVerifyErr {
4087    #[error("Argument must be a float")]
4088    ArgumentMustBeFloat,
4089    #[error("Fast math flags must be set")]
4090    FastMathFlagsMustBeSet,
4091}
4092
4093macro_rules! new_float_bin_op {
4094    (   $(#[$outer:meta])*
4095        $op_name:ident, $op_id:literal
4096    ) => {
4097        $(#[$outer])*
4098        /// ### Operands:
4099        ///
4100        /// | operand | description |
4101        /// |-----|-------|
4102        /// | `lhs` | float |
4103        /// | `rhs` | float |
4104        ///
4105        /// ### Result(s):
4106        ///
4107        /// | result | description |
4108        /// |-----|-------|
4109        /// | `res` | float |
4110        #[pliron_op(
4111            name = $op_id,
4112            format = "attr($llvm_fast_math_flags, $FastmathFlagsAttr) ` ` $0 `, ` $1 ` : ` type($0)",
4113            interfaces = [
4114                OneResultInterface, SameOperandsType, SameResultsType,
4115                SameOperandsAndResultType, BinArithOp, FloatBinArithOp,
4116                FloatBinArithOpWithFastMathFlags, FastMathFlags, NOpdsInterface<2>
4117            ],
4118            verifier = "succ"
4119        )]
4120        pub struct $op_name;
4121    }
4122}
4123
4124new_float_bin_op! {
4125    /// Equivalent to LLVM's `fadd` instruction.
4126    FAddOp,
4127    "llvm.fadd"
4128}
4129
4130new_float_bin_op! {
4131    /// Equivalent to LLVM's `fsub` instruction.
4132    FSubOp,
4133    "llvm.fsub"
4134}
4135
4136new_float_bin_op! {
4137    /// Equivalent to LLVM's `fmul` instruction.
4138    FMulOp,
4139    "llvm.fmul"
4140}
4141
4142new_float_bin_op! {
4143    /// Equivalent to LLVM's `fdiv` instruction.
4144    FDivOp,
4145    "llvm.fdiv"
4146}
4147
4148new_float_bin_op! {
4149    /// Equivalent to LLVM's `frem` instruction.
4150    FRemOp,
4151    "llvm.frem"
4152}
4153
4154/// Equivalent to LLVM'same `fcmp` instruction.
4155///
4156/// ### Operand(s):
4157/// | operand | description |
4158/// |-----|-------|
4159/// | `lhs` | float |
4160/// | `rhs` | float |
4161///
4162/// ### Result(s):
4163///
4164/// | result | description |
4165/// |-----|-------|
4166/// | `res` | 1-bit signless integer |
4167#[pliron_op(
4168    name = "llvm.fcmp",
4169    format = "attr($llvm_fast_math_flags, $FastmathFlagsAttr) ` ` $0 ` <` attr($fcmp_predicate, $FCmpPredicateAttr) `> ` $1 ` : ` type($0)",
4170    interfaces = [
4171        OneResultInterface,
4172        SameOperandsType,
4173        FastMathFlags,
4174        NOpdsInterface<2>
4175    ],
4176    attributes = (fcmp_predicate: FCmpPredicateAttr)
4177)]
4178pub struct FCmpOp;
4179
4180impl FCmpOp {
4181    /// Create a new [FCmpOp]
4182    pub fn new(ctx: &mut Context, pred: FCmpPredicateAttr, lhs: Value, rhs: Value) -> Self {
4183        let bool_ty = IntegerType::get(ctx, 1, Signedness::Signless);
4184        let op = Operation::new(
4185            ctx,
4186            Self::get_concrete_op_info(),
4187            vec![bool_ty.into()],
4188            vec![lhs, rhs],
4189            vec![],
4190            0,
4191        );
4192        let op = FCmpOp { op };
4193        op.set_attr_fcmp_predicate(ctx, pred);
4194        op
4195    }
4196
4197    /// Get the predicate
4198    pub fn predicate(&self, ctx: &Context) -> FCmpPredicateAttr {
4199        self.get_attr_fcmp_predicate(ctx)
4200            .expect("FCmpOp missing or incorrect predicate attribute type")
4201            .clone()
4202    }
4203}
4204
4205impl Verify for FCmpOp {
4206    fn verify(&self, ctx: &Context) -> Result<()> {
4207        let loc = self.loc(ctx);
4208
4209        if self.get_attr_fcmp_predicate(ctx).is_none() {
4210            verify_err!(loc.clone(), FCmpOpVerifyErr::PredAttrErr)?
4211        }
4212
4213        let res_ty: TypedHandle<IntegerType> = TypedHandle::from_handle(self.result_type(ctx), ctx)
4214            .map_err(|mut err| {
4215                err.set_loc(loc.clone());
4216                err
4217            })?;
4218
4219        if res_ty.deref(ctx).width() != 1 {
4220            return verify_err!(loc, FCmpOpVerifyErr::ResultNotBool);
4221        }
4222
4223        let opd_ty = self.operand_type_i(ctx, I::<0>.into()).deref(ctx);
4224        if !(type_impls::<dyn FloatTypeInterface>(&*opd_ty)) {
4225            return verify_err!(loc, FCmpOpVerifyErr::IncorrectOperandsType);
4226        }
4227
4228        Ok(())
4229    }
4230}
4231
4232#[derive(Error, Debug)]
4233pub enum FCmpOpVerifyErr {
4234    #[error("Result must be 1-bit integer (bool)")]
4235    ResultNotBool,
4236    #[error("Operand must be floating point type")]
4237    IncorrectOperandsType,
4238    #[error("Missing or incorrect predicate attribute")]
4239    PredAttrErr,
4240}
4241
4242/// All LLVM intrinsic calls are represented by this [Op].
4243/// Same as MLIR's [llvm.call_intrinsic](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmcall_intrinsic-llvmcallintrinsicop).
4244#[pliron_op(
4245    name = "llvm.call_intrinsic",
4246    interfaces = [OneResultInterface],
4247    attributes = (
4248        llvm_intrinsic_name: StringAttr,
4249        llvm_intrinsic_type: TypeAttr,
4250        llvm_intrinsic_fastmath_flags: FastmathFlagsAttr
4251    )
4252)]
4253pub struct CallIntrinsicOp;
4254
4255impl CallIntrinsicOp {
4256    /// Create a new [CallIntrinsicOp].
4257    pub fn new(
4258        ctx: &mut Context,
4259        intrinsic_name: StringAttr,
4260        intrinsic_type: TypedHandle<FuncType>,
4261        operands: Vec<Value>,
4262    ) -> Self {
4263        let res_ty = intrinsic_type.deref(ctx).result_type();
4264        let op = Operation::new(
4265            ctx,
4266            Self::get_concrete_op_info(),
4267            vec![res_ty],
4268            operands,
4269            vec![],
4270            0,
4271        );
4272        let op = CallIntrinsicOp { op };
4273        op.set_attr_llvm_intrinsic_name(ctx, intrinsic_name);
4274        op.set_attr_llvm_intrinsic_type(ctx, TypeAttr::new(intrinsic_type.into()));
4275        op
4276    }
4277}
4278
4279impl Printable for CallIntrinsicOp {
4280    fn fmt(
4281        &self,
4282        ctx: &Context,
4283        _state: &printable::State,
4284        f: &mut core::fmt::Formatter<'_>,
4285    ) -> core::fmt::Result {
4286        // [result = ] llvm.call_intrinsic @name <FastMathFlags> (operands) : type
4287        if let Some(res) = self.op.deref(ctx).results().next() {
4288            write!(f, "{} = ", res.disp(ctx))?;
4289        }
4290
4291        write!(
4292            f,
4293            "{} @{} ",
4294            Self::get_opid_static(),
4295            self.get_attr_llvm_intrinsic_name(ctx)
4296                .expect("CallIntrinsicOp missing or incorrect intrinsic name attribute")
4297                .disp(ctx),
4298        )?;
4299
4300        if let Some(fmf) = self.get_attr_llvm_intrinsic_fastmath_flags(ctx)
4301            && *fmf != FastmathFlagsAttr::default()
4302        {
4303            write!(f, " {} ", fmf.disp(ctx))?;
4304        }
4305
4306        write!(
4307            f,
4308            "({}) : {}",
4309            iter_with_sep(
4310                self.op.deref(ctx).operands(),
4311                printable::ListSeparator::CharSpace(',')
4312            )
4313            .disp(ctx),
4314            self.get_attr_llvm_intrinsic_type(ctx)
4315                .expect("CallIntrinsicOp missing or incorrect intrinsic type attribute")
4316                .disp(ctx),
4317        )
4318    }
4319}
4320
4321impl Parsable for CallIntrinsicOp {
4322    type Arg = Vec<(Identifier, Location)>;
4323    type Parsed = OpObj;
4324    fn parse<'a>(
4325        state_stream: &mut StateStream<'a>,
4326        results: Self::Arg,
4327    ) -> ParseResult<'a, Self::Parsed> {
4328        let pos = state_stream.loc();
4329
4330        let mut parser = (
4331            spaced(token('@').with(StringAttr::parser(()))),
4332            optional(spaced(FastmathFlagsAttr::parser(()))),
4333            delimited_list_parser('(', ')', ',', ssa_opd_parser()).skip(spaced(token(':'))),
4334            spaced(type_parser()),
4335        );
4336
4337        // Parse and build the call intrinsic op.
4338        let (iname, fmf, operands, ftype) = parser.parse_stream(state_stream).into_result()?.0;
4339
4340        let ctx = &mut state_stream.state.ctx;
4341        let intr_ty = TypedHandle::<FuncType>::from_handle(ftype, ctx).map_err(|mut err| {
4342            err.set_loc(pos);
4343            err
4344        })?;
4345        let op = CallIntrinsicOp::new(ctx, iname, intr_ty, operands);
4346        if let Some(fmf) = fmf {
4347            op.set_attr_llvm_intrinsic_fastmath_flags(ctx, fmf);
4348        }
4349        process_parsed_ssa_defs(state_stream, &results, op.get_operation())?;
4350        Ok(OpObj::new(op)).into_parse_result()
4351    }
4352}
4353
4354#[derive(Error, Debug)]
4355pub enum CallIntrinsicVerifyErr {
4356    #[error("Missing or incorrect intrinsic name attribute")]
4357    MissingIntrinsicNameAttr,
4358    #[error("Missing or incorrect intrinsic type attribute")]
4359    MissingIntrinsicTypeAttr,
4360    #[error("Number or types of operands does not match intrinsic type")]
4361    OperandsMismatch,
4362    #[error("Number or types of results does not match intrinsic type")]
4363    ResultsMismatch,
4364    #[error("Intrinsic name does not correspond to a known LLVM intrinsic")]
4365    UnknownIntrinsicName,
4366}
4367
4368impl Verify for CallIntrinsicOp {
4369    fn verify(&self, ctx: &Context) -> Result<()> {
4370        // Check that the intrinsic name and type attributes are present.
4371        let Some(name) = self.get_attr_llvm_intrinsic_name(ctx) else {
4372            return verify_err!(
4373                self.loc(ctx),
4374                CallIntrinsicVerifyErr::MissingIntrinsicNameAttr
4375            );
4376        };
4377
4378        let Some(ty) = self
4379            .get_attr_llvm_intrinsic_type(ctx)
4380            .and_then(|ty| TypedHandle::<FuncType>::from_handle(ty.get_type(ctx), ctx).ok())
4381        else {
4382            return verify_err!(
4383                self.loc(ctx),
4384                CallIntrinsicVerifyErr::MissingIntrinsicTypeAttr
4385            );
4386        };
4387
4388        let arg_types = ty.deref(ctx).arg_types();
4389        let res_type = ty.deref(ctx).result_type();
4390
4391        // Check that the operand and result types match the intrinsic type.
4392        let op = &*self.op.deref(ctx);
4393        let intrinsic_arg_types = ty.deref(ctx).arg_types();
4394        if op.operands().count() != intrinsic_arg_types.len() {
4395            return verify_err!(self.loc(ctx), CallIntrinsicVerifyErr::OperandsMismatch);
4396        }
4397
4398        for (i, operand) in op.operands().enumerate() {
4399            let opd_ty = pliron::r#type::Typed::get_type(&operand, ctx);
4400            if opd_ty != arg_types[i] {
4401                return verify_err!(self.loc(ctx), CallIntrinsicVerifyErr::OperandsMismatch);
4402            }
4403        }
4404
4405        let mut result_types = op.result_types();
4406        if let Some(result_type) = result_types.next()
4407            && result_type == res_type
4408            && result_types.next().is_none()
4409        {
4410        } else {
4411            return verify_err!(self.loc(ctx), CallIntrinsicVerifyErr::ResultsMismatch);
4412        }
4413
4414        let name: String = name.clone().into();
4415        #[cfg(feature = "llvm-sys")]
4416        if llvm_lookup_intrinsic_id(&name).is_none() {
4417            return verify_err!(self.loc(ctx), CallIntrinsicVerifyErr::UnknownIntrinsicName);
4418        }
4419        #[cfg(not(feature = "llvm-sys"))]
4420        // We can't verify the intrinsic name without llvm-sys, so just check that it's not empty.
4421        if name.is_empty() {
4422            return verify_err!(self.loc(ctx), CallIntrinsicVerifyErr::UnknownIntrinsicName);
4423        }
4424
4425        Ok(())
4426    }
4427}
4428
4429/// Equivalent to LLVM's `va_arg` operation.
4430#[pliron_op(
4431    name = "llvm.va_arg",
4432    format = "$0 ` : ` type($0)",
4433    interfaces = [OneResultInterface, OneOpdInterface]
4434)]
4435pub struct VAArgOp;
4436
4437#[derive(Error, Debug)]
4438pub enum VAArgOpVerifyErr {
4439    #[error("Operand must be a pointer type")]
4440    OperandNotPointer,
4441}
4442
4443impl Verify for VAArgOp {
4444    fn verify(&self, ctx: &Context) -> Result<()> {
4445        let loc = self.loc(ctx);
4446
4447        // Check that the argument is a pointer.
4448        let opd_ty = self.operand_type(ctx).deref(ctx);
4449        if !opd_ty.is::<PointerType>() {
4450            return verify_err!(loc, VAArgOpVerifyErr::OperandNotPointer);
4451        }
4452
4453        Ok(())
4454    }
4455}
4456
4457impl VAArgOp {
4458    /// Create a new [VAArgOp].
4459    pub fn new(ctx: &mut Context, list: Value, ty: TypeHandle) -> Self {
4460        let op = Operation::new(
4461            ctx,
4462            Self::get_concrete_op_info(),
4463            vec![ty],
4464            vec![list],
4465            vec![],
4466            0,
4467        );
4468        VAArgOp { op }
4469    }
4470}
4471
4472/// Equivalent to LLVM's `func` operation.
4473/// See [llvm.func](https://mlir.llvm.org/docs/Dialects/LLVM/#llvmfunc-llvmllvmfuncop).
4474#[pliron_op(
4475    name = "llvm.func",
4476    interfaces = [
4477        SymbolOpInterface,
4478        IsolatedFromAboveInterface,
4479        AtMostNRegionsInterface<1>,
4480        AtMostOneRegionInterface,
4481        NResultsInterface<0>,
4482        NOpdsInterface<0>,
4483        LlvmSymbolName
4484    ],
4485    attributes = (llvm_func_type: TypeAttr, llvm_function_linkage: LinkageAttr)
4486)]
4487pub struct FuncOp;
4488
4489impl FuncOp {
4490    /// Create a new empty [FuncOp].
4491    pub fn new(ctx: &mut Context, name: Identifier, ty: TypedHandle<FuncType>) -> Self {
4492        let ty_attr = TypeAttr::new(ty.into());
4493        let op = Operation::new(ctx, Self::get_concrete_op_info(), vec![], vec![], vec![], 0);
4494        let opop = FuncOp { op };
4495        opop.set_symbol_name(ctx, name);
4496        opop.set_attr_llvm_func_type(ctx, ty_attr);
4497
4498        opop
4499    }
4500
4501    /// Get the function signature (type).
4502    pub fn get_type(&self, ctx: &Context) -> TypedHandle<FuncType> {
4503        let ty = attr_cast::<dyn TypedAttrInterface>(&*self.get_attr_llvm_func_type(ctx).unwrap())
4504            .unwrap()
4505            .get_type(ctx);
4506        TypedHandle::from_handle(ty, ctx).unwrap()
4507    }
4508
4509    /// Get the entry block (if it exists) of this function.
4510    pub fn get_entry_block(&self, ctx: &Context) -> Option<Ptr<BasicBlock>> {
4511        self.op
4512            .deref(ctx)
4513            .regions()
4514            .next()
4515            .and_then(|region| region.deref(ctx).get_head())
4516    }
4517
4518    /// Get the entry block of this function, creating it if it does not exist.
4519    pub fn get_or_create_entry_block(&self, ctx: &mut Context) -> Ptr<BasicBlock> {
4520        if let Some(entry_block) = self.get_entry_block(ctx) {
4521            return entry_block;
4522        }
4523
4524        // Create an empty entry block.
4525        assert!(
4526            self.op.deref(ctx).regions().next().is_none(),
4527            "FuncOp already has a region, but no block inside it"
4528        );
4529        let region = Operation::add_region(self.op, ctx);
4530        let arg_types = self.get_type(ctx).deref(ctx).arg_types().clone();
4531        let body = BasicBlock::new(ctx, Some("entry".try_into().unwrap()), arg_types);
4532        body.insert_at_front(region, ctx);
4533        body
4534    }
4535}
4536
4537impl pliron::r#type::Typed for FuncOp {
4538    fn get_type(&self, ctx: &Context) -> TypeHandle {
4539        self.get_type(ctx).into()
4540    }
4541}
4542
4543impl Printable for FuncOp {
4544    fn fmt(
4545        &self,
4546        ctx: &Context,
4547        state: &printable::State,
4548        f: &mut core::fmt::Formatter<'_>,
4549    ) -> core::fmt::Result {
4550        typed_symb_op_header(self).fmt(ctx, state, f)?;
4551
4552        // Print attributes except for function type and symbol name.
4553        let mut attributes_to_print_separately =
4554            self.op.deref(ctx).attributes.clone_skip_outlined();
4555        attributes_to_print_separately
4556            .0
4557            .retain(|key, _| key != &*ATTR_KEY_LLVM_FUNC_TYPE && key != &*ATTR_KEY_SYM_NAME);
4558        indented_block!(state, {
4559            write!(
4560                f,
4561                "{}{}",
4562                indented_nl(state),
4563                attributes_to_print_separately.disp(ctx)
4564            )?;
4565        });
4566
4567        if let Some(r) = self.get_region(ctx) {
4568            write!(f, " ")?;
4569            r.fmt(ctx, state, f)?;
4570        }
4571        Ok(())
4572    }
4573}
4574
4575impl Parsable for FuncOp {
4576    type Arg = Vec<(Identifier, Location)>;
4577    type Parsed = OpObj;
4578    fn parse<'a>(
4579        state_stream: &mut StateStream<'a>,
4580        results: Self::Arg,
4581    ) -> ParseResult<'a, Self::Parsed> {
4582        if !results.is_empty() {
4583            input_err!(
4584                state_stream.loc(),
4585                op_interfaces::NResultsVerifyErr(0, results.len())
4586            )?
4587        }
4588
4589        let op = Operation::new(
4590            state_stream.state.ctx,
4591            Self::get_concrete_op_info(),
4592            vec![],
4593            vec![],
4594            vec![],
4595            0,
4596        );
4597
4598        let mut parser = (
4599            spaced(token('@').with(Identifier::parser(()))).skip(spaced(token(':'))),
4600            spaced(type_parser()),
4601            spaced(AttributeDict::parser(())),
4602            spaced(optional(Region::parser(op))),
4603        );
4604
4605        // Parse and build the function, providing name and type details.
4606        parser
4607            .parse_stream(state_stream)
4608            .map(|(fname, fty, attrs, _region)| -> OpObj {
4609                let ctx = &mut state_stream.state.ctx;
4610                op.deref_mut(ctx).attributes = attrs;
4611                let ty_attr = TypeAttr::new(fty);
4612                let opop = FuncOp { op };
4613                opop.set_symbol_name(ctx, fname);
4614                opop.set_attr_llvm_func_type(ctx, ty_attr);
4615                OpObj::new(opop)
4616            })
4617            .into()
4618    }
4619}
4620
4621#[derive(Error, Debug)]
4622#[error("llvm.func op does not have llvm.func type")]
4623pub struct FuncOpTypeErr;
4624
4625impl Verify for FuncOp {
4626    fn verify(&self, _ctx: &Context) -> Result<()> {
4627        Ok(())
4628    }
4629}
4630
4631impl IsDeclaration for FuncOp {
4632    fn is_declaration(&self, ctx: &Context) -> bool {
4633        self.get_region(ctx).is_none()
4634    }
4635}