Generate SSA code from returns (#5172)

Modify return pseudo-instructions to have pairs of registers: virtual and real. This allows us to constrain the virtual registers to the real ones specified by the abi, instead of directly emitting moves to those real registers.
This commit is contained in:
Trevor Elliott
2022-11-08 16:00:49 -08:00
committed by GitHub
parent d59caf39b6
commit b077854b57
171 changed files with 6368 additions and 6364 deletions

View File

@@ -136,6 +136,17 @@ pub struct ArgPair {
pub preg: Reg,
}
/// A type used by backends to track return register binding info in the "ret"
/// pseudoinst. The pseudoinst holds a vec of `RetPair` structs.
#[derive(Clone, Debug)]
pub struct RetPair {
/// The vreg that is returned by this pseudionst.
pub vreg: Reg,
/// The preg that the arg is returned through; this constrains the vreg's
/// placement at the pseudoinst.
pub preg: Reg,
}
/// A location for (part of) an argument or return value. These "storage slots"
/// are specified for each register-sized part of an argument.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -421,7 +432,7 @@ pub trait ABIMachineSpec {
fn gen_args(isa_flags: &Self::F, args: Vec<ArgPair>) -> Self::I;
/// Generate a return instruction.
fn gen_ret(setup_frame: bool, isa_flags: &Self::F, rets: Vec<Reg>) -> Self::I;
fn gen_ret(setup_frame: bool, isa_flags: &Self::F, rets: Vec<RetPair>) -> Self::I;
/// Generate an add-with-immediate. Note that even if this uses a scratch
/// register, it must satisfy two requirements:
@@ -1483,8 +1494,10 @@ impl<M: ABIMachineSpec> Callee<M> {
&self,
sigs: &SigSet,
idx: usize,
from_regs: ValueRegs<Writable<Reg>>,
) -> SmallInstVec<M::I> {
from_regs: ValueRegs<Reg>,
vregs: &mut VRegAllocator<M::I>,
) -> (SmallVec<[RetPair; 2]>, SmallInstVec<M::I>) {
let mut reg_pairs = smallvec![];
let mut ret = smallvec![];
let word_bits = M::word_bits() as u8;
match &sigs[self.sig].rets(sigs)[idx] {
@@ -1497,24 +1510,31 @@ impl<M: ABIMachineSpec> Callee<M> {
} => {
let from_bits = ty_bits(ty) as u8;
let ext = M::get_ext_mode(sigs[self.sig].call_conv, extension);
let reg: Writable<Reg> = Writable::from_reg(Reg::from(reg));
match (ext, from_bits) {
(ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n)
let vreg = match (ext, from_bits) {
(ir::ArgumentExtension::Uext, n)
| (ir::ArgumentExtension::Sext, n)
if n < word_bits =>
{
let signed = ext == ArgumentExtension::Sext;
let signed = ext == ir::ArgumentExtension::Sext;
let dst = writable_value_regs(vregs.alloc(ty).unwrap())
.only_reg()
.unwrap();
ret.push(M::gen_extend(
reg,
from_reg.to_reg(),
signed,
from_bits,
dst, from_reg, signed, from_bits,
/* to_bits = */ word_bits,
));
dst.to_reg()
}
_ => {
ret.push(M::gen_move(reg, from_reg.to_reg(), ty));
// No move needed, regalloc2 will emit it using the constraint
// added by the RetPair.
from_reg
}
};
reg_pairs.push(RetPair {
vreg,
preg: Reg::from(reg),
});
}
&ABIArgSlot::Stack {
offset,
@@ -1533,16 +1553,17 @@ impl<M: ABIMachineSpec> Callee<M> {
let ext = M::get_ext_mode(sigs[self.sig].call_conv, extension);
// Trash the from_reg; it should be its last use.
match (ext, from_bits) {
(ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n)
(ir::ArgumentExtension::Uext, n)
| (ir::ArgumentExtension::Sext, n)
if n < word_bits =>
{
assert_eq!(M::word_reg_class(), from_reg.to_reg().class());
let signed = ext == ArgumentExtension::Sext;
assert_eq!(M::word_reg_class(), from_reg.class());
let signed = ext == ir::ArgumentExtension::Sext;
let dst = writable_value_regs(vregs.alloc(ty).unwrap())
.only_reg()
.unwrap();
ret.push(M::gen_extend(
Writable::from_reg(from_reg.to_reg()),
from_reg.to_reg(),
signed,
from_bits,
dst, from_reg, signed, from_bits,
/* to_bits = */ word_bits,
));
// Store the extended version.
@@ -1553,21 +1574,21 @@ impl<M: ABIMachineSpec> Callee<M> {
ret.push(M::gen_store_base_offset(
self.ret_area_ptr.unwrap().to_reg(),
off,
from_reg.to_reg(),
from_reg,
ty,
));
}
}
}
}
&ABIArg::StructArg { .. } => {
ABIArg::StructArg { .. } => {
panic!("StructArg in return position is unsupported");
}
&ABIArg::ImplicitPtrArg { .. } => {
ABIArg::ImplicitPtrArg { .. } => {
panic!("ImplicitPtrArg in return position is unsupported");
}
}
ret
(reg_pairs, ret)
}
/// Generate any setup instruction needed to save values to the
@@ -1594,22 +1615,7 @@ impl<M: ABIMachineSpec> Callee<M> {
}
/// Generate a return instruction.
pub fn gen_ret(&self, sigs: &SigSet) -> M::I {
let mut rets = vec![];
for ret in sigs[self.sig].rets(sigs) {
match ret {
ABIArg::Slots { slots, .. } => {
for slot in slots {
match slot {
ABIArgSlot::Reg { reg, .. } => rets.push(Reg::from(*reg)),
_ => {}
}
}
}
_ => {}
}
}
pub fn gen_ret(&self, rets: Vec<RetPair>) -> M::I {
M::gen_ret(self.setup_frame, &self.isa_flags, rets)
}

View File

@@ -7,8 +7,9 @@ use std::cell::Cell;
use target_lexicon::Triple;
pub use super::MachLabel;
use super::RetPair;
pub use crate::ir::{
condcodes, condcodes::CondCode, dynamic_to_fixed, ArgumentExtension, Constant,
condcodes, condcodes::CondCode, dynamic_to_fixed, ArgumentExtension, ArgumentPurpose, Constant,
DynamicStackSlot, ExternalName, FuncRef, GlobalValue, Immediate, SigRef, StackSlot,
};
pub use crate::isa::unwind::UnwindInst;
@@ -23,7 +24,7 @@ pub type ValueSlice = (ValueList, usize);
pub type ValueArray2 = [Value; 2];
pub type ValueArray3 = [Value; 3];
pub type WritableReg = Writable<Reg>;
pub type VecReg = Vec<Reg>;
pub type VecRetPair = Vec<RetPair>;
pub type VecMask = Vec<u8>;
pub type ValueRegs = crate::machinst::ValueRegs<Reg>;
pub type WritableValueRegs = crate::machinst::ValueRegs<WritableReg>;
@@ -422,10 +423,6 @@ macro_rules! isle_lower_prelude_methods {
))
}
fn retval(&mut self, i: usize) -> WritableValueRegs {
self.lower_ctx.retval(i)
}
fn only_writable_reg(&mut self, regs: WritableValueRegs) -> Option<WritableReg> {
regs.only_reg()
}
@@ -590,6 +587,17 @@ macro_rules! isle_lower_prelude_methods {
fn floatcc_inverse(&mut self, cc: &FloatCC) -> FloatCC {
cc.inverse()
}
/// Generate the return instruction.
fn gen_return(&mut self, (list, off): ValueSlice) {
let rets = (off..list.len(&self.lower_ctx.dfg().value_lists))
.map(|ix| {
let val = list.get(ix, &self.lower_ctx.dfg().value_lists).unwrap();
self.put_in_regs(val)
})
.collect();
self.lower_ctx.gen_return(rets);
}
};
}

View File

@@ -158,8 +158,8 @@ pub struct Lower<'func, I: VCodeInst> {
/// Mapping from `Value` (SSA value in IR) to virtual register.
value_regs: SecondaryMap<Value, ValueRegs<Reg>>,
/// Return-value vregs.
retval_regs: Vec<ValueRegs<Reg>>,
/// sret registers, if needed.
sret_reg: Option<ValueRegs<Reg>>,
/// Instruction colors at block exits. From this map, we can recover all
/// instruction colors by scanning backward from the block end and
@@ -370,12 +370,13 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
}
}
// Assign vreg(s) to each return value.
let mut retval_regs = vec![];
// Make a sret register, if one is needed.
let mut sret_reg = None;
for ret in &vcode.abi().signature().returns.clone() {
let regs = vregs.alloc(ret.value_type)?;
retval_regs.push(regs);
trace!("retval gets regs {:?}", regs);
if ret.purpose == ArgumentPurpose::StructReturn {
assert!(sret_reg.is_none());
sret_reg = Some(vregs.alloc(ret.value_type)?);
}
}
// Compute instruction colors, find constant instructions, and find instructions with
@@ -414,7 +415,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
vcode,
vregs,
value_regs,
retval_regs,
sret_reg,
block_end_colors,
side_effect_inst_entry_colors,
inst_constants,
@@ -576,15 +577,15 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
let ty = self.abi().signature().params[i].value_type;
// The ABI implementation must have ensured that a StructReturn
// arg is present in the return values.
let struct_ret_idx = self
assert!(self
.abi()
.signature()
.returns
.iter()
.position(|ret| ret.purpose == ArgumentPurpose::StructReturn)
.expect("StructReturn return value not present!");
.is_some());
self.emit(I::gen_move(
Writable::from_reg(self.retval_regs[struct_ret_idx].regs()[0]),
Writable::from_reg(self.sret_reg.unwrap().regs()[0]),
regs.regs()[0].to_reg(),
ty,
));
@@ -611,21 +612,36 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
}
}
fn gen_retval_setup(&mut self) {
let retval_regs = self.retval_regs.clone();
for (i, regs) in retval_regs.into_iter().enumerate() {
let regs = writable_value_regs(regs);
for insn in self
.vcode
.abi()
.gen_copy_regs_to_retval(self.sigs(), i, regs)
.into_iter()
{
/// Generate the return instruction.
pub fn gen_return(&mut self, rets: Vec<ValueRegs<Reg>>) {
let mut out_rets = vec![];
let mut rets = rets.into_iter();
for (i, ret) in self
.abi()
.signature()
.returns
.clone()
.into_iter()
.enumerate()
{
let regs = if ret.purpose == ArgumentPurpose::StructReturn {
self.sret_reg.unwrap().clone()
} else {
rets.next().unwrap()
};
let (regs, insns) = self.vcode.abi().gen_copy_regs_to_retval(
self.vcode.sigs(),
i,
regs,
&mut self.vregs,
);
out_rets.extend(regs);
for insn in insns {
self.emit(insn);
}
}
let inst = self.vcode.abi().gen_ret(self.sigs());
self.emit(inst);
// Hack: generate a virtual instruction that uses vmctx in
// order to keep it alive for the duration of the function,
@@ -636,6 +652,9 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
self.emit(I::gen_dummy_use(vmctx_reg));
}
}
let inst = self.abi().gen_ret(out_rets);
self.emit(inst);
}
/// Has this instruction been sunk to a use-site (i.e., away from its
@@ -720,10 +739,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
trace!("lowering: inst {}: {:?}", inst, self.f.dfg[inst]);
backend.lower(self, inst)?;
}
if data.opcode().is_return() {
// Return: handle specially, using ABI-appropriate sequence.
self.gen_retval_setup();
}
let loc = self.srcloc(inst);
self.finish_ir_inst(loc);
@@ -1026,14 +1041,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
pub fn abi_mut(&mut self) -> &mut Callee<I::ABIMachineSpec> {
self.vcode.abi_mut()
}
/// Get the (virtual) register that receives the return value. A return
/// instruction should lower into a sequence that fills this register. (Why
/// not allow the backend to specify its own result register for the return?
/// Because there may be multiple return points.)
pub fn retval(&self, idx: usize) -> ValueRegs<Writable<Reg>> {
writable_value_regs(self.retval_regs[idx])
}
}
/// Instruction input/output queries.