1use crate::ir::{IrStatement, IrFunction};
13use std::collections::{HashMap, HashSet};
14use crate::debug_println;
15
16#[derive(Debug, Clone, PartialEq)]
17pub enum UseType {
18 Read, Write, Escape, FunctionArg, InLoop, InCondition, }
25
26#[derive(Debug, Clone)]
27pub struct UseInfo {
28 pub statement_idx: usize,
29 pub use_type: UseType,
30}
31
32pub struct LivenessAnalyzer {
33 uses: HashMap<String, Vec<UseInfo>>,
35 current_idx: usize,
37 in_loop_depth: usize,
39 in_conditional_depth: usize,
41}
42
43impl LivenessAnalyzer {
44 pub fn new() -> Self {
45 Self {
46 uses: HashMap::new(),
47 current_idx: 0,
48 in_loop_depth: 0,
49 in_conditional_depth: 0,
50 }
51 }
52
53 pub fn analyze(&mut self, function: &IrFunction) -> HashMap<String, usize> {
56 let first_block = function.cfg.node_weights().next();
59
60 if let Some(block) = first_block {
61 debug_println!("LIVENESS: Analyzing function with {} statements", block.statements.len());
62
63 self.collect_uses(&block.statements);
65 } else {
66 debug_println!("LIVENESS: No basic blocks found in function");
67 }
68
69 let last_uses = self.compute_last_uses();
71
72 debug_println!("LIVENESS: Found {} variables with determinable last use", last_uses.len());
73 for (var, idx) in &last_uses {
74 debug_println!("LIVENESS: '{}' last used at statement {}", var, idx);
75 }
76
77 last_uses
78 }
79
80 fn collect_uses(&mut self, statements: &[IrStatement]) {
81 for (idx, stmt) in statements.iter().enumerate() {
82 self.current_idx = idx;
83 self.collect_uses_from_statement(stmt);
84 }
85 }
86
87 fn collect_uses_from_statement(&mut self, stmt: &IrStatement) {
88 match stmt {
89 IrStatement::Borrow { from, to, .. } => {
90 self.record_use(from, UseType::Read);
92 self.record_use(to, UseType::Write);
94 }
95
96 IrStatement::Move { from, to } => {
97 self.record_use(from, UseType::Read);
99 self.record_use(to, UseType::Write);
101 }
102
103 IrStatement::Assign { lhs, rhs } => {
104 match rhs {
106 crate::ir::IrExpression::Variable(var) => {
107 self.record_use(var, UseType::Read);
108 }
109 crate::ir::IrExpression::Move(var) => {
110 self.record_use(var, UseType::Read);
111 }
112 crate::ir::IrExpression::Borrow(var, _) => {
113 self.record_use(var, UseType::Read);
114 }
115 crate::ir::IrExpression::New(_) => {
116 }
118 crate::ir::IrExpression::Literal(_) => {
119 }
121 }
122 self.record_use(lhs, UseType::Write);
124 }
125
126 IrStatement::Return { value } => {
127 if let Some(var) = value {
129 self.record_use(var, UseType::Escape);
130 }
131 }
132
133 IrStatement::CallExpr { args, result, .. } => {
134 for arg in args {
136 self.record_use(arg, UseType::FunctionArg);
137 }
138 if let Some(res) = result {
140 self.record_use(res, UseType::Write);
141 }
142 }
143
144 IrStatement::UseVariable { var, .. } => {
145 let use_type = if self.in_loop_depth > 0 {
147 UseType::InLoop
148 } else if self.in_conditional_depth > 0 {
149 UseType::InCondition
150 } else {
151 UseType::Read
152 };
153 self.record_use(var, use_type);
154 }
155
156 IrStatement::UseField { object, .. } => {
157 let use_type = if self.in_loop_depth > 0 {
159 UseType::InLoop
160 } else {
161 UseType::Read
162 };
163 self.record_use(object, use_type);
164 }
165
166 IrStatement::MoveField { object, to, .. } => {
167 self.record_use(object, UseType::Read);
169 self.record_use(to, UseType::Write);
170 }
171
172 IrStatement::BorrowField { object, to, .. } => {
173 self.record_use(object, UseType::Read);
175 self.record_use(to, UseType::Write);
176 }
177
178 IrStatement::EnterLoop => {
179 self.in_loop_depth += 1;
180 }
181
182 IrStatement::ExitLoop => {
183 if self.in_loop_depth > 0 {
184 self.in_loop_depth -= 1;
185 }
186 }
187
188 IrStatement::If { then_branch, else_branch } => {
189 self.in_conditional_depth += 1;
190
191 self.collect_uses(then_branch);
193
194 if let Some(else_stmts) = else_branch {
196 self.collect_uses(else_stmts);
197 }
198
199 self.in_conditional_depth -= 1;
200 }
201
202 IrStatement::PackExpansion { pack_name, operation } => {
203 let use_type = if operation == "move" || operation == "forward" {
205 UseType::Read } else {
207 UseType::Read };
209 self.record_use(pack_name, use_type);
210 }
211
212 IrStatement::EnterScope |
214 IrStatement::ExitScope |
215 IrStatement::EnterUnsafe |
216 IrStatement::ExitUnsafe |
217 IrStatement::Drop(_) |
218 IrStatement::ImplicitDrop { .. } |
219 IrStatement::LambdaCapture { .. } |
220 IrStatement::VarDecl { .. } => {}
221 }
222 }
223
224 fn record_use(&mut self, var: &str, use_type: UseType) {
225 if var.starts_with('_') || var.contains('.') {
227 return;
228 }
229
230 debug_println!("LIVENESS: Recording use of '{}' at index {} (type: {:?})",
231 var, self.current_idx, use_type);
232
233 let use_info = UseInfo {
234 statement_idx: self.current_idx,
235 use_type,
236 };
237
238 self.uses.entry(var.to_string()).or_default().push(use_info);
239 }
240
241 fn compute_last_uses(&self) -> HashMap<String, usize> {
242 let mut last_uses = HashMap::new();
243
244 for (var, uses) in &self.uses {
245 if uses.iter().any(|u| matches!(u.use_type, UseType::Escape)) {
249 debug_println!("LIVENESS: '{}' escapes scope - not clearing", var);
250 continue;
251 }
252
253 if uses.iter().any(|u| matches!(u.use_type, UseType::InLoop)) {
255 debug_println!("LIVENESS: '{}' used in loop - not clearing", var);
256 continue;
257 }
258
259 if uses.iter().any(|u| matches!(u.use_type, UseType::FunctionArg)) {
261 debug_println!("LIVENESS: '{}' passed to function - not clearing", var);
262 continue;
263 }
264
265 let last_read_or_use = uses.iter()
267 .filter(|u| matches!(u.use_type, UseType::Read | UseType::InCondition))
268 .map(|u| u.statement_idx)
269 .max();
270
271 if let Some(last_idx) = last_read_or_use {
272 debug_println!("LIVENESS: '{}' has last use (read) at statement {}", var, last_idx);
273 last_uses.insert(var.clone(), last_idx);
274 }
275 }
278
279 last_uses
280 }
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_liveness_simple_sequence() {
289 let mut analyzer = LivenessAnalyzer::new();
292 assert_eq!(analyzer.uses.len(), 0);
293 }
294
295 #[test]
296 fn test_conservative_loop() {
297 let mut analyzer = LivenessAnalyzer::new();
299
300 analyzer.in_loop_depth = 1;
302 analyzer.current_idx = 5;
303 analyzer.record_use("r", UseType::InLoop);
304
305 let last_uses = analyzer.compute_last_uses();
306
307 assert!(!last_uses.contains_key("r"));
309 }
310
311 #[test]
312 fn test_escaped_variable() {
313 let mut analyzer = LivenessAnalyzer::new();
315
316 analyzer.current_idx = 5;
317 analyzer.record_use("r", UseType::Escape);
318
319 let last_uses = analyzer.compute_last_uses();
320
321 assert!(!last_uses.contains_key("r"));
323 }
324
325 #[test]
326 fn test_function_argument() {
327 let mut analyzer = LivenessAnalyzer::new();
329
330 analyzer.current_idx = 5;
331 analyzer.record_use("r", UseType::FunctionArg);
332
333 let last_uses = analyzer.compute_last_uses();
334
335 assert!(!last_uses.contains_key("r"));
337 }
338
339 #[test]
340 fn test_simple_read_sequence() {
341 let mut analyzer = LivenessAnalyzer::new();
343
344 analyzer.current_idx = 2;
345 analyzer.record_use("r", UseType::Write); analyzer.current_idx = 3;
347 analyzer.record_use("r", UseType::Read); analyzer.current_idx = 4;
349 analyzer.record_use("r", UseType::Read); let last_uses = analyzer.compute_last_uses();
352
353 assert_eq!(last_uses.get("r"), Some(&4));
355 }
356}