calyx_frontend/
attribute.rs

1use calyx_utils::{CalyxResult, Error, Id};
2use std::str::FromStr;
3use strum::EnumCount;
4use strum_macros::{AsRefStr, EnumCount, EnumString, FromRepr};
5
6/// Attributes that have been deprecated.
7pub const DEPRECATED_ATTRIBUTES: &[&str] = &["static"];
8
9#[derive(
10    EnumCount,
11    FromRepr,
12    AsRefStr,
13    EnumString,
14    Clone,
15    Copy,
16    Hash,
17    PartialEq,
18    Eq,
19    Debug,
20)]
21#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
22#[repr(u8)]
23/// Attributes that are only allowed to take boolean values.
24pub enum BoolAttr {
25    #[strum(serialize = "toplevel")]
26    /// This is the top-level component
27    TopLevel,
28    #[strum(serialize = "external")]
29    /// Cell should be externalized
30    External,
31    #[strum(serialize = "nointerface")]
32    /// The component doesn't have a standard interface
33    NoInterface,
34    #[strum(serialize = "reset")]
35    /// Reset signal for the component
36    Reset,
37    #[strum(serialize = "clk")]
38    /// Clk for the signal
39    Clk,
40    #[strum(serialize = "stable")]
41    /// Is the port connected to a state element
42    Stable,
43    #[strum(serialize = "data")]
44    /// This is a data path instance
45    Data,
46    #[strum(serialize = "control")]
47    /// This is a control path instance
48    Control,
49    #[strum(serialize = "share")]
50    /// Is this component shareable
51    Share,
52    #[strum(serialize = "state_share")]
53    /// Is the component state shareable
54    StateShare,
55    #[strum(serialize = "generated")]
56    /// IR Node was generated by the compiler
57    Generated,
58    #[strum(serialize = "new_fsm")]
59    /// Generate a new FSM for this control node
60    NewFSM,
61    #[strum(serialize = "inline")]
62    /// Inline this subcomponent
63    Inline,
64    #[strum(serialize = "promoted")]
65    /// denotes a static component or control promoted from dynamic
66    Promoted,
67}
68impl From<BoolAttr> for Attribute {
69    fn from(attr: BoolAttr) -> Self {
70        Attribute::Bool(attr)
71    }
72}
73impl std::fmt::Display for BoolAttr {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        write!(f, "{}", self.as_ref())
76    }
77}
78
79#[derive(AsRefStr, EnumString, Clone, Copy, Hash, PartialEq, Eq, Debug)]
80#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
81/// Attributes that can take numeric values
82pub enum NumAttr {
83    // ============ numeric attributes ============
84    // Interface ports
85    #[strum(serialize = "go")]
86    Go,
87    #[strum(serialize = "done")]
88    Done,
89    // Interface properties
90    #[strum(serialize = "read_together")]
91    ReadTogether,
92    #[strum(serialize = "write_together")]
93    WriteTogether,
94    #[strum(serialize = "sync")]
95    /// Synchronize this thread with others in the current par block
96    Sync,
97    #[strum(serialize = "bound")]
98    /// The bound of a while loop
99    Bound,
100    #[strum(serialize = "pos")]
101    /// Source location position for this node
102    Pos,
103    #[strum(serialize = "promotable")]
104    /// Can promote the group, control, or @go port of the component to static
105    /// with the annotated latency
106    Promotable,
107    #[strum(serialize = "compactable")]
108    /// suggest that the current static seq block is compactable
109    Compactable,
110    #[strum(serialize = "interval")]
111    /// Placed on @go ports of components to denote the II of a component, which
112    /// is the same as the latency for non pipelined components.
113    /// This indicates the component can serve ``double-duty'' as both static and
114    /// dynamic.
115    /// Therefore, we only place if we can *guarantee* the interval of the component.
116    Interval,
117}
118impl From<NumAttr> for Attribute {
119    fn from(attr: NumAttr) -> Self {
120        Attribute::Num(attr)
121    }
122}
123impl std::fmt::Display for NumAttr {
124    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125        write!(f, "{}", self.as_ref())
126    }
127}
128
129#[derive(AsRefStr, Clone, Copy, Hash, PartialEq, Eq, Debug)]
130#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
131#[allow(non_camel_case_types)]
132/// Internal attributes that cannot be parsed back from the IL.
133pub enum InternalAttr {
134    DEAD,
135    NODE_ID,
136    BEGIN_ID,
137    END_ID,
138    ST_ID,
139    LOOP,
140    START,
141    END,
142}
143impl From<InternalAttr> for Attribute {
144    fn from(attr: InternalAttr) -> Self {
145        Attribute::Internal(attr)
146    }
147}
148
149#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
150#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
151/// Defines the known attributes that can be attached to IR nodes.
152/// All caps names represent attributes that are internal to the compiler and
153/// cannot be parsed back.
154pub enum Attribute {
155    Bool(BoolAttr),
156    Num(NumAttr),
157    Internal(InternalAttr),
158    /// Unknown attribute. Should not appear in the Calyx codebase.
159    /// Useful for other frontends using Calyx
160    Unknown(Id),
161}
162impl std::fmt::Display for Attribute {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        match self {
165            Attribute::Bool(b) => write!(f, "{}", b.as_ref()),
166            Attribute::Num(n) => write!(f, "{}", n.as_ref()),
167            Attribute::Internal(i) => write!(f, "{}", i.as_ref()),
168            Attribute::Unknown(s) => write!(f, "{}", s),
169        }
170    }
171}
172impl FromStr for Attribute {
173    type Err = Error;
174
175    fn from_str(s: &str) -> CalyxResult<Self> {
176        if let Ok(b) = BoolAttr::from_str(s) {
177            Ok(Attribute::Bool(b))
178        } else if let Ok(n) = NumAttr::from_str(s) {
179            Ok(Attribute::Num(n))
180        } else {
181            if DEPRECATED_ATTRIBUTES.contains(&s) {
182                log::warn!("The attribute @{s} is deprecated and will be ignored by the compiler.");
183            }
184            // Reject attributes that all caps since those are reserved for internal attributes
185            if s.to_uppercase() == s {
186                return Err(Error::misc(format!("Invalid attribute: {}. All caps attributes are reserved for internal use.", s)));
187            }
188            Ok(Attribute::Unknown(s.into()))
189        }
190    }
191}
192
193#[derive(Default, Debug, Clone)]
194/// Inline storage for boolean attributes.
195pub(super) struct InlineAttributes {
196    /// Boolean attributes stored in a 16-bit number.
197    attrs: u16,
198}
199
200impl InlineAttributes {
201    /// Is the attribute set empty?
202    pub const fn is_empty(&self) -> bool {
203        self.attrs == 0
204    }
205
206    /// Adds an attribute to the set
207    pub fn insert(&mut self, attr: BoolAttr) {
208        self.attrs |= 1 << attr as u8;
209    }
210
211    /// Checks if the set contains an attribute
212    pub fn has(&self, attr: BoolAttr) -> bool {
213        self.attrs & (1 << (attr as u8)) != 0
214    }
215
216    /// Remove attribute from the set if present
217    pub fn remove(&mut self, attr: BoolAttr) {
218        self.attrs &= !(1 << attr as u8);
219    }
220
221    /// Returns an iterator over the attributes in the set
222    pub(super) fn iter(&self) -> impl Iterator<Item = BoolAttr> + '_ {
223        (0..(BoolAttr::COUNT as u8)).filter_map(|idx| {
224            if self.attrs & (1 << idx) != 0 {
225                Some(BoolAttr::from_repr(idx).unwrap())
226            } else {
227                None
228            }
229        })
230    }
231}
232
233#[cfg(feature = "serialize")]
234impl serde::Serialize for InlineAttributes {
235    fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
236    where
237        S: serde::Serializer,
238    {
239        self.to_owned().attrs.serialize(ser)
240    }
241}