Skip to content

Commit

Permalink
[DebugInfo] Terminate all location-lists at end of block
Browse files Browse the repository at this point in the history
This commit reapplies r359426 (which was reverted in r360301 due to
performance problems) and rolls in D61940 to address the performance problem.
I've combined the two to avoid creating a span of slow-performance, and to
ease reverting if more problems crop up.

The summary of D61940: This patch removes the "ChangingRegs" facility in
DbgEntityHistoryCalculator, as its overapproximate nature can produce incorrect
variable locations. An unchanging register doesn't mean a variable doesn't
change its location.

The patch kills off everything that calculates the ChangingRegs vector.
Previously ChangingRegs spotted epilogues and marked registers as unchanging if
they weren't modified outside the epilogue, increasing the chance that we can
emit a single-location variable record. Without this feature,
debug-loc-offset.mir and pr19307.mir become temporarily XFAIL. They'll be
re-enabled by D62314, using the FrameDestroy flag to identify epilogues, I've
split this into two steps as FrameDestroy isn't necessarily supported by all
backends.

The logic for terminating variable locations at the end of a basic block now
becomes much more enjoyably simple: we just terminate them all.

Other test changes: inlined-argument.ll becomes XFAIL, but for a longer term.
The current algorithm for detecting that a variable has a single-location
doesn't work in this scenario (inlined function in multiple blocks), only other
bugs were making this test work. fission-ranges.ll gets slightly refreshed too,
as the location of "p" is now correctly determined to be a single location.

Differential Revision: https://reviews.llvm.org/D61940

llvm-svn: 362951
  • Loading branch information
jmorse committed Jun 10, 2019
1 parent 3dea527 commit bcff417
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 141 deletions.
168 changes: 63 additions & 105 deletions llvm/lib/CodeGen/AsmPrinter/DbgEntityHistoryCalculator.cpp
Expand Up @@ -224,111 +224,17 @@ static void clobberRegisterUses(RegDescribedVarsMap &RegVars, unsigned RegNo,
clobberRegisterUses(RegVars, I, HistMap, LiveEntries, ClobberingInstr);
}

// Returns the first instruction in @MBB which corresponds to
// the function epilogue, or nullptr if @MBB doesn't contain an epilogue.
static const MachineInstr *getFirstEpilogueInst(const MachineBasicBlock &MBB) {
auto LastMI = MBB.getLastNonDebugInstr();
if (LastMI == MBB.end() || !LastMI->isReturn())
return nullptr;
// Assume that epilogue starts with instruction having the same debug location
// as the return instruction.
DebugLoc LastLoc = LastMI->getDebugLoc();
auto Res = LastMI;
for (MachineBasicBlock::const_reverse_iterator I = LastMI.getReverse(),
E = MBB.rend();
I != E; ++I) {
if (I->getDebugLoc() != LastLoc)
return &*Res;
Res = &*I;
}
// If all instructions have the same debug location, assume whole MBB is
// an epilogue.
return &*MBB.begin();
}

// Collect registers that are modified in the function body (their
// contents is changed outside of the prologue and epilogue).
static void collectChangingRegs(const MachineFunction *MF,
const TargetRegisterInfo *TRI,
BitVector &Regs) {
for (const auto &MBB : *MF) {
auto FirstEpilogueInst = getFirstEpilogueInst(MBB);

for (const auto &MI : MBB) {
// Avoid looking at prologue or epilogue instructions.
if (&MI == FirstEpilogueInst)
break;
if (MI.getFlag(MachineInstr::FrameSetup))
continue;

// Look for register defs and register masks. Register masks are
// typically on calls and they clobber everything not in the mask.
for (const MachineOperand &MO : MI.operands()) {
// Skip virtual registers since they are handled by the parent.
if (MO.isReg() && MO.isDef() && MO.getReg() &&
!TRI->isVirtualRegister(MO.getReg())) {
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid();
++AI)
Regs.set(*AI);
} else if (MO.isRegMask()) {
Regs.setBitsNotInMask(MO.getRegMask());
}
}
}
}
}

