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