1 //===- unittests/IR/DroppedVariableStatsIRTest.cpp ------------------------===//
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
7 //===----------------------------------------------------------------------===//
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 <gtest/gtest.h>
20 #include <llvm/ADT/SmallString.h>
21 #include <llvm/IR/LLVMContext.h>
22 #include <llvm/IR/Module.h>
23 #include <llvm/IR/PassInstrumentation.h>
24 #include <llvm/IR/PassManager.h>
25 #include <llvm/IR/PassTimingInfo.h>
26 #include <llvm/Support/raw_ostream.h>
30 void initializePassTest1Pass(PassRegistry
&);
32 static std::unique_ptr
<Module
> parseIR(LLVMContext
&C
, const char *IR
) {
34 std::unique_ptr
<Module
> Mod
= parseAssemblyString(IR
, Err
, C
);
36 Err
.print("AbstractCallSiteTests", errs());
43 // This test ensures that if a #dbg_value and an instruction that exists in the
44 // same scope as that #dbg_value are both deleted as a result of an optimization
45 // pass, debug information is considered not dropped.
46 TEST(DroppedVariableStatsIR
, BothDeleted
) {
47 PassInstrumentationCallbacks PIC
;
48 PassInstrumentation
PI(&PIC
);
54 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)
55 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
57 #dbg_value(i32 %x, !15, !DIExpression(), !16)
58 %add = add nsw i32 %x, 1, !dbg !17
62 !llvm.module.flags = !{!3}
64 !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: "/")
65 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
66 !3 = !{i32 2, !"Debug Info Version
", i32 3}
68 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
69 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
70 !11 = !DISubroutineType(types: !12)
72 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
74 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
75 !16 = !DILocation(line: 0, scope: !9)
76 !17 = !DILocation(line: 2, column: 11, scope: !9))";
78 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
81 DroppedVariableStatsIR
Stats(true);
82 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
84 // This loop simulates an IR pass that drops debug information.
86 for (auto &I
: instructions(&F
)) {
93 Stats
.runAfterPass("Test",
94 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
95 ASSERT_EQ(Stats
.getPassDroppedVariables(), false);
98 // This test ensures that if a #dbg_value is dropped after an optimization pass,
99 // but an instruction that shares the same scope as the #dbg_value still exists,
100 // debug information is conisdered dropped.
101 TEST(DroppedVariableStatsIR
, DbgValLost
) {
102 PassInstrumentationCallbacks PIC
;
103 PassInstrumentation
PI(&PIC
);
109 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)
110 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
112 #dbg_value(i32 %x, !15, !DIExpression(), !16)
113 %add = add nsw i32 %x, 1, !dbg !17
117 !llvm.module.flags = !{!3}
119 !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: "/")
120 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
121 !3 = !{i32 2, !"Debug Info Version
", i32 3}
123 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
124 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
125 !11 = !DISubroutineType(types: !12)
127 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
129 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
130 !16 = !DILocation(line: 0, scope: !9)
131 !17 = !DILocation(line: 2, column: 11, scope: !9))";
133 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
136 DroppedVariableStatsIR
Stats(true);
137 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
139 // This loop simulates an IR pass that drops debug information.
141 for (auto &I
: instructions(&F
)) {
147 Stats
.runAfterPass("Test",
148 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
149 ASSERT_EQ(Stats
.getPassDroppedVariables(), true);
152 // This test ensures that if a #dbg_value is dropped after an optimization pass,
153 // but an instruction that has an unrelated scope as the #dbg_value still
154 // exists, debug information is conisdered not dropped.
155 TEST(DroppedVariableStatsIR
, UnrelatedScopes
) {
156 PassInstrumentationCallbacks PIC
;
157 PassInstrumentation
PI(&PIC
);
163 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)
164 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
166 #dbg_value(i32 %x, !15, !DIExpression(), !16)
167 %add = add nsw i32 %x, 1, !dbg !17
171 !llvm.module.flags = !{!3}
173 !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: "/")
174 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
175 !3 = !{i32 2, !"Debug Info Version
", i32 3}
177 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
178 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
179 !11 = !DISubroutineType(types: !12)
181 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
183 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
184 !16 = !DILocation(line: 0, scope: !9)
185 !17 = !DILocation(line: 2, column: 11, scope: !18)
186 !18 = distinct !DISubprogram(name: "bar
", linkageName: "_Z3bari
", scope: !10, file: !10, line: 11, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14))";
188 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
191 DroppedVariableStatsIR
Stats(true);
192 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
194 // This loop simulates an IR pass that drops debug information.
196 for (auto &I
: instructions(&F
)) {
202 Stats
.runAfterPass("Test",
203 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
204 ASSERT_EQ(Stats
.getPassDroppedVariables(), false);
207 // This test ensures that if a #dbg_value is dropped after an optimization pass,
208 // but an instruction that has a scope which is a child of the #dbg_value scope
209 // still exists, debug information is conisdered dropped.
210 TEST(DroppedVariableStatsIR
, ChildScopes
) {
211 PassInstrumentationCallbacks PIC
;
212 PassInstrumentation
PI(&PIC
);
218 ; Function Attrs: mustprogress nounwind ssp uwtable(sync)
219 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
221 #dbg_value(i32 %x, !15, !DIExpression(), !16)
222 %add = add nsw i32 %x, 1, !dbg !17
226 !llvm.module.flags = !{!3}
228 !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: "/")
229 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
230 !3 = !{i32 2, !"Debug Info Version
", i32 3}
232 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
233 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
234 !11 = !DISubroutineType(types: !12)
236 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
238 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
239 !16 = !DILocation(line: 0, scope: !9)
240 !17 = !DILocation(line: 2, column: 11, scope: !18)
241 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28))";
243 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
246 DroppedVariableStatsIR
Stats(true);
247 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
249 // This loop simulates an IR pass that drops debug information.
251 for (auto &I
: instructions(&F
)) {
257 Stats
.runAfterPass("Test",
258 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
259 ASSERT_EQ(Stats
.getPassDroppedVariables(), true);
262 // This test ensures that if a #dbg_value is dropped after an optimization pass,
263 // but an instruction that has a scope which is a child of the #dbg_value scope
264 // still exists, and the #dbg_value is inlined at another location, debug
265 // information is conisdered not dropped.
266 TEST(DroppedVariableStatsIR
, InlinedAt
) {
267 PassInstrumentationCallbacks PIC
;
268 PassInstrumentation
PI(&PIC
);
273 R
"(; Function Attrs: mustprogress nounwind ssp uwtable(sync)
274 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
276 #dbg_value(i32 %x, !15, !DIExpression(), !16)
277 %add = add nsw i32 %x, 1, !dbg !17
281 !llvm.module.flags = !{!3}
283 !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: "/")
284 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
285 !3 = !{i32 2, !"Debug Info Version
", i32 3}
287 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
288 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
289 !11 = !DISubroutineType(types: !12)
291 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
293 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
294 !16 = !DILocation(line: 0, scope: !9, inlinedAt: !19)
295 !17 = !DILocation(line: 2, column: 11, scope: !18)
296 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28)
297 !19 = !DILocation(line: 3, column: 2, scope: !9))";
299 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
302 DroppedVariableStatsIR
Stats(true);
303 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
305 // This loop simulates an IR pass that drops debug information.
307 for (auto &I
: instructions(&F
)) {
313 Stats
.runAfterPass("Test",
314 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
315 ASSERT_EQ(Stats
.getPassDroppedVariables(), false);
318 // This test ensures that if a #dbg_value is dropped after an optimization pass,
319 // but an instruction that has a scope which is a child of the #dbg_value scope
320 // still exists, and the #dbg_value and the instruction are inlined at another
321 // location, debug information is conisdered dropped.
322 TEST(DroppedVariableStatsIR
, InlinedAtShared
) {
323 PassInstrumentationCallbacks PIC
;
324 PassInstrumentation
PI(&PIC
);
329 R
"(; Function Attrs: mustprogress nounwind ssp uwtable(sync)
330 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
332 #dbg_value(i32 %x, !15, !DIExpression(), !16)
333 %add = add nsw i32 %x, 1, !dbg !17
337 !llvm.module.flags = !{!3}
339 !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: "/")
340 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
341 !3 = !{i32 2, !"Debug Info Version
", i32 3}
343 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
344 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
345 !11 = !DISubroutineType(types: !12)
347 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
349 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
350 !16 = !DILocation(line: 0, scope: !9, inlinedAt: !19)
351 !17 = !DILocation(line: 2, column: 11, scope: !18, inlinedAt: !19)
352 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28)
353 !19 = !DILocation(line: 3, column: 2, scope: !9))";
355 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
358 DroppedVariableStatsIR
Stats(true);
359 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
361 // This loop simulates an IR pass that drops debug information.
363 for (auto &I
: instructions(&F
)) {
369 Stats
.runAfterPass("Test",
370 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
371 ASSERT_EQ(Stats
.getPassDroppedVariables(), true);
374 // This test ensures that if a #dbg_value is dropped after an optimization pass,
375 // but an instruction that has a scope which is a child of the #dbg_value scope
376 // still exists, and the instruction is inlined at a location that is the
377 // #dbg_value's inlined at location, debug information is conisdered dropped.
378 TEST(DroppedVariableStatsIR
, InlinedAtChild
) {
379 PassInstrumentationCallbacks PIC
;
380 PassInstrumentation
PI(&PIC
);
385 R
"(; Function Attrs: mustprogress nounwind ssp uwtable(sync)
386 define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr #0 !dbg !9 {
388 #dbg_value(i32 %x, !15, !DIExpression(), !16)
389 %add = add nsw i32 %x, 1, !dbg !17
393 !llvm.module.flags = !{!3}
395 !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: "/")
396 !1 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "/")
397 !3 = !{i32 2, !"Debug Info Version
", i32 3}
399 !9 = distinct !DISubprogram(name: "foo
", linkageName: "_Z3fooi
", scope: !10, file: !10, line: 1, type: !11, scopeLine: 1, unit: !0, retainedNodes: !14)
400 !10 = !DIFile(filename: "/tmp
/code
.cpp
", directory: "")
401 !11 = !DISubroutineType(types: !12)
403 !13 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
405 !15 = !DILocalVariable(name: "x
", arg: 1, scope: !9, file: !10, line: 1, type: !13)
406 !16 = !DILocation(line: 0, scope: !9, inlinedAt: !19)
407 !17 = !DILocation(line: 2, column: 11, scope: !18, inlinedAt: !20)
408 !18 = distinct !DILexicalBlock(scope: !9, file: !10, line: 10, column: 28)
409 !19 = !DILocation(line: 3, column: 2, scope: !9);
410 !20 = !DILocation(line: 4, column: 5, scope: !18, inlinedAt: !19))";
412 std::unique_ptr
<llvm::Module
> M
= parseIR(C
, IR
);
415 DroppedVariableStatsIR
Stats(true);
416 Stats
.runBeforePass(llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
418 // This loop simulates an IR pass that drops debug information.
420 for (auto &I
: instructions(&F
)) {
426 Stats
.runAfterPass("Test",
427 llvm::Any(const_cast<const llvm::Module
*>(M
.get())));
428 ASSERT_EQ(Stats
.getPassDroppedVariables(), true);
431 } // end anonymous namespace