Skip to main content

luaur_code_gen/functions/
emit_inst_set_list.rs

1use crate::enums::abix_64::ABIX64;
2use crate::enums::condition_x_64::ConditionX64;
3use crate::enums::size_x_64::SizeX64;
4use crate::functions::call_barrier_table_fast::call_barrier_table_fast;
5use crate::functions::luau_reg_value::luau_reg_value;
6use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
7use crate::records::ir_call_wrapper_x_64::IrCallWrapperX64;
8use crate::records::ir_data::k_invalid_inst_idx;
9use crate::records::ir_op::IrOp;
10use crate::records::ir_reg_alloc_x_64::IrRegAllocX64;
11use crate::records::label::Label;
12use crate::records::native_context::NativeContext;
13use crate::records::operand_x_64::OperandX64;
14use crate::records::register_x_64::RegisterX64;
15use luaur_vm::macros::lua_multret::LUA_MULTRET;
16use luaur_vm::records::call_info::CallInfo;
17use luaur_vm::records::lua_state::lua_State;
18use luaur_vm::records::lua_table::LuaTable;
19use luaur_vm::type_aliases::t_value::TValue;
20
21pub fn emit_inst_set_list(
22    regs: &mut IrRegAllocX64,
23    build: &mut AssemblyBuilderX64,
24    ra: i32,
25    rb: i32,
26    count: i32,
27    index: u32,
28    known_size: i32,
29) {
30    let mut last = OperandX64::imm(index as i32 + count - 1);
31    let cscaled = RegisterX64::rbx;
32
33    if count == LUA_MULTRET {
34        let tmp = RegisterX64::rax;
35
36        build.mov(
37            OperandX64::reg(cscaled),
38            mem(
39                SizeX64::qword,
40                r_state(),
41                core::mem::offset_of!(lua_State, top) as i32,
42            ),
43        );
44        build.lea_operand_x_64_operand_x_64(OperandX64::reg(tmp), luau_reg_address(rb));
45        build.sub(OperandX64::reg(cscaled), OperandX64::reg(tmp));
46
47        build.mov(
48            OperandX64::reg(tmp),
49            mem(
50                SizeX64::qword,
51                r_state(),
52                core::mem::offset_of!(lua_State, ci) as i32,
53            ),
54        );
55        build.mov(
56            OperandX64::reg(tmp),
57            mem(
58                SizeX64::qword,
59                tmp,
60                core::mem::offset_of!(CallInfo, top) as i32,
61            ),
62        );
63        build.mov(
64            mem(
65                SizeX64::qword,
66                r_state(),
67                core::mem::offset_of!(lua_State, top) as i32,
68            ),
69            OperandX64::reg(tmp),
70        );
71
72        last = OperandX64::reg(sized(RegisterX64::rdx, SizeX64::dword));
73        build.mov(last, OperandX64::reg(sized(cscaled, SizeX64::dword)));
74        build.shr(last, OperandX64::imm(K_TVALUE_SIZE_LOG2));
75        build.add(last, OperandX64::imm(index as i32 - 1));
76    }
77
78    let mut table = regs.take_reg(RegisterX64::rax, k_invalid_inst_idx);
79    build.mov(OperandX64::reg(table), luau_reg_value(ra));
80
81    if count == LUA_MULTRET || known_size < 0 || known_size < (index as i32 + count - 1) {
82        let mut skip_resize = Label::default();
83
84        build.cmp(
85            mem(
86                SizeX64::dword,
87                table,
88                core::mem::offset_of!(LuaTable, sizearray) as i32,
89            ),
90            last,
91        );
92        build.jcc(ConditionX64::NotBelow, &mut skip_resize);
93
94        if luaur_common::FFlag::LuauCodeGenCallWrapperEmitInst.get() {
95            if count == LUA_MULTRET {
96                regs.take_reg(last.base, k_invalid_inst_idx);
97            }
98
99            let mut call_wrapper = IrCallWrapperX64::ir_call_wrapper_x_64_ir_call_wrapper_x_64(
100                regs,
101                build,
102                k_invalid_inst_idx,
103            );
104            call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
105                SizeX64::qword,
106                OperandX64::reg(r_state()),
107                IrOp::ir_op(),
108            );
109            call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
110                SizeX64::qword,
111                OperandX64::reg(table),
112                IrOp::ir_op(),
113            );
114            call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
115                SizeX64::dword,
116                last,
117                IrOp::ir_op(),
118            );
119            call_wrapper
120                .call(&native_context_slot(
121                    core::mem::offset_of!(NativeContext, luaH_resizearray) as i32,
122                ));
123
124            table = regs.take_reg(RegisterX64::rax, k_invalid_inst_idx);
125        } else {
126            let (_, r_arg2, r_arg3, _) = abi_arg_regs(build);
127            let r_arg1 = if build.abi == ABIX64::Windows {
128                RegisterX64::rcx
129            } else {
130                RegisterX64::rdi
131            };
132
133            crate::macros::codegen_assert::CODEGEN_ASSERT!(r_arg3 != table);
134            build.mov(OperandX64::reg(sized(r_arg3, SizeX64::dword)), last);
135            build.mov(OperandX64::reg(r_arg2), OperandX64::reg(table));
136            build.mov(OperandX64::reg(r_arg1), OperandX64::reg(r_state()));
137            build.call_operand_x_64(native_context_slot(core::mem::offset_of!(
138                NativeContext,
139                luaH_resizearray
140            ) as i32));
141        }
142
143        build.mov(OperandX64::reg(table), luau_reg_value(ra));
144        build.set_label_label(&mut skip_resize);
145    }
146
147    let array_dst = RegisterX64::rdx;
148    let offset = RegisterX64::rcx;
149
150    build.mov(
151        OperandX64::reg(array_dst),
152        mem(
153            SizeX64::qword,
154            table,
155            core::mem::offset_of!(LuaTable, array) as i32,
156        ),
157    );
158
159    const K_UNROLL_SET_LIST_LIMIT: i32 = 4;
160
161    if count != LUA_MULTRET && count <= K_UNROLL_SET_LIST_LIMIT {
162        for i in 0..count {
163            build.vmovups(OperandX64::reg(xmm(0)), luau_reg_value(rb + i));
164            build.vmovups(
165                mem(
166                    SizeX64::xmmword,
167                    array_dst,
168                    (index as i32 + i - 1) * core::mem::size_of::<TValue>() as i32,
169                ),
170                OperandX64::reg(xmm(0)),
171            );
172        }
173    } else {
174        crate::macros::codegen_assert::CODEGEN_ASSERT!(count != 0);
175
176        build.xor_(OperandX64::reg(offset), OperandX64::reg(offset));
177        if index != 1 {
178            build.add(
179                OperandX64::reg(array_dst),
180                OperandX64::imm((index as i32 - 1) * core::mem::size_of::<TValue>() as i32),
181            );
182        }
183
184        let mut repeat_loop = Label::default();
185        let mut end_loop = Label::default();
186        let limit = if count == LUA_MULTRET {
187            OperandX64::reg(cscaled)
188        } else {
189            OperandX64::imm(count * core::mem::size_of::<TValue>() as i32)
190        };
191
192        if count == LUA_MULTRET {
193            build.cmp(OperandX64::reg(offset), limit);
194            build.jcc(ConditionX64::NotBelow, &mut end_loop);
195        }
196
197        build.set_label(&mut repeat_loop);
198        build.vmovups(
199            OperandX64::reg(xmm(0)),
200            OperandX64::mem(
201                SizeX64::xmmword,
202                offset,
203                1,
204                r_base(),
205                rb * core::mem::size_of::<TValue>() as i32,
206            ),
207        );
208        build.vmovups(
209            OperandX64::mem(SizeX64::xmmword, offset, 1, array_dst, 0),
210            OperandX64::reg(xmm(0)),
211        );
212
213        build.add(
214            OperandX64::reg(offset),
215            OperandX64::imm(core::mem::size_of::<TValue>() as i32),
216        );
217        build.cmp(OperandX64::reg(offset), limit);
218        build.jcc(ConditionX64::Below, &mut repeat_loop);
219
220        build.set_label_label(&mut end_loop);
221    }
222
223    call_barrier_table_fast(regs, build, table, IrOp::ir_op());
224}
225
226const K_TVALUE_SIZE_LOG2: i32 = 4;
227
228const fn reg(index: u8, size: SizeX64) -> RegisterX64 {
229    RegisterX64 {
230        bits: (index << RegisterX64::INDEX_SHIFT) | size as u8,
231    }
232}
233
234const fn sized(reg: RegisterX64, size: SizeX64) -> RegisterX64 {
235    RegisterX64 {
236        bits: (reg.index() << RegisterX64::INDEX_SHIFT) | size as u8,
237    }
238}
239
240const fn xmm(index: u8) -> RegisterX64 {
241    reg(index, SizeX64::xmmword)
242}
243
244const fn r_state() -> RegisterX64 {
245    reg(15, SizeX64::qword)
246}
247
248const fn r_native_context() -> RegisterX64 {
249    reg(13, SizeX64::qword)
250}
251
252const fn r_base() -> RegisterX64 {
253    RegisterX64::rbp
254}
255
256fn mem(size: SizeX64, base: RegisterX64, disp: i32) -> OperandX64 {
257    OperandX64::mem(size, RegisterX64::noreg, 1, base, disp)
258}
259
260fn native_context_slot(disp: i32) -> OperandX64 {
261    mem(SizeX64::qword, r_native_context(), disp)
262}
263
264fn luau_reg_address(ri: i32) -> OperandX64 {
265    crate::functions::luau_reg_address::luau_reg_address(ri)
266}
267
268fn abi_arg_regs(
269    build: &AssemblyBuilderX64,
270) -> (RegisterX64, RegisterX64, RegisterX64, RegisterX64) {
271    if build.abi == ABIX64::Windows {
272        (
273            RegisterX64::rcx,
274            RegisterX64::rdx,
275            RegisterX64::r8,
276            RegisterX64::r9,
277        )
278    } else {
279        (
280            RegisterX64::rdi,
281            RegisterX64::rsi,
282            RegisterX64::rdx,
283            RegisterX64::rcx,
284        )
285    }
286}