xref: /wasmtime-44.0.1/winch/codegen/src/isa/x64/asm.rs (revision 7fb4e49b)
1 //! Assembler library implementation for x64.
2 
3 use crate::{
4     constant_pool::ConstantPool,
5     isa::{CallingConvention, reg::Reg},
6     masm::{
7         DivKind, Extend, ExtendKind, ExtendType, IntCmpKind, MulWideKind, OperandSize, RemKind,
8         RoundingMode, ShiftKind, Signed, V128ExtendKind, V128LoadExtendKind, Zero,
9     },
10     reg::writable,
11 };
12 use cranelift_codegen::{
13     CallInfo, Final, MachBuffer, MachBufferFinalized, MachInst, MachInstEmit, MachInstEmitState,
14     MachLabel, PatchRegion, Writable,
15     ir::{ExternalName, MemFlags, SourceLoc, TrapCode, Type, UserExternalNameRef, types},
16     isa::{
17         unwind::UnwindInst,
18         x64::{
19             AtomicRmwSeqOp, EmitInfo, EmitState, Inst,
20             args::{
21                 self, Amode, Avx512Opcode, AvxOpcode, CC, CmpOpcode, ExtMode, FromWritableReg, Gpr,
22                 GprMem, GprMemImm, RegMem, RegMemImm, SyntheticAmode, WritableGpr, WritableXmm,
23                 Xmm, XmmMem, XmmMemImm,
24             },
25             encoding::rex::{RexFlags, encode_modrm},
26             external::{PairedGpr, PairedXmm},
27             settings as x64_settings,
28         },
29     },
30     settings,
31 };
32 
33 use crate::reg::WritableReg;
34 use cranelift_assembler_x64 as asm;
35 use wasmtime_environ::Unsigned;
36 
37 use super::address::Address;
38 use smallvec::SmallVec;
39 
40 // Conversions between winch-codegen x64 types and cranelift-codegen x64 types.
41 
42 impl From<Reg> for RegMemImm {
43     fn from(reg: Reg) -> Self {
44         RegMemImm::reg(reg.into())
45     }
46 }
47 
48 impl From<Reg> for RegMem {
49     fn from(value: Reg) -> Self {
50         RegMem::Reg { reg: value.into() }
51     }
52 }
53 
54 impl From<Reg> for WritableGpr {
55     fn from(reg: Reg) -> Self {
56         let writable = Writable::from_reg(reg.into());
57         WritableGpr::from_writable_reg(writable).expect("valid writable gpr")
58     }
59 }
60 
61 impl From<Reg> for WritableXmm {
62     fn from(reg: Reg) -> Self {
63         let writable = Writable::from_reg(reg.into());
64         WritableXmm::from_writable_reg(writable).expect("valid writable xmm")
65     }
66 }
67 
68 /// Convert a writable GPR register to the read-write pair expected by
69 /// `cranelift-codegen`.
70 fn pair_gpr(reg: WritableReg) -> PairedGpr {
71     assert!(reg.to_reg().is_int());
72     let read = Gpr::unwrap_new(reg.to_reg().into());
73     let write = WritableGpr::from_reg(reg.to_reg().into());
74     PairedGpr { read, write }
75 }
76 
77 impl From<Reg> for asm::Gpr<Gpr> {
78     fn from(reg: Reg) -> Self {
79         asm::Gpr::new(reg.into())
80     }
81 }
82 
83 impl From<Reg> for asm::GprMem<Gpr, Gpr> {
84     fn from(reg: Reg) -> Self {
85         asm::GprMem::Gpr(reg.into())
86     }
87 }
88 
89 /// Convert a writable XMM register to the read-write pair expected by
90 /// `cranelift-codegen`.
91 fn pair_xmm(reg: WritableReg) -> PairedXmm {
92     assert!(reg.to_reg().is_float());
93     let read = Xmm::unwrap_new(reg.to_reg().into());
94     let write = WritableXmm::from_reg(reg.to_reg().into());
95     PairedXmm { read, write }
96 }
97 
98 impl From<Reg> for asm::Xmm<Xmm> {
99     fn from(reg: Reg) -> Self {
100         asm::Xmm::new(reg.into())
101     }
102 }
103 
104 impl From<Reg> for asm::XmmMem<Xmm, Gpr> {
105     fn from(reg: Reg) -> Self {
106         asm::XmmMem::Xmm(reg.into())
107     }
108 }
109 
110 impl From<Reg> for Gpr {
111     fn from(reg: Reg) -> Self {
112         Gpr::unwrap_new(reg.into())
113     }
114 }
115 
116 impl From<Reg> for GprMem {
117     fn from(value: Reg) -> Self {
118         GprMem::unwrap_new(value.into())
119     }
120 }
121 
122 impl From<Reg> for GprMemImm {
123     fn from(reg: Reg) -> Self {
124         GprMemImm::unwrap_new(reg.into())
125     }
126 }
127 
128 impl From<Reg> for Xmm {
129     fn from(reg: Reg) -> Self {
130         Xmm::unwrap_new(reg.into())
131     }
132 }
133 
134 impl From<Reg> for XmmMem {
135     fn from(value: Reg) -> Self {
136         XmmMem::unwrap_new(value.into())
137     }
138 }
139 
140 impl From<Reg> for XmmMemImm {
141     fn from(value: Reg) -> Self {
142         XmmMemImm::unwrap_new(value.into())
143     }
144 }
145 
146 impl From<OperandSize> for args::OperandSize {
147     fn from(size: OperandSize) -> Self {
148         match size {
149             OperandSize::S8 => Self::Size8,
150             OperandSize::S16 => Self::Size16,
151             OperandSize::S32 => Self::Size32,
152             OperandSize::S64 => Self::Size64,
153             s => panic!("Invalid operand size {s:?}"),
154         }
155     }
156 }
157 
158 impl From<IntCmpKind> for CC {
159     fn from(value: IntCmpKind) -> Self {
160         match value {
161             IntCmpKind::Eq => CC::Z,
162             IntCmpKind::Ne => CC::NZ,
163             IntCmpKind::LtS => CC::L,
164             IntCmpKind::LtU => CC::B,
165             IntCmpKind::GtS => CC::NLE,
166             IntCmpKind::GtU => CC::NBE,
167             IntCmpKind::LeS => CC::LE,
168             IntCmpKind::LeU => CC::BE,
169             IntCmpKind::GeS => CC::NL,
170             IntCmpKind::GeU => CC::NB,
171         }
172     }
173 }
174 
175 impl<T: ExtendType> From<Extend<T>> for ExtMode {
176     fn from(value: Extend<T>) -> Self {
177         match value {
178             Extend::I32Extend8 => ExtMode::BL,
179             Extend::I32Extend16 => ExtMode::WL,
180             Extend::I64Extend8 => ExtMode::BQ,
181             Extend::I64Extend16 => ExtMode::WQ,
182             Extend::I64Extend32 => ExtMode::LQ,
183             Extend::__Kind(_) => unreachable!(),
184         }
185     }
186 }
187 
188 impl From<ExtendKind> for ExtMode {
189     fn from(value: ExtendKind) -> Self {
190         match value {
191             ExtendKind::Signed(s) => s.into(),
192             ExtendKind::Unsigned(u) => u.into(),
193         }
194     }
195 }
196 
197 /// Kinds of extends supported by `vpmov`.
198 pub(super) enum VpmovKind {
199     /// Sign extends 8 lanes of 8-bit integers to 8 lanes of 16-bit integers.
200     E8x8S,
201     /// Zero extends 8 lanes of 8-bit integers to 8 lanes of 16-bit integers.
202     E8x8U,
203     /// Sign extends 4 lanes of 16-bit integers to 4 lanes of 32-bit integers.
204     E16x4S,
205     /// Zero extends 4 lanes of 16-bit integers to 4 lanes of 32-bit integers.
206     E16x4U,
207     /// Sign extends 2 lanes of 32-bit integers to 2 lanes of 64-bit integers.
208     E32x2S,
209     /// Zero extends 2 lanes of 32-bit integers to 2 lanes of 64-bit integers.
210     E32x2U,
211 }
212 
213 impl From<VpmovKind> for AvxOpcode {
214     fn from(value: VpmovKind) -> Self {
215         match value {
216             VpmovKind::E8x8S => AvxOpcode::Vpmovsxbw,
217             VpmovKind::E8x8U => AvxOpcode::Vpmovzxbw,
218             VpmovKind::E16x4S => AvxOpcode::Vpmovsxwd,
219             VpmovKind::E16x4U => AvxOpcode::Vpmovzxwd,
220             VpmovKind::E32x2S => AvxOpcode::Vpmovsxdq,
221             VpmovKind::E32x2U => AvxOpcode::Vpmovzxdq,
222         }
223     }
224 }
225 
226 impl From<V128LoadExtendKind> for VpmovKind {
227     fn from(value: V128LoadExtendKind) -> Self {
228         match value {
229             V128LoadExtendKind::E8x8S => Self::E8x8S,
230             V128LoadExtendKind::E8x8U => Self::E8x8U,
231             V128LoadExtendKind::E16x4S => Self::E16x4S,
232             V128LoadExtendKind::E16x4U => Self::E16x4U,
233             V128LoadExtendKind::E32x2S => Self::E32x2S,
234             V128LoadExtendKind::E32x2U => Self::E32x2U,
235         }
236     }
237 }
238 
239 impl From<V128ExtendKind> for VpmovKind {
240     fn from(value: V128ExtendKind) -> Self {
241         match value {
242             V128ExtendKind::LowI8x16S | V128ExtendKind::HighI8x16S => Self::E8x8S,
243             V128ExtendKind::LowI8x16U => Self::E8x8U,
244             V128ExtendKind::LowI16x8S | V128ExtendKind::HighI16x8S => Self::E16x4S,
245             V128ExtendKind::LowI16x8U => Self::E16x4U,
246             V128ExtendKind::LowI32x4S | V128ExtendKind::HighI32x4S => Self::E32x2S,
247             V128ExtendKind::LowI32x4U => Self::E32x2U,
248             _ => unimplemented!(),
249         }
250     }
251 }
252 
253 /// Kinds of comparisons supported by `vcmp`.
254 pub(super) enum VcmpKind {
255     /// Equal comparison.
256     Eq,
257     /// Not equal comparison.
258     Ne,
259     /// Less than comparison.
260     Lt,
261     /// Less than or equal comparison.
262     Le,
263     /// Unordered comparison. Sets result to all 1s if either source operand is
264     /// NaN.
265     Unord,
266 }
267 
268 /// Kinds of conversions supported by `vcvt`.
269 pub(super) enum VcvtKind {
270     /// Converts 32-bit integers to 32-bit floats.
271     I32ToF32,
272     /// Converts doubleword integers to double precision floats.
273     I32ToF64,
274     /// Converts double precision floats to single precision floats.
275     F64ToF32,
276     // Converts double precision floats to 32-bit integers.
277     F64ToI32,
278     /// Converts single precision floats to double precision floats.
279     F32ToF64,
280     /// Converts single precision floats to 32-bit integers.
281     F32ToI32,
282 }
283 
284 /// Modes supported by `vround`.
285 pub(crate) enum VroundMode {
286     /// Rounds toward nearest (ties to even).
287     TowardNearest,
288     /// Rounds toward negative infinity.
289     TowardNegativeInfinity,
290     /// Rounds toward positive infinity.
291     TowardPositiveInfinity,
292     /// Rounds toward zero.
293     TowardZero,
294 }
295 
296 /// Low level assembler implementation for x64.
297 pub(crate) struct Assembler {
298     /// The machine instruction buffer.
299     buffer: MachBuffer<Inst>,
300     /// Constant emission information.
301     emit_info: EmitInfo,
302     /// Emission state.
303     emit_state: EmitState,
304     /// x64 flags.
305     isa_flags: x64_settings::Flags,
306     /// Constant pool.
307     pool: ConstantPool,
308 }
309 
310 impl Assembler {
311     /// Create a new x64 assembler.
312     pub fn new(shared_flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self {
313         Self {
314             buffer: MachBuffer::<Inst>::new(),
315             emit_state: Default::default(),
316             emit_info: EmitInfo::new(shared_flags, isa_flags.clone()),
317             pool: ConstantPool::new(),
318             isa_flags,
319         }
320     }
321 
322     /// Get a mutable reference to underlying
323     /// machine buffer.
324     pub fn buffer_mut(&mut self) -> &mut MachBuffer<Inst> {
325         &mut self.buffer
326     }
327 
328     /// Get a reference to the underlying machine buffer.
329     pub fn buffer(&self) -> &MachBuffer<Inst> {
330         &self.buffer
331     }
332 
333     /// Adds a constant to the constant pool and returns its address.
334     pub fn add_constant(&mut self, constant: &[u8]) -> Address {
335         let handle = self.pool.register(constant, &mut self.buffer);
336         Address::constant(handle)
337     }
338 
339     /// Load a floating point constant, using the constant pool.
340     pub fn load_fp_const(&mut self, dst: WritableReg, constant: &[u8], size: OperandSize) {
341         let addr = self.add_constant(constant);
342         self.xmm_mov_mr(&addr, dst, size, MemFlags::trusted());
343     }
344 
345     /// Return the emitted code.
346     pub fn finalize(mut self, loc: Option<SourceLoc>) -> MachBufferFinalized<Final> {
347         let stencil = self
348             .buffer
349             .finish(&self.pool.constants(), self.emit_state.ctrl_plane_mut());
350         stencil.apply_base_srcloc(loc.unwrap_or_default())
351     }
352 
353     fn emit(&mut self, inst: Inst) {
354         inst.emit(&mut self.buffer, &self.emit_info, &mut self.emit_state);
355     }
356 
357     fn to_synthetic_amode(addr: &Address, memflags: MemFlags) -> SyntheticAmode {
358         match *addr {
359             Address::Offset { base, offset } => {
360                 let amode = Amode::imm_reg(offset as i32, base.into()).with_flags(memflags);
361                 SyntheticAmode::real(amode)
362             }
363             Address::Const(c) => SyntheticAmode::ConstantOffset(c),
364             Address::ImmRegRegShift {
365                 simm32,
366                 base,
367                 index,
368                 shift,
369             } => SyntheticAmode::Real(Amode::ImmRegRegShift {
370                 simm32,
371                 base: base.into(),
372                 index: index.into(),
373                 shift,
374                 flags: memflags,
375             }),
376         }
377     }
378 
379     /// Emit an unwind instruction.
380     pub fn unwind_inst(&mut self, inst: UnwindInst) {
381         self.emit(Inst::Unwind { inst })
382     }
383 
384     /// Push register.
385     pub fn push_r(&mut self, reg: Reg) {
386         let inst = asm::inst::pushq_o::new(reg).into();
387         self.emit(Inst::External { inst });
388     }
389 
390     /// Pop to register.
391     pub fn pop_r(&mut self, dst: WritableReg) {
392         let writable: WritableGpr = dst.map(Into::into);
393         let inst = asm::inst::popq_o::new(writable).into();
394         self.emit(Inst::External { inst });
395     }
396 
397     /// Return instruction.
398     pub fn ret(&mut self) {
399         self.emit(Inst::Ret {
400             stack_bytes_to_pop: 0,
401         });
402     }
403 
404     /// Register-to-register move.
405     pub fn mov_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
406         let dst: WritableGpr = dst.map(|r| r.into());
407         let inst = match size {
408             OperandSize::S8 => asm::inst::movb_mr::new(dst, src).into(),
409             OperandSize::S16 => asm::inst::movw_mr::new(dst, src).into(),
410             OperandSize::S32 => asm::inst::movl_mr::new(dst, src).into(),
411             OperandSize::S64 => asm::inst::movq_mr::new(dst, src).into(),
412             _ => unreachable!(),
413         };
414         self.emit(Inst::External { inst });
415     }
416 
417     /// Register-to-memory move.
418     pub fn mov_rm(&mut self, src: Reg, addr: &Address, size: OperandSize, flags: MemFlags) {
419         assert!(addr.is_offset());
420         let dst = Self::to_synthetic_amode(addr, flags);
421         let inst = match size {
422             OperandSize::S8 => asm::inst::movb_mr::new(dst, src).into(),
423             OperandSize::S16 => asm::inst::movw_mr::new(dst, src).into(),
424             OperandSize::S32 => asm::inst::movl_mr::new(dst, src).into(),
425             OperandSize::S64 => asm::inst::movq_mr::new(dst, src).into(),
426             _ => unreachable!(),
427         };
428         self.emit(Inst::External { inst });
429     }
430 
431     /// Immediate-to-memory move.
432     pub fn mov_im(&mut self, src: i32, addr: &Address, size: OperandSize, flags: MemFlags) {
433         assert!(addr.is_offset());
434         let dst = Self::to_synthetic_amode(addr, flags);
435         let inst = match size {
436             OperandSize::S8 => {
437                 let src = i8::try_from(src).unwrap();
438                 asm::inst::movb_mi::new(dst, src.unsigned()).into()
439             }
440             OperandSize::S16 => {
441                 let src = i16::try_from(src).unwrap();
442                 asm::inst::movw_mi::new(dst, src.unsigned()).into()
443             }
444             OperandSize::S32 => asm::inst::movl_mi::new(dst, src.unsigned()).into(),
445             OperandSize::S64 => asm::inst::movq_mi_sxl::new(dst, src).into(),
446             _ => unreachable!(),
447         };
448         self.emit(Inst::External { inst });
449     }
450 
451     /// Immediate-to-register move.
452     pub fn mov_ir(&mut self, imm: u64, dst: WritableReg, size: OperandSize) {
453         self.emit(Inst::imm(size.into(), imm, dst.map(Into::into)));
454     }
455 
456     /// Zero-extend memory-to-register load.
457     pub fn movzx_mr(
458         &mut self,
459         addr: &Address,
460         dst: WritableReg,
461         ext: Option<Extend<Zero>>,
462         memflags: MemFlags,
463     ) {
464         let src = Self::to_synthetic_amode(addr, memflags);
465 
466         if let Some(ext) = ext {
467             let dst = WritableGpr::from_reg(dst.to_reg().into());
468             let inst = match ext.into() {
469                 ExtMode::BL => asm::inst::movzbl_rm::new(dst, src).into(),
470                 ExtMode::BQ => asm::inst::movzbq_rm::new(dst, src).into(),
471                 ExtMode::WL => asm::inst::movzwl_rm::new(dst, src).into(),
472                 ExtMode::WQ => asm::inst::movzwq_rm::new(dst, src).into(),
473                 ExtMode::LQ => {
474                     // This instruction selection may seem strange but is
475                     // correct in 64-bit mode: section 3.4.1.1 of the Intel
476                     // manual says that "32-bit operands generate a 32-bit
477                     // result, zero-extended to a 64-bit result in the
478                     // destination general-purpose register." This is applicable
479                     // beyond `mov` but we use this fact to zero-extend `src`
480                     // into `dst`.
481                     asm::inst::movl_rm::new(dst, src).into()
482                 }
483             };
484             self.emit(Inst::External { inst });
485         } else {
486             let dst = WritableGpr::from_reg(dst.to_reg().into());
487             let inst = asm::inst::movq_rm::new(dst, src).into();
488             self.emit(Inst::External { inst });
489         }
490     }
491 
492     // Sign-extend memory-to-register load.
493     pub fn movsx_mr(
494         &mut self,
495         addr: &Address,
496         dst: WritableReg,
497         ext: Extend<Signed>,
498         memflags: MemFlags,
499     ) {
500         let src = Self::to_synthetic_amode(addr, memflags);
501         let dst = WritableGpr::from_reg(dst.to_reg().into());
502         let inst = match ext.into() {
503             ExtMode::BL => asm::inst::movsbl_rm::new(dst, src).into(),
504             ExtMode::BQ => asm::inst::movsbq_rm::new(dst, src).into(),
505             ExtMode::WL => asm::inst::movswl_rm::new(dst, src).into(),
506             ExtMode::WQ => asm::inst::movswq_rm::new(dst, src).into(),
507             ExtMode::LQ => asm::inst::movslq_rm::new(dst, src).into(),
508         };
509         self.emit(Inst::External { inst });
510     }
511 
512     /// Register-to-register move with zero extension.
513     pub fn movzx_rr(&mut self, src: Reg, dst: WritableReg, kind: Extend<Zero>) {
514         let dst = WritableGpr::from_reg(dst.to_reg().into());
515         let inst = match kind.into() {
516             ExtMode::BL => asm::inst::movzbl_rm::new(dst, src).into(),
517             ExtMode::BQ => asm::inst::movzbq_rm::new(dst, src).into(),
518             ExtMode::WL => asm::inst::movzwl_rm::new(dst, src).into(),
519             ExtMode::WQ => asm::inst::movzwq_rm::new(dst, src).into(),
520             ExtMode::LQ => {
521                 // This instruction selection may seem strange but is correct in
522                 // 64-bit mode: section 3.4.1.1 of the Intel manual says that
523                 // "32-bit operands generate a 32-bit result, zero-extended to a
524                 // 64-bit result in the destination general-purpose register."
525                 // This is applicable beyond `mov` but we use this fact to
526                 // zero-extend `src` into `dst`.
527                 asm::inst::movl_rm::new(dst, src).into()
528             }
529         };
530         self.emit(Inst::External { inst });
531     }
532 
533     /// Register-to-register move with sign extension.
534     pub fn movsx_rr(&mut self, src: Reg, dst: WritableReg, kind: Extend<Signed>) {
535         let dst = WritableGpr::from_reg(dst.to_reg().into());
536         let inst = match kind.into() {
537             ExtMode::BL => asm::inst::movsbl_rm::new(dst, src).into(),
538             ExtMode::BQ => asm::inst::movsbq_rm::new(dst, src).into(),
539             ExtMode::WL => asm::inst::movswl_rm::new(dst, src).into(),
540             ExtMode::WQ => asm::inst::movswq_rm::new(dst, src).into(),
541             ExtMode::LQ => asm::inst::movslq_rm::new(dst, src).into(),
542         };
543         self.emit(Inst::External { inst });
544     }
545 
546     /// Integer register conditional move.
547     pub fn cmov(&mut self, src: Reg, dst: WritableReg, cc: IntCmpKind, size: OperandSize) {
548         let dst: WritableGpr = dst.map(Into::into);
549         self.emit(Inst::Cmove {
550             size: size.into(),
551             cc: cc.into(),
552             consequent: src.into(),
553             alternative: dst.to_reg(),
554             dst,
555         })
556     }
557 
558     /// Single and double precision floating point
559     /// register-to-register move.
560     pub fn xmm_mov_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
561         let ty = match size {
562             OperandSize::S32 => types::F32,
563             OperandSize::S64 => types::F64,
564             OperandSize::S128 => types::I32X4,
565             OperandSize::S8 | OperandSize::S16 => unreachable!(),
566         };
567         self.emit(Inst::gen_move(dst.map(|r| r.into()), src.into(), ty));
568     }
569 
570     /// Single and double precision floating point load.
571     pub fn xmm_mov_mr(
572         &mut self,
573         src: &Address,
574         dst: WritableReg,
575         size: OperandSize,
576         flags: MemFlags,
577     ) {
578         use OperandSize::*;
579 
580         assert!(dst.to_reg().is_float());
581 
582         let src = Self::to_synthetic_amode(src, flags);
583         let dst: WritableXmm = dst.map(|r| r.into());
584         let inst = match size {
585             S32 => asm::inst::movss_a_m::new(dst, src).into(),
586             S64 => asm::inst::movsd_a_m::new(dst, src).into(),
587             S128 => asm::inst::movdqu_a::new(dst, src).into(),
588             S8 | S16 => unreachable!(),
589         };
590         self.emit(Inst::External { inst });
591     }
592 
593     /// Vector load and extend.
594     pub fn xmm_vpmov_mr(
595         &mut self,
596         src: &Address,
597         dst: WritableReg,
598         kind: VpmovKind,
599         flags: MemFlags,
600     ) {
601         assert!(dst.to_reg().is_float());
602 
603         let src = Self::to_synthetic_amode(src, flags);
604 
605         self.emit(Inst::XmmUnaryRmRVex {
606             op: kind.into(),
607             src: XmmMem::unwrap_new(RegMem::mem(src)),
608             dst: dst.to_reg().into(),
609         });
610     }
611 
612     /// Extends vector of integers in `src` and puts results in `dst`.
613     pub fn xmm_vpmov_rr(&mut self, src: Reg, dst: WritableReg, kind: VpmovKind) {
614         self.emit(Inst::XmmUnaryRmRVex {
615             op: kind.into(),
616             src: src.into(),
617             dst: dst.to_reg().into(),
618         });
619     }
620 
621     /// Vector load and broadcast.
622     pub fn xmm_vpbroadcast_mr(
623         &mut self,
624         src: &Address,
625         dst: WritableReg,
626         size: OperandSize,
627         flags: MemFlags,
628     ) {
629         assert!(dst.to_reg().is_float());
630 
631         let src = Self::to_synthetic_amode(src, flags);
632 
633         let op = match size {
634             OperandSize::S8 => AvxOpcode::Vpbroadcastb,
635             OperandSize::S16 => AvxOpcode::Vpbroadcastw,
636             OperandSize::S32 => AvxOpcode::Vpbroadcastd,
637             _ => unimplemented!(),
638         };
639 
640         self.emit(Inst::XmmUnaryRmRVex {
641             op,
642             src: XmmMem::unwrap_new(RegMem::mem(src)),
643             dst: dst.to_reg().into(),
644         });
645     }
646 
647     /// Value in `src` is broadcast into lanes of `size` in `dst`.
648     pub fn xmm_vpbroadcast_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
649         assert!(src.is_float() && dst.to_reg().is_float());
650 
651         let op = match size {
652             OperandSize::S8 => AvxOpcode::Vpbroadcastb,
653             OperandSize::S16 => AvxOpcode::Vpbroadcastw,
654             OperandSize::S32 => AvxOpcode::Vpbroadcastd,
655             _ => unimplemented!(),
656         };
657 
658         self.emit(Inst::XmmUnaryRmRVex {
659             op,
660             src: XmmMem::unwrap_new(src.into()),
661             dst: dst.to_reg().into(),
662         });
663     }
664 
665     /// Memory to register shuffle of bytes in vector.
666     pub fn xmm_vpshuf_mr(
667         &mut self,
668         src: &Address,
669         dst: WritableReg,
670         mask: u8,
671         size: OperandSize,
672         flags: MemFlags,
673     ) {
674         let dst: WritableXmm = dst.map(|r| r.into());
675         let src = Self::to_synthetic_amode(src, flags);
676         let inst = match size {
677             OperandSize::S32 => asm::inst::vpshufd_a::new(dst, src, mask).into(),
678             _ => unimplemented!(),
679         };
680         self.emit(Inst::External { inst });
681     }
682 
683     /// Register to register shuffle of bytes in vector.
684     pub fn xmm_vpshuf_rr(&mut self, src: Reg, dst: WritableReg, mask: u8, size: OperandSize) {
685         let dst: WritableXmm = dst.map(|r| r.into());
686 
687         let inst = match size {
688             OperandSize::S16 => asm::inst::vpshuflw_a::new(dst, src, mask).into(),
689             OperandSize::S32 => asm::inst::vpshufd_a::new(dst, src, mask).into(),
690             _ => unimplemented!(),
691         };
692 
693         self.emit(Inst::External { inst });
694     }
695 
696     /// Single and double precision floating point store.
697     pub fn xmm_mov_rm(&mut self, src: Reg, dst: &Address, size: OperandSize, flags: MemFlags) {
698         use OperandSize::*;
699 
700         assert!(src.is_float());
701 
702         let dst = Self::to_synthetic_amode(dst, flags);
703         let src: Xmm = src.into();
704         let inst = match size {
705             S32 => asm::inst::movss_c_m::new(dst, src).into(),
706             S64 => asm::inst::movsd_c_m::new(dst, src).into(),
707             S128 => asm::inst::movdqu_b::new(dst, src).into(),
708             S16 | S8 => unreachable!(),
709         };
710         self.emit(Inst::External { inst })
711     }
712 
713     /// Floating point register conditional move.
714     pub fn xmm_cmov(&mut self, src: Reg, dst: WritableReg, cc: IntCmpKind, size: OperandSize) {
715         let dst: WritableXmm = dst.map(Into::into);
716         let ty = match size {
717             OperandSize::S32 => types::F32,
718             OperandSize::S64 => types::F64,
719             // Move the entire 128 bits via movdqa.
720             OperandSize::S128 => types::I32X4,
721             OperandSize::S8 | OperandSize::S16 => unreachable!(),
722         };
723 
724         self.emit(Inst::XmmCmove {
725             ty,
726             cc: cc.into(),
727             consequent: Xmm::unwrap_new(src.into()),
728             alternative: dst.to_reg(),
729             dst,
730         })
731     }
732 
733     /// Subtract register and register
734     pub fn sub_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
735         let dst = pair_gpr(dst);
736         let inst = match size {
737             OperandSize::S8 => asm::inst::subb_rm::new(dst, src).into(),
738             OperandSize::S16 => asm::inst::subw_rm::new(dst, src).into(),
739             OperandSize::S32 => asm::inst::subl_rm::new(dst, src).into(),
740             OperandSize::S64 => asm::inst::subq_rm::new(dst, src).into(),
741             OperandSize::S128 => unimplemented!(),
742         };
743         self.emit(Inst::External { inst });
744     }
745 
746     /// Subtract immediate register.
747     pub fn sub_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) {
748         let dst = pair_gpr(dst);
749         let inst = match size {
750             OperandSize::S8 => asm::inst::subb_mi::new(dst, u8::try_from(imm).unwrap()).into(),
751             OperandSize::S16 => asm::inst::subw_mi::new(dst, u16::try_from(imm).unwrap()).into(),
752             OperandSize::S32 => asm::inst::subl_mi::new(dst, imm as u32).into(),
753             OperandSize::S64 => asm::inst::subq_mi_sxl::new(dst, imm).into(),
754             OperandSize::S128 => unimplemented!(),
755         };
756         self.emit(Inst::External { inst });
757     }
758 
759     /// "and" two registers.
760     pub fn and_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
761         let dst = pair_gpr(dst);
762         let inst = match size {
763             OperandSize::S8 => asm::inst::andb_rm::new(dst, src).into(),
764             OperandSize::S16 => asm::inst::andw_rm::new(dst, src).into(),
765             OperandSize::S32 => asm::inst::andl_rm::new(dst, src).into(),
766             OperandSize::S64 => asm::inst::andq_rm::new(dst, src).into(),
767             OperandSize::S128 => unimplemented!(),
768         };
769         self.emit(Inst::External { inst });
770     }
771 
772     pub fn and_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) {
773         let dst = pair_gpr(dst);
774         let inst = match size {
775             OperandSize::S8 => asm::inst::andb_mi::new(dst, u8::try_from(imm).unwrap()).into(),
776             OperandSize::S16 => asm::inst::andw_mi::new(dst, u16::try_from(imm).unwrap()).into(),
777             OperandSize::S32 => asm::inst::andl_mi::new(dst, imm as u32).into(),
778             OperandSize::S64 => asm::inst::andq_mi_sxl::new(dst, imm).into(),
779             OperandSize::S128 => unimplemented!(),
780         };
781         self.emit(Inst::External { inst });
782     }
783 
784     /// "and" two float registers.
785     pub fn xmm_and_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
786         let dst = pair_xmm(dst);
787         let inst = match size {
788             OperandSize::S32 => asm::inst::andps_a::new(dst, src).into(),
789             OperandSize::S64 => asm::inst::andpd_a::new(dst, src).into(),
790             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
791         };
792         self.emit(Inst::External { inst });
793     }
794 
795     /// "and not" two float registers.
796     pub fn xmm_andn_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
797         let dst = pair_xmm(dst);
798         let inst = match size {
799             OperandSize::S32 => asm::inst::andnps_a::new(dst, src).into(),
800             OperandSize::S64 => asm::inst::andnpd_a::new(dst, src).into(),
801             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
802         };
803         self.emit(Inst::External { inst });
804     }
805 
806     pub fn gpr_to_xmm(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
807         let dst: WritableXmm = dst.map(|r| r.into());
808         let inst = match size {
809             OperandSize::S32 => asm::inst::movd_a::new(dst, src).into(),
810             OperandSize::S64 => asm::inst::movq_a::new(dst, src).into(),
811             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
812         };
813 
814         self.emit(Inst::External { inst });
815     }
816 
817     pub fn xmm_to_gpr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
818         let dst: WritableGpr = dst.map(Into::into);
819         let src: Xmm = src.into();
820         let inst = match size {
821             OperandSize::S32 => asm::inst::movd_b::new(dst, src).into(),
822             OperandSize::S64 => asm::inst::movq_b::new(dst, src).into(),
823             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
824         };
825 
826         self.emit(Inst::External { inst })
827     }
828 
829     /// Convert float to signed int.
830     pub fn cvt_float_to_sint_seq(
831         &mut self,
832         src: Reg,
833         dst: WritableReg,
834         tmp_gpr: Reg,
835         tmp_xmm: Reg,
836         src_size: OperandSize,
837         dst_size: OperandSize,
838         saturating: bool,
839     ) {
840         self.emit(Inst::CvtFloatToSintSeq {
841             dst_size: dst_size.into(),
842             src_size: src_size.into(),
843             is_saturating: saturating,
844             src: src.into(),
845             dst: dst.map(Into::into),
846             tmp_gpr: tmp_gpr.into(),
847             tmp_xmm: tmp_xmm.into(),
848         });
849     }
850 
851     /// Convert float to unsigned int.
852     pub fn cvt_float_to_uint_seq(
853         &mut self,
854         src: Reg,
855         dst: WritableReg,
856         tmp_gpr: Reg,
857         tmp_xmm: Reg,
858         tmp_xmm2: Reg,
859         src_size: OperandSize,
860         dst_size: OperandSize,
861         saturating: bool,
862     ) {
863         self.emit(Inst::CvtFloatToUintSeq {
864             dst_size: dst_size.into(),
865             src_size: src_size.into(),
866             is_saturating: saturating,
867             src: src.into(),
868             dst: dst.map(Into::into),
869             tmp_gpr: tmp_gpr.into(),
870             tmp_xmm: tmp_xmm.into(),
871             tmp_xmm2: tmp_xmm2.into(),
872         });
873     }
874 
875     /// Convert signed int to float.
876     pub fn cvt_sint_to_float(
877         &mut self,
878         src: Reg,
879         dst: WritableReg,
880         src_size: OperandSize,
881         dst_size: OperandSize,
882     ) {
883         use OperandSize::*;
884         let dst = pair_xmm(dst);
885         let inst = match (src_size, dst_size) {
886             (S32, S32) => asm::inst::cvtsi2ssl_a::new(dst, src).into(),
887             (S32, S64) => asm::inst::cvtsi2sdl_a::new(dst, src).into(),
888             (S64, S32) => asm::inst::cvtsi2ssq_a::new(dst, src).into(),
889             (S64, S64) => asm::inst::cvtsi2sdq_a::new(dst, src).into(),
890             _ => unreachable!(),
891         };
892         self.emit(Inst::External { inst });
893     }
894 
895     /// Convert unsigned 64-bit int to float.
896     pub fn cvt_uint64_to_float_seq(
897         &mut self,
898         src: Reg,
899         dst: WritableReg,
900         tmp_gpr1: Reg,
901         tmp_gpr2: Reg,
902         dst_size: OperandSize,
903     ) {
904         self.emit(Inst::CvtUint64ToFloatSeq {
905             dst_size: dst_size.into(),
906             src: src.into(),
907             dst: dst.map(Into::into),
908             tmp_gpr1: tmp_gpr1.into(),
909             tmp_gpr2: tmp_gpr2.into(),
910         });
911     }
912 
913     /// Change precision of float.
914     pub fn cvt_float_to_float(
915         &mut self,
916         src: Reg,
917         dst: WritableReg,
918         src_size: OperandSize,
919         dst_size: OperandSize,
920     ) {
921         use OperandSize::*;
922         let dst = pair_xmm(dst);
923         let inst = match (src_size, dst_size) {
924             (S32, S64) => asm::inst::cvtss2sd_a::new(dst, src).into(),
925             (S64, S32) => asm::inst::cvtsd2ss_a::new(dst, src).into(),
926             _ => unimplemented!(),
927         };
928         self.emit(Inst::External { inst });
929     }
930 
931     pub fn or_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
932         let dst = pair_gpr(dst);
933         let inst = match size {
934             OperandSize::S8 => asm::inst::orb_rm::new(dst, src).into(),
935             OperandSize::S16 => asm::inst::orw_rm::new(dst, src).into(),
936             OperandSize::S32 => asm::inst::orl_rm::new(dst, src).into(),
937             OperandSize::S64 => asm::inst::orq_rm::new(dst, src).into(),
938             OperandSize::S128 => unimplemented!(),
939         };
940         self.emit(Inst::External { inst });
941     }
942 
943     pub fn or_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) {
944         let dst = pair_gpr(dst);
945         let inst = match size {
946             OperandSize::S8 => asm::inst::orb_mi::new(dst, u8::try_from(imm).unwrap()).into(),
947             OperandSize::S16 => asm::inst::orw_mi::new(dst, u16::try_from(imm).unwrap()).into(),
948             OperandSize::S32 => asm::inst::orl_mi::new(dst, imm as u32).into(),
949             OperandSize::S64 => asm::inst::orq_mi_sxl::new(dst, imm).into(),
950             OperandSize::S128 => unimplemented!(),
951         };
952         self.emit(Inst::External { inst });
953     }
954 
955     pub fn xmm_or_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
956         let dst = pair_xmm(dst);
957         let inst = match size {
958             OperandSize::S32 => asm::inst::orps_a::new(dst, src).into(),
959             OperandSize::S64 => asm::inst::orpd_a::new(dst, src).into(),
960             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
961         };
962         self.emit(Inst::External { inst });
963     }
964 
965     /// Logical exclusive or with registers.
966     pub fn xor_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
967         let dst = pair_gpr(dst);
968         let inst = match size {
969             OperandSize::S8 => asm::inst::xorb_rm::new(dst, src).into(),
970             OperandSize::S16 => asm::inst::xorw_rm::new(dst, src).into(),
971             OperandSize::S32 => asm::inst::xorl_rm::new(dst, src).into(),
972             OperandSize::S64 => asm::inst::xorq_rm::new(dst, src).into(),
973             OperandSize::S128 => unimplemented!(),
974         };
975         self.emit(Inst::External { inst });
976     }
977 
978     pub fn xor_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) {
979         let dst = pair_gpr(dst);
980         let inst = match size {
981             OperandSize::S8 => asm::inst::xorb_mi::new(dst, u8::try_from(imm).unwrap()).into(),
982             OperandSize::S16 => asm::inst::xorw_mi::new(dst, u16::try_from(imm).unwrap()).into(),
983             OperandSize::S32 => asm::inst::xorl_mi::new(dst, imm as u32).into(),
984             OperandSize::S64 => asm::inst::xorq_mi_sxl::new(dst, imm).into(),
985             OperandSize::S128 => unimplemented!(),
986         };
987         self.emit(Inst::External { inst });
988     }
989 
990     /// Logical exclusive or with float registers.
991     pub fn xmm_xor_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
992         let dst = pair_xmm(dst);
993         let inst = match size {
994             OperandSize::S32 => asm::inst::xorps_a::new(dst, src).into(),
995             OperandSize::S64 => asm::inst::xorpd_a::new(dst, src).into(),
996             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
997         };
998         self.emit(Inst::External { inst });
999     }
1000 
1001     /// Shift with register and register.
1002     pub fn shift_rr(&mut self, src: Reg, dst: WritableReg, kind: ShiftKind, size: OperandSize) {
1003         let dst = pair_gpr(dst);
1004         let src: Gpr = src.into();
1005         let inst = match (kind, size) {
1006             (ShiftKind::Shl, OperandSize::S32) => asm::inst::shll_mc::new(dst, src).into(),
1007             (ShiftKind::Shl, OperandSize::S64) => asm::inst::shlq_mc::new(dst, src).into(),
1008             (ShiftKind::Shl, _) => todo!(),
1009             (ShiftKind::ShrS, OperandSize::S32) => asm::inst::sarl_mc::new(dst, src).into(),
1010             (ShiftKind::ShrS, OperandSize::S64) => asm::inst::sarq_mc::new(dst, src).into(),
1011             (ShiftKind::ShrS, _) => todo!(),
1012             (ShiftKind::ShrU, OperandSize::S32) => asm::inst::shrl_mc::new(dst, src).into(),
1013             (ShiftKind::ShrU, OperandSize::S64) => asm::inst::shrq_mc::new(dst, src).into(),
1014             (ShiftKind::ShrU, _) => todo!(),
1015             (ShiftKind::Rotl, OperandSize::S32) => asm::inst::roll_mc::new(dst, src).into(),
1016             (ShiftKind::Rotl, OperandSize::S64) => asm::inst::rolq_mc::new(dst, src).into(),
1017             (ShiftKind::Rotl, _) => todo!(),
1018             (ShiftKind::Rotr, OperandSize::S32) => asm::inst::rorl_mc::new(dst, src).into(),
1019             (ShiftKind::Rotr, OperandSize::S64) => asm::inst::rorq_mc::new(dst, src).into(),
1020             (ShiftKind::Rotr, _) => todo!(),
1021         };
1022         self.emit(Inst::External { inst });
1023     }
1024 
1025     /// Shift with immediate and register.
1026     pub fn shift_ir(&mut self, imm: u8, dst: WritableReg, kind: ShiftKind, size: OperandSize) {
1027         let dst = pair_gpr(dst);
1028         let inst = match (kind, size) {
1029             (ShiftKind::Shl, OperandSize::S32) => asm::inst::shll_mi::new(dst, imm).into(),
1030             (ShiftKind::Shl, OperandSize::S64) => asm::inst::shlq_mi::new(dst, imm).into(),
1031             (ShiftKind::Shl, _) => todo!(),
1032             (ShiftKind::ShrS, OperandSize::S32) => asm::inst::sarl_mi::new(dst, imm).into(),
1033             (ShiftKind::ShrS, OperandSize::S64) => asm::inst::sarq_mi::new(dst, imm).into(),
1034             (ShiftKind::ShrS, _) => todo!(),
1035             (ShiftKind::ShrU, OperandSize::S32) => asm::inst::shrl_mi::new(dst, imm).into(),
1036             (ShiftKind::ShrU, OperandSize::S64) => asm::inst::shrq_mi::new(dst, imm).into(),
1037             (ShiftKind::ShrU, _) => todo!(),
1038             (ShiftKind::Rotl, OperandSize::S32) => asm::inst::roll_mi::new(dst, imm).into(),
1039             (ShiftKind::Rotl, OperandSize::S64) => asm::inst::rolq_mi::new(dst, imm).into(),
1040             (ShiftKind::Rotl, _) => todo!(),
1041             (ShiftKind::Rotr, OperandSize::S32) => asm::inst::rorl_mi::new(dst, imm).into(),
1042             (ShiftKind::Rotr, OperandSize::S64) => asm::inst::rorq_mi::new(dst, imm).into(),
1043             (ShiftKind::Rotr, _) => todo!(),
1044         };
1045         self.emit(Inst::External { inst });
1046     }
1047 
1048     /// Signed/unsigned division.
1049     ///
1050     /// Emits a sequence of instructions to ensure the correctness of
1051     /// the division invariants.  This function assumes that the
1052     /// caller has correctly allocated the dividend as `(rdx:rax)` and
1053     /// accounted for the quotient to be stored in `rax`.
1054     pub fn div(&mut self, divisor: Reg, dst: (Reg, Reg), kind: DivKind, size: OperandSize) {
1055         let trap = match kind {
1056             // Signed division has two trapping conditions, integer overflow and
1057             // divide-by-zero. Check for divide-by-zero explicitly and let the
1058             // hardware detect overflow.
1059             DivKind::Signed => {
1060                 self.emit(Inst::CmpRmiR {
1061                     size: size.into(),
1062                     src1: divisor.into(),
1063                     src2: GprMemImm::unwrap_new(RegMemImm::imm(0)),
1064                     opcode: CmpOpcode::Cmp,
1065                 });
1066                 self.emit(Inst::TrapIf {
1067                     cc: CC::Z,
1068                     trap_code: TrapCode::INTEGER_DIVISION_BY_ZERO,
1069                 });
1070 
1071                 // Sign-extend the dividend with tailor-made instructoins for
1072                 // just this operation.
1073                 let ext_dst: WritableGpr = dst.1.into();
1074                 let ext_src: Gpr = dst.0.into();
1075                 let inst = match size {
1076                     OperandSize::S32 => asm::inst::cltd_zo::new(ext_dst, ext_src).into(),
1077                     OperandSize::S64 => asm::inst::cqto_zo::new(ext_dst, ext_src).into(),
1078                     _ => unimplemented!(),
1079                 };
1080                 self.emit(Inst::External { inst });
1081                 TrapCode::INTEGER_OVERFLOW
1082             }
1083 
1084             // Unsigned division only traps in one case, on divide-by-zero, so
1085             // defer that to the trap opcode.
1086             //
1087             // The divisor_hi reg is initialized with zero through an
1088             // xor-against-itself op.
1089             DivKind::Unsigned => {
1090                 self.xor_rr(dst.1, writable!(dst.1), size);
1091                 TrapCode::INTEGER_DIVISION_BY_ZERO
1092             }
1093         };
1094         let dst0 = pair_gpr(writable!(dst.0));
1095         let dst1 = pair_gpr(writable!(dst.1));
1096         let inst = match (kind, size) {
1097             (DivKind::Signed, OperandSize::S32) => {
1098                 asm::inst::idivl_m::new(dst0, dst1, divisor, trap).into()
1099             }
1100             (DivKind::Unsigned, OperandSize::S32) => {
1101                 asm::inst::divl_m::new(dst0, dst1, divisor, trap).into()
1102             }
1103             (DivKind::Signed, OperandSize::S64) => {
1104                 asm::inst::idivq_m::new(dst0, dst1, divisor, trap).into()
1105             }
1106             (DivKind::Unsigned, OperandSize::S64) => {
1107                 asm::inst::divq_m::new(dst0, dst1, divisor, trap).into()
1108             }
1109             _ => todo!(),
1110         };
1111         self.emit(Inst::External { inst });
1112     }
1113 
1114     /// Signed/unsigned remainder.
1115     ///
1116     /// Emits a sequence of instructions to ensure the correctness of the
1117     /// division invariants and ultimately calculate the remainder.
1118     /// This function assumes that the
1119     /// caller has correctly allocated the dividend as `(rdx:rax)` and
1120     /// accounted for the remainder to be stored in `rdx`.
1121     pub fn rem(&mut self, divisor: Reg, dst: (Reg, Reg), kind: RemKind, size: OperandSize) {
1122         match kind {
1123             // Signed remainder goes through a pseudo-instruction which has
1124             // some internal branching. The `dividend_hi`, or `rdx`, is
1125             // initialized here with a `SignExtendData` instruction.
1126             RemKind::Signed => {
1127                 let ext_dst: WritableGpr = dst.1.into();
1128 
1129                 // Initialize `dividend_hi`, or `rdx`, with a tailor-made
1130                 // instruction for this operation.
1131                 let ext_src: Gpr = dst.0.into();
1132                 let inst = match size {
1133                     OperandSize::S32 => asm::inst::cltd_zo::new(ext_dst, ext_src).into(),
1134                     OperandSize::S64 => asm::inst::cqto_zo::new(ext_dst, ext_src).into(),
1135                     _ => unimplemented!(),
1136                 };
1137                 self.emit(Inst::External { inst });
1138                 self.emit(Inst::CheckedSRemSeq {
1139                     size: size.into(),
1140                     divisor: divisor.into(),
1141                     dividend_lo: dst.0.into(),
1142                     dividend_hi: dst.1.into(),
1143                     dst_quotient: dst.0.into(),
1144                     dst_remainder: dst.1.into(),
1145                 });
1146             }
1147 
1148             // Unsigned remainder initializes `dividend_hi` with zero and
1149             // then executes a normal `div` instruction.
1150             RemKind::Unsigned => {
1151                 self.xor_rr(dst.1, writable!(dst.1), size);
1152                 let dst0 = pair_gpr(writable!(dst.0));
1153                 let dst1 = pair_gpr(writable!(dst.1));
1154                 let trap = TrapCode::INTEGER_DIVISION_BY_ZERO;
1155                 let inst = match size {
1156                     OperandSize::S32 => asm::inst::divl_m::new(dst0, dst1, divisor, trap).into(),
1157                     OperandSize::S64 => asm::inst::divq_m::new(dst0, dst1, divisor, trap).into(),
1158                     _ => todo!(),
1159                 };
1160                 self.emit(Inst::External { inst });
1161             }
1162         }
1163     }
1164 
1165     /// Multiply immediate and register.
1166     pub fn mul_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) {
1167         use OperandSize::*;
1168         let src = dst.to_reg();
1169         let dst: WritableGpr = dst.to_reg().into();
1170         let inst = match size {
1171             S16 => asm::inst::imulw_rmi::new(dst, src, u16::try_from(imm).unwrap()).into(),
1172             S32 => asm::inst::imull_rmi::new(dst, src, imm as u32).into(),
1173             S64 => asm::inst::imulq_rmi_sxl::new(dst, src, imm).into(),
1174             S8 | S128 => unimplemented!(),
1175         };
1176         self.emit(Inst::External { inst });
1177     }
1178 
1179     /// Multiply register and register.
1180     pub fn mul_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1181         use OperandSize::*;
1182         let dst = pair_gpr(dst);
1183         let inst = match size {
1184             S16 => asm::inst::imulw_rm::new(dst, src).into(),
1185             S32 => asm::inst::imull_rm::new(dst, src).into(),
1186             S64 => asm::inst::imulq_rm::new(dst, src).into(),
1187             S8 | S128 => unimplemented!(),
1188         };
1189         self.emit(Inst::External { inst });
1190     }
1191 
1192     /// Add immediate and register.
1193     pub fn add_ir(&mut self, imm: i32, dst: WritableReg, size: OperandSize) {
1194         let dst = pair_gpr(dst);
1195         let inst = match size {
1196             OperandSize::S8 => asm::inst::addb_mi::new(dst, u8::try_from(imm).unwrap()).into(),
1197             OperandSize::S16 => asm::inst::addw_mi::new(dst, u16::try_from(imm).unwrap()).into(),
1198             OperandSize::S32 => asm::inst::addl_mi::new(dst, imm as u32).into(),
1199             OperandSize::S64 => asm::inst::addq_mi_sxl::new(dst, imm).into(),
1200             OperandSize::S128 => unimplemented!(),
1201         };
1202         self.emit(Inst::External { inst });
1203     }
1204 
1205     /// Add register and register.
1206     pub fn add_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1207         let dst = pair_gpr(dst);
1208         let inst = match size {
1209             OperandSize::S8 => asm::inst::addb_rm::new(dst, src).into(),
1210             OperandSize::S16 => asm::inst::addw_rm::new(dst, src).into(),
1211             OperandSize::S32 => asm::inst::addl_rm::new(dst, src).into(),
1212             OperandSize::S64 => asm::inst::addq_rm::new(dst, src).into(),
1213             OperandSize::S128 => unimplemented!(),
1214         };
1215         self.emit(Inst::External { inst });
1216     }
1217 
1218     pub fn lock_xadd(
1219         &mut self,
1220         addr: Address,
1221         dst: WritableReg,
1222         size: OperandSize,
1223         flags: MemFlags,
1224     ) {
1225         assert!(addr.is_offset());
1226         let mem = Self::to_synthetic_amode(&addr, flags);
1227         let dst = pair_gpr(dst);
1228         let inst = match size {
1229             OperandSize::S8 => asm::inst::lock_xaddb_mr::new(mem, dst).into(),
1230             OperandSize::S16 => asm::inst::lock_xaddw_mr::new(mem, dst).into(),
1231             OperandSize::S32 => asm::inst::lock_xaddl_mr::new(mem, dst).into(),
1232             OperandSize::S64 => asm::inst::lock_xaddq_mr::new(mem, dst).into(),
1233             OperandSize::S128 => unimplemented!(),
1234         };
1235 
1236         self.emit(Inst::External { inst });
1237     }
1238 
1239     pub fn atomic_rmw_seq(
1240         &mut self,
1241         addr: Address,
1242         operand: Reg,
1243         dst: WritableReg,
1244         temp: WritableReg,
1245         size: OperandSize,
1246         flags: MemFlags,
1247         op: AtomicRmwSeqOp,
1248     ) {
1249         assert!(addr.is_offset());
1250         let mem = Self::to_synthetic_amode(&addr, flags);
1251         self.emit(Inst::AtomicRmwSeq {
1252             ty: Type::int_with_byte_size(size.bytes() as _).unwrap(),
1253             mem,
1254             operand: operand.into(),
1255             temp: temp.map(Into::into),
1256             dst_old: dst.map(Into::into),
1257             op,
1258         });
1259     }
1260 
1261     pub fn xchg(&mut self, addr: Address, dst: WritableReg, size: OperandSize, flags: MemFlags) {
1262         assert!(addr.is_offset());
1263         let mem = Self::to_synthetic_amode(&addr, flags);
1264         let dst = pair_gpr(dst);
1265         let inst = match size {
1266             OperandSize::S8 => asm::inst::xchgb_rm::new(dst, mem).into(),
1267             OperandSize::S16 => asm::inst::xchgw_rm::new(dst, mem).into(),
1268             OperandSize::S32 => asm::inst::xchgl_rm::new(dst, mem).into(),
1269             OperandSize::S64 => asm::inst::xchgq_rm::new(dst, mem).into(),
1270             OperandSize::S128 => unimplemented!(),
1271         };
1272 
1273         self.emit(Inst::External { inst });
1274     }
1275     pub fn cmpxchg(
1276         &mut self,
1277         addr: Address,
1278         replacement: Reg,
1279         dst: WritableReg,
1280         size: OperandSize,
1281         flags: MemFlags,
1282     ) {
1283         assert!(addr.is_offset());
1284         let mem = Self::to_synthetic_amode(&addr, flags);
1285         let dst = pair_gpr(dst);
1286         let inst = match size {
1287             OperandSize::S8 => asm::inst::lock_cmpxchgb_mr::new(mem, replacement, dst).into(),
1288             OperandSize::S16 => asm::inst::lock_cmpxchgw_mr::new(mem, replacement, dst).into(),
1289             OperandSize::S32 => asm::inst::lock_cmpxchgl_mr::new(mem, replacement, dst).into(),
1290             OperandSize::S64 => asm::inst::lock_cmpxchgq_mr::new(mem, replacement, dst).into(),
1291             OperandSize::S128 => unimplemented!(),
1292         };
1293 
1294         self.emit(Inst::External { inst });
1295     }
1296 
1297     pub fn cmp_ir(&mut self, src1: Reg, imm: i32, size: OperandSize) {
1298         let imm = RegMemImm::imm(imm as u32);
1299 
1300         self.emit(Inst::CmpRmiR {
1301             size: size.into(),
1302             opcode: CmpOpcode::Cmp,
1303             src1: src1.into(),
1304             src2: GprMemImm::unwrap_new(imm),
1305         });
1306     }
1307 
1308     pub fn cmp_rr(&mut self, src1: Reg, src2: Reg, size: OperandSize) {
1309         self.emit(Inst::CmpRmiR {
1310             size: size.into(),
1311             opcode: CmpOpcode::Cmp,
1312             src1: src1.into(),
1313             src2: src2.into(),
1314         });
1315     }
1316 
1317     /// Compares values in src1 and src2 and sets ZF, PF, and CF flags in EFLAGS
1318     /// register.
1319     pub fn ucomis(&mut self, src1: Reg, src2: Reg, size: OperandSize) {
1320         let inst = match size {
1321             OperandSize::S32 => asm::inst::ucomiss_a::new(src1, src2).into(),
1322             OperandSize::S64 => asm::inst::ucomisd_a::new(src1, src2).into(),
1323             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
1324         };
1325         self.emit(Inst::External { inst });
1326     }
1327 
1328     pub fn popcnt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1329         assert!(
1330             self.isa_flags.has_popcnt() && self.isa_flags.has_sse42(),
1331             "Requires has_popcnt and has_sse42 flags"
1332         );
1333         let dst = WritableGpr::from_reg(dst.to_reg().into());
1334         let inst = match size {
1335             OperandSize::S16 => asm::inst::popcntw_rm::new(dst, src).into(),
1336             OperandSize::S32 => asm::inst::popcntl_rm::new(dst, src).into(),
1337             OperandSize::S64 => asm::inst::popcntq_rm::new(dst, src).into(),
1338             OperandSize::S8 | OperandSize::S128 => unreachable!(),
1339         };
1340         self.emit(Inst::External { inst });
1341     }
1342 
1343     /// Emit a test instruction with two register operands.
1344     pub fn test_rr(&mut self, src1: Reg, src2: Reg, size: OperandSize) {
1345         self.emit(Inst::CmpRmiR {
1346             size: size.into(),
1347             opcode: CmpOpcode::Test,
1348             src1: src1.into(),
1349             src2: src2.into(),
1350         })
1351     }
1352 
1353     /// Set value in dst to `0` or `1` based on flags in status register and
1354     /// [`CmpKind`].
1355     pub fn setcc(&mut self, kind: IntCmpKind, dst: WritableReg) {
1356         self.setcc_impl(kind.into(), dst);
1357     }
1358 
1359     /// Set value in dst to `1` if parity flag in status register is set, `0`
1360     /// otherwise.
1361     pub fn setp(&mut self, dst: WritableReg) {
1362         self.setcc_impl(CC::P, dst);
1363     }
1364 
1365     /// Set value in dst to `1` if parity flag in status register is not set,
1366     /// `0` otherwise.
1367     pub fn setnp(&mut self, dst: WritableReg) {
1368         self.setcc_impl(CC::NP, dst);
1369     }
1370 
1371     fn setcc_impl(&mut self, cc: CC, dst: WritableReg) {
1372         // Clear the dst register or bits 1 to 31 may be incorrectly set.
1373         // Don't use xor since it updates the status register.
1374         let dst: WritableGpr = dst.map(Into::into);
1375         let inst = asm::inst::movl_oi::new(dst, 0).into();
1376         self.emit(Inst::External { inst });
1377         // Copy correct bit from status register into dst register.
1378         self.emit(Inst::Setcc { cc, dst });
1379     }
1380 
1381     /// Store the count of leading zeroes in src in dst.
1382     /// Requires `has_lzcnt` flag.
1383     pub fn lzcnt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1384         assert!(self.isa_flags.has_lzcnt(), "Requires has_lzcnt flag");
1385         let dst = WritableGpr::from_reg(dst.to_reg().into());
1386         let inst = match size {
1387             OperandSize::S16 => asm::inst::lzcntw_rm::new(dst, src).into(),
1388             OperandSize::S32 => asm::inst::lzcntl_rm::new(dst, src).into(),
1389             OperandSize::S64 => asm::inst::lzcntq_rm::new(dst, src).into(),
1390             OperandSize::S8 | OperandSize::S128 => unreachable!(),
1391         };
1392         self.emit(Inst::External { inst });
1393     }
1394 
1395     /// Store the count of trailing zeroes in src in dst.
1396     /// Requires `has_bmi1` flag.
1397     pub fn tzcnt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1398         assert!(self.isa_flags.has_bmi1(), "Requires has_bmi1 flag");
1399         let dst = WritableGpr::from_reg(dst.to_reg().into());
1400         let inst = match size {
1401             OperandSize::S16 => asm::inst::tzcntw_a::new(dst, src).into(),
1402             OperandSize::S32 => asm::inst::tzcntl_a::new(dst, src).into(),
1403             OperandSize::S64 => asm::inst::tzcntq_a::new(dst, src).into(),
1404             OperandSize::S8 | OperandSize::S128 => unreachable!(),
1405         };
1406         self.emit(Inst::External { inst });
1407     }
1408 
1409     /// Stores position of the most significant bit set in src in dst.
1410     /// Zero flag is set if src is equal to 0.
1411     pub fn bsr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1412         let dst: WritableGpr = WritableGpr::from_reg(dst.to_reg().into());
1413         let inst = match size {
1414             OperandSize::S16 => asm::inst::bsrw_rm::new(dst, src).into(),
1415             OperandSize::S32 => asm::inst::bsrl_rm::new(dst, src).into(),
1416             OperandSize::S64 => asm::inst::bsrq_rm::new(dst, src).into(),
1417             OperandSize::S8 | OperandSize::S128 => unreachable!(),
1418         };
1419         self.emit(Inst::External { inst });
1420     }
1421 
1422     /// Performs integer negation on `src` and places result in `dst`.
1423     pub fn neg(&mut self, read: Reg, write: WritableReg, size: OperandSize) {
1424         let gpr = PairedGpr {
1425             read: read.into(),
1426             write: WritableGpr::from_reg(write.to_reg().into()),
1427         };
1428         let inst = match size {
1429             OperandSize::S8 => asm::inst::negb_m::new(gpr).into(),
1430             OperandSize::S16 => asm::inst::negw_m::new(gpr).into(),
1431             OperandSize::S32 => asm::inst::negl_m::new(gpr).into(),
1432             OperandSize::S64 => asm::inst::negq_m::new(gpr).into(),
1433             OperandSize::S128 => unreachable!(),
1434         };
1435         self.emit(Inst::External { inst });
1436     }
1437 
1438     /// Stores position of the least significant bit set in src in dst.
1439     /// Zero flag is set if src is equal to 0.
1440     pub fn bsf(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1441         let dst: WritableGpr = WritableGpr::from_reg(dst.to_reg().into());
1442         let inst = match size {
1443             OperandSize::S16 => asm::inst::bsfw_rm::new(dst, src).into(),
1444             OperandSize::S32 => asm::inst::bsfl_rm::new(dst, src).into(),
1445             OperandSize::S64 => asm::inst::bsfq_rm::new(dst, src).into(),
1446             OperandSize::S8 | OperandSize::S128 => unreachable!(),
1447         };
1448         self.emit(Inst::External { inst });
1449     }
1450 
1451     /// Performs float addition on src and dst and places result in dst.
1452     pub fn xmm_add_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1453         let dst = pair_xmm(dst);
1454         let inst = match size {
1455             OperandSize::S32 => asm::inst::addss_a::new(dst, src).into(),
1456             OperandSize::S64 => asm::inst::addsd_a::new(dst, src).into(),
1457             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
1458         };
1459         self.emit(Inst::External { inst });
1460     }
1461 
1462     /// Performs float subtraction on src and dst and places result in dst.
1463     pub fn xmm_sub_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1464         let dst = pair_xmm(dst);
1465         let inst = match size {
1466             OperandSize::S32 => asm::inst::subss_a::new(dst, src).into(),
1467             OperandSize::S64 => asm::inst::subsd_a::new(dst, src).into(),
1468             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
1469         };
1470         self.emit(Inst::External { inst });
1471     }
1472 
1473     /// Performs float multiplication on src and dst and places result in dst.
1474     pub fn xmm_mul_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1475         use OperandSize::*;
1476         let dst = pair_xmm(dst);
1477         let inst = match size {
1478             S32 => asm::inst::mulss_a::new(dst, src).into(),
1479             S64 => asm::inst::mulsd_a::new(dst, src).into(),
1480             S8 | S16 | S128 => unreachable!(),
1481         };
1482         self.emit(Inst::External { inst });
1483     }
1484 
1485     /// Performs float division on src and dst and places result in dst.
1486     pub fn xmm_div_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1487         let dst = pair_xmm(dst);
1488         let inst = match size {
1489             OperandSize::S32 => asm::inst::divss_a::new(dst, src).into(),
1490             OperandSize::S64 => asm::inst::divsd_a::new(dst, src).into(),
1491             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
1492         };
1493         self.emit(Inst::External { inst });
1494     }
1495 
1496     /// Minimum for src and dst XMM registers with results put in dst.
1497     pub fn xmm_min_seq(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1498         self.emit(Inst::XmmMinMaxSeq {
1499             size: size.into(),
1500             is_min: true,
1501             lhs: src.into(),
1502             rhs: dst.to_reg().into(),
1503             dst: dst.map(Into::into),
1504         });
1505     }
1506 
1507     /// Maximum for src and dst XMM registers with results put in dst.
1508     pub fn xmm_max_seq(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1509         self.emit(Inst::XmmMinMaxSeq {
1510             size: size.into(),
1511             is_min: false,
1512             lhs: src.into(),
1513             rhs: dst.to_reg().into(),
1514             dst: dst.map(Into::into),
1515         });
1516     }
1517 
1518     /// Perform rounding operation on float register src and place results in
1519     /// float register dst.
1520     pub fn xmm_rounds_rr(
1521         &mut self,
1522         src: Reg,
1523         dst: WritableReg,
1524         mode: RoundingMode,
1525         size: OperandSize,
1526     ) {
1527         let dst = dst.map(|r| r.into());
1528 
1529         let imm: u8 = match mode {
1530             RoundingMode::Nearest => 0x00,
1531             RoundingMode::Down => 0x01,
1532             RoundingMode::Up => 0x02,
1533             RoundingMode::Zero => 0x03,
1534         };
1535 
1536         let inst = match size {
1537             OperandSize::S32 => asm::inst::roundss_rmi::new(dst, src, imm).into(),
1538             OperandSize::S64 => asm::inst::roundsd_rmi::new(dst, src, imm).into(),
1539             OperandSize::S8 | OperandSize::S16 | OperandSize::S128 => unreachable!(),
1540         };
1541 
1542         self.emit(Inst::External { inst });
1543     }
1544 
1545     pub fn sqrt(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1546         use OperandSize::*;
1547         let dst = pair_xmm(dst);
1548         let inst = match size {
1549             S32 => asm::inst::sqrtss_a::new(dst, src).into(),
1550             S64 => asm::inst::sqrtsd_a::new(dst, src).into(),
1551             S8 | S16 | S128 => unimplemented!(),
1552         };
1553         self.emit(Inst::External { inst });
1554     }
1555 
1556     /// Emit a call to an unknown location through a register.
1557     pub fn call_with_reg(&mut self, cc: CallingConvention, callee: Reg) {
1558         self.emit(Inst::CallUnknown {
1559             info: Box::new(CallInfo::empty(RegMem::reg(callee.into()), cc.into())),
1560         });
1561     }
1562 
1563     /// Emit a call to a locally defined function through an index.
1564     pub fn call_with_name(&mut self, cc: CallingConvention, name: UserExternalNameRef) {
1565         self.emit(Inst::CallKnown {
1566             info: Box::new(CallInfo::empty(ExternalName::user(name), cc.into())),
1567         });
1568     }
1569 
1570     /// Emits a conditional jump to the given label.
1571     pub fn jmp_if(&mut self, cc: impl Into<CC>, taken: MachLabel) {
1572         self.emit(Inst::WinchJmpIf {
1573             cc: cc.into(),
1574             taken,
1575         });
1576     }
1577 
1578     /// Performs an unconditional jump to the given label.
1579     pub fn jmp(&mut self, target: MachLabel) {
1580         self.emit(Inst::JmpKnown { dst: target });
1581     }
1582 
1583     /// Emits a jump table sequence.
1584     pub fn jmp_table(
1585         &mut self,
1586         targets: SmallVec<[MachLabel; 4]>,
1587         default: MachLabel,
1588         index: Reg,
1589         tmp1: Reg,
1590         tmp2: Reg,
1591     ) {
1592         self.emit(Inst::JmpTableSeq {
1593             idx: index.into(),
1594             tmp1: Writable::from_reg(tmp1.into()),
1595             tmp2: Writable::from_reg(tmp2.into()),
1596             default_target: default,
1597             targets: Box::new(targets.to_vec()),
1598         })
1599     }
1600 
1601     /// Emit a trap instruction.
1602     pub fn trap(&mut self, code: TrapCode) {
1603         let inst = asm::inst::ud2_zo::new(code).into();
1604         self.emit(Inst::External { inst });
1605     }
1606 
1607     /// Conditional trap.
1608     pub fn trapif(&mut self, cc: impl Into<CC>, trap_code: TrapCode) {
1609         self.emit(Inst::TrapIf {
1610             cc: cc.into(),
1611             trap_code,
1612         });
1613     }
1614 
1615     /// Load effective address.
1616     pub fn lea(&mut self, addr: &Address, dst: WritableReg, size: OperandSize) {
1617         let addr = Self::to_synthetic_amode(addr, MemFlags::trusted());
1618         self.emit(Inst::LoadEffectiveAddress {
1619             addr,
1620             dst: dst.map(Into::into),
1621             size: size.into(),
1622         });
1623     }
1624 
1625     pub fn adc_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1626         let dst = pair_gpr(dst);
1627         let inst = match size {
1628             OperandSize::S8 => asm::inst::adcb_rm::new(dst, src).into(),
1629             OperandSize::S16 => asm::inst::adcw_rm::new(dst, src).into(),
1630             OperandSize::S32 => asm::inst::adcl_rm::new(dst, src).into(),
1631             OperandSize::S64 => asm::inst::adcq_rm::new(dst, src).into(),
1632             OperandSize::S128 => unimplemented!(),
1633         };
1634         self.emit(Inst::External { inst });
1635     }
1636 
1637     pub fn sbb_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1638         let dst = pair_gpr(dst);
1639         let inst = match size {
1640             OperandSize::S8 => asm::inst::sbbb_rm::new(dst, src).into(),
1641             OperandSize::S16 => asm::inst::sbbw_rm::new(dst, src).into(),
1642             OperandSize::S32 => asm::inst::sbbl_rm::new(dst, src).into(),
1643             OperandSize::S64 => asm::inst::sbbq_rm::new(dst, src).into(),
1644             OperandSize::S128 => unimplemented!(),
1645         };
1646         self.emit(Inst::External { inst });
1647     }
1648 
1649     pub fn mul_wide(
1650         &mut self,
1651         dst_lo: WritableReg,
1652         dst_hi: WritableReg,
1653         lhs: Reg,
1654         rhs: Reg,
1655         kind: MulWideKind,
1656         size: OperandSize,
1657     ) {
1658         use MulWideKind::*;
1659         use OperandSize::*;
1660         let rax = asm::Fixed(PairedGpr {
1661             read: lhs.into(),
1662             write: WritableGpr::from_reg(dst_lo.to_reg().into()),
1663         });
1664         let rdx = asm::Fixed(dst_hi.to_reg().into());
1665         if size == S8 {
1666             // For `mulb` and `imulb`, both the high and low bits are written to
1667             // RAX.
1668             assert_eq!(dst_lo, dst_hi);
1669         }
1670         let inst = match (size, kind) {
1671             (S8, Unsigned) => asm::inst::mulb_m::new(rax, rhs).into(),
1672             (S8, Signed) => asm::inst::imulb_m::new(rax, rhs).into(),
1673             (S16, Unsigned) => asm::inst::mulw_m::new(rax, rdx, rhs).into(),
1674             (S16, Signed) => asm::inst::imulw_m::new(rax, rdx, rhs).into(),
1675             (S32, Unsigned) => asm::inst::mull_m::new(rax, rdx, rhs).into(),
1676             (S32, Signed) => asm::inst::imull_m::new(rax, rdx, rhs).into(),
1677             (S64, Unsigned) => asm::inst::mulq_m::new(rax, rdx, rhs).into(),
1678             (S64, Signed) => asm::inst::imulq_m::new(rax, rdx, rhs).into(),
1679             (S128, _) => unimplemented!(),
1680         };
1681         self.emit(Inst::External { inst });
1682     }
1683 
1684     /// Shuffles bytes in `src` according to contents of `mask` and puts
1685     /// result in `dst`.
1686     pub fn xmm_vpshufb_rrm(&mut self, dst: WritableReg, src: Reg, mask: &Address) {
1687         let mask = Self::to_synthetic_amode(mask, MemFlags::trusted());
1688 
1689         self.emit(Inst::XmmRmiRVex {
1690             op: args::AvxOpcode::Vpshufb,
1691             src1: src.into(),
1692             src2: XmmMemImm::unwrap_new(RegMemImm::Mem { addr: mask }),
1693             dst: dst.to_reg().into(),
1694         });
1695     }
1696 
1697     /// Shuffles bytes in `src` according to contents of `mask` and puts
1698     /// result in `dst`.
1699     pub fn xmm_vpshufb_rrr(&mut self, dst: WritableReg, src: Reg, mask: Reg) {
1700         self.emit(Inst::XmmRmiRVex {
1701             op: args::AvxOpcode::Vpshufb,
1702             src1: src.into(),
1703             src2: XmmMemImm::unwrap_new(RegMemImm::reg(mask.into())),
1704             dst: dst.to_reg().into(),
1705         })
1706     }
1707 
1708     /// Bitwise OR of `src1` and `src2`.
1709     pub fn vpor(&mut self, dst: WritableReg, src1: Reg, src2: Reg) {
1710         self.emit(Inst::XmmRmiRVex {
1711             op: args::AvxOpcode::Vpor,
1712             src1: src1.into(),
1713             src2: XmmMemImm::unwrap_new(src2.into()),
1714             dst: dst.to_reg().into(),
1715         })
1716     }
1717 
1718     /// Add unsigned integers with unsigned saturation.
1719     ///
1720     /// Adds the src operands but when an individual byte result is larger than
1721     /// an unsigned byte integer, 0xFF is written instead.
1722     pub fn xmm_vpaddusb_rrm(&mut self, dst: WritableReg, src1: Reg, src2: &Address) {
1723         let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted());
1724 
1725         self.emit(Inst::XmmRmiRVex {
1726             op: args::AvxOpcode::Vpaddusb,
1727             src1: src1.into(),
1728             src2: XmmMemImm::unwrap_new(RegMemImm::mem(src2)),
1729             dst: dst.to_reg().into(),
1730         })
1731     }
1732 
1733     /// Converts an operand size to the appropriate opcode for `vpadd``.
1734     fn xmm_vpadd_opcode(size: OperandSize) -> AvxOpcode {
1735         match size {
1736             OperandSize::S8 => AvxOpcode::Vpaddb,
1737             OperandSize::S32 => AvxOpcode::Vpaddd,
1738             _ => unimplemented!(),
1739         }
1740     }
1741 
1742     pub fn xmm_vpadd_rmr(
1743         &mut self,
1744         src1: Reg,
1745         src2: &Address,
1746         dst: WritableReg,
1747         size: OperandSize,
1748     ) {
1749         let address = Self::to_synthetic_amode(src2, MemFlags::trusted());
1750 
1751         self.emit(Inst::XmmRmiRVex {
1752             op: Self::xmm_vpadd_opcode(size),
1753             src1: src1.into(),
1754             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
1755             dst: dst.to_reg().into(),
1756         });
1757     }
1758 
1759     /// Adds vectors of integers in `src1` and `src2` and puts the results in
1760     /// `dst`.
1761     pub fn xmm_vpadd_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
1762         self.emit(Inst::XmmRmiRVex {
1763             op: Self::xmm_vpadd_opcode(size),
1764             src1: src1.into(),
1765             src2: src2.into(),
1766             dst: dst.to_reg().into(),
1767         });
1768     }
1769 
1770     pub fn mfence(&mut self) {
1771         self.emit(Inst::External {
1772             inst: asm::inst::mfence_zo::new().into(),
1773         });
1774     }
1775 
1776     /// Extract a value from `src` into `dst` (zero extended) determined by `lane`.
1777     pub fn xmm_vpextr_rr(&mut self, dst: WritableReg, src: Reg, lane: u8, size: OperandSize) {
1778         self.emit(Inst::XmmToGprImmVex {
1779             op: Self::vpextr_opcode(size),
1780             src: src.into(),
1781             dst: dst.to_reg().into(),
1782             imm: lane,
1783         });
1784     }
1785 
1786     /// Copy value from `src2`, merge into `src1`, and put result in `dst` at
1787     /// the location specified in `count`.
1788     pub fn xmm_vpinsr_rrm(
1789         &mut self,
1790         dst: WritableReg,
1791         src1: Reg,
1792         src2: &Address,
1793         count: u8,
1794         size: OperandSize,
1795     ) {
1796         let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted());
1797         let dst: WritableXmm = dst.map(|r| r.into());
1798 
1799         let inst = match size {
1800             OperandSize::S8 => asm::inst::vpinsrb_b::new(dst, src1, src2, count).into(),
1801             OperandSize::S16 => asm::inst::vpinsrw_b::new(dst, src1, src2, count).into(),
1802             OperandSize::S32 => asm::inst::vpinsrd_b::new(dst, src1, src2, count).into(),
1803             OperandSize::S64 => asm::inst::vpinsrq_b::new(dst, src1, src2, count).into(),
1804             OperandSize::S128 => unreachable!(),
1805         };
1806         self.emit(Inst::External { inst });
1807     }
1808 
1809     /// Copy value from `src2`, merge into `src1`, and put result in `dst` at
1810     /// the location specified in `count`.
1811     pub fn xmm_vpinsr_rrr(
1812         &mut self,
1813         dst: WritableReg,
1814         src1: Reg,
1815         src2: Reg,
1816         count: u8,
1817         size: OperandSize,
1818     ) {
1819         let dst: WritableXmm = dst.map(|r| r.into());
1820         let inst = match size {
1821             OperandSize::S8 => asm::inst::vpinsrb_b::new(dst, src1, src2, count).into(),
1822             OperandSize::S16 => asm::inst::vpinsrw_b::new(dst, src1, src2, count).into(),
1823             OperandSize::S32 => asm::inst::vpinsrd_b::new(dst, src1, src2, count).into(),
1824             OperandSize::S64 => asm::inst::vpinsrq_b::new(dst, src1, src2, count).into(),
1825             OperandSize::S128 => unreachable!(),
1826         };
1827         self.emit(Inst::External { inst });
1828     }
1829 
1830     /// Copy a 32-bit float in `src2`, merge into `src1`, and put result in `dst`.
1831     pub fn xmm_vinsertps_rrm(&mut self, dst: WritableReg, src1: Reg, src2: &Address, imm: u8) {
1832         let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted());
1833 
1834         self.emit(Inst::XmmRmRImmVex {
1835             op: AvxOpcode::Vinsertps,
1836             src1: src1.into(),
1837             src2: XmmMem::unwrap_new(RegMem::mem(src2)),
1838             dst: dst.to_reg().into(),
1839             imm,
1840         });
1841     }
1842 
1843     /// Copy a 32-bit float in `src2`, merge into `src1`, and put result in `dst`.
1844     pub fn xmm_vinsertps_rrr(&mut self, dst: WritableReg, src1: Reg, src2: Reg, imm: u8) {
1845         self.emit(Inst::XmmRmRImmVex {
1846             op: AvxOpcode::Vinsertps,
1847             src1: src1.into(),
1848             src2: XmmMem::unwrap_new(RegMem::reg(src2.into())),
1849             dst: dst.to_reg().into(),
1850             imm,
1851         });
1852     }
1853 
1854     /// Moves lower 64-bit float from `src2` into lower 64-bits of `dst` and the
1855     /// upper 64-bits in `src1` into the upper 64-bits of `dst`.
1856     pub fn xmm_vmovsd_rrr(&mut self, dst: WritableReg, src1: Reg, src2: Reg) {
1857         self.emit(Inst::XmmRmiRVex {
1858             op: AvxOpcode::Vmovsd,
1859             src1: src1.into(),
1860             src2: XmmMemImm::unwrap_new(src2.into()),
1861             dst: dst.to_reg().into(),
1862         })
1863     }
1864 
1865     /// Moves 64-bit float from `src` into lower 64-bits of `dst`.
1866     /// Zeroes out the upper 64 bits of `dst`.
1867     pub fn xmm_vmovsd_rm(&mut self, dst: WritableReg, src: &Address) {
1868         let src = Self::to_synthetic_amode(src, MemFlags::trusted());
1869 
1870         self.emit(Inst::XmmUnaryRmRVex {
1871             op: AvxOpcode::Vmovsd,
1872             src: XmmMem::unwrap_new(RegMem::mem(src)),
1873             dst: dst.to_reg().into(),
1874         })
1875     }
1876 
1877     /// Moves two 32-bit floats from `src2` to the upper 64-bits of `dst`.
1878     /// Copies two 32-bit floats from the lower 64-bits of `src1` to lower
1879     /// 64-bits of `dst`.
1880     pub fn xmm_vmovlhps_rrm(&mut self, dst: WritableReg, src1: Reg, src2: &Address) {
1881         let src2 = Self::to_synthetic_amode(src2, MemFlags::trusted());
1882         let dst: WritableXmm = dst.map(|r| r.into());
1883         let inst = asm::inst::vmovhps_b::new(dst, src1, src2).into();
1884         self.emit(Inst::External { inst });
1885     }
1886 
1887     /// Moves two 32-bit floats from the lower 64-bits of `src2` to the upper
1888     /// 64-bits of `dst`. Copies two 32-bit floats from the lower 64-bits of
1889     /// `src1` to lower 64-bits of `dst`.
1890     pub fn xmm_vmovlhps_rrr(&mut self, dst: WritableReg, src1: Reg, src2: Reg) {
1891         let dst: WritableXmm = dst.map(|r| r.into());
1892         let inst = asm::inst::vmovlhps_rvm::new(dst, src1, src2).into();
1893         self.emit(Inst::External { inst });
1894     }
1895 
1896     /// Move unaligned packed integer values from address `src` to `dst`.
1897     pub fn xmm_vmovdqu_mr(&mut self, src: &Address, dst: WritableReg, flags: MemFlags) {
1898         let src = Self::to_synthetic_amode(src, flags);
1899         self.emit(Inst::XmmUnaryRmRVex {
1900             op: AvxOpcode::Vmovdqu,
1901             src: XmmMem::unwrap_new(RegMem::mem(src)),
1902             dst: dst.map(Into::into),
1903         });
1904     }
1905 
1906     /// Move integer from `src` to xmm register `dst` using an AVX instruction.
1907     pub fn avx_gpr_to_xmm(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
1908         let dst: WritableXmm = dst.map(|r| r.into());
1909         let inst = match size {
1910             OperandSize::S32 => asm::inst::vmovd_a::new(dst, src).into(),
1911             OperandSize::S64 => asm::inst::vmovq_a::new(dst, src).into(),
1912             _ => unreachable!(),
1913         };
1914 
1915         self.emit(Inst::External { inst });
1916     }
1917 
1918     /// Perform an AVX opcode `op` involving registers `src1` and `src2`, writing the
1919     /// result to `dst`.
1920     pub fn xmm_vex_rr(&mut self, op: AvxOpcode, src1: Reg, src2: Reg, dst: WritableReg) {
1921         self.emit(Inst::XmmRmiRVex {
1922             op,
1923             src1: src1.into(),
1924             src2: src2.into(),
1925             dst: dst.map(Into::into),
1926         })
1927     }
1928 
1929     /// Perform an AVX opcode `op` involving register `src1` and an immediate `imm`, writing the
1930     /// result to `dst`.
1931     pub fn xmm_vex_ri(&mut self, op: AvxOpcode, src1: Reg, imm: u32, dst: WritableReg) {
1932         self.emit(Inst::XmmRmiRVex {
1933             op,
1934             src1: src1.into(),
1935             src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)),
1936             dst: dst.map(Into::into),
1937         })
1938     }
1939 
1940     pub fn xmm_vptest(&mut self, src1: Reg, src2: Reg) {
1941         self.emit(Inst::XmmCmpRmRVex {
1942             op: AvxOpcode::Vptest,
1943             src1: src1.into(),
1944             src2: src2.into(),
1945         })
1946     }
1947 
1948     /// The `vpextr` opcode to use.
1949     fn vpextr_opcode(size: OperandSize) -> AvxOpcode {
1950         match size {
1951             OperandSize::S8 => AvxOpcode::Vpextrb,
1952             OperandSize::S16 => AvxOpcode::Vpextrw,
1953             OperandSize::S32 => AvxOpcode::Vpextrd,
1954             OperandSize::S64 => AvxOpcode::Vpextrq,
1955             _ => unimplemented!(),
1956         }
1957     }
1958 
1959     /// Extract a value from `src` into `addr` determined by `lane`.
1960     pub(crate) fn xmm_vpextr_rm(
1961         &mut self,
1962         addr: &Address,
1963         src: Reg,
1964         lane: u8,
1965         size: OperandSize,
1966         flags: MemFlags,
1967     ) -> anyhow::Result<()> {
1968         assert!(addr.is_offset());
1969         let dst = Self::to_synthetic_amode(addr, flags);
1970 
1971         self.emit(Inst::XmmMovRMImmVex {
1972             op: Self::vpextr_opcode(size),
1973             src: src.into(),
1974             dst,
1975             imm: lane,
1976         });
1977 
1978         Ok(())
1979     }
1980 
1981     /// Converts vector of integers into vector of floating values.
1982     pub fn xmm_vcvt_rr(&mut self, src: Reg, dst: WritableReg, kind: VcvtKind) {
1983         let dst: WritableXmm = dst.map(|x| x.into());
1984         let inst = match kind {
1985             VcvtKind::I32ToF32 => asm::inst::vcvtdq2ps_a::new(dst, src).into(),
1986             VcvtKind::I32ToF64 => asm::inst::vcvtdq2pd_a::new(dst, src).into(),
1987             VcvtKind::F64ToF32 => asm::inst::vcvtpd2ps_a::new(dst, src).into(),
1988             VcvtKind::F64ToI32 => asm::inst::vcvttpd2dq_a::new(dst, src).into(),
1989             VcvtKind::F32ToF64 => asm::inst::vcvtps2pd_a::new(dst, src).into(),
1990             VcvtKind::F32ToI32 => asm::inst::vcvttps2dq_a::new(dst, src).into(),
1991         };
1992 
1993         self.emit(Inst::External { inst });
1994     }
1995 
1996     /// Shift vector data left by `imm`.
1997     pub fn xmm_vpsll_rr(&mut self, src: Reg, dst: WritableReg, imm: u32, size: OperandSize) {
1998         let op = match size {
1999             OperandSize::S32 => AvxOpcode::Vpslld,
2000             OperandSize::S64 => AvxOpcode::Vpsllq,
2001             _ => unimplemented!(),
2002         };
2003 
2004         self.emit(Inst::XmmRmiRVex {
2005             op,
2006             src1: src.into(),
2007             src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)),
2008             dst: dst.to_reg().into(),
2009         });
2010     }
2011 
2012     /// Shift vector data right by `imm`.
2013     pub fn xmm_vpsrl_rr(&mut self, src: Reg, dst: WritableReg, imm: u32, size: OperandSize) {
2014         let op = match size {
2015             OperandSize::S16 => AvxOpcode::Vpsrlw,
2016             OperandSize::S32 => AvxOpcode::Vpsrld,
2017             OperandSize::S64 => AvxOpcode::Vpsrlq,
2018             _ => unimplemented!(),
2019         };
2020 
2021         self.emit(Inst::XmmRmiRVex {
2022             op,
2023             src1: src.into(),
2024             src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)),
2025             dst: dst.to_reg().into(),
2026         })
2027     }
2028 
2029     /// Subtract integers in vector `src1` from integers in vector `src2`.
2030     pub fn xmm_vpsub_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2031         let op = match size {
2032             OperandSize::S32 => AvxOpcode::Vpsubd,
2033             OperandSize::S64 => AvxOpcode::Vpsubq,
2034             _ => unimplemented!(),
2035         };
2036 
2037         self.emit(Inst::XmmRmiRVex {
2038             op,
2039             src1: src1.into(),
2040             src2: src2.into(),
2041             dst: dst.to_reg().into(),
2042         })
2043     }
2044 
2045     fn vaddp_opcode(size: OperandSize) -> AvxOpcode {
2046         match size {
2047             OperandSize::S32 => AvxOpcode::Vaddps,
2048             OperandSize::S64 => AvxOpcode::Vaddpd,
2049             _ => unimplemented!(),
2050         }
2051     }
2052 
2053     /// Add floats in vector `src1` to floats in vector `src2`.
2054     pub fn xmm_vaddp_rrm(
2055         &mut self,
2056         src1: Reg,
2057         src2: &Address,
2058         dst: WritableReg,
2059         size: OperandSize,
2060     ) {
2061         let address = Self::to_synthetic_amode(src2, MemFlags::trusted());
2062 
2063         self.emit(Inst::XmmRmiRVex {
2064             op: Self::vaddp_opcode(size),
2065             src1: src1.into(),
2066             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2067             dst: dst.to_reg().into(),
2068         });
2069     }
2070 
2071     /// Add floats in vector `src1` to floats in vector `src2`.
2072     pub fn xmm_vaddp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2073         self.emit(Inst::XmmRmiRVex {
2074             op: Self::vaddp_opcode(size),
2075             src1: src1.into(),
2076             src2: src2.into(),
2077             dst: dst.to_reg().into(),
2078         })
2079     }
2080 
2081     fn vpcmpeq_opcode(size: OperandSize) -> AvxOpcode {
2082         match size {
2083             OperandSize::S8 => AvxOpcode::Vpcmpeqb,
2084             OperandSize::S16 => AvxOpcode::Vpcmpeqw,
2085             OperandSize::S32 => AvxOpcode::Vpcmpeqd,
2086             OperandSize::S64 => AvxOpcode::Vpcmpeqq,
2087             _ => unimplemented!(),
2088         }
2089     }
2090 
2091     /// Compare vector register `lhs` with a vector of integers in `rhs` for
2092     /// equality between packed integers and write the resulting vector into
2093     /// `dst`.
2094     pub fn xmm_vpcmpeq_rrm(
2095         &mut self,
2096         dst: WritableReg,
2097         lhs: Reg,
2098         address: &Address,
2099         size: OperandSize,
2100     ) {
2101         let address = Self::to_synthetic_amode(address, MemFlags::trusted());
2102 
2103         self.emit(Inst::XmmRmiRVex {
2104             op: Self::vpcmpeq_opcode(size),
2105             src1: lhs.into(),
2106             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2107             dst: dst.to_reg().into(),
2108         });
2109     }
2110 
2111     /// Compare vector registers `lhs` and `rhs` for equality between packed
2112     /// integers and write the resulting vector into `dst`.
2113     pub fn xmm_vpcmpeq_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) {
2114         self.emit(Inst::XmmRmiRVex {
2115             op: Self::vpcmpeq_opcode(size),
2116             src1: lhs.into(),
2117             src2: XmmMemImm::unwrap_new(rhs.into()),
2118             dst: dst.to_reg().into(),
2119         })
2120     }
2121 
2122     /// Performs a greater than comparison with vectors of signed integers in
2123     /// `lhs` and `rhs` and puts the results in `dst`.
2124     pub fn xmm_vpcmpgt_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) {
2125         let op = match size {
2126             OperandSize::S8 => AvxOpcode::Vpcmpgtb,
2127             OperandSize::S16 => AvxOpcode::Vpcmpgtw,
2128             OperandSize::S32 => AvxOpcode::Vpcmpgtd,
2129             OperandSize::S64 => AvxOpcode::Vpcmpgtq,
2130             _ => unimplemented!(),
2131         };
2132 
2133         self.emit(Inst::XmmRmiRVex {
2134             op,
2135             src1: lhs.into(),
2136             src2: XmmMemImm::unwrap_new(rhs.into()),
2137             dst: dst.to_reg().into(),
2138         })
2139     }
2140 
2141     /// Performs a max operation with vectors of signed integers in `lhs` and
2142     /// `rhs` and puts the results in `dst`.
2143     pub fn xmm_vpmaxs_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) {
2144         let op = match size {
2145             OperandSize::S8 => AvxOpcode::Vpmaxsb,
2146             OperandSize::S16 => AvxOpcode::Vpmaxsw,
2147             OperandSize::S32 => AvxOpcode::Vpmaxsd,
2148             _ => unimplemented!(),
2149         };
2150 
2151         self.emit(Inst::XmmRmiRVex {
2152             op,
2153             src1: lhs.into(),
2154             src2: XmmMemImm::unwrap_new(rhs.into()),
2155             dst: dst.to_reg().into(),
2156         })
2157     }
2158 
2159     /// Performs a max operation with vectors of unsigned integers in `lhs` and
2160     /// `rhs` and puts the results in `dst`.
2161     pub fn xmm_vpmaxu_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) {
2162         let op = match size {
2163             OperandSize::S8 => AvxOpcode::Vpmaxub,
2164             OperandSize::S16 => AvxOpcode::Vpmaxuw,
2165             OperandSize::S32 => AvxOpcode::Vpmaxud,
2166             _ => unimplemented!(),
2167         };
2168 
2169         self.emit(Inst::XmmRmiRVex {
2170             op,
2171             src1: lhs.into(),
2172             src2: XmmMemImm::unwrap_new(rhs.into()),
2173             dst: dst.to_reg().into(),
2174         })
2175     }
2176 
2177     /// Performs a min operation with vectors of signed integers in `lhs` and
2178     /// `rhs` and puts the results in `dst`.
2179     pub fn xmm_vpmins_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) {
2180         let op = match size {
2181             OperandSize::S8 => AvxOpcode::Vpminsb,
2182             OperandSize::S16 => AvxOpcode::Vpminsw,
2183             OperandSize::S32 => AvxOpcode::Vpminsd,
2184             _ => unimplemented!(),
2185         };
2186 
2187         self.emit(Inst::XmmRmiRVex {
2188             op,
2189             src1: lhs.into(),
2190             src2: XmmMemImm::unwrap_new(rhs.into()),
2191             dst: dst.to_reg().into(),
2192         })
2193     }
2194 
2195     /// Performs a min operation with vectors of unsigned integers in `lhs` and
2196     /// `rhs` and puts the results in `dst`.
2197     pub fn xmm_vpminu_rrr(&mut self, dst: WritableReg, lhs: Reg, rhs: Reg, size: OperandSize) {
2198         let op = match size {
2199             OperandSize::S8 => AvxOpcode::Vpminub,
2200             OperandSize::S16 => AvxOpcode::Vpminuw,
2201             OperandSize::S32 => AvxOpcode::Vpminud,
2202             _ => unimplemented!(),
2203         };
2204 
2205         self.emit(Inst::XmmRmiRVex {
2206             op,
2207             src1: lhs.into(),
2208             src2: XmmMemImm::unwrap_new(rhs.into()),
2209             dst: dst.to_reg().into(),
2210         })
2211     }
2212 
2213     /// Performs a comparison operation between vectors of floats in `lhs` and
2214     /// `rhs` and puts the results in `dst`.
2215     pub fn xmm_vcmpp_rrr(
2216         &mut self,
2217         dst: WritableReg,
2218         lhs: Reg,
2219         rhs: Reg,
2220         size: OperandSize,
2221         kind: VcmpKind,
2222     ) {
2223         let op = match size {
2224             OperandSize::S32 => AvxOpcode::Vcmpps,
2225             OperandSize::S64 => AvxOpcode::Vcmppd,
2226             _ => unimplemented!(),
2227         };
2228 
2229         self.emit(Inst::XmmRmRImmVex {
2230             op,
2231             src1: lhs.into(),
2232             src2: XmmMem::unwrap_new(rhs.into()),
2233             dst: dst.to_reg().into(),
2234             imm: match kind {
2235                 VcmpKind::Eq => 0,
2236                 VcmpKind::Lt => 1,
2237                 VcmpKind::Le => 2,
2238                 VcmpKind::Unord => 3,
2239                 VcmpKind::Ne => 4,
2240             },
2241         });
2242     }
2243 
2244     /// Takes the lower lanes of vectors of floats in `src1` and `src2` and
2245     /// interleaves them in `dst`.
2246     pub fn xmm_vunpcklp_rrm(
2247         &mut self,
2248         src1: Reg,
2249         src2: &Address,
2250         dst: WritableReg,
2251         size: OperandSize,
2252     ) {
2253         let op = match size {
2254             OperandSize::S32 => AvxOpcode::Vunpcklps,
2255             _ => unimplemented!(),
2256         };
2257 
2258         let address = Self::to_synthetic_amode(src2, MemFlags::trusted());
2259 
2260         self.emit(Inst::XmmRmiRVex {
2261             op,
2262             src1: src1.into(),
2263             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2264             dst: dst.to_reg().into(),
2265         });
2266     }
2267 
2268     /// Performs a subtraction on two vectors of floats and puts the results in
2269     /// `dst`.
2270     pub fn xmm_vsub_rrm(&mut self, src1: Reg, src2: &Address, dst: WritableReg, size: OperandSize) {
2271         let op = match size {
2272             OperandSize::S64 => AvxOpcode::Vsubpd,
2273             _ => unimplemented!(),
2274         };
2275 
2276         let address = Self::to_synthetic_amode(src2, MemFlags::trusted());
2277 
2278         self.emit(Inst::XmmRmiRVex {
2279             op,
2280             src1: src1.into(),
2281             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2282             dst: dst.to_reg().into(),
2283         });
2284     }
2285 
2286     /// Performs a subtraction on two vectors of floats and puts the results in
2287     /// `dst`.
2288     pub fn xmm_vsub_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2289         let op = match size {
2290             OperandSize::S32 => AvxOpcode::Vsubps,
2291             OperandSize::S64 => AvxOpcode::Vsubpd,
2292             _ => unimplemented!(),
2293         };
2294 
2295         self.emit(Inst::XmmRmiRVex {
2296             op,
2297             src1: src1.into(),
2298             src2: src2.into(),
2299             dst: dst.to_reg().into(),
2300         });
2301     }
2302 
2303     /// Converts a vector of signed integers into a vector of narrower integers
2304     /// using saturation to handle overflow.
2305     pub fn xmm_vpackss_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2306         let op = match size {
2307             OperandSize::S8 => AvxOpcode::Vpacksswb,
2308             OperandSize::S16 => AvxOpcode::Vpackssdw,
2309             _ => unimplemented!(),
2310         };
2311 
2312         self.emit(Inst::XmmRmiRVex {
2313             op,
2314             src1: src1.into(),
2315             src2: src2.into(),
2316             dst: dst.to_reg().into(),
2317         });
2318     }
2319 
2320     /// Converts a vector of unsigned integers into a vector of narrower
2321     /// integers using saturation to handle overflow.
2322     pub fn xmm_vpackus_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2323         let op = match size {
2324             OperandSize::S8 => AvxOpcode::Vpackuswb,
2325             OperandSize::S16 => AvxOpcode::Vpackusdw,
2326             _ => unimplemented!(),
2327         };
2328 
2329         self.emit(Inst::XmmRmiRVex {
2330             op,
2331             src1: src1.into(),
2332             src2: src2.into(),
2333             dst: dst.to_reg().into(),
2334         });
2335     }
2336 
2337     /// Concatenates `src1` and `src2` and shifts right by `imm` and puts
2338     /// result in `dst`.
2339     pub fn xmm_vpalignr_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, imm: u8) {
2340         self.emit(Inst::XmmRmRImmVex {
2341             op: AvxOpcode::Vpalignr,
2342             src1: src1.into(),
2343             src2: src2.into(),
2344             dst: dst.to_reg().into(),
2345             imm,
2346         })
2347     }
2348 
2349     /// Unpacks and interleaves the higher lanes of vectors of integers in
2350     /// `src1` and `src2` and puts the results in `dst`.
2351     pub fn xmm_vpunpckh_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2352         let op = match size {
2353             OperandSize::S8 => AvxOpcode::Vpunpckhbw,
2354             OperandSize::S16 => AvxOpcode::Vpunpckhwd,
2355             _ => unimplemented!(),
2356         };
2357 
2358         self.emit(Inst::XmmRmiRVex {
2359             op,
2360             src1: src1.into(),
2361             src2: src2.into(),
2362             dst: dst.to_reg().into(),
2363         });
2364     }
2365 
2366     /// Bitwise logical xor of vectors of floats in `src1` and `src2` and puts
2367     /// the results in `dst`.
2368     pub fn xmm_vxorp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2369         let op = match size {
2370             OperandSize::S32 => AvxOpcode::Vxorps,
2371             OperandSize::S64 => AvxOpcode::Vxorpd,
2372             _ => unimplemented!(),
2373         };
2374 
2375         self.emit(Inst::XmmRmiRVex {
2376             op,
2377             src1: src1.into(),
2378             src2: src2.into(),
2379             dst: dst.to_reg().into(),
2380         });
2381     }
2382 
2383     /// Unpacks and interleaves high order data of floats in `src1` and `src2`
2384     /// and puts the results in `dst`.
2385     pub fn xmm_vunpckhp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2386         let op = match size {
2387             OperandSize::S32 => AvxOpcode::Vunpckhps,
2388             _ => unimplemented!(),
2389         };
2390 
2391         self.emit(Inst::XmmRmiRVex {
2392             op,
2393             src1: src1.into(),
2394             src2: src2.into(),
2395             dst: dst.to_reg().into(),
2396         });
2397     }
2398 
2399     pub(crate) fn xmm_rm_rvex3(
2400         &mut self,
2401         op: Avx512Opcode,
2402         src1: Reg,
2403         src2: Reg,
2404         dst: WritableReg,
2405     ) {
2406         self.emit(Inst::XmmRmREvex3 {
2407             op,
2408             // `src1` reuses `dst`, and is ignored in emission
2409             src1: dst.to_reg().into(),
2410             src2: src1.into(),
2411             src3: src2.into(),
2412             dst: dst.map(Into::into),
2413         });
2414     }
2415 
2416     /// Creates a mask made up of the most significant bit of each byte of
2417     /// `src` and stores the result in `dst`.
2418     pub fn xmm_vpmovmsk_rr(
2419         &mut self,
2420         src: Reg,
2421         dst: WritableReg,
2422         src_size: OperandSize,
2423         dst_size: OperandSize,
2424     ) {
2425         assert_eq!(dst_size, OperandSize::S32);
2426         let dst: WritableGpr = dst.map(|r| r.into());
2427         let inst = match src_size {
2428             OperandSize::S8 => asm::inst::vpmovmskb_rm::new(dst, src).into(),
2429             _ => unimplemented!(),
2430         };
2431 
2432         self.emit(Inst::External { inst });
2433     }
2434 
2435     /// Creates a mask made up of the most significant bit of each byte of
2436     /// in `src` and stores the result in `dst`.
2437     pub fn xmm_vmovskp_rr(
2438         &mut self,
2439         src: Reg,
2440         dst: WritableReg,
2441         src_size: OperandSize,
2442         dst_size: OperandSize,
2443     ) {
2444         assert_eq!(dst_size, OperandSize::S32);
2445         let dst: WritableGpr = dst.map(|r| r.into());
2446         let inst = match src_size {
2447             OperandSize::S32 => asm::inst::vmovmskps_rm::new(dst, src).into(),
2448             OperandSize::S64 => asm::inst::vmovmskpd_rm::new(dst, src).into(),
2449             _ => unimplemented!(),
2450         };
2451 
2452         self.emit(Inst::External { inst });
2453     }
2454 
2455     /// Compute the absolute value of elements in vector `src` and put the
2456     /// results in `dst`.
2457     pub fn xmm_vpabs_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
2458         let op = match size {
2459             OperandSize::S8 => AvxOpcode::Vpabsb,
2460             OperandSize::S16 => AvxOpcode::Vpabsw,
2461             OperandSize::S32 => AvxOpcode::Vpabsd,
2462             _ => unimplemented!(),
2463         };
2464 
2465         self.emit(Inst::XmmUnaryRmRVex {
2466             op,
2467             src: src.into(),
2468             dst: dst.to_reg().into(),
2469         });
2470     }
2471 
2472     /// Arithmetically (sign preserving) right shift on vector in `src` by
2473     /// `imm` with result written to `dst`.
2474     pub fn xmm_vpsra_rri(&mut self, src: Reg, dst: WritableReg, imm: u32, size: OperandSize) {
2475         let op = match size {
2476             OperandSize::S32 => AvxOpcode::Vpsrad,
2477             _ => unimplemented!(),
2478         };
2479 
2480         self.emit(Inst::XmmRmiRVex {
2481             op,
2482             src1: src.into(),
2483             src2: XmmMemImm::unwrap_new(RegMemImm::imm(imm)),
2484             dst: dst.to_reg().into(),
2485         });
2486     }
2487 
2488     fn vandp_opcode(size: OperandSize) -> AvxOpcode {
2489         match size {
2490             OperandSize::S32 => AvxOpcode::Vandps,
2491             OperandSize::S64 => AvxOpcode::Vandpd,
2492             _ => unimplemented!(),
2493         }
2494     }
2495 
2496     /// Perform an `and` operation on vectors of floats in `src1` and `src2`
2497     /// and put the results in `dst`.
2498     pub fn xmm_vandp_rrm(
2499         &mut self,
2500         src1: Reg,
2501         src2: &Address,
2502         dst: WritableReg,
2503         size: OperandSize,
2504     ) {
2505         let address = Self::to_synthetic_amode(src2, MemFlags::trusted());
2506 
2507         self.emit(Inst::XmmRmiRVex {
2508             op: Self::vandp_opcode(size),
2509             src1: src1.into(),
2510             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2511             dst: dst.to_reg().into(),
2512         });
2513     }
2514 
2515     /// Perform an `and` operation on vectors of floats in `src1` and `src2`
2516     /// and put the results in `dst`.
2517     pub fn xmm_vandp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2518         self.emit(Inst::XmmRmiRVex {
2519             op: Self::vandp_opcode(size),
2520             src1: src1.into(),
2521             src2: src2.into(),
2522             dst: dst.to_reg().into(),
2523         });
2524     }
2525 
2526     /// Perform an `and not` operation on vectors of floats in `src1` and
2527     /// `src2` and put the results in `dst`.
2528     pub fn xmm_vandnp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2529         let op = match size {
2530             OperandSize::S32 => AvxOpcode::Vandnps,
2531             OperandSize::S64 => AvxOpcode::Vandnpd,
2532             _ => unimplemented!(),
2533         };
2534 
2535         self.emit(Inst::XmmRmiRVex {
2536             op,
2537             src1: src1.into(),
2538             src2: src2.into(),
2539             dst: dst.to_reg().into(),
2540         });
2541     }
2542 
2543     /// Perform a max operation across two vectors of floats and put the
2544     /// results in `dst`.
2545     pub fn xmm_vmaxp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2546         let op = match size {
2547             OperandSize::S32 => AvxOpcode::Vmaxps,
2548             OperandSize::S64 => AvxOpcode::Vmaxpd,
2549             _ => unimplemented!(),
2550         };
2551 
2552         self.emit(Inst::XmmRmiRVex {
2553             op,
2554             src1: src1.into(),
2555             src2: src2.into(),
2556             dst: dst.to_reg().into(),
2557         });
2558     }
2559 
2560     fn vminp_opcode(size: OperandSize) -> AvxOpcode {
2561         match size {
2562             OperandSize::S32 => AvxOpcode::Vminps,
2563             OperandSize::S64 => AvxOpcode::Vminpd,
2564             _ => unimplemented!(),
2565         }
2566     }
2567 
2568     // Perform a min operation across two vectors of floats and put the
2569     // results in `dst`.
2570     pub fn xmm_vminp_rrm(
2571         &mut self,
2572         src1: Reg,
2573         src2: &Address,
2574         dst: WritableReg,
2575         size: OperandSize,
2576     ) {
2577         let address = Self::to_synthetic_amode(src2, MemFlags::trusted());
2578 
2579         self.emit(Inst::XmmRmiRVex {
2580             op: Self::vminp_opcode(size),
2581             src1: src1.into(),
2582             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2583             dst: dst.to_reg().into(),
2584         });
2585     }
2586 
2587     // Perform a min operation across two vectors of floats and put the
2588     // results in `dst`.
2589     pub fn xmm_vminp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2590         self.emit(Inst::XmmRmiRVex {
2591             op: Self::vminp_opcode(size),
2592             src1: src1.into(),
2593             src2: src2.into(),
2594             dst: dst.to_reg().into(),
2595         });
2596     }
2597 
2598     // Round a vector of floats.
2599     pub fn xmm_vroundp_rri(
2600         &mut self,
2601         src: Reg,
2602         dst: WritableReg,
2603         mode: VroundMode,
2604         size: OperandSize,
2605     ) {
2606         let dst: WritableXmm = dst.map(|r| r.into());
2607         let imm = match mode {
2608             VroundMode::TowardNearest => 0,
2609             VroundMode::TowardNegativeInfinity => 1,
2610             VroundMode::TowardPositiveInfinity => 2,
2611             VroundMode::TowardZero => 3,
2612         };
2613 
2614         let inst = match size {
2615             OperandSize::S32 => asm::inst::vroundps_rmi::new(dst, src, imm).into(),
2616             OperandSize::S64 => asm::inst::vroundpd_rmi::new(dst, src, imm).into(),
2617             _ => unimplemented!(),
2618         };
2619 
2620         self.emit(Inst::External { inst });
2621     }
2622 
2623     /// Shuffle of vectors of floats.
2624     pub fn xmm_vshufp_rrri(
2625         &mut self,
2626         src1: Reg,
2627         src2: Reg,
2628         dst: WritableReg,
2629         imm: u8,
2630         size: OperandSize,
2631     ) {
2632         let op = match size {
2633             OperandSize::S32 => AvxOpcode::Vshufps,
2634             _ => unimplemented!(),
2635         };
2636 
2637         self.emit(Inst::XmmRmRImmVex {
2638             op,
2639             src1: src1.into(),
2640             src2: src2.into(),
2641             dst: dst.to_reg().into(),
2642             imm,
2643         });
2644     }
2645 
2646     /// Each lane in `src1` is multiplied by the corresponding lane in `src2`
2647     /// producing intermediate 32-bit operands. Each intermediate 32-bit
2648     /// operand is truncated to 18 most significant bits. Rounding is performed
2649     /// by adding 1 to the least significant bit of the 18-bit intermediate
2650     /// result. The 16 bits immediately to the right of the most significant
2651     /// bit of each 18-bit intermediate result is placed in each lane of `dst`.
2652     pub fn xmm_vpmulhrs_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2653         let op = match size {
2654             OperandSize::S16 => AvxOpcode::Vpmulhrsw,
2655             _ => unimplemented!(),
2656         };
2657 
2658         self.emit(Inst::XmmRmiRVex {
2659             op,
2660             src1: src1.into(),
2661             src2: src2.into(),
2662             dst: dst.to_reg().into(),
2663         });
2664     }
2665 
2666     /// Performs a bitwise `and` operation on the vectors in `src1` and `src2`
2667     /// and stores the results in `dst`.
2668     pub fn xmm_vpand_rrm(&mut self, src1: Reg, src2: &Address, dst: WritableReg) {
2669         let address = Self::to_synthetic_amode(&src2, MemFlags::trusted());
2670 
2671         self.emit(Inst::XmmRmiRVex {
2672             op: AvxOpcode::Vpand,
2673             src1: src1.into(),
2674             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2675             dst: dst.to_reg().into(),
2676         });
2677     }
2678 
2679     /// Perform an average operation for the vector of unsigned integers in
2680     /// `src1` and `src2` and put the results in `dst`.
2681     pub fn xmm_vpavg_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2682         let op = match size {
2683             OperandSize::S8 => AvxOpcode::Vpavgb,
2684             OperandSize::S16 => AvxOpcode::Vpavgw,
2685             _ => unimplemented!(),
2686         };
2687 
2688         self.emit(Inst::XmmRmiRVex {
2689             op,
2690             src1: src1.into(),
2691             src2: src2.into(),
2692             dst: dst.to_reg().into(),
2693         });
2694     }
2695 
2696     /// Perform an or operation for the vectors of floats in `src1` and `src2`
2697     /// and put the results in `dst`.
2698     pub fn xmm_vorp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2699         let op = match size {
2700             OperandSize::S32 => AvxOpcode::Vorps,
2701             OperandSize::S64 => AvxOpcode::Vorpd,
2702             _ => unimplemented!(),
2703         };
2704 
2705         self.emit(Inst::XmmRmiRVex {
2706             op,
2707             src1: src1.into(),
2708             src2: src2.into(),
2709             dst: dst.to_reg().into(),
2710         });
2711     }
2712 
2713     /// Divide the vector of floats in `src1` by the vector of floats in `src2`
2714     /// and put the results in `dst`.
2715     pub fn xmm_vdivp_rrr(&mut self, src1: Reg, src2: Reg, dst: WritableReg, size: OperandSize) {
2716         let op = match size {
2717             OperandSize::S32 => AvxOpcode::Vdivps,
2718             OperandSize::S64 => AvxOpcode::Vdivpd,
2719             _ => unimplemented!(),
2720         };
2721 
2722         self.emit(Inst::XmmRmiRVex {
2723             op,
2724             src1: src1.into(),
2725             src2: src2.into(),
2726             dst: dst.to_reg().into(),
2727         });
2728     }
2729 
2730     /// Compute square roots of vector of floats in `src` and put the results
2731     /// in `dst`.
2732     pub fn xmm_vsqrtp_rr(&mut self, src: Reg, dst: WritableReg, size: OperandSize) {
2733         let op = match size {
2734             OperandSize::S32 => AvxOpcode::Vsqrtps,
2735             OperandSize::S64 => AvxOpcode::Vsqrtpd,
2736             _ => unimplemented!(),
2737         };
2738 
2739         self.emit(Inst::XmmUnaryRmRVex {
2740             op,
2741             src: src.into(),
2742             dst: dst.to_reg().into(),
2743         });
2744     }
2745 
2746     /// Multiply and add packed signed and unsigned bytes.
2747     pub fn xmm_vpmaddubs_rmr(
2748         &mut self,
2749         src: Reg,
2750         address: &Address,
2751         dst: WritableReg,
2752         size: OperandSize,
2753     ) {
2754         let address = Self::to_synthetic_amode(address, MemFlags::trusted());
2755 
2756         let op = match size {
2757             OperandSize::S16 => AvxOpcode::Vpmaddubsw,
2758             _ => unimplemented!(),
2759         };
2760 
2761         self.emit(Inst::XmmRmiRVex {
2762             op,
2763             src1: src.into(),
2764             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2765             dst: dst.to_reg().into(),
2766         });
2767     }
2768 
2769     /// Multiple and add packed integers.
2770     pub fn xmm_vpmaddwd_rmr(&mut self, src: Reg, address: &Address, dst: WritableReg) {
2771         let address = Self::to_synthetic_amode(address, MemFlags::trusted());
2772 
2773         self.emit(Inst::XmmRmiRVex {
2774             op: AvxOpcode::Vpmaddwd,
2775             src1: src.into(),
2776             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2777             dst: dst.to_reg().into(),
2778         })
2779     }
2780 
2781     /// Perform a logical on vector in `src` and in `address` and put the
2782     /// results in `dst`.
2783     pub fn xmm_vpxor_rmr(&mut self, src: Reg, address: &Address, dst: WritableReg) {
2784         let address = Self::to_synthetic_amode(address, MemFlags::trusted());
2785 
2786         self.emit(Inst::XmmRmiRVex {
2787             op: AvxOpcode::Vpxor,
2788             src1: src.into(),
2789             src2: XmmMemImm::unwrap_new(RegMemImm::mem(address)),
2790             dst: dst.to_reg().into(),
2791         })
2792     }
2793 }
2794 
2795 /// Captures the region in a MachBuffer where an add-with-immediate instruction would be emitted,
2796 /// but the immediate is not yet known. Currently, this implementation expects a 32-bit immediate,
2797 /// so 8 and 16 bit operand sizes are not supported.
2798 pub(crate) struct PatchableAddToReg {
2799     /// The region to be patched in the [`MachBuffer`]. It must contain a valid add instruction
2800     /// sequence, accepting a 32-bit immediate.
2801     region: PatchRegion,
2802 
2803     /// The offset into the patchable region where the patchable constant begins.
2804     constant_offset: usize,
2805 }
2806 
2807 impl PatchableAddToReg {
2808     /// Create a new [`PatchableAddToReg`] by capturing a region in the output buffer where the
2809     /// add-with-immediate occurs. The [`MachBuffer`] will have and add-with-immediate instruction
2810     /// present in that region, though it will add `0` until the `::finalize` method is called.
2811     ///
2812     /// Currently this implementation expects to be able to patch a 32-bit immediate, which means
2813     /// that 8 and 16-bit addition cannot be supported.
2814     pub(crate) fn new(reg: Reg, size: OperandSize, buf: &mut MachBuffer<Inst>) -> Self {
2815         let open = buf.start_patchable();
2816 
2817         // Emit the opcode and register use for the add instruction.
2818         let start = buf.cur_offset();
2819         Self::add_inst_bytes(reg, size, buf);
2820         let constant_offset = usize::try_from(buf.cur_offset() - start).unwrap();
2821 
2822         // Emit a placeholder for the 32-bit immediate.
2823         buf.put4(0);
2824 
2825         let region = buf.end_patchable(open);
2826 
2827         Self {
2828             region,
2829             constant_offset,
2830         }
2831     }
2832 
2833     /// Generate the prefix of the add instruction (rex byte (depending on register use), opcode,
2834     /// and register reference).
2835     fn add_inst_bytes(reg: Reg, size: OperandSize, buf: &mut MachBuffer<Inst>) {
2836         match size {
2837             OperandSize::S32 | OperandSize::S64 => {}
2838             _ => {
2839                 panic!(
2840                     "{}-bit addition is not supported, please see the comment on PatchableAddToReg::new",
2841                     size.num_bits(),
2842                 )
2843             }
2844         }
2845 
2846         let enc_g = 0;
2847 
2848         debug_assert!(reg.is_int());
2849         let enc_e = u8::try_from(reg.hw_enc()).unwrap();
2850 
2851         RexFlags::from(args::OperandSize::from(size)).emit_two_op(buf, enc_g, enc_e);
2852 
2853         // the opcode for an add
2854         buf.put1(0x81);
2855 
2856         // the modrm byte
2857         buf.put1(encode_modrm(0b11, enc_g & 7, enc_e & 7));
2858     }
2859 
2860     /// Patch the [`MachBuffer`] with the known constant to be added to the register. The final
2861     /// value is passed in as an i32, but the instruction encoding is fixed when
2862     /// [`PatchableAddToReg::new`] is called.
2863     pub(crate) fn finalize(self, val: i32, buffer: &mut MachBuffer<Inst>) {
2864         let slice = self.region.patch(buffer);
2865         debug_assert_eq!(slice.len(), self.constant_offset + 4);
2866         slice[self.constant_offset..].copy_from_slice(val.to_le_bytes().as_slice());
2867     }
2868 }
2869