1use super::cfg::ControlFlowGraph;
9use super::types::*;
10use std::collections::{HashMap, HashSet};
11
12#[derive(Debug)]
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 }
179}
180
181fn add_operand_uses(live: &mut HashSet<SlotId>, op: &Operand) {
183 match op {
184 Operand::Copy(place) | Operand::Move(place) | Operand::MoveExplicit(place) => {
185 live.insert(place.root_local());
186 }
187 Operand::Constant(_) => {}
188 }
189}
190
191fn add_terminator_uses(live: &mut HashSet<SlotId>, kind: &TerminatorKind) {
193 match kind {
194 TerminatorKind::SwitchBool { operand, .. } => {
195 add_operand_uses(live, operand);
196 }
197 TerminatorKind::Call { func, args, .. } => {
198 add_operand_uses(live, func);
199 for arg in args {
200 add_operand_uses(live, arg);
201 }
202 }
203 TerminatorKind::Goto(_) | TerminatorKind::Return | TerminatorKind::Unreachable => {}
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210
211 fn span() -> shape_ast::ast::Span {
212 shape_ast::ast::Span { start: 0, end: 1 }
213 }
214
215 fn make_stmt(kind: StatementKind, point: u32) -> MirStatement {
216 MirStatement {
217 kind,
218 span: span(),
219 point: Point(point),
220 }
221 }
222
223 fn make_terminator(kind: TerminatorKind) -> Terminator {
224 Terminator { kind, span: span() }
225 }
226
227 #[test]
228 fn test_simple_liveness() {
229 let mir = MirFunction {
231 name: "test".to_string(),
232 blocks: vec![BasicBlock {
233 id: BasicBlockId(0),
234 statements: vec![
235 make_stmt(
236 StatementKind::Assign(
237 Place::Local(SlotId(0)),
238 Rvalue::Use(Operand::Constant(MirConstant::Int(1))),
239 ),
240 0,
241 ),
242 make_stmt(
243 StatementKind::Assign(
244 Place::Local(SlotId(1)),
245 Rvalue::Use(Operand::Copy(Place::Local(SlotId(0)))),
246 ),
247 1,
248 ),
249 ],
250 terminator: make_terminator(TerminatorKind::Return),
251 }],
252 num_locals: 2,
253 param_slots: vec![],
254 param_reference_kinds: vec![],
255 local_types: vec![LocalTypeInfo::Copy, LocalTypeInfo::Copy],
256 span: span(),
257 };
258
259 let cfg = ControlFlowGraph::build(&mir);
260 let liveness = compute_liveness(&mir, &cfg);
261
262 assert!(liveness.is_live_after(BasicBlockId(0), 0, SlotId(0), &mir));
264 assert!(!liveness.is_live_after(BasicBlockId(0), 1, SlotId(0), &mir));
266 }
267
268 #[test]
269 fn test_branch_liveness() {
270 let mir = MirFunction {
275 name: "test".to_string(),
276 blocks: vec![
277 BasicBlock {
278 id: BasicBlockId(0),
279 statements: vec![make_stmt(
280 StatementKind::Assign(
281 Place::Local(SlotId(0)),
282 Rvalue::Use(Operand::Constant(MirConstant::Int(1))),
283 ),
284 0,
285 )],
286 terminator: make_terminator(TerminatorKind::SwitchBool {
287 operand: Operand::Copy(Place::Local(SlotId(2))),
288 true_bb: BasicBlockId(1),
289 false_bb: BasicBlockId(2),
290 }),
291 },
292 BasicBlock {
293 id: BasicBlockId(1),
294 statements: vec![make_stmt(
295 StatementKind::Assign(
296 Place::Local(SlotId(1)),
297 Rvalue::Use(Operand::Copy(Place::Local(SlotId(0)))),
298 ),
299 1,
300 )],
301 terminator: make_terminator(TerminatorKind::Goto(BasicBlockId(3))),
302 },
303 BasicBlock {
304 id: BasicBlockId(2),
305 statements: vec![],
306 terminator: make_terminator(TerminatorKind::Goto(BasicBlockId(3))),
307 },
308 BasicBlock {
309 id: BasicBlockId(3),
310 statements: vec![],
311 terminator: make_terminator(TerminatorKind::Return),
312 },
313 ],
314 num_locals: 3,
315 param_slots: vec![],
316 param_reference_kinds: vec![],
317 local_types: vec![
318 LocalTypeInfo::Copy,
319 LocalTypeInfo::Copy,
320 LocalTypeInfo::Copy,
321 ],
322 span: span(),
323 };
324
325 let cfg = ControlFlowGraph::build(&mir);
326 let liveness = compute_liveness(&mir, &cfg);
327
328 assert!(
331 liveness
332 .live_out
333 .get(&BasicBlockId(0))
334 .map_or(false, |s| s.contains(&SlotId(0)))
335 );
336 }
337}