Skip to content

Commit

Permalink
[DebugInfo][RemoveDIs] Interpret DPValue objects in SelectionDAG (#72253
Browse files Browse the repository at this point in the history
)

DPValues are the non-intrinsic replacements for dbg.values, and when an
IR function is converted by SelectionDAG we need to convert the variable
location information in the same way. Happily all the information is in
the same format, it's just stored in a slightly different object,
therefore this patch refactors a few things to store the set of
{Variable,Expr,DILocation,Location} instead of just a pointer to a
DbgValueInst.

This also adds a hook in llc that's much like the one I've added to opt
in PR #71937, allowing tests to optionally ask for the use RemoveDIs
mode if support for it is built into the compiler.

I've added that flag to a variety of SelectionDAG debug-info tests to
ensure that we get some coverage on the RemoveDIs / debug-info-iterator
buildbot.
  • Loading branch information
jmorse committed Nov 21, 2023
1 parent 2fe24fd commit 4495485
Show file tree
Hide file tree
Showing 15 changed files with 176 additions and 100 deletions.
144 changes: 87 additions & 57 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1148,12 +1148,7 @@ SDValue SelectionDAGBuilder::getControlRoot() {
return updateRoot(PendingExports);
}

void SelectionDAGBuilder::visit(const Instruction &I) {
// Set up outgoing PHI node register values before emitting the terminator.
if (I.isTerminator()) {
HandlePHINodesInSuccessorBlocks(I.getParent());
}

void SelectionDAGBuilder::visitDbgInfo(const Instruction &I) {
// Add SDDbgValue nodes for any var locs here. Do so before updating
// SDNodeOrder, as this mapping is {Inst -> Locs BEFORE Inst}.
if (FunctionVarLocs const *FnVarLocs = DAG.getFunctionVarLocs()) {
Expand All @@ -1169,10 +1164,56 @@ void SelectionDAGBuilder::visit(const Instruction &I) {
}
SmallVector<Value *> Values(It->Values.location_ops());
if (!handleDebugValue(Values, Var, It->Expr, It->DL, SDNodeOrder,
It->Values.hasArgList()))
addDanglingDebugInfo(It, SDNodeOrder);
It->Values.hasArgList())) {
SmallVector<Value *, 4> Vals;
for (Value *V : It->Values.location_ops())
Vals.push_back(V);
addDanglingDebugInfo(Vals,
FnVarLocs->getDILocalVariable(It->VariableID),
It->Expr, Vals.size() > 1, It->DL, SDNodeOrder);
}
}
}

// Is there is any debug-info attached to this instruction, in the form of
// DPValue non-instruction debug-info records.
for (DPValue &DPV : I.getDbgValueRange()) {
DILocalVariable *Variable = DPV.getVariable();
DIExpression *Expression = DPV.getExpression();
dropDanglingDebugInfo(Variable, Expression);

// A DPValue with no locations is a kill location.
SmallVector<Value *, 4> Values(DPV.location_ops());
if (Values.empty()) {
handleKillDebugValue(Variable, Expression, DPV.getDebugLoc(),
SDNodeOrder);
continue;
}

// A DPValue with an undef or absent location is also a kill location.
if (llvm::any_of(Values,
[](Value *V) { return !V || isa<UndefValue>(V); })) {
handleKillDebugValue(Variable, Expression, DPV.getDebugLoc(),
SDNodeOrder);
continue;
}

bool IsVariadic = DPV.hasArgList();
if (!handleDebugValue(Values, Variable, Expression, DPV.getDebugLoc(),
SDNodeOrder, IsVariadic)) {
addDanglingDebugInfo(Values, Variable, Expression, IsVariadic,
DPV.getDebugLoc(), SDNodeOrder);
}
}
}

