Skip to content

Commit

Permalink
[DebugInfo] Handle DBG_VALUES with multiple variable location operand…
Browse files Browse the repository at this point in the history
…s in MIR

This patch adds handling for DBG_VALUE_LIST in the MIR-passes (after
finalize-isel), excluding the debug liveness passes and DWARF emission. This
most significantly affects MachineSink, which now needs to consider all used
registers of a debug value when sinking, but for most passes this change is
simply replacing getDebugOperand(0) with an iteration over all debug operands.

Differential Revision: https://reviews.llvm.org/D92578
  • Loading branch information
SLTozer committed Mar 10, 2021
1 parent 6a9a686 commit 1db137b
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 110 deletions.
10 changes: 10 additions & 0 deletions llvm/include/llvm/CodeGen/MachineInstr.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/PointerSumType.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/ilist.h"
#include "llvm/ADT/ilist_node.h"
#include "llvm/ADT/iterator_range.h"
Expand Down Expand Up @@ -502,6 +503,15 @@ class MachineInstr
return *(debug_operands().begin() + Index);
}

SmallSet<Register, 4> getUsedDebugRegs() const {
assert(isDebugValue() && "not a DBG_VALUE*");
SmallSet<Register, 4> UsedRegs;
for (auto MO : debug_operands())
if (MO.isReg() && MO.getReg())
UsedRegs.insert(MO.getReg());
return UsedRegs;
}

/// Returns whether this debug value has at least one debug operand with the
/// register \p Reg.
bool hasDebugOperandForReg(Register Reg) const {
Expand Down
9 changes: 7 additions & 2 deletions llvm/include/llvm/CodeGen/MachineInstrBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -489,11 +489,16 @@ MachineInstrBuilder BuildMI(MachineBasicBlock &BB,
/// Clone a DBG_VALUE whose value has been spilled to FrameIndex.
MachineInstr *buildDbgValueForSpill(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,
const MachineInstr &Orig, int FrameIndex);
const MachineInstr &Orig, int FrameIndex,
Register SpillReg);
MachineInstr *
buildDbgValueForSpill(MachineBasicBlock &BB, MachineBasicBlock::iterator I,
const MachineInstr &Orig, int FrameIndex,
SmallVectorImpl<const MachineOperand *> &SpilledOperands);

/// Update a DBG_VALUE whose value has been spilled to FrameIndex. Useful when
/// modifying an instruction in place while iterating over a basic block.
void updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex);
void updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex, Register Reg);

