Cranelift: update to latest regalloc2: (#4324)

- Handle call instructions' clobbers with the clobbers API, using RA2's
  clobbers bitmask (bytecodealliance/regalloc2#58) rather than clobbers
  list;

- Pull in changes from bytecodealliance/regalloc2#59 for much more sane
  edge-case behavior w.r.t. liverange splitting.
This commit is contained in:
Chris Fallin
2022-06-28 09:01:59 -07:00
committed by GitHub
parent 66b829b1bf
commit b2e28b917a
21 changed files with 402 additions and 293 deletions

View File

@@ -11,7 +11,7 @@ use crate::{CodegenError, CodegenResult};
use alloc::boxed::Box;
use alloc::vec::Vec;
use args::*;
use regalloc2::VReg;
use regalloc2::{PRegSet, VReg};
use smallvec::{smallvec, SmallVec};
use std::convert::TryFrom;
@@ -488,9 +488,12 @@ impl ABIMachineSpec for X64ABIMachineSpec {
));
insts.push(Inst::CallKnown {
dest: ExternalName::LibCall(LibCall::Probestack),
uses: vec![regs::rax()],
defs: vec![],
opcode: Opcode::Call,
info: Box::new(CallInfo {
uses: smallvec![regs::rax()],
defs: smallvec![],
clobbers: PRegSet::empty(),
opcode: Opcode::Call,
}),
});
insts
}
@@ -633,8 +636,9 @@ impl ABIMachineSpec for X64ABIMachineSpec {
/// Generate a call instruction/sequence.
fn gen_call(
dest: &CallDest,
uses: Vec<Reg>,
defs: Vec<Writable<Reg>>,
uses: SmallVec<[Reg; 8]>,
defs: SmallVec<[Writable<Reg>; 8]>,
clobbers: PRegSet,
opcode: ir::Opcode,
tmp: Writable<Reg>,
_callee_conv: isa::CallConv,
@@ -643,7 +647,7 @@ impl ABIMachineSpec for X64ABIMachineSpec {
let mut insts = SmallVec::new();
match dest {
&CallDest::ExtName(ref name, RelocDistance::Near) => {
insts.push(Inst::call_known(name.clone(), uses, defs, opcode));
insts.push(Inst::call_known(name.clone(), uses, defs, clobbers, opcode));
}
&CallDest::ExtName(ref name, RelocDistance::Far) => {
insts.push(Inst::LoadExtName {
@@ -655,11 +659,18 @@ impl ABIMachineSpec for X64ABIMachineSpec {
RegMem::reg(tmp.to_reg()),
uses,
defs,
clobbers,
opcode,
));
}
&CallDest::Reg(reg) => {
insts.push(Inst::call_unknown(RegMem::reg(reg), uses, defs, opcode));
insts.push(Inst::call_unknown(
RegMem::reg(reg),
uses,
defs,
clobbers,
opcode,
));
}
}
insts
@@ -703,8 +714,9 @@ impl ABIMachineSpec for X64ABIMachineSpec {
});
insts.push(Inst::call_unknown(
RegMem::reg(memcpy_addr),
/* uses = */ vec![arg0, arg1, arg2],
/* defs = */ Self::get_regs_clobbered_by_call(call_conv),
/* uses = */ smallvec![arg0, arg1, arg2],
/* defs = */ smallvec![],
/* clobbers = */ Self::get_regs_clobbered_by_call(call_conv),
Opcode::Call,
));
insts
@@ -726,51 +738,21 @@ impl ABIMachineSpec for X64ABIMachineSpec {
s.nominal_sp_to_fp
}
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> Vec<Writable<Reg>> {
let mut caller_saved = vec![
// intersection of Systemv and FastCall calling conventions:
// - GPR: all except RDI, RSI, RBX, RBP, R12 to R15.
// SysV adds RDI, RSI (FastCall makes these callee-saved).
Writable::from_reg(regs::rax()),
Writable::from_reg(regs::rcx()),
Writable::from_reg(regs::rdx()),
Writable::from_reg(regs::r8()),
Writable::from_reg(regs::r9()),
Writable::from_reg(regs::r10()),
Writable::from_reg(regs::r11()),
// - XMM: XMM0-5. SysV adds the rest (XMM6-XMM15).
Writable::from_reg(regs::xmm0()),
Writable::from_reg(regs::xmm1()),
Writable::from_reg(regs::xmm2()),
Writable::from_reg(regs::xmm3()),
Writable::from_reg(regs::xmm4()),
Writable::from_reg(regs::xmm5()),
];
if !call_conv_of_callee.extends_windows_fastcall() {
caller_saved.push(Writable::from_reg(regs::rsi()));
caller_saved.push(Writable::from_reg(regs::rdi()));
caller_saved.push(Writable::from_reg(regs::xmm6()));
caller_saved.push(Writable::from_reg(regs::xmm7()));
caller_saved.push(Writable::from_reg(regs::xmm8()));
caller_saved.push(Writable::from_reg(regs::xmm9()));
caller_saved.push(Writable::from_reg(regs::xmm10()));
caller_saved.push(Writable::from_reg(regs::xmm11()));
caller_saved.push(Writable::from_reg(regs::xmm12()));
caller_saved.push(Writable::from_reg(regs::xmm13()));
caller_saved.push(Writable::from_reg(regs::xmm14()));
caller_saved.push(Writable::from_reg(regs::xmm15()));
}
fn get_regs_clobbered_by_call(call_conv_of_callee: isa::CallConv) -> PRegSet {
let mut clobbers = if call_conv_of_callee.extends_windows_fastcall() {
WINDOWS_CLOBBERS
} else {
SYSV_CLOBBERS
};
if call_conv_of_callee.extends_baldrdash() {
caller_saved.push(Writable::from_reg(regs::r12()));
caller_saved.push(Writable::from_reg(regs::r13()));
// Not r14; implicitly preserved in the entry.
caller_saved.push(Writable::from_reg(regs::r15()));
caller_saved.push(Writable::from_reg(regs::rbx()));
clobbers.add(regs::gpr_preg(regs::ENC_R12));
clobbers.add(regs::gpr_preg(regs::ENC_R13));
clobbers.add(regs::gpr_preg(regs::ENC_R15));
clobbers.add(regs::gpr_preg(regs::ENC_RBX));
}
caller_saved
clobbers
}
fn get_ext_mode(
@@ -1032,3 +1014,52 @@ fn compute_clobber_size(clobbers: &[Writable<RealReg>]) -> u32 {
}
align_to(clobbered_size, 16)
}
const WINDOWS_CLOBBERS: PRegSet = windows_clobbers();
const SYSV_CLOBBERS: PRegSet = sysv_clobbers();
const fn windows_clobbers() -> PRegSet {
PRegSet::empty()
.with(regs::gpr_preg(regs::ENC_RAX))
.with(regs::gpr_preg(regs::ENC_RCX))
.with(regs::gpr_preg(regs::ENC_RDX))
.with(regs::gpr_preg(regs::ENC_R8))
.with(regs::gpr_preg(regs::ENC_R9))
.with(regs::gpr_preg(regs::ENC_R10))
.with(regs::gpr_preg(regs::ENC_R11))
.with(regs::fpr_preg(0))
.with(regs::fpr_preg(1))
.with(regs::fpr_preg(2))
.with(regs::fpr_preg(3))
.with(regs::fpr_preg(4))
.with(regs::fpr_preg(5))
}
const fn sysv_clobbers() -> PRegSet {
PRegSet::empty()
.with(regs::gpr_preg(regs::ENC_RAX))
.with(regs::gpr_preg(regs::ENC_RCX))
.with(regs::gpr_preg(regs::ENC_RDX))
.with(regs::gpr_preg(regs::ENC_RSI))
.with(regs::gpr_preg(regs::ENC_RDI))
.with(regs::gpr_preg(regs::ENC_R8))
.with(regs::gpr_preg(regs::ENC_R9))
.with(regs::gpr_preg(regs::ENC_R10))
.with(regs::gpr_preg(regs::ENC_R11))
.with(regs::fpr_preg(0))
.with(regs::fpr_preg(1))
.with(regs::fpr_preg(2))
.with(regs::fpr_preg(3))
.with(regs::fpr_preg(4))
.with(regs::fpr_preg(5))
.with(regs::fpr_preg(6))
.with(regs::fpr_preg(7))
.with(regs::fpr_preg(8))
.with(regs::fpr_preg(9))
.with(regs::fpr_preg(10))
.with(regs::fpr_preg(11))
.with(regs::fpr_preg(12))
.with(regs::fpr_preg(13))
.with(regs::fpr_preg(14))
.with(regs::fpr_preg(15))
}

View File

@@ -315,15 +315,11 @@
;; Direct call: call simm32.
(CallKnown (dest ExternalName)
(uses VecReg)
(defs VecWritableReg)
(opcode Opcode))
(info BoxCallInfo))
;; Indirect call: callq (reg mem)
(CallUnknown (dest RegMem)
(uses VecReg)
(defs VecWritableReg)
(opcode Opcode))
(info BoxCallInfo))
;; Return.
(Ret (rets VecReg))
@@ -502,6 +498,8 @@
LFence
SFence))
(type BoxCallInfo extern (enum))
;; Get the `OperandSize` for a given `Type`, rounding smaller types up to 32 bits.
(decl operand_size_of_type_32_64 (Type) OperandSize)
(extern constructor operand_size_of_type_32_64 operand_size_of_type_32_64)

