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::__anonea65dc140111::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::__anonea65dc140111::DebugInfoDrop47   void getAnalysisUsage(AnalysisUsage &AU) const override {
48     AU.setPreservesCFG();
49   }
50 
DebugInfoDropllvm::__anonea65dc140111::DebugInfoDrop51   DebugInfoDrop() : FunctionPass(ID) {}
52 };
53 
54 struct DebugValueDrop : public FunctionPass {
55   static char ID;
runOnFunctionllvm::__anonea65dc140111::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       }
64     }
65 
66     for (auto &I : Dbgs)
67       I->eraseFromParent();
68 
69     return true;
70   }
71 
getAnalysisUsagellvm::__anonea65dc140111::DebugValueDrop72   void getAnalysisUsage(AnalysisUsage &AU) const override {
73     AU.setPreservesCFG();
74   }
75 
DebugValueDropllvm::__anonea65dc140111::DebugValueDrop76   DebugValueDrop() : FunctionPass(ID) {}
77 };
78 
79 struct DebugInfoDummyAnalysis : public FunctionPass {
80   static char ID;
runOnFunctionllvm::__anonea65dc140111::DebugInfoDummyAnalysis81   bool runOnFunction(Function &F) override {
82     // Do nothing, so debug info stays untouched.
83     return false;
84   }
getAnalysisUsagellvm::__anonea65dc140111::DebugInfoDummyAnalysis85   void getAnalysisUsage(AnalysisUsage &AU) const override {
86     AU.setPreservesAll();
87   }
88 
DebugInfoDummyAnalysisllvm::__anonea65dc140111::DebugInfoDummyAnalysis89   DebugInfoDummyAnalysis() : FunctionPass(ID) {}
90 };
91 }
92 
93 char DebugInfoDrop::ID = 0;
94 char DebugValueDrop::ID = 0;
95 char DebugInfoDummyAnalysis::ID = 0;
96 
TEST(DebugInfoDrop,DropOriginalDebugInfo)97 TEST(DebugInfoDrop, DropOriginalDebugInfo) {
98   LLVMContext C;
99   std::unique_ptr<Module> M = parseIR(C, R"(
100     define i16 @f(i16 %a) !dbg !6 {
101       %b = add i16 %a, 1, !dbg !11
102       call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
103       ret i16 0, !dbg !11
104     }
105     declare void @llvm.dbg.value(metadata, metadata, metadata)
106 
107     !llvm.dbg.cu = !{!0}
108     !llvm.module.flags = !{!5}
109 
110     !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
111     !1 = !DIFile(filename: "t.ll", directory: "/")
112     !2 = !{}
113     !5 = !{i32 2, !"Debug Info Version", i32 3}
114     !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
115     !7 = !DISubroutineType(types: !2)
116     !8 = !{!9}
117     !9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)
118     !10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
119     !11 = !DILocation(line: 1, column: 1, scope: !6)
120   )");
121 
122   DebugInfoDrop *P = new DebugInfoDrop();
123 
124   DebugInfoPerPassMap DIPreservationMap;
125   DebugifyCustomPassManager Passes;
126   Passes.setDIPreservationMap(DIPreservationMap);
127   Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
128                                       &(Passes.getDebugInfoPerPassMap())));
129   Passes.add(P);
130   Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
131                                            DebugifyMode::OriginalDebugInfo,
132                                            &(Passes.getDebugInfoPerPassMap())));
133 
134   testing::internal::CaptureStderr();
135   Passes.run(*M);
136 
137   std::string StdOut = testing::internal::GetCapturedStderr();
138 
139   std::string ErrorForSP = "ERROR:  dropped DISubprogram of";
140   std::string WarningForLoc = "WARNING:  dropped DILocation of";
141   std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";
142 
143   EXPECT_TRUE(StdOut.find(ErrorForSP) != std::string::npos);
144   EXPECT_TRUE(StdOut.find(WarningForLoc) != std::string::npos);
145   EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
146 }
147 
TEST(DebugValueDrop,DropOriginalDebugValues)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 
TEST(DebugInfoDummyAnalysis,PreserveOriginalDebugInfo)201 TEST(DebugInfoDummyAnalysis, PreserveOriginalDebugInfo) {
202   LLVMContext C;
203   std::unique_ptr<Module> M = parseIR(C, R"(
204     define i32 @g(i32 %b) !dbg !6 {
205       %c = add i32 %b, 1, !dbg !11
206       call void @llvm.dbg.value(metadata i32 %c, metadata !9, metadata !DIExpression()), !dbg !11
207       ret i32 1, !dbg !11
208     }
209     declare void @llvm.dbg.value(metadata, metadata, metadata)
210 
211     !llvm.dbg.cu = !{!0}
212     !llvm.module.flags = !{!5}
213 
214     !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
215     !1 = !DIFile(filename: "test.ll", directory: "/")
216     !2 = !{}
217     !5 = !{i32 2, !"Debug Info Version", i32 3}
218     !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
219     !7 = !DISubroutineType(types: !2)
220     !8 = !{!9}
221     !9 = !DILocalVariable(name: "c", scope: !6, file: !1, line: 1, type: !10)
222     !10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
223     !11 = !DILocation(line: 1, column: 1, scope: !6)
224   )");
225 
226   DebugInfoDummyAnalysis *P = new DebugInfoDummyAnalysis();
227 
228   DebugInfoPerPassMap DIPreservationMap;
229   DebugifyCustomPassManager Passes;
230   Passes.setDIPreservationMap(DIPreservationMap);
231   Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
232                                       &(Passes.getDebugInfoPerPassMap())));
233   Passes.add(P);
234   Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
235                                            DebugifyMode::OriginalDebugInfo,
236                                            &(Passes.getDebugInfoPerPassMap())));
237 
238   testing::internal::CaptureStderr();
239   Passes.run(*M);
240 
241   std::string StdOut = testing::internal::GetCapturedStderr();
242 
243   std::string ErrorForSP = "ERROR:  dropped DISubprogram of";
244   std::string WarningForLoc = "WARNING:  dropped DILocation of";
245   std::string WarningForVars = "WARNING:  drops dbg.value()/dbg.declare() for";
246   std::string FinalResult = "CheckModuleDebugify (original debuginfo): PASS";
247 
248   EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);
249   EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);
250   EXPECT_TRUE(StdOut.find(WarningForVars) == std::string::npos);
251   EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
252 }
253 
254 } // end namespace llvm
255 
256 INITIALIZE_PASS_BEGIN(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass",
257                       false, false)
258 INITIALIZE_PASS_END(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass", false,
259                     false)
260 
261 INITIALIZE_PASS_BEGIN(DebugInfoDummyAnalysis, "debuginfodummyanalysispass",
262                       "debuginfodummyanalysispass", false, false)
263 INITIALIZE_PASS_END(DebugInfoDummyAnalysis, "debuginfodummyanalysispass",
264                     "debuginfodummyanalysispass", false, false)
265