aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Malea <daniel.malea@intel.com>2013-05-08 20:44:14 +0000
committerDaniel Malea <daniel.malea@intel.com>2013-05-08 20:44:14 +0000
commit13ace6664fad8b4d0277d16690674f4e1f176642 (patch)
tree07e183914fcea6659f8e2d4fa527d109a2e703cb
parentd0fef32e5f3700c7de6dfc50a1a3815659b7d19e (diff)
Add DebugIR pass -- emits IR file and replace source lines with IR lines in MD
- requires existing debug information to be present - fixes up file name and line number information in metadata - emits a "<orig_filename>-debug.ll" succinct IR file (without !dbg metadata or debug intrinsics) that can be read by a debugger - initialize pass in opt tool to enable the "-debug-ir" flag - lit tests to follow git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@181467 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/DebugInfo.h3
-rw-r--r--include/llvm/InitializePasses.h1
-rw-r--r--include/llvm/Transforms/Instrumentation.h4
-rw-r--r--lib/IR/DebugInfo.cpp7
-rw-r--r--lib/Transforms/Instrumentation/CMakeLists.txt1
-rw-r--r--lib/Transforms/Instrumentation/DebugIR.cpp246
-rw-r--r--tools/opt/opt.cpp1
7 files changed, 263 insertions, 0 deletions
diff --git a/include/llvm/DebugInfo.h b/include/llvm/DebugInfo.h
index 1c7edc971b..5f92ca22ec 100644
--- a/include/llvm/DebugInfo.h
+++ b/include/llvm/DebugInfo.h
@@ -166,6 +166,9 @@ namespace llvm {
public:
explicit DIScope(const MDNode *N = 0) : DIDescriptor (N) {}
+ /// Set the filename by allocating a new string MDNode for
+ /// it and attaching it to the underlying node.
+ void setFilename(StringRef Name, LLVMContext &Context);
StringRef getFilename() const;
StringRef getDirectory() const;
};
diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h
index 5b2cd603c3..072c2e4895 100644
--- a/include/llvm/InitializePasses.h
+++ b/include/llvm/InitializePasses.h
@@ -100,6 +100,7 @@ void initializeDAEPass(PassRegistry&);
void initializeDAHPass(PassRegistry&);
void initializeDCEPass(PassRegistry&);
void initializeDSEPass(PassRegistry&);
+void initializeDebugIRPass(PassRegistry&);
void initializeDeadInstEliminationPass(PassRegistry&);
void initializeDeadMachineInstructionElimPass(PassRegistry&);
void initializeDependenceAnalysisPass(PassRegistry&);
diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h
index 4aae200dd0..142b40f1ea 100644
--- a/include/llvm/Transforms/Instrumentation.h
+++ b/include/llvm/Transforms/Instrumentation.h
@@ -78,6 +78,10 @@ FunctionPass *createThreadSanitizerPass(StringRef BlacklistFile = StringRef());
// checking on loads, stores, and other memory intrinsics.
FunctionPass *createBoundsCheckingPass();
+/// createDebugIRPass - Create and return a pass that modifies a module's
+/// debug metadata to point back to IR instead of the original source file
+ModulePass *createDebugIRPass(StringRef FilenamePostfix);
+
} // End llvm namespace
#endif
diff --git a/lib/IR/DebugInfo.cpp b/lib/IR/DebugInfo.cpp
index 3be5e0f849..38fc90f865 100644
--- a/lib/IR/DebugInfo.cpp
+++ b/lib/IR/DebugInfo.cpp
@@ -695,6 +695,13 @@ DIArray DISubprogram::getVariables() const {
return DIArray();
}
+void DIScope::setFilename(StringRef Name, LLVMContext &Context) {
+ if (!DbgNode)
+ return;
+ MDString *MDName(MDString::get(Context, Name));
+ const_cast<MDNode*>(getNodeField(DbgNode, 1))->replaceOperandWith(0, MDName);
+}
+
StringRef DIScope::getFilename() const {
if (!DbgNode)
return StringRef();
diff --git a/lib/Transforms/Instrumentation/CMakeLists.txt b/lib/Transforms/Instrumentation/CMakeLists.txt
index 1c9e053679..aa265a49ed 100644
--- a/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -2,6 +2,7 @@ add_llvm_library(LLVMInstrumentation
AddressSanitizer.cpp
BlackList.cpp
BoundsChecking.cpp
+ DebugIR.cpp
EdgeProfiling.cpp
GCOVProfiling.cpp
MemorySanitizer.cpp
diff --git a/lib/Transforms/Instrumentation/DebugIR.cpp b/lib/Transforms/Instrumentation/DebugIR.cpp
new file mode 100644
index 0000000000..62a9b8abd8
--- /dev/null
+++ b/lib/Transforms/Instrumentation/DebugIR.cpp
@@ -0,0 +1,246 @@
+//===--- DebugIR.cpp - Transform debug metadata to allow debugging IR -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A Module transform pass that emits a succinct version of the IR and replaces
+// the source file metadata to allow debuggers to step through the IR.
+//
+// The location where the IR file is emitted is the same as the directory
+// operand of the !llvm.dbg.cu metadata node present in the input module. The
+// file name is constructed from the original file name by stripping the
+// extension and replacing it with "-debug.ll" or the Postfix string specified
+// at construction.
+//
+// FIXME: instead of replacing debug metadata, additional metadata should be
+// used to point capable debuggers to the IR file without destroying the
+// mapping to the original source file.
+//
+// FIXME: this pass should not depend on the existance of debug metadata in
+// the module as it does now. Instead, it should use DIBuilder to create the
+// required metadata.
+//
+//===----------------------------------------------------------------------===//
+
+#include <string>
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/DebugInfo.h"
+#include "llvm/DIBuilder.h"
+#include "llvm/IR/AsmWriter.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Pass.h"
+#include "llvm/Transforms/Instrumentation.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace llvm;
+
+namespace {
+
+/// Returns true if Node's name contains the string "llvm.dbg"
+bool isDebugNamedMetadata(const NamedMDNode *Node) {
+ return Node->getName().str().find("llvm.dbg") != std::string::npos;
+}
+
+/// Returns true if Inst is a call to llvm.dbg.value or llvm.dbg.declare
+bool isDebugIntrinsic(const IntrinsicInst *Inst) {
+ Intrinsic::ID id = Inst->getIntrinsicID();
+ return id == Intrinsic::dbg_value || id == Intrinsic::dbg_declare;
+}
+
+/// An AssemblyWriter which generates a succinct representation of the module
+/// (without debug intrinsics or metadata) suitable for debugging. As IR
+/// instructions are printed, !dbg metadata nodes are added (or updated)
+/// to point to the corresponding line in the generated IR file instead
+/// of the original source file. The input module must have been constructed
+/// with debug metadata (i.e. clang -g).
+class IRDebugInfoHelper : public llvm::AssemblyWriter {
+ /// Directory of debug metadata
+ const DebugInfoFinder &Finder;
+
+ /// Flags to control the verbosity of the generated IR file
+ bool hideDebugIntrinsics;
+ bool hideDebugMetadata;
+
+ /// Set to track metadata nodes to be printed (used only when
+ /// printDebugMetadata == false)
+ SmallSet<const MDNode *, 4> NonDebugNodes;
+
+public:
+ IRDebugInfoHelper(
+ formatted_raw_ostream &o, const Module *M,
+ AssemblyAnnotationWriter *AAW, const DebugInfoFinder &Finder,
+ bool hideDebugIntrinsics = true, bool hideDebugMetadata = true)
+ : AssemblyWriter(o, M, AAW), Finder(Finder),
+ hideDebugIntrinsics(hideDebugIntrinsics),
+ hideDebugMetadata(hideDebugMetadata) {}
+
+private:
+ virtual void printInstruction(const Instruction &I) {
+ DebugLoc Loc(I.getDebugLoc());
+
+ if (hideDebugMetadata)
+ removeDebugMetadata(const_cast<Instruction &>(I));
+
+ AssemblyWriter::printInstruction(I);
+ Out.flush();
+ // Adjust line number by 1 because we have not yet printed the \n
+ unsigned Line = Out.getLine() + 1;
+
+ DebugLoc NewLoc;
+ if (!Loc.isUnknown())
+ // I had a previous debug location: re-use the DebugLoc
+ NewLoc = DebugLoc::get(Line, /* FIXME: support columns */ 0,
+ Loc.getScope(I.getContext()),
+ Loc.getInlinedAt(I.getContext()));
+ else if (MDNode *scope = findFunctionMD(I.getParent()->getParent()))
+ // I had no previous debug location, but M has some debug information
+ NewLoc = DebugLoc::get(Line, 0, scope, /*FIXME: inlined instructions*/ 0);
+ else
+ // Neither I nor M has any debug information -- nothing to do here.
+ // FIXME: support debugging of undecorated IR (generated by clang without
+ // the -g option)
+ return;
+
+ if (hideDebugMetadata)
+ saveNonDebugMetadata(I);
+
+ addDebugLocation(const_cast<Instruction &>(I), NewLoc);
+ }
+
+ virtual void printInstructionLine(const Instruction &I) {
+ if (hideDebugIntrinsics)
+ if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(&I))
+ if (isDebugIntrinsic(II))
+ return;
+ AssemblyWriter::printInstructionLine(I);
+ }
+
+ virtual void writeMDNode(unsigned Slot, const MDNode *Node) {
+ if (hideDebugMetadata == false || isDebugMetadata(Node) == false)
+ AssemblyWriter::writeMDNode(Slot, Node);
+ }
+
+ virtual void printNamedMDNode(const NamedMDNode *NMD) {
+ if (hideDebugMetadata == false || isDebugNamedMetadata(NMD) == false)
+ AssemblyWriter::printNamedMDNode(NMD);
+ }
+
+ /// Returns the MDNode that corresponds with F
+ MDNode *findFunctionMD(const Function *F) {
+ for (DebugInfoFinder::iterator i = Finder.subprogram_begin(),
+ e = Finder.subprogram_end();
+ i != e; ++i) {
+ DISubprogram S(*i);
+ if (S.getFunction() == F)
+ return *i;
+ }
+ // cannot find F -- likely means there is no debug information
+ return 0;
+ }
+
+ /// Saves all non-debug metadata attached to I
+ void saveNonDebugMetadata(const Instruction &I) {
+ typedef SmallVector<std::pair<unsigned, MDNode *>, 4> MDNodeVector;
+ MDNodeVector Others;
+ I.getAllMetadataOtherThanDebugLoc(Others);
+ for (MDNodeVector::iterator i = Others.begin(), e = Others.end(); i != e;
+ ++i)
+ NonDebugNodes.insert(i->second);
+ }
+
+ /// Returns true if Node was not saved as non-debug metadata with
+ /// saveNonDebugMetadata(), false otherwise.
+ bool isDebugMetadata(const MDNode *Node) {
+ return NonDebugNodes.count(Node) == 0;
+ }
+
+ void removeDebugMetadata(Instruction &I) {
+ if (I.getMetadata(LLVMContext::MD_dbg))
+ I.setMetadata(LLVMContext::MD_dbg, 0);
+ }
+
+ void addDebugLocation(Instruction &I, DebugLoc Loc) {
+ MDNode *MD = Loc.getAsMDNode(I.getContext());
+ I.setMetadata(LLVMContext::MD_dbg, MD);
+ }
+};
+
+class DebugIR : public ModulePass {
+ std::string Postfix;
+ std::string Filename;
+ DebugInfoFinder Finder;
+
+public:
+ static char ID;
+
+ DebugIR() : ModulePass(ID), Postfix("-debug.ll") {}
+
+ /// Customize the postfix string used to replace the extension of the
+ /// original filename that appears in the !llvm.dbg.cu metadata node.
+ DebugIR(StringRef postfix) : ModulePass(ID), Postfix(postfix) {}
+
+private:
+ // Modify the filename embedded in the Compilation-Unit debug information of M
+ bool replaceFilename(Module &M) {
+ bool changed = false;
+
+ // Sanity check -- if llvm.dbg.cu node exists, the DebugInfoFinder
+ // better have found at least one CU!
+ if (M.getNamedMetadata("llvm.dbg.cu"))
+ assert(Finder.compile_unit_count() > 0 &&
+ "Found no compile units but llvm.dbg.cu node exists");
+
+ for (DebugInfoFinder::iterator i = Finder.compile_unit_begin(),
+ e = Finder.compile_unit_end();
+ i != e; ++i) {
+ DICompileUnit CU(*i);
+ Filename = CU.getFilename();
+
+ // Replace extension with postfix
+ size_t dot = Filename.find_last_of(".");
+ if (dot != std::string::npos)
+ Filename.erase(dot);
+ Filename += Postfix;
+
+ CU.setFilename(Filename, M.getContext());
+ changed = true;
+ }
+ return changed;
+ }
+
+ void writeAndUpdateDebugIRFile(Module *M) {
+ std::string error;
+ tool_output_file OutFile(Filename.c_str(), error);
+ OutFile.keep();
+ formatted_raw_ostream OS;
+ OS.setStream(OutFile.os(), false);
+
+ IRDebugInfoHelper W(OS, M, 0, Finder);
+ W.printModule(M);
+ }
+
+ bool runOnModule(Module &M) {
+ Finder.processModule(M);
+ bool changed = replaceFilename(M);
+ if (changed)
+ writeAndUpdateDebugIRFile(&M);
+ return changed;
+ }
+};
+
+} // anonymous namespace
+
+char DebugIR::ID = 0;
+INITIALIZE_PASS(DebugIR, "debug-ir", "Enable debugging IR", false, false)
+ ModulePass *llvm::createDebugIRPass(StringRef FilenamePostfix) {
+ return new DebugIR(FilenamePostfix);
+}
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index e385d7f577..57b03b4c48 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -567,6 +567,7 @@ int main(int argc, char **argv) {
// Initialize passes
PassRegistry &Registry = *PassRegistry::getPassRegistry();
initializeCore(Registry);
+ initializeDebugIRPass(Registry);
initializeScalarOpts(Registry);
initializeObjCARCOpts(Registry);
initializeVectorization(Registry);