1use super::cfg::ControlFlowGraph;
9use super::types::*;
10use std::collections::{HashMap, HashSet};
11
12#[derive(Debug, Clone)]
14pub struct LivenessResult {
15 pub live_in: HashMap<BasicBlockId, HashSet<SlotId>>,
17 pub live_out: HashMap<BasicBlockId, HashSet<SlotId>>,
19}
20
21impl LivenessResult {
22 pub fn is_live_after(
25 &self,
26 block: BasicBlockId,
27 stmt_idx: usize,
28 slot: SlotId,
29 mir: &MirFunction,
30 ) -> bool {
31 let bb = mir.block(block);
32
33 let mut live = self.live_out.get(&block).cloned().unwrap_or_default();
35
36 for i in (stmt_idx + 1..bb.statements.len()).rev() {
39 let stmt = &bb.statements[i];
40 update_liveness_for_statement(&mut live, &stmt.kind);
41 }
42
43 add_terminator_uses(&mut live, &bb.terminator.kind);
45
46 live.contains(&slot)
47 }
48
49 pub fn is_live_at_entry(&self, block: BasicBlockId, slot: SlotId) -> bool {
51 self.live_in
52 .get(&block)
53 .map_or(false, |set| set.contains(&slot))
54 }
55}
56
57pub fn compute_liveness(mir: &MirFunction, cfg: &ControlFlowGraph) -> LivenessResult {
62 let mut live_in: HashMap<BasicBlockId, HashSet<SlotId>> = HashMap::new();
63 let mut live_out: HashMap<BasicBlockId, HashSet<SlotId>> = HashMap::new();
64
65 for block in &mir.blocks {
67 live_in.insert(block.id, HashSet::new());
68 live_out.insert(block.id, HashSet::new());
69 }
70
71 let mut changed = true;
73 while changed {
74 changed = false;
75
76 let rpo = cfg.reverse_postorder();
79 for &block_id in rpo.iter().rev() {
80 let block = mir.block(block_id);
81
82 let mut new_live_out = HashSet::new();
84 for &succ in cfg.successors(block_id) {
85 if let Some(succ_in) = live_in.get(&succ) {
86 new_live_out.extend(succ_in);
87 }
88 }
89
90 let mut new_live_in = new_live_out.clone();
92
93 add_terminator_uses(&mut new_live_in, &block.terminator.kind);
95
96 for stmt in block.statements.iter().rev() {
98 update_liveness_for_statement(&mut new_live_in, &stmt.kind);
99 }
100
101 if new_live_in != *live_in.get(&block_id).unwrap_or(&HashSet::new()) {
103 changed = true;
104 live_in.insert(block_id, new_live_in);
105 }
106 if new_live_out != *live_out.get(&block_id).unwrap_or(&HashSet::new()) {
107 changed = true;
108 live_out.insert(block_id, new_live_out);
109 }
110 }
111 }
112
113 LivenessResult { live_in, live_out }
114}
115
116fn update_liveness_for_statement(live: &mut HashSet<SlotId>, kind: &StatementKind) {
118 match kind {
119 StatementKind::Assign(place, rvalue) => {
120 if let Place::Local(slot) = place {
122 live.remove(slot);
123 }
124 add_rvalue_uses(live, rvalue);
126 }
127 StatementKind::Drop(place) => {
128 live.insert(place.root_local());
130 }
131 StatementKind::TaskBoundary(operands, _kind) => {
132 for operand in operands {
133 add_operand_uses(live, operand);
134 }
135 }
136 StatementKind::ClosureCapture { operands, .. } => {
137 for operand in operands {
138 add_operand_uses(live, operand);
139 }
140 }
141 StatementKind::ArrayStore { operands, .. } => {
142 for operand in operands {
143 add_operand_uses(live, operand);
144 }
145 }
146 StatementKind::ObjectStore { operands, .. } => {
147 for operand in operands {
148 add_operand_uses(live, operand);
149 }
150 }
151 StatementKind::EnumStore { operands, .. } => {
152 for operand in operands {
153 add_operand_uses(live, operand);
154 }
155 }
156 StatementKind::Nop => {}
157 }
158}
159
160fn add_rvalue_uses(live: &mut HashSet<SlotId>, rvalue: &Rvalue) {
162 match rvalue {
163 Rvalue::Use(op) | Rvalue::Clone(op) | Rvalue::UnaryOp(_, op) => {
164 add_operand_uses(live, op);
165 }
166 Rvalue::Borrow(_, place) => {
167 live.insert(place.root_local());
168 }
169 Rvalue::BinaryOp(_, lhs, rhs) => {
170 add_operand_uses(live, lhs);
171 add_operand_uses(live, rhs);
172 }
173 Rvalue::Aggregate(ops) => {
174 for op in ops {
175 add_operand_uses(live, op);
176 }
177 }
178 Rvalue::EnumTest { operand, .. }
179 | Rvalue::EnumPayload { operand, .. }
180 | Rvalue::TypePatternTest { operand, .. }
181 | Rvalue::EnumDiscriminantTest { operand, .. } => {
182 add_operand_uses(live, operand);
183 }
184 }
185}
186
187fn add_operand_uses(live: &mut HashSet<SlotId>, op: &Operand) {
189 match op {
190 Operand::Copy(place) | Operand::Move(place) | Operand::MoveExplicit(place) => {
191 live.insert(place.root_local());
192 }
193 Operand::Constant(_) => {}
194 }
195}
196
197fn add_terminator_uses(live: &mut HashSet<SlotId>, kind: &TerminatorKind) {
199 match kind {
200 TerminatorKind::SwitchBool { operand, .. } => {
201 add_operand_uses(live, operand);
202 }
203 TerminatorKind::Call { func, args, .. } => {
204 add_operand_uses(live, func);
205 for arg in args {
206 add_operand_uses(live, arg);
207 }
208 }
209 TerminatorKind::Goto(_) | TerminatorKind::Return | TerminatorKind::Unreachable => {}
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use super::*;
216
217 fn span() -> shape_ast::ast::Span {
218 shape_ast::ast::Span { start: 0, end: 1 }
219 }
220
221 fn make_stmt(kind: StatementKind, point: u32) -> MirStatement {
222 MirStatement {
223 kind,
224 span: span(),
225 point: Point(point),
226 }
227 }
228
229 fn make_terminator(kind: TerminatorKind) -> Terminator {
230 Terminator { kind, span: span() }
231 }
232
233 #[test]
234 fn test_simple_liveness() {
235 let mir = MirFunction {
237 name: "test".to_string(),
238 blocks: vec![BasicBlock {
239 id: BasicBlockId(0),
240 statements: vec![
241 make_stmt(
242 StatementKind::Assign(
243 Place::Local(SlotId(0)),
244 Rvalue::Use(Operand::Constant(MirConstant::Int(1))),
245 ),
246 0,
247 ),
248 make_stmt(
249 StatementKind::Assign(
250 Place::Local(SlotId(1)),
251 Rvalue::Use(Operand::Copy(Place::Local(SlotId(0)))),
252 ),
253 1,
254 ),
255 ],
256 terminator: make_terminator(TerminatorKind::Return),
257 }],
258 num_locals: 2,
259 param_slots: vec![],
260 param_reference_kinds: vec![],
261 local_types: vec![LocalTypeInfo::Copy, LocalTypeInfo::Copy],
262 span: span(),
263 field_name_table: std::collections::HashMap::new(),
264 local_struct_type_names: std::collections::HashMap::new(),
265 local_typed_array_element_types: std::collections::HashMap::new(),
266 local_declared_scalar_types: std::collections::HashMap::new(),
267 };
268
269 let cfg = ControlFlowGraph::build(&mir);
270 let liveness = compute_liveness(&mir, &cfg);
271
272 assert!(liveness.is_live_after(BasicBlockId(0), 0, SlotId(0), &mir));
274 assert!(!liveness.is_live_after(BasicBlockId(0), 1, SlotId(0), &mir));
276 }
277
278 #[test]
279 fn test_branch_liveness() {
280 let mir = MirFunction {
285 name: "test".to_string(),
286 blocks: vec![
287 BasicBlock {
288 id: BasicBlockId(0),
289 statements: vec![make_stmt(
290 StatementKind::Assign(
291 Place::Local(SlotId(0)),
292 Rvalue::Use(Operand::Constant(MirConstant::Int(1))),
293 ),
294 0,
295 )],
296 terminator: make_terminator(TerminatorKind::SwitchBool {
297 operand: Operand::Copy(Place::Local(SlotId(2))),
298 true_bb: BasicBlockId(1),
299 false_bb: BasicBlockId(2),
300 }),
301 },
302 BasicBlock {
303 id: BasicBlockId(1),
304 statements: vec![make_stmt(
305 StatementKind::Assign(
306 Place::Local(SlotId(1)),
307 Rvalue::Use(Operand::Copy(Place::Local(SlotId(0)))),
308 ),
309 1,
310 )],
311 terminator: make_terminator(TerminatorKind::Goto(BasicBlockId(3))),
312 },
313 BasicBlock {
314 id: BasicBlockId(2),
315 statements: vec![],
316 terminator: make_terminator(TerminatorKind::Goto(BasicBlockId(3))),
317 },
318 BasicBlock {
319 id: BasicBlockId(3),
320 statements: vec![],
321 terminator: make_terminator(TerminatorKind::Return),
322 },
323 ],
324 num_locals: 3,
325 param_slots: vec![],
326 param_reference_kinds: vec![],
327 local_types: vec![
328 LocalTypeInfo::Copy,
329 LocalTypeInfo::Copy,
330 LocalTypeInfo::Copy,
331 ],
332 span: span(),
333 field_name_table: std::collections::HashMap::new(),
334 local_struct_type_names: std::collections::HashMap::new(),
335 local_typed_array_element_types: std::collections::HashMap::new(),
336 local_declared_scalar_types: std::collections::HashMap::new(),
337 };
338
339 let cfg = ControlFlowGraph::build(&mir);
340 let liveness = compute_liveness(&mir, &cfg);
341
342 assert!(
345 liveness
346 .live_out
347 .get(&BasicBlockId(0))
348 .map_or(false, |s| s.contains(&SlotId(0)))
349 );
350 }
351}