Skip to main content

luaur_code_gen/functions/
build_entry_function_code_gen_x_64.rs

1use 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}