ABI: implement register arguments with constraints. (#4858)

* ABI: implement register arguments with constraints.

Currently, Cranelift's ABI code emits a sequence of moves from physical
registers into vregs at the top of the function body, one for every
register-carried argument.

For a number of reasons, we want to move to operand constraints instead,
and remove the use of explicitly-named "pinned vregs"; this allows for
better regalloc in theory, as it removes the need to "reverse-engineer"
the sequence of moves.

This PR alters the ABI code so that it generates a single "args"
pseudo-instruction as the first instruction in the function body. This
pseudo-inst defs all register arguments, and constrains them to the
appropriate registers at the def-point. Subsequently the regalloc can
move them wherever it needs to.

Some care was taken not to have this pseudo-inst show up in
post-regalloc disassemblies, but the change did cause a general regalloc
"shift" in many tests, so the precise-output updates are a bit noisy.
Sorry about that!

A subsequent PR will handle the other half of the ABI code, namely, the
callsite case, with a similar preg-to-constraint conversion.

* Update based on review feedback.

* Review feedback.
This commit is contained in:
Chris Fallin
2022-09-08 20:03:14 -05:00
committed by GitHub
parent 13c7846815
commit 2986f6b0ff
101 changed files with 2688 additions and 2441 deletions

View File

@@ -391,6 +391,10 @@ impl ABIMachineSpec for AArch64MachineDeps {
}
}
fn gen_args(_isa_flags: &aarch64_settings::Flags, args: Vec<ArgPair>) -> Inst {
Inst::Args { args }
}
fn gen_ret(setup_frame: bool, isa_flags: &aarch64_settings::Flags, rets: Vec<Reg>) -> Inst {
if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
let key = if isa_flags.sign_return_address_with_bkey() {

View File

@@ -779,6 +779,10 @@
(CallInd
(info BoxCallIndInfo))
;; A pseudo-instruction that captures register arguments in vregs.
(Args
(args VecArgPair))
;; ---- branches (exactly one must appear at end of BB) ----
;; A machine return instruction.

View File

@@ -3015,6 +3015,10 @@ impl MachInstEmit for Inst {
// Emit the jump itself.
sink.put4(enc_jump26(0b000101, dest.as_offset26_or_zero()));
}
&Inst::Args { .. } => {
// Nothing: this is a pseudoinstruction that serves
// only to constrain registers at a certain point.
}
&Inst::Ret { .. } => {
sink.put4(0xd65f03c0);
}

View File

@@ -1015,6 +1015,11 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
collector.reg_def(rd);
collector.reg_use(rn);
}
&Inst::Args { ref args } => {
for arg in args {
collector.reg_fixed_def(arg.vreg, arg.preg);
}
}
&Inst::Ret { ref rets } | &Inst::AuthenticatedRet { ref rets, .. } => {
for &ret in rets {
collector.reg_use(ret);
@@ -1131,6 +1136,13 @@ impl MachInst for Inst {
}
}
fn is_args(&self) -> bool {
match self {
Self::Args { .. } => true,
_ => false,
}
}
fn is_term(&self) -> MachTerminator {
match self {
&Inst::Ret { .. } | &Inst::AuthenticatedRet { .. } => MachTerminator::Ret,
@@ -2632,6 +2644,16 @@ impl Inst {
let rn = pretty_print_reg(info.rn, allocs);
format!("blr {}", rn)
}
&Inst::Args { ref args } => {
let mut s = "args".to_string();
for arg in args {
use std::fmt::Write;
let preg = pretty_print_reg(arg.preg, &mut empty_allocs);
let def = pretty_print_reg(arg.vreg.to_reg(), allocs);
write!(&mut s, " {}={}", def, preg).unwrap();
}
s
}
&Inst::Ret { .. } => "ret".to_string(),
&Inst::AuthenticatedRet { key, is_hint, .. } => {
let key = match key {

View File

@@ -30,7 +30,9 @@ use crate::{
isa::aarch64::abi::AArch64Caller,
isa::aarch64::inst::args::{ShiftOp, ShiftOpShiftImm},
isa::unwind::UnwindInst,
machinst::{ty_bits, InsnOutput, Lower, MachInst, VCodeConstant, VCodeConstantData},
machinst::{
abi::ArgPair, ty_bits, InsnOutput, Lower, MachInst, VCodeConstant, VCodeConstantData,
},
};
use regalloc2::PReg;
use std::boxed::Box;
@@ -43,6 +45,7 @@ type BoxCallIndInfo = Box<CallIndInfo>;
type VecMachLabel = Vec<MachLabel>;
type BoxJTSequenceInfo = Box<JTSequenceInfo>;
type BoxExternalName = Box<ExternalName>;
type VecArgPair = Vec<ArgPair>;
/// The main entry point for lowering with ISLE.
pub(crate) fn lower(

View File

@@ -459,6 +459,10 @@ impl ABIMachineSpec for S390xMachineDeps {
}
}
fn gen_args(_isa_flags: &s390x_settings::Flags, args: Vec<ArgPair>) -> Inst {
Inst::Args { args }
}
fn gen_ret(_setup_frame: bool, _isa_flags: &s390x_settings::Flags, rets: Vec<Reg>) -> Inst {
Inst::Ret {
link: gpr(14),

View File

@@ -860,6 +860,10 @@
(CallInd
(link WritableReg)
(info BoxCallIndInfo))
;; A pseudo-instruction that captures register arguments in vregs.
(Args
(args VecArgPair))
;; ---- branches (exactly one must appear at end of BB) ----

View File

@@ -3319,6 +3319,7 @@ impl MachInstEmit for Inst {
sink.add_call_site(info.opcode);
}
}
&Inst::Args { .. } => {}
&Inst::Ret { link, .. } => {
let link = allocs.next(link);

View File

@@ -208,6 +208,7 @@ impl Inst {
| Inst::VecReplicateLane { .. }
| Inst::Call { .. }
| Inst::CallInd { .. }
| Inst::Args { .. }
| Inst::Ret { .. }
| Inst::Jump { .. }
| Inst::CondBr { .. }
@@ -935,6 +936,11 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandC
collector.reg_defs(&*info.defs);
collector.reg_clobbers(info.clobbers);
}
&Inst::Args { ref args } => {
for arg in args {
collector.reg_fixed_def(arg.vreg, arg.preg);
}
}
&Inst::Ret { link, ref rets } => {
collector.reg_use(link);
collector.reg_uses(&rets[..]);
@@ -1011,6 +1017,13 @@ impl MachInst for Inst {
}
}
fn is_args(&self) -> bool {
match self {
Self::Args { .. } => true,
_ => false,
}
}
fn is_term(&self) -> MachTerminator {
match self {
&Inst::Ret { .. } => MachTerminator::Ret,
@@ -3070,6 +3083,16 @@ impl Inst {
let rn = pretty_print_reg(info.rn, allocs);
format!("basr {}, {}", link, rn)
}
&Inst::Args { ref args } => {
let mut s = "args".to_string();
for arg in args {
use std::fmt::Write;
let preg = pretty_print_reg(arg.preg, &mut empty_allocs);
let def = pretty_print_reg(arg.vreg.to_reg(), allocs);
write!(&mut s, " {}={}", def, preg).unwrap();
}
s
}
&Inst::Ret { link, .. } => {
let link = pretty_print_reg(link, allocs);
format!("br {}", link)

View File

@@ -22,7 +22,7 @@ use crate::{
isa::unwind::UnwindInst,
isa::CallConv,
machinst::abi::ABIMachineSpec,
machinst::{InsnOutput, Lower, MachInst, VCodeConstant, VCodeConstantData},
machinst::{ArgPair, InsnOutput, Lower, MachInst, VCodeConstant, VCodeConstantData},
};
use regalloc2::PReg;
use smallvec::{smallvec, SmallVec};
@@ -45,6 +45,7 @@ type BoxExternalName = Box<ExternalName>;
type BoxSymbolReloc = Box<SymbolReloc>;
type VecMInst = Vec<MInst>;
type VecMInstBuilder = Cell<Vec<MInst>>;
type VecArgPair = Vec<ArgPair>;
/// The main entry point for lowering with ISLE.
pub(crate) fn lower(

View File

@@ -297,6 +297,10 @@ impl ABIMachineSpec for X64ABIMachineSpec {
}
}
fn gen_args(_isa_flags: &x64_settings::Flags, args: Vec<ArgPair>) -> Inst {
Inst::Args { args }
}
fn gen_ret(_setup_frame: bool, _isa_flags: &x64_settings::Flags, rets: Vec<Reg>) -> Self::I {
Inst::ret(rets)
}

View File

@@ -323,6 +323,10 @@
(CallUnknown (dest RegMem)
(info BoxCallInfo))
;; A pseudo-instruction that captures register arguments in vregs.
(Args
(args VecArgPair))
;; Return.
(Ret (rets VecReg))

View File

@@ -1406,6 +1406,8 @@ pub(crate) fn emit(
}
}
Inst::Args { .. } => {}
Inst::Ret { .. } => sink.put1(0xC3),
Inst::JmpKnown { dst } => {

View File

@@ -101,6 +101,7 @@ impl Inst {
| Inst::Pop64 { .. }
| Inst::Push64 { .. }
| Inst::StackProbeLoop { .. }
| Inst::Args { .. }
| Inst::Ret { .. }
| Inst::Setcc { .. }
| Inst::ShiftR { .. }
@@ -1452,6 +1453,17 @@ impl PrettyPrint for Inst {
format!("{} *{}", ljustify("call".to_string()), dest)
}
Inst::Args { args } => {
let mut s = "args".to_string();
for arg in args {
use std::fmt::Write;
let preg = regs::show_reg(arg.preg);
let def = pretty_print_reg(arg.vreg.to_reg(), 8, allocs);
write!(&mut s, " {}={}", def, preg).unwrap();
}
s
}
Inst::Ret { .. } => "ret".to_string(),
Inst::JmpKnown { dst } => {
@@ -2037,6 +2049,12 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
mem.get_operands_late(collector)
}
Inst::Args { args } => {
for arg in args {
collector.reg_fixed_def(arg.vreg, arg.preg);
}
}
Inst::Ret { rets } => {
// The return value(s) are live-out; we represent this
// with register uses on the return instruction.
@@ -2134,6 +2152,13 @@ impl MachInst for Inst {
}
}
fn is_args(&self) -> bool {
match self {
Self::Args { .. } => true,
_ => false,
}
}
fn is_term(&self) -> MachTerminator {
match self {
// Interesting cases.

View File

@@ -30,8 +30,8 @@ use crate::{
},
},
machinst::{
isle::*, valueregs, InsnInput, InsnOutput, Lower, MachAtomicRmwOp, MachInst, VCodeConstant,
VCodeConstantData,
isle::*, valueregs, ArgPair, InsnInput, InsnOutput, Lower, MachAtomicRmwOp, MachInst,
VCodeConstant, VCodeConstantData,
},
};
use alloc::vec::Vec;
@@ -44,6 +44,7 @@ use target_lexicon::Triple;
type BoxCallInfo = Box<CallInfo>;
type BoxVecMachLabel = Box<SmallVec<[MachLabel; 4]>>;
type MachLabelSlice = [MachLabel];
type VecArgPair = Vec<ArgPair>;
pub struct SinkableLoad {
inst: Inst,