Skip to main content

luaur_bytecode/methods/
bytecode_builder_fold_jumps.rs

1use crate::records::bytecode_builder::BytecodeBuilder;
2use crate::records::jump::Jump;
3use luaur_common::enums::luau_opcode::LuauOpcode;
4use luaur_common::macros::luau_assert::LUAU_ASSERT;
5use luaur_common::macros::luau_insn_d::LUAU_INSN_D;
6use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
7
8impl BytecodeBuilder {
9    pub fn fold_jumps(&mut self) {
10        // if our function has long jumps, some processing below can make jump instructions not-jumps (e.g. JUMP->RETURN)
11        // it's safer to skip this processing
12        if self.has_long_jumps {
13            return;
14        }
15
16        for jump in &mut self.jumps {
17            let jump_label: u32 = jump.source;
18            let jump_insn: u32 = self.insns[jump_label as usize];
19
20            // follow jump target through forward unconditional jumps
21            // we only follow forward jumps to make sure the process terminates
22            // NB: C++ computes this with SIGNED `int` — `LUAU_INSN_D` is the signed
23            // jump offset (negative for backward jumps), so `jumpLabel + 1 + D`
24            // must be signed arithmetic. The model used `u32` with `D as u32`,
25            // which overflows on any backward jump (every loop's back-edge).
26            let mut target_label: i32 = jump_label as i32 + 1 + LUAU_INSN_D(jump_insn);
27            LUAU_ASSERT!((target_label as usize) < self.insns.len());
28            let mut target_insn: u32 = self.insns[target_label as usize];
29
30            while LUAU_INSN_OP(target_insn) == LuauOpcode::LOP_JUMP as u32
31                && LUAU_INSN_D(target_insn) >= 0
32            {
33                target_label = target_label + 1 + LUAU_INSN_D(target_insn);
34                LUAU_ASSERT!((target_label as usize) < self.insns.len());
35                target_insn = self.insns[target_label as usize];
36            }
37
38            let offset: i32 = target_label - jump_label as i32 - 1;
39
40            // for unconditional jumps to RETURN, we can replace JUMP with RETURN
41            if LUAU_INSN_OP(jump_insn) == LuauOpcode::LOP_JUMP as u32
42                && LUAU_INSN_OP(target_insn) == LuauOpcode::LOP_RETURN as u32
43            {
44                self.insns[jump_label as usize] = target_insn;
45            } else if (offset as i16) as i32 == offset {
46                let mut insn = self.insns[jump_label as usize];
47                insn &= 0xffff;
48                insn |= ((offset as u16) as u32) << 16;
49                self.insns[jump_label as usize] = insn;
50            }
51
52            jump.target = target_label as u32;
53        }
54    }
55}