[Debugify] Make the debugify aware of the original (-g) Debug Info
authorDjordje Todorovic <djordje.todorovic@syrmia.com>
Wed, 30 Sep 2020 13:06:55 +0000 (06:06 -0700)
committerDjordje Todorovic <djtodoro@cisco.com>
Thu, 18 Feb 2021 09:52:16 +0000 (01:52 -0800)
As discussed on the RFC [0], I am sharing the set of patches that
enables checking of original Debug Info metadata preservation in
optimizations. The proof-of-concept/proposal can be found at [1].

The implementation from the [1] was full of duplicated code,
so this set of patches tries to merge this approach into the existing
debugify utility.

For example, the utility pass in the original-debuginfo-check
mode could be invoked as follows:

  $ opt -verify-debuginfo-preserve -pass-to-test sample.ll

Since this is very initial stage of the implementation,
there is a space for improvements such as:
  - Add support for the new pass manager
  - Add support for metadata other than DILocations and DISubprograms

[0] https://groups.google.com/forum/#!msg/llvm-dev/QOyF-38YPlE/G213uiuwCAAJ
[1] https://github.com/djolertrk/llvm-di-checker

Differential Revision: https://reviews.llvm.org/D82545

llvm/docs/HowToUpdateDebugInfo.rst
llvm/include/llvm/Transforms/Utils/Debugify.h
llvm/lib/Transforms/Utils/Debugify.cpp
llvm/test/DebugInfo/debugify-original-no-dbg-info.ll [new file with mode: 0644]
llvm/tools/opt/opt.cpp
llvm/unittests/Transforms/Utils/CMakeLists.txt
llvm/unittests/Transforms/Utils/DebugifyTest.cpp [new file with mode: 0644]

index 0e3612e..1abd76e 100644 (file)
@@ -229,8 +229,8 @@ An IR test case for a transformation can, in many cases, be automatically
 mutated to test debug info handling within that transformation. This is a
 simple way to test for proper debug info handling.
 
-The ``debugify`` utility
-^^^^^^^^^^^^^^^^^^^^^^^^
+The ``debugify`` utility pass
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 The ``debugify`` testing utility is just a pair of passes: ``debugify`` and
 ``check-debugify``.
@@ -346,6 +346,21 @@ tests. Changes to this pass are not allowed to break existing tests.
 
 .. _MIRDebugify:
 
+Test original debug info preservation in optimizations
+------------------------------------------------------
+
+In addition to automatically generating debug info, the checks provided by
+the ``debugify`` utility pass can also be used to test the preservation of
+pre-existing debug info metadata. It could be run as follows:
+
+.. code-block:: bash
+
+  # Run the pass by checking original Debug Info preservation.
+  $ opt -verify-debuginfo-preserve -pass-to-test sample.ll
+
+  # Check the preservation of original Debug Info after each pass.
+  $ opt -verify-each-debuginfo-preserve -O2 sample.ll
+
 Mutation testing for MIR-level transformations
 ----------------------------------------------
 
index 44620de..91bc5fb 100644 (file)
@@ -1,4 +1,4 @@
-//===- Debugify.h - Attach synthetic debug info to everything -------------===//
+//===- Debugify.h - Check debug info preservation in optimizations --------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,19 +6,41 @@
 //
 //===----------------------------------------------------------------------===//
 ///
-/// \file Interface to the `debugify` synthetic debug info testing utility.
+/// \file Interface to the `debugify` synthetic/original debug info testing
+/// utility.
 ///
 //===----------------------------------------------------------------------===//
 
 #ifndef LLVM_TRANSFORMS_UTILS_DEBUGIFY_H
 #define LLVM_TRANSFORMS_UTILS_DEBUGIFY_H
 
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Bitcode/BitcodeWriterPass.h"
 #include "llvm/IR/IRPrintingPasses.h"
 #include "llvm/IR/LegacyPassManager.h"
 #include "llvm/IR/PassManager.h"
