luaur_bytecode/methods/
bytecode_builder_expand_jumps.rs1use crate::records::bytecode_builder::BytecodeBuilder;
2use crate::records::debug_local_bytecode_builder::DebugLocal;
3use crate::records::jump::Jump;
4use crate::records::typed_local_bytecode_builder::TypedLocal;
5use luaur_common::enums::luau_opcode::LuauOpcode;
6use luaur_common::functions::get_op_length::getOpLength;
7use luaur_common::macros::luau_assert::LUAU_ASSERT;
8use luaur_common::macros::luau_insn_d::LUAU_INSN_D;
9use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
10
11impl BytecodeBuilder {
12 pub fn expand_jumps(&mut self) {
13 if !self.has_long_jumps {
14 return;
15 }
16
17 const K_MAX_JUMP_DISTANCE_CONSERVATIVE: i32 = 32767 / 3;
31
32 self.jumps.sort_by(|lhs, rhs| lhs.source.cmp(&rhs.source));
34
35 let mut remap: Vec<u32> = vec![0; self.insns.len()];
38
39 let mut newinsns: Vec<u32> = Vec::with_capacity(self.insns.len());
40 let mut newlines: Vec<i32> = Vec::with_capacity(self.insns.len());
41
42 LUAU_ASSERT!(self.insns.len() == self.lines.len());
43
44 let mut current_jump: usize = 0;
45 let mut pending_trampolines: usize = 0;
46
47 let mut i: usize = 0;
48 while i < self.insns.len() {
49 let op = LUAU_INSN_OP(self.insns[i]) as u8;
50 LUAU_ASSERT!(op < LuauOpcode::LOP__COUNT as u8);
51
52 if current_jump < self.jumps.len() && self.jumps[current_jump].source == i as u32 {
53 let offset = (self.jumps[current_jump].target as i32)
54 - (self.jumps[current_jump].source as i32)
55 - 1;
56
57 if offset.abs() > K_MAX_JUMP_DISTANCE_CONSERVATIVE {
58 newinsns.push(LuauOpcode::LOP_JUMP as u32 | (1 << 16));
60 newinsns.push(LuauOpcode::LOP_JUMPX as u32);
61
62 newlines.push(self.lines[i]);
63 newlines.push(self.lines[i]);
64
65 pending_trampolines += 1;
66 }
67
68 current_jump += 1;
69 }
70
71 let oplen = getOpLength(unsafe { core::mem::transmute::<u8, LuauOpcode>(op) });
72
73 for _ in 0..oplen {
75 remap[i] = newinsns.len() as u32;
76
77 newinsns.push(self.insns[i]);
78 newlines.push(self.lines[i]);
79
80 i += 1;
81 }
82 }
83
84 LUAU_ASSERT!(current_jump == self.jumps.len());
85 LUAU_ASSERT!(pending_trampolines > 0);
86
87 for jump in &mut self.jumps {
90 let offset = (jump.target as i32) - (jump.source as i32) - 1;
91 let newoffset =
92 (remap[jump.target as usize] as i32) - (remap[jump.source as usize] as i32) - 1;
93
94 if offset.abs() > K_MAX_JUMP_DISTANCE_CONSERVATIVE {
95 let trampoline_pos = remap[jump.source as usize] as usize - 1;
97 let op_pos = trampoline_pos + 1;
98
99 let (left, right) = newinsns.split_at_mut(op_pos);
100 let insnt = &mut left[trampoline_pos];
101 let insnj = &mut right[0];
102
103 LUAU_ASSERT!(LUAU_INSN_OP(*insnt) == LuauOpcode::LOP_JUMPX as u32);
104
105 *insnt &= 0xff;
108 *insnt |= ((newoffset + 1) as u32) << 8;
109
110 *insnj &= 0xffff;
112 *insnj |= ((-2i16) as u32) << 16;
113
114 pending_trampolines -= 1;
115 } else {
116 let insn = &mut newinsns[remap[jump.source as usize] as usize];
117
118 LUAU_ASSERT!(LUAU_INSN_D(*insn) == offset);
120
121 LUAU_ASSERT!(i32::from((newoffset as i16)) == newoffset);
123
124 *insn &= 0xffff;
125 *insn |= (newoffset as u32) << 16;
126 }
127 }
128
129 LUAU_ASSERT!(pending_trampolines == 0);
130
131 core::mem::swap(&mut self.insns, &mut newinsns);
133 core::mem::swap(&mut self.lines, &mut newlines);
134
135 for debug_local in &mut self.debug_locals {
136 if debug_local.startpc != debug_local.endpc {
138 debug_local.endpc = remap[(debug_local.endpc - 1) as usize] + 1;
139 } else {
140 debug_local.endpc = remap[debug_local.endpc as usize];
141 }
142
143 debug_local.startpc = remap[debug_local.startpc as usize];
144 }
145
146 for typed_local in &mut self.typed_locals {
147 if typed_local.startpc != typed_local.endpc {
149 typed_local.endpc = remap[(typed_local.endpc - 1) as usize] + 1;
150 } else {
151 typed_local.endpc = remap[typed_local.endpc as usize];
152 }
153
154 typed_local.startpc = remap[typed_local.startpc as usize];
155 }
156 }
157}