blob: 777b8d9ae51c9262f5519cdd78a5022040009d23 [file] [log] [blame]
//===-- go-llvm-dibuildhelper.cpp - implementation of DIBuildHelper -------===//
//
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
//===----------------------------------------------------------------------===//
//
// Methods for DIBuildHelper class.
//
//===----------------------------------------------------------------------===//
#include "go-llvm-dibuildhelper.h"
#include "go-llvm-typemanager.h"
#include "go-llvm-bexpression.h"
#include "go-llvm-bstatement.h"
#include "go-llvm-bfunction.h"
#include "go-llvm-bvariable.h"
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/FileSystem.h"
DIBuildHelper::DIBuildHelper(llvm::Module *module,
TypeManager *typemanager,
Llvm_linemap *linemap)
: module_(module), typemanager_(typemanager), linemap_(linemap), moduleScope_(nullptr),
dibuilder_(new llvm::DIBuilder(*module)), topblock_(nullptr),
entryBlock_(nullptr), known_locations_(0)
{
}
void DIBuildHelper::createCompileUnitIfNeeded()
{
if (moduleScope_)
return;
// Create compile unit
llvm::SmallString<256> currentDir;
llvm::sys::fs::current_path(currentDir);
std::string filestr = applyDebugPrefix(linemap_->get_initial_file());
auto primaryFile = dibuilder_->createFile(filestr, currentDir);
bool isOptimized = true;
std::string compileFlags; // FIXME
unsigned runtimeVersion = 0; // not sure what would be for Go
moduleScope_ =
dibuilder_->createCompileUnit(llvm::dwarf::DW_LANG_Go, primaryFile,
"llvm-goc", isOptimized,
compileFlags, runtimeVersion);
pushDIScope(moduleScope_);
module_->addModuleFlag(llvm::Module::Warning, "Debug Info Version",
llvm::DEBUG_METADATA_VERSION);
module_->addModuleFlag(llvm::Module::Warning, "Dwarf Version", 4);
}
void DIBuildHelper::finalize()
{
createCompileUnitIfNeeded();
// Emit any globals we've collected along the way.
for (auto it : globalsToProcess_) {
Bvariable *v = it.first;
bool isExported = it.second;
llvm::DIFile *vfile = diFileFromLocation(v->location());
unsigned vline = linemap()->location_line(v->location());
llvm::DIType *vdit = typemanager()->buildDIType(v->btype(), *this);
bool isLocalToUnit = !isExported;
dibuilder().createGlobalVariableExpression(moduleScope_,
v->name(), v->name(),
vfile, vline, vdit,
isLocalToUnit);
}
dibuilder_->finalize();
}
void DIBuildHelper::processGlobal(Bvariable *v, bool isExported)
{
globalsToProcess_.push_back(std::make_pair(v, isExported));
}
void DIBuildHelper::beginFunction(Bfunction *function,
Bnode *topnode,
llvm::BasicBlock *entryBlock)
{
createCompileUnitIfNeeded();
assert(entryBlock_ == nullptr);
assert(entryBlock != nullptr);
entryBlock_ = entryBlock;
assert(topblock_ == nullptr);
assert(topnode->castToBblock());
topblock_ = topnode->castToBblock();
known_locations_ = 0;
// Create proper DIType for function
llvm::DIType *dit =
typemanager()->buildDIType(function->fcnType(), *this);
llvm::DISubroutineType *dst =
llvm::cast<llvm::DISubroutineType>(dit);
// Now the function entry itself
unsigned fcnLine = linemap()->location_line(function->location());
bool isLocalToUnit = !function->function()->hasExternalLinkage();
unsigned scopeLine = fcnLine; // FIXME -- determine correct value here
llvm::DIFile *difile = diFileFromLocation(function->location());
llvm::DINode::DIFlags flags = llvm::DINode::FlagZero;
llvm::DISubprogram::DISPFlags spflags = llvm::DISubprogram::SPFlagDefinition;
if (isLocalToUnit)
spflags |= llvm::DISubprogram::SPFlagLocalToUnit;
auto difunc =
dibuilder().createFunction(moduleScope(), function->name(),
isLocalToUnit ? "" : function->asmName(),
difile, fcnLine, dst, scopeLine, flags,
spflags);
pushDIScope(difunc);
}
void DIBuildHelper::processVarsInBLock(const std::vector<Bvariable*> &vars,
llvm::DIScope *scope)
{
for (auto &v : vars) {
if (v->isTemporary())
continue;
if (declared_.find(v) != declared_.end())
continue;
declared_.insert(v);
llvm::DIFile *vfile = diFileFromLocation(v->location());
llvm::DIType *vdit =
typemanager()->buildDIType(v->btype(), *this);
unsigned vline = linemap()->location_line(v->location());
llvm::DILocalVariable *dilv =
dibuilder().createAutoVariable(scope, v->name(), vfile, vline, vdit);
insertVarDecl(v, dilv);
}
}
void DIBuildHelper::insertVarDecl(Bvariable *var,
llvm::DILocalVariable *dilv)
{
llvm::DIExpression *expr = dibuilder().createExpression();
llvm::DILocation *vloc = debugLocFromLocation(var->location());
// Don't emit declaration for dead variable.
if(var->initializer() && !var->initializerInstruction()->getParent())
return;
// Create the declare instruction, giving it an initial position at
// the end of the entry block (the insertDeclare call below doesn't
// allow a NULL insert location, so we pick end-of-block arbitrarily).
assert(entryBlock_);
llvm::Instruction *decl =
dibuilder().insertDeclare(var->value(), dilv, expr, vloc, entryBlock_);
decl->removeFromParent();
// Extract the decl from the end of the entry block and reposition
// it according to the var properties.
llvm::Instruction *insertionPoint = nullptr;
if (var->initializer()) {
assert(var->initializerInstruction()->getParent());
insertionPoint = var->initializerInstruction();
} else if (var->flavor() == ParamVar) {
// parameters passing by reference may have no initializers.
// declare them at function entry.
entryBlock_->getInstList().push_front(decl);
return;
} else {
// locals with no initializer should only be zero-sized vars.
// make them available immediately after their alloca.
assert(typemanager()->typeSize(var->btype()) == 0);
llvm::Instruction *alloca = llvm::cast<llvm::Instruction>(var->value());
insertionPoint = alloca;
}
assert(! llvm::isa<llvm::BranchInst>(insertionPoint));
assert(insertionPoint != insertionPoint->getParent()->getTerminator());
decl->insertAfter(insertionPoint);
}
void DIBuildHelper::endFunction(Bfunction *function)
{
cleanFileScope();
llvm::DISubprogram *fscope = llvm::cast<llvm::DISubprogram>(currentDIScope());
// Create debug meta-data for parameter variables.
unsigned argIdx = 0;
for (auto &v : function->getParameterVars()) {
llvm::DIFile *vfile = diFileFromLocation(v->location());
llvm::DIType *vdit =
typemanager()->buildDIType(v->btype(), *this);
unsigned vline = linemap()->location_line(v->location());
llvm::DILocalVariable *dilv =
dibuilder().createParameterVariable(fscope, v->name(), ++argIdx,
vfile, vline, vdit);
insertVarDecl(v, dilv);
}
// Create debug meta-data for local variables. We wait to do this
// here so as to insure that the initializer instructions for the
// variables have been assigned to a basic block. Note that
// block-scoped locals (as opposed to function-scoped locals) will
// already have been processed at this point.
processVarsInBLock(function->getFunctionLocalVars(), fscope);
// If a given function has no debug locations at all, then don't
// try to mark it as having debug info (without doing this we can
// wind up having functions flagged as problematic by the verifier).
if (known_locations_)
function->function()->setSubprogram(fscope);
// Done with this scope
cleanFileScope();
popDIScope();
assert(diScopeStack_.size() == 1);
// Clean up
entryBlock_ = nullptr;
topblock_ = nullptr;
known_locations_ = 0;
declared_.clear();
}
// Front end tends to declare more blocks than strictly required for
// debug generation purposes, so here we examine each block to see
// whether it makes sense to generate a DWARF lexical scope record for
// it. In particular, we look for blocks containing no user-visible
// variables (these can be omitted).
//
// Return TRUE if this is an interesting block (needs DWARF scope) or
// FALSE otherwise.
bool DIBuildHelper::interestingBlock(Bblock *block)
{
assert(block);
bool foundInteresting = false;
for (auto &v : block->vars()) {
if (! v->isTemporary()) {
foundInteresting = true;
break;
}
}
return foundInteresting;
}
void DIBuildHelper::beginLexicalBlock(Bblock *block)
{
if (! interestingBlock(block) || block == topblock_)
return;
// Register block with DIBuilder
Location bloc = block->location();
llvm::DIFile *file = diFileFromLocation(block->location());
llvm::DILexicalBlock *dilb =
dibuilder().createLexicalBlock(currentDIScope(), file,
linemap()->location_line(bloc),
linemap()->location_column(bloc));
pushDIScope(dilb);
}
void DIBuildHelper::endLexicalBlock(Bblock *block)
{
if (! interestingBlock(block))
return;
cleanFileScope();
// In the case of the top block, we still want to process any local
// variables it contains, but we'll want to insure that they are
// parented by the function itself.
//
llvm::DIScope *scope =
(block == topblock_ ? currentDIScope() : popDIScope());
processVarsInBLock(block->vars(), scope);
}
void DIBuildHelper::processExprInst(Bstatement *stmt,
Bexpression *expr,
llvm::Instruction *inst)
{
Location loc = expr->location();
if (linemap()->is_unknown(loc)) {
Location sloc = stmt->location();
if (!linemap()->is_unknown(sloc))
loc = sloc;
}
if (! linemap()->is_unknown(loc)) {
known_locations_ += 1;
inst->setDebugLoc(debugLocFromLocation(loc));
}
}
void DIBuildHelper::addDebugPrefix(std::pair<llvm::StringRef, llvm::StringRef> pref)
{
std::string from(pref.first);
std::string to(pref.second);
debugPrefixMap_[from] = to;
}
std::string DIBuildHelper::applyDebugPrefix(llvm::StringRef path) {
for (const auto &remap : debugPrefixMap_)
if (path.startswith(remap.first))
return (llvm::Twine(remap.second) +
path.substr(remap.first.size())).str();
return path.str();
}
llvm::DIFile *DIBuildHelper::diFileFromLocation(Location location)
{
std::string locfile = applyDebugPrefix(linemap()->location_file(location));
llvm::StringRef locdir = llvm::sys::path::parent_path(locfile);
llvm::StringRef locfilename = llvm::sys::path::filename(locfile);
if (linemap()->is_predeclared(location))
locdir = "";
return dibuilder().createFile(locfilename, locdir);
}
llvm::DebugLoc DIBuildHelper::debugLocFromLocation(Location loc)
{
llvm::LLVMContext &context = typemanager()->context();
// In the (somewhat unusual) case of a file/line directive, create a new
// pseudo-scope to capture the fact that the file has changed. Pop off
// any previously created file scope prior to doing this.
llvm::DIFile *curFile = currentDIScope()->getFile();
llvm::DIFile *locFile = diFileFromLocation(loc);
if (curFile != locFile) {
cleanFileScope();
llvm::DILexicalBlockFile *dilbf =
dibuilder().createLexicalBlockFile(currentDIScope(), locFile);
pushDIScope(dilbf);
}
return llvm::DILocation::get(context, linemap()->location_line(loc),
linemap()->location_column(loc),
currentDIScope());
}
llvm::DIScope *DIBuildHelper::currentDIScope()
{
assert(diScopeStack_.size());
return diScopeStack_.back();
}
llvm::DIScope *DIBuildHelper::popDIScope()
{
assert(diScopeStack_.size());
llvm::DIScope *popped = diScopeStack_.back();
diScopeStack_.pop_back();
return popped;
}
void DIBuildHelper::pushDIScope(llvm::DIScope *scope)
{
assert(scope);
diScopeStack_.push_back(scope);
}
void DIBuildHelper::cleanFileScope()
{
assert(diScopeStack_.size());
if (llvm::dyn_cast<llvm::DILexicalBlockFile>(diScopeStack_.back()) != nullptr)
diScopeStack_.pop_back();
}