+#include "llvm/IR/ValueHandle.h"
+
+using DebugFnMap = llvm::DenseMap<llvm::StringRef, const llvm::DISubprogram *>;
+using DebugInstMap = llvm::DenseMap<const llvm::Instruction *, bool>;
+using WeakInstValueMap =
+    llvm::DenseMap<const llvm::Instruction *, llvm::WeakVH>;
+
+/// Used to track the Debug Info Metadata information.
+struct DebugInfoPerPass {
+  // This maps a function name to its associated DISubprogram.
+  DebugFnMap DIFunctions;
+  // This maps an instruction and the info about whether it has !dbg attached.
+  DebugInstMap DILocations;
+  // This tracks value (instruction) deletion. If an instruction gets deleted,
+  // WeakVH nulls itself.
+  WeakInstValueMap InstToDelete;
+};
+
+/// Map pass names to a per-pass DebugInfoPerPass instance.
+using DebugInfoPerPassMap = llvm::MapVector<llvm::StringRef, DebugInfoPerPass>;
 
 namespace llvm {
 class DIBuilder;
@@ -40,14 +62,49 @@ bool applyDebugifyMetadata(
 /// Returns true if any change was made.
 bool stripDebugifyMetadata(Module &M);
 
-llvm::ModulePass *createDebugifyModulePass();
-llvm::FunctionPass *createDebugifyFunctionPass();
+/// Collect original debug information before a pass.
+///
+/// \param M The module to collect debug information from.
+/// \param Functions A range of functions to collect debug information from.
+/// \param DIPreservationMap A map to collect the DI metadata.
+/// \param Banner A prefix string to add to debug/error messages.
+/// \param NameOfWrappedPass A name of a pass to add to debug/error messages.
+bool collectDebugInfoMetadata(Module &M,
+                              iterator_range<Module::iterator> Functions,
+                              DebugInfoPerPassMap &DIPreservationMap,
+                              StringRef Banner, StringRef NameOfWrappedPass);
+
+/// Check original debug information after a pass.
+///
+/// \param M The module to collect debug information from.
+/// \param Functions A range of functions to collect debug information from.
+/// \param DIPreservationMap A map used to check collected the DI metadata.
+/// \param Banner A prefix string to add to debug/error messages.
+/// \param NameOfWrappedPass A name of a pass to add to debug/error messages.
+bool checkDebugInfoMetadata(Module &M,
+                            iterator_range<Module::iterator> Functions,
+                            DebugInfoPerPassMap &DIPreservationMap,
+                            StringRef Banner, StringRef NameOfWrappedPass);
+} // namespace llvm
+
+/// Used to check whether we track synthetic or original debug info.
+enum class DebugifyMode { NoDebugify, SyntheticDebugInfo, OriginalDebugInfo };
+
+llvm::ModulePass *createDebugifyModulePass(
+    enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+    llvm::StringRef NameOfWrappedPass = "",
+    DebugInfoPerPassMap *DIPreservationMap = nullptr);
+llvm::FunctionPass *createDebugifyFunctionPass(
+    enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+    llvm::StringRef NameOfWrappedPass = "",
+    DebugInfoPerPassMap *DIPreservationMap = nullptr);
 
 struct NewPMDebugifyPass : public llvm::PassInfoMixin<NewPMDebugifyPass> {
   llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
 };
 
-/// Track how much `debugify` information has been lost.
+/// Track how much `debugify` information (in the `synthetic` mode only)
+/// has been lost.
 struct DebugifyStatistics {
   /// Number of missing dbg.values.
   unsigned NumDbgValuesMissing = 0;
@@ -75,23 +132,26 @@ struct DebugifyStatistics {
 /// Map pass names to a per-pass DebugifyStatistics instance.
 using DebugifyStatsMap = llvm::MapVector<llvm::StringRef, DebugifyStatistics>;
 
-void exportDebugifyStats(StringRef Path, const DebugifyStatsMap &Map);
-
-llvm::ModulePass *
-createCheckDebugifyModulePass(bool Strip = false,
-                              llvm::StringRef NameOfWrappedPass = "",
-                              DebugifyStatsMap *StatsMap = nullptr);
+llvm::ModulePass *createCheckDebugifyModulePass(
+    bool Strip = false, llvm::StringRef NameOfWrappedPass = "",
+    DebugifyStatsMap *StatsMap = nullptr,
+    enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+    DebugInfoPerPassMap *DIPreservationMap = nullptr);
 
-llvm::FunctionPass *
-createCheckDebugifyFunctionPass(bool Strip = false,
-                                llvm::StringRef NameOfWrappedPass = "",
-                                DebugifyStatsMap *StatsMap = nullptr);
+llvm::FunctionPass *createCheckDebugifyFunctionPass(
+    bool Strip = false, llvm::StringRef NameOfWrappedPass = "",
+    DebugifyStatsMap *StatsMap = nullptr,
+    enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+    DebugInfoPerPassMap *DIPreservationMap = nullptr);
 
 struct NewPMCheckDebugifyPass
     : public llvm::PassInfoMixin<NewPMCheckDebugifyPass> {
   llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
 };
 
+namespace llvm {
+void exportDebugifyStats(StringRef Path, const DebugifyStatsMap &Map);
+
 struct DebugifyEachInstrumentation {
   DebugifyStatsMap StatsMap;
 
@@ -103,8 +163,9 @@ struct DebugifyEachInstrumentation {
 /// NOTE: We support legacy custom pass manager only.
 /// TODO: Add New PM support for custom pass manager.
 class DebugifyCustomPassManager : public legacy::PassManager {
-  DebugifyStatsMap DIStatsMap;
-  bool EnableDebugifyEach = false;
+  DebugifyStatsMap *DIStatsMap = nullptr;
+  DebugInfoPerPassMap *DIPreservationMap = nullptr;
+  enum DebugifyMode Mode = DebugifyMode::NoDebugify;
 
 public:
   using super = legacy::PassManager;
@@ -112,29 +173,34 @@ public:
   void add(Pass *P) override {
     // Wrap each pass with (-check)-debugify passes if requested, making
     // exceptions for passes which shouldn't see -debugify instrumentation.
-    bool WrapWithDebugify = EnableDebugifyEach && !P->getAsImmutablePass() &&
-                            !isIRPrintingPass(P) && !isBitcodeWriterPass(P);
+    bool WrapWithDebugify =
+        Mode != DebugifyMode::NoDebugify &&
+        !P->getAsImmutablePass() && !isIRPrintingPass(P) &&
+        !isBitcodeWriterPass(P);
     if (!WrapWithDebugify) {
       super::add(P);
       return;
     }
 
-    // Apply -debugify/-check-debugify before/after each pass and collect
-    // debug info loss statistics.
+    // Either apply -debugify/-check-debugify before/after each pass and collect
+    // debug info loss statistics, or collect and check original debug info in
+    // the optimizations.
     PassKind Kind = P->getPassKind();
     StringRef Name = P->getPassName();
 
     // TODO: Implement Debugify for LoopPass.
     switch (Kind) {
     case PT_Function:
-      super::add(createDebugifyFunctionPass());
+      super::add(createDebugifyFunctionPass(Mode, Name, DIPreservationMap));
       super::add(P);
-      super::add(createCheckDebugifyFunctionPass(true, Name, &DIStatsMap));
+      super::add(createCheckDebugifyFunctionPass(
+          isSyntheticDebugInfo(), Name, DIStatsMap, Mode, DIPreservationMap));
       break;
     case PT_Module:
-      super::add(createDebugifyModulePass());
+      super::add(createDebugifyModulePass(Mode, Name, DIPreservationMap));
       super::add(P);
-      super::add(createCheckDebugifyModulePass(true, Name, &DIStatsMap));
+      super::add(createCheckDebugifyModulePass(
+          isSyntheticDebugInfo(), Name, DIStatsMap, Mode, DIPreservationMap));
       break;
     default:
       super::add(P);
@@ -142,9 +208,23 @@ public:
     }
   }
 
-  void enableDebugifyEach() { EnableDebugifyEach = true; }
+  // Used within DebugifyMode::SyntheticDebugInfo mode.
+  void setDIStatsMap(DebugifyStatsMap &StatMap) { DIStatsMap = &StatMap; }
+  // Used within DebugifyMode::OriginalDebugInfo mode.
+  void setDIPreservationMap(DebugInfoPerPassMap &PerPassMap) {
+    DIPreservationMap = &PerPassMap;
+  }
+  void setDebugifyMode(enum DebugifyMode M) { Mode = M; }
+
+  bool isSyntheticDebugInfo() const {
+    return Mode == DebugifyMode::SyntheticDebugInfo;
+  }
+  bool isOriginalDebugInfoMode() const {
+    return Mode == DebugifyMode::OriginalDebugInfo;
+  }
 
-  const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; }
+  const DebugifyStatsMap &getDebugifyStatsMap() const { return *DIStatsMap; }
+  DebugInfoPerPassMap &getDebugInfoPerPassMap() { return *DIPreservationMap; }
 };
 } // namespace llvm
 
index 3e4d53c..5c7780f 100644 (file)
@@ -1,4 +1,4 @@
-//===- Debugify.cpp - Attach synthetic debug info to everything -----------===//
+//===- Debugify.cpp - Check debug info preservation in optimizations ------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 ///
-/// \file This pass attaches synthetic debug info to everything. It can be used
-/// to create targeted tests for debug info preservation.
+/// \file In the `synthetic` mode, the `-debugify` attaches synthetic debug info
+/// to everything. It can be used to create targeted tests for debug info
+/// preservation. In addition, when using the `original` mode, it can check
+/// original debug info preservation. The `synthetic` mode is default one.
 ///
 //===----------------------------------------------------------------------===//
 
@@ -24,6 +26,8 @@
 #include "llvm/Pass.h"
 #include "llvm/Support/CommandLine.h"
 
+#define DEBUG_TYPE "debugify"
+
 using namespace llvm;
 
 namespace {
@@ -35,6 +39,8 @@ enum class Level {
   Locations,
   LocationsAndVariables
 };
+
+// Used for the synthetic mode only.
 cl::opt<Level> DebugifyLevel(
     "debugify-level", cl::desc("Kind of debug info to add"),
     cl::values(clEnumValN(Level::Locations, "locations", "Locations only"),
@@ -199,16 +205,33 @@ bool llvm::applyDebugifyMetadata(
   return true;
 }
 
-static bool applyDebugify(Function &F) {
+static bool
+applyDebugify(Function &F,
+              enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+              DebugInfoPerPassMap *DIPreservationMap = nullptr,
+              StringRef NameOfWrappedPass = "") {
   Module &M = *F.getParent();
   auto FuncIt = F.getIterator();
-  return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
-                               "FunctionDebugify: ", /*ApplyToMF=*/nullptr);
+  if (Mode == DebugifyMode::SyntheticDebugInfo)
+    return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
+                                 "FunctionDebugify: ", /*ApplyToMF*/ nullptr);
+  assert(DIPreservationMap);
+  return collectDebugInfoMetadata(M, M.functions(), *DIPreservationMap,
+                                  "FunctionDebugify (original debuginfo)",
+                                  NameOfWrappedPass);
 }
 
-static bool applyDebugify(Module &M) {
-  return applyDebugifyMetadata(M, M.functions(),
-                               "ModuleDebugify: ", /*ApplyToMF=*/nullptr);
+static bool
+applyDebugify(Module &M,
+              enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+              DebugInfoPerPassMap *DIPreservationMap = nullptr,
+              StringRef NameOfWrappedPass = "") {
+  if (Mode == DebugifyMode::SyntheticDebugInfo)
+    return applyDebugifyMetadata(M, M.functions(),
+                                 "ModuleDebugify: ", /*ApplyToMF*/ nullptr);
+  return collectDebugInfoMetadata(M, M.functions(), *DIPreservationMap,
+                                  "ModuleDebugify (original debuginfo)",
+                                  NameOfWrappedPass);
 }
 
 bool llvm::stripDebugifyMetadata(Module &M) {
@@ -256,6 +279,211 @@ bool llvm::stripDebugifyMetadata(Module &M) {
   return Changed;
 }
 
+bool llvm::collectDebugInfoMetadata(Module &M,
+                                    iterator_range<Module::iterator> Functions,
+                                    DebugInfoPerPassMap &DIPreservationMap,
+                                    StringRef Banner,
+                                    StringRef NameOfWrappedPass) {
+  LLVM_DEBUG(dbgs() << Banner << ": (before) " << NameOfWrappedPass << '\n');
+
+  // Clear the map with the debug info before every single pass.
+  DIPreservationMap.clear();
+
+  if (!M.getNamedMetadata("llvm.dbg.cu")) {
+    dbg() << Banner << ": Skipping module without debug info\n";
+    return false;
+  }
+
+  // Visit each instruction.
+  for (Function &F : Functions) {
+    if (isFunctionSkipped(F))
+      continue;
+
+    // Collect the DISubprogram.
+    auto *SP = F.getSubprogram();
+    DIPreservationMap[NameOfWrappedPass].DIFunctions.insert({F.getName(), SP});
+    if (SP)
+      LLVM_DEBUG(dbgs() << "  Collecting subprogram: " << *SP << '\n');
+
+    for (BasicBlock &BB : F) {
+      // Collect debug locations (!dbg).
+      // TODO: Collect dbg.values.
+      for (Instruction &I : BB) {
+        // Skip PHIs.
+        if (isa<PHINode>(I))
+          continue;
+
+        // Skip debug instructions.
+        if (isa<DbgInfoIntrinsic>(&I))
+          continue;
+
+        LLVM_DEBUG(dbgs() << "  Collecting info for inst: " << I << '\n');
+        DIPreservationMap[NameOfWrappedPass].InstToDelete.insert({&I, &I});
+
+        const DILocation *Loc = I.getDebugLoc().get();
+        bool HasLoc = Loc != nullptr;
+        DIPreservationMap[NameOfWrappedPass].DILocations.insert({&I, HasLoc});
+      }
+    }
+  }
+
+  return true;
+}
+
+// This checks the preservation of original debug info attached to functions.
+static bool checkFunctions(const DebugFnMap &DIFunctionsBefore,
+                           const DebugFnMap &DIFunctionsAfter,
+                           StringRef NameOfWrappedPass,
+                           StringRef FileNameFromCU) {
+  bool Preserved = true;
+  for (const auto &F : DIFunctionsAfter) {
+    if (F.second)
+      continue;
+    auto SPIt = DIFunctionsBefore.find(F.first);
+    if (SPIt == DIFunctionsBefore.end()) {
+      dbg() << "ERROR: " << NameOfWrappedPass
+            << " did not generate DISubprogram for " << F.first << " from "
+            << FileNameFromCU << '\n';
+      Preserved = false;
+    } else {
+      auto SP = SPIt->second;
+      if (!SP)
+        continue;
+      // If the function had the SP attached before the pass, consider it as
+      // a debug info bug.
+      dbg() << "ERROR: " << NameOfWrappedPass << " dropped DISubprogram of "
+            << F.first << " from " << FileNameFromCU << '\n';
+      Preserved = false;
+    }
+  }
+
+  return Preserved;
+}
+
+// This checks the preservation of the original debug info attached to
+// instructions.
+static bool checkInstructions(const DebugInstMap &DILocsBefore,
+                              const DebugInstMap &DILocsAfter,
+                              const WeakInstValueMap &InstToDelete,
+                              StringRef NameOfWrappedPass,
+                              StringRef FileNameFromCU) {
+  bool Preserved = true;
+  for (const auto &L : DILocsAfter) {
+    if (L.second)
+      continue;
+    auto Instr = L.first;
+
+    // In order to avoid pointer reuse/recycling, skip the values that might
+    // have been deleted during a pass.
+    auto WeakInstrPtr = InstToDelete.find(Instr);
+    if (WeakInstrPtr != InstToDelete.end() && !WeakInstrPtr->second)
+      continue;
+
+    auto FnName = Instr->getFunction()->getName();
+    auto BB = Instr->getParent();
+    auto BBName = BB->hasName() ? BB->getName() : "no-name";
+
+    auto InstrIt = DILocsBefore.find(Instr);
+    if (InstrIt == DILocsBefore.end()) {
+      dbg() << "WARNING: " << NameOfWrappedPass
+            << " did not generate DILocation for " << *Instr
+            << " (BB: " << BBName << ", Fn: " << FnName
+            << ", File: " << FileNameFromCU << ")\n";
+      Preserved = false;
+    } else {
+      if (!InstrIt->second)
+        continue;
+      // If the instr had the !dbg attached before the pass, consider it as
+      // a debug info issue.
+      dbg() << "WARNING: " << NameOfWrappedPass << " dropped DILocation of "
+            << *Instr << " (BB: " << BBName << ", Fn: " << FnName
+            << ", File: " << FileNameFromCU << ")\n";
+      Preserved = false;
+    }
+  }
+
+  return Preserved;
+}
+
+bool llvm::checkDebugInfoMetadata(Module &M,
+                                  iterator_range<Module::iterator> Functions,
+                                  DebugInfoPerPassMap &DIPreservationMap,
+                                  StringRef Banner,
+                                  StringRef NameOfWrappedPass) {
+  LLVM_DEBUG(dbgs() << Banner << ": (after) " << NameOfWrappedPass << '\n');
+
+  if (!M.getNamedMetadata("llvm.dbg.cu")) {
+    dbg() << Banner << ": Skipping module without debug info\n";
+    return false;
+  }
+
+  // Map the debug info holding DIs after a pass.
+  DebugInfoPerPassMap DIPreservationAfter;
+
+  // Visit each instruction.
+  for (Function &F : Functions) {
+    if (isFunctionSkipped(F))
+      continue;
+
+    // TODO: Collect metadata other than DISubprograms.
+    // Collect the DISubprogram.
+    auto *SP = F.getSubprogram();
+    DIPreservationAfter[NameOfWrappedPass].DIFunctions.insert({F.getName(), SP});
+    if (SP)
+      LLVM_DEBUG(dbgs() << "  Collecting subprogram: " << *SP << '\n');
+
+    for (BasicBlock &BB : F) {
+      // Collect debug locations (!dbg attachments).
+      // TODO: Collect dbg.values.
+      for (Instruction &I : BB) {
+        // Skip PHIs.
+        if (isa<PHINode>(I))
+          continue;
+
+        // Skip debug instructions.
+        if (isa<DbgInfoIntrinsic>(&I))
+          continue;
+
+        LLVM_DEBUG(dbgs() << "  Collecting info for inst: " << I << '\n');
+
+        const DILocation *Loc = I.getDebugLoc().get();
+        bool HasLoc = Loc != nullptr;
+
+        DIPreservationAfter[NameOfWrappedPass].DILocations.insert({&I, HasLoc});
+      }
+    }
+  }
+
+  // TODO: The name of the module could be read better?
+  StringRef FileNameFromCU =
+      (cast<DICompileUnit>(M.getNamedMetadata("llvm.dbg.cu")->getOperand(0)))
+          ->getFilename();
+
+  auto DIFunctionsBefore = DIPreservationMap[NameOfWrappedPass].DIFunctions;
+  auto DIFunctionsAfter = DIPreservationAfter[NameOfWrappedPass].DIFunctions;
+
+  auto DILocsBefore = DIPreservationMap[NameOfWrappedPass].DILocations;
+  auto DILocsAfter = DIPreservationAfter[NameOfWrappedPass].DILocations;
+
+  auto InstToDelete = DIPreservationAfter[NameOfWrappedPass].InstToDelete;
+
+  bool ResultForFunc = checkFunctions(DIFunctionsBefore, DIFunctionsAfter,
+                                      NameOfWrappedPass, FileNameFromCU);
+  bool ResultForInsts =
+      checkInstructions(DILocsBefore, DILocsAfter, InstToDelete,
+                        NameOfWrappedPass, FileNameFromCU);
+  bool Result = ResultForFunc && ResultForInsts;
+
+  StringRef ResultBanner = NameOfWrappedPass != "" ? NameOfWrappedPass : Banner;
+  if (Result)
+    dbg() << ResultBanner << ": PASS\n";
+  else
+    dbg() << ResultBanner << ": FAIL\n";
+
+  LLVM_DEBUG(dbgs() << "\n\n");
+  return Result;
+}
+
 namespace {
 /// Return true if a mis-sized diagnostic is issued for \p DVI.
 bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) {
@@ -394,43 +622,74 @@ bool checkDebugifyMetadata(Module &M,
 /// ModulePass for attaching synthetic debug info to everything, used with the
 /// legacy module pass manager.
 struct DebugifyModulePass : public ModulePass {
-  bool runOnModule(Module &M) override { return applyDebugify(M); }
+  bool runOnModule(Module &M) override {
+    return applyDebugify(M, Mode, DIPreservationMap, NameOfWrappedPass);
+  }
 
-  DebugifyModulePass() : ModulePass(ID) {}
+  DebugifyModulePass(enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+                     StringRef NameOfWrappedPass = "",
+                     DebugInfoPerPassMap *DIPreservationMap = nullptr)
+      : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass),
+        DIPreservationMap(DIPreservationMap), Mode(Mode) {}
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
   }
 
   static char ID; // Pass identification.
+
+private:
+  StringRef NameOfWrappedPass;
+  DebugInfoPerPassMap *DIPreservationMap;
+  enum DebugifyMode Mode;
 };
 
 /// FunctionPass for attaching synthetic debug info to instructions within a
 /// single function, used with the legacy module pass manager.
 struct DebugifyFunctionPass : public FunctionPass {
-  bool runOnFunction(Function &F) override { return applyDebugify(F); }
+  bool runOnFunction(Function &F) override {
+    return applyDebugify(F, Mode, DIPreservationMap, NameOfWrappedPass);
+  }
 
-  DebugifyFunctionPass() : FunctionPass(ID) {}
+  DebugifyFunctionPass(
+      enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+      StringRef NameOfWrappedPass = "",
+      DebugInfoPerPassMap *DIPreservationMap = nullptr)
+      : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass),
+        DIPreservationMap(DIPreservationMap), Mode(Mode) {}
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
   }
 
   static char ID; // Pass identification.
+
+private:
+  StringRef NameOfWrappedPass;
+  DebugInfoPerPassMap *DIPreservationMap;
+  enum DebugifyMode Mode;
 };
 
 /// ModulePass for checking debug info inserted by -debugify, used with the
 /// legacy module pass manager.
 struct CheckDebugifyModulePass : public ModulePass {
   bool runOnModule(Module &M) override {
-    return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass,
-                                 "CheckModuleDebugify", Strip, StatsMap);
+    if (Mode == DebugifyMode::SyntheticDebugInfo)
+      return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass,
+                                   "CheckModuleDebugify", Strip, StatsMap);
+    return checkDebugInfoMetadata(
+        M, M.functions(), *DIPreservationMap,
+        "CheckModuleDebugify (original debuginfo)", NameOfWrappedPass);
   }
 
-  CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "",
-                          DebugifyStatsMap *StatsMap = nullptr)
-      : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass),
-        StatsMap(StatsMap) {}
+  CheckDebugifyModulePass(
+      bool Strip = false, StringRef NameOfWrappedPass = "",
+      DebugifyStatsMap *StatsMap = nullptr,
+      enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+      DebugInfoPerPassMap *DIPreservationMap = nullptr)
+      : ModulePass(ID), NameOfWrappedPass(NameOfWrappedPass),
+        StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode),
+        Strip(Strip) {}
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
@@ -439,9 +698,11 @@ struct CheckDebugifyModulePass : public ModulePass {
   static char ID; // Pass identification.
 
 private:
-  bool Strip;
   StringRef NameOfWrappedPass;
   DebugifyStatsMap *StatsMap;
+  DebugInfoPerPassMap *DIPreservationMap;
+  enum DebugifyMode Mode;
+  bool Strip;
 };
 
 /// FunctionPass for checking debug info inserted by -debugify-function, used
@@ -450,16 +711,23 @@ struct CheckDebugifyFunctionPass : public FunctionPass {
   bool runOnFunction(Function &F) override {
     Module &M = *F.getParent();
     auto FuncIt = F.getIterator();
-    return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
-                                 NameOfWrappedPass, "CheckFunctionDebugify",
-                                 Strip, StatsMap);
+    if (Mode == DebugifyMode::SyntheticDebugInfo)
+      return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
+                                   NameOfWrappedPass, "CheckFunctionDebugify",
+                                   Strip, StatsMap);
+    return checkDebugInfoMetadata(
+        M, make_range(FuncIt, std::next(FuncIt)), *DIPreservationMap,
+        "CheckFunctionDebugify (original debuginfo)", NameOfWrappedPass);
   }
 
-  CheckDebugifyFunctionPass(bool Strip = false,
-                            StringRef NameOfWrappedPass = "",
-                            DebugifyStatsMap *StatsMap = nullptr)
-      : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass),
-        StatsMap(StatsMap) {}
+  CheckDebugifyFunctionPass(
+      bool Strip = false, StringRef NameOfWrappedPass = "",
+      DebugifyStatsMap *StatsMap = nullptr,
+      enum DebugifyMode Mode = DebugifyMode::SyntheticDebugInfo,
+      DebugInfoPerPassMap *DIPreservationMap = nullptr)
+      : FunctionPass(ID), NameOfWrappedPass(NameOfWrappedPass),
+        StatsMap(StatsMap), DIPreservationMap(DIPreservationMap), Mode(Mode),
+        Strip(Strip) {}
 
   void getAnalysisUsage(AnalysisUsage &AU) const override {
     AU.setPreservesAll();
@@ -468,9 +736,11 @@ struct CheckDebugifyFunctionPass : public FunctionPass {
   static char ID; // Pass identification.
 
 private:
-  bool Strip;
   StringRef NameOfWrappedPass;
   DebugifyStatsMap *StatsMap;
+  DebugInfoPerPassMap *DIPreservationMap;
+  enum DebugifyMode Mode;
+  bool Strip;
 };
 
 } // end anonymous namespace
