Skip to content

Commit b9076d1

Browse files
Djordje TodorovicDjordje Todorovic
authored andcommitted
Recommit: "[Debugify][Original DI] Test dbg var loc preservation""
[Debugify][Original DI] Test dbg var loc preservation This is an improvement of [0]. This adds checking of original llvm.dbg.values()/declares() instructions in optimizations. We have picked a real issue that has been found with this (actually, picked one variable location missing from [1] and resolved the issue), and the result is the fix for that -- D100844. Before applying the D100844, using the options from [0] (but with this patch applied) on the compilation of GDB 7.11, the final HTML report for the debug-info issues can be found at [1] (please scroll down, and look for "Summary of Variable Location Bugs"). After applying the D100844, the numbers has improved a bit -- please take a look into [2]. [0] https://llvm.org/docs/HowToUpdateDebugInfo.html#\ test-original-debug-info-preservation-in-optimizations [1] https://djolertrk.github.io/di-check-before-adce-fix/ [2] https://djolertrk.github.io/di-check-after-adce-fix/ Differential Revision: https://reviews.llvm.org/D100845 The Unit test was failing because the pass from the test that modifies the IR, in its runOnFunction() didn't return 'true', so the expensive-check configuration triggered an assertion.
1 parent fb8b2b8 commit b9076d1

File tree

6 files changed

+325
-15
lines changed

6 files changed

+325
-15
lines changed

llvm/docs/HowToUpdateDebugInfo.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,9 @@ as follows:
387387
# Test each pass and export the issues report into the JSON file.
388388
$ clang -Xclang -fverify-debuginfo-preserve -Xclang -fverify-debuginfo-preserve-export=sample.json -g -O2 sample.c
389389
390+
Please do note that there are some known false positives, for source locations
391+
and debug intrinsic checking, so that will be addressed as a future work.
392+
390393
Mutation testing for MIR-level transformations
391394
----------------------------------------------
392395

llvm/include/llvm/Transforms/Utils/Debugify.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
using DebugFnMap = llvm::DenseMap<llvm::StringRef, const llvm::DISubprogram *>;
2727
using DebugInstMap = llvm::DenseMap<const llvm::Instruction *, bool>;
28+
using DebugVarMap = llvm::DenseMap<const llvm::DILocalVariable *, unsigned>;
2829
using WeakInstValueMap =
2930
llvm::DenseMap<const llvm::Instruction *, llvm::WeakVH>;
3031

@@ -37,6 +38,8 @@ struct DebugInfoPerPass {
3738
// This tracks value (instruction) deletion. If an instruction gets deleted,
3839
// WeakVH nulls itself.
3940
WeakInstValueMap InstToDelete;
41+
// Maps variable into dbg users (#dbg values/declares for this variable).
42+
DebugVarMap DIVariables;
4043
};
4144

4245
/// Map pass names to a per-pass DebugInfoPerPass instance.

llvm/lib/Transforms/Utils/Debugify.cpp

