cranelift_codegen/isa/x64/encoding/
vex.rs

1//! Encodes VEX instructions. These instructions are those added by the Advanced Vector Extensions
2//! (AVX).
3
4use super::evex::{Register, RegisterOrAmode};
5use super::rex::{LegacyPrefixes, OpcodeMap};
6use super::ByteSink;
7use crate::isa::x64::args::Amode;
8use crate::isa::x64::encoding::rex;
9use crate::isa::x64::inst::Inst;
10use crate::machinst::MachBuffer;
11
12/// Constructs a VEX-encoded instruction using a builder pattern. This approach makes it visually
13/// easier to transform something the manual's syntax, `VEX.128.66.0F 73 /7 ib` to code:
14/// `VexInstruction::new().length(...).prefix(...).map(...).w(true).opcode(0x1F).reg(...).rm(...)`.
15pub struct VexInstruction {
16    length: VexVectorLength,
17    prefix: LegacyPrefixes,
18    map: OpcodeMap,
19    opcode: u8,
20    w: bool,
21    reg: u8,
22    rm: RegisterOrAmode,
23    vvvv: Option<Register>,
24    imm: Option<u8>,
25}
26
27impl Default for VexInstruction {
28    fn default() -> Self {
29        Self {
30            length: VexVectorLength::default(),
31            prefix: LegacyPrefixes::None,
32            map: OpcodeMap::None,
33            opcode: 0x00,
34            w: false,
35            reg: 0x00,
36            rm: RegisterOrAmode::Register(Register::default()),
37            vvvv: None,
38            imm: None,
39        }
40    }
41}
42
43impl VexInstruction {
44    /// Construct a default VEX instruction.
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    /// Set the length of the instruction.
50    #[inline(always)]
51    pub fn length(mut self, length: VexVectorLength) -> Self {
52        self.length = length;
53        self
54    }
55
56    /// Set the legacy prefix byte of the instruction: None | 66 | F2 | F3. VEX instructions
57    /// pack these into the prefix, not as separate bytes.
58    #[inline(always)]
59    pub fn prefix(mut self, prefix: LegacyPrefixes) -> Self {
60        debug_assert!(
61            prefix == LegacyPrefixes::None
62                || prefix == LegacyPrefixes::_66
63                || prefix == LegacyPrefixes::_F2
64                || prefix == LegacyPrefixes::_F3
65        );
66
67        self.prefix = prefix;
68        self
69    }
70
71    /// Set the opcode map byte of the instruction: None | 0F | 0F38 | 0F3A. VEX instructions pack
72    /// these into the prefix, not as separate bytes.
73    #[inline(always)]
74    pub fn map(mut self, map: OpcodeMap) -> Self {
75        self.map = map;
76        self
77    }
78
79    /// Set the W bit, denoted by `.W1` or `.W0` in the instruction string.
80    /// Typically used to indicate an instruction using 64 bits of an operand (e.g.
81    /// 64 bit lanes). EVEX packs this bit in the EVEX prefix; previous encodings used the REX
82    /// prefix.
83    #[inline(always)]
84    pub fn w(mut self, w: bool) -> Self {
85        self.w = w;
86        self
87    }
88
89    /// Set the instruction opcode byte.
90    #[inline(always)]
91    pub fn opcode(mut self, opcode: u8) -> Self {
92        self.opcode = opcode;
93        self
94    }
95
96    /// Set the register to use for the `reg` bits; many instructions use this as the write operand.
97    #[inline(always)]
98    pub fn reg(mut self, reg: impl Into<Register>) -> Self {
99        self.reg = reg.into().into();
100        self
101    }
102
103    /// Some instructions use the ModRM.reg field as an opcode extension. This is usually denoted by
104    /// a `/n` field in the manual.
105    #[inline(always)]
106    pub fn opcode_ext(mut self, n: u8) -> Self {
107        self.reg = n;
108        self
109    }
110
111    /// Set the register to use for the `rm` bits; many instructions use this
112    /// as the "read from register/memory" operand. Setting this affects both
113    /// the ModRM byte (`rm` section) and the VEX prefix (the extension bits
114    /// for register encodings > 8).
115    #[inline(always)]
116    pub fn rm(mut self, reg: impl Into<RegisterOrAmode>) -> Self {
117        self.rm = reg.into();
118        self
119    }
120
121    /// Set the `vvvv` register; some instructions allow using this as a second, non-destructive
122    /// source register in 3-operand instructions (e.g. 2 read, 1 write).
123    #[allow(dead_code)]
124    #[inline(always)]
125    pub fn vvvv(mut self, reg: impl Into<Register>) -> Self {
126        self.vvvv = Some(reg.into());
127        self
128    }
129
130    /// Set the imm byte when used for a register. The reg bits are stored in `imm8[7:4]` with
131    /// the lower bits unused. Overrides a previously set [Self::imm] field.
132    #[inline(always)]
133    pub fn imm_reg(mut self, reg: impl Into<Register>) -> Self {
134        let reg: u8 = reg.into().into();
135        self.imm = Some((reg & 0xf) << 4);
136        self
137    }
138
139    /// Set the imm byte.
140    /// Overrides a previously set [Self::imm_reg] field.
141    #[inline(always)]
142    pub fn imm(mut self, imm: u8) -> Self {
143        self.imm = Some(imm);
144        self
145    }
146
147    /// The R bit in encoded format (inverted).
148    #[inline(always)]
149    fn r_bit(&self) -> u8 {
150        (!(self.reg >> 3)) & 1
151    }
152
153    /// The X bit in encoded format (inverted).
154    #[inline(always)]
155    fn x_bit(&self) -> u8 {
156        let reg = match &self.rm {
157            RegisterOrAmode::Register(_) => 0,
158            RegisterOrAmode::Amode(Amode::ImmReg { .. }) => 0,
159            RegisterOrAmode::Amode(Amode::ImmRegRegShift { index, .. }) => {
160                index.to_real_reg().unwrap().hw_enc()
161            }
162            RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0,
163        };
164
165        !(reg >> 3) & 1
166    }
167
168    /// The B bit in encoded format (inverted).
169    #[inline(always)]
170    fn b_bit(&self) -> u8 {
171        let reg = match &self.rm {
172            RegisterOrAmode::Register(r) => (*r).into(),
173            RegisterOrAmode::Amode(Amode::ImmReg { base, .. }) => {
174                base.to_real_reg().unwrap().hw_enc()
175            }
176            RegisterOrAmode::Amode(Amode::ImmRegRegShift { base, .. }) => {
177                base.to_real_reg().unwrap().hw_enc()
178            }
179            RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0,
180        };
181
182        !(reg >> 3) & 1
183    }
184
185    /// Is the 2 byte prefix available for this instruction?
186    /// We essentially just check if we need any of the bits that are only available
187    /// in the 3 byte instruction
188    #[inline(always)]
189    fn use_2byte_prefix(&self) -> bool {
190        // These bits are only represented on the 3 byte prefix, so their presence
191        // implies the use of the 3 byte prefix
192        self.b_bit() == 1 && self.x_bit() == 1 &&
193        // The presence of W1 in the opcode column implies the opcode must be encoded using the
194        // 3-byte form of the VEX prefix.
195        self.w == false &&
196        // The presence of 0F3A and 0F38 in the opcode column implies that opcode can only be
197        // encoded by the three-byte form of VEX
198        !(self.map == OpcodeMap::_0F3A || self.map == OpcodeMap::_0F38)
199    }
200
201    /// The last byte of the 2byte and 3byte prefixes is mostly the same, share the common
202    /// encoding logic here.
203    #[inline(always)]
204    fn prefix_last_byte(&self) -> u8 {
205        let vvvv = self.vvvv.map(|r| r.into()).unwrap_or(0x00);
206
207        let mut byte = 0x00;
208        byte |= self.prefix.bits();
209        byte |= self.length.bits() << 2;
210        byte |= ((!vvvv) & 0xF) << 3;
211        byte
212    }
213
214    /// Encode the 2 byte prefix
215    #[inline(always)]
216    fn encode_2byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
217        //  2 bytes:
218        //    +-----+ +-------------------+
219        //    | C5h | | R | vvvv | L | pp |
220        //    +-----+ +-------------------+
221
222        let last_byte = self.prefix_last_byte() | (self.r_bit() << 7);
223
224        sink.put1(0xC5);
225        sink.put1(last_byte);
226    }
227
228    /// Encode the 3 byte prefix
229    #[inline(always)]
230    fn encode_3byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
231        //  3 bytes:
232        //    +-----+ +--------------+ +-------------------+
233        //    | C4h | | RXB | m-mmmm | | W | vvvv | L | pp |
234        //    +-----+ +--------------+ +-------------------+
235
236        let mut second_byte = 0x00;
237        second_byte |= self.map.bits(); // m-mmmm field
238        second_byte |= self.b_bit() << 5;
239        second_byte |= self.x_bit() << 6;
240        second_byte |= self.r_bit() << 7;
241
242        let w_bit = self.w as u8;
243        let last_byte = self.prefix_last_byte() | (w_bit << 7);
244
245        sink.put1(0xC4);
246        sink.put1(second_byte);
247        sink.put1(last_byte);
248    }
249
250    /// Emit the VEX-encoded instruction to the provided buffer.
251    pub fn encode(&self, sink: &mut MachBuffer<Inst>) {
252        if let RegisterOrAmode::Amode(amode) = &self.rm {
253            if let Some(trap_code) = amode.get_flags().trap_code() {
254                sink.add_trap(trap_code);
255            }
256        }
257
258        // 2/3 byte prefix
259        if self.use_2byte_prefix() {
260            self.encode_2byte_prefix(sink);
261        } else {
262            self.encode_3byte_prefix(sink);
263        }
264
265        // 1 Byte Opcode
266        sink.put1(self.opcode);
267
268        match &self.rm {
269            // Not all instructions use Reg as a reg, some use it as an extension
270            // of the opcode.
271            RegisterOrAmode::Register(reg) => {
272                let rm: u8 = (*reg).into();
273                sink.put1(rex::encode_modrm(3, self.reg & 7, rm & 7));
274            }
275            // For address-based modes reuse the logic from the `rex` module
276            // for the modrm and trailing bytes since VEX uses the same
277            // encoding.
278            RegisterOrAmode::Amode(amode) => {
279                let bytes_at_end = if self.imm.is_some() { 1 } else { 0 };
280                rex::emit_modrm_sib_disp(sink, self.reg & 7, amode, bytes_at_end, None);
281            }
282        }
283
284        // Optional 1 Byte imm
285        if let Some(imm) = self.imm {
286            sink.put1(imm);
287        }
288    }
289}
290
291/// The VEX format allows choosing a vector length in the `L` bit.
292#[allow(dead_code, missing_docs)] // Wider-length vectors are not yet used.
293pub enum VexVectorLength {
294    V128,
295    V256,
296}
297
298impl VexVectorLength {
299    /// Encode the `L` bit.
300    fn bits(&self) -> u8 {
301        match self {
302            Self::V128 => 0b0,
303            Self::V256 => 0b1,
304        }
305    }
306}
307
308impl Default for VexVectorLength {
309    fn default() -> Self {
310        Self::V128
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317    use crate::isa::x64::inst::args::Gpr;
318    use crate::isa::x64::inst::regs;
319    use crate::opts::MemFlags;
320
321    #[test]
322    fn vpslldq() {
323        // VEX.128.66.0F 73 /7 ib
324        // VPSLLDQ xmm1, xmm2, imm8
325
326        let dst = regs::xmm1().to_real_reg().unwrap().hw_enc();
327        let src = regs::xmm2().to_real_reg().unwrap().hw_enc();
328        let mut sink = MachBuffer::new();
329
330        VexInstruction::new()
331            .length(VexVectorLength::V128)
332            .prefix(LegacyPrefixes::_66)
333            .map(OpcodeMap::_0F)
334            .opcode(0x73)
335            .opcode_ext(7)
336            .vvvv(dst)
337            .rm(src)
338            .imm(0x17)
339            .encode(&mut sink);
340
341        let bytes = sink
342            .finish(&Default::default(), &mut Default::default())
343            .data;
344        assert_eq!(bytes.as_slice(), [0xc5, 0xf1, 0x73, 0xfa, 0x17]);
345    }
346
347    #[test]
348    fn vblendvpd() {
349        // A four operand instruction
350        // VEX.128.66.0F3A.W0 4B /r /is4
351        // VBLENDVPD xmm1, xmm2, xmm3, xmm4
352
353        let dst = regs::xmm1().to_real_reg().unwrap().hw_enc();
354        let a = regs::xmm2().to_real_reg().unwrap().hw_enc();
355        let b = regs::xmm3().to_real_reg().unwrap().hw_enc();
356        let c = regs::xmm4().to_real_reg().unwrap().hw_enc();
357        let mut sink = MachBuffer::new();
358
359        VexInstruction::new()
360            .length(VexVectorLength::V128)
361            .prefix(LegacyPrefixes::_66)
362            .map(OpcodeMap::_0F3A)
363            .w(false)
364            .opcode(0x4B)
365            .reg(dst)
366            .vvvv(a)
367            .rm(b)
368            .imm_reg(c)
369            .encode(&mut sink);
370
371        let bytes = sink
372            .finish(&Default::default(), &mut Default::default())
373            .data;
374        assert_eq!(bytes.as_slice(), [0xc4, 0xe3, 0x69, 0x4b, 0xcb, 0x40]);
375    }
376
377    #[test]
378    fn vcmpps() {
379        // VEX.128.0F.WIG C2 /r ib
380        // VCMPPS ymm10, ymm11, ymm12, 4 // neq
381
382        let dst = regs::xmm10().to_real_reg().unwrap().hw_enc();
383        let a = regs::xmm11().to_real_reg().unwrap().hw_enc();
384        let b = regs::xmm12().to_real_reg().unwrap().hw_enc();
385        let mut sink = MachBuffer::new();
386
387        VexInstruction::new()
388            .length(VexVectorLength::V256)
389            .prefix(LegacyPrefixes::None)
390            .map(OpcodeMap::_0F)
391            .opcode(0xC2)
392            .reg(dst)
393            .vvvv(a)
394            .rm(b)
395            .imm(4)
396            .encode(&mut sink);
397
398        let bytes = sink
399            .finish(&Default::default(), &mut Default::default())
400            .data;
401        assert_eq!(bytes.as_slice(), [0xc4, 0x41, 0x24, 0xc2, 0xd4, 0x04]);
402    }
403
404    #[test]
405    fn vandnps() {
406        // VEX.128.0F 55 /r
407        // VANDNPS xmm0, xmm1, xmm2
408
409        let dst = regs::xmm2().to_real_reg().unwrap().hw_enc();
410        let src1 = regs::xmm1().to_real_reg().unwrap().hw_enc();
411        let src2 = regs::xmm0().to_real_reg().unwrap().hw_enc();
412        let mut sink = MachBuffer::new();
413
414        VexInstruction::new()
415            .length(VexVectorLength::V128)
416            .prefix(LegacyPrefixes::None)
417            .map(OpcodeMap::_0F)
418            .opcode(0x55)
419            .reg(dst)
420            .vvvv(src1)
421            .rm(src2)
422            .encode(&mut sink);
423
424        let bytes = sink
425            .finish(&Default::default(), &mut Default::default())
426            .data;
427        assert_eq!(bytes.as_slice(), [0xc5, 0xf0, 0x55, 0xd0]);
428    }
429
430    #[test]
431    fn vandnps_mem() {
432        // VEX.128.0F 55 /r
433        // VANDNPS 10(%r13), xmm1, xmm2
434
435        let dst = regs::xmm2().to_real_reg().unwrap().hw_enc();
436        let src1 = regs::xmm1().to_real_reg().unwrap().hw_enc();
437        let src2 = Amode::ImmReg {
438            base: regs::r13(),
439            flags: MemFlags::trusted(),
440            simm32: 10,
441        };
442        let mut sink = MachBuffer::new();
443
444        VexInstruction::new()
445            .length(VexVectorLength::V128)
446            .prefix(LegacyPrefixes::None)
447            .map(OpcodeMap::_0F)
448            .opcode(0x55)
449            .reg(dst)
450            .vvvv(src1)
451            .rm(src2)
452            .encode(&mut sink);
453
454        let bytes = sink
455            .finish(&Default::default(), &mut Default::default())
456            .data;
457        assert_eq!(bytes.as_slice(), [0xc4, 0xc1, 0x70, 0x55, 0x55, 0x0a]);
458    }
459
460    #[test]
461    fn vandnps_more_mem() {
462        // VEX.128.0F 55 /r
463        // VANDNPS 100(%rax,%r13,4), xmm1, xmm2
464
465        let dst = regs::xmm2().to_real_reg().unwrap().hw_enc();
466        let src1 = regs::xmm1().to_real_reg().unwrap().hw_enc();
467        let src2 = Amode::ImmRegRegShift {
468            base: Gpr::unwrap_new(regs::rax()),
469            index: Gpr::unwrap_new(regs::r13()),
470            flags: MemFlags::trusted(),
471            simm32: 100,
472            shift: 2,
473        };
474        let mut sink = MachBuffer::new();
475
476        VexInstruction::new()
477            .length(VexVectorLength::V128)
478            .prefix(LegacyPrefixes::None)
479            .map(OpcodeMap::_0F)
480            .opcode(0x55)
481            .reg(dst)
482            .vvvv(src1)
483            .rm(src2)
484            .encode(&mut sink);
485
486        let bytes = sink
487            .finish(&Default::default(), &mut Default::default())
488            .data;
489        assert_eq!(bytes.as_slice(), [0xc4, 0xa1, 0x70, 0x55, 0x54, 0xa8, 100]);
490    }
491}