diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 1c4abb6427..72f1711aa5 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -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 diff --git a/gen/dibuilder.cpp b/gen/dibuilder.cpp index 76c1dfd1c0..ca117c2db8 100644 --- a/gen/dibuilder.cpp +++ b/gen/dibuilder.cpp @@ -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 //////////////////////////////////////////////////////////////////////////////// @@ -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(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 &elems) { + auto scope = CreateCompositeTypeDescription(ad->getType()); + + std::function 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(ir->diCompositeType) != nullptr) { - return ir->diCompositeType; + if (static_cast(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(ret)); - ir->diCompositeType = ret; + irAggr->diCompositeType = DBuilder.replaceTemporary( + llvm::TempDINode(irAggr->diCompositeType), static_cast(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(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(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(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(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(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 diff --git a/gen/dibuilder.h b/gen/dibuilder.h index acf41270c4..aa043e677a 100644 --- a/gen/dibuilder.h +++ b/gen/dibuilder.h @@ -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> 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 &elems); + void AddStaticMembers(AggregateDeclaration *sd, ldc::DIFile file, + llvm::SmallVector &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(); diff --git a/gen/functions.cpp b/gen/functions.cpp index c319b87055..85dffdc67b 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -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)); diff --git a/gen/modules.cpp b/gen/modules.cpp index 3699788074..a8cec0a27c 100644 --- a/gen/modules.cpp +++ b/gen/modules.cpp @@ -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 diff --git a/gen/statements.cpp b/gen/statements.cpp index cb35f67f76..f031a0284b 100644 --- a/gen/statements.cpp +++ b/gen/statements.cpp @@ -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(s)); + } } ////////////////////////////////////////////////////////////////////////// diff --git a/ir/iraggr.h b/ir/iraggr.h index 3bf720b814..bb26d617ea 100644 --- a/ir/iraggr.h +++ b/ir/iraggr.h @@ -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. diff --git a/ir/irclass.cpp b/ir/irclass.cpp index a99479ba33..762f1c901c 100644 --- a/ir/irclass.cpp +++ b/ir/irclass.cpp @@ -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 diff --git a/ir/irmodule.h b/ir/irmodule.h index 9dd915c5c5..39863b7869 100644 --- a/ir/irmodule.h +++ b/ir/irmodule.h @@ -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; }; diff --git a/ir/irtypeaggr.h b/ir/irtypeaggr.h index 5e7a27c7d9..ec6d8d82f8 100644 --- a/ir/irtypeaggr.h +++ b/ir/irtypeaggr.h @@ -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; diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index 048e2f5d70..f9e116f36f 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit 048e2f5d700251cd4516e1bfcd1fe664723b7866 +Subproject commit f9e116f36fa565327edd0256ff2b414c3c6afebd diff --git a/tests/debuginfo/args_cdb.d b/tests/debuginfo/args_cdb.d index c79c2c5e9f..4f9c6d27fc 100644 --- a/tests/debuginfo/args_cdb.d +++ b/tests/debuginfo/args_cdb.d @@ -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) diff --git a/tests/debuginfo/baseclass_cdb.d b/tests/debuginfo/baseclass_cdb.d index c59cb60450..5a149cbffe 100644 --- a/tests/debuginfo/baseclass_cdb.d +++ b/tests/debuginfo/baseclass_cdb.d @@ -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}} diff --git a/tests/debuginfo/basictypes_cdb.d b/tests/debuginfo/basictypes_cdb.d index 2c8e3740d7..32b4b6557b 100644 --- a/tests/debuginfo/basictypes_cdb.d +++ b/tests/debuginfo/basictypes_cdb.d @@ -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 diff --git a/tests/debuginfo/inputs/import_a.d b/tests/debuginfo/inputs/import_a.d new file mode 100644 index 0000000000..67b62e2805 --- /dev/null +++ b/tests/debuginfo/inputs/import_a.d @@ -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'; +} diff --git a/tests/debuginfo/inputs/import_b.d b/tests/debuginfo/inputs/import_b.d new file mode 100644 index 0000000000..7b5375fc92 --- /dev/null +++ b/tests/debuginfo/inputs/import_b.d @@ -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; +} diff --git a/tests/debuginfo/nested.d b/tests/debuginfo/nested.d index a8a78451f9..a811483110 100644 --- a/tests/debuginfo/nested.d +++ b/tests/debuginfo/nested.d @@ -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: diff --git a/tests/debuginfo/nested_gdb.d b/tests/debuginfo/nested_gdb.d index 803dfc6e41..7609004980 100644 --- a/tests/debuginfo/nested_gdb.d +++ b/tests/debuginfo/nested_gdb.d @@ -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 (`` for LLVM < 5.0) diff --git a/tests/debuginfo/print_gdb.d b/tests/debuginfo/print_gdb.d new file mode 100644 index 0000000000..33ae04d5dc --- /dev/null +++ b/tests/debuginfo/print_gdb.d @@ -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 diff --git a/tests/debuginfo/strings_cdb.d b/tests/debuginfo/strings_cdb.d index fe56a4b882..80ca8eaacd 100644 --- a/tests/debuginfo/strings_cdb.d +++ b/tests/debuginfo/strings_cdb.d @@ -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 diff --git a/tests/lit.site.cfg.in b/tests/lit.site.cfg.in index 775308e5eb..fadb390496 100644 --- a/tests/lit.site.cfg.in +++ b/tests/lit.site.cfg.in @@ -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) )