Merge pull request #2826 from Syniurge/debug-info-fixes

Debug info improvements (for GDB): printing global and imported symbols, non-member and member function calls
This commit is contained in:
Martin Kinkelin 2018-10-16 14:53:47 +02:00 committed by GitHub
commit c7c3f56415
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 479 additions and 116 deletions

View file

@ -11,6 +11,7 @@
#include "declaration.h"
#include "enum.h"
#include "id.h"
#include "import.h"
#include "init.h"
#include "nspace.h"
#include "rmem.h"
@ -50,6 +51,15 @@ public:
//////////////////////////////////////////////////////////////////////////
void visit(Import *im) override {
IF_LOG Logger::println("Import::codegen for %s", im->toPrettyChars());
LOG_SCOPE
irs->DBuilder.EmitImport(im);
}
//////////////////////////////////////////////////////////////////////////
void visit(Nspace *ns) override {
IF_LOG Logger::println("Nspace::codegen for %s", ns->toPrettyChars());
LOG_SCOPE

View file

@ -18,14 +18,21 @@
#include "gen/tollvm.h"
#include "gen/optimizer.h"
#include "ir/irfunction.h"
#include "ir/irmodule.h"
#include "ir/irfuncty.h"
#include "ir/irtypeaggr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "enum.h"
#include "ldcbindings.h"
#include "import.h"
#include "module.h"
#include "mtype.h"
#include "nspace.h"
#include "template.h"
#include <functional>
////////////////////////////////////////////////////////////////////////////////
@ -76,27 +83,53 @@ bool ldc::DIBuilder::mustEmitLocationsDebugInfo() {
////////////////////////////////////////////////////////////////////////////////
// get the module the symbol is in, or - for template instances - the current
// module
Module *ldc::DIBuilder::getDefinedModule(Dsymbol *s) {
// templates are defined in current module
if (DtoIsTemplateInstance(s)) {
return IR->dmodule;
}
// otherwise use the symbol's module
return s->getModule();
}
////////////////////////////////////////////////////////////////////////////////
ldc::DIBuilder::DIBuilder(IRState *const IR)
: IR(IR), DBuilder(IR->module), CUNode(nullptr),
isTargetMSVCx64(global.params.targetTriple->isWindowsMSVCEnvironment() &&
isTargetMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()),
isTargetMSVCx64(isTargetMSVC &&
global.params.targetTriple->isArch64Bit()) {}
llvm::LLVMContext &ldc::DIBuilder::getContext() { return IR->context(); }
// return the scope of a global symbol
// FIXME: template instances aren't supported properly by GDC and GDB, what debug info should we attach to them?
ldc::DIScope ldc::DIBuilder::GetSymbolScope(Dsymbol* s) {
// don't recreate parent entries if we only need location debug info
if (!mustEmitFullDebugInfo())
return GetCU();
auto parent = s->toParent();
auto vd = s->isVarDeclaration();
if (vd && vd->isDataseg()) {
// static variables get attached to the module scope, but their
// parent composite types have to get declared
while (!parent->isModule()) {
if (parent->isAggregateDeclaration())
CreateCompositeTypeDescription(parent->getType());
parent = parent->toParent();
}
}
if (auto tempinst = parent->isTemplateInstance()) {
return GetSymbolScope(tempinst->tempdecl);
} else if (auto m = parent->isModule()) {
return EmitModule(m);
} else if (parent->isAggregateDeclaration()) {
return CreateCompositeTypeDescription(parent->getType());
} else if (auto fd = parent->isFuncDeclaration()) {
DtoDeclareFunction(fd);
return EmitSubProgram(fd);
} else if (auto ns = parent->isNspace()) {
return GetSymbolScope(ns); // FIXME DINamespace?
}
llvm_unreachable("Unhandled parent");
}
ldc::DIScope ldc::DIBuilder::GetCurrentScope() {
if (IR->funcGenStates.empty())
return getIrModule(IR->dmodule)->diModule;
IrFunction *fn = IR->func();
if (fn->diLexicalBlocks.empty()) {
assert(static_cast<llvm::MDNode *>(fn->diSubprogram) != 0);
@ -163,7 +196,7 @@ ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) {
Encoding = DW_ATE_boolean;
break;
case Tchar:
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
if (isTargetMSVC) {
// VS debugger does not support DW_ATE_UTF for char
Encoding = DW_ATE_unsigned_char;
break;
@ -174,7 +207,7 @@ ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) {
Encoding = DW_ATE_UTF;
break;
case Tint8:
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
if (isTargetMSVC) {
// VS debugger does not support DW_ATE_signed for 8-bit
Encoding = DW_ATE_signed_char;
break;
@ -187,7 +220,7 @@ ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) {
Encoding = DW_ATE_signed;
break;
case Tuns8:
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
if (isTargetMSVC) {
// VS debugger does not support DW_ATE_unsigned for 8-bit
Encoding = DW_ATE_unsigned_char;
break;
@ -207,7 +240,7 @@ ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) {
case Timaginary32:
case Timaginary64:
case Timaginary80:
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
if (isTargetMSVC) {
// DW_ATE_imaginary_float not supported by the LLVM DWARF->CodeView
// conversion
Encoding = DW_ATE_float;
@ -218,7 +251,7 @@ ldc::DIType ldc::DIBuilder::CreateBasicType(Type *type) {
case Tcomplex32:
case Tcomplex64:
case Tcomplex80:
if (global.params.targetTriple->isWindowsMSVCEnvironment()) {
if (isTargetMSVC) {
// DW_ATE_complex_float not supported by the LLVM DWARF->CodeView
// conversion
return CreateComplexType(t);
@ -257,12 +290,12 @@ ldc::DIType ldc::DIBuilder::CreateEnumType(Type *type) {
subscripts.push_back(Subscript);
}
llvm::StringRef Name = te->sym->toPrettyChars(true);
llvm::StringRef Name = te->sym->toChars();
unsigned LineNumber = te->sym->loc.linnum;
ldc::DIFile File(CreateFile(te->sym));
return DBuilder.createEnumerationType(
GetCU(), Name, File, LineNumber,
GetSymbolScope(te->sym), Name, File, LineNumber,
getTypeAllocSize(T) * 8, // size (bits)
getABITypeAlign(T) * 8, // align (bits)
DBuilder.getOrCreateArray(subscripts), // subscripts
@ -361,7 +394,8 @@ ldc::DIType ldc::DIBuilder::CreateComplexType(Type *type) {
ldc::DIType ldc::DIBuilder::CreateMemberType(unsigned linnum, Type *type,
ldc::DIFile file,
const char *c_name,
unsigned offset, Prot::Kind prot) {
unsigned offset, Prot::Kind prot,
bool isStatic, DIScope scope) {
Type *t = type->toBasetype();
// translate functions to function pointers
@ -388,7 +422,10 @@ ldc::DIType ldc::DIBuilder::CreateMemberType(unsigned linnum, Type *type,
break;
}
return DBuilder.createMemberType(GetCU(),
if (isStatic)
Flags |= DIFlags::FlagStaticMember;
return DBuilder.createMemberType(scope,
c_name, // name
file, // file
linnum, // line number
@ -411,6 +448,34 @@ void ldc::DIBuilder::AddFields(AggregateDeclaration *ad, ldc::DIFile file,
}
}
void ldc::DIBuilder::AddStaticMembers(AggregateDeclaration *ad, ldc::DIFile file,
llvm::SmallVector<LLMetadata *, 16> &elems) {
auto scope = CreateCompositeTypeDescription(ad->getType());
std::function<void(Dsymbols*)> visitMembers = [&] (Dsymbols* members) {
for (auto s : *members) {
if (auto attrib = s->isAttribDeclaration()) {
if (Dsymbols *d = attrib->include(nullptr))
visitMembers(d);
} else if (auto tmixin = s->isTemplateMixin()) {
visitMembers(tmixin->members); // FIXME: static variables inside a template mixin need to be put inside a child DICompositeType for their value to become accessible (mangling issue).
// Also DWARF supports imported declarations, but LLVM currently does nothing with DIImportedEntity except at CU-level.
} else if (auto vd = s->isVarDeclaration())
if (vd->isDataseg() && !vd->aliassym /* TODO: tuples*/) {
llvm::MDNode* elem = CreateMemberType(vd->loc.linnum, vd->type, file,
vd->toChars(), 0,
vd->prot().kind,
/*isStatic = */true, scope);
elems.push_back(elem);
StaticDataMemberCache[vd].reset(elem);
}
} /*else if (auto fd = s->isFuncDeclaration())*/ // Clang also adds static functions as declarations,
// but they already work without adding them.
};
visitMembers(ad->members);
}
ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
Type *t = type->toBasetype();
assert((t->ty == Tstruct || t->ty == Tclass) &&
@ -430,17 +495,13 @@ ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
LLType *T = DtoType(ad->type);
if (t->ty == Tclass)
T = T->getPointerElementType();
IrTypeAggr *ir = ad->type->ctype->isAggr();
assert(ir);
IrAggr *irAggr = getIrAggr(ad, true);
if (static_cast<llvm::MDNode *>(ir->diCompositeType) != nullptr) {
return ir->diCompositeType;
if (static_cast<llvm::MDNode *>(irAggr->diCompositeType) != nullptr) {
return irAggr->diCompositeType;
}
const llvm::StringRef name =
(ad->isClassDeclaration() && ad->isClassDeclaration()->isCPPinterface()
? ad->ident->toChars()
: ad->toPrettyChars(true));
const llvm::StringRef name = ad->ident->toChars();
// if we don't know the aggregate's size, we don't know enough about it
// to provide debug info. probably a forward-declared struct?
@ -453,16 +514,16 @@ ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
// defaults
unsigned linnum = ad->loc.linnum;
ldc::DICompileUnit CU(GetCU());
assert(CU && "Compilation unit missing or corrupted");
assert(GetCU() && "Compilation unit missing or corrupted");
ldc::DIFile file = CreateFile(ad);
ldc::DIType derivedFrom = getNullDIType();
ldc::DIScope scope = GetSymbolScope(ad);
// set diCompositeType to handle recursive types properly
unsigned tag = (t->ty == Tstruct) ? llvm::dwarf::DW_TAG_structure_type
: llvm::dwarf::DW_TAG_class_type;
ir->diCompositeType = DBuilder.createReplaceableCompositeType(
tag, name, CU, file, linnum);
irAggr->diCompositeType = DBuilder.createReplaceableCompositeType(
tag, name, scope, file, linnum);
if (!ad->isInterfaceDeclaration()) // plain interfaces don't have one
{
@ -471,7 +532,7 @@ ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
derivedFrom = CreateCompositeType(classDecl->baseClass->getType());
// needs a forward declaration to add inheritence information to elems
ldc::DIType fwd =
DBuilder.createClassType(CU, // compile unit where defined
DBuilder.createClassType(scope, // context where defined
name, // name
file, // file where defined
linnum, // line number where defined
@ -495,12 +556,13 @@ ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
}
AddFields(ad, file, elems);
}
AddStaticMembers(ad, file, elems);
auto elemsArray = DBuilder.getOrCreateArray(elems);
ldc::DIType ret;
if (t->ty == Tclass) {
ret = DBuilder.createClassType(CU, // compile unit where defined
ret = DBuilder.createClassType(scope, // context where defined
name, // name
file, // file where defined
linnum, // line number where defined
@ -514,7 +576,7 @@ ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
nullptr, // TemplateParms
uniqueIdent(t)); // UniqueIdentifier
} else {
ret = DBuilder.createStructType(CU, // compile unit where defined
ret = DBuilder.createStructType(scope, // context where defined
name, // name
file, // file where defined
linnum, // line number where defined
@ -528,9 +590,9 @@ ldc::DIType ldc::DIBuilder::CreateCompositeType(Type *type) {
uniqueIdent(t)); // UniqueIdentifier
}
ir->diCompositeType = DBuilder.replaceTemporary(
llvm::TempDINode(ir->diCompositeType), static_cast<llvm::DIType *>(ret));
ir->diCompositeType = ret;
irAggr->diCompositeType = DBuilder.replaceTemporary(
llvm::TempDINode(irAggr->diCompositeType), static_cast<llvm::DIType *>(ret));
irAggr->diCompositeType = ret;
return ret;
}
@ -541,14 +603,17 @@ ldc::DIType ldc::DIBuilder::CreateArrayType(Type *type) {
assert(t->ty == Tarray);
ldc::DIFile file = CreateFile();
ldc::DIScope scope = type->toDsymbol(nullptr)
? GetSymbolScope(type->toDsymbol(nullptr))
: GetCU();
LLMetadata *elems[] = {
CreateMemberType(0, Type::tsize_t, file, "length", 0, Prot::public_),
CreateMemberType(0, t->nextOf()->pointerTo(), file, "ptr",
global.params.is64bit ? 8 : 4, Prot::public_)};
return DBuilder.createStructType(GetCU(),
type->toPrettyChars(true), // Name
return DBuilder.createStructType(scope,
type->toChars(), // Name
file, // File
0, // LineNo
getTypeAllocSize(T) * 8, // size in bits
@ -597,6 +662,8 @@ ldc::DIType ldc::DIBuilder::CreateAArrayType(Type *type) {
////////////////////////////////////////////////////////////////////////////////
const unsigned DW_CC_D_dmd = 0x43; // new calling convention constant being proposed as a Dwarf extension
ldc::DISubroutineType ldc::DIBuilder::CreateFunctionType(Type *type) {
assert(type->toBasetype()->ty == Tfunction);
@ -607,7 +674,13 @@ ldc::DISubroutineType ldc::DIBuilder::CreateFunctionType(Type *type) {
LLMetadata *params = {CreateTypeDescription(retType)};
auto paramsArray = DBuilder.getOrCreateTypeArray(params);
return DBuilder.createSubroutineType(paramsArray);
// The calling convention has to be recorded to distinguish
// extern(D) functions from extern(C++) ones.
DtoType(t);
assert(t->ctype);
unsigned CC = t->ctype->getIrFuncTy().reverseParams ? DW_CC_D_dmd : 0;
return DBuilder.createSubroutineType(paramsArray, DIFlagZero, CC);
}
ldc::DISubroutineType ldc::DIBuilder::CreateEmptyFunctionType() {
@ -631,7 +704,7 @@ ldc::DIType ldc::DIBuilder::CreateDelegateType(Type *type) {
global.params.is64bit ? 8 : 4, Prot::public_)};
return DBuilder.createStructType(CU, // compile unit where defined
type->toPrettyChars(true), // name
type->toChars(), // name
file, // file where defined
0, // line number where defined
getTypeAllocSize(T) * 8, // size in bits
@ -657,7 +730,7 @@ ldc::DIType ldc::DIBuilder::CreateTypeDescription(Type *type) {
// Check for opaque enum first, Bugzilla 13792
if (isOpaqueEnumType(type)) {
const auto ed = static_cast<TypeEnum *>(type)->sym;
return DBuilder.createUnspecifiedType(ed->toPrettyChars(true));
return DBuilder.createUnspecifiedType(ed->toChars());
}
Type *t = type->toBasetype();
@ -709,6 +782,12 @@ ldc::DIType ldc::DIBuilder::CreateTypeDescription(Type *type) {
llvm_unreachable("Unsupported type in debug info");
}
ldc::DICompositeType ldc::DIBuilder::CreateCompositeTypeDescription(Type *type) {
DIType ret = type->toBasetype()->ty == Tclass
? CreateCompositeType(type) : CreateTypeDescription(type);
return llvm::cast<llvm::DICompositeType>(ret);
}
////////////////////////////////////////////////////////////////////////////////
llvm::DICompileUnit::DebugEmissionKind getDebugEmissionKind()
@ -747,7 +826,7 @@ void ldc::DIBuilder::EmitCompileUnit(Module *m) {
auto producerName = std::string("LDC ") + ldc::ldc_version + " (LLVM " +
ldc::llvm_version + ")";
if (global.params.targetTriple->isWindowsMSVCEnvironment())
if (isTargetMSVC)
IR->module.addModuleFlag(llvm::Module::Warning, "CodeView", 1);
else if (global.params.dwarfVersion > 0)
IR->module.addModuleFlag(llvm::Module::Warning, "Dwarf Version",
@ -775,40 +854,105 @@ void ldc::DIBuilder::EmitCompileUnit(Module *m) {
);
}
ldc::DIModule ldc::DIBuilder::EmitModule(Module *m)
{
if (!mustEmitFullDebugInfo()) {
return nullptr;
}
IrModule *irm = getIrModule(m);
if (irm->diModule)
return irm->diModule;
irm->diModule = DBuilder.createModule(
CUNode,
m->toPrettyChars(true), // qualified module name
llvm::StringRef(), // (clang modules specific) ConfigurationMacros
llvm::StringRef(), // (clang modules specific) IncludePath
llvm::StringRef() // (clang modules specific) ISysRoot
);
return irm->diModule;
}
void ldc::DIBuilder::EmitImport(Import *im)
{
if (!mustEmitFullDebugInfo()) {
return;
}
auto diModule = EmitModule(im->mod);
DBuilder.createImportedModule(
GetCurrentScope(),
diModule, // imported module
#if LDC_LLVM_VER >= 500
CreateFile(im), // file
#endif
im->loc.linnum // line num
);
}
ldc::DISubprogram ldc::DIBuilder::EmitSubProgram(FuncDeclaration *fd) {
if (!mustEmitLocationsDebugInfo()) {
return nullptr;
}
IrFunction *const irFunc = getIrFunc(fd);
if (irFunc->diSubprogram)
return irFunc->diSubprogram;
Logger::println("D to dwarf subprogram");
LOG_SCOPE;
ldc::DICompileUnit CU(GetCU());
assert(CU &&
assert(GetCU() &&
"Compilation unit missing or corrupted in DIBuilder::EmitSubProgram");
ldc::DIFile file = CreateFile(fd);
// FIXME: work around apparent LLVM CodeView bug wrt. nested functions
ldc::DIScope scope;
const char *name;
if (isTargetMSVC && fd->toParent2()->isFuncDeclaration()) {
// emit into module & use fully qualified name
scope = GetCU();
name = fd->toPrettyChars(true);
} else {
scope = GetSymbolScope(fd);
name = fd->isMain() ? fd->toPrettyChars(true) : fd->toChars();
}
// Create subroutine type
ldc::DISubroutineType DIFnType = mustEmitFullDebugInfo() ?
CreateFunctionType(static_cast<TypeFunction *>(fd->type)) :
CreateEmptyFunctionType();
const auto linkageName = irFunc->getLLVMFuncName();
const auto file = CreateFile(fd);
const auto lineNo = fd->loc.linnum;
const auto isLocalToUnit = fd->protection.kind == Prot::private_;
const auto isDefinition = true;
const auto scopeLine = lineNo; // FIXME
const auto flags = DIFlags::FlagPrototyped;
const auto isOptimized = isOptimizationEnabled();
ldc::DISubroutineType diFnType = nullptr;
if (!mustEmitFullDebugInfo()) {
diFnType = CreateEmptyFunctionType();
} else {
// A special case is `auto foo() { struct S{}; S s; return s; }`
// The return type is a nested struct, so for this particular
// chicken-and-egg case we need to create a temporary subprogram.
irFunc->diSubprogram = DBuilder.createTempFunctionFwdDecl(
scope, name, linkageName, file, lineNo, /*ty=*/nullptr, isLocalToUnit,
isDefinition, scopeLine, flags, isOptimized);
// Now create subroutine type.
diFnType = CreateFunctionType(static_cast<TypeFunction *>(fd->type));
}
// FIXME: duplicates?
auto SP = DBuilder.createFunction(
CU, // context
fd->toPrettyChars(true), // name
getIrFunc(fd)->getLLVMFuncName(), // linkage name
file, // file
fd->loc.linnum, // line no
DIFnType, // type
fd->protection.kind == Prot::private_, // is local to unit
true, // isdefinition
fd->loc.linnum, // FIXME: scope line
DIFlags::FlagPrototyped, // Flags
isOptimizationEnabled() // isOptimized
);
DtoFunction(fd)->setSubprogram(SP);
auto SP = DBuilder.createFunction(scope, name, linkageName, file, lineNo,
diFnType, isLocalToUnit, isDefinition,
scopeLine, flags, isOptimized);
if (mustEmitFullDebugInfo())
DBuilder.replaceTemporary(llvm::TempDINode(irFunc->diSubprogram), SP);
irFunc->diSubprogram = SP;
return SP;
}
@ -821,20 +965,19 @@ ldc::DISubprogram ldc::DIBuilder::EmitThunk(llvm::Function *Thunk,
Logger::println("Thunk to dwarf subprogram");
LOG_SCOPE;
ldc::DICompileUnit CU(GetCU());
assert(CU && "Compilation unit missing or corrupted in DIBuilder::EmitThunk");
assert(GetCU() && "Compilation unit missing or corrupted in DIBuilder::EmitThunk");
ldc::DIFile file = CreateFile(fd);
// Create subroutine type (thunk has same type as wrapped function)
ldc::DISubroutineType DIFnType = CreateFunctionType(fd->type);
std::string name = fd->toPrettyChars(true);
std::string name = fd->toChars();
name.append(".__thunk");
// FIXME: duplicates?
auto SP = DBuilder.createFunction(
CU, // context
GetSymbolScope(fd), // context
name, // name
Thunk->getName(), // linkage name
file, // file
@ -846,8 +989,6 @@ ldc::DISubprogram ldc::DIBuilder::EmitThunk(llvm::Function *Thunk,
DIFlags::FlagPrototyped, // Flags
isOptimizationEnabled() // isOptimized
);
if (fd->fbody)
DtoFunction(fd)->setSubprogram(SP);
return SP;
}
@ -860,8 +1001,7 @@ ldc::DISubprogram ldc::DIBuilder::EmitModuleCTor(llvm::Function *Fn,
Logger::println("D to dwarf subprogram");
LOG_SCOPE;
ldc::DICompileUnit CU(GetCU());
assert(CU &&
assert(GetCU() &&
"Compilation unit missing or corrupted in DIBuilder::EmitSubProgram");
ldc::DIFile file = CreateFile();
@ -872,7 +1012,7 @@ ldc::DISubprogram ldc::DIBuilder::EmitModuleCTor(llvm::Function *Fn,
// FIXME: duplicates?
auto SP =
DBuilder.createFunction(CU, // context
DBuilder.createFunction(GetCurrentScope(), // context
prettyname, // name
Fn->getName(), // linkage name
file, // file
@ -908,6 +1048,9 @@ void ldc::DIBuilder::EmitFuncEnd(FuncDeclaration *fd) {
assert(static_cast<llvm::MDNode *>(getIrFunc(fd)->diSubprogram) != 0);
EmitStopPoint(fd->endloc);
// Only attach subprogram entries to function definitions
DtoFunction(fd)->setSubprogram(getIrFunc(fd)->diSubprogram);
}
void ldc::DIBuilder::EmitBlockStart(Loc &loc) {
@ -1019,6 +1162,14 @@ void ldc::DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd,
bool useDbgValueIntrinsic = false;
if (isRefOrOut || isPassedExplicitlyByval) {
// DW_TAG_reference_type sounds like the correct tag for `this`, but member
// function calls won't work with GDB unless `this` gets declared as
// DW_TAG_pointer_type.
// This matches what GDC and Clang do.
auto Tag = isThisPtr
? llvm::dwarf::DW_TAG_pointer_type
: llvm::dwarf::DW_TAG_reference_type;
// With the exception of special-ref loop variables, the reference/pointer
// itself is constant. So we don't have to attach the debug information to a
// memory location and can use llvm.dbg.value to set the constant pointer
@ -1028,7 +1179,7 @@ void ldc::DIBuilder::EmitLocalVariable(llvm::Value *ll, VarDeclaration *vd,
// Note: createReferenceType expects the size to be the size of a pointer,
// not the size of the type the reference refers to.
TD = DBuilder.createReferenceType(
llvm::dwarf::DW_TAG_reference_type, TD,
Tag, TD,
gDataLayout->getPointerSizeInBits(), // size (bits)
DtoAlignment(type) * 8); // align (bits)
} else {
@ -1105,6 +1256,15 @@ void ldc::DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar,
assert(vd->isDataseg() ||
(vd->storage_class & (STCconst | STCimmutable) && vd->_init));
DIScope scope = GetSymbolScope(vd);
llvm::MDNode* Decl = nullptr;
if (vd->isDataseg() && vd->toParent()->isAggregateDeclaration()) {
// static aggregate member
Decl = StaticDataMemberCache[vd];
assert(Decl && "static aggregate member not declared");
}
OutBuffer mangleBuf;
mangleToBuffer(vd, &mangleBuf);
@ -1113,7 +1273,7 @@ void ldc::DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar,
#else
DBuilder.createGlobalVariable(
#endif
GetCU(), // context
scope, // context
vd->toChars(), // name
mangleBuf.peekString(), // linkage name
CreateFile(vd), // file
@ -1121,10 +1281,11 @@ void ldc::DIBuilder::EmitGlobalVariable(llvm::GlobalVariable *llVar,
CreateTypeDescription(vd->type), // type
vd->protection.kind == Prot::private_, // is local to unit
#if LDC_LLVM_VER >= 400
nullptr // relative location of field
nullptr, // relative location of field
#else
llVar // value
llVar, // value
#endif
Decl // declaration
);
#if LDC_LLVM_VER >= 400

View file

@ -10,6 +10,7 @@
#ifndef LDC_GEN_DIBUILDER_H
#define LDC_GEN_DIBUILDER_H
#include "llvm/ADT/DenseMap.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/DataLayout.h"
@ -24,6 +25,7 @@ struct IRState;
class ClassDeclaration;
class Dsymbol;
class FuncDeclaration;
class Import;
class Module;
class Type;
class VarDeclaration;
@ -44,6 +46,7 @@ namespace ldc {
// Define some basic types
typedef llvm::DIType *DIType;
typedef llvm::DICompositeType *DICompositeType;
typedef llvm::DIFile *DIFile;
typedef llvm::DIGlobalVariable *DIGlobalVariable;
typedef llvm::DILocalVariable *DILocalVariable;
@ -52,6 +55,7 @@ typedef llvm::DILexicalBlock *DILexicalBlock;
typedef llvm::DIScope *DIScope;
typedef llvm::DISubroutineType *DISubroutineType;
typedef llvm::DISubprogram *DISubprogram;
typedef llvm::DIModule *DIModule;
typedef llvm::DICompileUnit *DICompileUnit;
class DIBuilder {
@ -60,8 +64,11 @@ class DIBuilder {
DICompileUnit CUNode;
const bool isTargetMSVC;
const bool isTargetMSVCx64;
llvm::DenseMap<Declaration*, llvm::TypedTrackingMDRef<llvm::MDNode>> StaticDataMemberCache;
DICompileUnit GetCU() {
return CUNode;
}
@ -75,6 +82,14 @@ public:
/// \param m Module to emit as compile unit.
void EmitCompileUnit(Module *m);
/// \brief Emit the Dwarf module global for a Module m.
/// \param m Module to emit (either as definition or declaration).
DIModule EmitModule(Module *m);
/// \brief Emit the Dwarf imported entity and module global for an Import im.
/// \param im Import to emit.
void EmitImport(Import *im);
/// \brief Emit the Dwarf subprogram global for a function declaration fd.
/// \param fd Function declaration to emit as subprogram.
/// \returns the Dwarf subprogram global.
@ -144,7 +159,7 @@ public:
private:
llvm::LLVMContext &getContext();
Module *getDefinedModule(Dsymbol *s);
DIScope GetSymbolScope(Dsymbol *s);
DIScope GetCurrentScope();
void Declare(const Loc &loc, llvm::Value *storage, ldc::DILocalVariable divar,
ldc::DIExpression diexpr);
@ -152,6 +167,8 @@ private:
ldc::DIExpression diexpr);
void AddFields(AggregateDeclaration *sd, ldc::DIFile file,
llvm::SmallVector<llvm::Metadata *, 16> &elems);
void AddStaticMembers(AggregateDeclaration *sd, ldc::DIFile file,
llvm::SmallVector<llvm::Metadata *, 16> &elems);
DIFile CreateFile(Loc &loc);
DIFile CreateFile();
DIFile CreateFile(Dsymbol* decl);
@ -161,7 +178,8 @@ private:
DIType CreateVectorType(Type *type);
DIType CreateComplexType(Type *type);
DIType CreateMemberType(unsigned linnum, Type *type, DIFile file,
const char *c_name, unsigned offset, Prot::Kind);
const char *c_name, unsigned offset, Prot::Kind,
bool isStatic = false, DIScope scope = nullptr);
DIType CreateCompositeType(Type *type);
DIType CreateArrayType(Type *type);
DIType CreateSArrayType(Type *type);
@ -170,6 +188,7 @@ private:
DISubroutineType CreateEmptyFunctionType();
DIType CreateDelegateType(Type *type);
DIType CreateTypeDescription(Type *type);
DICompositeType CreateCompositeTypeDescription(Type *type);
bool mustEmitFullDebugInfo();
bool mustEmitLocationsDebugInfo();

View file

@ -1000,7 +1000,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
};
// debug info
irFunc->diSubprogram = gIR->DBuilder.EmitSubProgram(fd);
gIR->DBuilder.EmitSubProgram(fd);
IF_LOG Logger::println("Doing function body for: %s", fd->toChars());
gIR->funcGenStates.emplace_back(new FuncGenState(*irFunc, *gIR));

View file

@ -577,6 +577,8 @@ void codegenModule(IRState *irs, Module *m) {
assert(!gIR && "gIR not null, codegen already in progress?!");
gIR = irs;
irs->DBuilder.EmitModule(m);
initRuntime();
// Skip pseudo-modules for coverage analysis

View file

@ -7,6 +7,7 @@
//
//===----------------------------------------------------------------------===//
#include "import.h"
#include "init.h"
#include "mars.h"
#include "module.h"
@ -1590,7 +1591,10 @@ public:
//////////////////////////////////////////////////////////////////////////
void visit(ImportStatement *stmt) override {
// Empty.
for (auto s: *stmt->imports) {
assert(s->isImport());
irs->DBuilder.EmitImport(static_cast<Import*>(s));
}
}
//////////////////////////////////////////////////////////////////////////

View file

@ -46,6 +46,10 @@ struct IrAggr {
/// Aggregate D type.
Type *type = nullptr;
/// Composite type debug description. This is not only to cache, but also
/// used for resolving forward references.
llvm::DIType *diCompositeType = nullptr;
//////////////////////////////////////////////////////////////////////////
// Returns the static default initializer of a field.

View file

@ -409,6 +409,7 @@ void IrAggr::defineInterfaceVtbl(BaseClass *b, bool new_instance,
gIR->funcGenStates.emplace_back(new FuncGenState(*thunkFunc, *gIR));
// debug info
thunkFunc->diSubprogram = nullptr;
thunkFunc->diSubprogram = gIR->DBuilder.EmitThunk(thunk, thunkFd);
// create entry and end blocks

View file

@ -22,6 +22,7 @@ class Module;
namespace llvm {
class GlobalVariable;
class Function;
class DIModule;
}
struct IrModule {
@ -44,6 +45,8 @@ struct IrModule {
FuncDeclList unitTests;
llvm::Function *coverageCtor = nullptr;
llvm::DIModule *diModule = nullptr;
private:
llvm::GlobalVariable *moduleInfoVar = nullptr;
};

View file

@ -67,10 +67,6 @@ public:
void getMemberLocation(VarDeclaration *var, unsigned &fieldIndex,
unsigned &byteOffset) const;
/// Composite type debug description. This is not only to cache, but also
/// used for resolving forward references.
llvm::DIType *diCompositeType = nullptr;
/// true, if the LLVM struct type for the aggregate is declared as packed
bool packed = false;

@ -1 +1 @@
Subproject commit 048e2f5d700251cd4516e1bfcd1fe664723b7866
Subproject commit f9e116f36fa565327edd0256ff2b414c3c6afebd

View file

@ -29,7 +29,7 @@ int byValue(ubyte ub, ushort us, uint ui, ulong ul,
// arguments implicitly passed by reference aren't shown if unused
float cim = c.im + fa[7] + dg() + small.val + large.a;
return 1;
// CHECK: !args_cdb.byValue
// CHECK: !args_cdb::byValue
// CDB: dv /t
// CHECK: unsigned char ub = 0x01
@ -50,10 +50,10 @@ int byValue(ubyte ub, ushort us, uint ui, ulong ul,
// x86: unsigned char [16] fa
// x86: float [4] f4 = float [4]
// x86: double [4] d4 = double [4]
// CHECK: args_cdb.Small small
// x64: args_cdb.Large * large
// x86: args_cdb.Large large
// CHECK: struct object.TypeInfo_Class * ti = {{0x[0-9a-f`]*}}
// CHECK: args_cdb::Small small
// x64: args_cdb::Large * large
// x86: args_cdb::Large large
// CHECK: struct object::TypeInfo_Class * ti = {{0x[0-9a-f`]*}}
// CHECK: void * np = {{0x[0`]*}}
// params emitted as locals (listed after params) for Win64:
@ -90,17 +90,17 @@ int byValue(ubyte ub, ushort us, uint ui, ulong ul,
// CHECK: double 17
// CDB: ?? small
// CHECK: args_cdb.Small
// CHECK: args_cdb::Small
// x64-NEXT: val : 0x12
// no-x86-NEXT: val : 0x12 (displays garbage)
// CDB: ?? large
// CHECK: args_cdb.Large
// CHECK: args_cdb::Large
// x64-NEXT: a : 0x13
// no-x86-NEXT: a : 0x13 (displays garbage)
// CDB: ?? ti
// CHECK: object.TypeInfo_Class
// CHECK: object::TypeInfo_Class
// CHECK-NEXT: m_init : byte[]
}
@ -115,7 +115,7 @@ int byPtr(ubyte* ub, ushort* us, uint* ui, ulong* ul,
// CDB: bp `args_cdb.d:115`
// CDB: g
return 3;
// CHECK: !args_cdb.byPtr
// CHECK: !args_cdb::byPtr
// CDB: dv /t
// CDB: ?? *ub
// CHECK: unsigned char 0x01
@ -156,14 +156,14 @@ int byPtr(ubyte* ub, ushort* us, uint* ui, ulong* ul,
// CDB: ?? (*d4)[2]
// CHECK: double 17
// CDB: ?? *small
// CHECK: struct args_cdb.Small
// CHECK: struct args_cdb::Small
// CHECK-NEXT: val : 0x12
// CDB: ?? *large
// CHECK: struct args_cdb.Large
// CHECK: struct args_cdb::Large
// CHECK-NEXT: a : 0x13
// CHECK-NEXT: b :
// CDB: ?? *ti
// CHECK: struct object.TypeInfo_Class
// CHECK: struct object::TypeInfo_Class
// CHECK-NEXT: m_init : byte[]
// CDB: ?? *np
// CHECK: void * {{0x[0`]*}}
@ -179,7 +179,7 @@ int byRef(ref ubyte ub, ref ushort us, ref uint ui, ref ulong ul,
{
// CDB: bp `args_cdb.d:180`
// CDB: g
// CHECK: !args_cdb.byRef
// CHECK: !args_cdb::byRef
// CDB: dv /t
// cdb displays references as pointers
@ -221,14 +221,14 @@ int byRef(ref ubyte ub, ref ushort us, ref uint ui, ref ulong ul,
// CDB: ?? (*d4)[2]
// CHECK: double 17
// CDB: ?? *small
// CHECK: struct args_cdb.Small
// CHECK: struct args_cdb::Small
// CHECK-NEXT: val : 0x12
// CDB: ?? *large
// CHECK: struct args_cdb.Large
// CHECK: struct args_cdb::Large
// CHECK-NEXT: a : 0x13
// CHECK-NEXT: b :
// CDB: ?? *ti
// CHECK: struct object.TypeInfo_Class * {{0x[0-9a-f`]*}}
// CHECK: struct object::TypeInfo_Class * {{0x[0-9a-f`]*}}
// CHECK-NEXT: m_init : byte[]
// CDB: ?? *np
// no-CHECK: void * {{0x[0`]*}} (not available)

View file

@ -26,11 +26,11 @@ int main(string[] args)
// CDB: bp `baseclass_cdb.d:28`
// CDB: g
return 0;
// CHECK: !D main
// CHECK: !baseclass_cdb::D main
// CDB: ?? dc
// cdb doesn't show base class info, but lists their members
// CHECK: baseclass_cdb.DerivedClass
// CHECK: baseclass_cdb::DerivedClass
// CHECK: baseMember{{ *: *3}}
// verify baseMember is not listed twice
// CHECK-NEXT: derivedMember{{ *: *7}}

View file

@ -42,7 +42,7 @@ int basic_types()
// CDB: ld basictypes_cdb*
// CDB: bp `basictypes_cdb.d:41`
// CDB: g
// CHECK: !basictypes_cdb.basic_types
// CHECK: !basictypes_cdb::basic_types
// enable case sensitive symbol lookup
// CDB: .symopt-1

View file

@ -0,0 +1,12 @@
module inputs.import_a;
static __gshared int a_Glob = 4213;
struct a_sA
{
static char statChar = 'B';
}
void bar() {
a_sA.statChar = 'C';
}

View file

@ -0,0 +1,10 @@
module inputs.import_b;
static __gshared float b_Glob = 55.22;
enum b_eA { aaa = 9, zzz = 12 }
class b_cA {
int n;
static __gshared int staticVal = 52;
}

View file

@ -24,7 +24,7 @@ void encloser(int arg0, int arg1)
}
}
// CHECK-LABEL: !DISubprogram(name:{{.*}}"{{.*}}encloser.nested"
// CHECK-LABEL: !DISubprogram(name:{{.*}}nested"
// CHECK: !DILocalVariable{{.*}}nes_i
// CHECK: !DILocalVariable{{.*}}arg1
// CHECK-NOT: arg:

View file

@ -4,11 +4,14 @@
// RUN: gdb %t --batch -x %t.gdb >%t.out 2>&1
// RUN: FileCheck %s -check-prefix=CHECK < %t.out
// GDB: b _Dmain
// GDB: r
void encloser(int arg0, ref int arg1)
{
int enc_n = 123;
// GDB: b 10
// GDB: r
// GDB: b 13
// GDB: c
// GDB: p arg0
// CHECK: $1 = 1
// GDB: p arg1
@ -20,7 +23,7 @@ void encloser(int arg0, ref int arg1)
void nested(int nes_i)
{
int blub = arg0 + arg1 + enc_n;
// GDB: b 23
// GDB: b 26
// GDB: c
// GDB: p arg0
// CHECK: $4 = 1
@ -29,7 +32,7 @@ void encloser(int arg0, ref int arg1)
// GDB: p enc_n
// CHECK: $6 = 125
arg0 = arg1 = enc_n = nes_i;
// GDB: b 32
// GDB: b 35
// GDB: c
// GDB: p arg0
// CHECK: $7 = 456
@ -40,7 +43,7 @@ void encloser(int arg0, ref int arg1)
}
nested(456);
// GDB: b 43
// GDB: b 46
// GDB: c
// GDB: p arg0
// no-CHECK: $10 = 456 (`<optimized out>` for LLVM < 5.0)

135
tests/debuginfo/print_gdb.d Normal file
View file

@ -0,0 +1,135 @@
// REQUIRES: atleast_gdb80
// RUN: %ldc %_gdb_dflags -I%S -g -of=%t %s %S/inputs/import_a.d %S/inputs/import_b.d
// RUN: sed -e "/^\\/\\/ GDB:/!d" -e "s,// GDB:,," %s >%t.gdb
// RUN: env LANG=C gdb %t --batch -x %t.gdb >%t.out 2>&1
// RUN: FileCheck %s -check-prefix=CHECK < %t.out
module print_gdb;
import inputs.import_a;
__gshared int globVal = 987;
enum eA { ABC = 2, ZYX }
struct sA
{
static int someVal = 246;
}
struct sB
{
uint k = 9;
uint memberFunc(uint a) { return k*k+a; }
static staticFunc(uint b) { return b * 2; }
}
class cC
{
char c = '0';
char classMemberFunc(byte a) { return cast(char)(cast(byte)c+a); }
static classStaticFunc(byte b) { return cast(char)(cast(byte)'a' + b); }
mixin mix;
}
struct templatedStruct(T)
{
T z;
T pal(T m) { return z * m; }
}
mixin template mix()
{
uint mixedVal = 5;
}
double foo(double plus)
{
return 42.13 + plus;
}
void main()
{
// GDB: b _Dmain
// GDB: r
uint n = 5;
n = globVal; // reference every global symbol in main() to have them emitted
n = sA.someVal;
n = cast(int) a_sA.statChar;
foo(242.0);
bar();
// BP
// GDB: b 66
// GDB: c
// GDB: p globVal
// CHECK: = 987
// GDB: p sA.someVal
// CHECK: = 246
// GDB: p a_sA.statChar
// CHECK: = 67 'C'
// GDB: p foo(0.12)
// CHECK: = 42.25
sB strB;
strB.k = 12;
strB.k = strB.memberFunc(2);
strB.k = strB.staticFunc(3); // k = 6
eA e = eA.ZYX;
import inputs.import_b;
b_Glob = 99.88;
inputs.import_b.b_cA.staticVal = 78;
// BP
// GDB: b 89
// GDB: c
// GDB: p strB
// GDB: p strB.memberFunc(4)
// CHECK: = 40
// GDB: p sB.staticFunc(44)
// CHECK: = 88
// GDB: whatis eA
// CHECK: type = print_gdb.eA
// GDB: whatis print_gdb.eA
// CHECK: type = print_gdb.eA
// GDB: p b_Glob
// CHECK: = 99.8
cC clsC = new cC;
clsC.classMemberFunc(2);
cC.classStaticFunc(4);
clsC.mixedVal++;
// BP
// GDB: b 109
// GDB: c
// GDB: p *clsC
// GDB: p clsC.classMemberFunc(6)
// CHECK: = 54 '6'
// GDB: p clsC.classStaticFunc(4)
// CHECK: = 101 'e'
// GDB: p clsC.mixedVal
// CHECK: = 6
// class cD { static int staticVal = 741; }
// FIXME: GDB doesn't support access to nested type declarations yet
templatedStruct!int tsI;
templatedStruct!float tsF;
// BP
// GDB: b 126
// GDB: c
// GDB: whatis tsF
// CHECK: type = print_gdb.templatedStruct
}
// GDB: c
// GDB: q
// CHECK: exited normally

View file

@ -20,7 +20,7 @@ int main(string[] args)
// CDB: bp `strings_cdb.d:22`
// CDB: g
return 0;
// CHECK: !D main
// CHECK: !strings_cdb::D main
// CDB: dt string
// CHECK: !string

View file

@ -183,4 +183,7 @@ if lit.util.which('gdb', config.environment['PATH']):
gdb_version = m.group(1)
if LooseVersion(gdb_version) < LooseVersion('7.8'):
gdb_dflags = '-dwarf-version=2'
elif LooseVersion(gdb_version) >= LooseVersion('8.0'):
config.available_features.add('atleast_gdb80')
config.substitutions.append( ('%_gdb_dflags', gdb_dflags) )