diff --git a/0001-Reland-Add-a-pass-to-collect-dropped-var-stats-for-M.patch b/0001-Reland-Add-a-pass-to-collect-dropped-var-stats-for-M.patch new file mode 100644 index 0000000000000..95c0a0b54f7e2 --- /dev/null +++ b/0001-Reland-Add-a-pass-to-collect-dropped-var-stats-for-M.patch @@ -0,0 +1,1301 @@ +From 8f00eaaa595c1b908d43b1de288e3c03f1f998bf Mon Sep 17 00:00:00 2001 +From: Shubham Sandeep Rastogi +Date: Mon, 18 Nov 2024 16:06:59 -0800 +Subject: [PATCH] Reland "Add a pass to collect dropped var stats for MIR" + +Moved the MIR Test to the unittests/CodeGen folder +--- + .../llvm/CodeGen/DroppedVariableStats.h | 48 +- + .../llvm/CodeGen/MachineFunctionPass.h | 2 + + llvm/lib/CodeGen/DroppedVariableStats.cpp | 63 +- + llvm/lib/CodeGen/MachineFunctionPass.cpp | 15 +- + llvm/unittests/CodeGen/CMakeLists.txt | 1 + + .../CodeGen/DroppedVariableStatsMIRTest.cpp | 1067 +++++++++++++++++ + 6 files changed, 1193 insertions(+), 3 deletions(-) + create mode 100644 llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp + +diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStats.h b/llvm/include/llvm/CodeGen/DroppedVariableStats.h +index 371d775b02e8..f6050c68c91a 100644 +--- a/llvm/include/llvm/CodeGen/DroppedVariableStats.h ++++ b/llvm/include/llvm/CodeGen/DroppedVariableStats.h +@@ -7,7 +7,7 @@ + ///===---------------------------------------------------------------------===// + /// \file + /// Dropped Variable Statistics for Debug Information. Reports any number +-/// of #dbg_value that get dropped due to an optimization pass. ++/// of #dbg_values or DBG_VALUEs that get dropped due to an optimization pass. + /// + ///===---------------------------------------------------------------------===// + +@@ -221,6 +221,52 @@ private: + } + }; + ++/// A class to collect and print dropped debug information due to MIR ++/// optimization passes. After every MIR pass is run, it will print how many ++/// #DBG_VALUEs were dropped due to that pass. ++class DroppedVariableStatsMIR : public DroppedVariableStats { ++public: ++ DroppedVariableStatsMIR() : llvm::DroppedVariableStats(false) {} ++ ++ void runBeforePass(StringRef PassID, MachineFunction *MF) { ++ if (PassID == "Debug Variable Analysis") ++ return; ++ setup(); ++ return runOnMachineFunction(MF, true); ++ } ++ ++ void runAfterPass(StringRef PassID, MachineFunction *MF) { ++ if (PassID == "Debug Variable Analysis") ++ return; ++ runOnMachineFunction(MF, false); ++ calculateDroppedVarStatsOnMachineFunction(MF, PassID, MF->getName().str()); ++ cleanup(); ++ } ++ ++private: ++ const MachineFunction *MFunc; ++ /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or ++ /// after a pass has run to facilitate dropped variable calculation for an ++ /// llvm::MachineFunction. ++ void runOnMachineFunction(const MachineFunction *MF, bool Before); ++ /// Iterate over all Instructions in a MachineFunction and report any dropped ++ /// debug information. ++ void calculateDroppedVarStatsOnMachineFunction(const MachineFunction *MF, ++ StringRef PassID, ++ StringRef FuncOrModName); ++ /// Override base class method to run on an llvm::MachineFunction ++ /// specifically. ++ virtual void ++ visitEveryInstruction(unsigned &DroppedCount, ++ DenseMap &InlinedAtsMap, ++ VarID Var) override; ++ /// Override base class method to run on DBG_VALUEs specifically. ++ virtual void visitEveryDebugRecord( ++ DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before) override; ++}; ++ + } // namespace llvm + + #endif +diff --git a/llvm/include/llvm/CodeGen/MachineFunctionPass.h b/llvm/include/llvm/CodeGen/MachineFunctionPass.h +index caaf22c2139e..d82b593497ff 100644 +--- a/llvm/include/llvm/CodeGen/MachineFunctionPass.h ++++ b/llvm/include/llvm/CodeGen/MachineFunctionPass.h +@@ -18,6 +18,7 @@ + #ifndef LLVM_CODEGEN_MACHINEFUNCTIONPASS_H + #define LLVM_CODEGEN_MACHINEFUNCTIONPASS_H + ++#include "llvm/CodeGen/DroppedVariableStats.h" + #include "llvm/CodeGen/MachineFunction.h" + #include "llvm/Pass.h" + +@@ -67,6 +68,7 @@ private: + MachineFunctionProperties RequiredProperties; + MachineFunctionProperties SetProperties; + MachineFunctionProperties ClearedProperties; ++ DroppedVariableStatsMIR DroppedVarStatsMF; + + /// createPrinterPass - Get a machine function printer pass. + Pass *createPrinterPass(raw_ostream &O, +diff --git a/llvm/lib/CodeGen/DroppedVariableStats.cpp b/llvm/lib/CodeGen/DroppedVariableStats.cpp +index 122fcad1293f..71f91292160f 100644 +--- a/llvm/lib/CodeGen/DroppedVariableStats.cpp ++++ b/llvm/lib/CodeGen/DroppedVariableStats.cpp +@@ -7,7 +7,7 @@ + ///===---------------------------------------------------------------------===// + /// \file + /// Dropped Variable Statistics for Debug Information. Reports any number +-/// of #dbg_value that get dropped due to an optimization pass. ++/// of #dbg_values or DBG_VALUEs that get dropped due to an optimization pass. + /// + ///===---------------------------------------------------------------------===// + +@@ -192,3 +192,64 @@ void DroppedVariableStatsIR::visitEveryDebugRecord( + } + } + } ++ ++void DroppedVariableStatsMIR::runOnMachineFunction(const MachineFunction *MF, ++ bool Before) { ++ auto &DebugVariables = DebugVariablesStack.back()[&MF->getFunction()]; ++ auto FuncName = MF->getName(); ++ MFunc = MF; ++ run(DebugVariables, FuncName, Before); ++} ++ ++void DroppedVariableStatsMIR::calculateDroppedVarStatsOnMachineFunction( ++ const MachineFunction *MF, StringRef PassID, StringRef FuncOrModName) { ++ MFunc = MF; ++ StringRef FuncName = MF->getName(); ++ const Function *Func = &MF->getFunction(); ++ DebugVariables &DbgVariables = DebugVariablesStack.back()[Func]; ++ calculateDroppedStatsAndPrint(DbgVariables, FuncName, PassID, FuncOrModName, ++ "MachineFunction", Func); ++} ++ ++void DroppedVariableStatsMIR::visitEveryInstruction( ++ unsigned &DroppedCount, DenseMap &InlinedAtsMap, ++ VarID Var) { ++ unsigned PrevDroppedCount = DroppedCount; ++ const DIScope *DbgValScope = std::get<0>(Var); ++ for (const auto &MBB : *MFunc) { ++ for (const auto &MI : MBB) { ++ if (!MI.isDebugInstr()) { ++ auto *DbgLoc = MI.getDebugLoc().get(); ++ if (!DbgLoc) ++ continue; ++ ++ auto *Scope = DbgLoc->getScope(); ++ if (updateDroppedCount(DbgLoc, Scope, DbgValScope, InlinedAtsMap, Var, ++ DroppedCount)) ++ break; ++ } ++ } ++ if (PrevDroppedCount != DroppedCount) { ++ PrevDroppedCount = DroppedCount; ++ break; ++ } ++ } ++} ++ ++void DroppedVariableStatsMIR::visitEveryDebugRecord( ++ DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before) { ++ for (const auto &MBB : *MFunc) { ++ for (const auto &MI : MBB) { ++ if (MI.isDebugValueLike()) { ++ auto *DbgVar = MI.getDebugVariable(); ++ if (!DbgVar) ++ continue; ++ auto DbgLoc = MI.getDebugLoc(); ++ populateVarIDSetAndInlinedMap(DbgVar, DbgLoc, VarIDSet, InlinedAtsMap, ++ FuncName, Before); ++ } ++ } ++ } ++} +diff --git a/llvm/lib/CodeGen/MachineFunctionPass.cpp b/llvm/lib/CodeGen/MachineFunctionPass.cpp +index 62ac3e32d24d..e803811643f8 100644 +--- a/llvm/lib/CodeGen/MachineFunctionPass.cpp ++++ b/llvm/lib/CodeGen/MachineFunctionPass.cpp +@@ -32,6 +32,11 @@ + using namespace llvm; + using namespace ore; + ++static cl::opt DroppedVarStatsMIR( ++ "dropped-variable-stats-mir", cl::Hidden, ++ cl::desc("Dump dropped debug variables stats for MIR passes"), ++ cl::init(false)); ++ + Pass *MachineFunctionPass::createPrinterPass(raw_ostream &O, + const std::string &Banner) const { + return createMachineFunctionPrinterPass(O, Banner); +@@ -91,7 +96,15 @@ bool MachineFunctionPass::runOnFunction(Function &F) { + + MFProps.reset(ClearedProperties); + +- bool RV = runOnMachineFunction(MF); ++ bool RV; ++ if (DroppedVarStatsMIR) { ++ auto PassName = getPassName(); ++ DroppedVarStatsMF.runBeforePass(PassName, &MF); ++ RV = runOnMachineFunction(MF); ++ DroppedVarStatsMF.runAfterPass(PassName, &MF); ++ } else { ++ RV = runOnMachineFunction(MF); ++ } + + if (ShouldEmitSizeRemarks) { + // We wanted size remarks. Check if there was a change to the number of +diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt +index 807fd1a9b7b5..50ef1bb5b7af 100644 +--- a/llvm/unittests/CodeGen/CMakeLists.txt ++++ b/llvm/unittests/CodeGen/CMakeLists.txt +@@ -28,6 +28,7 @@ add_llvm_unittest(CodeGenTests + DIEHashTest.cpp + DIETest.cpp + DroppedVariableStatsIRTest.cpp ++ DroppedVariableStatsMIRTest.cpp + DwarfStringPoolEntryRefTest.cpp + InstrRefLDVTest.cpp + LowLevelTypeTest.cpp +diff --git a/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp b/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp +new file mode 100644 +index 000000000000..b26a89c7adcb +--- /dev/null ++++ b/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp +@@ -0,0 +1,1067 @@ ++//===- unittests/IR/DroppedVariableStatsTest.cpp - TimePassesHandler tests ++//----------===// ++// ++// 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/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/Passes/StandardInstrumentations.h" ++#include "llvm/Support/TargetSelect.h" ++#include "llvm/Target/TargetMachine.h" ++#include "gtest/gtest.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++using namespace llvm; ++ ++namespace { ++ ++std::unique_ptr ++createTargetMachine(std::string TT, StringRef CPU, StringRef FS) { ++ std::string Error; ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 ++hasEHCatchret: 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--"), "", ""); ++ 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 +-- +2.46.2 + diff --git a/0001-Reland-NFC-Move-DroppedVariableStats-to-its-own-file.patch b/0001-Reland-NFC-Move-DroppedVariableStats-to-its-own-file.patch new file mode 100644 index 0000000000000..e68aa98b82b09 --- /dev/null +++ b/0001-Reland-NFC-Move-DroppedVariableStats-to-its-own-file.patch @@ -0,0 +1,1045 @@ +From 1f4f368b9c3b92787018a6ee410c5ab4e79b072d Mon Sep 17 00:00:00 2001 +From: Shubham Sandeep Rastogi +Date: Mon, 18 Nov 2024 16:06:26 -0800 +Subject: [PATCH] Reland [NFC] Move DroppedVariableStats to its own file and + redesign it to be extensible. + +Moved the IR unit test to the CodeGen folder to resolve linker errors: + +error: undefined reference to 'vtable for llvm::DroppedVariableStatsIR' +--- + .../llvm/CodeGen/DroppedVariableStats.h | 226 ++++++++++++++++++ + .../llvm/Passes/StandardInstrumentations.h | 80 +------ + llvm/lib/CodeGen/CMakeLists.txt | 1 + + llvm/lib/CodeGen/DroppedVariableStats.cpp | 194 +++++++++++++++ + llvm/lib/Passes/StandardInstrumentations.cpp | 178 +------------- + llvm/unittests/CodeGen/CMakeLists.txt | 1 + + .../DroppedVariableStatsIRTest.cpp} | 74 +++--- + llvm/unittests/IR/CMakeLists.txt | 1 - + 8 files changed, 456 insertions(+), 299 deletions(-) + create mode 100644 llvm/include/llvm/CodeGen/DroppedVariableStats.h + create mode 100644 llvm/lib/CodeGen/DroppedVariableStats.cpp + rename llvm/unittests/{IR/DroppedVariableStatsTest.cpp => CodeGen/DroppedVariableStatsIRTest.cpp} (91%) + +diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStats.h b/llvm/include/llvm/CodeGen/DroppedVariableStats.h +new file mode 100644 +index 000000000000..371d775b02e8 +--- /dev/null ++++ b/llvm/include/llvm/CodeGen/DroppedVariableStats.h +@@ -0,0 +1,226 @@ ++///===- DroppedVariableStats.h - Opt Diagnostics -*- C++ -*----------------===// ++/// ++/// 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 ++/// ++///===---------------------------------------------------------------------===// ++/// \file ++/// Dropped Variable Statistics for Debug Information. Reports any number ++/// of #dbg_value that get dropped due to an optimization pass. ++/// ++///===---------------------------------------------------------------------===// ++ ++#ifndef LLVM_CODEGEN_DROPPEDVARIABLESTATS_H ++#define LLVM_CODEGEN_DROPPEDVARIABLESTATS_H ++ ++#include "llvm/CodeGen/MachinePassManager.h" ++#include "llvm/IR/DebugInfoMetadata.h" ++#include "llvm/IR/DiagnosticInfo.h" ++#include "llvm/IR/Function.h" ++#include "llvm/IR/Module.h" ++#include "llvm/IR/PassInstrumentation.h" ++ ++namespace llvm { ++ ++/// A unique key that represents a debug variable. ++/// First const DIScope *: Represents the scope of the debug variable. ++/// Second const DIScope *: Represents the InlinedAt scope of the debug ++/// variable. const DILocalVariable *: It is a pointer to the debug variable ++/// itself. ++using VarID = ++ std::tuple; ++ ++/// A base class to collect and print dropped debug information variable ++/// statistics. ++class DroppedVariableStats { ++public: ++ DroppedVariableStats(bool DroppedVarStatsEnabled) ++ : DroppedVariableStatsEnabled(DroppedVarStatsEnabled) { ++ if (DroppedVarStatsEnabled) ++ llvm::outs() ++ << "Pass Level, Pass Name, Num of Dropped Variables, Func or " ++ "Module Name\n"; ++ }; ++ ++ virtual ~DroppedVariableStats() = default; ++ ++ // We intend this to be unique per-compilation, thus no copies. ++ DroppedVariableStats(const DroppedVariableStats &) = delete; ++ void operator=(const DroppedVariableStats &) = delete; ++ ++ bool getPassDroppedVariables() { return PassDroppedVariables; } ++ ++protected: ++ void setup() { ++ DebugVariablesStack.push_back( ++ {DenseMap()}); ++ InlinedAts.push_back( ++ {DenseMap>()}); ++ } ++ ++ void cleanup() { ++ assert(!DebugVariablesStack.empty() && ++ "DebugVariablesStack shouldn't be empty!"); ++ assert(!InlinedAts.empty() && "InlinedAts shouldn't be empty!"); ++ DebugVariablesStack.pop_back(); ++ InlinedAts.pop_back(); ++ } ++ ++ bool DroppedVariableStatsEnabled = false; ++ struct DebugVariables { ++ /// DenseSet of VarIDs before an optimization pass has run. ++ DenseSet DebugVariablesBefore; ++ /// DenseSet of VarIDs after an optimization pass has run. ++ DenseSet DebugVariablesAfter; ++ }; ++ ++protected: ++ /// A stack of a DenseMap, that maps DebugVariables for every pass to an ++ /// llvm::Function. A stack is used because an optimization pass can call ++ /// other passes. ++ SmallVector> DebugVariablesStack; ++ ++ /// A DenseSet tracking whether a scope was visited before. ++ DenseSet VisitedScope; ++ /// A stack of DenseMaps, which map the name of an llvm::Function to a ++ /// DenseMap of VarIDs and their inlinedAt locations before an optimization ++ /// pass has run. ++ SmallVector>> InlinedAts; ++ /// Calculate the number of dropped variables in an llvm::Function or ++ /// llvm::MachineFunction and print the relevant information to stdout. ++ void calculateDroppedStatsAndPrint(DebugVariables &DbgVariables, ++ StringRef FuncName, StringRef PassID, ++ StringRef FuncOrModName, ++ StringRef PassLevel, const Function *Func); ++ ++ /// Check if a \p Var has been dropped or is a false positive. Also update the ++ /// \p DroppedCount if a debug variable is dropped. ++ bool updateDroppedCount(DILocation *DbgLoc, const DIScope *Scope, ++ const DIScope *DbgValScope, ++ DenseMap &InlinedAtsMap, ++ VarID Var, unsigned &DroppedCount); ++ /// Run code to populate relevant data structures over an llvm::Function or ++ /// llvm::MachineFunction. ++ void run(DebugVariables &DbgVariables, StringRef FuncName, bool Before); ++ /// Populate the VarIDSet and InlinedAtMap with the relevant information ++ /// needed for before and after pass analysis to determine dropped variable ++ /// status. ++ void populateVarIDSetAndInlinedMap( ++ const DILocalVariable *DbgVar, DebugLoc DbgLoc, DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before); ++ /// Visit every llvm::Instruction or llvm::MachineInstruction and check if the ++ /// debug variable denoted by its ID \p Var may have been dropped by an ++ /// optimization pass. ++ virtual void ++ visitEveryInstruction(unsigned &DroppedCount, ++ DenseMap &InlinedAtsMap, ++ VarID Var) = 0; ++ /// Visit every debug record in an llvm::Function or llvm::MachineFunction ++ /// and call populateVarIDSetAndInlinedMap on it. ++ virtual void visitEveryDebugRecord( ++ DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before) = 0; ++ ++private: ++ /// Remove a dropped debug variable's VarID from all Sets in the ++ /// DroppedVariablesBefore stack. ++ void removeVarFromAllSets(VarID Var, const Function *F) { ++ // Do not remove Var from the last element, it will be popped from the ++ // stack. ++ for (auto &DebugVariablesMap : llvm::drop_end(DebugVariablesStack)) ++ DebugVariablesMap[F].DebugVariablesBefore.erase(Var); ++ } ++ /// Return true if \p Scope is the same as \p DbgValScope or a child scope of ++ /// \p DbgValScope, return false otherwise. ++ bool isScopeChildOfOrEqualTo(const DIScope *Scope, ++ const DIScope *DbgValScope); ++ /// Return true if \p InlinedAt is the same as \p DbgValInlinedAt or part of ++ /// the InlinedAt chain, return false otherwise. ++ bool isInlinedAtChildOfOrEqualTo(const DILocation *InlinedAt, ++ const DILocation *DbgValInlinedAt); ++ bool PassDroppedVariables = false; ++}; ++ ++/// A class to collect and print dropped debug information due to LLVM IR ++/// optimization passes. After every LLVM IR pass is run, it will print how many ++/// #dbg_values were dropped due to that pass. ++class DroppedVariableStatsIR : public DroppedVariableStats { ++public: ++ DroppedVariableStatsIR(bool DroppedVarStatsEnabled) ++ : llvm::DroppedVariableStats(DroppedVarStatsEnabled) {} ++ ++ virtual ~DroppedVariableStatsIR() = default; ++ ++ void runBeforePass(Any IR) { ++ setup(); ++ if (const auto *M = unwrapIR(IR)) ++ return this->runOnModule(M, true); ++ if (const auto *F = unwrapIR(IR)) ++ return this->runOnFunction(F, true); ++ } ++ ++ void runAfterPass(StringRef P, Any IR) { ++ if (const auto *M = unwrapIR(IR)) ++ runAfterPassModule(P, M); ++ else if (const auto *F = unwrapIR(IR)) ++ runAfterPassFunction(P, F); ++ cleanup(); ++ } ++ ++ void registerCallbacks(PassInstrumentationCallbacks &PIC); ++ ++private: ++ const Function *Func; ++ ++ void runAfterPassFunction(StringRef PassID, const Function *F) { ++ runOnFunction(F, false); ++ calculateDroppedVarStatsOnFunction(F, PassID, F->getName().str(), ++ "Function"); ++ } ++ ++ void runAfterPassModule(StringRef PassID, const Module *M) { ++ runOnModule(M, false); ++ calculateDroppedVarStatsOnModule(M, PassID, M->getName().str(), "Module"); ++ } ++ /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or ++ /// after a pass has run to facilitate dropped variable calculation for an ++ /// llvm::Function. ++ void runOnFunction(const Function *F, bool Before); ++ /// Iterate over all Instructions in a Function and report any dropped debug ++ /// information. ++ void calculateDroppedVarStatsOnFunction(const Function *F, StringRef PassID, ++ StringRef FuncOrModName, ++ StringRef PassLevel); ++ /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or ++ /// after a pass has run to facilitate dropped variable calculation for an ++ /// llvm::Module. Calls runOnFunction on every Function in the Module. ++ void runOnModule(const Module *M, bool Before); ++ /// Iterate over all Functions in a Module and report any dropped debug ++ /// information. Will call calculateDroppedVarStatsOnFunction on every ++ /// Function. ++ void calculateDroppedVarStatsOnModule(const Module *M, StringRef PassID, ++ StringRef FuncOrModName, ++ StringRef PassLevel); ++ /// Override base class method to run on an llvm::Function specifically. ++ virtual void ++ visitEveryInstruction(unsigned &DroppedCount, ++ DenseMap &InlinedAtsMap, ++ VarID Var) override; ++ /// Override base class method to run on #dbg_values specifically. ++ virtual void visitEveryDebugRecord( ++ DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before) override; ++ ++ template static const IRUnitT *unwrapIR(Any IR) { ++ const IRUnitT **IRPtr = llvm::any_cast(&IR); ++ return IRPtr ? *IRPtr : nullptr; ++ } ++}; ++ ++} // namespace llvm ++ ++#endif +diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h +index 9301a12c740e..12a34c099eaf 100644 +--- a/llvm/include/llvm/Passes/StandardInstrumentations.h ++++ b/llvm/include/llvm/Passes/StandardInstrumentations.h +@@ -19,6 +19,7 @@ + #include "llvm/ADT/SmallVector.h" + #include "llvm/ADT/StringRef.h" + #include "llvm/ADT/StringSet.h" ++#include "llvm/CodeGen/DroppedVariableStats.h" + #include "llvm/CodeGen/MachineBasicBlock.h" + #include "llvm/IR/BasicBlock.h" + #include "llvm/IR/DebugInfoMetadata.h" +@@ -579,83 +580,6 @@ private: + static void SignalHandler(void *); + }; + +-/// A class to collect and print dropped debug information variable statistics. +-/// After every LLVM IR pass is run, it will print how many #dbg_values were +-/// dropped due to that pass. +-class DroppedVariableStats { +-public: +- DroppedVariableStats(bool DroppedVarStatsEnabled) { +- if (DroppedVarStatsEnabled) +- llvm::outs() +- << "Pass Level, Pass Name, Num of Dropped Variables, Func or " +- "Module Name\n"; +- }; +- // We intend this to be unique per-compilation, thus no copies. +- DroppedVariableStats(const DroppedVariableStats &) = delete; +- void operator=(const DroppedVariableStats &) = delete; +- +- void registerCallbacks(PassInstrumentationCallbacks &PIC); +- void runBeforePass(StringRef PassID, Any IR); +- void runAfterPass(StringRef PassID, Any IR, const PreservedAnalyses &PA); +- void runAfterPassInvalidated(StringRef PassID, const PreservedAnalyses &PA); +- bool getPassDroppedVariables() { return PassDroppedVariables; } +- +-private: +- bool PassDroppedVariables = false; +- /// A unique key that represents a #dbg_value. +- using VarID = +- std::tuple; +- +- struct DebugVariables { +- /// DenseSet of VarIDs before an optimization pass has run. +- DenseSet DebugVariablesBefore; +- /// DenseSet of VarIDs after an optimization pass has run. +- DenseSet DebugVariablesAfter; +- }; +- +- /// A stack of a DenseMap, that maps DebugVariables for every pass to an +- /// llvm::Function. A stack is used because an optimization pass can call +- /// other passes. +- SmallVector> DebugVariablesStack; +- +- /// A DenseSet tracking whether a scope was visited before. +- DenseSet VisitedScope; +- /// A stack of DenseMaps, which map the name of an llvm::Function to a +- /// DenseMap of VarIDs and their inlinedAt locations before an optimization +- /// pass has run. +- SmallVector>> InlinedAts; +- +- /// Iterate over all Functions in a Module and report any dropped debug +- /// information. Will call calculateDroppedVarStatsOnFunction on every +- /// Function. +- void calculateDroppedVarStatsOnModule(const Module *M, StringRef PassID, +- std::string FuncOrModName, +- std::string PassLevel); +- /// Iterate over all Instructions in a Function and report any dropped debug +- /// information. +- void calculateDroppedVarStatsOnFunction(const Function *F, StringRef PassID, +- std::string FuncOrModName, +- std::string PassLevel); +- /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or +- /// after a pass has run to facilitate dropped variable calculation for an +- /// llvm::Function. +- void runOnFunction(const Function *F, bool Before); +- /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or +- /// after a pass has run to facilitate dropped variable calculation for an +- /// llvm::Module. Calls runOnFunction on every Function in the Module. +- void runOnModule(const Module *M, bool Before); +- /// Remove a dropped #dbg_value VarID from all Sets in the +- /// DroppedVariablesBefore stack. +- void removeVarFromAllSets(VarID Var, const Function *F); +- /// Return true if \p Scope is the same as \p DbgValScope or a child scope of +- /// \p DbgValScope, return false otherwise. +- bool isScopeChildOfOrEqualTo(DIScope *Scope, const DIScope *DbgValScope); +- /// Return true if \p InlinedAt is the same as \p DbgValInlinedAt or part of +- /// the InlinedAt chain, return false otherwise. +- bool isInlinedAtChildOfOrEqualTo(const DILocation *InlinedAt, +- const DILocation *DbgValInlinedAt); +-}; +- + /// This class provides an interface to register all the standard pass + /// instrumentations and manages their state (if any). + class StandardInstrumentations { +@@ -673,7 +597,7 @@ class StandardInstrumentations { + PrintCrashIRInstrumentation PrintCrashIR; + IRChangedTester ChangeTester; + VerifyInstrumentation Verify; +- DroppedVariableStats DroppedStats; ++ DroppedVariableStatsIR DroppedStatsIR; + + bool VerifyEach; + +diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt +index 7b47c0e6f75d..263d4a9ee94d 100644 +--- a/llvm/lib/CodeGen/CMakeLists.txt ++++ b/llvm/lib/CodeGen/CMakeLists.txt +@@ -50,6 +50,7 @@ add_llvm_component_library(LLVMCodeGen + DeadMachineInstructionElim.cpp + DetectDeadLanes.cpp + DFAPacketizer.cpp ++ DroppedVariableStats.cpp + DwarfEHPrepare.cpp + EarlyIfConversion.cpp + EdgeBundles.cpp +diff --git a/llvm/lib/CodeGen/DroppedVariableStats.cpp b/llvm/lib/CodeGen/DroppedVariableStats.cpp +new file mode 100644 +index 000000000000..122fcad1293f +--- /dev/null ++++ b/llvm/lib/CodeGen/DroppedVariableStats.cpp +@@ -0,0 +1,194 @@ ++///===- DroppedVariableStats.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 ++/// ++///===---------------------------------------------------------------------===// ++/// \file ++/// Dropped Variable Statistics for Debug Information. Reports any number ++/// of #dbg_value that get dropped due to an optimization pass. ++/// ++///===---------------------------------------------------------------------===// ++ ++#include "llvm/CodeGen/DroppedVariableStats.h" ++#include "llvm/IR/DebugInfoMetadata.h" ++#include "llvm/IR/InstIterator.h" ++#include "llvm/IR/Module.h" ++ ++using namespace llvm; ++ ++bool DroppedVariableStats::isScopeChildOfOrEqualTo(const DIScope *Scope, ++ const DIScope *DbgValScope) { ++ while (Scope != nullptr) { ++ if (VisitedScope.find(Scope) == VisitedScope.end()) { ++ VisitedScope.insert(Scope); ++ if (Scope == DbgValScope) { ++ VisitedScope.clear(); ++ return true; ++ } ++ Scope = Scope->getScope(); ++ } else { ++ VisitedScope.clear(); ++ return false; ++ } ++ } ++ return false; ++} ++ ++bool DroppedVariableStats::isInlinedAtChildOfOrEqualTo( ++ const DILocation *InlinedAt, const DILocation *DbgValInlinedAt) { ++ if (DbgValInlinedAt == InlinedAt) ++ return true; ++ if (!DbgValInlinedAt) ++ return false; ++ auto *IA = InlinedAt; ++ while (IA) { ++ if (IA == DbgValInlinedAt) ++ return true; ++ IA = IA->getInlinedAt(); ++ } ++ return false; ++} ++ ++void DroppedVariableStats::calculateDroppedStatsAndPrint( ++ DebugVariables &DbgVariables, StringRef FuncName, StringRef PassID, ++ StringRef FuncOrModName, StringRef PassLevel, const Function *Func) { ++ unsigned DroppedCount = 0; ++ DenseSet &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore; ++ DenseSet &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter; ++ DenseMap &InlinedAtsMap = InlinedAts.back()[FuncName]; ++ // Find an Instruction that shares the same scope as the dropped #dbg_value or ++ // has a scope that is the child of the scope of the #dbg_value, and has an ++ // inlinedAt equal to the inlinedAt of the #dbg_value or it's inlinedAt chain ++ // contains the inlinedAt of the #dbg_value, if such an Instruction is found, ++ // debug information is dropped. ++ for (VarID Var : DebugVariablesBeforeSet) { ++ if (DebugVariablesAfterSet.contains(Var)) ++ continue; ++ visitEveryInstruction(DroppedCount, InlinedAtsMap, Var); ++ removeVarFromAllSets(Var, Func); ++ } ++ if (DroppedCount > 0) { ++ llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", " ++ << FuncOrModName << "\n"; ++ PassDroppedVariables = true; ++ } else ++ PassDroppedVariables = false; ++} ++ ++bool DroppedVariableStats::updateDroppedCount( ++ DILocation *DbgLoc, const DIScope *Scope, const DIScope *DbgValScope, ++ DenseMap &InlinedAtsMap, VarID Var, ++ unsigned &DroppedCount) { ++ ++ // If the Scope is a child of, or equal to the DbgValScope and is inlined at ++ // the Var's InlinedAt location, return true to signify that the Var has been ++ // dropped. ++ if (isScopeChildOfOrEqualTo(Scope, DbgValScope)) ++ if (isInlinedAtChildOfOrEqualTo(DbgLoc->getInlinedAt(), ++ InlinedAtsMap[Var])) { ++ // Found another instruction in the variable's scope, so there exists a ++ // break point at which the variable could be observed. Count it as ++ // dropped. ++ DroppedCount++; ++ return true; ++ } ++ return false; ++} ++ ++void DroppedVariableStats::run(DebugVariables &DbgVariables, StringRef FuncName, ++ bool Before) { ++ auto &VarIDSet = (Before ? DbgVariables.DebugVariablesBefore ++ : DbgVariables.DebugVariablesAfter); ++ auto &InlinedAtsMap = InlinedAts.back(); ++ if (Before) ++ InlinedAtsMap.try_emplace(FuncName, DenseMap()); ++ VarIDSet = DenseSet(); ++ visitEveryDebugRecord(VarIDSet, InlinedAtsMap, FuncName, Before); ++} ++ ++void DroppedVariableStats::populateVarIDSetAndInlinedMap( ++ const DILocalVariable *DbgVar, DebugLoc DbgLoc, DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before) { ++ VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar}; ++ VarIDSet.insert(Key); ++ if (Before) ++ InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt()); ++} ++ ++void DroppedVariableStatsIR::runOnFunction(const Function *F, bool Before) { ++ auto &DebugVariables = DebugVariablesStack.back()[F]; ++ auto FuncName = F->getName(); ++ Func = F; ++ run(DebugVariables, FuncName, Before); ++} ++ ++void DroppedVariableStatsIR::calculateDroppedVarStatsOnFunction( ++ const Function *F, StringRef PassID, StringRef FuncOrModName, ++ StringRef PassLevel) { ++ Func = F; ++ StringRef FuncName = F->getName(); ++ DebugVariables &DbgVariables = DebugVariablesStack.back()[F]; ++ calculateDroppedStatsAndPrint(DbgVariables, FuncName, PassID, FuncOrModName, ++ PassLevel, Func); ++} ++ ++void DroppedVariableStatsIR::runOnModule(const Module *M, bool Before) { ++ for (auto &F : *M) ++ runOnFunction(&F, Before); ++} ++ ++void DroppedVariableStatsIR::calculateDroppedVarStatsOnModule( ++ const Module *M, StringRef PassID, StringRef FuncOrModName, ++ StringRef PassLevel) { ++ for (auto &F : *M) { ++ calculateDroppedVarStatsOnFunction(&F, PassID, FuncOrModName, PassLevel); ++ } ++} ++ ++void DroppedVariableStatsIR::registerCallbacks( ++ PassInstrumentationCallbacks &PIC) { ++ if (!DroppedVariableStatsEnabled) ++ return; ++ ++ PIC.registerBeforeNonSkippedPassCallback( ++ [this](StringRef P, Any IR) { return runBeforePass(IR); }); ++ PIC.registerAfterPassCallback( ++ [this](StringRef P, Any IR, const PreservedAnalyses &PA) { ++ return runAfterPass(P, IR); ++ }); ++ PIC.registerAfterPassInvalidatedCallback( ++ [this](StringRef P, const PreservedAnalyses &PA) { return cleanup(); }); ++} ++ ++void DroppedVariableStatsIR::visitEveryInstruction( ++ unsigned &DroppedCount, DenseMap &InlinedAtsMap, ++ VarID Var) { ++ const DIScope *DbgValScope = std::get<0>(Var); ++ for (const auto &I : instructions(Func)) { ++ auto *DbgLoc = I.getDebugLoc().get(); ++ if (!DbgLoc) ++ continue; ++ if (updateDroppedCount(DbgLoc, DbgLoc->getScope(), DbgValScope, ++ InlinedAtsMap, Var, DroppedCount)) ++ break; ++ } ++} ++ ++void DroppedVariableStatsIR::visitEveryDebugRecord( ++ DenseSet &VarIDSet, ++ DenseMap> &InlinedAtsMap, ++ StringRef FuncName, bool Before) { ++ for (const auto &I : instructions(Func)) { ++ for (DbgRecord &DR : I.getDbgRecordRange()) { ++ if (auto *Dbg = dyn_cast(&DR)) { ++ auto *DbgVar = Dbg->getVariable(); ++ auto DbgLoc = DR.getDebugLoc(); ++ populateVarIDSetAndInlinedMap(DbgVar, DbgLoc, VarIDSet, InlinedAtsMap, ++ FuncName, Before); ++ } ++ } ++ } ++} +diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp +index 6259f8f736c8..b766517e68eb 100644 +--- a/llvm/lib/Passes/StandardInstrumentations.cpp ++++ b/llvm/lib/Passes/StandardInstrumentations.cpp +@@ -2462,7 +2462,7 @@ StandardInstrumentations::StandardInstrumentations( + PrintChanged == ChangePrinter::ColourDiffVerbose || + PrintChanged == ChangePrinter::ColourDiffQuiet), + WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose), +- Verify(DebugLogging), DroppedStats(DroppedVarStats), ++ Verify(DebugLogging), DroppedStatsIR(DroppedVarStats), + VerifyEach(VerifyEach) {} + + PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter = +@@ -2523,180 +2523,6 @@ void PrintCrashIRInstrumentation::registerCallbacks( + }); + } + +-void DroppedVariableStats::registerCallbacks( +- PassInstrumentationCallbacks &PIC) { +- if (!DroppedVarStats) +- return; +- +- PIC.registerBeforeNonSkippedPassCallback( +- [this](StringRef P, Any IR) { return this->runBeforePass(P, IR); }); +- PIC.registerAfterPassCallback( +- [this](StringRef P, Any IR, const PreservedAnalyses &PA) { +- return this->runAfterPass(P, IR, PA); +- }); +- PIC.registerAfterPassInvalidatedCallback( +- [this](StringRef P, const PreservedAnalyses &PA) { +- return this->runAfterPassInvalidated(P, PA); +- }); +-} +- +-void DroppedVariableStats::runBeforePass(StringRef PassID, Any IR) { +- DebugVariablesStack.push_back({DenseMap()}); +- InlinedAts.push_back({DenseMap>()}); +- if (auto *M = unwrapIR(IR)) +- return this->runOnModule(M, true); +- if (auto *F = unwrapIR(IR)) +- return this->runOnFunction(F, true); +-} +- +-void DroppedVariableStats::runOnFunction(const Function *F, bool Before) { +- auto &DebugVariables = DebugVariablesStack.back()[F]; +- auto &VarIDSet = (Before ? DebugVariables.DebugVariablesBefore +- : DebugVariables.DebugVariablesAfter); +- auto &InlinedAtsMap = InlinedAts.back(); +- auto FuncName = F->getName(); +- if (Before) +- InlinedAtsMap.try_emplace(FuncName, DenseMap()); +- VarIDSet = DenseSet(); +- for (const auto &I : instructions(F)) { +- for (DbgRecord &DR : I.getDbgRecordRange()) { +- if (auto *Dbg = dyn_cast(&DR)) { +- auto *DbgVar = Dbg->getVariable(); +- auto DbgLoc = DR.getDebugLoc(); +- VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar}; +- VarIDSet.insert(Key); +- if (Before) +- InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt()); +- } +- } +- } +-} +- +-void DroppedVariableStats::runOnModule(const Module *M, bool Before) { +- for (auto &F : *M) +- runOnFunction(&F, Before); +-} +- +-void DroppedVariableStats::removeVarFromAllSets(VarID Var, const Function *F) { +- // Do not remove Var from the last element, it will be popped from the stack. +- for (auto &DebugVariablesMap : llvm::drop_end(DebugVariablesStack)) +- DebugVariablesMap[F].DebugVariablesBefore.erase(Var); +-} +- +-void DroppedVariableStats::calculateDroppedVarStatsOnModule( +- const Module *M, StringRef PassID, std::string FuncOrModName, +- std::string PassLevel) { +- for (auto &F : *M) { +- calculateDroppedVarStatsOnFunction(&F, PassID, FuncOrModName, PassLevel); +- } +-} +- +-void DroppedVariableStats::calculateDroppedVarStatsOnFunction( +- const Function *F, StringRef PassID, std::string FuncOrModName, +- std::string PassLevel) { +- unsigned DroppedCount = 0; +- StringRef FuncName = F->getName(); +- DebugVariables &DbgVariables = DebugVariablesStack.back()[F]; +- DenseSet &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore; +- DenseSet &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter; +- DenseMap &InlinedAtsMap = InlinedAts.back()[FuncName]; +- // Find an Instruction that shares the same scope as the dropped #dbg_value or +- // has a scope that is the child of the scope of the #dbg_value, and has an +- // inlinedAt equal to the inlinedAt of the #dbg_value or it's inlinedAt chain +- // contains the inlinedAt of the #dbg_value, if such an Instruction is found, +- // debug information is dropped. +- for (VarID Var : DebugVariablesBeforeSet) { +- if (DebugVariablesAfterSet.contains(Var)) +- continue; +- const DIScope *DbgValScope = std::get<0>(Var); +- for (const auto &I : instructions(F)) { +- auto *DbgLoc = I.getDebugLoc().get(); +- if (!DbgLoc) +- continue; +- +- auto *Scope = DbgLoc->getScope(); +- if (isScopeChildOfOrEqualTo(Scope, DbgValScope)) { +- if (isInlinedAtChildOfOrEqualTo(DbgLoc->getInlinedAt(), +- InlinedAtsMap[Var])) { +- // Found another instruction in the variable's scope, so there exists +- // a break point at which the variable could be observed. Count it as +- // dropped. +- DroppedCount++; +- break; +- } +- } +- } +- removeVarFromAllSets(Var, F); +- } +- if (DroppedCount > 0) { +- llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", " +- << FuncOrModName << "\n"; +- PassDroppedVariables = true; +- } else +- PassDroppedVariables = false; +-} +- +-void DroppedVariableStats::runAfterPassInvalidated( +- StringRef PassID, const PreservedAnalyses &PA) { +- DebugVariablesStack.pop_back(); +- InlinedAts.pop_back(); +-} +- +-void DroppedVariableStats::runAfterPass(StringRef PassID, Any IR, +- const PreservedAnalyses &PA) { +- std::string PassLevel; +- std::string FuncOrModName; +- if (auto *M = unwrapIR(IR)) { +- this->runOnModule(M, false); +- PassLevel = "Module"; +- FuncOrModName = M->getName(); +- calculateDroppedVarStatsOnModule(M, PassID, FuncOrModName, PassLevel); +- } else if (auto *F = unwrapIR(IR)) { +- this->runOnFunction(F, false); +- PassLevel = "Function"; +- FuncOrModName = F->getName(); +- calculateDroppedVarStatsOnFunction(F, PassID, FuncOrModName, PassLevel); +- } +- +- DebugVariablesStack.pop_back(); +- InlinedAts.pop_back(); +-} +- +-bool DroppedVariableStats::isScopeChildOfOrEqualTo(DIScope *Scope, +- const DIScope *DbgValScope) { +- while (Scope != nullptr) { +- if (VisitedScope.find(Scope) == VisitedScope.end()) { +- VisitedScope.insert(Scope); +- if (Scope == DbgValScope) { +- VisitedScope.clear(); +- return true; +- } +- Scope = Scope->getScope(); +- } else { +- VisitedScope.clear(); +- return false; +- } +- } +- return false; +-} +- +-bool DroppedVariableStats::isInlinedAtChildOfOrEqualTo( +- const DILocation *InlinedAt, const DILocation *DbgValInlinedAt) { +- if (DbgValInlinedAt == InlinedAt) +- return true; +- if (!DbgValInlinedAt) +- return false; +- if (!InlinedAt) +- return false; +- auto *IA = InlinedAt; +- while (IA) { +- if (IA == DbgValInlinedAt) +- return true; +- IA = IA->getInlinedAt(); +- } +- return false; +-} +- + void StandardInstrumentations::registerCallbacks( + PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM) { + PrintIR.registerCallbacks(PIC); +@@ -2712,7 +2538,7 @@ void StandardInstrumentations::registerCallbacks( + WebsiteChangeReporter.registerCallbacks(PIC); + ChangeTester.registerCallbacks(PIC); + PrintCrashIR.registerCallbacks(PIC); +- DroppedStats.registerCallbacks(PIC); ++ DroppedStatsIR.registerCallbacks(PIC); + if (MAM) + PreservedCFGChecker.registerCallbacks(PIC, *MAM); + +diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt +index 963cdcc0275e..807fd1a9b7b5 100644 +--- a/llvm/unittests/CodeGen/CMakeLists.txt ++++ b/llvm/unittests/CodeGen/CMakeLists.txt +@@ -27,6 +27,7 @@ add_llvm_unittest(CodeGenTests + CCStateTest.cpp + DIEHashTest.cpp + DIETest.cpp ++ DroppedVariableStatsIRTest.cpp + DwarfStringPoolEntryRefTest.cpp + InstrRefLDVTest.cpp + LowLevelTypeTest.cpp +diff --git a/llvm/unittests/IR/DroppedVariableStatsTest.cpp b/llvm/unittests/CodeGen/DroppedVariableStatsIRTest.cpp +similarity index 91% +rename from llvm/unittests/IR/DroppedVariableStatsTest.cpp +rename to llvm/unittests/CodeGen/DroppedVariableStatsIRTest.cpp +index 61f3a87bb355..094ec7b65763 100644 +--- a/llvm/unittests/IR/DroppedVariableStatsTest.cpp ++++ b/llvm/unittests/CodeGen/DroppedVariableStatsIRTest.cpp +@@ -1,5 +1,4 @@ +-//===- unittests/IR/DroppedVariableStatsTest.cpp - TimePassesHandler tests +-//----------===// ++//===- unittests/IR/DroppedVariableStatsIRTest.cpp ------------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + // See https://llvm.org/LICENSE.txt for license information. +@@ -8,6 +7,7 @@ + //===----------------------------------------------------------------------===// + + #include "llvm/AsmParser/Parser.h" ++#include "llvm/CodeGen/DroppedVariableStats.h" + #include "llvm/IR/Function.h" + #include "llvm/IR/InstIterator.h" + #include "llvm/IR/LegacyPassManager.h" +@@ -44,7 +44,7 @@ namespace { + // 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(DroppedVariableStats, BothDeleted) { ++TEST(DroppedVariableStatsIR, BothDeleted) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -79,9 +79,8 @@ TEST(DroppedVariableStats, BothDeleted) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -92,16 +91,15 @@ TEST(DroppedVariableStats, BothDeleted) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + 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(DroppedVariableStats, DbgValLost) { ++TEST(DroppedVariableStatsIR, DbgValLost) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -136,9 +134,8 @@ TEST(DroppedVariableStats, DbgValLost) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -148,16 +145,15 @@ TEST(DroppedVariableStats, DbgValLost) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + 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(DroppedVariableStats, UnrelatedScopes) { ++TEST(DroppedVariableStatsIR, UnrelatedScopes) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -193,9 +189,8 @@ TEST(DroppedVariableStats, UnrelatedScopes) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -205,16 +200,15 @@ TEST(DroppedVariableStats, UnrelatedScopes) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + 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(DroppedVariableStats, ChildScopes) { ++TEST(DroppedVariableStatsIR, ChildScopes) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -250,9 +244,8 @@ TEST(DroppedVariableStats, ChildScopes) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -262,9 +255,8 @@ TEST(DroppedVariableStats, ChildScopes) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); + } + +@@ -272,7 +264,7 @@ TEST(DroppedVariableStats, ChildScopes) { + // 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(DroppedVariableStats, InlinedAt) { ++TEST(DroppedVariableStatsIR, InlinedAt) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -308,9 +300,8 @@ TEST(DroppedVariableStats, InlinedAt) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -320,9 +311,8 @@ TEST(DroppedVariableStats, InlinedAt) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + ASSERT_EQ(Stats.getPassDroppedVariables(), false); + } + +@@ -330,7 +320,7 @@ TEST(DroppedVariableStats, InlinedAt) { + // 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(DroppedVariableStats, InlinedAtShared) { ++TEST(DroppedVariableStatsIR, InlinedAtShared) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -366,9 +356,8 @@ TEST(DroppedVariableStats, InlinedAtShared) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -378,9 +367,8 @@ TEST(DroppedVariableStats, InlinedAtShared) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); + } + +@@ -388,7 +376,7 @@ TEST(DroppedVariableStats, InlinedAtShared) { + // 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(DroppedVariableStats, InlinedAtChild) { ++TEST(DroppedVariableStatsIR, InlinedAtChild) { + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + +@@ -425,9 +413,8 @@ TEST(DroppedVariableStats, InlinedAtChild) { + std::unique_ptr M = parseIR(C, IR); + ASSERT_TRUE(M); + +- DroppedVariableStats Stats(true); +- Stats.runBeforePass("Test", +- llvm::Any(const_cast(M.get()))); ++ DroppedVariableStatsIR Stats(true); ++ Stats.runBeforePass(llvm::Any(const_cast(M.get()))); + + // This loop simulates an IR pass that drops debug information. + for (auto &F : *M) { +@@ -437,9 +424,8 @@ TEST(DroppedVariableStats, InlinedAtChild) { + } + break; + } +- PreservedAnalyses PA; + Stats.runAfterPass("Test", +- llvm::Any(const_cast(M.get())), PA); ++ llvm::Any(const_cast(M.get()))); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); + } + +diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt +index ed93ee547d22..e5c8630f3eed 100644 +--- a/llvm/unittests/IR/CMakeLists.txt ++++ b/llvm/unittests/IR/CMakeLists.txt +@@ -43,7 +43,6 @@ add_llvm_unittest(IRTests + ShuffleVectorInstTest.cpp + StructuralHashTest.cpp + TimePassesTest.cpp +- DroppedVariableStatsTest.cpp + TypesTest.cpp + UseTest.cpp + UserTest.cpp +-- +2.46.2 + diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStats.h b/llvm/include/llvm/CodeGen/DroppedVariableStats.h new file mode 100644 index 0000000000000..c7b654ea58557 --- /dev/null +++ b/llvm/include/llvm/CodeGen/DroppedVariableStats.h @@ -0,0 +1,224 @@ +///===- DroppedVariableStats.h - Opt Diagnostics -*- C++ -*----------------===// +/// +/// 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 +/// +///===---------------------------------------------------------------------===// +/// \file +/// Dropped Variable Statistics for Debug Information. Reports any number +/// of #dbg_value that get dropped due to an optimization pass. +/// +///===---------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_DROPPEDVARIABLESTATS_H +#define LLVM_CODEGEN_DROPPEDVARIABLESTATS_H + +#include "llvm/CodeGen/MachinePassManager.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassInstrumentation.h" + +namespace llvm { + +/// A unique key that represents a debug variable. +/// First const DIScope *: Represents the scope of the debug variable. +/// Second const DIScope *: Represents the InlinedAt scope of the debug +/// variable. const DILocalVariable *: It is a pointer to the debug variable +/// itself. +using VarID = + std::tuple; + +/// A base class to collect and print dropped debug information variable +/// statistics. +class DroppedVariableStats { +public: + DroppedVariableStats(bool DroppedVarStatsEnabled) + : DroppedVariableStatsEnabled(DroppedVarStatsEnabled) { + if (DroppedVarStatsEnabled) + llvm::outs() + << "Pass Level, Pass Name, Num of Dropped Variables, Func or " + "Module Name\n"; + }; + + virtual ~DroppedVariableStats() = default; + + // We intend this to be unique per-compilation, thus no copies. + DroppedVariableStats(const DroppedVariableStats &) = delete; + void operator=(const DroppedVariableStats &) = delete; + + bool getPassDroppedVariables() { return PassDroppedVariables; } + +protected: + void setup() { + DebugVariablesStack.push_back( + {DenseMap()}); + InlinedAts.push_back( + {DenseMap>()}); + } + + void cleanup() { + assert(!DebugVariablesStack.empty() && + "DebugVariablesStack shouldn't be empty!"); + assert(!InlinedAts.empty() && "InlinedAts shouldn't be empty!"); + DebugVariablesStack.pop_back(); + InlinedAts.pop_back(); + } + + bool DroppedVariableStatsEnabled = false; + struct DebugVariables { + /// DenseSet of VarIDs before an optimization pass has run. + DenseSet DebugVariablesBefore; + /// DenseSet of VarIDs after an optimization pass has run. + DenseSet DebugVariablesAfter; + }; + +protected: + /// A stack of a DenseMap, that maps DebugVariables for every pass to an + /// llvm::Function. A stack is used because an optimization pass can call + /// other passes. + SmallVector> DebugVariablesStack; + + /// A DenseSet tracking whether a scope was visited before. + DenseSet VisitedScope; + /// A stack of DenseMaps, which map the name of an llvm::Function to a + /// DenseMap of VarIDs and their inlinedAt locations before an optimization + /// pass has run. + SmallVector>> InlinedAts; + /// Calculate the number of dropped variables in an llvm::Function or + /// llvm::MachineFunction and print the relevant information to stdout. + void calculateDroppedStatsAndPrint(DebugVariables &DbgVariables, + StringRef FuncName, StringRef PassID, + StringRef FuncOrModName, + StringRef PassLevel, const Function *Func); + + /// Check if a \p Var has been dropped or is a false positive. Also update the + /// \p DroppedCount if a debug variable is dropped. + bool updateDroppedCount(DILocation *DbgLoc, const DIScope *Scope, + const DIScope *DbgValScope, + DenseMap &InlinedAtsMap, + VarID Var, unsigned &DroppedCount); + /// Run code to populate relevant data structures over an llvm::Function or + /// llvm::MachineFunction. + void run(DebugVariables &DbgVariables, StringRef FuncName, bool Before); + /// Populate the VarIDSet and InlinedAtMap with the relevant information + /// needed for before and after pass analysis to determine dropped variable + /// status. + void populateVarIDSetAndInlinedMap( + const DILocalVariable *DbgVar, DebugLoc DbgLoc, DenseSet &VarIDSet, + DenseMap> &InlinedAtsMap, + StringRef FuncName, bool Before); + /// Visit every llvm::Instruction or llvm::MachineInstruction and check if the + /// debug variable denoted by its ID \p Var may have been dropped by an + /// optimization pass. + virtual void + visitEveryInstruction(unsigned &DroppedCount, + DenseMap &InlinedAtsMap, + VarID Var) = 0; + /// Visit every debug record in an llvm::Function or llvm::MachineFunction + /// and call populateVarIDSetAndInlinedMap on it. + virtual void visitEveryDebugRecord( + DenseSet &VarIDSet, + DenseMap> &InlinedAtsMap, + StringRef FuncName, bool Before) = 0; + +private: + /// Remove a dropped debug variable's VarID from all Sets in the + /// DroppedVariablesBefore stack. + void removeVarFromAllSets(VarID Var, const Function *F) { + // Do not remove Var from the last element, it will be popped from the + // stack. + for (auto &DebugVariablesMap : llvm::drop_end(DebugVariablesStack)) + DebugVariablesMap[F].DebugVariablesBefore.erase(Var); + } + /// Return true if \p Scope is the same as \p DbgValScope or a child scope of + /// \p DbgValScope, return false otherwise. + bool isScopeChildOfOrEqualTo(const DIScope *Scope, + const DIScope *DbgValScope); + /// Return true if \p InlinedAt is the same as \p DbgValInlinedAt or part of + /// the InlinedAt chain, return false otherwise. + bool isInlinedAtChildOfOrEqualTo(const DILocation *InlinedAt, + const DILocation *DbgValInlinedAt); + bool PassDroppedVariables = false; +}; + +/// A class to collect and print dropped debug information due to LLVM IR +/// optimization passes. After every LLVM IR pass is run, it will print how many +/// #dbg_values were dropped due to that pass. +class DroppedVariableStatsIR : public DroppedVariableStats { +public: + DroppedVariableStatsIR(bool DroppedVarStatsEnabled) + : llvm::DroppedVariableStats(DroppedVarStatsEnabled) {} + + void runBeforePass(Any IR) { + setup(); + if (const auto *M = unwrapIR(IR)) + return this->runOnModule(M, true); + if (const auto *F = unwrapIR(IR)) + return this->runOnFunction(F, true); + } + + void runAfterPass(StringRef P, Any IR) { + if (const auto *M = unwrapIR(IR)) + runAfterPassModule(P, M); + else if (const auto *F = unwrapIR(IR)) + runAfterPassFunction(P, F); + cleanup(); + } + + void registerCallbacks(PassInstrumentationCallbacks &PIC); + +private: + const Function *Func; + + void runAfterPassFunction(StringRef PassID, const Function *F) { + runOnFunction(F, false); + calculateDroppedVarStatsOnFunction(F, PassID, F->getName().str(), + "Function"); + } + + void runAfterPassModule(StringRef PassID, const Module *M) { + runOnModule(M, false); + calculateDroppedVarStatsOnModule(M, PassID, M->getName().str(), "Module"); + } + /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or + /// after a pass has run to facilitate dropped variable calculation for an + /// llvm::Function. + void runOnFunction(const Function *F, bool Before); + /// Iterate over all Instructions in a Function and report any dropped debug + /// information. + void calculateDroppedVarStatsOnFunction(const Function *F, StringRef PassID, + StringRef FuncOrModName, + StringRef PassLevel); + /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or + /// after a pass has run to facilitate dropped variable calculation for an + /// llvm::Module. Calls runOnFunction on every Function in the Module. + void runOnModule(const Module *M, bool Before); + /// Iterate over all Functions in a Module and report any dropped debug + /// information. Will call calculateDroppedVarStatsOnFunction on every + /// Function. + void calculateDroppedVarStatsOnModule(const Module *M, StringRef PassID, + StringRef FuncOrModName, + StringRef PassLevel); + /// Override base class method to run on an llvm::Function specifically. + virtual void + visitEveryInstruction(unsigned &DroppedCount, + DenseMap &InlinedAtsMap, + VarID Var) override; + /// Override base class method to run on #dbg_values specifically. + virtual void visitEveryDebugRecord( + DenseSet &VarIDSet, + DenseMap> &InlinedAtsMap, + StringRef FuncName, bool Before) override; + + template static const IRUnitT *unwrapIR(Any IR) { + const IRUnitT **IRPtr = llvm::any_cast(&IR); + return IRPtr ? *IRPtr : nullptr; + } +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h index 9301a12c740ee..12a34c099eaff 100644 --- a/llvm/include/llvm/Passes/StandardInstrumentations.h +++ b/llvm/include/llvm/Passes/StandardInstrumentations.h @@ -19,6 +19,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" +#include "llvm/CodeGen/DroppedVariableStats.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/DebugInfoMetadata.h" @@ -579,83 +580,6 @@ class PrintCrashIRInstrumentation { static void SignalHandler(void *); }; -/// A class to collect and print dropped debug information variable statistics. -/// After every LLVM IR pass is run, it will print how many #dbg_values were -/// dropped due to that pass. -class DroppedVariableStats { -public: - DroppedVariableStats(bool DroppedVarStatsEnabled) { - if (DroppedVarStatsEnabled) - llvm::outs() - << "Pass Level, Pass Name, Num of Dropped Variables, Func or " - "Module Name\n"; - }; - // We intend this to be unique per-compilation, thus no copies. - DroppedVariableStats(const DroppedVariableStats &) = delete; - void operator=(const DroppedVariableStats &) = delete; - - void registerCallbacks(PassInstrumentationCallbacks &PIC); - void runBeforePass(StringRef PassID, Any IR); - void runAfterPass(StringRef PassID, Any IR, const PreservedAnalyses &PA); - void runAfterPassInvalidated(StringRef PassID, const PreservedAnalyses &PA); - bool getPassDroppedVariables() { return PassDroppedVariables; } - -private: - bool PassDroppedVariables = false; - /// A unique key that represents a #dbg_value. - using VarID = - std::tuple; - - struct DebugVariables { - /// DenseSet of VarIDs before an optimization pass has run. - DenseSet DebugVariablesBefore; - /// DenseSet of VarIDs after an optimization pass has run. - DenseSet DebugVariablesAfter; - }; - - /// A stack of a DenseMap, that maps DebugVariables for every pass to an - /// llvm::Function. A stack is used because an optimization pass can call - /// other passes. - SmallVector> DebugVariablesStack; - - /// A DenseSet tracking whether a scope was visited before. - DenseSet VisitedScope; - /// A stack of DenseMaps, which map the name of an llvm::Function to a - /// DenseMap of VarIDs and their inlinedAt locations before an optimization - /// pass has run. - SmallVector>> InlinedAts; - - /// Iterate over all Functions in a Module and report any dropped debug - /// information. Will call calculateDroppedVarStatsOnFunction on every - /// Function. - void calculateDroppedVarStatsOnModule(const Module *M, StringRef PassID, - std::string FuncOrModName, - std::string PassLevel); - /// Iterate over all Instructions in a Function and report any dropped debug - /// information. - void calculateDroppedVarStatsOnFunction(const Function *F, StringRef PassID, - std::string FuncOrModName, - std::string PassLevel); - /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or - /// after a pass has run to facilitate dropped variable calculation for an - /// llvm::Function. - void runOnFunction(const Function *F, bool Before); - /// Populate DebugVariablesBefore, DebugVariablesAfter, InlinedAts before or - /// after a pass has run to facilitate dropped variable calculation for an - /// llvm::Module. Calls runOnFunction on every Function in the Module. - void runOnModule(const Module *M, bool Before); - /// Remove a dropped #dbg_value VarID from all Sets in the - /// DroppedVariablesBefore stack. - void removeVarFromAllSets(VarID Var, const Function *F); - /// Return true if \p Scope is the same as \p DbgValScope or a child scope of - /// \p DbgValScope, return false otherwise. - bool isScopeChildOfOrEqualTo(DIScope *Scope, const DIScope *DbgValScope); - /// Return true if \p InlinedAt is the same as \p DbgValInlinedAt or part of - /// the InlinedAt chain, return false otherwise. - bool isInlinedAtChildOfOrEqualTo(const DILocation *InlinedAt, - const DILocation *DbgValInlinedAt); -}; - /// This class provides an interface to register all the standard pass /// instrumentations and manages their state (if any). class StandardInstrumentations { @@ -673,7 +597,7 @@ class StandardInstrumentations { PrintCrashIRInstrumentation PrintCrashIR; IRChangedTester ChangeTester; VerifyInstrumentation Verify; - DroppedVariableStats DroppedStats; + DroppedVariableStatsIR DroppedStatsIR; bool VerifyEach; diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 7b47c0e6f75db..263d4a9ee94d2 100644 --- a/llvm/lib/CodeGen/CMakeLists.txt +++ b/llvm/lib/CodeGen/CMakeLists.txt @@ -50,6 +50,7 @@ add_llvm_component_library(LLVMCodeGen DeadMachineInstructionElim.cpp DetectDeadLanes.cpp DFAPacketizer.cpp + DroppedVariableStats.cpp DwarfEHPrepare.cpp EarlyIfConversion.cpp EdgeBundles.cpp diff --git a/llvm/lib/CodeGen/DroppedVariableStats.cpp b/llvm/lib/CodeGen/DroppedVariableStats.cpp new file mode 100644 index 0000000000000..122fcad1293f1 --- /dev/null +++ b/llvm/lib/CodeGen/DroppedVariableStats.cpp @@ -0,0 +1,194 @@ +///===- DroppedVariableStats.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 +/// +///===---------------------------------------------------------------------===// +/// \file +/// Dropped Variable Statistics for Debug Information. Reports any number +/// of #dbg_value that get dropped due to an optimization pass. +/// +///===---------------------------------------------------------------------===// + +#include "llvm/CodeGen/DroppedVariableStats.h" +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +bool DroppedVariableStats::isScopeChildOfOrEqualTo(const DIScope *Scope, + const DIScope *DbgValScope) { + while (Scope != nullptr) { + if (VisitedScope.find(Scope) == VisitedScope.end()) { + VisitedScope.insert(Scope); + if (Scope == DbgValScope) { + VisitedScope.clear(); + return true; + } + Scope = Scope->getScope(); + } else { + VisitedScope.clear(); + return false; + } + } + return false; +} + +bool DroppedVariableStats::isInlinedAtChildOfOrEqualTo( + const DILocation *InlinedAt, const DILocation *DbgValInlinedAt) { + if (DbgValInlinedAt == InlinedAt) + return true; + if (!DbgValInlinedAt) + return false; + auto *IA = InlinedAt; + while (IA) { + if (IA == DbgValInlinedAt) + return true; + IA = IA->getInlinedAt(); + } + return false; +} + +void DroppedVariableStats::calculateDroppedStatsAndPrint( + DebugVariables &DbgVariables, StringRef FuncName, StringRef PassID, + StringRef FuncOrModName, StringRef PassLevel, const Function *Func) { + unsigned DroppedCount = 0; + DenseSet &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore; + DenseSet &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter; + DenseMap &InlinedAtsMap = InlinedAts.back()[FuncName]; + // Find an Instruction that shares the same scope as the dropped #dbg_value or + // has a scope that is the child of the scope of the #dbg_value, and has an + // inlinedAt equal to the inlinedAt of the #dbg_value or it's inlinedAt chain + // contains the inlinedAt of the #dbg_value, if such an Instruction is found, + // debug information is dropped. + for (VarID Var : DebugVariablesBeforeSet) { + if (DebugVariablesAfterSet.contains(Var)) + continue; + visitEveryInstruction(DroppedCount, InlinedAtsMap, Var); + removeVarFromAllSets(Var, Func); + } + if (DroppedCount > 0) { + llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", " + << FuncOrModName << "\n"; + PassDroppedVariables = true; + } else + PassDroppedVariables = false; +} + +bool DroppedVariableStats::updateDroppedCount( + DILocation *DbgLoc, const DIScope *Scope, const DIScope *DbgValScope, + DenseMap &InlinedAtsMap, VarID Var, + unsigned &DroppedCount) { + + // If the Scope is a child of, or equal to the DbgValScope and is inlined at + // the Var's InlinedAt location, return true to signify that the Var has been + // dropped. + if (isScopeChildOfOrEqualTo(Scope, DbgValScope)) + if (isInlinedAtChildOfOrEqualTo(DbgLoc->getInlinedAt(), + InlinedAtsMap[Var])) { + // Found another instruction in the variable's scope, so there exists a + // break point at which the variable could be observed. Count it as + // dropped. + DroppedCount++; + return true; + } + return false; +} + +void DroppedVariableStats::run(DebugVariables &DbgVariables, StringRef FuncName, + bool Before) { + auto &VarIDSet = (Before ? DbgVariables.DebugVariablesBefore + : DbgVariables.DebugVariablesAfter); + auto &InlinedAtsMap = InlinedAts.back(); + if (Before) + InlinedAtsMap.try_emplace(FuncName, DenseMap()); + VarIDSet = DenseSet(); + visitEveryDebugRecord(VarIDSet, InlinedAtsMap, FuncName, Before); +} + +void DroppedVariableStats::populateVarIDSetAndInlinedMap( + const DILocalVariable *DbgVar, DebugLoc DbgLoc, DenseSet &VarIDSet, + DenseMap> &InlinedAtsMap, + StringRef FuncName, bool Before) { + VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar}; + VarIDSet.insert(Key); + if (Before) + InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt()); +} + +void DroppedVariableStatsIR::runOnFunction(const Function *F, bool Before) { + auto &DebugVariables = DebugVariablesStack.back()[F]; + auto FuncName = F->getName(); + Func = F; + run(DebugVariables, FuncName, Before); +} + +void DroppedVariableStatsIR::calculateDroppedVarStatsOnFunction( + const Function *F, StringRef PassID, StringRef FuncOrModName, + StringRef PassLevel) { + Func = F; + StringRef FuncName = F->getName(); + DebugVariables &DbgVariables = DebugVariablesStack.back()[F]; + calculateDroppedStatsAndPrint(DbgVariables, FuncName, PassID, FuncOrModName, + PassLevel, Func); +} + +void DroppedVariableStatsIR::runOnModule(const Module *M, bool Before) { + for (auto &F : *M) + runOnFunction(&F, Before); +} + +void DroppedVariableStatsIR::calculateDroppedVarStatsOnModule( + const Module *M, StringRef PassID, StringRef FuncOrModName, + StringRef PassLevel) { + for (auto &F : *M) { + calculateDroppedVarStatsOnFunction(&F, PassID, FuncOrModName, PassLevel); + } +} + +void DroppedVariableStatsIR::registerCallbacks( + PassInstrumentationCallbacks &PIC) { + if (!DroppedVariableStatsEnabled) + return; + + PIC.registerBeforeNonSkippedPassCallback( + [this](StringRef P, Any IR) { return runBeforePass(IR); }); + PIC.registerAfterPassCallback( + [this](StringRef P, Any IR, const PreservedAnalyses &PA) { + return runAfterPass(P, IR); + }); + PIC.registerAfterPassInvalidatedCallback( + [this](StringRef P, const PreservedAnalyses &PA) { return cleanup(); }); +} + +void DroppedVariableStatsIR::visitEveryInstruction( + unsigned &DroppedCount, DenseMap &InlinedAtsMap, + VarID Var) { + const DIScope *DbgValScope = std::get<0>(Var); + for (const auto &I : instructions(Func)) { + auto *DbgLoc = I.getDebugLoc().get(); + if (!DbgLoc) + continue; + if (updateDroppedCount(DbgLoc, DbgLoc->getScope(), DbgValScope, + InlinedAtsMap, Var, DroppedCount)) + break; + } +} + +void DroppedVariableStatsIR::visitEveryDebugRecord( + DenseSet &VarIDSet, + DenseMap> &InlinedAtsMap, + StringRef FuncName, bool Before) { + for (const auto &I : instructions(Func)) { + for (DbgRecord &DR : I.getDbgRecordRange()) { + if (auto *Dbg = dyn_cast(&DR)) { + auto *DbgVar = Dbg->getVariable(); + auto DbgLoc = DR.getDebugLoc(); + populateVarIDSetAndInlinedMap(DbgVar, DbgLoc, VarIDSet, InlinedAtsMap, + FuncName, Before); + } + } + } +} diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp index 6259f8f736c80..b766517e68eba 100644 --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -2462,7 +2462,7 @@ StandardInstrumentations::StandardInstrumentations( PrintChanged == ChangePrinter::ColourDiffVerbose || PrintChanged == ChangePrinter::ColourDiffQuiet), WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose), - Verify(DebugLogging), DroppedStats(DroppedVarStats), + Verify(DebugLogging), DroppedStatsIR(DroppedVarStats), VerifyEach(VerifyEach) {} PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter = @@ -2523,180 +2523,6 @@ void PrintCrashIRInstrumentation::registerCallbacks( }); } -void DroppedVariableStats::registerCallbacks( - PassInstrumentationCallbacks &PIC) { - if (!DroppedVarStats) - return; - - PIC.registerBeforeNonSkippedPassCallback( - [this](StringRef P, Any IR) { return this->runBeforePass(P, IR); }); - PIC.registerAfterPassCallback( - [this](StringRef P, Any IR, const PreservedAnalyses &PA) { - return this->runAfterPass(P, IR, PA); - }); - PIC.registerAfterPassInvalidatedCallback( - [this](StringRef P, const PreservedAnalyses &PA) { - return this->runAfterPassInvalidated(P, PA); - }); -} - -void DroppedVariableStats::runBeforePass(StringRef PassID, Any IR) { - DebugVariablesStack.push_back({DenseMap()}); - InlinedAts.push_back({DenseMap>()}); - if (auto *M = unwrapIR(IR)) - return this->runOnModule(M, true); - if (auto *F = unwrapIR(IR)) - return this->runOnFunction(F, true); -} - -void DroppedVariableStats::runOnFunction(const Function *F, bool Before) { - auto &DebugVariables = DebugVariablesStack.back()[F]; - auto &VarIDSet = (Before ? DebugVariables.DebugVariablesBefore - : DebugVariables.DebugVariablesAfter); - auto &InlinedAtsMap = InlinedAts.back(); - auto FuncName = F->getName(); - if (Before) - InlinedAtsMap.try_emplace(FuncName, DenseMap()); - VarIDSet = DenseSet(); - for (const auto &I : instructions(F)) { - for (DbgRecord &DR : I.getDbgRecordRange()) { - if (auto *Dbg = dyn_cast(&DR)) { - auto *DbgVar = Dbg->getVariable(); - auto DbgLoc = DR.getDebugLoc(); - VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar}; - VarIDSet.insert(Key); - if (Before) - InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt()); - } - } - } -} - -void DroppedVariableStats::runOnModule(const Module *M, bool Before) { - for (auto &F : *M) - runOnFunction(&F, Before); -} - -void DroppedVariableStats::removeVarFromAllSets(VarID Var, const Function *F) { - // Do not remove Var from the last element, it will be popped from the stack. - for (auto &DebugVariablesMap : llvm::drop_end(DebugVariablesStack)) - DebugVariablesMap[F].DebugVariablesBefore.erase(Var); -} - -void DroppedVariableStats::calculateDroppedVarStatsOnModule( - const Module *M, StringRef PassID, std::string FuncOrModName, - std::string PassLevel) { - for (auto &F : *M) { - calculateDroppedVarStatsOnFunction(&F, PassID, FuncOrModName, PassLevel); - } -} - -void DroppedVariableStats::calculateDroppedVarStatsOnFunction( - const Function *F, StringRef PassID, std::string FuncOrModName, - std::string PassLevel) { - unsigned DroppedCount = 0; - StringRef FuncName = F->getName(); - DebugVariables &DbgVariables = DebugVariablesStack.back()[F]; - DenseSet &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore; - DenseSet &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter; - DenseMap &InlinedAtsMap = InlinedAts.back()[FuncName]; - // Find an Instruction that shares the same scope as the dropped #dbg_value or - // has a scope that is the child of the scope of the #dbg_value, and has an - // inlinedAt equal to the inlinedAt of the #dbg_value or it's inlinedAt chain - // contains the inlinedAt of the #dbg_value, if such an Instruction is found, - // debug information is dropped. - for (VarID Var : DebugVariablesBeforeSet) { - if (DebugVariablesAfterSet.contains(Var)) - continue; - const DIScope *DbgValScope = std::get<0>(Var); - for (const auto &I : instructions(F)) { - auto *DbgLoc = I.getDebugLoc().get(); - if (!DbgLoc) - continue; - - auto *Scope = DbgLoc->getScope(); - if (isScopeChildOfOrEqualTo(Scope, DbgValScope)) { - if (isInlinedAtChildOfOrEqualTo(DbgLoc->getInlinedAt(), - InlinedAtsMap[Var])) { - // Found another instruction in the variable's scope, so there exists - // a break point at which the variable could be observed. Count it as - // dropped. - DroppedCount++; - break; - } - } - } - removeVarFromAllSets(Var, F); - } - if (DroppedCount > 0) { - llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", " - << FuncOrModName << "\n"; - PassDroppedVariables = true; - } else - PassDroppedVariables = false; -} - -void DroppedVariableStats::runAfterPassInvalidated( - StringRef PassID, const PreservedAnalyses &PA) { - DebugVariablesStack.pop_back(); - InlinedAts.pop_back(); -} - -void DroppedVariableStats::runAfterPass(StringRef PassID, Any IR, - const PreservedAnalyses &PA) { - std::string PassLevel; - std::string FuncOrModName; - if (auto *M = unwrapIR(IR)) { - this->runOnModule(M, false); - PassLevel = "Module"; - FuncOrModName = M->getName(); - calculateDroppedVarStatsOnModule(M, PassID, FuncOrModName, PassLevel); - } else if (auto *F = unwrapIR(IR)) { - this->runOnFunction(F, false); - PassLevel = "Function"; - FuncOrModName = F->getName(); - calculateDroppedVarStatsOnFunction(F, PassID, FuncOrModName, PassLevel); - } - - DebugVariablesStack.pop_back(); - InlinedAts.pop_back(); -} - -bool DroppedVariableStats::isScopeChildOfOrEqualTo(DIScope *Scope, - const DIScope *DbgValScope) { - while (Scope != nullptr) { - if (VisitedScope.find(Scope) == VisitedScope.end()) { - VisitedScope.insert(Scope); - if (Scope == DbgValScope) { - VisitedScope.clear(); - return true; - } - Scope = Scope->getScope(); - } else { - VisitedScope.clear(); - return false; - } - } - return false; -} - -bool DroppedVariableStats::isInlinedAtChildOfOrEqualTo( - const DILocation *InlinedAt, const DILocation *DbgValInlinedAt) { - if (DbgValInlinedAt == InlinedAt) - return true; - if (!DbgValInlinedAt) - return false; - if (!InlinedAt) - return false; - auto *IA = InlinedAt; - while (IA) { - if (IA == DbgValInlinedAt) - return true; - IA = IA->getInlinedAt(); - } - return false; -} - void StandardInstrumentations::registerCallbacks( PassInstrumentationCallbacks &PIC, ModuleAnalysisManager *MAM) { PrintIR.registerCallbacks(PIC); @@ -2712,7 +2538,7 @@ void StandardInstrumentations::registerCallbacks( WebsiteChangeReporter.registerCallbacks(PIC); ChangeTester.registerCallbacks(PIC); PrintCrashIR.registerCallbacks(PIC); - DroppedStats.registerCallbacks(PIC); + DroppedStatsIR.registerCallbacks(PIC); if (MAM) PreservedCFGChecker.registerCallbacks(PIC, *MAM); diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt index 963cdcc0275e1..807fd1a9b7b56 100644 --- a/llvm/unittests/CodeGen/CMakeLists.txt +++ b/llvm/unittests/CodeGen/CMakeLists.txt @@ -27,6 +27,7 @@ add_llvm_unittest(CodeGenTests CCStateTest.cpp DIEHashTest.cpp DIETest.cpp + DroppedVariableStatsIRTest.cpp DwarfStringPoolEntryRefTest.cpp InstrRefLDVTest.cpp LowLevelTypeTest.cpp diff --git a/llvm/unittests/IR/DroppedVariableStatsTest.cpp b/llvm/unittests/CodeGen/DroppedVariableStatsIRTest.cpp similarity index 91% rename from llvm/unittests/IR/DroppedVariableStatsTest.cpp rename to llvm/unittests/CodeGen/DroppedVariableStatsIRTest.cpp index 61f3a87bb355e..094ec7b657634 100644 --- a/llvm/unittests/IR/DroppedVariableStatsTest.cpp +++ b/llvm/unittests/CodeGen/DroppedVariableStatsIRTest.cpp @@ -1,5 +1,4 @@ -//===- unittests/IR/DroppedVariableStatsTest.cpp - TimePassesHandler tests -//----------===// +//===- unittests/IR/DroppedVariableStatsIRTest.cpp ------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -8,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/AsmParser/Parser.h" +#include "llvm/CodeGen/DroppedVariableStats.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" #include "llvm/IR/LegacyPassManager.h" @@ -44,7 +44,7 @@ namespace { // 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(DroppedVariableStats, BothDeleted) { +TEST(DroppedVariableStatsIR, BothDeleted) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -79,9 +79,8 @@ TEST(DroppedVariableStats, BothDeleted) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -92,16 +91,15 @@ TEST(DroppedVariableStats, BothDeleted) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); 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(DroppedVariableStats, DbgValLost) { +TEST(DroppedVariableStatsIR, DbgValLost) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -136,9 +134,8 @@ TEST(DroppedVariableStats, DbgValLost) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -148,16 +145,15 @@ TEST(DroppedVariableStats, DbgValLost) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); 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(DroppedVariableStats, UnrelatedScopes) { +TEST(DroppedVariableStatsIR, UnrelatedScopes) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -193,9 +189,8 @@ TEST(DroppedVariableStats, UnrelatedScopes) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -205,16 +200,15 @@ TEST(DroppedVariableStats, UnrelatedScopes) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); 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(DroppedVariableStats, ChildScopes) { +TEST(DroppedVariableStatsIR, ChildScopes) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -250,9 +244,8 @@ TEST(DroppedVariableStats, ChildScopes) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -262,9 +255,8 @@ TEST(DroppedVariableStats, ChildScopes) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); ASSERT_EQ(Stats.getPassDroppedVariables(), true); } @@ -272,7 +264,7 @@ TEST(DroppedVariableStats, ChildScopes) { // 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(DroppedVariableStats, InlinedAt) { +TEST(DroppedVariableStatsIR, InlinedAt) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -308,9 +300,8 @@ TEST(DroppedVariableStats, InlinedAt) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -320,9 +311,8 @@ TEST(DroppedVariableStats, InlinedAt) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); ASSERT_EQ(Stats.getPassDroppedVariables(), false); } @@ -330,7 +320,7 @@ TEST(DroppedVariableStats, InlinedAt) { // 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(DroppedVariableStats, InlinedAtShared) { +TEST(DroppedVariableStatsIR, InlinedAtShared) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -366,9 +356,8 @@ TEST(DroppedVariableStats, InlinedAtShared) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -378,9 +367,8 @@ TEST(DroppedVariableStats, InlinedAtShared) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); ASSERT_EQ(Stats.getPassDroppedVariables(), true); } @@ -388,7 +376,7 @@ TEST(DroppedVariableStats, InlinedAtShared) { // 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(DroppedVariableStats, InlinedAtChild) { +TEST(DroppedVariableStatsIR, InlinedAtChild) { PassInstrumentationCallbacks PIC; PassInstrumentation PI(&PIC); @@ -425,9 +413,8 @@ TEST(DroppedVariableStats, InlinedAtChild) { std::unique_ptr M = parseIR(C, IR); ASSERT_TRUE(M); - DroppedVariableStats Stats(true); - Stats.runBeforePass("Test", - llvm::Any(const_cast(M.get()))); + DroppedVariableStatsIR Stats(true); + Stats.runBeforePass(llvm::Any(const_cast(M.get()))); // This loop simulates an IR pass that drops debug information. for (auto &F : *M) { @@ -437,9 +424,8 @@ TEST(DroppedVariableStats, InlinedAtChild) { } break; } - PreservedAnalyses PA; Stats.runAfterPass("Test", - llvm::Any(const_cast(M.get())), PA); + llvm::Any(const_cast(M.get()))); ASSERT_EQ(Stats.getPassDroppedVariables(), true); } diff --git a/llvm/unittests/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt index ed93ee547d223..e5c8630f3eed7 100644 --- a/llvm/unittests/IR/CMakeLists.txt +++ b/llvm/unittests/IR/CMakeLists.txt @@ -43,7 +43,6 @@ add_llvm_unittest(IRTests ShuffleVectorInstTest.cpp StructuralHashTest.cpp TimePassesTest.cpp - DroppedVariableStatsTest.cpp TypesTest.cpp UseTest.cpp UserTest.cpp