[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