luaur_code_gen/functions/
build_entry_function_code_gen_x_64.rs1use crate::enums::abix_64::ABIX64;
2use crate::enums::alignment_data_x_64::AlignmentDataX64;
3use crate::enums::size_x_64::SizeX64;
4use crate::functions::get_full_stack_size::{
5 get_full_stack_size, kStackAlign, kStackOffsetToLocals,
6};
7use crate::functions::get_non_vol_xmm_storage_size::get_non_vol_xmm_storage_size;
8use crate::functions::get_xmm_register_count::get_xmm_register_count;
9use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
10use crate::records::entry_locations_code_gen_x_64::EntryLocations;
11use crate::records::ir_call_wrapper_x_64::IrCallWrapperX64;
12use crate::records::label::Label;
13use crate::records::operand_x_64::OperandX64;
14use crate::records::register_x_64::RegisterX64;
15use crate::records::unwind_builder::UnwindBuilder;
16use alloc::vec::Vec;
17use luaur_vm::records::call_info::CallInfo;
18use luaur_vm::records::lua_state::lua_State;
19use luaur_vm::records::proto::Proto;
20use luaur_vm::type_aliases::t_value::TValue;
21use luaur_vm::type_aliases::value::Value;
22
23const K_FUNCTION_ALIGNMENT: u32 = 32;
24const K_WINDOWS_FIRST_NON_VOL_XMM_REG: u8 = 6;
25const K_FULL_BLOCK_FUNCTION: u32 = 0xffff_ffff;
26
27const fn reg(index: u8, size: SizeX64) -> RegisterX64 {
28 RegisterX64 {
29 bits: (index << RegisterX64::INDEX_SHIFT) | size as u8,
30 }
31}
32
33const R12: RegisterX64 = reg(12, SizeX64::qword);
34const R13: RegisterX64 = reg(13, SizeX64::qword);
35const R14: RegisterX64 = reg(14, SizeX64::qword);
36const R15: RegisterX64 = reg(15, SizeX64::qword);
37
38const R_CONSTANTS: RegisterX64 = R12;
39const R_NATIVE_CONTEXT: RegisterX64 = R13;
40const R_BASE: RegisterX64 = R14;
41const R_STATE: RegisterX64 = R15;
42
43fn mem(size: SizeX64, base: RegisterX64, disp: i32) -> OperandX64 {
44 OperandX64::mem(size, RegisterX64::noreg, 1, base, disp)
45}
46
47fn s_closure() -> OperandX64 {
48 mem(
49 SizeX64::qword,
50 RegisterX64::rsp,
51 kStackOffsetToLocals as i32,
52 )
53}
54
55fn s_code() -> OperandX64 {
56 mem(
57 SizeX64::qword,
58 RegisterX64::rsp,
59 kStackOffsetToLocals as i32 + 8,
60 )
61}
62
63fn set_fresh_label(build: &mut AssemblyBuilderX64) -> Label {
64 let mut label = Label::default();
65 build.set_label(&mut label);
66 label
67}
68
69#[cfg(target_os = "windows")]
70fn unwind_start_function(unwind: &mut UnwindBuilder) {
71 unsafe {
72 (&mut *(unwind as *mut UnwindBuilder)
73 .cast::<crate::records::unwind_builder_win::UnwindBuilderWin>())
74 .start_function();
75 }
76}
77
78#[cfg(not(target_os = "windows"))]
79fn unwind_start_function(unwind: &mut UnwindBuilder) {
80 unsafe {
81 (&mut *(unwind as *mut UnwindBuilder)
82 .cast::<crate::records::unwind_builder_dwarf_2::UnwindBuilderDwarf2>())
83 .start_function();
84 }
85}
86
87#[cfg(target_os = "windows")]
88fn unwind_prologue_x_64(
89 unwind: &mut UnwindBuilder,
90 prologue_size: u32,
91 full_stack_size: u32,
92 setup_frame: bool,
93 gpr: &[RegisterX64],
94 simd: &[RegisterX64],
95) {
96 unsafe {
97 (&mut *(unwind as *mut UnwindBuilder)
98 .cast::<crate::records::unwind_builder_win::UnwindBuilderWin>())
99 .prologue_x_64(prologue_size, full_stack_size, setup_frame, gpr, simd);
100 }
101}
102
103#[cfg(not(target_os = "windows"))]
104fn unwind_prologue_x_64(
105 unwind: &mut UnwindBuilder,
106 prologue_size: u32,
107 full_stack_size: u32,
108 setup_frame: bool,
109 gpr: &[RegisterX64],
110 simd: &[RegisterX64],
111) {
112 unsafe {
113 (&mut *(unwind as *mut UnwindBuilder)
114 .cast::<crate::records::unwind_builder_dwarf_2::UnwindBuilderDwarf2>())
115 .prologue_x_64(prologue_size, full_stack_size, setup_frame, gpr, simd);
116 }
117}
118
119#[cfg(target_os = "windows")]
120fn unwind_finish_function(unwind: &mut UnwindBuilder, begin_offset: u32, end_offset: u32) {
121 unsafe {
122 (&mut *(unwind as *mut UnwindBuilder)
123 .cast::<crate::records::unwind_builder_win::UnwindBuilderWin>())
124 .finish_function(begin_offset, end_offset);
125 }
126}
127
128#[cfg(not(target_os = "windows"))]
129fn unwind_finish_function(unwind: &mut UnwindBuilder, begin_offset: u32, end_offset: u32) {
130 unsafe {
131 (&mut *(unwind as *mut UnwindBuilder)
132 .cast::<crate::records::unwind_builder_dwarf_2::UnwindBuilderDwarf2>())
133 .finish_function(begin_offset, end_offset);
134 }
135}
136
137pub fn build_entry_function(
138 build: &mut AssemblyBuilderX64,
139 unwind: &mut UnwindBuilder,
140) -> EntryLocations {
141 let mut locations = EntryLocations::default();
142
143 build.align(K_FUNCTION_ALIGNMENT, AlignmentDataX64::Ud2);
144
145 locations.start = set_fresh_label(build);
146 unwind_start_function(unwind);
147
148 let (r_arg1, r_arg2, r_arg3, r_arg4) =
149 if luaur_common::FFlag::LuauCodegenSuggestArgumentRegisterX64.get() {
150 (
151 IrCallWrapperX64::suggest_argument_register::<0>(SizeX64::qword, build),
152 IrCallWrapperX64::suggest_argument_register::<1>(SizeX64::qword, build),
153 IrCallWrapperX64::suggest_argument_register::<2>(SizeX64::qword, build),
154 IrCallWrapperX64::suggest_argument_register::<3>(SizeX64::qword, build),
155 )
156 } else if build.abi == ABIX64::Windows {
157 (
158 RegisterX64::rcx,
159 RegisterX64::rdx,
160 RegisterX64::r8,
161 RegisterX64::r9,
162 )
163 } else {
164 (
165 RegisterX64::rdi,
166 RegisterX64::rsi,
167 RegisterX64::rdx,
168 RegisterX64::rcx,
169 )
170 };
171
172 if build.abi == ABIX64::SystemV {
173 build.push(OperandX64::reg(RegisterX64::rbp));
174 build.mov(
175 OperandX64::reg(RegisterX64::rbp),
176 OperandX64::reg(RegisterX64::rsp),
177 );
178 }
179
180 build.push(OperandX64::reg(RegisterX64::rbx));
181 build.push(OperandX64::reg(R12));
182 build.push(OperandX64::reg(R13));
183 build.push(OperandX64::reg(R14));
184 build.push(OperandX64::reg(R15));
185
186 if build.abi == ABIX64::Windows {
187 build.push(OperandX64::reg(RegisterX64::rdi));
188 build.push(OperandX64::reg(RegisterX64::rsi));
189 build.push(OperandX64::reg(RegisterX64::rbp));
190 }
191
192 let usable_xmm_reg_count = get_xmm_register_count(build.abi);
193 let xmm_storage_size = get_non_vol_xmm_storage_size(build.abi, usable_xmm_reg_count);
194 let full_stack_size = get_full_stack_size(build.abi, usable_xmm_reg_count);
195
196 build.sub(
197 OperandX64::reg(RegisterX64::rsp),
198 OperandX64::imm(full_stack_size as i32),
199 );
200
201 let xmm_storage_offset = full_stack_size as i32 - (kStackAlign + xmm_storage_size) as i32;
202 let mut saved_xmm_regs: Vec<RegisterX64> = Vec::new();
203
204 if build.abi == ABIX64::Windows {
205 if usable_xmm_reg_count > K_WINDOWS_FIRST_NON_VOL_XMM_REG {
206 saved_xmm_regs
207 .reserve((usable_xmm_reg_count - K_WINDOWS_FIRST_NON_VOL_XMM_REG) as usize);
208 }
209
210 let mut offset = 0;
211 for i in K_WINDOWS_FIRST_NON_VOL_XMM_REG..usable_xmm_reg_count {
212 let xmm_reg = reg(i, SizeX64::xmmword);
213 build.vmovaps(
214 mem(
215 SizeX64::xmmword,
216 RegisterX64::rsp,
217 xmm_storage_offset + offset,
218 ),
219 OperandX64::reg(xmm_reg),
220 );
221 saved_xmm_regs.push(xmm_reg);
222 offset += 16;
223 }
224 }
225
226 locations.prologueEnd = set_fresh_label(build);
227
228 let prologue_size =
229 build.get_label_offset(&locations.prologueEnd) - build.get_label_offset(&locations.start);
230
231 if build.abi == ABIX64::SystemV {
232 unwind_prologue_x_64(
233 unwind,
234 prologue_size,
235 full_stack_size,
236 true,
237 &[RegisterX64::rbx, R12, R13, R14, R15],
238 &[],
239 );
240 } else if build.abi == ABIX64::Windows {
241 unwind_prologue_x_64(
242 unwind,
243 prologue_size,
244 full_stack_size,
245 false,
246 &[
247 RegisterX64::rbx,
248 R12,
249 R13,
250 R14,
251 R15,
252 RegisterX64::rdi,
253 RegisterX64::rsi,
254 RegisterX64::rbp,
255 ],
256 &saved_xmm_regs,
257 );
258 }
259
260 build.mov(OperandX64::reg(R_STATE), OperandX64::reg(r_arg1));
261 build.mov(OperandX64::reg(R_NATIVE_CONTEXT), OperandX64::reg(r_arg4));
262 build.mov(
263 OperandX64::reg(R_BASE),
264 mem(
265 SizeX64::qword,
266 R_STATE,
267 core::mem::offset_of!(lua_State, base) as i32,
268 ),
269 );
270 build.mov(
271 OperandX64::reg(RegisterX64::rax),
272 mem(
273 SizeX64::qword,
274 R_STATE,
275 core::mem::offset_of!(lua_State, ci) as i32,
276 ),
277 );
278 build.mov(
279 OperandX64::reg(RegisterX64::rax),
280 mem(
281 SizeX64::qword,
282 RegisterX64::rax,
283 core::mem::offset_of!(CallInfo, func) as i32,
284 ),
285 );
286 build.mov(
287 OperandX64::reg(RegisterX64::rax),
288 mem(
289 SizeX64::qword,
290 RegisterX64::rax,
291 (core::mem::offset_of!(TValue, value) + core::mem::offset_of!(Value, gc)) as i32,
292 ),
293 );
294 build.mov(s_closure(), OperandX64::reg(RegisterX64::rax));
295 build.mov(
296 OperandX64::reg(R_CONSTANTS),
297 mem(
298 SizeX64::qword,
299 r_arg2,
300 core::mem::offset_of!(Proto, k) as i32,
301 ),
302 );
303 build.mov(
304 OperandX64::reg(RegisterX64::rax),
305 mem(
306 SizeX64::qword,
307 r_arg2,
308 core::mem::offset_of!(Proto, code) as i32,
309 ),
310 );
311 build.mov(s_code(), OperandX64::reg(RegisterX64::rax));
312
313 build.jmp_operand_x_64(OperandX64::reg(r_arg3));
314
315 locations.epilogueStart = set_fresh_label(build);
316
317 if build.abi == ABIX64::Windows {
318 let mut offset = 0;
319 for i in K_WINDOWS_FIRST_NON_VOL_XMM_REG..usable_xmm_reg_count {
320 build.vmovaps(
321 OperandX64::reg(reg(i, SizeX64::xmmword)),
322 mem(
323 SizeX64::xmmword,
324 RegisterX64::rsp,
325 xmm_storage_offset + offset,
326 ),
327 );
328 offset += 16;
329 }
330 }
331
332 build.add(
333 OperandX64::reg(RegisterX64::rsp),
334 OperandX64::imm(full_stack_size as i32),
335 );
336
337 if build.abi == ABIX64::Windows {
338 build.pop(OperandX64::reg(RegisterX64::rbp));
339 build.pop(OperandX64::reg(RegisterX64::rsi));
340 build.pop(OperandX64::reg(RegisterX64::rdi));
341 }
342
343 build.pop(OperandX64::reg(R15));
344 build.pop(OperandX64::reg(R14));
345 build.pop(OperandX64::reg(R13));
346 build.pop(OperandX64::reg(R12));
347 build.pop(OperandX64::reg(RegisterX64::rbx));
348
349 if build.abi == ABIX64::SystemV {
350 build.pop(OperandX64::reg(RegisterX64::rbp));
351 }
352
353 build.ret();
354
355 unwind_finish_function(
356 unwind,
357 build.get_label_offset(&locations.start),
358 K_FULL_BLOCK_FUNCTION,
359 );
360
361 locations
362}