cranelift_codegen/isa/x64/encoding/
vex.rs1use 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
13pub 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 pub fn new() -> Self {
65 Self::default()
66 }
67
68 #[inline(always)]
70 pub fn length(mut self, length: VexVectorLength) -> Self {
71 self.length = length;
72 self
73 }
74
75 #[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 #[inline(always)]
93 pub fn map(mut self, map: OpcodeMap) -> Self {
94 self.map = map;
95 self
96 }
97
98 #[inline(always)]
103 pub fn w(mut self, w: bool) -> Self {
104 self.w = w;
105 self
106 }
107
108 #[inline(always)]
110 pub fn opcode(mut self, opcode: u8) -> Self {
111 self.opcode = opcode;
112 self
113 }
114
115 #[inline(always)]
117 pub fn reg(mut self, reg: impl Into<Register>) -> Self {
118 self.reg = reg.into().into();
119 self
120 }
121
122 #[inline(always)]
125 pub fn opcode_ext(mut self, n: u8) -> Self {
126 self.reg = n;
127 self
128 }
129
130 #[inline(always)]
135 pub fn rm(mut self, reg: impl Into<RegisterOrAmode>) -> Self {
136 self.rm = reg.into();
137 self
138 }
139
140 #[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 #[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 #[inline(always)]
161 pub fn imm(mut self, imm: u8) -> Self {
162 self.imm = Some(imm);
163 self
164 }
165
166 #[inline(always)]
168 fn r_bit(&self) -> u8 {
169 (!(self.reg >> 3)) & 1
170 }
171
172 #[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 #[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 #[inline(always)]
208 fn use_2byte_prefix(&self) -> bool {
209 self.b_bit() == 1 && self.x_bit() == 1 &&
212 self.w == false &&
215 !(self.map == OpcodeMap::_0F3A || self.map == OpcodeMap::_0F38)
218 }
219
220 #[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 #[inline(always)]
235 fn encode_2byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
236 let last_byte = self.prefix_last_byte() | (self.r_bit() << 7);
242
243 sink.put1(0xC5);
244 sink.put1(last_byte);
245 }
246
247 #[inline(always)]
249 fn encode_3byte_prefix<CS: ByteSink + ?Sized>(&self, sink: &mut CS) {
250 let mut second_byte = 0x00;
256 second_byte |= self.map.bits(); 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 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 if self.use_2byte_prefix() {
279 self.encode_2byte_prefix(sink);
280 } else {
281 self.encode_3byte_prefix(sink);
282 }
283
284 sink.put1(self.opcode);
286
287 match &self.rm {
288 RegisterOrAmode::Register(reg) => {
291 let rm: u8 = (*reg).into();
292 sink.put1(rex::encode_modrm(3, self.reg & 7, rm & 7));
293 }
294 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 if let Some(imm) = self.imm {
305 sink.put1(imm);
306 }
307 }
308}
309
310#[allow(dead_code, missing_docs)] pub enum VexVectorLength {
313 V128,
314 V256,
315}
316
317impl VexVectorLength {
318 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 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 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 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 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 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 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}