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 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; 35 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 47 void getAnalysisUsage(AnalysisUsage &AU) const override { 48 AU.setPreservesCFG(); 49 } 50 51 DebugInfoDrop() : FunctionPass(ID) {} 52 }; 53 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 // If there are any non-intrinsic records (DPValues), drop those too. 64 I.dropDbgValues(); 65 } 66 } 67 68 for (auto &I : Dbgs) 69 I->eraseFromParent(); 70 71 return true; 72 } 73 74 void getAnalysisUsage(AnalysisUsage &AU) const override { 75 AU.setPreservesCFG(); 76 } 77 78 DebugValueDrop() : FunctionPass(ID) {} 79 }; 80 81 struct DebugInfoDummyAnalysis : public FunctionPass { 82 static char ID; 83 bool runOnFunction(Function &F) override { 84 // Do nothing, so debug info stays untouched. 85 return false; 86 } 87 void getAnalysisUsage(AnalysisUsage &AU) const override { 88 AU.setPreservesAll(); 89 } 90 91 DebugInfoDummyAnalysis() : FunctionPass(ID) {} 92 }; 93 } 94 95 char DebugInfoDrop::ID = 0; 96 char DebugValueDrop::ID = 0; 97 char DebugInfoDummyAnalysis::ID = 0; 98 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 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 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