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::borrow::ToOwned;
16use alloc::boxed::Box;
17use alloc::vec::Vec;
18use regalloc2::{MachineEnv, PReg, PRegSet};
19use smallvec::{SmallVec, smallvec};
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 core::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 patchable: false,
1071 }),
1072 });
1073 insts
1074 }
1075
1076 fn get_number_of_spillslots_for_value(
1077 rc: RegClass,
1078 vector_size: u32,
1079 _isa_flags: &Self::F,
1080 ) -> u32 {
1081 assert_eq!(vector_size % 8, 0);
1082 match rc {
1084 RegClass::Int => 1,
1085 RegClass::Float => vector_size / 8,
1086 RegClass::Vector => unreachable!(),
1087 }
1088 }
1089
1090 fn get_machine_env(flags: &settings::Flags, _call_conv: isa::CallConv) -> &MachineEnv {
1091 if flags.enable_pinned_reg() {
1092 static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
1093 MACHINE_ENV.get_or_init(|| create_reg_env(true))
1094 } else {
1095 static MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
1096 MACHINE_ENV.get_or_init(|| create_reg_env(false))
1097 }
1098 }
1099
1100 fn get_regs_clobbered_by_call(call_conv: isa::CallConv, is_exception: bool) -> PRegSet {
1101 match (call_conv, is_exception) {
1102 (isa::CallConv::Tail, true) => ALL_CLOBBERS,
1103 (isa::CallConv::Winch, true) => ALL_CLOBBERS,
1104 (isa::CallConv::Winch, false) => WINCH_CLOBBERS,
1105 (isa::CallConv::PreserveAll, true) => ALL_CLOBBERS,
1111 (isa::CallConv::SystemV, _) => DEFAULT_AAPCS_CLOBBERS,
1112 (isa::CallConv::PreserveAll, _) => NO_CLOBBERS,
1113 (_, false) => DEFAULT_AAPCS_CLOBBERS,
1114 (_, true) => panic!("unimplemented clobbers for exn abi of {call_conv:?}"),
1115 }
1116 }
1117
1118 fn get_ext_mode(
1119 call_conv: isa::CallConv,
1120 specified: ir::ArgumentExtension,
1121 ) -> ir::ArgumentExtension {
1122 if call_conv == isa::CallConv::AppleAarch64 {
1123 specified
1124 } else {
1125 ir::ArgumentExtension::None
1126 }
1127 }
1128
1129 fn compute_frame_layout(
1130 call_conv: isa::CallConv,
1131 flags: &settings::Flags,
1132 sig: &Signature,
1133 regs: &[Writable<RealReg>],
1134 function_calls: FunctionCalls,
1135 incoming_args_size: u32,
1136 tail_args_size: u32,
1137 stackslots_size: u32,
1138 fixed_frame_storage_size: u32,
1139 outgoing_args_size: u32,
1140 ) -> FrameLayout {
1141 let mut regs: Vec<Writable<RealReg>> = regs
1142 .iter()
1143 .cloned()
1144 .filter(|r| {
1145 is_reg_saved_in_prologue(call_conv, flags.enable_pinned_reg(), sig, r.to_reg())
1146 })
1147 .collect();
1148
1149 regs.sort_unstable();
1152
1153 let clobber_size = compute_clobber_size(®s);
1155
1156 let setup_area_size = if flags.preserve_frame_pointers()
1158 || function_calls != FunctionCalls::None
1159 || incoming_args_size > 0
1162 || clobber_size > 0
1163 || fixed_frame_storage_size > 0
1164 {
1165 16 } else {
1167 0
1168 };
1169
1170 FrameLayout {
1172 word_bytes: 8,
1173 incoming_args_size,
1174 tail_args_size,
1175 setup_area_size,
1176 clobber_size,
1177 fixed_frame_storage_size,
1178 stackslots_size,
1179 outgoing_args_size,
1180 clobbered_callee_saves: regs,
1181 function_calls,
1182 }
1183 }
1184
1185 fn retval_temp_reg(_call_conv_of_callee: isa::CallConv) -> Writable<Reg> {
1186 regs::writable_xreg(9)
1189 }
1190
1191 fn exception_payload_regs(call_conv: isa::CallConv) -> &'static [Reg] {
1192 const PAYLOAD_REGS: &'static [Reg] = &[regs::xreg(0), regs::xreg(1)];
1193 match call_conv {
1194 isa::CallConv::SystemV | isa::CallConv::Tail | isa::CallConv::PreserveAll => {
1195 PAYLOAD_REGS
1196 }
1197 _ => &[],
1198 }
1199 }
1200}
1201
1202impl AArch64MachineDeps {
1203 fn gen_probestack_unroll(insts: &mut SmallInstVec<Inst>, guard_size: u32, probe_count: u32) {
1204 for _ in 0..probe_count {
1212 insts.extend(Self::gen_sp_reg_adjust(-(guard_size as i32)));
1213
1214 insts.push(Inst::gen_store(
1215 AMode::SPOffset { off: 0 },
1216 zero_reg(),
1217 I32,
1218 MemFlags::trusted(),
1219 ));
1220 }
1221
1222 insts.extend(Self::gen_sp_reg_adjust((guard_size * probe_count) as i32));
1224 }
1225
1226 fn gen_probestack_loop(insts: &mut SmallInstVec<Inst>, frame_size: u32, guard_size: u32) {
1227 let start = writable_spilltmp_reg();
1236 let end = writable_tmp2_reg();
1237 insts.extend(Inst::load_constant(start, 0));
1240 insts.extend(Inst::load_constant(end, frame_size.into()));
1241 insts.push(Inst::StackProbeLoop {
1242 start,
1243 end: end.to_reg(),
1244 step: Imm12::maybe_from_u64(guard_size.into()).unwrap(),
1245 });
1246 }
1247
1248 pub fn select_api_key(
1249 isa_flags: &aarch64_settings::Flags,
1250 call_conv: isa::CallConv,
1251 setup_frame: bool,
1252 ) -> Option<APIKey> {
1253 if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
1254 Some(if isa_flags.sign_return_address_with_bkey() {
1259 match call_conv {
1260 isa::CallConv::Tail => APIKey::BZ,
1261 _ => APIKey::BSP,
1262 }
1263 } else {
1264 match call_conv {
1265 isa::CallConv::Tail => APIKey::AZ,
1266 _ => APIKey::ASP,
1267 }
1268 })
1269 } else {
1270 None
1271 }
1272 }
1273}
1274
1275fn is_reg_saved_in_prologue(
1278 call_conv: isa::CallConv,
1279 enable_pinned_reg: bool,
1280 sig: &Signature,
1281 r: RealReg,
1282) -> bool {
1283 if call_conv == isa::CallConv::PreserveAll {
1284 return true;
1285 }
1286
1287 let save_z_regs = sig
1289 .params
1290 .iter()
1291 .filter(|p| p.value_type.is_dynamic_vector())
1292 .count()
1293 != 0;
1294
1295 match r.class() {
1296 RegClass::Int => {
1297 if enable_pinned_reg && r.hw_enc() == PINNED_REG {
1302 false
1303 } else {
1304 r.hw_enc() >= 19 && r.hw_enc() <= 28
1305 }
1306 }
1307 RegClass::Float => {
1308 if save_z_regs {
1314 r.hw_enc() >= 8 && r.hw_enc() <= 23
1315 } else {
1316 r.hw_enc() >= 8 && r.hw_enc() <= 15
1318 }
1319 }
1320 RegClass::Vector => unreachable!(),
1321 }
1322}
1323
1324const fn default_aapcs_clobbers() -> PRegSet {
1325 PRegSet::empty()
1326 .with(xreg_preg(0))
1328 .with(xreg_preg(1))
1329 .with(xreg_preg(2))
1330 .with(xreg_preg(3))
1331 .with(xreg_preg(4))
1332 .with(xreg_preg(5))
1333 .with(xreg_preg(6))
1334 .with(xreg_preg(7))
1335 .with(xreg_preg(8))
1336 .with(xreg_preg(9))
1337 .with(xreg_preg(10))
1338 .with(xreg_preg(11))
1339 .with(xreg_preg(12))
1340 .with(xreg_preg(13))
1341 .with(xreg_preg(14))
1342 .with(xreg_preg(15))
1343 .with(xreg_preg(16))
1344 .with(xreg_preg(17))
1345 .with(vreg_preg(0))
1361 .with(vreg_preg(1))
1362 .with(vreg_preg(2))
1363 .with(vreg_preg(3))
1364 .with(vreg_preg(4))
1365 .with(vreg_preg(5))
1366 .with(vreg_preg(6))
1367 .with(vreg_preg(7))
1368 .with(vreg_preg(8))
1369 .with(vreg_preg(9))
1370 .with(vreg_preg(10))
1371 .with(vreg_preg(11))
1372 .with(vreg_preg(12))
1373 .with(vreg_preg(13))
1374 .with(vreg_preg(14))
1375 .with(vreg_preg(15))
1376 .with(vreg_preg(16))
1377 .with(vreg_preg(17))
1378 .with(vreg_preg(18))
1379 .with(vreg_preg(19))
1380 .with(vreg_preg(20))
1381 .with(vreg_preg(21))
1382 .with(vreg_preg(22))
1383 .with(vreg_preg(23))
1384 .with(vreg_preg(24))
1385 .with(vreg_preg(25))
1386 .with(vreg_preg(26))
1387 .with(vreg_preg(27))
1388 .with(vreg_preg(28))
1389 .with(vreg_preg(29))
1390 .with(vreg_preg(30))
1391 .with(vreg_preg(31))
1392}
1393
1394const fn winch_clobbers() -> PRegSet {
1395 PRegSet::empty()
1396 .with(xreg_preg(0))
1397 .with(xreg_preg(1))
1398 .with(xreg_preg(2))
1399 .with(xreg_preg(3))
1400 .with(xreg_preg(4))
1401 .with(xreg_preg(5))
1402 .with(xreg_preg(6))
1403 .with(xreg_preg(7))
1404 .with(xreg_preg(8))
1405 .with(xreg_preg(9))
1406 .with(xreg_preg(10))
1407 .with(xreg_preg(11))
1408 .with(xreg_preg(12))
1409 .with(xreg_preg(13))
1410 .with(xreg_preg(14))
1411 .with(xreg_preg(15))
1412 .with(xreg_preg(16))
1413 .with(xreg_preg(17))
1414 .with(xreg_preg(19))
1418 .with(xreg_preg(20))
1419 .with(xreg_preg(21))
1420 .with(xreg_preg(22))
1421 .with(xreg_preg(23))
1422 .with(xreg_preg(24))
1423 .with(xreg_preg(25))
1424 .with(xreg_preg(26))
1425 .with(xreg_preg(27))
1426 .with(vreg_preg(0))
1431 .with(vreg_preg(1))
1432 .with(vreg_preg(2))
1433 .with(vreg_preg(3))
1434 .with(vreg_preg(4))
1435 .with(vreg_preg(5))
1436 .with(vreg_preg(6))
1437 .with(vreg_preg(7))
1438 .with(vreg_preg(8))
1439 .with(vreg_preg(9))
1440 .with(vreg_preg(10))
1441 .with(vreg_preg(11))
1442 .with(vreg_preg(12))
1443 .with(vreg_preg(13))
1444 .with(vreg_preg(14))
1445 .with(vreg_preg(15))
1446 .with(vreg_preg(16))
1447 .with(vreg_preg(17))
1448 .with(vreg_preg(18))
1449 .with(vreg_preg(19))
1450 .with(vreg_preg(20))
1451 .with(vreg_preg(21))
1452 .with(vreg_preg(22))
1453 .with(vreg_preg(23))
1454 .with(vreg_preg(24))
1455 .with(vreg_preg(25))
1456 .with(vreg_preg(26))
1457 .with(vreg_preg(27))
1458 .with(vreg_preg(28))
1459 .with(vreg_preg(29))
1460 .with(vreg_preg(30))
1461 .with(vreg_preg(31))
1462}
1463
1464const fn all_clobbers() -> PRegSet {
1465 PRegSet::empty()
1466 .with(xreg_preg(0))
1469 .with(xreg_preg(1))
1470 .with(xreg_preg(2))
1471 .with(xreg_preg(3))
1472 .with(xreg_preg(4))
1473 .with(xreg_preg(5))
1474 .with(xreg_preg(6))
1475 .with(xreg_preg(7))
1476 .with(xreg_preg(8))
1477 .with(xreg_preg(9))
1478 .with(xreg_preg(10))
1479 .with(xreg_preg(11))
1480 .with(xreg_preg(12))
1481 .with(xreg_preg(13))
1482 .with(xreg_preg(14))
1483 .with(xreg_preg(15))
1484 .with(xreg_preg(16))
1485 .with(xreg_preg(17))
1486 .with(xreg_preg(18))
1487 .with(xreg_preg(19))
1488 .with(xreg_preg(20))
1489 .with(xreg_preg(21))
1490 .with(xreg_preg(22))
1491 .with(xreg_preg(23))
1492 .with(xreg_preg(24))
1493 .with(xreg_preg(25))
1494 .with(xreg_preg(26))
1495 .with(xreg_preg(27))
1496 .with(xreg_preg(28))
1497 .with(vreg_preg(0))
1499 .with(vreg_preg(1))
1500 .with(vreg_preg(2))
1501 .with(vreg_preg(3))
1502 .with(vreg_preg(4))
1503 .with(vreg_preg(5))
1504 .with(vreg_preg(6))
1505 .with(vreg_preg(7))
1506 .with(vreg_preg(8))
1507 .with(vreg_preg(9))
1508 .with(vreg_preg(10))
1509 .with(vreg_preg(11))
1510 .with(vreg_preg(12))
1511 .with(vreg_preg(13))
1512 .with(vreg_preg(14))
1513 .with(vreg_preg(15))
1514 .with(vreg_preg(16))
1515 .with(vreg_preg(17))
1516 .with(vreg_preg(18))
1517 .with(vreg_preg(19))
1518 .with(vreg_preg(20))
1519 .with(vreg_preg(21))
1520 .with(vreg_preg(22))
1521 .with(vreg_preg(23))
1522 .with(vreg_preg(24))
1523 .with(vreg_preg(25))
1524 .with(vreg_preg(26))
1525 .with(vreg_preg(27))
1526 .with(vreg_preg(28))
1527 .with(vreg_preg(29))
1528 .with(vreg_preg(30))
1529 .with(vreg_preg(31))
1530}
1531
1532const DEFAULT_AAPCS_CLOBBERS: PRegSet = default_aapcs_clobbers();
1533const WINCH_CLOBBERS: PRegSet = winch_clobbers();
1534const ALL_CLOBBERS: PRegSet = all_clobbers();
1535const NO_CLOBBERS: PRegSet = PRegSet::empty();
1536
1537fn create_reg_env(enable_pinned_reg: bool) -> MachineEnv {
1538 fn preg(r: Reg) -> PReg {
1539 r.to_real_reg().unwrap().into()
1540 }
1541
1542 let mut env = MachineEnv {
1543 preferred_regs_by_class: [
1544 vec![
1545 preg(xreg(0)),
1546 preg(xreg(1)),
1547 preg(xreg(2)),
1548 preg(xreg(3)),
1549 preg(xreg(4)),
1550 preg(xreg(5)),
1551 preg(xreg(6)),
1552 preg(xreg(7)),
1553 preg(xreg(8)),
1554 preg(xreg(9)),
1555 preg(xreg(10)),
1556 preg(xreg(11)),
1557 preg(xreg(12)),
1558 preg(xreg(13)),
1559 preg(xreg(14)),
1560 preg(xreg(15)),
1561 ],
1568 vec![
1569 preg(vreg(0)),
1570 preg(vreg(1)),
1571 preg(vreg(2)),
1572 preg(vreg(3)),
1573 preg(vreg(4)),
1574 preg(vreg(5)),
1575 preg(vreg(6)),
1576 preg(vreg(7)),
1577 preg(vreg(16)),
1579 preg(vreg(17)),
1580 preg(vreg(18)),
1581 preg(vreg(19)),
1582 preg(vreg(20)),
1583 preg(vreg(21)),
1584 preg(vreg(22)),
1585 preg(vreg(23)),
1586 preg(vreg(24)),
1587 preg(vreg(25)),
1588 preg(vreg(26)),
1589 preg(vreg(27)),
1590 preg(vreg(28)),
1591 preg(vreg(29)),
1592 preg(vreg(30)),
1593 preg(vreg(31)),
1594 ],
1595 vec![],
1597 ],
1598 non_preferred_regs_by_class: [
1599 vec![
1600 preg(xreg(19)),
1601 preg(xreg(20)),
1602 preg(xreg(22)),
1604 preg(xreg(23)),
1605 preg(xreg(24)),
1606 preg(xreg(25)),
1607 preg(xreg(26)),
1608 preg(xreg(27)),
1609 preg(xreg(28)),
1610 ],
1611 vec![
1612 preg(vreg(8)),
1613 preg(vreg(9)),
1614 preg(vreg(10)),
1615 preg(vreg(11)),
1616 preg(vreg(12)),
1617 preg(vreg(13)),
1618 preg(vreg(14)),
1619 preg(vreg(15)),
1620 ],
1621 vec![],
1623 ],
1624 fixed_stack_slots: vec![],
1625 scratch_by_class: [None, None, None],
1626 };
1627
1628 if !enable_pinned_reg {
1629 debug_assert_eq!(PINNED_REG, 21); env.non_preferred_regs_by_class[0].push(preg(xreg(PINNED_REG)));
1631 }
1632
1633 env
1634}