Skip to main content

luaur_code_gen/functions/
emit_inst_call.rs

1use crate::enums::abix_64::ABIX64;
2use crate::enums::condition_x_64::ConditionX64;
3use crate::enums::size_x_64::SizeX64;
4use crate::functions::dword_reg::dword_reg;
5use crate::functions::emit_update_base_emit_common_x_64::emit_update_base;
6use crate::functions::get_full_stack_size::kStackOffsetToLocals;
7use crate::functions::luau_reg::luau_reg;
8use crate::functions::luau_reg_address::luau_reg_address;
9use crate::functions::luau_reg_tag::luau_reg_tag;
10use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
11use crate::records::ir_call_wrapper_x_64::IrCallWrapperX64;
12use crate::records::ir_data::k_invalid_inst_idx;
13use crate::records::ir_op::IrOp;
14use crate::records::ir_reg_alloc_x_64::IrRegAllocX64;
15use crate::records::label::Label;
16use crate::records::module_helpers::ModuleHelpers;
17use crate::records::native_context::NativeContext;
18use crate::records::operand_x_64::OperandX64;
19use crate::records::register_x_64::RegisterX64;
20use luaur_vm::enums::lua_type::lua_Type;
21use luaur_vm::macros::lua_callinfo_native::LUA_CALLINFO_NATIVE;
22use luaur_vm::macros::lua_multret::LUA_MULTRET;
23use luaur_vm::records::call_info::CallInfo;
24use luaur_vm::records::closure::{CClosure, Closure, LClosure};
25use luaur_vm::records::lua_state::lua_State;
26use luaur_vm::records::proto::Proto;
27use luaur_vm::type_aliases::t_value::TValue;
28
29pub fn emit_inst_call(
30    regs: &mut IrRegAllocX64,
31    build: &mut AssemblyBuilderX64,
32    helpers: &mut ModuleHelpers,
33    ra: i32,
34    nparams: i32,
35    nresults: i32,
36) {
37    if luaur_common::FFlag::LuauCodeGenCallWrapperEmitInst.get() {
38        let mut call_wrapper = IrCallWrapperX64::ir_call_wrapper_x_64_ir_call_wrapper_x_64(
39            regs,
40            build,
41            k_invalid_inst_idx,
42        );
43
44        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
45            SizeX64::qword,
46            OperandX64::reg(r_state()),
47            IrOp::ir_op(),
48        );
49        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
50            SizeX64::qword,
51            luau_reg_address(ra),
52            IrOp::ir_op(),
53        );
54        if nparams == LUA_MULTRET {
55            call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
56                SizeX64::qword,
57                mem(
58                    SizeX64::qword,
59                    r_state(),
60                    core::mem::offset_of!(lua_State, top) as i32,
61                ),
62                IrOp::ir_op(),
63            );
64        } else {
65            call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
66                SizeX64::qword,
67                luau_reg_address(ra + 1 + nparams),
68                IrOp::ir_op(),
69            );
70        }
71        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
72            SizeX64::dword,
73            OperandX64::imm(nresults),
74            IrOp::ir_op(),
75        );
76        call_wrapper.call(&native_context_slot(
77            core::mem::offset_of!(NativeContext, callProlog) as i32,
78        ));
79    } else {
80        let (r_arg1, r_arg2, r_arg3, r_arg4) = abi_arg_regs(build);
81
82        build.mov(OperandX64::reg(r_arg1), OperandX64::reg(r_state()));
83        build.lea_operand_x_64_operand_x_64(OperandX64::reg(r_arg2), luau_reg_address(ra));
84
85        if nparams == LUA_MULTRET {
86            build.mov(
87                OperandX64::reg(r_arg3),
88                mem(
89                    SizeX64::qword,
90                    r_state(),
91                    core::mem::offset_of!(lua_State, top) as i32,
92                ),
93            );
94        } else {
95            build.lea_operand_x_64_operand_x_64(
96                OperandX64::reg(r_arg3),
97                luau_reg_address(ra + 1 + nparams),
98            );
99        }
100
101        build.mov(
102            OperandX64::reg(sized(r_arg4, SizeX64::dword)),
103            OperandX64::imm(nresults),
104        );
105        build.call_operand_x_64(native_context_slot(
106            core::mem::offset_of!(NativeContext, callProlog) as i32,
107        ));
108    }
109
110    let ccl = RegisterX64::rax;
111    emit_update_base(build);
112
113    let mut c_func_call = Label::default();
114
115    build.test(
116        mem(
117            SizeX64::byte,
118            ccl,
119            core::mem::offset_of!(Closure, isC) as i32,
120        ),
121        OperandX64::imm(1),
122    );
123    build.jcc(ConditionX64::NotZero, &mut c_func_call);
124
125    {
126        let proto = RegisterX64::rcx;
127        let ci = RegisterX64::rdx;
128        let argi = RegisterX64::rsi;
129        let argend = RegisterX64::rdi;
130
131        build.mov(
132            OperandX64::reg(proto),
133            mem(
134                SizeX64::qword,
135                ccl,
136                (core::mem::offset_of!(Closure, inner) + core::mem::offset_of!(LClosure, p)) as i32,
137            ),
138        );
139
140        build.mov(s_closure(), OperandX64::reg(ccl));
141        build.mov(
142            OperandX64::reg(ci),
143            mem(
144                SizeX64::qword,
145                r_state(),
146                core::mem::offset_of!(lua_State, ci) as i32,
147            ),
148        );
149
150        let mut fillnil = Label::default();
151        let mut exitfillnil = Label::default();
152
153        build.mov(
154            OperandX64::reg(argi),
155            mem(
156                SizeX64::qword,
157                r_state(),
158                core::mem::offset_of!(lua_State, top) as i32,
159            ),
160        );
161
162        build.movzx(
163            sized(RegisterX64::rax, SizeX64::dword),
164            mem(
165                SizeX64::byte,
166                proto,
167                core::mem::offset_of!(Proto, numparams) as i32,
168            ),
169        );
170        build.shl(
171            OperandX64::reg(sized(RegisterX64::rax, SizeX64::dword)),
172            OperandX64::imm(K_TVALUE_SIZE_LOG2),
173        );
174        build.lea_operand_x_64_operand_x_64(
175            OperandX64::reg(argend),
176            OperandX64::mem(
177                SizeX64::none,
178                sized(RegisterX64::rax, SizeX64::qword),
179                1,
180                r_base(),
181                0,
182            ),
183        );
184
185        build.set_label(&mut fillnil);
186        build.cmp(OperandX64::reg(argi), OperandX64::reg(argend));
187        build.jcc(ConditionX64::NotBelow, &mut exitfillnil);
188
189        build.mov(
190            mem(
191                SizeX64::dword,
192                argi,
193                core::mem::offset_of!(TValue, tt) as i32,
194            ),
195            OperandX64::imm(lua_Type::LUA_TNIL as i32),
196        );
197        build.add(
198            OperandX64::reg(argi),
199            OperandX64::imm(core::mem::size_of::<TValue>() as i32),
200        );
201        build.jmp_label(&mut fillnil);
202
203        build.set_label_label(&mut exitfillnil);
204
205        build.mov(
206            OperandX64::reg(RegisterX64::rax),
207            mem(
208                SizeX64::qword,
209                ci,
210                core::mem::offset_of!(CallInfo, top) as i32,
211            ),
212        );
213
214        let mut skip_vararg = Label::default();
215        build.test(
216            mem(
217                SizeX64::byte,
218                proto,
219                core::mem::offset_of!(Proto, is_vararg) as i32,
220            ),
221            OperandX64::imm(1),
222        );
223        build.jcc(ConditionX64::Zero, &mut skip_vararg);
224        build.mov(OperandX64::reg(RegisterX64::rax), OperandX64::reg(argi));
225
226        build.set_label_label(&mut skip_vararg);
227
228        build.mov(
229            mem(
230                SizeX64::qword,
231                r_state(),
232                core::mem::offset_of!(lua_State, top) as i32,
233            ),
234            OperandX64::reg(RegisterX64::rax),
235        );
236
237        build.mov(
238            OperandX64::reg(RegisterX64::rax),
239            mem(
240                SizeX64::qword,
241                proto,
242                core::mem::offset_of!(Proto, code) as i32,
243            ),
244        );
245        build.mov(s_code(), OperandX64::reg(RegisterX64::rax));
246        build.mov(
247            mem(
248                SizeX64::qword,
249                ci,
250                core::mem::offset_of!(CallInfo, savedpc) as i32,
251            ),
252            OperandX64::reg(RegisterX64::rax),
253        );
254
255        build.mov(
256            OperandX64::reg(r_constants()),
257            mem(
258                SizeX64::qword,
259                proto,
260                core::mem::offset_of!(Proto, k) as i32,
261            ),
262        );
263
264        build.mov(
265            OperandX64::reg(RegisterX64::rax),
266            mem(
267                SizeX64::qword,
268                proto,
269                core::mem::offset_of!(Proto, exectarget) as i32,
270            ),
271        );
272        build.test(
273            OperandX64::reg(RegisterX64::rax),
274            OperandX64::reg(RegisterX64::rax),
275        );
276        build.jcc(ConditionX64::Zero, &mut helpers.exitContinueVm);
277
278        build.mov(
279            mem(
280                SizeX64::dword,
281                ci,
282                core::mem::offset_of!(CallInfo, flags) as i32,
283            ),
284            OperandX64::imm(LUA_CALLINFO_NATIVE as i32),
285        );
286
287        build.jmp_operand_x_64(OperandX64::reg(RegisterX64::rax));
288    }
289
290    build.set_label_label(&mut c_func_call);
291
292    {
293        if luaur_common::FFlag::LuauCodeGenCallWrapperEmitInst.get() {
294            regs.take_reg(ccl, k_invalid_inst_idx);
295            let mut call_wrapper = IrCallWrapperX64::ir_call_wrapper_x_64_ir_call_wrapper_x_64(
296                regs,
297                build,
298                k_invalid_inst_idx,
299            );
300            call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
301                SizeX64::qword,
302                OperandX64::reg(r_state()),
303                IrOp::ir_op(),
304            );
305            call_wrapper.call(&mem(
306                SizeX64::qword,
307                ccl,
308                (core::mem::offset_of!(Closure, inner) + core::mem::offset_of!(CClosure, f)) as i32,
309            ));
310        } else {
311            let (r_arg1, _, _, _) = abi_arg_regs(build);
312            build.mov(OperandX64::reg(r_arg1), OperandX64::reg(r_state()));
313            build.call_operand_x_64(mem(
314                SizeX64::qword,
315                ccl,
316                (core::mem::offset_of!(Closure, inner) + core::mem::offset_of!(CClosure, f)) as i32,
317            ));
318        }
319
320        let results = sized(RegisterX64::rax, SizeX64::dword);
321
322        build.test(OperandX64::reg(results), OperandX64::reg(results));
323        build.jcc(ConditionX64::Less, &mut helpers.exitNoContinueVm);
324
325        if nresults != 0 && nresults != 1 {
326            if luaur_common::FFlag::LuauCodeGenCallWrapperEmitInst.get() {
327                regs.take_reg(results, k_invalid_inst_idx);
328                let mut call_wrapper = IrCallWrapperX64::ir_call_wrapper_x_64_ir_call_wrapper_x_64(
329                    regs,
330                    build,
331                    k_invalid_inst_idx,
332                );
333                call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
334                    SizeX64::qword,
335                    OperandX64::reg(r_state()),
336                    IrOp::ir_op(),
337                );
338                call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
339                    SizeX64::dword,
340                    OperandX64::imm(nresults),
341                    IrOp::ir_op(),
342                );
343                call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
344                    SizeX64::dword,
345                    OperandX64::reg(results),
346                    IrOp::ir_op(),
347                );
348                call_wrapper
349                    .call(&native_context_slot(
350                        core::mem::offset_of!(NativeContext, callEpilogC) as i32,
351                    ));
352            } else {
353                let (r_arg1, r_arg2, r_arg3, _) = abi_arg_regs(build);
354
355                build.mov(OperandX64::reg(r_arg1), OperandX64::reg(r_state()));
356                build.mov(
357                    OperandX64::reg(sized(r_arg2, SizeX64::dword)),
358                    OperandX64::imm(nresults),
359                );
360                build.mov(
361                    OperandX64::reg(sized(r_arg3, SizeX64::dword)),
362                    OperandX64::reg(results),
363                );
364                build.call_operand_x_64(native_context_slot(core::mem::offset_of!(
365                    NativeContext,
366                    callEpilogC
367                ) as i32));
368            }
369
370            emit_update_base(build);
371            return;
372        }
373
374        let ci = RegisterX64::rdx;
375        let cip = RegisterX64::rcx;
376        let vali = RegisterX64::rsi;
377
378        build.mov(
379            OperandX64::reg(ci),
380            mem(
381                SizeX64::qword,
382                r_state(),
383                core::mem::offset_of!(lua_State, ci) as i32,
384            ),
385        );
386        build.lea_operand_x_64_operand_x_64(
387            OperandX64::reg(cip),
388            mem(
389                SizeX64::none,
390                ci,
391                -(core::mem::size_of::<CallInfo>() as i32),
392            ),
393        );
394
395        build.mov(
396            OperandX64::reg(r_base()),
397            mem(
398                SizeX64::qword,
399                cip,
400                core::mem::offset_of!(CallInfo, base) as i32,
401            ),
402        );
403        build.mov(
404            mem(
405                SizeX64::qword,
406                r_state(),
407                core::mem::offset_of!(lua_State, base) as i32,
408            ),
409            OperandX64::reg(r_base()),
410        );
411
412        if nresults == 1 {
413            build.mov(
414                OperandX64::reg(vali),
415                mem(
416                    SizeX64::qword,
417                    r_state(),
418                    core::mem::offset_of!(lua_State, top) as i32,
419                ),
420            );
421            build.shl(
422                OperandX64::reg(results),
423                OperandX64::imm(K_TVALUE_SIZE_LOG2),
424            );
425            build.sub(
426                OperandX64::reg(vali),
427                OperandX64::reg(sized(results, SizeX64::qword)),
428            );
429            build.vmovups(
430                OperandX64::reg(xmm(0)),
431                OperandX64::mem(SizeX64::xmmword, RegisterX64::noreg, 1, vali, 0),
432            );
433            build.vmovups(luau_reg(ra), OperandX64::reg(xmm(0)));
434
435            let mut skipnil = Label::default();
436            build.test(OperandX64::reg(results), OperandX64::reg(results));
437            build.jcc(ConditionX64::NotZero, &mut skipnil);
438            build.mov(luau_reg_tag(ra), OperandX64::imm(lua_Type::LUA_TNIL as i32));
439            build.set_label_label(&mut skipnil);
440        }
441
442        build.mov(
443            mem(
444                SizeX64::qword,
445                r_state(),
446                core::mem::offset_of!(lua_State, ci) as i32,
447            ),
448            OperandX64::reg(cip),
449        );
450        build.mov(
451            OperandX64::reg(RegisterX64::rax),
452            mem(
453                SizeX64::qword,
454                cip,
455                core::mem::offset_of!(CallInfo, top) as i32,
456            ),
457        );
458        build.mov(
459            mem(
460                SizeX64::qword,
461                r_state(),
462                core::mem::offset_of!(lua_State, top) as i32,
463            ),
464            OperandX64::reg(RegisterX64::rax),
465        );
466    }
467}
468
469const K_TVALUE_SIZE_LOG2: i32 = 4;
470
471const fn reg(index: u8, size: SizeX64) -> RegisterX64 {
472    RegisterX64 {
473        bits: (index << RegisterX64::INDEX_SHIFT) | size as u8,
474    }
475}
476
477const fn sized(reg: RegisterX64, size: SizeX64) -> RegisterX64 {
478    RegisterX64 {
479        bits: (reg.index() << RegisterX64::INDEX_SHIFT) | size as u8,
480    }
481}
482
483const fn xmm(index: u8) -> RegisterX64 {
484    reg(index, SizeX64::xmmword)
485}
486
487const fn r_state() -> RegisterX64 {
488    reg(15, SizeX64::qword)
489}
490
491const fn r_native_context() -> RegisterX64 {
492    reg(13, SizeX64::qword)
493}
494
495const fn r_constants() -> RegisterX64 {
496    reg(12, SizeX64::qword)
497}
498
499const fn r_base() -> RegisterX64 {
500    RegisterX64::rbp
501}
502
503fn mem(size: SizeX64, base: RegisterX64, disp: i32) -> OperandX64 {
504    OperandX64::mem(size, RegisterX64::noreg, 1, base, disp)
505}
506
507fn native_context_slot(disp: i32) -> OperandX64 {
508    mem(SizeX64::qword, r_native_context(), disp)
509}
510
511fn s_closure() -> OperandX64 {
512    mem(
513        SizeX64::qword,
514        RegisterX64::rsp,
515        kStackOffsetToLocals as i32,
516    )
517}
518
519fn s_code() -> OperandX64 {
520    mem(
521        SizeX64::qword,
522        RegisterX64::rsp,
523        kStackOffsetToLocals as i32 + 8,
524    )
525}
526
527fn abi_arg_regs(
528    build: &AssemblyBuilderX64,
529) -> (RegisterX64, RegisterX64, RegisterX64, RegisterX64) {
530    if build.abi == ABIX64::Windows {
531        (
532            RegisterX64::rcx,
533            RegisterX64::rdx,
534            RegisterX64::r8,
535            RegisterX64::r9,
536        )
537    } else {
538        (
539            RegisterX64::rdi,
540            RegisterX64::rsi,
541            RegisterX64::rdx,
542            RegisterX64::rcx,
543        )
544    }
545}