cranelift_assembler_x64/
vex.rs

1//! Encoding logic for VEX instructions.
2
3use crate::api::CodeSink;
4
5/// Construct and emit the VEX prefix bytes.
6pub enum VexPrefix {
7    TwoByte(u8),
8    ThreeByte(u8, u8),
9}
10
11/// The VEX prefix only ever uses the top bit (bit 3--the fourth bit) of any
12/// HW-encoded register.
13#[inline(always)]
14fn invert_top_bit(enc: u8) -> u8 {
15    (!(enc >> 3)) & 1
16}
17
18fn use_2byte_prefix(x: u8, b: u8, w: bool, mmmmm: u8) -> bool {
19    // These bits are only represented on the 3 byte prefix, so their presence
20    // implies the use of the 3 byte prefix
21    b == 1 && x == 1 &&
22    // The presence of W1 in the opcode column implies the opcode must be
23    // encoded using the 3-byte form of the VEX prefix.
24    w == false &&
25    // The presence of 0F3A and 0F38 in the opcode column implies that
26    // opcode can only be encoded by the three-byte form of VEX.
27    !(mmmmm == 0b10 || mmmmm == 0b11)
28}
29
30impl VexPrefix {
31    /// Construct the [`VexPrefix`] for a ternary instruction.
32    ///
33    /// Used with a single register operand:
34    /// - `reg` and `vvvv` hold HW-encoded registers.
35    /// - `b` and `x` hold the (optional) HW-encoded registers for the `rm`
36    ///   operand.
37    /// - the other fields (`l`, `pp`, `mmmmm`, `w`) correspond directly to
38    ///   fields in the VEX prefix.
39    #[inline]
40    #[must_use]
41    pub fn three_op(
42        reg: u8,
43        vvvv: u8,
44        (b, x): (Option<u8>, Option<u8>),
45        l: u8,
46        pp: u8,
47        mmmmm: u8,
48        w: bool,
49    ) -> Self {
50        let r = invert_top_bit(reg);
51        let b = invert_top_bit(b.unwrap_or(0));
52        let x = invert_top_bit(x.unwrap_or(0));
53
54        if use_2byte_prefix(x, b, w, mmmmm) {
55            // 2-byte VEX prefix.
56            //
57            // +-----+ +-------------------+
58            // | C5h | | R | vvvv | L | pp |
59            // +-----+ +-------------------+
60            debug_assert!(vvvv <= 0b1111);
61            debug_assert!(l <= 0b1);
62            debug_assert!(pp <= 0b11);
63            let last_byte = r << 7 | (!vvvv & 0b1111) << 3 | (l & 0b1) << 2 | (pp & 0b11);
64
65            Self::TwoByte(last_byte)
66        } else {
67            // 3-byte VEX prefix.
68            //
69            // +-----+ +--------------+ +-------------------+
70            // | C4h | | RXB | m-mmmm | | W | vvvv | L | pp |
71            // +-----+ +--------------+ +-------------------+
72            debug_assert!(mmmmm >= 0b01 && mmmmm <= 0b11);
73            let second_byte = r << 7 | x << 6 | b << 5 | mmmmm;
74
75            debug_assert!(vvvv <= 0b1111);
76            debug_assert!(l <= 0b1);
77            debug_assert!(pp <= 0b11);
78            let last_byte = (w as u8) << 7 | (!vvvv & 0b1111) << 3 | (l & 0b1) << 2 | (pp & 0b11);
79
80            Self::ThreeByte(second_byte, last_byte)
81        }
82    }
83
84    /// Construct the [`VexPrefix`] for a binary instruction.
85    ///
86    /// This simply but conveniently reuses [`VexPrefix::three_op`] with a
87    /// `vvvv` value of `0`.
88    #[inline]
89    #[must_use]
90    pub fn two_op(
91        reg: u8,
92        (b, x): (Option<u8>, Option<u8>),
93        l: u8,
94        pp: u8,
95        mmmmm: u8,
96        w: bool,
97    ) -> Self {
98        Self::three_op(reg, 0, (b, x), l, pp, mmmmm, w)
99    }
100
101    pub(crate) fn encode(&self, sink: &mut impl CodeSink) {
102        match self {
103            VexPrefix::TwoByte(last_byte) => {
104                sink.put1(0xC5);
105                sink.put1(*last_byte);
106            }
107            VexPrefix::ThreeByte(second_byte, last_byte) => {
108                sink.put1(0xC4);
109                sink.put1(*second_byte);
110                sink.put1(*last_byte);
111            }
112        }
113    }
114}