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}