forked from llvm-mirror/llvm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add DebugIR pass -- emits IR file and replace source lines with IR li…
…nes 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
- Loading branch information
Daniel Malea
committed
May 8, 2013
1 parent
d0fef32
commit 13ace66
Showing
7 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters