Skip to main content

luaur_code_gen/methods/
assembly_builder_x_64_place_vex.rs

1use crate::enums::category_x_64::CategoryX64;
2use crate::enums::size_x_64::SizeX64;
3use crate::macros::avx_3_1::AVX_3_1;
4use crate::macros::avx_3_2::AVX_3_2;
5use crate::macros::avx_3_3::AVX_3_3;
6use crate::macros::codegen_assert::CODEGEN_ASSERT;
7use crate::records::assembly_builder_x_64::AssemblyBuilderX64;
8use crate::records::operand_x_64::OperandX64;
9use crate::records::register_x_64::RegisterX64;
10
11impl AssemblyBuilderX64 {
12    pub fn place_vex(
13        &mut self,
14        dst: OperandX64,
15        src1: OperandX64,
16        src2: OperandX64,
17        set_w: bool,
18        mode: u8,
19        prefix: u8,
20    ) {
21        // Keep the asserts, but avoid CODEGEN_ASSERT!'s pointer-signature mismatch by
22        // using explicit boolean checks that don't route through the macro.
23        if !(dst.cat == CategoryX64::reg) {
24            luaur_common::LUAU_DEBUGBREAK!();
25        }
26        if !(src1.cat == CategoryX64::reg) {
27            luaur_common::LUAU_DEBUGBREAK!();
28        }
29        if !(src2.cat == CategoryX64::reg || src2.cat == CategoryX64::mem) {
30            luaur_common::LUAU_DEBUGBREAK!();
31        }
32
33        // Several translated AVX call sites pass the RAW x86 opcode-map /
34        // mandatory-prefix bytes (0x0F/0x38/0x3A and 0x66/0xF2/0xF3) instead of the
35        // narrow VEX field encodings the C++ constants use (AVX_0F=0b00001,
36        // AVX_66=0b01, ...). Those overflow the 5-bit mmmmm / 2-bit pp fields and
37        // corrupt the surrounding bits. Normalize here (the single VEX choke point);
38        // values already in proper field form (0..=3) fall through unchanged.
39        let mode = match mode {
40            0x0F => 0b00001,
41            0x38 => 0b00010,
42            0x3A => 0b00011,
43            m => m,
44        };
45        let prefix = match prefix {
46            0x66 => 0b01,
47            0xF3 => 0b10,
48            0xF2 => 0b11,
49            p => p,
50        };
51
52        // C++ `dst.base.size == ymmword` — the L (256-bit) bit comes from the
53        // destination REGISTER's size. A reg operand's `memSize` is always `none`,
54        // so the original `dst.memSize` check forced L=0 (wrong for every ymm op).
55        let l: u8 = if dst.base.size() == SizeX64::ymmword {
56            1
57        } else {
58            0
59        };
60
61        self.place(AVX_3_1());
62        self.place(AVX_3_2(dst.base, src2.index, src2.base, mode));
63        self.place(AVX_3_3(set_w, src1.base, l, prefix));
64    }
65}