430 lines · cpp
1//===- unittests/IR/DroppedVariableStatsIRTest.cpp ------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "llvm/AsmParser/Parser.h"10#include "llvm/IR/Function.h"11#include "llvm/IR/InstIterator.h"12#include "llvm/IR/LegacyPassManager.h"13#include "llvm/IR/Module.h"14#include "llvm/Pass.h"15#include "llvm/PassRegistry.h"16#include "llvm/Passes/StandardInstrumentations.h"17#include "llvm/Support/SourceMgr.h"18#include "gtest/gtest.h"19#include <llvm/ADT/SmallString.h>20#include <llvm/IR/LLVMContext.h>21#include <llvm/IR/PassInstrumentation.h>22#include <llvm/IR/PassManager.h>23#include <llvm/IR/PassTimingInfo.h>24#include <llvm/Support/raw_ostream.h>25 26using namespace llvm;27namespace llvm {28void initializePassTest1Pass(PassRegistry &);29 30static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {31 SMDiagnostic Err;32 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);33 if (!Mod)34 Err.print("AbstractCallSiteTests", errs());35 return Mod;36}37} // namespace llvm38 39namespace {40 41// This test ensures that if a #dbg_value and an instruction that exists in the42// same scope as that #dbg_value are both deleted as a result of an optimization43// pass, debug information is considered not dropped.44TEST(DroppedVariableStatsIR, BothDeleted) {45 PassInstrumentationCallbacks PIC;46 PassInstrumentation PI(&PIC);47 48 LLVMContext C;49 50 const char *IR =51 R"(52 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)53 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {54 entry:55 #dbg_value(i32 %x, !15, !DIExpression(), !16)56 %add = add nsw i32 %x, 1, !dbg !1757 ret i32 058 }59 !llvm.dbg.cu = !{!0}60 !llvm.module.flags = !{!3}61 !llvm.ident = !{!8}62 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")63 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")64 !3 = !{i32 2, !"Debug Info Version", i32 3}65 !8 = !{!"clang"}66 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)67 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")68 !11 = !DISubroutineType(types: !12)69 !12 = !{!13, !13}70 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)71 !14 = !{!15}72 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)73 !16 = !DILocation(line: 0, scope: !9)74 !17 = !DILocation(line: 2, column: 11, scope: !9))";75 76 std::unique_ptr<llvm::Module> M = parseIR(C, IR);77 ASSERT_TRUE(M);78 79 DroppedVariableStatsIR Stats(true);80 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));81 82 // This loop simulates an IR pass that drops debug information.83 for (auto &F : *M) {84 for (auto &I : instructions(&F)) {85 I.dropDbgRecords();86 I.eraseFromParent();87 break;88 }89 break;90 }91 Stats.runAfterPass("Test",92 llvm::Any(const_cast<const llvm::Module *>(M.get())));93 ASSERT_EQ(Stats.getPassDroppedVariables(), false);94}95 96// This test ensures that if a #dbg_value is dropped after an optimization pass,97// but an instruction that shares the same scope as the #dbg_value still exists,98// debug information is conisdered dropped.99TEST(DroppedVariableStatsIR, DbgValLost) {100 PassInstrumentationCallbacks PIC;101 PassInstrumentation PI(&PIC);102 103 LLVMContext C;104 105 const char *IR =106 R"(107 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)108 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {109 entry:110 #dbg_value(i32 %x, !15, !DIExpression(), !16)111 %add = add nsw i32 %x, 1, !dbg !17112 ret i32 0113 }114 !llvm.dbg.cu = !{!0}115 !llvm.module.flags = !{!3}116 !llvm.ident = !{!8}117 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")118 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")119 !3 = !{i32 2, !"Debug Info Version", i32 3}120 !8 = !{!"clang"}121 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)122 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")123 !11 = !DISubroutineType(types: !12)124 !12 = !{!13, !13}125 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)126 !14 = !{!15}127 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)128 !16 = !DILocation(line: 0, scope: !9)129 !17 = !DILocation(line: 2, column: 11, scope: !9))";130 131 std::unique_ptr<llvm::Module> M = parseIR(C, IR);132 ASSERT_TRUE(M);133 134 DroppedVariableStatsIR Stats(true);135 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));136 137 // This loop simulates an IR pass that drops debug information.138 for (auto &F : *M) {139 for (auto &I : instructions(&F)) {140 I.dropDbgRecords();141 break;142 }143 break;144 }145 Stats.runAfterPass("Test",146 llvm::Any(const_cast<const llvm::Module *>(M.get())));147 ASSERT_EQ(Stats.getPassDroppedVariables(), true);148}149 150// This test ensures that if a #dbg_value is dropped after an optimization pass,151// but an instruction that has an unrelated scope as the #dbg_value still152// exists, debug information is conisdered not dropped.153TEST(DroppedVariableStatsIR, UnrelatedScopes) {154 PassInstrumentationCallbacks PIC;155 PassInstrumentation PI(&PIC);156 157 LLVMContext C;158 159 const char *IR =160 R"(161 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)162 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {163 entry:164 #dbg_value(i32 %x, !15, !DIExpression(), !16)165 %add = add nsw i32 %x, 1, !dbg !17166 ret i32 0167 }168 !llvm.dbg.cu = !{!0}169 !llvm.module.flags = !{!3}170 !llvm.ident = !{!8}171 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")172 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")173 !3 = !{i32 2, !"Debug Info Version", i32 3}174 !8 = !{!"clang"}175 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)176 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")177 !11 = !DISubroutineType(types: !12)178 !12 = !{!13, !13}179 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)180 !14 = !{!15}181 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)182 !16 = !DILocation(line: 0, scope: !9)183 !17 = !DILocation(line: 2, column: 11, scope: !18)184 !18 = distinct !DISubprogram(name: "bar", linkageName: "_Z3bari", scope: !10, file: !10, line: 11, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14))";185 186 std::unique_ptr<llvm::Module> M = parseIR(C, IR);187 ASSERT_TRUE(M);188 189 DroppedVariableStatsIR Stats(true);190 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));191 192 // This loop simulates an IR pass that drops debug information.193 for (auto &F : *M) {194 for (auto &I : instructions(&F)) {195 I.dropDbgRecords();196 break;197 }198 break;199 }200 Stats.runAfterPass("Test",201 llvm::Any(const_cast<const llvm::Module *>(M.get())));202 ASSERT_EQ(Stats.getPassDroppedVariables(), false);203}204 205// This test ensures that if a #dbg_value is dropped after an optimization pass,206// but an instruction that has a scope which is a child of the #dbg_value scope207// still exists, debug information is conisdered dropped.208TEST(DroppedVariableStatsIR, ChildScopes) {209 PassInstrumentationCallbacks PIC;210 PassInstrumentation PI(&PIC);211 212 LLVMContext C;213 214 const char *IR =215 R"(216 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)217 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {218 entry:219 #dbg_value(i32 %x, !15, !DIExpression(), !16)220 %add = add nsw i32 %x, 1, !dbg !17221 ret i32 0222 }223 !llvm.dbg.cu = !{!0}224 !llvm.module.flags = !{!3}225 !llvm.ident = !{!8}226 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")227 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")228 !3 = !{i32 2, !"Debug Info Version", i32 3}229 !8 = !{!"clang"}230 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)231 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")232 !11 = !DISubroutineType(types: !12)233 !12 = !{!13, !13}234 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)235 !14 = !{!15}236 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)237 !16 = !DILocation(line: 0, scope: !9)238 !17 = !DILocation(line: 2, column: 11, scope: !18)239 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28))";240 241 std::unique_ptr<llvm::Module> M = parseIR(C, IR);242 ASSERT_TRUE(M);243 244 DroppedVariableStatsIR Stats(true);245 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));246 247 // This loop simulates an IR pass that drops debug information.248 for (auto &F : *M) {249 for (auto &I : instructions(&F)) {250 I.dropDbgRecords();251 break;252 }253 break;254 }255 Stats.runAfterPass("Test",256 llvm::Any(const_cast<const llvm::Module *>(M.get())));257 ASSERT_EQ(Stats.getPassDroppedVariables(), true);258}259 260// This test ensures that if a #dbg_value is dropped after an optimization pass,261// but an instruction that has a scope which is a child of the #dbg_value scope262// still exists, and the #dbg_value is inlined at another location, debug263// information is conisdered not dropped.264TEST(DroppedVariableStatsIR, InlinedAt) {265 PassInstrumentationCallbacks PIC;266 PassInstrumentation PI(&PIC);267 268 LLVMContext C;269 270 const char *IR =271 R"(; Function Attrs: mustprogress nounwind ssp uwtable(sync)272 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {273 entry:274 #dbg_value(i32 %x, !15, !DIExpression(), !16)275 %add = add nsw i32 %x, 1, !dbg !17276 ret i32 0277 }278 !llvm.dbg.cu = !{!0}279 !llvm.module.flags = !{!3}280 !llvm.ident = !{!8}281 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")282 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")283 !3 = !{i32 2, !"Debug Info Version", i32 3}284 !8 = !{!"clang"}285 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)286 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")287 !11 = !DISubroutineType(types: !12)288 !12 = !{!13, !13}289 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)290 !14 = !{!15}291 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)292 !16 = !DILocation(line: 0, scope: !9, inlinedAt: !19)293 !17 = !DILocation(line: 2, column: 11, scope: !18)294 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28)295 !19 = !DILocation(line: 3, column: 2, scope: !9))";296 297 std::unique_ptr<llvm::Module> M = parseIR(C, IR);298 ASSERT_TRUE(M);299 300 DroppedVariableStatsIR Stats(true);301 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));302 303 // This loop simulates an IR pass that drops debug information.304 for (auto &F : *M) {305 for (auto &I : instructions(&F)) {306 I.dropDbgRecords();307 break;308 }309 break;310 }311 Stats.runAfterPass("Test",312 llvm::Any(const_cast<const llvm::Module *>(M.get())));313 ASSERT_EQ(Stats.getPassDroppedVariables(), false);314}315 316// This test ensures that if a #dbg_value is dropped after an optimization pass,317// but an instruction that has a scope which is a child of the #dbg_value scope318// still exists, and the #dbg_value and the instruction are inlined at another319// location, debug information is conisdered dropped.320TEST(DroppedVariableStatsIR, InlinedAtShared) {321 PassInstrumentationCallbacks PIC;322 PassInstrumentation PI(&PIC);323 324 LLVMContext C;325 326 const char *IR =327 R"(; Function Attrs: mustprogress nounwind ssp uwtable(sync)328 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {329 entry:330 #dbg_value(i32 %x, !15, !DIExpression(), !16)331 %add = add nsw i32 %x, 1, !dbg !17332 ret i32 0333 }334 !llvm.dbg.cu = !{!0}335 !llvm.module.flags = !{!3}336 !llvm.ident = !{!8}337 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")338 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")339 !3 = !{i32 2, !"Debug Info Version", i32 3}340 !8 = !{!"clang"}341 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)342 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")343 !11 = !DISubroutineType(types: !12)344 !12 = !{!13, !13}345 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)346 !14 = !{!15}347 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)348 !16 = !DILocation(line: 0, scope: !9, inlinedAt: !19)349 !17 = !DILocation(line: 2, column: 11, scope: !18, inlinedAt: !19)350 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28)351 !19 = !DILocation(line: 3, column: 2, scope: !9))";352 353 std::unique_ptr<llvm::Module> M = parseIR(C, IR);354 ASSERT_TRUE(M);355 356 DroppedVariableStatsIR Stats(true);357 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));358 359 // This loop simulates an IR pass that drops debug information.360 for (auto &F : *M) {361 for (auto &I : instructions(&F)) {362 I.dropDbgRecords();363 break;364 }365 break;366 }367 Stats.runAfterPass("Test",368 llvm::Any(const_cast<const llvm::Module *>(M.get())));369 ASSERT_EQ(Stats.getPassDroppedVariables(), true);370}371 372// This test ensures that if a #dbg_value is dropped after an optimization pass,373// but an instruction that has a scope which is a child of the #dbg_value scope374// still exists, and the instruction is inlined at a location that is the375// #dbg_value's inlined at location, debug information is conisdered dropped.376TEST(DroppedVariableStatsIR, InlinedAtChild) {377 PassInstrumentationCallbacks PIC;378 PassInstrumentation PI(&PIC);379 380 LLVMContext C;381 382 const char *IR =383 R"(; Function Attrs: mustprogress nounwind ssp uwtable(sync)384 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {385 entry:386 #dbg_value(i32 %x, !15, !DIExpression(), !16)387 %add = add nsw i32 %x, 1, !dbg !17388 ret i32 0389 }390 !llvm.dbg.cu = !{!0}391 !llvm.module.flags = !{!3}392 !llvm.ident = !{!8}393 !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")394 !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")395 !3 = !{i32 2, !"Debug Info Version", i32 3}396 !8 = !{!"clang"}397 !9 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)398 !10 = !DIFile(filename: "/tmp/code.cpp", directory: "")399 !11 = !DISubroutineType(types: !12)400 !12 = !{!13, !13}401 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)402 !14 = !{!15}403 !15 = !DILocalVariable(name: "x", arg: 1, scope: !9, file: !10, line: 1, type: !13)404 !16 = !DILocation(line: 0, scope: !9, inlinedAt: !19)405 !17 = !DILocation(line: 2, column: 11, scope: !18, inlinedAt: !20)406 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28)407 !19 = !DILocation(line: 3, column: 2, scope: !9);408 !20 = !DILocation(line: 4, column: 5, scope: !18, inlinedAt: !19))";409 410 std::unique_ptr<llvm::Module> M = parseIR(C, IR);411 ASSERT_TRUE(M);412 413 DroppedVariableStatsIR Stats(true);414 Stats.runBeforePass("", llvm::Any(const_cast<const llvm::Module *>(M.get())));415 416 // This loop simulates an IR pass that drops debug information.417 for (auto &F : *M) {418 for (auto &I : instructions(&F)) {419 I.dropDbgRecords();420 break;421 }422 break;423 }424 Stats.runAfterPass("Test",425 llvm::Any(const_cast<const llvm::Module *>(M.get())));426 ASSERT_EQ(Stats.getPassDroppedVariables(), true);427}428 429} // end anonymous namespace430