View File

@@ -1202,7 +1202,11 @@ pub(crate) fn emit(
sink.put1(0x58 + (enc_dst & 7));
}
Inst::CallKnown { dest, opcode, .. } => {
Inst::CallKnown {
dest,
info: call_info,
..
} => {
if info.flags.enable_probestack() {
sink.add_trap(TrapCode::StackOverflow);
}
@@ -1214,12 +1218,16 @@ pub(crate) fn emit(
// beginning of the immediate field.
emit_reloc(sink, Reloc::X86CallPCRel4, &dest, -4);
sink.put4(0);
if opcode.is_call() {
sink.add_call_site(*opcode);
if call_info.opcode.is_call() {
sink.add_call_site(call_info.opcode);
}
}
Inst::CallUnknown { dest, opcode, .. } => {
Inst::CallUnknown {
dest,
info: call_info,
..
} => {
let dest = dest.with_allocs(allocs);
if info.flags.enable_probestack() {
@@ -1258,8 +1266,8 @@ pub(crate) fn emit(
if let Some(s) = state.take_stack_map() {
sink.add_stack_map(StackMapExtent::StartedAtOffset(start_offset), s);
}
if opcode.is_call() {
sink.add_call_site(*opcode);
if call_info.opcode.is_call() {
sink.add_call_site(call_info.opcode);
}
}

View File

@@ -3572,8 +3572,9 @@ fn test_x64_emit() {
namespace: 0,
index: 0,
},
Vec::new(),
Vec::new(),
smallvec![],
smallvec![],
PRegSet::default(),
Opcode::Call,
),
"E800000000",
@@ -3583,7 +3584,13 @@ fn test_x64_emit() {
// ========================================================
// CallUnknown
fn call_unknown(rm: RegMem) -> Inst {
Inst::call_unknown(rm, Vec::new(), Vec::new(), Opcode::CallIndirect)
Inst::call_unknown(
rm,
smallvec![],
smallvec![],
PRegSet::default(),
Opcode::CallIndirect,
)
}
insns.push((call_unknown(RegMem::reg(rbp)), "FFD5", "call *%rbp"));

View File

@@ -8,8 +8,9 @@ use crate::isa::x64::settings as x64_settings;
use crate::isa::CallConv;
use crate::machinst::*;
use crate::{settings, CodegenError, CodegenResult};
use alloc::boxed::Box;
use alloc::vec::Vec;
use regalloc2::{Allocation, VReg};
use regalloc2::{Allocation, PRegSet, VReg};
use smallvec::{smallvec, SmallVec};
use std::fmt;
use std::string::{String, ToString};
@@ -29,6 +30,19 @@ use args::*;
// `Inst` is defined inside ISLE as `MInst`. We publicly re-export it here.
pub use super::lower::isle::generated_code::MInst as Inst;
// Out-of-line data for calls, to keep the size of `Inst` downn.
#[derive(Clone, Debug)]
pub struct CallInfo {
/// Register uses of this call.
pub uses: SmallVec<[Reg; 8]>,
/// Register defs of this call.
pub defs: SmallVec<[Writable<Reg>; 8]>,
/// Registers clobbered by this call, as per its calling convention.
pub clobbers: PRegSet,
/// The opcode of this call.
pub opcode: Opcode,
}
pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
let xs = x as i64;
xs == ((xs << 32) >> 32)
@@ -646,30 +660,38 @@ impl Inst {
pub(crate) fn call_known(
dest: ExternalName,
uses: Vec<Reg>,
defs: Vec<Writable<Reg>>,
uses: SmallVec<[Reg; 8]>,
defs: SmallVec<[Writable<Reg>; 8]>,
clobbers: PRegSet,
opcode: Opcode,
) -> Inst {
Inst::CallKnown {
dest,
uses,
defs,
opcode,
info: Box::new(CallInfo {
uses,
defs,
clobbers,
opcode,
}),
}
}
pub(crate) fn call_unknown(
dest: RegMem,
uses: Vec<Reg>,
defs: Vec<Writable<Reg>>,
uses: SmallVec<[Reg; 8]>,
defs: SmallVec<[Writable<Reg>; 8]>,
clobbers: PRegSet,
opcode: Opcode,
) -> Inst {
dest.assert_regclass_is(RegClass::Int);
Inst::CallUnknown {
dest,
uses,
defs,
opcode,
info: Box::new(CallInfo {
uses,
defs,
clobbers,
opcode,
}),
}
}
@@ -1977,34 +1999,25 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
collector.reg_def(dst.to_writable_reg());
}
Inst::CallKnown {
ref uses, ref defs, ..
} => {
for &u in uses {
Inst::CallKnown { ref info, .. } => {
for &u in &info.uses {
collector.reg_use(u);
}
for &d in defs {
for &d in &info.defs {
collector.reg_def(d);
}
// FIXME: keep clobbers separate in the Inst and use
// `reg_clobber()`.
collector.reg_clobbers(info.clobbers);
}
Inst::CallUnknown {
ref uses,
ref defs,
dest,
..
} => {
Inst::CallUnknown { ref info, dest, .. } => {
dest.get_operands(collector);
for &u in uses {
for &u in &info.uses {
collector.reg_use(u);
}
for &d in defs {
for &d in &info.defs {
collector.reg_def(d);
}
// FIXME: keep clobbers separate in the Inst and use
// `reg_clobber()`.
collector.reg_clobbers(info.clobbers);
}
Inst::JmpTableSeq {
@@ -2076,10 +2089,9 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
// pseudoinstruction (and relocation that it emits) is specific to
// ELF systems; other x86-64 targets with other conventions (i.e.,
// Windows) use different TLS strategies.
for reg in X64ABIMachineSpec::get_regs_clobbered_by_call(CallConv::SystemV) {
// FIXME: use actual clobber functionality.
collector.reg_def(reg);
}
collector.reg_clobbers(X64ABIMachineSpec::get_regs_clobbered_by_call(
CallConv::SystemV,
));
}
Inst::Unwind { .. } => {}

View File

@@ -33,9 +33,12 @@ pub const ENC_R15: u8 = 15;
// Constructors for Regs.
fn gpr(enc: u8) -> Reg {
let preg = PReg::new(enc as usize, RegClass::Int);
let preg = gpr_preg(enc);
Reg::from(VReg::new(preg.index(), RegClass::Int))
}
pub(crate) const fn gpr_preg(enc: u8) -> PReg {
PReg::new(enc as usize, RegClass::Int)
}
pub(crate) fn rsi() -> Reg {
gpr(ENC_RSI)
@@ -96,10 +99,14 @@ pub(crate) fn pinned_reg() -> Reg {
}
fn fpr(enc: u8) -> Reg {
let preg = PReg::new(enc as usize, RegClass::Float);
let preg = fpr_preg(enc);
Reg::from(VReg::new(preg.index(), RegClass::Float))
}
pub(crate) const fn fpr_preg(enc: u8) -> PReg {
PReg::new(enc as usize, RegClass::Float)
}
pub(crate) fn xmm0() -> Reg {
fpr(0)
}

View File

@@ -18,7 +18,7 @@ use crate::{
settings::Flags,
unwind::UnwindInst,
x64::{
inst::{args::*, regs},
inst::{args::*, regs, CallInfo},
settings::Flags as IsaFlags,
},
},
@@ -26,8 +26,11 @@ use crate::{
isle::*, AtomicRmwOp, InsnInput, InsnOutput, LowerCtx, VCodeConstant, VCodeConstantData,
},
};
use std::boxed::Box;
use std::convert::TryFrom;
type BoxCallInfo = Box<CallInfo>;
pub struct SinkableLoad {
inst: Inst,
addr_input: InsnInput,