Skip to main content

luaur_code_gen/functions/
emit_inst_for_g_loop.rs

1use crate::enums::abix_64::ABIX64;
2use crate::enums::condition_x_64::ConditionX64;
3use crate::enums::size_x_64::SizeX64;
4use crate::functions::luau_reg::luau_reg;
5use crate::functions::luau_reg_address::luau_reg_address;
6use crate::functions::luau_reg_tag::luau_reg_tag;
7use crate::functions::luau_reg_value::luau_reg_value;
8use crate::functions::set_luau_reg::set_luau_reg;
9use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
10use crate::records::ir_call_wrapper_x_64::IrCallWrapperX64;
11use crate::records::ir_data::k_invalid_inst_idx;
12use crate::records::ir_op::IrOp;
13use crate::records::ir_reg_alloc_x_64::IrRegAllocX64;
14use crate::records::label::Label;
15use crate::records::native_context::NativeContext;
16use crate::records::operand_x_64::OperandX64;
17use crate::records::register_x_64::RegisterX64;
18use luaur_vm::enums::lua_type::lua_Type;
19use luaur_vm::records::lua_table::LuaTable;
20use luaur_vm::type_aliases::t_value::TValue;
21
22pub fn emit_inst_for_g_loop(
23    regs: &mut IrRegAllocX64,
24    build: &mut AssemblyBuilderX64,
25    ra: i32,
26    aux: i32,
27    loop_repeat: &mut Label,
28) {
29    crate::macros::codegen_assert::CODEGEN_ASSERT!(aux >= 0);
30
31    let (table, index) = if luaur_common::FFlag::LuauCodegenSuggestArgumentRegisterX64.get() {
32        (
33            IrCallWrapperX64::suggest_argument_register::<1>(SizeX64::qword, build),
34            IrCallWrapperX64::suggest_argument_register::<2>(SizeX64::qword, build),
35        )
36    } else if build.abi == ABIX64::Windows {
37        (RegisterX64::rdx, RegisterX64::r8)
38    } else {
39        (RegisterX64::rsi, RegisterX64::rdx)
40    };
41
42    let elem_ptr = RegisterX64::rax;
43
44    build.mov(OperandX64::reg(table), luau_reg_value(ra + 1));
45    build.mov(OperandX64::reg(index), luau_reg_value(ra + 2));
46
47    build.mov(
48        OperandX64::reg(sized(elem_ptr, SizeX64::dword)),
49        OperandX64::reg(sized(index, SizeX64::dword)),
50    );
51    build.shl(
52        OperandX64::reg(sized(elem_ptr, SizeX64::dword)),
53        OperandX64::imm(K_TVALUE_SIZE_LOG2),
54    );
55    build.add(
56        OperandX64::reg(elem_ptr),
57        mem(
58            SizeX64::qword,
59            table,
60            core::mem::offset_of!(LuaTable, array) as i32,
61        ),
62    );
63
64    for i in 2..aux {
65        build.mov(
66            luau_reg_tag(ra + 3 + i),
67            OperandX64::imm(lua_Type::LUA_TNIL as i32),
68        );
69    }
70
71    let mut skip_array = Label::default();
72    let mut skip_array_nil = Label::default();
73
74    let mut array_loop = Label::default();
75    build.set_label(&mut array_loop);
76    build.cmp(
77        OperandX64::reg(sized(index, SizeX64::dword)),
78        mem(
79            SizeX64::dword,
80            table,
81            core::mem::offset_of!(LuaTable, sizearray) as i32,
82        ),
83    );
84    build.jcc(ConditionX64::NotBelow, &mut skip_array);
85
86    build.inc(OperandX64::reg(index));
87
88    build.cmp(
89        mem(
90            SizeX64::dword,
91            elem_ptr,
92            core::mem::offset_of!(TValue, tt) as i32,
93        ),
94        OperandX64::imm(lua_Type::LUA_TNIL as i32),
95    );
96    build.jcc(ConditionX64::Equal, &mut skip_array_nil);
97
98    build.mov(luau_reg_value(ra + 2), OperandX64::reg(index));
99
100    build.vcvtsi2sd(
101        OperandX64::reg(xmm(0)),
102        OperandX64::reg(xmm(0)),
103        OperandX64::reg(sized(index, SizeX64::dword)),
104    );
105    build.vmovsd_operand_x_64_operand_x_64(luau_reg_value(ra + 3), OperandX64::reg(xmm(0)));
106    build.mov(
107        luau_reg_tag(ra + 3),
108        OperandX64::imm(lua_Type::LUA_TNUMBER as i32),
109    );
110
111    set_luau_reg(build, xmm(2), ra + 4, mem(SizeX64::xmmword, elem_ptr, 0));
112
113    build.jmp_label(loop_repeat);
114
115    build.set_label_label(&mut skip_array_nil);
116    build.add(
117        OperandX64::reg(elem_ptr),
118        OperandX64::imm(core::mem::size_of::<TValue>() as i32),
119    );
120    build.jmp_label(&mut array_loop);
121
122    build.set_label_label(&mut skip_array);
123
124    if luaur_common::FFlag::LuauCodeGenCallWrapperEmitInst.get() {
125        regs.take_reg(table, k_invalid_inst_idx);
126        regs.take_reg(index, k_invalid_inst_idx);
127
128        let mut call_wrapper = IrCallWrapperX64::ir_call_wrapper_x_64_ir_call_wrapper_x_64(
129            regs,
130            build,
131            k_invalid_inst_idx,
132        );
133        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
134            SizeX64::qword,
135            OperandX64::reg(r_state()),
136            IrOp::ir_op(),
137        );
138        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
139            SizeX64::qword,
140            OperandX64::reg(table),
141            IrOp::ir_op(),
142        );
143        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
144            SizeX64::qword,
145            OperandX64::reg(index),
146            IrOp::ir_op(),
147        );
148        call_wrapper.add_argument_size_x_64_operand_x_64_ir_op(
149            SizeX64::qword,
150            luau_reg_address(ra),
151            IrOp::ir_op(),
152        );
153        call_wrapper
154            .call(&native_context_slot(
155                core::mem::offset_of!(NativeContext, forgLoopNodeIter) as i32,
156            ));
157    } else {
158        let r_arg1 = if build.abi == ABIX64::Windows {
159            RegisterX64::rcx
160        } else {
161            RegisterX64::rdi
162        };
163        let r_arg4 = if build.abi == ABIX64::Windows {
164            RegisterX64::r9
165        } else {
166            RegisterX64::rcx
167        };
168
169        build.mov(OperandX64::reg(r_arg1), OperandX64::reg(r_state()));
170        build.lea_operand_x_64_operand_x_64(OperandX64::reg(r_arg4), luau_reg_address(ra));
171        build.call_operand_x_64(native_context_slot(core::mem::offset_of!(
172            NativeContext,
173            forgLoopNodeIter
174        ) as i32));
175    }
176
177    build.test(
178        OperandX64::reg(sized(RegisterX64::rax, SizeX64::byte)),
179        OperandX64::reg(sized(RegisterX64::rax, SizeX64::byte)),
180    );
181    build.jcc(ConditionX64::NotZero, loop_repeat);
182}
183
184const K_TVALUE_SIZE_LOG2: i32 = 4;
185
186const fn reg(index: u8, size: SizeX64) -> RegisterX64 {
187    RegisterX64 {
188        bits: (index << RegisterX64::INDEX_SHIFT) | size as u8,
189    }
190}
191
192const fn sized(reg: RegisterX64, size: SizeX64) -> RegisterX64 {
193    RegisterX64 {
194        bits: (reg.index() << RegisterX64::INDEX_SHIFT) | size as u8,
195    }
196}
197
198const fn xmm(index: u8) -> RegisterX64 {
199    reg(index, SizeX64::xmmword)
200}
201
202const fn r_state() -> RegisterX64 {
203    reg(15, SizeX64::qword)
204}
205
206const fn r_native_context() -> RegisterX64 {
207    reg(13, SizeX64::qword)
208}
209
210fn mem(size: SizeX64, base: RegisterX64, disp: i32) -> OperandX64 {
211    OperandX64::mem(size, RegisterX64::noreg, 1, base, disp)
212}
213
214fn native_context_slot(disp: i32) -> OperandX64 {
215    mem(SizeX64::qword, r_native_context(), disp)
216}