void SelectionDAGBuilder::visit(const Instruction &I) {
visitDbgInfo(I);

// Set up outgoing PHI node register values before emitting the terminator.
if (I.isTerminator()) {
HandlePHINodesInSuccessorBlocks(I.getParent());
}

// Increase the SDNodeOrder if dealing with a non-debug instruction.
if (!isa<DbgInfoIntrinsic>(I))
Expand Down Expand Up @@ -1232,14 +1273,12 @@ void SelectionDAGBuilder::visit(unsigned Opcode, const User &I) {
static bool handleDanglingVariadicDebugInfo(SelectionDAG &DAG,
DILocalVariable *Variable,
DebugLoc DL, unsigned Order,
RawLocationWrapper Values,
SmallVectorImpl<Value *> &Values,
DIExpression *Expression) {
if (!Values.hasArgList())
return false;
// For variadic dbg_values we will now insert an undef.
// FIXME: We can potentially recover these!
SmallVector<SDDbgOperand, 2> Locs;
for (const Value *V : Values.location_ops()) {
for (const Value *V : Values) {
auto *Undef = UndefValue::get(V->getType());
Locs.push_back(SDDbgOperand::fromConst(Undef));
}
Expand All @@ -1250,44 +1289,31 @@ static bool handleDanglingVariadicDebugInfo(SelectionDAG &DAG,
return true;
}

void SelectionDAGBuilder::addDanglingDebugInfo(const VarLocInfo *VarLoc,
unsigned Order) {
if (!handleDanglingVariadicDebugInfo(
DAG,
const_cast<DILocalVariable *>(DAG.getFunctionVarLocs()
->getVariable(VarLoc->VariableID)
.getVariable()),
VarLoc->DL, Order, VarLoc->Values, VarLoc->Expr)) {
DanglingDebugInfoMap[VarLoc->Values.getVariableLocationOp(0)].emplace_back(
VarLoc, Order);
}
}

void SelectionDAGBuilder::addDanglingDebugInfo(const DbgValueInst *DI,
void SelectionDAGBuilder::addDanglingDebugInfo(SmallVectorImpl<Value *> &Values,
DILocalVariable *Var,
DIExpression *Expr,
bool IsVariadic, DebugLoc DL,
unsigned Order) {
// We treat variadic dbg_values differently at this stage.
if (!handleDanglingVariadicDebugInfo(
DAG, DI->getVariable(), DI->getDebugLoc(), Order,
DI->getWrappedLocation(), DI->getExpression())) {
// TODO: Dangling debug info will eventually either be resolved or produce
// an Undef DBG_VALUE. However in the resolution case, a gap may appear
// between the original dbg.value location and its resolved DBG_VALUE,
// which we should ideally fill with an extra Undef DBG_VALUE.
assert(DI->getNumVariableLocationOps() == 1 &&
"DbgValueInst without an ArgList should have a single location "
"operand.");
DanglingDebugInfoMap[DI->getValue(0)].emplace_back(DI, Order);
if (IsVariadic) {
handleDanglingVariadicDebugInfo(DAG, Var, DL, Order, Values, Expr);
return;
}
// TODO: Dangling debug info will eventually either be resolved or produce
// an Undef DBG_VALUE. However in the resolution case, a gap may appear
// between the original dbg.value location and its resolved DBG_VALUE,
// which we should ideally fill with an extra Undef DBG_VALUE.
assert(Values.size() == 1);
DanglingDebugInfoMap[Values[0]].emplace_back(Var, Expr, DL, Order);
}

void SelectionDAGBuilder::dropDanglingDebugInfo(const DILocalVariable *Variable,
const DIExpression *Expr) {
auto isMatchingDbgValue = [&](DanglingDebugInfo &DDI) {
DIVariable *DanglingVariable = DDI.getVariable(DAG.getFunctionVarLocs());
DIVariable *DanglingVariable = DDI.getVariable();
DIExpression *DanglingExpr = DDI.getExpression();
if (DanglingVariable == Variable && Expr->fragmentsOverlap(DanglingExpr)) {
LLVM_DEBUG(dbgs() << "Dropping dangling debug info for " << printDDI(DDI)
<< "\n");
LLVM_DEBUG(dbgs() << "Dropping dangling debug info for "
<< printDDI(nullptr, DDI) << "\n");
return true;
}
return false;
Expand All @@ -1300,7 +1326,7 @@ void SelectionDAGBuilder::dropDanglingDebugInfo(const DILocalVariable *Variable,
// whether it can be salvaged.
for (auto &DDI : DDIV)
if (isMatchingDbgValue(DDI))
salvageUnresolvedDbgValue(DDI);
salvageUnresolvedDbgValue(DDIMI.first, DDI);

erase_if(DDIV, isMatchingDbgValue);
}
Expand All @@ -1319,7 +1345,7 @@ void SelectionDAGBuilder::resolveDanglingDebugInfo(const Value *V,
DebugLoc DL = DDI.getDebugLoc();
unsigned ValSDNodeOrder = Val.getNode()->getIROrder();
unsigned DbgSDNodeOrder = DDI.getSDNodeOrder();
DILocalVariable *Variable = DDI.getVariable(DAG.getFunctionVarLocs());
DILocalVariable *Variable = DDI.getVariable();
DIExpression *Expr = DDI.getExpression();
assert(Variable->isValidLocationForIntrinsic(DL) &&
"Expected inlined-at fields to agree");
Expand All @@ -1333,8 +1359,8 @@ void SelectionDAGBuilder::resolveDanglingDebugInfo(const Value *V,
// calling EmitFuncArgumentDbgValue here.
if (!EmitFuncArgumentDbgValue(V, Variable, Expr, DL,
FuncArgumentDbgValueKind::Value, Val)) {
LLVM_DEBUG(dbgs() << "Resolve dangling debug info for " << printDDI(DDI)
<< "\n");
LLVM_DEBUG(dbgs() << "Resolve dangling debug info for "
<< printDDI(V, DDI) << "\n");
LLVM_DEBUG(dbgs() << " By mapping to:\n "; Val.dump());
// Increase the SDNodeOrder for the DbgValue here to make sure it is
// inserted after the definition of Val when emitting the instructions
Expand All @@ -1348,9 +1374,11 @@ void SelectionDAGBuilder::resolveDanglingDebugInfo(const Value *V,
DAG.AddDbgValue(SDV, false);
} else
LLVM_DEBUG(dbgs() << "Resolved dangling debug info for "
<< printDDI(DDI) << " in EmitFuncArgumentDbgValue\n");
<< printDDI(V, DDI)
<< " in EmitFuncArgumentDbgValue\n");
} else {
LLVM_DEBUG(dbgs() << "Dropping debug info for " << printDDI(DDI) << "\n");
LLVM_DEBUG(dbgs() << "Dropping debug info for " << printDDI(V, DDI)
<< "\n");
auto Undef = UndefValue::get(V->getType());
auto SDV =
DAG.getConstantDbgValue(Variable, Expr, Undef, DL, DbgSDNodeOrder);
Expand All @@ -1360,14 +1388,14 @@ void SelectionDAGBuilder::resolveDanglingDebugInfo(const Value *V,
DDIV.clear();
}

void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) {
void SelectionDAGBuilder::salvageUnresolvedDbgValue(const Value *V,
DanglingDebugInfo &DDI) {
// TODO: For the variadic implementation, instead of only checking the fail
// state of `handleDebugValue`, we need know specifically which values were
// invalid, so that we attempt to salvage only those values when processing
// a DIArgList.
Value *V = DDI.getVariableLocationOp(0);
Value *OrigV = V;
DILocalVariable *Var = DDI.getVariable(DAG.getFunctionVarLocs());
const Value *OrigV = V;
DILocalVariable *Var = DDI.getVariable();
DIExpression *Expr = DDI.getExpression();
DebugLoc DL = DDI.getDebugLoc();
unsigned SDOrder = DDI.getSDNodeOrder();
Expand All @@ -1384,11 +1412,12 @@ void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) {
// a non-instruction is seen, such as a constant expression or global
// variable. FIXME: Further work could recover those too.
while (isa<Instruction>(V)) {
Instruction &VAsInst = *cast<Instruction>(V);
const Instruction &VAsInst = *cast<const Instruction>(V);
// Temporary "0", awaiting real implementation.
SmallVector<uint64_t, 16> Ops;
SmallVector<Value *, 4> AdditionalValues;
V = salvageDebugInfoImpl(VAsInst, Expr->getNumLocationOperands(), Ops,
V = salvageDebugInfoImpl(const_cast<Instruction &>(VAsInst),
Expr->getNumLocationOperands(), Ops,
AdditionalValues);
// If we cannot salvage any further, and haven't yet found a suitable debug
// expression, bail out.
Expand Down Expand Up @@ -1421,8 +1450,8 @@ void SelectionDAGBuilder::salvageUnresolvedDbgValue(DanglingDebugInfo &DDI) {
auto *Undef = UndefValue::get(OrigV->getType());
auto *SDV = DAG.getConstantDbgValue(Var, Expr, Undef, DL, SDNodeOrder);
DAG.AddDbgValue(SDV, false);
LLVM_DEBUG(dbgs() << "Dropping debug value info for:\n " << printDDI(DDI)
<< "\n");
LLVM_DEBUG(dbgs() << "Dropping debug value info for:\n "
<< printDDI(OrigV, DDI) << "\n");
}

void SelectionDAGBuilder::handleKillDebugValue(DILocalVariable *Var,
Expand Down Expand Up @@ -1572,7 +1601,7 @@ void SelectionDAGBuilder::resolveOrClearDbgInfo() {
// Try to fixup any remaining dangling debug info -- and drop it if we can't.
for (auto &Pair : DanglingDebugInfoMap)
for (auto &DDI : Pair.second)
salvageUnresolvedDbgValue(DDI);
salvageUnresolvedDbgValue(const_cast<Value *>(Pair.first), DDI);
clearDanglingDebugInfo();
}

Expand Down Expand Up @@ -6315,7 +6344,8 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
bool IsVariadic = DI.hasArgList();
if (!handleDebugValue(Values, Variable, Expression, DI.getDebugLoc(),
SDNodeOrder, IsVariadic))
addDanglingDebugInfo(&DI, SDNodeOrder);
addDanglingDebugInfo(Values, Variable, Expression, IsVariadic,
DI.getDebugLoc(), SDNodeOrder);
return;
}

Expand Down
70 changes: 29 additions & 41 deletions llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,54 +106,39 @@ class SelectionDAGBuilder {

/// Helper type for DanglingDebugInfoMap.
class DanglingDebugInfo {
using DbgValTy = const DbgValueInst *;
using VarLocTy = const VarLocInfo *;
PointerUnion<DbgValTy, VarLocTy> Info;
unsigned SDNodeOrder = 0;

public:
DILocalVariable *Variable;
DIExpression *Expression;
DebugLoc dl;
DanglingDebugInfo() = default;
DanglingDebugInfo(const DbgValueInst *DI, unsigned SDNO)
: Info(DI), SDNodeOrder(SDNO) {}
DanglingDebugInfo(const VarLocInfo *VarLoc, unsigned SDNO)
: Info(VarLoc), SDNodeOrder(SDNO) {}

DILocalVariable *getVariable(const FunctionVarLocs *Locs) const {
if (isa<VarLocTy>(Info))
return Locs->getDILocalVariable(cast<VarLocTy>(Info)->VariableID);
return cast<DbgValTy>(Info)->getVariable();
}
DIExpression *getExpression() const {
if (isa<VarLocTy>(Info))
return cast<VarLocTy>(Info)->Expr;
return cast<DbgValTy>(Info)->getExpression();
}
Value *getVariableLocationOp(unsigned Idx) const {
assert(Idx == 0 && "Dangling variadic debug values not supported yet");
if (isa<VarLocTy>(Info))
return cast<VarLocTy>(Info)->Values.getVariableLocationOp(Idx);
return cast<DbgValTy>(Info)->getVariableLocationOp(Idx);
}
DebugLoc getDebugLoc() const {
if (isa<VarLocTy>(Info))
return cast<VarLocTy>(Info)->DL;
return cast<DbgValTy>(Info)->getDebugLoc();
}
DanglingDebugInfo(DILocalVariable *Var, DIExpression *Expr, DebugLoc DL,
unsigned SDNO)
: SDNodeOrder(SDNO), Variable(Var), Expression(Expr),
dl(std::move(DL)) {}

DILocalVariable *getVariable() const { return Variable; }
DIExpression *getExpression() const { return Expression; }
DebugLoc getDebugLoc() const { return dl; }
unsigned getSDNodeOrder() const { return SDNodeOrder; }

/// Helper for printing DanglingDebugInfo. This hoop-jumping is to
/// accommodate the fact that an argument is required for getVariable.
/// store a Value pointer, so that we can print a whole DDI as one object.
/// Call SelectionDAGBuilder::printDDI instead of using directly.
struct Print {
Print(const DanglingDebugInfo &DDI, const FunctionVarLocs *VarLocs)
: DDI(DDI), VarLocs(VarLocs) {}
Print(const Value *V, const DanglingDebugInfo &DDI) : V(V), DDI(DDI) {}
const Value *V;
const DanglingDebugInfo &DDI;
const FunctionVarLocs *VarLocs;
friend raw_ostream &operator<<(raw_ostream &OS,
const DanglingDebugInfo::Print &P) {
OS << "DDI(var=" << *P.DDI.getVariable(P.VarLocs)
<< ", val= " << *P.DDI.getVariableLocationOp(0)
<< ", expr=" << *P.DDI.getExpression()
OS << "DDI(var=" << *P.DDI.getVariable();
if (P.V)
OS << ", val=" << *P.V;
else
OS << ", val=nullptr";

OS << ", expr=" << *P.DDI.getExpression()
<< ", order=" << P.DDI.getSDNodeOrder()
<< ", loc=" << P.DDI.getDebugLoc() << ")";
return OS;
Expand All @@ -164,8 +149,9 @@ class SelectionDAGBuilder {
/// Returns an object that defines `raw_ostream &operator<<` for printing.
/// Usage example:
//// errs() << printDDI(MyDanglingInfo) << " is dangling\n";
DanglingDebugInfo::Print printDDI(const DanglingDebugInfo &DDI) {
return DanglingDebugInfo::Print(DDI, DAG.getFunctionVarLocs());
DanglingDebugInfo::Print printDDI(const Value *V,
const DanglingDebugInfo &DDI) {
return DanglingDebugInfo::Print(V, DDI);
}

/// Helper type for DanglingDebugInfoMap.
Expand Down Expand Up @@ -344,6 +330,7 @@ class SelectionDAGBuilder {
ISD::NodeType ExtendType = ISD::ANY_EXTEND);

void visit(const Instruction &I);
void visitDbgInfo(const Instruction &I);

void visit(unsigned Opcode, const User &I);

Expand All @@ -352,8 +339,9 @@ class SelectionDAGBuilder {
SDValue getCopyFromRegs(const Value *V, Type *Ty);

/// Register a dbg_value which relies on a Value which we have not yet seen.
void addDanglingDebugInfo(const DbgValueInst *DI, unsigned Order);
void addDanglingDebugInfo(const VarLocInfo *VarLoc, unsigned Order);
void addDanglingDebugInfo(SmallVectorImpl<Value *> &Values,
DILocalVariable *Var, DIExpression *Expr,
bool IsVariadic, DebugLoc DL, unsigned Order);

/// If we have dangling debug info that describes \p Variable, or an
/// overlapping part of variable considering the \p Expr, then this method
Expand All @@ -368,7 +356,7 @@ class SelectionDAGBuilder {
/// For the given dangling debuginfo record, perform last-ditch efforts to
/// resolve the debuginfo to something that is represented in this DAG. If
/// this cannot be done, produce an Undef debug value record.
void salvageUnresolvedDbgValue(DanglingDebugInfo &DDI);
void salvageUnresolvedDbgValue(const Value *V, DanglingDebugInfo &DDI);

/// For a given list of Values, attempt to create and record a SDDbgValue in
/// the SelectionDAG.
Expand Down
5 changes: 4 additions & 1 deletion llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,10 +719,13 @@ void SelectionDAGISel::SelectBasicBlock(BasicBlock::const_iterator Begin,
CurDAG->NewNodesMustHaveLegalTypes = false;

// Lower the instructions. If a call is emitted as a tail call, cease emitting
// nodes for this block.
// nodes for this block. If an instruction is elided, don't emit it, but do
// handle any debug-info attached to it.
for (BasicBlock::const_iterator I = Begin; I != End && !SDB->HasTailCall; ++I) {
if (!ElidedArgCopyInstrs.count(&*I))
SDB->visit(*I);
else
SDB->visitDbgInfo(*I);
}

// Make sure the root of the DAG is up-to-date.
Expand Down
3 changes: 3 additions & 0 deletions llvm/test/DebugInfo/X86/arg-dbg-value-list.ll
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations=false -stop-before=finalize-isel -o - | FileCheck %s
; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations -stop-before=finalize-isel -o - | FileCheck %s

; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations=false -stop-before=finalize-isel -o - --try-experimental-debuginfo-iterators | FileCheck %s
; RUN: llc %s -start-after=codegenprepare --experimental-debug-variable-locations -stop-before=finalize-isel -o - --try-experimental-debuginfo-iterators | FileCheck %s

;; Check that unused argument values are handled the same way for variadic
;; dbg_values as non-variadics.

Expand Down
Loading

0 comments on commit 4495485

Please sign in to comment.