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``.
.. _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
----------------------------------------------
-//===- 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.
//
//===----------------------------------------------------------------------===//
///
-/// \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;
/// 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;
/// 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;
/// 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;
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);
}
}
- 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
-//===- 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.
//
//===----------------------------------------------------------------------===//
///
-/// \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.
///
//===----------------------------------------------------------------------===//
#include "llvm/Pass.h"
#include "llvm/Support/CommandLine.h"
+#define DEBUG_TYPE "debugify"
+
using namespace llvm;
namespace {
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"),
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) {
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) {
/// 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();
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
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();
static char ID; // Pass identification.
private:
- bool Strip;
StringRef NameOfWrappedPass;
DebugifyStatsMap *StatsMap;
+ DebugInfoPerPassMap *DIPreservationMap;
+ enum DebugifyMode Mode;
+ bool Strip;
};
} // end anonymous namespace
}
}
-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 &) {
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,
--- /dev/null
+; 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"}
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"));
// 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));
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 ||
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
set(LLVM_LINK_COMPONENTS
Analysis
AsmParser
+ BitWriter
Core
Support
TransformUtils
CloningTest.cpp
CodeExtractorTest.cpp
CodeMoverUtilsTest.cpp
+ DebugifyTest.cpp
FunctionComparatorTest.cpp
IntegerDivisionTest.cpp
LocalTest.cpp
--- /dev/null
+//===- 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)