Skip to main content

luaur_bytecode/methods/
bytecode_graph_parser_rebuild_graph.rs

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        // Create instructions.
49        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                        // forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
125                        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                    // Call target.
278                    self.add_vm_reg_input(node, LUAU_INSN_A(insn) as u8);
279                    // Fixed arguments.
280                    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                        // it is long jump trampoline
350                        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                    // repeat .. until loops use it for back edge.
377                    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                    // Note that FASTCALL will read the actual call arguments, such as argument/result registers and counts, from the CALL instruction
508                    self.add_imm_input_bc_inst_i32(node, LUAU_INSN_A(insn) as i32);
509                    // turn it in BcOp to CALL BcInst&.
510                    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                    // turn it in BcOp to CALL BcInst&.
517                    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                    // turn it in BcOp to CALL BcInst&.
525                    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                    // turn it in BcOp to CALL BcInst&.
533                    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                    // turn it in BcOp to CALL BcInst&.
542                    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                        // try to find it in the same loop before
694                        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}