calyx_opt/passes/
lower_guards.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
use crate::traversal::{Action, Named, VisResult, Visitor};
use calyx_ir::{self as ir, RRC};
use ir::Nothing;

/// Lowers guards into a purely structural representation. After this pass,
/// all guards are guaranteed to be either [ir::Guard::True] or [ir::Guard::Port].
#[derive(Default)]
pub struct LowerGuards;

impl Named for LowerGuards {
    fn name() -> &'static str {
        "lower-guards"
    }

    fn description() -> &'static str {
        "lower guards to a purely structural representation"
    }
}

fn guard_to_prim(guard: &ir::Guard<ir::Nothing>) -> Option<String> {
    let var_name = match guard {
        ir::Guard::Or(..) => "or",
        ir::Guard::And(..) => "and",
        ir::Guard::CompOp(op, _, _) => match op {
            ir::PortComp::Eq => "eq",
            ir::PortComp::Neq => "neq",
            ir::PortComp::Gt => "gt",
            ir::PortComp::Lt => "lt",
            ir::PortComp::Geq => "ge",
            ir::PortComp::Leq => "le",
        },
        ir::Guard::True | ir::Guard::Not(_) | ir::Guard::Port(_) => {
            return None;
        }
        ir::Guard::Info(_) => {
            panic!("Guards Shouldn't Take Info at this Point")
        }
    };
    Some(var_name.to_string())
}

fn lower_guard(
    guard: ir::Guard<Nothing>,
    assigns: &mut Vec<ir::Assignment<Nothing>>,
    builder: &mut ir::Builder,
) -> RRC<ir::Port> {
    let maybe_prim = guard_to_prim(&guard);
    match guard {
        ir::Guard::And(l, r) | ir::Guard::Or(l, r) => {
            let l_low = lower_guard(*l, assigns, builder);
            let r_low = lower_guard(*r, assigns, builder);

            let prim = maybe_prim.unwrap();
            let prim_name = format!("std_{}", prim);
            let prim_cell =
                builder.add_primitive(prim, prim_name, &[l_low.borrow().width]);
            let prim = prim_cell.borrow();

            assigns.push(builder.build_assignment(
                prim.get("left"),
                l_low,
                ir::Guard::True,
            ));
            assigns.push(builder.build_assignment(
                prim.get("right"),
                r_low,
                ir::Guard::True,
            ));
            prim.get("out")
        }

        ir::Guard::CompOp(_, l, r) => {
            let prim = maybe_prim.unwrap();
            let prim_name = format!("std_{}", prim);
            let prim_cell =
                builder.add_primitive(prim, prim_name, &[l.borrow().width]);
            let prim = prim_cell.borrow();

            assigns.push(builder.build_assignment(
                prim.get("left"),
                l,
                ir::Guard::True,
            ));
            assigns.push(builder.build_assignment(
                prim.get("right"),
                r,
                ir::Guard::True,
            ));
            prim.get("out")
        }
        ir::Guard::Not(g) => {
            let g_low = lower_guard(*g, assigns, builder);
            let not_prim = builder.add_primitive(
                "not",
                "std_not",
                &[g_low.borrow().width],
            );
            let not = not_prim.borrow();
            assigns.push(builder.build_assignment(
                not.get("in"),
                g_low,
                ir::Guard::True,
            ));
            not.get("out")
        }
        ir::Guard::True => builder.add_constant(1, 1).borrow().get("out"),
        ir::Guard::Port(p) => p,
        ir::Guard::Info(_) => panic!("shouldn't have info ports at this point"),
    }
}

fn lower_assigns(
    assigns: Vec<ir::Assignment<Nothing>>,
    builder: &mut ir::Builder,
) -> Vec<ir::Assignment<Nothing>> {
    let mut new_assigns = Vec::with_capacity(assigns.len() * 2);
    for mut assign in assigns {
        let g = std::mem::take(&mut assign.guard);
        let mut assigns = vec![];
        let port = lower_guard(*g, &mut assigns, builder);
        assign.guard = Box::new(port.into());
        new_assigns.extend(assigns);
        new_assigns.push(assign);
    }
    new_assigns
}

impl Visitor for LowerGuards {
    fn start(
        &mut self,
        comp: &mut ir::Component,
        sigs: &ir::LibrarySignatures,
        _comps: &[ir::Component],
    ) -> VisResult {
        let mut builder = ir::Builder::new(comp, sigs);

        // Transform continuous assignments
        let conts: Vec<_> =
            builder.component.continuous_assignments.drain(..).collect();
        let new_conts = lower_assigns(conts, &mut builder);
        builder.component.continuous_assignments = new_conts;

        // Transform group assignments
        let groups = builder
            .component
            .get_groups_mut()
            .drain()
            .inspect(|group| {
                let assigns =
                    group.borrow_mut().assignments.drain(..).collect();
                let new_assigns = lower_assigns(assigns, &mut builder);
                group.borrow_mut().assignments = new_assigns;
            })
            .into();
        builder.component.set_groups(groups);

        /*let static_groups = builder
            .component
            .get_static_groups_mut()
            .drain()
            .map(|group| {
                let assigns =
                    group.borrow_mut().assignments.drain(..).collect();
                let new_assigns = lower_assigns(assigns, &mut builder);
                group.borrow_mut().assignments = new_assigns;
                group
            })
            .into();
        builder.component.set_static_groups(static_groups);*/

        // Transform comb group assignments
        let comb_groups = builder
            .component
            .comb_groups
            .drain()
            .inspect(|group| {
                let assigns =
                    group.borrow_mut().assignments.drain(..).collect();
                let new_assigns = lower_assigns(assigns, &mut builder);
                group.borrow_mut().assignments = new_assigns;
            })
            .into();
        builder.component.comb_groups = comb_groups;

        Ok(Action::Stop)
    }
}