1 //! Encoding logic for VEX instructions.
2 
3 use crate::api::CodeSink;
4 
5 /// Construct and emit the VEX prefix bytes.
6 pub 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)]
invert_top_bit(enc: u8) -> u814 fn invert_top_bit(enc: u8) -> u8 {
15     (!(enc >> 3)) & 1
16 }
17 
use_2byte_prefix(x: u8, b: u8, w: bool, mmmmm: u8) -> bool18 fn 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 
30 impl 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]
three_op( reg: u8, vvvv: u8, (b, x): (Option<u8>, Option<u8>), l: u8, pp: u8, mmmmm: u8, w: bool, ) -> Self41     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]
two_op( reg: u8, (b, x): (Option<u8>, Option<u8>), l: u8, pp: u8, mmmmm: u8, w: bool, ) -> Self90     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 
encode(&self, sink: &mut impl CodeSink)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 }
115