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 = "one_hot")]
62    /// Generate a one-hot FSM for this control node. (Not necesarily a
63    /// guarantee: if the control node does not get its own FSM, then this attribute
64    /// won't necesarily be honored.)
65    OneHot,
66    #[strum(serialize = "inline")]
67    /// Inline this subcomponent
68    Inline,
69    #[strum(serialize = "promoted")]
70    /// denotes a static component or control promoted from dynamic
71    Promoted,
72    #[strum(serialize = "par")]
73    /// Denotes a group that was generated from a `staticpar` during static
74    /// inlining.
75    ParCtrl,
76    #[strum(serialize = "fast")]
77    /// https://github.com/calyxir/calyx/issues/1828
78    Fast,
79    #[strum(serialize = "protected")]
80    /// Indicate that the cell should not be removed or shared during optimization.
81    Protected,
82    #[strum(serialize = "fsm_control")]
83    /// Protects a cell controlling an FSM from being removed
84    FSMControl,
85    #[strum(serialize = "one_state")]
86    /// Indicates that a StaticEnable should be allocated only one state in an FSM
87    OneState,
88}
89
90impl From<BoolAttr> for Attribute {
91    fn from(attr: BoolAttr) -> Self {
92        Attribute::Bool(attr)
93    }
94}
95impl std::fmt::Display for BoolAttr {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        write!(f, "{}", self.as_ref())
98    }
99}
100
101#[derive(AsRefStr, EnumString, Clone, Copy, Hash, PartialEq, Eq, Debug)]
102#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
103/// Attributes that can take numeric values
104pub enum NumAttr {
105    // ============ numeric attributes ============
106    // Interface ports
107    #[strum(serialize = "go")]
108    Go,
109    #[strum(serialize = "done")]
110    Done,
111    // Interface properties
112    #[strum(serialize = "read_together")]
113    ReadTogether,
114    #[strum(serialize = "write_together")]
115    WriteTogether,
116    #[strum(serialize = "sync")]
117    /// Synchronize this thread with others in the current par block
118    Sync,
119    #[strum(serialize = "bound")]
120    /// The bound of a while loop
121    Bound,
122    #[strum(serialize = "promotable")]
123    /// Can promote the group, control, or @go port of the component to static
124    /// with the annotated latency
125    Promotable,
126    #[strum(serialize = "compactable")]
127    /// suggest that the current static seq block is compactable
128    Compactable,
129    #[strum(serialize = "interval")]
130    /// Placed on @go ports of components to denote the II of a component, which
131    /// is the same as the latency for non pipelined components.
132    /// This indicates the component can serve ``double-duty'' as both static and
133    /// dynamic.
134    /// Therefore, we only place if we can *guarantee* the interval of the component.
135    Interval,
136    #[strum(serialize = "state")]
137    State,
138}
139impl From<NumAttr> for Attribute {
140    fn from(attr: NumAttr) -> Self {
141        Attribute::Num(attr)
142    }
143}
144impl std::fmt::Display for NumAttr {
145    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146        write!(f, "{}", self.as_ref())
147    }
148}
149
150#[derive(AsRefStr, Clone, Copy, Hash, PartialEq, Eq, Debug)]
151#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
152#[allow(non_camel_case_types)]
153/// Internal attributes that cannot be parsed back from the IL.
154pub enum InternalAttr {
155    DEAD,
156    NODE_ID,
157    BEGIN_ID,
158    END_ID,
159    ST_ID,
160    LOOP,
161    START,
162    END,
163    SCHEDULE_ID,
164
165    // fsm analysis
166    UNROLL,
167    INLINE,
168    OFFLOAD,
169    ACYCLIC,
170    NUM_STATES,
171}
172impl From<InternalAttr> for Attribute {
173    fn from(attr: InternalAttr) -> Self {
174        Attribute::Internal(attr)
175    }
176}
177
178#[derive(
179    AsRefStr,
180    EnumString,
181    Clone,
182    Copy,
183    Hash,
184    PartialEq,
185    Eq,
186    Debug,
187    strum_macros::Display,
188    PartialOrd,
189    Ord,
190)]
191#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
192pub enum SetAttr {
193    #[strum(serialize = "pos")]
194    /// Source location position for this node
195    Pos,
196}
197#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
198#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
199pub enum SetAttribute {
200    Set(SetAttr),
201    Unknown(Id),
202}
203
204impl Ord for SetAttribute {
205    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
206        match (self, other) {
207            (SetAttribute::Set(s1), SetAttribute::Set(s2)) => s1.cmp(s2),
208            (SetAttribute::Set(_), SetAttribute::Unknown(_)) => {
209                std::cmp::Ordering::Less
210            }
211            (SetAttribute::Unknown(_), SetAttribute::Set(_)) => {
212                std::cmp::Ordering::Greater
213            }
214            (SetAttribute::Unknown(id1), SetAttribute::Unknown(id2)) => {
215                id1.as_ref().cmp(id2.as_ref())
216            }
217        }
218    }
219}
220
221impl PartialOrd for SetAttribute {
222    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
223        Some(self.cmp(other))
224    }
225}
226
227impl From<Id> for SetAttribute {
228    fn from(v: Id) -> Self {
229        Self::Unknown(v)
230    }
231}
232
233impl From<SetAttr> for SetAttribute {
234    fn from(v: SetAttr) -> Self {
235        Self::Set(v)
236    }
237}
238
239impl std::fmt::Display for SetAttribute {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        match self {
242            SetAttribute::Set(set_attr) => set_attr.fmt(f),
243            SetAttribute::Unknown(id) => id.fmt(f),
244        }
245    }
246}
247
248impl FromStr for SetAttribute {
249    type Err = Error;
250    fn from_str(s: &str) -> CalyxResult<Self> {
251        if let Ok(s) = SetAttr::from_str(s) {
252            Ok(SetAttribute::Set(s))
253        } else {
254            // Reject attributes that all caps since those are reserved for internal attributes
255            if s.to_uppercase() == s {
256                return Err(Error::misc(format!(
257                    "Invalid attribute: {s}. All caps attributes are reserved for internal use."
258                )));
259            }
260            Ok(SetAttribute::Unknown(s.into()))
261        }
262    }
263}
264
265#[derive(Clone, Copy, Hash, PartialEq, Eq, Debug)]
266#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
267/// Defines the known attributes that can be attached to IR nodes.
268/// All caps names represent attributes that are internal to the compiler and
269/// cannot be parsed back.
270pub enum Attribute {
271    Bool(BoolAttr),
272    Num(NumAttr),
273    Internal(InternalAttr),
274    /// Unknown attribute. Should not appear in the Calyx codebase.
275    /// Useful for other frontends using Calyx
276    Unknown(Id),
277}
278impl std::fmt::Display for Attribute {
279    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
280        match self {
281            Attribute::Bool(b) => write!(f, "{}", b.as_ref()),
282            Attribute::Num(n) => write!(f, "{}", n.as_ref()),
283            Attribute::Internal(i) => write!(f, "{}", i.as_ref()),
284            Attribute::Unknown(s) => write!(f, "{s}"),
285        }
286    }
287}
288impl FromStr for Attribute {
289    type Err = Error;
290
291    fn from_str(s: &str) -> CalyxResult<Self> {
292        if let Ok(b) = BoolAttr::from_str(s) {
293            Ok(Attribute::Bool(b))
294        } else if let Ok(n) = NumAttr::from_str(s) {
295            Ok(Attribute::Num(n))
296        } else {
297            if DEPRECATED_ATTRIBUTES.contains(&s) {
298                log::warn!(
299                    "The attribute @{s} is deprecated and will be ignored by the compiler."
300                );
301            }
302
303            if let Ok(SetAttribute::Set(_)) = SetAttribute::from_str(s) {
304                log::warn!(
305                    "Set attribute {s} incorrectly written as a standard attribute, i.e. '@{s}(..)' or '\"{s}\" = ..'. This will be ignored by the compiler. Instead write '@{s}{{..}}' or '\"{s}\" = {{..}}'."
306                );
307            }
308
309            // Reject attributes that all caps since those are reserved for internal attributes
310            if s.to_uppercase() == s {
311                return Err(Error::misc(format!(
312                    "Invalid attribute: {s}. All caps attributes are reserved for internal use."
313                )));
314            }
315            Ok(Attribute::Unknown(s.into()))
316        }
317    }
318}
319
320#[derive(Default, Debug, Clone, PartialEq, Eq)]
321/// Inline storage for boolean attributes.
322pub(super) struct InlineAttributes {
323    /// Boolean attributes stored in a 32-bit number.
324    attrs: u32,
325}
326
327impl InlineAttributes {
328    /// Is the attribute set empty?
329    pub const fn is_empty(&self) -> bool {
330        self.attrs == 0
331    }
332
333    /// Adds an attribute to the set
334    pub fn insert(&mut self, attr: BoolAttr) {
335        self.attrs |= 1 << attr as u8;
336    }
337
338    /// Checks if the set contains an attribute
339    pub fn has(&self, attr: BoolAttr) -> bool {
340        self.attrs & (1 << (attr as u8)) != 0
341    }
342
343    /// Remove attribute from the set if present
344    pub fn remove(&mut self, attr: BoolAttr) {
345        self.attrs &= !(1 << attr as u8);
346    }
347
348    /// Returns an iterator over the attributes in the set
349    pub(super) fn iter(&self) -> impl Iterator<Item = BoolAttr> + '_ {
350        (0..(BoolAttr::COUNT as u8)).filter_map(|idx| {
351            if self.attrs & (1 << idx) != 0 {
352                Some(BoolAttr::from_repr(idx).unwrap())
353            } else {
354                None
355            }
356        })
357    }
358}
359
360#[cfg(feature = "serialize")]
361impl serde::Serialize for InlineAttributes {
362    fn serialize<S>(&self, ser: S) -> Result<S::Ok, S::Error>
363    where
364        S: serde::Serializer,
365    {
366        self.to_owned().attrs.serialize(ser)
367    }
368}