1use crate::ir;
136use crate::ir::condcodes::IntCC;
137use crate::ir::types;
138use crate::ir::MemFlags;
139use crate::ir::Signature;
140use crate::ir::Type;
141use crate::isa;
142use crate::isa::s390x::{inst::*, settings as s390x_settings};
143use crate::isa::unwind::UnwindInst;
144use crate::machinst::*;
145use crate::settings;
146use crate::CodegenResult;
147use alloc::vec::Vec;
148use regalloc2::{MachineEnv, PRegSet};
149use smallvec::{smallvec, SmallVec};
150use std::borrow::ToOwned;
151use std::sync::OnceLock;
152
153pub type S390xCallee = Callee<S390xMachineDeps>;
157
158fn in_int_reg(ty: Type) -> bool {
161 match ty {
162 types::I8 | types::I16 | types::I32 | types::I64 => true,
163 _ => false,
164 }
165}
166
167fn in_flt_reg(ty: Type) -> bool {
168 match ty {
169 types::F32 | types::F64 => true,
170 _ => false,
171 }
172}
173
174fn in_vec_reg(ty: Type) -> bool {
175 ty.is_vector() && ty.bits() == 128
176}
177
178fn get_intreg_for_arg(call_conv: isa::CallConv, idx: usize) -> Option<Reg> {
179 match idx {
180 0 => Some(regs::gpr(2)),
181 1 => Some(regs::gpr(3)),
182 2 => Some(regs::gpr(4)),
183 3 => Some(regs::gpr(5)),
184 4 => Some(regs::gpr(6)),
185 5 if call_conv == isa::CallConv::Tail => Some(regs::gpr(7)),
186 _ => None,
187 }
188}
189
190fn get_fltreg_for_arg(idx: usize) -> Option<Reg> {
191 match idx {
192 0 => Some(regs::vr(0)),
193 1 => Some(regs::vr(2)),
194 2 => Some(regs::vr(4)),
195 3 => Some(regs::vr(6)),
196 _ => None,
197 }
198}
199
200fn get_vecreg_for_arg(idx: usize) -> Option<Reg> {
201 match idx {
202 0 => Some(regs::vr(24)),
203 1 => Some(regs::vr(25)),
204 2 => Some(regs::vr(26)),
205 3 => Some(regs::vr(27)),
206 4 => Some(regs::vr(28)),
207 5 => Some(regs::vr(29)),
208 6 => Some(regs::vr(30)),
209 7 => Some(regs::vr(31)),
210 _ => None,
211 }
212}
213
214fn get_intreg_for_ret(call_conv: isa::CallConv, idx: usize) -> Option<Reg> {
215 match idx {
216 0 => Some(regs::gpr(2)),
217 1 => Some(regs::gpr(3)),
219 2 => Some(regs::gpr(4)),
220 3 => Some(regs::gpr(5)),
221 4 if call_conv == isa::CallConv::Tail => Some(regs::gpr(6)),
222 5 if call_conv == isa::CallConv::Tail => Some(regs::gpr(7)),
223 _ => None,
224 }
225}
226
227fn get_fltreg_for_ret(idx: usize) -> Option<Reg> {
228 match idx {
229 0 => Some(regs::vr(0)),
230 1 => Some(regs::vr(2)),
232 2 => Some(regs::vr(4)),
233 3 => Some(regs::vr(6)),
234 _ => None,
235 }
236}
237
238fn get_vecreg_for_ret(idx: usize) -> Option<Reg> {
239 match idx {
240 0 => Some(regs::vr(24)),
241 1 => Some(regs::vr(25)),
243 2 => Some(regs::vr(26)),
244 3 => Some(regs::vr(27)),
245 4 => Some(regs::vr(28)),
246 5 => Some(regs::vr(29)),
247 6 => Some(regs::vr(30)),
248 7 => Some(regs::vr(31)),
249 _ => None,
250 }
251}
252
253pub static REG_SAVE_AREA_SIZE: u32 = 160;
255
256impl Into<MemArg> for StackAMode {
257 fn into(self) -> MemArg {
258 match self {
259 StackAMode::IncomingArg(off, _) => MemArg::InitialSPOffset { off },
261 StackAMode::Slot(off) => MemArg::SlotOffset { off },
262 StackAMode::OutgoingArg(off) => MemArg::NominalSPOffset { off },
263 }
264 }
265}
266
267pub struct S390xMachineDeps;
270
271impl IsaFlags for s390x_settings::Flags {}
272
273impl ABIMachineSpec for S390xMachineDeps {
274 type I = Inst;
275
276 type F = s390x_settings::Flags;
277
278 const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
282
283 fn word_bits() -> u32 {
284 64
285 }
286
287 fn stack_align(_call_conv: isa::CallConv) -> u32 {
289 8
290 }
291
292 fn compute_arg_locs(
293 call_conv: isa::CallConv,
294 flags: &settings::Flags,
295 params: &[ir::AbiParam],
296 args_or_rets: ArgsOrRets,
297 add_ret_area_ptr: bool,
298 mut args: ArgsAccumulator,
299 ) -> CodegenResult<(u32, Option<usize>)> {
300 assert_ne!(
301 call_conv,
302 isa::CallConv::Winch,
303 "s390x does not support the 'winch' calling convention yet"
304 );
305
306 let mut next_gpr = 0;
307 let mut next_fpr = 0;
308 let mut next_vr = 0;
309 let mut next_stack: u32 = 0;
310
311 if call_conv != isa::CallConv::Tail && args_or_rets == ArgsOrRets::Args {
316 next_stack = REG_SAVE_AREA_SIZE;
317 }
318
319 let ret_area_ptr = if add_ret_area_ptr {
320 debug_assert_eq!(args_or_rets, ArgsOrRets::Args);
321 next_gpr += 1;
322 Some(ABIArg::reg(
323 get_intreg_for_arg(call_conv, 0)
324 .unwrap()
325 .to_real_reg()
326 .unwrap(),
327 types::I64,
328 ir::ArgumentExtension::None,
329 ir::ArgumentPurpose::Normal,
330 ))
331 } else {
332 None
333 };
334
335 for mut param in params.into_iter().copied() {
336 if let ir::ArgumentPurpose::StructArgument(_) = param.purpose {
337 panic!(
338 "StructArgument parameters are not supported on s390x. \
339 Use regular pointer arguments instead."
340 );
341 }
342
343 let intreg = in_int_reg(param.value_type);
344 let fltreg = in_flt_reg(param.value_type);
345 let vecreg = in_vec_reg(param.value_type);
346 debug_assert!(intreg as i32 + fltreg as i32 + vecreg as i32 <= 1);
347
348 let (next_reg, candidate, implicit_ref) = if intreg {
349 let candidate = match args_or_rets {
350 ArgsOrRets::Args => get_intreg_for_arg(call_conv, next_gpr),
351 ArgsOrRets::Rets => get_intreg_for_ret(call_conv, next_gpr),
352 };
353 (&mut next_gpr, candidate, None)
354 } else if fltreg {
355 let candidate = match args_or_rets {
356 ArgsOrRets::Args => get_fltreg_for_arg(next_fpr),
357 ArgsOrRets::Rets => get_fltreg_for_ret(next_fpr),
358 };
359 (&mut next_fpr, candidate, None)
360 } else if vecreg {
361 let candidate = match args_or_rets {
362 ArgsOrRets::Args => get_vecreg_for_arg(next_vr),
363 ArgsOrRets::Rets => get_vecreg_for_ret(next_vr),
364 };
365 (&mut next_vr, candidate, None)
366 } else {
367 if args_or_rets == ArgsOrRets::Rets {
369 (&mut next_gpr, None, None)
371 } else {
372 let implicit_ref = Some(param.value_type);
374 param = ir::AbiParam::new(types::I64);
375 let candidate = get_intreg_for_arg(call_conv, next_gpr);
376 (&mut next_gpr, candidate, implicit_ref)
377 }
378 };
379
380 let slot = if let Some(reg) = candidate {
381 *next_reg += 1;
382 ABIArgSlot::Reg {
383 reg: reg.to_real_reg().unwrap(),
384 ty: param.value_type,
385 extension: param.extension,
386 }
387 } else {
388 if args_or_rets == ArgsOrRets::Rets && !flags.enable_multi_ret_implicit_sret() {
389 return Err(crate::CodegenError::Unsupported(
390 "Too many return values to fit in registers. \
391 Use a StructReturn argument instead. (#9510)"
392 .to_owned(),
393 ));
394 }
395
396 let size = (ty_bits(param.value_type) / 8) as u32;
399 let slot_size = std::cmp::max(size, 8);
400
401 debug_assert!(slot_size.is_power_of_two());
403 let slot_align = std::cmp::min(slot_size, 8);
404 next_stack = align_to(next_stack, slot_align);
405
406 let offset = if size < slot_size && param.extension == ir::ArgumentExtension::None {
409 slot_size - size
410 } else {
411 0
412 };
413 let offset = (next_stack + offset) as i64;
414 next_stack += slot_size;
415 ABIArgSlot::Stack {
416 offset,
417 ty: param.value_type,
418 extension: param.extension,
419 }
420 };
421
422 if let Some(ty) = implicit_ref {
423 assert!(
424 (ty_bits(ty) / 8) % 8 == 0,
425 "implicit argument size is not properly aligned"
426 );
427 args.push(ABIArg::ImplicitPtrArg {
428 pointer: slot,
429 offset: 0, ty,
431 purpose: param.purpose,
432 });
433 } else {
434 args.push(ABIArg::Slots {
435 slots: smallvec![slot],
436 purpose: param.purpose,
437 });
438 }
439 }
440
441 next_stack = align_to(next_stack, 8);
442
443 let extra_arg = if let Some(ret_area_ptr) = ret_area_ptr {
444 args.push_non_formal(ret_area_ptr);
445 Some(args.args().len() - 1)
446 } else {
447 None
448 };
449
450 for arg in args.args_mut() {
453 match arg {
454 ABIArg::StructArg { .. } => unreachable!(),
455 ABIArg::ImplicitPtrArg { offset, ty, .. } => {
456 *offset = next_stack as i64;
457 next_stack += (ty_bits(*ty) / 8) as u32;
458 }
459 _ => {}
460 }
461 }
462
463 if call_conv == isa::CallConv::Tail && args_or_rets == ArgsOrRets::Args && next_stack != 0 {
467 for arg in args.args_mut() {
468 match arg {
469 ABIArg::Slots { slots, .. } => {
470 for slot in slots {
471 match slot {
472 ABIArgSlot::Reg { .. } => {}
473 ABIArgSlot::Stack { offset, .. } => {
474 *offset -= next_stack as i64;
475 }
476 }
477 }
478 }
479 ABIArg::StructArg { .. } => unreachable!(),
480 ABIArg::ImplicitPtrArg { offset, .. } => {
481 *offset -= next_stack as i64;
482 }
483 }
484 }
485 next_stack += REG_SAVE_AREA_SIZE;
489 }
490
491 Ok((next_stack, extra_arg))
492 }
493
494 fn gen_load_stack(mem: StackAMode, into_reg: Writable<Reg>, ty: Type) -> Inst {
495 Inst::gen_load(into_reg, mem.into(), ty)
496 }
497
498 fn gen_store_stack(mem: StackAMode, from_reg: Reg, ty: Type) -> Inst {
499 Inst::gen_store(mem.into(), from_reg, ty)
500 }
501
502 fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Inst {
503 Inst::gen_move(to_reg, from_reg, ty)
504 }
505
506 fn gen_extend(
507 to_reg: Writable<Reg>,
508 from_reg: Reg,
509 signed: bool,
510 from_bits: u8,
511 to_bits: u8,
512 ) -> Inst {
513 assert!(from_bits < to_bits);
514 Inst::Extend {
515 rd: to_reg,
516 rn: from_reg,
517 signed,
518 from_bits,
519 to_bits,
520 }
521 }
522
523 fn gen_args(args: Vec<ArgPair>) -> Inst {
524 Inst::Args { args }
525 }
526
527 fn gen_rets(rets: Vec<RetPair>) -> Inst {
528 Inst::Rets { rets }
529 }
530
531 fn gen_add_imm(
532 _call_conv: isa::CallConv,
533 into_reg: Writable<Reg>,
534 from_reg: Reg,
535 imm: u32,
536 ) -> SmallInstVec<Inst> {
537 let mut insts = SmallVec::new();
538 if let Some(imm) = UImm12::maybe_from_u64(imm as u64) {
539 insts.push(Inst::LoadAddr {
540 rd: into_reg,
541 mem: MemArg::BXD12 {
542 base: from_reg,
543 index: zero_reg(),
544 disp: imm,
545 flags: MemFlags::trusted(),
546 },
547 });
548 } else if let Some(imm) = SImm20::maybe_from_i64(imm as i64) {
549 insts.push(Inst::LoadAddr {
550 rd: into_reg,
551 mem: MemArg::BXD20 {
552 base: from_reg,
553 index: zero_reg(),
554 disp: imm,
555 flags: MemFlags::trusted(),
556 },
557 });
558 } else {
559 if from_reg != into_reg.to_reg() {
560 insts.push(Inst::mov64(into_reg, from_reg));
561 }
562 insts.push(Inst::AluRUImm32 {
563 alu_op: ALUOp::AddLogical64,
564 rd: into_reg,
565 ri: into_reg.to_reg(),
566 imm,
567 });
568 }
569 insts
570 }
571
572 fn gen_stack_lower_bound_trap(limit_reg: Reg) -> SmallInstVec<Inst> {
573 let mut insts = SmallVec::new();
574 insts.push(Inst::CmpTrapRR {
575 op: CmpOp::CmpL64,
576 rn: stack_reg(),
577 rm: limit_reg,
578 cond: Cond::from_intcc(IntCC::UnsignedLessThanOrEqual),
579 trap_code: ir::TrapCode::STACK_OVERFLOW,
580 });
581 insts
582 }
583
584 fn gen_get_stack_addr(mem: StackAMode, into_reg: Writable<Reg>) -> Inst {
585 let mem = mem.into();
586 Inst::LoadAddr { rd: into_reg, mem }
587 }
588
589 fn get_stacklimit_reg(_call_conv: isa::CallConv) -> Reg {
590 spilltmp_reg()
591 }
592
593 fn gen_load_base_offset(into_reg: Writable<Reg>, base: Reg, offset: i32, ty: Type) -> Inst {
594 let mem = MemArg::reg_plus_off(base, offset.into(), MemFlags::trusted());
595 Inst::gen_load(into_reg, mem, ty)
596 }
597
598 fn gen_store_base_offset(base: Reg, offset: i32, from_reg: Reg, ty: Type) -> Inst {
599 let mem = MemArg::reg_plus_off(base, offset.into(), MemFlags::trusted());
600 Inst::gen_store(mem, from_reg, ty)
601 }
602
603 fn gen_sp_reg_adjust(imm: i32) -> SmallInstVec<Inst> {
604 if imm == 0 {
605 return SmallVec::new();
606 }
607
608 let mut insts = SmallVec::new();
609 if let Ok(imm) = i16::try_from(imm) {
610 insts.push(Inst::AluRSImm16 {
611 alu_op: ALUOp::Add64,
612 rd: writable_stack_reg(),
613 ri: stack_reg(),
614 imm,
615 });
616 } else {
617 insts.push(Inst::AluRSImm32 {
618 alu_op: ALUOp::Add64,
619 rd: writable_stack_reg(),
620 ri: stack_reg(),
621 imm,
622 });
623 }
624 insts
625 }
626
627 fn gen_prologue_frame_setup(
628 _call_conv: isa::CallConv,
629 _flags: &settings::Flags,
630 _isa_flags: &s390x_settings::Flags,
631 _frame_layout: &FrameLayout,
632 ) -> SmallInstVec<Inst> {
633 SmallVec::new()
634 }
635
636 fn gen_epilogue_frame_restore(
637 _call_conv: isa::CallConv,
638 _flags: &settings::Flags,
639 _isa_flags: &s390x_settings::Flags,
640 _frame_layout: &FrameLayout,
641 ) -> SmallInstVec<Inst> {
642 SmallVec::new()
643 }
644
645 fn gen_return(
646 _call_conv: isa::CallConv,
647 _isa_flags: &s390x_settings::Flags,
648 _frame_layout: &FrameLayout,
649 ) -> SmallInstVec<Inst> {
650 smallvec![Inst::Ret { link: gpr(14) }]
651 }
652
653 fn gen_probestack(_insts: &mut SmallInstVec<Self::I>, _: u32) {
654 unimplemented!("Stack probing is unimplemented on S390x");
657 }
658
659 fn gen_inline_probestack(
660 insts: &mut SmallInstVec<Self::I>,
661 _call_conv: isa::CallConv,
662 frame_size: u32,
663 guard_size: u32,
664 ) {
665 const PROBE_MAX_UNROLL: u32 = 2;
668
669 let probe_count = frame_size / guard_size;
672 if probe_count == 0 {
673 } else if probe_count <= PROBE_MAX_UNROLL {
675 for _ in 0..probe_count {
677 insts.extend(Self::gen_sp_reg_adjust(-(guard_size as i32)));
678
679 insts.push(Inst::StoreImm8 {
680 imm: 0,
681 mem: MemArg::reg(stack_reg(), MemFlags::trusted()),
682 });
683 }
684 } else {
685 let probe_count_reg = writable_spilltmp_reg();
691 if let Ok(probe_count) = i16::try_from(probe_count) {
692 insts.push(Inst::Mov32SImm16 {
693 rd: probe_count_reg,
694 imm: probe_count,
695 });
696 } else {
697 insts.push(Inst::Mov32Imm {
698 rd: probe_count_reg,
699 imm: probe_count,
700 });
701 }
702
703 insts.push(Inst::StackProbeLoop {
705 probe_count: probe_count_reg,
706 guard_size: i16::try_from(guard_size).unwrap(),
707 });
708 }
709
710 insts.extend(Self::gen_sp_reg_adjust((probe_count * guard_size) as i32));
712 }
713
714 fn gen_clobber_save(
715 call_conv: isa::CallConv,
716 flags: &settings::Flags,
717 frame_layout: &FrameLayout,
718 ) -> SmallVec<[Inst; 16]> {
719 let mut insts = SmallVec::new();
720
721 let incoming_tail_args_size = if call_conv == isa::CallConv::Tail {
724 frame_layout.incoming_args_size
725 } else {
726 0
727 };
728
729 if flags.unwind_info() {
731 insts.push(Inst::Unwind {
732 inst: UnwindInst::DefineNewFrame {
733 offset_upward_to_caller_sp: REG_SAVE_AREA_SIZE + incoming_tail_args_size,
734 offset_downward_to_clobbers: frame_layout.clobber_size
735 - incoming_tail_args_size,
736 },
737 });
738 }
739
740 if let Some((first_clobbered_gpr, _)) = get_clobbered_gprs(frame_layout) {
743 let mut last_clobbered_gpr = 15;
744 let offset = 8 * first_clobbered_gpr as i64 + incoming_tail_args_size as i64;
745 insts.push(Inst::StoreMultiple64 {
746 rt: gpr(first_clobbered_gpr),
747 rt2: gpr(last_clobbered_gpr),
748 mem: MemArg::reg_plus_off(stack_reg(), offset, MemFlags::trusted()),
749 });
750 if flags.unwind_info() {
751 if incoming_tail_args_size != 0 {
757 insts.push(Inst::Unwind {
758 inst: UnwindInst::RegStackOffset {
759 clobber_offset: frame_layout.clobber_size,
760 reg: gpr(last_clobbered_gpr).to_real_reg().unwrap(),
761 },
762 });
763 last_clobbered_gpr = last_clobbered_gpr - 1;
764 }
765 for i in first_clobbered_gpr..(last_clobbered_gpr + 1) {
766 insts.push(Inst::Unwind {
767 inst: UnwindInst::SaveReg {
768 clobber_offset: frame_layout.clobber_size + (i * 8) as u32,
769 reg: gpr(i).to_real_reg().unwrap(),
770 },
771 });
772 }
773 }
774 }
775
776 if flags.preserve_frame_pointers() {
778 if incoming_tail_args_size == 0 {
779 insts.push(Inst::mov64(writable_gpr(1), stack_reg()));
780 } else {
781 insts.extend(Self::gen_add_imm(
782 call_conv,
783 writable_gpr(1),
784 stack_reg(),
785 incoming_tail_args_size,
786 ));
787 }
788 }
789
790 let stack_size = frame_layout.outgoing_args_size as i32
792 + frame_layout.clobber_size as i32
793 + frame_layout.fixed_frame_storage_size as i32
794 - incoming_tail_args_size as i32;
795 insts.extend(Self::gen_sp_reg_adjust(-stack_size));
796 if flags.unwind_info() {
797 insts.push(Inst::Unwind {
798 inst: UnwindInst::StackAlloc {
799 size: stack_size as u32,
800 },
801 });
802 }
803
804 if flags.preserve_frame_pointers() {
806 insts.push(Inst::Store64 {
807 rd: gpr(1),
808 mem: MemArg::reg_plus_off(stack_reg(), 0, MemFlags::trusted()),
809 });
810 }
811
812 for (i, reg) in get_clobbered_fprs(frame_layout).iter().enumerate() {
814 insts.push(Inst::VecStoreLane {
815 size: 64,
816 rd: reg.to_reg().into(),
817 mem: MemArg::reg_plus_off(
818 stack_reg(),
819 (i * 8) as i64
820 + frame_layout.outgoing_args_size as i64
821 + frame_layout.fixed_frame_storage_size as i64,
822 MemFlags::trusted(),
823 ),
824 lane_imm: 0,
825 });
826 if flags.unwind_info() {
827 insts.push(Inst::Unwind {
828 inst: UnwindInst::SaveReg {
829 clobber_offset: (i * 8) as u32,
830 reg: reg.to_reg(),
831 },
832 });
833 }
834 }
835
836 insts
837 }
838
839 fn gen_clobber_restore(
840 call_conv: isa::CallConv,
841 _flags: &settings::Flags,
842 frame_layout: &FrameLayout,
843 ) -> SmallVec<[Inst; 16]> {
844 let mut insts = SmallVec::new();
845
846 insts.extend(gen_restore_fprs(frame_layout));
848
849 insts.extend(gen_restore_gprs(call_conv, frame_layout, 0));
851
852 insts
853 }
854
855 fn gen_call(_dest: &CallDest, _tmp: Writable<Reg>, _info: CallInfo<()>) -> SmallVec<[Inst; 2]> {
856 unreachable!();
857 }
858
859 fn gen_memcpy<F: FnMut(Type) -> Writable<Reg>>(
860 _call_conv: isa::CallConv,
861 _dst: Reg,
862 _src: Reg,
863 _size: usize,
864 _alloc: F,
865 ) -> SmallVec<[Self::I; 8]> {
866 unimplemented!("StructArgs not implemented for S390X yet");
867 }
868
869 fn get_number_of_spillslots_for_value(
870 rc: RegClass,
871 _vector_scale: u32,
872 _isa_flags: &Self::F,
873 ) -> u32 {
874 match rc {
876 RegClass::Int => 1,
877 RegClass::Float => 2,
878 RegClass::Vector => unreachable!(),
879 }
880 }
881
882 fn get_machine_env(_flags: &settings::Flags, call_conv: isa::CallConv) -> &MachineEnv {
883 match call_conv {
884 isa::CallConv::Tail => {
885 static TAIL_MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
886 TAIL_MACHINE_ENV.get_or_init(tail_create_machine_env)
887 }
888 _ => {
889 static SYSV_MACHINE_ENV: OnceLock<MachineEnv> = OnceLock::new();
890 SYSV_MACHINE_ENV.get_or_init(sysv_create_machine_env)
891 }
892 }
893 }
894
895 fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet {
896 match call_conv_of_callee {
897 isa::CallConv::Tail => TAIL_CLOBBERS,
898 _ => SYSV_CLOBBERS,
899 }
900 }
901
902 fn get_ext_mode(
903 _call_conv: isa::CallConv,
904 specified: ir::ArgumentExtension,
905 ) -> ir::ArgumentExtension {
906 specified
907 }
908
909 fn compute_frame_layout(
910 call_conv: isa::CallConv,
911 flags: &settings::Flags,
912 _sig: &Signature,
913 regs: &[Writable<RealReg>],
914 _is_leaf: bool,
915 incoming_args_size: u32,
916 tail_args_size: u32,
917 fixed_frame_storage_size: u32,
918 mut outgoing_args_size: u32,
919 ) -> FrameLayout {
920 assert!(
921 !flags.enable_pinned_reg(),
922 "Pinned register not supported on s390x"
923 );
924
925 let mut regs: Vec<Writable<RealReg>> = regs
926 .iter()
927 .cloned()
928 .filter(|r| is_reg_saved_in_prologue(call_conv, r.to_reg()))
929 .collect();
930
931 if flags.preserve_frame_pointers() {
937 if outgoing_args_size < REG_SAVE_AREA_SIZE {
938 outgoing_args_size = REG_SAVE_AREA_SIZE;
939 }
940 }
941
942 if outgoing_args_size > 0 {
948 let link_reg = Writable::from_reg(RealReg::from(gpr_preg(14)));
949 if !regs.contains(&link_reg) {
950 regs.push(link_reg);
951 }
952 }
953
954 regs.sort_unstable();
957
958 let mut clobber_size = 0;
960 for reg in ®s {
961 match reg.to_reg().class() {
962 RegClass::Int => {}
963 RegClass::Float => {
964 clobber_size += 8;
965 }
966 RegClass::Vector => unreachable!(),
967 }
968 }
969
970 if call_conv == isa::CallConv::Tail {
976 clobber_size += tail_args_size;
977 }
978
979 FrameLayout {
981 incoming_args_size,
982 tail_args_size: incoming_args_size,
985 setup_area_size: 0,
986 clobber_size,
987 fixed_frame_storage_size,
988 outgoing_args_size,
989 clobbered_callee_saves: regs,
990 }
991 }
992}
993
994impl S390xMachineDeps {
995 pub fn gen_tail_epilogue(
996 frame_layout: &FrameLayout,
997 callee_pop_size: u32,
998 target_reg: Option<&mut Reg>,
999 ) -> SmallVec<[Inst; 16]> {
1000 let mut insts = SmallVec::new();
1001 let call_conv = isa::CallConv::Tail;
1002
1003 insts.extend(gen_restore_fprs(frame_layout));
1005
1006 if let Some(reg) = target_reg {
1010 if is_reg_saved_in_prologue(call_conv, reg.to_real_reg().unwrap()) {
1011 insts.push(Inst::Mov64 {
1012 rd: writable_gpr(1),
1013 rm: *reg,
1014 });
1015 *reg = gpr(1);
1016 }
1017 }
1018
1019 insts.extend(gen_restore_gprs(call_conv, frame_layout, callee_pop_size));
1021
1022 insts
1023 }
1024}
1025
1026fn is_reg_saved_in_prologue(call_conv: isa::CallConv, r: RealReg) -> bool {
1027 match (call_conv, r.class()) {
1028 (isa::CallConv::Tail, RegClass::Int) => {
1029 r.hw_enc() >= 8 && r.hw_enc() <= 15
1031 }
1032 (_, RegClass::Int) => {
1033 r.hw_enc() >= 6 && r.hw_enc() <= 15
1035 }
1036 (_, RegClass::Float) => {
1037 r.hw_enc() >= 8 && r.hw_enc() <= 15
1039 }
1040 (_, RegClass::Vector) => unreachable!(),
1041 }
1042}
1043
1044fn get_clobbered_gprs(frame_layout: &FrameLayout) -> Option<(u8, u8)> {
1045 let (clobbered_gpr, _) = frame_layout.clobbered_callee_saves_by_class();
1050 if clobbered_gpr.is_empty() {
1051 return None;
1052 }
1053
1054 let first = clobbered_gpr.first().unwrap().to_reg().hw_enc();
1055 let last = clobbered_gpr.last().unwrap().to_reg().hw_enc();
1056 debug_assert!(clobbered_gpr.iter().all(|r| r.to_reg().hw_enc() >= first));
1057 debug_assert!(clobbered_gpr.iter().all(|r| r.to_reg().hw_enc() <= last));
1058 Some((first, last))
1059}
1060
1061fn get_clobbered_fprs(frame_layout: &FrameLayout) -> &[Writable<RealReg>] {
1062 let (_, clobbered_fpr) = frame_layout.clobbered_callee_saves_by_class();
1064 clobbered_fpr
1065}
1066
1067fn gen_restore_gprs(
1070 call_conv: isa::CallConv,
1071 frame_layout: &FrameLayout,
1072 callee_pop_size: u32,
1073) -> SmallVec<[Inst; 16]> {
1074 let mut insts = SmallVec::new();
1075
1076 let clobbered_gpr = get_clobbered_gprs(frame_layout);
1078
1079 let stack_size = frame_layout.outgoing_args_size as i32
1083 + frame_layout.clobber_size as i32
1084 + frame_layout.fixed_frame_storage_size as i32;
1085 let implicit_sp_restore = callee_pop_size == 0
1086 && (call_conv != isa::CallConv::Tail || frame_layout.incoming_args_size == 0)
1087 && clobbered_gpr.map_or(false, |(first, _)| {
1088 SImm20::maybe_from_i64(8 * first as i64 + stack_size as i64).is_some()
1089 });
1090 if !implicit_sp_restore {
1091 insts.extend(S390xMachineDeps::gen_sp_reg_adjust(
1092 stack_size - callee_pop_size as i32,
1093 ));
1094 }
1095
1096 if let Some((first, mut last)) = clobbered_gpr {
1098 let mut reg = stack_reg();
1100 let mut offset = callee_pop_size as i64 + 8 * first as i64;
1101 if implicit_sp_restore {
1102 offset += stack_size as i64 - callee_pop_size as i64;
1103 last = 15;
1104 }
1105 if SImm20::maybe_from_i64(offset).is_none() {
1108 insts.extend(S390xMachineDeps::gen_add_imm(
1109 call_conv,
1110 writable_gpr(first),
1111 stack_reg(),
1112 offset as u32,
1113 ));
1114 reg = gpr(first);
1115 offset = 0;
1116 }
1117 insts.push(Inst::LoadMultiple64 {
1119 rt: writable_gpr(first),
1120 rt2: writable_gpr(last),
1121 mem: MemArg::reg_plus_off(reg, offset, MemFlags::trusted()),
1122 });
1123 }
1124
1125 insts
1126}
1127
1128fn gen_restore_fprs(frame_layout: &FrameLayout) -> SmallVec<[Inst; 16]> {
1130 let mut insts = SmallVec::new();
1131
1132 let clobbered_fpr = get_clobbered_fprs(frame_layout);
1134
1135 for (i, reg) in clobbered_fpr.iter().enumerate() {
1137 insts.push(Inst::VecLoadLaneUndef {
1138 size: 64,
1139 rd: Writable::from_reg(reg.to_reg().into()),
1140 mem: MemArg::reg_plus_off(
1141 stack_reg(),
1142 (i * 8) as i64
1143 + frame_layout.outgoing_args_size as i64
1144 + frame_layout.fixed_frame_storage_size as i64,
1145 MemFlags::trusted(),
1146 ),
1147 lane_imm: 0,
1148 });
1149 }
1150
1151 insts
1152}
1153
1154const fn sysv_clobbers() -> PRegSet {
1155 PRegSet::empty()
1156 .with(gpr_preg(0))
1157 .with(gpr_preg(1))
1158 .with(gpr_preg(2))
1159 .with(gpr_preg(3))
1160 .with(gpr_preg(4))
1161 .with(gpr_preg(5))
1162 .with(vr_preg(0))
1178 .with(vr_preg(1))
1179 .with(vr_preg(2))
1180 .with(vr_preg(3))
1181 .with(vr_preg(4))
1182 .with(vr_preg(5))
1183 .with(vr_preg(6))
1184 .with(vr_preg(7))
1185 .with(vr_preg(8))
1186 .with(vr_preg(9))
1187 .with(vr_preg(10))
1188 .with(vr_preg(11))
1189 .with(vr_preg(12))
1190 .with(vr_preg(13))
1191 .with(vr_preg(14))
1192 .with(vr_preg(15))
1193 .with(vr_preg(16))
1194 .with(vr_preg(17))
1195 .with(vr_preg(18))
1196 .with(vr_preg(19))
1197 .with(vr_preg(20))
1198 .with(vr_preg(21))
1199 .with(vr_preg(22))
1200 .with(vr_preg(23))
1201 .with(vr_preg(24))
1202 .with(vr_preg(25))
1203 .with(vr_preg(26))
1204 .with(vr_preg(27))
1205 .with(vr_preg(28))
1206 .with(vr_preg(29))
1207 .with(vr_preg(30))
1208 .with(vr_preg(31))
1209}
1210const SYSV_CLOBBERS: PRegSet = sysv_clobbers();
1211
1212const fn tail_clobbers() -> PRegSet {
1213 PRegSet::empty()
1215 .with(gpr_preg(0))
1216 .with(gpr_preg(1))
1217 .with(gpr_preg(2))
1218 .with(gpr_preg(3))
1219 .with(gpr_preg(4))
1220 .with(gpr_preg(5))
1221 .with(gpr_preg(6))
1222 .with(gpr_preg(7))
1223 .with(vr_preg(0))
1224 .with(vr_preg(1))
1225 .with(vr_preg(2))
1226 .with(vr_preg(3))
1227 .with(vr_preg(4))
1228 .with(vr_preg(5))
1229 .with(vr_preg(6))
1230 .with(vr_preg(7))
1231 .with(vr_preg(8))
1232 .with(vr_preg(9))
1233 .with(vr_preg(10))
1234 .with(vr_preg(11))
1235 .with(vr_preg(12))
1236 .with(vr_preg(13))
1237 .with(vr_preg(14))
1238 .with(vr_preg(15))
1239 .with(vr_preg(16))
1240 .with(vr_preg(17))
1241 .with(vr_preg(18))
1242 .with(vr_preg(19))
1243 .with(vr_preg(20))
1244 .with(vr_preg(21))
1245 .with(vr_preg(22))
1246 .with(vr_preg(23))
1247 .with(vr_preg(24))
1248 .with(vr_preg(25))
1249 .with(vr_preg(26))
1250 .with(vr_preg(27))
1251 .with(vr_preg(28))
1252 .with(vr_preg(29))
1253 .with(vr_preg(30))
1254 .with(vr_preg(31))
1255}
1256const TAIL_CLOBBERS: PRegSet = tail_clobbers();
1257
1258fn sysv_create_machine_env() -> MachineEnv {
1259 MachineEnv {
1260 preferred_regs_by_class: [
1261 vec![
1262 gpr_preg(2),
1265 gpr_preg(3),
1266 gpr_preg(4),
1267 gpr_preg(5),
1268 ],
1269 vec![
1270 vr_preg(0),
1271 vr_preg(1),
1272 vr_preg(2),
1273 vr_preg(3),
1274 vr_preg(4),
1275 vr_preg(5),
1276 vr_preg(6),
1277 vr_preg(7),
1278 vr_preg(16),
1279 vr_preg(17),
1280 vr_preg(18),
1281 vr_preg(19),
1282 vr_preg(20),
1283 vr_preg(21),
1284 vr_preg(22),
1285 vr_preg(23),
1286 vr_preg(24),
1287 vr_preg(25),
1288 vr_preg(26),
1289 vr_preg(27),
1290 vr_preg(28),
1291 vr_preg(29),
1292 vr_preg(30),
1293 vr_preg(31),
1294 ],
1295 vec![],
1297 ],
1298 non_preferred_regs_by_class: [
1299 vec![
1300 gpr_preg(6),
1301 gpr_preg(7),
1302 gpr_preg(8),
1303 gpr_preg(9),
1304 gpr_preg(10),
1305 gpr_preg(11),
1306 gpr_preg(12),
1307 gpr_preg(13),
1308 gpr_preg(14),
1309 ],
1311 vec![
1312 vr_preg(8),
1313 vr_preg(9),
1314 vr_preg(10),
1315 vr_preg(11),
1316 vr_preg(12),
1317 vr_preg(13),
1318 vr_preg(14),
1319 vr_preg(15),
1320 ],
1321 vec![],
1323 ],
1324 fixed_stack_slots: vec![],
1325 scratch_by_class: [None, None, None],
1326 }
1327}
1328
1329fn tail_create_machine_env() -> MachineEnv {
1330 MachineEnv {
1332 preferred_regs_by_class: [
1333 vec![
1334 gpr_preg(2),
1337 gpr_preg(3),
1338 gpr_preg(4),
1339 gpr_preg(5),
1340 gpr_preg(6),
1341 gpr_preg(7),
1342 ],
1343 vec![
1344 vr_preg(0),
1345 vr_preg(1),
1346 vr_preg(2),
1347 vr_preg(3),
1348 vr_preg(4),
1349 vr_preg(5),
1350 vr_preg(6),
1351 vr_preg(7),
1352 vr_preg(16),
1353 vr_preg(17),
1354 vr_preg(18),
1355 vr_preg(19),
1356 vr_preg(20),
1357 vr_preg(21),
1358 vr_preg(22),
1359 vr_preg(23),
1360 vr_preg(24),
1361 vr_preg(25),
1362 vr_preg(26),
1363 vr_preg(27),
1364 vr_preg(28),
1365 vr_preg(29),
1366 vr_preg(30),
1367 vr_preg(31),
1368 ],
1369 vec![],
1371 ],
1372 non_preferred_regs_by_class: [
1373 vec![
1374 gpr_preg(8),
1375 gpr_preg(9),
1376 gpr_preg(10),
1377 gpr_preg(11),
1378 gpr_preg(12),
1379 gpr_preg(13),
1380 gpr_preg(14),
1381 ],
1383 vec![
1384 vr_preg(8),
1385 vr_preg(9),
1386 vr_preg(10),
1387 vr_preg(11),
1388 vr_preg(12),
1389 vr_preg(13),
1390 vr_preg(14),
1391 vr_preg(15),
1392 ],
1393 vec![],
1395 ],
1396 fixed_stack_slots: vec![],
1397 scratch_by_class: [None, None, None],
1398 }
1399}