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