Avoid re-spilling to spillslot when still clean: intra-block edition (inter-block needs more analysis and careful thought)

This commit is contained in:
Chris Fallin
2021-05-26 17:08:14 -07:00
parent dcf6f473ca
commit e521811b88

View File

@@ -3541,6 +3541,8 @@ impl<'a, F: Function> Env<'a, F> {
None
};
let mut clean_spillslot: Option<SpillSlot> = None;
// For each range in each vreg, insert moves or
// half-moves. We also scan over `blockparam_ins` and
// `blockparam_outs`, which are sorted by (block, vreg),
@@ -3614,9 +3616,20 @@ impl<'a, F: Function> Env<'a, F> {
let first_is_def =
self.ranges[entry.index.index()].has_flag(LiveRangeFlag::StartsAtDef);
debug_assert!(prev_alloc != Allocation::none());
// If this is a stack-to-reg move, track that the reg is a clean copy of a spillslot.
if prev_alloc.is_stack() && alloc.is_reg() {
clean_spillslot = Some(prev_alloc.as_stack().unwrap());
}
// If this is a reg-to-stack move, elide it if the spillslot is still clean.
let skip_spill = prev_alloc.is_reg()
&& alloc.is_stack()
&& clean_spillslot == alloc.as_stack();
if prev_range.to == range.from
&& !self.is_start_of_block(range.from)
&& !first_is_def
&& !skip_spill
{
log::debug!(
"prev LR {} abuts LR {} in same block; moving {} -> {} for v{}",
@@ -3637,6 +3650,27 @@ impl<'a, F: Function> Env<'a, F> {
}
}
// If this range either spans any block boundary, or
// has any mods/defs, then the spillslot (if any) that
// its value came from is no longer 'clean'.
if clean_spillslot.is_some() {
if self.cfginfo.insn_block[range.from.inst().index()]
!= self.cfginfo.insn_block[range.to.prev().inst().index()]
{
clean_spillslot = None;
} else {
for u in &self.ranges[entry.index.index()].uses {
match u.operand.kind() {
OperandKind::Def | OperandKind::Mod => {
clean_spillslot = None;
break;
}
_ => {}
}
}
}
}
// The block-to-block edge-move logic is not
// applicable to pinned vregs, which are always in one
// PReg (so never need moves within their own vreg