@@ -496,12 +766,23 @@ void llvm::exportDebugifyStats(StringRef Path, const DebugifyStatsMap &Map) {
   }
 }
 
-ModulePass *llvm::createDebugifyModulePass() {
-  return new DebugifyModulePass();
+ModulePass *createDebugifyModulePass(enum DebugifyMode Mode,
+                                     llvm::StringRef NameOfWrappedPass,
+                                     DebugInfoPerPassMap *DIPreservationMap) {
+  if (Mode == DebugifyMode::SyntheticDebugInfo)
+    return new DebugifyModulePass();
+  assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");
+  return new DebugifyModulePass(Mode, NameOfWrappedPass, DIPreservationMap);
 }
 
-FunctionPass *llvm::createDebugifyFunctionPass() {
-  return new DebugifyFunctionPass();
+FunctionPass *
+createDebugifyFunctionPass(enum DebugifyMode Mode,
+                           llvm::StringRef NameOfWrappedPass,
+                           DebugInfoPerPassMap *DIPreservationMap) {
+  if (Mode == DebugifyMode::SyntheticDebugInfo)
+    return new DebugifyFunctionPass();
+  assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");
+  return new DebugifyFunctionPass(Mode, NameOfWrappedPass, DIPreservationMap);
 }
 
 PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) {
@@ -510,16 +791,24 @@ PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) {
   return PreservedAnalyses::all();
 }
 
-ModulePass *llvm::createCheckDebugifyModulePass(bool Strip,
-                                                StringRef NameOfWrappedPass,
-                                                DebugifyStatsMap *StatsMap) {
-  return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap);
+ModulePass *createCheckDebugifyModulePass(
+    bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap,
+    enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) {
+  if (Mode == DebugifyMode::SyntheticDebugInfo)
+    return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap);
+  assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");
+  return new CheckDebugifyModulePass(false, NameOfWrappedPass, nullptr, Mode,
+                                     DIPreservationMap);
 }
 
