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::{cmp, 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 group and add it to the Component.
64    /// The group is guaranteed to start with `prefix`.
65    /// Returns a reference to the group.
66    pub fn add_group<S>(&mut self, prefix: S) -> RRC<ir::Group>
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        // Check if there is a group with the same name.
78        let group = ir::rrc(ir::Group::new(name));
79
80        // Add default holes to the group.
81        for (name, width) in &[("go", 1), ("done", 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::Group(WRC::from(&group)),
87                attributes: ir::Attributes::default(),
88            });
89            group.borrow_mut().holes.push(hole);
90        }
91
92        // Add the group to the component.
93        self.component.get_groups_mut().add(Rc::clone(&group));
94
95        group
96    }
97
98    /// Construct a new static 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_static_group<S>(
102        &mut self,
103        prefix: S,
104        latency: u64,
105    ) -> RRC<ir::StaticGroup>
106    where
107        S: Into<ir::Id>,
108    {
109        let prefix: ir::Id = prefix.into();
110        assert!(
111            prefix != "",
112            "Cannot construct group with empty name prefix"
113        );
114        let name = self.component.generate_name(prefix);
115
116        // Check if there is a group with the same name.
117        let group = ir::rrc(ir::StaticGroup::new(name, latency));
118
119        // Add default holes to the group.
120        // Static Groups don't need a done hole.
121        // May be beneficial to have a go hole, though (although maybe not)
122        let (name, width) = ("go", 1);
123        let hole = ir::rrc(ir::Port {
124            name: ir::Id::from(name),
125            width,
126            direction: ir::Direction::Inout,
127            parent: ir::PortParent::StaticGroup(WRC::from(&group)),
128            attributes: ir::Attributes::default(),
129        });
130        group.borrow_mut().holes.push(hole);
131
132        // Add the group to the component.
133        self.component
134            .get_static_groups_mut()
135            .add(Rc::clone(&group));
136
137        group
138    }
139
140    /// Construct a combinational group
141    pub fn add_comb_group<S>(&mut self, prefix: S) -> RRC<ir::CombGroup>
142    where
143        S: Into<ir::Id> + ToString + Clone,
144    {
145        let name = self.component.generate_name(prefix);
146
147        // Check if there is a group with the same name.
148        let group = ir::rrc(ir::CombGroup {
149            name,
150            attributes: ir::Attributes::default(),
151            assignments: vec![],
152        });
153
154        // Add the group to the component.
155        self.component.comb_groups.add(Rc::clone(&group));
156
157        group
158    }
159
160    /// Return reference for a constant cell associated with the (val, width)
161    /// pair, building and adding it to the component if needed..
162    /// If the constant does not exist, it is added to the Context.
163    pub fn add_constant(&mut self, val: u64, width: u64) -> RRC<ir::Cell> {
164        // Ensure that the value can fit within the width
165        assert!(
166            val < match width.cmp(&64) {
167                cmp::Ordering::Less => 1 << width,
168                cmp::Ordering::Equal => u64::MAX,
169                cmp::Ordering::Greater =>
170                    panic!("Widths greater than 64 are not supported."),
171            },
172            "Constant value {} cannot fit in {} bits",
173            val,
174            width
175        );
176        let name = ir::Cell::constant_name(val, width);
177        // If this constant has already been instantiated, return the relevant
178        // cell.
179        if let Some(cell) = self.component.cells.find(name) {
180            return Rc::clone(&cell);
181        }
182
183        // Construct this cell if it's not already present in the context.
184        let cell = Self::cell_from_signature(
185            name,
186            ir::CellType::Constant { val, width },
187            vec![ir::PortDef::new(
188                ir::Id::from("out"),
189                width,
190                ir::Direction::Output,
191                ir::Attributes::default(),
192            )],
193        );
194
195        // Add constant to the Component.
196        self.component.cells.add(Rc::clone(&cell));
197
198        cell
199    }
200
201    /// Consturcts a primitive cell of type `primitive`.
202    /// The name of the cell is guaranteed to start with `prefix`.
203    /// Adds this cell to the underlying component and returns a reference
204    /// to the Cell.
205    ///
206    /// For example:
207    /// ```
208    /// // Construct a std_reg.
209    /// builder.add_primitive("fsm", "std_reg", vec![32]);
210    /// ```
211    pub fn add_primitive<Pre, Prim>(
212        &mut self,
213        prefix: Pre,
214        primitive: Prim,
215        param_values: &[u64],
216    ) -> RRC<ir::Cell>
217    where
218        Pre: Into<ir::Id> + ToString + Clone,
219        Prim: Into<ir::Id>,
220    {
221        self.try_add_primitive(prefix, primitive, param_values)
222            .expect("failed to add primitive:")
223    }
224
225    /// Result variant of [[ir::Builder::add_primitive()]].
226    pub fn try_add_primitive<Pre, Prim>(
227        &mut self,
228        prefix: Pre,
229        primitive: Prim,
230        param_values: &[u64],
231    ) -> CalyxResult<RRC<ir::Cell>>
232    where
233        Pre: Into<ir::Id> + ToString + Clone,
234        Prim: Into<ir::Id>,
235    {
236        let prim_id = primitive.into();
237        let prim = &self.lib.get_primitive(prim_id);
238        let (param_binding, ports) = prim.resolve(param_values)?;
239
240        let name = self.component.generate_name(prefix);
241        let cell = Self::cell_from_signature(
242            name,
243            ir::CellType::Primitive {
244                name: prim_id,
245                param_binding: Box::new(param_binding),
246                is_comb: prim.is_comb,
247                latency: prim.latency,
248            },
249            ports,
250        );
251        if self.generated {
252            cell.borrow_mut().add_attribute(BoolAttr::Generated, 1);
253        }
254        self.component.cells.add(Rc::clone(&cell));
255        Ok(cell)
256    }
257
258    /// Add a component instance to this component using its name and port
259    /// signature.
260    pub fn add_component<Pre>(
261        &mut self,
262        prefix: Pre,
263        component: Pre,
264        sig: Vec<PortDef<u64>>,
265    ) -> RRC<ir::Cell>
266    where
267        Pre: Into<ir::Id> + ToString + Clone,
268    {
269        let name = self.component.generate_name(prefix);
270        let cell = Self::cell_from_signature(
271            name,
272            CellType::Component {
273                name: component.into(),
274            },
275            sig,
276        );
277        if self.generated {
278            cell.borrow_mut().add_attribute(BoolAttr::Generated, 1);
279        }
280        self.component.cells.add(Rc::clone(&cell));
281        cell
282    }
283
284    /// Construct an assignment.
285    pub fn build_assignment<T>(
286        &self,
287        dst: RRC<ir::Port>,
288        src: RRC<ir::Port>,
289        guard: ir::Guard<T>,
290    ) -> ir::Assignment<T> {
291        // Valid the ports if required.
292        if self.validate {
293            self.is_port_well_formed(&dst.borrow());
294            self.is_port_well_formed(&src.borrow());
295            guard
296                .all_ports()
297                .into_iter()
298                .for_each(|p| self.is_port_well_formed(&p.borrow()));
299        }
300        // If the ports have different widths, error out.
301        debug_assert!(
302            src.borrow().width == dst.borrow().width,
303            "Invalid assignment. `{}.{}' and `{}.{}' have different widths",
304            src.borrow().get_parent_name(),
305            src.borrow().name,
306            dst.borrow().get_parent_name(),
307            dst.borrow().name,
308        );
309        // If ports have the wrong directions, error out.
310        debug_assert!(
311            // Allow for both Input and Inout ports.
312            src.borrow().direction != ir::Direction::Input,
313            "Not an ouput port: {}.{}",
314            src.borrow().get_parent_name(),
315            src.borrow().name
316        );
317        debug_assert!(
318            // Allow for both Input and Inout ports.
319            dst.borrow().direction != ir::Direction::Output,
320            "Not an input port: {}.{}",
321            dst.borrow().get_parent_name(),
322            dst.borrow().name
323        );
324
325        ir::Assignment {
326            dst,
327            src,
328            guard: Box::new(guard),
329            attributes: ir::Attributes::default(),
330        }
331    }
332
333    ///////////////////// Internal functions/////////////////////////////////
334    /// VALIDATE: Check if the component contains the cell/group associated
335    /// with the port exists in the Component.
336    /// Validate methods panic! in order to generate a stacktrace to the
337    /// offending code.
338    fn is_port_well_formed(&self, port: &ir::Port) {
339        match &port.parent {
340            ir::PortParent::Cell(cell_wref) => {
341                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.");
342
343                let cell = &cell_ref.borrow();
344                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.");
345            }
346            ir::PortParent::Group(group_wref) => {
347                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.");
348
349                let group = &group_ref.borrow();
350                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.");
351            }
352            ir::PortParent::StaticGroup(group_wref) => {
353                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.");
354
355                let group = &group_ref.borrow();
356                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.");
357            }
358        };
359    }
360    /// Construct a cell from input/output signature.
361    /// Input and output port definition in the form (name, width).
362    pub(super) fn cell_from_signature(
363        name: ir::Id,
364        typ: ir::CellType,
365        ports: Vec<ir::PortDef<u64>>,
366    ) -> RRC<ir::Cell> {
367        let cell = ir::rrc(ir::Cell::new(name, typ));
368        ports.into_iter().for_each(|pd| {
369            let port = ir::rrc(ir::Port {
370                name: pd.name(),
371                width: pd.width,
372                direction: pd.direction,
373                parent: ir::PortParent::Cell(WRC::from(&cell)),
374                attributes: pd.attributes,
375            });
376            cell.borrow_mut().ports.push(port);
377        });
378        cell
379    }
380}