mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-05-07 11:26:02 +03:00
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:
commit
c7c3f56415
21 changed files with 479 additions and 116 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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
|
||||
|
|
12
tests/debuginfo/inputs/import_a.d
Normal file
12
tests/debuginfo/inputs/import_a.d
Normal 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';
|
||||
}
|
10
tests/debuginfo/inputs/import_b.d
Normal file
10
tests/debuginfo/inputs/import_b.d
Normal 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;
|
||||
}
|
|
@ -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:
|
||||
|
|
|
@ -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
135
tests/debuginfo/print_gdb.d
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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) )
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue