Skip to main content

luaur_code_gen/methods/
ir_builder_translate_inst.rs

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}