[llvm] r181467 - Add DebugIR pass -- emits IR file and replace source lines with IR lines in MD

Daniel Malea daniel.malea at intel.com
Wed May 8 13:44:14 PDT 2013


Author: dmalea
Date: Wed May  8 15:44:14 2013
New Revision: 181467

URL: http://llvm.org/viewvc/llvm-project?rev=181467&view=rev
Log:
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


Added:
    llvm/trunk/lib/Transforms/Instrumentation/DebugIR.cpp
Modified:
    llvm/trunk/include/llvm/DebugInfo.h
    llvm/trunk/include/llvm/InitializePasses.h
    llvm/trunk/include/llvm/Transforms/Instrumentation.h
    llvm/trunk/lib/IR/DebugInfo.cpp
    llvm/trunk/lib/Transforms/Instrumentation/CMakeLists.txt
    llvm/trunk/tools/opt/opt.cpp

Modified: llvm/trunk/include/llvm/DebugInfo.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/DebugInfo.h?rev=181467&r1=181466&r2=181467&view=diff
==============================================================================
--- llvm/trunk/include/llvm/DebugInfo.h (original)
+++ llvm/trunk/include/llvm/DebugInfo.h Wed May  8 15:44:14 2013
@@ -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;
   };

Modified: llvm/trunk/include/llvm/InitializePasses.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/InitializePasses.h?rev=181467&r1=181466&r2=181467&view=diff
==============================================================================
--- llvm/trunk/include/llvm/InitializePasses.h (original)
+++ llvm/trunk/include/llvm/InitializePasses.h Wed May  8 15:44:14 2013
@@ -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&);

Modified: llvm/trunk/include/llvm/Transforms/Instrumentation.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Transforms/Instrumentation.h?rev=181467&r1=181466&r2=181467&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Transforms/Instrumentation.h (original)
+++ llvm/trunk/include/llvm/Transforms/Instrumentation.h Wed May  8 15:44:14 2013
@@ -78,6 +78,10 @@ FunctionPass *createThreadSanitizerPass(
 // 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

Modified: llvm/trunk/lib/IR/DebugInfo.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/DebugInfo.cpp?rev=181467&r1=181466&r2=181467&view=diff
==============================================================================
--- llvm/trunk/lib/IR/DebugInfo.cpp (original)
+++ llvm/trunk/lib/IR/DebugInfo.cpp Wed May  8 15:44:14 2013
@@ -695,6 +695,13 @@ DIArray DISubprogram::getVariables() con
   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();

Modified: llvm/trunk/lib/Transforms/Instrumentation/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Instrumentation/CMakeLists.txt?rev=181467&r1=181466&r2=181467&view=diff
==============================================================================
--- llvm/trunk/lib/Transforms/Instrumentation/CMakeLists.txt (original)
+++ llvm/trunk/lib/Transforms/Instrumentation/CMakeLists.txt Wed May  8 15:44:14 2013
@@ -2,6 +2,7 @@ add_llvm_library(LLVMInstrumentation
   AddressSanitizer.cpp
   BlackList.cpp
   BoundsChecking.cpp
+  DebugIR.cpp
   EdgeProfiling.cpp
   GCOVProfiling.cpp
   MemorySanitizer.cpp

Added: llvm/trunk/lib/Transforms/Instrumentation/DebugIR.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/Instrumentation/DebugIR.cpp?rev=181467&view=auto
==============================================================================
--- llvm/trunk/lib/Transforms/Instrumentation/DebugIR.cpp (added)
+++ llvm/trunk/lib/Transforms/Instrumentation/DebugIR.cpp Wed May  8 15:44:14 2013
@@ -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);
+}

Modified: llvm/trunk/tools/opt/opt.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/opt/opt.cpp?rev=181467&r1=181466&r2=181467&view=diff
==============================================================================
--- llvm/trunk/tools/opt/opt.cpp (original)
+++ llvm/trunk/tools/opt/opt.cpp Wed May  8 15:44:14 2013
@@ -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);





More information about the llvm-commits mailing list