-FunctionPass *
-llvm::createCheckDebugifyFunctionPass(bool Strip, StringRef NameOfWrappedPass,
-                                      DebugifyStatsMap *StatsMap) {
-  return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap);
+FunctionPass *createCheckDebugifyFunctionPass(
+    bool Strip, StringRef NameOfWrappedPass, DebugifyStatsMap *StatsMap,
+    enum DebugifyMode Mode, DebugInfoPerPassMap *DIPreservationMap) {
+  if (Mode == DebugifyMode::SyntheticDebugInfo)
+    return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap);
+  assert(Mode == DebugifyMode::OriginalDebugInfo && "Must be original mode");
+  return new CheckDebugifyFunctionPass(false, NameOfWrappedPass, nullptr, Mode,
+                                       DIPreservationMap);
 }
 
 PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M,
diff --git a/llvm/test/DebugInfo/debugify-original-no-dbg-info.ll b/llvm/test/DebugInfo/debugify-original-no-dbg-info.ll
new file mode 100644 (file)
index 0000000..ebe8d14
--- /dev/null
@@ -0,0 +1,23 @@
+; RUN: opt -verify-debuginfo-preserve -instcombine -S -o - < %s 2>&1 | FileCheck %s
+
+; CHECK: ModuleDebugify (original debuginfo): Skipping module without debug info
+; CHECK-NEXT: CheckModuleDebugify (original debuginfo): Skipping module without debug info
+
+; ModuleID = 'no-dbg-info.c'
+source_filename = "no-dbg-info.c"
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+target triple = "x86_64-unknown-linux-gnu"
+
+; Function Attrs: nounwind uwtable
+define dso_local i32 @fn() {
+  %1 = call i32 (...) @fn2()
+  ret i32 %1
+}
+
+declare dso_local i32 @fn2(...)
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{!"clang version 11.0.0"}
index bf2d59b..48b0e39 100644 (file)
@@ -219,6 +219,16 @@ static cl::opt<bool> EnableDebugify(
     cl::desc(
         "Start the pipeline with debugify and end it with check-debugify"));
 
