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}