diff options
Diffstat (limited to 'llvm/lib/Transforms')
| -rw-r--r-- | llvm/lib/Transforms/Instrumentation/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/lib/Transforms/Instrumentation/DebugIR.cpp | 246 | 
2 files changed, 247 insertions, 0 deletions
| diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt index 1c9e0536794..aa265a49ed1 100644 --- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ b/llvm/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/llvm/lib/Transforms/Instrumentation/DebugIR.cpp b/llvm/lib/Transforms/Instrumentation/DebugIR.cpp new file mode 100644 index 00000000000..62a9b8abd8f --- /dev/null +++ b/llvm/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); +} | 