+static cl::opt<bool> VerifyDebugInfoPreserve(
+    "verify-debuginfo-preserve",
+    cl::desc("Start the pipeline with collecting and end it with checking of "
+             "debug info preservation."));
+
+static cl::opt<bool> VerifyEachDebugInfoPreserve(
+    "verify-each-debuginfo-preserve",
+    cl::desc("Start each pass with collecting and end it with checking of "
+             "debug info preservation."));
+
 static cl::opt<bool>
 PrintBreakpoints("print-breakpoints-for-testing",
                  cl::desc("Print select breakpoints location for testing"));
@@ -813,10 +823,19 @@ int main(int argc, char **argv) {
   // about to build. If the -debugify-each option is set, wrap each pass with
   // the (-check)-debugify passes.
   DebugifyCustomPassManager Passes;
-  if (DebugifyEach)
-    Passes.enableDebugifyEach();
+  DebugifyStatsMap DIStatsMap;
+  DebugInfoPerPassMap DIPreservationMap;
+  if (DebugifyEach) {
+    Passes.setDebugifyMode(DebugifyMode::SyntheticDebugInfo);
+    Passes.setDIStatsMap(DIStatsMap);
+  } else if (VerifyEachDebugInfoPreserve) {
+    Passes.setDebugifyMode(DebugifyMode::OriginalDebugInfo);
+    Passes.setDIPreservationMap(DIPreservationMap);
+  }
 
-  bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach;
+  bool AddOneTimeDebugifyPasses =
+      (EnableDebugify && !DebugifyEach) ||
+      (VerifyDebugInfoPreserve && !VerifyEachDebugInfoPreserve);
 
   Passes.add(new TargetLibraryInfoWrapperPass(TLII));
 
@@ -824,8 +843,17 @@ int main(int argc, char **argv) {
   Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis()
                                                      : TargetIRAnalysis()));
 
