calyx_ir/
structure.rs

1//! Representation for structure (wires and cells) in a Calyx program.
2
3use crate::guard::StaticTiming;
4use crate::Nothing;
5
6use super::{
7    Attributes, Direction, GetAttributes, Guard, Id, PortDef, RRC, WRC,
8};
9use calyx_frontend::Attribute;
10use calyx_utils::{CalyxResult, Error, GetName};
11use itertools::Itertools;
12use smallvec::{smallvec, SmallVec};
13use std::hash::Hash;
14use std::rc::Rc;
15
16/// Ports can come from Cells or Groups
17#[derive(Debug, Clone)]
18pub enum PortParent {
19    Cell(WRC<Cell>),
20    Group(WRC<Group>),
21    StaticGroup(WRC<StaticGroup>),
22}
23
24/// Represents a port on a cell.
25#[derive(Debug, Clone)]
26#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
27pub struct Port {
28    /// Name of the port
29    pub name: Id,
30    /// Width of the port
31    pub width: u64,
32    /// Direction of the port
33    pub direction: Direction,
34    /// Weak pointer to this port's parent
35    pub parent: PortParent,
36    /// Attributes associated with this port.
37    pub attributes: Attributes,
38}
39
40/// Canonical name of a Port
41#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
42pub struct Canonical {
43    pub cell: Id,
44    pub port: Id,
45}
46
47impl Canonical {
48    pub const fn new(cell: Id, port: Id) -> Self {
49        Self { cell, port }
50    }
51}
52
53impl std::fmt::Display for Canonical {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(f, "{}.{}", self.cell, self.port)
56    }
57}
58
59impl Port {
60    /// Checks if this port is a hole
61    pub fn is_hole(&self) -> bool {
62        matches!(&self.parent, PortParent::Group(_))
63            || matches!(&self.parent, PortParent::StaticGroup(_))
64    }
65
66    /// Returns the parent of the [Port] which must be [Cell]. Throws an error
67    /// otherwise.
68    pub fn cell_parent(&self) -> RRC<Cell> {
69        if let PortParent::Cell(cell_wref) = &self.parent {
70            return cell_wref.upgrade();
71        }
72        unreachable!("This port should have a cell parent")
73    }
74
75    /// Checks if this port is a constant of value: `val`.
76    pub fn is_constant(&self, val: u64, width: u64) -> bool {
77        if let PortParent::Cell(cell) = &self.parent {
78            match cell.upgrade().borrow().prototype {
79                CellType::Constant { val: v, width: w } => {
80                    v == val && width == w
81                }
82                _ => false,
83            }
84        } else {
85            false
86        }
87    }
88
89    /// Gets name of parent object.
90    pub fn get_parent_name(&self) -> Id {
91        match &self.parent {
92            PortParent::Cell(cell) => cell.upgrade().borrow().name,
93            PortParent::Group(group) => group.upgrade().borrow().name,
94            PortParent::StaticGroup(group) => group.upgrade().borrow().name,
95        }
96    }
97
98    /// Checks if parent is combinational component
99    pub fn parent_is_comb(&self) -> bool {
100        match &self.parent {
101            PortParent::Cell(cell) => cell.upgrade().borrow().is_comb_cell(),
102            _ => false,
103        }
104    }
105
106    /// Get the canonical representation for this Port.
107    pub fn canonical(&self) -> Canonical {
108        Canonical {
109            cell: self.get_parent_name(),
110            port: self.name,
111        }
112    }
113
114    /// Returns the value of an attribute if present
115    pub fn get_attribute<A>(&self, attr: A) -> Option<u64>
116    where
117        A: Into<Attribute>,
118    {
119        self.get_attributes().get(attr)
120    }
121
122    /// Returns true if the node has a specific attribute
123    pub fn has_attribute<A>(&self, attr: A) -> bool
124    where
125        A: Into<Attribute>,
126    {
127        self.get_attributes().has(attr)
128    }
129}
130
131impl GetAttributes for Port {
132    fn get_attributes(&self) -> &Attributes {
133        &self.attributes
134    }
135
136    fn get_mut_attributes(&mut self) -> &mut Attributes {
137        &mut self.attributes
138    }
139}
140
141impl PartialEq for Port {
142    fn eq(&self, other: &Self) -> bool {
143        self.get_parent_name() == other.get_parent_name()
144            && self.name == other.name
145    }
146}
147
148impl Eq for Port {}
149
150/// Wraps generic iterators over ports to allow functions to build and return port iterators in
151/// different ways.
152pub struct PortIterator<'a> {
153    port_iter: Box<dyn Iterator<Item = RRC<Port>> + 'a>,
154}
155
156impl<'a> PortIterator<'a> {
157    /// Construct a new PortIterator from an iterator over ports.
158    pub fn new<T>(iter: T) -> Self
159    where
160        T: Iterator<Item = RRC<Port>> + 'a,
161    {
162        PortIterator {
163            port_iter: Box::new(iter),
164        }
165    }
166
167    /// Returns an empty iterator over ports.
168    pub fn empty() -> Self {
169        PortIterator {
170            port_iter: Box::new(std::iter::empty()),
171        }
172    }
173}
174
175impl Iterator for PortIterator<'_> {
176    type Item = RRC<Port>;
177
178    fn next(&mut self) -> Option<Self::Item> {
179        self.port_iter.next()
180    }
181}
182
183/// Alias for bindings
184pub type Binding = SmallVec<[(Id, u64); 5]>;
185
186/// The type for a Cell
187#[derive(Debug, PartialEq, Eq, Hash, Clone)]
188#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
189pub enum CellType {
190    /// Cell constructed using a primitive definition
191    Primitive {
192        /// Name of the primitive cell used to instantiate this cell.
193        name: Id,
194        /// Bindings for the parameters. Uses Vec to retain the input order.
195        param_binding: Box<Binding>,
196        /// True iff this is a combinational primitive
197        is_comb: bool,
198        /// (Optional) latency of the primitive
199        latency: Option<std::num::NonZeroU64>,
200    },
201    /// Cell constructed using a Calyx component
202    Component {
203        /// Name of the component used to instantiate this cell.
204        name: Id,
205    },
206    /// This cell represents the current component
207    ThisComponent,
208    /// Cell representing a Constant
209    Constant {
210        /// Value of this constant
211        val: u64,
212        /// Width of this constant
213        width: u64,
214    },
215}
216
217impl CellType {
218    /// Return the name associated with this CellType is present
219    pub fn get_name(&self) -> Option<Id> {
220        match self {
221            CellType::Primitive { name, .. } | CellType::Component { name } => {
222                Some(*name)
223            }
224            CellType::ThisComponent | CellType::Constant { .. } => None,
225        }
226    }
227
228    /// Generate string representation of CellType appropriate for error messages.
229    pub fn surface_name(&self) -> Option<String> {
230        match self {
231            CellType::Primitive {
232                name,
233                param_binding,
234                ..
235            } => Some(format!(
236                "{}({})",
237                name,
238                param_binding.iter().map(|(_, v)| v.to_string()).join(", ")
239            )),
240            CellType::Component { name } => Some(name.to_string()),
241            CellType::ThisComponent | CellType::Constant { .. } => None,
242        }
243    }
244}
245
246/// Represents an instantiated cell.
247#[derive(Debug)]
248#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
249pub struct Cell {
250    /// Name of this cell.
251    name: Id,
252    /// Ports on this cell
253    pub ports: SmallVec<[RRC<Port>; 10]>,
254    /// Underlying type for this cell
255    pub prototype: CellType,
256    /// Attributes for this group.
257    pub attributes: Attributes,
258    /// Whether the cell is external
259    reference: bool,
260}
261
262impl GetAttributes for Cell {
263    fn get_attributes(&self) -> &Attributes {
264        &self.attributes
265    }
266
267    fn get_mut_attributes(&mut self) -> &mut Attributes {
268        &mut self.attributes
269    }
270}
271
272impl Cell {
273    /// Construct a cell
274    pub fn new(name: Id, prototype: CellType) -> Self {
275        Self {
276            name,
277            ports: smallvec![],
278            prototype,
279            attributes: Attributes::default(),
280            reference: false,
281        }
282    }
283
284    ///Get a boolean describing whether the cell is external.
285    pub fn is_reference(&self) -> bool {
286        self.reference
287    }
288
289    ///Set the external field
290    pub(super) fn set_reference(&mut self, reference: bool) -> bool {
291        self.reference = reference;
292        self.reference
293    }
294
295    /// Get a reference to the named port if it exists.
296    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
297    where
298        S: std::fmt::Display + Clone,
299        Id: PartialEq<S>,
300    {
301        self.ports
302            .iter()
303            .find(|&g| g.borrow().name == name)
304            .map(Rc::clone)
305    }
306
307    /// Return all ports that have the attribute `attr`.
308    pub fn find_all_with_attr<A>(
309        &self,
310        attr: A,
311    ) -> impl Iterator<Item = RRC<Port>> + '_
312    where
313        A: Into<Attribute>,
314    {
315        let attr = attr.into();
316        self.ports
317            .iter()
318            .filter(move |&p| p.borrow().attributes.has(attr))
319            .map(Rc::clone)
320    }
321
322    /// Return the unique port with the given attribute.
323    /// If multiple ports have the same attribute, then we panic.
324    /// If there are not ports with the give attribute, then we return None.
325    pub fn find_unique_with_attr<A>(
326        &self,
327        attr: A,
328    ) -> CalyxResult<Option<RRC<Port>>>
329    where
330        A: Into<Attribute>,
331    {
332        let attr = attr.into();
333        let mut ports = self.find_all_with_attr(attr);
334        if let Some(port) = ports.next() {
335            if ports.next().is_some() {
336                Err(Error::malformed_structure(format!(
337                    "Multiple ports with attribute `{}` found on cell `{}`",
338                    attr, self.name
339                )))
340            } else {
341                Ok(Some(port))
342            }
343        } else {
344            Ok(None)
345        }
346    }
347
348    /// Get a reference to the named port and throw an error if it doesn't
349    /// exist.
350    pub fn get<S>(&self, name: S) -> RRC<Port>
351    where
352        S: std::fmt::Display + Clone,
353        Id: PartialEq<S>,
354    {
355        self.find(name.clone()).unwrap_or_else(|| {
356            panic!(
357                "Port `{name}' not found on cell `{}'. Known ports are: {}",
358                self.name,
359                self.ports
360                    .iter()
361                    .map(|p| p.borrow().name.to_string())
362                    .join(",")
363            )
364        })
365    }
366
367    /// Returns true iff this cell is an instance of a Calyx-defined component.
368    pub fn is_component(&self) -> bool {
369        matches!(&self.prototype, CellType::Component { .. })
370    }
371
372    /// Returns true iff this cell is the signature of the current component
373    pub fn is_this(&self) -> bool {
374        matches!(&self.prototype, CellType::ThisComponent)
375    }
376
377    /// Returns true if this is an instance of a primitive. If the optional name is provided then
378    /// only returns true if the primitive has the given name.
379    pub fn is_primitive<S>(&self, prim: Option<S>) -> bool
380    where
381        Id: PartialEq<S>,
382    {
383        match &self.prototype {
384            CellType::Primitive { name, .. } => {
385                prim.as_ref().map(|p| name == p).unwrap_or(true)
386            }
387            _ => false,
388        }
389    }
390
391    /// Get the unique port with the given attribute.
392    /// Panic if no port with the attribute is found and returns an error if multiple ports with the attribute are found.
393    pub fn get_unique_with_attr<A>(&self, attr: A) -> CalyxResult<RRC<Port>>
394    where
395        A: Into<Attribute> + std::fmt::Display + Copy,
396    {
397        Ok(self.find_unique_with_attr(attr)?.unwrap_or_else(|| {
398            panic!(
399                "Port with attribute `{attr}' not found on cell `{}'",
400                self.name,
401            )
402        }))
403    }
404
405    /// Returns the name of the component that is this cells type.
406    pub fn type_name(&self) -> Option<Id> {
407        self.prototype.get_name()
408    }
409
410    /// Get parameter binding from the prototype used to build this cell.
411    pub fn get_parameter<S>(&self, param: S) -> Option<u64>
412    where
413        Id: PartialEq<S>,
414    {
415        match &self.prototype {
416            CellType::Primitive { param_binding, .. } => param_binding
417                .iter()
418                .find(|(key, _)| *key == param)
419                .map(|(_, val)| *val),
420            CellType::Component { .. } => None,
421            CellType::ThisComponent => None,
422            CellType::Constant { .. } => None,
423        }
424    }
425
426    /// Return the canonical name for the cell generated to represent this
427    /// (val, width) constant.
428    pub fn constant_name(val: u64, width: u64) -> Id {
429        format!("_{}_{}", val, width).into()
430    }
431
432    /// Return the value associated with this attribute key.
433    pub fn get_attribute<A: Into<Attribute>>(&self, attr: A) -> Option<u64> {
434        self.attributes.get(attr.into())
435    }
436
437    /// Add a new attribute to the group.
438    pub fn add_attribute<A: Into<Attribute>>(&mut self, attr: A, value: u64) {
439        self.attributes.insert(attr.into(), value);
440    }
441
442    /// Grants immutable access to the name of this cell.
443    pub fn name(&self) -> Id {
444        self.name
445    }
446
447    /// Returns a reference to all [super::Port] attached to this cells.
448    pub fn ports(&self) -> &SmallVec<[RRC<Port>; 10]> {
449        &self.ports
450    }
451
452    // Get the signature of this cell as a vector. Each element corresponds to a port in the Cell.
453    pub fn get_signature(&self) -> Vec<PortDef<u64>> {
454        self.ports
455            .iter()
456            .map(|port_ref| {
457                let port = port_ref.borrow();
458                PortDef::new(
459                    port.name,
460                    port.width,
461                    port.direction.clone(),
462                    port.attributes.clone(),
463                )
464            })
465            .collect()
466    }
467
468    // returns true if cell is comb, false otherwise
469    // note that this component/component cannot be combinational
470    // XXX(rachit): Combinational components are now supported so this function returns
471    // the wrong answer when the parent is a combinational component
472    pub fn is_comb_cell(&self) -> bool {
473        match self.prototype {
474            CellType::Primitive { is_comb, .. } => is_comb,
475            _ => false,
476        }
477    }
478}
479
480/// Represents a guarded assignment in the program
481#[derive(Clone, Debug)]
482#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
483pub struct Assignment<T> {
484    /// The destination for the assignment.
485    pub dst: RRC<Port>,
486
487    /// The source for the assignment.
488    pub src: RRC<Port>,
489
490    /// The guard for this assignment.
491    pub guard: Box<Guard<T>>,
492
493    /// Attributes for this assignment.
494    pub attributes: Attributes,
495}
496
497impl<T> Assignment<T> {
498    /// Build a new unguarded assignment
499    pub fn new(dst: RRC<Port>, src: RRC<Port>) -> Self {
500        assert!(
501            dst.borrow().direction == Direction::Input,
502            "{} is not in input port",
503            dst.borrow().canonical()
504        );
505        assert!(
506            src.borrow().direction == Direction::Output,
507            "{} is not in output port",
508            src.borrow().canonical()
509        );
510        Self {
511            dst,
512            src,
513            guard: Box::new(Guard::True),
514            attributes: Attributes::default(),
515        }
516    }
517
518    /// Apply function `f` to each port contained within the assignment and
519    /// replace the port with the generated value if not None.
520    pub fn for_each_port<F>(&mut self, mut f: F)
521    where
522        F: FnMut(&RRC<Port>) -> Option<RRC<Port>>,
523    {
524        if let Some(new_src) = f(&self.src) {
525            self.src = new_src;
526        }
527        if let Some(new_dst) = f(&self.dst) {
528            self.dst = new_dst;
529        }
530        self.guard.for_each(&mut |port| f(&port).map(Guard::port))
531    }
532}
533
534impl From<Assignment<Nothing>> for Assignment<StaticTiming> {
535    /// Turns a normal assignment into a static assignment
536    fn from(assgn: Assignment<Nothing>) -> Assignment<StaticTiming> {
537        Assignment {
538            dst: Rc::clone(&assgn.dst),
539            src: Rc::clone(&assgn.src),
540            guard: Box::new(Guard::from(*assgn.guard)),
541            attributes: assgn.attributes,
542        }
543    }
544}
545
546impl<StaticTiming> Assignment<StaticTiming> {
547    /// Apply function `f` to each port contained within the assignment and
548    /// replace the port with the generated value if not None.
549    pub fn for_each_interval<F>(&mut self, mut f: F)
550    where
551        F: FnMut(&mut StaticTiming) -> Option<Guard<StaticTiming>>,
552    {
553        self.guard.for_each_info(&mut |interval| f(interval))
554    }
555}
556
557/// A Group of assignments that perform a logical action.
558#[derive(Debug)]
559#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
560pub struct Group {
561    /// Name of this group
562    name: Id,
563
564    /// The assignments used in this group
565    pub assignments: Vec<Assignment<Nothing>>,
566
567    /// Holes for this group
568    pub holes: SmallVec<[RRC<Port>; 3]>,
569
570    /// Attributes for this group.
571    pub attributes: Attributes,
572}
573impl Group {
574    pub fn new(name: Id) -> Self {
575        Self {
576            name,
577            assignments: vec![],
578            holes: smallvec![],
579            attributes: Attributes::default(),
580        }
581    }
582
583    /// Get a reference to the named hole if it exists.
584    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
585    where
586        S: std::fmt::Display,
587        Id: PartialEq<S>,
588    {
589        self.holes
590            .iter()
591            .find(|&g| g.borrow().name == name)
592            .map(Rc::clone)
593    }
594
595    /// Get a reference to the named hole or panic.
596    pub fn get<S>(&self, name: S) -> RRC<Port>
597    where
598        S: std::fmt::Display + Clone,
599        Id: PartialEq<S>,
600    {
601        self.find(name.clone()).unwrap_or_else(|| {
602            panic!("Hole `{name}' not found on group `{}'", self.name)
603        })
604    }
605
606    /// Returns the index to the done assignment in the group.
607    fn find_done_cond(&self) -> usize {
608        self.assignments
609            .iter()
610            .position(|assign| {
611                let dst = assign.dst.borrow();
612                dst.is_hole() && dst.name == "done"
613            })
614            .unwrap_or_else(|| {
615                panic!("Group `{}' has no done condition", self.name)
616            })
617    }
618
619    /// Returns a reference to the assignment in the group that writes to the done condition.
620    pub fn done_cond(&self) -> &Assignment<Nothing> {
621        let idx = self.find_done_cond();
622        &self.assignments[idx]
623    }
624
625    /// Returns a mutable reference to the assignment in the group that writes to the done
626    /// condition.
627    pub fn done_cond_mut(&mut self) -> &mut Assignment<Nothing> {
628        let idx = self.find_done_cond();
629        &mut self.assignments[idx]
630    }
631
632    /// The name of this group.
633    #[inline]
634    pub fn name(&self) -> Id {
635        self.name
636    }
637
638    /// The attributes of this group.
639    #[inline]
640    pub fn get_attributes(&self) -> Option<&Attributes> {
641        Some(&self.attributes)
642    }
643
644    pub fn remove_attribute(&mut self, attr: Attribute) {
645        self.attributes.remove(attr);
646    }
647}
648
649/// A Group of assignments that perform a logical action.
650#[derive(Debug)]
651#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
652pub struct StaticGroup {
653    /// Name of this group
654    name: Id,
655
656    /// The assignments used in this group
657    pub assignments: Vec<Assignment<StaticTiming>>,
658
659    /// Holes for this group
660    pub holes: SmallVec<[RRC<Port>; 3]>,
661
662    /// Attributes for this group.
663    pub attributes: Attributes,
664
665    /// Latency of static group
666    pub latency: u64,
667}
668
669///implement the StaticGroup struct
670impl StaticGroup {
671    pub fn new(name: Id, latency: u64) -> Self {
672        Self {
673            name,
674            assignments: vec![],
675            holes: smallvec![],
676            attributes: Attributes::default(),
677            latency,
678        }
679    }
680
681    pub fn get_latency(&self) -> u64 {
682        self.latency
683    }
684
685    /// Get a reference to the named hole if it exists.
686    pub fn find<S>(&self, name: S) -> Option<RRC<Port>>
687    where
688        S: std::fmt::Display,
689        Id: PartialEq<S>,
690    {
691        self.holes
692            .iter()
693            .find(|&g| g.borrow().name == name)
694            .map(Rc::clone)
695    }
696
697    /// Get a reference to the named hole or panic.
698    pub fn get<S>(&self, name: S) -> RRC<Port>
699    where
700        S: std::fmt::Display + Clone,
701        Id: PartialEq<S>,
702    {
703        self.find(name.clone()).unwrap_or_else(|| {
704            panic!("Hole `{name}' not found on group `{}'", self.name)
705        })
706    }
707
708    /// The name of this group.
709    #[inline]
710    pub fn name(&self) -> Id {
711        self.name
712    }
713
714    /// The attributes of this group.
715    #[inline]
716    pub fn get_attributes(&self) -> Option<&Attributes> {
717        Some(&self.attributes)
718    }
719
720    pub fn remove_attribute(&mut self, attr: Attribute) {
721        self.attributes.remove(attr);
722    }
723}
724
725/// A combinational group.
726/// A combinational group does not have any holes and should only contain assignments that should
727/// will be combinationally active
728#[derive(Debug)]
729#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
730pub struct CombGroup {
731    /// Name of this group
732    pub(super) name: Id,
733
734    /// The assignments used in this group
735    pub assignments: Vec<Assignment<Nothing>>,
736
737    /// Attributes for this group.
738    pub attributes: Attributes,
739}
740impl CombGroup {
741    /// The name of this group.
742    #[inline]
743    pub fn name(&self) -> Id {
744        self.name
745    }
746
747    /// The attributes of this group.
748    #[inline]
749    pub fn get_attributes(&self) -> Option<&Attributes> {
750        Some(&self.attributes)
751    }
752}
753
754impl GetName for Cell {
755    fn name(&self) -> Id {
756        self.name()
757    }
758}
759
760impl GetName for Group {
761    fn name(&self) -> Id {
762        self.name()
763    }
764}
765
766impl GetName for CombGroup {
767    fn name(&self) -> Id {
768        self.name()
769    }
770}
771
772impl GetName for StaticGroup {
773    fn name(&self) -> Id {
774        self.name()
775    }
776}