Some fixes to allow for call instructions to name args, returns, and clobbers with constraints. (#74)

* Some fixes to allow for call instructions to name args, returns, and clobbers with constraints.

- Allow early-pos uses with fixed regs that conflict with
  clobbers (which happen at late-pos), in addition to the
  existing logic for conflicts with late-pos defs with fixed
  regs.

  This is a pretty subtle issue that was uncovered in #53 for the def
  case, and the fix here is the mirror of that fix for clobbers. The
  root cause for all this complexity is that we can't split in the
  middle of an instruction (because there's no way to insert a move
  there!) so if a use is live-downward, we can't let it live in preg A
  at early-pos and preg B != A at late-pos; instead we need to rewrite
  the constraints and use a fixup move.

  The earlier change to fix #53 was actually a bit too conservative in
  that it always applied when such conflicts existed, even if the
  downward arg was not live. This PR fixes that (it's fine for the
  early-use and late-def to be fixed to the same reg if the use's
  liverange ends after early-pos) and adapts the same flexibility to
  the clobbers case as well.

- Reworks the fixups for the def case mentioned above to not shift the
  def to the Early point. Doing so causes issues when the def is to a
  reffy vreg: it can then be falsely included in a stackmap if the
  instruction containing this operand is a safepoint.

- Fixes the last-resort split-bundle-into-minimal-pieces logic from
  #59 to properly limit a minimal bundle piece to end after the
  early-pos, rather than cover the entire instruction. This was causing
  artificial overlaps between args that end after early-pos and defs
  that start at late-pos when one of the vregs hit the fallback split
  behavior.

* Fix fuzzbug: do not merge when a liverange has a fixed-reg def.

This can create impossible situations: e.g., if a vreg is constrained
to p0 as a late-def, and another, completely different vreg is
constrained to p0 as an early-use on the same instruction, and the
instruction also has a third vreg (early-use), we do not want to merge
the liverange for the third vreg with the first, because it would
result in an unsolveable conflict for p0 at the early-point.

* Review comments.
This commit is contained in:
Chris Fallin
2022-09-20 15:58:20 -07:00
committed by GitHub
parent 906a053208
commit 1b38a71e38
6 changed files with 100 additions and 48 deletions

View File

@@ -53,6 +53,14 @@ impl CodeRange {
pub fn len(&self) -> usize {
self.to.inst().index() - self.from.inst().index()
}
/// Returns the range covering just one program point.
#[inline(always)]
pub fn singleton(pos: ProgPoint) -> CodeRange {
CodeRange {
from: pos,
to: pos.next(),
}
}
}
impl std::cmp::PartialOrd for CodeRange {
@@ -185,7 +193,7 @@ pub struct LiveBundle {
pub spill_weight_and_props: u32,
}
pub const BUNDLE_MAX_SPILL_WEIGHT: u32 = (1 << 29) - 1;
pub const BUNDLE_MAX_SPILL_WEIGHT: u32 = (1 << 28) - 1;
pub const MINIMAL_FIXED_BUNDLE_SPILL_WEIGHT: u32 = BUNDLE_MAX_SPILL_WEIGHT;
pub const MINIMAL_BUNDLE_SPILL_WEIGHT: u32 = BUNDLE_MAX_SPILL_WEIGHT - 1;
pub const BUNDLE_MAX_NORMAL_SPILL_WEIGHT: u32 = BUNDLE_MAX_SPILL_WEIGHT - 2;
@@ -197,13 +205,15 @@ impl LiveBundle {
spill_weight: u32,
minimal: bool,
fixed: bool,
fixed_def: bool,
stack: bool,
) {
debug_assert!(spill_weight <= BUNDLE_MAX_SPILL_WEIGHT);
self.spill_weight_and_props = spill_weight
| (if minimal { 1 << 31 } else { 0 })
| (if fixed { 1 << 30 } else { 0 })
| (if stack { 1 << 29 } else { 0 });
| (if fixed_def { 1 << 29 } else { 0 })
| (if stack { 1 << 28 } else { 0 });
}
#[inline(always)]
@@ -217,23 +227,33 @@ impl LiveBundle {
}
#[inline(always)]
pub fn cached_stack(&self) -> bool {
pub fn cached_fixed_def(&self) -> bool {
self.spill_weight_and_props & (1 << 29) != 0
}
#[inline(always)]
pub fn cached_stack(&self) -> bool {
self.spill_weight_and_props & (1 << 28) != 0
}
#[inline(always)]
pub fn set_cached_fixed(&mut self) {
self.spill_weight_and_props |= 1 << 30;
}
#[inline(always)]
pub fn set_cached_stack(&mut self) {
pub fn set_cached_fixed_def(&mut self) {
self.spill_weight_and_props |= 1 << 29;
}
#[inline(always)]
pub fn set_cached_stack(&mut self) {
self.spill_weight_and_props |= 1 << 28;
}
#[inline(always)]
pub fn cached_spill_weight(&self) -> u32 {
self.spill_weight_and_props & ((1 << 29) - 1)
self.spill_weight_and_props & ((1 << 28) - 1)
}
}