cranelift_codegen/isa/x64/inst/
mod.rs

1//! This module defines x86_64-specific machine instruction types.
2
3pub use emit_state::EmitState;
4
5use crate::binemit::{Addend, CodeOffset, Reloc};
6use crate::ir::{types, ExternalName, LibCall, TrapCode, Type};
7use crate::isa::x64::abi::X64ABIMachineSpec;
8use crate::isa::x64::inst::regs::{pretty_print_reg, show_ireg_sized};
9use crate::isa::x64::settings as x64_settings;
10use crate::isa::{CallConv, FunctionAlignment};
11use crate::{machinst::*, trace};
12use crate::{settings, CodegenError, CodegenResult};
13use alloc::boxed::Box;
14use alloc::vec::Vec;
15use core::slice;
16use cranelift_assembler_x64 as asm;
17use smallvec::{smallvec, SmallVec};
18use std::fmt::{self, Write};
19use std::string::{String, ToString};
20
21pub mod args;
22mod emit;
23mod emit_state;
24#[cfg(test)]
25mod emit_tests;
26pub mod external;
27pub mod regs;
28mod stack_switch;
29pub mod unwind;
30
31use args::*;
32
33//=============================================================================
34// Instructions (top level): definition
35
36// `Inst` is defined inside ISLE as `MInst`. We publicly re-export it here.
37pub use super::lower::isle::generated_code::AtomicRmwSeqOp;
38pub use super::lower::isle::generated_code::MInst as Inst;
39
40/// Out-of-line data for return-calls, to keep the size of `Inst` down.
41#[derive(Clone, Debug)]
42pub struct ReturnCallInfo<T> {
43    /// Where this call is going.
44    pub dest: T,
45
46    /// The size of the argument area for this return-call, potentially smaller than that of the
47    /// caller, but never larger.
48    pub new_stack_arg_size: u32,
49
50    /// The in-register arguments and their constraints.
51    pub uses: CallArgList,
52
53    /// A temporary for use when moving the return address.
54    pub tmp: WritableGpr,
55}
56
57#[test]
58#[cfg(target_pointer_width = "64")]
59fn inst_size_test() {
60    // This test will help with unintentionally growing the size
61    // of the Inst enum.
62    assert_eq!(48, std::mem::size_of::<Inst>());
63}
64
65pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
66    let xs = x as i64;
67    xs == ((xs << 32) >> 32)
68}
69
70impl Inst {
71    /// Retrieve a list of ISA feature sets in which the instruction is available. An empty list
72    /// indicates that the instruction is available in the baseline feature set (i.e. SSE2 and
73    /// below); more than one `InstructionSet` in the list indicates that the instruction is present
74    /// *any* of the included ISA feature sets.
75    fn available_in_any_isa(&self) -> SmallVec<[InstructionSet; 2]> {
76        match self {
77            // These instructions are part of SSE2, which is a basic requirement in Cranelift, and
78            // don't have to be checked.
79            Inst::AtomicRmwSeq { .. }
80            | Inst::Bswap { .. }
81            | Inst::CallKnown { .. }
82            | Inst::CallUnknown { .. }
83            | Inst::ReturnCallKnown { .. }
84            | Inst::ReturnCallUnknown { .. }
85            | Inst::CheckedSRemSeq { .. }
86            | Inst::CheckedSRemSeq8 { .. }
87            | Inst::Cmove { .. }
88            | Inst::CmpRmiR { .. }
89            | Inst::CvtFloatToSintSeq { .. }
90            | Inst::CvtFloatToUintSeq { .. }
91            | Inst::CvtUint64ToFloatSeq { .. }
92            | Inst::Div { .. }
93            | Inst::Div8 { .. }
94            | Inst::Fence { .. }
95            | Inst::Hlt
96            | Inst::Imm { .. }
97            | Inst::JmpCond { .. }
98            | Inst::JmpCondOr { .. }
99            | Inst::WinchJmpIf { .. }
100            | Inst::JmpKnown { .. }
101            | Inst::JmpTableSeq { .. }
102            | Inst::JmpUnknown { .. }
103            | Inst::LoadEffectiveAddress { .. }
104            | Inst::LoadExtName { .. }
105            | Inst::LockCmpxchg { .. }
106            | Inst::LockXadd { .. }
107            | Inst::Xchg { .. }
108            | Inst::Mov64MR { .. }
109            | Inst::MovImmM { .. }
110            | Inst::MovRM { .. }
111            | Inst::MovRR { .. }
112            | Inst::MovFromPReg { .. }
113            | Inst::MovToPReg { .. }
114            | Inst::MovsxRmR { .. }
115            | Inst::MovzxRmR { .. }
116            | Inst::Mul { .. }
117            | Inst::Mul8 { .. }
118            | Inst::IMul { .. }
119            | Inst::IMulImm { .. }
120            | Inst::Neg { .. }
121            | Inst::Not { .. }
122            | Inst::Nop { .. }
123            | Inst::Pop64 { .. }
124            | Inst::Push64 { .. }
125            | Inst::StackProbeLoop { .. }
126            | Inst::Args { .. }
127            | Inst::Rets { .. }
128            | Inst::Ret { .. }
129            | Inst::Setcc { .. }
130            | Inst::ShiftR { .. }
131            | Inst::SignExtendData { .. }
132            | Inst::StackSwitchBasic { .. }
133            | Inst::TrapIf { .. }
134            | Inst::TrapIfAnd { .. }
135            | Inst::TrapIfOr { .. }
136            | Inst::Ud2 { .. }
137            | Inst::XmmCmove { .. }
138            | Inst::XmmCmpRmR { .. }
139            | Inst::XmmMinMaxSeq { .. }
140            | Inst::XmmUninitializedValue { .. }
141            | Inst::GprUninitializedValue { .. }
142            | Inst::ElfTlsGetAddr { .. }
143            | Inst::MachOTlsGetAddr { .. }
144            | Inst::CoffTlsGetAddr { .. }
145            | Inst::Unwind { .. }
146            | Inst::DummyUse { .. } => smallvec![],
147
148            Inst::LockCmpxchg16b { .. }
149            | Inst::Atomic128RmwSeq { .. }
150            | Inst::Atomic128XchgSeq { .. } => smallvec![InstructionSet::CMPXCHG16b],
151
152            Inst::AluRmRVex { op, .. } => op.available_from(),
153            Inst::UnaryRmR { op, .. } => op.available_from(),
154            Inst::UnaryRmRVex { op, .. } => op.available_from(),
155            Inst::UnaryRmRImmVex { op, .. } => op.available_from(),
156
157            // These use dynamic SSE opcodes.
158            Inst::GprToXmm { op, .. }
159            | Inst::XmmMovRM { op, .. }
160            | Inst::XmmMovRMImm { op, .. }
161            | Inst::XmmRmiReg { opcode: op, .. }
162            | Inst::XmmRmR { op, .. }
163            | Inst::XmmRmRUnaligned { op, .. }
164            | Inst::XmmRmRBlend { op, .. }
165            | Inst::XmmRmRImm { op, .. }
166            | Inst::XmmToGpr { op, .. }
167            | Inst::XmmToGprImm { op, .. }
168            | Inst::XmmUnaryRmRImm { op, .. }
169            | Inst::XmmUnaryRmRUnaligned { op, .. }
170            | Inst::XmmUnaryRmR { op, .. }
171            | Inst::CvtIntToFloat { op, .. } => smallvec![op.available_from()],
172
173            Inst::XmmUnaryRmREvex { op, .. }
174            | Inst::XmmRmREvex { op, .. }
175            | Inst::XmmRmREvex3 { op, .. }
176            | Inst::XmmUnaryRmRImmEvex { op, .. } => op.available_from(),
177
178            Inst::XmmRmiRVex { op, .. }
179            | Inst::XmmRmRVex3 { op, .. }
180            | Inst::XmmRmRImmVex { op, .. }
181            | Inst::XmmRmRBlendVex { op, .. }
182            | Inst::XmmVexPinsr { op, .. }
183            | Inst::XmmUnaryRmRVex { op, .. }
184            | Inst::XmmUnaryRmRImmVex { op, .. }
185            | Inst::XmmMovRMVex { op, .. }
186            | Inst::XmmMovRMImmVex { op, .. }
187            | Inst::XmmToGprImmVex { op, .. }
188            | Inst::XmmToGprVex { op, .. }
189            | Inst::GprToXmmVex { op, .. }
190            | Inst::CvtIntToFloatVex { op, .. }
191            | Inst::XmmCmpRmRVex { op, .. } => op.available_from(),
192
193            Inst::MulX { .. } => smallvec![InstructionSet::BMI2],
194
195            Inst::External { inst } => {
196                use cranelift_assembler_x64::Feature::*;
197                let mut features = smallvec![];
198                for f in inst.features() {
199                    match f {
200                        _64b | compat => {}
201                        sse => features.push(InstructionSet::SSE),
202                        sse2 => features.push(InstructionSet::SSE2),
203                    }
204                }
205                features
206            }
207        }
208    }
209}
210
211// Handy constructors for Insts.
212
213impl Inst {
214    pub(crate) fn nop(len: u8) -> Self {
215        debug_assert!(len <= 15);
216        Self::Nop { len }
217    }
218
219    pub(crate) fn addq_mi(dst: Writable<Reg>, simm32: i32) -> Self {
220        let inst = if let Ok(simm8) = i8::try_from(simm32) {
221            asm::inst::addq_mi_sxb::new(dst, simm8).into()
222        } else {
223            asm::inst::addq_mi_sxl::new(dst, simm32).into()
224        };
225        Inst::External { inst }
226    }
227
228    pub(crate) fn subq_mi(dst: Writable<Reg>, simm32: i32) -> Self {
229        let inst = if let Ok(simm8) = i8::try_from(simm32) {
230            asm::inst::subq_mi_sxb::new(dst, simm8).into()
231        } else {
232            asm::inst::subq_mi_sxl::new(dst, simm32).into()
233        };
234        Inst::External { inst }
235    }
236
237    #[allow(dead_code)]
238    pub(crate) fn unary_rm_r(
239        size: OperandSize,
240        op: UnaryRmROpcode,
241        src: RegMem,
242        dst: Writable<Reg>,
243    ) -> Self {
244        src.assert_regclass_is(RegClass::Int);
245        debug_assert!(dst.to_reg().class() == RegClass::Int);
246        debug_assert!(size.is_one_of(&[
247            OperandSize::Size16,
248            OperandSize::Size32,
249            OperandSize::Size64
250        ]));
251        Self::UnaryRmR {
252            size,
253            op,
254            src: GprMem::unwrap_new(src),
255            dst: WritableGpr::from_writable_reg(dst).unwrap(),
256        }
257    }
258
259    pub(crate) fn not(size: OperandSize, src: Writable<Reg>) -> Inst {
260        debug_assert_eq!(src.to_reg().class(), RegClass::Int);
261        Inst::Not {
262            size,
263            src: Gpr::unwrap_new(src.to_reg()),
264            dst: WritableGpr::from_writable_reg(src).unwrap(),
265        }
266    }
267
268    pub(crate) fn div(
269        size: OperandSize,
270        sign: DivSignedness,
271        trap: TrapCode,
272        divisor: RegMem,
273        dividend_lo: Gpr,
274        dividend_hi: Gpr,
275        dst_quotient: WritableGpr,
276        dst_remainder: WritableGpr,
277    ) -> Inst {
278        divisor.assert_regclass_is(RegClass::Int);
279        Inst::Div {
280            size,
281            sign,
282            trap,
283            divisor: GprMem::unwrap_new(divisor),
284            dividend_lo,
285            dividend_hi,
286            dst_quotient,
287            dst_remainder,
288        }
289    }
290
291    pub(crate) fn div8(
292        sign: DivSignedness,
293        trap: TrapCode,
294        divisor: RegMem,
295        dividend: Gpr,
296        dst: WritableGpr,
297    ) -> Inst {
298        divisor.assert_regclass_is(RegClass::Int);
299        Inst::Div8 {
300            sign,
301            trap,
302            divisor: GprMem::unwrap_new(divisor),
303            dividend,
304            dst,
305        }
306    }
307
308    pub(crate) fn imm(dst_size: OperandSize, simm64: u64, dst: Writable<Reg>) -> Inst {
309        debug_assert!(dst_size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
310        debug_assert!(dst.to_reg().class() == RegClass::Int);
311        // Try to generate a 32-bit immediate when the upper high bits are zeroed (which matches
312        // the semantics of movl).
313        let dst_size = match dst_size {
314            OperandSize::Size64 if simm64 > u32::max_value() as u64 => OperandSize::Size64,
315            _ => OperandSize::Size32,
316        };
317        Inst::Imm {
318            dst_size,
319            simm64,
320            dst: WritableGpr::from_writable_reg(dst).unwrap(),
321        }
322    }
323
324    pub(crate) fn mov_r_r(size: OperandSize, src: Reg, dst: Writable<Reg>) -> Inst {
325        debug_assert!(size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
326        debug_assert!(src.class() == RegClass::Int);
327        debug_assert!(dst.to_reg().class() == RegClass::Int);
328        let src = Gpr::unwrap_new(src);
329        let dst = WritableGpr::from_writable_reg(dst).unwrap();
330        Inst::MovRR { size, src, dst }
331    }
332
333    /// Convenient helper for unary float operations.
334    pub(crate) fn xmm_unary_rm_r(op: SseOpcode, src: RegMem, dst: Writable<Reg>) -> Inst {
335        src.assert_regclass_is(RegClass::Float);
336        debug_assert!(dst.to_reg().class() == RegClass::Float);
337        Inst::XmmUnaryRmR {
338            op,
339            src: XmmMemAligned::unwrap_new(src),
340            dst: WritableXmm::from_writable_reg(dst).unwrap(),
341        }
342    }
343
344    pub(crate) fn xmm_rm_r(op: SseOpcode, src: RegMem, dst: Writable<Reg>) -> Self {
345        src.assert_regclass_is(RegClass::Float);
346        debug_assert!(dst.to_reg().class() == RegClass::Float);
347        Inst::XmmRmR {
348            op,
349            src1: Xmm::unwrap_new(dst.to_reg()),
350            src2: XmmMemAligned::unwrap_new(src),
351            dst: WritableXmm::from_writable_reg(dst).unwrap(),
352        }
353    }
354
355    #[cfg(test)]
356    pub(crate) fn xmm_rmr_vex3(op: AvxOpcode, src3: RegMem, src2: Reg, dst: Writable<Reg>) -> Self {
357        src3.assert_regclass_is(RegClass::Float);
358        debug_assert!(src2.class() == RegClass::Float);
359        debug_assert!(dst.to_reg().class() == RegClass::Float);
360        Inst::XmmRmRVex3 {
361            op,
362            src3: XmmMem::unwrap_new(src3),
363            src2: Xmm::unwrap_new(src2),
364            src1: Xmm::unwrap_new(dst.to_reg()),
365            dst: WritableXmm::from_writable_reg(dst).unwrap(),
366        }
367    }
368
369    pub(crate) fn xmm_mov_r_m(op: SseOpcode, src: Reg, dst: impl Into<SyntheticAmode>) -> Inst {
370        debug_assert!(src.class() == RegClass::Float);
371        Inst::XmmMovRM {
372            op,
373            src: Xmm::unwrap_new(src),
374            dst: dst.into(),
375        }
376    }
377
378    pub(crate) fn xmm_to_gpr(
379        op: SseOpcode,
380        src: Reg,
381        dst: Writable<Reg>,
382        dst_size: OperandSize,
383    ) -> Inst {
384        debug_assert!(src.class() == RegClass::Float);
385        debug_assert!(dst.to_reg().class() == RegClass::Int);
386        debug_assert!(dst_size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
387        Inst::XmmToGpr {
388            op,
389            src: Xmm::unwrap_new(src),
390            dst: WritableGpr::from_writable_reg(dst).unwrap(),
391            dst_size,
392        }
393    }
394
395    pub(crate) fn gpr_to_xmm(
396        op: SseOpcode,
397        src: RegMem,
398        src_size: OperandSize,
399        dst: Writable<Reg>,
400    ) -> Inst {
401        src.assert_regclass_is(RegClass::Int);
402        debug_assert!(src_size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
403        debug_assert!(dst.to_reg().class() == RegClass::Float);
404        Inst::GprToXmm {
405            op,
406            src: GprMem::unwrap_new(src),
407            dst: WritableXmm::from_writable_reg(dst).unwrap(),
408            src_size,
409        }
410    }
411
412    pub(crate) fn xmm_cmp_rm_r(op: SseOpcode, src1: Reg, src2: RegMem) -> Inst {
413        src2.assert_regclass_is(RegClass::Float);
414        debug_assert!(src1.class() == RegClass::Float);
415        let src2 = XmmMemAligned::unwrap_new(src2);
416        let src1 = Xmm::unwrap_new(src1);
417        Inst::XmmCmpRmR { op, src1, src2 }
418    }
419
420    #[allow(dead_code)]
421    pub(crate) fn xmm_min_max_seq(
422        size: OperandSize,
423        is_min: bool,
424        lhs: Reg,
425        rhs: Reg,
426        dst: Writable<Reg>,
427    ) -> Inst {
428        debug_assert!(size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
429        debug_assert_eq!(lhs.class(), RegClass::Float);
430        debug_assert_eq!(rhs.class(), RegClass::Float);
431        debug_assert_eq!(dst.to_reg().class(), RegClass::Float);
432        Inst::XmmMinMaxSeq {
433            size,
434            is_min,
435            lhs: Xmm::unwrap_new(lhs),
436            rhs: Xmm::unwrap_new(rhs),
437            dst: WritableXmm::from_writable_reg(dst).unwrap(),
438        }
439    }
440
441    pub(crate) fn movzx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
442        src.assert_regclass_is(RegClass::Int);
443        debug_assert!(dst.to_reg().class() == RegClass::Int);
444        let src = GprMem::unwrap_new(src);
445        let dst = WritableGpr::from_writable_reg(dst).unwrap();
446        Inst::MovzxRmR { ext_mode, src, dst }
447    }
448
449    pub(crate) fn movsx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
450        src.assert_regclass_is(RegClass::Int);
451        debug_assert!(dst.to_reg().class() == RegClass::Int);
452        let src = GprMem::unwrap_new(src);
453        let dst = WritableGpr::from_writable_reg(dst).unwrap();
454        Inst::MovsxRmR { ext_mode, src, dst }
455    }
456
457    pub(crate) fn mov64_m_r(src: impl Into<SyntheticAmode>, dst: Writable<Reg>) -> Inst {
458        debug_assert!(dst.to_reg().class() == RegClass::Int);
459        Inst::Mov64MR {
460            src: src.into(),
461            dst: WritableGpr::from_writable_reg(dst).unwrap(),
462        }
463    }
464
465    pub(crate) fn mov_r_m(size: OperandSize, src: Reg, dst: impl Into<SyntheticAmode>) -> Inst {
466        debug_assert!(src.class() == RegClass::Int);
467        Inst::MovRM {
468            size,
469            src: Gpr::unwrap_new(src),
470            dst: dst.into(),
471        }
472    }
473
474    pub(crate) fn lea(addr: impl Into<SyntheticAmode>, dst: Writable<Reg>) -> Inst {
475        debug_assert!(dst.to_reg().class() == RegClass::Int);
476        Inst::LoadEffectiveAddress {
477            addr: addr.into(),
478            dst: WritableGpr::from_writable_reg(dst).unwrap(),
479            size: OperandSize::Size64,
480        }
481    }
482
483    pub(crate) fn shift_r(
484        size: OperandSize,
485        kind: ShiftKind,
486        num_bits: Imm8Gpr,
487        src: Reg,
488        dst: Writable<Reg>,
489    ) -> Inst {
490        if let &Imm8Reg::Imm8 { imm: num_bits } = num_bits.as_imm8_reg() {
491            debug_assert!(num_bits < size.to_bits());
492        }
493        debug_assert!(dst.to_reg().class() == RegClass::Int);
494        Inst::ShiftR {
495            size,
496            kind,
497            src: Gpr::unwrap_new(src),
498            num_bits,
499            dst: WritableGpr::from_writable_reg(dst).unwrap(),
500        }
501    }
502
503    /// Does a comparison of dst - src for operands of size `size`, as stated by the machine
504    /// instruction semantics. Be careful with the order of parameters!
505    pub(crate) fn cmp_rmi_r(size: OperandSize, src1: Reg, src2: RegMemImm) -> Inst {
506        src2.assert_regclass_is(RegClass::Int);
507        debug_assert_eq!(src1.class(), RegClass::Int);
508        Inst::CmpRmiR {
509            size,
510            src1: Gpr::unwrap_new(src1),
511            src2: GprMemImm::unwrap_new(src2),
512            opcode: CmpOpcode::Cmp,
513        }
514    }
515
516    pub(crate) fn trap(trap_code: TrapCode) -> Inst {
517        Inst::Ud2 { trap_code }
518    }
519
520    pub(crate) fn trap_if(cc: CC, trap_code: TrapCode) -> Inst {
521        Inst::TrapIf { cc, trap_code }
522    }
523
524    pub(crate) fn cmove(size: OperandSize, cc: CC, src: RegMem, dst: Writable<Reg>) -> Inst {
525        debug_assert!(size.is_one_of(&[
526            OperandSize::Size16,
527            OperandSize::Size32,
528            OperandSize::Size64
529        ]));
530        debug_assert!(dst.to_reg().class() == RegClass::Int);
531        Inst::Cmove {
532            size,
533            cc,
534            consequent: GprMem::unwrap_new(src),
535            alternative: Gpr::unwrap_new(dst.to_reg()),
536            dst: WritableGpr::from_writable_reg(dst).unwrap(),
537        }
538    }
539
540    pub(crate) fn push64(src: RegMemImm) -> Inst {
541        src.assert_regclass_is(RegClass::Int);
542        let src = GprMemImm::unwrap_new(src);
543        Inst::Push64 { src }
544    }
545
546    pub(crate) fn pop64(dst: Writable<Reg>) -> Inst {
547        debug_assert!(dst.to_reg().class() == RegClass::Int);
548        let dst = WritableGpr::from_writable_reg(dst).unwrap();
549        Inst::Pop64 { dst }
550    }
551
552    pub(crate) fn call_known(info: Box<CallInfo<ExternalName>>) -> Inst {
553        Inst::CallKnown { info }
554    }
555
556    pub(crate) fn call_unknown(info: Box<CallInfo<RegMem>>) -> Inst {
557        info.dest.assert_regclass_is(RegClass::Int);
558        Inst::CallUnknown { info }
559    }
560
561    pub(crate) fn ret(stack_bytes_to_pop: u32) -> Inst {
562        Inst::Ret { stack_bytes_to_pop }
563    }
564
565    pub(crate) fn jmp_known(dst: MachLabel) -> Inst {
566        Inst::JmpKnown { dst }
567    }
568
569    pub(crate) fn jmp_unknown(target: RegMem) -> Inst {
570        target.assert_regclass_is(RegClass::Int);
571        Inst::JmpUnknown { target }
572    }
573
574    /// Choose which instruction to use for loading a register value from memory. For loads smaller
575    /// than 64 bits, this method expects a way to extend the value (i.e. [ExtKind::SignExtend],
576    /// [ExtKind::ZeroExtend]); loads with no extension necessary will ignore this.
577    pub(crate) fn load(
578        ty: Type,
579        from_addr: impl Into<SyntheticAmode>,
580        to_reg: Writable<Reg>,
581        ext_kind: ExtKind,
582    ) -> Inst {
583        let rc = to_reg.to_reg().class();
584        match rc {
585            RegClass::Int => {
586                let ext_mode = match ty.bytes() {
587                    1 => Some(ExtMode::BQ),
588                    2 => Some(ExtMode::WQ),
589                    4 => Some(ExtMode::LQ),
590                    8 => None,
591                    _ => unreachable!("the type should never use a scalar load: {}", ty),
592                };
593                if let Some(ext_mode) = ext_mode {
594                    // Values smaller than 64 bits must be extended in some way.
595                    match ext_kind {
596                        ExtKind::SignExtend => {
597                            Inst::movsx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg)
598                        }
599                        ExtKind::ZeroExtend => {
600                            Inst::movzx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg)
601                        }
602                        ExtKind::None => {
603                            panic!("expected an extension kind for extension mode: {ext_mode:?}")
604                        }
605                    }
606                } else {
607                    // 64-bit values can be moved directly.
608                    Inst::mov64_m_r(from_addr, to_reg)
609                }
610            }
611            RegClass::Float => {
612                let opcode = match ty {
613                    types::F16 | types::I8X2 => {
614                        panic!("loading a f16 or i8x2 requires multiple instructions")
615                    }
616                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 32 => SseOpcode::Movss,
617                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 64 => SseOpcode::Movsd,
618                    types::F32X4 => SseOpcode::Movups,
619                    types::F64X2 => SseOpcode::Movupd,
620                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 128 => SseOpcode::Movdqu,
621                    _ => unimplemented!("unable to load type: {}", ty),
622                };
623                Inst::xmm_unary_rm_r(opcode, RegMem::mem(from_addr), to_reg)
624            }
625            RegClass::Vector => unreachable!(),
626        }
627    }
628
629    /// Choose which instruction to use for storing a register value to memory.
630    pub(crate) fn store(ty: Type, from_reg: Reg, to_addr: impl Into<SyntheticAmode>) -> Inst {
631        let rc = from_reg.class();
632        match rc {
633            RegClass::Int => Inst::mov_r_m(OperandSize::from_ty(ty), from_reg, to_addr),
634            RegClass::Float => {
635                let opcode = match ty {
636                    types::F16 | types::I8X2 => {
637                        panic!("storing a f16 or i8x2 requires multiple instructions")
638                    }
639                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 32 => SseOpcode::Movss,
640                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 64 => SseOpcode::Movsd,
641                    types::F32X4 => SseOpcode::Movups,
642                    types::F64X2 => SseOpcode::Movupd,
643                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 128 => SseOpcode::Movdqu,
644                    _ => unimplemented!("unable to store type: {}", ty),
645                };
646                Inst::xmm_mov_r_m(opcode, from_reg, to_addr)
647            }
648            RegClass::Vector => unreachable!(),
649        }
650    }
651}
652
653//=============================================================================
654// Instructions: printing
655
656impl PrettyPrint for Inst {
657    fn pretty_print(&self, _size: u8) -> String {
658        fn ljustify(s: String) -> String {
659            let w = 7;
660            if s.len() >= w {
661                s
662            } else {
663                let need = usize::min(w, w - s.len());
664                s + &format!("{nil: <width$}", nil = "", width = need)
665            }
666        }
667
668        fn ljustify2(s1: String, s2: String) -> String {
669            ljustify(s1 + &s2)
670        }
671
672        fn suffix_lq(size: OperandSize) -> String {
673            match size {
674                OperandSize::Size32 => "l",
675                OperandSize::Size64 => "q",
676                _ => unreachable!(),
677            }
678            .to_string()
679        }
680
681        #[allow(dead_code)]
682        fn suffix_lqb(size: OperandSize) -> String {
683            match size {
684                OperandSize::Size32 => "l",
685                OperandSize::Size64 => "q",
686                _ => unreachable!(),
687            }
688            .to_string()
689        }
690
691        fn suffix_bwlq(size: OperandSize) -> String {
692            match size {
693                OperandSize::Size8 => "b".to_string(),
694                OperandSize::Size16 => "w".to_string(),
695                OperandSize::Size32 => "l".to_string(),
696                OperandSize::Size64 => "q".to_string(),
697            }
698        }
699
700        match self {
701            Inst::Nop { len } => format!("{} len={}", ljustify("nop".to_string()), len),
702
703            Inst::AluRmRVex {
704                size,
705                op,
706                src1,
707                src2,
708                dst,
709            } => {
710                let size_bytes = size.to_bytes();
711                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
712                let src1 = pretty_print_reg(src1.to_reg(), size_bytes);
713                let src2 = src2.pretty_print(size_bytes);
714                let op = ljustify2(op.to_string(), String::new());
715                format!("{op} {src2}, {src1}, {dst}")
716            }
717            Inst::UnaryRmR { src, dst, op, size } => {
718                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
719                let src = src.pretty_print(size.to_bytes());
720                let op = ljustify2(op.to_string(), suffix_bwlq(*size));
721                format!("{op} {src}, {dst}")
722            }
723
724            Inst::UnaryRmRVex { src, dst, op, size } => {
725                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
726                let src = src.pretty_print(size.to_bytes());
727                let op = ljustify2(op.to_string(), suffix_bwlq(*size));
728                format!("{op} {src}, {dst}")
729            }
730
731            Inst::UnaryRmRImmVex {
732                src,
733                dst,
734                op,
735                size,
736                imm,
737            } => {
738                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
739                let src = src.pretty_print(size.to_bytes());
740                format!(
741                    "{} ${imm}, {src}, {dst}",
742                    ljustify2(op.to_string(), suffix_bwlq(*size))
743                )
744            }
745
746            Inst::Not { size, src, dst } => {
747                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
748                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
749                let op = ljustify2("not".to_string(), suffix_bwlq(*size));
750                format!("{op} {src}, {dst}")
751            }
752
753            Inst::Neg { size, src, dst } => {
754                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
755                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
756                let op = ljustify2("neg".to_string(), suffix_bwlq(*size));
757                format!("{op} {src}, {dst}")
758            }
759
760            Inst::Div {
761                size,
762                sign,
763                trap,
764                divisor,
765                dividend_lo,
766                dividend_hi,
767                dst_quotient,
768                dst_remainder,
769            } => {
770                let divisor = divisor.pretty_print(size.to_bytes());
771                let dividend_lo = pretty_print_reg(dividend_lo.to_reg(), size.to_bytes());
772                let dividend_hi = pretty_print_reg(dividend_hi.to_reg(), size.to_bytes());
773                let dst_quotient =
774                    pretty_print_reg(dst_quotient.to_reg().to_reg(), size.to_bytes());
775                let dst_remainder =
776                    pretty_print_reg(dst_remainder.to_reg().to_reg(), size.to_bytes());
777                let op = ljustify(match sign {
778                    DivSignedness::Signed => "idiv".to_string(),
779                    DivSignedness::Unsigned => "div".to_string(),
780                });
781                format!(
782                    "{op} {dividend_lo}, {dividend_hi}, {divisor}, {dst_quotient}, {dst_remainder} ; trap={trap}"
783                )
784            }
785
786            Inst::Div8 {
787                sign,
788                trap,
789                divisor,
790                dividend,
791                dst,
792            } => {
793                let divisor = divisor.pretty_print(1);
794                let dividend = pretty_print_reg(dividend.to_reg(), 1);
795                let dst = pretty_print_reg(dst.to_reg().to_reg(), 1);
796                let op = ljustify(match sign {
797                    DivSignedness::Signed => "idiv".to_string(),
798                    DivSignedness::Unsigned => "div".to_string(),
799                });
800                format!("{op} {dividend}, {divisor}, {dst} ; trap={trap}")
801            }
802
803            Inst::Mul {
804                size,
805                signed,
806                src1,
807                src2,
808                dst_lo,
809                dst_hi,
810            } => {
811                let src1 = pretty_print_reg(src1.to_reg(), size.to_bytes());
812                let dst_lo = pretty_print_reg(dst_lo.to_reg().to_reg(), size.to_bytes());
813                let dst_hi = pretty_print_reg(dst_hi.to_reg().to_reg(), size.to_bytes());
814                let src2 = src2.pretty_print(size.to_bytes());
815                let suffix = suffix_bwlq(*size);
816                let op = ljustify(if *signed {
817                    format!("imul{suffix}")
818                } else {
819                    format!("mul{suffix}")
820                });
821                format!("{op} {src1}, {src2}, {dst_lo}, {dst_hi}")
822            }
823
824            Inst::MulX {
825                size,
826                src1,
827                src2,
828                dst_lo,
829                dst_hi,
830            } => {
831                let src1 = pretty_print_reg(src1.to_reg(), size.to_bytes());
832                let dst_hi = pretty_print_reg(dst_hi.to_reg().to_reg(), size.to_bytes());
833                let dst_lo = if dst_lo.to_reg().is_invalid_sentinel() {
834                    dst_hi.clone()
835                } else {
836                    pretty_print_reg(dst_lo.to_reg().to_reg(), size.to_bytes())
837                };
838                let src2 = src2.pretty_print(size.to_bytes());
839                let suffix = suffix_bwlq(*size);
840                let op = ljustify(format!("mulx{suffix}"));
841                format!("{op} {src1}, {src2}, {dst_lo}, {dst_hi}")
842            }
843
844            Inst::Mul8 {
845                signed,
846                src1,
847                src2,
848                dst,
849            } => {
850                let src1 = pretty_print_reg(src1.to_reg(), 1);
851                let dst = pretty_print_reg(dst.to_reg().to_reg(), 1);
852                let src2 = src2.pretty_print(1);
853                let op = ljustify(if *signed {
854                    "imulb".to_string()
855                } else {
856                    "mulb".to_string()
857                });
858                format!("{op} {src1}, {src2}, {dst}")
859            }
860
861            Inst::IMul {
862                size,
863                src1,
864                src2,
865                dst,
866            } => {
867                let src1 = pretty_print_reg(src1.to_reg(), size.to_bytes());
868                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
869                let src2 = src2.pretty_print(size.to_bytes());
870                let suffix = suffix_bwlq(*size);
871                let op = ljustify(format!("imul{suffix}"));
872                format!("{op} {src1}, {src2}, {dst}")
873            }
874
875            Inst::IMulImm {
876                size,
877                src1,
878                src2,
879                dst,
880            } => {
881                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
882                let src1 = src1.pretty_print(size.to_bytes());
883                let suffix = suffix_bwlq(*size);
884                let op = ljustify(format!("imul{suffix}"));
885                format!("{op} {src1}, {src2:#x}, {dst}")
886            }
887
888            Inst::CheckedSRemSeq {
889                size,
890                divisor,
891                dividend_lo,
892                dividend_hi,
893                dst_quotient,
894                dst_remainder,
895            } => {
896                let divisor = pretty_print_reg(divisor.to_reg(), size.to_bytes());
897                let dividend_lo = pretty_print_reg(dividend_lo.to_reg(), size.to_bytes());
898                let dividend_hi = pretty_print_reg(dividend_hi.to_reg(), size.to_bytes());
899                let dst_quotient =
900                    pretty_print_reg(dst_quotient.to_reg().to_reg(), size.to_bytes());
901                let dst_remainder =
902                    pretty_print_reg(dst_remainder.to_reg().to_reg(), size.to_bytes());
903                format!(
904                    "checked_srem_seq {dividend_lo}, {dividend_hi}, \
905                        {divisor}, {dst_quotient}, {dst_remainder}",
906                )
907            }
908
909            Inst::CheckedSRemSeq8 {
910                divisor,
911                dividend,
912                dst,
913            } => {
914                let divisor = pretty_print_reg(divisor.to_reg(), 1);
915                let dividend = pretty_print_reg(dividend.to_reg(), 1);
916                let dst = pretty_print_reg(dst.to_reg().to_reg(), 1);
917                format!("checked_srem_seq {dividend}, {divisor}, {dst}")
918            }
919
920            Inst::SignExtendData { size, src, dst } => {
921                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
922                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
923                let op = match size {
924                    OperandSize::Size8 => "cbw",
925                    OperandSize::Size16 => "cwd",
926                    OperandSize::Size32 => "cdq",
927                    OperandSize::Size64 => "cqo",
928                };
929                format!("{op} {src}, {dst}")
930            }
931
932            Inst::XmmUnaryRmR { op, src, dst, .. } => {
933                let dst = pretty_print_reg(dst.to_reg().to_reg(), op.src_size());
934                let src = src.pretty_print(op.src_size());
935                let op = ljustify(op.to_string());
936                format!("{op} {src}, {dst}")
937            }
938
939            Inst::XmmUnaryRmRUnaligned { op, src, dst, .. } => {
940                let dst = pretty_print_reg(dst.to_reg().to_reg(), op.src_size());
941                let src = src.pretty_print(op.src_size());
942                let op = ljustify(op.to_string());
943                format!("{op} {src}, {dst}")
944            }
945
946            Inst::XmmUnaryRmRImm {
947                op, src, dst, imm, ..
948            } => {
949                let dst = pretty_print_reg(dst.to_reg().to_reg(), op.src_size());
950                let src = src.pretty_print(op.src_size());
951                let op = ljustify(op.to_string());
952                format!("{op} ${imm}, {src}, {dst}")
953            }
954
955            Inst::XmmUnaryRmRVex { op, src, dst, .. } => {
956                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
957                let src = src.pretty_print(8);
958                let op = ljustify(op.to_string());
959                format!("{op} {src}, {dst}")
960            }
961
962            Inst::XmmUnaryRmRImmVex {
963                op, src, dst, imm, ..
964            } => {
965                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
966                let src = src.pretty_print(8);
967                let op = ljustify(op.to_string());
968                format!("{op} ${imm}, {src}, {dst}")
969            }
970
971            Inst::XmmUnaryRmREvex { op, src, dst, .. } => {
972                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
973                let src = src.pretty_print(8);
974                let op = ljustify(op.to_string());
975                format!("{op} {src}, {dst}")
976            }
977
978            Inst::XmmUnaryRmRImmEvex {
979                op, src, dst, imm, ..
980            } => {
981                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
982                let src = src.pretty_print(8);
983                let op = ljustify(op.to_string());
984                format!("{op} ${imm}, {src}, {dst}")
985            }
986
987            Inst::XmmMovRM { op, src, dst, .. } => {
988                let src = pretty_print_reg(src.to_reg(), 8);
989                let dst = dst.pretty_print(8);
990                let op = ljustify(op.to_string());
991                format!("{op} {src}, {dst}")
992            }
993
994            Inst::XmmMovRMVex { op, src, dst, .. } => {
995                let src = pretty_print_reg(src.to_reg(), 8);
996                let dst = dst.pretty_print(8);
997                let op = ljustify(op.to_string());
998                format!("{op} {src}, {dst}")
999            }
1000
1001            Inst::XmmMovRMImm {
1002                op, src, dst, imm, ..
1003            } => {
1004                let src = pretty_print_reg(src.to_reg(), 8);
1005                let dst = dst.pretty_print(8);
1006                let op = ljustify(op.to_string());
1007                format!("{op} ${imm}, {src}, {dst}")
1008            }
1009
1010            Inst::XmmMovRMImmVex {
1011                op, src, dst, imm, ..
1012            } => {
1013                let src = pretty_print_reg(src.to_reg(), 8);
1014                let dst = dst.pretty_print(8);
1015                let op = ljustify(op.to_string());
1016                format!("{op} ${imm}, {src}, {dst}")
1017            }
1018
1019            Inst::XmmRmR {
1020                op,
1021                src1,
1022                src2,
1023                dst,
1024                ..
1025            } => {
1026                let src1 = pretty_print_reg(src1.to_reg(), 8);
1027                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1028                let src2 = src2.pretty_print(8);
1029                let op = ljustify(op.to_string());
1030                format!("{op} {src1}, {src2}, {dst}")
1031            }
1032
1033            Inst::XmmRmRUnaligned {
1034                op,
1035                src1,
1036                src2,
1037                dst,
1038                ..
1039            } => {
1040                let src1 = pretty_print_reg(src1.to_reg(), 8);
1041                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1042                let src2 = src2.pretty_print(8);
1043                let op = ljustify(op.to_string());
1044                format!("{op} {src1}, {src2}, {dst}")
1045            }
1046
1047            Inst::XmmRmRBlend {
1048                op,
1049                src1,
1050                src2,
1051                mask,
1052                dst,
1053            } => {
1054                let src1 = pretty_print_reg(src1.to_reg(), 8);
1055                let mask = mask.to_reg();
1056                let mask = if mask.is_virtual() {
1057                    format!(" <{}>", show_ireg_sized(mask, 8))
1058                } else {
1059                    debug_assert_eq!(mask, regs::xmm0());
1060                    String::new()
1061                };
1062                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1063                let src2 = src2.pretty_print(8);
1064                let op = ljustify(op.to_string());
1065                format!("{op} {src1}, {src2}, {dst}{mask}")
1066            }
1067
1068            Inst::XmmRmiRVex {
1069                op,
1070                src1,
1071                src2,
1072                dst,
1073                ..
1074            } => {
1075                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1076                let src1 = pretty_print_reg(src1.to_reg(), 8);
1077                let src2 = src2.pretty_print(8);
1078                let op = ljustify(op.to_string());
1079                format!("{op} {src1}, {src2}, {dst}")
1080            }
1081
1082            Inst::XmmRmRImmVex {
1083                op,
1084                src1,
1085                src2,
1086                dst,
1087                imm,
1088                ..
1089            } => {
1090                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1091                let src1 = pretty_print_reg(src1.to_reg(), 8);
1092                let src2 = src2.pretty_print(8);
1093                let op = ljustify(op.to_string());
1094                format!("{op} ${imm}, {src1}, {src2}, {dst}")
1095            }
1096
1097            Inst::XmmVexPinsr {
1098                op,
1099                src1,
1100                src2,
1101                dst,
1102                imm,
1103                ..
1104            } => {
1105                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1106                let src1 = pretty_print_reg(src1.to_reg(), 8);
1107                let src2 = src2.pretty_print(8);
1108                let op = ljustify(op.to_string());
1109                format!("{op} ${imm}, {src1}, {src2}, {dst}")
1110            }
1111
1112            Inst::XmmRmRVex3 {
1113                op,
1114                src1,
1115                src2,
1116                src3,
1117                dst,
1118                ..
1119            } => {
1120                let src1 = pretty_print_reg(src1.to_reg(), 8);
1121                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1122                let src2 = pretty_print_reg(src2.to_reg(), 8);
1123                let src3 = src3.pretty_print(8);
1124                let op = ljustify(op.to_string());
1125                format!("{op} {src1}, {src2}, {src3}, {dst}")
1126            }
1127
1128            Inst::XmmRmRBlendVex {
1129                op,
1130                src1,
1131                src2,
1132                mask,
1133                dst,
1134                ..
1135            } => {
1136                let src1 = pretty_print_reg(src1.to_reg(), 8);
1137                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1138                let src2 = src2.pretty_print(8);
1139                let mask = pretty_print_reg(mask.to_reg(), 8);
1140                let op = ljustify(op.to_string());
1141                format!("{op} {src1}, {src2}, {mask}, {dst}")
1142            }
1143
1144            Inst::XmmRmREvex {
1145                op,
1146                src1,
1147                src2,
1148                dst,
1149                ..
1150            } => {
1151                let src1 = pretty_print_reg(src1.to_reg(), 8);
1152                let src2 = src2.pretty_print(8);
1153                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1154                let op = ljustify(op.to_string());
1155                format!("{op} {src2}, {src1}, {dst}")
1156            }
1157
1158            Inst::XmmRmREvex3 {
1159                op,
1160                src1,
1161                src2,
1162                src3,
1163                dst,
1164                ..
1165            } => {
1166                let src1 = pretty_print_reg(src1.to_reg(), 8);
1167                let src2 = pretty_print_reg(src2.to_reg(), 8);
1168                let src3 = src3.pretty_print(8);
1169                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1170                let op = ljustify(op.to_string());
1171                format!("{op} {src3}, {src2}, {src1}, {dst}")
1172            }
1173
1174            Inst::XmmMinMaxSeq {
1175                lhs,
1176                rhs,
1177                dst,
1178                is_min,
1179                size,
1180            } => {
1181                let rhs = pretty_print_reg(rhs.to_reg(), 8);
1182                let lhs = pretty_print_reg(lhs.to_reg(), 8);
1183                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1184                let op = ljustify2(
1185                    if *is_min {
1186                        "xmm min seq ".to_string()
1187                    } else {
1188                        "xmm max seq ".to_string()
1189                    },
1190                    format!("f{}", size.to_bits()),
1191                );
1192                format!("{op} {lhs}, {rhs}, {dst}")
1193            }
1194
1195            Inst::XmmRmRImm {
1196                op,
1197                src1,
1198                src2,
1199                dst,
1200                imm,
1201                size,
1202                ..
1203            } => {
1204                let src1 = pretty_print_reg(*src1, 8);
1205                let dst = pretty_print_reg(dst.to_reg(), 8);
1206                let src2 = src2.pretty_print(8);
1207                let op = ljustify(format!(
1208                    "{}{}",
1209                    op.to_string(),
1210                    if *size == OperandSize::Size64 {
1211                        ".w"
1212                    } else {
1213                        ""
1214                    }
1215                ));
1216                format!("{op} ${imm}, {src1}, {src2}, {dst}")
1217            }
1218
1219            Inst::XmmUninitializedValue { dst } => {
1220                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1221                let op = ljustify("uninit".into());
1222                format!("{op} {dst}")
1223            }
1224
1225            Inst::GprUninitializedValue { dst } => {
1226                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1227                let op = ljustify("uninit".into());
1228                format!("{op} {dst}")
1229            }
1230
1231            Inst::XmmToGpr {
1232                op,
1233                src,
1234                dst,
1235                dst_size,
1236            } => {
1237                let dst_size = dst_size.to_bytes();
1238                let src = pretty_print_reg(src.to_reg(), 8);
1239                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size);
1240                let op = ljustify(op.to_string());
1241                format!("{op} {src}, {dst}")
1242            }
1243
1244            Inst::XmmToGprVex {
1245                op,
1246                src,
1247                dst,
1248                dst_size,
1249            } => {
1250                let dst_size = dst_size.to_bytes();
1251                let src = pretty_print_reg(src.to_reg(), 8);
1252                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size);
1253                let op = ljustify(op.to_string());
1254                format!("{op} {src}, {dst}")
1255            }
1256
1257            Inst::XmmToGprImm { op, src, dst, imm } => {
1258                let src = pretty_print_reg(src.to_reg(), 8);
1259                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1260                let op = ljustify(op.to_string());
1261                format!("{op} ${imm}, {src}, {dst}")
1262            }
1263
1264            Inst::XmmToGprImmVex { op, src, dst, imm } => {
1265                let src = pretty_print_reg(src.to_reg(), 8);
1266                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1267                let op = ljustify(op.to_string());
1268                format!("{op} ${imm}, {src}, {dst}")
1269            }
1270
1271            Inst::GprToXmm {
1272                op,
1273                src,
1274                src_size,
1275                dst,
1276            } => {
1277                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1278                let src = src.pretty_print(src_size.to_bytes());
1279                let op = ljustify(op.to_string());
1280                format!("{op} {src}, {dst}")
1281            }
1282
1283            Inst::GprToXmmVex {
1284                op,
1285                src,
1286                src_size,
1287                dst,
1288            } => {
1289                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1290                let src = src.pretty_print(src_size.to_bytes());
1291                let op = ljustify(op.to_string());
1292                format!("{op} {src}, {dst}")
1293            }
1294
1295            Inst::XmmCmpRmR { op, src1, src2 } => {
1296                let src1 = pretty_print_reg(src1.to_reg(), 8);
1297                let src2 = src2.pretty_print(8);
1298                let op = ljustify(op.to_string());
1299                format!("{op} {src2}, {src1}")
1300            }
1301
1302            Inst::CvtIntToFloat {
1303                op,
1304                src1,
1305                src2,
1306                dst,
1307                src2_size,
1308            } => {
1309                let src1 = pretty_print_reg(src1.to_reg(), 8);
1310                let dst = pretty_print_reg(*dst.to_reg(), 8);
1311                let src2 = src2.pretty_print(src2_size.to_bytes());
1312                let op = ljustify(op.to_string());
1313                format!("{op} {src1}, {src2}, {dst}")
1314            }
1315
1316            Inst::CvtIntToFloatVex {
1317                op,
1318                src1,
1319                src2,
1320                dst,
1321                src2_size,
1322            } => {
1323                let dst = pretty_print_reg(*dst.to_reg(), 8);
1324                let src1 = pretty_print_reg(src1.to_reg(), 8);
1325                let src2 = src2.pretty_print(src2_size.to_bytes());
1326                let op = ljustify(op.to_string());
1327                format!("{op} {src1}, {src2}, {dst}")
1328            }
1329
1330            Inst::XmmCmpRmRVex { op, src1, src2 } => {
1331                let src1 = pretty_print_reg(src1.to_reg(), 8);
1332                let src2 = src2.pretty_print(8);
1333                format!("{} {src2}, {src1}", ljustify(op.to_string()))
1334            }
1335
1336            Inst::CvtUint64ToFloatSeq {
1337                src,
1338                dst,
1339                dst_size,
1340                tmp_gpr1,
1341                tmp_gpr2,
1342                ..
1343            } => {
1344                let src = pretty_print_reg(src.to_reg(), 8);
1345                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
1346                let tmp_gpr1 = pretty_print_reg(tmp_gpr1.to_reg().to_reg(), 8);
1347                let tmp_gpr2 = pretty_print_reg(tmp_gpr2.to_reg().to_reg(), 8);
1348                let op = ljustify(format!(
1349                    "u64_to_{}_seq",
1350                    if *dst_size == OperandSize::Size64 {
1351                        "f64"
1352                    } else {
1353                        "f32"
1354                    }
1355                ));
1356                format!("{op} {src}, {dst}, {tmp_gpr1}, {tmp_gpr2}")
1357            }
1358
1359            Inst::CvtFloatToSintSeq {
1360                src,
1361                dst,
1362                src_size,
1363                dst_size,
1364                tmp_xmm,
1365                tmp_gpr,
1366                is_saturating,
1367            } => {
1368                let src = pretty_print_reg(src.to_reg(), src_size.to_bytes());
1369                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
1370                let tmp_gpr = pretty_print_reg(tmp_gpr.to_reg().to_reg(), 8);
1371                let tmp_xmm = pretty_print_reg(tmp_xmm.to_reg().to_reg(), 8);
1372                let op = ljustify(format!(
1373                    "cvt_float{}_to_sint{}{}_seq",
1374                    src_size.to_bits(),
1375                    dst_size.to_bits(),
1376                    if *is_saturating { "_sat" } else { "" },
1377                ));
1378                format!("{op} {src}, {dst}, {tmp_gpr}, {tmp_xmm}")
1379            }
1380
1381            Inst::CvtFloatToUintSeq {
1382                src,
1383                dst,
1384                src_size,
1385                dst_size,
1386                tmp_gpr,
1387                tmp_xmm,
1388                tmp_xmm2,
1389                is_saturating,
1390            } => {
1391                let src = pretty_print_reg(src.to_reg(), src_size.to_bytes());
1392                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
1393                let tmp_gpr = pretty_print_reg(tmp_gpr.to_reg().to_reg(), 8);
1394                let tmp_xmm = pretty_print_reg(tmp_xmm.to_reg().to_reg(), 8);
1395                let tmp_xmm2 = pretty_print_reg(tmp_xmm2.to_reg().to_reg(), 8);
1396                let op = ljustify(format!(
1397                    "cvt_float{}_to_uint{}{}_seq",
1398                    src_size.to_bits(),
1399                    dst_size.to_bits(),
1400                    if *is_saturating { "_sat" } else { "" },
1401                ));
1402                format!("{op} {src}, {dst}, {tmp_gpr}, {tmp_xmm}, {tmp_xmm2}")
1403            }
1404
1405            Inst::Imm {
1406                dst_size,
1407                simm64,
1408                dst,
1409            } => {
1410                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
1411                if *dst_size == OperandSize::Size64 {
1412                    let op = ljustify("movabsq".to_string());
1413                    let imm = *simm64 as i64;
1414                    format!("{op} ${imm}, {dst}")
1415                } else {
1416                    let op = ljustify("movl".to_string());
1417                    let imm = (*simm64 as u32) as i32;
1418                    format!("{op} ${imm}, {dst}")
1419                }
1420            }
1421
1422            Inst::MovImmM { size, simm32, dst } => {
1423                let dst = dst.pretty_print(size.to_bytes());
1424                let suffix = suffix_bwlq(*size);
1425                let imm = match *size {
1426                    OperandSize::Size8 => ((*simm32 as u8) as i8).to_string(),
1427                    OperandSize::Size16 => ((*simm32 as u16) as i16).to_string(),
1428                    OperandSize::Size32 => simm32.to_string(),
1429                    OperandSize::Size64 => (*simm32 as i64).to_string(),
1430                };
1431                let op = ljustify2("mov".to_string(), suffix);
1432                format!("{op} ${imm}, {dst}")
1433            }
1434
1435            Inst::MovRR { size, src, dst } => {
1436                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
1437                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
1438                let op = ljustify2("mov".to_string(), suffix_lq(*size));
1439                format!("{op} {src}, {dst}")
1440            }
1441
1442            Inst::MovFromPReg { src, dst } => {
1443                let src: Reg = (*src).into();
1444                let src = regs::show_ireg_sized(src, 8);
1445                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1446                let op = ljustify("movq".to_string());
1447                format!("{op} {src}, {dst}")
1448            }
1449
1450            Inst::MovToPReg { src, dst } => {
1451                let src = pretty_print_reg(src.to_reg(), 8);
1452                let dst: Reg = (*dst).into();
1453                let dst = regs::show_ireg_sized(dst, 8);
1454                let op = ljustify("movq".to_string());
1455                format!("{op} {src}, {dst}")
1456            }
1457
1458            Inst::MovzxRmR {
1459                ext_mode, src, dst, ..
1460            } => {
1461                let dst_size = if *ext_mode == ExtMode::LQ {
1462                    4
1463                } else {
1464                    ext_mode.dst_size()
1465                };
1466                let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size);
1467                let src = src.pretty_print(ext_mode.src_size());
1468
1469                if *ext_mode == ExtMode::LQ {
1470                    let op = ljustify("movl".to_string());
1471                    format!("{op} {src}, {dst}")
1472                } else {
1473                    let op = ljustify2("movz".to_string(), ext_mode.to_string());
1474                    format!("{op} {src}, {dst}")
1475                }
1476            }
1477
1478            Inst::Mov64MR { src, dst, .. } => {
1479                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1480                let src = src.pretty_print(8);
1481                let op = ljustify("movq".to_string());
1482                format!("{op} {src}, {dst}")
1483            }
1484
1485            Inst::LoadEffectiveAddress { addr, dst, size } => {
1486                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
1487                let addr = addr.pretty_print(8);
1488                let op = ljustify("lea".to_string());
1489                format!("{op} {addr}, {dst}")
1490            }
1491
1492            Inst::MovsxRmR {
1493                ext_mode, src, dst, ..
1494            } => {
1495                let dst = pretty_print_reg(dst.to_reg().to_reg(), ext_mode.dst_size());
1496                let src = src.pretty_print(ext_mode.src_size());
1497                let op = ljustify2("movs".to_string(), ext_mode.to_string());
1498                format!("{op} {src}, {dst}")
1499            }
1500
1501            Inst::MovRM { size, src, dst, .. } => {
1502                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
1503                let dst = dst.pretty_print(size.to_bytes());
1504                let op = ljustify2("mov".to_string(), suffix_bwlq(*size));
1505                format!("{op} {src}, {dst}")
1506            }
1507
1508            Inst::ShiftR {
1509                size,
1510                kind,
1511                num_bits,
1512                src,
1513                dst,
1514                ..
1515            } => {
1516                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
1517                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
1518                match num_bits.as_imm8_reg() {
1519                    &Imm8Reg::Reg { reg } => {
1520                        let reg = pretty_print_reg(reg, 1);
1521                        let op = ljustify2(kind.to_string(), suffix_bwlq(*size));
1522                        format!("{op} {reg}, {src}, {dst}")
1523                    }
1524
1525                    &Imm8Reg::Imm8 { imm: num_bits } => {
1526                        let op = ljustify2(kind.to_string(), suffix_bwlq(*size));
1527                        format!("{op} ${num_bits}, {src}, {dst}")
1528                    }
1529                }
1530            }
1531
1532            Inst::XmmRmiReg {
1533                opcode,
1534                src1,
1535                src2,
1536                dst,
1537                ..
1538            } => {
1539                let src1 = pretty_print_reg(src1.to_reg(), 8);
1540                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1541                let src2 = src2.pretty_print(8);
1542                let op = ljustify(opcode.to_string());
1543                format!("{op} {src1}, {src2}, {dst}")
1544            }
1545
1546            Inst::CmpRmiR {
1547                size,
1548                src1,
1549                src2,
1550                opcode,
1551            } => {
1552                let src1 = pretty_print_reg(src1.to_reg(), size.to_bytes());
1553                let src2 = src2.pretty_print(size.to_bytes());
1554                let op = match opcode {
1555                    CmpOpcode::Cmp => "cmp",
1556                    CmpOpcode::Test => "test",
1557                };
1558                let op = ljustify2(op.to_string(), suffix_bwlq(*size));
1559                format!("{op} {src2}, {src1}")
1560            }
1561
1562            Inst::Setcc { cc, dst } => {
1563                let dst = pretty_print_reg(dst.to_reg().to_reg(), 1);
1564                let op = ljustify2("set".to_string(), cc.to_string());
1565                format!("{op} {dst}")
1566            }
1567
1568            Inst::Bswap { size, src, dst } => {
1569                let src = pretty_print_reg(src.to_reg(), size.to_bytes());
1570                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
1571                let op = ljustify2("bswap".to_string(), suffix_bwlq(*size));
1572                format!("{op} {src}, {dst}")
1573            }
1574
1575            Inst::Cmove {
1576                size,
1577                cc,
1578                consequent,
1579                alternative,
1580                dst,
1581            } => {
1582                let alternative = pretty_print_reg(alternative.to_reg(), size.to_bytes());
1583                let dst = pretty_print_reg(dst.to_reg().to_reg(), size.to_bytes());
1584                let consequent = consequent.pretty_print(size.to_bytes());
1585                let op = ljustify(format!("cmov{}{}", cc.to_string(), suffix_bwlq(*size)));
1586                format!("{op} {consequent}, {alternative}, {dst}")
1587            }
1588
1589            Inst::XmmCmove {
1590                ty,
1591                cc,
1592                consequent,
1593                alternative,
1594                dst,
1595                ..
1596            } => {
1597                let size = u8::try_from(ty.bytes()).unwrap();
1598                let alternative = pretty_print_reg(alternative.to_reg(), size);
1599                let dst = pretty_print_reg(dst.to_reg().to_reg(), size);
1600                let consequent = pretty_print_reg(consequent.to_reg(), size);
1601                let suffix = match *ty {
1602                    types::F64 => "sd",
1603                    types::F32 => "ss",
1604                    types::F16 => "ss",
1605                    types::F32X4 => "aps",
1606                    types::F64X2 => "apd",
1607                    _ => "dqa",
1608                };
1609                let cc = cc.invert();
1610                format!(
1611                    "mov{suffix} {alternative}, {dst}; \
1612                    j{cc} $next; \
1613                    mov{suffix} {consequent}, {dst}; \
1614                    $next:"
1615                )
1616            }
1617
1618            Inst::Push64 { src } => {
1619                let src = src.pretty_print(8);
1620                let op = ljustify("pushq".to_string());
1621                format!("{op} {src}")
1622            }
1623
1624            Inst::StackProbeLoop {
1625                tmp,
1626                frame_size,
1627                guard_size,
1628            } => {
1629                let tmp = pretty_print_reg(tmp.to_reg(), 8);
1630                let op = ljustify("stack_probe_loop".to_string());
1631                format!("{op} {tmp}, frame_size={frame_size}, guard_size={guard_size}")
1632            }
1633
1634            Inst::Pop64 { dst } => {
1635                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1636                let op = ljustify("popq".to_string());
1637                format!("{op} {dst}")
1638            }
1639
1640            Inst::CallKnown { info } => {
1641                let op = ljustify("call".to_string());
1642                let try_call = info
1643                    .try_call_info
1644                    .as_ref()
1645                    .map(|tci| pretty_print_try_call(tci))
1646                    .unwrap_or_default();
1647                format!("{op} {:?}{try_call}", info.dest)
1648            }
1649
1650            Inst::CallUnknown { info } => {
1651                let dest = info.dest.pretty_print(8);
1652                let op = ljustify("call".to_string());
1653                let try_call = info
1654                    .try_call_info
1655                    .as_ref()
1656                    .map(|tci| pretty_print_try_call(tci))
1657                    .unwrap_or_default();
1658                format!("{op} *{dest}{try_call}")
1659            }
1660
1661            Inst::ReturnCallKnown { info } => {
1662                let ReturnCallInfo {
1663                    uses,
1664                    new_stack_arg_size,
1665                    tmp,
1666                    dest,
1667                } = &**info;
1668                let tmp = pretty_print_reg(tmp.to_reg().to_reg(), 8);
1669                let mut s = format!("return_call_known {dest:?} ({new_stack_arg_size}) tmp={tmp}");
1670                for ret in uses {
1671                    let preg = regs::show_reg(ret.preg);
1672                    let vreg = pretty_print_reg(ret.vreg, 8);
1673                    write!(&mut s, " {vreg}={preg}").unwrap();
1674                }
1675                s
1676            }
1677
1678            Inst::ReturnCallUnknown { info } => {
1679                let ReturnCallInfo {
1680                    uses,
1681                    new_stack_arg_size,
1682                    tmp,
1683                    dest,
1684                } = &**info;
1685                let callee = pretty_print_reg(*dest, 8);
1686                let tmp = pretty_print_reg(tmp.to_reg().to_reg(), 8);
1687                let mut s =
1688                    format!("return_call_unknown {callee} ({new_stack_arg_size}) tmp={tmp}");
1689                for ret in uses {
1690                    let preg = regs::show_reg(ret.preg);
1691                    let vreg = pretty_print_reg(ret.vreg, 8);
1692                    write!(&mut s, " {vreg}={preg}").unwrap();
1693                }
1694                s
1695            }
1696
1697            Inst::Args { args } => {
1698                let mut s = "args".to_string();
1699                for arg in args {
1700                    let preg = regs::show_reg(arg.preg);
1701                    let def = pretty_print_reg(arg.vreg.to_reg(), 8);
1702                    write!(&mut s, " {def}={preg}").unwrap();
1703                }
1704                s
1705            }
1706
1707            Inst::Rets { rets } => {
1708                let mut s = "rets".to_string();
1709                for ret in rets {
1710                    let preg = regs::show_reg(ret.preg);
1711                    let vreg = pretty_print_reg(ret.vreg, 8);
1712                    write!(&mut s, " {vreg}={preg}").unwrap();
1713                }
1714                s
1715            }
1716
1717            Inst::Ret { stack_bytes_to_pop } => {
1718                let mut s = "ret".to_string();
1719                if *stack_bytes_to_pop != 0 {
1720                    write!(&mut s, " {stack_bytes_to_pop}").unwrap();
1721                }
1722                s
1723            }
1724
1725            Inst::StackSwitchBasic {
1726                store_context_ptr,
1727                load_context_ptr,
1728                in_payload0,
1729                out_payload0,
1730            } => {
1731                let store_context_ptr = pretty_print_reg(**store_context_ptr, 8);
1732                let load_context_ptr = pretty_print_reg(**load_context_ptr, 8);
1733                let in_payload0 = pretty_print_reg(**in_payload0, 8);
1734                let out_payload0 = pretty_print_reg(*out_payload0.to_reg(), 8);
1735                format!("{out_payload0} = stack_switch_basic {store_context_ptr}, {load_context_ptr}, {in_payload0}")
1736            }
1737
1738            Inst::JmpKnown { dst } => {
1739                let op = ljustify("jmp".to_string());
1740                let dst = dst.to_string();
1741                format!("{op} {dst}")
1742            }
1743
1744            Inst::WinchJmpIf { cc, taken } => {
1745                let taken = taken.to_string();
1746                let op = ljustify2("j".to_string(), cc.to_string());
1747                format!("{op} {taken}")
1748            }
1749
1750            Inst::JmpCondOr {
1751                cc1,
1752                cc2,
1753                taken,
1754                not_taken,
1755            } => {
1756                let taken = taken.to_string();
1757                let not_taken = not_taken.to_string();
1758                let op = ljustify(format!("j{cc1},{cc2}"));
1759                format!("{op} {taken}; j {not_taken}")
1760            }
1761
1762            Inst::JmpCond {
1763                cc,
1764                taken,
1765                not_taken,
1766            } => {
1767                let taken = taken.to_string();
1768                let not_taken = not_taken.to_string();
1769                let op = ljustify2("j".to_string(), cc.to_string());
1770                format!("{op} {taken}; j {not_taken}")
1771            }
1772
1773            Inst::JmpTableSeq {
1774                idx, tmp1, tmp2, ..
1775            } => {
1776                let idx = pretty_print_reg(*idx, 8);
1777                let tmp1 = pretty_print_reg(tmp1.to_reg(), 8);
1778                let tmp2 = pretty_print_reg(tmp2.to_reg(), 8);
1779                let op = ljustify("br_table".into());
1780                format!("{op} {idx}, {tmp1}, {tmp2}")
1781            }
1782
1783            Inst::JmpUnknown { target } => {
1784                let target = target.pretty_print(8);
1785                let op = ljustify("jmp".to_string());
1786                format!("{op} *{target}")
1787            }
1788
1789            Inst::TrapIf { cc, trap_code, .. } => {
1790                format!("j{cc} #trap={trap_code}")
1791            }
1792
1793            Inst::TrapIfAnd {
1794                cc1,
1795                cc2,
1796                trap_code,
1797                ..
1798            } => {
1799                let cc1 = cc1.invert();
1800                let cc2 = cc2.invert();
1801                format!("trap_if_and {cc1}, {cc2}, {trap_code}")
1802            }
1803
1804            Inst::TrapIfOr {
1805                cc1,
1806                cc2,
1807                trap_code,
1808                ..
1809            } => {
1810                let cc2 = cc2.invert();
1811                format!("trap_if_or {cc1}, {cc2}, {trap_code}")
1812            }
1813
1814            Inst::LoadExtName {
1815                dst, name, offset, ..
1816            } => {
1817                let dst = pretty_print_reg(dst.to_reg(), 8);
1818                let name = name.display(None);
1819                let op = ljustify("load_ext_name".into());
1820                format!("{op} {name}+{offset}, {dst}")
1821            }
1822
1823            Inst::LockCmpxchg {
1824                ty,
1825                replacement,
1826                expected,
1827                mem,
1828                dst_old,
1829                ..
1830            } => {
1831                let size = ty.bytes() as u8;
1832                let replacement = pretty_print_reg(*replacement, size);
1833                let expected = pretty_print_reg(*expected, size);
1834                let dst_old = pretty_print_reg(dst_old.to_reg(), size);
1835                let mem = mem.pretty_print(size);
1836                let suffix = suffix_bwlq(OperandSize::from_bytes(size as u32));
1837                format!(
1838                    "lock cmpxchg{suffix} {replacement}, {mem}, expected={expected}, dst_old={dst_old}"
1839                )
1840            }
1841
1842            Inst::LockCmpxchg16b {
1843                replacement_low,
1844                replacement_high,
1845                expected_low,
1846                expected_high,
1847                mem,
1848                dst_old_low,
1849                dst_old_high,
1850                ..
1851            } => {
1852                let replacement_low = pretty_print_reg(*replacement_low, 8);
1853                let replacement_high = pretty_print_reg(*replacement_high, 8);
1854                let expected_low = pretty_print_reg(*expected_low, 8);
1855                let expected_high = pretty_print_reg(*expected_high, 8);
1856                let dst_old_low = pretty_print_reg(dst_old_low.to_reg(), 8);
1857                let dst_old_high = pretty_print_reg(dst_old_high.to_reg(), 8);
1858                let mem = mem.pretty_print(16);
1859                format!(
1860                    "lock cmpxchg16b {mem}, replacement={replacement_high}:{replacement_low}, expected={expected_high}:{expected_low}, dst_old={dst_old_high}:{dst_old_low}"
1861                )
1862            }
1863
1864            Inst::LockXadd {
1865                size,
1866                operand,
1867                mem,
1868                dst_old,
1869            } => {
1870                let operand = pretty_print_reg(*operand, size.to_bytes());
1871                let dst_old = pretty_print_reg(dst_old.to_reg(), size.to_bytes());
1872                let mem = mem.pretty_print(size.to_bytes());
1873                let suffix = suffix_bwlq(*size);
1874                format!("lock xadd{suffix} {operand}, {mem}, dst_old={dst_old}")
1875            }
1876
1877            Inst::Xchg {
1878                size,
1879                operand,
1880                mem,
1881                dst_old,
1882            } => {
1883                let operand = pretty_print_reg(*operand, size.to_bytes());
1884                let dst_old = pretty_print_reg(dst_old.to_reg(), size.to_bytes());
1885                let mem = mem.pretty_print(size.to_bytes());
1886                let suffix = suffix_bwlq(*size);
1887                format!("xchg{suffix} {operand}, {mem}, dst_old={dst_old}")
1888            }
1889
1890            Inst::AtomicRmwSeq { ty, op, .. } => {
1891                let ty = ty.bits();
1892                format!(
1893                    "atomically {{ {ty}_bits_at_[%r9] {op:?}= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }}"
1894                )
1895            }
1896
1897            Inst::Atomic128RmwSeq {
1898                op,
1899                mem,
1900                operand_low,
1901                operand_high,
1902                temp_low,
1903                temp_high,
1904                dst_old_low,
1905                dst_old_high,
1906            } => {
1907                let operand_low = pretty_print_reg(*operand_low, 8);
1908                let operand_high = pretty_print_reg(*operand_high, 8);
1909                let temp_low = pretty_print_reg(temp_low.to_reg(), 8);
1910                let temp_high = pretty_print_reg(temp_high.to_reg(), 8);
1911                let dst_old_low = pretty_print_reg(dst_old_low.to_reg(), 8);
1912                let dst_old_high = pretty_print_reg(dst_old_high.to_reg(), 8);
1913                let mem = mem.pretty_print(16);
1914                format!("atomically {{ {dst_old_high}:{dst_old_low} = {mem}; {temp_high}:{temp_low} = {dst_old_high}:{dst_old_low} {op:?} {operand_high}:{operand_low}; {mem} = {temp_high}:{temp_low} }}")
1915            }
1916
1917            Inst::Atomic128XchgSeq {
1918                mem,
1919                operand_low,
1920                operand_high,
1921                dst_old_low,
1922                dst_old_high,
1923            } => {
1924                let operand_low = pretty_print_reg(*operand_low, 8);
1925                let operand_high = pretty_print_reg(*operand_high, 8);
1926                let dst_old_low = pretty_print_reg(dst_old_low.to_reg(), 8);
1927                let dst_old_high = pretty_print_reg(dst_old_high.to_reg(), 8);
1928                let mem = mem.pretty_print(16);
1929                format!("atomically {{ {dst_old_high}:{dst_old_low} = {mem}; {mem} = {operand_high}:{operand_low} }}")
1930            }
1931
1932            Inst::Fence { kind } => match kind {
1933                FenceKind::MFence => "mfence".to_string(),
1934                FenceKind::LFence => "lfence".to_string(),
1935                FenceKind::SFence => "sfence".to_string(),
1936            },
1937
1938            Inst::Hlt => "hlt".into(),
1939
1940            Inst::Ud2 { trap_code } => format!("ud2 {trap_code}"),
1941
1942            Inst::ElfTlsGetAddr { symbol, dst } => {
1943                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1944                format!("{dst} = elf_tls_get_addr {symbol:?}")
1945            }
1946
1947            Inst::MachOTlsGetAddr { symbol, dst } => {
1948                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1949                format!("{dst} = macho_tls_get_addr {symbol:?}")
1950            }
1951
1952            Inst::CoffTlsGetAddr { symbol, dst, tmp } => {
1953                let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
1954                let tmp = tmp.to_reg().to_reg();
1955
1956                let mut s = format!("{dst} = coff_tls_get_addr {symbol:?}");
1957                if tmp.is_virtual() {
1958                    let tmp = show_ireg_sized(tmp, 8);
1959                    write!(&mut s, ", {tmp}").unwrap();
1960                };
1961
1962                s
1963            }
1964
1965            Inst::Unwind { inst } => format!("unwind {inst:?}"),
1966
1967            Inst::DummyUse { reg } => {
1968                let reg = pretty_print_reg(*reg, 8);
1969                format!("dummy_use {reg}")
1970            }
1971
1972            Inst::External { inst } => {
1973                format!("{inst}")
1974            }
1975        }
1976    }
1977}
1978
1979fn pretty_print_try_call(info: &TryCallInfo) -> String {
1980    let dests = info
1981        .exception_dests
1982        .iter()
1983        .map(|(tag, label)| format!("{tag:?}: {label:?}"))
1984        .collect::<Vec<_>>()
1985        .join(", ");
1986    format!("; jmp {:?}; catch [{dests}]", info.continuation)
1987}
1988
1989impl fmt::Debug for Inst {
1990    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1991        write!(fmt, "{}", self.pretty_print_inst(&mut Default::default()))
1992    }
1993}
1994
1995fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
1996    // Note: because we need to statically know the indices of each
1997    // reg in the operands list in order to fetch its allocation
1998    // later, we put the variable-operand-count bits (the RegMem,
1999    // RegMemImm, etc args) last. regalloc2 doesn't care what order
2000    // the operands come in; they can be freely reordered.
2001
2002    // N.B.: we MUST keep the below in careful sync with (i) emission,
2003    // in `emit.rs`, and (ii) pretty-printing, in the `pretty_print`
2004    // method above.
2005    match inst {
2006        Inst::AluRmRVex {
2007            src1, src2, dst, ..
2008        } => {
2009            collector.reg_def(dst);
2010            collector.reg_use(src1);
2011            src2.get_operands(collector);
2012        }
2013        Inst::Not { src, dst, .. } => {
2014            collector.reg_use(src);
2015            collector.reg_reuse_def(dst, 0);
2016        }
2017        Inst::Neg { src, dst, .. } => {
2018            collector.reg_use(src);
2019            collector.reg_reuse_def(dst, 0);
2020        }
2021        Inst::Div {
2022            divisor,
2023            dividend_lo,
2024            dividend_hi,
2025            dst_quotient,
2026            dst_remainder,
2027            ..
2028        } => {
2029            divisor.get_operands(collector);
2030            collector.reg_fixed_use(dividend_lo, regs::rax());
2031            collector.reg_fixed_use(dividend_hi, regs::rdx());
2032            collector.reg_fixed_def(dst_quotient, regs::rax());
2033            collector.reg_fixed_def(dst_remainder, regs::rdx());
2034        }
2035        Inst::CheckedSRemSeq {
2036            divisor,
2037            dividend_lo,
2038            dividend_hi,
2039            dst_quotient,
2040            dst_remainder,
2041            ..
2042        } => {
2043            collector.reg_use(divisor);
2044            collector.reg_fixed_use(dividend_lo, regs::rax());
2045            collector.reg_fixed_use(dividend_hi, regs::rdx());
2046            collector.reg_fixed_def(dst_quotient, regs::rax());
2047            collector.reg_fixed_def(dst_remainder, regs::rdx());
2048        }
2049        Inst::Div8 {
2050            divisor,
2051            dividend,
2052            dst,
2053            ..
2054        } => {
2055            divisor.get_operands(collector);
2056            collector.reg_fixed_use(dividend, regs::rax());
2057            collector.reg_fixed_def(dst, regs::rax());
2058        }
2059        Inst::CheckedSRemSeq8 {
2060            divisor,
2061            dividend,
2062            dst,
2063            ..
2064        } => {
2065            collector.reg_use(divisor);
2066            collector.reg_fixed_use(dividend, regs::rax());
2067            collector.reg_fixed_def(dst, regs::rax());
2068        }
2069        Inst::Mul {
2070            src1,
2071            src2,
2072            dst_lo,
2073            dst_hi,
2074            ..
2075        } => {
2076            collector.reg_fixed_use(src1, regs::rax());
2077            collector.reg_fixed_def(dst_lo, regs::rax());
2078            collector.reg_fixed_def(dst_hi, regs::rdx());
2079            src2.get_operands(collector);
2080        }
2081        Inst::Mul8 {
2082            src1, src2, dst, ..
2083        } => {
2084            collector.reg_fixed_use(src1, regs::rax());
2085            collector.reg_fixed_def(dst, regs::rax());
2086            src2.get_operands(collector);
2087        }
2088        Inst::IMul {
2089            src1, src2, dst, ..
2090        } => {
2091            collector.reg_use(src1);
2092            collector.reg_reuse_def(dst, 0);
2093            src2.get_operands(collector);
2094        }
2095        Inst::IMulImm { src1, dst, .. } => {
2096            collector.reg_def(dst);
2097            src1.get_operands(collector);
2098        }
2099        Inst::MulX {
2100            src1,
2101            src2,
2102            dst_lo,
2103            dst_hi,
2104            ..
2105        } => {
2106            if !dst_lo.to_reg().is_invalid_sentinel() {
2107                collector.reg_def(dst_lo);
2108            }
2109            collector.reg_def(dst_hi);
2110            collector.reg_fixed_use(src1, regs::rdx());
2111            src2.get_operands(collector);
2112        }
2113        Inst::SignExtendData { size, src, dst } => {
2114            match size {
2115                OperandSize::Size8 => {
2116                    // Note `rax` on both src and dest: 8->16 extend
2117                    // does AL -> AX.
2118                    collector.reg_fixed_use(src, regs::rax());
2119                    collector.reg_fixed_def(dst, regs::rax());
2120                }
2121                _ => {
2122                    // All other widths do RAX -> RDX (AX -> DX:AX,
2123                    // EAX -> EDX:EAX).
2124                    collector.reg_fixed_use(src, regs::rax());
2125                    collector.reg_fixed_def(dst, regs::rdx());
2126                }
2127            }
2128        }
2129        Inst::UnaryRmR { src, dst, .. }
2130        | Inst::UnaryRmRVex { src, dst, .. }
2131        | Inst::UnaryRmRImmVex { src, dst, .. } => {
2132            collector.reg_def(dst);
2133            src.get_operands(collector);
2134        }
2135        Inst::XmmUnaryRmR { src, dst, .. } | Inst::XmmUnaryRmRImm { src, dst, .. } => {
2136            collector.reg_def(dst);
2137            src.get_operands(collector);
2138        }
2139        Inst::XmmUnaryRmREvex { src, dst, .. }
2140        | Inst::XmmUnaryRmRImmEvex { src, dst, .. }
2141        | Inst::XmmUnaryRmRUnaligned { src, dst, .. }
2142        | Inst::XmmUnaryRmRVex { src, dst, .. }
2143        | Inst::XmmUnaryRmRImmVex { src, dst, .. } => {
2144            collector.reg_def(dst);
2145            src.get_operands(collector);
2146        }
2147        Inst::XmmRmR {
2148            src1, src2, dst, ..
2149        } => {
2150            collector.reg_use(src1);
2151            collector.reg_reuse_def(dst, 0);
2152            src2.get_operands(collector);
2153        }
2154        Inst::XmmRmRUnaligned {
2155            src1, src2, dst, ..
2156        } => {
2157            collector.reg_use(src1);
2158            collector.reg_reuse_def(dst, 0);
2159            src2.get_operands(collector);
2160        }
2161        Inst::XmmRmRBlend {
2162            src1,
2163            src2,
2164            mask,
2165            dst,
2166            op,
2167        } => {
2168            assert!(matches!(
2169                op,
2170                SseOpcode::Blendvpd | SseOpcode::Blendvps | SseOpcode::Pblendvb
2171            ));
2172            collector.reg_use(src1);
2173            collector.reg_fixed_use(mask, regs::xmm0());
2174            collector.reg_reuse_def(dst, 0);
2175            src2.get_operands(collector);
2176        }
2177        Inst::XmmRmiRVex {
2178            src1, src2, dst, ..
2179        } => {
2180            collector.reg_def(dst);
2181            collector.reg_use(src1);
2182            src2.get_operands(collector);
2183        }
2184        Inst::XmmRmRImmVex {
2185            src1, src2, dst, ..
2186        } => {
2187            collector.reg_def(dst);
2188            collector.reg_use(src1);
2189            src2.get_operands(collector);
2190        }
2191        Inst::XmmVexPinsr {
2192            src1, src2, dst, ..
2193        } => {
2194            collector.reg_def(dst);
2195            collector.reg_use(src1);
2196            src2.get_operands(collector);
2197        }
2198        Inst::XmmRmRVex3 {
2199            src1,
2200            src2,
2201            src3,
2202            dst,
2203            ..
2204        } => {
2205            collector.reg_use(src1);
2206            collector.reg_reuse_def(dst, 0);
2207            collector.reg_use(src2);
2208            src3.get_operands(collector);
2209        }
2210        Inst::XmmRmRBlendVex {
2211            src1,
2212            src2,
2213            mask,
2214            dst,
2215            ..
2216        } => {
2217            collector.reg_def(dst);
2218            collector.reg_use(src1);
2219            src2.get_operands(collector);
2220            collector.reg_use(mask);
2221        }
2222        Inst::XmmRmREvex {
2223            op,
2224            src1,
2225            src2,
2226            dst,
2227            ..
2228        } => {
2229            assert_ne!(*op, Avx512Opcode::Vpermi2b);
2230            collector.reg_use(src1);
2231            src2.get_operands(collector);
2232            collector.reg_def(dst);
2233        }
2234        Inst::XmmRmREvex3 {
2235            op,
2236            src1,
2237            src2,
2238            src3,
2239            dst,
2240            ..
2241        } => {
2242            assert_eq!(*op, Avx512Opcode::Vpermi2b);
2243            collector.reg_use(src1);
2244            collector.reg_use(src2);
2245            src3.get_operands(collector);
2246            collector.reg_reuse_def(dst, 0); // Reuse `src1`.
2247        }
2248        Inst::XmmRmRImm {
2249            src1, src2, dst, ..
2250        } => {
2251            collector.reg_use(src1);
2252            collector.reg_reuse_def(dst, 0);
2253            src2.get_operands(collector);
2254        }
2255        Inst::XmmUninitializedValue { dst } => collector.reg_def(dst),
2256        Inst::GprUninitializedValue { dst } => collector.reg_def(dst),
2257        Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
2258            collector.reg_use(rhs);
2259            collector.reg_use(lhs);
2260            collector.reg_reuse_def(dst, 0); // Reuse RHS.
2261        }
2262        Inst::XmmRmiReg {
2263            src1, src2, dst, ..
2264        } => {
2265            collector.reg_use(src1);
2266            collector.reg_reuse_def(dst, 0); // Reuse RHS.
2267            src2.get_operands(collector);
2268        }
2269        Inst::XmmMovRM { src, dst, .. }
2270        | Inst::XmmMovRMVex { src, dst, .. }
2271        | Inst::XmmMovRMImm { src, dst, .. }
2272        | Inst::XmmMovRMImmVex { src, dst, .. } => {
2273            collector.reg_use(src);
2274            dst.get_operands(collector);
2275        }
2276        Inst::XmmCmpRmR { src1, src2, .. } => {
2277            collector.reg_use(src1);
2278            src2.get_operands(collector);
2279        }
2280        Inst::XmmCmpRmRVex { src1, src2, .. } => {
2281            collector.reg_use(src1);
2282            src2.get_operands(collector);
2283        }
2284        Inst::Imm { dst, .. } => {
2285            collector.reg_def(dst);
2286        }
2287        Inst::MovRR { src, dst, .. } => {
2288            collector.reg_use(src);
2289            collector.reg_def(dst);
2290        }
2291        Inst::MovFromPReg { dst, src } => {
2292            debug_assert!(dst.to_reg().to_reg().is_virtual());
2293            collector.reg_fixed_nonallocatable(*src);
2294            collector.reg_def(dst);
2295        }
2296        Inst::MovToPReg { dst, src } => {
2297            debug_assert!(src.to_reg().is_virtual());
2298            collector.reg_use(src);
2299            collector.reg_fixed_nonallocatable(*dst);
2300        }
2301        Inst::XmmToGpr { src, dst, .. }
2302        | Inst::XmmToGprVex { src, dst, .. }
2303        | Inst::XmmToGprImm { src, dst, .. }
2304        | Inst::XmmToGprImmVex { src, dst, .. } => {
2305            collector.reg_use(src);
2306            collector.reg_def(dst);
2307        }
2308        Inst::GprToXmm { src, dst, .. } | Inst::GprToXmmVex { src, dst, .. } => {
2309            collector.reg_def(dst);
2310            src.get_operands(collector);
2311        }
2312        Inst::CvtIntToFloat {
2313            src1, src2, dst, ..
2314        } => {
2315            collector.reg_use(src1);
2316            collector.reg_reuse_def(dst, 0);
2317            src2.get_operands(collector);
2318        }
2319        Inst::CvtIntToFloatVex {
2320            src1, src2, dst, ..
2321        } => {
2322            collector.reg_def(dst);
2323            collector.reg_use(src1);
2324            src2.get_operands(collector);
2325        }
2326        Inst::CvtUint64ToFloatSeq {
2327            src,
2328            dst,
2329            tmp_gpr1,
2330            tmp_gpr2,
2331            ..
2332        } => {
2333            collector.reg_use(src);
2334            collector.reg_early_def(dst);
2335            collector.reg_early_def(tmp_gpr1);
2336            collector.reg_early_def(tmp_gpr2);
2337        }
2338        Inst::CvtFloatToSintSeq {
2339            src,
2340            dst,
2341            tmp_xmm,
2342            tmp_gpr,
2343            ..
2344        } => {
2345            collector.reg_use(src);
2346            collector.reg_early_def(dst);
2347            collector.reg_early_def(tmp_gpr);
2348            collector.reg_early_def(tmp_xmm);
2349        }
2350        Inst::CvtFloatToUintSeq {
2351            src,
2352            dst,
2353            tmp_gpr,
2354            tmp_xmm,
2355            tmp_xmm2,
2356            ..
2357        } => {
2358            collector.reg_use(src);
2359            collector.reg_early_def(dst);
2360            collector.reg_early_def(tmp_gpr);
2361            collector.reg_early_def(tmp_xmm);
2362            collector.reg_early_def(tmp_xmm2);
2363        }
2364
2365        Inst::MovImmM { dst, .. } => {
2366            dst.get_operands(collector);
2367        }
2368
2369        Inst::MovzxRmR { src, dst, .. } => {
2370            collector.reg_def(dst);
2371            src.get_operands(collector);
2372        }
2373        Inst::Mov64MR { src, dst, .. } => {
2374            collector.reg_def(dst);
2375            src.get_operands(collector);
2376        }
2377        Inst::LoadEffectiveAddress { addr: src, dst, .. } => {
2378            collector.reg_def(dst);
2379            src.get_operands(collector);
2380        }
2381        Inst::MovsxRmR { src, dst, .. } => {
2382            collector.reg_def(dst);
2383            src.get_operands(collector);
2384        }
2385        Inst::MovRM { src, dst, .. } => {
2386            collector.reg_use(src);
2387            dst.get_operands(collector);
2388        }
2389        Inst::ShiftR {
2390            num_bits, src, dst, ..
2391        } => {
2392            collector.reg_use(src);
2393            collector.reg_reuse_def(dst, 0);
2394            if let Imm8Reg::Reg { reg } = num_bits.as_imm8_reg_mut() {
2395                collector.reg_fixed_use(reg, regs::rcx());
2396            }
2397        }
2398        Inst::CmpRmiR { src1, src2, .. } => {
2399            collector.reg_use(src1);
2400            src2.get_operands(collector);
2401        }
2402        Inst::Setcc { dst, .. } => {
2403            collector.reg_def(dst);
2404        }
2405        Inst::Bswap { src, dst, .. } => {
2406            collector.reg_use(src);
2407            collector.reg_reuse_def(dst, 0);
2408        }
2409        Inst::Cmove {
2410            consequent,
2411            alternative,
2412            dst,
2413            ..
2414        } => {
2415            collector.reg_use(alternative);
2416            collector.reg_reuse_def(dst, 0);
2417            consequent.get_operands(collector);
2418        }
2419        Inst::XmmCmove {
2420            consequent,
2421            alternative,
2422            dst,
2423            ..
2424        } => {
2425            collector.reg_use(alternative);
2426            collector.reg_reuse_def(dst, 0);
2427            collector.reg_use(consequent);
2428        }
2429        Inst::Push64 { src } => {
2430            src.get_operands(collector);
2431        }
2432        Inst::Pop64 { dst } => {
2433            collector.reg_def(dst);
2434        }
2435        Inst::StackProbeLoop { tmp, .. } => {
2436            collector.reg_early_def(tmp);
2437        }
2438
2439        Inst::CallKnown { info } => {
2440            // Probestack is special and is only inserted after
2441            // regalloc, so we do not need to represent its ABI to the
2442            // register allocator. Assert that we don't alter that
2443            // arrangement.
2444            let CallInfo {
2445                uses,
2446                defs,
2447                clobbers,
2448                dest,
2449                ..
2450            } = &mut **info;
2451            debug_assert_ne!(*dest, ExternalName::LibCall(LibCall::Probestack));
2452            for CallArgPair { vreg, preg } in uses {
2453                collector.reg_fixed_use(vreg, *preg);
2454            }
2455            for CallRetPair { vreg, location } in defs {
2456                match location {
2457                    RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
2458                    RetLocation::Stack(..) => collector.any_def(vreg),
2459                }
2460            }
2461            collector.reg_clobbers(*clobbers);
2462        }
2463
2464        Inst::CallUnknown { info } => {
2465            let CallInfo {
2466                uses,
2467                defs,
2468                clobbers,
2469                callee_conv,
2470                dest,
2471                ..
2472            } = &mut **info;
2473            match dest {
2474                RegMem::Reg { reg } if *callee_conv == CallConv::Winch => {
2475                    // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
2476                    // This shouldn't be a fixed register constraint. r10 is caller-saved, so this
2477                    // should be safe to use.
2478                    collector.reg_fixed_use(reg, regs::r10());
2479                }
2480                _ => dest.get_operands(collector),
2481            }
2482            for CallArgPair { vreg, preg } in uses {
2483                collector.reg_fixed_use(vreg, *preg);
2484            }
2485            for CallRetPair { vreg, location } in defs {
2486                match location {
2487                    RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
2488                    RetLocation::Stack(..) => collector.any_def(vreg),
2489                }
2490            }
2491            collector.reg_clobbers(*clobbers);
2492        }
2493        Inst::StackSwitchBasic {
2494            store_context_ptr,
2495            load_context_ptr,
2496            in_payload0,
2497            out_payload0,
2498        } => {
2499            collector.reg_use(load_context_ptr);
2500            collector.reg_use(store_context_ptr);
2501            collector.reg_fixed_use(in_payload0, stack_switch::payload_register());
2502            collector.reg_fixed_def(out_payload0, stack_switch::payload_register());
2503
2504            let mut clobbers = crate::isa::x64::abi::ALL_CLOBBERS;
2505            // The return/payload reg must not be included in the clobber set
2506            clobbers.remove(
2507                stack_switch::payload_register()
2508                    .to_real_reg()
2509                    .unwrap()
2510                    .into(),
2511            );
2512            collector.reg_clobbers(clobbers);
2513        }
2514
2515        Inst::ReturnCallKnown { info } => {
2516            let ReturnCallInfo {
2517                dest, uses, tmp, ..
2518            } = &mut **info;
2519            collector.reg_fixed_def(tmp, regs::r11());
2520            // Same as in the `Inst::CallKnown` branch.
2521            debug_assert_ne!(*dest, ExternalName::LibCall(LibCall::Probestack));
2522            for CallArgPair { vreg, preg } in uses {
2523                collector.reg_fixed_use(vreg, *preg);
2524            }
2525        }
2526
2527        Inst::ReturnCallUnknown { info } => {
2528            let ReturnCallInfo {
2529                dest, uses, tmp, ..
2530            } = &mut **info;
2531
2532            // TODO(https://github.com/bytecodealliance/regalloc2/issues/145):
2533            // This shouldn't be a fixed register constraint, but it's not clear how to
2534            // pick a register that won't be clobbered by the callee-save restore code
2535            // emitted with a return_call_indirect. r10 is caller-saved, so this should be
2536            // safe to use.
2537            collector.reg_fixed_use(dest, regs::r10());
2538
2539            collector.reg_fixed_def(tmp, regs::r11());
2540            for CallArgPair { vreg, preg } in uses {
2541                collector.reg_fixed_use(vreg, *preg);
2542            }
2543        }
2544
2545        Inst::JmpTableSeq {
2546            idx, tmp1, tmp2, ..
2547        } => {
2548            collector.reg_use(idx);
2549            collector.reg_early_def(tmp1);
2550            // In the sequence emitted for this pseudoinstruction in emit.rs,
2551            // tmp2 is only written after idx is read, so it doesn't need to be
2552            // an early def.
2553            collector.reg_def(tmp2);
2554        }
2555
2556        Inst::JmpUnknown { target } => {
2557            target.get_operands(collector);
2558        }
2559
2560        Inst::LoadExtName { dst, .. } => {
2561            collector.reg_def(dst);
2562        }
2563
2564        Inst::LockCmpxchg {
2565            replacement,
2566            expected,
2567            mem,
2568            dst_old,
2569            ..
2570        } => {
2571            collector.reg_use(replacement);
2572            collector.reg_fixed_use(expected, regs::rax());
2573            collector.reg_fixed_def(dst_old, regs::rax());
2574            mem.get_operands(collector);
2575        }
2576
2577        Inst::LockCmpxchg16b {
2578            replacement_low,
2579            replacement_high,
2580            expected_low,
2581            expected_high,
2582            mem,
2583            dst_old_low,
2584            dst_old_high,
2585            ..
2586        } => {
2587            collector.reg_fixed_use(replacement_low, regs::rbx());
2588            collector.reg_fixed_use(replacement_high, regs::rcx());
2589            collector.reg_fixed_use(expected_low, regs::rax());
2590            collector.reg_fixed_use(expected_high, regs::rdx());
2591            collector.reg_fixed_def(dst_old_low, regs::rax());
2592            collector.reg_fixed_def(dst_old_high, regs::rdx());
2593            mem.get_operands(collector);
2594        }
2595
2596        Inst::LockXadd {
2597            operand,
2598            mem,
2599            dst_old,
2600            ..
2601        } => {
2602            collector.reg_use(operand);
2603            collector.reg_reuse_def(dst_old, 0);
2604            mem.get_operands(collector);
2605        }
2606
2607        Inst::Xchg {
2608            operand,
2609            mem,
2610            dst_old,
2611            ..
2612        } => {
2613            collector.reg_use(operand);
2614            collector.reg_reuse_def(dst_old, 0);
2615            mem.get_operands(collector);
2616        }
2617
2618        Inst::AtomicRmwSeq {
2619            operand,
2620            temp,
2621            dst_old,
2622            mem,
2623            ..
2624        } => {
2625            collector.reg_late_use(operand);
2626            collector.reg_early_def(temp);
2627            // This `fixed_def` is needed because `CMPXCHG` always uses this
2628            // register implicitly.
2629            collector.reg_fixed_def(dst_old, regs::rax());
2630            mem.get_operands_late(collector)
2631        }
2632
2633        Inst::Atomic128RmwSeq {
2634            operand_low,
2635            operand_high,
2636            temp_low,
2637            temp_high,
2638            dst_old_low,
2639            dst_old_high,
2640            mem,
2641            ..
2642        } => {
2643            // All registers are collected in the `Late` position so that they don't overlap.
2644            collector.reg_late_use(operand_low);
2645            collector.reg_late_use(operand_high);
2646            collector.reg_fixed_def(temp_low, regs::rbx());
2647            collector.reg_fixed_def(temp_high, regs::rcx());
2648            collector.reg_fixed_def(dst_old_low, regs::rax());
2649            collector.reg_fixed_def(dst_old_high, regs::rdx());
2650            mem.get_operands_late(collector)
2651        }
2652
2653        Inst::Atomic128XchgSeq {
2654            operand_low,
2655            operand_high,
2656            dst_old_low,
2657            dst_old_high,
2658            mem,
2659            ..
2660        } => {
2661            // All registers are collected in the `Late` position so that they don't overlap.
2662            collector.reg_fixed_late_use(operand_low, regs::rbx());
2663            collector.reg_fixed_late_use(operand_high, regs::rcx());
2664            collector.reg_fixed_def(dst_old_low, regs::rax());
2665            collector.reg_fixed_def(dst_old_high, regs::rdx());
2666            mem.get_operands_late(collector)
2667        }
2668
2669        Inst::Args { args } => {
2670            for ArgPair { vreg, preg } in args {
2671                collector.reg_fixed_def(vreg, *preg);
2672            }
2673        }
2674
2675        Inst::Rets { rets } => {
2676            // The return value(s) are live-out; we represent this
2677            // with register uses on the return instruction.
2678            for RetPair { vreg, preg } in rets {
2679                collector.reg_fixed_use(vreg, *preg);
2680            }
2681        }
2682
2683        Inst::JmpKnown { .. }
2684        | Inst::WinchJmpIf { .. }
2685        | Inst::JmpCond { .. }
2686        | Inst::JmpCondOr { .. }
2687        | Inst::Ret { .. }
2688        | Inst::Nop { .. }
2689        | Inst::TrapIf { .. }
2690        | Inst::TrapIfAnd { .. }
2691        | Inst::TrapIfOr { .. }
2692        | Inst::Hlt
2693        | Inst::Ud2 { .. }
2694        | Inst::Fence { .. } => {
2695            // No registers are used.
2696        }
2697
2698        Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => {
2699            collector.reg_fixed_def(dst, regs::rax());
2700            // All caller-saves are clobbered.
2701            //
2702            // We use the SysV calling convention here because the
2703            // pseudoinstruction (and relocation that it emits) is specific to
2704            // ELF systems; other x86-64 targets with other conventions (i.e.,
2705            // Windows) use different TLS strategies.
2706            let mut clobbers =
2707                X64ABIMachineSpec::get_regs_clobbered_by_call(CallConv::SystemV, false);
2708            clobbers.remove(regs::gpr_preg(regs::ENC_RAX));
2709            collector.reg_clobbers(clobbers);
2710        }
2711
2712        Inst::CoffTlsGetAddr { dst, tmp, .. } => {
2713            // We also use the gs register. But that register is not allocatable by the
2714            // register allocator, so we don't need to mark it as used here.
2715
2716            // We use %rax to set the address
2717            collector.reg_fixed_def(dst, regs::rax());
2718
2719            // We use %rcx as a temporary variable to load the _tls_index
2720            collector.reg_fixed_def(tmp, regs::rcx());
2721        }
2722
2723        Inst::Unwind { .. } => {}
2724
2725        Inst::DummyUse { reg } => {
2726            collector.reg_use(reg);
2727        }
2728
2729        Inst::External { inst } => {
2730            inst.visit(&mut external::RegallocVisitor { collector });
2731        }
2732    }
2733}
2734
2735//=============================================================================
2736// Instructions: misc functions and external interface
2737
2738impl MachInst for Inst {
2739    type ABIMachineSpec = X64ABIMachineSpec;
2740
2741    fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
2742        x64_get_operands(self, collector)
2743    }
2744
2745    fn is_move(&self) -> Option<(Writable<Reg>, Reg)> {
2746        match self {
2747            // Note (carefully!) that a 32-bit mov *isn't* a no-op since it zeroes
2748            // out the upper 32 bits of the destination.  For example, we could
2749            // conceivably use `movl %reg, %reg` to zero out the top 32 bits of
2750            // %reg.
2751            Self::MovRR { size, src, dst, .. } if *size == OperandSize::Size64 => {
2752                Some((dst.to_writable_reg(), src.to_reg()))
2753            }
2754            // Note as well that MOVS[S|D] when used in the `XmmUnaryRmR` context are pure moves of
2755            // scalar floating-point values (and annotate `dst` as `def`s to the register allocator)
2756            // whereas the same operation in a packed context, e.g. `XMM_RM_R`, is used to merge a
2757            // value into the lowest lane of a vector (not a move).
2758            Self::XmmUnaryRmR { op, src, dst, .. }
2759                if *op == SseOpcode::Movss
2760                    || *op == SseOpcode::Movsd
2761                    || *op == SseOpcode::Movaps
2762                    || *op == SseOpcode::Movapd
2763                    || *op == SseOpcode::Movups
2764                    || *op == SseOpcode::Movupd
2765                    || *op == SseOpcode::Movdqa
2766                    || *op == SseOpcode::Movdqu =>
2767            {
2768                if let RegMem::Reg { reg } = src.clone().to_reg_mem() {
2769                    Some((dst.to_writable_reg(), reg))
2770                } else {
2771                    None
2772                }
2773            }
2774            _ => None,
2775        }
2776    }
2777
2778    fn is_included_in_clobbers(&self) -> bool {
2779        match self {
2780            &Inst::Args { .. } => false,
2781            _ => true,
2782        }
2783    }
2784
2785    fn is_trap(&self) -> bool {
2786        match self {
2787            Self::Ud2 { .. } => true,
2788            _ => false,
2789        }
2790    }
2791
2792    fn is_args(&self) -> bool {
2793        match self {
2794            Self::Args { .. } => true,
2795            _ => false,
2796        }
2797    }
2798
2799    fn is_term(&self) -> MachTerminator {
2800        match self {
2801            // Interesting cases.
2802            &Self::Rets { .. } => MachTerminator::Ret,
2803            &Self::ReturnCallKnown { .. } | &Self::ReturnCallUnknown { .. } => {
2804                MachTerminator::RetCall
2805            }
2806            &Self::JmpKnown { .. } => MachTerminator::Branch,
2807            &Self::JmpCond { .. } => MachTerminator::Branch,
2808            &Self::JmpCondOr { .. } => MachTerminator::Branch,
2809            &Self::JmpTableSeq { .. } => MachTerminator::Branch,
2810            &Self::CallKnown { ref info } if info.try_call_info.is_some() => MachTerminator::Branch,
2811            &Self::CallUnknown { ref info } if info.try_call_info.is_some() => {
2812                MachTerminator::Branch
2813            }
2814            // All other cases are boring.
2815            _ => MachTerminator::None,
2816        }
2817    }
2818
2819    fn is_low_level_branch(&self) -> bool {
2820        match self {
2821            &Self::WinchJmpIf { .. } => true,
2822            _ => false,
2823        }
2824    }
2825
2826    fn is_mem_access(&self) -> bool {
2827        panic!("TODO FILL ME OUT")
2828    }
2829
2830    fn gen_move(dst_reg: Writable<Reg>, src_reg: Reg, ty: Type) -> Inst {
2831        trace!(
2832            "Inst::gen_move {:?} -> {:?} (type: {:?})",
2833            src_reg,
2834            dst_reg.to_reg(),
2835            ty
2836        );
2837        let rc_dst = dst_reg.to_reg().class();
2838        let rc_src = src_reg.class();
2839        // If this isn't true, we have gone way off the rails.
2840        debug_assert!(rc_dst == rc_src);
2841        match rc_dst {
2842            RegClass::Int => Inst::mov_r_r(OperandSize::Size64, src_reg, dst_reg),
2843            RegClass::Float => {
2844                // The Intel optimization manual, in "3.5.1.13 Zero-Latency MOV Instructions",
2845                // doesn't include MOVSS/MOVSD as instructions with zero-latency. Use movaps for
2846                // those, which may write more lanes that we need, but are specified to have
2847                // zero-latency.
2848                let opcode = match ty {
2849                    types::F16 | types::F32 | types::F64 | types::F32X4 => SseOpcode::Movaps,
2850                    types::F64X2 => SseOpcode::Movapd,
2851                    _ if (ty.is_float() || ty.is_vector()) && ty.bits() <= 128 => SseOpcode::Movdqa,
2852                    _ => unimplemented!("unable to move type: {}", ty),
2853                };
2854                Inst::xmm_unary_rm_r(opcode, RegMem::reg(src_reg), dst_reg)
2855            }
2856            RegClass::Vector => unreachable!(),
2857        }
2858    }
2859
2860    fn gen_nop(preferred_size: usize) -> Inst {
2861        Inst::nop(std::cmp::min(preferred_size, 15) as u8)
2862    }
2863
2864    fn rc_for_type(ty: Type) -> CodegenResult<(&'static [RegClass], &'static [Type])> {
2865        match ty {
2866            types::I8 => Ok((&[RegClass::Int], &[types::I8])),
2867            types::I16 => Ok((&[RegClass::Int], &[types::I16])),
2868            types::I32 => Ok((&[RegClass::Int], &[types::I32])),
2869            types::I64 => Ok((&[RegClass::Int], &[types::I64])),
2870            types::F16 => Ok((&[RegClass::Float], &[types::F16])),
2871            types::F32 => Ok((&[RegClass::Float], &[types::F32])),
2872            types::F64 => Ok((&[RegClass::Float], &[types::F64])),
2873            types::F128 => Ok((&[RegClass::Float], &[types::F128])),
2874            types::I128 => Ok((&[RegClass::Int, RegClass::Int], &[types::I64, types::I64])),
2875            _ if ty.is_vector() && ty.bits() <= 128 => {
2876                let types = &[types::I8X2, types::I8X4, types::I8X8, types::I8X16];
2877                Ok((
2878                    &[RegClass::Float],
2879                    slice::from_ref(&types[ty.bytes().ilog2() as usize - 1]),
2880                ))
2881            }
2882            _ => Err(CodegenError::Unsupported(format!(
2883                "Unexpected SSA-value type: {ty}"
2884            ))),
2885        }
2886    }
2887
2888    fn canonical_type_for_rc(rc: RegClass) -> Type {
2889        match rc {
2890            RegClass::Float => types::I8X16,
2891            RegClass::Int => types::I64,
2892            RegClass::Vector => unreachable!(),
2893        }
2894    }
2895
2896    fn gen_jump(label: MachLabel) -> Inst {
2897        Inst::jmp_known(label)
2898    }
2899
2900    fn gen_imm_u64(value: u64, dst: Writable<Reg>) -> Option<Self> {
2901        Some(Inst::imm(OperandSize::Size64, value, dst))
2902    }
2903
2904    fn gen_imm_f64(value: f64, tmp: Writable<Reg>, dst: Writable<Reg>) -> SmallVec<[Self; 2]> {
2905        let imm_to_gpr = Inst::imm(OperandSize::Size64, value.to_bits(), tmp);
2906        let gpr_to_xmm = Self::gpr_to_xmm(
2907            SseOpcode::Movd,
2908            tmp.to_reg().into(),
2909            OperandSize::Size64,
2910            dst,
2911        );
2912        smallvec![imm_to_gpr, gpr_to_xmm]
2913    }
2914
2915    fn gen_dummy_use(reg: Reg) -> Self {
2916        Inst::DummyUse { reg }
2917    }
2918
2919    fn worst_case_size() -> CodeOffset {
2920        15
2921    }
2922
2923    fn ref_type_regclass(_: &settings::Flags) -> RegClass {
2924        RegClass::Int
2925    }
2926
2927    fn is_safepoint(&self) -> bool {
2928        match self {
2929            Inst::CallKnown { .. } | Inst::CallUnknown { .. } => true,
2930            _ => false,
2931        }
2932    }
2933
2934    fn function_alignment() -> FunctionAlignment {
2935        FunctionAlignment {
2936            minimum: 1,
2937            // Change the alignment from 16-bytes to 32-bytes for better performance.
2938            // fix-8573: https://github.com/bytecodealliance/wasmtime/issues/8573
2939            preferred: 32,
2940        }
2941    }
2942
2943    type LabelUse = LabelUse;
2944
2945    const TRAP_OPCODE: &'static [u8] = &[0x0f, 0x0b];
2946}
2947
2948/// Constant state used during emissions of a sequence of instructions.
2949pub struct EmitInfo {
2950    pub(super) flags: settings::Flags,
2951    isa_flags: x64_settings::Flags,
2952}
2953
2954impl EmitInfo {
2955    /// Create a constant state for emission of instructions.
2956    pub fn new(flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self {
2957        Self { flags, isa_flags }
2958    }
2959}
2960
2961impl MachInstEmit for Inst {
2962    type State = EmitState;
2963    type Info = EmitInfo;
2964
2965    fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
2966        emit::emit(self, sink, info, state);
2967    }
2968
2969    fn pretty_print_inst(&self, _: &mut Self::State) -> String {
2970        PrettyPrint::pretty_print(self, 0)
2971    }
2972}
2973
2974/// A label-use (internal relocation) in generated code.
2975#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2976pub enum LabelUse {
2977    /// A 32-bit offset from location of relocation itself, added to the existing value at that
2978    /// location. Used for control flow instructions which consider an offset from the start of the
2979    /// next instruction (so the size of the payload -- 4 bytes -- is subtracted from the payload).
2980    JmpRel32,
2981
2982    /// A 32-bit offset from location of relocation itself, added to the existing value at that
2983    /// location.
2984    PCRel32,
2985}
2986
2987impl MachInstLabelUse for LabelUse {
2988    const ALIGN: CodeOffset = 1;
2989
2990    fn max_pos_range(self) -> CodeOffset {
2991        match self {
2992            LabelUse::JmpRel32 | LabelUse::PCRel32 => 0x7fff_ffff,
2993        }
2994    }
2995
2996    fn max_neg_range(self) -> CodeOffset {
2997        match self {
2998            LabelUse::JmpRel32 | LabelUse::PCRel32 => 0x8000_0000,
2999        }
3000    }
3001
3002    fn patch_size(self) -> CodeOffset {
3003        match self {
3004            LabelUse::JmpRel32 | LabelUse::PCRel32 => 4,
3005        }
3006    }
3007
3008    fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset) {
3009        let pc_rel = (label_offset as i64) - (use_offset as i64);
3010        debug_assert!(pc_rel <= self.max_pos_range() as i64);
3011        debug_assert!(pc_rel >= -(self.max_neg_range() as i64));
3012        let pc_rel = pc_rel as u32;
3013        match self {
3014            LabelUse::JmpRel32 => {
3015                let addend = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
3016                let value = pc_rel.wrapping_add(addend).wrapping_sub(4);
3017                buffer.copy_from_slice(&value.to_le_bytes()[..]);
3018            }
3019            LabelUse::PCRel32 => {
3020                let addend = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
3021                let value = pc_rel.wrapping_add(addend);
3022                buffer.copy_from_slice(&value.to_le_bytes()[..]);
3023            }
3024        }
3025    }
3026
3027    fn supports_veneer(self) -> bool {
3028        match self {
3029            LabelUse::JmpRel32 | LabelUse::PCRel32 => false,
3030        }
3031    }
3032
3033    fn veneer_size(self) -> CodeOffset {
3034        match self {
3035            LabelUse::JmpRel32 | LabelUse::PCRel32 => 0,
3036        }
3037    }
3038
3039    fn worst_case_veneer_size() -> CodeOffset {
3040        0
3041    }
3042
3043    fn generate_veneer(self, _: &mut [u8], _: CodeOffset) -> (CodeOffset, LabelUse) {
3044        match self {
3045            LabelUse::JmpRel32 | LabelUse::PCRel32 => {
3046                panic!("Veneer not supported for JumpRel32 label-use.");
3047            }
3048        }
3049    }
3050
3051    fn from_reloc(reloc: Reloc, addend: Addend) -> Option<Self> {
3052        match (reloc, addend) {
3053            (Reloc::X86CallPCRel4, -4) => Some(LabelUse::JmpRel32),
3054            _ => None,
3055        }
3056    }
3057}