From 7398dc069e93b10db5b4771425b542fdb1161262 Mon Sep 17 00:00:00 2001 From: Shubham Sandeep Rastogi Date: Wed, 18 Dec 2024 08:58:14 -0800 Subject: [PATCH 1/3] [NFC] Move DroppedVariableStats to its own file (#120711) Move DroppedVariableStats code to its own file and change the class to have an extensible design so that we can use it to add dropped statistics to MIR passes and the instruction selector. Also moved class DroppedVariableStatsIR to its own file. Reland 2de78815604e9027efd93cac27c517bf732587d2 (cherry picked from commit 5717a99d8de458a0d74a8167c8d7aa751c1e4008) (cherry picked from commit 0b5b09b67c572867d88bbf5b41bcc5e722ec653a) --- .../llvm/Passes/DroppedVariableStats.h | 225 ++++++++++++++++++ .../llvm/Passes/DroppedVariableStatsIR.h | 101 ++++++++ .../llvm/Passes/StandardInstrumentations.h | 80 +------ llvm/lib/Passes/CMakeLists.txt | 1 + llvm/lib/Passes/DroppedVariableStatsIR.cpp | 91 +++++++ llvm/lib/Passes/StandardInstrumentations.cpp | 180 +------------- llvm/unittests/IR/CMakeLists.txt | 2 +- ...est.cpp => DroppedVariableStatsIRTest.cpp} | 73 +++--- 8 files changed, 452 insertions(+), 301 deletions(-) create mode 100644 llvm/include/llvm/Passes/DroppedVariableStats.h create mode 100644 llvm/include/llvm/Passes/DroppedVariableStatsIR.h create mode 100644 llvm/lib/Passes/DroppedVariableStatsIR.cpp rename llvm/unittests/IR/{DroppedVariableStatsTest.cpp => DroppedVariableStatsIRTest.cpp} (91%) diff --git a/llvm/include/llvm/Passes/DroppedVariableStats.h b/llvm/include/llvm/Passes/DroppedVariableStats.h new file mode 100644 index 000000000000000..c4de849ca7554c2 --- /dev/null +++ b/llvm/include/llvm/Passes/DroppedVariableStats.h @@ -0,0 +1,225 @@ +///===- 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/IR/DebugInfoMetadata.h" +#include "llvm/IR/DiagnosticInfo.h" +#include "llvm/IR/Function.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() {} + + // 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) { + 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; + } + + /// 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) { + // 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; + } + /// Run code to populate relevant data structures over an llvm::Function or + /// llvm::MachineFunction. + void 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); + } + /// 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) { + VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar}; + VarIDSet.insert(Key); + if (Before) + InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt()); + } + /// 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) { + 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; + } + /// 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) { + if (DbgValInlinedAt == InlinedAt) + return true; + if (!DbgValInlinedAt) + return false; + auto *IA = InlinedAt; + while (IA) { + if (IA == DbgValInlinedAt) + return true; + IA = IA->getInlinedAt(); + } + return false; + } + bool PassDroppedVariables = false; +}; + +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Passes/DroppedVariableStatsIR.h b/llvm/include/llvm/Passes/DroppedVariableStatsIR.h new file mode 100644 index 000000000000000..99701e8c8e1c05e --- /dev/null +++ b/llvm/include/llvm/Passes/DroppedVariableStatsIR.h @@ -0,0 +1,101 @@ +///===- DroppedVariableStatsIR.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_DROPPEDVARIABLESTATSIR_H +#define LLVM_CODEGEN_DROPPEDVARIABLESTATSIR_H + +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/Module.h" +#include "llvm/Passes/DroppedVariableStats.h" + +namespace llvm { + +/// 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 c5f57bfd0bcabca..7c49f4556e35803 100644 --- a/llvm/include/llvm/Passes/StandardInstrumentations.h +++ b/llvm/include/llvm/Passes/StandardInstrumentations.h @@ -25,6 +25,7 @@ #include "llvm/IR/OptBisect.h" #include "llvm/IR/PassTimingInfo.h" #include "llvm/IR/ValueHandle.h" +#include "llvm/Passes/DroppedVariableStatsIR.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/TimeProfiler.h" #include "llvm/Transforms/IPO/SampleProfileProbe.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/Passes/CMakeLists.txt b/llvm/lib/Passes/CMakeLists.txt index 6425f4934b21034..23799ac4f98f7ad 100644 --- a/llvm/lib/Passes/CMakeLists.txt +++ b/llvm/lib/Passes/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_component_library(LLVMPasses CodeGenPassBuilder.cpp + DroppedVariableStatsIR.cpp OptimizationLevel.cpp PassBuilder.cpp PassBuilderBindings.cpp diff --git a/llvm/lib/Passes/DroppedVariableStatsIR.cpp b/llvm/lib/Passes/DroppedVariableStatsIR.cpp new file mode 100644 index 000000000000000..496a47e71182e92 --- /dev/null +++ b/llvm/lib/Passes/DroppedVariableStatsIR.cpp @@ -0,0 +1,91 @@ +///===- DroppedVariableStatsIR.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/Passes/DroppedVariableStatsIR.h" + +using namespace llvm; + +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 f0ce0b0d70ed835..9fc479f81b96d06 100644 --- a/llvm/lib/Passes/StandardInstrumentations.cpp +++ b/llvm/lib/Passes/StandardInstrumentations.cpp @@ -2458,7 +2458,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 = @@ -2519,182 +2519,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); - return; -} - -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(); - return; -} - -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); @@ -2710,7 +2534,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/IR/CMakeLists.txt b/llvm/unittests/IR/CMakeLists.txt index 513fc0e66a7003b..cf36e46ca2b80d5 100644 --- a/llvm/unittests/IR/CMakeLists.txt +++ b/llvm/unittests/IR/CMakeLists.txt @@ -25,6 +25,7 @@ add_llvm_unittest(IRTests DemandedBitsTest.cpp DominatorTreeTest.cpp DominatorTreeBatchUpdatesTest.cpp + DroppedVariableStatsIRTest.cpp FunctionTest.cpp PassBuilderCallbacksTest.cpp IRBuilderTest.cpp @@ -42,7 +43,6 @@ add_llvm_unittest(IRTests ShuffleVectorInstTest.cpp StructuralHashTest.cpp TimePassesTest.cpp - DroppedVariableStatsTest.cpp TypesTest.cpp UseTest.cpp UserTest.cpp diff --git a/llvm/unittests/IR/DroppedVariableStatsTest.cpp b/llvm/unittests/IR/DroppedVariableStatsIRTest.cpp similarity index 91% rename from llvm/unittests/IR/DroppedVariableStatsTest.cpp rename to llvm/unittests/IR/DroppedVariableStatsIRTest.cpp index 63b7b4de1937cb5..5f289193081498e 100644 --- a/llvm/unittests/IR/DroppedVariableStatsTest.cpp +++ b/llvm/unittests/IR/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. @@ -44,7 +43,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 +78,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.get()) { @@ -92,16 +90,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 +133,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.get()) { @@ -148,16 +144,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 +188,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.get()) { @@ -205,16 +199,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 +243,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.get()) { @@ -262,9 +254,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 +263,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 +299,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.get()) { @@ -320,9 +310,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 +319,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 +355,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.get()) { @@ -378,9 +366,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 +375,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 +412,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.get()) { @@ -437,9 +423,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); } From 7323c4ef30da6dfc71b0e9c6dbf2ab8f69f48ef8 Mon Sep 17 00:00:00 2001 From: Shubham Sandeep Rastogi Date: Fri, 20 Dec 2024 10:08:54 -0800 Subject: [PATCH 2/3] Add a pass to collect dropped var stats for MIR (#120780) This patch uses the DroppedVariableStats class to add dropped variable statistics for MIR passes. Reland 1c082c9cd12efaa67a32c5da89a328c458ed51c5 (cherry picked from commit 3bf91ad2a9c75dd045961e45fdd830fd7b7a5455) --- .../llvm/CodeGen/DroppedVariableStatsMIR.h | 70 ++ .../llvm/CodeGen/MachineFunctionPass.h | 2 + llvm/lib/CodeGen/CMakeLists.txt | 1 + llvm/lib/CodeGen/DroppedVariableStatsMIR.cpp | 77 ++ llvm/lib/CodeGen/MachineFunctionPass.cpp | 15 +- llvm/unittests/CodeGen/CMakeLists.txt | 1 + .../CodeGen/DroppedVariableStatsMIRTest.cpp | 1052 +++++++++++++++++ 7 files changed, 1217 insertions(+), 1 deletion(-) create mode 100644 llvm/include/llvm/CodeGen/DroppedVariableStatsMIR.h create mode 100644 llvm/lib/CodeGen/DroppedVariableStatsMIR.cpp create mode 100644 llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStatsMIR.h b/llvm/include/llvm/CodeGen/DroppedVariableStatsMIR.h new file mode 100644 index 000000000000000..462bbb8b404bcfb --- /dev/null +++ b/llvm/include/llvm/CodeGen/DroppedVariableStatsMIR.h @@ -0,0 +1,70 @@ +///===- DroppedVariableStatsMIR.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_VALUEs that get dropped due to an optimization pass. +/// +///===---------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_DROPPEDVARIABLESTATSMIR_H +#define LLVM_CODEGEN_DROPPEDVARIABLESTATSMIR_H + +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/Passes/DroppedVariableStats.h" + +namespace llvm { + +/// 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 caaf22c2139e31a..8d7e4192003d22a 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/DroppedVariableStatsMIR.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/Pass.h" @@ -67,6 +68,7 @@ class MachineFunctionPass : public FunctionPass { 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/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt index 213407efaf7a3e5..2ea213b57f15c9f 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 + DroppedVariableStatsMIR.cpp DwarfEHPrepare.cpp EarlyIfConversion.cpp EdgeBundles.cpp diff --git a/llvm/lib/CodeGen/DroppedVariableStatsMIR.cpp b/llvm/lib/CodeGen/DroppedVariableStatsMIR.cpp new file mode 100644 index 000000000000000..4379db8b1667913 --- /dev/null +++ b/llvm/lib/CodeGen/DroppedVariableStatsMIR.cpp @@ -0,0 +1,77 @@ +///===- DroppedVariableStatsMIR.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_VALUEs that get dropped due to an optimization pass. +/// +///===---------------------------------------------------------------------===// + +#include "llvm/CodeGen/DroppedVariableStatsMIR.h" + +using namespace llvm; + +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); + } + } + } +} \ No newline at end of file diff --git a/llvm/lib/CodeGen/MachineFunctionPass.cpp b/llvm/lib/CodeGen/MachineFunctionPass.cpp index 62ac3e32d24d9d1..e803811643f874a 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 963cdcc0275e162..4f580e7539f4d98 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 + 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 000000000000000..7fdd30390a44483 --- /dev/null +++ b/llvm/unittests/CodeGen/DroppedVariableStatsMIRTest.cpp @@ -0,0 +1,1052 @@ +//===- unittests/CodeGen/DroppedVariableStatsMIRTest.cpp ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/DroppedVariableStatsMIR.h" +#include "llvm/AsmParser/Parser.h" +#include "llvm/CodeGen/MIRParser/MIRParser.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Pass.h" +#include "llvm/Support/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 LLVMTargetMachine &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 +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + for (auto &MI : MBB) { + auto *DbgLoc = MI.getDebugLoc().get(); + if (DbgLoc) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), false); +} + +// This test ensures that if a DBG_VALUE is dropped after an optimization pass, +// but an instruction that shares the same scope as the DBG_VALUE still exists, +// debug information is conisdered dropped. +TEST(DroppedVariableStatsMIR, DbgValLost) { + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + + LLVMContext C; + + const char *MIR = + R"( +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + + define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { + entry: + #dbg_value(i32 %x, !10, !DIExpression(), !11) + %add = add nsw i32 %x, 1, !dbg !12 + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !llvm.ident = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") + !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = !{!"clang"} + !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") + !6 = !DISubroutineType(types: !7) + !7 = !{!8, !8} + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{!10} + !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) + !11 = !DILocation(line: 0, scope: !4) + !12 = !DILocation(line: 2, column: 11, scope: !4) + +... +--- +name: _Z3fooi +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); +} + +// This test ensures that if a #dbg_value is dropped after an optimization pass, +// but an instruction that has an unrelated scope as the #dbg_value still +// exists, debug information is conisdered not dropped. +TEST(DroppedVariableStatsMIR, UnrelatedScopes) { + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + + LLVMContext C; + + const char *MIR = + R"( +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + + define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { + entry: + #dbg_value(i32 %x, !10, !DIExpression(), !11) + %add = add nsw i32 %x, 1, !dbg !12 + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !llvm.ident = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") + !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = !{!"clang"} + !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") + !6 = !DISubroutineType(types: !7) + !7 = !{!8, !8} + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{!10} + !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) + !11 = !DILocation(line: 0, scope: !4) + !12 = !DILocation(line: 2, column: 11, scope: !13) + !13 = distinct !DISubprogram(name: "bar", linkageName: "_Z3bari", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + +... +--- +name: _Z3fooi +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), false); +} + +// This test ensures that if a #dbg_value is dropped after an optimization pass, +// but an instruction that has a scope which is a child of the #dbg_value scope +// still exists, debug information is conisdered dropped. +TEST(DroppedVariableStatsMIR, ChildScopes) { + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + + LLVMContext C; + + const char *MIR = + R"( +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + + define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { + entry: + #dbg_value(i32 %x, !10, !DIExpression(), !11) + %add = add nsw i32 %x, 1, !dbg !12 + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !llvm.ident = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") + !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = !{!"clang"} + !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") + !6 = !DISubroutineType(types: !7) + !7 = !{!8, !8} + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{!10} + !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) + !11 = !DILocation(line: 0, scope: !4) + !12 = !DILocation(line: 2, column: 11, scope: !13) + !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) + +... +--- +name: _Z3fooi +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); +} + +// This test ensures that if a DBG_VALUE is dropped after an optimization pass, +// but an instruction that has a scope which is a child of the DBG_VALUE scope +// still exists, and the DBG_VALUE is inlined at another location, debug +// information is conisdered not dropped. +TEST(DroppedVariableStatsMIR, InlinedAt) { + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + + LLVMContext C; + + const char *MIR = + R"( +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + + define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { + entry: + #dbg_value(i32 %x, !10, !DIExpression(), !11) + %add = add nsw i32 %x, 1, !dbg !12 + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !llvm.ident = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") + !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = !{!"clang"} + !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") + !6 = !DISubroutineType(types: !7) + !7 = !{!8, !8} + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{!10} + !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) + !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14) + !12 = !DILocation(line: 2, column: 11, scope: !13) + !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) + !14 = !DILocation(line: 3, column: 2, scope: !4) + +... +--- +name: _Z3fooi +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), false); +} + +// This test ensures that if a DBG_VALUE is dropped after an optimization pass, +// but an instruction that has a scope which is a child of the DBG_VALUE scope +// still exists, and the DBG_VALUE and the instruction are inlined at another +// location, debug information is conisdered dropped. +TEST(DroppedVariableStatsMIR, InlinedAtShared) { + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + + LLVMContext C; + + const char *MIR = + R"( +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + + define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { + entry: + #dbg_value(i32 %x, !10, !DIExpression(), !11) + %add = add nsw i32 %x, 1, !dbg !12 + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !llvm.ident = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") + !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = !{!"clang"} + !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") + !6 = !DISubroutineType(types: !7) + !7 = !{!8, !8} + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{!10} + !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) + !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14) + !12 = !DILocation(line: 2, column: 11, scope: !13, inlinedAt: !14) + !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) + !14 = !DILocation(line: 3, column: 2, scope: !4) + +... +--- +name: _Z3fooi +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); +} + +// This test ensures that if a DBG_VALUE is dropped after an optimization pass, +// but an instruction that has a scope which is a child of the DBG_VALUE scope +// still exists, and the instruction is inlined at a location that is the +// DBG_VALUE's inlined at location, debug information is conisdered dropped. +TEST(DroppedVariableStatsMIR, InlinedAtChild) { + InitializeAllTargetInfos(); + InitializeAllTargets(); + InitializeAllTargetMCs(); + PassInstrumentationCallbacks PIC; + PassInstrumentation PI(&PIC); + + LLVMContext C; + + const char *MIR = + R"( +--- | + ; ModuleID = '/tmp/test.ll' + source_filename = "/tmp/test.ll" + target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32" + + define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 { + entry: + #dbg_value(i32 %x, !10, !DIExpression(), !11) + %add = add nsw i32 %x, 1, !dbg !12 + ret i32 0 + } + + !llvm.dbg.cu = !{!0} + !llvm.module.flags = !{!2} + !llvm.ident = !{!3} + + !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/") + !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/") + !2 = !{i32 2, !"Debug Info Version", i32 3} + !3 = !{!"clang"} + !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9) + !5 = !DIFile(filename: "/tmp/code.cpp", directory: "") + !6 = !DISubroutineType(types: !7) + !7 = !{!8, !8} + !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) + !9 = !{!10} + !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8) + !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14) + !12 = !DILocation(line: 2, column: 11, scope: !13, inlinedAt: !15) + !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28) + !14 = !DILocation(line: 3, column: 2, scope: !4) + !15 = !DILocation(line: 4, column: 5, scope: !13, inlinedAt: !14) + +... +--- +name: _Z3fooi +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +callsEHReturn: false +callsUnwindInit: false +hasEHCatchret: false +hasEHScopes: false +hasEHFunclets: false +isOutlined: false +debugInstrRef: false +failsVerification: false +tracksDebugUserValues: false +registers: + - { id: 0, class: _, preferred-register: ''} + - { id: 1, class: _, preferred-register: ''} + - { id: 2, class: _, preferred-register: ''} + - { id: 3, class: _, preferred-register: ''} +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 0 + offsetAdjustment: 0 + maxAlignment: 1 + adjustsStack: false + hasCalls: false + stackProtector: '' + functionContext: '' + maxCallFrameSize: 4294967295 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + isCalleeSavedInfoValid: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: [] +entry_values: [] +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: {} +body: | + bb.1.entry: + liveins: $w0 + + %0:_(s32) = COPY $w0 + %1:_(s32) = G_CONSTANT i32 1 + %3:_(s32) = G_CONSTANT i32 0 + DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11 + %2:_(s32) = nsw G_ADD %0, %1, debug-location !12 + $w0 = COPY %3(s32) + RET_ReallyLR implicit $w0 + )"; + auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", ""); + if (!TM) + return; + MachineModuleInfo MMI(TM.get()); + std::unique_ptr M = parseMIR(*TM, MIR, MMI, &C); + ASSERT_TRUE(M); + + DroppedVariableStatsMIR Stats; + auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi")); + Stats.runBeforePass("Test", MF); + + // This loop simulates an IR pass that drops debug information. + for (auto &MBB : *MF) { + for (auto &MI : MBB) { + if (MI.isDebugValueLike()) { + MI.eraseFromParent(); + break; + } + } + break; + } + + Stats.runAfterPass("Test", MF); + ASSERT_EQ(Stats.getPassDroppedVariables(), true); +} + +} // end anonymous namespace \ No newline at end of file From 84bd9377babb0ff80964cf51f57ba153d561c14f Mon Sep 17 00:00:00 2001 From: Shubham Sandeep Rastogi Date: Tue, 21 Jan 2025 19:08:08 -0800 Subject: [PATCH 3/3] [NFC] Add error checking for InlinedAts. The DroppedVariableStats::calculateDroppedStatsAndPrint should check if it's InlinedAts stack contains the the function name that is being accessed to make sure that a pass did not create a new function declaration which may then lead to a crash. For example, in hot-cold splitting, the Module before the pass will not contain the newly created cold function and can cause a crash when trying to access the InlinedAts stack with the function name of the newly created cold function. (cherry picked from commit e433fc3ce3155860e5f07c8bbc790b117a45e33f) --- llvm/include/llvm/Passes/DroppedVariableStats.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/include/llvm/Passes/DroppedVariableStats.h b/llvm/include/llvm/Passes/DroppedVariableStats.h index c4de849ca7554c2..e2e91891e24c19e 100644 --- a/llvm/include/llvm/Passes/DroppedVariableStats.h +++ b/llvm/include/llvm/Passes/DroppedVariableStats.h @@ -96,6 +96,8 @@ class DroppedVariableStats { DenseSet &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore; DenseSet &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter; + if (InlinedAts.back().find(FuncName) == InlinedAts.back().end()) + return; 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