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