mirror of
https://github.com/llvm/llvm-project.git
synced 2026-10-26 13:53:31 +08:00
Fixes https://github.com/llvm/llvm-project/issues/204224 Guard against a possibly wrong interference result for a discontiguous stack slot interval by using the entire range. A spilled stack slot can have a discontiguous live interval, e.g. a single value live across several disjoint segments: [a, b) [c, d) ........gap........ [e, f) with gaps where the slot is dead. The interference check previously only considered the covered segments, so it could pick a PhysReg that is free within them but busy inside a gap. Unspilling replaces the slot with a vreg whose recomputed interval is continuous over [a, f) (it fills the gaps), so assigning that PhysReg could overlap the value live in the gap and trip the "Overlapping insert" assertion in LiveRegMatrix::assign. Checking interference over the whole [a, f) hull avoids this. Rather than constructing a temporary "hull" LiveInterval and calling the LiveInterval-based checkInterference overload -- which risks a query-cache identity hazard when that temporary interval is stack-allocated and reused across the AllocOrder loop (see the FIXME comment in LiveRegMatrix::checkInterference(SlotIndex, SlotIndex, MCRegister)) -- this patch switches the unspilling code to call the slot-index-based checkInterference(SlotIndex, SlotIndex, MCRegister) overload directly with the hull's [beginIndex, endIndex). Doing so exposed a real bug in that overload: LiveRegMatrix.h documents that returning false means "PhysReg is free at [Start, End)", but the implementation only checked the matrix of already-assigned virtual registers. It never checked fixed register-unit interference (e.g. PhysReg is defined directly by some instruction in [Start, End)) or regmask interference (e.g. PhysReg is clobbered by a call in [Start, End)) -- both of which the LiveInterval-based overload does check. This patch adds both checks to the slot-index-based overload, in the same priority order (regmask -> regunit -> matrix) as the LiveInterval-based one, bringing the two implementations in line. (Note: checkInterferenceLanes(SlotIndex, SlotIndex, MCRegister) has the same matrix-only shape, but is out of scope for this patch -- it has a single caller with different semantics.) Prior to this patch, the only callers of the slot-index-based checkInterference were two call sites in InlineSpiller (hoistSpillInsideBB, hasPhysRegAvailable). Both check narrow windows by construction (a block's PHI/label/debug prologue, or a single instruction boundary), which is likely why missing regunit/regmask coverage there hasn't caused any existing LIT test to regress. Three new tests: - rewrite-vgpr-mfma-to-agpr-spill-discontiguous-interval.mir: adapted from #204224. Verifies we no longer crash when there is interference inside the gap between a discontiguous stack slot's live segments. - rewrite-vgpr-mfma-to-agpr-unspill-regunit-interference.mir: a stack slot value is live across a block of instructions that directly define every VGPR. Without the new regunit check, the slot is wrongly unspilled into a clobbered register, and the CHECK lines fail. - rewrite-vgpr-mfma-to-agpr-unspill-regmask-interference.mir: same scenario, but the value is live across a call whose regmask (amdgpu_allagprs, a synthetic mask that preserves only AGPRs, used here to reliably clobber every VGPR) clobbers the register. Without the new regmask check, the unspill proceeds, and either the CHECK lines fail or the machine verifier reports a use of an undefined physical register. Assisted-by: Cursor/Claude Opus
417 lines
15 KiB
C++
417 lines
15 KiB
C++
//===- LiveRegMatrix.cpp - Track register interference --------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines the LiveRegMatrix analysis pass.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/CodeGen/LiveRegMatrix.h"
|
|
#include "RegisterCoalescer.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/ADT/Statistic.h"
|
|
#include "llvm/CodeGen/LiveInterval.h"
|
|
#include "llvm/CodeGen/LiveIntervalUnion.h"
|
|
#include "llvm/CodeGen/LiveIntervals.h"
|
|
#include "llvm/CodeGen/MachineFunction.h"
|
|
#include "llvm/CodeGen/MachineOperand.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
|
#include "llvm/CodeGen/VirtRegMap.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/MC/LaneBitmask.h"
|
|
#include "llvm/MC/MCRegisterInfo.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "regalloc"
|
|
|
|
STATISTIC(NumAssigned , "Number of registers assigned");
|
|
STATISTIC(NumUnassigned , "Number of registers unassigned");
|
|
|
|
char LiveRegMatrixWrapperLegacy::ID = 0;
|
|
INITIALIZE_PASS_BEGIN(LiveRegMatrixWrapperLegacy, "liveregmatrix",
|
|
"Live Register Matrix", false, false)
|
|
INITIALIZE_PASS_DEPENDENCY(LiveIntervalsWrapperPass)
|
|
INITIALIZE_PASS_DEPENDENCY(VirtRegMapWrapperLegacy)
|
|
INITIALIZE_PASS_END(LiveRegMatrixWrapperLegacy, "liveregmatrix",
|
|
"Live Register Matrix", false, true)
|
|
|
|
void LiveRegMatrixWrapperLegacy::getAnalysisUsage(AnalysisUsage &AU) const {
|
|
AU.setPreservesAll();
|
|
AU.addRequiredTransitive<LiveIntervalsWrapperPass>();
|
|
AU.addRequiredTransitive<VirtRegMapWrapperLegacy>();
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
}
|
|
|
|
bool LiveRegMatrixWrapperLegacy::runOnMachineFunction(MachineFunction &MF) {
|
|
auto &LIS = getAnalysis<LiveIntervalsWrapperPass>().getLIS();
|
|
auto &VRM = getAnalysis<VirtRegMapWrapperLegacy>().getVRM();
|
|
LRM.init(MF, LIS, VRM);
|
|
return false;
|
|
}
|
|
|
|
void LiveRegMatrix::init(MachineFunction &MF, LiveIntervals &pLIS,
|
|
VirtRegMap &pVRM) {
|
|
TRI = MF.getSubtarget().getRegisterInfo();
|
|
LIS = &pLIS;
|
|
VRM = &pVRM;
|
|
|
|
unsigned NumRegUnits = TRI->getNumRegUnits();
|
|
if (NumRegUnits != Matrix.size())
|
|
Queries.reset(new LiveIntervalUnion::Query[NumRegUnits]);
|
|
Matrix.init(*LIUAlloc, NumRegUnits);
|
|
|
|
// Make sure no stale queries get reused.
|
|
invalidateVirtRegs();
|
|
}
|
|
|
|
void LiveRegMatrixWrapperLegacy::releaseMemory() { LRM.releaseMemory(); }
|
|
|
|
void LiveRegMatrix::releaseMemory() {
|
|
for (unsigned i = 0, e = Matrix.size(); i != e; ++i) {
|
|
Matrix[static_cast<MCRegUnit>(i)].clear();
|
|
// No need to clear Queries here, since LiveIntervalUnion::Query doesn't
|
|
// have anything important to clear and LiveRegMatrix's runOnFunction()
|
|
// does a std::unique_ptr::reset anyways.
|
|
}
|
|
}
|
|
|
|
template <typename Callable>
|
|
static bool foreachUnit(const TargetRegisterInfo *TRI,
|
|
const LiveInterval &VRegInterval, MCRegister PhysReg,
|
|
Callable Func) {
|
|
if (VRegInterval.hasSubRanges()) {
|
|
for (MCRegUnitMaskIterator Units(PhysReg, TRI); Units.isValid(); ++Units) {
|
|
MCRegUnit Unit = (*Units).first;
|
|
LaneBitmask Mask = (*Units).second;
|
|
for (const LiveInterval::SubRange &S : VRegInterval.subranges()) {
|
|
if ((S.LaneMask & Mask).any()) {
|
|
if (Func(Unit, S))
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
if (Func(Unit, VRegInterval))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void LiveRegMatrix::assign(const LiveInterval &VirtReg, MCRegister PhysReg) {
|
|
LLVM_DEBUG(dbgs() << "assigning " << printReg(VirtReg.reg(), TRI) << " to "
|
|
<< printReg(PhysReg, TRI) << ':');
|
|
assert(!VRM->hasPhys(VirtReg.reg()) && "Duplicate VirtReg assignment");
|
|
VRM->assignVirt2Phys(VirtReg.reg(), PhysReg);
|
|
|
|
foreachUnit(
|
|
TRI, VirtReg, PhysReg, [&](MCRegUnit Unit, const LiveRange &Range) {
|
|
LLVM_DEBUG(dbgs() << ' ' << printRegUnit(Unit, TRI) << ' ' << Range);
|
|
Matrix[Unit].unify(VirtReg, Range);
|
|
return false;
|
|
});
|
|
|
|
++NumAssigned;
|
|
LLVM_DEBUG(dbgs() << '\n');
|
|
}
|
|
|
|
void LiveRegMatrix::unassign(const LiveInterval &VirtReg,
|
|
bool ClearAllReferencingSegments) {
|
|
Register PhysReg = VRM->getPhys(VirtReg.reg());
|
|
LLVM_DEBUG(dbgs() << "unassigning " << printReg(VirtReg.reg(), TRI)
|
|
<< " from " << printReg(PhysReg, TRI) << ':');
|
|
VRM->clearVirt(VirtReg.reg());
|
|
|
|
if (!ClearAllReferencingSegments) {
|
|
foreachUnit(TRI, VirtReg, PhysReg,
|
|
[&](MCRegUnit Unit, const LiveRange &Range) {
|
|
LLVM_DEBUG(dbgs() << ' ' << printRegUnit(Unit, TRI));
|
|
Matrix[Unit].extract(VirtReg, Range);
|
|
return false;
|
|
});
|
|
} else {
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
Matrix[Unit].clearAllSegmentsReferencing(VirtReg);
|
|
}
|
|
}
|
|
|
|
++NumUnassigned;
|
|
LLVM_DEBUG(dbgs() << '\n');
|
|
}
|
|
|
|
bool LiveRegMatrix::isPhysRegUsed(MCRegister PhysReg) const {
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
if (!Matrix[Unit].empty())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LiveRegMatrix::checkRegMaskInterference(const LiveInterval &VirtReg,
|
|
MCRegister PhysReg) {
|
|
// Check if the cached information is valid.
|
|
// The same BitVector can be reused for all PhysRegs.
|
|
// We could cache multiple VirtRegs if it becomes necessary.
|
|
if (RegMaskVirtReg != VirtReg.reg() || RegMaskTag != UserTag) {
|
|
RegMaskVirtReg = VirtReg.reg();
|
|
RegMaskTag = UserTag;
|
|
RegMaskUsable.clear();
|
|
LIS->checkRegMaskInterference(VirtReg, RegMaskUsable);
|
|
}
|
|
|
|
// The BitVector is indexed by PhysReg, not register unit.
|
|
// Regmask interference is more fine grained than regunits.
|
|
// For example, a Win64 call can clobber %ymm8 yet preserve %xmm8.
|
|
return !RegMaskUsable.empty() &&
|
|
(!PhysReg || !RegMaskUsable.test(PhysReg.id()));
|
|
}
|
|
|
|
bool LiveRegMatrix::checkRegUnitInterference(const LiveInterval &VirtReg,
|
|
MCRegister PhysReg) {
|
|
if (VirtReg.empty())
|
|
return false;
|
|
CoalescerPair CP(VirtReg.reg(), PhysReg, *TRI);
|
|
|
|
bool Result = foreachUnit(
|
|
TRI, VirtReg, PhysReg, [&](MCRegUnit Unit, const LiveRange &Range) {
|
|
const LiveRange &UnitRange = LIS->getRegUnit(Unit);
|
|
return Range.overlaps(UnitRange, CP, *LIS->getSlotIndexes());
|
|
});
|
|
return Result;
|
|
}
|
|
|
|
bool LiveRegMatrix::checkRegMaskInterference(SlotIndex Start, SlotIndex End,
|
|
MCRegister PhysReg) {
|
|
ArrayRef<SlotIndex> Slots = LIS->getRegMaskSlots();
|
|
ArrayRef<const uint32_t *> Bits = LIS->getRegMaskBits();
|
|
|
|
// Find the first regmask slot that is not before Start.
|
|
auto SlotI = llvm::lower_bound(Slots, Start);
|
|
for (; SlotI != Slots.end() && *SlotI < End; ++SlotI) {
|
|
if (MachineOperand::clobbersPhysReg(Bits[SlotI - Slots.begin()], PhysReg))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool LiveRegMatrix::checkRegUnitInterference(SlotIndex Start, SlotIndex End,
|
|
MCRegister PhysReg) {
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
if (LIS->getRegUnit(Unit).overlaps(Start, End))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LiveIntervalUnion::Query &LiveRegMatrix::query(const LiveRange &LR,
|
|
MCRegUnit RegUnit) {
|
|
LiveIntervalUnion::Query &Q = Queries[static_cast<unsigned>(RegUnit)];
|
|
Q.init(UserTag, LR, Matrix[RegUnit]);
|
|
return Q;
|
|
}
|
|
|
|
LiveRegMatrix::InterferenceKind
|
|
LiveRegMatrix::checkInterference(const LiveInterval &VirtReg,
|
|
MCRegister PhysReg) {
|
|
if (VirtReg.empty())
|
|
return IK_Free;
|
|
|
|
// Regmask interference is the fastest check.
|
|
if (checkRegMaskInterference(VirtReg, PhysReg))
|
|
return IK_RegMask;
|
|
|
|
// Check for fixed interference.
|
|
if (checkRegUnitInterference(VirtReg, PhysReg))
|
|
return IK_RegUnit;
|
|
|
|
// Check the matrix for virtual register interference.
|
|
bool Interference = foreachUnit(TRI, VirtReg, PhysReg,
|
|
[&](MCRegUnit Unit, const LiveRange &LR) {
|
|
return query(LR, Unit).checkInterference();
|
|
});
|
|
if (Interference)
|
|
return IK_VirtReg;
|
|
|
|
return IK_Free;
|
|
}
|
|
|
|
bool LiveRegMatrix::checkInterference(SlotIndex Start, SlotIndex End,
|
|
MCRegister PhysReg) {
|
|
// Regmask interference is the fastest check.
|
|
if (checkRegMaskInterference(Start, End, PhysReg))
|
|
return true;
|
|
|
|
// Check for fixed interference.
|
|
if (checkRegUnitInterference(Start, End, PhysReg))
|
|
return true;
|
|
|
|
// Construct artificial live range containing only one segment [Start, End).
|
|
VNInfo valno(0, Start);
|
|
LiveRange::Segment Seg(Start, End, &valno);
|
|
LiveRange LR;
|
|
LR.addSegment(Seg);
|
|
|
|
// Check the matrix for virtual register interference with that segment.
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
// LR is stack-allocated. LiveRegMatrix caches queries by a key that
|
|
// includes the address of the live range. If (for the same reg unit) this
|
|
// checkInterference overload is called twice, without any other query()
|
|
// calls in between (on heap-allocated LiveRanges) - which would invalidate
|
|
// the cached query - the LR address seen the second time may well be the
|
|
// same as that seen the first time, while the Start/End/valno may not - yet
|
|
// the same cached result would be fetched. To avoid that, we don't cache
|
|
// this query.
|
|
//
|
|
// FIXME: the usability of the Query API needs to be improved to avoid
|
|
// subtle bugs due to query identity. Avoiding caching, for example, would
|
|
// greatly simplify things.
|
|
LiveIntervalUnion::Query Q;
|
|
Q.reset(UserTag, LR, Matrix[Unit]);
|
|
if (Q.checkInterference())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
LaneBitmask LiveRegMatrix::checkInterferenceLanes(SlotIndex Start,
|
|
SlotIndex End,
|
|
MCRegister PhysReg) {
|
|
// Construct artificial live range containing only one segment [Start, End).
|
|
VNInfo valno(0, Start);
|
|
LiveRange::Segment Seg(Start, End, &valno);
|
|
LiveRange LR;
|
|
LR.addSegment(Seg);
|
|
|
|
LaneBitmask InterferingLanes;
|
|
|
|
// Check for interference with that segment
|
|
for (MCRegUnitMaskIterator MCRU(PhysReg, TRI); MCRU.isValid(); ++MCRU) {
|
|
auto [Unit, Lanes] = *MCRU;
|
|
// LR is stack-allocated. LiveRegMatrix caches queries by a key that
|
|
// includes the address of the live range. If (for the same reg unit) this
|
|
// checkInterference overload is called twice, without any other query()
|
|
// calls in between (on heap-allocated LiveRanges) - which would invalidate
|
|
// the cached query - the LR address seen the second time may well be the
|
|
// same as that seen the first time, while the Start/End/valno may not - yet
|
|
// the same cached result would be fetched. To avoid that, we don't cache
|
|
// this query.
|
|
//
|
|
// FIXME: the usability of the Query API needs to be improved to avoid
|
|
// subtle bugs due to query identity. Avoiding caching, for example, would
|
|
// greatly simplify things.
|
|
LiveIntervalUnion::Query Q;
|
|
Q.reset(UserTag, LR, Matrix[Unit]);
|
|
if (Q.checkInterference())
|
|
InterferingLanes |= Lanes;
|
|
}
|
|
|
|
return InterferingLanes;
|
|
}
|
|
|
|
Register LiveRegMatrix::getOneVReg(unsigned PhysReg) const {
|
|
const LiveInterval *VRegInterval = nullptr;
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
if ((VRegInterval = Matrix[Unit].getOneVReg()))
|
|
return VRegInterval->reg();
|
|
}
|
|
|
|
return MCRegister::NoRegister;
|
|
}
|
|
|
|
#ifndef NDEBUG
|
|
bool LiveRegMatrix::isValid() const {
|
|
// Build set of all valid LiveInterval pointers from LiveIntervals.
|
|
DenseSet<const LiveInterval *> ValidIntervals;
|
|
for (unsigned RegIdx = 0, NumRegs = VRM->getRegInfo().getNumVirtRegs();
|
|
RegIdx < NumRegs; ++RegIdx) {
|
|
Register VReg = Register::index2VirtReg(RegIdx);
|
|
// Only track assigned registers since unassigned ones won't be in Matrix
|
|
if (VRM->hasPhys(VReg) && LIS->hasInterval(VReg))
|
|
ValidIntervals.insert(&LIS->getInterval(VReg));
|
|
}
|
|
|
|
// Now scan all LiveIntervalUnions in the matrix and verify each pointer
|
|
unsigned NumDanglingPointers = 0;
|
|
for (unsigned I = 0, Size = Matrix.size(); I < Size; ++I) {
|
|
MCRegUnit Unit = static_cast<MCRegUnit>(I);
|
|
for (const LiveInterval *LI : Matrix[Unit]) {
|
|
if (!ValidIntervals.contains(LI)) {
|
|
++NumDanglingPointers;
|
|
dbgs() << "ERROR: LiveInterval pointer is not found in LiveIntervals:\n"
|
|
<< " Register Unit: " << printRegUnit(Unit, TRI) << '\n'
|
|
<< " LiveInterval pointer: " << LI << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reverse check: every VRM-assigned vreg with a non-empty live interval
|
|
// must have its segments present in the Matrix for its assigned phys reg.
|
|
unsigned NumMissing = 0;
|
|
for (unsigned RegIdx = 0, NumRegs = VRM->getRegInfo().getNumVirtRegs();
|
|
RegIdx < NumRegs; ++RegIdx) {
|
|
Register VReg = Register::index2VirtReg(RegIdx);
|
|
if (!VRM->hasPhys(VReg) || !LIS->hasInterval(VReg))
|
|
continue;
|
|
const LiveInterval &LI = LIS->getInterval(VReg);
|
|
if (LI.empty())
|
|
continue;
|
|
MCRegister PhysReg = VRM->getPhys(VReg);
|
|
// Check that the first segment of LI is present in the LiveUnion for
|
|
// at least one reg unit of PhysReg.
|
|
SlotIndex FirstStart = LI.beginIndex();
|
|
bool Found = false;
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
auto It = Matrix[Unit].find(FirstStart);
|
|
if (It.valid() && It.start() == FirstStart && It.value() == &LI) {
|
|
Found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!Found) {
|
|
++NumMissing;
|
|
dbgs() << "ERROR: VirtReg " << printReg(VReg, TRI) << " assigned to "
|
|
<< printReg(PhysReg, TRI)
|
|
<< " in VirtRegMap but not found in LiveRegMatrix\n";
|
|
dbgs() << " LiveInterval: " << LI << "\n";
|
|
dbgs() << " FirstStart: " << FirstStart << "\n";
|
|
for (MCRegUnit Unit : TRI->regunits(PhysReg)) {
|
|
dbgs() << " RegUnit " << printRegUnit(Unit, TRI) << " segments: ";
|
|
auto It = Matrix[Unit].find(FirstStart);
|
|
if (It.valid())
|
|
dbgs() << "[" << It.start() << "," << It.stop() << ") -> "
|
|
<< printReg(It.value()->reg(), TRI);
|
|
else
|
|
dbgs() << "(none found)";
|
|
dbgs() << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
return NumDanglingPointers == 0 && NumMissing == 0;
|
|
}
|
|
#endif
|
|
|
|
AnalysisKey LiveRegMatrixAnalysis::Key;
|
|
|
|
LiveRegMatrix LiveRegMatrixAnalysis::run(MachineFunction &MF,
|
|
MachineFunctionAnalysisManager &MFAM) {
|
|
auto &LIS = MFAM.getResult<LiveIntervalsAnalysis>(MF);
|
|
auto &VRM = MFAM.getResult<VirtRegMapAnalysis>(MF);
|
|
LiveRegMatrix LRM;
|
|
LRM.init(MF, LIS, VRM);
|
|
return LRM;
|
|
}
|