1use super::rex::{self, LegacyPrefixes, OpcodeMap};
18use crate::isa::x64::args::{Amode, Avx512TupleType};
19use crate::isa::x64::inst::Inst;
20use crate::MachBuffer;
21use core::ops::RangeInclusive;
22
23pub struct EvexInstruction {
27 bits: u32,
28 opcode: u8,
29 reg: Register,
30 rm: RegisterOrAmode,
31 tuple_type: Option<Avx512TupleType>,
32 imm: Option<u8>,
33}
34
35impl Default for EvexInstruction {
46 fn default() -> Self {
47 Self {
48 bits: 0x08_7C_F0_62,
49 opcode: 0,
50 reg: Register::default(),
51 rm: RegisterOrAmode::Register(Register::default()),
52 tuple_type: None,
53 imm: None,
54 }
55 }
56}
57
58#[allow(non_upper_case_globals)] impl EvexInstruction {
60 pub fn new() -> Self {
62 Self::default()
63 }
64
65 #[inline(always)]
70 pub fn length(mut self, length: EvexVectorLength) -> Self {
71 self.write(Self::LL, EvexContext::Other { length }.bits() as u32);
72 self
73 }
74
75 #[inline(always)]
78 pub fn prefix(mut self, prefix: LegacyPrefixes) -> Self {
79 self.write(Self::pp, prefix.bits() as u32);
80 self
81 }
82
83 #[inline(always)]
86 pub fn map(mut self, map: OpcodeMap) -> Self {
87 self.write(Self::mm, map.bits() as u32);
88 self
89 }
90
91 #[inline(always)]
95 pub fn w(mut self, w: bool) -> Self {
96 self.write(Self::W, w as u32);
97 self
98 }
99
100 #[inline(always)]
102 pub fn opcode(mut self, opcode: u8) -> Self {
103 self.opcode = opcode;
104 self
105 }
106
107 #[inline(always)]
110 pub fn tuple_type(mut self, tt: Avx512TupleType) -> Self {
111 self.tuple_type = Some(tt);
112 self
113 }
114
115 #[inline(always)]
119 pub fn reg(mut self, reg: impl Into<Register>) -> Self {
120 self.reg = reg.into();
121 let r = !(self.reg.0 >> 3) & 1;
122 let r_ = !(self.reg.0 >> 4) & 1;
123 self.write(Self::R, r as u32);
124 self.write(Self::R_, r_ as u32);
125 self
126 }
127
128 #[allow(dead_code)]
131 #[inline(always)]
132 pub fn mask(mut self, mask: EvexMasking) -> Self {
133 self.write(Self::aaa, mask.aaa_bits() as u32);
134 self.write(Self::z, mask.z_bit() as u32);
135 self
136 }
137
138 #[allow(dead_code)]
141 #[inline(always)]
142 pub fn vvvvv(mut self, reg: impl Into<Register>) -> Self {
143 let reg = reg.into();
144 self.write(Self::vvvv, !(reg.0 as u32) & 0b1111);
145 self.write(Self::V_, !(reg.0 as u32 >> 4) & 0b1);
146 self
147 }
148
149 #[inline(always)]
154 pub fn rm(mut self, reg: impl Into<RegisterOrAmode>) -> Self {
155 self.rm = reg.into();
158 let x = match &self.rm {
159 RegisterOrAmode::Register(r) => r.0 >> 4,
160 RegisterOrAmode::Amode(Amode::ImmRegRegShift { index, .. }) => {
161 index.to_real_reg().unwrap().hw_enc() >> 3
162 }
163
164 RegisterOrAmode::Amode(Amode::ImmReg { .. }) => 0,
167 RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0,
168 };
169 self.write(Self::X, u32::from(!x & 1));
171
172 let b = match &self.rm {
173 RegisterOrAmode::Register(r) => r.0 >> 3,
174 RegisterOrAmode::Amode(Amode::ImmReg { base, .. }) => {
175 base.to_real_reg().unwrap().hw_enc() >> 3
176 }
177 RegisterOrAmode::Amode(Amode::ImmRegRegShift { base, .. }) => {
178 base.to_real_reg().unwrap().hw_enc() >> 3
179 }
180 RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0,
182 };
183 self.write(Self::B, u32::from(!b & 1));
185 self
186 }
187
188 #[inline(always)]
190 pub fn imm(mut self, imm: u8) -> Self {
191 self.imm = Some(imm);
192 self
193 }
194
195 pub fn encode(&self, sink: &mut MachBuffer<Inst>) {
203 if let RegisterOrAmode::Amode(amode) = &self.rm {
204 if let Some(trap_code) = amode.get_flags().trap_code() {
205 sink.add_trap(trap_code);
206 }
207 }
208 sink.put4(self.bits);
209 sink.put1(self.opcode);
210
211 match &self.rm {
212 RegisterOrAmode::Register(reg) => {
213 let rm: u8 = (*reg).into();
214 sink.put1(rex::encode_modrm(3, self.reg.0 & 7, rm & 7));
215 }
216 RegisterOrAmode::Amode(amode) => {
217 let scaling = self.scaling_for_8bit_disp();
218
219 let bytes_at_end = if self.imm.is_some() { 1 } else { 0 };
220 rex::emit_modrm_sib_disp(sink, self.reg.0 & 7, amode, bytes_at_end, Some(scaling));
221 }
222 }
223 if let Some(imm) = self.imm {
224 sink.put1(imm);
225 }
226 }
227
228 const mm: RangeInclusive<u8> = 8..=9;
243 const R_: RangeInclusive<u8> = 12..=12;
244 const B: RangeInclusive<u8> = 13..=13;
245 const X: RangeInclusive<u8> = 14..=14;
246 const R: RangeInclusive<u8> = 15..=15;
247
248 const pp: RangeInclusive<u8> = 16..=17;
250 const vvvv: RangeInclusive<u8> = 19..=22;
251 const W: RangeInclusive<u8> = 23..=23;
252
253 const aaa: RangeInclusive<u8> = 24..=26;
255 const V_: RangeInclusive<u8> = 27..=27;
256 const b: RangeInclusive<u8> = 28..=28;
257 const LL: RangeInclusive<u8> = 29..=30;
258 const z: RangeInclusive<u8> = 31..=31;
259
260 #[inline]
262 fn write(&mut self, range: RangeInclusive<u8>, value: u32) {
263 assert!(ExactSizeIterator::len(&range) > 0);
264 let size = range.end() - range.start() + 1; let mask: u32 = (1 << size) - 1; debug_assert!(
267 value <= mask,
268 "The written value should have fewer than {size} bits."
269 );
270 let mask_complement = !(mask << *range.start()); self.bits &= mask_complement; let value = value << *range.start(); self.bits |= value; }
275
276 #[inline]
279 fn read(&self, range: RangeInclusive<u8>) -> u32 {
280 (self.bits >> range.start()) & ((1 << range.len()) - 1)
281 }
282
283 fn scaling_for_8bit_disp(&self) -> i8 {
284 use Avx512TupleType::*;
285
286 let vector_size_scaling = || match self.read(Self::LL) {
287 0b00 => 16,
288 0b01 => 32,
289 0b10 => 64,
290 _ => unreachable!(),
291 };
292
293 match self.tuple_type {
294 Some(Full) => {
295 if self.read(Self::b) == 1 {
296 if self.read(Self::W) == 0 {
297 4
298 } else {
299 8
300 }
301 } else {
302 vector_size_scaling()
303 }
304 }
305 Some(FullMem) => vector_size_scaling(),
306 Some(Mem128) => 16,
307 None => panic!("tuple type was not set"),
308 }
309 }
310}
311
312#[derive(Debug, Copy, Clone, Default)]
315pub struct Register(u8);
316impl From<u8> for Register {
317 fn from(reg: u8) -> Self {
318 debug_assert!(reg < 16);
319 Self(reg)
320 }
321}
322impl Into<u8> for Register {
323 fn into(self) -> u8 {
324 self.0
325 }
326}
327
328#[allow(missing_docs)]
329#[derive(Debug, Clone)]
330pub enum RegisterOrAmode {
331 Register(Register),
332 Amode(Amode),
333}
334
335impl From<u8> for RegisterOrAmode {
336 fn from(reg: u8) -> Self {
337 RegisterOrAmode::Register(reg.into())
338 }
339}
340
341impl From<Amode> for RegisterOrAmode {
342 fn from(amode: Amode) -> Self {
343 RegisterOrAmode::Amode(amode)
344 }
345}
346
347#[allow(dead_code, missing_docs)] pub enum EvexContext {
354 RoundingRegToRegFP {
355 rc: EvexRoundingControl,
356 },
357 NoRoundingFP {
358 sae: bool,
359 length: EvexVectorLength,
360 },
361 MemoryOp {
362 broadcast: bool,
363 length: EvexVectorLength,
364 },
365 Other {
366 length: EvexVectorLength,
367 },
368}
369
370impl Default for EvexContext {
371 fn default() -> Self {
372 Self::Other {
373 length: EvexVectorLength::default(),
374 }
375 }
376}
377
378impl EvexContext {
379 pub fn bits(&self) -> u8 {
381 match self {
382 Self::RoundingRegToRegFP { rc } => 0b001 | rc.bits() << 1,
383 Self::NoRoundingFP { sae, length } => (*sae as u8) | length.bits() << 1,
384 Self::MemoryOp { broadcast, length } => (*broadcast as u8) | length.bits() << 1,
385 Self::Other { length } => length.bits() << 1,
386 }
387 }
388}
389
390#[allow(dead_code, missing_docs)] pub enum EvexVectorLength {
393 V128,
394 V256,
395 V512,
396}
397
398impl EvexVectorLength {
399 fn bits(&self) -> u8 {
401 match self {
402 Self::V128 => 0b00,
403 Self::V256 => 0b01,
404 Self::V512 => 0b10,
405 }
407 }
408}
409
410impl Default for EvexVectorLength {
411 fn default() -> Self {
412 Self::V128
413 }
414}
415
416#[allow(dead_code, missing_docs)] pub enum EvexRoundingControl {
419 RNE,
420 RD,
421 RU,
422 RZ,
423}
424
425impl EvexRoundingControl {
426 fn bits(&self) -> u8 {
428 match self {
429 Self::RNE => 0b00,
430 Self::RD => 0b01,
431 Self::RU => 0b10,
432 Self::RZ => 0b11,
433 }
434 }
435}
436
437#[allow(dead_code, missing_docs)] pub enum EvexMasking {
441 None,
442 Merging { k: u8 },
443 Zeroing { k: u8 },
444}
445
446impl Default for EvexMasking {
447 fn default() -> Self {
448 EvexMasking::None
449 }
450}
451
452impl EvexMasking {
453 pub fn z_bit(&self) -> u8 {
455 match self {
456 Self::None | Self::Merging { .. } => 0,
457 Self::Zeroing { .. } => 1,
458 }
459 }
460
461 pub fn aaa_bits(&self) -> u8 {
463 match self {
464 Self::None => 0b000,
465 Self::Merging { k } | Self::Zeroing { k } => {
466 debug_assert!(*k <= 7);
467 *k
468 }
469 }
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476 use crate::ir::MemFlags;
477 use crate::isa::x64::args::Gpr;
478 use crate::isa::x64::inst::regs;
479 use std::vec::Vec;
480
481 #[test]
484 fn vpabsq() {
485 let mut tmp = MachBuffer::<Inst>::new();
486 let tests: &[(crate::Reg, RegisterOrAmode, Vec<u8>)] = &[
487 (
489 regs::xmm0(),
490 regs::xmm1().to_real_reg().unwrap().hw_enc().into(),
491 vec![0x62, 0xf2, 0xfd, 0x08, 0x1f, 0xc1],
492 ),
493 (
495 regs::xmm10(),
496 regs::xmm8().to_real_reg().unwrap().hw_enc().into(),
497 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0xd0],
498 ),
499 (
501 regs::xmm3(),
502 regs::xmm15().to_real_reg().unwrap().hw_enc().into(),
503 vec![0x62, 0xd2, 0xfd, 0x08, 0x1f, 0xdf],
504 ),
505 (
507 regs::xmm12(),
508 Amode::ImmReg {
509 simm32: 0,
510 base: regs::rsi(),
511 flags: MemFlags::trusted(),
512 }
513 .into(),
514 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x26],
515 ),
516 (
518 regs::xmm14(),
519 Amode::ImmReg {
520 simm32: 8,
521 base: regs::r15(),
522 flags: MemFlags::trusted(),
523 }
524 .into(),
525 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0xb7, 0x08, 0x00, 0x00, 0x00],
526 ),
527 (
529 regs::xmm14(),
530 Amode::ImmReg {
531 simm32: 16,
532 base: regs::r15(),
533 flags: MemFlags::trusted(),
534 }
535 .into(),
536 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x77, 0x01],
537 ),
538 (
540 regs::xmm3(),
541 Amode::ImmReg {
542 simm32: 17,
543 base: regs::rax(),
544 flags: MemFlags::trusted(),
545 }
546 .into(),
547 vec![0x62, 0xf2, 0xfd, 0x08, 0x1f, 0x98, 0x11, 0x00, 0x00, 0x00],
548 ),
549 (
551 regs::xmm9(),
552 Amode::ImmRegRegShift {
553 simm32: 0,
554 base: Gpr::unwrap_new(regs::rbx()),
555 index: Gpr::unwrap_new(regs::rsi()),
556 shift: 3,
557 flags: MemFlags::trusted(),
558 }
559 .into(),
560 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x0c, 0xf3],
561 ),
562 (
564 regs::xmm13(),
565 Amode::ImmRegRegShift {
566 simm32: 1,
567 base: Gpr::unwrap_new(regs::r11()),
568 index: Gpr::unwrap_new(regs::rdi()),
569 shift: 2,
570 flags: MemFlags::trusted(),
571 }
572 .into(),
573 vec![
574 0x62, 0x52, 0xfd, 0x08, 0x1f, 0xac, 0xbb, 0x01, 0x00, 0x00, 0x00,
575 ],
576 ),
577 (
579 regs::xmm5(),
580 Amode::ImmRegRegShift {
581 simm32: 128,
582 base: Gpr::unwrap_new(regs::rsp()),
583 index: Gpr::unwrap_new(regs::r10()),
584 shift: 1,
585 flags: MemFlags::trusted(),
586 }
587 .into(),
588 vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x6c, 0x54, 0x08],
589 ),
590 (
592 regs::xmm6(),
593 Amode::ImmRegRegShift {
594 simm32: 112,
595 base: Gpr::unwrap_new(regs::rbp()),
596 index: Gpr::unwrap_new(regs::r13()),
597 shift: 0,
598 flags: MemFlags::trusted(),
599 }
600 .into(),
601 vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x74, 0x2d, 0x07],
602 ),
603 (
605 regs::xmm7(),
606 Amode::ImmRegRegShift {
607 simm32: 0,
608 base: Gpr::unwrap_new(regs::rbp()),
609 index: Gpr::unwrap_new(regs::r13()),
610 shift: 0,
611 flags: MemFlags::trusted(),
612 }
613 .into(),
614 vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x7c, 0x2d, 0x00],
615 ),
616 (
618 regs::xmm8(),
619 Amode::ImmReg {
620 simm32: 2032,
621 base: regs::r12(),
622 flags: MemFlags::trusted(),
623 }
624 .into(),
625 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x44, 0x24, 0x7f],
626 ),
627 (
629 regs::xmm9(),
630 Amode::ImmReg {
631 simm32: 2048,
632 base: regs::r13(),
633 flags: MemFlags::trusted(),
634 }
635 .into(),
636 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x8d, 0x00, 0x08, 0x00, 0x00],
637 ),
638 (
640 regs::xmm10(),
641 Amode::ImmReg {
642 simm32: -16,
643 base: regs::r14(),
644 flags: MemFlags::trusted(),
645 }
646 .into(),
647 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x56, 0xff],
648 ),
649 (
651 regs::xmm11(),
652 Amode::ImmReg {
653 simm32: -5,
654 base: regs::r15(),
655 flags: MemFlags::trusted(),
656 }
657 .into(),
658 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x9f, 0xfb, 0xff, 0xff, 0xff],
659 ),
660 (
662 regs::xmm12(),
663 Amode::ImmReg {
664 simm32: -2048,
665 base: regs::rdx(),
666 flags: MemFlags::trusted(),
667 }
668 .into(),
669 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x62, 0x80],
670 ),
671 (
673 regs::xmm13(),
674 Amode::ImmReg {
675 simm32: -2064,
676 base: regs::rsi(),
677 flags: MemFlags::trusted(),
678 }
679 .into(),
680 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0xae, 0xf0, 0xf7, 0xff, 0xff],
681 ),
682 (
684 regs::xmm14(),
685 Amode::RipRelative {
686 target: tmp.get_label(),
687 }
688 .into(),
689 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x35, 0xf6, 0xff, 0xff, 0xff],
690 ),
691 ];
692
693 for (dst, src, encoding) in tests {
694 let mut sink = MachBuffer::new();
695 let label = sink.get_label();
696 sink.bind_label(label, &mut Default::default());
697 EvexInstruction::new()
698 .prefix(LegacyPrefixes::_66)
699 .map(OpcodeMap::_0F38)
700 .w(true)
701 .opcode(0x1F)
702 .reg(dst.to_real_reg().unwrap().hw_enc())
703 .rm(src.clone())
704 .length(EvexVectorLength::V128)
705 .tuple_type(Avx512TupleType::Full)
706 .encode(&mut sink);
707 let bytes0 = sink
708 .finish(&Default::default(), &mut Default::default())
709 .data;
710 assert_eq!(
711 bytes0.as_slice(),
712 encoding.as_slice(),
713 "dst={dst:?} src={src:?}"
714 );
715 }
716 }
717
718 #[test]
723 fn default_emission() {
724 let mut sink = MachBuffer::new();
725 EvexInstruction::new().encode(&mut sink);
726 let bytes0 = sink
727 .finish(&Default::default(), &mut Default::default())
728 .data;
729
730 let mut sink = MachBuffer::new();
731 EvexInstruction::new()
732 .length(EvexVectorLength::V128)
733 .prefix(LegacyPrefixes::None)
734 .map(OpcodeMap::None)
735 .w(false)
736 .opcode(0x00)
737 .reg(regs::rax().to_real_reg().unwrap().hw_enc())
738 .rm(regs::rax().to_real_reg().unwrap().hw_enc())
739 .mask(EvexMasking::None)
740 .encode(&mut sink);
741 let bytes1 = sink
742 .finish(&Default::default(), &mut Default::default())
743 .data;
744
745 assert_eq!(bytes0, bytes1);
746 }
747}