1pub use emit_state::EmitState;
4
5use crate::binemit::{Addend, CodeOffset, Reloc};
6use crate::ir::{ExternalName, LibCall, TrapCode, Type, types};
7use crate::isa::x64::abi::X64ABIMachineSpec;
8use crate::isa::x64::inst::regs::pretty_print_reg;
9use crate::isa::x64::settings as x64_settings;
10use crate::isa::{CallConv, FunctionAlignment};
11use crate::{CodegenError, CodegenResult, settings};
12use crate::{machinst::*, trace};
13use alloc::boxed::Box;
14use alloc::string::{String, ToString};
15use alloc::vec;
16use alloc::vec::Vec;
17use core::fmt::{self, Write};
18use core::slice;
19use cranelift_assembler_x64 as asm;
20use smallvec::{SmallVec, smallvec};
21
22pub mod args;
23mod emit;
24mod emit_state;
25#[cfg(test)]
26mod emit_tests;
27pub mod external;
28pub mod regs;
29mod stack_switch;
30pub mod unwind;
31
32use args::*;
33
34pub use super::lower::isle::generated_code::AtomicRmwSeqOp;
39pub use super::lower::isle::generated_code::MInst as Inst;
40
41#[derive(Clone, Debug)]
43pub struct ReturnCallInfo<T> {
44 pub dest: T,
46
47 pub new_stack_arg_size: u32,
50
51 pub uses: CallArgList,
53
54 pub tmp: WritableGpr,
56}
57
58#[test]
59#[cfg(target_pointer_width = "64")]
60fn inst_size_test() {
61 assert_eq!(48, core::mem::size_of::<Inst>());
64}
65
66impl Inst {
67 fn is_available(&self, emit_info: &EmitInfo) -> bool {
72 use asm::AvailableFeatures;
73
74 match self {
75 Inst::AtomicRmwSeq { .. }
78 | Inst::CallKnown { .. }
79 | Inst::CallUnknown { .. }
80 | Inst::ReturnCallKnown { .. }
81 | Inst::ReturnCallUnknown { .. }
82 | Inst::CheckedSRemSeq { .. }
83 | Inst::CheckedSRemSeq8 { .. }
84 | Inst::CvtFloatToSintSeq { .. }
85 | Inst::CvtFloatToUintSeq { .. }
86 | Inst::CvtUint64ToFloatSeq { .. }
87 | Inst::JmpCond { .. }
88 | Inst::JmpCondOr { .. }
89 | Inst::WinchJmpIf { .. }
90 | Inst::JmpKnown { .. }
91 | Inst::JmpTableSeq { .. }
92 | Inst::LoadExtName { .. }
93 | Inst::MovFromPReg { .. }
94 | Inst::MovToPReg { .. }
95 | Inst::StackProbeLoop { .. }
96 | Inst::Args { .. }
97 | Inst::Rets { .. }
98 | Inst::StackSwitchBasic { .. }
99 | Inst::InterruptPoll { .. }
100 | Inst::TrapIf { .. }
101 | Inst::TrapIfAnd { .. }
102 | Inst::TrapIfOr { .. }
103 | Inst::XmmCmove { .. }
104 | Inst::XmmMinMaxSeq { .. }
105 | Inst::XmmUninitializedValue { .. }
106 | Inst::GprUninitializedValue { .. }
107 | Inst::ElfTlsGetAddr { .. }
108 | Inst::MachOTlsGetAddr { .. }
109 | Inst::CoffTlsGetAddr { .. }
110 | Inst::Unwind { .. }
111 | Inst::DummyUse { .. }
112 | Inst::LabelAddress { .. }
113 | Inst::SequencePoint => true,
114
115 Inst::Atomic128RmwSeq { .. } | Inst::Atomic128XchgSeq { .. } => emit_info.cmpxchg16b(),
116
117 Inst::External { inst } => inst.is_available(&emit_info),
118 }
119 }
120}
121
122impl Inst {
125 pub(crate) fn nop(len: u8) -> Self {
126 assert!(len > 0 && len <= 9);
127 let inst = match len {
128 1 => asm::inst::nop_1b::new().into(),
129 2 => asm::inst::nop_2b::new().into(),
130 3 => asm::inst::nop_3b::new().into(),
131 4 => asm::inst::nop_4b::new().into(),
132 5 => asm::inst::nop_5b::new().into(),
133 6 => asm::inst::nop_6b::new().into(),
134 7 => asm::inst::nop_7b::new().into(),
135 8 => asm::inst::nop_8b::new().into(),
136 9 => asm::inst::nop_9b::new().into(),
137 _ => unreachable!("nop length must be between 1 and 9"),
138 };
139 Self::External { inst }
140 }
141
142 pub(crate) fn addq_mi(dst: Writable<Reg>, simm32: i32) -> Self {
143 let inst = if let Ok(simm8) = i8::try_from(simm32) {
144 asm::inst::addq_mi_sxb::new(dst, simm8).into()
145 } else {
146 asm::inst::addq_mi_sxl::new(dst, simm32).into()
147 };
148 Inst::External { inst }
149 }
150
151 pub(crate) fn subq_mi(dst: Writable<Reg>, simm32: i32) -> Self {
152 let inst = if let Ok(simm8) = i8::try_from(simm32) {
153 asm::inst::subq_mi_sxb::new(dst, simm8).into()
154 } else {
155 asm::inst::subq_mi_sxl::new(dst, simm32).into()
156 };
157 Inst::External { inst }
158 }
159
160 pub fn imm(dst_size: OperandSize, simm64: u64, dst: Writable<Reg>) -> Inst {
165 debug_assert!(dst_size.is_one_of(&[OperandSize::Size32, OperandSize::Size64]));
166 debug_assert!(dst.to_reg().class() == RegClass::Int);
167 let dst = WritableGpr::from_writable_reg(dst).unwrap();
168 let inst = match dst_size {
169 OperandSize::Size64 => match u32::try_from(simm64) {
170 Ok(imm32) => asm::inst::movl_oi::new(dst, imm32).into(),
173 _ => match i32::try_from(simm64.cast_signed()) {
174 Ok(simm32) => asm::inst::movq_mi_sxl::new(dst, simm32).into(),
177 _ => asm::inst::movabsq_oi::new(dst, simm64).into(),
179 },
180 },
181 _ => asm::inst::movl_oi::new(dst, simm64 as u32).into(),
193 };
194 Inst::External { inst }
195 }
196
197 pub(crate) fn movzx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
198 src.assert_regclass_is(RegClass::Int);
199 debug_assert!(dst.to_reg().class() == RegClass::Int);
200 let src = match src {
201 RegMem::Reg { reg } => asm::GprMem::Gpr(Gpr::new(reg).unwrap()),
202 RegMem::Mem { addr } => asm::GprMem::Mem(addr.into()),
203 };
204 let inst = match ext_mode {
205 ExtMode::BL => asm::inst::movzbl_rm::new(dst, src).into(),
206 ExtMode::BQ => asm::inst::movzbq_rm::new(dst, src).into(),
207 ExtMode::WL => asm::inst::movzwl_rm::new(dst, src).into(),
208 ExtMode::WQ => asm::inst::movzwq_rm::new(dst, src).into(),
209 ExtMode::LQ => {
210 asm::inst::movl_rm::new(dst, src).into()
217 }
218 };
219 Inst::External { inst }
220 }
221
222 pub(crate) fn movsx_rm_r(ext_mode: ExtMode, src: RegMem, dst: Writable<Reg>) -> Inst {
223 src.assert_regclass_is(RegClass::Int);
224 debug_assert!(dst.to_reg().class() == RegClass::Int);
225 let src = match src {
226 RegMem::Reg { reg } => asm::GprMem::Gpr(Gpr::new(reg).unwrap()),
227 RegMem::Mem { addr } => asm::GprMem::Mem(addr.into()),
228 };
229 let inst = match ext_mode {
230 ExtMode::BL => asm::inst::movsbl_rm::new(dst, src).into(),
231 ExtMode::BQ => asm::inst::movsbq_rm::new(dst, src).into(),
232 ExtMode::WL => asm::inst::movswl_rm::new(dst, src).into(),
233 ExtMode::WQ => asm::inst::movswq_rm::new(dst, src).into(),
234 ExtMode::LQ => asm::inst::movslq_rm::new(dst, src).into(),
235 };
236 Inst::External { inst }
237 }
238
239 pub(crate) fn cmp_mi_sxb(size: OperandSize, src1: Gpr, src2: i8) -> Inst {
241 let inst = match size {
242 OperandSize::Size8 => asm::inst::cmpb_mi::new(src1, src2.cast_unsigned()).into(),
243 OperandSize::Size16 => asm::inst::cmpw_mi_sxb::new(src1, src2).into(),
244 OperandSize::Size32 => asm::inst::cmpl_mi_sxb::new(src1, src2).into(),
245 OperandSize::Size64 => asm::inst::cmpq_mi_sxb::new(src1, src2).into(),
246 };
247 Inst::External { inst }
248 }
249
250 pub(crate) fn trap_if(cc: CC, trap_code: TrapCode) -> Inst {
251 Inst::TrapIf { cc, trap_code }
252 }
253
254 pub(crate) fn call_known(info: Box<CallInfo<ExternalName>>) -> Inst {
255 Inst::CallKnown { info }
256 }
257
258 pub(crate) fn call_unknown(info: Box<CallInfo<RegMem>>) -> Inst {
259 info.dest.assert_regclass_is(RegClass::Int);
260 Inst::CallUnknown { info }
261 }
262
263 pub(crate) fn jmp_known(dst: MachLabel) -> Inst {
264 Inst::JmpKnown { dst }
265 }
266
267 pub(crate) fn load(
271 ty: Type,
272 from_addr: impl Into<SyntheticAmode>,
273 to_reg: Writable<Reg>,
274 ext_kind: ExtKind,
275 ) -> Inst {
276 let rc = to_reg.to_reg().class();
277 match rc {
278 RegClass::Int => {
279 let ext_mode = match ty.bytes() {
280 1 => Some(ExtMode::BQ),
281 2 => Some(ExtMode::WQ),
282 4 => Some(ExtMode::LQ),
283 8 => None,
284 _ => unreachable!("the type should never use a scalar load: {}", ty),
285 };
286 if let Some(ext_mode) = ext_mode {
287 match ext_kind {
289 ExtKind::SignExtend => {
290 Inst::movsx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg)
291 }
292 ExtKind::ZeroExtend => {
293 Inst::movzx_rm_r(ext_mode, RegMem::mem(from_addr), to_reg)
294 }
295 ExtKind::None => {
296 panic!("expected an extension kind for extension mode: {ext_mode:?}")
297 }
298 }
299 } else {
300 let from_addr = asm::GprMem::from(from_addr.into());
302 Inst::External {
303 inst: asm::inst::movq_rm::new(to_reg, from_addr).into(),
304 }
305 }
306 }
307 RegClass::Float => {
308 let to_reg = to_reg.map(|r| Xmm::new(r).unwrap());
309 let from_addr = from_addr.into();
310 let inst = match ty {
311 types::F16 | types::I8X2 => {
312 panic!("loading a f16 or i8x2 requires multiple instructions")
313 }
314 _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 32 => {
315 asm::inst::movss_a_m::new(to_reg, from_addr).into()
316 }
317 _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 64 => {
318 asm::inst::movsd_a_m::new(to_reg, from_addr).into()
319 }
320 types::F32X4 => asm::inst::movups_a::new(to_reg, from_addr).into(),
321 types::F64X2 => asm::inst::movupd_a::new(to_reg, from_addr).into(),
322 _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 128 => {
323 asm::inst::movdqu_a::new(to_reg, from_addr).into()
324 }
325 _ => unimplemented!("unable to load type: {}", ty),
326 };
327 Inst::External { inst }
328 }
329 RegClass::Vector => unreachable!(),
330 }
331 }
332
333 pub(crate) fn store(ty: Type, from_reg: Reg, to_addr: impl Into<SyntheticAmode>) -> Inst {
335 let rc = from_reg.class();
336 let to_addr = to_addr.into();
337 let inst = match rc {
338 RegClass::Int => {
339 let from_reg = Gpr::unwrap_new(from_reg);
340 match ty {
341 types::I8 => asm::inst::movb_mr::new(to_addr, from_reg).into(),
342 types::I16 => asm::inst::movw_mr::new(to_addr, from_reg).into(),
343 types::I32 => asm::inst::movl_mr::new(to_addr, from_reg).into(),
344 types::I64 => asm::inst::movq_mr::new(to_addr, from_reg).into(),
345 _ => unreachable!(),
346 }
347 }
348 RegClass::Float => {
349 let from_reg = Xmm::new(from_reg).unwrap();
350 match ty {
351 types::F16 | types::I8X2 => {
352 panic!("storing a f16 or i8x2 requires multiple instructions")
353 }
354 _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 32 => {
355 asm::inst::movss_c_m::new(to_addr, from_reg).into()
356 }
357 _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 64 => {
358 asm::inst::movsd_c_m::new(to_addr, from_reg).into()
359 }
360 types::F32X4 => asm::inst::movups_b::new(to_addr, from_reg).into(),
361 types::F64X2 => asm::inst::movupd_b::new(to_addr, from_reg).into(),
362 _ if (ty.is_float() || ty.is_vector()) && ty.bits() == 128 => {
363 asm::inst::movdqu_b::new(to_addr, from_reg).into()
364 }
365 _ => unimplemented!("unable to store type: {}", ty),
366 }
367 }
368 RegClass::Vector => unreachable!(),
369 };
370 Inst::External { inst }
371 }
372}
373
374impl PrettyPrint for Inst {
378 fn pretty_print(&self, _size: u8) -> String {
379 fn ljustify(s: String) -> String {
380 let w = 7;
381 if s.len() >= w {
382 s
383 } else {
384 let need = usize::min(w, w - s.len());
385 s + &format!("{nil: <width$}", nil = "", width = need)
386 }
387 }
388
389 fn ljustify2(s1: String, s2: String) -> String {
390 ljustify(s1 + &s2)
391 }
392
393 match self {
394 Inst::CheckedSRemSeq {
395 size,
396 divisor,
397 dividend_lo,
398 dividend_hi,
399 dst_quotient,
400 dst_remainder,
401 } => {
402 let divisor = pretty_print_reg(divisor.to_reg(), size.to_bytes());
403 let dividend_lo = pretty_print_reg(dividend_lo.to_reg(), size.to_bytes());
404 let dividend_hi = pretty_print_reg(dividend_hi.to_reg(), size.to_bytes());
405 let dst_quotient =
406 pretty_print_reg(dst_quotient.to_reg().to_reg(), size.to_bytes());
407 let dst_remainder =
408 pretty_print_reg(dst_remainder.to_reg().to_reg(), size.to_bytes());
409 format!(
410 "checked_srem_seq {dividend_lo}, {dividend_hi}, \
411 {divisor}, {dst_quotient}, {dst_remainder}",
412 )
413 }
414
415 Inst::CheckedSRemSeq8 {
416 divisor,
417 dividend,
418 dst,
419 } => {
420 let divisor = pretty_print_reg(divisor.to_reg(), 1);
421 let dividend = pretty_print_reg(dividend.to_reg(), 1);
422 let dst = pretty_print_reg(dst.to_reg().to_reg(), 1);
423 format!("checked_srem_seq {dividend}, {divisor}, {dst}")
424 }
425
426 Inst::XmmMinMaxSeq {
427 lhs,
428 rhs,
429 dst,
430 is_min,
431 size,
432 } => {
433 let rhs = pretty_print_reg(rhs.to_reg(), 8);
434 let lhs = pretty_print_reg(lhs.to_reg(), 8);
435 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
436 let op = ljustify2(
437 if *is_min {
438 "xmm min seq ".to_string()
439 } else {
440 "xmm max seq ".to_string()
441 },
442 format!("f{}", size.to_bits()),
443 );
444 format!("{op} {lhs}, {rhs}, {dst}")
445 }
446
447 Inst::XmmUninitializedValue { dst } => {
448 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
449 let op = ljustify("uninit".into());
450 format!("{op} {dst}")
451 }
452
453 Inst::GprUninitializedValue { dst } => {
454 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
455 let op = ljustify("uninit".into());
456 format!("{op} {dst}")
457 }
458
459 Inst::CvtUint64ToFloatSeq {
460 src,
461 dst,
462 dst_size,
463 tmp_gpr1,
464 tmp_gpr2,
465 ..
466 } => {
467 let src = pretty_print_reg(src.to_reg(), 8);
468 let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
469 let tmp_gpr1 = pretty_print_reg(tmp_gpr1.to_reg().to_reg(), 8);
470 let tmp_gpr2 = pretty_print_reg(tmp_gpr2.to_reg().to_reg(), 8);
471 let op = ljustify(format!(
472 "u64_to_{}_seq",
473 if *dst_size == OperandSize::Size64 {
474 "f64"
475 } else {
476 "f32"
477 }
478 ));
479 format!("{op} {src}, {dst}, {tmp_gpr1}, {tmp_gpr2}")
480 }
481
482 Inst::CvtFloatToSintSeq {
483 src,
484 dst,
485 src_size,
486 dst_size,
487 tmp_xmm,
488 tmp_gpr,
489 is_saturating,
490 } => {
491 let src = pretty_print_reg(src.to_reg(), src_size.to_bytes());
492 let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
493 let tmp_gpr = pretty_print_reg(tmp_gpr.to_reg().to_reg(), 8);
494 let tmp_xmm = pretty_print_reg(tmp_xmm.to_reg().to_reg(), 8);
495 let op = ljustify(format!(
496 "cvt_float{}_to_sint{}{}_seq",
497 src_size.to_bits(),
498 dst_size.to_bits(),
499 if *is_saturating { "_sat" } else { "" },
500 ));
501 format!("{op} {src}, {dst}, {tmp_gpr}, {tmp_xmm}")
502 }
503
504 Inst::CvtFloatToUintSeq {
505 src,
506 dst,
507 src_size,
508 dst_size,
509 tmp_gpr,
510 tmp_xmm,
511 tmp_xmm2,
512 is_saturating,
513 } => {
514 let src = pretty_print_reg(src.to_reg(), src_size.to_bytes());
515 let dst = pretty_print_reg(dst.to_reg().to_reg(), dst_size.to_bytes());
516 let tmp_gpr = pretty_print_reg(tmp_gpr.to_reg().to_reg(), 8);
517 let tmp_xmm = pretty_print_reg(tmp_xmm.to_reg().to_reg(), 8);
518 let tmp_xmm2 = pretty_print_reg(tmp_xmm2.to_reg().to_reg(), 8);
519 let op = ljustify(format!(
520 "cvt_float{}_to_uint{}{}_seq",
521 src_size.to_bits(),
522 dst_size.to_bits(),
523 if *is_saturating { "_sat" } else { "" },
524 ));
525 format!("{op} {src}, {dst}, {tmp_gpr}, {tmp_xmm}, {tmp_xmm2}")
526 }
527
528 Inst::MovFromPReg { src, dst } => {
529 let src: Reg = (*src).into();
530 let src = pretty_print_reg(src, 8);
531 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
532 let op = ljustify("movq".to_string());
533 format!("{op} {src}, {dst}")
534 }
535
536 Inst::MovToPReg { src, dst } => {
537 let src = pretty_print_reg(src.to_reg(), 8);
538 let dst: Reg = (*dst).into();
539 let dst = pretty_print_reg(dst, 8);
540 let op = ljustify("movq".to_string());
541 format!("{op} {src}, {dst}")
542 }
543
544 Inst::XmmCmove {
545 ty,
546 cc,
547 consequent,
548 alternative,
549 dst,
550 ..
551 } => {
552 let size = u8::try_from(ty.bytes()).unwrap();
553 let alternative = pretty_print_reg(alternative.to_reg(), size);
554 let dst = pretty_print_reg(dst.to_reg().to_reg(), size);
555 let consequent = pretty_print_reg(consequent.to_reg(), size);
556 let suffix = match *ty {
557 types::F64 => "sd",
558 types::F32 => "ss",
559 types::F16 => "ss",
560 types::F32X4 => "aps",
561 types::F64X2 => "apd",
562 _ => "dqa",
563 };
564 let cc = cc.invert();
565 format!(
566 "mov{suffix} {alternative}, {dst}; \
567 j{cc} $next; \
568 mov{suffix} {consequent}, {dst}; \
569 $next:"
570 )
571 }
572
573 Inst::StackProbeLoop {
574 tmp,
575 frame_size,
576 guard_size,
577 } => {
578 let tmp = pretty_print_reg(tmp.to_reg(), 8);
579 let op = ljustify("stack_probe_loop".to_string());
580 format!("{op} {tmp}, frame_size={frame_size}, guard_size={guard_size}")
581 }
582
583 Inst::CallKnown { info } => {
584 let op = ljustify("call".to_string());
585 let try_call = info
586 .try_call_info
587 .as_ref()
588 .map(|tci| pretty_print_try_call(tci))
589 .unwrap_or_default();
590 format!("{op} {:?}{try_call}", info.dest)
591 }
592
593 Inst::CallUnknown { info } => {
594 let dest = info.dest.pretty_print(8);
595 let op = ljustify("call".to_string());
596 let try_call = info
597 .try_call_info
598 .as_ref()
599 .map(|tci| pretty_print_try_call(tci))
600 .unwrap_or_default();
601 format!("{op} *{dest}{try_call}")
602 }
603
604 Inst::ReturnCallKnown { info } => {
605 let ReturnCallInfo {
606 uses,
607 new_stack_arg_size,
608 tmp,
609 dest,
610 } = &**info;
611 let tmp = pretty_print_reg(tmp.to_reg().to_reg(), 8);
612 let mut s = format!("return_call_known {dest:?} ({new_stack_arg_size}) tmp={tmp}");
613 for ret in uses {
614 let preg = pretty_print_reg(ret.preg, 8);
615 let vreg = pretty_print_reg(ret.vreg, 8);
616 write!(&mut s, " {vreg}={preg}").unwrap();
617 }
618 s
619 }
620
621 Inst::ReturnCallUnknown { info } => {
622 let ReturnCallInfo {
623 uses,
624 new_stack_arg_size,
625 tmp,
626 dest,
627 } = &**info;
628 let callee = pretty_print_reg(*dest, 8);
629 let tmp = pretty_print_reg(tmp.to_reg().to_reg(), 8);
630 let mut s =
631 format!("return_call_unknown {callee} ({new_stack_arg_size}) tmp={tmp}");
632 for ret in uses {
633 let preg = pretty_print_reg(ret.preg, 8);
634 let vreg = pretty_print_reg(ret.vreg, 8);
635 write!(&mut s, " {vreg}={preg}").unwrap();
636 }
637 s
638 }
639
640 Inst::Args { args } => {
641 let mut s = "args".to_string();
642 for arg in args {
643 let preg = pretty_print_reg(arg.preg, 8);
644 let def = pretty_print_reg(arg.vreg.to_reg(), 8);
645 write!(&mut s, " {def}={preg}").unwrap();
646 }
647 s
648 }
649
650 Inst::Rets { rets } => {
651 let mut s = "rets".to_string();
652 for ret in rets {
653 let preg = pretty_print_reg(ret.preg, 8);
654 let vreg = pretty_print_reg(ret.vreg, 8);
655 write!(&mut s, " {vreg}={preg}").unwrap();
656 }
657 s
658 }
659
660 Inst::StackSwitchBasic {
661 store_context_ptr,
662 load_context_ptr,
663 in_payload0,
664 out_payload0,
665 } => {
666 let store_context_ptr = pretty_print_reg(**store_context_ptr, 8);
667 let load_context_ptr = pretty_print_reg(**load_context_ptr, 8);
668 let in_payload0 = pretty_print_reg(**in_payload0, 8);
669 let out_payload0 = pretty_print_reg(*out_payload0.to_reg(), 8);
670 format!(
671 "{out_payload0} = stack_switch_basic {store_context_ptr}, {load_context_ptr}, {in_payload0}"
672 )
673 }
674
675 Inst::InterruptPoll {
676 dst,
677 load_ptr,
678 context,
679 trap_code,
680 next_load_ptr,
681 } => {
682 let dst = pretty_print_reg(*dst.to_reg(), 8);
683 let load_ptr = pretty_print_reg(**load_ptr, 8);
684 let context = pretty_print_reg(**context, 8);
685 let next_load_ptr = pretty_print_reg(*next_load_ptr.to_reg(), 8);
686 format!(
687 "{next_load_ptr} = interrupt_poll load_ptr={load_ptr}, context={context}, scratch={dst} #trap={trap_code}"
688 )
689 }
690
691 Inst::JmpKnown { dst } => {
692 let op = ljustify("jmp".to_string());
693 let dst = dst.to_string();
694 format!("{op} {dst}")
695 }
696
697 Inst::WinchJmpIf { cc, taken } => {
698 let taken = taken.to_string();
699 let op = ljustify2("j".to_string(), cc.to_string());
700 format!("{op} {taken}")
701 }
702
703 Inst::JmpCondOr {
704 cc1,
705 cc2,
706 taken,
707 not_taken,
708 } => {
709 let taken = taken.to_string();
710 let not_taken = not_taken.to_string();
711 let op = ljustify(format!("j{cc1},{cc2}"));
712 format!("{op} {taken}; j {not_taken}")
713 }
714
715 Inst::JmpCond {
716 cc,
717 taken,
718 not_taken,
719 } => {
720 let taken = taken.to_string();
721 let not_taken = not_taken.to_string();
722 let op = ljustify2("j".to_string(), cc.to_string());
723 format!("{op} {taken}; j {not_taken}")
724 }
725
726 Inst::JmpTableSeq {
727 idx, tmp1, tmp2, ..
728 } => {
729 let idx = pretty_print_reg(*idx, 8);
730 let tmp1 = pretty_print_reg(tmp1.to_reg(), 8);
731 let tmp2 = pretty_print_reg(tmp2.to_reg(), 8);
732 let op = ljustify("br_table".into());
733 format!("{op} {idx}, {tmp1}, {tmp2}")
734 }
735
736 Inst::TrapIf { cc, trap_code, .. } => {
737 format!("j{cc} #trap={trap_code}")
738 }
739
740 Inst::TrapIfAnd {
741 cc1,
742 cc2,
743 trap_code,
744 ..
745 } => {
746 let cc1 = cc1.invert();
747 let cc2 = cc2.invert();
748 format!("trap_if_and {cc1}, {cc2}, {trap_code}")
749 }
750
751 Inst::TrapIfOr {
752 cc1,
753 cc2,
754 trap_code,
755 ..
756 } => {
757 let cc2 = cc2.invert();
758 format!("trap_if_or {cc1}, {cc2}, {trap_code}")
759 }
760
761 Inst::LoadExtName {
762 dst, name, offset, ..
763 } => {
764 let dst = pretty_print_reg(*dst.to_reg(), 8);
765 let name = name.display(None);
766 let op = ljustify("load_ext_name".into());
767 format!("{op} {name}+{offset}, {dst}")
768 }
769
770 Inst::AtomicRmwSeq { ty, op, .. } => {
771 let ty = ty.bits();
772 format!(
773 "atomically {{ {ty}_bits_at_[%r9] {op:?}= %r10; %rax = old_value_at_[%r9]; %r11, %rflags = trash }}"
774 )
775 }
776
777 Inst::Atomic128RmwSeq { args } => {
778 let Atomic128RmwSeqArgs {
779 op,
780 mem_low,
781 mem_high,
782 operand_low,
783 operand_high,
784 temp_low,
785 temp_high,
786 dst_old_low,
787 dst_old_high,
788 } = &**args;
789 let operand_low = pretty_print_reg(**operand_low, 8);
790 let operand_high = pretty_print_reg(**operand_high, 8);
791 let temp_low = pretty_print_reg(*temp_low.to_reg(), 8);
792 let temp_high = pretty_print_reg(*temp_high.to_reg(), 8);
793 let dst_old_low = pretty_print_reg(*dst_old_low.to_reg(), 8);
794 let dst_old_high = pretty_print_reg(*dst_old_high.to_reg(), 8);
795 let mem_low = mem_low.pretty_print(16);
796 let mem_high = mem_high.pretty_print(16);
797 format!(
798 "atomically {{ {dst_old_high}:{dst_old_low} = {mem_low}:{mem_high}; {temp_high}:{temp_low} = {dst_old_high}:{dst_old_low} {op:?} {operand_high}:{operand_low}; {mem_low}:{mem_high} = {temp_high}:{temp_low} }}"
799 )
800 }
801
802 Inst::Atomic128XchgSeq { args } => {
803 let Atomic128XchgSeqArgs {
804 mem_low,
805 mem_high,
806 operand_low,
807 operand_high,
808 dst_old_low,
809 dst_old_high,
810 } = &**args;
811 let operand_low = pretty_print_reg(**operand_low, 8);
812 let operand_high = pretty_print_reg(**operand_high, 8);
813 let dst_old_low = pretty_print_reg(*dst_old_low.to_reg(), 8);
814 let dst_old_high = pretty_print_reg(*dst_old_high.to_reg(), 8);
815 let mem_low = mem_low.pretty_print(16);
816 let mem_high = mem_high.pretty_print(16);
817 format!(
818 "atomically {{ {dst_old_high}:{dst_old_low} = {mem_low}:{mem_high}; {mem_low}:{mem_high} = {operand_high}:{operand_low} }}"
819 )
820 }
821
822 Inst::ElfTlsGetAddr { symbol, dst } => {
823 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
824 format!("{dst} = elf_tls_get_addr {symbol:?}")
825 }
826
827 Inst::MachOTlsGetAddr { symbol, dst } => {
828 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
829 format!("{dst} = macho_tls_get_addr {symbol:?}")
830 }
831
832 Inst::CoffTlsGetAddr { symbol, dst, tmp } => {
833 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
834 let tmp = tmp.to_reg().to_reg();
835
836 let mut s = format!("{dst} = coff_tls_get_addr {symbol:?}");
837 if tmp.is_virtual() {
838 let tmp = pretty_print_reg(tmp, 8);
839 write!(&mut s, ", {tmp}").unwrap();
840 };
841
842 s
843 }
844
845 Inst::Unwind { inst } => format!("unwind {inst:?}"),
846
847 Inst::DummyUse { reg } => {
848 let reg = pretty_print_reg(*reg, 8);
849 format!("dummy_use {reg}")
850 }
851
852 Inst::LabelAddress { dst, label } => {
853 let dst = pretty_print_reg(dst.to_reg().to_reg(), 8);
854 format!("label_address {dst}, {label:?}")
855 }
856
857 Inst::SequencePoint {} => {
858 format!("sequence_point")
859 }
860
861 Inst::External { inst } => {
862 format!("{inst}")
863 }
864 }
865 }
866}
867
868fn pretty_print_try_call(info: &TryCallInfo) -> String {
869 format!(
870 "; jmp {:?}; catch [{}]",
871 info.continuation,
872 info.pretty_print_dests()
873 )
874}
875
876impl fmt::Debug for Inst {
877 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
878 write!(fmt, "{}", self.pretty_print_inst(&mut Default::default()))
879 }
880}
881
882fn x64_get_operands(inst: &mut Inst, collector: &mut impl OperandVisitor) {
883 match inst {
893 Inst::CheckedSRemSeq {
894 divisor,
895 dividend_lo,
896 dividend_hi,
897 dst_quotient,
898 dst_remainder,
899 ..
900 } => {
901 collector.reg_use(divisor);
902 collector.reg_fixed_use(dividend_lo, regs::rax());
903 collector.reg_fixed_use(dividend_hi, regs::rdx());
904 collector.reg_fixed_def(dst_quotient, regs::rax());
905 collector.reg_fixed_def(dst_remainder, regs::rdx());
906 }
907 Inst::CheckedSRemSeq8 {
908 divisor,
909 dividend,
910 dst,
911 ..
912 } => {
913 collector.reg_use(divisor);
914 collector.reg_fixed_use(dividend, regs::rax());
915 collector.reg_fixed_def(dst, regs::rax());
916 }
917 Inst::XmmUninitializedValue { dst } => collector.reg_def(dst),
918 Inst::GprUninitializedValue { dst } => collector.reg_def(dst),
919 Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
920 collector.reg_use(rhs);
921 collector.reg_use(lhs);
922 collector.reg_reuse_def(dst, 0); }
924 Inst::MovFromPReg { dst, src } => {
925 debug_assert!(dst.to_reg().to_reg().is_virtual());
926 collector.reg_fixed_nonallocatable(*src);
927 collector.reg_def(dst);
928 }
929 Inst::MovToPReg { dst, src } => {
930 debug_assert!(src.to_reg().is_virtual());
931 collector.reg_use(src);
932 collector.reg_fixed_nonallocatable(*dst);
933 }
934 Inst::CvtUint64ToFloatSeq {
935 src,
936 dst,
937 tmp_gpr1,
938 tmp_gpr2,
939 ..
940 } => {
941 collector.reg_use(src);
942 collector.reg_early_def(dst);
943 collector.reg_early_def(tmp_gpr1);
944 collector.reg_early_def(tmp_gpr2);
945 }
946 Inst::CvtFloatToSintSeq {
947 src,
948 dst,
949 tmp_xmm,
950 tmp_gpr,
951 ..
952 } => {
953 collector.reg_use(src);
954 collector.reg_early_def(dst);
955 collector.reg_early_def(tmp_gpr);
956 collector.reg_early_def(tmp_xmm);
957 }
958 Inst::CvtFloatToUintSeq {
959 src,
960 dst,
961 tmp_gpr,
962 tmp_xmm,
963 tmp_xmm2,
964 ..
965 } => {
966 collector.reg_use(src);
967 collector.reg_early_def(dst);
968 collector.reg_early_def(tmp_gpr);
969 collector.reg_early_def(tmp_xmm);
970 collector.reg_early_def(tmp_xmm2);
971 }
972
973 Inst::XmmCmove {
974 consequent,
975 alternative,
976 dst,
977 ..
978 } => {
979 collector.reg_use(alternative);
980 collector.reg_reuse_def(dst, 0);
981 collector.reg_use(consequent);
982 }
983 Inst::StackProbeLoop { tmp, .. } => {
984 collector.reg_early_def(tmp);
985 }
986
987 Inst::CallKnown { info } => {
988 let CallInfo {
993 uses,
994 defs,
995 clobbers,
996 dest,
997 try_call_info,
998 ..
999 } = &mut **info;
1000 debug_assert_ne!(*dest, ExternalName::LibCall(LibCall::Probestack));
1001 for CallArgPair { vreg, preg } in uses {
1002 collector.reg_fixed_use(vreg, *preg);
1003 }
1004 for CallRetPair { vreg, location } in defs {
1005 match location {
1006 RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
1007 RetLocation::Stack(..) => collector.any_def(vreg),
1008 }
1009 }
1010 collector.reg_clobbers(*clobbers);
1011 if let Some(try_call_info) = try_call_info {
1012 try_call_info.collect_operands(collector);
1013 }
1014 }
1015
1016 Inst::CallUnknown { info } => {
1017 let CallInfo {
1018 uses,
1019 defs,
1020 clobbers,
1021 callee_conv,
1022 dest,
1023 try_call_info,
1024 ..
1025 } = &mut **info;
1026 match dest {
1027 RegMem::Reg { reg } if *callee_conv == CallConv::Winch => {
1028 collector.reg_fixed_use(reg, regs::r10());
1032 }
1033 _ => dest.get_operands(collector),
1034 }
1035 for CallArgPair { vreg, preg } in uses {
1036 collector.reg_fixed_use(vreg, *preg);
1037 }
1038 for CallRetPair { vreg, location } in defs {
1039 match location {
1040 RetLocation::Reg(preg, ..) => collector.reg_fixed_def(vreg, *preg),
1041 RetLocation::Stack(..) => collector.any_def(vreg),
1042 }
1043 }
1044 collector.reg_clobbers(*clobbers);
1045 if let Some(try_call_info) = try_call_info {
1046 try_call_info.collect_operands(collector);
1047 }
1048 }
1049 Inst::StackSwitchBasic {
1050 store_context_ptr,
1051 load_context_ptr,
1052 in_payload0,
1053 out_payload0,
1054 } => {
1055 collector.reg_use(load_context_ptr);
1056 collector.reg_use(store_context_ptr);
1057 collector.reg_fixed_use(in_payload0, stack_switch::payload_register());
1058 collector.reg_fixed_def(out_payload0, stack_switch::payload_register());
1059
1060 let mut clobbers = crate::isa::x64::abi::ALL_CLOBBERS;
1061 clobbers.remove(
1063 stack_switch::payload_register()
1064 .to_real_reg()
1065 .unwrap()
1066 .into(),
1067 );
1068 collector.reg_clobbers(clobbers);
1069 }
1070
1071 Inst::InterruptPoll {
1072 dst,
1073 load_ptr,
1074 context,
1075 trap_code: _,
1076 next_load_ptr,
1077 } => {
1078 collector.reg_fixed_use(load_ptr, regs::r11());
1082 collector.reg_fixed_use(context, regs::rdi());
1084 collector.reg_fixed_def(dst, regs::r10());
1098 collector.reg_fixed_def(next_load_ptr, regs::r11());
1104 }
1105
1106 Inst::ReturnCallKnown { info } => {
1107 let ReturnCallInfo {
1108 dest, uses, tmp, ..
1109 } = &mut **info;
1110 collector.reg_fixed_def(tmp, regs::r11());
1111 debug_assert_ne!(*dest, ExternalName::LibCall(LibCall::Probestack));
1113 for CallArgPair { vreg, preg } in uses {
1114 collector.reg_fixed_use(vreg, *preg);
1115 }
1116 }
1117
1118 Inst::ReturnCallUnknown { info } => {
1119 let ReturnCallInfo {
1120 dest, uses, tmp, ..
1121 } = &mut **info;
1122
1123 collector.reg_fixed_use(dest, regs::r10());
1129
1130 collector.reg_fixed_def(tmp, regs::r11());
1131 for CallArgPair { vreg, preg } in uses {
1132 collector.reg_fixed_use(vreg, *preg);
1133 }
1134 }
1135
1136 Inst::JmpTableSeq {
1137 idx, tmp1, tmp2, ..
1138 } => {
1139 collector.reg_use(idx);
1140 collector.reg_early_def(tmp1);
1141 collector.reg_def(tmp2);
1145 }
1146
1147 Inst::LoadExtName { dst, .. } => {
1148 collector.reg_def(dst);
1149 }
1150
1151 Inst::AtomicRmwSeq {
1152 operand,
1153 temp,
1154 dst_old,
1155 mem,
1156 ..
1157 } => {
1158 collector.reg_late_use(operand);
1159 collector.reg_early_def(temp);
1160 collector.reg_fixed_def(dst_old, regs::rax());
1163 mem.get_operands_late(collector)
1164 }
1165
1166 Inst::Atomic128RmwSeq { args } => {
1167 let Atomic128RmwSeqArgs {
1168 mem_low,
1169 mem_high,
1170 operand_low,
1171 operand_high,
1172 temp_low,
1173 temp_high,
1174 dst_old_low,
1175 dst_old_high,
1176 op: _,
1177 } = &mut **args;
1178 collector.reg_late_use(operand_low);
1180 collector.reg_late_use(operand_high);
1181 collector.reg_fixed_def(temp_low, regs::rbx());
1182 collector.reg_fixed_def(temp_high, regs::rcx());
1183 collector.reg_fixed_def(dst_old_low, regs::rax());
1184 collector.reg_fixed_def(dst_old_high, regs::rdx());
1185 mem_low.get_operands_late(collector);
1186 mem_high.get_operands_late(collector);
1187 }
1188
1189 Inst::Atomic128XchgSeq { args } => {
1190 let Atomic128XchgSeqArgs {
1191 mem_low,
1192 mem_high,
1193 operand_low,
1194 operand_high,
1195 dst_old_low,
1196 dst_old_high,
1197 } = &mut **args;
1198 collector.reg_fixed_late_use(operand_low, regs::rbx());
1200 collector.reg_fixed_late_use(operand_high, regs::rcx());
1201 collector.reg_fixed_def(dst_old_low, regs::rax());
1202 collector.reg_fixed_def(dst_old_high, regs::rdx());
1203 mem_low.get_operands_late(collector);
1204 mem_high.get_operands_late(collector);
1205 }
1206
1207 Inst::Args { args } => {
1208 for ArgPair { vreg, preg } in args {
1209 collector.reg_fixed_def(vreg, *preg);
1210 }
1211 }
1212
1213 Inst::Rets { rets } => {
1214 for RetPair { vreg, preg } in rets {
1217 collector.reg_fixed_use(vreg, *preg);
1218 }
1219 }
1220
1221 Inst::JmpKnown { .. }
1222 | Inst::WinchJmpIf { .. }
1223 | Inst::JmpCond { .. }
1224 | Inst::JmpCondOr { .. }
1225 | Inst::TrapIf { .. }
1226 | Inst::TrapIfAnd { .. }
1227 | Inst::TrapIfOr { .. } => {
1228 }
1230
1231 Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => {
1232 collector.reg_fixed_def(dst, regs::rax());
1233 let mut clobbers =
1240 X64ABIMachineSpec::get_regs_clobbered_by_call(CallConv::SystemV, false);
1241 clobbers.remove(regs::gpr_preg(asm::gpr::enc::RAX));
1242 collector.reg_clobbers(clobbers);
1243 }
1244
1245 Inst::CoffTlsGetAddr { dst, tmp, .. } => {
1246 collector.reg_fixed_def(dst, regs::rax());
1251
1252 collector.reg_fixed_def(tmp, regs::rcx());
1254 }
1255
1256 Inst::Unwind { .. } => {}
1257
1258 Inst::DummyUse { reg } => {
1259 collector.reg_use(reg);
1260 }
1261
1262 Inst::LabelAddress { dst, .. } => {
1263 collector.reg_def(dst);
1264 }
1265
1266 Inst::SequencePoint { .. } => {}
1267
1268 Inst::External { inst } => {
1269 inst.visit(&mut external::RegallocVisitor { collector });
1270 }
1271 }
1272}
1273
1274impl MachInst for Inst {
1278 type ABIMachineSpec = X64ABIMachineSpec;
1279
1280 fn get_operands(&mut self, collector: &mut impl OperandVisitor) {
1281 x64_get_operands(self, collector)
1282 }
1283
1284 fn is_move(&self) -> Option<(Writable<Reg>, Reg)> {
1285 use asm::inst::Inst as I;
1286 match self {
1287 Self::External {
1292 inst: I::movq_mr(asm::inst::movq_mr { rm64, r64 }),
1293 } => match rm64 {
1294 asm::GprMem::Gpr(reg) => Some((reg.map(|r| r.to_reg()), r64.as_ref().to_reg())),
1295 asm::GprMem::Mem(_) => None,
1296 },
1297 Self::External {
1298 inst: I::movq_rm(asm::inst::movq_rm { r64, rm64 }),
1299 } => match rm64 {
1300 asm::GprMem::Gpr(reg) => Some((r64.as_ref().map(|r| r.to_reg()), reg.to_reg())),
1301 asm::GprMem::Mem(_) => None,
1302 },
1303
1304 Self::External {
1313 inst:
1314 I::movaps_a(asm::inst::movaps_a { xmm1, xmm_m128 })
1315 | I::movups_a(asm::inst::movups_a { xmm1, xmm_m128 })
1316 | I::movapd_a(asm::inst::movapd_a { xmm1, xmm_m128 })
1317 | I::movupd_a(asm::inst::movupd_a { xmm1, xmm_m128 })
1318 | I::movdqa_a(asm::inst::movdqa_a { xmm1, xmm_m128 })
1319 | I::movdqu_a(asm::inst::movdqu_a { xmm1, xmm_m128 }),
1320 } => match xmm_m128 {
1321 asm::XmmMem::Xmm(xmm2) => Some((xmm1.as_ref().map(|r| r.to_reg()), xmm2.to_reg())),
1322 asm::XmmMem::Mem(_) => None,
1323 },
1324 Self::External {
1328 inst:
1329 I::movaps_b(asm::inst::movaps_b { xmm_m128, xmm1 })
1330 | I::movups_b(asm::inst::movups_b { xmm_m128, xmm1 })
1331 | I::movapd_b(asm::inst::movapd_b { xmm_m128, xmm1 })
1332 | I::movupd_b(asm::inst::movupd_b { xmm_m128, xmm1 })
1333 | I::movdqa_b(asm::inst::movdqa_b { xmm_m128, xmm1 })
1334 | I::movdqu_b(asm::inst::movdqu_b { xmm_m128, xmm1 }),
1335 } => match xmm_m128 {
1336 asm::XmmMem::Xmm(dst) => Some((dst.map(|r| r.to_reg()), xmm1.as_ref().to_reg())),
1337 asm::XmmMem::Mem(_) => None,
1338 },
1339 _ => None,
1340 }
1341 }
1342
1343 fn is_included_in_clobbers(&self) -> bool {
1344 match self {
1345 &Inst::Args { .. } => false,
1346 _ => true,
1347 }
1348 }
1349
1350 fn is_trap(&self) -> bool {
1351 match self {
1352 Self::External {
1353 inst: asm::inst::Inst::ud2_zo(..),
1354 } => true,
1355 _ => false,
1356 }
1357 }
1358
1359 fn is_args(&self) -> bool {
1360 match self {
1361 Self::Args { .. } => true,
1362 _ => false,
1363 }
1364 }
1365
1366 fn call_type(&self) -> CallType {
1367 match self {
1368 Inst::CallKnown { .. }
1369 | Inst::CallUnknown { .. }
1370 | Inst::ElfTlsGetAddr { .. }
1371 | Inst::MachOTlsGetAddr { .. } => CallType::Regular,
1372
1373 Inst::ReturnCallKnown { .. } | Inst::ReturnCallUnknown { .. } => CallType::TailCall,
1374
1375 _ => CallType::None,
1376 }
1377 }
1378
1379 fn is_term(&self) -> MachTerminator {
1380 match self {
1381 &Self::Rets { .. } => MachTerminator::Ret,
1383 &Self::ReturnCallKnown { .. } | &Self::ReturnCallUnknown { .. } => {
1384 MachTerminator::RetCall
1385 }
1386 &Self::JmpKnown { .. } => MachTerminator::Branch,
1387 &Self::JmpCond { .. } => MachTerminator::Branch,
1388 &Self::JmpCondOr { .. } => MachTerminator::Branch,
1389 &Self::JmpTableSeq { .. } => MachTerminator::Branch,
1390 &Self::CallKnown { ref info } if info.try_call_info.is_some() => MachTerminator::Branch,
1391 &Self::CallUnknown { ref info } if info.try_call_info.is_some() => {
1392 MachTerminator::Branch
1393 }
1394 _ => MachTerminator::None,
1396 }
1397 }
1398
1399 fn is_low_level_branch(&self) -> bool {
1400 match self {
1401 &Self::WinchJmpIf { .. } => true,
1402 _ => false,
1403 }
1404 }
1405
1406 fn is_mem_access(&self) -> bool {
1407 panic!("TODO FILL ME OUT")
1408 }
1409
1410 fn gen_move(dst_reg: Writable<Reg>, src_reg: Reg, ty: Type) -> Inst {
1411 trace!(
1412 "Inst::gen_move {:?} -> {:?} (type: {:?})",
1413 src_reg,
1414 dst_reg.to_reg(),
1415 ty
1416 );
1417 let rc_dst = dst_reg.to_reg().class();
1418 let rc_src = src_reg.class();
1419 debug_assert!(rc_dst == rc_src);
1421 let inst = match rc_dst {
1422 RegClass::Int => {
1423 asm::inst::movq_mr::new(dst_reg.map(Gpr::unwrap_new), Gpr::unwrap_new(src_reg))
1424 .into()
1425 }
1426 RegClass::Float => {
1427 let dst_reg = dst_reg.map(|r| Xmm::new(r).unwrap());
1432 let src_reg = Xmm::new(src_reg).unwrap();
1433 match ty {
1434 types::F16 | types::F32 | types::F64 | types::F32X4 => {
1435 asm::inst::movaps_a::new(dst_reg, src_reg).into()
1436 }
1437 types::F64X2 => asm::inst::movapd_a::new(dst_reg, src_reg).into(),
1438 _ if (ty.is_float() || ty.is_vector()) && ty.bits() <= 128 => {
1439 asm::inst::movdqa_a::new(dst_reg, src_reg).into()
1440 }
1441 _ => unimplemented!("unable to move type: {}", ty),
1442 }
1443 }
1444 RegClass::Vector => unreachable!(),
1445 };
1446 Inst::External { inst }
1447 }
1448
1449 fn gen_nop(preferred_size: usize) -> Inst {
1450 Inst::nop(core::cmp::min(preferred_size, 9) as u8)
1451 }
1452
1453 fn gen_nop_units() -> Vec<Vec<u8>> {
1454 vec![
1455 vec![0x90],
1457 vec![0x0f, 0x1f, 0x44, 0x00, 0x00],
1459 ]
1460 }
1461
1462 fn rc_for_type(ty: &Type) -> CodegenResult<(&[RegClass], &[Type])> {
1463 match *ty {
1464 types::I8 | types::I16 | types::I32 | types::I64 => {
1465 Ok((&[RegClass::Int], core::slice::from_ref(ty)))
1466 }
1467 types::F16 | types::F32 | types::F64 | types::F128 => {
1468 Ok((&[RegClass::Float], core::slice::from_ref(ty)))
1469 }
1470 types::I128 => Ok((&[RegClass::Int, RegClass::Int], &[types::I64, types::I64])),
1471 _ if ty.is_vector() && ty.bits() <= 128 => {
1472 let types = &[types::I8X2, types::I8X4, types::I8X8, types::I8X16];
1473 Ok((
1474 &[RegClass::Float],
1475 slice::from_ref(&types[ty.bytes().ilog2() as usize - 1]),
1476 ))
1477 }
1478 _ => Err(CodegenError::Unsupported(format!(
1479 "Unexpected SSA-value type: {ty}"
1480 ))),
1481 }
1482 }
1483
1484 fn canonical_type_for_rc(rc: RegClass) -> Type {
1485 match rc {
1486 RegClass::Float => types::I8X16,
1487 RegClass::Int => types::I64,
1488 RegClass::Vector => unreachable!(),
1489 }
1490 }
1491
1492 fn gen_jump(label: MachLabel) -> Inst {
1493 Inst::jmp_known(label)
1494 }
1495
1496 fn gen_imm_u64(value: u64, dst: Writable<Reg>) -> Option<Self> {
1497 Some(Inst::imm(OperandSize::Size64, value, dst))
1498 }
1499
1500 fn gen_imm_f64(value: f64, tmp: Writable<Reg>, dst: Writable<Reg>) -> SmallVec<[Self; 2]> {
1501 let imm_to_gpr = Inst::imm(OperandSize::Size64, value.to_bits(), tmp);
1502 let gpr_to_xmm = Inst::External {
1503 inst: asm::inst::movq_a::new(dst.map(|r| Xmm::new(r).unwrap()), tmp.to_reg()).into(),
1504 };
1505 smallvec![imm_to_gpr, gpr_to_xmm]
1506 }
1507
1508 fn gen_dummy_use(reg: Reg) -> Self {
1509 Inst::DummyUse { reg }
1510 }
1511
1512 fn worst_case_size() -> CodeOffset {
1513 15
1514 }
1515
1516 fn worst_case_island_growth() -> CodeOffset {
1517 0
1518 }
1519
1520 fn is_safepoint(&self) -> bool {
1521 match self {
1522 Inst::CallKnown { .. } | Inst::CallUnknown { .. } | Inst::StackSwitchBasic { .. } => {
1523 true
1524 }
1525 _ => false,
1526 }
1527 }
1528
1529 fn function_alignment() -> FunctionAlignment {
1530 FunctionAlignment {
1531 minimum: 1,
1532 preferred: 32,
1535 }
1536 }
1537
1538 type LabelUse = LabelUse;
1539
1540 const TRAP_OPCODE: &'static [u8] = &[0x0f, 0x0b];
1541}
1542
1543pub struct EmitInfo {
1545 pub(super) flags: settings::Flags,
1546 isa_flags: x64_settings::Flags,
1547}
1548
1549impl EmitInfo {
1550 pub fn new(flags: settings::Flags, isa_flags: x64_settings::Flags) -> Self {
1552 Self { flags, isa_flags }
1553 }
1554}
1555
1556impl asm::AvailableFeatures for &EmitInfo {
1557 fn _64b(&self) -> bool {
1558 true
1560 }
1561
1562 fn compat(&self) -> bool {
1563 false
1566 }
1567
1568 fn sse(&self) -> bool {
1569 true
1571 }
1572
1573 fn sse2(&self) -> bool {
1574 true
1576 }
1577
1578 fn sse3(&self) -> bool {
1579 self.isa_flags.has_sse3()
1580 }
1581
1582 fn ssse3(&self) -> bool {
1583 self.isa_flags.has_ssse3()
1584 }
1585
1586 fn sse41(&self) -> bool {
1587 self.isa_flags.has_sse41()
1588 }
1589
1590 fn sse42(&self) -> bool {
1591 self.isa_flags.has_sse42()
1592 }
1593
1594 fn bmi1(&self) -> bool {
1595 self.isa_flags.has_bmi1()
1596 }
1597
1598 fn bmi2(&self) -> bool {
1599 self.isa_flags.has_bmi2()
1600 }
1601
1602 fn lzcnt(&self) -> bool {
1603 self.isa_flags.has_lzcnt()
1604 }
1605
1606 fn popcnt(&self) -> bool {
1607 self.isa_flags.has_popcnt()
1608 }
1609
1610 fn avx(&self) -> bool {
1611 self.isa_flags.has_avx()
1612 }
1613
1614 fn avx2(&self) -> bool {
1615 self.isa_flags.has_avx2()
1616 }
1617
1618 fn avx512f(&self) -> bool {
1619 self.isa_flags.has_avx512f()
1620 }
1621
1622 fn avx512vl(&self) -> bool {
1623 self.isa_flags.has_avx512vl()
1624 }
1625
1626 fn cmpxchg16b(&self) -> bool {
1627 self.isa_flags.has_cmpxchg16b()
1628 }
1629
1630 fn fma(&self) -> bool {
1631 self.isa_flags.has_fma()
1632 }
1633 fn avx_vnni(&self) -> bool {
1634 self.isa_flags.has_avx_vnni()
1635 }
1636
1637 fn avx512dq(&self) -> bool {
1638 self.isa_flags.has_avx512dq()
1639 }
1640
1641 fn avx512bitalg(&self) -> bool {
1642 self.isa_flags.has_avx512bitalg()
1643 }
1644
1645 fn avx512vbmi(&self) -> bool {
1646 self.isa_flags.has_avx512vbmi()
1647 }
1648
1649 fn avx512vnni(&self) -> bool {
1650 self.isa_flags.has_avx512vnni()
1651 }
1652
1653 fn apx(&self) -> bool {
1654 self.isa_flags.has_apx()
1655 }
1656}
1657
1658impl MachInstEmit for Inst {
1659 type State = EmitState;
1660 type Info = EmitInfo;
1661
1662 fn emit(&self, sink: &mut MachBuffer<Inst>, info: &Self::Info, state: &mut Self::State) {
1663 emit::emit(self, sink, info, state);
1664 }
1665
1666 fn pretty_print_inst(&self, _: &mut Self::State) -> String {
1667 PrettyPrint::pretty_print(self, 0)
1668 }
1669}
1670
1671#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1673pub enum LabelUse {
1674 JmpRel32,
1678
1679 PCRel32,
1682}
1683
1684impl MachInstLabelUse for LabelUse {
1685 const ALIGN: CodeOffset = 1;
1686
1687 fn max_pos_range(self) -> CodeOffset {
1688 match self {
1689 LabelUse::JmpRel32 | LabelUse::PCRel32 => 0x7fff_ffff,
1690 }
1691 }
1692
1693 fn max_neg_range(self) -> CodeOffset {
1694 match self {
1695 LabelUse::JmpRel32 | LabelUse::PCRel32 => 0x8000_0000,
1696 }
1697 }
1698
1699 fn patch_size(self) -> CodeOffset {
1700 match self {
1701 LabelUse::JmpRel32 | LabelUse::PCRel32 => 4,
1702 }
1703 }
1704
1705 fn patch(self, buffer: &mut [u8], use_offset: CodeOffset, label_offset: CodeOffset) {
1706 let pc_rel = (label_offset as i64) - (use_offset as i64);
1707 debug_assert!(pc_rel <= self.max_pos_range() as i64);
1708 debug_assert!(pc_rel >= -(self.max_neg_range() as i64));
1709 let pc_rel = pc_rel as u32;
1710 match self {
1711 LabelUse::JmpRel32 => {
1712 let addend = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
1713 let value = pc_rel.wrapping_add(addend).wrapping_sub(4);
1714 buffer.copy_from_slice(&value.to_le_bytes()[..]);
1715 }
1716 LabelUse::PCRel32 => {
1717 let addend = u32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]);
1718 let value = pc_rel.wrapping_add(addend);
1719 buffer.copy_from_slice(&value.to_le_bytes()[..]);
1720 }
1721 }
1722 }
1723
1724 fn supports_veneer(self) -> bool {
1725 match self {
1726 LabelUse::JmpRel32 | LabelUse::PCRel32 => false,
1727 }
1728 }
1729
1730 fn veneer_size(self) -> CodeOffset {
1731 match self {
1732 LabelUse::JmpRel32 | LabelUse::PCRel32 => 0,
1733 }
1734 }
1735
1736 fn worst_case_veneer_size() -> CodeOffset {
1737 0
1738 }
1739
1740 fn generate_veneer(self, _: &mut [u8], _: CodeOffset) -> (CodeOffset, LabelUse) {
1741 match self {
1742 LabelUse::JmpRel32 | LabelUse::PCRel32 => {
1743 panic!("Veneer not supported for JumpRel32 label-use.");
1744 }
1745 }
1746 }
1747
1748 fn from_reloc(reloc: Reloc, addend: Addend) -> Option<Self> {
1749 match (reloc, addend) {
1750 (Reloc::X86PCRel4 | Reloc::X86CallPCRel4, -4) => Some(LabelUse::JmpRel32),
1751 _ => None,
1752 }
1753 }
1754}