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 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/// Integer overflow flags for arithmetic operations.
49/// The description below is from LLVM's
50/// [release notes](https://releases.llvm.org/2.6/docs/ReleaseNotes.html)
51/// that added the flags.
52/// "nsw" and "nuw" bits indicate that the operation is guaranteed to not overflow
53/// (in the signed or unsigned case, respectively). This gives the optimizer more information
54///  and can be used for things like C signed integer values, which are undefined on overflow.
55#[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    /// Fast math flags for floating point operations.
64    #[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    /// No-wrap flags for getelementptr operations.
148    #[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    /// Normalize flags to LLVM semantics: `inbounds` implies `nusw`.
158    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        // `inbounds` implies `nusw`
194        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/// An index for a GEP can be either a constant or an SSA operand.
279/// Contrary to its name, this isn't an [Attribute][pliron::attribute::Attribute].
280#[derive(PartialEq, Eq, Clone, Debug, Hash)]
281#[format]
282pub enum GepIndexAttr {
283    /// This GEP index is a raw u32 compile time constant
284    Constant(u32),
285    /// This GEP Index is the SSA value in the containing [Operation]s `operands[idx]`
286    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/// An attribute that contains a list of case values for a switch operation.
298#[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/// Address space of a pointer or global, corresponding to LLVM's `addrspace(N)`.
357#[pliron_attr(name = "llvm.addrspace", format = "$0", verifier = "succ")]
358#[derive(PartialEq, Eq, Clone, Debug, Hash)]
359pub struct AddressSpaceAttr(pub u32);
360
361/// The "zero" value of a type: a null pointer, or an all-zero-bits aggregate.
362#[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/// The `undef` value of a type.
374#[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/// The `poison` value of a type.
393#[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/// An attribute containing a sequence of bytes: LLVM's `ConstantDataArray` of `i8`s.
412#[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
420/// Byte attributes smaller than this remain inline.
421pub const BYTES_ATTR_OUTLINE_THRESHOLD: usize = 8;
422
423impl BytesAttr {
424    /// Create a new [BytesAttr].
425    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/// The type of [BytesAttr] is `[N x i8]`.
449#[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/// A vector constant all of whose elements are `element`: LLVM's `splat (...)`
468#[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    /// A vector constant of type `ty`, every element of which is `element`.
492    pub fn new(element: Box<dyn TypedAttrInterface>, ty: TypedHandle<VectorType>) -> Self {
493        SplatAttr { element, ty }
494    }
495
496    /// The element that this splat repeats.
497    pub fn element(&self) -> &dyn TypedAttrInterface {
498        &*self.element
499    }
500
501    /// The vector type of this splat.
502    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
521/// Verify that `element`, the `idx`'th element of an aggregate or splat, is of type `expected`.
522fn 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        // That the type is a vector is the [TypedHandle]'s to guarantee.
543        let elem_ty = self.ty.deref(ctx).elem_type();
544        verify_element(ctx, 0, self.element(), elem_ty)
545    }
546}
547
548/// The address of a global variable or a function, LLVM's `ptr @symbol`.
549#[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    /// The address, of pointer type `ty`, of `symbol` — a global or a function of
562    /// the module.
563    pub fn new(symbol: Identifier, ty: TypedHandle<PointerType>) -> Self {
564        SymbolAddrAttr { symbol, ty }
565    }
566
567    /// The symbol whose address this is.
568    pub fn symbol(&self) -> &Identifier {
569        &self.symbol
570    }
571
572    /// The pointer type of this address.
573    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/// A constant aggregate, with a constant attribute per element:
586/// LLVM's `ConstantArray`, `ConstantStruct` or `ConstantVector`
587/// (and their `Data` variants)
588#[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
598/// Aggregate attributes with fewer elements than this remain inline.
599pub const AGGREGATE_ATTR_OUTLINE_THRESHOLD: usize = 4;
600
601impl AggregateAttr {
602    /// A constant aggregate of type `ty`, with one constant attribute per element.
603    pub fn new(elements: Vec<Box<dyn TypedAttrInterface>>, ty: TypeHandle) -> Self {
604        AggregateAttr { elements, ty }
605    }
606
607    /// The elements of this aggregate.
608    pub fn elements(&self) -> &[Box<dyn TypedAttrInterface>] {
609        &self.elements
610    }
611
612    /// The type of this aggregate.
613    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/// Memory ordering for an atomic operation
711#[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/// The kind of an LLVM `atomicrmw` operation.
722#[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/// Synchronization scope of an atomic operation
743#[pliron_attr(name = "llvm.sync_scope", verifier = "succ", format)]
744#[derive(PartialEq, Eq, Clone, Debug, Default, Hash)]
745pub enum SyncScopeAttr {
746    /// Synchronizes with all other threads in the system.
747    #[default]
748    System,
749    /// Synchronizes only with other atomic operations in the same thread.
750    SingleThread,
751    /// A target specific scope, named in LLVM-IR.
752    NamedScope(StringAttr),
753}
754
755impl SyncScopeAttr {
756    /// The LLVM-IR name of this scope. The system scope is unnamed in LLVM-IR
757    /// and hence maps to the empty string (as expected by `LLVMGetSyncScopeID`).
758    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 input with FAST flag set
830    #[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        // FAST also means all the other flags.
838        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    /// Attribute key for the LLVM data layout string of a [ModuleOp].
1000    ATTR_KEY_LLVM_DATA_LAYOUT,
1001    "llvm_data_layout"
1002);
1003
1004dict_key!(
1005    /// Attribute key for the LLVM target triple of a [ModuleOp].
1006    ATTR_KEY_LLVM_TARGET_TRIPLE,
1007    "llvm_target_triple"
1008);
1009
1010/// Get the LLVM data layout of `module`, if set.
1011pub 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
1020/// Set the LLVM data layout of `module`.
1021pub 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
1028/// Get the LLVM target triple of `module`, if set.
1029pub 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
1038/// Set the LLVM target triple of `module`.
1039pub 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}