gollvm: emit debug meta-data for global variables
Fill in debug meta-data for global variables. This required
reworking the DIBuildHelper class, which previously was active
only during control flow generation for a function, and is now
a module-scope entity.
Change-Id: I15d82e0fd862ff976c385eb09b53bfc42347ca8d
Reviewed-on: https://go-review.googlesource.com/46419
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/llvm-gofrontend/go-llvm-dibuildhelper.cpp b/llvm-gofrontend/go-llvm-dibuildhelper.cpp
index 5632c14..8d5ae17 100644
--- a/llvm-gofrontend/go-llvm-dibuildhelper.cpp
+++ b/llvm-gofrontend/go-llvm-dibuildhelper.cpp
@@ -24,23 +24,70 @@
#include "llvm/IR/DebugLoc.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
+#include "llvm/Support/FileSystem.h"
-DIBuildHelper::DIBuildHelper(Bnode *topnode,
+DIBuildHelper::DIBuildHelper(llvm::Module *module,
TypeManager *typemanager,
- Llvm_linemap *linemap,
- llvm::DIBuilder &builder,
- llvm::DIScope *moduleScope,
- llvm::BasicBlock *entryBlock)
- : typemanager_(typemanager), linemap_(linemap),
- dibuilder_(builder), moduleScope_(moduleScope),
- topblock_(topnode->castToBblock()),
- entryBlock_(entryBlock), known_locations_(0)
+ Llvm_linemap *linemap)
+ : module_(module), typemanager_(typemanager), linemap_(linemap), moduleScope_(nullptr),
+ dibuilder_(new llvm::DIBuilder(*module)), topblock_(nullptr),
+ entryBlock_(nullptr), known_locations_(0)
{
- pushDIScope(moduleScope);
}
-void DIBuildHelper::beginFunction(llvm::DIScope *scope, Bfunction *function)
+void DIBuildHelper::createCompileUnitIfNeeded()
{
+ if (moduleScope_)
+ return;
+
+ // Create compile unit
+ llvm::SmallString<256> currentDir;
+ llvm::sys::fs::current_path(currentDir);
+ auto primaryFile =
+ dibuilder_->createFile(linemap_->get_initial_file(), 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-goparse", isOptimized,
+ compileFlags, runtimeVersion);
+ pushDIScope(moduleScope_);
+}
+
+void DIBuildHelper::finalize()
+{
+ dibuilder_->finalize();
+}
+
+void DIBuildHelper::processGlobal(Bvariable *v, bool isExported)
+{
+ createCompileUnitIfNeeded();
+
+ 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);
+}
+
+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
@@ -56,11 +103,10 @@
unsigned scopeLine = fcnLine; // FIXME -- determine correct value here
llvm::DIFile *difile = diFileFromLocation(function->location());
auto difunc =
- dibuilder().createFunction(scope, function->name(), function->asmName(),
+ dibuilder().createFunction(moduleScope(), function->name(), function->asmName(),
difile, fcnLine, dst, isLocalToUnit,
isDefinition, scopeLine);
pushDIScope(difunc);
-
}
void DIBuildHelper::processVarsInBLock(const std::vector<Bvariable*> &vars,
@@ -139,6 +185,13 @@
// Done with this scope
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
@@ -210,12 +263,6 @@
llvm::StringRef locfilename = llvm::sys::path::filename(locfile);
if (linemap()->is_predeclared(location))
locdir = "";
-#if 0
- llvm::SmallString<256> currentDir;
- llvm::sys::fs::current_path(currentDir);
- if (locdir == "" || locdir == ".")
- locdir = currentDir;
-#endif
return dibuilder().createFile(locfilename, locdir);
}
diff --git a/llvm-gofrontend/go-llvm-dibuildhelper.h b/llvm-gofrontend/go-llvm-dibuildhelper.h
index 14484ce..776797f 100644
--- a/llvm-gofrontend/go-llvm-dibuildhelper.h
+++ b/llvm-gofrontend/go-llvm-dibuildhelper.h
@@ -29,6 +29,7 @@
class DIType;
class DebugLoc;
class Instruction;
+class Module;
class Type;
}
@@ -47,19 +48,20 @@
class DIBuildHelper {
public:
- DIBuildHelper(Bnode *topNode,
+ DIBuildHelper(llvm::Module *module,
TypeManager *typemanager,
- Llvm_linemap *linemap,
- llvm::DIBuilder &builder,
- llvm::DIScope *moduleScope,
- llvm::BasicBlock *entryBlock);
+ Llvm_linemap *linemap);
- void beginFunction(llvm::DIScope *scope, Bfunction *function);
+ void processGlobal(Bvariable *gvar, bool isExported);
+
+ void beginFunction(Bfunction *function, Bnode *topnode, llvm::BasicBlock *entryBlock);
void endFunction(Bfunction *function);
void beginLexicalBlock(Bblock *block);
void endLexicalBlock(Bblock *block);
+ void finalize();
+
llvm::DIFile *diFileFromLocation(Location location);
void processExprInst(Bexpression *expr, llvm::Instruction *inst);
@@ -75,7 +77,7 @@
void pushDIScope(llvm::DIScope *);
// Various getters
- llvm::DIBuilder &dibuilder() { return dibuilder_; }
+ llvm::DIBuilder &dibuilder() { return *dibuilder_.get(); }
Llvm_linemap *linemap() { return linemap_; }
TypeManager *typemanager() { return typemanager_; }
@@ -88,20 +90,23 @@
}
private:
+ llvm::Module *module_;
TypeManager *typemanager_;
Llvm_linemap *linemap_;
- llvm::DIBuilder &dibuilder_;
llvm::DIScope *moduleScope_;
- Bblock *topblock_;
+ std::unique_ptr<llvm::DIBuilder> dibuilder_;
std::vector<llvm::DIScope*> diScopeStack_;
std::unordered_map<Btype *, llvm::DIType*> typeCache_;
std::unordered_map<llvm::DIType *, llvm::DIType*> typeReplacements_;
+
+ // The following items are specific to the current function we're visiting.
std::unordered_set<Bvariable *> declared_;
+ Bblock *topblock_;
llvm::BasicBlock *entryBlock_;
unsigned known_locations_;
-
private:
+ void createCompileUnitIfNeeded();
llvm::DebugLoc debugLocFromLocation(Location location);
void insertVarDecl(Bvariable *var, llvm::DILocalVariable *dilv);
bool interestingBlock(Bblock *block);
diff --git a/llvm-gofrontend/go-llvm.cpp b/llvm-gofrontend/go-llvm.cpp
index 1603103..d9eb8a8 100644
--- a/llvm-gofrontend/go-llvm.cpp
+++ b/llvm-gofrontend/go-llvm.cpp
@@ -38,7 +38,6 @@
#include "llvm/IR/Type.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
-#include "llvm/Support/FileSystem.h"
Llvm_backend::Llvm_backend(llvm::LLVMContext &context,
llvm::Module *module,
@@ -48,7 +47,6 @@
, module_(module)
, datalayout_(module ? &module->getDataLayout() : nullptr)
, nbuilder_(this)
- , diCompileUnit_(nullptr)
, linemap_(linemap)
, addressSpace_(0)
, traceLevel_(0)
@@ -111,6 +109,9 @@
// Initialize machinery for builtins
builtinTable_->defineAllBuiltins();
+
+ if (createDebugMetaData_)
+ dibuildhelper_.reset(new DIBuildHelper(module_, typeManager(), linemap_));
}
Llvm_backend::~Llvm_backend() {
@@ -120,34 +121,6 @@
delete bfcn;
}
-llvm::DICompileUnit *Llvm_backend::getDICompUnit()
-{
- if (!createDebugMetaData_)
- return nullptr;
-
- if (diCompileUnit_ || !createDebugMetaData_)
- return diCompileUnit_;
-
- // Create debug info builder
- assert(dibuilder_.get() == nullptr);
- dibuilder_.reset(new llvm::DIBuilder(*module_));
-
- // Create compile unit
- llvm::SmallString<256> currentDir;
- llvm::sys::fs::current_path(currentDir);
- auto primaryFile =
- dibuilder_->createFile(linemap_->get_initial_file(), currentDir);
- bool isOptimized = true;
- std::string compileFlags; // FIXME
- unsigned runtimeVersion = 0; // not sure what would be for Go
- diCompileUnit_ =
- dibuilder_->createCompileUnit(llvm::dwarf::DW_LANG_Go, primaryFile,
- "llvm-goparse", isOptimized,
- compileFlags, runtimeVersion);
-
- return diCompileUnit_;
-}
-
TypeManager *Llvm_backend::typeManager() const {
const TypeManager *tm = this;
return const_cast<TypeManager*>(tm);
@@ -707,7 +680,7 @@
Bvariable *rv = makeModuleVar(type, ctag, "", Location(),
MV_Constant, MV_DefaultSection,
MV_NotInComdat, MV_DefaultVisibility,
- MV_NotExternallyInitialized,
+ MV_NotExternallyInitialized, MV_SkipDebug,
llvm::GlobalValue::PrivateLinkage,
conval, 0);
assert(llvm::isa<llvm::GlobalVariable>(rv->value()));
@@ -1134,7 +1107,7 @@
makeModuleVar(makeAuxType(scon->getType()),
"", "", Location(), MV_Constant, MV_DefaultSection,
MV_NotInComdat, MV_DefaultVisibility,
- MV_NotExternallyInitialized,
+ MV_NotExternallyInitialized, MV_SkipDebug,
llvm::GlobalValue::PrivateLinkage, scon, 1);
llvm::Constant *varval = llvm::cast<llvm::Constant>(svar->value());
llvm::Constant *bitcast =
@@ -3016,6 +2989,7 @@
ModVarComdat inComdat,
ModVarVis isHiddenVisibility,
ModVarExtInit isExtInit,
+ ModVarGenDebug genDebug,
llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *initializer,
unsigned alignment)
@@ -3034,8 +3008,6 @@
// variable.
assert(inUniqueSection == MV_DefaultSection);
- // FIXME: add DIGlobalVariable to debug info for this variable
-
llvm::Constant *init =
(isExtInit == MV_ExternallyInitialized ? nullptr :
llvm::Constant::getNullValue(btype->type()));
@@ -3063,6 +3035,10 @@
new Bvariable(btype, location, gname, GlobalVar, addressTaken, glob);
assert(valueVarMap_.find(bv->value()) == valueVarMap_.end());
valueVarMap_[bv->value()] = bv;
+ if (genDebug == MV_GenDebug && dibuildhelper() && !errorCount_) {
+ bool exported = (isHiddenVisibility != MV_HiddenVisibility);
+ dibuildhelper()->processGlobal(bv, exported);
+ }
return bv;
}
@@ -3087,7 +3063,7 @@
Bvariable *gvar =
makeModuleVar(btype, var_name, asm_name, location,
MV_NonConstant, inUniqSec, MV_NotInComdat,
- varVis, extInit, linkage, nullptr);
+ varVis, extInit, MV_GenDebug, linkage, nullptr);
return gvar;
}
@@ -3213,7 +3189,8 @@
Bvariable *gvar =
makeModuleVar(btype, name, asm_name, Location(),
isConst, inUniqSec, inComdat,
- varVis, extInit, linkage, nullptr, alignment);
+ varVis, extInit, MV_SkipDebug, linkage,
+ nullptr, alignment);
return gvar;
}
@@ -3271,7 +3248,7 @@
Bvariable *gvar =
makeModuleVar(btype, name, asm_name, location,
MV_Constant, inUniqueSec, inComdat,
- varVis, extInit, linkage, nullptr);
+ varVis, extInit, MV_SkipDebug, linkage, nullptr);
return gvar;
}
@@ -3490,8 +3467,8 @@
class GenBlocks {
public:
GenBlocks(llvm::LLVMContext &context, Llvm_backend *be,
- Bfunction *function, Bnode *topNode, llvm::DIScope *scope,
- bool createDebugMetadata, llvm::BasicBlock *entryBlock);
+ Bfunction *function, Bnode *topNode,
+ DIBuildHelper *diBuildHelper, llvm::BasicBlock *entryBlock);
llvm::BasicBlock *walk(Bnode *node, llvm::BasicBlock *curblock);
void finishFunction(llvm::BasicBlock *entry);
@@ -3519,15 +3496,14 @@
std::pair<llvm::Instruction*, llvm::BasicBlock *>
postProcessInst(llvm::Instruction *inst,
llvm::BasicBlock *curblock);
- llvm::DIBuilder &dibuilder() { return be_->dibuilder(); }
- DIBuildHelper &dibuildhelper() { return *dibuildhelper_.get(); }
+ DIBuildHelper *dibuildhelper() const { return dibuildhelper_; }
Llvm_linemap *linemap() { return be_->linemap(); }
private:
llvm::LLVMContext &context_;
Llvm_backend *be_;
Bfunction *function_;
- std::unique_ptr<DIBuildHelper> dibuildhelper_;
+ DIBuildHelper *dibuildhelper_;
std::map<LabelId, llvm::BasicBlock *> labelmap_;
std::vector<llvm::BasicBlock*> padBlockStack_;
std::set<llvm::AllocaInst *> temporariesDiscovered_;
@@ -3535,37 +3511,27 @@
llvm::BasicBlock* finallyBlock_;
Bstatement *cachedReturn_;
bool emitOrphanedCode_;
- bool createDebugMetaData_;
};
GenBlocks::GenBlocks(llvm::LLVMContext &context,
Llvm_backend *be,
Bfunction *function,
Bnode *topNode,
- llvm::DIScope *scope,
- bool createDebugMetadata,
+ DIBuildHelper *dibuildhelper,
llvm::BasicBlock *entryBlock)
: context_(context), be_(be), function_(function),
- dibuildhelper_(nullptr), finallyBlock_(nullptr),
- cachedReturn_(nullptr), emitOrphanedCode_(false),
- createDebugMetaData_(createDebugMetadata)
+ dibuildhelper_(dibuildhelper), finallyBlock_(nullptr),
+ cachedReturn_(nullptr), emitOrphanedCode_(false)
{
- if (createDebugMetaData_) {
- dibuildhelper_.reset(new DIBuildHelper(topNode,
- be->typeManager(),
- be->linemap(),
- be->dibuilder(),
- be->getDICompUnit(),
- entryBlock));
- dibuildhelper().beginFunction(scope, function);
- }
+ if (dibuildhelper_)
+ dibuildhelper_->beginFunction(function, topNode, entryBlock);
}
void GenBlocks::finishFunction(llvm::BasicBlock *entry)
{
function_->fixupProlog(entry, newTemporaries_);
- if (createDebugMetaData_)
- dibuildhelper().endFunction(function_);
+ if (dibuildhelper_)
+ dibuildhelper_->endFunction(function_);
}
llvm::BasicBlock *GenBlocks::mkLLVMBlock(const std::string &name,
@@ -3680,8 +3646,8 @@
}
auto pair = postProcessInst(originst, curblock);
auto inst = pair.first;
- if (createDebugMetaData_)
- dibuildhelper().processExprInst(expr, inst);
+ if (dibuildhelper_)
+ dibuildhelper_->processExprInst(expr, inst);
curblock->getInstList().push_back(inst);
curblock = pair.second;
}
@@ -4015,12 +3981,12 @@
}
case N_BlockStmt: {
Bblock *bblock = stmt->castToBblock();
- if (createDebugMetaData_)
- dibuildhelper().beginLexicalBlock(bblock);
+ if (dibuildhelper_)
+ dibuildhelper_->beginLexicalBlock(bblock);
for (auto &st : stmt->getChildStmts())
curblock = walk(st, curblock);
- if (createDebugMetaData_)
- dibuildhelper().endLexicalBlock(bblock);
+ if (dibuildhelper_)
+ dibuildhelper_->endLexicalBlock(bblock);
break;
}
case N_IfStmt: {
@@ -4126,11 +4092,13 @@
llvm::BasicBlock *entryBlock = genEntryBlock(function);
// Avoid debug meta-generation if errors seen
- bool dodebug = (createDebugMetaData_ && errorCount_ == 0);
+ DIBuildHelper *dibh = nullptr;
+ if (createDebugMetaData_ && errorCount_ == 0)
+ dibh = dibuildhelper();
// Walk the code statements
GenBlocks gb(context_, this, function, code_stmt,
- getDICompUnit(), dodebug, entryBlock);
+ dibh, entryBlock);
llvm::BasicBlock *block = gb.walk(code_stmt, entryBlock);
gb.finishFunction(entryBlock);
@@ -4190,8 +4158,8 @@
void Llvm_backend::finalizeExportData()
{
// Calling this here, for lack of a better spot
- if (dibuilder_.get())
- dibuilder_->finalize();
+ if (errorCount_ == 0 && dibuildhelper())
+ dibuildhelper_->finalize();
assert(! exportDataFinalized_);
exportDataFinalized_ = true;
diff --git a/llvm-gofrontend/go-llvm.h b/llvm-gofrontend/go-llvm.h
index 52ae2b9..86d877c 100644
--- a/llvm-gofrontend/go-llvm.h
+++ b/llvm-gofrontend/go-llvm.h
@@ -339,8 +339,8 @@
// Type manager functionality
TypeManager *typeManager() const;
- // DI builder
- llvm::DIBuilder &dibuilder() { return *dibuilder_.get(); }
+ // DI build helper. Will be NULL if debug meta-data generation disabled.
+ DIBuildHelper *dibuildhelper() { return dibuildhelper_.get(); }
// Bnode builder
BnodeBuilder &nodeBuilder() { return nbuilder_; }
@@ -417,6 +417,7 @@
enum ModVarComdat { MV_InComdat, MV_NotInComdat };
enum ModVarVis { MV_HiddenVisibility, MV_DefaultVisibility };
enum ModVarExtInit { MV_ExternallyInitialized, MV_NotExternallyInitialized };
+ enum ModVarGenDebug { MV_GenDebug, MV_SkipDebug };
// Make a module-scope variable (static, global, or external).
Bvariable *makeModuleVar(Btype *btype,
@@ -428,6 +429,7 @@
ModVarComdat inComdat,
ModVarVis isHiddenVisibility,
ModVarExtInit isExtInit,
+ ModVarGenDebug genDebug,
llvm::GlobalValue::LinkageTypes linkage,
llvm::Constant *initializer,
unsigned alignmentInBytes = 0);
@@ -666,11 +668,8 @@
// Builder for constructing Bexpressions and Bstatements.
BnodeBuilder nbuilder_;
- // Debug info builder
- std::unique_ptr<llvm::DIBuilder> dibuilder_;
-
- // Root debug meta-data scope for compilation unit
- llvm::DICompileUnit *diCompileUnit_;
+ // Debug info builder.
+ std::unique_ptr<DIBuildHelper> dibuildhelper_;
// Linemap to use. If client did not supply a linemap during
// construction, then ownLinemap_ is filled in.
diff --git a/unittests/BackendCore/BackendDebugEmit.cpp b/unittests/BackendCore/BackendDebugEmit.cpp
index d9c8ad1..0fc3fcf 100644
--- a/unittests/BackendCore/BackendDebugEmit.cpp
+++ b/unittests/BackendCore/BackendDebugEmit.cpp
@@ -130,4 +130,29 @@
std::cerr << fdump;
}
+TEST(BackendVarTests, TestGlobalVarDebugEmit) {
+ FcnTestHarness h("foo");
+ Llvm_backend *be = h.be();
+ Location loc = h.loc();
+
+ Btype *bi32t = be->integer_type(false, 32);
+ Bvariable *g1 =
+ be->global_variable("_bar", "bar", bi32t,
+ true, /* is_external */
+ false, /* is_hidden */
+ false, /* unique_section */
+ loc);
+ be->global_variable_set_init(g1, mkInt32Const(be, 101));
+
+ bool broken = h.finish(PreserveDebugInfo);
+ EXPECT_FALSE(broken && "Module failed to verify.");
+
+ // This is a long way from verifying that the debug meta-data is in fact
+ // completely correct, but at least it checks that the global
+ // wasn't skipped.
+ bool ok = h.expectModuleDumpContains("!DIGlobalVariable(name: \"bar\",");
+ EXPECT_TRUE(ok);
+
+}
+
}
diff --git a/unittests/BackendCore/TestUtils.cpp b/unittests/BackendCore/TestUtils.cpp
index 8a65ee2..da06c14 100644
--- a/unittests/BackendCore/TestUtils.cpp
+++ b/unittests/BackendCore/TestUtils.cpp
@@ -703,6 +703,15 @@
return equal;
}
+bool FcnTestHarness::expectModuleDumpContains(const std::string &expected)
+{
+ std::string res;
+ llvm::raw_string_ostream os(res);
+ be()->module().print(os, nullptr);
+ std::string actual(trimsp(os.str()));
+ return containstokens(actual, expected);
+}
+
bool FcnTestHarness::expectRepr(Bnode *node, const std::string &expected)
{
std::string reason;
@@ -715,21 +724,24 @@
bool FcnTestHarness::finish(DebugDisposition whatToDoWithDebugInfo)
{
- // Emit a label def for the pending block if needed
- Bstatement *bst = be()->block_statement(curBlock_);
- if (nextLabel_) {
- Bstatement *ldef = be()->label_definition_statement(nextLabel_);
- bst = be()->compound_statement(ldef, bst);
+ if (func_) {
+
+ // Emit a label def for the pending block if needed
+ Bstatement *bst = be()->block_statement(curBlock_);
+ if (nextLabel_) {
+ Bstatement *ldef = be()->label_definition_statement(nextLabel_);
+ bst = be()->compound_statement(ldef, bst);
+ }
+
+ // Add current block as stmt to entry block
+ addStmtToBlock(be(), entryBlock_, bst);
+
+ // Set function body
+ be()->function_set_body(func_, entryBlock_);
+
}
-
- // Add current block as stmt to entry block
- addStmtToBlock(be(), entryBlock_, bst);
-
- // Set function body
- be()->function_set_body(func_, entryBlock_);
-
- // Finalize export data. This has the side effect of finalizing
- // debug meta-data, which we need to do before invoking the verifier.
+ // Finalize export data. This has the side effect of finalizing
+ // debug meta-data, which we need to do before invoking the verifier.
be()->finalizeExportData();
// Strip debug info now if requested
diff --git a/unittests/BackendCore/TestUtils.h b/unittests/BackendCore/TestUtils.h
index 2a5e193..85cf0e3 100644
--- a/unittests/BackendCore/TestUtils.h
+++ b/unittests/BackendCore/TestUtils.h
@@ -229,6 +229,12 @@
// and emit diagnostics if not.
bool expectRepr(Bnode *node, const std::string &expected);
+ // Verify that a dump of the module contains the specified token sequence
+ // somewhere within it. To be used only if the various methods above
+ // are inadequate, since obviously there is more potential for
+ // trouble here.
+ bool expectModuleDumpContains(const std::string &expected);
+
//
// Finish function:
// - attach current block to function