//===- 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 #include #include #include #include #include using namespace llvm; namespace { std::unique_ptr 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( static_cast(T->createTargetMachine( TT, CPU, FS, Options, std::nullopt, std::nullopt))); } std::unique_ptr parseMIR(const TargetMachine &TM, StringRef MIRCode, MachineModuleInfo &MMI, LLVMContext *Context) { SMDiagnostic Diagnostic; std::unique_ptr M; std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIRCode); auto MIR = createMIRParser(std::move(MBuffer), *Context); if (!MIR) return nullptr; std::unique_ptr 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 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 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 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 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 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 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 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