1use alloc::{
7 boxed::Box,
8 string::{String, ToString},
9 vec::Vec,
10};
11use core::{
12 fmt::Display,
13 hash::{Hash, Hasher},
14};
15use thiserror::Error;
16
17use pliron::{
18 attribute::verify_attr,
19 builtin::{
20 attr_interfaces::{MaterializableAttr, OutlinedAttr, PrintOnceAttr, TypedAttrInterface},
21 attributes::{IntegerAttr, StringAttr},
22 ops::ModuleOp,
23 types::{IntegerType, Signedness},
24 },
25 combine::{self, Parser, choice, parser::char::spaces},
26 common_traits::Verify,
27 context::{Context, Ptr},
28 derive::{attr_interface_impl, format, pliron_attr},
29 dict_key,
30 identifier::Identifier,
31 impl_printable_for_display, input_error,
32 op::Op,
33 operation::Operation,
34 parsable::{IntoParseResult, Parsable, StreamLocation},
35 printable::Printable,
36 result::Result,
37 r#type::{TypeHandle, TypedHandle},
38 verify_err_noloc,
39};
40
41use crate::{
42 ops::{ConstantOp, PoisonOp, UndefOp},
43 types::{ArrayType, PointerType, StructType, VectorType},
44};
45
46use bitflags::bitflags;
47
48#[pliron_attr(name = "llvm.integer_overlflow_flags", format, verifier = "succ")]
56#[derive(PartialEq, Eq, Clone, Debug, Default, Hash)]
57pub struct IntegerOverflowFlagsAttr {
58 pub nsw: bool,
59 pub nuw: bool,
60}
61
62bitflags! {
63 #[derive(PartialEq, Eq, Clone, Debug, Hash, Copy)]
65 pub struct FastmathFlags: u8 {
66 const NNAN = 1;
67 const NINF = 2;
68 const NSZ = 4;
69 const ARCP = 8;
70 const CONTRACT = 16;
71 const AFN = 32;
72 const REASSOC = 64;
73 const FAST = 127;
74 }
75}
76
77#[pliron_attr(name = "llvm.fast_math_flags", verifier = "succ")]
78#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
79pub struct FastmathFlagsAttr(pub FastmathFlags);
80
81impl Default for FastmathFlagsAttr {
82 fn default() -> Self {
83 FastmathFlagsAttr(FastmathFlags::empty())
84 }
85}
86
87impl From<FastmathFlags> for FastmathFlagsAttr {
88 fn from(value: FastmathFlags) -> Self {
89 FastmathFlagsAttr(value)
90 }
91}
92
93impl From<FastmathFlagsAttr> for FastmathFlags {
94 fn from(attr: FastmathFlagsAttr) -> Self {
95 attr.0
96 }
97}
98
99impl Display for FastmathFlagsAttr {
100 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101 write!(f, "<")?;
102 bitflags::parser::to_writer(&self.0, &mut *f)?;
103 write!(f, ">")
104 }
105}
106
107impl_printable_for_display!(FastmathFlagsAttr);
108
109#[derive(Debug, Error)]
110#[error("Error parsing fastmath flags: {0}")]
111pub struct FastmathFlagParseErr(pub bitflags::parser::ParseError);
112
113impl Parsable for FastmathFlagsAttr {
114 type Arg = ();
115
116 type Parsed = Self;
117
118 fn parse<'a>(
119 state_stream: &mut pliron::parsable::StateStream<'a>,
120 _arg: Self::Arg,
121 ) -> pliron::parsable::ParseResult<'a, Self::Parsed> {
122 let pos = state_stream.loc();
123 let allowed_chars = combine::choice!(
124 combine::parser::char::space().map(|c| c.to_string()),
125 combine::parser::char::alpha_num().map(|c| c.to_string()),
126 combine::parser::char::char('|').map(|c: char| c.to_string())
127 );
128
129 let (parsed, _): (Vec<String>, _) = combine::between(
130 combine::parser::char::char('<').with(spaces()),
131 spaces().with(combine::parser::char::char('>')),
132 combine::many(allowed_chars),
133 )
134 .parse_stream(state_stream)
135 .into_result()?;
136 let parsed_string = parsed.concat();
137
138 let (fast_math_flags, _) = bitflags::parser::from_str::<FastmathFlags>(&parsed_string)
139 .map_err(|e| input_error!(pos.clone(), FastmathFlagParseErr(e)))
140 .into_parse_result()?;
141
142 Ok(FastmathFlagsAttr(fast_math_flags)).into_parse_result()
143 }
144}
145
146bitflags! {
147 #[derive(PartialEq, Eq, Clone, Debug, Hash, Copy)]
149 pub struct GepNoWrapFlags: u8 {
150 const INBOUNDS = 1;
151 const NUSW = 2;
152 const NUW = 4;
153 }
154}
155
156impl GepNoWrapFlags {
157 pub fn normalized(self) -> Self {
159 if self.contains(Self::INBOUNDS) {
160 self | Self::NUSW
161 } else {
162 self
163 }
164 }
165}
166
167#[pliron_attr(name = "llvm.gep_no_wrap_flags", verifier = "succ")]
168#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
169pub struct GepNoWrapFlagsAttr(pub GepNoWrapFlags);
170
171impl Default for GepNoWrapFlagsAttr {
172 fn default() -> Self {
173 Self(GepNoWrapFlags::empty())
174 }
175}
176
177impl From<GepNoWrapFlags> for GepNoWrapFlagsAttr {
178 fn from(value: GepNoWrapFlags) -> Self {
179 Self(value.normalized())
180 }
181}
182
183impl From<GepNoWrapFlagsAttr> for GepNoWrapFlags {
184 fn from(attr: GepNoWrapFlagsAttr) -> Self {
185 attr.0
186 }
187}
188
189impl Display for GepNoWrapFlagsAttr {
190 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
191 let mut flags = self.0.normalized();
192
193 if flags.contains(GepNoWrapFlags::INBOUNDS) {
195 flags.remove(GepNoWrapFlags::NUSW);
196 }
197
198 write!(f, "<")?;
199 bitflags::parser::to_writer(&flags, &mut *f)?;
200 write!(f, ">")
201 }
202}
203
204impl_printable_for_display!(GepNoWrapFlagsAttr);
205
206#[derive(Debug, Error)]
207#[error("Error parsing GEP no-wrap flags: {0}")]
208pub struct GepNoWrapFlagParseErr(pub bitflags::parser::ParseError);
209
210impl Parsable for GepNoWrapFlagsAttr {
211 type Arg = ();
212 type Parsed = Self;
213
214 fn parse<'a>(
215 state_stream: &mut pliron::parsable::StateStream<'a>,
216 _arg: Self::Arg,
217 ) -> pliron::parsable::ParseResult<'a, Self::Parsed> {
218 let pos = state_stream.loc();
219 let allowed_chars = combine::choice!(
220 combine::parser::char::space().map(|c| c.to_string()),
221 combine::parser::char::alpha_num().map(|c| c.to_string()),
222 combine::parser::char::char('|').map(|c: char| c.to_string())
223 );
224
225 let (parsed, _): (Vec<String>, _) = combine::between(
226 combine::parser::char::char('<').with(spaces()),
227 spaces().with(combine::parser::char::char('>')),
228 combine::many(allowed_chars),
229 )
230 .parse_stream(state_stream)
231 .into_result()?;
232 let parsed_string = parsed.concat();
233
234 let (flags, _) = bitflags::parser::from_str::<GepNoWrapFlags>(&parsed_string)
235 .map_err(|e| input_error!(pos.clone(), GepNoWrapFlagParseErr(e)))
236 .into_parse_result()?;
237
238 Ok(GepNoWrapFlagsAttr(flags.normalized())).into_parse_result()
239 }
240}
241
242#[pliron_attr(name = "llvm.icmp_predicate", verifier = "succ", format)]
243#[derive(PartialEq, Eq, Clone, Debug, Hash)]
244pub enum ICmpPredicateAttr {
245 EQ,
246 NE,
247 SLT,
248 SLE,
249 SGT,
250 SGE,
251 ULT,
252 ULE,
253 UGT,
254 UGE,
255}
256
257#[pliron_attr(name = "llvm.fcmp_predicate", format, verifier = "succ")]
258#[derive(PartialEq, Eq, Clone, Debug, Hash)]
259pub enum FCmpPredicateAttr {
260 False,
261 OEQ,
262 OGT,
263 OGE,
264 OLT,
265 OLE,
266 ONE,
267 ORD,
268 UEQ,
269 UGT,
270 UGE,
271 ULT,
272 ULE,
273 UNE,
274 UNO,
275 True,
276}
277
278#[derive(PartialEq, Eq, Clone, Debug, Hash)]
281#[format]
282pub enum GepIndexAttr {
283 Constant(u32),
285 OperandIdx(usize),
287}
288
289#[pliron_attr(
290 name = "llvm.gep_indices",
291 format = "`[` vec($0, CharSpace(`,`)) `]`",
292 verifier = "succ"
293)]
294#[derive(PartialEq, Eq, Clone, Debug, Hash)]
295pub struct GepIndicesAttr(pub Vec<GepIndexAttr>);
296
297#[pliron_attr(name = "llvm.case_values", format = "`[` vec($0, CharSpace(`,`)) `]`")]
299#[derive(PartialEq, Eq, Clone, Debug, Hash)]
300pub struct CaseValuesAttr(pub Vec<IntegerAttr>);
301
302#[derive(Debug, Error)]
303#[error("Case values must be of the same type, but found different types: {0} and {1}")]
304pub struct CaseValuesAttrVerifyErr(pub String, pub String);
305
306impl Verify for CaseValuesAttr {
307 fn verify(&self, ctx: &Context) -> Result<()> {
308 self.0.windows(2).try_for_each(|pair| {
309 pair[0].verify(ctx)?;
310 if pair[0].get_type() != pair[1].get_type() {
311 verify_err_noloc!(CaseValuesAttrVerifyErr(
312 pair[0].get_type().disp(ctx).to_string(),
313 pair[1].get_type().disp(ctx).to_string()
314 ))
315 } else {
316 Ok(())
317 }
318 })
319 }
320}
321
322#[pliron_attr(name = "llvm.linkage", format, verifier = "succ")]
323#[derive(PartialEq, Eq, Clone, Debug, Hash)]
324pub enum LinkageAttr {
325 ExternalLinkage,
326 AvailableExternallyLinkage,
327 LinkOnceAnyLinkage,
328 LinkOnceODRLinkage,
329 LinkOnceODRAutoHideLinkage,
330 WeakAnyLinkage,
331 WeakODRLinkage,
332 AppendingLinkage,
333 InternalLinkage,
334 PrivateLinkage,
335 DLLImportLinkage,
336 DLLExportLinkage,
337 ExternalWeakLinkage,
338 GhostLinkage,
339 CommonLinkage,
340 LinkerPrivateLinkage,
341 LinkerPrivateWeakLinkage,
342}
343
344#[pliron_attr(
345 name = "llvm.insert_extract_value_indices",
346 format = "`[` vec($0, CharSpace(`,`)) `]`",
347 verifier = "succ"
348)]
349#[derive(PartialEq, Eq, Clone, Debug, Hash)]
350pub struct InsertExtractValueIndicesAttr(pub Vec<u32>);
351
352#[pliron_attr(name = "llvm.align", format = "$0", verifier = "succ")]
353#[derive(PartialEq, Eq, Clone, Debug, Hash)]
354pub struct AlignmentAttr(pub u32);
355
356#[pliron_attr(name = "llvm.addrspace", format = "$0", verifier = "succ")]
358#[derive(PartialEq, Eq, Clone, Debug, Hash)]
359pub struct AddressSpaceAttr(pub u32);
360
361#[pliron_attr(name = "llvm.zero", format = "$0", verifier = "succ")]
363#[derive(PartialEq, Eq, Clone, Debug, Hash)]
364pub struct ZeroAttr(pub TypeHandle);
365
366#[attr_interface_impl]
367impl TypedAttrInterface for ZeroAttr {
368 fn get_type(&self, _ctx: &Context) -> TypeHandle {
369 self.0
370 }
371}
372
373#[pliron_attr(name = "llvm.undef", format = "$0", verifier = "succ")]
375#[derive(PartialEq, Eq, Clone, Debug, Hash)]
376pub struct UndefAttr(pub TypeHandle);
377
378#[attr_interface_impl]
379impl TypedAttrInterface for UndefAttr {
380 fn get_type(&self, _ctx: &Context) -> TypeHandle {
381 self.0
382 }
383}
384
385#[attr_interface_impl]
386impl MaterializableAttr for UndefAttr {
387 fn materialize(&self, ctx: &mut Context) -> Ptr<Operation> {
388 UndefOp::new(ctx, self.0).get_operation()
389 }
390}
391
392#[pliron_attr(name = "llvm.poison", format = "$0", verifier = "succ")]
394#[derive(PartialEq, Eq, Clone, Debug, Hash)]
395pub struct PoisonAttr(pub TypeHandle);
396
397#[attr_interface_impl]
398impl TypedAttrInterface for PoisonAttr {
399 fn get_type(&self, _ctx: &Context) -> TypeHandle {
400 self.0
401 }
402}
403
404#[attr_interface_impl]
405impl MaterializableAttr for PoisonAttr {
406 fn materialize(&self, ctx: &mut Context) -> Ptr<Operation> {
407 PoisonOp::new(ctx, self.0).get_operation()
408 }
409}
410
411#[pliron_attr(
413 name = "llvm.bytes",
414 format = "`[` vec($0, CharSpace(`,`)) `]`",
415 verifier = "succ"
416)]
417#[derive(PartialEq, Eq, Clone, Debug, Hash)]
418pub struct BytesAttr(Vec<u8>);
419
420pub const BYTES_ATTR_OUTLINE_THRESHOLD: usize = 8;
422
423impl BytesAttr {
424 pub fn new(bytes: Vec<u8>) -> Self {
426 BytesAttr(bytes)
427 }
428}
429
430impl From<BytesAttr> for Vec<u8> {
431 fn from(value: BytesAttr) -> Self {
432 value.0
433 }
434}
435
436impl From<Vec<u8>> for BytesAttr {
437 fn from(value: Vec<u8>) -> Self {
438 BytesAttr::new(value)
439 }
440}
441
442impl AsRef<Vec<u8>> for BytesAttr {
443 fn as_ref(&self) -> &Vec<u8> {
444 &self.0
445 }
446}
447
448#[attr_interface_impl]
450impl TypedAttrInterface for BytesAttr {
451 fn get_type(&self, ctx: &Context) -> TypeHandle {
452 let i8_ty = IntegerType::get(ctx, 8, Signedness::Signless);
453 ArrayType::get(ctx, i8_ty.into(), self.0.len() as u64).into()
454 }
455}
456
457#[attr_interface_impl]
458impl OutlinedAttr for BytesAttr {
459 fn outline(&self, _ctx: &Context) -> bool {
460 self.0.len() >= BYTES_ATTR_OUTLINE_THRESHOLD
461 }
462}
463
464#[attr_interface_impl]
465impl PrintOnceAttr for BytesAttr {}
466
467#[pliron_attr(name = "llvm.splat", format = "`<` $element ` : ` $ty `>`")]
469#[derive(Clone, Debug)]
470pub struct SplatAttr {
471 element: Box<dyn TypedAttrInterface>,
472 ty: TypedHandle<VectorType>,
473}
474
475impl PartialEq for SplatAttr {
476 fn eq(&self, other: &Self) -> bool {
477 self.ty == other.ty && PartialEq::eq(&self.element, &other.element)
478 }
479}
480
481impl Eq for SplatAttr {}
482
483impl Hash for SplatAttr {
484 fn hash<H: Hasher>(&self, state: &mut H) {
485 self.element.hash(state);
486 self.ty.hash(state);
487 }
488}
489
490impl SplatAttr {
491 pub fn new(element: Box<dyn TypedAttrInterface>, ty: TypedHandle<VectorType>) -> Self {
493 SplatAttr { element, ty }
494 }
495
496 pub fn element(&self) -> &dyn TypedAttrInterface {
498 &*self.element
499 }
500
501 pub fn ty(&self) -> TypedHandle<VectorType> {
503 self.ty
504 }
505}
506
507#[attr_interface_impl]
508impl TypedAttrInterface for SplatAttr {
509 fn get_type(&self, _ctx: &Context) -> TypeHandle {
510 self.ty.into()
511 }
512}
513
514#[attr_interface_impl]
515impl MaterializableAttr for SplatAttr {
516 fn materialize(&self, ctx: &mut Context) -> Ptr<Operation> {
517 ConstantOp::new(ctx, Box::new(self.clone())).get_operation()
518 }
519}
520
521fn verify_element(
523 ctx: &Context,
524 idx: usize,
525 element: &dyn TypedAttrInterface,
526 expected: TypeHandle,
527) -> Result<()> {
528 verify_attr(element, ctx)?;
529 let ty = element.get_type(ctx);
530 if ty != expected {
531 verify_err_noloc!(ConstAggregateVerifyErr::ElementType(
532 idx,
533 ty.disp(ctx).to_string(),
534 expected.disp(ctx).to_string()
535 ))?
536 }
537 Ok(())
538}
539
540impl Verify for SplatAttr {
541 fn verify(&self, ctx: &Context) -> Result<()> {
542 let elem_ty = self.ty.deref(ctx).elem_type();
544 verify_element(ctx, 0, self.element(), elem_ty)
545 }
546}
547
548#[pliron_attr(
550 name = "llvm.symbol_addr",
551 format = "`<@` $symbol ` : ` $ty `>`",
552 verifier = "succ"
553)]
554#[derive(PartialEq, Eq, Clone, Debug, Hash)]
555pub struct SymbolAddrAttr {
556 symbol: Identifier,
557 ty: TypedHandle<PointerType>,
558}
559
560impl SymbolAddrAttr {
561 pub fn new(symbol: Identifier, ty: TypedHandle<PointerType>) -> Self {
564 SymbolAddrAttr { symbol, ty }
565 }
566
567 pub fn symbol(&self) -> &Identifier {
569 &self.symbol
570 }
571
572 pub fn ty(&self) -> TypedHandle<PointerType> {
574 self.ty
575 }
576}
577
578#[attr_interface_impl]
579impl TypedAttrInterface for SymbolAddrAttr {
580 fn get_type(&self, _ctx: &Context) -> TypeHandle {
581 self.ty.into()
582 }
583}
584
585#[pliron_attr(
589 name = "llvm.aggregate",
590 format = "`<[` vec($elements, CharSpace(`,`)) `] : ` $ty `>`"
591)]
592#[derive(PartialEq, Eq, Clone, Debug, Hash)]
593pub struct AggregateAttr {
594 elements: Vec<Box<dyn TypedAttrInterface>>,
595 ty: TypeHandle,
596}
597
598pub const AGGREGATE_ATTR_OUTLINE_THRESHOLD: usize = 4;
600
601impl AggregateAttr {
602 pub fn new(elements: Vec<Box<dyn TypedAttrInterface>>, ty: TypeHandle) -> Self {
604 AggregateAttr { elements, ty }
605 }
606
607 pub fn elements(&self) -> &[Box<dyn TypedAttrInterface>] {
609 &self.elements
610 }
611
612 pub fn ty(&self) -> TypeHandle {
614 self.ty
615 }
616}
617
618#[attr_interface_impl]
619impl TypedAttrInterface for AggregateAttr {
620 fn get_type(&self, _ctx: &Context) -> TypeHandle {
621 self.ty
622 }
623}
624
625#[attr_interface_impl]
626impl MaterializableAttr for AggregateAttr {
627 fn materialize(&self, ctx: &mut Context) -> Ptr<Operation> {
628 ConstantOp::new(ctx, Box::new(self.clone())).get_operation()
629 }
630}
631
632#[attr_interface_impl]
633impl OutlinedAttr for AggregateAttr {
634 fn outline(&self, _ctx: &Context) -> bool {
635 self.elements.len() >= AGGREGATE_ATTR_OUTLINE_THRESHOLD
636 }
637}
638
639#[attr_interface_impl]
640impl PrintOnceAttr for AggregateAttr {}
641
642#[derive(Debug, Error)]
643pub enum ConstAggregateVerifyErr {
644 #[error("{0} is not an array, struct or vector type")]
645 NotAnAggregate(String),
646 #[error("A constant of the scalable vector type {0} must use llvm.splat")]
647 ScalableAggregate(String),
648 #[error("Type {0} has {1} element(s), but {2} were provided")]
649 NumElements(String, u64, usize),
650 #[error("Element {0} is of type {1}, but {2} was expected")]
651 ElementType(usize, String, String),
652}
653
654impl Verify for AggregateAttr {
655 fn verify(&self, ctx: &Context) -> Result<()> {
656 let ty = self.ty.deref(ctx);
657 if let Some(array_ty) = ty.downcast_ref::<ArrayType>() {
658 if array_ty.size() != self.elements.len() as u64 {
659 verify_err_noloc!(ConstAggregateVerifyErr::NumElements(
660 self.ty.disp(ctx).to_string(),
661 array_ty.size(),
662 self.elements.len()
663 ))?
664 }
665 let elem_ty = array_ty.elem_type();
666 for (idx, element) in self.elements.iter().enumerate() {
667 verify_element(ctx, idx, &**element, elem_ty)?;
668 }
669 } else if let Some(struct_ty) = ty.downcast_ref::<StructType>() {
670 if struct_ty.is_opaque() || struct_ty.num_fields() != self.elements.len() {
671 verify_err_noloc!(ConstAggregateVerifyErr::NumElements(
672 self.ty.disp(ctx).to_string(),
673 if struct_ty.is_opaque() {
674 0
675 } else {
676 struct_ty.num_fields() as u64
677 },
678 self.elements.len()
679 ))?
680 }
681 for (idx, element) in self.elements.iter().enumerate() {
682 verify_element(ctx, idx, &**element, struct_ty.field_type(idx))?;
683 }
684 } else if let Some(vector_ty) = ty.downcast_ref::<VectorType>() {
685 if vector_ty.is_scalable() {
686 verify_err_noloc!(ConstAggregateVerifyErr::ScalableAggregate(
687 self.ty.disp(ctx).to_string()
688 ))?
689 }
690 if vector_ty.num_elements() as usize != self.elements.len() {
691 verify_err_noloc!(ConstAggregateVerifyErr::NumElements(
692 self.ty.disp(ctx).to_string(),
693 vector_ty.num_elements() as u64,
694 self.elements.len()
695 ))?
696 }
697 let elem_ty = vector_ty.elem_type();
698 for (idx, element) in self.elements.iter().enumerate() {
699 verify_element(ctx, idx, &**element, elem_ty)?;
700 }
701 } else {
702 verify_err_noloc!(ConstAggregateVerifyErr::NotAnAggregate(
703 self.ty.disp(ctx).to_string()
704 ))?
705 }
706 Ok(())
707 }
708}
709
710#[pliron_attr(name = "llvm.atomic_ordering", verifier = "succ", format)]
712#[derive(PartialEq, Eq, Clone, Debug, Hash)]
713pub enum AtomicOrderingAttr {
714 Monotonic,
715 Acquire,
716 Release,
717 AcqRel,
718 SeqCst,
719}
720
721#[pliron_attr(name = "llvm.atomic_rmw_kind", verifier = "succ", format)]
723#[derive(PartialEq, Eq, Clone, Debug, Hash)]
724pub enum AtomicRmwKindAttr {
725 Xchg,
726 Add,
727 Sub,
728 And,
729 Nand,
730 Or,
731 Xor,
732 Max,
733 Min,
734 UMax,
735 UMin,
736 FAdd,
737 FSub,
738 FMax,
739 FMin,
740}
741
742#[pliron_attr(name = "llvm.sync_scope", verifier = "succ", format)]
744#[derive(PartialEq, Eq, Clone, Debug, Default, Hash)]
745pub enum SyncScopeAttr {
746 #[default]
748 System,
749 SingleThread,
751 NamedScope(StringAttr),
753}
754
755impl SyncScopeAttr {
756 pub fn to_name(&self) -> String {
759 match self {
760 SyncScopeAttr::SingleThread => "singlethread".to_string(),
761 SyncScopeAttr::System => String::new(),
762 SyncScopeAttr::NamedScope(name) => name.as_str().to_string(),
763 }
764 }
765}
766
767#[pliron_attr(
768 name = "llvm.shuffle_vector_mask",
769 format = "`[` vec($0, CharSpace(`,`)) `]`",
770 verifier = "succ"
771)]
772#[derive(PartialEq, Eq, Clone, Debug, Hash)]
773pub struct ShuffleVectorMaskAttr(pub Vec<i32>);
774
775#[cfg(test)]
776mod tests {
777 use expect_test::expect;
778 use pliron::{parsable::parse_from_str, result::ExpectOk};
779
780 use super::*;
781
782 #[test]
783 fn test_fastmath_flags_attr_empty() {
784 let flags = FastmathFlags::empty();
785 assert_eq!(flags.bits(), 0);
786
787 let ctx = &mut Context::default();
788 let flags_attr: FastmathFlagsAttr = flags.into();
789 expect!["<>"].assert_eq(&flags_attr.disp(ctx).to_string());
790
791 let parsed = parse_from_str(FastmathFlagsAttr::parser(()), ctx, "<>").expect_ok(ctx);
792 assert_eq!(parsed, flags_attr);
793 }
794
795 #[test]
796 fn test_fastmath_flags_attr_set_flags() {
797 let mut flags = FastmathFlags::empty();
798 flags |= FastmathFlags::NNAN | FastmathFlags::NINF;
799 assert!(flags.contains(FastmathFlags::NNAN));
800 assert!(flags.contains(FastmathFlags::NINF));
801 assert!(!flags.contains(FastmathFlags::NSZ));
802 }
803
804 #[test]
805 fn test_fastmath_flags_attr_fmt() {
806 let ctx = &Context::default();
807 let flags: FastmathFlagsAttr = (FastmathFlags::NNAN | FastmathFlags::ARCP).into();
808 expect!["<NNAN | ARCP>"].assert_eq(&flags.disp(ctx).to_string());
809 }
810
811 #[test]
812 fn test_fastmath_flags_attr_fmt_fast() {
813 let ctx = &Context::default();
814 let flags: FastmathFlagsAttr = FastmathFlags::FAST.into();
815 expect!["<NNAN | NINF | NSZ | ARCP | CONTRACT | AFN | REASSOC>"]
816 .assert_eq(&flags.disp(ctx).to_string());
817 }
818
819 #[test]
820 fn test_fastmath_flags_attr_parse_valid() {
821 let ctx = &mut Context::default();
822
823 let parsed =
824 parse_from_str(FastmathFlagsAttr::parser(()), ctx, "<NNAN | ARCP>").expect_ok(ctx);
825 assert!(parsed.0.contains(FastmathFlags::NNAN));
826 assert!(parsed.0.contains(FastmathFlags::ARCP));
827 }
828
829 #[test]
831 fn test_fastmath_flags_attr_parse_fast() {
832 let ctx = &mut Context::default();
833
834 let parsed = parse_from_str(FastmathFlagsAttr::parser(()), ctx, "<FAST>").expect_ok(ctx);
835 assert!(parsed.0.contains(FastmathFlags::FAST));
836
837 assert!(parsed.0.contains(FastmathFlags::NNAN));
839 assert!(parsed.0.contains(FastmathFlags::NINF));
840 assert!(parsed.0.contains(FastmathFlags::NSZ));
841 assert!(parsed.0.contains(FastmathFlags::ARCP));
842 assert!(parsed.0.contains(FastmathFlags::CONTRACT));
843 assert!(parsed.0.contains(FastmathFlags::REASSOC));
844 }
845
846 #[test]
847 fn test_fastmath_flags_attr_parse_invalid() {
848 let ctx = &mut Context::default();
849 let input = "<INVALIDFLAG>";
850 match parse_from_str(FastmathFlagsAttr::parser(()), ctx, input) {
851 Ok(parsed) => {
852 panic!("Expected error, but got: {}", parsed);
853 }
854 Err(e) => {
855 expect![[r#"
856 Compilation error: invalid input program.
857 Parse error at line: 1, column: 1
858 Error parsing fastmath flags: unrecognized named flag `INVALIDFLAG`
859 "#]]
860 .assert_eq(&e.to_string());
861 }
862 }
863 }
864
865 #[test]
866 fn test_gep_no_wrap_flags_attr_fmt() {
867 let ctx = &Context::default();
868
869 let flags: GepNoWrapFlagsAttr = (GepNoWrapFlags::NUSW | GepNoWrapFlags::NUW).into();
870 expect!["<NUSW | NUW>"].assert_eq(&flags.disp(ctx).to_string());
871
872 let flags: GepNoWrapFlagsAttr = (GepNoWrapFlags::INBOUNDS | GepNoWrapFlags::NUW).into();
873 expect!["<INBOUNDS | NUW>"].assert_eq(&flags.disp(ctx).to_string());
874 }
875
876 #[test]
877 fn test_gep_no_wrap_flags_inbounds_implies_nusw() {
878 let flags: GepNoWrapFlagsAttr = GepNoWrapFlags::INBOUNDS.into();
879
880 assert!(flags.0.contains(GepNoWrapFlags::INBOUNDS));
881 assert!(flags.0.contains(GepNoWrapFlags::NUSW));
882 }
883
884 #[test]
885 fn test_gep_no_wrap_flags_attr_parse_valid() {
886 let ctx = &mut Context::default();
887
888 let parsed =
889 parse_from_str(GepNoWrapFlagsAttr::parser(()), ctx, "<INBOUNDS | NUW>").expect_ok(ctx);
890 assert!(parsed.0.contains(GepNoWrapFlags::INBOUNDS));
891 assert!(parsed.0.contains(GepNoWrapFlags::NUSW));
892 assert!(parsed.0.contains(GepNoWrapFlags::NUW));
893 }
894
895 #[test]
896 fn test_gep_no_wrap_flags_attr_parse_invalid() {
897 let ctx = &mut Context::default();
898 let input = "<INVALIDFLAG>";
899
900 let err = parse_from_str(GepNoWrapFlagsAttr::parser(()), ctx, input)
901 .expect_err("invalid GEP no-wrap flag must fail to parse");
902 expect![[r#"
903 Compilation error: invalid input program.
904 Parse error at line: 1, column: 1
905 Error parsing GEP no-wrap flags: unrecognized named flag `INVALIDFLAG`
906 "#]]
907 .assert_eq(&err.to_string());
908 }
909
910 fn assert_attr_roundtrips<A>(ctx: &mut Context, attr: A)
911 where
912 A: Parsable<Arg = (), Parsed = A> + Printable + PartialEq + core::fmt::Debug,
913 {
914 let printed = attr.disp(ctx).to_string();
915 let parsed = parse_from_str(A::parser(()), ctx, &printed).expect_ok(ctx);
916 assert_eq!(parsed, attr, "round-trip mismatch for `{printed}`");
917 }
918
919 #[test]
920 fn test_atomic_ordering_attr_roundtrip() {
921 let ctx = &mut Context::default();
922 for ordering in [
923 AtomicOrderingAttr::Monotonic,
924 AtomicOrderingAttr::Acquire,
925 AtomicOrderingAttr::Release,
926 AtomicOrderingAttr::AcqRel,
927 AtomicOrderingAttr::SeqCst,
928 ] {
929 assert_attr_roundtrips(ctx, ordering);
930 }
931 }
932
933 #[test]
934 fn test_atomic_rmw_kind_attr_roundtrip() {
935 let ctx = &mut Context::default();
936 for kind in [
937 AtomicRmwKindAttr::Xchg,
938 AtomicRmwKindAttr::Add,
939 AtomicRmwKindAttr::Sub,
940 AtomicRmwKindAttr::And,
941 AtomicRmwKindAttr::Nand,
942 AtomicRmwKindAttr::Or,
943 AtomicRmwKindAttr::Xor,
944 AtomicRmwKindAttr::Max,
945 AtomicRmwKindAttr::Min,
946 AtomicRmwKindAttr::UMax,
947 AtomicRmwKindAttr::UMin,
948 AtomicRmwKindAttr::FAdd,
949 AtomicRmwKindAttr::FSub,
950 AtomicRmwKindAttr::FMax,
951 AtomicRmwKindAttr::FMin,
952 ] {
953 assert_attr_roundtrips(ctx, kind);
954 }
955 }
956
957 #[test]
958 fn test_sync_scope_attr_roundtrip() {
959 let ctx = &mut Context::default();
960 for scope in [
961 SyncScopeAttr::SingleThread,
962 SyncScopeAttr::System,
963 SyncScopeAttr::NamedScope(StringAttr::new("device".to_string())),
964 ] {
965 assert_attr_roundtrips(ctx, scope);
966 }
967 }
968
969 #[test]
970 fn test_address_space_attr_roundtrip() {
971 let ctx = &mut Context::default();
972 for n in [0u32, 1, 3, 5, 7] {
973 assert_attr_roundtrips(ctx, AddressSpaceAttr(n));
974 }
975 }
976
977 #[test]
978 fn test_fp_half_attr_roundtrip() {
979 use pliron::{builtin::attributes::FPHalfAttr, utils::apfloat::Half};
980 let ctx = &mut Context::default();
981 for s in ["0.0", "1.5", "-2.25"] {
982 let value: Half = s.parse().expect("valid half literal");
983 assert_attr_roundtrips(ctx, FPHalfAttr(value));
984 }
985 }
986
987 #[test]
988 fn test_fp_bfloat_attr_roundtrip() {
989 use pliron::{builtin::attributes::FPBFloatAttr, utils::apfloat::BFloat};
990 let ctx = &mut Context::default();
991 for s in ["0.0", "1.5", "-2.25"] {
992 let value: BFloat = s.parse().expect("valid bfloat literal");
993 assert_attr_roundtrips(ctx, FPBFloatAttr(value));
994 }
995 }
996}
997
998dict_key!(
999 ATTR_KEY_LLVM_DATA_LAYOUT,
1001 "llvm_data_layout"
1002);
1003
1004dict_key!(
1005 ATTR_KEY_LLVM_TARGET_TRIPLE,
1007 "llvm_target_triple"
1008);
1009
1010pub fn get_data_layout(ctx: &Context, module: ModuleOp) -> Option<String> {
1012 module
1013 .get_operation()
1014 .deref(ctx)
1015 .attributes
1016 .get::<StringAttr>(&ATTR_KEY_LLVM_DATA_LAYOUT)
1017 .map(|attr| attr.clone().into())
1018}
1019
1020pub fn set_data_layout(ctx: &Context, module: ModuleOp, data_layout: String) {
1022 module.get_operation().deref_mut(ctx).attributes.set(
1023 ATTR_KEY_LLVM_DATA_LAYOUT.clone(),
1024 StringAttr::new(data_layout),
1025 );
1026}
1027
1028pub fn get_target_triple(ctx: &Context, module: ModuleOp) -> Option<String> {
1030 module
1031 .get_operation()
1032 .deref(ctx)
1033 .attributes
1034 .get::<StringAttr>(&ATTR_KEY_LLVM_TARGET_TRIPLE)
1035 .map(|attr| attr.clone().into())
1036}
1037
1038pub fn set_target_triple(ctx: &Context, module: ModuleOp, target_triple: String) {
1040 module.get_operation().deref_mut(ctx).attributes.set(
1041 ATTR_KEY_LLVM_TARGET_TRIPLE.clone(),
1042 StringAttr::new(target_triple),
1043 );
1044}