luaur_code_gen/functions/
emit_inst_set_list.rs1use 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}