-  if (AddOneTimeDebugifyPasses)
-    Passes.add(createDebugifyModulePass());
+  if (AddOneTimeDebugifyPasses) {
+    if (EnableDebugify) {
+      Passes.setDIStatsMap(DIStatsMap);
+      Passes.add(createDebugifyModulePass());
+    } else if (VerifyDebugInfoPreserve) {
+      Passes.setDIPreservationMap(DIPreservationMap);
+      Passes.add(createDebugifyModulePass(
+          DebugifyMode::OriginalDebugInfo, "",
+          &(Passes.getDebugInfoPerPassMap())));
+    }
+  }
 
   std::unique_ptr<legacy::FunctionPassManager> FPasses;
   if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
@@ -969,8 +997,14 @@ int main(int argc, char **argv) {
   if (!NoVerify && !VerifyEach)
     Passes.add(createVerifierPass());
 
-  if (AddOneTimeDebugifyPasses)
-    Passes.add(createCheckDebugifyModulePass(false));
+  if (AddOneTimeDebugifyPasses) {
+    if (EnableDebugify)
+      Passes.add(createCheckDebugifyModulePass(false));
+    else if (VerifyDebugInfoPreserve)
+      Passes.add(createCheckDebugifyModulePass(
+          false, "", nullptr, DebugifyMode::OriginalDebugInfo,
+          &(Passes.getDebugInfoPerPassMap())));
+  }
 
   // In run twice mode, we want to make sure the output is bit-by-bit
   // equivalent if we run the pass manager again, so setup two buffers and
index 64723a0..c27af27 100644 (file)
@@ -1,6 +1,7 @@
 set(LLVM_LINK_COMPONENTS
   Analysis
   AsmParser
+  BitWriter
   Core
   Support
   TransformUtils
@@ -13,6 +14,7 @@ add_llvm_unittest(UtilsTests
   CloningTest.cpp
   CodeExtractorTest.cpp
   CodeMoverUtilsTest.cpp
+  DebugifyTest.cpp
   FunctionComparatorTest.cpp
   IntegerDivisionTest.cpp
   LocalTest.cpp
diff --git a/llvm/unittests/Transforms/Utils/DebugifyTest.cpp b/llvm/unittests/Transforms/Utils/DebugifyTest.cpp
new file mode 100644 (file)
index 0000000..e4eff4b
--- /dev/null
@@ -0,0 +1,180 @@
+//===- DebugifyTest.cpp - Debugify unit 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/IR/DebugInfoMetadata.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Transforms/Utils/Debugify.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+static std::unique_ptr<Module> parseIR(LLVMContext &C, const char *IR) {
+  SMDiagnostic Err;
+  std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
+  if (!Mod)
+    Err.print("DebugifyTest", errs());
+  return Mod;
+}
+
+namespace llvm {
+void initializeDebugInfoDropPass(PassRegistry &);
+void initializeDebugInfoDummyAnalysisPass(PassRegistry &);
+
+namespace {
+struct DebugInfoDrop : public FunctionPass {
+  static char ID;
+  bool runOnFunction(Function &F) override {
+    // Drop DISubprogram.
+    F.setSubprogram(nullptr);
+    for (BasicBlock &BB : F) {
+      // Remove debug locations.
+      for (Instruction &I : BB)
+        I.setDebugLoc(DebugLoc());
+    }
+
+    return false;
+  }
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesCFG();
+  }
+
+  DebugInfoDrop() : FunctionPass(ID) {}
+};
+
+struct DebugInfoDummyAnalysis : public FunctionPass {
+  static char ID;
+  bool runOnFunction(Function &F) override {
+    // Do nothing, so debug info stays untouched.
+    return false;
+  }
+  void getAnalysisUsage(AnalysisUsage &AU) const override {
+    AU.setPreservesAll();
+  }
+
+  DebugInfoDummyAnalysis() : FunctionPass(ID) {}
+};
+}
+
+char DebugInfoDrop::ID = 0;
+char DebugInfoDummyAnalysis::ID = 0;
+
+TEST(DebugInfoDrop, DropOriginalDebugInfo) {
+  LLVMContext C;
+  std::unique_ptr<Module> M = parseIR(C, R"(
+    define i16 @f(i16 %a) !dbg !6 {
+      %b = add i16 %a, 1, !dbg !11
+      call void @llvm.dbg.value(metadata i16 %b, metadata !9, metadata !DIExpression()), !dbg !11
+      ret i16 0, !dbg !11
+    }
+    declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+    !llvm.dbg.cu = !{!0}
+    !llvm.module.flags = !{!5}
+
+    !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+    !1 = !DIFile(filename: "t.ll", directory: "/")
+    !2 = !{}
+    !5 = !{i32 2, !"Debug Info Version", i32 3}
+    !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
+    !7 = !DISubroutineType(types: !2)
+    !8 = !{!9}
+    !9 = !DILocalVariable(name: "b", scope: !6, file: !1, line: 1, type: !10)
+    !10 = !DIBasicType(name: "ty16", size: 16, encoding: DW_ATE_unsigned)
+    !11 = !DILocation(line: 1, column: 1, scope: !6)
+  )");
+
+  DebugInfoDrop *P = new DebugInfoDrop();
+
+  DebugInfoPerPassMap DIPreservationMap;
+  DebugifyCustomPassManager Passes;
+  Passes.setDIPreservationMap(DIPreservationMap);
+  Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
+                                      &(Passes.getDebugInfoPerPassMap())));
+  Passes.add(P);
+  Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
+                                           DebugifyMode::OriginalDebugInfo,
+                                           &(Passes.getDebugInfoPerPassMap())));
+
+  testing::internal::CaptureStderr();
+  Passes.run(*M);
+
+  std::string StdOut = testing::internal::GetCapturedStderr();
+
+  std::string ErrorForSP = "ERROR:  dropped DISubprogram of";
+  std::string WarningForLoc = "WARNING:  dropped DILocation of";
+  std::string FinalResult = "CheckModuleDebugify (original debuginfo): FAIL";
+
+  EXPECT_TRUE(StdOut.find(ErrorForSP) != std::string::npos);
+  EXPECT_TRUE(StdOut.find(WarningForLoc) != std::string::npos);
+  EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
+}
+
+TEST(DebugInfoDummyAnalysis, PreserveOriginalDebugInfo) {
+  LLVMContext C;
+  std::unique_ptr<Module> M = parseIR(C, R"(
+    define i32 @g(i32 %b) !dbg !6 {
+      %c = add i32 %b, 1, !dbg !11
+      call void @llvm.dbg.value(metadata i32 %c, metadata !9, metadata !DIExpression()), !dbg !11
+      ret i32 1, !dbg !11
+    }
+    declare void @llvm.dbg.value(metadata, metadata, metadata)
+
+    !llvm.dbg.cu = !{!0}
+    !llvm.module.flags = !{!5}
+
+    !0 = distinct !DICompileUnit(language: DW_LANG_C, file: !1, producer: "debugify", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
+    !1 = !DIFile(filename: "test.ll", directory: "/")
+    !2 = !{}
+    !5 = !{i32 2, !"Debug Info Version", i32 3}
+    !6 = distinct !DISubprogram(name: "f", linkageName: "f", scope: null, file: !1, line: 1, type: !7, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0, retainedNodes: !8)
+    !7 = !DISubroutineType(types: !2)
+    !8 = !{!9}
+    !9 = !DILocalVariable(name: "c", scope: !6, file: !1, line: 1, type: !10)
+    !10 = !DIBasicType(name: "ty32", size: 32, encoding: DW_ATE_unsigned)
+    !11 = !DILocation(line: 1, column: 1, scope: !6)
+  )");
+
+  DebugInfoDummyAnalysis *P = new DebugInfoDummyAnalysis();
+
+  DebugInfoPerPassMap DIPreservationMap;
+  DebugifyCustomPassManager Passes;
+  Passes.setDIPreservationMap(DIPreservationMap);
+  Passes.add(createDebugifyModulePass(DebugifyMode::OriginalDebugInfo, "",
+                                      &(Passes.getDebugInfoPerPassMap())));
+  Passes.add(P);
+  Passes.add(createCheckDebugifyModulePass(false, "", nullptr,
+                                           DebugifyMode::OriginalDebugInfo,
+                                           &(Passes.getDebugInfoPerPassMap())));
+
+  testing::internal::CaptureStderr();
+  Passes.run(*M);
+
+  std::string StdOut = testing::internal::GetCapturedStderr();
+
+  std::string ErrorForSP = "ERROR:  dropped DISubprogram of";
+  std::string WarningForLoc = "WARNING:  dropped DILocation of";
+  std::string FinalResult = "CheckModuleDebugify (original debuginfo): PASS";
+
+  EXPECT_TRUE(StdOut.find(ErrorForSP) == std::string::npos);
+  EXPECT_TRUE(StdOut.find(WarningForLoc) == std::string::npos);
+  EXPECT_TRUE(StdOut.find(FinalResult) != std::string::npos);
+}
+
+} // end namespace llvm
+
+INITIALIZE_PASS_BEGIN(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass",
+                      false, false)
+INITIALIZE_PASS_END(DebugInfoDrop, "debuginfodroppass", "debuginfodroppass", false,
+                    false)
+
+INITIALIZE_PASS_BEGIN(DebugInfoDummyAnalysis, "debuginfodummyanalysispass",
+                      "debuginfodummyanalysispass", false, false)
+INITIALIZE_PASS_END(DebugInfoDummyAnalysis, "debuginfodummyanalysispass",
+                    "debuginfodummyanalysispass", false, false)