[llvm] Add a pass to collect dropped var stats for MIR. (PR #115566)

Shubham Sandeep Rastogi via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 11 12:34:36 PST 2024


https://github.com/rastogishubham updated https://github.com/llvm/llvm-project/pull/115566

>From f7045b3447c1d4eee4234a46ac69fffca0367734 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Mon, 11 Nov 2024 11:53:00 -0800
Subject: [PATCH 1/3] [NFC] Move DroppedVariableStats to its own file

---
 .../llvm/CodeGen/DroppedVariableStats.h       | 111 ++++++++++
 .../llvm/Passes/StandardInstrumentations.h    |  78 +------
 llvm/lib/CodeGen/CMakeLists.txt               |   1 +
 llvm/lib/CodeGen/DroppedVariableStats.cpp     | 195 ++++++++++++++++++
 llvm/lib/Passes/StandardInstrumentations.cpp  | 176 ----------------
 5 files changed, 308 insertions(+), 253 deletions(-)
 create mode 100644 llvm/include/llvm/CodeGen/DroppedVariableStats.h
 create mode 100644 llvm/lib/CodeGen/DroppedVariableStats.cpp

diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStats.h b/llvm/include/llvm/CodeGen/DroppedVariableStats.h
new file mode 100644
index 00000000000000..4ce432cb81c13a
--- /dev/null
+++ b/llvm/include/llvm/CodeGen/DroppedVariableStats.h
@@ -0,0 +1,111 @@
+///===- DroppedVariableStats.h - Opt Diagnostics -*- C++ -*----------------===//
+///
+/// Part of the LLVM Project, under the Apache License v2.0 with LLVM
+/// Exceptions. See https://llvm.org/LICENSE.txt for license information.
+/// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+///
+///===---------------------------------------------------------------------===//
+/// \file
+/// Dropped Variable Statistics for Debug Information. Reports any number
+/// of #dbg_value that get dropped due to an optimization pass.
+///
+///===---------------------------------------------------------------------===//
+
+#ifndef LLVM_CODEGEN_DROPPEDVARIABLESTATS_H
+#define LLVM_CODEGEN_DROPPEDVARIABLESTATS_H
+
+#include "llvm/CodeGen/MachinePassManager.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/DiagnosticInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassInstrumentation.h"
+
+namespace llvm {
+
+/// A 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)
+      : DroppedVariableStatsEnabled(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;
+  bool DroppedVariableStatsEnabled = false;
+  /// A unique key that represents a #dbg_value.
+  using VarID =
+      std::tuple<const DIScope *, const DIScope *, const DILocalVariable *>;
+
+  struct DebugVariables {
+    /// DenseSet of VarIDs before an optimization pass has run.
+    DenseSet<VarID> DebugVariablesBefore;
+    /// DenseSet of VarIDs after an optimization pass has run.
+    DenseSet<VarID> 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<DenseMap<const Function *, DebugVariables>> DebugVariablesStack;
+
+  /// A DenseSet tracking whether a scope was visited before.
+  DenseSet<const DIScope *> 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<DenseMap<StringRef, DenseMap<VarID, DILocation *>>> 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);
+  template <typename IRUnitT> static const IRUnitT *unwrapIR(Any IR) {
+    const IRUnitT **IRPtr = llvm::any_cast<const IRUnitT *>(&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 9301a12c740eec..9a8d09c264f08d 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -19,6 +19,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSet.h"
+#include "llvm/CodeGen/DroppedVariableStats.h"
 #include "llvm/CodeGen/MachineBasicBlock.h"
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/DebugInfoMetadata.h"
@@ -579,83 +580,6 @@ class PrintCrashIRInstrumentation {
   static void SignalHandler(void *);
 };
 
-/// A class to collect and print dropped debug information variable statistics.
-/// After every LLVM IR pass is run, it will print how many #dbg_values were
-/// dropped due to that pass.
-class DroppedVariableStats {
-public:
-  DroppedVariableStats(bool DroppedVarStatsEnabled) {
-    if (DroppedVarStatsEnabled)
-      llvm::outs()
-          << "Pass Level, Pass Name, Num of Dropped Variables, Func or "
-             "Module Name\n";
-  };
-  // We intend this to be unique per-compilation, thus no copies.
-  DroppedVariableStats(const DroppedVariableStats &) = delete;
-  void operator=(const DroppedVariableStats &) = delete;
-
-  void registerCallbacks(PassInstrumentationCallbacks &PIC);
-  void runBeforePass(StringRef PassID, Any IR);
-  void runAfterPass(StringRef PassID, Any IR, const PreservedAnalyses &PA);
-  void runAfterPassInvalidated(StringRef PassID, const PreservedAnalyses &PA);
-  bool getPassDroppedVariables() { return PassDroppedVariables; }
-
-private:
-  bool PassDroppedVariables = false;
-  /// A unique key that represents a #dbg_value.
-  using VarID =
-      std::tuple<const DIScope *, const DIScope *, const DILocalVariable *>;
-
-  struct DebugVariables {
-    /// DenseSet of VarIDs before an optimization pass has run.
-    DenseSet<VarID> DebugVariablesBefore;
-    /// DenseSet of VarIDs after an optimization pass has run.
-    DenseSet<VarID> 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<DenseMap<const Function *, DebugVariables>> DebugVariablesStack;
-
-  /// A DenseSet tracking whether a scope was visited before.
-  DenseSet<const DIScope *> 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<DenseMap<StringRef, DenseMap<VarID, DILocation *>>> 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 {
diff --git a/llvm/lib/CodeGen/CMakeLists.txt b/llvm/lib/CodeGen/CMakeLists.txt
index 5a17944db0ae03..3eebeaaf04b3a2 100644
--- a/llvm/lib/CodeGen/CMakeLists.txt
+++ b/llvm/lib/CodeGen/CMakeLists.txt
@@ -50,6 +50,7 @@ add_llvm_component_library(LLVMCodeGen
   DeadMachineInstructionElim.cpp
   DetectDeadLanes.cpp
   DFAPacketizer.cpp
+  DroppedVariableStats.cpp
   DwarfEHPrepare.cpp
   EarlyIfConversion.cpp
   EdgeBundles.cpp
diff --git a/llvm/lib/CodeGen/DroppedVariableStats.cpp b/llvm/lib/CodeGen/DroppedVariableStats.cpp
new file mode 100644
index 00000000000000..b138161695d529
--- /dev/null
+++ b/llvm/lib/CodeGen/DroppedVariableStats.cpp
@@ -0,0 +1,195 @@
+///===- DroppedVariableStats.cpp - Opt Diagnostic -*- 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.
+///
+///===---------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/DroppedVariableStats.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/InstIterator.h"
+#include "llvm/IR/Module.h"
+
+using namespace llvm;
+
+void DroppedVariableStats::registerCallbacks(
+    PassInstrumentationCallbacks &PIC) {
+  if (!DroppedVariableStatsEnabled)
+    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<const Function *, DebugVariables>()});
+  InlinedAts.push_back({DenseMap<StringRef, DenseMap<VarID, DILocation *>>()});
+  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 &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<VarID, DILocation *>());
+  VarIDSet = DenseSet<VarID>();
+  for (const auto &I : instructions(F)) {
+    for (DbgRecord &DR : I.getDbgRecordRange()) {
+      if (auto *Dbg = dyn_cast<DbgVariableRecord>(&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<VarID> &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore;
+  DenseSet<VarID> &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter;
+  DenseMap<VarID, DILocation *> &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<Module>(IR)) {
+    this->runOnModule(M, false);
+    PassLevel = "Module";
+    FuncOrModName = M->getName();
+    calculateDroppedVarStatsOnModule(M, PassID, FuncOrModName, PassLevel);
+  } else if (auto *F = unwrapIR<Function>(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;
+}
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index d4866a025c1b48..b34fe9976c9493 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -2521,182 +2521,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<const Function *, DebugVariables>()});
-  InlinedAts.push_back({DenseMap<StringRef, DenseMap<VarID, DILocation *>>()});
-  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 &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<VarID, DILocation *>());
-  VarIDSet = DenseSet<VarID>();
-  for (const auto &I : instructions(F)) {
-    for (DbgRecord &DR : I.getDbgRecordRange()) {
-      if (auto *Dbg = dyn_cast<DbgVariableRecord>(&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<VarID> &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore;
-  DenseSet<VarID> &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter;
-  DenseMap<VarID, DILocation *> &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<Module>(IR)) {
-    this->runOnModule(M, false);
-    PassLevel = "Module";
-    FuncOrModName = M->getName();
-    calculateDroppedVarStatsOnModule(M, PassID, FuncOrModName, PassLevel);
-  } else if (auto *F = unwrapIR<Function>(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);

>From 5a99e4123847315b963e91cb4c68a6ab48413ab0 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Mon, 11 Nov 2024 12:07:49 -0800
Subject: [PATCH 2/3] Change DroppedVariableStats to be extensible.

Change DroppedVariableStats to have an extensible design so that we can
use it to add dropped statistics to MIR passes and the instruction
selector.
---
 .../llvm/CodeGen/DroppedVariableStats.h       | 118 ++++++++++----
 .../llvm/Passes/StandardInstrumentations.h    |   2 +-
 llvm/lib/CodeGen/DroppedVariableStats.cpp     | 153 +++++++-----------
 llvm/lib/Passes/StandardInstrumentations.cpp  |   4 +-
 llvm/unittests/IR/CMakeLists.txt              |   2 +-
 ...est.cpp => DroppedVariableStatsIRTest.cpp} |  72 ++++-----
 6 files changed, 173 insertions(+), 178 deletions(-)
 rename llvm/unittests/IR/{DroppedVariableStatsTest.cpp => DroppedVariableStatsIRTest.cpp} (91%)

diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStats.h b/llvm/include/llvm/CodeGen/DroppedVariableStats.h
index 4ce432cb81c13a..2494f9599c09ef 100644
--- a/llvm/include/llvm/CodeGen/DroppedVariableStats.h
+++ b/llvm/include/llvm/CodeGen/DroppedVariableStats.h
@@ -23,9 +23,12 @@
 
 namespace llvm {
 
-/// 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.
+/// A unique key that represents a #dbg_value.
+using VarID =
+    std::tuple<const DIScope *, const DIScope *, const DILocalVariable *>;
+
+/// A base class to collect and print dropped debug information variable
+/// statistics.
 class DroppedVariableStats {
 public:
   DroppedVariableStats(bool DroppedVarStatsEnabled)
@@ -39,18 +42,25 @@ class DroppedVariableStats {
   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:
+protected:
+  void setup() {
+    DebugVariablesStack.push_back(
+        {DenseMap<const Function *, DebugVariables>()});
+    InlinedAts.push_back(
+        {DenseMap<StringRef, DenseMap<VarID, DILocation *>>()});
+    return;
+  }
+
+  void cleanup() {
+    DebugVariablesStack.pop_back();
+    InlinedAts.pop_back();
+    return;
+  }
+
   bool PassDroppedVariables = false;
   bool DroppedVariableStatsEnabled = false;
-  /// A unique key that represents a #dbg_value.
-  using VarID =
-      std::tuple<const DIScope *, const DIScope *, const DILocalVariable *>;
 
   struct DebugVariables {
     /// DenseSet of VarIDs before an optimization pass has run.
@@ -70,13 +80,64 @@ class DroppedVariableStats {
   /// DenseMap of VarIDs and their inlinedAt locations before an optimization
   /// pass has run.
   SmallVector<DenseMap<StringRef, DenseMap<VarID, DILocation *>>> InlinedAts;
+  /// Remove a dropped #dbg_value 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(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);
+};
 
-  /// 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);
+/// 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<Module>(IR))
+      return this->runOnModule(M, true);
+    if (const auto *F = unwrapIR<Function>(IR))
+      return this->runOnFunction(F, true);
+  }
+
+  void runAfterPass(StringRef P, Any IR) {
+    if (const auto *M = unwrapIR<Module>(IR))
+      runAfterPassModule(P, M);
+    else if (const auto *F = unwrapIR<Function>(IR))
+      runAfterPassFunction(P, F);
+    return cleanup();
+  }
+
+  void registerCallbacks(PassInstrumentationCallbacks &PIC);
+
+private:
+  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,
@@ -84,22 +145,15 @@ class DroppedVariableStats {
                                           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);
+  /// 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);
+
   template <typename IRUnitT> static const IRUnitT *unwrapIR(Any IR) {
     const IRUnitT **IRPtr = llvm::any_cast<const IRUnitT *>(&IR);
     return IRPtr ? *IRPtr : nullptr;
@@ -108,4 +162,4 @@ class DroppedVariableStats {
 
 } // namespace llvm
 
-#endif
+#endif
\ No newline at end of file
diff --git a/llvm/include/llvm/Passes/StandardInstrumentations.h b/llvm/include/llvm/Passes/StandardInstrumentations.h
index 9a8d09c264f08d..12a34c099eaffe 100644
--- a/llvm/include/llvm/Passes/StandardInstrumentations.h
+++ b/llvm/include/llvm/Passes/StandardInstrumentations.h
@@ -597,7 +597,7 @@ class StandardInstrumentations {
   PrintCrashIRInstrumentation PrintCrashIR;
   IRChangedTester ChangeTester;
   VerifyInstrumentation Verify;
-  DroppedVariableStats DroppedStats;
+  DroppedVariableStatsIR DroppedStatsIR;
 
   bool VerifyEach;
 
diff --git a/llvm/lib/CodeGen/DroppedVariableStats.cpp b/llvm/lib/CodeGen/DroppedVariableStats.cpp
index b138161695d529..fca38b8ef8c66f 100644
--- a/llvm/lib/CodeGen/DroppedVariableStats.cpp
+++ b/llvm/lib/CodeGen/DroppedVariableStats.cpp
@@ -18,34 +18,42 @@
 
 using namespace llvm;
 
-void DroppedVariableStats::registerCallbacks(
-    PassInstrumentationCallbacks &PIC) {
-  if (!DroppedVariableStatsEnabled)
-    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);
-      });
+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;
 }
 
-void DroppedVariableStats::runBeforePass(StringRef PassID, Any IR) {
-  DebugVariablesStack.push_back({DenseMap<const Function *, DebugVariables>()});
-  InlinedAts.push_back({DenseMap<StringRef, DenseMap<VarID, DILocation *>>()});
-  if (auto *M = unwrapIR<Module>(IR))
-    return this->runOnModule(M, true);
-  if (auto *F = unwrapIR<Function>(IR))
-    return this->runOnFunction(F, true);
-  return;
+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 DroppedVariableStats::runOnFunction(const Function *F, bool Before) {
+void DroppedVariableStatsIR::runOnFunction(const Function *F, bool Before) {
   auto &DebugVariables = DebugVariablesStack.back()[F];
   auto &VarIDSet = (Before ? DebugVariables.DebugVariablesBefore
                            : DebugVariables.DebugVariablesAfter);
@@ -68,26 +76,7 @@ void DroppedVariableStats::runOnFunction(const Function *F, bool Before) {
   }
 }
 
-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(
+void DroppedVariableStatsIR::calculateDroppedVarStatsOnFunction(
     const Function *F, StringRef PassID, std::string FuncOrModName,
     std::string PassLevel) {
   unsigned DroppedCount = 0;
@@ -132,64 +121,30 @@ void DroppedVariableStats::calculateDroppedVarStatsOnFunction(
     PassDroppedVariables = false;
 }
 
-void DroppedVariableStats::runAfterPassInvalidated(
-    StringRef PassID, const PreservedAnalyses &PA) {
-  DebugVariablesStack.pop_back();
-  InlinedAts.pop_back();
+void DroppedVariableStatsIR::runOnModule(const Module *M, bool Before) {
+  for (auto &F : *M)
+    runOnFunction(&F, Before);
 }
 
-void DroppedVariableStats::runAfterPass(StringRef PassID, Any IR,
-                                        const PreservedAnalyses &PA) {
-  std::string PassLevel;
-  std::string FuncOrModName;
-  if (auto *M = unwrapIR<Module>(IR)) {
-    this->runOnModule(M, false);
-    PassLevel = "Module";
-    FuncOrModName = M->getName();
-    calculateDroppedVarStatsOnModule(M, PassID, FuncOrModName, PassLevel);
-  } else if (auto *F = unwrapIR<Function>(IR)) {
-    this->runOnFunction(F, false);
-    PassLevel = "Function";
-    FuncOrModName = F->getName();
-    calculateDroppedVarStatsOnFunction(F, PassID, FuncOrModName, PassLevel);
+void DroppedVariableStatsIR::calculateDroppedVarStatsOnModule(
+    const Module *M, StringRef PassID, std::string FuncOrModName,
+    std::string PassLevel) {
+  for (auto &F : *M) {
+    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;
-}
+void DroppedVariableStatsIR::registerCallbacks(
+    PassInstrumentationCallbacks &PIC) {
+  if (!DroppedVariableStatsEnabled)
+    return;
 
-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;
-}
+  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(); });
+}
\ No newline at end of file
diff --git a/llvm/lib/Passes/StandardInstrumentations.cpp b/llvm/lib/Passes/StandardInstrumentations.cpp
index b34fe9976c9493..c79774ec03aef1 100644
--- a/llvm/lib/Passes/StandardInstrumentations.cpp
+++ b/llvm/lib/Passes/StandardInstrumentations.cpp
@@ -2460,7 +2460,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 =
@@ -2536,7 +2536,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 ed93ee547d2231..ffa989bec269e4 100644
--- a/llvm/unittests/IR/CMakeLists.txt
+++ b/llvm/unittests/IR/CMakeLists.txt
@@ -43,7 +43,7 @@ add_llvm_unittest(IRTests
   ShuffleVectorInstTest.cpp
   StructuralHashTest.cpp
   TimePassesTest.cpp
-  DroppedVariableStatsTest.cpp
+  DroppedVariableStatsIRTest.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 61f3a87bb355e0..651fe6471eeed2 100644
--- a/llvm/unittests/IR/DroppedVariableStatsTest.cpp
+++ b/llvm/unittests/IR/DroppedVariableStatsIRTest.cpp
@@ -1,4 +1,4 @@
-//===- unittests/IR/DroppedVariableStatsTest.cpp - TimePassesHandler tests
+//===- unittests/IR/DroppedVariableStatsIRTest.cpp - TimePassesHandler tests
 //----------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
@@ -44,7 +44,7 @@ namespace {
 // This test ensures that if a #dbg_value and an instruction that exists in the
 // same scope as that #dbg_value are both deleted as a result of an optimization
 // pass, debug information is considered not dropped.
-TEST(DroppedVariableStats, BothDeleted) {
+TEST(DroppedVariableStatsIR, BothDeleted) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -79,9 +79,8 @@ TEST(DroppedVariableStats, BothDeleted) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -92,16 +91,15 @@ TEST(DroppedVariableStats, BothDeleted) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), false);
 }
 
 // This test ensures that if a #dbg_value is dropped after an optimization pass,
 // but an instruction that shares the same scope as the #dbg_value still exists,
 // debug information is conisdered dropped.
-TEST(DroppedVariableStats, DbgValLost) {
+TEST(DroppedVariableStatsIR, DbgValLost) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -136,9 +134,8 @@ TEST(DroppedVariableStats, DbgValLost) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -148,16 +145,15 @@ TEST(DroppedVariableStats, DbgValLost) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), true);
 }
 
 // This test ensures that if a #dbg_value is dropped after an optimization pass,
 // but an instruction that has an unrelated scope as the #dbg_value still
 // exists, debug information is conisdered not dropped.
-TEST(DroppedVariableStats, UnrelatedScopes) {
+TEST(DroppedVariableStatsIR, UnrelatedScopes) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -193,9 +189,8 @@ TEST(DroppedVariableStats, UnrelatedScopes) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -205,16 +200,15 @@ TEST(DroppedVariableStats, UnrelatedScopes) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), false);
 }
 
 // This test ensures that if a #dbg_value is dropped after an optimization pass,
 // but an instruction that has a scope which is a child of the #dbg_value scope
 // still exists, debug information is conisdered dropped.
-TEST(DroppedVariableStats, ChildScopes) {
+TEST(DroppedVariableStatsIR, ChildScopes) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -250,9 +244,8 @@ TEST(DroppedVariableStats, ChildScopes) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -262,9 +255,8 @@ TEST(DroppedVariableStats, ChildScopes) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), true);
 }
 
@@ -272,7 +264,7 @@ TEST(DroppedVariableStats, ChildScopes) {
 // but an instruction that has a scope which is a child of the #dbg_value scope
 // still exists, and the #dbg_value is inlined at another location, debug
 // information is conisdered not dropped.
-TEST(DroppedVariableStats, InlinedAt) {
+TEST(DroppedVariableStatsIR, InlinedAt) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -308,9 +300,8 @@ TEST(DroppedVariableStats, InlinedAt) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -320,9 +311,8 @@ TEST(DroppedVariableStats, InlinedAt) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), false);
 }
 
@@ -330,7 +320,7 @@ TEST(DroppedVariableStats, InlinedAt) {
 // but an instruction that has a scope which is a child of the #dbg_value scope
 // still exists, and the #dbg_value and the instruction are inlined at another
 // location, debug information is conisdered dropped.
-TEST(DroppedVariableStats, InlinedAtShared) {
+TEST(DroppedVariableStatsIR, InlinedAtShared) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -366,9 +356,8 @@ TEST(DroppedVariableStats, InlinedAtShared) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -378,9 +367,8 @@ TEST(DroppedVariableStats, InlinedAtShared) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), true);
 }
 
@@ -388,7 +376,7 @@ TEST(DroppedVariableStats, InlinedAtShared) {
 // but an instruction that has a scope which is a child of the #dbg_value scope
 // still exists, and the instruction is inlined at a location that is the
 // #dbg_value's inlined at location, debug information is conisdered dropped.
-TEST(DroppedVariableStats, InlinedAtChild) {
+TEST(DroppedVariableStatsIR, InlinedAtChild) {
   PassInstrumentationCallbacks PIC;
   PassInstrumentation PI(&PIC);
 
@@ -425,9 +413,8 @@ TEST(DroppedVariableStats, InlinedAtChild) {
   std::unique_ptr<llvm::Module> M = parseIR(C, IR);
   ASSERT_TRUE(M);
 
-  DroppedVariableStats Stats(true);
-  Stats.runBeforePass("Test",
-                      llvm::Any(const_cast<const llvm::Module *>(M.get())));
+  DroppedVariableStatsIR Stats(true);
+  Stats.runBeforePass(llvm::Any(const_cast<const llvm::Module *>(M.get())));
 
   // This loop simulates an IR pass that drops debug information.
   for (auto &F : *M) {
@@ -437,9 +424,8 @@ TEST(DroppedVariableStats, InlinedAtChild) {
     }
     break;
   }
-  PreservedAnalyses PA;
   Stats.runAfterPass("Test",
-                     llvm::Any(const_cast<const llvm::Module *>(M.get())), PA);
+                     llvm::Any(const_cast<const llvm::Module *>(M.get())));
   ASSERT_EQ(Stats.getPassDroppedVariables(), true);
 }
 

>From 6fbe1c5a75528eb3468c9648e9e13c821bedda29 Mon Sep 17 00:00:00 2001
From: Shubham Sandeep Rastogi <srastogi22 at apple.com>
Date: Mon, 11 Nov 2024 12:30:51 -0800
Subject: [PATCH 3/3] Add a pass to collect dropped var stats for MIR.

This patch extends the DroppedVariableStats class to add dropped
variable statistics for MIR passes.
---
 .../llvm/CodeGen/DroppedVariableStats.h       |   36 +-
 .../llvm/CodeGen/MachineFunctionPass.h        |    2 +
 llvm/lib/CodeGen/DroppedVariableStats.cpp     |   74 +-
 llvm/lib/CodeGen/MachineFunctionPass.cpp      |   15 +-
 llvm/unittests/MIR/CMakeLists.txt             |    1 +
 .../MIR/DroppedVariableStatsMIRTest.cpp       | 1067 +++++++++++++++++
 6 files changed, 1192 insertions(+), 3 deletions(-)
 create mode 100644 llvm/unittests/MIR/DroppedVariableStatsMIRTest.cpp

diff --git a/llvm/include/llvm/CodeGen/DroppedVariableStats.h b/llvm/include/llvm/CodeGen/DroppedVariableStats.h
index 2494f9599c09ef..d99c88aa55d307 100644
--- a/llvm/include/llvm/CodeGen/DroppedVariableStats.h
+++ b/llvm/include/llvm/CodeGen/DroppedVariableStats.h
@@ -7,7 +7,7 @@
 ///===---------------------------------------------------------------------===//
 /// \file
 /// Dropped Variable Statistics for Debug Information. Reports any number
-/// of #dbg_value that get dropped due to an optimization pass.
+/// of #dbg_values or DBG_VALUEs that get dropped due to an optimization pass.
 ///
 ///===---------------------------------------------------------------------===//
 
@@ -160,6 +160,40 @@ class DroppedVariableStatsIR : public DroppedVariableStats {
   }
 };
 
+/// 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:
+  /// 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,
+                                                 std::string FuncOrModName);
+};
+
 } // namespace llvm
 
 #endif
\ No newline at end of file
diff --git a/llvm/include/llvm/CodeGen/MachineFunctionPass.h b/llvm/include/llvm/CodeGen/MachineFunctionPass.h
index caaf22c2139e31..d82b593497ffc3 100644
--- a/llvm/include/llvm/CodeGen/MachineFunctionPass.h
+++ b/llvm/include/llvm/CodeGen/MachineFunctionPass.h
@@ -18,6 +18,7 @@
 #ifndef LLVM_CODEGEN_MACHINEFUNCTIONPASS_H
 #define LLVM_CODEGEN_MACHINEFUNCTIONPASS_H
 
+#include "llvm/CodeGen/DroppedVariableStats.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/Pass.h"
 
@@ -67,6 +68,7 @@ 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/DroppedVariableStats.cpp b/llvm/lib/CodeGen/DroppedVariableStats.cpp
index fca38b8ef8c66f..b8130784d28874 100644
--- a/llvm/lib/CodeGen/DroppedVariableStats.cpp
+++ b/llvm/lib/CodeGen/DroppedVariableStats.cpp
@@ -7,7 +7,7 @@
 ///===---------------------------------------------------------------------===//
 /// \file
 /// Dropped Variable Statistics for Debug Information. Reports any number
-/// of #dbg_value that get dropped due to an optimization pass.
+/// of #dbg_values or DBG_VALUEs that get dropped due to an optimization pass.
 ///
 ///===---------------------------------------------------------------------===//
 
@@ -147,4 +147,76 @@ void DroppedVariableStatsIR::registerCallbacks(
       });
   PIC.registerAfterPassInvalidatedCallback(
       [this](StringRef P, const PreservedAnalyses &PA) { return cleanup(); });
+}
+
+void DroppedVariableStatsMIR::runOnMachineFunction(const MachineFunction *MF,
+                                                   bool Before) {
+  auto &DebugVariables = DebugVariablesStack.back()[&MF->getFunction()];
+  auto &VarIDSet = (Before ? DebugVariables.DebugVariablesBefore
+                           : DebugVariables.DebugVariablesAfter);
+  auto &InlinedAtsMap = InlinedAts.back();
+  auto FuncName = MF->getName();
+  if (Before)
+    InlinedAtsMap.try_emplace(FuncName, DenseMap<VarID, DILocation *>());
+  VarIDSet = DenseSet<VarID>();
+  for (const auto &MBB : *MF) {
+    for (const auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        auto *DbgVar = MI.getDebugVariable();
+        if (!DbgVar)
+          continue;
+        auto DbgLoc = MI.getDebugLoc();
+        VarID Key{DbgVar->getScope(), DbgLoc->getInlinedAtScope(), DbgVar};
+        VarIDSet.insert(Key);
+        if (Before)
+          InlinedAtsMap[FuncName].try_emplace(Key, DbgLoc.getInlinedAt());
+      }
+    }
+  }
+}
+
+void DroppedVariableStatsMIR::calculateDroppedVarStatsOnMachineFunction(
+    const MachineFunction *MF, StringRef PassID, std::string FuncOrModName) {
+  unsigned DroppedCount = 0;
+  StringRef FuncName = MF->getName();
+  DebugVariables &DbgVariables = DebugVariablesStack.back()[&MF->getFunction()];
+  DenseSet<VarID> &DebugVariablesBeforeSet = DbgVariables.DebugVariablesBefore;
+  DenseSet<VarID> &DebugVariablesAfterSet = DbgVariables.DebugVariablesAfter;
+  DenseMap<VarID, DILocation *> &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 &MBB : *MF) {
+      for (const auto &MI : MBB) {
+        auto *DbgLoc = MI.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, &MF->getFunction());
+  }
+  if (DroppedCount > 0) {
+    llvm::outs() << "MachineFunction" << ", " << PassID << ", " << DroppedCount
+                 << ", " << FuncOrModName << "\n";
+    PassDroppedVariables = true;
+  } else
+    PassDroppedVariables = false;
 }
\ No newline at end of file
diff --git a/llvm/lib/CodeGen/MachineFunctionPass.cpp b/llvm/lib/CodeGen/MachineFunctionPass.cpp
index 62ac3e32d24d9d..e803811643f874 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<bool> 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/MIR/CMakeLists.txt b/llvm/unittests/MIR/CMakeLists.txt
index 206094266ba148..58d5a5df4819b6 100644
--- a/llvm/unittests/MIR/CMakeLists.txt
+++ b/llvm/unittests/MIR/CMakeLists.txt
@@ -16,6 +16,7 @@ set(LLVM_LINK_COMPONENTS
 add_llvm_unittest(MIRTests
   MachineMetadata.cpp
   MachineStableHashTest.cpp
+  DroppedVariableStatsMIRTest.cpp
   )
 
 target_link_libraries(MIRTests PRIVATE LLVMTestingSupport)
diff --git a/llvm/unittests/MIR/DroppedVariableStatsMIRTest.cpp b/llvm/unittests/MIR/DroppedVariableStatsMIRTest.cpp
new file mode 100644
index 00000000000000..ab8dfcc937bba0
--- /dev/null
+++ b/llvm/unittests/MIR/DroppedVariableStatsMIRTest.cpp
@@ -0,0 +1,1067 @@
+//===- unittests/IR/DroppedVariableStatsTest.cpp - TimePassesHandler tests
+//----------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Pass.h"
+#include "llvm/Passes/StandardInstrumentations.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Target/TargetMachine.h"
+#include "gtest/gtest.h"
+#include <gtest/gtest.h>
+#include <llvm/ADT/SmallString.h>
+#include <llvm/IR/LLVMContext.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/PassInstrumentation.h>
+#include <llvm/IR/PassManager.h>
+#include <llvm/IR/PassTimingInfo.h>
+#include <llvm/Support/raw_ostream.h>
+
+using namespace llvm;
+
+namespace {
+
+std::unique_ptr<LLVMTargetMachine>
+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<LLVMTargetMachine>(
+      static_cast<LLVMTargetMachine *>(T->createTargetMachine(
+          TT, CPU, FS, Options, std::nullopt, std::nullopt)));
+}
+
+std::unique_ptr<Module> parseMIR(const TargetMachine &TM, StringRef MIRCode,
+                                 MachineModuleInfo &MMI, LLVMContext *Context) {
+  SMDiagnostic Diagnostic;
+  std::unique_ptr<Module> M;
+  std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);
+  auto MIR = createMIRParser(std::move(MBuffer), *Context);
+  if (!MIR)
+    return nullptr;
+
+  std::unique_ptr<Module> Mod = MIR->parseIRModule();
+  if (!Mod)
+    return nullptr;
+
+  Mod->setDataLayout(TM.createDataLayout());
+
+  if (MIR->parseMachineFunctions(*Mod, MMI)) {
+    M.reset();
+    return nullptr;
+  }
+  return Mod;
+}
+// This test ensures that if a DBG_VALUE and an instruction that exists in the
+// same scope as that DBG_VALUE are both deleted as a result of an optimization
+// pass, debug information is considered not dropped.
+TEST(DroppedVariableStatsMIR, BothDeleted) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4)
+  !12 = !DILocation(line: 2, column: 11, scope: !4)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C);
+  ASSERT_TRUE(M);
+
+  DroppedVariableStatsMIR Stats;
+  auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi"));
+  Stats.runBeforePass("Test", MF);
+
+  // This loop simulates an IR pass that drops debug information.
+  for (auto &MBB : *MF) {
+    for (auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    for (auto &MI : MBB) {
+      auto *DbgLoc = MI.getDebugLoc().get();
+      if (DbgLoc) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    break;
+  }
+
+  Stats.runAfterPass("Test", MF);
+  ASSERT_EQ(Stats.getPassDroppedVariables(), false);
+}
+
+// This test ensures that if a DBG_VALUE is dropped after an optimization pass,
+// but an instruction that shares the same scope as the DBG_VALUE still exists,
+// debug information is conisdered dropped.
+TEST(DroppedVariableStatsMIR, DbgValLost) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4)
+  !12 = !DILocation(line: 2, column: 11, scope: !4)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C);
+  ASSERT_TRUE(M);
+
+  DroppedVariableStatsMIR Stats;
+  auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi"));
+  Stats.runBeforePass("Test", MF);
+
+  // This loop simulates an IR pass that drops debug information.
+  for (auto &MBB : *MF) {
+    for (auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    break;
+  }
+
+  Stats.runAfterPass("Test", MF);
+  ASSERT_EQ(Stats.getPassDroppedVariables(), true);
+}
+
+// This test ensures that if a #dbg_value is dropped after an optimization pass,
+// but an instruction that has an unrelated scope as the #dbg_value still
+// exists, debug information is conisdered not dropped.
+TEST(DroppedVariableStatsMIR, UnrelatedScopes) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4)
+  !12 = !DILocation(line: 2, column: 11, scope: !13)
+  !13 = distinct !DISubprogram(name: "bar", linkageName: "_Z3bari", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C);
+  ASSERT_TRUE(M);
+
+  DroppedVariableStatsMIR Stats;
+  auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi"));
+  Stats.runBeforePass("Test", MF);
+
+  // This loop simulates an IR pass that drops debug information.
+  for (auto &MBB : *MF) {
+    for (auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    break;
+  }
+
+  Stats.runAfterPass("Test", MF);
+  ASSERT_EQ(Stats.getPassDroppedVariables(), false);
+}
+
+// This test ensures that if a #dbg_value is dropped after an optimization pass,
+// but an instruction that has a scope which is a child of the #dbg_value scope
+// still exists, debug information is conisdered dropped.
+TEST(DroppedVariableStatsMIR, ChildScopes) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4)
+  !12 = !DILocation(line: 2, column: 11, scope: !13)
+  !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C);
+  ASSERT_TRUE(M);
+
+  DroppedVariableStatsMIR Stats;
+  auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi"));
+  Stats.runBeforePass("Test", MF);
+
+  // This loop simulates an IR pass that drops debug information.
+  for (auto &MBB : *MF) {
+    for (auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    break;
+  }
+
+  Stats.runAfterPass("Test", MF);
+  ASSERT_EQ(Stats.getPassDroppedVariables(), true);
+}
+
+// This test ensures that if a DBG_VALUE is dropped after an optimization pass,
+// but an instruction that has a scope which is a child of the DBG_VALUE scope
+// still exists, and the DBG_VALUE is inlined at another location, debug
+// information is conisdered not dropped.
+TEST(DroppedVariableStatsMIR, InlinedAt) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14)
+  !12 = !DILocation(line: 2, column: 11, scope: !13)
+  !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28)
+  !14 = !DILocation(line: 3, column: 2, scope: !4)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C);
+  ASSERT_TRUE(M);
+
+  DroppedVariableStatsMIR Stats;
+  auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi"));
+  Stats.runBeforePass("Test", MF);
+
+  // This loop simulates an IR pass that drops debug information.
+  for (auto &MBB : *MF) {
+    for (auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    break;
+  }
+
+  Stats.runAfterPass("Test", MF);
+  ASSERT_EQ(Stats.getPassDroppedVariables(), false);
+}
+
+// This test ensures that if a DBG_VALUE is dropped after an optimization pass,
+// but an instruction that has a scope which is a child of the DBG_VALUE scope
+// still exists, and the DBG_VALUE and the instruction are inlined at another
+// location, debug information is conisdered dropped.
+TEST(DroppedVariableStatsMIR, InlinedAtShared) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14)
+  !12 = !DILocation(line: 2, column: 11, scope: !13, inlinedAt: !14)
+  !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28)
+  !14 = !DILocation(line: 3, column: 2, scope: !4)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> M = parseMIR(*TM, MIR, MMI, &C);
+  ASSERT_TRUE(M);
+
+  DroppedVariableStatsMIR Stats;
+  auto *MF = MMI.getMachineFunction(*M->getFunction("_Z3fooi"));
+  Stats.runBeforePass("Test", MF);
+
+  // This loop simulates an IR pass that drops debug information.
+  for (auto &MBB : *MF) {
+    for (auto &MI : MBB) {
+      if (MI.isDebugValueLike()) {
+        MI.eraseFromParent();
+        break;
+      }
+    }
+    break;
+  }
+
+  Stats.runAfterPass("Test", MF);
+  ASSERT_EQ(Stats.getPassDroppedVariables(), true);
+}
+
+// This test ensures that if a DBG_VALUE is dropped after an optimization pass,
+// but an instruction that has a scope which is a child of the DBG_VALUE scope
+// still exists, and the instruction is inlined at a location that is the
+// DBG_VALUE's inlined at location, debug information is conisdered dropped.
+TEST(DroppedVariableStatsMIR, InlinedAtChild) {
+  InitializeAllTargetInfos();
+  InitializeAllTargets();
+  InitializeAllTargetMCs();
+  PassInstrumentationCallbacks PIC;
+  PassInstrumentation PI(&PIC);
+
+  LLVMContext C;
+
+  const char *MIR =
+      R"(
+--- |
+  ; ModuleID = '/tmp/test.ll'
+  source_filename = "/tmp/test.ll"
+  target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-n32:64-S128-Fn32"
+  
+  define noundef range(i32 -2147483647, -2147483648) i32 @_Z3fooi(i32 noundef %x) local_unnamed_addr !dbg !4 {
+  entry:
+      #dbg_value(i32 %x, !10, !DIExpression(), !11)
+    %add = add nsw i32 %x, 1, !dbg !12
+    ret i32 0
+  }
+  
+  !llvm.dbg.cu = !{!0}
+  !llvm.module.flags = !{!2}
+  !llvm.ident = !{!3}
+  
+  !0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
+  !1 = !DIFile(filename: "/tmp/code.cpp", directory: "/")
+  !2 = !{i32 2, !"Debug Info Version", i32 3}
+  !3 = !{!"clang"}
+  !4 = distinct !DISubprogram(name: "foo", linkageName: "_Z3fooi", scope: !5, file: !5, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !9)
+  !5 = !DIFile(filename: "/tmp/code.cpp", directory: "")
+  !6 = !DISubroutineType(types: !7)
+  !7 = !{!8, !8}
+  !8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+  !9 = !{!10}
+  !10 = !DILocalVariable(name: "x", arg: 1, scope: !4, file: !5, line: 1, type: !8)
+  !11 = !DILocation(line: 0, scope: !4, inlinedAt: !14)
+  !12 = !DILocation(line: 2, column: 11, scope: !13, inlinedAt: !15)
+  !13 = distinct !DILexicalBlock(scope: !4, file: !5, line: 10, column: 28)
+  !14 = !DILocation(line: 3, column: 2, scope: !4)
+  !15 = !DILocation(line: 4, column: 5, scope: !13, inlinedAt: !14)
+
+...
+---
+name:            _Z3fooi
+alignment:       4
+exposesReturnsTwice: false
+legalized:       false
+regBankSelected: false
+selected:        false
+failedISel:      false
+tracksRegLiveness: true
+hasWinCFI:       false
+noPhis:          false
+isSSA:           true
+noVRegs:         false
+hasFakeUses:     false
+callsEHReturn:   false
+callsUnwindInit: false
+hasEHCatchret:   false
+hasEHScopes:     false
+hasEHFunclets:   false
+isOutlined:      false
+debugInstrRef:   false
+failsVerification: false
+tracksDebugUserValues: false
+registers:
+  - { id: 0, class: _, preferred-register: '', flags: [  ] }
+  - { id: 1, class: _, preferred-register: '', flags: [  ] }
+  - { id: 2, class: _, preferred-register: '', flags: [  ] }
+  - { id: 3, class: _, preferred-register: '', flags: [  ] }
+liveins:
+  - { reg: '$w0', virtual-reg: '' }
+frameInfo:
+  isFrameAddressTaken: false
+  isReturnAddressTaken: false
+  hasStackMap:     false
+  hasPatchPoint:   false
+  stackSize:       0
+  offsetAdjustment: 0
+  maxAlignment:    1
+  adjustsStack:    false
+  hasCalls:        false
+  stackProtector:  ''
+  functionContext: ''
+  maxCallFrameSize: 4294967295
+  cvBytesOfCalleeSavedRegisters: 0
+  hasOpaqueSPAdjustment: false
+  hasVAStart:      false
+  hasMustTailInVarArgFunc: false
+  hasTailCall:     false
+  isCalleeSavedInfoValid: false
+  localFrameSize:  0
+  savePoint:       ''
+  restorePoint:    ''
+fixedStack:      []
+stack:           []
+entry_values:    []
+callSites:       []
+debugValueSubstitutions: []
+constants:       []
+machineFunctionInfo: {}
+body:             |
+  bb.1.entry:
+    liveins: $w0
+  
+    %0:_(s32) = COPY $w0
+    %1:_(s32) = G_CONSTANT i32 1
+    %3:_(s32) = G_CONSTANT i32 0
+    DBG_VALUE %0(s32), $noreg, !10, !DIExpression(), debug-location !11
+    %2:_(s32) = nsw G_ADD %0, %1, debug-location !12
+    $w0 = COPY %3(s32)
+    RET_ReallyLR implicit $w0
+    )";
+  auto TM = createTargetMachine(Triple::normalize("aarch64--"), "", "");
+  MachineModuleInfo MMI(TM.get());
+  std::unique_ptr<Module> 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



More information about the llvm-commits mailing list