| //===- unittests/CodeGen/DroppedVariableStatsMIRTest.cpp ------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/CodeGen/DroppedVariableStatsMIR.h" |
| #include "llvm/AsmParser/Parser.h" |
| #include "llvm/CodeGen/MIRParser/MIRParser.h" |
| #include "llvm/CodeGen/MachineModuleInfo.h" |
| #include "llvm/IR/Function.h" |
| #include "llvm/IR/LegacyPassManager.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/MC/TargetRegistry.h" |
| #include "llvm/Pass.h" |
| #include "llvm/Support/SourceMgr.h" |
| #include "llvm/Support/TargetSelect.h" |
| #include "llvm/Target/TargetMachine.h" |
| #include "gtest/gtest.h" |
| #include <gtest/gtest.h> |
| #include <llvm/ADT/SmallString.h> |
| #include <llvm/IR/LLVMContext.h> |
| #include <llvm/IR/Module.h> |
| #include <llvm/IR/PassInstrumentation.h> |
| #include <llvm/IR/PassManager.h> |
| #include <llvm/IR/PassTimingInfo.h> |
| #include <llvm/Support/raw_ostream.h> |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| std::unique_ptr<TargetMachine> |
| createTargetMachine(std::string TargetStr, StringRef CPU, StringRef FS) { |
| std::string Error; |
| Triple TT(TargetStr); |
| const Target *T = TargetRegistry::lookupTarget(TT, Error); |
| if (!T) |
| return nullptr; |
| TargetOptions Options; |
| return std::unique_ptr<TargetMachine>( |
| static_cast<TargetMachine *>(T->createTargetMachine( |
| TT, CPU, FS, Options, std::nullopt, std::nullopt))); |
| } |
| |
| std::unique_ptr<Module> parseMIR(const TargetMachine &TM, StringRef MIRCode, |
| MachineModuleInfo &MMI, LLVMContext *Context) { |
| SMDiagnostic Diagnostic; |
| std::unique_ptr<Module> M; |
| std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode); |
| auto MIR = createMIRParser(std::move(MBuffer), *Context); |
| if (!MIR) |
| return nullptr; |
| |
| std::unique_ptr<Module> Mod = MIR->parseIRModule(); |
| if (!Mod) |
| return nullptr; |
| |
| Mod->setDataLayout(TM.createDataLayout()); |
| |
| if (MIR->parseMachineFunctions(*Mod, MMI)) { |
| M.reset(); |
| return nullptr; |
| } |
| return Mod; |
| } |
| // This test ensures that if a DBG_VALUE and an instruction that exists in the |
| // same scope as that DBG_VALUE are both deleted as a result of an optimization |
| // pass, debug information is considered not dropped. |
| TEST(DroppedVariableStatsMIR, BothDeleted) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4) |
| !12 = !DILocation(line: 2, column: 11, scope: !4) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| for (auto &MI : MBB) { |
| auto *DbgLoc = MI.getDebugLoc().get(); |
| if (DbgLoc) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), false); |
| } |
| |
| // This test ensures that if a DBG_VALUE is dropped after an optimization pass, |
| // but an instruction that shares the same scope as the DBG_VALUE still exists, |
| // debug information is conisdered dropped. |
| TEST(DroppedVariableStatsMIR, DbgValLost) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4) |
| !12 = !DILocation(line: 2, column: 11, scope: !4) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), true); |
| } |
| |
| // This test ensures that if a #dbg_value is dropped after an optimization pass, |
| // but an instruction that has an unrelated scope as the #dbg_value still |
| // exists, debug information is conisdered not dropped. |
| TEST(DroppedVariableStatsMIR, UnrelatedScopes) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4) |
| !12 = !DILocation(line: 2, column: 11, scope: !13) |
| !13 = distinct !DISubprogram(name: "bar", linkageName: "_Z3bari", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), false); |
| } |
| |
| // This test ensures that if a #dbg_value is dropped after an optimization pass, |
| // but an instruction that has a scope which is a child of the #dbg_value scope |
| // still exists, debug information is conisdered dropped. |
| TEST(DroppedVariableStatsMIR, ChildScopes) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4) |
| !12 = !DILocation(line: 2, column: 11, scope: !13) |
| !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), true); |
| } |
| |
| // This test ensures that if a DBG_VALUE is dropped after an optimization pass, |
| // but an instruction that has a scope which is a child of the DBG_VALUE scope |
| // still exists, and the DBG_VALUE is inlined at another location, debug |
| // information is conisdered not dropped. |
| TEST(DroppedVariableStatsMIR, InlinedAt) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14) |
| !12 = !DILocation(line: 2, column: 11, scope: !13) |
| !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) |
| !14 = !DILocation(line: 3, column: 2, scope: !4) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), false); |
| } |
| |
| // This test ensures that if a DBG_VALUE is dropped after an optimization pass, |
| // but an instruction that has a scope which is a child of the DBG_VALUE scope |
| // still exists, and the DBG_VALUE and the instruction are inlined at another |
| // location, debug information is conisdered dropped. |
| TEST(DroppedVariableStatsMIR, InlinedAtShared) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14) |
| !12 = !DILocation(line: 2, column: 11, scope: !13, inlinedAt: !14) |
| !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) |
| !14 = !DILocation(line: 3, column: 2, scope: !4) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), true); |
| } |
| |
| // This test ensures that if a DBG_VALUE is dropped after an optimization pass, |
| // but an instruction that has a scope which is a child of the DBG_VALUE scope |
| // still exists, and the instruction is inlined at a location that is the |
| // DBG_VALUE's inlined at location, debug information is conisdered dropped. |
| TEST(DroppedVariableStatsMIR, InlinedAtChild) { |
| InitializeAllTargetInfos(); |
| InitializeAllTargets(); |
| InitializeAllTargetMCs(); |
| PassInstrumentationCallbacks PIC; |
| PassInstrumentation PI(&PIC); |
| |
| LLVMContext C; |
| |
| const char *MIR = |
| R"( |
| --- | |
| ; ModuleID = '/tmp/test.ll' |
| source_filename = "/tmp/test.ll" |
| target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" |
| |
| define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { |
| entry: |
| #dbg_value(i32 %x, !10, !DIExpression(), !11) |
| %add = add nsw i32 %x, 1, !dbg !12 |
| ret i32 0 |
| } |
| |
| !llvm.dbg.cu = !{!0} |
| !llvm.module.flags = !{!2} |
| !llvm.ident = !{!3} |
| |
| !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: "/") |
| !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") |
| !2 = !{i32 2, !"Debug Info Version", i32 3} |
| !3 = !{!"clang"} |
| !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) |
| !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") |
| !6 = !DISubroutineType(types: !7) |
| !7 = !{!8, !8} |
| !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) |
| !9 = !{!10} |
| !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) |
| !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14) |
| !12 = !DILocation(line: 2, column: 11, scope: !13, inlinedAt: !15) |
| !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) |
| !14 = !DILocation(line: 3, column: 2, scope: !4) |
| !15 = !DILocation(line: 4, column: 5, scope: !13, inlinedAt: !14) |
| |
| ... |
| --- |
| name: _Z3fooi |
| alignment: 4 |
| exposesReturnsTwice: false |
| legalized: false |
| regBankSelected: false |
| selected: false |
| failedISel: false |
| tracksRegLiveness: true |
| hasWinCFI: false |
| noPhis: false |
| isSSA: true |
| noVRegs: false |
| hasFakeUses: false |
| callsEHReturn: false |
| callsUnwindInit: false |
| hasEHContTarget: false |
| hasEHScopes: false |
| hasEHFunclets: false |
| isOutlined: false |
| debugInstrRef: false |
| failsVerification: false |
| tracksDebugUserValues: false |
| registers: |
| - { id: 0, class: _, preferred-register: '', flags: [ ] } |
| - { id: 1, class: _, preferred-register: '', flags: [ ] } |
| - { id: 2, class: _, preferred-register: '', flags: [ ] } |
| - { id: 3, class: _, preferred-register: '', flags: [ ] } |
| liveins: |
| - { reg: '$w0', virtual-reg: '' } |
| frameInfo: |
| isFrameAddressTaken: false |
| isReturnAddressTaken: false |
| hasStackMap: false |
| hasPatchPoint: false |
| stackSize: 0 |
| offsetAdjustment: 0 |
| maxAlignment: 1 |
| adjustsStack: false |
| hasCalls: false |
| stackProtector: '' |
| functionContext: '' |
| maxCallFrameSize: 4294967295 |
| cvBytesOfCalleeSavedRegisters: 0 |
| hasOpaqueSPAdjustment: false |
| hasVAStart: false |
| hasMustTailInVarArgFunc: false |
| hasTailCall: false |
| isCalleeSavedInfoValid: false |
| localFrameSize: 0 |
| savePoint: '' |
| restorePoint: '' |
| fixedStack: [] |
| stack: [] |
| entry_values: [] |
| callSites: [] |
| debugValueSubstitutions: [] |
| constants: [] |
| machineFunctionInfo: {} |
| body: | |
| bb.1.entry: |
| liveins: $w0 |
| |
| %0:_(s32) = COPY $w0 |
| %1:_(s32) = G_CONSTANT i32 1 |
| %3:_(s32) = G_CONSTANT i32 0 |
| DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 |
| %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 |
| $w0 = COPY %3(s32) |
| RET_ReallyLR implicit $w0 |
| )"; |
| auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); |
| if (!TM) |
| return; |
| MachineModuleInfo MMI(TM.get()); |
| std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C); |
| ASSERT_TRUE(M); |
| |
| DroppedVariableStatsMIR Stats; |
| auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); |
| Stats.runBeforePass("Test", MF); |
| |
| // This loop simulates an IR pass that drops debug information. |
| for (auto &MBB : *MF) { |
| for (auto &MI : MBB) { |
| if (MI.isDebugValueLike()) { |
| MI.eraseFromParent(); |
| break; |
| } |
| } |
| break; |
| } |
| |
| Stats.runAfterPass("Test", MF); |
| ASSERT_EQ(Stats.getPassDroppedVariables(), true); |
| } |
| |
| } // end anonymous namespace |