luadec_rust/lua51/
liveness.rs1use crate::lua51::cfg::ControlFlowGraph;
2use crate::lua51::instruction::{is_k, Instruction};
3use crate::lua51::opcodes::OpCode;
4
5#[derive(Debug, Clone, Default)]
7pub struct RegUsage {
8 pub defs: Vec<u32>,
10 pub uses: Vec<u32>,
12}
13
14pub fn instruction_reg_usage(inst: &Instruction) -> RegUsage {
16 let mut u = RegUsage::default();
17 let a = inst.a;
18
19 match inst.op {
20 OpCode::Move => {
21 u.defs.push(a);
22 u.uses.push(inst.b());
23 }
24 OpCode::LoadK | OpCode::LoadBool => {
25 u.defs.push(a);
26 }
27 OpCode::LoadNil => {
28 for r in a..=inst.b() {
29 u.defs.push(r);
30 }
31 }
32 OpCode::GetUpval => {
33 u.defs.push(a);
34 }
35 OpCode::GetGlobal => {
36 u.defs.push(a);
37 }
38 OpCode::GetTable => {
39 u.defs.push(a);
40 u.uses.push(inst.b());
41 if !is_k(inst.c()) { u.uses.push(inst.c()); }
42 }
43 OpCode::SetGlobal => {
44 u.uses.push(a);
45 }
46 OpCode::SetUpval => {
47 u.uses.push(a);
48 }
49 OpCode::SetTable => {
50 u.uses.push(a);
51 if !is_k(inst.b()) { u.uses.push(inst.b()); }
52 if !is_k(inst.c()) { u.uses.push(inst.c()); }
53 }
54 OpCode::NewTable => {
55 u.defs.push(a);
56 }
57 OpCode::Self_ => {
58 u.defs.push(a);
59 u.defs.push(a + 1);
60 u.uses.push(inst.b());
61 if !is_k(inst.c()) { u.uses.push(inst.c()); }
62 }
63 OpCode::Add | OpCode::Sub | OpCode::Mul | OpCode::Div
64 | OpCode::Mod | OpCode::Pow => {
65 u.defs.push(a);
66 if !is_k(inst.b()) { u.uses.push(inst.b()); }
67 if !is_k(inst.c()) { u.uses.push(inst.c()); }
68 }
69 OpCode::Unm | OpCode::Not | OpCode::Len => {
70 u.defs.push(a);
71 u.uses.push(inst.b());
72 }
73 OpCode::Concat => {
74 u.defs.push(a);
75 for r in inst.b()..=inst.c() {
76 u.uses.push(r);
77 }
78 }
79 OpCode::Jmp => {}
80 OpCode::Eq | OpCode::Lt | OpCode::Le => {
81 if !is_k(inst.b()) { u.uses.push(inst.b()); }
82 if !is_k(inst.c()) { u.uses.push(inst.c()); }
83 }
84 OpCode::Test => {
85 u.uses.push(a);
86 }
87 OpCode::TestSet => {
88 u.defs.push(a);
89 u.uses.push(inst.b());
90 }
91 OpCode::Call => {
92 let num_args = if inst.b() == 0 { 0 } else { inst.b() - 1 };
94 u.uses.push(a);
95 for i in 1..=num_args {
96 u.uses.push(a + i);
97 }
98 let num_results = if inst.c() == 0 { 1 } else { inst.c() - 1 };
100 for i in 0..num_results {
101 u.defs.push(a + i);
102 }
103 }
104 OpCode::TailCall => {
105 let num_args = if inst.b() == 0 { 0 } else { inst.b() - 1 };
106 u.uses.push(a);
107 for i in 1..=num_args {
108 u.uses.push(a + i);
109 }
110 }
111 OpCode::Return => {
112 let num_ret = if inst.b() == 0 { 0 } else { inst.b() - 1 };
113 for i in 0..num_ret {
114 u.uses.push(a + i);
115 }
116 }
117 OpCode::ForLoop => {
118 u.uses.push(a);
120 u.uses.push(a + 1);
121 u.uses.push(a + 2);
122 u.defs.push(a);
123 u.defs.push(a + 3);
124 }
125 OpCode::ForPrep => {
126 u.uses.push(a);
127 u.uses.push(a + 2);
128 u.defs.push(a);
129 }
130 OpCode::TForLoop => {
131 u.uses.push(a);
132 u.uses.push(a + 1);
133 u.uses.push(a + 2);
134 let num_vars = inst.c();
135 for i in 0..=num_vars {
136 u.defs.push(a + 3 + i);
137 }
138 }
139 OpCode::SetList => {
140 let num = if inst.b() == 0 { 0 } else { inst.b() };
141 u.uses.push(a);
142 for i in 1..=num {
143 u.uses.push(a + i);
144 }
145 }
146 OpCode::Close => {}
147 OpCode::Closure => {
148 u.defs.push(a);
149 }
150 OpCode::VarArg => {
151 let num = if inst.b() == 0 { 1 } else { inst.b() - 1 };
152 for i in 0..num {
153 u.defs.push(a + i);
154 }
155 }
156 }
157 u
158}
159
160#[derive(Debug)]
162pub struct LivenessInfo {
163 pub live_in: Vec<Vec<bool>>,
165 pub live_out: Vec<Vec<bool>>,
167 pub max_reg: usize,
169}
170
171pub fn compute_liveness(cfg: &ControlFlowGraph, max_stack: usize) -> LivenessInfo {
174 let n = cfg.num_blocks();
175 let nregs = max_stack;
176 if n == 0 {
177 return LivenessInfo {
178 live_in: Vec::new(),
179 live_out: Vec::new(),
180 max_reg: nregs,
181 };
182 }
183
184 let mut use_set = vec![vec![false; nregs]; n];
186 let mut kill = vec![vec![false; nregs]; n];
187
188 for (bid, block) in cfg.blocks.iter().enumerate() {
189 for pc in block.start..=block.end {
190 let inst = &cfg.instructions[pc];
191 let usage = instruction_reg_usage(inst);
192 for &r in &usage.uses {
194 let r = r as usize;
195 if r < nregs && !kill[bid][r] {
196 use_set[bid][r] = true;
197 }
198 }
199 for &r in &usage.defs {
201 let r = r as usize;
202 if r < nregs {
203 kill[bid][r] = true;
204 }
205 }
206 }
207 }
208
209 let mut live_in = vec![vec![false; nregs]; n];
212 let mut live_out = vec![vec![false; nregs]; n];
213
214 let mut changed = true;
215 while changed {
216 changed = false;
217 for bid in (0..n).rev() {
219 for r in 0..nregs {
221 let mut out = false;
222 for &succ in &cfg.blocks[bid].successors {
223 if succ < n && live_in[succ][r] {
224 out = true;
225 break;
226 }
227 }
228 live_out[bid][r] = out;
229 }
230
231 for r in 0..nregs {
233 let new_in = use_set[bid][r] || (live_out[bid][r] && !kill[bid][r]);
234 if new_in != live_in[bid][r] {
235 live_in[bid][r] = new_in;
236 changed = true;
237 }
238 }
239 }
240 }
241
242 LivenessInfo { live_in, live_out, max_reg: nregs }
243}
244
245pub fn is_reg_live_after(
248 cfg: &ControlFlowGraph,
249 liveness: &LivenessInfo,
250 pc: usize,
251 reg: u32,
252) -> bool {
253 let r = reg as usize;
254 if r >= liveness.max_reg {
255 return false;
256 }
257 let block_id = cfg.block_of(pc);
258 let block = &cfg.blocks[block_id];
259
260 let mut live = liveness.live_out[block_id][r];
262
263 for scan_pc in (pc + 1..=block.end).rev() {
265 let inst = &cfg.instructions[scan_pc];
266 let usage = instruction_reg_usage(inst);
267 if usage.defs.contains(®) {
269 live = false;
270 }
271 if usage.uses.contains(®) {
273 live = true;
274 }
275 }
276
277 live
278}