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