1use crate::enums::ir_cmd::IrCmd;
2use crate::enums::ir_condition::IrCondition;
3use crate::functions::get_op_length::get_op_length;
4use crate::functions::is_direct_compare::is_direct_compare;
5use crate::functions::translate_fast_call_n::translate_fast_call_n;
6use crate::functions::translate_inst_and_x::translate_inst_and_x;
7use crate::functions::translate_inst_binary::translate_inst_binary;
8use crate::functions::translate_inst_binary_k::translate_inst_binary_k;
9use crate::functions::translate_inst_binary_rk::translate_inst_binary_rk;
10use crate::functions::translate_inst_capture::translate_inst_capture;
11use crate::functions::translate_inst_close_upvals::translate_inst_close_upvals;
12use crate::functions::translate_inst_cmp_proto::translate_inst_cmp_proto;
13use crate::functions::translate_inst_concat::translate_inst_concat;
14use crate::functions::translate_inst_dup_table::translate_inst_dup_table;
15use crate::functions::translate_inst_for_g_loop_ipairs::translate_inst_for_g_loop_ipairs;
16use crate::functions::translate_inst_for_g_prep_inext::translate_inst_for_g_prep_inext;
17use crate::functions::translate_inst_for_g_prep_next::translate_inst_for_g_prep_next;
18use crate::functions::translate_inst_for_n_loop::translate_inst_for_n_loop;
19use crate::functions::translate_inst_for_n_prep::translate_inst_for_n_prep;
20use crate::functions::translate_inst_get_global::translate_inst_get_global;
21use crate::functions::translate_inst_get_import::translate_inst_get_import;
22use crate::functions::translate_inst_get_table::translate_inst_get_table;
23use crate::functions::translate_inst_get_table_ks::translate_inst_get_table_ks;
24use crate::functions::translate_inst_get_table_n::translate_inst_get_table_n;
25use crate::functions::translate_inst_get_upval::translate_inst_get_upval;
26use crate::functions::translate_inst_jump::translate_inst_jump;
27use crate::functions::translate_inst_jump_back::translate_inst_jump_back;
28use crate::functions::translate_inst_jump_if::translate_inst_jump_if;
29use crate::functions::translate_inst_jump_if_cond::translate_inst_jump_if_cond;
30use crate::functions::translate_inst_jump_if_eq::translate_inst_jump_if_eq;
31use crate::functions::translate_inst_jump_if_eq_shortcut::translate_inst_jump_if_eq_shortcut;
32use crate::functions::translate_inst_jump_x::translate_inst_jump_x;
33use crate::functions::translate_inst_jumpx_eq_b::translate_inst_jumpx_eq_b;
34use crate::functions::translate_inst_jumpx_eq_b_shortcut::translate_inst_jumpx_eq_b_shortcut;
35use crate::functions::translate_inst_jumpx_eq_n::translate_inst_jumpx_eq_n;
36use crate::functions::translate_inst_jumpx_eq_n_shortcut::translate_inst_jumpx_eq_n_shortcut;
37use crate::functions::translate_inst_jumpx_eq_nil::translate_inst_jumpx_eq_nil;
38use crate::functions::translate_inst_jumpx_eq_nil_shortcut::translate_inst_jumpx_eq_nil_shortcut;
39use crate::functions::translate_inst_jumpx_eq_s::translate_inst_jumpx_eq_s;
40use crate::functions::translate_inst_jumpx_eq_s_shortcut::translate_inst_jumpx_eq_s_shortcut;
41use crate::functions::translate_inst_length::translate_inst_length;
42use crate::functions::translate_inst_load_b::translate_inst_load_b;
43use crate::functions::translate_inst_load_k::translate_inst_load_k;
44use crate::functions::translate_inst_load_kx::translate_inst_load_kx;
45use crate::functions::translate_inst_load_n::translate_inst_load_n;
46use crate::functions::translate_inst_load_nil::translate_inst_load_nil;
47use crate::functions::translate_inst_minus::translate_inst_minus;
48use crate::functions::translate_inst_move::translate_inst_move;
49use crate::functions::translate_inst_namecall::translate_inst_namecall;
50use crate::functions::translate_inst_new_closure::translate_inst_new_closure;
51use crate::functions::translate_inst_new_table::translate_inst_new_table;
52use crate::functions::translate_inst_not::translate_inst_not;
53use crate::functions::translate_inst_or_x::translate_inst_or_x;
54use crate::functions::translate_inst_set_global::translate_inst_set_global;
55use crate::functions::translate_inst_set_table::translate_inst_set_table;
56use crate::functions::translate_inst_set_table_ks::translate_inst_set_table_ks;
57use crate::functions::translate_inst_set_table_n::translate_inst_set_table_n;
58use crate::functions::translate_inst_set_upval::translate_inst_set_upval;
59use crate::macros::codegen_assert::CODEGEN_ASSERT;
60use crate::records::ir_builder::IrBuilder;
61use crate::type_aliases::instruction_ir_builder::Instruction;
62use luaur_common::enums::luau_opcode::LuauOpcode;
63use luaur_common::macros::luau_insn_a::LUAU_INSN_A;
64use luaur_common::macros::luau_insn_b::LUAU_INSN_B;
65use luaur_common::macros::luau_insn_c::LUAU_INSN_C;
66use luaur_common::macros::luau_insn_d::LUAU_INSN_D;
67use luaur_common::macros::luau_insn_op::LUAU_INSN_OP;
68use luaur_common::FFlag;
69use luaur_vm::enums::lua_type::lua_Type;
70use luaur_vm::type_aliases::tms::TMS;
71
72impl IrBuilder {
73 pub fn translate_inst(&mut self, op: LuauOpcode, pc: *const Instruction, i: i32) {
74 unsafe {
75 match op {
76 LuauOpcode::LOP_NOP => {}
77 LuauOpcode::LOP_LOADNIL => translate_inst_load_nil(self, pc),
78 LuauOpcode::LOP_LOADB => translate_inst_load_b(self, pc, i),
79 LuauOpcode::LOP_LOADN => translate_inst_load_n(self, pc),
80 LuauOpcode::LOP_LOADK => translate_inst_load_k(self, pc),
81 LuauOpcode::LOP_LOADKX => translate_inst_load_kx(self, pc),
82 LuauOpcode::LOP_MOVE => translate_inst_move(self, pc),
83 LuauOpcode::LOP_GETGLOBAL => translate_inst_get_global(self, pc, i),
84 LuauOpcode::LOP_SETGLOBAL => translate_inst_set_global(self, pc, i),
85 LuauOpcode::LOP_CALL | LuauOpcode::LOP_CALLFB => {
86 let interrupt_pc = self.const_uint(i as u32);
87 self.inst_ir_cmd_ir_op(IrCmd::INTERRUPT, interrupt_pc);
88
89 let savedpc = if FFlag::LuauCallFeedback.get() {
90 i + get_op_length(op)
91 } else {
92 i + 1
93 };
94 let savedpc = self.const_uint(savedpc as u32);
95 self.inst_ir_cmd_ir_op(IrCmd::SET_SAVEDPC, savedpc);
96
97 let ra = self.vm_reg(LUAU_INSN_A(*pc) as u8);
98 let b = self.const_int(LUAU_INSN_B(*pc) as i32 - 1);
99 let c = self.const_int(LUAU_INSN_C(*pc) as i32 - 1);
100 self.inst_ir_cmd_ir_op_ir_op_ir_op(IrCmd::CALL, ra, b, c);
101
102 if self.active_fastcall_fallback {
103 let ret = self.fastcall_fallback_return;
104 self.inst_ir_cmd_ir_op(IrCmd::JUMP, ret);
105 self.begin_block(ret);
106 self.active_fastcall_fallback = false;
107 }
108 }
109 LuauOpcode::LOP_RETURN => {
110 let interrupt_pc = self.const_uint(i as u32);
111 self.inst_ir_cmd_ir_op(IrCmd::INTERRUPT, interrupt_pc);
112 let ra = self.vm_reg(LUAU_INSN_A(*pc) as u8);
113 let b = self.const_int(LUAU_INSN_B(*pc) as i32 - 1);
114 self.inst_ir_cmd_ir_op_ir_op(IrCmd::RETURN, ra, b);
115 }
116 LuauOpcode::LOP_GETTABLE => translate_inst_get_table(self, pc, i),
117 LuauOpcode::LOP_SETTABLE => translate_inst_set_table(self, pc, i),
118 LuauOpcode::LOP_GETTABLEKS | LuauOpcode::LOP_GETUDATAKS => {
119 translate_inst_get_table_ks(self, pc, i)
120 }
121 LuauOpcode::LOP_SETTABLEKS | LuauOpcode::LOP_SETUDATAKS => {
122 translate_inst_set_table_ks(self, pc, i)
123 }
124 LuauOpcode::LOP_GETTABLEN => translate_inst_get_table_n(self, pc, i),
125 LuauOpcode::LOP_SETTABLEN => translate_inst_set_table_n(self, pc, i),
126 LuauOpcode::LOP_JUMP => translate_inst_jump(self, pc, i),
127 LuauOpcode::LOP_JUMPBACK => translate_inst_jump_back(self, pc, i),
128 LuauOpcode::LOP_JUMPIF => translate_inst_jump_if(self, pc, i, false),
129 LuauOpcode::LOP_JUMPIFNOT => translate_inst_jump_if(self, pc, i, true),
130 LuauOpcode::LOP_JUMPIFEQ => {
131 if is_direct_compare(self.function.proto, pc, i) {
132 translate_inst_jump_if_eq_shortcut(self, pc, i, false);
133 self.cmd_skip_target = i + 3;
134 } else {
135 translate_inst_jump_if_eq(self, pc, i, false);
136 }
137 }
138 LuauOpcode::LOP_JUMPIFLE => {
139 translate_inst_jump_if_cond(self, pc, i, IrCondition::LessEqual)
140 }
141 LuauOpcode::LOP_JUMPIFLT => {
142 translate_inst_jump_if_cond(self, pc, i, IrCondition::Less)
143 }
144 LuauOpcode::LOP_JUMPIFNOTEQ => {
145 if is_direct_compare(self.function.proto, pc, i) {
146 translate_inst_jump_if_eq_shortcut(self, pc, i, true);
147 self.cmd_skip_target = i + 3;
148 } else {
149 translate_inst_jump_if_eq(self, pc, i, true);
150 }
151 }
152 LuauOpcode::LOP_JUMPIFNOTLE => {
153 translate_inst_jump_if_cond(self, pc, i, IrCondition::NotLessEqual)
154 }
155 LuauOpcode::LOP_JUMPIFNOTLT => {
156 translate_inst_jump_if_cond(self, pc, i, IrCondition::NotLess)
157 }
158 LuauOpcode::LOP_JUMPX => translate_inst_jump_x(self, pc, i),
159 LuauOpcode::LOP_JUMPXEQKNIL => {
160 if is_direct_compare(self.function.proto, pc, i) {
161 translate_inst_jumpx_eq_nil_shortcut(self, pc, i);
162 self.cmd_skip_target = i + 3;
163 } else {
164 translate_inst_jumpx_eq_nil(self, pc, i);
165 }
166 }
167 LuauOpcode::LOP_JUMPXEQKB => {
168 if is_direct_compare(self.function.proto, pc, i) {
169 translate_inst_jumpx_eq_b_shortcut(self, pc, i);
170 self.cmd_skip_target = i + 3;
171 } else {
172 translate_inst_jumpx_eq_b(self, pc, i);
173 }
174 }
175 LuauOpcode::LOP_JUMPXEQKN => {
176 if is_direct_compare(self.function.proto, pc, i) {
177 translate_inst_jumpx_eq_n_shortcut(self, pc, i);
178 self.cmd_skip_target = i + 3;
179 } else {
180 translate_inst_jumpx_eq_n(self, pc, i);
181 }
182 }
183 LuauOpcode::LOP_JUMPXEQKS => {
184 if is_direct_compare(self.function.proto, pc, i) {
185 translate_inst_jumpx_eq_s_shortcut(self, pc, i);
186 self.cmd_skip_target = i + 3;
187 } else {
188 translate_inst_jumpx_eq_s(self, pc, i);
189 }
190 }
191 LuauOpcode::LOP_ADD => translate_inst_binary(self, pc, i, TMS::TM_ADD),
192 LuauOpcode::LOP_SUB => translate_inst_binary(self, pc, i, TMS::TM_SUB),
193 LuauOpcode::LOP_MUL => translate_inst_binary(self, pc, i, TMS::TM_MUL),
194 LuauOpcode::LOP_DIV => translate_inst_binary(self, pc, i, TMS::TM_DIV),
195 LuauOpcode::LOP_IDIV => translate_inst_binary(self, pc, i, TMS::TM_IDIV),
196 LuauOpcode::LOP_MOD => translate_inst_binary(self, pc, i, TMS::TM_MOD),
197 LuauOpcode::LOP_POW => translate_inst_binary(self, pc, i, TMS::TM_POW),
198 LuauOpcode::LOP_ADDK => translate_inst_binary_k(self, pc, i, TMS::TM_ADD),
199 LuauOpcode::LOP_SUBK => translate_inst_binary_k(self, pc, i, TMS::TM_SUB),
200 LuauOpcode::LOP_MULK => translate_inst_binary_k(self, pc, i, TMS::TM_MUL),
201 LuauOpcode::LOP_DIVK => translate_inst_binary_k(self, pc, i, TMS::TM_DIV),
202 LuauOpcode::LOP_IDIVK => translate_inst_binary_k(self, pc, i, TMS::TM_IDIV),
203 LuauOpcode::LOP_MODK => translate_inst_binary_k(self, pc, i, TMS::TM_MOD),
204 LuauOpcode::LOP_POWK => translate_inst_binary_k(self, pc, i, TMS::TM_POW),
205 LuauOpcode::LOP_SUBRK => translate_inst_binary_rk(self, pc, i, TMS::TM_SUB),
206 LuauOpcode::LOP_DIVRK => translate_inst_binary_rk(self, pc, i, TMS::TM_DIV),
207 LuauOpcode::LOP_NOT => translate_inst_not(self, pc),
208 LuauOpcode::LOP_MINUS => translate_inst_minus(self, pc, i),
209 LuauOpcode::LOP_LENGTH => translate_inst_length(self, pc, i),
210 LuauOpcode::LOP_NEWTABLE => translate_inst_new_table(self, pc, i),
211 LuauOpcode::LOP_DUPTABLE => translate_inst_dup_table(self, pc, i),
212 LuauOpcode::LOP_SETLIST => {
213 let pcpos = self.const_uint(i as u32);
214 let ra = self.vm_reg(LUAU_INSN_A(*pc) as u8);
215 let rb = self.vm_reg(LUAU_INSN_B(*pc) as u8);
216 let c = self.const_int(LUAU_INSN_C(*pc) as i32 - 1);
217 let aux = self.const_uint(*pc.add(1));
218 let undef = self.undef();
219 self.inst_ir_cmd_ir_op_ir_op_ir_op_ir_op_ir_op_ir_op(
220 IrCmd::SETLIST,
221 pcpos,
222 ra,
223 rb,
224 c,
225 aux,
226 undef,
227 );
228 }
229 LuauOpcode::LOP_GETUPVAL => translate_inst_get_upval(self, pc, i),
230 LuauOpcode::LOP_SETUPVAL => translate_inst_set_upval(self, pc, i),
231 LuauOpcode::LOP_CLOSEUPVALS => translate_inst_close_upvals(self, pc),
232 LuauOpcode::LOP_FASTCALL => {
233 let undef1 = self.undef();
234 let undef2 = self.undef();
235 let fallback = translate_fast_call_n(self, pc, i, false, 0, undef1, undef2);
236 self.handle_fastcall_fallback(fallback, pc, i);
237 }
238 LuauOpcode::LOP_FASTCALL1 => {
239 let undef1 = self.undef();
240 let undef2 = self.undef();
241 let fallback = translate_fast_call_n(self, pc, i, true, 1, undef1, undef2);
242 self.handle_fastcall_fallback(fallback, pc, i);
243 }
244 LuauOpcode::LOP_FASTCALL2 => {
245 let arg = self.vm_reg(*pc.add(1) as u8);
246 let undef = self.undef();
247 let fallback = translate_fast_call_n(self, pc, i, true, 2, arg, undef);
248 self.handle_fastcall_fallback(fallback, pc, i);
249 }
250 LuauOpcode::LOP_FASTCALL2K => {
251 let arg = self.vm_const(*pc.add(1));
252 let undef = self.undef();
253 let fallback = translate_fast_call_n(self, pc, i, true, 2, arg, undef);
254 self.handle_fastcall_fallback(fallback, pc, i);
255 }
256 LuauOpcode::LOP_FASTCALL3 => {
257 let aux = *pc.add(1);
258 let arg2 = self.vm_reg((aux & 0xff) as u8);
259 let arg3 = self.vm_reg(((aux >> 8) & 0xff) as u8);
260 let fallback = translate_fast_call_n(self, pc, i, true, 3, arg2, arg3);
261 self.handle_fastcall_fallback(fallback, pc, i);
262 }
263 LuauOpcode::LOP_FORNPREP => translate_inst_for_n_prep(self, pc, i),
264 LuauOpcode::LOP_FORNLOOP => translate_inst_for_n_loop(self, pc, i),
265 LuauOpcode::LOP_FORGLOOP => {
266 let aux = *pc.add(1) as i32;
267 if aux < 0 {
268 translate_inst_for_g_loop_ipairs(self, pc, i);
269 } else {
270 let ra = LUAU_INSN_A(*pc) as u8;
271 let loop_repeat = self.block_at_inst((i + 1 + LUAU_INSN_D(*pc)) as u32);
272 let loop_exit = self
273 .block_at_inst((i + get_op_length(LuauOpcode::LOP_FORGLOOP)) as u32);
274 let fallback = self.fallback_block(i as u32);
275
276 let pcpos = self.const_uint(i as u32);
277 self.inst_ir_cmd_ir_op(IrCmd::INTERRUPT, pcpos);
278 let reg_ra = self.vm_reg(ra);
279 self.load_and_check_tag(reg_ra, lua_Type::LUA_TNIL as u8, fallback);
280
281 let reg_ra = self.vm_reg(ra);
282 let aux_op = self.const_int(aux);
283 self.inst_ir_cmd_ir_op_ir_op_ir_op_ir_op(
284 IrCmd::FORGLOOP,
285 reg_ra,
286 aux_op,
287 loop_repeat,
288 loop_exit,
289 );
290
291 self.begin_block(fallback);
292 let savedpc = self.const_uint((i + 1) as u32);
293 self.inst_ir_cmd_ir_op(IrCmd::SET_SAVEDPC, savedpc);
294 let reg_ra = self.vm_reg(ra);
295 let aux_op = self.const_int(aux);
296 self.inst_ir_cmd_ir_op_ir_op_ir_op_ir_op(
297 IrCmd::FORGLOOP_FALLBACK,
298 reg_ra,
299 aux_op,
300 loop_repeat,
301 loop_exit,
302 );
303
304 self.begin_block(loop_exit);
305 }
306 }
307 LuauOpcode::LOP_FORGPREP_NEXT => translate_inst_for_g_prep_next(self, pc, i),
308 LuauOpcode::LOP_FORGPREP_INEXT => translate_inst_for_g_prep_inext(self, pc, i),
309 LuauOpcode::LOP_AND => {
310 let c = self.vm_reg(LUAU_INSN_C(*pc) as u8);
311 translate_inst_and_x(self, pc, i, c);
312 }
313 LuauOpcode::LOP_ANDK => {
314 let c = self.vm_const(LUAU_INSN_C(*pc) as u32);
315 translate_inst_and_x(self, pc, i, c);
316 }
317 LuauOpcode::LOP_OR => {
318 let c = self.vm_reg(LUAU_INSN_C(*pc) as u8);
319 translate_inst_or_x(self, pc, i, c);
320 }
321 LuauOpcode::LOP_ORK => {
322 let c = self.vm_const(LUAU_INSN_C(*pc) as u32);
323 translate_inst_or_x(self, pc, i, c);
324 }
325 LuauOpcode::LOP_COVERAGE => {
326 let pcpos = self.const_uint(i as u32);
327 self.inst_ir_cmd_ir_op(IrCmd::COVERAGE, pcpos);
328 }
329 LuauOpcode::LOP_GETIMPORT => translate_inst_get_import(self, pc, i),
330 LuauOpcode::LOP_CONCAT => translate_inst_concat(self, pc, i),
331 LuauOpcode::LOP_CAPTURE => translate_inst_capture(self, pc, i),
332 LuauOpcode::LOP_NAMECALL | LuauOpcode::LOP_NAMECALLUDATA => {
333 if translate_inst_namecall(self, pc, i) {
334 if FFlag::LuauCallFeedback.get() {
335 let namecall = get_op_length(LuauOpcode::LOP_NAMECALL);
336 let call_op =
337 LuauOpcode::from(LUAU_INSN_OP(*pc.add(namecall as usize)) as u8);
338 CODEGEN_ASSERT!(
339 call_op == LuauOpcode::LOP_CALL
340 || call_op == LuauOpcode::LOP_CALLFB
341 );
342 let call = get_op_length(call_op);
343 self.cmd_skip_target = i + namecall + call;
344 } else {
345 self.cmd_skip_target = i + 3;
346 }
347 }
348 }
349 LuauOpcode::LOP_PREPVARARGS => {
350 let pcpos = self.const_uint(i as u32);
351 let a = self.const_int(LUAU_INSN_A(*pc) as i32);
352 self.inst_ir_cmd_ir_op_ir_op(IrCmd::FALLBACK_PREPVARARGS, pcpos, a);
353 }
354 LuauOpcode::LOP_GETVARARGS => {
355 let pcpos = self.const_uint(i as u32);
356 let ra = self.vm_reg(LUAU_INSN_A(*pc) as u8);
357 let b = self.const_int(LUAU_INSN_B(*pc) as i32 - 1);
358 self.inst_ir_cmd_ir_op_ir_op_ir_op(IrCmd::FALLBACK_GETVARARGS, pcpos, ra, b);
359 }
360 LuauOpcode::LOP_NEWCLOSURE => translate_inst_new_closure(self, pc, i),
361 LuauOpcode::LOP_DUPCLOSURE => {
362 let pcpos = self.const_uint(i as u32);
363 let ra = self.vm_reg(LUAU_INSN_A(*pc) as u8);
364 let kd = self.vm_const(LUAU_INSN_D(*pc) as u32);
365 self.inst_ir_cmd_ir_op_ir_op_ir_op(IrCmd::FALLBACK_DUPCLOSURE, pcpos, ra, kd);
366 }
367 LuauOpcode::LOP_FORGPREP => {
368 let loop_start = self.block_at_inst((i + 1 + LUAU_INSN_D(*pc)) as u32);
369 let pcpos = self.const_uint(i as u32);
370 let ra = self.vm_reg(LUAU_INSN_A(*pc) as u8);
371 self.inst_ir_cmd_ir_op_ir_op_ir_op(
372 IrCmd::FALLBACK_FORGPREP,
373 pcpos,
374 ra,
375 loop_start,
376 );
377 }
378 LuauOpcode::LOP_NEWCLASSMEMBER => {
379 let exit = self.vm_exit(i as u32);
380 self.inst_ir_cmd_ir_op(IrCmd::JUMP, exit);
381 }
382 LuauOpcode::LOP_CMPPROTO => translate_inst_cmp_proto(self, pc, i),
383 _ => CODEGEN_ASSERT!(false),
384 }
385 }
386 }
387}