From 53a314a227d8c869f4fe3f7bcdb052c8642afd5e Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Fri, 11 Sep 2015 01:03:26 +0000 Subject: [PATCH] Module Debugging: Emit forward declarations for types that are defined in clang modules, if -dwarf-ext-refs (DebugTypesExtRefs) is specified. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@247369 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/CGDebugInfo.cpp | 154 +++++++++++++++++++++++++++++++--- lib/CodeGen/CGDebugInfo.h | 9 ++ test/Modules/ExtDebugInfo.cpp | 74 ++++++++++++++++ test/Modules/ExtDebugInfo.m | 29 +++++++ 4 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 test/Modules/ExtDebugInfo.cpp create mode 100644 test/Modules/ExtDebugInfo.m diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 693d0f6fdf..7f9216a10c 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -148,7 +148,9 @@ void CGDebugInfo::setLocation(SourceLocation Loc) { } llvm::DIScope *CGDebugInfo::getDeclContextDescriptor(const Decl *D) { - return getContextDescriptor(cast(D->getDeclContext()), TheCU); + llvm::DIScope *Mod = getParentModuleOrNull(D); + return getContextDescriptor(cast(D->getDeclContext()), + Mod ? Mod : TheCU); } llvm::DIScope *CGDebugInfo::getContextDescriptor(const Decl *Context, @@ -1448,6 +1450,9 @@ void CGDebugInfo::completeRequiredType(const RecordDecl *RD) { if (CXXDecl->isDynamicClass()) return; + if (DebugTypeExtRefs && RD->isFromASTFile()) + return; + QualType Ty = CGM.getContext().getRecordType(RD); llvm::DIType *T = getTypeOrNull(Ty); if (T && T->isForwardDecl()) @@ -1669,9 +1674,9 @@ CGDebugInfo::getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod) { TheCU->getSourceLanguage(), internString(Mod.ModuleName), internString(Mod.Path), TheCU->getProducer(), true, StringRef(), 0, internString(Mod.ASTFile), llvm::DIBuilder::FullDebug, Mod.Signature); - llvm::DIModule *M = - DIB.createModule(CU, Mod.ModuleName, ConfigMacros, internString(Mod.Path), - internString(CGM.getHeaderSearchOpts().Sysroot)); + llvm::DIModule *M = DIB.createModule( + CU, Mod.ModuleName, ConfigMacros, internString(Mod.Path), + internString(CGM.getHeaderSearchOpts().Sysroot)); DIB.finalize(); ModRef.reset(M); return M; @@ -2081,9 +2086,16 @@ llvm::DIType *CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile *Unit) { if (auto *T = getTypeOrNull(Ty)) return T; + llvm::DIType *Res = nullptr; + if (DebugTypeExtRefs) + // Make a forward declaration of an external type. + Res = getTypeExtRefOrNull(Ty, Unit); + // Otherwise create the type. - llvm::DIType *Res = CreateTypeNode(Ty, Unit); - void *TyPtr = Ty.getAsOpaquePtr(); + if (!Res) + Res = CreateTypeNode(Ty, Unit); + + void* TyPtr = Ty.getAsOpaquePtr(); // And update the type cache. TypeCache[TyPtr].reset(Res); @@ -2115,6 +2127,123 @@ ObjCInterfaceDecl *CGDebugInfo::getObjCInterfaceDecl(QualType Ty) { } } +llvm::DIModule *CGDebugInfo::getParentModuleOrNull(const Decl *D) { + if (!DebugTypeExtRefs || !D || !D->isFromASTFile()) + return nullptr; + + llvm::DIModule *ModuleRef = nullptr; + auto *Reader = CGM.getContext().getExternalSource(); + auto Idx = D->getOwningModuleID(); + auto Info = Reader->getSourceDescriptor(Idx); + if (Info) + ModuleRef = getOrCreateModuleRef(*Info); + return ModuleRef; +} + +llvm::DIType *CGDebugInfo::getTypeExtRefOrNull(QualType Ty, llvm::DIFile *F, + bool Anchored) { + assert(DebugTypeExtRefs && "module debugging only"); + Decl *TyDecl = nullptr; + StringRef Name; + SmallString<256> UID; + unsigned Tag = 0; + + // Handle all types that have a declaration. + switch (Ty->getTypeClass()) { + case Type::Typedef: { + TyDecl = cast(Ty)->getDecl(); + if (!TyDecl->isFromASTFile()) + return nullptr; + + // A typedef will anchor a type in the module. + if (auto *TD = dyn_cast(TyDecl)) { + // This is a working around the fact that LLVM does not allow + // typedefs to be forward declarations. + QualType Ty = TD->getUnderlyingType(); + Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext()); + if (auto *AnchoredTy = getTypeExtRefOrNull(Ty, F, /*Anchored=*/true)) { + TypeCache[Ty.getAsOpaquePtr()].reset(AnchoredTy); + SourceLocation Loc = TD->getLocation(); + return DBuilder.createTypedef(AnchoredTy, TD->getName(), + getOrCreateFile(Loc), getLineNumber(Loc), + getDeclContextDescriptor(TD)); + } + } + break; + } + + case Type::Record: { + TyDecl = cast(Ty)->getDecl(); + if (!TyDecl->isFromASTFile()) + return nullptr; + + if (auto *CTSD = dyn_cast(TyDecl)) + if (!CTSD->isExplicitInstantiationOrSpecialization() && !Anchored) + // We may not assume that this type made it into the module. + return nullptr; + // C++ classes and template instantiations. + if (auto *RD = dyn_cast(TyDecl)) { + if (!RD->getDefinition()) + return nullptr; + Tag = getTagForRecord(RD); + UID = + getUniqueTagTypeName(cast(RD->getTypeForDecl()), CGM, TheCU); + Name = getClassName(RD); + } else if (auto *RD = dyn_cast(TyDecl)) { + // C-style structs. + if (!RD->getDefinition()) + return nullptr; + Tag = getTagForRecord(RD); + Name = getClassName(RD); + } + break; + } + + case Type::Enum: { + TyDecl = cast(Ty)->getDecl(); + if (!TyDecl->isFromASTFile()) + return nullptr; + + if (auto *ED = dyn_cast(TyDecl)) { + if (!ED->getDefinition()) + return nullptr; + Tag = llvm::dwarf::DW_TAG_enumeration_type; + if ((TheCU->getSourceLanguage() == llvm::dwarf::DW_LANG_C_plus_plus) || + (TheCU->getSourceLanguage() == llvm::dwarf::DW_LANG_ObjC_plus_plus)) { + UID = getUniqueTagTypeName(cast(ED->getTypeForDecl()), CGM, + TheCU); + Name = ED->getName(); + } + } + break; + } + + case Type::ObjCInterface: { + TyDecl = cast(Ty)->getDecl(); + if (!TyDecl->isFromASTFile()) + return nullptr; + + if (auto *ID = dyn_cast(TyDecl)) { + if (!ID->getDefinition()) + return nullptr; + Tag = llvm::dwarf::DW_TAG_structure_type; + Name = ID->getName(); + } + break; + } + + default: + return nullptr; + } + + if (Tag && !Name.empty()) { + assert(TyDecl); + auto *Ctx = getDeclContextDescriptor(TyDecl); + return DBuilder.createForwardDecl(Tag, Name, Ctx, F, 0, 0, 0, 0, UID); + } else + return nullptr; +} + llvm::DIType *CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile *Unit) { // Handle qualifiers, which recursively handles what they refer to. if (Ty.hasLocalQualifiers()) @@ -2325,8 +2454,10 @@ void CGDebugInfo::collectFunctionDeclProps(GlobalDecl GD, llvm::DIFile *Unit, dyn_cast_or_null(FD->getDeclContext())) FDContext = getOrCreateNameSpace(NSDecl); else if (const RecordDecl *RDecl = - dyn_cast_or_null(FD->getDeclContext())) - FDContext = getContextDescriptor(RDecl, TheCU); + dyn_cast_or_null(FD->getDeclContext())) { + llvm::DIScope *Mod = getParentModuleOrNull(RDecl); + FDContext = getContextDescriptor(RDecl, Mod ? Mod : TheCU); + } // Collect template parameters. TParamsArray = CollectFunctionTemplateParams(FD, Unit); } @@ -2374,7 +2505,9 @@ void CGDebugInfo::collectVarDeclProps(const VarDecl *VD, llvm::DIFile *&Unit, // outside the class by putting it in the global scope. if (DC->isRecord()) DC = CGM.getContext().getTranslationUnitDecl(); - VDContext = getContextDescriptor(cast(DC), TheCU); + + llvm::DIScope *Mod = getParentModuleOrNull(VD); + VDContext = getContextDescriptor(cast(DC), Mod ? Mod : TheCU); } llvm::DISubprogram * @@ -3299,7 +3432,8 @@ void CGDebugInfo::EmitGlobalVariable(const ValueDecl *VD, llvm::DIScope *CGDebugInfo::getCurrentContextDescriptor(const Decl *D) { if (!LexicalBlockStack.empty()) return LexicalBlockStack.back(); - return getContextDescriptor(D, TheCU); + llvm::DIScope *Mod = getParentModuleOrNull(D); + return getContextDescriptor(D, Mod ? Mod : TheCU); } void CGDebugInfo::EmitUsingDirective(const UsingDirectiveDecl &UD) { diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index 507496ba3a..0872aa3e27 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -397,6 +397,15 @@ private: llvm::DIModule * getOrCreateModuleRef(ExternalASTSource::ASTSourceDescriptor Mod); + /// DebugTypeExtRefs: If \p D originated in a clang module, return it. + llvm::DIModule *getParentModuleOrNull(const Decl *D); + + /// Return a forward declaration of an external type, if this type + /// came from a clang module. If \p Anchored is true, template + /// types will be assumed to have been instantiated in the module. + llvm::DIType *getTypeExtRefOrNull(QualType Ty, llvm::DIFile *F, + bool Anchored = false); + /// Get the type from the cache or create a new partial type if /// necessary. llvm::DICompositeType *getOrCreateLimitedType(const RecordType *Ty, diff --git a/test/Modules/ExtDebugInfo.cpp b/test/Modules/ExtDebugInfo.cpp new file mode 100644 index 0000000000..a514e9d4a3 --- /dev/null +++ b/test/Modules/ExtDebugInfo.cpp @@ -0,0 +1,74 @@ +// RUN: rm -rf %t +// Test that only forward declarations are emitted for types dfined in modules. + +// Modules: +// RUN: %clang_cc1 -x objective-c++ -std=c++11 -g -dwarf-ext-refs -fmodules \ +// RUN: -fmodule-format=obj -fimplicit-module-maps -DMODULES \ +// RUN: -fmodules-cache-path=%t %s -I %S/Inputs -I %t -emit-llvm -o %t-mod.ll +// RUN: cat %t-mod.ll | FileCheck %s + +// PCH: +// RUN: %clang_cc1 -x c++ -std=c++11 -fmodule-format=obj -emit-pch -I%S/Inputs \ +// RUN: -o %t.pch %S/Inputs/DebugCXX.h +// RUN: %clang_cc1 -std=c++11 -g -dwarf-ext-refs -fmodule-format=obj \ +// RUN: -include-pch %t.pch %s -emit-llvm -o %t-pch.ll %s +// RUN: cat %t-pch.ll | FileCheck %s + +#ifdef MODULES +@import DebugCXX; +#endif + +using DebugCXX::Struct; + +Struct s; +DebugCXX::Enum e; +DebugCXX::Template implicitTemplate; +DebugCXX::Template explicitTemplate; +DebugCXX::FloatInstatiation typedefTemplate; +int Struct::static_member = -1; +enum { + e3 = -1 +} conflicting_uid = e3; +auto anon_enum = DebugCXX::e2; +char _anchor = anon_enum + conflicting_uid; + +// CHECK: ![[ANON_ENUM:[0-9]+]] = !DICompositeType(tag: DW_TAG_enumeration_type +// CHECK-SAME: scope: ![[MOD:[0-9]+]], +// CHECK-SAME: {{.*}}line: 16, {{.*}}, elements: ![[EE:[0-9]+]]) + +// CHECK: ![[NS:.*]] = !DINamespace(name: "DebugCXX", scope: ![[MOD:[0-9]+]], +// CHECK: ![[MOD]] = !DIModule(scope: null, name: {{.*}}DebugCXX + +// CHECK: ![[EE]] = !{![[E2:[0-9]+]]} +// CHECK: ![[E2]] = !DIEnumerator(name: "e2", value: 50) + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Struct", +// CHECK-SAME: scope: ![[NS]], +// CHECK-SAME: flags: DIFlagFwdDecl, +// CHECK-SAME: identifier: "_ZTSN8DebugCXX6StructE") + +// CHECK: !DICompositeType(tag: DW_TAG_enumeration_type, name: "Enum", +// CHECK-SAME: scope: ![[NS]], +// CHECK-SAME: flags: DIFlagFwdDecl, +// CHECK-SAME: identifier: "_ZTSN8DebugCXX4EnumE") + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, +// CHECK-SAME: name: "Template >", +// CHECK-SAME: scope: ![[NS]], +// CHECK-SAME: flags: DIFlagFwdDecl, +// CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIiNS_6traitsIiEEEE") + +// CHECK: !DICompositeType(tag: DW_TAG_class_type, +// CHECK-SAME: name: "Template >", +// CHECK-SAME: scope: ![[NS]], +// CHECK-SAME: flags: DIFlagFwdDecl, +// CHECK-SAME: identifier: "_ZTSN8DebugCXX8TemplateIfNS_6traitsIfEEEE") + +// CHECK: !DIDerivedType(tag: DW_TAG_member, name: "static_member", +// CHECK-SAME: scope: !"_ZTSN8DebugCXX6StructE" + +// CHECK: !DIGlobalVariable(name: "anon_enum", {{.*}}, type: ![[ANON_ENUM]] + +// CHECK: !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !0, entity: !"_ZTSN8DebugCXX6StructE", line: 21) diff --git a/test/Modules/ExtDebugInfo.m b/test/Modules/ExtDebugInfo.m new file mode 100644 index 0000000000..6431a88677 --- /dev/null +++ b/test/Modules/ExtDebugInfo.m @@ -0,0 +1,29 @@ +// RUN: rm -rf %t +// Test that only forward declarations are emitted for types dfined in modules. + +// Modules: +// RUN: %clang_cc1 -x objective-c -g -dwarf-ext-refs -fmodules \ +// RUN: -fmodule-format=obj -fimplicit-module-maps -DMODULES \ +// RUN: -fmodules-cache-path=%t %s -I %S/Inputs -I %t -emit-llvm -o %t-mod.ll +// RUN: cat %t-mod.ll | FileCheck %s + +// PCH: +// RUN: %clang_cc1 -x objective-c -fmodule-format=obj -emit-pch -I%S/Inputs \ +// RUN: -o %t.pch %S/Inputs/DebugObjC.h +// RUN: %clang_cc1 -x objective-c -g -dwarf-ext-refs -fmodule-format=obj \ +// RUN: -include-pch %t.pch %s -emit-llvm -o %t-pch.ll %s +// RUN: cat %t-pch.ll | FileCheck %s + +#ifdef MODULES +@import DebugObjC; +#endif + +int foo(ObjCClass *c) { + [c instanceMethodWithInt: 0]; + return [c property]; +} + +// CHECK: !DICompositeType(tag: DW_TAG_structure_type, +// CHECK-SAME: scope: ![[MOD:[0-9]+]], +// CHECK-SAME: flags: DIFlagFwdDecl) +// CHECK: ![[MOD]] = !DIModule(scope: null, name: {{.*}}DebugObjC -- 2.40.0