1use core::fmt::Display;
7use thiserror::Error;
8
9use pliron::{
10 builtin::attributes::IntegerAttr,
11 combine::{self, Parser, choice, parser::char::spaces},
12 common_traits::Verify,
13 context::Context,
14 derive::{format, pliron_attr},
15 impl_printable_for_display, input_error,
16 location::Located,
17 parsable::{IntoParseResult, Parsable},
18 printable::Printable,
19 result::Result,
20 verify_err_noloc,
21};
22
23use bitflags::bitflags;
24
25#[pliron_attr(name = "llvm.integer_overlflow_flags", format, verifier = "succ")]
33#[derive(PartialEq, Eq, Clone, Debug, Default, Hash)]
34pub struct IntegerOverflowFlagsAttr {
35 pub nsw: bool,
36 pub nuw: bool,
37}
38
39bitflags! {
40 #[derive(PartialEq, Eq, Clone, Debug, Hash, Copy)]
42 pub struct FastmathFlags: u8 {
43 const NNAN = 1;
44 const NINF = 2;
45 const NSZ = 4;
46 const ARCP = 8;
47 const CONTRACT = 16;
48 const AFN = 32;
49 const REASSOC = 64;
50 const FAST = 127;
51 }
52}
53
54#[pliron_attr(name = "llvm.fast_math_flags", verifier = "succ")]
55#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
56pub struct FastmathFlagsAttr(pub FastmathFlags);
57
58impl Default for FastmathFlagsAttr {
59 fn default() -> Self {
60 FastmathFlagsAttr(FastmathFlags::empty())
61 }
62}
63
64impl From<FastmathFlags> for FastmathFlagsAttr {
65 fn from(value: FastmathFlags) -> Self {
66 FastmathFlagsAttr(value)
67 }
68}
69
70impl From<FastmathFlagsAttr> for FastmathFlags {
71 fn from(attr: FastmathFlagsAttr) -> Self {
72 attr.0
73 }
74}
75
76impl Display for FastmathFlagsAttr {
77 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
78 write!(f, "<")?;
79 bitflags::parser::to_writer(&self.0, &mut *f)?;
80 write!(f, ">")
81 }
82}
83
84impl_printable_for_display!(FastmathFlagsAttr);
85
86#[derive(Debug, Error)]
87#[error("Error parsing fastmath flags: {0}")]
88pub struct FastmathFlagParseErr(pub bitflags::parser::ParseError);
89
90impl Parsable for FastmathFlagsAttr {
91 type Arg = ();
92
93 type Parsed = Self;
94
95 fn parse<'a>(
96 state_stream: &mut pliron::parsable::StateStream<'a>,
97 _arg: Self::Arg,
98 ) -> pliron::parsable::ParseResult<'a, Self::Parsed> {
99 let pos = state_stream.loc();
100 let allowed_chars = combine::choice!(
101 combine::parser::char::space().map(|c| c.to_string()),
102 combine::parser::char::alpha_num().map(|c| c.to_string()),
103 combine::parser::char::char('|').map(|c: char| c.to_string())
104 );
105
106 let (parsed, _): (Vec<String>, _) = combine::between(
107 combine::parser::char::char('<').with(spaces()),
108 spaces().with(combine::parser::char::char('>')),
109 combine::many(allowed_chars),
110 )
111 .parse_stream(state_stream)
112 .into_result()?;
113 let parsed_string = parsed.concat();
114
115 let (fast_math_flags, _) = bitflags::parser::from_str::<FastmathFlags>(&parsed_string)
116 .map_err(|e| input_error!(pos.clone(), FastmathFlagParseErr(e)))
117 .into_parse_result()?;
118
119 Ok(FastmathFlagsAttr(fast_math_flags)).into_parse_result()
120 }
121}
122
123#[pliron_attr(name = "llvm.icmp_predicate", verifier = "succ", format)]
124#[derive(PartialEq, Eq, Clone, Debug, Hash)]
125pub enum ICmpPredicateAttr {
126 EQ,
127 NE,
128 SLT,
129 SLE,
130 SGT,
131 SGE,
132 ULT,
133 ULE,
134 UGT,
135 UGE,
136}
137
138#[pliron_attr(name = "llvm.fcmp_predicate", format, verifier = "succ")]
139#[derive(PartialEq, Eq, Clone, Debug, Hash)]
140pub enum FCmpPredicateAttr {
141 False,
142 OEQ,
143 OGT,
144 OGE,
145 OLT,
146 OLE,
147 ONE,
148 ORD,
149 UEQ,
150 UGT,
151 UGE,
152 ULT,
153 ULE,
154 UNE,
155 UNO,
156 True,
157}
158
159#[derive(PartialEq, Eq, Clone, Debug, Hash)]
162#[format]
163pub enum GepIndexAttr {
164 Constant(u32),
166 OperandIdx(usize),
169}
170
171#[pliron_attr(
172 name = "llvm.gep_indices",
173 format = "`[` vec($0, CharSpace(`,`)) `]`",
174 verifier = "succ"
175)]
176#[derive(PartialEq, Eq, Clone, Debug, Hash)]
177pub struct GepIndicesAttr(pub Vec<GepIndexAttr>);
178
179#[pliron_attr(name = "llvm.case_values", format = "`[` vec($0, CharSpace(`,`)) `]`")]
181#[derive(PartialEq, Eq, Clone, Debug, Hash)]
182pub struct CaseValuesAttr(pub Vec<IntegerAttr>);
183
184#[derive(Debug, Error)]
185#[error("Case values must be of the same type, but found different types: {0} and {1}")]
186pub struct CaseValuesAttrVerifyErr(pub String, pub String);
187
188impl Verify for CaseValuesAttr {
189 fn verify(&self, ctx: &Context) -> Result<()> {
190 self.0.windows(2).try_for_each(|pair| {
191 pair[0].verify(ctx)?;
192 if pair[0].get_type() != pair[1].get_type() {
193 verify_err_noloc!(CaseValuesAttrVerifyErr(
194 pair[0].get_type().disp(ctx).to_string(),
195 pair[1].get_type().disp(ctx).to_string()
196 ))
197 } else {
198 Ok(())
199 }
200 })
201 }
202}
203
204#[pliron_attr(name = "llvm.linkage", format, verifier = "succ")]
205#[derive(PartialEq, Eq, Clone, Debug, Hash)]
206pub enum LinkageAttr {
207 ExternalLinkage,
208 AvailableExternallyLinkage,
209 LinkOnceAnyLinkage,
210 LinkOnceODRLinkage,
211 LinkOnceODRAutoHideLinkage,
212 WeakAnyLinkage,
213 WeakODRLinkage,
214 AppendingLinkage,
215 InternalLinkage,
216 PrivateLinkage,
217 DLLImportLinkage,
218 DLLExportLinkage,
219 ExternalWeakLinkage,
220 GhostLinkage,
221 CommonLinkage,
222 LinkerPrivateLinkage,
223 LinkerPrivateWeakLinkage,
224}
225
226#[pliron_attr(
227 name = "llvm.insert_extract_value_indices",
228 format = "`[` vec($0, CharSpace(`,`)) `]`",
229 verifier = "succ"
230)]
231#[derive(PartialEq, Eq, Clone, Debug, Hash)]
232pub struct InsertExtractValueIndicesAttr(pub Vec<u32>);
233
234#[pliron_attr(name = "llvm.align", format = "$0", verifier = "succ")]
235#[derive(PartialEq, Eq, Clone, Debug, Hash)]
236pub struct AlignmentAttr(pub u32);
237
238#[pliron_attr(name = "llvm.addrspace", format = "$0", verifier = "succ")]
240#[derive(PartialEq, Eq, Clone, Debug, Hash)]
241pub struct AddressSpaceAttr(pub u32);
242
243#[pliron_attr(name = "llvm.atomic_ordering", verifier = "succ", format)]
246#[derive(PartialEq, Eq, Clone, Debug, Hash)]
247pub enum AtomicOrderingAttr {
248 Monotonic,
249 Acquire,
250 Release,
251 AcqRel,
252 SeqCst,
253}
254
255#[pliron_attr(name = "llvm.atomic_rmw_kind", verifier = "succ", format)]
257#[derive(PartialEq, Eq, Clone, Debug, Hash)]
258pub enum AtomicRmwKindAttr {
259 Xchg,
260 Add,
261 Sub,
262 And,
263 Nand,
264 Or,
265 Xor,
266 Max,
267 Min,
268 UMax,
269 UMin,
270 FAdd,
271 FSub,
272 FMax,
273 FMin,
274}
275
276#[pliron_attr(
277 name = "llvm.shuffle_vector_mask",
278 format = "`[` vec($0, CharSpace(`,`)) `]`",
279 verifier = "succ"
280)]
281#[derive(PartialEq, Eq, Clone, Debug, Hash)]
282pub struct ShuffleVectorMaskAttr(pub Vec<i32>);
283
284#[cfg(test)]
285mod tests {
286 use expect_test::expect;
287 use pliron::{parsable::parse_from_str, result::ExpectOk};
288
289 use super::*;
290
291 #[test]
292 fn test_fastmath_flags_attr_empty() {
293 let flags = FastmathFlags::empty();
294 assert_eq!(flags.bits(), 0);
295
296 let ctx = &mut Context::default();
297 let flags_attr: FastmathFlagsAttr = flags.into();
298 expect!["<>"].assert_eq(&flags_attr.disp(ctx).to_string());
299
300 let parsed = parse_from_str(FastmathFlagsAttr::parser(()), ctx, "<>").expect_ok(ctx);
301 assert_eq!(parsed, flags_attr);
302 }
303
304 #[test]
305 fn test_fastmath_flags_attr_set_flags() {
306 let mut flags = FastmathFlags::empty();
307 flags |= FastmathFlags::NNAN | FastmathFlags::NINF;
308 assert!(flags.contains(FastmathFlags::NNAN));
309 assert!(flags.contains(FastmathFlags::NINF));
310 assert!(!flags.contains(FastmathFlags::NSZ));
311 }
312
313 #[test]
314 fn test_fastmath_flags_attr_fmt() {
315 let ctx = &Context::default();
316 let flags: FastmathFlagsAttr = (FastmathFlags::NNAN | FastmathFlags::ARCP).into();
317 expect!["<NNAN | ARCP>"].assert_eq(&flags.disp(ctx).to_string());
318 }
319
320 #[test]
321 fn test_fastmath_flags_attr_fmt_fast() {
322 let ctx = &Context::default();
323 let flags: FastmathFlagsAttr = FastmathFlags::FAST.into();
324 expect!["<NNAN | NINF | NSZ | ARCP | CONTRACT | AFN | REASSOC>"]
325 .assert_eq(&flags.disp(ctx).to_string());
326 }
327
328 #[test]
329 fn test_fastmath_flags_attr_parse_valid() {
330 let ctx = &mut Context::default();
331
332 let parsed =
333 parse_from_str(FastmathFlagsAttr::parser(()), ctx, "<NNAN | ARCP>").expect_ok(ctx);
334 assert!(parsed.0.contains(FastmathFlags::NNAN));
335 assert!(parsed.0.contains(FastmathFlags::ARCP));
336 }
337
338 #[test]
340 fn test_fastmath_flags_attr_parse_fast() {
341 let ctx = &mut Context::default();
342
343 let parsed = parse_from_str(FastmathFlagsAttr::parser(()), ctx, "<FAST>").expect_ok(ctx);
344 assert!(parsed.0.contains(FastmathFlags::FAST));
345
346 assert!(parsed.0.contains(FastmathFlags::NNAN));
348 assert!(parsed.0.contains(FastmathFlags::NINF));
349 assert!(parsed.0.contains(FastmathFlags::NSZ));
350 assert!(parsed.0.contains(FastmathFlags::ARCP));
351 assert!(parsed.0.contains(FastmathFlags::CONTRACT));
352 assert!(parsed.0.contains(FastmathFlags::REASSOC));
353 }
354
355 #[test]
356 fn test_fastmath_flags_attr_parse_invalid() {
357 let ctx = &mut Context::default();
358 let input = "<INVALIDFLAG>";
359 match parse_from_str(FastmathFlagsAttr::parser(()), ctx, input) {
360 Ok(parsed) => {
361 panic!("Expected error, but got: {}", parsed);
362 }
363 Err(e) => {
364 expect![[r#"
365 Compilation error: invalid input program.
366 Parse error at line: 1, column: 1
367 Error parsing fastmath flags: unrecognized named flag `INVALIDFLAG`
368 "#]]
369 .assert_eq(&e.to_string());
370 }
371 }
372 }
373
374 fn assert_attr_roundtrips<A>(ctx: &mut Context, attr: A)
375 where
376 A: Parsable<Arg = (), Parsed = A> + Printable + PartialEq + core::fmt::Debug,
377 {
378 let printed = attr.disp(ctx).to_string();
379 let parsed = parse_from_str(A::parser(()), ctx, &printed).expect_ok(ctx);
380 assert_eq!(parsed, attr, "round-trip mismatch for `{printed}`");
381 }
382
383 #[test]
384 fn test_atomic_ordering_attr_roundtrip() {
385 let ctx = &mut Context::default();
386 for ordering in [
387 AtomicOrderingAttr::Monotonic,
388 AtomicOrderingAttr::Acquire,
389 AtomicOrderingAttr::Release,
390 AtomicOrderingAttr::AcqRel,
391 AtomicOrderingAttr::SeqCst,
392 ] {
393 assert_attr_roundtrips(ctx, ordering);
394 }
395 }
396
397 #[test]
398 fn test_atomic_rmw_kind_attr_roundtrip() {
399 let ctx = &mut Context::default();
400 for kind in [
401 AtomicRmwKindAttr::Xchg,
402 AtomicRmwKindAttr::Add,
403 AtomicRmwKindAttr::Sub,
404 AtomicRmwKindAttr::And,
405 AtomicRmwKindAttr::Nand,
406 AtomicRmwKindAttr::Or,
407 AtomicRmwKindAttr::Xor,
408 AtomicRmwKindAttr::Max,
409 AtomicRmwKindAttr::Min,
410 AtomicRmwKindAttr::UMax,
411 AtomicRmwKindAttr::UMin,
412 AtomicRmwKindAttr::FAdd,
413 AtomicRmwKindAttr::FSub,
414 AtomicRmwKindAttr::FMax,
415 AtomicRmwKindAttr::FMin,
416 ] {
417 assert_attr_roundtrips(ctx, kind);
418 }
419 }
420
421 #[test]
422 fn test_address_space_attr_roundtrip() {
423 let ctx = &mut Context::default();
424 for n in [0u32, 1, 3, 5, 7] {
425 assert_attr_roundtrips(ctx, AddressSpaceAttr(n));
426 }
427 }
428
429 #[test]
430 fn test_fp_half_attr_roundtrip() {
431 use pliron::{builtin::attributes::FPHalfAttr, utils::apfloat::Half};
432 let ctx = &mut Context::default();
433 for s in ["0.0", "1.5", "-2.25"] {
434 let value: Half = s.parse().expect("valid half literal");
435 assert_attr_roundtrips(ctx, FPHalfAttr(value));
436 }
437 }
438}