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