From aadaf9f230a187b0bc87a18ddff78600b1622175 Mon Sep 17 00:00:00 2001 From: Daniel Malea Date: Fri, 28 Jun 2013 19:05:23 +0000 Subject: [PATCH] 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 --- include/llvm/Transforms/Instrumentation.h | 35 +- lib/Transforms/Instrumentation/DebugIR.cpp | 616 ++++++++++++++++++++++------- 2 files changed, 493 insertions(+), 158 deletions(-) diff --git a/include/llvm/Transforms/Instrumentation.h b/include/llvm/Transforms/Instrumentation.h index f2027ce82a3..de397af5bd0 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 020804ff5f7..2f602be78a0 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 +#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 +#include + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) + using namespace llvm; namespace { @@ -53,11 +53,16 @@ class ValueToLineMap : public AssemblyAnnotationWriter { ValueMap Lines; typedef ValueMap::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 { 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 { + 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 { - /// 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 { + /// 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 SubprogramDescriptors; + DenseMap 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(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(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_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_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 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 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 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 == "") + return false; - /// Flags to control the verbosity of the generated IR file - bool hideDebugIntrinsics; - bool hideDebugMetadata; + Filename = sys::path::filename(PathStr); + SmallVector 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 &fd) { + StringRef FileModel("debug-ir-%s%s%s%s.ll"); + SmallVector 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 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 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 &DisplayM) { + if (M.getFunctionList().size() == 0) + // no functions -- no debug info needed + return; - void removeDebugMetadata(Module *M) { - DebugMetadataRemover Remover; - Remover.run(M); - } + OwningPtr 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 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 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(); } -- 2.11.0