summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Malea <daniel.malea@intel.com>2013-06-28 19:05:23 +0000
committerDaniel Malea <daniel.malea@intel.com>2013-06-28 19:05:23 +0000
commitaadaf9f230a187b0bc87a18ddff78600b1622175 (patch)
treeaddee3e832ae0c93f2dd7121d8b4c00958d38a1b
parenta5545bc2b9b1295f8443f6350487ec9b775b2d73 (diff)
downloadllvm-aadaf9f230a187b0bc87a18ddff78600b1622175.tar.gz
llvm-aadaf9f230a187b0bc87a18ddff78600b1622175.tar.bz2
llvm-aadaf9f230a187b0bc87a18ddff78600b1622175.tar.xz
Remove limitation on DebugIR that made it require existing debug metadata.
- Build debug metadata for 'bare' Modules using DIBuilder - DebugIR can be constructed to generate an IR file (to be seen by a debugger) or not in cases where the user already has an IR file on disk. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@185193 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/llvm/Transforms/Instrumentation.h35
-rw-r--r--lib/Transforms/Instrumentation/DebugIR.cpp616
2 files changed, 493 insertions, 158 deletions
diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h
index f2027ce82a..de397af5bd 100644
--- a/include/llvm/Transforms/Instrumentation.h
+++ b/include/llvm/Transforms/Instrumentation.h
@@ -78,11 +78,36 @@ 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,
- bool hideDebugIntrinsics = true,
- bool hideDebugMetadata = true);
+/// createDebugIRPass - Enable interactive stepping through LLVM IR in LLDB (or
+/// GDB) and generate a file with the LLVM IR to be
+/// displayed in the debugger.
+///
+/// Existing debug metadata is preserved (but may be modified) in order to allow
+/// accessing variables in the original source. The line table and file
+/// information is modified to correspond to the lines in the LLVM IR. If
+/// Filename and Directory are empty, a file name is generated based on existing
+/// debug information. If no debug information is available, a temporary file
+/// name is generated.
+///
+/// @param HideDebugIntrinsics Omit debug intrinsics in emitted IR source file.
+/// @param HideDebugMetadata Omit debug metadata in emitted IR source file.
+/// @param Filename Embed this file name in the debug information.
+/// @param Directory Embed this directory in the debug information.
+ModulePass *createDebugIRPass(bool HideDebugIntrinsics,
+ bool HideDebugMetadata,
+ StringRef Filename = StringRef(),
+ StringRef Directory = StringRef());
+
+/// createDebugIRPass - Enable interactive stepping through LLVM IR in LLDB
+/// (or GDB) with an existing IR file on disk. When creating
+/// a DebugIR pass with this function, no source file is
+/// output to disk and the existing one is unmodified. Debug
+/// metadata in the Module is created/updated to point to
+/// the existing textual IR file on disk.
+/// NOTE: If the IR file to be debugged is not on disk, use the version of this
+/// function with parameters in order to generate the file that will be
+/// seen by the debugger.
+ModulePass *createDebugIRPass();
} // End llvm namespace
diff --git a/lib/Transforms/Instrumentation/DebugIR.cpp b/lib/Transforms/Instrumentation/DebugIR.cpp
index 020804ff5f..2f602be78a 100644
--- a/lib/Transforms/Instrumentation/DebugIR.cpp
+++ b/lib/Transforms/Instrumentation/DebugIR.cpp
@@ -10,38 +10,38 @@
// 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.
+// FIXME: instead of replacing debug metadata, this pass should allow for
+// additional metadata to be used to point capable debuggers to the IR file
+// without destroying the mapping to the original source file.
//
//===----------------------------------------------------------------------===//
-#include <string>
+#define DEBUG_TYPE "debug-ir"
-#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/ValueMap.h"
#include "llvm/Assembly/AssemblyAnnotationWriter.h"
#include "llvm/DebugInfo.h"
#include "llvm/DIBuilder.h"
#include "llvm/InstVisitor.h"
+#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Module.h"
-#include "llvm/Pass.h"
#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+#include "DebugIR.h"
+
+#include <string>
+#include <unistd.h>
+
+#define STR_HELPER(x) #x
+#define STR(x) STR_HELPER(x)
+
using namespace llvm;
namespace {
@@ -53,11 +53,16 @@ class ValueToLineMap : public AssemblyAnnotationWriter {
ValueMap<const Value *, unsigned int> Lines;
typedef ValueMap<const Value *, unsigned int>::const_iterator LineIter;
+ void addEntry(const Value *V, formatted_raw_ostream &Out) {
+ Out.flush();
+ Lines.insert(std::make_pair(V, Out.getLine() + 1));
+ }
+
public:
/// Prints Module to a null buffer in order to build the map of Value pointers
/// to line numbers.
- ValueToLineMap(Module *M) {
+ ValueToLineMap(const Module *M) {
raw_null_ostream ThrowAway;
M->print(ThrowAway, this);
}
@@ -65,8 +70,11 @@ public:
// This function is called after an Instruction, GlobalValue, or GlobalAlias
// is printed.
void printInfoComment(const Value &V, formatted_raw_ostream &Out) {
- Out.flush();
- Lines.insert(std::make_pair(&V, Out.getLine() + 1));
+ addEntry(&V, Out);
+ }
+
+ void emitFunctionAnnot(const Function *F, formatted_raw_ostream &Out) {
+ addEntry(F, Out);
}
/// If V appears on a line in the textual IR representation, sets Line to the
@@ -86,15 +94,29 @@ class DebugIntrinsicsRemover : public InstVisitor<DebugIntrinsicsRemover> {
void remove(Instruction &I) { I.eraseFromParent(); }
public:
+ static void process(Module &M) {
+ DebugIntrinsicsRemover Remover;
+ Remover.visit(&M);
+ }
void visitDbgDeclareInst(DbgDeclareInst &I) { remove(I); }
void visitDbgValueInst(DbgValueInst &I) { remove(I); }
void visitDbgInfoIntrinsic(DbgInfoIntrinsic &I) { remove(I); }
};
-/// Removes debug metadata (!dbg) nodes from all instructions as well as
-/// metadata named "llvm.dbg.cu" in the Module.
+/// Removes debug metadata (!dbg) nodes from all instructions, and optionally
+/// metadata named "llvm.dbg.cu" if RemoveNamedInfo is true.
class DebugMetadataRemover : public InstVisitor<DebugMetadataRemover> {
+ bool RemoveNamedInfo;
+
public:
+ static void process(Module &M, bool RemoveNamedInfo = true) {
+ DebugMetadataRemover Remover(RemoveNamedInfo);
+ Remover.run(&M);
+ }
+
+ DebugMetadataRemover(bool RemoveNamedInfo)
+ : RemoveNamedInfo(RemoveNamedInfo) {}
+
void visitInstruction(Instruction &I) {
if (I.getMetadata(LLVMContext::MD_dbg))
I.setMetadata(LLVMContext::MD_dbg, 0);
@@ -104,207 +126,495 @@ public:
// Remove debug metadata attached to instructions
visit(M);
- // Remove CU named metadata (and all children nodes)
- NamedMDNode *Node = M->getNamedMetadata("llvm.dbg.cu");
- M->eraseNamedMetadata(Node);
+ if (RemoveNamedInfo) {
+ // Remove CU named metadata (and all children nodes)
+ NamedMDNode *Node = M->getNamedMetadata("llvm.dbg.cu");
+ if (Node)
+ M->eraseNamedMetadata(Node);
+ }
}
};
-/// Replaces line number metadata attached to Instruction nodes with new line
-/// numbers provided by the ValueToLineMap.
-class LineNumberReplacer : public InstVisitor<LineNumberReplacer> {
- /// Table of line numbers
- const ValueToLineMap &LineTable;
+/// Updates debug metadata in a Module:
+/// - changes Filename/Directory to values provided on construction
+/// - adds/updates line number (DebugLoc) entries associated with each
+/// instruction to reflect the instruction's location in an LLVM IR file
+class DIUpdater : public InstVisitor<DIUpdater> {
+ /// Builder of debug information
+ DIBuilder Builder;
+
+ /// Helper for type attributes/sizes/etc
+ DataLayout Layout;
- /// Table of cloned values
- const ValueToValueMapTy &VMap;
+ /// Map of Value* to line numbers
+ const ValueToLineMap LineTable;
+
+ /// Map of Value* (in original Module) to Value* (in optional cloned Module)
+ const ValueToValueMapTy *VMap;
/// Directory of debug metadata
- const DebugInfoFinder &Finder;
+ DebugInfoFinder Finder;
+
+ /// Source filename and directory
+ StringRef Filename;
+ StringRef Directory;
+
+ // CU nodes needed when creating DI subprograms
+ MDNode *FileNode;
+ MDNode *LexicalBlockFileNode;
+ const MDNode *CUNode;
+
+ ValueMap<const Function *, MDNode *> SubprogramDescriptors;
+ DenseMap<const Type *, MDNode *> TypeDescriptors;
public:
- LineNumberReplacer(const ValueToLineMap &VLM, const DebugInfoFinder &Finder,
- const ValueToValueMapTy &VMap)
- : LineTable(VLM), VMap(VMap), Finder(Finder) {}
+ DIUpdater(Module &M, StringRef Filename = StringRef(),
+ StringRef Directory = StringRef(), const Module *DisplayM = 0,
+ const ValueToValueMapTy *VMap = 0)
+ : Builder(M), Layout(&M), LineTable(DisplayM ? DisplayM : &M), VMap(VMap),
+ Finder(), Filename(Filename), Directory(Directory), FileNode(0),
+ LexicalBlockFileNode(0), CUNode(0) {
+ Finder.processModule(M);
+ visit(&M);
+ }
+
+ ~DIUpdater() { Builder.finalize(); }
+
+ void visitModule(Module &M) {
+ if (Finder.compile_unit_count() > 1)
+ report_fatal_error("DebugIR pass supports only a signle compile unit per "
+ "Module.");
+ createCompileUnit(
+ Finder.compile_unit_count() == 1 ? *Finder.compile_unit_begin() : 0);
+ }
+
+ void visitFunction(Function &F) {
+ if (F.isDeclaration() || findDISubprogram(&F))
+ return;
+
+ StringRef MangledName = F.getName();
+ DICompositeType Sig = createFunctionSignature(&F);
+
+ // find line of function declaration
+ unsigned Line = 0;
+ if (!findLine(&F, Line)) {
+ DEBUG(dbgs() << "WARNING: No line for Function " << F.getName().str()
+ << "\n");
+ return;
+ }
+
+ Instruction *FirstInst = F.begin()->begin();
+ unsigned ScopeLine = 0;
+ if (!findLine(FirstInst, ScopeLine)) {
+ DEBUG(dbgs() << "WARNING: No line for 1st Instruction in Function "
+ << F.getName().str() << "\n");
+ return;
+ }
+
+ bool Local = F.hasInternalLinkage();
+ bool IsDefinition = !F.isDeclaration();
+ bool IsOptimized = false;
+
+ int FuncFlags = llvm::DIDescriptor::FlagPrototyped;
+ assert(CUNode && FileNode);
+ MDNode *Sub = Builder.createFunction(
+ DICompileUnit(CUNode), F.getName(), MangledName, DIFile(FileNode), Line,
+ Sig, Local, IsDefinition, ScopeLine, FuncFlags, IsOptimized, &F);
+ assert(DISubprogram(Sub).Verify());
+ DEBUG(dbgs() << "create subprogram mdnode " << Sub << ": "
+ << "\n");
+
+ SubprogramDescriptors.insert(std::make_pair(&F, Sub));
+ }
void visitInstruction(Instruction &I) {
DebugLoc Loc(I.getDebugLoc());
+ /// If a ValueToValueMap is provided, use it to get the real instruction as
+ /// the line table was generated on a clone of the module on which we are
+ /// operating.
+ Value *RealInst = 0;
+ if (VMap)
+ RealInst = VMap->lookup(&I);
+
+ if (!RealInst)
+ RealInst = &I;
+
unsigned Col = 0; // FIXME: support columns
unsigned Line;
- if (!LineTable.getLine(VMap.lookup(&I), Line))
+ if (!LineTable.getLine(RealInst, Line)) {
// Instruction has no line, it may have been removed (in the module that
// will be passed to the debugger) so there is nothing to do here.
+ DEBUG(dbgs() << "WARNING: no LineTable entry for instruction " << RealInst
+ << "\n");
+ DEBUG(RealInst->dump());
return;
+ }
DebugLoc NewLoc;
if (!Loc.isUnknown())
// I had a previous debug location: re-use the DebugLoc
- NewLoc = DebugLoc::get(Line, Col, 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, Col, 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)
+ NewLoc = DebugLoc::get(Line, Col, Loc.getScope(RealInst->getContext()),
+ Loc.getInlinedAt(RealInst->getContext()));
+ else if (MDNode *scope = findScope(&I))
+ NewLoc = DebugLoc::get(Line, Col, scope, 0);
+ else {
+ DEBUG(dbgs() << "WARNING: no valid scope for instruction " << &I
+ << ". no DebugLoc will be present."
+ << "\n");
return;
+ }
- addDebugLocation(const_cast<Instruction &>(I), NewLoc);
+ addDebugLocation(I, NewLoc);
}
private:
- /// Returns the MDNode that corresponds with F
- MDNode *findFunctionMD(const Function *F) {
+ void createCompileUnit(MDNode *CUToReplace) {
+ std::string Flags;
+ bool IsOptimized = false;
+ StringRef Producer;
+ unsigned RuntimeVersion(0);
+ StringRef SplitName;
+
+ if (CUToReplace) {
+ // save fields from existing CU to re-use in the new CU
+ DICompileUnit ExistingCU(CUToReplace);
+ Producer = ExistingCU.getProducer();
+ IsOptimized = ExistingCU.isOptimized();
+ Flags = ExistingCU.getFlags();
+ RuntimeVersion = ExistingCU.getRunTimeVersion();
+ SplitName = ExistingCU.getSplitDebugFilename();
+ } else {
+ Producer =
+ "LLVM Version " STR(LLVM_VERSION_MAJOR) "." STR(LLVM_VERSION_MINOR);
+ }
+
+ Builder.createCompileUnit(dwarf::DW_LANG_C99, Filename, Directory, Producer,
+ IsOptimized, Flags, RuntimeVersion);
+ CUNode = Builder.getCU();
+
+ if (CUToReplace)
+ CUToReplace->replaceAllUsesWith(const_cast<MDNode *>(CUNode));
+
+ DICompileUnit CU(CUNode);
+ FileNode = Builder.createFile(Filename, Directory);
+ LexicalBlockFileNode = Builder.createLexicalBlockFile(CU, DIFile(FileNode));
+ }
+
+ /// Returns the MDNode* that represents the DI scope to associate with I
+ MDNode *findScope(const Instruction *I) {
+ const Function *F = I->getParent()->getParent();
+ if (MDNode *ret = findDISubprogram(F))
+ return ret;
+
+ DEBUG(dbgs() << "WARNING: Using fallback lexical block file scope "
+ << LexicalBlockFileNode << " as scope for instruction " << I
+ << "\n");
+ return LexicalBlockFileNode;
+ }
+
+ /// Returns the MDNode* that is the descriptor for F
+ MDNode *findDISubprogram(const Function *F) {
+ typedef ValueMap<const Function *, MDNode *>::const_iterator FuncNodeIter;
+ FuncNodeIter i = SubprogramDescriptors.find(F);
+ if (i != SubprogramDescriptors.end())
+ return i->second;
+
+ DEBUG(dbgs() << "searching for DI scope node for Function " << F
+ << " in a list of " << Finder.subprogram_count()
+ << " subprogram nodes"
+ << "\n");
+
for (DebugInfoFinder::iterator i = Finder.subprogram_begin(),
e = Finder.subprogram_end();
i != e; ++i) {
DISubprogram S(*i);
- if (S.getFunction() == F)
+ if (S.getFunction() == F) {
+ DEBUG(dbgs() << "Found DISubprogram " << *i << " for function "
+ << S.getFunction() << "\n");
return *i;
+ }
}
- // cannot find F -- likely means there is no debug information
+ DEBUG(dbgs() << "unable to find DISubprogram node for function "
+ << F->getName().str() << "\n");
return 0;
}
+ /// Sets Line to the line number on which V appears and returns true. If a
+ /// line location for V is not found, returns false.
+ bool findLine(const Value *V, unsigned &Line) {
+ if (LineTable.getLine(V, Line))
+ return true;
+
+ if (VMap) {
+ Value *mapped = VMap->lookup(V);
+ if (mapped && LineTable.getLine(mapped, Line))
+ return true;
+ }
+ return false;
+ }
+
+ std::string getTypeName(Type *T) {
+ std::string TypeName;
+ raw_string_ostream TypeStream(TypeName);
+ T->print(TypeStream);
+ TypeStream.flush();
+ return TypeName;
+ }
+
+ /// Returns the MDNode that represents type T if it is already created, or 0
+ /// if it is not.
+ MDNode *getType(const Type *T) {
+ typedef DenseMap<const Type *, MDNode *>::const_iterator TypeNodeIter;
+ TypeNodeIter i = TypeDescriptors.find(T);
+ if (i != TypeDescriptors.end())
+ return i->second;
+ return 0;
+ }
+
+ /// Returns a DebugInfo type from an LLVM type T.
+ DIDerivedType getOrCreateType(Type *T) {
+ MDNode *N = getType(T);
+ if (N)
+ return DIDerivedType(N);
+ else if (T->isVoidTy())
+ return DIDerivedType(0);
+ else if (T->isStructTy()) {
+ N = Builder.createStructType(
+ DIScope(LexicalBlockFileNode), T->getStructName(), DIFile(FileNode),
+ 0, Layout.getTypeSizeInBits(T), Layout.getABITypeAlignment(T), 0,
+ DIType(0), DIArray(0)); // filled in later
+
+ // N is added to the map (early) so that element search below can find it,
+ // so as to avoid infinite recursion for structs that contain pointers to
+ // their own type.
+ TypeDescriptors[T] = N;
+ DICompositeType StructDescriptor(N);
+
+ SmallVector<Value *, 4> Elements;
+ for (unsigned i = 0; i < T->getStructNumElements(); ++i)
+ Elements.push_back(getOrCreateType(T->getStructElementType(i)));
+
+ // set struct elements
+ StructDescriptor.setTypeArray(Builder.getOrCreateArray(Elements));
+ } else if (T->isPointerTy()) {
+ Type *PointeeTy = T->getPointerElementType();
+ if (!(N = getType(PointeeTy)))
+ N = Builder.createPointerType(
+ getOrCreateType(PointeeTy), Layout.getPointerSizeInBits(),
+ Layout.getPrefTypeAlignment(T), getTypeName(T));
+ } else if (T->isArrayTy()) {
+ SmallVector<Value *, 1> Subrange;
+ Subrange.push_back(
+ Builder.getOrCreateSubrange(0, T->getArrayNumElements() - 1));
+
+ N = Builder.createArrayType(Layout.getTypeSizeInBits(T),
+ Layout.getPrefTypeAlignment(T),
+ getOrCreateType(T->getArrayElementType()),
+ Builder.getOrCreateArray(Subrange));
+ } else {
+ int encoding = llvm::dwarf::DW_ATE_signed;
+ if (T->isIntegerTy())
+ encoding = llvm::dwarf::DW_ATE_unsigned;
+ else if (T->isFloatingPointTy())
+ encoding = llvm::dwarf::DW_ATE_float;
+
+ N = Builder.createBasicType(getTypeName(T), T->getPrimitiveSizeInBits(),
+ 0, encoding);
+ }
+ TypeDescriptors[T] = N;
+ return DIDerivedType(N);
+ }
+
+ /// Returns a DebugInfo type that represents a function signature for Func.
+ DICompositeType createFunctionSignature(const Function *Func) {
+ SmallVector<Value *, 4> Params;
+ DIDerivedType ReturnType(getOrCreateType(Func->getReturnType()));
+ Params.push_back(ReturnType);
+
+ const Function::ArgumentListType &Args(Func->getArgumentList());
+ for (Function::ArgumentListType::const_iterator i = Args.begin(),
+ e = Args.end();
+ i != e; ++i) {
+ Type *T(i->getType());
+ Params.push_back(getOrCreateType(T));
+ }
+
+ DIArray ParamArray = Builder.getOrCreateArray(Params);
+ return Builder.createSubroutineType(DIFile(FileNode), ParamArray);
+ }
+
+ /// Associates Instruction I with debug location Loc.
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;
+/// Sets Filename/Directory from the Module identifier and returns true, or
+/// false if source information is not present.
+bool getSourceInfoFromModule(const Module &M, std::string &Directory,
+ std::string &Filename) {
+ std::string PathStr(M.getModuleIdentifier());
+ if (PathStr.length() == 0 || PathStr == "<stdin>")
+ return false;
- /// Flags to control the verbosity of the generated IR file
- bool hideDebugIntrinsics;
- bool hideDebugMetadata;
+ Filename = sys::path::filename(PathStr);
+ SmallVector<char, 16> Path(PathStr.begin(), PathStr.end());
+ sys::path::remove_filename(Path);
+ Directory = StringRef(Path.data(), Path.size());
+ return true;
+}
-public:
- static char ID;
+// Sets Filename/Directory from debug information in M and returns true, or
+// false if no debug information available, or cannot be parsed.
+bool getSourceInfoFromDI(const Module &M, std::string &Directory,
+ std::string &Filename) {
+ NamedMDNode *CUNode = M.getNamedMetadata("llvm.dbg.cu");
+ if (!CUNode || CUNode->getNumOperands() == 0)
+ return false;
+
+ DICompileUnit CU(CUNode->getOperand(0));
+ if (!CU.Verify())
+ return false;
+
+ Filename = CU.getFilename();
+ Directory = CU.getDirectory();
+ return true;
+}
- const char *getPassName() const { return "DebugIR"; }
+} // anonymous namespace
- // FIXME: figure out if we are compiling something that already exists on disk
- // in text IR form, in which case we can omit outputting a new IR file, or if
- // we're building something from memory where we actually need to emit a new
- // IR file for the debugger.
+namespace llvm {
- /// Output a file with the same base name as the original, but with the
- /// postfix "-debug-ll" appended.
- DebugIR()
- : ModulePass(ID), Postfix("-debug-ll"), hideDebugIntrinsics(true),
- hideDebugMetadata(true) {}
+bool DebugIR::getSourceInfo(const Module &M) {
+ ParsedPath = getSourceInfoFromDI(M, Directory, Filename) ||
+ getSourceInfoFromModule(M, Directory, Filename);
+ return ParsedPath;
+}
- /// 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, bool hideDebugIntrinsics, bool hideDebugMetadata)
- : ModulePass(ID), Postfix(postfix),
- hideDebugIntrinsics(hideDebugIntrinsics),
- hideDebugMetadata(hideDebugMetadata) {}
+bool DebugIR::updateExtension(StringRef NewExtension) {
+ size_t dot = Filename.find_last_of(".");
+ if (dot == std::string::npos)
+ return false;
-private:
- // Modify the filename embedded in the Compilation-Unit debug information of M
- bool replaceFilename(Module &M, const DebugInfoFinder &Finder) {
- 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();
+ Filename.erase(dot);
+ Filename += NewExtension.str();
+ return true;
+}
- // Replace extension with postfix
- size_t dot = Filename.find_last_of(".");
- if (dot != std::string::npos)
- Filename.erase(dot);
- Filename += Postfix;
+void DebugIR::generateFilename(OwningPtr<int> &fd) {
+ StringRef FileModel("debug-ir-%s%s%s%s.ll");
+ SmallVector<char, 16> PathVec;
+ fd.reset(new int);
+ sys::fs::unique_file(FileModel, *fd, PathVec);
+ StringRef Path(PathVec.data(), PathVec.size());
+ Filename = sys::path::filename(Path);
+ sys::path::remove_filename(PathVec);
+ Directory = StringRef(PathVec.data(), PathVec.size());
+
+ GeneratedPath = true;
+}
- CU.setFilename(Filename, M.getContext());
- changed = true;
- }
- return changed;
- }
+std::string DebugIR::getPath() {
+ SmallVector<char, 16> Path;
+ sys::path::append(Path, Directory, Filename);
+ Path.resize(Filename.size() + Directory.size() + 2);
+ Path[Filename.size() + Directory.size() + 1] = '\0';
+ return std::string(Path.data());
+}
- /// Replace existing line number metadata with line numbers that correspond
- /// with the IR file that is seen by the debugger.
- void addLineNumberMetadata(Module *M, const ValueToLineMap &VLM,
- const ValueToValueMapTy &VMap,
- const DebugInfoFinder &Finder) {
- LineNumberReplacer Replacer(VLM, Finder, VMap);
- Replacer.visit(M);
+void DebugIR::writeDebugBitcode(const Module *M, int *fd) {
+ OwningPtr<raw_fd_ostream> Out;
+ std::string error;
+
+ if (!fd) {
+ std::string Path = getPath();
+ Out.reset(new raw_fd_ostream(Path.c_str(), error));
+ DEBUG(dbgs() << "WRITING debug bitcode from Module " << M << " to file "
+ << Path << "\n");
+ } else {
+ DEBUG(dbgs() << "WRITING debug bitcode from Module " << M << " to fd "
+ << *fd << "\n");
+ Out.reset(new raw_fd_ostream(*fd, true));
}
- void writeDebugBitcode(Module *M) {
- std::string error;
- tool_output_file OutFile(Filename.c_str(), error);
- OutFile.keep();
- formatted_raw_ostream OS;
- OS.setStream(OutFile.os());
- M->print(OS, 0);
- }
+ M->print(*Out, 0);
+ Out->close();
+ sync();
+}
- void removeDebugIntrinsics(Module *M) {
- DebugIntrinsicsRemover Remover;
- Remover.visit(M);
- }
+void DebugIR::createDebugInfo(Module &M, OwningPtr<Module> &DisplayM) {
+ if (M.getFunctionList().size() == 0)
+ // no functions -- no debug info needed
+ return;
- void removeDebugMetadata(Module *M) {
- DebugMetadataRemover Remover;
- Remover.run(M);
- }
+ OwningPtr<ValueToValueMapTy> VMap;
+
+ if (WriteSourceToDisk && (HideDebugIntrinsics || HideDebugMetadata)) {
+ VMap.reset(new ValueToValueMapTy);
+ DisplayM.reset(CloneModule(&M, *VMap));
- void updateAndWriteDebugIRFile(Module *M, const DebugInfoFinder &Finder) {
- // The module we output in text form for a debugger to open is stripped of
- // 'extras' like debug intrinsics that end up in DWARF anyways and just
- // clutter the debug experience.
+ if (HideDebugIntrinsics)
+ DebugIntrinsicsRemover::process(*DisplayM);
- ValueToValueMapTy VMap;
- Module *DebuggerM = CloneModule(M, VMap);
+ if (HideDebugMetadata)
+ DebugMetadataRemover::process(*DisplayM);
+ }
- if (hideDebugIntrinsics)
- removeDebugIntrinsics(DebuggerM);
+ DIUpdater R(M, Filename, Directory, DisplayM.get(), VMap.get());
+}
- if (hideDebugMetadata)
- removeDebugMetadata(DebuggerM);
+bool DebugIR::isMissingPath() { return Filename.empty() || Directory.empty(); }
- // FIXME: remove all debug metadata from M once we support generating DWARF
- // subprogram attributes.
+bool DebugIR::runOnModule(Module &M) {
+ OwningPtr<int> fd;
- ValueToLineMap LineTable(DebuggerM);
- addLineNumberMetadata(M, LineTable, VMap, Finder);
- writeDebugBitcode(DebuggerM);
+ if (isMissingPath() && !getSourceInfo(M)) {
+ if (!WriteSourceToDisk)
+ report_fatal_error("DebugIR unable to determine file name in input. "
+ "Ensure Module contains an identifier, a valid "
+ "DICompileUnit, or construct DebugIR with "
+ "non-empty Filename/Directory parameters.");
+ else
+ generateFilename(fd);
}
- bool runOnModule(Module &M) {
- // Stores existing debug info needed when creating new line number entries.
- DebugInfoFinder Finder;
- Finder.processModule(M);
+ if (!GeneratedPath && WriteSourceToDisk)
+ updateExtension(".debug-ll");
+
+ // Clear line numbers. Keep debug info (if any) if we were able to read the
+ // file name from the DICompileUnit descriptor.
+ DebugMetadataRemover::process(M, !ParsedPath);
- bool changed = replaceFilename(M, Finder);
- if (changed)
- updateAndWriteDebugIRFile(&M, Finder);
- return changed;
+ OwningPtr<Module> DisplayM;
+ createDebugInfo(M, DisplayM);
+ if (WriteSourceToDisk) {
+ Module *OutputM = DisplayM.get() ? DisplayM.get() : &M;
+ writeDebugBitcode(OutputM, fd.get());
}
-};
-} // anonymous namespace
+ DEBUG(M.dump());
+ return true;
+}
+
+bool DebugIR::runOnModule(Module &M, std::string &Path) {
+ bool result = runOnModule(M);
+ Path = getPath();
+ return result;
+}
+
+} // llvm namespace
char DebugIR::ID = 0;
INITIALIZE_PASS(DebugIR, "debug-ir", "Enable debugging IR", false, false)
-ModulePass *llvm::createDebugIRPass(StringRef FilenamePostfix,
- bool hideDebugIntrinsics,
- bool hideDebugMetadata) {
- return new DebugIR(FilenamePostfix, hideDebugIntrinsics, hideDebugMetadata);
+ModulePass *llvm::createDebugIRPass(bool HideDebugIntrinsics,
+ bool HideDebugMetadata, StringRef Directory,
+ StringRef Filename) {
+ return new DebugIR(HideDebugIntrinsics, HideDebugMetadata, Directory,
+ Filename);
}
+
+ModulePass *llvm::createDebugIRPass() { return new DebugIR(); }