1
-use std::collections::HashSet;
1
+use std::{cell::RefCell, rc::Rc};
2
3
-use hir::{BlockId, Environment, Function, HIR};
3
+use bumpalo::collections::{CollectIn, Vec};
4
+use hir::{
5
+ BasicBlock, BlockId, Blocks, Environment, Function, Identifier, IdentifierData, IdentifierId,
6
+ IdentifierOperand, Instruction, LValue, MutableRange, Phi, HIR,
7
+};
8
+use indexmap::{IndexMap, IndexSet};
9
use thiserror::Error;
10
11
#[derive(Error, Debug)]
12
#[error("Error constructing SSA form")]
13
pub struct SSAError;
14
10
-pub fn enter_ssa<'a>(env: &'a Environment<'a>, hir: &mut Function<'a>) -> Result<(), SSAError> {
11
- let mut builder = Builder::new(env);
12
- enter_ssa_impl(&mut builder, hir, hir.body.entry)
15
+pub fn enter_ssa<'a>(env: &Environment<'a>, fun: &mut Function<'a>) -> Result<(), SSAError> {
16
+ let blocks = &fun.body.blocks;
17
+ let instructions = &mut fun.body.instructions;
18
+ let mut builder = Builder::new(env, fun.body.entry, blocks);
19
+ for param in &mut fun.params {
20
+ builder.visit_param(param);
21
+ }
22
+ enter_ssa_impl(&mut builder, instructions)?;
23
+
24
+ let mut states = builder.complete();
25
+
26
+ for block in fun.body.blocks.values_mut() {
27
+ let state = states.remove(&block.id).unwrap();
28
+ block.phis = state.phis;
29
+ }
30
+
31
+ Ok(())
32
}
33
15
-struct Builder<'a> {
16
- env: &'a Environment<'a>,
34
+fn enter_ssa_impl<'a, 'e, 'f>(
35
+ builder: &mut Builder<'a, 'e, 'f>,
36
+ instructions: &mut Vec<'a, Instruction<'a>>,
37
+) -> Result<(), SSAError> {
38
+ builder.each_block(|block, builder| {
39
+ for instr_ix in &block.instructions {
40
+ let instr = &mut instructions[usize::from(*instr_ix)];
41
+ instr.each_identifier_store(|store| builder.visit_store(store));
42
+ instr.each_identifier_load(|load| builder.visit_load(load));
43
+ }
44
+ });
45
+ Ok(())
46
+}
47
+
48
+#[derive(Debug)]
49
+struct Builder<'a, 'e, 'f> {
50
+ env: &'e Environment<'a>,
51
+ blocks: &'f Blocks<'a>,
52
+
53
+ states: IndexMap<BlockId, BlockState<'a>>,
54
+ current: BlockId,
55
+ unsealed_predecessors: IndexMap<BlockId, usize>,
56
+ unknown: IndexSet<IdentifierId>,
57
+ context: IndexSet<IdentifierId>,
58
}
59
19
-impl<'a> Builder<'a> {
20
- fn new(env: &'a Environment<'a>) -> Self {
21
- Self { env }
60
+#[derive(Debug)]
61
+struct BlockState<'a> {
62
+ defs: IndexMap<IdentifierId, Identifier<'a>>,
63
+ incomplete_phis: Vec<'a, IncompletePhi<'a>>,
64
+ phis: Vec<'a, Phi<'a>>,
65
+}
66
+
67
+impl<'a> BlockState<'a> {
68
+ fn new(env: &Environment<'a>) -> Self {
69
+ Self {
70
+ defs: Default::default(),
71
+ incomplete_phis: Vec::new_in(&env.allocator),
72
+ phis: Vec::new_in(&env.allocator),
73
+ }
74
}
75
}
76
25
-fn enter_ssa_impl<'a>(
26
- builder: &mut Builder<'a>,
27
- hir: &mut Function<'a>,
28
- root_block_id: BlockId,
29
-) -> Result<(), SSAError> {
30
- let mut visited = HashSet::with_capacity(hir.body.blocks.len());
31
- for (block_id, block) in hir.body.blocks.iter_mut() {
32
- if !visited.insert(*block_id) {
33
- // Visited the same block twice
34
- return Err(SSAError);
77
+#[derive(Debug)]
78
+struct IncompletePhi<'a> {
79
+ old_id: Identifier<'a>,
80
+ new_id: Identifier<'a>,
81
+}
82
+
83
+impl<'a, 'e, 'f> Builder<'a, 'e, 'f> {
84
+ fn new(env: &'e Environment<'a>, entry: BlockId, blocks: &'f Blocks<'a>) -> Self {
85
+ let states = blocks
86
+ .keys()
87
+ .map(|block_id| (*block_id, BlockState::new(env)))
88
+ .collect();
89
+ Self {
90
+ env,
91
+ blocks,
92
+ states,
93
+ current: entry,
94
+ unsealed_predecessors: Default::default(),
95
+ unknown: Default::default(),
96
+ context: Default::default(),
97
+ }
98
+ }
99
+
100
+ fn complete(self) -> IndexMap<BlockId, BlockState<'a>> {
101
+ self.states
102
+ }
103
+
104
+ fn next_ssa_id(&self) -> IdentifierId {
105
+ self.env.next_identifier_id()
106
+ }
107
+
108
+ fn visit_store(&mut self, lvalue: &mut LValue<'a>) -> () {
109
+ let old_identifier = &lvalue.identifier.identifier;
110
+ // TODO: use Result (?)
111
+ assert!(!self.unknown.contains(&old_identifier.id), "EnterSSA: Expected identifier to be defined before being used. Identifier {old_identifier:?} is undefined.");
112
+
113
+ if self.context.contains(&old_identifier.id) {
114
+ let new_identifier = self.get_id_at(self.current, old_identifier);
115
+ lvalue.identifier.identifier = new_identifier;
116
+ return;
117
+ }
118
+
119
+ let new_identifier = self.make_identifier(old_identifier);
120
+ let state = self.states.get_mut(&self.current).unwrap();
121
+ state.defs.insert(old_identifier.id, new_identifier.clone());
122
+ lvalue.identifier.identifier = new_identifier;
123
+ }
124
+
125
+ fn visit_param(&mut self, param: &mut IdentifierOperand<'a>) -> () {
126
+ let old_identifier = ¶m.identifier;
127
+ let new_identifier = self.make_identifier(old_identifier);
128
+ let state = self.states.get_mut(&self.current).unwrap();
129
+ state.defs.insert(old_identifier.id, new_identifier.clone());
130
+ param.identifier = new_identifier;
131
+ }
132
+
133
+ fn visit_load(&mut self, local: &mut IdentifierOperand<'a>) -> () {
134
+ let new_identifier = self.get_id_at(self.current, &local.identifier);
135
+ local.identifier = new_identifier;
136
+ }
137
+
138
+ fn get_id_at(&mut self, block_id: BlockId, old_identifier: &Identifier<'a>) -> Identifier<'a> {
139
+ // Check if we've already resolved this identifier in this block
140
+ let state = self.states.get(&block_id).unwrap();
141
+ if let Some(identifier) = state.defs.get(&old_identifier.id) {
142
+ return identifier.clone();
143
+ }
144
+ // Else we have to look at predecessor blocks: bail if no predecessors
145
+ let block = self.blocks.get(&block_id).unwrap();
146
+ if block.predecessors.is_empty() {
147
+ println!("Unable to find previous id for {old_identifier:?}");
148
+ self.unknown.insert(old_identifier.id);
149
+ return old_identifier.clone();
150
+ }
151
+ // If we haven't visited all predecessors, synthesize a new identifier
152
+ // and save it as an incomplete phi
153
+ if self.unsealed_predecessors.get(&block_id).cloned().unwrap() > 0 {
154
+ let new_identifier = self.make_identifier(old_identifier);
155
+ let state = self.states.get_mut(&block_id).unwrap();
156
+ state.incomplete_phis.push(IncompletePhi {
157
+ old_id: old_identifier.clone(),
158
+ new_id: new_identifier.clone(),
159
+ });
160
+ state.defs.insert(old_identifier.id, new_identifier.clone());
161
+ return new_identifier;
162
+ }
163
+ // If exactly one predecessor, check to see if we have a definition there
164
+ if block.predecessors.len() == 1 {
165
+ let predecessor = block.predecessors.iter().next().unwrap();
166
+ let new_identifier = self.get_id_at(*predecessor, old_identifier);
167
+ let state = self.states.get_mut(&block_id).unwrap();
168
+ state.defs.insert(old_identifier.id, new_identifier.clone());
169
+ return new_identifier;
170
+ }
171
+ // There are multiple predecessors, we may need a phi
172
+ let new_identifier = self.make_identifier(old_identifier);
173
+ let state = self.states.get_mut(&block_id).unwrap();
174
+ state.defs.insert(old_identifier.id, new_identifier.clone());
175
+ self.add_phi(block_id, old_identifier, new_identifier)
176
+ }
177
+
178
+ fn add_phi(
179
+ &mut self,
180
+ block_id: BlockId,
181
+ old_identifier: &Identifier<'a>,
182
+ new_identifier: Identifier<'a>,
183
+ ) -> Identifier<'a> {
184
+ let mut phi = Phi {
185
+ identifier: new_identifier.clone(),
186
+ operands: Default::default(),
187
+ };
188
+ let block = self.blocks.get(&block_id).unwrap();
189
+ let preds = block.predecessors.clone();
190
+ for pred_block_id in preds {
191
+ let pred_id = self.get_id_at(pred_block_id, old_identifier);
192
+ phi.operands.insert(pred_block_id, pred_id);
193
+ }
194
+ let state = self.states.get_mut(&block_id).unwrap();
195
+ state.phis.push(phi);
196
+ new_identifier
197
+ }
198
+
199
+ fn make_identifier(&self, old_identifier: &Identifier<'a>) -> Identifier<'a> {
200
+ let old_data = old_identifier.data.borrow();
201
+ Identifier {
202
+ id: self.next_ssa_id(),
203
+ name: old_identifier.name.clone(),
204
+ data: Rc::new(RefCell::new(IdentifierData {
205
+ mutable_range: MutableRange::new(),
206
+ scope: None,
207
+ type_: old_data.type_.clone(),
208
+ })),
209
+ }
210
+ }
211
+
212
+ fn fix_incomplete_phis(&mut self, block_id: BlockId) -> () {
213
+ let state = self.states.get_mut(&block_id).unwrap();
214
+ let incomplete_phis =
215
+ std::mem::replace(&mut state.incomplete_phis, Vec::new_in(&self.env.allocator));
216
+ for phi in incomplete_phis {
217
+ self.add_phi(block_id, &phi.old_id, phi.new_id);
218
+ }
219
+ }
220
+
221
+ fn each_block<F>(&mut self, mut f: F) -> ()
222
+ where
223
+ F: FnMut(&BasicBlock<'a>, &mut Self) -> (),
224
+ {
225
+ let mut visited = IndexSet::new();
226
+ let block_ids: Vec<_> = self.blocks.keys().cloned().collect_in(self.env.allocator);
227
+ for block_id in block_ids {
228
+ visited.insert(block_id);
229
+ self.current = block_id;
230
+ let block = self.blocks.get(&block_id).unwrap();
231
+ f(block, self);
232
+ let successors = block.terminal.value.successors();
233
+ for successor in successors {
234
+ let block = self.blocks.get(&successor).unwrap();
235
+ let count = self
236
+ .unsealed_predecessors
237
+ .get(&successor)
238
+ .cloned()
239
+ .unwrap_or(block.predecessors.len())
240
+ - 1;
241
+ self.unsealed_predecessors.insert(successor, count);
242
+ if count == 0 && visited.contains(&successor) {
243
+ self.fix_incomplete_phis(successor)
244
+ }
245
+ }
246
}
247
}
37
- Ok(())
248
}