llvm.org GIT mirror llvm / 2cac108
[Debugify] Add a pass to test debug info preservation The Debugify pass synthesizes debug info for IR. It's paired with a CheckDebugify pass which determines how much of the original debug info is preserved. These passes make it easier to create targeted tests for debug info preservation. Here is the Debugify algorithm: NextLine = 1 for (Instruction &I : M) attach DebugLoc(NextLine++) to I NextVar = 1 for (Instruction &I : M) if (canAttachDebugValue(I)) attach dbg.value(NextVar++) to I The CheckDebugify pass expects contiguous ranges of DILocations and DILocalVariables. If it fails to find all of the expected debug info, it prints a specific error to stderr which can be FileChecked. This was discussed on llvm-dev in the thread: "Passes to add/validate synthetic debug info" Differential Revision: https://reviews.llvm.org/D40512 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@320202 91177308-0d34-0410-b5e6-96231b3b80d8 Vedant Kumar 1 year, 11 months ago
5 changed file(s) with 287 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
4747 information to be fed back into TableGen. Out-of-tree targets will need to add
4848 the name used in the `def X : Target` definition to the call to
4949 `RegisterTarget`.
50
51 * The ``Debugify`` pass was added to ``opt`` to facilitate testing of debug
52 info preservation. This pass attaches synthetic ``DILocations`` and
53 ``DIVariables`` to the instructions in a ``Module``. The ``CheckDebugify``
54 pass determines how much of the metadata is lost.
5055
5156 * Note..
5257
0 ; RUN: opt -debugify -S -o - < %s | FileCheck %s
1
2 ; RUN: opt -debugify -debugify -S -o - < %s 2>&1 | \
3 ; RUN: FileCheck %s -check-prefix=CHECK-REPEAT
4
5 ; RUN: opt -debugify -check-debugify -S -o - < %s | \
6 ; RUN: FileCheck %s -implicit-check-not="CheckDebugify: FAIL"
7
8 ; RUN: opt -debugify -strip -check-debugify -S -o - < %s | \
9 ; RUN: FileCheck %s -check-prefix=CHECK-FAIL
10
11 ; CHECK-LABEL: define void @foo
12 define void @foo() {
13 ; CHECK: ret void, !dbg ![[RET1:.*]]
14 ret void
15 }
16
17 ; CHECK-LABEL: define i32 @bar
18 define i32 @bar() {
19 ; CHECK: call void @foo(), !dbg ![[CALL1:.*]]
20 call void @foo()
21
22 ; CHECK: add i32 0, 1, !dbg ![[ADD1:.*]]
23 %sum = add i32 0, 1
24
25 ; CHECK: ret i32 0, !dbg ![[RET2:.*]]
26 ret i32 0
27 }
28
29 ; CHECK-DAG: !llvm.dbg.cu = !{![[CU:.*]]}
30 ; CHECK-DAG: !llvm.debugify = !{![[NUM_INSTS:.*]], ![[NUM_VARS:.*]]}
31
32 ; CHECK-DAG: ![[CU]] = distinct !DICompileUnit(language: DW_LANG_C, file: {{.*}}, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: {{.*}})
33 ; CHECK-DAG: !DIFile(filename: "", directory: "/")
34 ; CHECK-DAG: distinct !DISubprogram(name: "foo", linkageName: "foo", scope: null, file: {{.*}}, line: 1, type: {{.*}}, isLocal: false, isDefinition: true, scopeLine: 1, isOptimized: true, unit: {{.*}}, variables: {{.*}})
35 ; CHECK-DAG: distinct !DISubprogram(name: "bar", linkageName: "bar", scope: null, file: {{.*}}, line: 2, type: {{.*}}, isLocal: false, isDefinition: true, scopeLine: 2, isOptimized: true, unit: {{.*}}, variables: {{.*}})
36
37 ; --- DILocations
38 ; CHECK-DAG: ![[RET1]] = !DILocation(line: 1, column: 1
39 ; CHECK-DAG: ![[CALL1]] = !DILocation(line: 2, column: 1
40 ; CHECK-DAG: ![[ADD1]] = !DILocation(line: 3, column: 1
41 ; CHECK-DAG: ![[RET2]] = !DILocation(line: 4, column: 1
42
43 ; --- DILocalVariables
44 ; CHECK-DAG: ![[TY32:.*]] = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
45 ; CHECK-DAG: !DILocalVariable(name: "1", scope: {{.*}}, file: {{.*}}, line: 3, type: ![[TY32]])
46
47 ; --- Metadata counts
48 ; CHECK-DAG: ![[NUM_INSTS]] = !{i32 4}
49 ; CHECK-DAG: ![[NUM_VARS]] = !{i32 1}
50
51 ; --- Repeat case
52 ; CHECK-REPEAT: Debugify: Skipping module with debug info
53
54 ; --- Failure case
55 ; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- ret void
56 ; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- call void @foo()
57 ; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- {{.*}} add i32 0, 1
58 ; CHECK-FAIL: ERROR: Instruction with empty DebugLoc -- ret i32 0
59 ; CHECK-FAIL: WARNING: Missing line 1
60 ; CHECK-FAIL: WARNING: Missing line 2
61 ; CHECK-FAIL: WARNING: Missing line 3
62 ; CHECK-FAIL: WARNING: Missing line 4
63 ; CHECK-FAIL: ERROR: Missing variable 1
64 ; CHECK-FAIL: CheckDebugify: FAIL
0 ; Simple sanity check testcase. Both alloca's should be eliminated.
1 ; RUN: opt < %s -mem2reg -S | not grep alloca
1 ; RUN: opt < %s -debugify -mem2reg -check-debugify -S | FileCheck %s
2
3 ; CHECK-NOT: alloca
4 ; CHECK: CheckDebugify: PASS
25
36 define double @testfunc(i32 %i, double %j) {
47 %I = alloca i32 ; [#uses=4]
2424 add_llvm_tool(opt
2525 AnalysisWrappers.cpp
2626 BreakpointPrinter.cpp
27 Debugify.cpp
2728 GraphPrinters.cpp
2829 NewPMDriver.cpp
2930 PassPrinters.cpp
0 //===- Debugify.cpp - Attach synthetic debug info to everything -----------===//
1 //
2 // The LLVM Compiler Infrastructure
3 //
4 // This file is distributed under the University of Illinois Open Source
5 // License. See LICENSE.TXT for details.
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file This pass attaches synthetic debug info to everything. It can be used
10 /// to create targeted tests for debug info preservation.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/ADT/BitVector.h"
15 #include "llvm/ADT/StringExtras.h"
16 #include "llvm/IR/BasicBlock.h"
17 #include "llvm/IR/Constants.h"
18 #include "llvm/IR/DIBuilder.h"
19 #include "llvm/IR/DebugInfo.h"
20 #include "llvm/IR/Function.h"
21 #include "llvm/IR/GlobalVariable.h"
22 #include "llvm/IR/InstIterator.h"
23 #include "llvm/IR/Instruction.h"
24 #include "llvm/IR/Instructions.h"
25 #include "llvm/IR/IntrinsicInst.h"
26 #include "llvm/IR/Module.h"
27 #include "llvm/IR/Type.h"
28 #include "llvm/Pass.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include "llvm/Transforms/IPO.h"
31
32 using namespace llvm;
33
34 namespace {
35
36 bool applyDebugifyMetadata(Module &M) {
37 // Skip modules with debug info.
38 if (M.getNamedMetadata("llvm.dbg.cu")) {
39 errs() << "Debugify: Skipping module with debug info\n";
40 return false;
41 }
42
43 DIBuilder DIB(M);
44 LLVMContext &Ctx = M.getContext();
45
46 // Get a DIType which corresponds to Ty.
47 DenseMap TypeCache;
48 auto getCachedDIType = [&](Type *Ty) -> DIType * {
49 uint64_t Size = M.getDataLayout().getTypeAllocSizeInBits(Ty);
50 DIType *&DTy = TypeCache[Size];
51 if (!DTy) {
52 std::string Name = "ty" + utostr(Size);
53 DTy = DIB.createBasicType(Name, Size, dwarf::DW_ATE_unsigned);
54 }
55 return DTy;
56 };
57
58 unsigned NextLine = 1;
59 unsigned NextVar = 1;
60 auto File = DIB.createFile(M.getName(), "/");
61 auto CU =
62 DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile(M.getName(), "/"),
63 "debugify", /*isOptimized=*/true, "", 0);
64
65 // Visit each instruction.
66 for (Function &F : M) {
67 if (F.isDeclaration())
68 continue;
69
70 auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
71 bool IsLocalToUnit = F.hasPrivateLinkage() || F.hasInternalLinkage();
72 auto SP =
73 DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, SPType,
74 IsLocalToUnit, F.hasExactDefinition(), NextLine,
75 DINode::FlagZero, /*isOptimized=*/true);
76 F.setSubprogram(SP);
77 for (BasicBlock &BB : F) {
78 // Attach debug locations.
79 for (Instruction &I : BB)
80 I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP));
81
82 // Attach debug values.
83 for (Instruction &I : BB) {
84 // Skip void-valued instructions.
85 if (I.getType()->isVoidTy())
86 continue;
87
88 // Skip the terminator instruction and any just-inserted intrinsics.
89 if (isa(&I) || isa(&I))
90 break;
91
92 std::string Name = utostr(NextVar++);
93 const DILocation *Loc = I.getDebugLoc().get();
94 auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),
95 getCachedDIType(I.getType()),
96 /*AlwaysPreserve=*/true);
97 DIB.insertDbgValueIntrinsic(&I, LocalVar, DIB.createExpression(), Loc,
98 BB.getTerminator());
99 }
100 }
101 DIB.finalizeSubprogram(SP);
102 }
103 DIB.finalize();
104
105 // Track the number of distinct lines and variables.
106 NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.debugify");
107 auto *IntTy = Type::getInt32Ty(Ctx);
108 auto addDebugifyOperand = [&](unsigned N) {
109 NMD->addOperand(MDNode::get(
110 Ctx, ValueAsMetadata::getConstant(ConstantInt::get(IntTy, N))));
111 };
112 addDebugifyOperand(NextLine - 1); // Original number of lines.
113 addDebugifyOperand(NextVar - 1); // Original number of variables.
114 return true;
115 }
116
117 void checkDebugifyMetadata(Module &M) {
118 // Skip modules without debugify metadata.
119 NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");
120 if (!NMD)
121 return;
122
123 auto getDebugifyOperand = [&](unsigned Idx) -> unsigned {
124 return mdconst::extract(NMD->getOperand(Idx)->getOperand(0))
125 ->getZExtValue();
126 };
127 unsigned OriginalNumLines = getDebugifyOperand(0);
128 unsigned OriginalNumVars = getDebugifyOperand(1);
129 bool HasErrors = false;
130
131 // Find missing lines.
132 BitVector MissingLines{OriginalNumLines, true};
133 for (Function &F : M) {
134 for (Instruction &I : instructions(F)) {
135 if (isa(&I))
136 continue;
137
138 auto DL = I.getDebugLoc();
139 if (DL) {
140 MissingLines.reset(DL.getLine() - 1);
141 continue;
142 }
143
144 outs() << "ERROR: Instruction with empty DebugLoc -- ";
145 I.print(outs());
146 outs() << "\n";
147 HasErrors = true;
148 }
149 }
150 for (unsigned Idx : MissingLines.set_bits())
151 outs() << "WARNING: Missing line " << Idx + 1 << "\n";
152
153 // Find missing variables.
154 BitVector MissingVars{OriginalNumVars, true};
155 for (Function &F : M) {
156 for (Instruction &I : instructions(F)) {
157 auto *DVI = dyn_cast(&I);
158 if (!DVI)
159 continue;
160
161 unsigned Var = ~0U;
162 (void)to_integer(DVI->getVariable()->getName(), Var, 10);
163 assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable");
164 MissingVars.reset(Var - 1);
165 }
166 }
167 for (unsigned Idx : MissingVars.set_bits())
168 outs() << "ERROR: Missing variable " << Idx + 1 << "\n";
169 HasErrors |= MissingVars.count() > 0;
170
171 outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n";
172 }
173
174 /// Attach synthetic debug info to everything.
175 struct DebugifyPass : public ModulePass {
176 bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); }
177
178 DebugifyPass() : ModulePass(ID) {}
179
180 void getAnalysisUsage(AnalysisUsage &AU) const override {
181 AU.setPreservesAll();
182 }
183
184 static char ID; // Pass identification.
185 };
186
187 /// Check debug info inserted by -debugify for completeness.
188 struct CheckDebugifyPass : public ModulePass {
189 bool runOnModule(Module &M) override {
190 checkDebugifyMetadata(M);
191 return false;
192 }
193
194 CheckDebugifyPass() : ModulePass(ID) {}
195
196 void getAnalysisUsage(AnalysisUsage &AU) const override {
197 AU.setPreservesAll();
198 }
199
200 static char ID; // Pass identification.
201 };
202
203 } // end anonymous namespace
204
205 char DebugifyPass::ID = 0;
206 static RegisterPass X("debugify",
207 "Attach debug info to everything");
208
209 char CheckDebugifyPass::ID = 0;
210 static RegisterPass Y("check-debugify",
211 "Check debug info from -debugify");