1use 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#[pliron_op(
109 name = "llvm.return",
110 format = "operands(CharSpace(`,`))",
111 interfaces = [IsTerminatorInterface, NResultsInterface<0>, OptionalOpdInterface],
112)]
113pub struct ReturnOp;
114impl ReturnOp {
115 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 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 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#[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 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 #[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 $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 AddOp,
258 "llvm.add"
259);
260
261new_int_bin_op_with_overflow!(
262 SubOp,
264 "llvm.sub"
265);
266
267new_int_bin_op_with_overflow!(
268 MulOp,
270 "llvm.mul"
271);
272
273new_int_bin_op_with_overflow!(
274 ShlOp,
276 "llvm.shl"
277);
278
279new_int_bin_op!(
280 UDivOp,
282 "llvm.udiv"
283);
284
285new_int_bin_op!(
286 SDivOp,
288 "llvm.sdiv"
289);
290
291new_int_bin_op!(
292 URemOp,
294 "llvm.urem"
295);
296
297new_int_bin_op!(
298 SRemOp,
300 "llvm.srem"
301);
302
303new_int_bin_op!(
304 AndOp,
306 "llvm.and"
307);
308
309new_int_bin_op!(
310 OrOp,
312 "llvm.or"
313);
314
315new_int_bin_op!(
316 XorOp,
318 "llvm.xor"
319);
320
321new_int_bin_op!(
322 LShrOp,
324 "llvm.lshr"
325);
326
327new_int_bin_op!(
328 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#[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 pub fn new(ctx: &mut Context, pred: ICmpPredicateAttr, lhs: Value, rhs: Value) -> Self {
373 use pliron::r#type::Typed;
374
375 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 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#[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 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 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#[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#[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#[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#[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#[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 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#[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 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 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 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 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 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 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 self.remove_from_segment(ctx, succ_idx + 1, opd_idx)
881 }
882}
883
884#[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#[derive(Clone)]
908pub struct SwitchCase {
909 pub value: IntegerAttr,
911 pub dest: Ptr<BasicBlock>,
913 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 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 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 op.set_operand_segment_sizes(ctx, segment_sizes);
1105 op.set_attr_llvm_switch_case_values(ctx, CaseValuesAttr(case_values));
1107 op
1108 }
1109
1110 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 let successors = op.successors().skip(1);
1120
1121 successors
1122 .zip(case_values.0.iter())
1123 .enumerate()
1124 .map(|(i, (dest, value))| {
1125 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 pub fn default_dest(&self, ctx: &Context) -> Ptr<BasicBlock> {
1138 self.get_operation().deref(ctx).get_successor(0)
1139 }
1140
1141 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 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 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 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 if case_value.get_type() != condition_ty {
1207 verify_err!(loc, SwitchOpVerifyErr::ConditionErr)?;
1208 }
1209 }
1210
1211 Ok(())
1212 }
1213}
1214
1215#[derive(Clone)]
1217pub struct IndirectBrDest {
1218 pub dest: Ptr<BasicBlock>,
1220 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#[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 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 op.set_operand_segment_sizes(ctx, segment_sizes);
1377 op
1378 }
1379
1380 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 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 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 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#[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#[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 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 backtrace: pliron::std_deps::backtrace::Backtrace::capture(),
1517 ..e
1518 });
1519 }
1520
1521 Ok(())
1522 }
1523}
1524
1525impl GetElementPtrOp {
1526 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 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 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 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 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 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 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 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#[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 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#[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 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#[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 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#[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 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 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#[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 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 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#[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 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 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#[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 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 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#[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 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#[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 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 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 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 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#[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 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 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#[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 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#[pliron_op(
2556 name = "llvm.poison",
2557 format = "`: ` type($0)",
2558 interfaces = [OneResultInterface],
2559 verifier = "succ"
2560)]
2561pub struct PoisonOp;
2562
2563impl PoisonOp {
2564 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#[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 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#[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 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#[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 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 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 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 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 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 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 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 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 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 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 if self.get_attr_llvm_global_type(ctx).is_none() {
2785 return verify_err!(loc, GlobalOpVerifyErr::MissingType);
2786 }
2787
2788 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 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 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 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 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 }
2921 }
2922 }
2923
2924 Ok(OpObj::new(op)).into_parse_result()
2925 }
2926}
2927
2928#[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 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 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 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 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 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#[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#[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 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 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 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 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 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
3244fn 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#[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#[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#[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#[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#[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#[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#[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#[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#[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#[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 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 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 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 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 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#[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 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 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 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 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 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 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#[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 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 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#[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 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 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#[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#[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 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#[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 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 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 } 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 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#[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 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 #[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 FAddOp,
4328 "llvm.fadd"
4329}
4330
4331new_float_bin_op! {
4332 FSubOp,
4334 "llvm.fsub"
4335}
4336
4337new_float_bin_op! {
4338 FMulOp,
4340 "llvm.fmul"
4341}
4342
4343new_float_bin_op! {
4344 FDivOp,
4346 "llvm.fdiv"
4347}
4348
4349new_float_bin_op! {
4350 FRemOp,
4352 "llvm.frem"
4353}
4354
4355#[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 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 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#[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 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 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 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 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 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 if name.is_empty() {
4628 return verify_err!(self.loc(ctx), CallIntrinsicVerifyErr::UnknownIntrinsicName);
4629 }
4630
4631 Ok(())
4632 }
4633}
4634
4635#[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 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 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#[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 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 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 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 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 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 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 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}