inline unsigned getDefRegState(bool B) {
return B ? RegState::Define : 0;
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/CodeGen/InlineSpiller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ void InlineSpiller::spillAroundUses(Register Reg) {
// Modify DBG_VALUE now that the value is in a spill slot.
MachineBasicBlock *MBB = MI->getParent();
LLVM_DEBUG(dbgs() << "Modifying debug info due to spill:\t" << *MI);
buildDbgValueForSpill(*MBB, MI, *MI, StackSlot);
buildDbgValueForSpill(*MBB, MI, *MI, StackSlot, Reg);
MBB->erase(MI);
continue;
}
Expand Down
50 changes: 39 additions & 11 deletions llvm/lib/CodeGen/MachineInstr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2196,9 +2196,9 @@ MachineInstrBuilder llvm::BuildMI(MachineBasicBlock &BB,

/// Compute the new DIExpression to use with a DBG_VALUE for a spill slot.
/// This prepends DW_OP_deref when spilling an indirect DBG_VALUE.
static const DIExpression *computeExprForSpill(const MachineInstr &MI,
Register SpillReg) {
assert(MI.hasDebugOperandForReg(SpillReg) && "Spill Reg is not used in MI.");
static const DIExpression *
computeExprForSpill(const MachineInstr &MI,
SmallVectorImpl<const MachineOperand *> &SpilledOperands) {
assert(MI.getDebugVariable()->isValidLocationForIntrinsic(MI.getDebugLoc()) &&
"Expected inlined-at fields to agree");

Expand All @@ -2211,19 +2211,26 @@ static const DIExpression *computeExprForSpill(const MachineInstr &MI,
// We will replace the spilled register with a frame index, so
// immediately deref all references to the spilled register.
std::array<uint64_t, 1> Ops{{dwarf::DW_OP_deref}};
for (const MachineOperand &Op : MI.getDebugOperandsForReg(SpillReg)) {
unsigned OpIdx = MI.getDebugOperandIndex(&Op);
for (const MachineOperand *Op : SpilledOperands) {
unsigned OpIdx = MI.getDebugOperandIndex(Op);
Expr = DIExpression::appendOpsToArg(Expr, Ops, OpIdx);
}
}
return Expr;
}
static const DIExpression *computeExprForSpill(const MachineInstr &MI,
Register SpillReg) {
assert(MI.hasDebugOperandForReg(SpillReg) && "Spill Reg is not used in MI.");
SmallVector<const MachineOperand *> SpillOperands;
for (const MachineOperand &Op : MI.getDebugOperandsForReg(SpillReg))
SpillOperands.push_back(&Op);
return computeExprForSpill(MI, SpillOperands);
}

MachineInstr *llvm::buildDbgValueForSpill(MachineBasicBlock &BB,
MachineBasicBlock::iterator I,
const MachineInstr &Orig,
int FrameIndex) {
Register SpillReg = Orig.getDebugOperand(0).getReg();
int FrameIndex, Register SpillReg) {
const DIExpression *Expr = computeExprForSpill(Orig, SpillReg);
MachineInstrBuilder NewMI =
BuildMI(BB, I, Orig.getDebugLoc(), Orig.getDesc());
Expand All @@ -2241,13 +2248,34 @@ MachineInstr *llvm::buildDbgValueForSpill(MachineBasicBlock &BB,
}
return NewMI;
}
MachineInstr *llvm::buildDbgValueForSpill(
MachineBasicBlock &BB, MachineBasicBlock::iterator I,
const MachineInstr &Orig, int FrameIndex,
SmallVectorImpl<const MachineOperand *> &SpilledOperands) {
const DIExpression *Expr = computeExprForSpill(Orig, SpilledOperands);
MachineInstrBuilder NewMI =
BuildMI(BB, I, Orig.getDebugLoc(), Orig.getDesc());
// Non-Variadic Operands: Location, Offset, Variable, Expression
// Variadic Operands: Variable, Expression, Locations...
if (Orig.isNonListDebugValue())
NewMI.addFrameIndex(FrameIndex).addImm(0U);
NewMI.addMetadata(Orig.getDebugVariable()).addMetadata(Expr);
if (Orig.isDebugValueList()) {
for (const MachineOperand &Op : Orig.debug_operands())
if (is_contained(SpilledOperands, &Op))
NewMI.addFrameIndex(FrameIndex);
else
NewMI.add(MachineOperand(Op));
}
return NewMI;
}

void llvm::updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex) {
Register SpillReg = Orig.getDebugOperand(0).getReg();
const DIExpression *Expr = computeExprForSpill(Orig, SpillReg);
void llvm::updateDbgValueForSpill(MachineInstr &Orig, int FrameIndex,
Register Reg) {
const DIExpression *Expr = computeExprForSpill(Orig, Reg);
if (Orig.isNonListDebugValue())
Orig.getDebugOffset().ChangeToImmediate(0U);
for (MachineOperand &Op : Orig.getDebugOperandsForReg(SpillReg))
for (MachineOperand &Op : Orig.getDebugOperandsForReg(Reg))
Op.ChangeToFrameIndex(FrameIndex);
Orig.getDebugExpressionOp().setMetadata(Expr);
}
Expand Down
125 changes: 82 additions & 43 deletions llvm/lib/CodeGen/MachineSink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//

#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallSet.h"
Expand Down Expand Up @@ -566,9 +567,10 @@ void MachineSinking::ProcessDbgInst(MachineInstr &MI) {
MI.getDebugLoc()->getInlinedAt());
bool SeenBefore = SeenDbgVars.contains(Var);

MachineOperand &MO = MI.getDebugOperand(0);
if (MO.isReg() && MO.getReg().isVirtual())
SeenDbgUsers[MO.getReg()].push_back(SeenDbgUser(&MI, SeenBefore));
for (MachineOperand &MO : MI.debug_operands()) {
if (MO.isReg() && MO.getReg().isVirtual())
SeenDbgUsers[MO.getReg()].push_back(SeenDbgUser(&MI, SeenBefore));
}

// Record the variable for any DBG_VALUE, to avoid re-ordering any of them.
SeenDbgVars.insert(Var);
Expand Down Expand Up @@ -1028,14 +1030,14 @@ static bool SinkingPreventsImplicitNullCheck(MachineInstr &MI,
/// leaving an 'undef' DBG_VALUE in the original location. Don't do this if
/// there's any subregister weirdness involved. Returns true if copy
/// propagation occurred.
static bool attemptDebugCopyProp(MachineInstr &SinkInst, MachineInstr &DbgMI) {
static bool attemptDebugCopyProp(MachineInstr &SinkInst, MachineInstr &DbgMI,
Register Reg) {
const MachineRegisterInfo &MRI = SinkInst.getMF()->getRegInfo();
const TargetInstrInfo &TII = *SinkInst.getMF()->getSubtarget().getInstrInfo();

// Copy DBG_VALUE operand and set the original to undef. We then check to
// see whether this is something that can be copy-forwarded. If it isn't,
// continue around the loop.
MachineOperand &DbgMO = DbgMI.getDebugOperand(0);

const MachineOperand *SrcMO = nullptr, *DstMO = nullptr;
auto CopyOperands = TII.isCopyInstr(SinkInst);
Expand All @@ -1048,36 +1050,41 @@ static bool attemptDebugCopyProp(MachineInstr &SinkInst, MachineInstr &DbgMI) {
bool PostRA = MRI.getNumVirtRegs() == 0;

// Trying to forward between physical and virtual registers is too hard.
if (DbgMO.getReg().isVirtual() != SrcMO->getReg().isVirtual())
if (Reg.isVirtual() != SrcMO->getReg().isVirtual())
return false;

// Only try virtual register copy-forwarding before regalloc, and physical
// register copy-forwarding after regalloc.
bool arePhysRegs = !DbgMO.getReg().isVirtual();
bool arePhysRegs = !Reg.isVirtual();
if (arePhysRegs != PostRA)
return false;

// Pre-regalloc, only forward if all subregisters agree (or there are no
// subregs at all). More analysis might recover some forwardable copies.
if (!PostRA && (DbgMO.getSubReg() != SrcMO->getSubReg() ||
DbgMO.getSubReg() != DstMO->getSubReg()))
return false;
if (!PostRA)
for (auto &DbgMO : DbgMI.getDebugOperandsForReg(Reg))
if (DbgMO.getSubReg() != SrcMO->getSubReg() ||
DbgMO.getSubReg() != DstMO->getSubReg())
return false;

// Post-regalloc, we may be sinking a DBG_VALUE of a sub or super-register
// of this copy. Only forward the copy if the DBG_VALUE operand exactly
// matches the copy destination.
if (PostRA && DbgMO.getReg() != DstMO->getReg())
if (PostRA && Reg != DstMO->getReg())
return false;

DbgMO.setReg(SrcMO->getReg());
DbgMO.setSubReg(SrcMO->getSubReg());
for (auto &DbgMO : DbgMI.getDebugOperandsForReg(Reg)) {
DbgMO.setReg(SrcMO->getReg());
DbgMO.setSubReg(SrcMO->getSubReg());
}
return true;
}

using MIRegs = std::pair<MachineInstr *, SmallVector<unsigned, 2>>;
/// Sink an instruction and its associated debug instructions.
static void performSink(MachineInstr &MI, MachineBasicBlock &SuccToSinkTo,
MachineBasicBlock::iterator InsertPos,
SmallVectorImpl<MachineInstr *> &DbgValuesToSink) {
SmallVectorImpl<MIRegs> &DbgValuesToSink) {

// If we cannot find a location to use (merge with), then we erase the debug
// location to prevent debug-info driven tools from potentially reporting
Expand All @@ -1097,11 +1104,21 @@ static void performSink(MachineInstr &MI, MachineBasicBlock &SuccToSinkTo,
// DBG_VALUE location as 'undef', indicating that any earlier variable
// location should be terminated as we've optimised away the value at this
// point.
for (MachineInstr *DbgMI : DbgValuesToSink) {
for (auto DbgValueToSink : DbgValuesToSink) {
MachineInstr *DbgMI = DbgValueToSink.first;
MachineInstr *NewDbgMI = DbgMI->getMF()->CloneMachineInstr(DbgMI);
SuccToSinkTo.insert(InsertPos, NewDbgMI);

if (!attemptDebugCopyProp(MI, *DbgMI))
bool PropagatedAllSunkOps = true;
for (unsigned Reg : DbgValueToSink.second) {
if (DbgMI->hasDebugOperandForReg(Reg)) {
if (!attemptDebugCopyProp(MI, *DbgMI, Reg)) {
PropagatedAllSunkOps = false;
break;
}
}
}
if (!PropagatedAllSunkOps)
DbgMI->setDebugValueUndef();
}
}
Expand Down Expand Up @@ -1384,7 +1401,7 @@ bool MachineSinking::SinkInstruction(MachineInstr &MI, bool &SawStore,
++InsertPos;

// Collect debug users of any vreg that this inst defines.
SmallVector<MachineInstr *, 4> DbgUsersToSink;
SmallVector<MIRegs, 4> DbgUsersToSink;
for (auto &MO : MI.operands()) {
if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual())
continue;
Expand All @@ -1398,10 +1415,11 @@ bool MachineSinking::SinkInstruction(MachineInstr &MI, bool &SawStore,
if (User.getInt()) {
// This DBG_VALUE would re-order assignments. If we can't copy-propagate
// it, it can't be recovered. Set it undef.
if (!attemptDebugCopyProp(MI, *DbgMI))
if (!attemptDebugCopyProp(MI, *DbgMI, MO.getReg()))
DbgMI->setDebugValueUndef();
} else {
DbgUsersToSink.push_back(DbgMI);
DbgUsersToSink.push_back(
{DbgMI, SmallVector<unsigned, 2>(1, MO.getReg())});
}
}
}
Expand Down Expand Up @@ -1436,10 +1454,12 @@ void MachineSinking::SalvageUnsunkDebugUsersOfCopy(
// be sunk. For the rest, if they are not dominated by the block we will sink
// MI into, propagate the copy source to them.
SmallVector<MachineInstr *, 4> DbgDefUsers;
SmallVector<Register, 4> DbgUseRegs;
const MachineRegisterInfo &MRI = MI.getMF()->getRegInfo();
for (auto &MO : MI.operands()) {
if (!MO.isReg() || !MO.isDef() || !MO.getReg().isVirtual())
continue;
DbgUseRegs.push_back(MO.getReg());
for (auto &User : MRI.use_instructions(MO.getReg())) {
if (!User.isDebugValue() || DT->dominates(TargetBlock, User.getParent()))
continue;
Expand All @@ -1448,17 +1468,21 @@ void MachineSinking::SalvageUnsunkDebugUsersOfCopy(
if (User.getParent() == MI.getParent())
continue;

assert(User.getDebugOperand(0).isReg() &&
"DBG_VALUE user of vreg, but non reg operand?");
assert(User.hasDebugOperandForReg(MO.getReg()) &&
"DBG_VALUE user of vreg, but has no operand for it?");
DbgDefUsers.push_back(&User);
}
}

// Point the users of this copy that are no longer dominated, at the source
// of the copy.
for (auto *User : DbgDefUsers) {
User->getDebugOperand(0).setReg(MI.getOperand(1).getReg());
User->getDebugOperand(0).setSubReg(MI.getOperand(1).getSubReg());
for (auto &Reg : DbgUseRegs) {
for (auto &DbgOp : User->getDebugOperandsForReg(Reg)) {
DbgOp.setReg(MI.getOperand(1).getReg());
DbgOp.setSubReg(MI.getOperand(1).getSubReg());
}
}
}
}

Expand Down Expand Up @@ -1521,8 +1545,10 @@ class PostRAMachineSinking : public MachineFunctionPass {
LiveRegUnits ModifiedRegUnits, UsedRegUnits;

/// Track DBG_VALUEs of (unmodified) register units. Each DBG_VALUE has an
/// entry in this map for each unit it touches.
DenseMap<unsigned, TinyPtrVector<MachineInstr *>> SeenDbgInstrs;
/// entry in this map for each unit it touches. The DBG_VALUE's entry
/// consists of a pointer to the instruction itself, and a vector of registers
/// referred to by the instruction that overlap the key register unit.
DenseMap<unsigned, SmallVector<MIRegs, 2>> SeenDbgInstrs;

/// Sink Copy instructions unused in the same block close to their uses in
/// successors.
Expand Down Expand Up @@ -1704,18 +1730,27 @@ bool PostRAMachineSinking::tryToSinkCopy(MachineBasicBlock &CurBB,
// We must sink this DBG_VALUE if its operand is sunk. To avoid searching
// for DBG_VALUEs later, record them when they're encountered.
if (MI->isDebugValue()) {
auto &MO = MI->getDebugOperand(0);
if (MO.isReg() && Register::isPhysicalRegister(MO.getReg())) {
// Bail if we can already tell the sink would be rejected, rather
// than needlessly accumulating lots of DBG_VALUEs.
if (hasRegisterDependency(MI, UsedOpsInCopy, DefedRegsInCopy,
ModifiedRegUnits, UsedRegUnits))
continue;

// Record debug use of each reg unit.
SmallSet<MCRegister, 4> Units = getRegUnits(MO.getReg(), TRI);
for (MCRegister Reg : Units)
SeenDbgInstrs[Reg].push_back(MI);
SmallDenseMap<MCRegister, SmallVector<unsigned, 2>, 4> MIUnits;
bool IsValid = true;
for (MachineOperand &MO : MI->debug_operands()) {
if (MO.isReg() && Register::isPhysicalRegister(MO.getReg())) {
// Bail if we can already tell the sink would be rejected, rather
// than needlessly accumulating lots of DBG_VALUEs.
if (hasRegisterDependency(MI, UsedOpsInCopy, DefedRegsInCopy,
ModifiedRegUnits, UsedRegUnits)) {
IsValid = false;
break;
}

// Record debug use of each reg unit.
SmallSet<MCRegister, 4> RegUnits = getRegUnits(MO.getReg(), TRI);
for (MCRegister Reg : RegUnits)
MIUnits[Reg].push_back(MO.getReg());
}
}
if (IsValid) {
for (auto RegOps : MIUnits)
SeenDbgInstrs[RegOps.first].push_back({MI, RegOps.second});
}
continue;
}
Expand Down Expand Up @@ -1757,18 +1792,22 @@ bool PostRAMachineSinking::tryToSinkCopy(MachineBasicBlock &CurBB,
// Collect DBG_VALUEs that must sink with this copy. We've previously
// recorded which reg units that DBG_VALUEs read, if this instruction
// writes any of those units then the corresponding DBG_VALUEs must sink.
SetVector<MachineInstr *> DbgValsToSinkSet;
MapVector<MachineInstr *, MIRegs::second_type> DbgValsToSinkMap;
for (auto &MO : MI->operands()) {
if (!MO.isReg() || !MO.isDef())
continue;

SmallSet<MCRegister, 4> Units = getRegUnits(MO.getReg(), TRI);
for (MCRegister Reg : Units)
for (auto *MI : SeenDbgInstrs.lookup(Reg))
DbgValsToSinkSet.insert(MI);
for (MCRegister Reg : Units) {
for (auto MIRegs : SeenDbgInstrs.lookup(Reg)) {
auto &Regs = DbgValsToSinkMap[MIRegs.first];
for (unsigned Reg : MIRegs.second)
Regs.push_back(Reg);
}
}
}
SmallVector<MachineInstr *, 4> DbgValsToSink(DbgValsToSinkSet.begin(),
DbgValsToSinkSet.end());
SmallVector<MIRegs, 4> DbgValsToSink(DbgValsToSinkMap.begin(),
DbgValsToSinkMap.end());

// Clear the kill flag if SrcReg is killed between MI and the end of the
// block.
Expand Down
Loading

0 comments on commit 1db137b

Please sign in to comment.