Skip to main content

pliron_llvm/
attributes.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) The pliron contributors
3
4//! Attributes belonging to the LLVM dialect.
5
6use 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/// Integer overflow flags for arithmetic operations.
26/// The description below is from LLVM's
27/// [release notes](https://releases.llvm.org/2.6/docs/ReleaseNotes.html)
28/// that added the flags.
29/// "nsw" and "nuw" bits indicate that the operation is guaranteed to not overflow
30/// (in the signed or unsigned case, respectively). This gives the optimizer more information
31///  and can be used for things like C signed integer values, which are undefined on overflow.
32#[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    /// Fast math flags for floating point operations.
41    #[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/// An index for a GEP can be either a constant or an SSA operand.
160/// Contrary to its name, this isn't an [Attribute][pliron::attribute::Attribute].
161#[derive(PartialEq, Eq, Clone, Debug, Hash)]
162#[format]
163pub enum GepIndexAttr {
164    /// This GEP index is a raw u32 compile time constant
165    Constant(u32),
166    /// This GEP Index is the SSA value in the containing
167    /// [Operation](pliron::operation::Operation)s `operands[idx]`
168    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/// An attribute that contains a list of case values for a switch operation.
180#[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/// Address space of a pointer or global, corresponding to LLVM's `addrspace(N)`.
239#[pliron_attr(name = "llvm.addrspace", format = "$0", verifier = "succ")]
240#[derive(PartialEq, Eq, Clone, Debug, Hash)]
241pub struct AddressSpaceAttr(pub u32);
242
243/// Memory ordering for an atomic operation (`atomicrmw` / `cmpxchg` / `fence` /
244/// atomic `load` / `store`).
245#[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/// The kind of an LLVM `atomicrmw` operation.
256#[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 input with FAST flag set
339    #[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        // FAST also means all the other flags.
347        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}