1use crate::CodegenResult;
4use crate::ir;
5use crate::ir::MemFlags;
6use crate::ir::types;
7use crate::ir::types::*;
8use crate::ir::{ExternalName, LibCall, Signature, dynamic_to_fixed};
9use crate::isa;
10use crate::isa::aarch64::{inst::*, settings as aarch64_settings};
11use crate::isa::unwind::UnwindInst;
12use crate::isa::winch;
13use crate::machinst::*;
14use crate::settings;
15use alloc::boxed::Box;
16use alloc::vec::Vec;
17use regalloc2::{MachineEnv, PReg, PRegSet};
18use smallvec::{SmallVec, smallvec};
19use std::borrow::ToOwned;
20use std::sync::OnceLock;
21
22pub(crate) type AArch64Callee = Callee<AArch64MachineDeps>;
27
28impl From<StackAMode> for AMode {
29 fn from(stack: StackAMode) -> AMode {
30 match stack {
31 StackAMode::IncomingArg(off, stack_args_size) => AMode::IncomingArg {
32 off: i64::from(stack_args_size) - off,
33 },
34 StackAMode::Slot(off) => AMode::SlotOffset { off },
35 StackAMode::OutgoingArg(off) => AMode::SPOffset { off },
36 }
37 }
38}
39
40fn compute_clobber_size(clobbered_callee_saves: &[Writable<RealReg>]) -> u32 {
43 let mut int_regs = 0;
44 let mut vec_regs = 0;
45 for ® in clobbered_callee_saves {
46 match reg.to_reg().class() {
47 RegClass::Int => {
48 int_regs += 1;
49 }
50 RegClass::Float => {
51 vec_regs += 1;
52 }
53 RegClass::Vector => unreachable!(),
54 }
55 }
56
57 let int_save_bytes = (int_regs + (int_regs & 1)) * 8;
59 let vec_reg_size = 8;
66 let vec_save_padding = vec_regs & 1;
67 let vec_save_bytes = (vec_regs + vec_save_padding) * vec_reg_size;
69
70 int_save_bytes + vec_save_bytes
71}
72
73pub struct AArch64MachineDeps;
76
77impl IsaFlags for aarch64_settings::Flags {
78 fn is_forward_edge_cfi_enabled(&self) -> bool {
79 self.use_bti()
80 }
81}
82
83impl ABIMachineSpec for AArch64MachineDeps {
84 type I = Inst;
85
86 type F = aarch64_settings::Flags;
87
88 const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
92
93 fn word_bits() -> u32 {
94 64
95 }
96
97 fn stack_align(_call_conv: isa::CallConv) -> u32 {
99 16
100 }
101
102 fn compute_arg_locs(
103 call_conv: isa::CallConv,
104 flags: &settings::Flags,
105 params: &[ir::AbiParam],
106 args_or_rets: ArgsOrRets,
107 add_ret_area_ptr: bool,
108 mut args: ArgsAccumulator,
109 ) -> CodegenResult<(u32, Option<usize>)> {
110 let is_apple_cc = call_conv == isa::CallConv::AppleAarch64;
111 let is_winch_return = call_conv == isa::CallConv::Winch && args_or_rets == ArgsOrRets::Rets;
112
113 let mut next_xreg = if call_conv == isa::CallConv::Tail {
128 2
138 } else {
139 0
140 };
141 let mut next_vreg = 0;
142 let mut next_stack: u32 = 0;
143
144 let max_per_class_reg_vals = 8; let mut remaining_reg_vals = 16;
151
152 let ret_area_ptr = if add_ret_area_ptr {
153 debug_assert_eq!(args_or_rets, ArgsOrRets::Args);
154 if call_conv != isa::CallConv::Winch {
155 Some(ABIArg::reg(
158 xreg(8).to_real_reg().unwrap(),
159 I64,
160 ir::ArgumentExtension::None,
161 ir::ArgumentPurpose::Normal,
162 ))
163 } else {
164 next_xreg += 1;
168 Some(ABIArg::reg(
169 xreg(0).to_real_reg().unwrap(),
170 I64,
171 ir::ArgumentExtension::None,
172 ir::ArgumentPurpose::Normal,
173 ))
174 }
175 } else {
176 None
177 };
178
179 for (i, param) in params.into_iter().enumerate() {
180 if is_apple_cc && param.value_type == types::F128 && !flags.enable_llvm_abi_extensions()
181 {
182 panic!(
183 "f128 args/return values not supported for apple_aarch64 unless LLVM ABI extensions are enabled"
184 );
185 }
186
187 let (rcs, reg_types) = Inst::rc_for_type(param.value_type)?;
188
189 if let ir::ArgumentPurpose::StructReturn = param.purpose {
190 assert!(
191 call_conv != isa::CallConv::Tail,
192 "support for StructReturn parameters is not implemented for the `tail` \
193 calling convention yet",
194 );
195 }
196
197 if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {
198 panic!(
199 "StructArgument parameters are not supported on arm64. \
200 Use regular pointer arguments instead."
201 );
202 }
203
204 if let ir::ArgumentPurpose::StructReturn = param.purpose {
205 assert!(
208 param.value_type == types::I64,
209 "StructReturn must be a pointer sized integer"
210 );
211 args.push(ABIArg::Slots {
212 slots: smallvec![ABIArgSlot::Reg {
213 reg: xreg(8).to_real_reg().unwrap(),
214 ty: types::I64,
215 extension: param.extension,
216 },],
217 purpose: ir::ArgumentPurpose::StructReturn,
218 });
219 continue;
220 }
221
222 let is_multi_reg = rcs.len() >= 2;
243 if is_multi_reg {
244 assert!(
245 rcs.len() == 2,
246 "Unable to handle multi reg params with more than 2 regs"
247 );
248 assert!(
249 rcs == &[RegClass::Int, RegClass::Int],
250 "Unable to handle non i64 regs"
251 );
252
253 let reg_class_space = max_per_class_reg_vals - next_xreg;
254 let reg_space = remaining_reg_vals;
255
256 if reg_space >= 2 && reg_class_space >= 2 {
257 if !is_apple_cc && next_xreg % 2 != 0 {
262 next_xreg += 1;
263 }
264
265 let lower_reg = xreg(next_xreg);
266 let upper_reg = xreg(next_xreg + 1);
267
268 args.push(ABIArg::Slots {
269 slots: smallvec![
270 ABIArgSlot::Reg {
271 reg: lower_reg.to_real_reg().unwrap(),
272 ty: reg_types[0],
273 extension: param.extension,
274 },
275 ABIArgSlot::Reg {
276 reg: upper_reg.to_real_reg().unwrap(),
277 ty: reg_types[1],
278 extension: param.extension,
279 },
280 ],
281 purpose: param.purpose,
282 });
283
284 next_xreg += 2;
285 remaining_reg_vals -= 2;
286 continue;
287 }
288 } else {
289 let rc = rcs[0];
291 let next_reg = match rc {
292 RegClass::Int => &mut next_xreg,
293 RegClass::Float => &mut next_vreg,
294 RegClass::Vector => unreachable!(),
295 };
296
297 let push_to_reg = if is_winch_return {
298 i == params.len() - 1
300 } else {
301 *next_reg < max_per_class_reg_vals && remaining_reg_vals > 0
303 };
304
305 if push_to_reg {
306 let reg = match rc {
307 RegClass::Int => xreg(*next_reg),
308 RegClass::Float => vreg(*next_reg),
309 RegClass::Vector => unreachable!(),
310 };
311 let ty = if param.value_type.is_dynamic_vector() {
313 dynamic_to_fixed(param.value_type)
314 } else {
315 param.value_type
316 };
317 args.push(ABIArg::reg(
318 reg.to_real_reg().unwrap(),
319 ty,
320 param.extension,
321 param.purpose,
322 ));
323 *next_reg += 1;
324 remaining_reg_vals -= 1;
325 continue;
326 }
327 }
328
329 if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
332 return Err(crate::CodegenError::Unsupported(
333 "Too many return values to fit in registers. \
334 Use a StructReturn argument instead. (#9510)"
335 .to_owned(),
336 ));
337 }
338
339 let size = (ty_bits(param.value_type) / 8) as u32;
341
342 let size = if is_apple_cc || is_winch_return {
343 size
348 } else {
349 std::cmp::max(size, 8)
352 };
353
354 if !is_winch_return {
355 debug_assert!(size.is_power_of_two());
357 next_stack = align_to(next_stack, size);
358 }
359
360 let slots = reg_types
361 .iter()
362 .copied()
363 .scan(next_stack, |next_stack, ty| {
365 let slot_offset = *next_stack as i64;
366 *next_stack += (ty_bits(ty) / 8) as u32;
367
368 Some((ty, slot_offset))
369 })
370 .map(|(ty, offset)| ABIArgSlot::Stack {
371 offset,
372 ty,
373 extension: param.extension,
374 })
375 .collect();
376
377 args.push(ABIArg::Slots {
378 slots,
379 purpose: param.purpose,
380 });
381
382 next_stack += size;
383 }
384
385 let extra_arg = if let Some(ret_area_ptr) = ret_area_ptr {
386 args.push_non_formal(ret_area_ptr);
387 Some(args.args().len() - 1)
388 } else {
389 None
390 };
391
392 if is_winch_return {
393 winch::reverse_stack(args, next_stack, false);
394 }
395
396 next_stack = align_to(next_stack, 16);
397
398 Ok((next_stack, extra_arg))
399 }
400
401 fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
402 Inst::gen_load(into_reg, mem.into(), ty, MemFlags::trusted())
403 }
404
405 fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
406 Inst::gen_store(mem.into(), from_reg, ty, MemFlags::trusted())
407 }
408
409 fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
410 Inst::gen_move(to_reg, from_reg, ty)
411 }
412
413 fn gen_extend(
414 to_reg: Writable<Reg>,
415 from_reg: Reg,
416 signed: bool,
417 from_bits: u8,
418 to_bits: u8,
419 ) -> Inst {
420 assert!(from_bits < to_bits);
421 Inst::Extend {
422 rd: to_reg,
423 rn: from_reg,
424 signed,
425 from_bits,
426 to_bits,
427 }
428 }
429
430 fn gen_args(args: Vec<ArgPair>) -> Inst {
431 Inst::Args { args }
432 }
433
434 fn gen_rets(rets: Vec<RetPair>) -> Inst {
435 Inst::Rets { rets }
436 }
437
438 fn gen_add_imm(
439 _call_conv: isa::CallConv,
440 into_reg: Writable<Reg>,
441 from_reg: Reg,
442 imm: u32,
443 ) -> SmallInstVec<Inst> {
444 let imm = imm as u64;
445 let mut insts = SmallVec::new();
446 if let Some(imm12) = Imm12::maybe_from_u64(imm) {
447 insts.push(Inst::AluRRImm12 {
448 alu_op: ALUOp::Add,
449 size: OperandSize::Size64,
450 rd: into_reg,
451 rn: from_reg,
452 imm12,
453 });
454 } else {
455 let scratch2 = writable_tmp2_reg();
456 assert_ne!(scratch2.to_reg(), from_reg);
457 insts.extend(Inst::load_constant(scratch2, imm));
461 insts.push(Inst::AluRRRExtend {
462 alu_op: ALUOp::Add,
463 size: OperandSize::Size64,
464 rd: into_reg,
465 rn: from_reg,
466 rm: scratch2.to_reg(),
467 extendop: ExtendOp::UXTX,
468 });
469 }
470 insts
471 }
472
473 fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
474 let mut insts = SmallVec::new();
475 insts.push(Inst::AluRRRExtend {
476 alu_op: ALUOp::SubS,
477 size: OperandSize::Size64,
478 rd: writable_zero_reg(),
479 rn: stack_reg(),
480 rm: limit_reg,
481 extendop: ExtendOp::UXTX,
482 });
483 insts.push(Inst::TrapIf {
484 trap_code: ir::TrapCode::STACK_OVERFLOW,
485 kind: CondBrKind::Cond(Cond::Lo),
488 });
489 insts
490 }
491
492 fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
493 let mem = mem.into();
495 Inst::LoadAddr { rd: into_reg, mem }
496 }
497
498 fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
499 spilltmp_reg()
500 }
501
502 fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
503 let mem = AMode::RegOffset {
504 rn: base,
505 off: offset as i64,
506 };
507 Inst::gen_load(into_reg, mem, ty, MemFlags::trusted())
508 }
509
510 fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
511 let mem = AMode::RegOffset {
512 rn: base,
513 off: offset as i64,
514 };
515 Inst::gen_store(mem, from_reg, ty, MemFlags::trusted())
516 }
517
518 fn gen_sp_reg_adjust(amount: i32) -> SmallInstVec<Inst> {
519 if amount == 0 {
520 return SmallVec::new();
521 }
522
523 let (amount, is_sub) = if amount > 0 {
524 (amount as u64, false)
525 } else {
526 (-amount as u64, true)
527 };
528
529 let alu_op = if is_sub { ALUOp::Sub } else { ALUOp::Add };
530
531 let mut ret = SmallVec::new();
532 if let Some(imm12) = Imm12::maybe_from_u64(amount) {
533 let adj_inst = Inst::AluRRImm12 {
534 alu_op,
535 size: OperandSize::Size64,
536 rd: writable_stack_reg(),
537 rn: stack_reg(),
538 imm12,
539 };
540 ret.push(adj_inst);
541 } else {
542 let tmp = writable_spilltmp_reg();
543 let const_inst = Inst::load_constant(tmp, amount);
546 let adj_inst = Inst::AluRRRExtend {
547 alu_op,
548 size: OperandSize::Size64,
549 rd: writable_stack_reg(),
550 rn: stack_reg(),
551 rm: tmp.to_reg(),
552 extendop: ExtendOp::UXTX,
553 };
554 ret.extend(const_inst);
555 ret.push(adj_inst);
556 }
557 ret
558 }
559
560 fn gen_prologue_frame_setup(
561 call_conv: isa::CallConv,
562 flags: &settings::Flags,
563 isa_flags: &aarch64_settings::Flags,
564 frame_layout: &FrameLayout,
565 ) -> SmallInstVec<Inst> {
566 let setup_frame = frame_layout.setup_area_size > 0;
567 let mut insts = SmallVec::new();
568
569 match Self::select_api_key(isa_flags, call_conv, setup_frame) {
570 Some(key) => {
571 insts.push(Inst::Paci { key });
572 if flags.unwind_info() {
573 insts.push(Inst::Unwind {
574 inst: UnwindInst::Aarch64SetPointerAuth {
575 return_addresses: true,
576 },
577 });
578 }
579 }
580 None => {
581 if isa_flags.use_bti() {
582 insts.push(Inst::Bti {
583 targets: BranchTargetType::C,
584 });
585 }
586
587 if flags.unwind_info() && call_conv == isa::CallConv::AppleAarch64 {
588 insts.push(Inst::Unwind {
590 inst: UnwindInst::Aarch64SetPointerAuth {
591 return_addresses: false,
592 },
593 });
594 }
595 }
596 }
597
598 if setup_frame {
599 insts.push(Inst::StoreP64 {
601 rt: fp_reg(),
602 rt2: link_reg(),
603 mem: PairAMode::SPPreIndexed {
604 simm7: SImm7Scaled::maybe_from_i64(-16, types::I64).unwrap(),
605 },
606 flags: MemFlags::trusted(),
607 });
608
609 if flags.unwind_info() {
610 insts.push(Inst::Unwind {
611 inst: UnwindInst::PushFrameRegs {
612 offset_upward_to_caller_sp: frame_layout.setup_area_size,
613 },
614 });
615 }
616
617 insts.push(Inst::AluRRImm12 {
620 alu_op: ALUOp::Add,
621 size: OperandSize::Size64,
622 rd: writable_fp_reg(),
623 rn: stack_reg(),
624 imm12: Imm12 {
625 bits: 0,
626 shift12: false,
627 },
628 });
629 }
630
631 insts
632 }
633
634 fn gen_epilogue_frame_restore(
635 call_conv: isa::CallConv,
636 _flags: &settings::Flags,
637 _isa_flags: &aarch64_settings::Flags,
638 frame_layout: &FrameLayout,
639 ) -> SmallInstVec<Inst> {
640 let setup_frame = frame_layout.setup_area_size > 0;
641 let mut insts = SmallVec::new();
642
643 if setup_frame {
644 insts.push(Inst::LoadP64 {
650 rt: writable_fp_reg(),
651 rt2: writable_link_reg(),
652 mem: PairAMode::SPPostIndexed {
653 simm7: SImm7Scaled::maybe_from_i64(16, types::I64).unwrap(),
654 },
655 flags: MemFlags::trusted(),
656 });
657 }
658
659 if call_conv == isa::CallConv::Tail && frame_layout.tail_args_size > 0 {
660 insts.extend(Self::gen_sp_reg_adjust(
661 frame_layout.tail_args_size.try_into().unwrap(),
662 ));
663 }
664
665 insts
666 }
667
668 fn gen_return(
669 call_conv: isa::CallConv,
670 isa_flags: &aarch64_settings::Flags,
671 frame_layout: &FrameLayout,
672 ) -> SmallInstVec<Inst> {
673 let setup_frame = frame_layout.setup_area_size > 0;
674
675 match Self::select_api_key(isa_flags, call_conv, setup_frame) {
676 Some(key) => {
677 smallvec![Inst::AuthenticatedRet {
678 key,
679 is_hint: !isa_flags.has_pauth(),
680 }]
681 }
682 None => {
683 smallvec![Inst::Ret {}]
684 }
685 }
686 }
687
688 fn gen_probestack(_insts: &mut SmallInstVec<Self::I>, _: u32) {
689 unimplemented!("Stack probing is unimplemented on AArch64");
692 }
693
694 fn gen_inline_probestack(
695 insts: &mut SmallInstVec<Self::I>,
696 _call_conv: isa::CallConv,
697 frame_size: u32,
698 guard_size: u32,
699 ) {
700 const PROBE_MAX_UNROLL: u32 = 3;
704
705 let probe_count = frame_size / guard_size;
708 if probe_count == 0 {
709 } else if probe_count <= PROBE_MAX_UNROLL {
711 Self::gen_probestack_unroll(insts, guard_size, probe_count)
712 } else {
713 Self::gen_probestack_loop(insts, frame_size, guard_size)
714 }
715 }
716
717 fn gen_clobber_save(
718 _call_conv: isa::CallConv,
719 flags: &settings::Flags,
720 frame_layout: &FrameLayout,
721 ) -> SmallVec<[Inst; 16]> {
722 let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class();
723
724 let mut insts = SmallVec::new();
725 let setup_frame = frame_layout.setup_area_size > 0;
726
727 let incoming_args_diff = frame_layout.tail_args_size - frame_layout.incoming_args_size;
730 if incoming_args_diff > 0 {
731 insts.extend(Self::gen_sp_reg_adjust(-(incoming_args_diff as i32)));
733 if flags.unwind_info() {
734 insts.push(Inst::Unwind {
735 inst: UnwindInst::StackAlloc {
736 size: incoming_args_diff,
737 },
738 });
739 }
740
741 if setup_frame {
743 insts.push(Inst::ULoad64 {
745 rd: regs::writable_fp_reg(),
746 mem: AMode::SPOffset {
747 off: i64::from(incoming_args_diff),
748 },
749 flags: MemFlags::trusted(),
750 });
751
752 insts.push(Inst::StoreP64 {
754 rt: fp_reg(),
755 rt2: link_reg(),
756 mem: PairAMode::SignedOffset {
757 reg: regs::stack_reg(),
758 simm7: SImm7Scaled::maybe_from_i64(0, types::I64).unwrap(),
759 },
760 flags: MemFlags::trusted(),
761 });
762
763 insts.push(Self::gen_move(
765 regs::writable_fp_reg(),
766 regs::stack_reg(),
767 types::I64,
768 ));
769 }
770 }
771
772 if flags.unwind_info() && setup_frame {
773 insts.push(Inst::Unwind {
776 inst: UnwindInst::DefineNewFrame {
777 offset_downward_to_clobbers: frame_layout.clobber_size,
778 offset_upward_to_caller_sp: frame_layout.setup_area_size,
779 },
780 });
781 }
782
783 let mut clobber_offset = frame_layout.clobber_size;
797 let clobber_offset_change = 16;
798 let iter = clobbered_int.chunks_exact(2);
799
800 if let [rd] = iter.remainder() {
801 let rd: Reg = rd.to_reg().into();
802
803 debug_assert_eq!(rd.class(), RegClass::Int);
804 insts.push(Inst::Store64 {
806 rd,
807 mem: AMode::SPPreIndexed {
808 simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(),
809 },
810 flags: MemFlags::trusted(),
811 });
812
813 if flags.unwind_info() {
814 clobber_offset -= clobber_offset_change as u32;
815 insts.push(Inst::Unwind {
816 inst: UnwindInst::SaveReg {
817 clobber_offset,
818 reg: rd.to_real_reg().unwrap(),
819 },
820 });
821 }
822 }
823
824 let mut iter = iter.rev();
825
826 while let Some([rt, rt2]) = iter.next() {
827 let rt: Reg = rt.to_reg().into();
829 let rt2: Reg = rt2.to_reg().into();
830
831 debug_assert!(rt.class() == RegClass::Int);
832 debug_assert!(rt2.class() == RegClass::Int);
833
834 insts.push(Inst::StoreP64 {
836 rt,
837 rt2,
838 mem: PairAMode::SPPreIndexed {
839 simm7: SImm7Scaled::maybe_from_i64(-clobber_offset_change, types::I64).unwrap(),
840 },
841 flags: MemFlags::trusted(),
842 });
843
844 if flags.unwind_info() {
845 clobber_offset -= clobber_offset_change as u32;
846 insts.push(Inst::Unwind {
847 inst: UnwindInst::SaveReg {
848 clobber_offset,
849 reg: rt.to_real_reg().unwrap(),
850 },
851 });
852 insts.push(Inst::Unwind {
853 inst: UnwindInst::SaveReg {
854 clobber_offset: clobber_offset + (clobber_offset_change / 2) as u32,
855 reg: rt2.to_real_reg().unwrap(),
856 },
857 });
858 }
859 }
860
861 let store_vec_reg = |rd| Inst::FpuStore64 {
862 rd,
863 mem: AMode::SPPreIndexed {
864 simm9: SImm9::maybe_from_i64(-clobber_offset_change).unwrap(),
865 },
866 flags: MemFlags::trusted(),
867 };
868 let iter = clobbered_vec.chunks_exact(2);
869
870 if let [rd] = iter.remainder() {
871 let rd: Reg = rd.to_reg().into();
872
873 debug_assert_eq!(rd.class(), RegClass::Float);
874 insts.push(store_vec_reg(rd));
875
876 if flags.unwind_info() {
877 clobber_offset -= clobber_offset_change as u32;
878 insts.push(Inst::Unwind {
879 inst: UnwindInst::SaveReg {
880 clobber_offset,
881 reg: rd.to_real_reg().unwrap(),
882 },
883 });
884 }
885 }
886
887 let store_vec_reg_pair = |rt, rt2| {
888 let clobber_offset_change = 16;
889
890 (
891 Inst::FpuStoreP64 {
892 rt,
893 rt2,
894 mem: PairAMode::SPPreIndexed {
895 simm7: SImm7Scaled::maybe_from_i64(-clobber_offset_change, F64).unwrap(),
896 },
897 flags: MemFlags::trusted(),
898 },
899 clobber_offset_change as u32,
900 )
901 };
902 let mut iter = iter.rev();
903
904 while let Some([rt, rt2]) = iter.next() {
905 let rt: Reg = rt.to_reg().into();
906 let rt2: Reg = rt2.to_reg().into();
907
908 debug_assert_eq!(rt.class(), RegClass::Float);
909 debug_assert_eq!(rt2.class(), RegClass::Float);
910
911 let (inst, clobber_offset_change) = store_vec_reg_pair(rt, rt2);
912
913 insts.push(inst);
914
915 if flags.unwind_info() {
916 clobber_offset -= clobber_offset_change;
917 insts.push(Inst::Unwind {
918 inst: UnwindInst::SaveReg {
919 clobber_offset,
920 reg: rt.to_real_reg().unwrap(),
921 },
922 });
923 insts.push(Inst::Unwind {
924 inst: UnwindInst::SaveReg {
925 clobber_offset: clobber_offset + clobber_offset_change / 2,
926 reg: rt2.to_real_reg().unwrap(),
927 },
928 });
929 }
930 }
931
932 let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
934 if stack_size > 0 {
935 insts.extend(Self::gen_sp_reg_adjust(-(stack_size as i32)));
936 if flags.unwind_info() {
937 insts.push(Inst::Unwind {
938 inst: UnwindInst::StackAlloc { size: stack_size },
939 });
940 }
941 }
942
943 insts
944 }
945
946 fn gen_clobber_restore(
947 _call_conv: isa::CallConv,
948 _flags: &settings::Flags,
949 frame_layout: &FrameLayout,
950 ) -> SmallVec<[Inst; 16]> {
951 let mut insts = SmallVec::new();
952 let (clobbered_int, clobbered_vec) = frame_layout.clobbered_callee_saves_by_class();
953
954 let stack_size = frame_layout.fixed_frame_storage_size + frame_layout.outgoing_args_size;
956 if stack_size > 0 {
957 insts.extend(Self::gen_sp_reg_adjust(stack_size as i32));
958 }
959
960 let load_vec_reg = |rd| Inst::FpuLoad64 {
961 rd,
962 mem: AMode::SPPostIndexed {
963 simm9: SImm9::maybe_from_i64(16).unwrap(),
964 },
965 flags: MemFlags::trusted(),
966 };
967 let load_vec_reg_pair = |rt, rt2| Inst::FpuLoadP64 {
968 rt,
969 rt2,
970 mem: PairAMode::SPPostIndexed {
971 simm7: SImm7Scaled::maybe_from_i64(16, F64).unwrap(),
972 },
973 flags: MemFlags::trusted(),
974 };
975
976 let mut iter = clobbered_vec.chunks_exact(2);
977
978 while let Some([rt, rt2]) = iter.next() {
979 let rt: Writable<Reg> = rt.map(|r| r.into());
980 let rt2: Writable<Reg> = rt2.map(|r| r.into());
981
982 debug_assert_eq!(rt.to_reg().class(), RegClass::Float);
983 debug_assert_eq!(rt2.to_reg().class(), RegClass::Float);
984 insts.push(load_vec_reg_pair(rt, rt2));
985 }
986
987 debug_assert!(iter.remainder().len() <= 1);
988
989 if let [rd] = iter.remainder() {
990 let rd: Writable<Reg> = rd.map(|r| r.into());
991
992 debug_assert_eq!(rd.to_reg().class(), RegClass::Float);
993 insts.push(load_vec_reg(rd));
994 }
995
996 let mut iter = clobbered_int.chunks_exact(2);
997
998 while let Some([rt, rt2]) = iter.next() {
999 let rt: Writable<Reg> = rt.map(|r| r.into());
1000 let rt2: Writable<Reg> = rt2.map(|r| r.into());
1001
1002 debug_assert_eq!(rt.to_reg().class(), RegClass::Int);
1003 debug_assert_eq!(rt2.to_reg().class(), RegClass::Int);
1004 insts.push(Inst::LoadP64 {
1006 rt,
1007 rt2,
1008 mem: PairAMode::SPPostIndexed {
1009 simm7: SImm7Scaled::maybe_from_i64(16, I64).unwrap(),
1010 },
1011 flags: MemFlags::trusted(),
1012 });
1013 }
1014
1015 debug_assert!(iter.remainder().len() <= 1);
1016
1017 if let [rd] = iter.remainder() {
1018 let rd: Writable<Reg> = rd.map(|r| r.into());
1019
1020 debug_assert_eq!(rd.to_reg().class(), RegClass::Int);
1021 insts.push(Inst::ULoad64 {
1023 rd,
1024 mem: AMode::SPPostIndexed {
1025 simm9: SImm9::maybe_from_i64(16).unwrap(),
1026 },
1027 flags: MemFlags::trusted(),
1028 });
1029 }
1030
1031 insts
1032 }
1033
1034 fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
1035 call_conv: isa::CallConv,
1036 dst: Reg,
1037 src: Reg,
1038 size: usize,
1039 mut alloc_tmp: F,
1040 ) -> SmallVec<[Self::I; 8]> {
1041 let mut insts = SmallVec::new();
1042 let arg0 = writable_xreg(0);
1043 let arg1 = writable_xreg(1);
1044 let arg2 = writable_xreg(2);
1045 let tmp = alloc_tmp(Self::word_type());
1046 insts.extend(Inst::load_constant(tmp, size as u64));
1047 insts.push(Inst::Call {
1048 info: Box::new(CallInfo {
1049 dest: ExternalName::LibCall(LibCall::Memcpy),
1050 uses: smallvec![
1051 CallArgPair {
1052 vreg: dst,
1053 preg: arg0.to_reg()
1054 },
1055 CallArgPair {
1056 vreg: src,
1057 preg: arg1.to_reg()
1058 },
1059 CallArgPair {
1060 vreg: tmp.to_reg(),
1061 preg: arg2.to_reg()
1062 }
1063 ],
1064 defs: smallvec![],
1065 clobbers: Self::get_regs_clobbered_by_call(call_conv, false),
1066 caller_conv: call_conv,
1067 callee_conv: call_conv,
1068 callee_pop_size: 0,
1069 try_call_info: None,
1070 }),
1071 });
1072 insts
1073 }
1074
1075 fn get_number_of_spillslots_for_value(
1076 rc: RegClass,
1077 vector_size: u32,
1078 _isa_flags: &Self::F,
1079 ) -> u32 {
1080 assert_eq!(vector_size % 8, 0);
1081 match rc {
1083 RegClass::Int => 1,
1084 RegClass::Float => vector_size / 8,
1085 RegClass::Vector => unreachable!(),
1086 }
1087 }
1088
1089 fn get_machine_env(flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
1090 if flags.enable_pinned_reg() {
1091 static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
1092 MACHINE_ENV.get_or_init(|| create_reg_env(true))
1093 } else {
1094 static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
1095 MACHINE_ENV.get_or_init(|| create_reg_env(false))
1096 }
1097 }
1098
1099 fn get_regs_clobbered_by_call(call_conv: isa::CallConv, is_exception: bool) -> PRegSet {
1100 match (call_conv, is_exception) {
1101 (isa::CallConv::Tail, true) => ALL_CLOBBERS,
1102 (isa::CallConv::Winch, true) => ALL_CLOBBERS,
1103 (isa::CallConv::Winch, false) => WINCH_CLOBBERS,
1104 (isa::CallConv::SystemV, _) => DEFAULT_AAPCS_CLOBBERS,
1105 (_, false) => DEFAULT_AAPCS_CLOBBERS,
1106 (_, true) => panic!("unimplemented clobbers for exn abi of {call_conv:?}"),
1107 }
1108 }
1109
1110 fn get_ext_mode(
1111 call_conv: isa::CallConv,
1112 specified: ir::ArgumentExtension,
1113 ) -> ir::ArgumentExtension {
1114 if call_conv == isa::CallConv::AppleAarch64 {
1115 specified
1116 } else {
1117 ir::ArgumentExtension::None
1118 }
1119 }
1120
1121 fn compute_frame_layout(
1122 call_conv: isa::CallConv,
1123 flags: &settings::Flags,
1124 sig: &Signature,
1125 regs: &[Writable<RealReg>],
1126 function_calls: FunctionCalls,
1127 incoming_args_size: u32,
1128 tail_args_size: u32,
1129 stackslots_size: u32,
1130 fixed_frame_storage_size: u32,
1131 outgoing_args_size: u32,
1132 ) -> FrameLayout {
1133 let mut regs: Vec<Writable<RealReg>> = regs
1134 .iter()
1135 .cloned()
1136 .filter(|r| {
1137 is_reg_saved_in_prologue(call_conv, flags.enable_pinned_reg(), sig, r.to_reg())
1138 })
1139 .collect();
1140
1141 regs.sort_unstable();
1144
1145 let clobber_size = compute_clobber_size(®s);
1147
1148 let setup_area_size = if flags.preserve_frame_pointers()
1150 || function_calls != FunctionCalls::None
1151 || incoming_args_size > 0
1154 || clobber_size > 0
1155 || fixed_frame_storage_size > 0
1156 {
1157 16 } else {
1159 0
1160 };
1161
1162 FrameLayout {
1164 word_bytes: 8,
1165 incoming_args_size,
1166 tail_args_size,
1167 setup_area_size,
1168 clobber_size,
1169 fixed_frame_storage_size,
1170 stackslots_size,
1171 outgoing_args_size,
1172 clobbered_callee_saves: regs,
1173 function_calls,
1174 }
1175 }
1176
1177 fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
1178 regs::writable_xreg(9)
1181 }
1182
1183 fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
1184 const PAYLOAD_REGS: &'static [Reg] = &[regs::xreg(0), regs::xreg(1)];
1185 match call_conv {
1186 isa::CallConv::SystemV | isa::CallConv::Tail => PAYLOAD_REGS,
1187 _ => &[],
1188 }
1189 }
1190}
1191
1192impl AArch64MachineDeps {
1193 fn gen_probestack_unroll(insts: &mut SmallInstVec<Inst>, guard_size: u32, probe_count: u32) {
1194 for _ in 0..probe_count {
1202 insts.extend(Self::gen_sp_reg_adjust(-(guard_size as i32)));
1203
1204 insts.push(Inst::gen_store(
1205 AMode::SPOffset { off: 0 },
1206 zero_reg(),
1207 I32,
1208 MemFlags::trusted(),
1209 ));
1210 }
1211
1212 insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1214 }
1215
1216 fn gen_probestack_loop(insts: &mut SmallInstVec<Inst>, frame_size: u32, guard_size: u32) {
1217 let start = writable_spilltmp_reg();
1226 let end = writable_tmp2_reg();
1227 insts.extend(Inst::load_constant(start, 0));
1230 insts.extend(Inst::load_constant(end, frame_size.into()));
1231 insts.push(Inst::StackProbeLoop {
1232 start,
1233 end: end.to_reg(),
1234 step: Imm12::maybe_from_u64(guard_size.into()).unwrap(),
1235 });
1236 }
1237
1238 pub fn select_api_key(
1239 isa_flags: &aarch64_settings::Flags,
1240 call_conv: isa::CallConv,
1241 setup_frame: bool,
1242 ) -> Option<APIKey> {
1243 if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
1244 Some(if isa_flags.sign_return_address_with_bkey() {
1249 match call_conv {
1250 isa::CallConv::Tail => APIKey::BZ,
1251 _ => APIKey::BSP,
1252 }
1253 } else {
1254 match call_conv {
1255 isa::CallConv::Tail => APIKey::AZ,
1256 _ => APIKey::ASP,
1257 }
1258 })
1259 } else {
1260 None
1261 }
1262 }
1263}
1264
1265fn is_reg_saved_in_prologue(
1268 _call_conv: isa::CallConv,
1269 enable_pinned_reg: bool,
1270 sig: &Signature,
1271 r: RealReg,
1272) -> bool {
1273 let save_z_regs = sig
1275 .params
1276 .iter()
1277 .filter(|p| p.value_type.is_dynamic_vector())
1278 .count()
1279 != 0;
1280
1281 match r.class() {
1282 RegClass::Int => {
1283 if enable_pinned_reg && r.hw_enc() == PINNED_REG {
1288 false
1289 } else {
1290 r.hw_enc() >= 19 && r.hw_enc() <= 28
1291 }
1292 }
1293 RegClass::Float => {
1294 if save_z_regs {
1300 r.hw_enc() >= 8 && r.hw_enc() <= 23
1301 } else {
1302 r.hw_enc() >= 8 && r.hw_enc() <= 15
1304 }
1305 }
1306 RegClass::Vector => unreachable!(),
1307 }
1308}
1309
1310const fn default_aapcs_clobbers() -> PRegSet {
1311 PRegSet::empty()
1312 .with(xreg_preg(0))
1314 .with(xreg_preg(1))
1315 .with(xreg_preg(2))
1316 .with(xreg_preg(3))
1317 .with(xreg_preg(4))
1318 .with(xreg_preg(5))
1319 .with(xreg_preg(6))
1320 .with(xreg_preg(7))
1321 .with(xreg_preg(8))
1322 .with(xreg_preg(9))
1323 .with(xreg_preg(10))
1324 .with(xreg_preg(11))
1325 .with(xreg_preg(12))
1326 .with(xreg_preg(13))
1327 .with(xreg_preg(14))
1328 .with(xreg_preg(15))
1329 .with(xreg_preg(16))
1330 .with(xreg_preg(17))
1331 .with(vreg_preg(0))
1347 .with(vreg_preg(1))
1348 .with(vreg_preg(2))
1349 .with(vreg_preg(3))
1350 .with(vreg_preg(4))
1351 .with(vreg_preg(5))
1352 .with(vreg_preg(6))
1353 .with(vreg_preg(7))
1354 .with(vreg_preg(8))
1355 .with(vreg_preg(9))
1356 .with(vreg_preg(10))
1357 .with(vreg_preg(11))
1358 .with(vreg_preg(12))
1359 .with(vreg_preg(13))
1360 .with(vreg_preg(14))
1361 .with(vreg_preg(15))
1362 .with(vreg_preg(16))
1363 .with(vreg_preg(17))
1364 .with(vreg_preg(18))
1365 .with(vreg_preg(19))
1366 .with(vreg_preg(20))
1367 .with(vreg_preg(21))
1368 .with(vreg_preg(22))
1369 .with(vreg_preg(23))
1370 .with(vreg_preg(24))
1371 .with(vreg_preg(25))
1372 .with(vreg_preg(26))
1373 .with(vreg_preg(27))
1374 .with(vreg_preg(28))
1375 .with(vreg_preg(29))
1376 .with(vreg_preg(30))
1377 .with(vreg_preg(31))
1378}
1379
1380const fn winch_clobbers() -> PRegSet {
1381 PRegSet::empty()
1382 .with(xreg_preg(0))
1383 .with(xreg_preg(1))
1384 .with(xreg_preg(2))
1385 .with(xreg_preg(3))
1386 .with(xreg_preg(4))
1387 .with(xreg_preg(5))
1388 .with(xreg_preg(6))
1389 .with(xreg_preg(7))
1390 .with(xreg_preg(8))
1391 .with(xreg_preg(9))
1392 .with(xreg_preg(10))
1393 .with(xreg_preg(11))
1394 .with(xreg_preg(12))
1395 .with(xreg_preg(13))
1396 .with(xreg_preg(14))
1397 .with(xreg_preg(15))
1398 .with(xreg_preg(16))
1399 .with(xreg_preg(17))
1400 .with(xreg_preg(19))
1404 .with(xreg_preg(20))
1405 .with(xreg_preg(21))
1406 .with(xreg_preg(22))
1407 .with(xreg_preg(23))
1408 .with(xreg_preg(24))
1409 .with(xreg_preg(25))
1410 .with(xreg_preg(26))
1411 .with(xreg_preg(27))
1412 .with(vreg_preg(0))
1417 .with(vreg_preg(1))
1418 .with(vreg_preg(2))
1419 .with(vreg_preg(3))
1420 .with(vreg_preg(4))
1421 .with(vreg_preg(5))
1422 .with(vreg_preg(6))
1423 .with(vreg_preg(7))
1424 .with(vreg_preg(8))
1425 .with(vreg_preg(9))
1426 .with(vreg_preg(10))
1427 .with(vreg_preg(11))
1428 .with(vreg_preg(12))
1429 .with(vreg_preg(13))
1430 .with(vreg_preg(14))
1431 .with(vreg_preg(15))
1432 .with(vreg_preg(16))
1433 .with(vreg_preg(17))
1434 .with(vreg_preg(18))
1435 .with(vreg_preg(19))
1436 .with(vreg_preg(20))
1437 .with(vreg_preg(21))
1438 .with(vreg_preg(22))
1439 .with(vreg_preg(23))
1440 .with(vreg_preg(24))
1441 .with(vreg_preg(25))
1442 .with(vreg_preg(26))
1443 .with(vreg_preg(27))
1444 .with(vreg_preg(28))
1445 .with(vreg_preg(29))
1446 .with(vreg_preg(30))
1447 .with(vreg_preg(31))
1448}
1449
1450const fn all_clobbers() -> PRegSet {
1451 PRegSet::empty()
1452 .with(xreg_preg(0))
1455 .with(xreg_preg(1))
1456 .with(xreg_preg(2))
1457 .with(xreg_preg(3))
1458 .with(xreg_preg(4))
1459 .with(xreg_preg(5))
1460 .with(xreg_preg(6))
1461 .with(xreg_preg(7))
1462 .with(xreg_preg(8))
1463 .with(xreg_preg(9))
1464 .with(xreg_preg(10))
1465 .with(xreg_preg(11))
1466 .with(xreg_preg(12))
1467 .with(xreg_preg(13))
1468 .with(xreg_preg(14))
1469 .with(xreg_preg(15))
1470 .with(xreg_preg(16))
1471 .with(xreg_preg(17))
1472 .with(xreg_preg(18))
1473 .with(xreg_preg(19))
1474 .with(xreg_preg(20))
1475 .with(xreg_preg(21))
1476 .with(xreg_preg(22))
1477 .with(xreg_preg(23))
1478 .with(xreg_preg(24))
1479 .with(xreg_preg(25))
1480 .with(xreg_preg(26))
1481 .with(xreg_preg(27))
1482 .with(xreg_preg(28))
1483 .with(vreg_preg(0))
1485 .with(vreg_preg(1))
1486 .with(vreg_preg(2))
1487 .with(vreg_preg(3))
1488 .with(vreg_preg(4))
1489 .with(vreg_preg(5))
1490 .with(vreg_preg(6))
1491 .with(vreg_preg(7))
1492 .with(vreg_preg(8))
1493 .with(vreg_preg(9))
1494 .with(vreg_preg(10))
1495 .with(vreg_preg(11))
1496 .with(vreg_preg(12))
1497 .with(vreg_preg(13))
1498 .with(vreg_preg(14))
1499 .with(vreg_preg(15))
1500 .with(vreg_preg(16))
1501 .with(vreg_preg(17))
1502 .with(vreg_preg(18))
1503 .with(vreg_preg(19))
1504 .with(vreg_preg(20))
1505 .with(vreg_preg(21))
1506 .with(vreg_preg(22))
1507 .with(vreg_preg(23))
1508 .with(vreg_preg(24))
1509 .with(vreg_preg(25))
1510 .with(vreg_preg(26))
1511 .with(vreg_preg(27))
1512 .with(vreg_preg(28))
1513 .with(vreg_preg(29))
1514 .with(vreg_preg(30))
1515 .with(vreg_preg(31))
1516}
1517
1518const DEFAULT_AAPCS_CLOBBERS: PRegSet = default_aapcs_clobbers();
1519const WINCH_CLOBBERS: PRegSet = winch_clobbers();
1520const ALL_CLOBBERS: PRegSet = all_clobbers();
1521
1522fn create_reg_env(enable_pinned_reg: bool) -> MachineEnv {
1523 fn preg(r: Reg) -> PReg {
1524 r.to_real_reg().unwrap().into()
1525 }
1526
1527 let mut env = MachineEnv {
1528 preferred_regs_by_class: [
1529 vec![
1530 preg(xreg(0)),
1531 preg(xreg(1)),
1532 preg(xreg(2)),
1533 preg(xreg(3)),
1534 preg(xreg(4)),
1535 preg(xreg(5)),
1536 preg(xreg(6)),
1537 preg(xreg(7)),
1538 preg(xreg(8)),
1539 preg(xreg(9)),
1540 preg(xreg(10)),
1541 preg(xreg(11)),
1542 preg(xreg(12)),
1543 preg(xreg(13)),
1544 preg(xreg(14)),
1545 preg(xreg(15)),
1546 ],
1553 vec![
1554 preg(vreg(0)),
1555 preg(vreg(1)),
1556 preg(vreg(2)),
1557 preg(vreg(3)),
1558 preg(vreg(4)),
1559 preg(vreg(5)),
1560 preg(vreg(6)),
1561 preg(vreg(7)),
1562 preg(vreg(16)),
1564 preg(vreg(17)),
1565 preg(vreg(18)),
1566 preg(vreg(19)),
1567 preg(vreg(20)),
1568 preg(vreg(21)),
1569 preg(vreg(22)),
1570 preg(vreg(23)),
1571 preg(vreg(24)),
1572 preg(vreg(25)),
1573 preg(vreg(26)),
1574 preg(vreg(27)),
1575 preg(vreg(28)),
1576 preg(vreg(29)),
1577 preg(vreg(30)),
1578 preg(vreg(31)),
1579 ],
1580 vec![],
1582 ],
1583 non_preferred_regs_by_class: [
1584 vec![
1585 preg(xreg(19)),
1586 preg(xreg(20)),
1587 preg(xreg(22)),
1589 preg(xreg(23)),
1590 preg(xreg(24)),
1591 preg(xreg(25)),
1592 preg(xreg(26)),
1593 preg(xreg(27)),
1594 preg(xreg(28)),
1595 ],
1596 vec![
1597 preg(vreg(8)),
1598 preg(vreg(9)),
1599 preg(vreg(10)),
1600 preg(vreg(11)),
1601 preg(vreg(12)),
1602 preg(vreg(13)),
1603 preg(vreg(14)),
1604 preg(vreg(15)),
1605 ],
1606 vec![],
1608 ],
1609 fixed_stack_slots: vec![],
1610 scratch_by_class: [None, None, None],
1611 };
1612
1613 if !enable_pinned_reg {
1614 debug_assert_eq!(PINNED_REG, 21); env.non_preferred_regs_by_class[0].push(preg(xreg(PINNED_REG)));
1616 }
1617
1618 env
1619}