1use super::rex::{self, LegacyPrefixes, OpcodeMap};
18use crate::MachBuffer;
19use crate::isa::x64::args::{Amode, Avx512TupleType};
20use crate::isa::x64::inst::Inst;
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#[expect(
59 non_upper_case_globals,
60 reason = "This makes it easier to match the bit range names to the manual's names"
61)]
62impl EvexInstruction {
63 pub fn new() -> Self {
65 Self::default()
66 }
67
68 #[inline(always)]
73 pub fn length(mut self, length: EvexVectorLength) -> Self {
74 self.write(Self::LL, EvexContext::Other { length }.bits() as u32);
75 self
76 }
77
78 #[inline(always)]
81 pub fn prefix(mut self, prefix: LegacyPrefixes) -> Self {
82 self.write(Self::pp, prefix.bits() as u32);
83 self
84 }
85
86 #[inline(always)]
89 pub fn map(mut self, map: OpcodeMap) -> Self {
90 self.write(Self::mm, map.bits() as u32);
91 self
92 }
93
94 #[inline(always)]
98 pub fn w(mut self, w: bool) -> Self {
99 self.write(Self::W, w as u32);
100 self
101 }
102
103 #[inline(always)]
105 pub fn opcode(mut self, opcode: u8) -> Self {
106 self.opcode = opcode;
107 self
108 }
109
110 #[inline(always)]
113 pub fn tuple_type(mut self, tt: Avx512TupleType) -> Self {
114 self.tuple_type = Some(tt);
115 self
116 }
117
118 #[inline(always)]
122 pub fn reg(mut self, reg: impl Into<Register>) -> Self {
123 self.reg = reg.into();
124 let r = !(self.reg.0 >> 3) & 1;
125 let r_ = !(self.reg.0 >> 4) & 1;
126 self.write(Self::R, r as u32);
127 self.write(Self::R_, r_ as u32);
128 self
129 }
130
131 #[inline(always)]
134 #[cfg_attr(not(test), expect(dead_code, reason = "here for future use"))]
135 pub fn mask(mut self, mask: EvexMasking) -> Self {
136 self.write(Self::aaa, mask.aaa_bits() as u32);
137 self.write(Self::z, mask.z_bit() as u32);
138 self
139 }
140
141 #[inline(always)]
144 pub fn vvvvv(mut self, reg: impl Into<Register>) -> Self {
145 let reg = reg.into();
146 self.write(Self::vvvv, !(reg.0 as u32) & 0b1111);
147 self.write(Self::V_, !(reg.0 as u32 >> 4) & 0b1);
148 self
149 }
150
151 #[inline(always)]
156 pub fn rm(mut self, reg: impl Into<RegisterOrAmode>) -> Self {
157 self.rm = reg.into();
160 let x = match &self.rm {
161 RegisterOrAmode::Register(r) => r.0 >> 4,
162 RegisterOrAmode::Amode(Amode::ImmRegRegShift { index, .. }) => {
163 index.to_real_reg().unwrap().hw_enc() >> 3
164 }
165
166 RegisterOrAmode::Amode(Amode::ImmReg { .. }) => 0,
169 RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0,
170 };
171 self.write(Self::X, u32::from(!x & 1));
173
174 let b = match &self.rm {
175 RegisterOrAmode::Register(r) => r.0 >> 3,
176 RegisterOrAmode::Amode(Amode::ImmReg { base, .. }) => {
177 base.to_real_reg().unwrap().hw_enc() >> 3
178 }
179 RegisterOrAmode::Amode(Amode::ImmRegRegShift { base, .. }) => {
180 base.to_real_reg().unwrap().hw_enc() >> 3
181 }
182 RegisterOrAmode::Amode(Amode::RipRelative { .. }) => 0,
184 };
185 self.write(Self::B, u32::from(!b & 1));
187 self
188 }
189
190 #[inline(always)]
192 pub fn imm(mut self, imm: u8) -> Self {
193 self.imm = Some(imm);
194 self
195 }
196
197 pub fn encode(&self, sink: &mut MachBuffer<Inst>) {
205 if let RegisterOrAmode::Amode(amode) = &self.rm {
206 if let Some(trap_code) = amode.get_flags().trap_code() {
207 sink.add_trap(trap_code);
208 }
209 }
210 sink.put4(self.bits);
211 sink.put1(self.opcode);
212
213 match &self.rm {
214 RegisterOrAmode::Register(reg) => {
215 let rm: u8 = (*reg).into();
216 sink.put1(rex::encode_modrm(3, self.reg.0 & 7, rm & 7));
217 }
218 RegisterOrAmode::Amode(amode) => {
219 let scaling = self.scaling_for_8bit_disp();
220
221 let bytes_at_end = if self.imm.is_some() { 1 } else { 0 };
222 rex::emit_modrm_sib_disp(sink, self.reg.0 & 7, amode, bytes_at_end, Some(scaling));
223 }
224 }
225 if let Some(imm) = self.imm {
226 sink.put1(imm);
227 }
228 }
229
230 const mm: RangeInclusive<u8> = 8..=9;
245 const R_: RangeInclusive<u8> = 12..=12;
246 const B: RangeInclusive<u8> = 13..=13;
247 const X: RangeInclusive<u8> = 14..=14;
248 const R: RangeInclusive<u8> = 15..=15;
249
250 const pp: RangeInclusive<u8> = 16..=17;
252 const vvvv: RangeInclusive<u8> = 19..=22;
253 const W: RangeInclusive<u8> = 23..=23;
254
255 const aaa: RangeInclusive<u8> = 24..=26;
257 const V_: RangeInclusive<u8> = 27..=27;
258 const b: RangeInclusive<u8> = 28..=28;
259 const LL: RangeInclusive<u8> = 29..=30;
260 const z: RangeInclusive<u8> = 31..=31;
261
262 #[inline]
264 fn write(&mut self, range: RangeInclusive<u8>, value: u32) {
265 assert!(ExactSizeIterator::len(&range) > 0);
266 let size = range.end() - range.start() + 1; let mask: u32 = (1 << size) - 1; debug_assert!(
269 value <= mask,
270 "The written value should have fewer than {size} bits."
271 );
272 let mask_complement = !(mask << *range.start()); self.bits &= mask_complement; let value = value << *range.start(); self.bits |= value; }
277
278 #[inline]
281 fn read(&self, range: RangeInclusive<u8>) -> u32 {
282 (self.bits >> range.start()) & ((1 << range.len()) - 1)
283 }
284
285 fn scaling_for_8bit_disp(&self) -> i8 {
286 use Avx512TupleType::*;
287
288 let vector_size_scaling = || match self.read(Self::LL) {
289 0b00 => 16,
290 0b01 => 32,
291 0b10 => 64,
292 _ => unreachable!(),
293 };
294
295 match self.tuple_type {
296 Some(Full) => {
297 if self.read(Self::b) == 1 {
298 if self.read(Self::W) == 0 { 4 } else { 8 }
299 } else {
300 vector_size_scaling()
301 }
302 }
303 Some(FullMem) => vector_size_scaling(),
304 Some(Mem128) => 16,
305 None => panic!("tuple type was not set"),
306 }
307 }
308}
309
310#[derive(Debug, Copy, Clone, Default)]
313pub struct Register(u8);
314impl From<u8> for Register {
315 fn from(reg: u8) -> Self {
316 debug_assert!(reg < 16);
317 Self(reg)
318 }
319}
320impl From<Register> for u8 {
321 fn from(reg: Register) -> u8 {
322 reg.0
323 }
324}
325
326#[derive(Debug, Clone)]
327pub enum RegisterOrAmode {
328 Register(Register),
329 Amode(Amode),
330}
331
332impl From<u8> for RegisterOrAmode {
333 fn from(reg: u8) -> Self {
334 RegisterOrAmode::Register(reg.into())
335 }
336}
337
338impl From<Amode> for RegisterOrAmode {
339 fn from(amode: Amode) -> Self {
340 RegisterOrAmode::Amode(amode)
341 }
342}
343
344pub enum EvexContext {
350 #[expect(dead_code, reason = "here for future use")]
351 RoundingRegToRegFP {
352 rc: EvexRoundingControl,
353 },
354 #[expect(dead_code, reason = "here for future use")]
355 NoRoundingFP {
356 sae: bool,
357 length: EvexVectorLength,
358 },
359 #[expect(dead_code, reason = "here for future use")]
360 MemoryOp {
361 broadcast: bool,
362 length: EvexVectorLength,
363 },
364 Other {
365 length: EvexVectorLength,
366 },
367}
368
369impl Default for EvexContext {
370 fn default() -> Self {
371 Self::Other {
372 length: EvexVectorLength::default(),
373 }
374 }
375}
376
377impl EvexContext {
378 pub fn bits(&self) -> u8 {
380 match self {
381 Self::RoundingRegToRegFP { rc } => 0b001 | rc.bits() << 1,
382 Self::NoRoundingFP { sae, length } => (*sae as u8) | length.bits() << 1,
383 Self::MemoryOp { broadcast, length } => (*broadcast as u8) | length.bits() << 1,
384 Self::Other { length } => length.bits() << 1,
385 }
386 }
387}
388
389pub enum EvexVectorLength {
391 V128,
392 #[expect(dead_code, reason = "here for future cranelift use")]
393 V256,
394 #[expect(dead_code, reason = "here for future cranelift use")]
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#[expect(dead_code, reason = "here for future use")]
418pub 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
437pub enum EvexMasking {
440 None,
441 #[expect(dead_code, reason = "here for future use")]
442 Merging {
443 k: u8,
444 },
445 #[expect(dead_code, reason = "here for future use")]
446 Zeroing {
447 k: u8,
448 },
449}
450
451impl Default for EvexMasking {
452 fn default() -> Self {
453 EvexMasking::None
454 }
455}
456
457impl EvexMasking {
458 pub fn z_bit(&self) -> u8 {
460 match self {
461 Self::None | Self::Merging { .. } => 0,
462 Self::Zeroing { .. } => 1,
463 }
464 }
465
466 pub fn aaa_bits(&self) -> u8 {
468 match self {
469 Self::None => 0b000,
470 Self::Merging { k } | Self::Zeroing { k } => {
471 debug_assert!(*k <= 7);
472 *k
473 }
474 }
475 }
476}
477
478#[cfg(test)]
479mod tests {
480 use super::*;
481 use crate::ir::MemFlags;
482 use crate::isa::x64::args::Gpr;
483 use crate::isa::x64::inst::regs;
484 use std::vec::Vec;
485
486 #[test]
489 fn vpabsq() {
490 let mut tmp = MachBuffer::<Inst>::new();
491 let tests: &[(crate::Reg, RegisterOrAmode, Vec<u8>)] = &[
492 (
494 regs::xmm0(),
495 regs::xmm1().to_real_reg().unwrap().hw_enc().into(),
496 vec![0x62, 0xf2, 0xfd, 0x08, 0x1f, 0xc1],
497 ),
498 (
500 regs::xmm10(),
501 regs::xmm8().to_real_reg().unwrap().hw_enc().into(),
502 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0xd0],
503 ),
504 (
506 regs::xmm3(),
507 regs::xmm15().to_real_reg().unwrap().hw_enc().into(),
508 vec![0x62, 0xd2, 0xfd, 0x08, 0x1f, 0xdf],
509 ),
510 (
512 regs::xmm12(),
513 Amode::ImmReg {
514 simm32: 0,
515 base: regs::rsi(),
516 flags: MemFlags::trusted(),
517 }
518 .into(),
519 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x26],
520 ),
521 (
523 regs::xmm14(),
524 Amode::ImmReg {
525 simm32: 8,
526 base: regs::r15(),
527 flags: MemFlags::trusted(),
528 }
529 .into(),
530 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0xb7, 0x08, 0x00, 0x00, 0x00],
531 ),
532 (
534 regs::xmm14(),
535 Amode::ImmReg {
536 simm32: 16,
537 base: regs::r15(),
538 flags: MemFlags::trusted(),
539 }
540 .into(),
541 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x77, 0x01],
542 ),
543 (
545 regs::xmm3(),
546 Amode::ImmReg {
547 simm32: 17,
548 base: regs::rax(),
549 flags: MemFlags::trusted(),
550 }
551 .into(),
552 vec![0x62, 0xf2, 0xfd, 0x08, 0x1f, 0x98, 0x11, 0x00, 0x00, 0x00],
553 ),
554 (
556 regs::xmm9(),
557 Amode::ImmRegRegShift {
558 simm32: 0,
559 base: Gpr::unwrap_new(regs::rbx()),
560 index: Gpr::unwrap_new(regs::rsi()),
561 shift: 3,
562 flags: MemFlags::trusted(),
563 }
564 .into(),
565 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x0c, 0xf3],
566 ),
567 (
569 regs::xmm13(),
570 Amode::ImmRegRegShift {
571 simm32: 1,
572 base: Gpr::unwrap_new(regs::r11()),
573 index: Gpr::unwrap_new(regs::rdi()),
574 shift: 2,
575 flags: MemFlags::trusted(),
576 }
577 .into(),
578 vec![
579 0x62, 0x52, 0xfd, 0x08, 0x1f, 0xac, 0xbb, 0x01, 0x00, 0x00, 0x00,
580 ],
581 ),
582 (
584 regs::xmm5(),
585 Amode::ImmRegRegShift {
586 simm32: 128,
587 base: Gpr::unwrap_new(regs::rsp()),
588 index: Gpr::unwrap_new(regs::r10()),
589 shift: 1,
590 flags: MemFlags::trusted(),
591 }
592 .into(),
593 vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x6c, 0x54, 0x08],
594 ),
595 (
597 regs::xmm6(),
598 Amode::ImmRegRegShift {
599 simm32: 112,
600 base: Gpr::unwrap_new(regs::rbp()),
601 index: Gpr::unwrap_new(regs::r13()),
602 shift: 0,
603 flags: MemFlags::trusted(),
604 }
605 .into(),
606 vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x74, 0x2d, 0x07],
607 ),
608 (
610 regs::xmm7(),
611 Amode::ImmRegRegShift {
612 simm32: 0,
613 base: Gpr::unwrap_new(regs::rbp()),
614 index: Gpr::unwrap_new(regs::r13()),
615 shift: 0,
616 flags: MemFlags::trusted(),
617 }
618 .into(),
619 vec![0x62, 0xb2, 0xfd, 0x08, 0x1f, 0x7c, 0x2d, 0x00],
620 ),
621 (
623 regs::xmm8(),
624 Amode::ImmReg {
625 simm32: 2032,
626 base: regs::r12(),
627 flags: MemFlags::trusted(),
628 }
629 .into(),
630 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x44, 0x24, 0x7f],
631 ),
632 (
634 regs::xmm9(),
635 Amode::ImmReg {
636 simm32: 2048,
637 base: regs::r13(),
638 flags: MemFlags::trusted(),
639 }
640 .into(),
641 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x8d, 0x00, 0x08, 0x00, 0x00],
642 ),
643 (
645 regs::xmm10(),
646 Amode::ImmReg {
647 simm32: -16,
648 base: regs::r14(),
649 flags: MemFlags::trusted(),
650 }
651 .into(),
652 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x56, 0xff],
653 ),
654 (
656 regs::xmm11(),
657 Amode::ImmReg {
658 simm32: -5,
659 base: regs::r15(),
660 flags: MemFlags::trusted(),
661 }
662 .into(),
663 vec![0x62, 0x52, 0xfd, 0x08, 0x1f, 0x9f, 0xfb, 0xff, 0xff, 0xff],
664 ),
665 (
667 regs::xmm12(),
668 Amode::ImmReg {
669 simm32: -2048,
670 base: regs::rdx(),
671 flags: MemFlags::trusted(),
672 }
673 .into(),
674 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x62, 0x80],
675 ),
676 (
678 regs::xmm13(),
679 Amode::ImmReg {
680 simm32: -2064,
681 base: regs::rsi(),
682 flags: MemFlags::trusted(),
683 }
684 .into(),
685 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0xae, 0xf0, 0xf7, 0xff, 0xff],
686 ),
687 (
689 regs::xmm14(),
690 Amode::RipRelative {
691 target: tmp.get_label(),
692 }
693 .into(),
694 vec![0x62, 0x72, 0xfd, 0x08, 0x1f, 0x35, 0xf6, 0xff, 0xff, 0xff],
695 ),
696 ];
697
698 for (dst, src, encoding) in tests {
699 let mut sink = MachBuffer::new();
700 let label = sink.get_label();
701 sink.bind_label(label, &mut Default::default());
702 EvexInstruction::new()
703 .prefix(LegacyPrefixes::_66)
704 .map(OpcodeMap::_0F38)
705 .w(true)
706 .opcode(0x1F)
707 .reg(dst.to_real_reg().unwrap().hw_enc())
708 .rm(src.clone())
709 .length(EvexVectorLength::V128)
710 .tuple_type(Avx512TupleType::Full)
711 .encode(&mut sink);
712 let bytes0 = sink
713 .finish(&Default::default(), &mut Default::default())
714 .data;
715 assert_eq!(
716 bytes0.as_slice(),
717 encoding.as_slice(),
718 "dst={dst:?} src={src:?}"
719 );
720 }
721 }
722
723 #[test]
728 fn default_emission() {
729 let mut sink = MachBuffer::new();
730 EvexInstruction::new().encode(&mut sink);
731 let bytes0 = sink
732 .finish(&Default::default(), &mut Default::default())
733 .data;
734
735 let mut sink = MachBuffer::new();
736 EvexInstruction::new()
737 .length(EvexVectorLength::V128)
738 .prefix(LegacyPrefixes::None)
739 .map(OpcodeMap::None)
740 .w(false)
741 .opcode(0x00)
742 .reg(regs::rax().to_real_reg().unwrap().hw_enc())
743 .rm(regs::rax().to_real_reg().unwrap().hw_enc())
744 .mask(EvexMasking::None)
745 .encode(&mut sink);
746 let bytes1 = sink
747 .finish(&Default::default(), &mut Default::default())
748 .data;
749
750 assert_eq!(bytes0, bytes1);
751 }
752}