[llvm] Add a pass to collect dropped variable statistics (PR #102233)
Shubham Sandeep Rastogi via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 31 00:10:13 PDT 2024
https://github.com/rastogishubham updated https://github.com/llvm/llvm-project/pull/102233
>From 52efbcc1faf4691de5de42cb6a20205b9137e2e9 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Mon, 5 Aug 2024 15:12:37 -0700
Subject: [PATCH] Add a pass to collect dropped variable statistics
Add an instrumentation pass to llvm to collect dropped debug information
variable statistics for every Function-level and Module-level IR pass.
---
.../llvm/Passes/StandardInstrumentations.h | 39 +++++
llvm/lib/Passes/StandardInstrumentations.cpp | 136 +++++++++++++++++-
llvm/test/Other/dropped-var-stats.ll | 26 ++++
3 files changed, 198 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Other/dropped-var-stats.ll
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index fa9c744294a666..f9e67668f56e68 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -559,6 +559,44 @@ class DotCfgChangeReporter : public ChangeReporter<IRDataT<DCData>> {
std::unique_ptr<raw_fd_ostream> HTML;
};
+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);
+
+private:
+ using VarID = std::tuple<const DILocalScope *, const DILocalScope *,
+ StringRef, unsigned>;
+
+ SmallVector<llvm::DenseSet<VarID>> DebugVariablesBefore;
+ SmallVector<llvm::DenseSet<VarID>> DebugVariablesAfter;
+
+ DenseMap<const DISubprogram *, const llvm::Function *>
+ SubprogramToFunctionMap;
+
+ // Implementation of pass instrumentation callbacks.
+ void runBeforePass(StringRef PassID, Any IR);
+ void runAfterPass(StringRef PassID, Any IR, const PreservedAnalyses &PA);
+
+ void runOnFunction(const Function *F, bool Before);
+ void runOnModule(const Module *M, bool Before);
+
+ void removeVarFromAllSets(VarID Var);
+
+ // Populates a DenseMap<const DISubprogram*, const Function*>
+ // SubprogramToFunctionMap every time it is called
+ void makeDISubprogramToFunctionMap(const llvm::Module *M);
+};
+
// Print IR on crash.
class PrintCrashIRInstrumentation {
public:
@@ -595,6 +633,7 @@ class StandardInstrumentations {
PrintCrashIRInstrumentation PrintCrashIR;
IRChangedTester ChangeTester;
VerifyInstrumentation Verify;
+ DroppedVariableStats DroppedStats;
bool VerifyEach;
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index 8f2461f40cb004..ad6c77441c279b 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -25,6 +25,8 @@
#include "llvm/Demangle/Demangle.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Function.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassInstrumentation.h"
#include "llvm/IR/PassManager.h"
@@ -139,6 +141,11 @@ static cl::opt<std::string> IRDumpDirectory(
"files in this directory rather than written to stderr"),
cl::Hidden, cl::value_desc("filename"));
+static cl::opt<bool>
+ DroppedVarStats("dropped-variable-stats", cl::Hidden,
+ cl::desc("Dump dropped debug variables stats"),
+ cl::init(false));
+
template <typename IRUnitT> static const IRUnitT *unwrapIR(Any IR) {
const IRUnitT **IRPtr = llvm::any_cast<const IRUnitT *>(&IR);
return IRPtr ? *IRPtr : nullptr;
@@ -2441,11 +2448,132 @@ void DotCfgChangeReporter::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);
+ });
+}
+
+void DroppedVariableStats::runBeforePass(StringRef PassID, Any IR) {
+ DebugVariablesBefore.push_back(llvm::DenseSet<VarID>());
+ DebugVariablesAfter.push_back(llvm::DenseSet<VarID>());
+ if (auto *M = unwrapIR<Module>(IR))
+ return this->runOnModule(M, true);
+ if (auto *F = unwrapIR<Function>(IR))
+ return this->runOnFunction(F, true);
+ return;
+}
+
+void DroppedVariableStats::runOnFunction(const Function *F, bool Before) {
+ auto &VarIDs = (Before ? DebugVariablesBefore : DebugVariablesAfter).back();
+ for (const auto &I : instructions(F)) {
+ for (DbgRecord &DR : I.getDbgRecordRange()) {
+ if (auto *Dbg = dyn_cast<DbgVariableRecord>(&DR)) {
+ auto *DbgVar = Dbg->getVariable();
+ StringRef UniqueName = DbgVar->getName();
+ auto DbgLoc = DR.getDebugLoc();
+ unsigned Line = DbgVar->getLine();
+ VarID Key{cast<DILocalScope>(DbgVar->getScope()),
+ DbgLoc->getInlinedAtScope(), UniqueName, Line};
+ VarIDs.insert(Key);
+ }
+ }
+ }
+}
+
+void DroppedVariableStats::runOnModule(const Module *M, bool Before) {
+ for (auto &F : *M)
+ runOnFunction(&F, Before);
+}
+
+void DroppedVariableStats::removeVarFromAllSets(VarID Var) {
+ // Do not remove Var from the last element, it will be popped from the stack
+ // anyway.
+ for (auto *It = DebugVariablesBefore.begin();
+ It != DebugVariablesBefore.end() - 1; It++)
+ It->erase(Var);
+}
+
+void DroppedVariableStats::makeDISubprogramToFunctionMap(
+ const llvm::Module *M) {
+ for (auto &F : *M) {
+ if (auto *SP = cast_or_null<const DISubprogram>(F.getSubprogram()))
+ SubprogramToFunctionMap[SP] = &F;
+ }
+}
+
+void DroppedVariableStats::runAfterPass(StringRef PassID, Any IR,
+ const PreservedAnalyses &PA) {
+ unsigned DroppedCount = 0;
+ std::string PassLevel;
+ std::string FuncOrModName;
+ if (auto *M = unwrapIR<Module>(IR)) {
+ this->runOnModule(M, false);
+ PassLevel = "Module";
+ FuncOrModName = M->getName();
+ makeDISubprogramToFunctionMap(M);
+ } else if (auto *F = unwrapIR<Function>(IR)) {
+ this->runOnFunction(F, false);
+ PassLevel = "Function";
+ FuncOrModName = F->getName();
+ makeDISubprogramToFunctionMap(F->getParent());
+ } else {
+ return;
+ }
+ for (auto Var : DebugVariablesBefore.back()) {
+ if (!DebugVariablesAfter.back().contains(Var)) {
+ // Ensure that a variable that doesn't exist in the DebugVariablesAfter
+ // map is actually dropped from the pass and not removed due to the
+ // Function being removed due to code elimination. Do this by checking if
+ // the InlinedAt for a debug variable is in the SubprogramToFunctionMap,
+ // only if it exists, find a real instruction in the function obtained by
+ // the SubprogramToFunctionMap that has the same scope as the debug
+ // variable, if such an instruction exists, the debug variable has been
+ // dropped.
+ // TODO: Improve the dropped variable statistics by not just checking if
+ // the scope of an Insturction matches the scope of the debug variable but
+ // if any child scope matches it too.
+ const DISubprogram *InlinedAt = nullptr;
+ if (auto *LS = dyn_cast<const DILexicalBlock>(std::get<1>(Var)))
+ InlinedAt = LS->getSubprogram();
+ else if (auto *DSP = dyn_cast<const DISubprogram>(std::get<1>(Var)))
+ InlinedAt = DSP;
+ if (InlinedAt) {
+ auto It = SubprogramToFunctionMap.find(InlinedAt);
+ if (It != SubprogramToFunctionMap.end() && !It->second->empty()) {
+ for (const auto &I : instructions(It->second)) {
+ auto *DbgLoc = I.getDebugLoc().get();
+ if (DbgLoc) {
+ auto *Scope = cast<DILocalScope>(DbgLoc->getScope());
+ if (Scope == It->first)
+ DroppedCount++;
+ }
+ }
+ }
+ }
+ removeVarFromAllSets(Var);
+ }
+ }
+ if (DroppedCount > 0)
+ llvm::outs() << PassLevel << ", " << PassID << ", " << DroppedCount << ", "
+ << FuncOrModName << "\n";
+ DebugVariablesBefore.pop_back();
+ DebugVariablesAfter.pop_back();
+ SubprogramToFunctionMap.clear();
+ return;
+}
+
StandardInstrumentations::StandardInstrumentations(
LLVMContext &Context, bool DebugLogging, bool VerifyEach,
PrintPassOptions PrintPassOpts)
- : PrintPass(DebugLogging, PrintPassOpts),
- OptNone(DebugLogging),
+ : PrintPass(DebugLogging, PrintPassOpts), OptNone(DebugLogging),
OptPassGate(Context),
PrintChangedIR(PrintChanged == ChangePrinter::Verbose),
PrintChangedDiff(PrintChanged == ChangePrinter::DiffVerbose ||
@@ -2453,7 +2581,8 @@ StandardInstrumentations::StandardInstrumentations(
PrintChanged == ChangePrinter::ColourDiffVerbose ||
PrintChanged == ChangePrinter::ColourDiffQuiet),
WebsiteChangeReporter(PrintChanged == ChangePrinter::DotCfgVerbose),
- Verify(DebugLogging), VerifyEach(VerifyEach) {}
+ Verify(DebugLogging), DroppedStats(DroppedVarStats),
+ VerifyEach(VerifyEach) {}
PrintCrashIRInstrumentation *PrintCrashIRInstrumentation::CrashReporter =
nullptr;
@@ -2528,6 +2657,7 @@ void StandardInstrumentations::registerCallbacks(
WebsiteChangeReporter.registerCallbacks(PIC);
ChangeTester.registerCallbacks(PIC);
PrintCrashIR.registerCallbacks(PIC);
+ DroppedStats.registerCallbacks(PIC);
if (MAM)
PreservedCFGChecker.registerCallbacks(PIC, *MAM);
diff --git a/llvm/test/Other/dropped-var-stats.ll b/llvm/test/Other/dropped-var-stats.ll
new file mode 100644
index 00000000000000..93e00f2c744907
--- /dev/null
+++ b/llvm/test/Other/dropped-var-stats.ll
@@ -0,0 +1,26 @@
+; RUN: opt -dropped-variable-stats %s -passes='adce' -S | FileCheck %s
+
+; CHECK: Pass Level, Pass Name, Num of Dropped Variables, Func or Module Name
+; CHECK-NEXT: Function, ADCEPass, 1, _Z3bari
+
+; ModuleID = '/tmp/dropped.cpp'
+define noundef range(i32 -2147483646, -2147483648) i32 @_Z3bari(i32 noundef %y) local_unnamed_addr #1 !dbg !19 {
+ #dbg_value(i32 %y, !15, !DIExpression(), !23)
+ %add = add nsw i32 %y, 2,!dbg !25
+ ret i32 %add,!dbg !26
+}
+!llvm.module.flags = !{ !3, !7}
+!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, sysroot: "/")
+!1 = !DIFile(filename: "/tmp/dropped.cpp", directory: "/Users/shubham/Development/llvm-project")
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+!7 = !{i32 7, !"frame-pointer", i32 1}
+!9 = distinct !DISubprogram( unit: !0, retainedNodes: !14)
+!13 = !DIBasicType()
+!14 = !{}
+!15 = !DILocalVariable( scope: !9, type: !13)
+!19 = distinct !DISubprogram( unit: !0, retainedNodes: !20)
+!20 = !{}
+!23 = !DILocation( scope: !9, inlinedAt: !24)
+!24 = distinct !DILocation( scope: !19)
+!25 = !DILocation( scope: !19)
+!26 = !DILocation( scope: !19)
More information about the llvm-commits
mailing list