1use crate::enums::bc_block_edge_kind::BcBlockEdgeKind;
2use crate::enums::bc_imm_kind::BcImmKind;
3use crate::enums::bc_op_kind::BcOpKind;
4use crate::records::bc_imm::BcImm;
5use crate::records::bc_inst::BcInst;
6use crate::records::bc_op::BcOp;
7use crate::records::bc_op_hash::BcOpHash;
8use crate::records::bytecode_graph_parser::BytecodeGraphParser;
9use crate::type_aliases::instruction::Instruction;
10use crate::type_aliases::reg::Reg;
11use alloc::vec::Vec;
12use luaur_common::enums::luau_opcode::LuauOpcode;
13use luaur_common::functions::get_jump_target::get_jump_target;
14use luaur_common::functions::get_op_length::get_op_length;
15use luaur_common::macros::luau_assert::LUAU_ASSERT;
16use luaur_common::macros::luau_insn_a::LUAU_INSN_A;
17use luaur_common::macros::luau_insn_b::LUAU_INSN_B;
18use luaur_common::macros::luau_insn_c::LUAU_INSN_C;
19use luaur_common::macros::luau_insn_d::LUAU_INSN_D;
20use luaur_common::macros::luau_insn_e::LUAU_INSN_E;
21use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
22use std::collections::HashSet;
23impl<'a> BytecodeGraphParser<'a> {
24 pub fn rebuild_graph(
25 &mut self,
26 code: *const Instruction,
27 codesize: u32,
28 lines: &mut Vec<u32>,
29 pcs: &mut Vec<u32>,
30 ) -> bool {
31 let instructions_count = self.rebuild_blocks(code, codesize);
32 if self.block_by_pc.size() > Self::K_MAX_CFG_BLOCKS as usize {
33 return false;
34 }
35
36 let mut loops: Vec<crate::records::loop_info::LoopInfo> = Vec::new();
37
38 self.producers
39 .resize(self.func.blocks.len(), Default::default());
40 pcs.resize(codesize as usize, 0);
41
42 self.current_block = self.func.entry_block;
43
44 for i in 0..self.func.numparams {
45 self.add_producer(i, BcOp::bc_op_bc_op_kind_u32(BcOpKind::VmReg, i as u32));
46 }
47
48 self.current_block = self.func.entry_block;
50 self.func.instructions.reserve(instructions_count);
51
52 let mut i: u32 = 0;
53 while i < codesize {
54 let insn = unsafe { *code.add(i as usize) };
55 let op: LuauOpcode = unsafe { core::mem::transmute((LUAU_INSN_OP(insn) & 0xff) as u8) };
56 let op_length = get_op_length(op) as u32;
57 let aux = if op_length > 1 && i + 1 < codesize {
58 unsafe { *code.add((i + 1) as usize) }
59 } else {
60 0
61 };
62 let node_op = self.func.add_inst();
63 self.func
64 .block_op(self.current_block)
65 .append_instruction(node_op);
66 let node: *mut BcInst = self.func.inst_op(node_op);
67 unsafe {
68 (*node).block = self.current_block;
69 }
70 if (i as usize) < lines.len() {
71 unsafe {
72 (*node).line = lines[i as usize];
73 }
74 }
75 unsafe {
76 (*node).op = op;
77 }
78
79 pcs[i as usize] = node_op.index;
80
81 let parse_jump = |parser: &mut BytecodeGraphParser,
82 op: LuauOpcode,
83 jump_target: i32,
84 insn: u32,
85 aux: u32,
86 node_op: BcOp| {
87 let node: *mut BcInst = parser.func.inst_op(node_op);
88 unsafe {
89 (*node).op = op;
90 }
91 match op {
92 LuauOpcode::LOP_JUMPXEQKNIL => {
93 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
94 parser.add_imm_input_bc_inst_bool(node, (aux >> 31) != 0);
95 parser.add_jump_input(node, jump_target);
96 }
97 LuauOpcode::LOP_JUMPXEQKB => {
98 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
99 parser.add_imm_input_bc_inst_bool(node, (aux >> 31) != 0);
100 parser.add_jump_input(node, jump_target);
101 parser.add_imm_input_bc_inst_bool(node, (aux & 0x1) != 0);
102 }
103 LuauOpcode::LOP_JUMPXEQKN | LuauOpcode::LOP_JUMPXEQKS => {
104 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
105 parser.add_imm_input_bc_inst_bool(node, (aux >> 31) != 0);
106 parser.add_jump_input(node, jump_target);
107 parser.add_vm_const_input(node, aux & 0xFFFFFF);
108 }
109 LuauOpcode::LOP_JUMPIF | LuauOpcode::LOP_JUMPIFNOT => {
110 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
111 parser.add_jump_input(node, jump_target);
112 }
113 LuauOpcode::LOP_JUMPIFEQ
114 | LuauOpcode::LOP_JUMPIFLE
115 | LuauOpcode::LOP_JUMPIFLT
116 | LuauOpcode::LOP_JUMPIFNOTEQ
117 | LuauOpcode::LOP_JUMPIFNOTLE
118 | LuauOpcode::LOP_JUMPIFNOTLT => {
119 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
120 parser.add_vm_reg_input(node, aux as u8);
121 parser.add_jump_input(node, jump_target);
122 }
123 LuauOpcode::LOP_FORNPREP => {
124 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
126 parser.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 1) as u8);
127 parser.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 2) as u8);
128 parser.add_jump_input(node, jump_target);
129 let node: *mut BcInst = parser.func.inst_op(node_op);
130 parser.func.regs.insert(node_op, LUAU_INSN_A(insn) as u8);
131 let __proj1 = parser.func.add_proj(node_op, 0);
132 parser.add_producer(LUAU_INSN_A(insn) as u8, __proj1);
133 let __proj2 = parser.func.add_proj(node_op, 1);
134 parser.add_producer((LUAU_INSN_A(insn) + 1) as u8, __proj2);
135 let __proj3 = parser.func.add_proj(node_op, 2);
136 parser.add_producer((LUAU_INSN_A(insn) + 2) as u8, __proj3);
137 }
138 LuauOpcode::LOP_FORNLOOP => {
139 parser.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
140 parser.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 1) as u8);
141 parser.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 2) as u8);
142 parser.add_jump_input(node, jump_target);
143 }
144 _ => {
145 LUAU_ASSERT!(false);
146 }
147 }
148 };
149
150 match op {
151 LuauOpcode::LOP_NOP | LuauOpcode::LOP_BREAK | LuauOpcode::LOP_NATIVECALL => {}
152
153 LuauOpcode::LOP_LOADNIL => {
154 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
155 }
156
157 LuauOpcode::LOP_LOADB => {
158 self.add_imm_input_bc_inst_bool(node, LUAU_INSN_B(insn) != 0);
159 self.add_jump_input(node, get_jump_target(insn, i));
160 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
161 }
162
163 LuauOpcode::LOP_LOADN => {
164 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_D(insn));
165 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
166 }
167
168 LuauOpcode::LOP_LOADK => {
169 self.add_vm_const_input(node, LUAU_INSN_D(insn) as u32);
170 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
171 }
172
173 LuauOpcode::LOP_MOVE => {
174 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
175 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
176 }
177
178 LuauOpcode::LOP_GETGLOBAL => {
179 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
180 self.add_vm_const_input(node, aux);
181 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
182 }
183
184 LuauOpcode::LOP_SETGLOBAL => {
185 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
186 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as u8 as i32);
187 self.add_vm_const_input(node, aux);
188 }
189
190 LuauOpcode::LOP_GETUPVAL => {
191 self.add_upval_input(node, LUAU_INSN_B(insn));
192 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
193 }
194
195 LuauOpcode::LOP_SETUPVAL => {
196 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
197 self.add_upval_input(node, LUAU_INSN_B(insn));
198 }
199
200 LuauOpcode::LOP_CLOSEUPVALS => unsafe {
201 (*node).ops.push_back(BcOp::bc_op_bc_op_kind_u32(
202 BcOpKind::VmReg,
203 LUAU_INSN_A(insn),
204 ));
205 },
206
207 LuauOpcode::LOP_GETIMPORT => {
208 self.add_vm_const_input(node, LUAU_INSN_D(insn) as u32);
209 self.add_imm_input_bc_inst_u32(node, aux);
210 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
211 }
212
213 LuauOpcode::LOP_GETTABLE => {
214 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
215 self.add_vm_reg_input(node, LUAU_INSN_C(insn) as u8);
216 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
217 }
218
219 LuauOpcode::LOP_SETTABLE => {
220 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
221 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
222 self.add_vm_reg_input(node, LUAU_INSN_C(insn) as u8);
223 }
224
225 LuauOpcode::LOP_GETUDATAKS | LuauOpcode::LOP_GETTABLEKS => {
226 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
227 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
228 self.add_vm_const_input(node, aux);
229 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
230 }
231
232 LuauOpcode::LOP_SETUDATAKS | LuauOpcode::LOP_SETTABLEKS => {
233 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
234 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
235 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
236 self.add_vm_const_input(node, aux);
237 }
238
239 LuauOpcode::LOP_GETTABLEN => {
240 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
241 self.add_imm_input_bc_inst_i32(node, (LUAU_INSN_C(insn) + 1) as i32);
242 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
243 }
244
245 LuauOpcode::LOP_SETTABLEN => {
246 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
247 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
248 self.add_imm_input_bc_inst_i32(node, (LUAU_INSN_C(insn) + 1) as i32);
249 }
250
251 LuauOpcode::LOP_NEWCLOSURE => {
252 self.add_proto_input(node, LUAU_INSN_D(insn) as u32);
253 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
254 }
255
256 LuauOpcode::LOP_NAMECALLUDATA | LuauOpcode::LOP_NAMECALL => {
257 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
258 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
259 self.add_vm_const_input(node, aux);
260 self.func.regs.insert(node_op, LUAU_INSN_A(insn) as u8);
261 let __proj4 = self.func.add_proj(node_op, 0);
262 self.add_producer(LUAU_INSN_A(insn) as u8, __proj4);
263 let __proj5 = self.func.add_proj(node_op, 1);
264 self.add_producer((LUAU_INSN_A(insn) + 1) as u8, __proj5);
265 }
266
267 LuauOpcode::LOP_CALL | LuauOpcode::LOP_CALLFB => {
268 let nparams = LUAU_INSN_B(insn) as i32 - 1;
269 let nresults = LUAU_INSN_C(insn) as i32 - 1;
270 let node: *mut BcInst = self.func.inst_op(node_op);
271 self.add_imm_input_bc_inst_i32(node, nparams);
272 self.add_imm_input_bc_inst_i32(node, nresults);
273 if op == LuauOpcode::LOP_CALLFB {
274 self.add_imm_input_bc_inst_i32(node, aux as i32);
275 }
276
277 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
279 let mut j = 1i32;
281 while j <= nparams {
282 self.add_vm_reg_input(node, (LUAU_INSN_A(insn) as i32 + j) as u8);
283 j += 1;
284 }
285
286 if nparams < 0 {
287 let producers_up_to_top = self.find_producers_up_to_top(
288 self.current_block,
289 (LUAU_INSN_A(insn) + 1) as u8,
290 );
291 let node: *mut BcInst = self.func.inst_op(node_op);
292 for inp in producers_up_to_top {
293 unsafe {
294 (*node).ops.push_back(inp);
295 }
296 }
297 }
298
299 let block_producers: *mut _ =
300 &mut self.producers[self.current_block.index as usize];
301 self.apply_call(
302 unsafe { &mut *block_producers },
303 node_op,
304 LUAU_INSN_A(insn) as u8,
305 nresults,
306 );
307
308 self.func.regs.insert(node_op, LUAU_INSN_A(insn) as u8);
309 let mut j = 0i32;
310 while j < nresults {
311 let __proj6 = self.func.add_proj(node_op, j as u32);
312 self.add_producer((LUAU_INSN_A(insn) as i32 + j) as u8, __proj6);
313 j += 1;
314 }
315 }
316
317 LuauOpcode::LOP_RETURN => {
318 let nresults = LUAU_INSN_B(insn) as i32 - 1;
319 let node: *mut BcInst = self.func.inst_op(node_op);
320 self.add_imm_input_bc_inst_i32(node, nresults);
321 let mut j = 0i32;
322 while j < nresults {
323 self.add_vm_reg_input(node, (LUAU_INSN_A(insn) as i32 + j) as u8);
324 j += 1;
325 }
326 if nresults < 0 {
327 let producers_up_to_top = self
328 .find_producers_up_to_top(self.current_block, LUAU_INSN_A(insn) as u8);
329 let node: *mut BcInst = self.func.inst_op(node_op);
330 for inp in producers_up_to_top {
331 unsafe {
332 (*node).ops.push_back(inp);
333 }
334 }
335 }
336 if nresults == 0 {
337 let node: *mut BcInst = self.func.inst_op(node_op);
338 unsafe {
339 (*node).ops.push_back(BcOp::bc_op_bc_op_kind_u32(
340 BcOpKind::VmReg,
341 LUAU_INSN_A(insn),
342 ));
343 }
344 }
345 }
346
347 LuauOpcode::LOP_JUMP => {
348 if self.is_jump_trampoline(i, code, codesize) {
349 let long_offset = LUAU_INSN_E(unsafe { *code.add((i + 1) as usize) });
351 i += get_op_length(LuauOpcode::LOP_JUMP) as u32
352 + get_op_length(LuauOpcode::LOP_JUMPX) as u32;
353 let next_insn = unsafe { *code.add(i as usize) };
354 let next_op: LuauOpcode =
355 unsafe { core::mem::transmute((LUAU_INSN_OP(next_insn) & 0xff) as u8) };
356 let next_op_length = get_op_length(next_op) as u32;
357 let next_aux = if next_op_length > 1 && i + 1 < codesize {
358 unsafe { *code.add((i + 1) as usize) }
359 } else {
360 0
361 };
362 parse_jump(
363 self,
364 next_op,
365 (i as i32) + long_offset,
366 next_insn,
367 next_aux,
368 node_op,
369 );
370 } else {
371 self.add_jump_input(node, get_jump_target(insn, i));
372 }
373 }
374
375 LuauOpcode::LOP_JUMPBACK => {
376 self.add_jump_input(node, get_jump_target(insn, i));
378 }
379
380 LuauOpcode::LOP_JUMPXEQKNIL
381 | LuauOpcode::LOP_JUMPXEQKB
382 | LuauOpcode::LOP_JUMPXEQKN
383 | LuauOpcode::LOP_JUMPXEQKS
384 | LuauOpcode::LOP_JUMPIF
385 | LuauOpcode::LOP_JUMPIFNOT
386 | LuauOpcode::LOP_JUMPIFEQ
387 | LuauOpcode::LOP_JUMPIFLE
388 | LuauOpcode::LOP_JUMPIFLT
389 | LuauOpcode::LOP_JUMPIFNOTEQ
390 | LuauOpcode::LOP_JUMPIFNOTLE
391 | LuauOpcode::LOP_JUMPIFNOTLT
392 | LuauOpcode::LOP_FORNPREP
393 | LuauOpcode::LOP_FORNLOOP => {
394 parse_jump(self, op, get_jump_target(insn, i), insn, aux, node_op);
395 }
396
397 LuauOpcode::LOP_ADD
398 | LuauOpcode::LOP_SUB
399 | LuauOpcode::LOP_MUL
400 | LuauOpcode::LOP_DIV
401 | LuauOpcode::LOP_MOD
402 | LuauOpcode::LOP_POW
403 | LuauOpcode::LOP_AND
404 | LuauOpcode::LOP_OR => {
405 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
406 self.add_vm_reg_input(node, LUAU_INSN_C(insn) as u8);
407 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
408 }
409
410 LuauOpcode::LOP_ADDK
411 | LuauOpcode::LOP_SUBK
412 | LuauOpcode::LOP_MULK
413 | LuauOpcode::LOP_DIVK
414 | LuauOpcode::LOP_MODK
415 | LuauOpcode::LOP_POWK
416 | LuauOpcode::LOP_ANDK
417 | LuauOpcode::LOP_ORK => {
418 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
419 self.add_vm_const_input(node, LUAU_INSN_C(insn));
420 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
421 }
422
423 LuauOpcode::LOP_CONCAT => {
424 LUAU_ASSERT!(LUAU_INSN_B(insn) <= LUAU_INSN_C(insn));
425 let mut param = LUAU_INSN_B(insn);
426 while param <= LUAU_INSN_C(insn) {
427 self.add_vm_reg_input(node, param as u8);
428 param += 1;
429 }
430 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
431 }
432
433 LuauOpcode::LOP_NOT | LuauOpcode::LOP_MINUS | LuauOpcode::LOP_LENGTH => {
434 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
435 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
436 }
437
438 LuauOpcode::LOP_NEWTABLE => {
439 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_B(insn) as i32);
440 self.add_imm_input_bc_inst_i32(node, aux as i32);
441 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
442 }
443
444 LuauOpcode::LOP_DUPTABLE => {
445 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
446 self.add_vm_const_input(node, LUAU_INSN_D(insn) as u32);
447 }
448
449 LuauOpcode::LOP_SETLIST => {
450 let count = LUAU_INSN_C(insn) as i32 - 1;
451 self.add_imm_input_bc_inst_i32(node, aux as i32);
452 self.add_imm_input_bc_inst_i32(node, count);
453 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
454 let mut param = 0i32;
455 while param < count {
456 self.add_vm_reg_input(node, (LUAU_INSN_B(insn) as i32 + param) as u8);
457 param += 1;
458 }
459 if count < 0 {
460 let producers_up_to_top = self
461 .find_producers_up_to_top(self.current_block, LUAU_INSN_B(insn) as u8);
462 let node: *mut BcInst = self.func.inst_op(node_op);
463 for inp in producers_up_to_top {
464 unsafe {
465 (*node).ops.push_back(inp);
466 }
467 }
468 }
469 }
470
471 LuauOpcode::LOP_FORGPREP
472 | LuauOpcode::LOP_FORGPREP_NEXT
473 | LuauOpcode::LOP_FORGPREP_INEXT => {
474 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
475 self.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 1) as u8);
476 self.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 2) as u8);
477 let loop_insn_pc = get_jump_target(insn, i);
478 self.add_jump_input(node, loop_insn_pc);
479 let loop_insn = unsafe { *code.add(loop_insn_pc as usize) };
480 let loop_insn_op: LuauOpcode =
481 unsafe { core::mem::transmute((LUAU_INSN_OP(loop_insn) & 0xff) as u8) };
482 LUAU_ASSERT!(
483 loop_insn_pc + 1 < codesize as i32
484 && loop_insn_op == LuauOpcode::LOP_FORGLOOP
485 );
486 let vars = unsafe { *code.add((loop_insn_pc + 1) as usize) } as i32 & 0xFF;
487 self.func.regs.insert(node_op, LUAU_INSN_A(insn) as u8);
488 let mut idx = 0i32;
489 while idx <= core::cmp::max(vars, 2) {
490 let __proj7 = self.func.add_proj(node_op, (2 + idx) as u32);
491 self.add_producer((LUAU_INSN_A(insn) as i32 + 2 + idx) as u8, __proj7);
492 idx += 1;
493 }
494 }
495
496 LuauOpcode::LOP_FORGLOOP => {
497 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
498 self.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 1) as u8);
499 self.add_vm_reg_input(node, (LUAU_INSN_A(insn) + 2) as u8);
500 self.add_imm_input_bc_inst_bool(node, (aux >> 31) != 0);
501 let vars = (aux & 0xFF) as i32;
502 self.add_imm_input_bc_inst_i32(node, vars);
503 self.add_jump_input(node, get_jump_target(insn, i));
504 }
505
506 LuauOpcode::LOP_FASTCALL => {
507 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
509 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
511 }
512
513 LuauOpcode::LOP_FASTCALL1 => {
514 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
515 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
516 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
518 }
519
520 LuauOpcode::LOP_FASTCALL2 => {
521 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
522 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
523 self.add_vm_reg_input(node, (aux & 0xFF) as u8);
524 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
526 }
527
528 LuauOpcode::LOP_FASTCALL2K => {
529 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
530 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
531 self.add_vm_const_input(node, aux);
532 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
534 }
535
536 LuauOpcode::LOP_FASTCALL3 => {
537 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
538 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
539 self.add_vm_reg_input(node, (aux & 0xFF) as u8);
540 self.add_vm_reg_input(node, ((aux >> 8) & 0xFF) as u8);
541 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
543 }
544
545 LuauOpcode::LOP_GETVARARGS => {
546 unsafe {
547 (*node).ops.push_back(BcOp::bc_op_bc_op_kind_u32(
548 BcOpKind::VmReg,
549 LUAU_INSN_A(insn),
550 ));
551 }
552 let count = LUAU_INSN_B(insn) as i32 - 1;
553 self.add_imm_input_bc_inst_i32(node, count);
554 self.func.regs.insert(node_op, LUAU_INSN_A(insn) as u8);
555 if count < 0 {
556 let block_producers =
557 &mut self.producers[self.current_block.index as usize];
558 block_producers.multiReturn = node_op;
559 block_producers.multiReturnStart = LUAU_INSN_A(insn) as u8;
560 block_producers.invalidAfter = 255;
561 } else {
562 let mut j = 0i32;
563 while j < count {
564 let __proj8 = self.func.add_proj(node_op, j as u32);
565 self.add_producer((LUAU_INSN_A(insn) as i32 + j) as u8, __proj8);
566 j += 1;
567 }
568 }
569 }
570
571 LuauOpcode::LOP_DUPCLOSURE => {
572 self.add_vm_const_input(node, LUAU_INSN_D(insn) as u32);
573 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
574 }
575
576 LuauOpcode::LOP_PREPVARARGS => {
577 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
578 }
579
580 LuauOpcode::LOP_LOADKX => {
581 self.add_vm_const_input(node, aux);
582 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
583 }
584
585 LuauOpcode::LOP_JUMPX => {
586 LUAU_ASSERT!(false);
587 self.add_jump_input(node, get_jump_target(insn, i));
588 }
589
590 LuauOpcode::LOP_COVERAGE => {
591 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_E(insn));
592 }
593
594 LuauOpcode::LOP_CAPTURE => {
595 let capture_type = LUAU_INSN_A(insn);
596 self.add_imm_input_bc_inst_i32(node, capture_type as i32);
597 if capture_type
598 == luaur_common::enums::luau_capture_type::LuauCaptureType::LCT_VAL as u32
599 || capture_type
600 == luaur_common::enums::luau_capture_type::LuauCaptureType::LCT_REF
601 as u32
602 {
603 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
604 } else {
605 self.add_upval_input(node, LUAU_INSN_B(insn));
606 }
607 self.add_imm_input_bc_inst_i32(node, LUAU_INSN_C(insn) as i32);
608 }
609
610 LuauOpcode::LOP_SUBRK | LuauOpcode::LOP_DIVRK => {
611 self.add_vm_const_input(node, LUAU_INSN_B(insn));
612 self.add_vm_reg_input(node, LUAU_INSN_C(insn) as u8);
613 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
614 }
615
616 LuauOpcode::LOP_IDIV => {
617 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
618 self.add_vm_reg_input(node, LUAU_INSN_C(insn) as u8);
619 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
620 }
621
622 LuauOpcode::LOP_IDIVK => {
623 self.add_vm_reg_input(node, LUAU_INSN_B(insn) as u8);
624 self.add_vm_const_input(node, LUAU_INSN_C(insn));
625 self.add_producer(LUAU_INSN_A(insn) as u8, node_op);
626 }
627
628 LuauOpcode::LOP_CMPPROTO => {
629 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
630 self.add_imm_input_bc_inst_i32(node, aux as i32);
631 self.add_jump_input(node, get_jump_target(insn, i));
632 }
633
634 LuauOpcode::LOP_NEWCLASSMEMBER => {
635 LUAU_ASSERT!(luaur_common::FFlag::DebugLuauUserDefinedClasses.get());
636 self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
637 self.add_vm_reg_input(node, LUAU_INSN_C(insn) as u8);
638 self.add_vm_const_input(node, aux);
639 }
640
641 LuauOpcode::LOP__COUNT => {
642 LUAU_ASSERT!(false);
643 }
644
645 _ => {}
646 }
647
648 if luaur_common::functions::is_loop_jump::is_loop_jump(op) {
649 let target = get_jump_target(insn, i);
650 LUAU_ASSERT!(target >= 0 && self.block_by_pc.contains_key(&(target as u32)));
651 loops.push(crate::records::loop_info::LoopInfo {
652 entry: *self.block_by_pc.get(&(target as u32)).unwrap(),
653 exit: self.current_block,
654 });
655 }
656
657 i += op_length;
658 if self.block_by_pc.contains_key(&i) {
659 self.current_block = *self.block_by_pc.get(&i).unwrap();
660 }
661 }
662
663 for loop_ in &loops {
664 let mut visited: HashSet<BcOp, BcOpHash> = HashSet::default();
665 let mut queue: Vec<BcOp> = Vec::new();
666 queue.push(loop_.exit);
667 while !queue.is_empty() {
668 let cur = queue.pop().unwrap();
669 if visited.contains(&cur) {
670 continue;
671 }
672 visited.insert(cur);
673 let predecessors: alloc::collections::VecDeque<(BcBlockEdgeKind, BcOp)> = {
674 let bl = self.func.block_op(cur);
675 bl.predecessors.iter().map(|e| (e.kind, e.target)).collect()
676 };
677 let ops: alloc::collections::VecDeque<BcOp> = {
678 let bl = self.func.block_op(cur);
679 bl.ops.clone()
680 };
681
682 for op in &ops {
683 let inst_ops: crate::type_aliases::bc_ops::BcOps = {
684 let inst = self.func.inst_op(*op);
685 inst.ops.clone()
686 };
687 for inp_idx in 0..inst_ops.len() {
688 let inp = inst_ops[inp_idx];
689 let reg_it = self.func.regs.get(&inp);
690 let Some(reg) = reg_it.copied() else {
691 continue;
692 };
693 if self.has_producer_before_bc_op_bc_op_bc_op_reg(
695 loop_.entry,
696 cur,
697 *op,
698 reg,
699 ) {
700 continue;
701 }
702 if let Some(forward_input) = self
703 .find_forward_producer_in_range_bc_op_bc_op_bc_op_reg(
704 cur, loop_.exit, *op, reg,
705 )
706 {
707 let inst: *mut BcInst = self.func.inst_op(*op);
708 let op_val = unsafe {
709 let ops = &(*inst).ops;
710 ops[inp_idx]
711 };
712 let new_val = self.add_to_phi(op_val, forward_input);
713 unsafe {
714 let ops = &mut (*inst).ops;
715 ops[inp_idx] = new_val;
716 }
717 self.func.regs.insert(new_val, reg);
718 }
719 }
720 }
721
722 for &(ctrl, pred) in &predecessors {
723 if ctrl != BcBlockEdgeKind::Loop && !visited.contains(&pred) {
724 queue.push(pred);
725 }
726 }
727 }
728 }
729 true
730 }
731}