cranelift_codegen/isa/s390x/
abi.rs

1//! Implementation of a standard S390x ABI.
2//!
3//! This machine uses the "vanilla" ABI implementation from abi.rs,
4//! however a few details are different from the description there:
5//!
6//! - On s390x, the caller must provide a "register save area" of 160
7//!   bytes to any function it calls.  The called function is free to use
8//!   this space for any purpose; usually to save callee-saved GPRs.
9//!   (Note that while this area is allocated by the caller, it is counted
10//!   as part of the callee's stack frame; in particular, the callee's CFA
11//!   is the top of the register save area, not the incoming SP value.)
12//!
13//! - Overflow arguments are passed on the stack starting immediately
14//!   above the register save area.  On s390x, this space is allocated
15//!   only once directly in the prologue, using a size large enough to
16//!   hold overflow arguments for every call in the function.
17//!
18//! - On s390x we do not use a frame pointer register; instead, every
19//!   element of the stack frame is addressed via (constant) offsets
20//!   from the stack pointer.  Note that due to the above (and because
21//!   there are no variable-sized stack allocations in cranelift), the
22//!   value of the stack pointer register never changes after the
23//!   initial allocation in the function prologue.
24//!
25//! - If we are asked to "preserve frame pointers" to enable stack
26//!   unwinding, we use the stack backchain feature instead, which
27//!   is documented by the s390x ELF ABI, but marked as optional.
28//!   This ensures that at all times during execution of a function,
29//!   the lowest word on the stack (part of the register save area)
30//!   holds a copy of the stack pointer at function entry.
31//!
32//! Overall, the stack frame layout on s390x is as follows:
33//!
34//! ```plain
35//!   (high address)
36//!
37//!                              +---------------------------+
38//!                              |          ...              |
39//! CFA                  ----->  | stack args                |
40//!                              +---------------------------+
41//!                              |          ...              |
42//!                              | 160 bytes reg save area   |
43//!                              | (used to save GPRs)       |
44//! SP at function entry ----->  | (incl. caller's backchain)|
45//!                              +---------------------------+
46//!                              |          ...              |
47//!                              | clobbered callee-saves    |
48//!                              | (used to save FPRs)       |
49//! unwind-frame base     ---->  | (alloc'd by prologue)     |
50//!                              +---------------------------+
51//!                              |          ...              |
52//!                              | spill slots               |
53//!                              | (accessed via SP)         |
54//!                              |          ...              |
55//!                              | stack slots               |
56//!                              | (accessed via SP)         |
57//!                              | (alloc'd by prologue)     |
58//!                              +---------------------------+
59//!                              |          ...              |
60//!                              | args for call             |
61//!                              | outgoing reg save area    |
62//!                              | (alloc'd by prologue)     |
63//! SP during function  ------>  | (incl. callee's backchain)|
64//!                              +---------------------------+
65//!
66//!   (low address)
67//! ```
68//!
69//!
70//! The tail-call ABI has the following changes to the system ABI:
71//!
72//! - %r6 and %r7 are both non-callee-saved argument registers.
73//!
74//! - The argument save area for outgoing (non-tail) calls to
75//!   a tail-call ABI function is placed *below* the caller's
76//!   stack frame.  This means the caller temporarily allocates
77//!   a part of the callee's frame, including temporary space
78//!   for a register save area holding a copy of the backchain.
79//!
80//! - For tail calls, the caller puts outgoing arguments at the
81//!   very top of its stack frame, overlapping the incoming
82//!   argument area.  This is extended by the prolog if needed.
83//!
84//! Overall, the tail-call stack frame layout on s390x is as follows:
85//!
86//! ```plain
87//!   (high address)
88//!
89//!                              +---------------------------+
90//!                              |          ...              |
91//! CFA                  ----->  | (caller's frame)          |
92//!                              +---------------------------+
93//!                              |          ...              |
94//!                              | 160 bytes reg save area   |
95//!                              | (used to save GPRs)       |
96//! SP at function return----->  | (incl. caller's backchain)|
97//!                              +---------------------------+
98//!                              |          ...              |
99//!                              | incoming stack args       |
100//! SP at function entry ----->  | (incl. backchain copy)    |
101//!                              +---------------------------+
102//!                              |          ...              |
103//!                              | outgoing tail call args   |
104//!                              | (overlaps incoming args)  |
105//!                              | (incl. backchain copy)    |
106//! SP at tail cail       ---->  | (alloc'd by prologue)     |
107//!                              +---------------------------+
108//!                              |          ...              |
109//!                              | clobbered callee-saves    |
110//!                              | (used to save FPRs)       |
111//! unwind-frame base     ---->  | (alloc'd by prologue)     |
112//!                              +---------------------------+
113//!                              |          ...              |
114//!                              | spill slots               |
115//!                              | (accessed via SP)         |
116//!                              |          ...              |
117//!                              | stack slots               |
118//!                              | (accessed via SP)         |
119//!                              | (alloc'd by prologue)     |
120//!                              +---------------------------+
121//!                              |          ...              |
122//!                              | outgoing calls return buf |
123//!                              | outgoing reg save area    |
124//!                              | (alloc'd by prologue)     |
125//! SP during function  ------>  | (incl. callee's backchain)|
126//!                              +---------------------------+
127//!                              |          ...              |
128//!                              | outgoing stack args       |
129//!                              | (alloc'd by call sequence)|
130//! SP at non-tail call  ----->  | (incl. backchain copy)    |
131//!                              +---------------------------+
132//!   (low address)
133//! ```
134
135use 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
153// We use a generic implementation that factors out ABI commonalities.
154
155/// Support for the S390x ABI from the callee side (within a function body).
156pub type S390xCallee = Callee<S390xMachineDeps>;
157
158/// ABI Register usage
159
160fn 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        // ABI extension to support multi-value returns:
218        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        // ABI extension to support multi-value returns:
231        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        // ABI extension to support multi-value returns:
242        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
253/// The size of the register save area
254pub static REG_SAVE_AREA_SIZE: u32 = 160;
255
256impl Into<MemArg> for StackAMode {
257    fn into(self) -> MemArg {
258        match self {
259            // Argument area always begins at the initial SP.
260            StackAMode::IncomingArg(off, _) => MemArg::InitialSPOffset { off },
261            StackAMode::Slot(off) => MemArg::SlotOffset { off },
262            StackAMode::OutgoingArg(off) => MemArg::NominalSPOffset { off },
263        }
264    }
265}
266
267/// S390x-specific ABI behavior. This struct just serves as an implementation
268/// point for the trait; it is never actually instantiated.
269pub 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    /// This is the limit for the size of argument and return-value areas on the
279    /// stack. We place a reasonable limit here to avoid integer overflow issues
280    /// with 32-bit arithmetic: for now, 128 MB.
281    const STACK_ARG_RET_SIZE_LIMIT: u32 = 128 * 1024 * 1024;
282
283    fn word_bits() -> u32 {
284        64
285    }
286
287    /// Return required stack alignment in bytes.
288    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        // The bottom of the stack frame holds the register save area.  To simplify
312        // offset computation, include this area as part of the argument area;
313        // however, this does not apply to the tail-call convention, which uses the
314        // callee frame instead to pass arguments.
315        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                // We must pass this by implicit reference.
368                if args_or_rets == ArgsOrRets::Rets {
369                    // For return values, just force them to memory.
370                    (&mut next_gpr, None, None)
371                } else {
372                    // For arguments, implicitly convert to pointer type.
373                    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                // Compute size. Every argument or return value takes a slot of
397                // at least 8 bytes.
398                let size = (ty_bits(param.value_type) / 8) as u32;
399                let slot_size = std::cmp::max(size, 8);
400
401                // Align the stack slot.
402                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                // If the type is actually of smaller size (and the argument
407                // was not extended), it is passed right-aligned.
408                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, // Will be filled in later
430                    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        // After all arguments are in their well-defined location,
451        // allocate buffers for all ImplicitPtrArg arguments.
452        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        // With the tail-call convention, arguments are passed in the *callee*'s
464        // frame instead of the caller's frame.  Update all offsets accordingly
465        // (note that resulting offsets will all be negative).
466        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            // If we have any stack arguments, also allow for a temporary copy
486            // of the register save area.  This is only used until the callee
487            // has finished setting up its own frame.
488            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        // TODO: implement if we ever require stack probes on an s390x host
655        // (unlikely unless Lucet is ported)
656        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        // The stack probe loop currently takes 4 instructions and each unrolled
666        // probe takes 2.  Set this to 2 to keep the max size to 4 instructions.
667        const PROBE_MAX_UNROLL: u32 = 2;
668
669        // Calculate how many probes we need to perform. Round down, as we only
670        // need to probe whole guard_size regions we'd otherwise skip over.
671        let probe_count = frame_size / guard_size;
672        if probe_count == 0 {
673            // No probe necessary
674        } else if probe_count <= PROBE_MAX_UNROLL {
675            // Unrolled probe loop.
676            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            // Explicit probe loop.
686
687            // Load the number of probes into a register used as loop counter.
688            // `gen_inline_probestack` is called after regalloc2, so we can
689            // use the nonallocatable spilltmp register for this purpose.
690            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            // Emit probe loop.  The guard size is assumed to fit in 16 bits.
704            insts.push(Inst::StackProbeLoop {
705                probe_count: probe_count_reg,
706                guard_size: i16::try_from(guard_size).unwrap(),
707            });
708        }
709
710        // Restore the stack pointer to its original position.
711        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        // With the tail call convention, the caller already allocated the
722        // part of our stack frame that contains incoming arguments.
723        let incoming_tail_args_size = if call_conv == isa::CallConv::Tail {
724            frame_layout.incoming_args_size
725        } else {
726            0
727        };
728
729        // Define unwind stack frame.
730        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        // Use STMG to save clobbered GPRs into save area.
741        // Note that we always save SP (%r15) here if anything is saved.
742        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                // Normally, we instruct the unwinder to restore the stack pointer
752                // from its slot in the save area.  However, if we have incoming
753                // tail-call arguments, the value saved in that slot is incorrect.
754                // In that case, we instead instruct the unwinder to compute the
755                // unwound SP relative to the current CFA, as CFA == SP + 160.
756                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        // Save current stack pointer value if we need to write the backchain.
777        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        // Decrement stack pointer.
791        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        // Write the stack backchain if requested, using the value saved above.
805        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        // Save FPRs.
813        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        // Restore FPRs.
847        insts.extend(gen_restore_fprs(frame_layout));
848
849        // Restore GPRs (including SP).
850        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        // We allocate in terms of 8-byte slots.
875        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 the front end asks to preserve frame pointers (which we do not
932        // really have in the s390x ABI), we use the stack backchain instead.
933        // For this to work in all cases, we must allocate a stack frame with
934        // at least the outgoing register save area even in leaf functions.
935        // Update our caller's outgoing_args_size to reflect this.
936        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        // We need to save/restore the link register in non-leaf functions.
943        // This is not included in the clobber list because we have excluded
944        // call instructions via the is_included_in_clobbers callback.
945        // We also want to enforce saving the link register in leaf functions
946        // for stack unwinding, if we're asked to preserve frame pointers.
947        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        // Sort registers for deterministic code output. We can do an unstable
955        // sort because the registers will be unique (there are no dups).
956        regs.sort_unstable();
957
958        // Compute clobber size.  We only need to count FPR save slots.
959        let mut clobber_size = 0;
960        for reg in &regs {
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        // Common code assumes that tail-call arguments are part of the caller's
971        // frame.  This is not correct for our tail-call convention.  To ensure
972        // common code still gets the total size of this stack frame correct,
973        // we add the (incoming and outgoing) taill-call argument size to the
974        // "clobber" size.
975        if call_conv == isa::CallConv::Tail {
976            clobber_size += tail_args_size;
977        }
978
979        // Return FrameLayout structure.
980        FrameLayout {
981            incoming_args_size,
982            // We already accounted for tail-call arguments above, so reset
983            // this value to its default.
984            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        // Restore FPRs.
1004        insts.extend(gen_restore_fprs(frame_layout));
1005
1006        // If the tail call target is in a callee-saved GPR, we need to move it
1007        // to %r1 (as the only available temp register) before restoring GPRs
1008        // (but after restoring FPRs, which might clobber %r1).
1009        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        // Restore GPRs (including SP).
1020        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            // r8 - r15 inclusive are callee-saves.
1030            r.hw_enc() >= 8 && r.hw_enc() <= 15
1031        }
1032        (_, RegClass::Int) => {
1033            // r6 - r15 inclusive are callee-saves.
1034            r.hw_enc() >= 6 && r.hw_enc() <= 15
1035        }
1036        (_, RegClass::Float) => {
1037            // f8 - f15 inclusive are callee-saves.
1038            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    // Collect clobbered GPRs.  Note we save/restore GPR always as
1046    // a block of registers using LOAD MULTIPLE / STORE MULTIPLE, starting
1047    // with the clobbered GPR with the lowest number up to the clobbered GPR
1048    // with the highest number.
1049    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    // Collect clobbered floating-point registers.
1063    let (_, clobbered_fpr) = frame_layout.clobbered_callee_saves_by_class();
1064    clobbered_fpr
1065}
1066
1067// Restore GPRs (including SP) from the register save area.
1068// This must not clobber any register, specifically including %r1.
1069fn 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    // Determine GPRs to be restored.
1077    let clobbered_gpr = get_clobbered_gprs(frame_layout);
1078
1079    // Increment stack pointer unless it will be restored implicitly.
1080    // Note that implicit stack pointer restoration cannot be done in the
1081    // presence of either incoming or outgoing tail call arguments.
1082    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    // Use LMG to restore clobbered GPRs from save area.
1097    if let Some((first, mut last)) = clobbered_gpr {
1098        // Attempt to restore via SP, taking implicit restoration into account.
1099        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 the offset still overflows, use the first restored GPR
1106        // as temporary holding the address, as we cannot use %r1.
1107        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        // Now this LMG will always have an in-range offset.
1118        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
1128// Restore FPRs from the clobber area.
1129fn gen_restore_fprs(frame_layout: &FrameLayout) -> SmallVec<[Inst; 16]> {
1130    let mut insts = SmallVec::new();
1131
1132    // Determine FPRs to be restored.
1133    let clobbered_fpr = get_clobbered_fprs(frame_layout);
1134
1135    // Restore FPRs.
1136    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        // v0 - v7 inclusive and v16 - v31 inclusive are
1163        // caller-saves. The upper 64 bits of v8 - v15 inclusive are
1164        // also caller-saves.  However, because we cannot currently
1165        // represent partial registers to regalloc2, we indicate here
1166        // that every vector register is caller-save. Because this
1167        // function is used at *callsites*, approximating in this
1168        // direction (save more than necessary) is conservative and
1169        // thus safe.
1170        //
1171        // Note that we exclude clobbers from a call instruction when
1172        // a call instruction's callee has the same ABI as the caller
1173        // (the current function body); this is safe (anything
1174        // clobbered by callee can be clobbered by caller as well) and
1175        // avoids unnecessary saves of v8-v15 in the prologue even
1176        // though we include them as defs here.
1177        .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    // Same as the SystemV ABI, except that %r6 and %r7 are clobbered.
1214    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                // no r0; can't use for addressing?
1263                // no r1; it is our spilltmp.
1264                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            // Vector Regclass is unused
1296            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                // no r15; it is the stack pointer.
1310            ],
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            // Vector Regclass is unused
1322            vec![],
1323        ],
1324        fixed_stack_slots: vec![],
1325        scratch_by_class: [None, None, None],
1326    }
1327}
1328
1329fn tail_create_machine_env() -> MachineEnv {
1330    // Same as the SystemV ABI, except that %r6 and %r7 are preferred.
1331    MachineEnv {
1332        preferred_regs_by_class: [
1333            vec![
1334                // no r0; can't use for addressing?
1335                // no r1; it is our spilltmp.
1336                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            // Vector Regclass is unused
1370            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                // no r15; it is the stack pointer.
1382            ],
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            // Vector Regclass is unused
1394            vec![],
1395        ],
1396        fixed_stack_slots: vec![],
1397        scratch_by_class: [None, None, None],
1398    }
1399}