calyx_ir/
builder.rs

1//! IR Builder. Provides convience methods to build various parts of the internal
2//! representation.
3use crate::{self as ir, LibrarySignatures, Nothing, RRC, WRC};
4use calyx_frontend::BoolAttr;
5use calyx_utils::CalyxResult;
6use std::rc::Rc;
7
8use super::{CellType, PortDef};
9
10/// IR builder.
11/// Uses internal references to the component to construct and validate
12/// constructs when needed.
13/// By default, assumes that the cells are being added by a pass and marks
14/// them with the `@generated` attribute.
15///
16/// In order to disable this behavior, call [[ir::Builder::not_generated()]].
17pub struct Builder<'a> {
18    /// Component for which this builder is constructing.
19    pub component: &'a mut ir::Component,
20    /// Library signatures.
21    lib: &'a LibrarySignatures,
22    /// Enable validation of components.
23    /// Useful for debugging malformed AST errors.
24    validate: bool,
25    /// Cells added are generated during a compiler pass.
26    generated: bool,
27}
28
29impl<'a> Builder<'a> {
30    /// Instantiate a new builder using for a component.
31    pub fn new(
32        component: &'a mut ir::Component,
33        lib: &'a LibrarySignatures,
34    ) -> Self {
35        Self {
36            component,
37            lib,
38            validate: false,
39            // By default, assume that builder is called from a pass
40            generated: true,
41        }
42    }
43
44    /// Enable the validation flag on a builder.
45    pub fn validate(mut self) -> Self {
46        self.validate = true;
47        self
48    }
49
50    /// Disable the generated flag on the builder
51    pub fn not_generated(mut self) -> Self {
52        self.generated = false;
53        self
54    }
55
56    pub fn add_continuous_assignments(
57        &mut self,
58        assigns: Vec<ir::Assignment<Nothing>>,
59    ) {
60        self.component.continuous_assignments.extend(assigns);
61    }
62
63    /// Construct a new FSM and add it to the Component.
64    /// The FSM is guaranteed to start with `prefix`.
65    /// Returns a reference to the group.
66    pub fn add_fsm<S>(&mut self, prefix: S) -> RRC<ir::FSM>
67    where
68        S: Into<ir::Id>,
69    {
70        let prefix: ir::Id = prefix.into();
71        assert!(
72            prefix != "",
73            "Cannot construct group with empty name prefix"
74        );
75        let name = self.component.generate_name(prefix);
76
77        // Construct a new FSM
78        let fsm = ir::rrc(ir::FSM::new(name));
79
80        // Fill in the ports of the FSM with default wires
81        for (name, width) in &[("start", 1), ("done", 1), ("state", 1)] {
82            let hole = ir::rrc(ir::Port {
83                name: ir::Id::from(*name),
84                width: *width,
85                direction: ir::Direction::Inout,
86                parent: ir::PortParent::FSM(WRC::from(&fsm)),
87                attributes: ir::Attributes::default(),
88            });
89            fsm.borrow_mut().wires.push(hole);
90        }
91
92        // Add the group to the component.
93        self.component.get_fsms_mut().add(Rc::clone(&fsm));
94
95        fsm
96    }
97
98    /// Construct a new group and add it to the Component.
99    /// The group is guaranteed to start with `prefix`.
100    /// Returns a reference to the group.
101    pub fn add_group<S>(&mut self, prefix: S) -> RRC<ir::Group>
102    where
103        S: Into<ir::Id>,
104    {
105        let prefix: ir::Id = prefix.into();
106        assert!(
107            prefix != "",
108            "Cannot construct group with empty name prefix"
109        );
110        let name = self.component.generate_name(prefix);
111
112        // Check if there is a group with the same name.
113        let group = ir::rrc(ir::Group::new(name));
114
115        // Add default holes to the group.
116        for (name, width) in &[("go", 1), ("done", 1)] {
117            let hole = ir::rrc(ir::Port {
118                name: ir::Id::from(*name),
119                width: *width,
120                direction: ir::Direction::Inout,
121                parent: ir::PortParent::Group(WRC::from(&group)),
122                attributes: ir::Attributes::default(),
123            });
124            group.borrow_mut().holes.push(hole);
125        }
126
127        // Add the group to the component.
128        self.component.get_groups_mut().add(Rc::clone(&group));
129
130        group
131    }
132
133    /// Construct a new static group and add it to the Component.
134    /// The group is guaranteed to start with `prefix`.
135    /// Returns a reference to the group.
136    pub fn add_static_group<S>(
137        &mut self,
138        prefix: S,
139        latency: u64,
140    ) -> RRC<ir::StaticGroup>
141    where
142        S: Into<ir::Id>,
143    {
144        let prefix: ir::Id = prefix.into();
145        assert!(
146            prefix != "",
147            "Cannot construct group with empty name prefix"
148        );
149        let name = self.component.generate_name(prefix);
150
151        // Check if there is a group with the same name.
152        let group = ir::rrc(ir::StaticGroup::new(name, latency));
153
154        // Add default holes to the group.
155        // Static Groups don't need a done hole.
156        // May be beneficial to have a go hole, though (although maybe not)
157        let (name, width) = ("go", 1);
158        let hole = ir::rrc(ir::Port {
159            name: ir::Id::from(name),
160            width,
161            direction: ir::Direction::Inout,
162            parent: ir::PortParent::StaticGroup(WRC::from(&group)),
163            attributes: ir::Attributes::default(),
164        });
165        group.borrow_mut().holes.push(hole);
166
167        // Add the group to the component.
168        self.component
169            .get_static_groups_mut()
170            .add(Rc::clone(&group));
171
172        group
173    }
174
175    /// Construct a combinational group
176    pub fn add_comb_group<S>(&mut self, prefix: S) -> RRC<ir::CombGroup>
177    where
178        S: Into<ir::Id> + ToString + Clone,
179    {
180        let name = self.component.generate_name(prefix);
181
182        // Check if there is a group with the same name.
183        let group = ir::rrc(ir::CombGroup {
184            name,
185            attributes: ir::Attributes::default(),
186            assignments: vec![],
187        });
188
189        // Add the group to the component.
190        self.component.comb_groups.add(Rc::clone(&group));
191
192        group
193    }
194
195    /// Return reference for a constant cell associated with the (val, width)
196    /// pair, building and adding it to the component if needed..
197    /// If the constant does not exist, it is added to the Context.
198    pub fn add_constant(&mut self, val: u64, width: u64) -> RRC<ir::Cell> {
199        // Ensure that the value can fit within the width
200        assert!(
201            // This calculates the position of the most significant 1 bit which
202            // tells us the minimum number of bits required to represent the
203            // constant. Note that this will not work for constants that require
204            // more than 64 bits as those currently cannot be parsed
205            (64 - val.leading_zeros()) as u64 <= width,
206            "Constant value {val} cannot fit in {width} bits"
207        );
208        let name = ir::Cell::constant_name(val, width);
209        // If this constant has already been instantiated, return the relevant
210        // cell.
211        if let Some(cell) = self.component.cells.find(name) {
212            return Rc::clone(&cell);
213        }
214
215        // Construct this cell if it's not already present in the context.
216        let cell = Self::cell_from_signature(
217            name,
218            ir::CellType::Constant { val, width },
219            vec![ir::PortDef::new(
220                ir::Id::from("out"),
221                width,
222                ir::Direction::Output,
223                ir::Attributes::default(),
224            )],
225        );
226
227        // Add constant to the Component.
228        self.component.cells.add(Rc::clone(&cell));
229
230        cell
231    }
232
233    /// Consturcts a primitive cell of type `primitive`.
234    /// The name of the cell is guaranteed to start with `prefix`.
235    /// Adds this cell to the underlying component and returns a reference
236    /// to the Cell.
237    ///
238    /// For example:
239    /// ```
240    /// // Construct a std_reg.
241    /// builder.add_primitive("fsm", "std_reg", vec![32]);
242    /// ```
243    pub fn add_primitive<Pre, Prim>(
244        &mut self,
245        prefix: Pre,
246        primitive: Prim,
247        param_values: &[u64],
248    ) -> RRC<ir::Cell>
249    where
250        Pre: Into<ir::Id> + ToString + Clone,
251        Prim: Into<ir::Id>,
252    {
253        self.try_add_primitive(prefix, primitive, param_values)
254            .expect("failed to add primitive:")
255    }
256
257    /// Result variant of [[ir::Builder::add_primitive()]].
258    pub fn try_add_primitive<Pre, Prim>(
259        &mut self,
260        prefix: Pre,
261        primitive: Prim,
262        param_values: &[u64],
263    ) -> CalyxResult<RRC<ir::Cell>>
264    where
265        Pre: Into<ir::Id> + ToString + Clone,
266        Prim: Into<ir::Id>,
267    {
268        let prim_id = primitive.into();
269        let prim = &self.lib.get_primitive(prim_id);
270        let (param_binding, ports) = prim.resolve(param_values)?;
271
272        let name = self.component.generate_name(prefix);
273        let cell = Self::cell_from_signature(
274            name,
275            ir::CellType::Primitive {
276                name: prim_id,
277                param_binding: Box::new(param_binding),
278                is_comb: prim.is_comb,
279                latency: prim.latency,
280            },
281            ports,
282        );
283        if self.generated {
284            cell.borrow_mut().add_attribute(BoolAttr::Generated, 1);
285        }
286        self.component.cells.add(Rc::clone(&cell));
287        Ok(cell)
288    }
289
290    /// Add a component instance to this component using its name and port
291    /// signature.
292    pub fn add_component<Pre>(
293        &mut self,
294        prefix: Pre,
295        component: Pre,
296        sig: Vec<PortDef<u64>>,
297    ) -> RRC<ir::Cell>
298    where
299        Pre: Into<ir::Id> + ToString + Clone,
300    {
301        let name = self.component.generate_name(prefix);
302        let cell = Self::cell_from_signature(
303            name,
304            CellType::Component {
305                name: component.into(),
306            },
307            sig,
308        );
309        if self.generated {
310            cell.borrow_mut().add_attribute(BoolAttr::Generated, 1);
311        }
312        self.component.cells.add(Rc::clone(&cell));
313        cell
314    }
315
316    /// Construct an assignment.
317    pub fn build_assignment<T>(
318        &self,
319        dst: RRC<ir::Port>,
320        src: RRC<ir::Port>,
321        guard: ir::Guard<T>,
322    ) -> ir::Assignment<T> {
323        // Valid the ports if required.
324        if self.validate {
325            self.is_port_well_formed(&dst.borrow());
326            self.is_port_well_formed(&src.borrow());
327            guard
328                .all_ports()
329                .into_iter()
330                .for_each(|p| self.is_port_well_formed(&p.borrow()));
331        }
332        // If the ports have different widths, error out.
333        debug_assert!(
334            src.borrow().width == dst.borrow().width,
335            "Invalid assignment. `{}.{}' and `{}.{}' have different widths",
336            src.borrow().get_parent_name(),
337            src.borrow().name,
338            dst.borrow().get_parent_name(),
339            dst.borrow().name,
340        );
341        // If ports have the wrong directions, error out.
342        debug_assert!(
343            // Allow for both Input and Inout ports.
344            src.borrow().direction != ir::Direction::Input,
345            "Not an ouput port: {}.{}",
346            src.borrow().get_parent_name(),
347            src.borrow().name
348        );
349        debug_assert!(
350            // Allow for both Input and Inout ports.
351            dst.borrow().direction != ir::Direction::Output,
352            "Not an input port: {}.{}",
353            dst.borrow().get_parent_name(),
354            dst.borrow().name
355        );
356
357        ir::Assignment {
358            dst,
359            src,
360            guard: Box::new(guard),
361            attributes: ir::Attributes::default(),
362        }
363    }
364
365    ///////////////////// Internal functions/////////////////////////////////
366    /// VALIDATE: Check if the component contains the cell/group associated
367    /// with the port exists in the Component.
368    /// Validate methods panic! in order to generate a stacktrace to the
369    /// offending code.
370    fn is_port_well_formed(&self, port: &ir::Port) {
371        match &port.parent {
372            ir::PortParent::Cell(cell_wref) => {
373                let cell_ref = cell_wref.internal.upgrade().expect("Weak reference to port's parent cell points to nothing. This usually means that the Component did not retain a pointer to the Cell.");
374
375                let cell = &cell_ref.borrow();
376                self.component.find_cell(cell.name()).expect("Port's parent cell not present in the component. Add the cell to the component before using the Port.");
377            }
378            ir::PortParent::Group(group_wref) => {
379                let group_ref = group_wref.internal.upgrade().expect("Weak reference to hole's parent group points to nothing. This usually means that the Component did not retain a pointer to the Group.");
380
381                let group = &group_ref.borrow();
382                self.component.find_group(group.name()).expect("Hole's parent cell not present in the component. Add the group to the component before using the Hole.");
383            }
384            ir::PortParent::FSM(_) => todo!(),
385            ir::PortParent::StaticGroup(group_wref) => {
386                let group_ref = group_wref.internal.upgrade().expect("Weak reference to hole's parent group points to nothing. This usually means that the Component did not retain a pointer to the Group.");
387
388                let group = &group_ref.borrow();
389                self.component.find_static_group(group.name()).expect("Hole's parent cell not present in the component. Add the static group to the component before using the Hole.");
390            }
391        };
392    }
393    /// Construct a cell from input/output signature.
394    /// Input and output port definition in the form (name, width).
395    pub(super) fn cell_from_signature(
396        name: ir::Id,
397        typ: ir::CellType,
398        ports: Vec<ir::PortDef<u64>>,
399    ) -> RRC<ir::Cell> {
400        let cell = ir::rrc(ir::Cell::new(name, typ));
401        ports.into_iter().for_each(|pd| {
402            let port = ir::rrc(ir::Port {
403                name: pd.name(),
404                width: pd.width,
405                direction: pd.direction,
406                parent: ir::PortParent::Cell(WRC::from(&cell)),
407                attributes: pd.attributes,
408            });
409            cell.borrow_mut().ports.push(port);
410        });
411        cell
412    }
413}