mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-28 19:31:58 +00:00
f8c243abba
- move AsmWriter.h from public headers into lib - marked all AssemblyWriter functions as non-virtual; no need to override them - DebugIR now "plugs into" AssemblyWriter with an AssemblyAnnotationWriter helper - exposed flags to control hiding of a) debug metadata b) debug intrinsic calls C/R: Paul Redmond git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@182617 91177308-0d34-0410-b5e6-96231b3b80d8
311 lines
10 KiB
C++
311 lines
10 KiB
C++
//===--- 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/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/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"
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
/// Builds a map of Value* to line numbers on which the Value appears in a
|
|
/// textual representation of the IR by plugging into the AssemblyWriter by
|
|
/// masquerading as an AssemblyAnnotationWriter.
|
|
class ValueToLineMap : public AssemblyAnnotationWriter {
|
|
ValueMap<const Value *, unsigned int> Lines;
|
|
typedef ValueMap<const Value *, unsigned int>::const_iterator LineIter;
|
|
|
|
public:
|
|
|
|
/// Prints Module to a null buffer in order to build the map of Value pointers
|
|
/// to line numbers.
|
|
ValueToLineMap(Module *M) {
|
|
raw_null_ostream ThrowAway;
|
|
M->print(ThrowAway, this);
|
|
}
|
|
|
|
// 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));
|
|
}
|
|
|
|
/// If V appears on a line in the textual IR representation, sets Line to the
|
|
/// line number and returns true, otherwise returns false.
|
|
bool getLine(const Value *V, unsigned int &Line) const {
|
|
LineIter i = Lines.find(V);
|
|
if (i != Lines.end()) {
|
|
Line = i->second;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/// Removes debug intrisncs like llvm.dbg.declare and llvm.dbg.value.
|
|
class DebugIntrinsicsRemover : public InstVisitor<DebugIntrinsicsRemover> {
|
|
void remove(Instruction &I) { I.eraseFromParent(); }
|
|
|
|
public:
|
|
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.
|
|
class DebugMetadataRemover : public InstVisitor<DebugMetadataRemover> {
|
|
public:
|
|
void visitInstruction(Instruction &I) {
|
|
if (I.getMetadata(LLVMContext::MD_dbg))
|
|
I.setMetadata(LLVMContext::MD_dbg, 0);
|
|
}
|
|
|
|
void run(Module *M) {
|
|
// 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);
|
|
}
|
|
};
|
|
|
|
/// 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;
|
|
|
|
/// Table of cloned values
|
|
const ValueToValueMapTy &VMap;
|
|
|
|
/// Directory of debug metadata
|
|
const DebugInfoFinder &Finder;
|
|
|
|
public:
|
|
LineNumberReplacer(const ValueToLineMap &VLM, const DebugInfoFinder &Finder,
|
|
const ValueToValueMapTy &VMap)
|
|
: LineTable(VLM), VMap(VMap), Finder(Finder) {}
|
|
|
|
void visitInstruction(Instruction &I) {
|
|
DebugLoc Loc(I.getDebugLoc());
|
|
|
|
unsigned Col = 0; // FIXME: support columns
|
|
unsigned Line;
|
|
if (!LineTable.getLine(VMap.lookup(&I), 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.
|
|
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)
|
|
return;
|
|
|
|
addDebugLocation(const_cast<Instruction &>(I), NewLoc);
|
|
}
|
|
|
|
private:
|
|
|
|
/// 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;
|
|
}
|
|
|
|
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;
|
|
|
|
/// Flags to control the verbosity of the generated IR file
|
|
bool hideDebugIntrinsics;
|
|
bool hideDebugMetadata;
|
|
|
|
public:
|
|
static char ID;
|
|
|
|
const char *getPassName() const { return "DebugIR"; }
|
|
|
|
// 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.
|
|
|
|
/// 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) {}
|
|
|
|
/// 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) {}
|
|
|
|
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();
|
|
|
|
// 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;
|
|
}
|
|
|
|
/// 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 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);
|
|
}
|
|
|
|
void removeDebugIntrinsics(Module *M) {
|
|
DebugIntrinsicsRemover Remover;
|
|
Remover.visit(M);
|
|
}
|
|
|
|
void removeDebugMetadata(Module *M) {
|
|
DebugMetadataRemover Remover;
|
|
Remover.run(M);
|
|
}
|
|
|
|
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.
|
|
|
|
ValueToValueMapTy VMap;
|
|
Module *DebuggerM = CloneModule(M, VMap);
|
|
|
|
if (hideDebugIntrinsics)
|
|
removeDebugIntrinsics(DebuggerM);
|
|
|
|
if (hideDebugMetadata)
|
|
removeDebugMetadata(DebuggerM);
|
|
|
|
// FIXME: remove all debug metadata from M once we support generating DWARF
|
|
// subprogram attributes.
|
|
|
|
ValueToLineMap LineTable(DebuggerM);
|
|
addLineNumberMetadata(M, LineTable, VMap, Finder);
|
|
writeDebugBitcode(DebuggerM);
|
|
}
|
|
|
|
bool runOnModule(Module &M) {
|
|
// Stores existing debug info needed when creating new line number entries.
|
|
DebugInfoFinder Finder;
|
|
Finder.processModule(M);
|
|
|
|
bool changed = replaceFilename(M, Finder);
|
|
if (changed)
|
|
updateAndWriteDebugIRFile(&M, Finder);
|
|
return changed;
|
|
}
|
|
};
|
|
|
|
} // anonymous 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);
|
|
}
|