Skip to main content

pliron_arith/attributes/
mod.rs

1//! Attributes and enums used by `arith` operations.
2//!
3//! Enum and flag attributes carry valid Rust values, so their standalone
4//! verification is infallible. Operation verification checks the contextual
5//! rules, such as whether a predicate matches the operand element type.
6
7use pliron::{
8    combine::Parser,
9    context::Context,
10    derive::pliron_attr,
11    identifier::Identifier,
12    irfmt::parsers::delimited_list_parser,
13    parsable::{IntoParseResult, Parsable, ParseResult, StateStream, StreamLocation},
14    printable::{self, Printable},
15};
16use std::fmt;
17
18mod enums;
19pub use enums::{EnumParseError, EnumValueError};
20
21/// Predicate of `arith.cmpf`, with MLIR's numeric encoding.
22///
23/// Ordered predicates (`o*`) are false when either operand is NaN; unordered
24/// predicates (`u*`) are true in that case.
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26#[repr(u32)]
27pub enum CmpFPredicate {
28    /// Always false.
29    AlwaysFalse = 0,
30    /// Ordered and equal.
31    Oeq = 1,
32    /// Ordered and greater than.
33    Ogt = 2,
34    /// Ordered and greater than or equal.
35    Oge = 3,
36    /// Ordered and less than.
37    Olt = 4,
38    /// Ordered and less than or equal.
39    Ole = 5,
40    /// Ordered and not equal.
41    One = 6,
42    /// Ordered: neither operand is NaN.
43    Ord = 7,
44    /// Unordered or equal.
45    Ueq = 8,
46    /// Unordered or greater than.
47    Ugt = 9,
48    /// Unordered or greater than or equal.
49    Uge = 10,
50    /// Unordered or less than.
51    Ult = 11,
52    /// Unordered or less than or equal.
53    Ule = 12,
54    /// Unordered or not equal.
55    Une = 13,
56    /// Unordered: either operand is NaN.
57    Uno = 14,
58    /// Always true.
59    AlwaysTrue = 15,
60}
61
62/// Predicate of `arith.cmpi`, with MLIR's numeric encoding.
63#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
64#[repr(u32)]
65pub enum CmpIPredicate {
66    /// Equal.
67    Eq = 0,
68    /// Not equal.
69    Ne = 1,
70    /// Signed less than.
71    Slt = 2,
72    /// Signed less than or equal.
73    Sle = 3,
74    /// Signed greater than.
75    Sgt = 4,
76    /// Signed greater than or equal.
77    Sge = 5,
78    /// Unsigned less than.
79    Ult = 6,
80    /// Unsigned less than or equal.
81    Ule = 7,
82    /// Unsigned greater than.
83    Ugt = 8,
84    /// Unsigned greater than or equal.
85    Uge = 9,
86}
87
88/// IEEE-754 rounding mode (`roundingmode`), with MLIR's numeric encoding.
89///
90/// Matches `llvm::RoundingMode`. When absent, operations round to nearest even.
91#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
92#[repr(u32)]
93pub enum RoundingMode {
94    /// Round to nearest, ties to even.
95    ToNearestEven = 0,
96    /// Round toward negative infinity.
97    Downward = 1,
98    /// Round toward positive infinity.
99    Upward = 2,
100    /// Round toward zero.
101    TowardZero = 3,
102    /// Round to nearest, ties away from zero.
103    ToNearestAway = 4,
104}
105
106/// Rounding mode stored on `addf`, `subf`, `mulf`, `divf`, `truncf`,
107/// `convertf`, and `scaling_truncf`.
108#[pliron_attr(name = "arith.roundingmode", format = "$value", verifier = "succ")]
109#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
110pub struct RoundingModeAttr {
111    /// Rounding applied to inexact results.
112    pub value: RoundingMode,
113}
114
115impl RoundingModeAttr {
116    /// Creates an attribute for the given rounding mode.
117    pub const fn new(value: RoundingMode) -> Self {
118        Self { value }
119    }
120}
121
122/// Typed predicate attribute stored on `arith.cmpf`.
123#[pliron_attr(name = "arith.cmpf_predicate", format = "$value", verifier = "succ")]
124#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125pub struct CmpFPredicateAttr {
126    /// Comparison applied to each element pair.
127    pub value: CmpFPredicate,
128}
129
130impl CmpFPredicateAttr {
131    /// Creates an attribute for the given predicate.
132    pub const fn new(value: CmpFPredicate) -> Self {
133        Self { value }
134    }
135}
136
137/// Typed predicate attribute stored on `arith.cmpi`.
138#[pliron_attr(name = "arith.cmpi_predicate", format = "$value", verifier = "succ")]
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
140pub struct CmpIPredicateAttr {
141    /// Comparison applied to each element pair.
142    pub value: CmpIPredicate,
143}
144
145impl CmpIPredicateAttr {
146    /// Creates an attribute for the given predicate.
147    pub const fn new(value: CmpIPredicate) -> Self {
148        Self { value }
149    }
150}
151
152macro_rules! flag_set {
153    (
154        $(#[$meta:meta])*
155        $type:ident, $name:literal, $separator:literal, $all:expr,
156        { $($(#[$fmeta:meta])* $flag:ident = $bit:literal => $spelling:literal),+ $(,)? }
157    ) => {
158        $(#[$meta])*
159        #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
160        pub struct $type {
161            bits: u8,
162        }
163
164        impl $type {
165            /// Upstream MLIR attribute mnemonic, without the dialect prefix.
166            pub const NAME: &'static str = $name;
167            /// No flags set.
168            pub const NONE: Self = Self { bits: 0 };
169            $(
170                $(#[$fmeta])*
171                pub const $flag: Self = Self { bits: $bit };
172            )+
173            const ALL_BITS: u8 = $all;
174
175            /// Returns the raw bit mask, matching MLIR's encoding.
176            pub const fn bits(self) -> u8 {
177                self.bits
178            }
179
180            /// Creates a set from a raw mask, rejecting undefined bits.
181            pub const fn from_bits(bits: u8) -> Option<Self> {
182                if bits & !Self::ALL_BITS == 0 {
183                    Some(Self { bits })
184                } else {
185                    None
186                }
187            }
188
189            /// Returns whether no flags are set.
190            pub const fn is_none(self) -> bool {
191                self.bits == 0
192            }
193
194            /// Returns whether every flag in `other` is set.
195            pub const fn contains(self, other: Self) -> bool {
196                self.bits & other.bits == other.bits
197            }
198
199            /// Returns the union of two sets.
200            #[must_use]
201            pub const fn union(self, other: Self) -> Self {
202                Self { bits: self.bits | other.bits }
203            }
204
205            fn names(self) -> Vec<&'static str> {
206                let mut names = Vec::new();
207                $(
208                    if self.contains(Self::$flag) {
209                        names.push($spelling);
210                    }
211                )+
212                names
213            }
214
215            /// Returns the comma-separated MLIR spelling, or `none`.
216            pub fn mlir_spelling(self) -> String {
217                if self.is_none() {
218                    "none".to_owned()
219                } else {
220                    self.names().join($separator)
221                }
222            }
223
224            fn from_name(name: &str) -> Result<Self, EnumParseError> {
225                match name {
226                    "none" => Ok(Self::NONE),
227                    $($spelling => Ok(Self::$flag),)+
228                    _ => Err(EnumParseError {
229                        enum_name: $name,
230                        spelling: name.to_owned(),
231                    }),
232                }
233            }
234        }
235
236        impl std::ops::BitOr for $type {
237            type Output = Self;
238
239            fn bitor(self, rhs: Self) -> Self {
240                self.union(rhs)
241            }
242        }
243
244        impl fmt::Display for $type {
245            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
246                formatter.write_str(&self.mlir_spelling())
247            }
248        }
249
250        impl Printable for $type {
251            fn fmt(
252                &self,
253                _: &Context,
254                _: &printable::State,
255                formatter: &mut fmt::Formatter<'_>,
256            ) -> fmt::Result {
257                let names = if self.is_none() { vec!["none"] } else { self.names() };
258                write!(formatter, "<{}>", names.join(","))
259            }
260        }
261
262        impl Parsable for $type {
263            type Arg = ();
264            type Parsed = Self;
265
266            fn parse<'a>(
267                state_stream: &mut StateStream<'a>,
268                _: Self::Arg,
269            ) -> ParseResult<'a, Self::Parsed> {
270                let loc = state_stream.loc();
271                let names = delimited_list_parser('<', '>', ',', Identifier::parser(()))
272                    .parse_stream(state_stream)
273                    .into_result()?
274                    .0;
275                let mut flags = Self::NONE;
276                for name in names {
277                    match Self::from_name(&name.to_string()) {
278                        Ok(flag) => flags = flags.union(flag),
279                        Err(error) => return pliron::input_err!(loc, error).into_parse_result(),
280                    }
281                }
282                Ok(flags).into_parse_result()
283            }
284        }
285    };
286}
287
288flag_set!(
289    /// Floating-point fast-math flags (`#arith.fastmath`).
290    ///
291    /// Each flag relaxes IEEE-754 semantics to permit additional optimization.
292    FastMathFlags, "fastmath", ",", 0x7f, {
293        /// Allow reassociation.
294        REASSOC = 1 => "reassoc",
295        /// Assume no NaN operands or results.
296        NNAN = 2 => "nnan",
297        /// Assume no infinite operands or results.
298        NINF = 4 => "ninf",
299        /// Ignore the sign of zero.
300        NSZ = 8 => "nsz",
301        /// Allow reciprocal approximation.
302        ARCP = 16 => "arcp",
303        /// Allow floating-point contraction such as fused multiply-add.
304        CONTRACT = 32 => "contract",
305        /// Allow approximate library functions.
306        AFN = 64 => "afn",
307    }
308);
309
310impl FastMathFlags {
311    /// Every fast-math flag (`fast`).
312    pub const FAST: Self = Self { bits: 0x7f };
313}
314
315flag_set!(
316    /// Integer overflow flags (`#arith.overflow`).
317    ///
318    /// Setting a flag makes the corresponding overflow produce poison.
319    IntegerOverflowFlags, "overflow", ", ", 0x3, {
320        /// No signed wrap.
321        NSW = 1 => "nsw",
322        /// No unsigned wrap.
323        NUW = 2 => "nuw",
324    }
325);
326
327/// Fast-math flags stored on floating-point operations.
328#[pliron_attr(name = "arith.fastmath", format = "$value", verifier = "succ")]
329#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
330pub struct FastMathFlagsAttr {
331    /// Flag set.
332    pub value: FastMathFlags,
333}
334
335impl FastMathFlagsAttr {
336    /// Creates an attribute for the given flags.
337    pub const fn new(value: FastMathFlags) -> Self {
338        Self { value }
339    }
340}
341
342/// Integer overflow flags stored on `addi`, `subi`, `muli`, and `shli`.
343#[pliron_attr(name = "arith.overflow", format = "$value", verifier = "succ")]
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
345pub struct IntegerOverflowFlagsAttr {
346    /// Flag set.
347    pub value: IntegerOverflowFlags,
348}
349
350impl IntegerOverflowFlagsAttr {
351    /// Creates an attribute for the given flags.
352    pub const fn new(value: IntegerOverflowFlags) -> Self {
353        Self { value }
354    }
355}