Lines changed: 92 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -304,18 +304,39 @@ bool llvm::collectDebugInfoMetadata(Module &M,
304304
// Collect the DISubprogram.
305305
auto *SP = F.getSubprogram();
306306
DIPreservationMap[NameOfWrappedPass].DIFunctions.insert({F.getName(), SP});
307-
if (SP)
307+
if (SP) {
308308
LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n');
309+
for (const DINode *DN : SP->getRetainedNodes()) {
310+
if (const auto *DV = dyn_cast<DILocalVariable>(DN)) {
311+
DIPreservationMap[NameOfWrappedPass].DIVariables[DV] = 0;
312+
}
313+
}
314+
}
309315

310316
for (BasicBlock &BB : F) {
311-
// Collect debug locations (!dbg).
312-
// TODO: Collect dbg.values.
317+
// Collect debug locations (!dbg) and debug variable intrinsics.
313318
for (Instruction &I : BB) {
314319
// Skip PHIs.
315320
if (isa<PHINode>(I))
316321
continue;
317322

318-
// Skip debug instructions.
323+
// Collect dbg.values and dbg.declares.
324+
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I)) {
325+
if (!SP)
326+
continue;
327+
// Skip inlined variables.
328+
if (I.getDebugLoc().getInlinedAt())
329+
continue;
330+
// Skip undef values.
331+
if (DVI->isUndef())
332+
continue;
333+
334+
auto *Var = DVI->getVariable();
335+
DIPreservationMap[NameOfWrappedPass].DIVariables[Var]++;
336+
continue;
337+
}
338+
339+
// Skip debug instructions other than dbg.value and dbg.declare.
319340
if (isa<DbgInfoIntrinsic>(&I))
320341
continue;
321342

@@ -435,6 +456,39 @@ static bool checkInstructions(const DebugInstMap &DILocsBefore,
435456
return Preserved;
436457
}
437458

459+
// This checks the preservation of original debug variable intrinsics.
460+
static bool checkVars(const DebugVarMap &DIFunctionsBefore,
461+
const DebugVarMap &DIFunctionsAfter,
462+
StringRef NameOfWrappedPass, StringRef FileNameFromCU,
463+
bool ShouldWriteIntoJSON, llvm::json::Array &Bugs) {
464+
bool Preserved = true;
465+
for (const auto &V : DIFunctionsBefore) {
466+
auto VarIt = DIFunctionsAfter.find(V.first);
467+
if (VarIt == DIFunctionsAfter.end())
468+
continue;
469+
470+
unsigned NumOfDbgValsAfter = VarIt->second;
471+
472+
if (V.second > NumOfDbgValsAfter) {
473+
if (ShouldWriteIntoJSON)
474+
Bugs.push_back(llvm::json::Object(
475+
{{"metadata", "dbg-var-intrinsic"},
476+
{"name", V.first->getName()},
477+
{"fn-name", V.first->getScope()->getSubprogram()->getName()},
478+
{"action", "drop"}}));
479+
else
480+
dbg() << "WARNING: " << NameOfWrappedPass
481+
<< " drops dbg.value()/dbg.declare() for " << V.first->getName()
482+
<< " from "
483+
<< "function " << V.first->getScope()->getSubprogram()->getName()
484+
<< " (file " << FileNameFromCU << ")\n";
485+
Preserved = false;
486+
}
487+
}
488+
489+
return Preserved;
490+
}
491+
438492
// Write the json data into the specifed file.
439493
static void writeJSON(StringRef OrigDIVerifyBugsReportFilePath,
440494
StringRef FileNameFromCU, StringRef NameOfWrappedPass,
@@ -484,18 +538,40 @@ bool llvm::checkDebugInfoMetadata(Module &M,
484538
auto *SP = F.getSubprogram();
485539
DIPreservationAfter[NameOfWrappedPass].DIFunctions.insert(
486540
{F.getName(), SP});
487-
if (SP)
541+
542+
if (SP) {
488543
LLVM_DEBUG(dbgs() << " Collecting subprogram: " << *SP << '\n');
544+
for (const DINode *DN : SP->getRetainedNodes()) {
545+
if (const auto *DV = dyn_cast<DILocalVariable>(DN)) {
546+
DIPreservationAfter[NameOfWrappedPass].DIVariables[DV] = 0;
547+
}
548+
}
549+
}
489550

490551
for (BasicBlock &BB : F) {
491-
// Collect debug locations (!dbg attachments).
492-
// TODO: Collect dbg.values.
552+
// Collect debug locations (!dbg) and debug variable intrinsics.
493553
for (Instruction &I : BB) {
494554
// Skip PHIs.
495555
if (isa<PHINode>(I))
496556
continue;
497557

498-
// Skip debug instructions.
558+
// Collect dbg.values and dbg.declares.
559+
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I)) {
560+
if (!SP)
561+
continue;
562+
// Skip inlined variables.
563+
if (I.getDebugLoc().getInlinedAt())
564+
continue;
565+
// Skip undef values.
566+
if (DVI->isUndef())
567+
continue;
568+
569+
auto *Var = DVI->getVariable();
570+
DIPreservationAfter[NameOfWrappedPass].DIVariables[Var]++;
571+
continue;
572+
}
573+
574+
// Skip debug instructions other than dbg.value and dbg.declare.
499575
if (isa<DbgInfoIntrinsic>(&I))
500576
continue;
501577

@@ -522,6 +598,9 @@ bool llvm::checkDebugInfoMetadata(Module &M,
522598

523599
auto InstToDelete = DIPreservationAfter[NameOfWrappedPass].InstToDelete;
524600

601+
auto DIVarsBefore = DIPreservationMap[NameOfWrappedPass].DIVariables;
602+
auto DIVarsAfter = DIPreservationAfter[NameOfWrappedPass].DIVariables;
603+
525604
bool ShouldWriteIntoJSON = !OrigDIVerifyBugsReportFilePath.empty();
526605
llvm::json::Array Bugs;
527606

@@ -531,7 +610,11 @@ bool llvm::checkDebugInfoMetadata(Module &M,
531610
bool ResultForInsts = checkInstructions(
532611
DILocsBefore, DILocsAfter, InstToDelete, NameOfWrappedPass,
533612
FileNameFromCU, ShouldWriteIntoJSON, Bugs);
534-
bool Result = ResultForFunc && ResultForInsts;
613+
614+
bool ResultForVars = checkVars(DIVarsBefore, DIVarsAfter, NameOfWrappedPass,
615+
FileNameFromCU, ShouldWriteIntoJSON, Bugs);
616+
617+
bool Result = ResultForFunc && ResultForInsts && ResultForVars;
535618

536619
StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner;
537620
if (ShouldWriteIntoJSON && !Bugs.empty())

llvm/test/tools/llvm-original-di-preservation/Inputs/expected-sample.html

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,32 @@
127127
<td colspan='2'> No bugs found </td>
128128
</tr>
129129
</table>
130+
<br>
131+
<br>
132+
<table>
133+
<caption><b>Variable Location Bugs found by the Debugify</b></caption>
134+
<tr>
135+
<th>File</th>
136+
<th>LLVM Pass Name</th>
137+
<th>Variable</th>
138+
<th>Function</th>
139+
<th>Action</th>
140+
</tr>
141+
<tr>
142+
<td colspan='4'> No bugs found </td>
143+
</tr>
144+
</table>
145+
<br>
146+
<table>
147+
<caption><b>Summary of Variable Location Bugs</b></caption>
148+
<tr>
149+
<th>LLVM Pass Name</th>
150+
<th>Number of bugs</th>
151+
</tr>
152+
<tr>
153+
<tr>
154+
<td colspan='2'> No bugs found </td>
155+
</tr>
156+
</table>
130157
</body>
131158
</html>

llvm/unittests/Transforms/Utils/DebugifyTest.cpp

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "llvm/ADT/SmallVector.h"
910
#include "llvm/AsmParser/Parser.h"
1011
#include "llvm/IR/DebugInfoMetadata.h"
12+
#include "llvm/IR/IntrinsicInst.h"
1113
#include "llvm/IR/LegacyPassManager.h"
1214
#include "llvm/Support/SourceMgr.h"
1315
#include "llvm/Transforms/Utils/Debugify.h"
@@ -41,13 +43,39 @@ struct DebugInfoDrop : public FunctionPass {
4143

4244
return false;
4345
}
46+
4447
void getAnalysisUsage(AnalysisUsage &AU) const override {
4548
AU.setPreservesCFG();
4649
}
4750

4851
DebugInfoDrop() : FunctionPass(ID) {}
4952
};
5053

54+
struct DebugValueDrop : public FunctionPass {
55+
static char ID;
56+
bool runOnFunction(Function &F) override {
57+
SmallVector<DbgVariableIntrinsic *, 4> Dbgs;
58+
for (BasicBlock &BB : F) {
59+
// Remove dbg var intrinsics.
60+
for (Instruction &I : BB) {
61+
if (auto *DVI = dyn_cast<DbgVariableIntrinsic>(&I))
62+
Dbgs.push_back(DVI);
63+
}
64+
}
65+
66+
for (auto &I : Dbgs)
67+
I->eraseFromParent();
68+
69+
return true;
70+
}
71+
72+
void getAnalysisUsage(AnalysisUsage &AU) const override {
73+
AU.setPreservesCFG();
74+
}
75+
76+
DebugValueDrop() : FunctionPass(ID) {}
77+
};
78+
5179
struct DebugInfoDummyAnalysis : public FunctionPass {
5280
static char ID;
5381
bool runOnFunction(Function &F) override {
@@ -63,6 +91,7 @@ struct DebugInfoDummyAnalysis : public FunctionPass {
6391
}
6492

6593
char DebugInfoDrop::ID = 0;
94+
char DebugValueDrop::ID = 0;
6695
char DebugInfoDummyAnalysis::ID = 0;
6796

6897
TEST(DebugInfoDrop, DropOriginalDebugInfo) {
@@ -116,6 +145,59 @@ TEST(DebugInfoDrop, DropOriginalDebugInfo) {
116145
EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
117146
}
118147

148+
TEST(DebugValueDrop, DropOriginalDebugValues) {
149+
LLVMContext C;
150+
std::unique_ptr<Module> M = parseIR(C, R"(
151+
define i16 @f(i16 %a) !dbg !6 {
152+
%b = add i16 %a, 1, !dbg !11
153+
call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
154+
ret i16 0, !dbg !11
155+
}
156+
declare void @llvm.dbg.value(metadata, metadata, metadata)
157+
158+
!llvm.dbg.cu = !{!0}
159+
!llvm.module.flags = !{!5}
160+
161+
!0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
162+
!1 = !DIFile(filename: "t.ll", directory: "/")
163+
!2 = !{}
164+
!5 = !{i32 2, !"Debug Info Version", i32 3}
165+
!6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
166+
!7 = !DISubroutineType(types: !2)
167+
!8 = !{!9}
168+
!9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)
169+
!10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
170+
!11 = !DILocation(line: 1, column: 1, scope: !6)
171+
)");
172+
173+
DebugValueDrop *P = new DebugValueDrop();
174+
175+
DebugInfoPerPassMap DIPreservationMap;
176+
DebugifyCustomPassManager Passes;
177+
Passes.setDIPreservationMap(DIPreservationMap);
178+
Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
179+
&(Passes.getDebugInfoPerPassMap())));
180+
Passes.add(P);
181+
Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
182+
DebugifyMode::OriginalDebugInfo,
183+
&(Passes.getDebugInfoPerPassMap())));
184+
185+
testing::internal::CaptureStderr();
186+
Passes.run(*M);
187+
188+
std::string StdOut = testing::internal::GetCapturedStderr();
189+
190+
std::string ErrorForSP = "ERROR: dropped DISubprogram of";
191+
std::string WarningForLoc = "WARNING: dropped DILocation of";
192+
std::string WarningForVars = "WARNING: drops dbg.value()/dbg.declare() for";
193+
std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";
194+
195+
EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);
196+
EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);
197+
EXPECT_TRUE(StdOut.find(WarningForVars) != std::string::npos);
198+
EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
199+
}
200+
119201
TEST(DebugInfoDummyAnalysis, PreserveOriginalDebugInfo) {
120202
LLVMContext C;
121203
std::unique_ptr<Module> M = parseIR(C, R"(
@@ -160,10 +242,12 @@ TEST(DebugInfoDummyAnalysis, PreserveOriginalDebugInfo) {
160242

161243
std::string ErrorForSP = "ERROR: dropped DISubprogram of";
162244
std::string WarningForLoc = "WARNING: dropped DILocation of";
245+
std::string WarningForVars = "WARNING: drops dbg.value()/dbg.declare() for";
163246
std::string FinalResult = "CheckModuleDebugify (original debuginfo): PASS";
164247

165248
EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);
166249
EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);
250+
EXPECT_TRUE(StdOut.find(WarningForVars) == std::string::npos);
167251
EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
168252
}
169253

0 commit comments

Comments
 (0)