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