-
Notifications
You must be signed in to change notification settings - Fork 52
/
unused_port_removal.rs
200 lines (176 loc) · 7.58 KB
/
unused_port_removal.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
188
189
190
191
192
193
194
195
196
197
198
199
200
use crate::traversal::{
Action, ConstructVisitor, Named, Order, VisResult, Visitor,
};
use calyx_ir::{
BoolAttr, Builder, Cell, CellType, Component, Context, Id,
LibrarySignatures, PortParent, RRC,
};
use calyx_utils::CalyxResult;
use std::collections::{HashMap, HashSet};
pub struct UnusedPortRemoval {
used_ports: HashMap<Id, HashSet<Id>>,
}
impl Named for UnusedPortRemoval {
fn name() -> &'static str {
"unused-port-removal"
}
fn description() -> &'static str {
"Remove unused ports"
}
}
impl ConstructVisitor for UnusedPortRemoval {
fn from(_ctx: &Context) -> CalyxResult<Self>
where
Self: Sized,
{
// create and return an instance of an
// UnusedPortRemoval pass over a single component (?)
let u_p_r = UnusedPortRemoval {
used_ports: HashMap::new(),
};
Ok(u_p_r)
}
// what is this for? clear data after visiting every component?
fn clear_data(&mut self) {}
}
impl Visitor for UnusedPortRemoval {
fn iteration_order() -> Order {
Order::Pre
}
fn start(
&mut self,
comp: &mut Component,
sigs: &LibrarySignatures,
_comps: &[Component],
) -> VisResult {
// By the time we get to analyze the current component, all the ports of this
// component that have been used are the only ones that are ever going to be used,
// so we can compare against the complete set of ports defined in the component
// signature to figure out which ones are not used by any external component <-- verify claim
// get a list of all ports instantiated in the component signature
let all_ports: HashSet<Id> = comp
.signature
.borrow()
.ports()
.iter()
.map(|port| port.borrow().name)
.collect();
// know these these signature-instantiated ports are a super set of ports
// that are instantiated by other components, all of which we have access
// to based on our pre-order traversal :)
let unused_ports: HashSet<Id> = match self.used_ports.get(&comp.name) {
None => all_ports,
Some(used_set) => all_ports
.difference(used_set)
.map(|item: &Id| *item)
.collect(),
};
// runt -i tests/passes/unused-port-removal/simple -d
// if port from signature is an unused port, add an attribute @internal
if comp.name != "main" {
// adds @internal attribute to (up-till-now) unused ports
comp.signature
.borrow_mut()
.ports
.iter_mut()
.for_each(|port| {
let mut port_ref = port.borrow_mut();
let name = port_ref.name;
if unused_ports.contains(&name) {
port_ref.attributes.insert(BoolAttr::Internal, 1);
}
});
// if either the source or a destination of an assignment are unused,
// drop that assignment (meaning we don't care about those guards)
comp.continuous_assignments
.retain(|assign| assign.is_used(&unused_ports));
// for a given group, keep only those assignments with both dest / source used
// retain used assignments for regular groups
comp.get_groups_mut().iter_mut().for_each(|group| {
group
.borrow_mut()
.assignments
.retain(|assign| assign.is_used(&unused_ports))
});
// retain used assignments for static groups
comp.get_static_groups_mut()
.iter_mut()
.for_each(|static_group| {
static_group
.borrow_mut()
.assignments
.retain(|assign| assign.is_used(&unused_ports))
});
// retain used assigments for combinational groups
comp.get_comb_groups_mut()
.iter_mut()
.for_each(|comb_group| {
comb_group
.borrow_mut()
.assignments
.retain(|assign| assign.is_used(&unused_ports))
});
// get widths of the unused ports within the guards of each assignment
let mut port_widths: HashSet<u64> = HashSet::new();
// push port widths of non-static assignments
comp.for_each_assignment(|assign| {
assign.push_guard_port_widths(&mut port_widths, &unused_ports);
});
// push port widths of static assignments
comp.for_each_static_assignment(|assign| {
assign.push_guard_port_widths(&mut port_widths, &unused_ports);
});
// initialize map from port widths to cells
let mut width_to_cell: HashMap<u64, RRC<Cell>> = HashMap::new();
// from set of ports-widths unused in guard, fill in hash mapping widths to Id's of
// new instatiated constant cells in component
let mut builder = Builder::new(comp, sigs);
port_widths.iter().for_each(|port_width| {
let low_const_cell =
builder.add_constant(0, port_width.clone());
width_to_cell.insert(port_width.clone(), low_const_cell);
});
// now, we're simply left with assignments that assign to both used source
// and destination ports; for assignments, it's possible that the guard of an assignment
// uses an unused port; if this is the case, replace with n'b0 signal.
comp.for_each_assignment(|assign| {
// deference to get rid of Box pointer
let guard = (assign.guard).as_mut();
guard.collapse_unused(&mut width_to_cell, &unused_ports);
});
// replace unused ports in static assignment guards with n'b0 signals too
comp.for_each_static_assignment(|assign| {
let guard = (assign.guard).as_mut();
guard.collapse_unused(&mut width_to_cell, &unused_ports);
});
// gets rid of all ports from all unused ports in the component signature
// comp.signature.borrow_mut().ports.retain(|port| {
// !(port.borrow().has_attribute(BoolAttr::Internal))
// });
}
// main way to indicate unused ports:
// insert a mapping from each of this component's children components to
// the ports that each child uses
comp.iter_assignments(|assign| {
assign.iter_ports(|port| {
match port.borrow().parent {
// only care about ports belonging to cells, not groups/static groups
PortParent::Cell(_) => {
match port.borrow().cell_parent().borrow().prototype {
// only care about non-primitives (i.e. components and not registers, etc.)
CellType::Component { name: comp_name } => {
self.used_ports
.entry(comp_name)
.or_default()
.insert(port.borrow().name);
}
_ => (),
}
}
_ => (),
}
});
});
Ok(Action::Continue)
}
}