cranelift_codegen/isa/x64/encoding/
vex.rs1use 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
12pub 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 pub fn new() -> Self {
46 Self::default()
47 }
48
49 #[inline(always)]
51 pub fn length(mut self, length: VexVectorLength) -> Self {
52 self.length = length;
53 self
54 }
55
56 #[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 #[inline(always)]
74 pub fn map(mut self, map: OpcodeMap) -> Self {
75 self.map = map;
76 self
77 }
78
79 #[inline(always)]
84 pub fn w(mut self, w: bool) -> Self {
85 self.w = w;
86 self
87 }
88
89 #[inline(always)]
91 pub fn opcode(mut self, opcode: u8) -> Self {
92 self.opcode = opcode;
93 self
94 }
95
96 #[inline(always)]
98 pub fn reg(mut self, reg: impl Into<Register>) -> Self {
99 self.reg = reg.into().into();
100 self
101 }
102
103 #[inline(always)]
106 pub fn opcode_ext(mut self, n: u8) -> Self {
107 self.reg = n;
108 self
109 }
110
111 #[inline(always)]
116 pub fn rm(mut self, reg: impl Into<RegisterOrAmode>) -> Self {
117 self.rm = reg.into();
118 self
119 }
120
121 #[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 #[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 #[inline(always)]
142 pub fn imm(mut self, imm: u8) -> Self {
143 self.imm = Some(imm);
144 self
145 }
146
147 #[inline(always)]
149 fn r_bit(&self) -> u8 {
150 (!(self.reg >> 3)) & 1
151 }
152
153 #[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 #[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 #[inline(always)]
189 fn use_2byte_prefix(&self) -> bool {
190 self.b_bit() == 1 && self.x_bit() == 1 &&
193 self.w == false &&
196 !(self.map == OpcodeMap::_0F3A || self.map == OpcodeMap::_0F38)
199 }
200
201 #[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 #[inline(always)]
216 fn encode_2byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
217 let last_byte = self.prefix_last_byte() | (self.r_bit() << 7);
223
224 sink.put1(0xC5);
225 sink.put1(last_byte);
226 }
227
228 #[inline(always)]
230 fn encode_3byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
231 let mut second_byte = 0x00;
237 second_byte |= self.map.bits(); 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 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 if self.use_2byte_prefix() {
260 self.encode_2byte_prefix(sink);
261 } else {
262 self.encode_3byte_prefix(sink);
263 }
264
265 sink.put1(self.opcode);
267
268 match &self.rm {
269 RegisterOrAmode::Register(reg) => {
272 let rm: u8 = (*reg).into();
273 sink.put1(rex::encode_modrm(3, self.reg & 7, rm & 7));
274 }
275 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 if let Some(imm) = self.imm {
286 sink.put1(imm);
287 }
288 }
289}
290
291#[allow(dead_code, missing_docs)] pub enum VexVectorLength {
294 V128,
295 V256,
296}
297
298impl VexVectorLength {
299 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 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 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 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 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 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 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}