1 //===- DebugifyTest.cpp - Debugify unit tests -----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "llvm/ADT/SmallVector.h"
10 #include "llvm/AsmParser/Parser.h"
11 #include "llvm/IR/DebugInfoMetadata.h"
12 #include "llvm/IR/IntrinsicInst.h"
13 #include "llvm/IR/LegacyPassManager.h"
14 #include "llvm/Support/SourceMgr.h"
15 #include "llvm/Transforms/Utils/Debugify.h"
16 #include "gtest/gtest.h"
17
18 using namespace llvm;
19
parseIR(LLVMContext & C,const char * IR)20 static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
21 SMDiagnostic Err;
22 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
23 if (!Mod)
24 Err.print("DebugifyTest", errs());
25 return Mod;
26 }
27
28 namespace llvm {
29 void initializeDebugInfoDropPass(PassRegistry &);
30 void initializeDebugInfoDummyAnalysisPass(PassRegistry &);
31
32 namespace {
33 struct DebugInfoDrop : public FunctionPass {
34 static char ID;
runOnFunctionllvm::__anonf5b6608c0111::DebugInfoDrop35 bool runOnFunction(Function &F) override {
36 // Drop DISubprogram.
37 F.setSubprogram(nullptr);
38 for (BasicBlock &BB : F) {
39 // Remove debug locations.
40 for (Instruction &I : BB)
41 I.setDebugLoc(DebugLoc());
42 }
43
44 return false;
45 }
46
getAnalysisUsagellvm::__anonf5b6608c0111::DebugInfoDrop47 void getAnalysisUsage(AnalysisUsage &AU) const override {
48 AU.setPreservesCFG();
49 }
50
DebugInfoDropllvm::__anonf5b6608c0111::DebugInfoDrop51 DebugInfoDrop() : FunctionPass(ID) {}
52 };
53
54 struct DebugValueDrop : public FunctionPass {
55 static char ID;
runOnFunctionllvm::__anonf5b6608c0111::DebugValueDrop56 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 // If there are any non-intrinsic records (DbgRecords), drop those too.
64 I.dropDbgRecords();
65 }
66 }
67
68 for (auto &I : Dbgs)
69 I->eraseFromParent();
70
71 return true;
72 }
73
getAnalysisUsagellvm::__anonf5b6608c0111::DebugValueDrop74 void getAnalysisUsage(AnalysisUsage &AU) const override {
75 AU.setPreservesCFG();
76 }
77
DebugValueDropllvm::__anonf5b6608c0111::DebugValueDrop78 DebugValueDrop() : FunctionPass(ID) {}
79 };
80
81 struct DebugInfoDummyAnalysis : public FunctionPass {
82 static char ID;
runOnFunctionllvm::__anonf5b6608c0111::DebugInfoDummyAnalysis83 bool runOnFunction(Function &F) override {
84 // Do nothing, so debug info stays untouched.
85 return false;
86 }
getAnalysisUsagellvm::__anonf5b6608c0111::DebugInfoDummyAnalysis87 void getAnalysisUsage(AnalysisUsage &AU) const override {
88 AU.setPreservesAll();
89 }
90
DebugInfoDummyAnalysisllvm::__anonf5b6608c0111::DebugInfoDummyAnalysis91 DebugInfoDummyAnalysis() : FunctionPass(ID) {}
92 };
93 }
94
95 char DebugInfoDrop::ID = 0;
96 char DebugValueDrop::ID = 0;
97 char DebugInfoDummyAnalysis::ID = 0;
98
TEST(DebugInfoDrop,DropOriginalDebugInfo)99 TEST(DebugInfoDrop, DropOriginalDebugInfo) {
100 LLVMContext C;
101 std::unique_ptr<Module> M = parseIR(C, R"(
102 define i16 @f(i16 %a) !dbg !6 {
103 %b = add i16 %a, 1, !dbg !11
104 call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
105 ret i16 0, !dbg !11
106 }
107 declare void @llvm.dbg.value(metadata, metadata, metadata)
108
109 !llvm.dbg.cu = !{!0}
110 !llvm.module.flags = !{!5}
111
112 !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
113 !1 = !DIFile(filename: "t.ll", directory: "/")
114 !2 = !{}
115 !5 = !{i32 2, !"Debug Info Version", i32 3}
116 !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
117 !7 = !DISubroutineType(types: !2)
118 !8 = !{!9}
119 !9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)
120 !10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
121 !11 = !DILocation(line: 1, column: 1, scope: !6)
122 )");
123
124 DebugInfoDrop *P = new DebugInfoDrop();
125
126 DebugInfoPerPass DIBeforePass;
127 DebugifyCustomPassManager Passes;
128 Passes.setDebugInfoBeforePass(DIBeforePass);
129 Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
130 &(Passes.getDebugInfoPerPass())));
131 Passes.add(P);
132 Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
133 DebugifyMode::OriginalDebugInfo,
134 &(Passes.getDebugInfoPerPass())));
135
136 testing::internal::CaptureStderr();
137 Passes.run(*M);
138
139 std::string StdOut = testing::internal::GetCapturedStderr();
140
141 std::string ErrorForSP = "ERROR: dropped DISubprogram of";
142 std::string WarningForLoc = "WARNING: dropped DILocation of";
143 std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";
144
145 EXPECT_TRUE(StdOut.find(ErrorForSP) != std::string::npos);
146 EXPECT_TRUE(StdOut.find(WarningForLoc) != std::string::npos);
147 EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
148 }
149
TEST(DebugValueDrop,DropOriginalDebugValues)150 TEST(DebugValueDrop, DropOriginalDebugValues) {
151 LLVMContext C;
152 std::unique_ptr<Module> M = parseIR(C, R"(
153 define i16 @f(i16 %a) !dbg !6 {
154 %b = add i16 %a, 1, !dbg !11
155 call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
156 ret i16 0, !dbg !11
157 }
158 declare void @llvm.dbg.value(metadata, metadata, metadata)
159
160 !llvm.dbg.cu = !{!0}
161 !llvm.module.flags = !{!5}
162
163 !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
164 !1 = !DIFile(filename: "t.ll", directory: "/")
165 !2 = !{}
166 !5 = !{i32 2, !"Debug Info Version", i32 3}
167 !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
168 !7 = !DISubroutineType(types: !2)
169 !8 = !{!9}
170 !9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)
171 !10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
172 !11 = !DILocation(line: 1, column: 1, scope: !6)
173 )");
174
175 DebugValueDrop *P = new DebugValueDrop();
176
177 DebugInfoPerPass DIBeforePass;
178 DebugifyCustomPassManager Passes;
179 Passes.setDebugInfoBeforePass(DIBeforePass);
180 Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
181 &(Passes.getDebugInfoPerPass())));
182 Passes.add(P);
183 Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
184 DebugifyMode::OriginalDebugInfo,
185 &(Passes.getDebugInfoPerPass())));
186
187 testing::internal::CaptureStderr();
188 Passes.run(*M);
189
190 std::string StdOut = testing::internal::GetCapturedStderr();
191
192 std::string ErrorForSP = "ERROR: dropped DISubprogram of";
193 std::string WarningForLoc = "WARNING: dropped DILocation of";
194 std::string WarningForVars = "WARNING: drops dbg.value()/dbg.declare() for";
195 std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";
196
197 EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);
198 EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);
199 EXPECT_TRUE(StdOut.find(WarningForVars) != std::string::npos);
200 EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
201 }
202
TEST(DebugInfoDummyAnalysis,PreserveOriginalDebugInfo)203 TEST(DebugInfoDummyAnalysis, PreserveOriginalDebugInfo) {
204 LLVMContext C;
205 std::unique_ptr<Module> M = parseIR(C, R"(
206 define i32 @g(i32 %b) !dbg !6 {
207 %c = add i32 %b, 1, !dbg !11
208 call void @llvm.dbg.value(metadata i32 %c, metadata !9, metadata !DIExpression()), !dbg !11
209 ret i32 1, !dbg !11
210 }
211 declare void @llvm.dbg.value(metadata, metadata, metadata)
212
213 !llvm.dbg.cu = !{!0}
214 !llvm.module.flags = !{!5}
215
216 !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
217 !1 = !DIFile(filename: "test.ll", directory: "/")
218 !2 = !{}
219 !5 = !{i32 2, !"Debug Info Version", i32 3}
220 !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
221 !7 = !DISubroutineType(types: !2)
222 !8 = !{!9}
223 !9 = !DILocalVariable(name: "c", scope: !6, file: !1, line: 1, type: !10)
224 !10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
225 !11 = !DILocation(line: 1, column: 1, scope: !6)
226 )");
227
228 DebugInfoDummyAnalysis *P = new DebugInfoDummyAnalysis();
229
230 DebugInfoPerPass DIBeforePass;
231 DebugifyCustomPassManager Passes;
232 Passes.setDebugInfoBeforePass(DIBeforePass);
233 Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
234 &(Passes.getDebugInfoPerPass())));
235 Passes.add(P);
236 Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
237 DebugifyMode::OriginalDebugInfo,
238 &(Passes.getDebugInfoPerPass())));
239
240 testing::internal::CaptureStderr();
241 Passes.run(*M);
242
243 std::string StdOut = testing::internal::GetCapturedStderr();
244
245 std::string ErrorForSP = "ERROR: dropped DISubprogram of";
246 std::string WarningForLoc = "WARNING: dropped DILocation of";
247 std::string WarningForVars = "WARNING: drops dbg.value()/dbg.declare() for";
248 std::string FinalResult = "CheckModuleDebugify (original debuginfo): PASS";
249
250 EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);
251 EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);
252 EXPECT_TRUE(StdOut.find(WarningForVars) == std::string::npos);
253 EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
254 }
255
256 } // end namespace llvm
257
258 INITIALIZE_PASS_BEGIN(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass",
259 false, false)
260 INITIALIZE_PASS_END(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass", false,
261 false)
262
263 INITIALIZE_PASS_BEGIN(DebugInfoDummyAnalysis, "debuginfodummyanalysispass",
264 "debuginfodummyanalysispass", false, false)
265 INITIALIZE_PASS_END(DebugInfoDummyAnalysis, "debuginfodummyanalysispass",
266 "debuginfodummyanalysispass", false, false)
267