blob: 3ebba43bda788b99d00701255e039fe871ce2239 [file] [log] [blame]
//! S390x ISA definitions: registers.
use crate::settings;
use regalloc::{RealRegUniverse, Reg, RegClass, RegClassInfo, Writable, NUM_REG_CLASSES};
//=============================================================================
// Registers, the Universe thereof, and printing
#[rustfmt::skip]
const GPR_INDICES: [u8; 16] = [
// r0 and r1 reserved
30, 31,
// r2 - r5 call-clobbered
16, 17, 18, 19,
// r6 - r14 call-saved (order reversed)
28, 27, 26, 25, 24, 23, 22, 21, 20,
// r15 (SP)
29,
];
#[rustfmt::skip]
const FPR_INDICES: [u8; 16] = [
// f0 - f7 as pairs
0, 4, 1, 5, 2, 6, 3, 7,
// f8 - f15 as pairs
8, 12, 9, 13, 10, 14, 11, 15,
];
/// Get a reference to a GPR (integer register).
pub fn gpr(num: u8) -> Reg {
assert!(num < 16);
Reg::new_real(
RegClass::I64,
/* enc = */ num,
/* index = */ GPR_INDICES[num as usize],
)
}
/// Get a writable reference to a GPR.
pub fn writable_gpr(num: u8) -> Writable<Reg> {
Writable::from_reg(gpr(num))
}
/// Get a reference to a FPR (floating-point register).
pub fn fpr(num: u8) -> Reg {
assert!(num < 16);
Reg::new_real(
RegClass::F64,
/* enc = */ num,
/* index = */ FPR_INDICES[num as usize],
)
}
/// Get a writable reference to a V-register.
pub fn writable_fpr(num: u8) -> Writable<Reg> {
Writable::from_reg(fpr(num))
}
/// Get a reference to the stack-pointer register.
pub fn stack_reg() -> Reg {
gpr(15)
}
/// Get a writable reference to the stack-pointer register.
pub fn writable_stack_reg() -> Writable<Reg> {
Writable::from_reg(stack_reg())
}
/// Get a reference to the first temporary, sometimes "spill temporary", register. This register is
/// used to compute the address of a spill slot when a direct offset addressing mode from FP is not
/// sufficient (+/- 2^11 words). We exclude this register from regalloc and reserve it for this
/// purpose for simplicity; otherwise we need a multi-stage analysis where we first determine how
/// many spill slots we have, then perhaps remove the reg from the pool and recompute regalloc.
///
/// We use r1 for this because it's a scratch register but is slightly special (used for linker
/// veneers). We're free to use it as long as we don't expect it to live through call instructions.
pub fn spilltmp_reg() -> Reg {
gpr(1)
}
/// Get a writable reference to the spilltmp reg.
pub fn writable_spilltmp_reg() -> Writable<Reg> {
Writable::from_reg(spilltmp_reg())
}
pub fn zero_reg() -> Reg {
gpr(0)
}
/// Create the register universe for AArch64.
pub fn create_reg_universe(_flags: &settings::Flags) -> RealRegUniverse {
let mut regs = vec![];
let mut allocable_by_class = [None; NUM_REG_CLASSES];
// Numbering Scheme: we put FPRs first, then GPRs. The GPRs exclude several registers:
// r0 (we cannot use this for addressing // FIXME regalloc)
// r1 (spilltmp)
// r15 (stack pointer)
// FPRs.
let mut base = regs.len();
regs.push((fpr(0).to_real_reg(), "%f0".into()));
regs.push((fpr(2).to_real_reg(), "%f2".into()));
regs.push((fpr(4).to_real_reg(), "%f4".into()));
regs.push((fpr(6).to_real_reg(), "%f6".into()));
regs.push((fpr(1).to_real_reg(), "%f1".into()));
regs.push((fpr(3).to_real_reg(), "%f3".into()));
regs.push((fpr(5).to_real_reg(), "%f5".into()));
regs.push((fpr(7).to_real_reg(), "%f7".into()));
regs.push((fpr(8).to_real_reg(), "%f8".into()));
regs.push((fpr(10).to_real_reg(), "%f10".into()));
regs.push((fpr(12).to_real_reg(), "%f12".into()));
regs.push((fpr(14).to_real_reg(), "%f14".into()));
regs.push((fpr(9).to_real_reg(), "%f9".into()));
regs.push((fpr(11).to_real_reg(), "%f11".into()));
regs.push((fpr(13).to_real_reg(), "%f13".into()));
regs.push((fpr(15).to_real_reg(), "%f15".into()));
allocable_by_class[RegClass::F64.rc_to_usize()] = Some(RegClassInfo {
first: base,
last: regs.len() - 1,
suggested_scratch: Some(fpr(1).get_index()),
});
// Caller-saved GPRs in the SystemV s390x ABI.
base = regs.len();
regs.push((gpr(2).to_real_reg(), "%r2".into()));
regs.push((gpr(3).to_real_reg(), "%r3".into()));
regs.push((gpr(4).to_real_reg(), "%r4".into()));
regs.push((gpr(5).to_real_reg(), "%r5".into()));
// Callee-saved GPRs in the SystemV s390x ABI.
// We start from r14 downwards in an attempt to allow the
// prolog to use as short a STMG as possible.
regs.push((gpr(14).to_real_reg(), "%r14".into()));
regs.push((gpr(13).to_real_reg(), "%r13".into()));
regs.push((gpr(12).to_real_reg(), "%r12".into()));
regs.push((gpr(11).to_real_reg(), "%r11".into()));
regs.push((gpr(10).to_real_reg(), "%r10".into()));
regs.push((gpr(9).to_real_reg(), "%r9".into()));
regs.push((gpr(8).to_real_reg(), "%r8".into()));
regs.push((gpr(7).to_real_reg(), "%r7".into()));
regs.push((gpr(6).to_real_reg(), "%r6".into()));
allocable_by_class[RegClass::I64.rc_to_usize()] = Some(RegClassInfo {
first: base,
last: regs.len() - 1,
suggested_scratch: Some(gpr(13).get_index()),
});
// Other regs, not available to the allocator.
let allocable = regs.len();
regs.push((gpr(15).to_real_reg(), "%r15".into()));
regs.push((gpr(0).to_real_reg(), "%r0".into()));
regs.push((gpr(1).to_real_reg(), "%r1".into()));
// Assert sanity: the indices in the register structs must match their
// actual indices in the array.
for (i, reg) in regs.iter().enumerate() {
assert_eq!(i, reg.0.get_index());
}
RealRegUniverse {
regs,
allocable,
allocable_by_class,
}
}