void llvm::calculateDbgEntityHistory(const MachineFunction *MF,
const TargetRegisterInfo *TRI,
DbgValueHistoryMap &DbgValues,
DbgLabelInstrMap &DbgLabels) {
BitVector ChangingRegs(TRI->getNumRegs());
collectChangingRegs(MF, TRI, ChangingRegs);

const TargetLowering *TLI = MF->getSubtarget().getTargetLowering();
unsigned SP = TLI->getStackPointerRegisterToSaveRestore();
unsigned FrameReg = TRI->getFrameRegister(*MF);
RegDescribedVarsMap RegVars;
DbgValueEntriesMap LiveEntries;
for (const auto &MBB : *MF) {
for (const auto &MI : MBB) {
if (!MI.isDebugInstr()) {
// Not a DBG_VALUE instruction. It may clobber registers which describe
// some variables.
for (const MachineOperand &MO : MI.operands()) {
if (MO.isReg() && MO.isDef() && MO.getReg()) {
// Ignore call instructions that claim to clobber SP. The AArch64
// backend does this for aggregate function arguments.
if (MI.isCall() && MO.getReg() == SP)
continue;
// If this is a virtual register, only clobber it since it doesn't
// have aliases.
if (TRI->isVirtualRegister(MO.getReg()))
clobberRegisterUses(RegVars, MO.getReg(), DbgValues, LiveEntries,
MI);
// If this is a register def operand, it may end a debug value
// range.
else {
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid();
++AI)
if (ChangingRegs.test(*AI))
clobberRegisterUses(RegVars, *AI, DbgValues, LiveEntries, MI);
}
} else if (MO.isRegMask()) {
// If this is a register mask operand, clobber all debug values in
// non-CSRs.
for (unsigned I : ChangingRegs.set_bits()) {
// Don't consider SP to be clobbered by register masks.
if (unsigned(I) != SP && TRI->isPhysicalRegister(I) &&
MO.clobbersPhysReg(I)) {
clobberRegisterUses(RegVars, I, DbgValues, LiveEntries, MI);
}
}
}
}
continue;
}

if (MI.isDebugValue()) {
assert(MI.getNumOperands() > 1 && "Invalid DBG_VALUE instruction!");
// Use the base variable (without any DW_OP_piece expressions)
Expand All @@ -351,20 +257,72 @@ void llvm::calculateDbgEntityHistory(const MachineFunction *MF,
InlinedEntity L(RawLabel, MI.getDebugLoc()->getInlinedAt());
DbgLabels.addInstr(L, MI);
}
}

// Make sure locations for register-described variables are valid only
// until the end of the basic block (unless it's the last basic block, in
// which case let their liveness run off to the end of the function).
if (MI.isDebugInstr())
continue;

// Not a DBG_VALUE instruction. It may clobber registers which describe
// some variables.
for (const MachineOperand &MO : MI.operands()) {
if (MO.isReg() && MO.isDef() && MO.getReg()) {
// Ignore call instructions that claim to clobber SP. The AArch64
// backend does this for aggregate function arguments.
if (MI.isCall() && MO.getReg() == SP)
continue;
// If this is a virtual register, only clobber it since it doesn't
// have aliases.
if (TRI->isVirtualRegister(MO.getReg()))
clobberRegisterUses(RegVars, MO.getReg(), DbgValues, LiveEntries,
MI);
// If this is a register def operand, it may end a debug value
// range. Ignore defs of the frame register in the prologue.
else if (MO.getReg() != FrameReg ||
!MI.getFlag(MachineInstr::FrameSetup)) {
for (MCRegAliasIterator AI(MO.getReg(), TRI, true); AI.isValid();
++AI)
clobberRegisterUses(RegVars, *AI, DbgValues, LiveEntries, MI);
}
} else if (MO.isRegMask()) {
// If this is a register mask operand, clobber all debug values in
// non-CSRs.
SmallVector<unsigned, 32> RegsToClobber;
// Don't consider SP to be clobbered by register masks.
for (auto It : RegVars) {
unsigned int Reg = It.first;
if (Reg != SP && TRI->isPhysicalRegister(Reg) &&
MO.clobbersPhysReg(Reg))
RegsToClobber.push_back(Reg);
}

for (unsigned Reg : RegsToClobber) {
clobberRegisterUses(RegVars, Reg, DbgValues, LiveEntries, MI);
}
}
} // End MO loop.
} // End instr loop.

// Make sure locations for all variables are valid only until the end of
// the basic block (unless it's the last basic block, in which case let
// their liveness run off to the end of the function).
if (!MBB.empty() && &MBB != &MF->back()) {
for (auto I = RegVars.begin(), E = RegVars.end(); I != E;) {
auto CurElem = I++; // CurElem can be erased below.
if (TRI->isVirtualRegister(CurElem->first) ||
ChangingRegs.test(CurElem->first) ||
CurElem->first == FrameReg)
clobberRegisterUses(RegVars, CurElem, DbgValues, LiveEntries,
MBB.back());
// Iterate over all variables that have open debug values.
for (auto &Pair : LiveEntries) {
if (Pair.second.empty())
continue;

// Create a clobbering entry.
EntryIndex ClobIdx = DbgValues.startClobber(Pair.first, MBB.back());

// End all entries.
for (EntryIndex Idx : Pair.second) {
DbgValueHistoryMap::Entry &Ent = DbgValues.getEntry(Pair.first, Idx);
assert(Ent.isDbgValue() && !Ent.isClosed());
Ent.endEntry(ClobIdx);
}
}

LiveEntries.clear();
RegVars.clear();
}
}
}
Expand Down
62 changes: 50 additions & 12 deletions llvm/lib/CodeGen/LiveDebugValues.cpp
Expand Up @@ -143,7 +143,8 @@ class LiveDebugValues : public MachineFunctionPass {
enum VarLocKind {
InvalidKind = 0,
RegisterKind,
SpillLocKind
SpillLocKind,
ImmediateKind
} Kind = InvalidKind;

/// The value location. Stored separately to avoid repeatedly
Expand All @@ -152,6 +153,9 @@ class LiveDebugValues : public MachineFunctionPass {
uint64_t RegNo;
SpillLoc SpillLocation;
uint64_t Hash;
int64_t Immediate;
const ConstantFP *FPImm;
const ConstantInt *CImm;
} Loc;

VarLoc(const MachineInstr &MI, LexicalScopes &LS)
Expand All @@ -164,6 +168,15 @@ class LiveDebugValues : public MachineFunctionPass {
if (int RegNo = isDbgValueDescribedByReg(MI)) {
Kind = RegisterKind;
Loc.RegNo = RegNo;
} else if (MI.getOperand(0).isImm()) {
Kind = ImmediateKind;
Loc.Immediate = MI.getOperand(0).getImm();
} else if (MI.getOperand(0).isFPImm()) {
Kind = ImmediateKind;
Loc.FPImm = MI.getOperand(0).getFPImm();
} else if (MI.getOperand(0).isCImm()) {
Kind = ImmediateKind;
Loc.CImm = MI.getOperand(0).getCImm();
}
}

Expand All @@ -178,6 +191,9 @@ class LiveDebugValues : public MachineFunctionPass {
Loc.SpillLocation = {SpillBase, SpillOffset};
}

// Is the Loc field a constant or constant object?
bool isConstant() const { return Kind == ImmediateKind; }

/// If this variable is described by a register, return it,
/// otherwise return 0.
unsigned isDescribedByReg() const {
Expand All @@ -195,7 +211,8 @@ class LiveDebugValues : public MachineFunctionPass {
#endif

bool operator==(const VarLoc &Other) const {
return Var == Other.Var && Loc.Hash == Other.Loc.Hash;
return Kind == Other.Kind && Var == Other.Var &&
Loc.Hash == Other.Loc.Hash;
}

/// This operator guarantees that VarLocs are sorted by Variable first.
Expand Down Expand Up @@ -409,11 +426,23 @@ void LiveDebugValues::transferDebugValue(const MachineInstr &MI,
OpenRanges.erase(V);

// Add the VarLoc to OpenRanges from this DBG_VALUE.
// TODO: Currently handles DBG_VALUE which has only reg as location.
if (isDbgValueDescribedByReg(MI)) {
unsigned ID;
if (isDbgValueDescribedByReg(MI) || MI.getOperand(0).isImm() ||
MI.getOperand(0).isFPImm() || MI.getOperand(0).isCImm()) {
// Use normal VarLoc constructor for registers and immediates.
VarLoc VL(MI, LS);
unsigned ID = VarLocIDs.insert(VL);
ID = VarLocIDs.insert(VL);
OpenRanges.insert(ID, VL.Var);
} else if (MI.hasOneMemOperand()) {
// It's a stack spill -- fetch spill base and offset.
VarLoc::SpillLoc SpillLocation = extractSpillBaseRegAndOffset(MI);
VarLoc VL(MI, SpillLocation.SpillBase, SpillLocation.SpillOffset, LS);
ID = VarLocIDs.insert(VL);
OpenRanges.insert(ID, VL.Var);
} else {
// This must be an undefined location. We should leave OpenRanges closed.
assert(MI.getOperand(0).isReg() && MI.getOperand(0).getReg() == 0 &&
"Unexpected non-undef DBG_VALUE encountered");
}
}

Expand Down Expand Up @@ -826,13 +855,22 @@ bool LiveDebugValues::join(
// a new DBG_VALUE. process() will end this range however appropriate.
const VarLoc &DiffIt = VarLocIDs[ID];
const MachineInstr *DebugInstr = &DiffIt.MI;
MachineInstr *MI = BuildMI(
MBB, MBB.instr_begin(), DebugInstr->getDebugLoc(),
DebugInstr->getDesc(), DebugInstr->isIndirectDebugValue(),
DebugInstr->getOperand(0).getReg(), DebugInstr->getDebugVariable(),
DebugInstr->getDebugExpression());
if (DebugInstr->isIndirectDebugValue())
MI->getOperand(1).setImm(DebugInstr->getOperand(1).getImm());
MachineInstr *MI = nullptr;
if (DiffIt.isConstant()) {
MachineOperand MO(DebugInstr->getOperand(0));
MI = BuildMI(MBB, MBB.instr_begin(), DebugInstr->getDebugLoc(),
DebugInstr->getDesc(), false, MO,
DebugInstr->getDebugVariable(),
DebugInstr->getDebugExpression());
} else {
MI = BuildMI(MBB, MBB.instr_begin(), DebugInstr->getDebugLoc(),
DebugInstr->getDesc(), DebugInstr->isIndirectDebugValue(),
DebugInstr->getOperand(0).getReg(),
DebugInstr->getDebugVariable(),
DebugInstr->getDebugExpression());
if (DebugInstr->isIndirectDebugValue())
MI->getOperand(1).setImm(DebugInstr->getOperand(1).getImm());
}
LLVM_DEBUG(dbgs() << "Inserted: "; MI->dump(););
ILS.set(ID);
++NumInserted;
Expand Down
9 changes: 9 additions & 0 deletions llvm/test/DebugInfo/AArch64/inlined-argument.ll
Expand Up @@ -4,6 +4,15 @@
; CHECK-NEXT: DW_AT_location (DW_OP_reg1 W1)
; CHECK-NEXT: DW_AT_abstract_origin {{.*}}"resource"
;
; XFAIL: *
; This test now fails as it requires the single-location variable recognizer
; to spot that the inlined function goes out of scope before the 'find.exit'
; exit block. Previously, unchanging variable locations could be extended to
; the end of the function, often erronously, and that's why this test used to
; pass.
; A future algorithm _should_ be able to recognize that "resource"/!37 covers
; all blocks in its lexical scope.
;
; Generated from:
; typedef struct t *t_t;
; extern unsigned int enable;
Expand Down
12 changes: 7 additions & 5 deletions llvm/test/DebugInfo/AArch64/struct_by_value.ll
@@ -1,10 +1,12 @@
; A by-value struct is a register-indirect value (breg).
; RUN: llc %s -filetype=asm -o - | FileCheck %s
; RUN: llc %s -filetype=obj -o - | llvm-dwarfdump - | FileCheck %s
; REQUIRES: object-emission

; CHECK: Lsection_info:
; CHECK: DW_AT_location
; CHECK-NEXT: .byte 112
; 112 = 0x70 = DW_OP_breg0
; Test that the 'f' parameter is present, with a location, and that the
; expression for the location contains a DW_OP_breg
; CHECK: DW_TAG_formal_parameter
; CHECK-NEXT: DW_AT_location
; CHECK-NEXT: DW_OP_breg

; rdar://problem/13658587
;
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/DebugInfo/COFF/fpo-shrink-wrap.ll
Expand Up @@ -33,7 +33,7 @@
; ASM: popl %ebx
; ASM: [[EPILOGUE]]: # %return
; ASM: retl $8
; ASM: Ltmp11:
; ASM: Ltmp10:
; ASM: .cv_fpo_endproc

; Note how RvaStart advances 7 bytes to skip the shrink-wrapped portion.
Expand Down

0 comments on commit bcff417

Please sign in to comment.