aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/Utils/Debugify.cpp
diff options
context:
space:
mode:
authorDjordje Todorovic <djordje.todorovic@syrmia.com>2021-02-18 09:49:44 -0800
committerDjordje Todorovic <djtodoro@cisco.com>2021-02-18 23:29:22 -0800
commit1a2b3536efef20f12c44201c2834a383b7c5c4c2 (patch)
tree3d20e62691eb4fbf5e4fe93d2fb490f7c5fac900 /llvm/lib/Transforms/Utils/Debugify.cpp
parentd82ecb0ac72261189ec3a5dccd3ad55aeafe07b4 (diff)
downloadllvm-1a2b3536efef20f12c44201c2834a383b7c5c4c2.zip
llvm-1a2b3536efef20f12c44201c2834a383b7c5c4c2.tar.gz
llvm-1a2b3536efef20f12c44201c2834a383b7c5c4c2.tar.bz2
Reland "[Debugify] Make the debugify aware of the original (-g) Debug Info"
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 The test that was failing is now forced to use the old PM.
Diffstat (limited to 'llvm/lib/Transforms/Utils/Debugify.cpp')
-rw-r--r--llvm/lib/Transforms/Utils/Debugify.cpp371
1 files changed, 330 insertions, 41 deletions
diff --git a/llvm/lib/Transforms/Utils/Debugify.cpp b/llvm/lib/Transforms/Utils/Debugify.cpp
index 3e4d53c..5c7780f0 100644
--- a/llvm/lib/Transforms/Utils/Debugify.cpp
+++ b/llvm/lib/Transforms/Utils/Debugify.cpp
@@ -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,