1use core::cmp::Reverse;
4
5use crate::CodegenResult;
6use crate::FxHashSet;
7use crate::ir;
8use crate::ir::MemFlagsData;
9use crate::ir::types;
10use crate::ir::types::*;
11use crate::ir::{ExternalName, LibCall, Signature, dynamic_to_fixed};
12use crate::isa;
13use crate::isa::aarch64::{inst::*, settings as aarch64_settings};
14use crate::isa::unwind::UnwindInst;
15use crate::isa::winch;
16use crate::machinst::*;
17use crate::settings;
18use alloc::borrow::ToOwned;
19use alloc::boxed::Box;
20use alloc::vec::Vec;
21use regalloc2::{MachineEnv, PReg, PRegSet};
22use smallvec::{SmallVec, smallvec};
23
24pub(crate) type AArch64Callee = Callee<AArch64MachineDeps>;
29
30impl From<StackAMode> for AMode {
31 fn from(stack: StackAMode) -> AMode {
32 match stack {
33 StackAMode::IncomingArg(off, stack_args_size) => AMode::IncomingArg {
34 off: i64::from(stack_args_size) - off,
35 },
36 StackAMode::Slot(off) => AMode::SlotOffset { off },
37 StackAMode::OutgoingArg(off) => AMode::SPOffset { off },
38 }
39 }
40}
41
42fn compute_clobber_size(
45 call_conv: isa::CallConv,
46 clobbered_callee_saves: &[Writable<RealReg>],
47) -> u32 {
48 let mut int_regs = 0;
49 let mut vec_regs = 0;
50 for ® in clobbered_callee_saves {
51 match reg.to_reg().class() {
52 RegClass::Int => {
53 int_regs += 1;
54 }
55 RegClass::Float => {
56 vec_regs += 1;
57 }
58 RegClass::Vector => unreachable!(),
59 }
60 }
61
62 let int_save_bytes = (int_regs + (int_regs & 1)) * 8;
64 let vec_save_bytes = if call_conv == isa::CallConv::PreserveAll {
65 vec_regs * 16
68 } else {
69 let vec_reg_size = 8;
76 let vec_save_padding = vec_regs & 1;
77 (vec_regs + vec_save_padding) * vec_reg_size
79 };
80
81 int_save_bytes + vec_save_bytes
82}
83
84fn add_macho_compact_unwind_paired_regs(regs: &mut Vec<Writable<RealReg>>) {
87 let int_regs: FxHashSet<_> = regs
88 .iter()
89 .filter_map(|r| {
90 if r.to_reg().class() == RegClass::Int {
91 Some(r.to_reg().hw_enc())
92 } else {
93 None
94 }
95 })
96 .collect();
97 for (a, b) in [(19, 20), (21, 22), (23, 24), (25, 26), (27, 28)] {
98 if int_regs.contains(&a) && !int_regs.contains(&b) {
99 regs.push(Writable::from_reg(xreg(b).to_real_reg().unwrap()))
100 } else if int_regs.contains(&b) && !int_regs.contains(&a) {
101 regs.push(Writable::from_reg(xreg(a).to_real_reg().unwrap()))
102 }
103 }
104
105 let fp_regs: FxHashSet<_> = regs
106 .iter()
107 .filter_map(|r| {
108 if r.to_reg().class() == RegClass::Float {
109 Some(r.to_reg().hw_enc())
110 } else {
111 None
112 }
113 })
114 .collect();
115 for (a, b) in [(8, 9), (10, 11), (12, 13), (14, 15)] {
116 if fp_regs.contains(&a) && !fp_regs.contains(&b) {
117 regs.push(Writable::from_reg(vreg(b).to_real_reg().unwrap()))
118 } else if fp_regs.contains(&b) && !fp_regs.contains(&a) {
119 regs.push(Writable::from_reg(vreg(a).to_real_reg().unwrap()))
120 }
121 }
122}
123
124pub struct AArch64MachineDeps;
127
128impl IsaFlags for aarch64_settings::Flags {
129 fn is_forward_edge_cfi_enabled(&self) -> bool {
130 self.use_bti()
131 }
132}
133
134impl ABIMachineSpec for AArch64MachineDeps {
135 type I = Inst;
136
137 type F = aarch64_settings::Flags;
138
139 const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
143
144 fn word_bits() -> u32 {
145 64
146 }
147
148 fn stack_align(_call_conv: isa::CallConv) -> u32 {
150 16
151 }
152
153 fn compute_arg_locs(
154 call_conv: isa::CallConv,
155 flags: &settings::Flags,
156 params: &[ir::AbiParam],
157 args_or_rets: ArgsOrRets,
158 add_ret_area_ptr: bool,
159 mut args: ArgsAccumulator,
160 ) -> CodegenResult<(u32, Option<usize>)> {
161 let is_apple_cc = call_conv == isa::CallConv::AppleAarch64;
162 let is_winch_return = call_conv == isa::CallConv::Winch && args_or_rets == ArgsOrRets::Rets;
163
164 let mut next_xreg = if call_conv == isa::CallConv::Tail {
179 2
189 } else {
190 0
191 };
192 let mut next_vreg = 0;
193 let mut next_stack: u32 = 0;
194
195 let max_per_class_reg_vals = 8; let mut remaining_reg_vals = 16;
202
203 let ret_area_ptr = if add_ret_area_ptr {
204 debug_assert_eq!(args_or_rets, ArgsOrRets::Args);
205 if call_conv != isa::CallConv::Winch {
206 Some(ABIArg::reg(
209 xreg(8).to_real_reg().unwrap(),
210 I64,
211 ir::ArgumentExtension::None,
212 ir::ArgumentPurpose::Normal,
213 ))
214 } else {
215 next_xreg += 1;
219 Some(ABIArg::reg(
220 xreg(0).to_real_reg().unwrap(),
221 I64,
222 ir::ArgumentExtension::None,
223 ir::ArgumentPurpose::Normal,
224 ))
225 }
226 } else {
227 None
228 };
229
230 for (i, param) in params.into_iter().enumerate() {
231 if is_apple_cc && param.value_type == types::F128 && !flags.enable_llvm_abi_extensions()
232 {
233 panic!(
234 "f128 args/return values not supported for apple_aarch64 unless LLVM ABI extensions are enabled"
235 );
236 }
237
238 let (rcs, reg_types) = Inst::rc_for_type(¶m.value_type)?;
239
240 if let ir::ArgumentPurpose::StructReturn = param.purpose {
241 assert!(
242 call_conv != isa::CallConv::Tail,
243 "support for StructReturn parameters is not implemented for the `tail` \
244 calling convention yet",
245 );
246 }
247
248 if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {
249 panic!(
250 "StructArgument parameters are not supported on arm64. \
251 Use regular pointer arguments instead."
252 );
253 }
254
255 if let ir::ArgumentPurpose::StructReturn = param.purpose {
256 assert!(
259 param.value_type == types::I64,
260 "StructReturn must be a pointer sized integer"
261 );
262 args.push(ABIArg::Slots {
263 slots: smallvec![ABIArgSlot::Reg {
264 reg: xreg(8).to_real_reg().unwrap(),
265 ty: types::I64,
266 extension: param.extension,
267 },],
268 purpose: ir::ArgumentPurpose::StructReturn,
269 });
270 continue;
271 }
272
273 let is_multi_reg = rcs.len() >= 2;
294 if is_multi_reg {
295 assert!(
296 rcs.len() == 2,
297 "Unable to handle multi reg params with more than 2 regs"
298 );
299 assert!(
300 rcs == &[RegClass::Int, RegClass::Int],
301 "Unable to handle non i64 regs"
302 );
303
304 let reg_class_space = max_per_class_reg_vals - next_xreg;
305 let reg_space = remaining_reg_vals;
306
307 if reg_space >= 2 && reg_class_space >= 2 {
308 if !is_apple_cc && next_xreg % 2 != 0 {
313 next_xreg += 1;
314 }
315
316 let lower_reg = xreg(next_xreg);
317 let upper_reg = xreg(next_xreg + 1);
318
319 args.push(ABIArg::Slots {
320 slots: smallvec![
321 ABIArgSlot::Reg {
322 reg: lower_reg.to_real_reg().unwrap(),
323 ty: reg_types[0],
324 extension: param.extension,
325 },
326 ABIArgSlot::Reg {
327 reg: upper_reg.to_real_reg().unwrap(),
328 ty: reg_types[1],
329 extension: param.extension,
330 },
331 ],
332 purpose: param.purpose,
333 });
334
335 next_xreg += 2;
336 remaining_reg_vals -= 2;
337 continue;
338 }
339 } else {
340 let rc = rcs[0];
342 let next_reg = match rc {
343 RegClass::Int => &mut next_xreg,
344 RegClass::Float => &mut next_vreg,
345 RegClass::Vector => unreachable!(),
346 };
347
348 let push_to_reg = if is_winch_return {
349 i == params.len() - 1
351 } else {
352 *next_reg < max_per_class_reg_vals && remaining_reg_vals > 0
354 };
355
356 if push_to_reg {
357 let reg = match rc {
358 RegClass::Int => xreg(*next_reg),
359 RegClass::Float => vreg(*next_reg),
360 RegClass::Vector => unreachable!(),
361 };
362 let ty = if param.value_type.is_dynamic_vector() {
364 dynamic_to_fixed(param.value_type)
365 } else {
366 param.value_type
367 };
368 args.push(ABIArg::reg(
369 reg.to_real_reg().unwrap(),
370 ty,
371 param.extension,
372 param.purpose,
373 ));
374 *next_reg += 1;
375 remaining_reg_vals -= 1;
376 continue;
377 }
378 }
379
380 if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
383 return Err(crate::CodegenError::Unsupported(
384 "Too many return values to fit in registers. \
385 Use a StructReturn argument instead. (#9510)"
386 .to_owned(),
387 ));
388 }
389
390 let size = (ty_bits(param.value_type) / 8) as u32;
392
393 let size = if (is_apple_cc || is_winch_return)
403 && (args_or_rets == ArgsOrRets::Args
404 || param.extension == ir::ArgumentExtension::None)
405 {
406 size
407 } else {
408 core::cmp::max(size, 8)
409 };
410
411 if !is_winch_return {
412 debug_assert!(size.is_power_of_two());
414 next_stack = align_to(next_stack, size);
415 }
416
417 let slots = reg_types
418 .iter()
419 .copied()
420 .scan(next_stack, |next_stack, ty| {
422 let slot_offset = *next_stack as i64;
423 *next_stack += (ty_bits(ty) / 8) as u32;
424
425 Some((ty, slot_offset))
426 })
427 .map(|(ty, offset)| ABIArgSlot::Stack {
428 offset,
429 ty,
430 extension: param.extension,
431 })
432 .collect();
433
434 args.push(ABIArg::Slots {
435 slots,
436 purpose: param.purpose,
437 });
438
439 next_stack += size;
440 }
441
442 let extra_arg = if let Some(ret_area_ptr) = ret_area_ptr {
443 args.push_non_formal(ret_area_ptr);
444 Some(args.args().len() - 1)
445 } else {
446 None
447 };
448
449 if is_winch_return {
450 winch::reverse_stack(args, next_stack, false);
451 }
452
453 next_stack = align_to(next_stack, 16);
454
455 Ok((next_stack, extra_arg))
456 }
457
458 fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
459 Inst::gen_load(into_reg, mem.into(), ty, MemFlagsData::trusted())
460 }
461
462 fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
463 Inst::gen_store(mem.into(), from_reg, ty, MemFlagsData::trusted())
464 }
465
466 fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
467 Inst::gen_move(to_reg, from_reg, ty)
468 }
469
470 fn gen_extend(
471 to_reg: Writable<Reg>,
472 from_reg: Reg,
473 signed: bool,
474 from_bits: u8,
475 to_bits: u8,
476 ) -> Inst {
477 assert!(from_bits < to_bits);
478 Inst::Extend {
479 rd: to_reg,
480 rn: from_reg,
481 signed,
482 from_bits,
483 to_bits,
484 }
485 }
486
487 fn gen_args(args: Vec<ArgPair>) -> Inst {
488 Inst::Args { args }
489 }
490
491 fn gen_rets(rets: Vec<RetPair>) -> Inst {
492 Inst::Rets { rets }
493 }
494
495 fn gen_add_imm(
496 _call_conv: isa::CallConv,
497 into_reg: Writable<Reg>,
498 from_reg: Reg,
499 imm: u32,
500 ) -> SmallInstVec<Inst> {
501 let imm = imm as u64;
502 let mut insts = SmallVec::new();
503 if let Some(imm12) = Imm12::maybe_from_u64(imm) {
504 insts.push(Inst::AluRRImm12 {
505 alu_op: ALUOp::Add,
506 size: OperandSize::Size64,
507 rd: into_reg,
508 rn: from_reg,
509 imm12,
510 });
511 } else {
512 let scratch2 = writable_tmp2_reg();
513 assert_ne!(scratch2.to_reg(), from_reg);
514 insts.extend(Inst::load_constant(scratch2, imm));
518 insts.push(Inst::AluRRRExtend {
519 alu_op: ALUOp::Add,
520 size: OperandSize::Size64,
521 rd: into_reg,
522 rn: from_reg,
523 rm: scratch2.to_reg(),
524 extendop: ExtendOp::UXTX,
525 });
526 }
527 insts
528 }
529
530 fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
531 let mut insts = SmallVec::new();
532 insts.push(Inst::AluRRRExtend {
533 alu_op: ALUOp::SubS,
534 size: OperandSize::Size64,
535 rd: writable_zero_reg(),
536 rn: stack_reg(),
537 rm: limit_reg,
538 extendop: ExtendOp::UXTX,
539 });
540 insts.push(Inst::TrapIf {
541 trap_code: ir::TrapCode::STACK_OVERFLOW,
542 kind: CondBrKind::Cond(Cond::Lo),
545 });
546 insts
547 }
548
549 fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
550 let mem = mem.into();
552 Inst::LoadAddr { rd: into_reg, mem }
553 }
554
555 fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
556 spilltmp_reg()
557 }
558
559 fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
560 let mem = AMode::RegOffset {
561 rn: base,
562 off: offset as i64,
563 };
564 Inst::gen_load(into_reg, mem, ty, MemFlagsData::trusted())
565 }
566
567 fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
568 let mem = AMode::RegOffset {
569 rn: base,
570 off: offset as i64,
571 };
572 Inst::gen_store(mem, from_reg, ty, MemFlagsData::trusted())
573 }
574
575 fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {
576 if amount == 0 {
577 return SmallVec::new();
578 }
579
580 let (amount, is_sub) = if amount > 0 {
581 (amount as u64, false)
582 } else {
583 (-amount as u64, true)
584 };
585
586 let alu_op = if is_sub { ALUOp::Sub } else { ALUOp::Add };
587
588 let mut ret = SmallVec::new();
589 if let Some(imm12) = Imm12::maybe_from_u64(amount) {
590 let adj_inst = Inst::AluRRImm12 {
591 alu_op,
592 size: OperandSize::Size64,
593 rd: writable_stack_reg(),
594 rn: stack_reg(),
595 imm12,
596 };
597 ret.push(adj_inst);
598 } else {
599 let tmp = writable_spilltmp_reg();
600 let const_inst = Inst::load_constant(tmp, amount);
603 let adj_inst = Inst::AluRRRExtend {
604 alu_op,
605 size: OperandSize::Size64,
606 rd: writable_stack_reg(),
607 rn: stack_reg(),
608 rm: tmp.to_reg(),
609 extendop: ExtendOp::UXTX,
610 };
611 ret.extend(const_inst);
612 ret.push(adj_inst);
613 }
614 ret
615 }
616
617 fn gen_prologue_frame_setup(
618 call_conv: isa::CallConv,
619 flags: &settings::Flags,
620 isa_flags: &aarch64_settings::Flags,
621 frame_layout: &FrameLayout,
622 ) -> SmallInstVec<Inst> {
623 let setup_frame = frame_layout.setup_area_size > 0;
624 let mut insts = SmallVec::new();
625
626 match Self::select_api_key(isa_flags, call_conv, setup_frame) {
627 Some(key) => {
628 insts.push(Inst::Paci { key });
629 if flags.unwind_info() {
630 insts.push(Inst::Unwind {
631 inst: UnwindInst::Aarch64SetPointerAuth {
632 return_addresses: true,
633 },
634 });
635 }
636 }
637 None => {
638 if isa_flags.use_bti() {
639 insts.push(Inst::Bti {
640 targets: BranchTargetType::C,
641 });
642 }
643
644 if flags.unwind_info() && call_conv == isa::CallConv::AppleAarch64 {
645 insts.push(Inst::Unwind {
647 inst: UnwindInst::Aarch64SetPointerAuth {
648 return_addresses: false,
649 },
650 });
651 }
652 }
653 }
654
655 if setup_frame {
656 insts.push(Inst::StoreP64 {
658 rt: fp_reg(),
659 rt2: link_reg(),
660 mem: PairAMode::SPPreIndexed {
661 simm7: SImm7Scaled::maybe_from_i64(-16, types::I64).unwrap(),
662 },
663 flags: MemFlagsData::trusted(),
664 });
665
666 if flags.unwind_info() {
667 insts.push(Inst::Unwind {
668 inst: UnwindInst::PushFrameRegs {
669 offset_upward_to_caller_sp: frame_layout.setup_area_size,
670 },
671 });
672 }
673
674 insts.push(Inst::AluRRImm12 {
677 alu_op: ALUOp::Add,
678 size: OperandSize::Size64,
679 rd: writable_fp_reg(),
680 rn: stack_reg(),
681 imm12: Imm12 {
682 bits: 0,
683 shift12: false,
684 },
685 });
686 }
687
688 insts
689 }
690
691 fn gen_epilogue_frame_restore(
692 call_conv: isa::CallConv,
693 _flags: &settings::Flags,
694 _isa_flags: &aarch64_settings::Flags,
695 frame_layout: &FrameLayout,
696 ) -> SmallInstVec<Inst> {
697 let setup_frame = frame_layout.setup_area_size > 0;
698 let mut insts = SmallVec::new();
699
700 if setup_frame {
701 insts.push(Inst::LoadP64 {
707 rt: writable_fp_reg(),
708 rt2: writable_link_reg(),
709 mem: PairAMode::SPPostIndexed {
710 simm7: SImm7Scaled::maybe_from_i64(16, types::I64).unwrap(),
711 },
712 flags: MemFlagsData::trusted(),
713 });
714 }
715
716 let callee_pop_size = match call_conv {
717 isa::CallConv::Tail => frame_layout.tail_args_size,
718 isa::CallConv::Winch => frame_layout.incoming_args_size,
719 _ => 0,
720 };
721 if callee_pop_size > 0 {
722 insts.extend(Self::gen_sp_reg_adjust(callee_pop_size.try_into().unwrap()));
723 }
724
725 insts
726 }
727
728 fn gen_return(
729 call_conv: isa::CallConv,
730 isa_flags: &aarch64_settings::Flags,
731 frame_layout: &FrameLayout,
732 ) -> SmallInstVec<Inst> {
733 let setup_frame = frame_layout.setup_area_size > 0;
734
735 match Self::select_api_key(isa_flags, call_conv, setup_frame) {
736 Some(key) => {
737 smallvec![Inst::AuthenticatedRet {
738 key,
739 is_hint: !isa_flags.has_pauth(),
740 }]
741 }
742 None => {
743 smallvec![Inst::Ret {}]
744 }
745 }
746 }
747
748 fn gen_probestack(_insts: &mut SmallInstVec<Self::I>, _: u32) {
749 unimplemented!("Stack probing is unimplemented on AArch64");
752 }
753
754 fn gen_inline_probestack(
755 insts: &mut SmallInstVec<Self::I>,
756 _call_conv: isa::CallConv,
757 frame_size: u32,
758 guard_size: u32,
759 ) {
760 const PROBE_MAX_UNROLL: u32 = 3;
764
765 let probe_count = frame_size / guard_size;
768 if probe_count == 0 {
769 } else if probe_count <= PROBE_MAX_UNROLL {
771 Self::gen_probestack_unroll(insts, guard_size, probe_count)
772 } else {
773 Self::gen_probestack_loop(insts, frame_size, guard_size)
774 }
775 }
776
777 fn gen_clobber_save(
778 call_conv: isa::CallConv,
779 flags: &settings::Flags,
780 frame_layout: &FrameLayout,
781 ) -> SmallVec<[Inst; 16]> {
782 let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class();
783
784 let mut insts = SmallVec::new();
785 let setup_frame = frame_layout.setup_area_size > 0;
786
787 let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
790 if incoming_args_diff > 0 {
791 insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
793 if flags.unwind_info() {
794 insts.push(Inst::Unwind {
795 inst: UnwindInst::StackAlloc {
796 size: incoming_args_diff,
797 },
798 });
799 }
800
801 if setup_frame {
803 insts.push(Inst::ULoad64 {
805 rd: regs::writable_fp_reg(),
806 mem: AMode::SPOffset {
807 off: i64::from(incoming_args_diff),
808 },
809 flags: MemFlagsData::trusted(),
810 });
811
812 insts.push(Inst::StoreP64 {
814 rt: fp_reg(),
815 rt2: link_reg(),
816 mem: PairAMode::SignedOffset {
817 reg: regs::stack_reg(),
818 simm7: SImm7Scaled::maybe_from_i64(0, types::I64).unwrap(),
819 },
820 flags: MemFlagsData::trusted(),
821 });
822
823 insts.push(Self::gen_move(
825 regs::writable_fp_reg(),
826 regs::stack_reg(),
827 types::I64,
828 ));
829 }
830 }
831
832 if flags.unwind_info() && setup_frame {
833 insts.push(Inst::Unwind {
836 inst: UnwindInst::DefineNewFrame {
837 offset_downward_to_clobbers: frame_layout.clobber_size,
838 offset_upward_to_caller_sp: frame_layout.setup_area_size,
839 },
840 });
841 }
842
843 let mut clobber_offset = frame_layout.clobber_size;
857 let clobber_offset_change = 16;
858 let iter = clobbered_int.chunks_exact(2);
859
860 if let [rd] = iter.remainder() {
861 let rd: Reg = rd.to_reg().into();
862
863 debug_assert_eq!(rd.class(), RegClass::Int);
864 insts.push(Inst::Store64 {
866 rd,
867 mem: AMode::SPPreIndexed {
868 simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(),
869 },
870 flags: MemFlagsData::trusted(),
871 });
872
873 if flags.unwind_info() {
874 clobber_offset -= clobber_offset_change as u32;
875 insts.push(Inst::Unwind {
876 inst: UnwindInst::SaveReg {
877 clobber_offset,
878 reg: rd.to_real_reg().unwrap(),
879 },
880 });
881 }
882 }
883
884 let mut iter = iter.rev();
885
886 while let Some([rt, rt2]) = iter.next() {
887 let rt: Reg = rt.to_reg().into();
889 let rt2: Reg = rt2.to_reg().into();
890
891 debug_assert!(rt.class() == RegClass::Int);
892 debug_assert!(rt2.class() == RegClass::Int);
893
894 insts.push(Inst::StoreP64 {
896 rt,
897 rt2,
898 mem: PairAMode::SPPreIndexed {
899 simm7: SImm7Scaled::maybe_from_i64(-clobber_offset_change, types::I64).unwrap(),
900 },
901 flags: MemFlagsData::trusted(),
902 });
903
904 if flags.unwind_info() {
905 clobber_offset -= clobber_offset_change as u32;
906 insts.push(Inst::Unwind {
907 inst: UnwindInst::SaveReg {
908 clobber_offset,
909 reg: rt.to_real_reg().unwrap(),
910 },
911 });
912 insts.push(Inst::Unwind {
913 inst: UnwindInst::SaveReg {
914 clobber_offset: clobber_offset + (clobber_offset_change / 2) as u32,
915 reg: rt2.to_real_reg().unwrap(),
916 },
917 });
918 }
919 }
920
921 if call_conv == isa::CallConv::PreserveAll {
922 for reg in clobbered_vec.iter().rev() {
924 let inst = Inst::FpuStore128 {
925 rd: reg.to_reg().into(),
926 mem: AMode::SPPreIndexed {
927 simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(),
928 },
929 flags: MemFlagsData::trusted(),
930 };
931 insts.push(inst);
932 }
935 } else {
936 let store_vec_reg_half = |rd| Inst::FpuStore64 {
937 rd,
938 mem: AMode::SPPreIndexed {
939 simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(),
940 },
941 flags: MemFlagsData::trusted(),
942 };
943 let iter = clobbered_vec.chunks_exact(2);
944
945 if let [rd] = iter.remainder() {
946 let rd: Reg = rd.to_reg().into();
947
948 debug_assert_eq!(rd.class(), RegClass::Float);
949 insts.push(store_vec_reg_half(rd));
950
951 if flags.unwind_info() {
952 clobber_offset -= clobber_offset_change as u32;
953 insts.push(Inst::Unwind {
954 inst: UnwindInst::SaveReg {
955 clobber_offset,
956 reg: rd.to_real_reg().unwrap(),
957 },
958 });
959 }
960 }
961
962 let store_vec_reg_half_pair = |rt, rt2| {
963 let clobber_offset_change = 16;
964
965 (
966 Inst::FpuStoreP64 {
967 rt,
968 rt2,
969 mem: PairAMode::SPPreIndexed {
970 simm7: SImm7Scaled::maybe_from_i64(-clobber_offset_change, F64)
971 .unwrap(),
972 },
973 flags: MemFlagsData::trusted(),
974 },
975 clobber_offset_change as u32,
976 )
977 };
978 let mut iter = iter.rev();
979
980 while let Some([rt, rt2]) = iter.next() {
981 let rt: Reg = rt.to_reg().into();
982 let rt2: Reg = rt2.to_reg().into();
983
984 debug_assert_eq!(rt.class(), RegClass::Float);
985 debug_assert_eq!(rt2.class(), RegClass::Float);
986
987 let (inst, clobber_offset_change) = store_vec_reg_half_pair(rt, rt2);
988
989 insts.push(inst);
990
991 if flags.unwind_info() {
992 clobber_offset -= clobber_offset_change;
993 insts.push(Inst::Unwind {
994 inst: UnwindInst::SaveReg {
995 clobber_offset,
996 reg: rt.to_real_reg().unwrap(),
997 },
998 });
999 insts.push(Inst::Unwind {
1000 inst: UnwindInst::SaveReg {
1001 clobber_offset: clobber_offset + clobber_offset_change / 2,
1002 reg: rt2.to_real_reg().unwrap(),
1003 },
1004 });
1005 }
1006 }
1007 }
1008
1009 let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
1011 if stack_size > 0 {
1012 insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32)));
1013 if flags.unwind_info() {
1014 insts.push(Inst::Unwind {
1015 inst: UnwindInst::StackAlloc { size: stack_size },
1016 });
1017 }
1018 }
1019
1020 insts
1021 }
1022
1023 fn gen_clobber_restore(
1024 call_conv: isa::CallConv,
1025 _flags: &settings::Flags,
1026 frame_layout: &FrameLayout,
1027 ) -> SmallVec<[Inst; 16]> {
1028 let mut insts = SmallVec::new();
1029 let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class();
1030
1031 let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
1033 if stack_size > 0 {
1034 insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));
1035 }
1036
1037 if call_conv == isa::CallConv::PreserveAll {
1038 for reg in clobbered_vec.iter() {
1039 let inst = Inst::FpuLoad128 {
1040 rd: reg.map(|r| r.into()),
1041 mem: AMode::SPPostIndexed {
1042 simm9: SImm9::maybe_from_i64(16).unwrap(),
1043 },
1044 flags: MemFlagsData::trusted(),
1045 };
1046 insts.push(inst);
1047 }
1050 } else {
1051 let load_vec_reg_half = |rd| Inst::FpuLoad64 {
1052 rd,
1053 mem: AMode::SPPostIndexed {
1054 simm9: SImm9::maybe_from_i64(16).unwrap(),
1055 },
1056 flags: MemFlagsData::trusted(),
1057 };
1058 let load_vec_reg_half_pair = |rt, rt2| Inst::FpuLoadP64 {
1059 rt,
1060 rt2,
1061 mem: PairAMode::SPPostIndexed {
1062 simm7: SImm7Scaled::maybe_from_i64(16, F64).unwrap(),
1063 },
1064 flags: MemFlagsData::trusted(),
1065 };
1066
1067 let mut iter = clobbered_vec.chunks_exact(2);
1068
1069 while let Some([rt, rt2]) = iter.next() {
1070 let rt: Writable<Reg> = rt.map(|r| r.into());
1071 let rt2: Writable<Reg> = rt2.map(|r| r.into());
1072
1073 debug_assert_eq!(rt.to_reg().class(), RegClass::Float);
1074 debug_assert_eq!(rt2.to_reg().class(), RegClass::Float);
1075 insts.push(load_vec_reg_half_pair(rt, rt2));
1076 }
1077
1078 debug_assert!(iter.remainder().len() <= 1);
1079
1080 if let [rd] = iter.remainder() {
1081 let rd: Writable<Reg> = rd.map(|r| r.into());
1082
1083 debug_assert_eq!(rd.to_reg().class(), RegClass::Float);
1084 insts.push(load_vec_reg_half(rd));
1085 }
1086 }
1087
1088 let mut iter = clobbered_int.chunks_exact(2);
1089
1090 while let Some([rt, rt2]) = iter.next() {
1091 let rt: Writable<Reg> = rt.map(|r| r.into());
1092 let rt2: Writable<Reg> = rt2.map(|r| r.into());
1093
1094 debug_assert_eq!(rt.to_reg().class(), RegClass::Int);
1095 debug_assert_eq!(rt2.to_reg().class(), RegClass::Int);
1096 insts.push(Inst::LoadP64 {
1098 rt,
1099 rt2,
1100 mem: PairAMode::SPPostIndexed {
1101 simm7: SImm7Scaled::maybe_from_i64(16, I64).unwrap(),
1102 },
1103 flags: MemFlagsData::trusted(),
1104 });
1105 }
1106
1107 debug_assert!(iter.remainder().len() <= 1);
1108
1109 if let [rd] = iter.remainder() {
1110 let rd: Writable<Reg> = rd.map(|r| r.into());
1111
1112 debug_assert_eq!(rd.to_reg().class(), RegClass::Int);
1113 insts.push(Inst::ULoad64 {
1115 rd,
1116 mem: AMode::SPPostIndexed {
1117 simm9: SImm9::maybe_from_i64(16).unwrap(),
1118 },
1119 flags: MemFlagsData::trusted(),
1120 });
1121 }
1122
1123 insts
1124 }
1125
1126 fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
1127 call_conv: isa::CallConv,
1128 dst: Reg,
1129 src: Reg,
1130 size: usize,
1131 mut alloc_tmp: F,
1132 ) -> SmallVec<[Self::I; 8]> {
1133 let mut insts = SmallVec::new();
1134 let arg0 = writable_xreg(0);
1135 let arg1 = writable_xreg(1);
1136 let arg2 = writable_xreg(2);
1137 let tmp = alloc_tmp(Self::word_type());
1138 insts.extend(Inst::load_constant(tmp, size as u64));
1139 insts.push(Inst::Call {
1140 info: Box::new(CallInfo {
1141 dest: ExternalName::LibCall(LibCall::Memcpy),
1142 uses: smallvec![
1143 CallArgPair {
1144 vreg: dst,
1145 preg: arg0.to_reg()
1146 },
1147 CallArgPair {
1148 vreg: src,
1149 preg: arg1.to_reg()
1150 },
1151 CallArgPair {
1152 vreg: tmp.to_reg(),
1153 preg: arg2.to_reg()
1154 }
1155 ],
1156 defs: smallvec![],
1157 clobbers: Self::get_regs_clobbered_by_call(call_conv, false),
1158 caller_conv: call_conv,
1159 callee_conv: call_conv,
1160 callee_pop_size: 0,
1161 try_call_info: None,
1162 patchable: false,
1163 }),
1164 });
1165 insts
1166 }
1167
1168 fn get_number_of_spillslots_for_value(
1169 rc: RegClass,
1170 vector_size: u32,
1171 _isa_flags: &Self::F,
1172 ) -> u32 {
1173 assert_eq!(vector_size % 8, 0);
1174 match rc {
1176 RegClass::Int => 1,
1177 RegClass::Float => vector_size / 8,
1178 RegClass::Vector => unreachable!(),
1179 }
1180 }
1181
1182 fn get_machine_env(flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
1183 if flags.enable_pinned_reg() {
1184 static MACHINE_ENV: MachineEnv = create_reg_env(true);
1185 &MACHINE_ENV
1186 } else {
1187 static MACHINE_ENV: MachineEnv = create_reg_env(false);
1188 &MACHINE_ENV
1189 }
1190 }
1191
1192 fn get_regs_clobbered_by_call(call_conv: isa::CallConv, is_exception: bool) -> PRegSet {
1193 match (call_conv, is_exception) {
1194 (isa::CallConv::Tail, true) => ALL_CLOBBERS,
1195 (isa::CallConv::Winch, true) => ALL_CLOBBERS,
1196 (isa::CallConv::Winch, false) => WINCH_CLOBBERS,
1197 (isa::CallConv::PreserveAll, true) => ALL_CLOBBERS,
1203 (isa::CallConv::SystemV, _) => DEFAULT_AAPCS_CLOBBERS,
1204 (isa::CallConv::AppleAarch64, true) => DEFAULT_AAPCS_CLOBBERS,
1207 (isa::CallConv::PreserveAll, _) => NO_CLOBBERS,
1208 (_, false) => DEFAULT_AAPCS_CLOBBERS,
1209 (_, true) => panic!("unimplemented clobbers for exn abi of {call_conv:?}"),
1210 }
1211 }
1212
1213 fn get_ext_mode(
1214 call_conv: isa::CallConv,
1215 specified: ir::ArgumentExtension,
1216 location: ABIArgLocation,
1217 ) -> ir::ArgumentExtension {
1218 if call_conv == isa::CallConv::AppleAarch64 && location == ABIArgLocation::Reg {
1224 specified
1225 } else {
1226 ir::ArgumentExtension::None
1227 }
1228 }
1229
1230 fn compute_frame_layout(
1231 call_conv: isa::CallConv,
1232 flags: &settings::Flags,
1233 sig: &Signature,
1234 regs: &[Writable<RealReg>],
1235 function_calls: FunctionCalls,
1236 incoming_args_size: u32,
1237 tail_args_size: u32,
1238 stackslots_size: u32,
1239 fixed_frame_storage_size: u32,
1240 outgoing_args_size: u32,
1241 ) -> FrameLayout {
1242 let mut regs: Vec<Writable<RealReg>> = regs
1243 .iter()
1244 .cloned()
1245 .filter(|r| {
1246 is_reg_saved_in_prologue(call_conv, flags.enable_pinned_reg(), sig, r.to_reg())
1247 })
1248 .collect();
1249
1250 if call_conv == isa::CallConv::AppleAarch64 && flags.enable_compact_unwind_abi() {
1251 add_macho_compact_unwind_paired_regs(&mut regs);
1252 regs.sort_unstable_by_key(|r| {
1256 let reg = r.to_reg();
1257 (reg.class(), Reverse(reg.hw_enc()))
1258 });
1259 } else {
1260 regs.sort_unstable();
1263 }
1264
1265 let clobber_size = compute_clobber_size(call_conv, ®s);
1267
1268 let needs_linkage_frame = flags.preserve_frame_pointers()
1269 || incoming_args_size > 0
1272 || tail_args_size > incoming_args_size
1273 || clobber_size > 0
1274 || fixed_frame_storage_size > 0
1275 || outgoing_args_size > 0;
1276
1277 let setup_area_size = if needs_linkage_frame || function_calls == FunctionCalls::Regular {
1279 16 } else {
1281 0
1282 };
1283
1284 FrameLayout {
1286 word_bytes: 8,
1287 incoming_args_size,
1288 tail_args_size,
1289 setup_area_size,
1290 clobber_size,
1291 fixed_frame_storage_size,
1292 stackslots_size,
1293 outgoing_args_size,
1294 clobbered_callee_saves: regs,
1295 function_calls,
1296 }
1297 }
1298
1299 fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
1300 regs::writable_xreg(9)
1303 }
1304
1305 fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
1306 const PAYLOAD_REGS: &'static [Reg] = &[regs::xreg(0), regs::xreg(1)];
1307 match call_conv {
1308 isa::CallConv::SystemV
1309 | isa::CallConv::Tail
1310 | isa::CallConv::PreserveAll
1311 | isa::CallConv::AppleAarch64 => PAYLOAD_REGS,
1312 _ => &[],
1313 }
1314 }
1315}
1316
1317impl AArch64MachineDeps {
1318 fn gen_probestack_unroll(insts: &mut SmallInstVec<Inst>, guard_size: u32, probe_count: u32) {
1319 for _ in 0..probe_count {
1327 insts.extend(Self::gen_sp_reg_adjust(-(guard_size as i32)));
1328
1329 insts.push(Inst::gen_store(
1330 AMode::SPOffset { off: 0 },
1331 zero_reg(),
1332 I32,
1333 MemFlagsData::trusted(),
1334 ));
1335 }
1336
1337 insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1339 }
1340
1341 fn gen_probestack_loop(insts: &mut SmallInstVec<Inst>, frame_size: u32, guard_size: u32) {
1342 let start = writable_spilltmp_reg();
1351 let end = writable_tmp2_reg();
1352 insts.extend(Inst::load_constant(start, 0));
1355 insts.extend(Inst::load_constant(end, frame_size.into()));
1356 insts.push(Inst::StackProbeLoop {
1357 start,
1358 end: end.to_reg(),
1359 step: Imm12::maybe_from_u64(guard_size.into()).unwrap(),
1360 });
1361 }
1362
1363 pub fn select_api_key(
1364 isa_flags: &aarch64_settings::Flags,
1365 call_conv: isa::CallConv,
1366 setup_frame: bool,
1367 ) -> Option<APIKey> {
1368 if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
1369 Some(if isa_flags.sign_return_address_with_bkey() {
1374 match call_conv {
1375 isa::CallConv::Tail => APIKey::BZ,
1376 _ => APIKey::BSP,
1377 }
1378 } else {
1379 match call_conv {
1380 isa::CallConv::Tail => APIKey::AZ,
1381 _ => APIKey::ASP,
1382 }
1383 })
1384 } else {
1385 None
1386 }
1387 }
1388}
1389
1390fn is_reg_saved_in_prologue(
1393 call_conv: isa::CallConv,
1394 enable_pinned_reg: bool,
1395 sig: &Signature,
1396 r: RealReg,
1397) -> bool {
1398 if call_conv == isa::CallConv::PreserveAll {
1399 return true;
1400 }
1401
1402 let save_z_regs = sig
1404 .params
1405 .iter()
1406 .filter(|p| p.value_type.is_dynamic_vector())
1407 .count()
1408 != 0;
1409
1410 match r.class() {
1411 RegClass::Int => {
1412 if enable_pinned_reg && r.hw_enc() == PINNED_REG {
1417 false
1418 } else {
1419 r.hw_enc() >= 19 && r.hw_enc() <= 28
1420 }
1421 }
1422 RegClass::Float => {
1423 if save_z_regs {
1429 r.hw_enc() >= 8 && r.hw_enc() <= 23
1430 } else {
1431 r.hw_enc() >= 8 && r.hw_enc() <= 15
1433 }
1434 }
1435 RegClass::Vector => unreachable!(),
1436 }
1437}
1438
1439const fn default_aapcs_clobbers() -> PRegSet {
1440 PRegSet::empty()
1441 .with(xreg_preg(0))
1443 .with(xreg_preg(1))
1444 .with(xreg_preg(2))
1445 .with(xreg_preg(3))
1446 .with(xreg_preg(4))
1447 .with(xreg_preg(5))
1448 .with(xreg_preg(6))
1449 .with(xreg_preg(7))
1450 .with(xreg_preg(8))
1451 .with(xreg_preg(9))
1452 .with(xreg_preg(10))
1453 .with(xreg_preg(11))
1454 .with(xreg_preg(12))
1455 .with(xreg_preg(13))
1456 .with(xreg_preg(14))
1457 .with(xreg_preg(15))
1458 .with(xreg_preg(16))
1459 .with(xreg_preg(17))
1460 .with(vreg_preg(0))
1476 .with(vreg_preg(1))
1477 .with(vreg_preg(2))
1478 .with(vreg_preg(3))
1479 .with(vreg_preg(4))
1480 .with(vreg_preg(5))
1481 .with(vreg_preg(6))
1482 .with(vreg_preg(7))
1483 .with(vreg_preg(8))
1484 .with(vreg_preg(9))
1485 .with(vreg_preg(10))
1486 .with(vreg_preg(11))
1487 .with(vreg_preg(12))
1488 .with(vreg_preg(13))
1489 .with(vreg_preg(14))
1490 .with(vreg_preg(15))
1491 .with(vreg_preg(16))
1492 .with(vreg_preg(17))
1493 .with(vreg_preg(18))
1494 .with(vreg_preg(19))
1495 .with(vreg_preg(20))
1496 .with(vreg_preg(21))
1497 .with(vreg_preg(22))
1498 .with(vreg_preg(23))
1499 .with(vreg_preg(24))
1500 .with(vreg_preg(25))
1501 .with(vreg_preg(26))
1502 .with(vreg_preg(27))
1503 .with(vreg_preg(28))
1504 .with(vreg_preg(29))
1505 .with(vreg_preg(30))
1506 .with(vreg_preg(31))
1507}
1508
1509const fn winch_clobbers() -> PRegSet {
1510 PRegSet::empty()
1511 .with(xreg_preg(0))
1512 .with(xreg_preg(1))
1513 .with(xreg_preg(2))
1514 .with(xreg_preg(3))
1515 .with(xreg_preg(4))
1516 .with(xreg_preg(5))
1517 .with(xreg_preg(6))
1518 .with(xreg_preg(7))
1519 .with(xreg_preg(8))
1520 .with(xreg_preg(9))
1521 .with(xreg_preg(10))
1522 .with(xreg_preg(11))
1523 .with(xreg_preg(12))
1524 .with(xreg_preg(13))
1525 .with(xreg_preg(14))
1526 .with(xreg_preg(15))
1527 .with(xreg_preg(16))
1528 .with(xreg_preg(17))
1529 .with(xreg_preg(19))
1533 .with(xreg_preg(20))
1534 .with(xreg_preg(21))
1535 .with(xreg_preg(22))
1536 .with(xreg_preg(23))
1537 .with(xreg_preg(24))
1538 .with(xreg_preg(25))
1539 .with(xreg_preg(26))
1540 .with(xreg_preg(27))
1541 .with(vreg_preg(0))
1546 .with(vreg_preg(1))
1547 .with(vreg_preg(2))
1548 .with(vreg_preg(3))
1549 .with(vreg_preg(4))
1550 .with(vreg_preg(5))
1551 .with(vreg_preg(6))
1552 .with(vreg_preg(7))
1553 .with(vreg_preg(8))
1554 .with(vreg_preg(9))
1555 .with(vreg_preg(10))
1556 .with(vreg_preg(11))
1557 .with(vreg_preg(12))
1558 .with(vreg_preg(13))
1559 .with(vreg_preg(14))
1560 .with(vreg_preg(15))
1561 .with(vreg_preg(16))
1562 .with(vreg_preg(17))
1563 .with(vreg_preg(18))
1564 .with(vreg_preg(19))
1565 .with(vreg_preg(20))
1566 .with(vreg_preg(21))
1567 .with(vreg_preg(22))
1568 .with(vreg_preg(23))
1569 .with(vreg_preg(24))
1570 .with(vreg_preg(25))
1571 .with(vreg_preg(26))
1572 .with(vreg_preg(27))
1573 .with(vreg_preg(28))
1574 .with(vreg_preg(29))
1575 .with(vreg_preg(30))
1576 .with(vreg_preg(31))
1577}
1578
1579const fn all_clobbers() -> PRegSet {
1580 PRegSet::empty()
1581 .with(xreg_preg(0))
1584 .with(xreg_preg(1))
1585 .with(xreg_preg(2))
1586 .with(xreg_preg(3))
1587 .with(xreg_preg(4))
1588 .with(xreg_preg(5))
1589 .with(xreg_preg(6))
1590 .with(xreg_preg(7))
1591 .with(xreg_preg(8))
1592 .with(xreg_preg(9))
1593 .with(xreg_preg(10))
1594 .with(xreg_preg(11))
1595 .with(xreg_preg(12))
1596 .with(xreg_preg(13))
1597 .with(xreg_preg(14))
1598 .with(xreg_preg(15))
1599 .with(xreg_preg(16))
1600 .with(xreg_preg(17))
1601 .with(xreg_preg(18))
1602 .with(xreg_preg(19))
1603 .with(xreg_preg(20))
1604 .with(xreg_preg(21))
1605 .with(xreg_preg(22))
1606 .with(xreg_preg(23))
1607 .with(xreg_preg(24))
1608 .with(xreg_preg(25))
1609 .with(xreg_preg(26))
1610 .with(xreg_preg(27))
1611 .with(xreg_preg(28))
1612 .with(vreg_preg(0))
1614 .with(vreg_preg(1))
1615 .with(vreg_preg(2))
1616 .with(vreg_preg(3))
1617 .with(vreg_preg(4))
1618 .with(vreg_preg(5))
1619 .with(vreg_preg(6))
1620 .with(vreg_preg(7))
1621 .with(vreg_preg(8))
1622 .with(vreg_preg(9))
1623 .with(vreg_preg(10))
1624 .with(vreg_preg(11))
1625 .with(vreg_preg(12))
1626 .with(vreg_preg(13))
1627 .with(vreg_preg(14))
1628 .with(vreg_preg(15))
1629 .with(vreg_preg(16))
1630 .with(vreg_preg(17))
1631 .with(vreg_preg(18))
1632 .with(vreg_preg(19))
1633 .with(vreg_preg(20))
1634 .with(vreg_preg(21))
1635 .with(vreg_preg(22))
1636 .with(vreg_preg(23))
1637 .with(vreg_preg(24))
1638 .with(vreg_preg(25))
1639 .with(vreg_preg(26))
1640 .with(vreg_preg(27))
1641 .with(vreg_preg(28))
1642 .with(vreg_preg(29))
1643 .with(vreg_preg(30))
1644 .with(vreg_preg(31))
1645}
1646
1647const DEFAULT_AAPCS_CLOBBERS: PRegSet = default_aapcs_clobbers();
1648const WINCH_CLOBBERS: PRegSet = winch_clobbers();
1649const ALL_CLOBBERS: PRegSet = all_clobbers();
1650const NO_CLOBBERS: PRegSet = PRegSet::empty();
1651
1652const fn create_reg_env(enable_pinned_reg: bool) -> MachineEnv {
1653 const fn preg(r: Reg) -> PReg {
1654 r.to_real_reg().unwrap().preg()
1655 }
1656
1657 let mut env = MachineEnv {
1658 preferred_regs_by_class: [
1659 PRegSet::empty()
1660 .with(preg(xreg(0)))
1661 .with(preg(xreg(1)))
1662 .with(preg(xreg(2)))
1663 .with(preg(xreg(3)))
1664 .with(preg(xreg(4)))
1665 .with(preg(xreg(5)))
1666 .with(preg(xreg(6)))
1667 .with(preg(xreg(7)))
1668 .with(preg(xreg(8)))
1669 .with(preg(xreg(9)))
1670 .with(preg(xreg(10)))
1671 .with(preg(xreg(11)))
1672 .with(preg(xreg(12)))
1673 .with(preg(xreg(13)))
1674 .with(preg(xreg(14)))
1675 .with(preg(xreg(15))),
1676 PRegSet::empty()
1683 .with(preg(vreg(0)))
1684 .with(preg(vreg(1)))
1685 .with(preg(vreg(2)))
1686 .with(preg(vreg(3)))
1687 .with(preg(vreg(4)))
1688 .with(preg(vreg(5)))
1689 .with(preg(vreg(6)))
1690 .with(preg(vreg(7)))
1691 .with(preg(vreg(16)))
1693 .with(preg(vreg(17)))
1694 .with(preg(vreg(18)))
1695 .with(preg(vreg(19)))
1696 .with(preg(vreg(20)))
1697 .with(preg(vreg(21)))
1698 .with(preg(vreg(22)))
1699 .with(preg(vreg(23)))
1700 .with(preg(vreg(24)))
1701 .with(preg(vreg(25)))
1702 .with(preg(vreg(26)))
1703 .with(preg(vreg(27)))
1704 .with(preg(vreg(28)))
1705 .with(preg(vreg(29)))
1706 .with(preg(vreg(30)))
1707 .with(preg(vreg(31))),
1708 PRegSet::empty(),
1710 ],
1711 non_preferred_regs_by_class: [
1712 PRegSet::empty()
1713 .with(preg(xreg(19)))
1714 .with(preg(xreg(20)))
1715 .with(preg(xreg(22)))
1717 .with(preg(xreg(23)))
1718 .with(preg(xreg(24)))
1719 .with(preg(xreg(25)))
1720 .with(preg(xreg(26)))
1721 .with(preg(xreg(27)))
1722 .with(preg(xreg(28))),
1723 PRegSet::empty()
1724 .with(preg(vreg(8)))
1725 .with(preg(vreg(9)))
1726 .with(preg(vreg(10)))
1727 .with(preg(vreg(11)))
1728 .with(preg(vreg(12)))
1729 .with(preg(vreg(13)))
1730 .with(preg(vreg(14)))
1731 .with(preg(vreg(15))),
1732 PRegSet::empty(),
1734 ],
1735 fixed_stack_slots: vec![],
1736 scratch_by_class: [None, None, None],
1737 };
1738
1739 if !enable_pinned_reg {
1740 debug_assert!(PINNED_REG == 21);
1741 env.non_preferred_regs_by_class[0].add(preg(xreg(PINNED_REG)));
1742 }
1743
1744 env
1745}