Skip to content

[RemoveDIs][DebugInfo] Add DPValue checks to the verifier, prepare DPValue for parsing support #79810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions llvm/include/llvm/IR/BasicBlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,6 @@ class BasicBlock final : public Value, // Basic blocks are data objects also
/// if necessary.
void setIsNewDbgInfoFormat(bool NewFlag);

/// Validate any DPMarkers / DPValues attached to instructions in this block,
/// and block-level stored data too (TrailingDPValues).
/// \p Assert Should this method fire an assertion if a problem is found?
/// \p Msg Should this method print a message to errs() if a problem is found?
/// \p OS Output stream to write errors to.
/// \returns True if a problem is found.
bool validateDbgValues(bool Assert = true, bool Msg = false,
raw_ostream *OS = nullptr);

/// Record that the collection of DPValues in \p M "trails" after the last
/// instruction of this block. These are equivalent to dbg.value intrinsics
/// that exist at the end of a basic block with no terminator (a transient
Expand Down
7 changes: 4 additions & 3 deletions llvm/include/llvm/IR/DebugProgramInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class DPValue : public DbgRecord, protected DebugValueUser {
// DebugValueUser superclass instead. The referred to Value can either be a
// ValueAsMetadata or a DIArgList.

DILocalVariable *Variable;
TrackingMDNodeRef Variable;
DIExpression *Expression;
DIExpression *AddressExpression;

Expand Down Expand Up @@ -331,7 +331,7 @@ class DPValue : public DbgRecord, protected DebugValueUser {
void addVariableLocationOps(ArrayRef<Value *> NewValues,
DIExpression *NewExpr);

void setVariable(DILocalVariable *NewVar) { Variable = NewVar; }
void setVariable(DILocalVariable *NewVar);

void setExpression(DIExpression *NewExpr) { Expression = NewExpr; }

Expand All @@ -349,7 +349,8 @@ class DPValue : public DbgRecord, protected DebugValueUser {
void setKillLocation();
bool isKillLocation() const;

DILocalVariable *getVariable() const { return Variable; }
DILocalVariable *getVariable() const;
MDNode *getRawVariable() const { return Variable; }

DIExpression *getExpression() const { return Expression; }

Expand Down
6 changes: 3 additions & 3 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1143,15 +1143,15 @@ void SlotTracker::processDbgRecordMetadata(const DbgRecord &DR) {
// Process metadata used by DbgRecords; we only specifically care about the
// DILocalVariable, DILocation, and DIAssignID fields, as the Value and
// Expression fields should only be printed inline and so do not use a slot.
CreateMetadataSlot(DPV->getVariable());
CreateMetadataSlot(DPV->getRawVariable());
if (DPV->isDbgAssign())
CreateMetadataSlot(DPV->getAssignID());
CreateMetadataSlot(cast<MDNode>(DPV->getRawAssignID()));
} else if (const DPLabel *DPL = dyn_cast<const DPLabel>(&DR)) {
CreateMetadataSlot(DPL->getLabel());
} else {
llvm_unreachable("unsupported DbgRecord kind");
}
CreateMetadataSlot(DR.getDebugLoc());
CreateMetadataSlot(DR.getDebugLoc().getAsMDNode());
}

void SlotTracker::processInstructionMetadata(const Instruction &I) {
Expand Down
61 changes: 0 additions & 61 deletions llvm/lib/IR/BasicBlock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,67 +122,6 @@ void BasicBlock::convertFromNewDbgValues() {
assert(!getTrailingDPValues());
}

bool BasicBlock::validateDbgValues(bool Assert, bool Msg, raw_ostream *OS) {
bool RetVal = false;
if (!OS)
OS = &errs();

// Helper lambda for reporting failures: via assertion, printing, and return
// value.
auto TestFailure = [Assert, Msg, &RetVal, OS](bool Val, const char *Text) {
// Did the test fail?
if (Val)
return;

// If we're asserting, then fire off an assertion.
if (Assert)
llvm_unreachable(Text);

if (Msg)
*OS << Text << "\n";
RetVal = true;
};

// We should have the same debug-format as the parent function.
TestFailure(getParent()->IsNewDbgInfoFormat == IsNewDbgInfoFormat,
"Parent function doesn't have the same debug-info format");

// Only validate if we are using the new format.
if (!IsNewDbgInfoFormat)
return RetVal;

// Match every DPMarker to every Instruction and vice versa, and
// verify that there are no invalid DPValues.
for (auto It = begin(); It != end(); ++It) {
if (!It->DbgMarker)
continue;

// Validate DebugProgramMarkers.
DPMarker *CurrentDebugMarker = It->DbgMarker;

// If this is a marker, it should match the instruction and vice versa.
TestFailure(CurrentDebugMarker->MarkedInstr == &*It,
"Debug Marker points to incorrect instruction?");

// Now validate any DPValues in the marker.
for (DbgRecord &DPR : CurrentDebugMarker->getDbgValueRange()) {
// Validate DebugProgramValues.
TestFailure(DPR.getMarker() == CurrentDebugMarker,
"Not pointing at correct next marker!");

// Verify that no DbgValues appear prior to PHIs.
TestFailure(
!isa<PHINode>(It),
"DebugProgramValues must not appear before PHI nodes in a block!");
}
}

// Except transiently when removing + re-inserting the block terminator, there
// should be no trailing DPValues.
TestFailure(!getTrailingDPValues(), "Trailing DPValues in block");
return RetVal;
}

#ifndef NDEBUG
void BasicBlock::dumpDbgValues() const {
for (auto &Inst : *this) {
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/IR/DebugProgramInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ DPValue *DPValue::createLinkedDPVAssign(Instruction *LinkedInstr, Value *Val,
return NewDPVAssign;
}

void DPValue::setVariable(DILocalVariable *NewVar) { Variable.reset(NewVar); }

iterator_range<DPValue::location_op_iterator> DPValue::location_ops() const {
auto *MD = getRawLocation();
// If a Value has been deleted, the "location" for this DPValue will be
Expand Down Expand Up @@ -313,6 +315,10 @@ bool DPValue::isKillLocation() const {
any_of(location_ops(), [](Value *V) { return isa<UndefValue>(V); });
}

DILocalVariable *DPValue::getVariable() const {
return cast<DILocalVariable>(Variable.get());
}

std::optional<uint64_t> DPValue::getFragmentSizeInBits() const {
if (auto Fragment = getExpression()->getFragmentInfo())
return Fragment->SizeInBits;
Expand Down
115 changes: 109 additions & 6 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,36 @@ struct VerifierSupport {
}
}

void Write(const DbgRecord *DR) {
if (DR)
DR->print(*OS, MST, false);
}

void Write(const DPValue *V) {
if (V)
V->print(*OS, MST, false);
}

void Write(DPValue::LocationType Type) {
switch (Type) {
case DPValue::LocationType::Value:
*OS << "value";
break;
case DPValue::LocationType::Declare:
*OS << "declare";
break;
case DPValue::LocationType::Assign:
*OS << "assign";
break;
case DPValue::LocationType::End:
*OS << "end";
break;
case DPValue::LocationType::Any:
*OS << "any";
break;
};
}

void Write(const Metadata *MD) {
if (!MD)
return;
Expand Down Expand Up @@ -522,8 +547,10 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {

void visitTemplateParams(const MDNode &N, const Metadata &RawParams);

void visit(DPValue &DPV);
// InstVisitor overrides...
using InstVisitor<Verifier>::visit;
void visitDbgRecords(Instruction &I);
void visit(Instruction &I);

void visitTruncInst(TruncInst &I);
Expand Down Expand Up @@ -649,7 +676,22 @@ class Verifier : public InstVisitor<Verifier>, VerifierSupport {
} \
} while (false)

void Verifier::visitDbgRecords(Instruction &I) {
if (!I.DbgMarker)
return;
CheckDI(I.DbgMarker->MarkedInstr == &I, "Instruction has invalid DbgMarker",
&I);
CheckDI(!isa<PHINode>(&I) || !I.hasDbgValues(),
"PHI Node must not have any attached DbgRecords", &I);
for (DPValue &DPV : DPValue::filter(I.getDbgValueRange())) {
CheckDI(DPV.getMarker() == I.DbgMarker, "DbgRecord had invalid DbgMarker",
&I, &DPV);
visit(DPV);
}
}

void Verifier::visit(Instruction &I) {
visitDbgRecords(I);
for (unsigned i = 0, e = I.getNumOperands(); i != e; ++i)
Check(I.getOperand(i) != nullptr, "Operand is null", &I);
InstVisitor<Verifier>::visit(I);
Expand Down Expand Up @@ -2976,12 +3018,9 @@ void Verifier::visitBasicBlock(BasicBlock &BB) {
}

// Confirm that no issues arise from the debug program.
if (BB.IsNewDbgInfoFormat) {
// Configure the validate function to not fire assertions, instead print
// errors and return true if there's a problem.
bool RetVal = BB.validateDbgValues(false, true, OS);
Check(!RetVal, "Invalid configuration of new-debug-info data found");
}
if (BB.IsNewDbgInfoFormat)
CheckDI(!BB.getTrailingDPValues(), "Basic Block has trailing DbgRecords!",
&BB);
}

void Verifier::visitTerminator(Instruction &I) {
Expand Down Expand Up @@ -6148,6 +6187,70 @@ static DISubprogram *getSubprogram(Metadata *LocalScope) {
return nullptr;
}

void Verifier::visit(DPValue &DPV) {
CheckDI(DPV.getType() == DPValue::LocationType::Value ||
DPV.getType() == DPValue::LocationType::Declare ||
DPV.getType() == DPValue::LocationType::Assign,
"invalid #dbg record type", &DPV, DPV.getType());
// The location for a DPValue must be either a ValueAsMetadata, DIArgList, or
// an empty MDNode (which is a legacy representation for an "undef" location).
auto *MD = DPV.getRawLocation();
CheckDI(isa<ValueAsMetadata>(MD) || isa<DIArgList>(MD) ||
(isa<MDNode>(MD) && !cast<MDNode>(MD)->getNumOperands()),
"invalid #dbg record address/value", &DPV, MD);
CheckDI(isa<DILocalVariable>(DPV.getRawVariable()),
"invalid #dbg record variable", &DPV, DPV.getRawVariable());
CheckDI(DPV.getExpression(), "missing #dbg record expression", &DPV,
DPV.getExpression());

if (DPV.isDbgAssign()) {
CheckDI(isa<DIAssignID>(DPV.getRawAssignID()),
"invalid #dbg_assign DIAssignID", &DPV, DPV.getRawAssignID());
const auto *RawAddr = DPV.getRawAddress();
// Similarly to the location above, the address for an assign DPValue must
// be a ValueAsMetadata or an empty MDNode, which represents an undef
// address.
CheckDI(
isa<ValueAsMetadata>(RawAddr) ||
(isa<MDNode>(RawAddr) && !cast<MDNode>(RawAddr)->getNumOperands()),
"invalid #dbg_assign address", &DPV, DPV.getRawAddress());
CheckDI(DPV.getAddressExpression(),
"missing #dbg_assign address expression", &DPV,
DPV.getAddressExpression());
// All of the linked instructions should be in the same function as DPV.
for (Instruction *I : at::getAssignmentInsts(&DPV))
CheckDI(DPV.getFunction() == I->getFunction(),
"inst not in same function as #dbg_assign", I, &DPV);
}

if (MDNode *N = DPV.getDebugLoc().getAsMDNode()) {
CheckDI(isa<DILocation>(N), "invalid #dbg record location", &DPV, N);
visitDILocation(*cast<DILocation>(N));
}

BasicBlock *BB = DPV.getParent();
Function *F = BB ? BB->getParent() : nullptr;

// The scopes for variables and !dbg attachments must agree.
DILocalVariable *Var = DPV.getVariable();
DILocation *Loc = DPV.getDebugLoc();
CheckDI(Loc, "missing #dbg record DILocation", &DPV, BB, F);

DISubprogram *VarSP = getSubprogram(Var->getRawScope());
DISubprogram *LocSP = getSubprogram(Loc->getRawScope());
if (!VarSP || !LocSP)
return; // Broken scope chains are checked elsewhere.

CheckDI(VarSP == LocSP,
"mismatched subprogram between #dbg record variable and DILocation",
&DPV, BB, F, Var, Var->getScope()->getSubprogram(), Loc,
Loc->getScope()->getSubprogram());

// This check is redundant with one in visitLocalVariable().
CheckDI(isType(Var->getRawType()), "invalid type ref", Var,
Var->getRawType());
}

void Verifier::visitVPIntrinsic(VPIntrinsic &VPI) {
if (auto *VPCast = dyn_cast<VPCastIntrinsic>(&VPI)) {
auto *RetTy = cast<VectorType>(VPCast->getType());
Expand Down
13 changes: 10 additions & 3 deletions llvm/unittests/IR/DebugInfoTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1082,7 +1082,10 @@ TEST(MetadataTest, DPValueConversionRoutines) {
EXPECT_FALSE(BB1->IsNewDbgInfoFormat);
// Validating the block for DPValues / DPMarkers shouldn't fail -- there's
// no data stored right now.
EXPECT_FALSE(BB1->validateDbgValues(false, false));
bool BrokenDebugInfo = false;
bool Error = verifyModule(*M, &errs(), &BrokenDebugInfo);
EXPECT_FALSE(Error);
EXPECT_FALSE(BrokenDebugInfo);

// Function and module should be marked as not having the new format too.
EXPECT_FALSE(F->IsNewDbgInfoFormat);
Expand Down Expand Up @@ -1135,13 +1138,17 @@ TEST(MetadataTest, DPValueConversionRoutines) {
EXPECT_TRUE(!Inst.DbgMarker || Inst.DbgMarker->StoredDPValues.empty());

// Validating the first block should continue to not be a problem,
EXPECT_FALSE(BB1->validateDbgValues(false, false));
Error = verifyModule(*M, &errs(), &BrokenDebugInfo);
EXPECT_FALSE(Error);
EXPECT_FALSE(BrokenDebugInfo);
// But if we were to break something, it should be able to fire. Don't attempt
// to comprehensively test the validator, it's a smoke-test rather than a
// "proper" verification pass.
DPV1->setMarker(nullptr);
// A marker pointing the wrong way should be an error.
EXPECT_TRUE(BB1->validateDbgValues(false, false));
Error = verifyModule(*M, &errs(), &BrokenDebugInfo);
EXPECT_FALSE(Error);
EXPECT_TRUE(BrokenDebugInfo);
DPV1->setMarker(FirstInst->DbgMarker);

DILocalVariable *DLV1 = DPV1->getVariable();
Expand Down