From a03a85a425842ff5528caec7e7d6ebfdf4528a62 Mon Sep 17 00:00:00 2001 From: Adrian Prantl Date: Wed, 6 Mar 2013 22:03:30 +0000 Subject: [PATCH] Ensure that DIType is regenerated after we visit an implementation that adds ivars to an interface. Fixes rdar://13175234 This is an update to r176116 that performs a smart caching of interfaces. llvm-svn: 176584 --- clang/include/clang/AST/DeclObjC.h | 7 ++- clang/lib/AST/DeclObjC.cpp | 53 +++++++++++------- clang/lib/CodeGen/CGDebugInfo.cpp | 63 +++++++++++++++++++--- clang/lib/CodeGen/CGDebugInfo.h | 11 ++++ .../test/CodeGenObjC/debug-info-ivars-extension.m | 28 ++++++++++ clang/test/CodeGenObjC/debug-info-ivars-indirect.m | 32 +++++++++++ clang/test/CodeGenObjC/debug-info-ivars-private.m | 36 +++++++++++++ 7 files changed, 203 insertions(+), 27 deletions(-) create mode 100644 clang/test/CodeGenObjC/debug-info-ivars-extension.m create mode 100644 clang/test/CodeGenObjC/debug-info-ivars-indirect.m create mode 100644 clang/test/CodeGenObjC/debug-info-ivars-private.m diff --git a/clang/include/clang/AST/DeclObjC.h b/clang/include/clang/AST/DeclObjC.h index 1c5588f..43f255f 100644 --- a/clang/include/clang/AST/DeclObjC.h +++ b/clang/include/clang/AST/DeclObjC.h @@ -651,6 +651,10 @@ class ObjCInterfaceDecl : public ObjCContainerDecl /// completed by the external AST source when required. mutable bool ExternallyCompleted : 1; + /// \brief Indicates that the ivar cache does not yet include ivars + /// declared in the implementation. + mutable bool IvarListMissingImplementation : 1; + /// \brief The location of the superclass, if any. SourceLocation SuperClassLoc; @@ -660,7 +664,8 @@ class ObjCInterfaceDecl : public ObjCContainerDecl SourceLocation EndLoc; DefinitionData() : Definition(), SuperClass(), CategoryList(), IvarList(), - ExternallyCompleted() { } + ExternallyCompleted(), + IvarListMissingImplementation(true) { } }; ObjCInterfaceDecl(DeclContext *DC, SourceLocation atLoc, IdentifierInfo *Id, diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp index d539e00..d1bf9a9 100644 --- a/clang/lib/AST/DeclObjC.cpp +++ b/clang/lib/AST/DeclObjC.cpp @@ -1093,38 +1093,51 @@ namespace { /// all_declared_ivar_begin - return first ivar declared in this class, /// its extensions and its implementation. Lazily build the list on first /// access. +/// +/// Caveat: The list returned by this method reflects the current +/// state of the parser. The cache will be updated for every ivar +/// added by an extension or the implementation when they are +/// encountered. +/// See also ObjCIvarDecl::Create(). ObjCIvarDecl *ObjCInterfaceDecl::all_declared_ivar_begin() { // FIXME: Should make sure no callers ever do this. if (!hasDefinition()) return 0; - if (data().IvarList) - return data().IvarList; - ObjCIvarDecl *curIvar = 0; - if (!ivar_empty()) { - ObjCInterfaceDecl::ivar_iterator I = ivar_begin(), E = ivar_end(); - data().IvarList = *I; ++I; - for (curIvar = data().IvarList; I != E; curIvar = *I, ++I) - curIvar->setNextIvar(*I); - } + if (!data().IvarList) { + if (!ivar_empty()) { + ObjCInterfaceDecl::ivar_iterator I = ivar_begin(), E = ivar_end(); + data().IvarList = *I; ++I; + for (curIvar = data().IvarList; I != E; curIvar = *I, ++I) + curIvar->setNextIvar(*I); + } - for (ObjCInterfaceDecl::known_extensions_iterator - Ext = known_extensions_begin(), - ExtEnd = known_extensions_end(); - Ext != ExtEnd; ++Ext) { - if (!Ext->ivar_empty()) { - ObjCCategoryDecl::ivar_iterator I = Ext->ivar_begin(),E = Ext->ivar_end(); - if (!data().IvarList) { - data().IvarList = *I; ++I; - curIvar = data().IvarList; + for (ObjCInterfaceDecl::known_extensions_iterator + Ext = known_extensions_begin(), + ExtEnd = known_extensions_end(); + Ext != ExtEnd; ++Ext) { + if (!Ext->ivar_empty()) { + ObjCCategoryDecl::ivar_iterator + I = Ext->ivar_begin(), + E = Ext->ivar_end(); + if (!data().IvarList) { + data().IvarList = *I; ++I; + curIvar = data().IvarList; + } + for ( ;I != E; curIvar = *I, ++I) + curIvar->setNextIvar(*I); } - for ( ;I != E; curIvar = *I, ++I) - curIvar->setNextIvar(*I); } + data().IvarListMissingImplementation = true; } + + // cached and complete! + if (!data().IvarListMissingImplementation) + return data().IvarList; if (ObjCImplementationDecl *ImplDecl = getImplementation()) { + data().IvarListMissingImplementation = false; if (!ImplDecl->ivar_empty()) { SmallVector layout; for (ObjCImplementationDecl::ivar_iterator I = ImplDecl->ivar_begin(), diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 970ba50..8d6352c 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -1343,7 +1343,7 @@ llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty) { LexicalBlockStack.push_back(FwdDeclNode); RegionMap[Ty->getDecl()] = llvm::WeakVH(FwdDecl); - // Add this to the completed types cache since we're completing it. + // Add this to the completed-type cache while we're completing it recursively. CompletedTypeCache[QualType(Ty, 0).getAsOpaquePtr()] = FwdDecl; // Convert all the elements. @@ -1436,7 +1436,8 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, // Otherwise, insert it into the CompletedTypeCache so that recursive uses // will find it and we're emitting the complete type. - CompletedTypeCache[QualType(Ty, 0).getAsOpaquePtr()] = RealDecl; + QualType QualTy = QualType(Ty, 0); + CompletedTypeCache[QualTy.getAsOpaquePtr()] = RealDecl; // Push the struct on region stack. llvm::TrackingVH FwdDeclNode(RealDecl); @@ -1561,6 +1562,12 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty, llvm::DIArray Elements = DBuilder.getOrCreateArray(EltTys); FwdDeclNode->replaceOperandWith(10, Elements); + + // If the implementation is not yet set, we do not want to mark it + // as complete. An implementation may declare additional + // private ivars that we would miss otherwise. + if (ID->getImplementation() == 0) + CompletedTypeCache.erase(QualTy.getAsOpaquePtr()); LexicalBlockStack.pop_back(); return llvm::DIType(FwdDeclNode); @@ -1790,14 +1797,28 @@ llvm::DIType CGDebugInfo::getCompletedTypeOrNull(QualType Ty) { Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext()); // Check for existing entry. + llvm::Value *V = 0; llvm::DenseMap::iterator it = CompletedTypeCache.find(Ty.getAsOpaquePtr()); - if (it != CompletedTypeCache.end()) { - // Verify that the debug info still exists. - if (llvm::Value *V = it->second) - return llvm::DIType(cast(V)); + if (it != CompletedTypeCache.end()) + V = it->second; + else { + // Is there a cached interface that hasn't changed? + llvm::DenseMap > + ::iterator it1 = ObjCInterfaceCache.find(Ty.getAsOpaquePtr()); + + if (it1 != ObjCInterfaceCache.end()) + if (ObjCInterfaceDecl* Decl = getObjCInterfaceDecl(Ty)) + if (Checksum(Decl) == it1->second.second) { + // Return cached type. + V = it1->second.first; + } } + // Verify that any cached debug info still exists. + if (V != 0) + return llvm::DIType(cast(V)); + return llvm::DIType(); } @@ -1824,6 +1845,16 @@ llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) { ReplaceMap.push_back(std::make_pair(Ty.getAsOpaquePtr(), static_cast(TC))); + // Do not cache the type if it may be incomplete. + if (ObjCInterfaceDecl* Decl = getObjCInterfaceDecl(Ty)) { + // clang::ParseAST handles each TopLevelDecl immediately after it was parsed. + // A subsequent implementation may add more ivars to an interface, which is + // why we cache it together with a checksum to see if it changed. + ObjCInterfaceCache[Ty.getAsOpaquePtr()] = + std::make_pair(Res, Checksum(Decl)); + return Res; + } + // And update the type cache. TypeCache[Ty.getAsOpaquePtr()] = Res; @@ -1833,6 +1864,26 @@ llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) { return Res; } +/// Currently the checksum merely consists of the number of ivars. +unsigned CGDebugInfo::Checksum(const ObjCInterfaceDecl + *InterfaceDecl) { + unsigned IvarNo = 0; + for (const ObjCIvarDecl *Ivar = InterfaceDecl->all_declared_ivar_begin(); + Ivar != 0; Ivar = Ivar->getNextIvar()) ++IvarNo; + return IvarNo; +} + +ObjCInterfaceDecl *CGDebugInfo::getObjCInterfaceDecl(QualType Ty) { + switch (Ty->getTypeClass()) { + case Type::ObjCObjectPointer: + return getObjCInterfaceDecl(cast(Ty)->getPointeeType()); + case Type::ObjCInterface: + return cast(Ty)->getDecl(); + default: + return 0; + } +} + /// CreateTypeNode - Create a new debug type node. llvm::DIType CGDebugInfo::CreateTypeNode(QualType Ty, llvm::DIFile Unit) { // Handle qualifiers, which recursively handles what they refer to. diff --git a/clang/lib/CodeGen/CGDebugInfo.h b/clang/lib/CodeGen/CGDebugInfo.h index fbbee0b..9650f2e 100644 --- a/clang/lib/CodeGen/CGDebugInfo.h +++ b/clang/lib/CodeGen/CGDebugInfo.h @@ -32,6 +32,7 @@ namespace clang { class CXXMethodDecl; class VarDecl; class ObjCInterfaceDecl; + class ObjCIvarDecl; class ClassTemplateSpecializationDecl; class GlobalDecl; @@ -60,6 +61,11 @@ class CGDebugInfo { /// TypeCache - Cache of previously constructed Types. llvm::DenseMap TypeCache; + /// ObjCInterfaceCache - Cache of previously constructed interfaces + /// which may change. Storing a pair of DIType and checksum. + llvm::DenseMap > + ObjCInterfaceCache; + /// CompleteTypeCache - Cache of previously constructed complete RecordTypes. llvm::DenseMap CompletedTypeCache; @@ -89,6 +95,7 @@ class CGDebugInfo { llvm::DenseMap StaticDataMemberCache; /// Helper functions for getOrCreateType. + unsigned Checksum(const ObjCInterfaceDecl *InterfaceDecl); llvm::DIType CreateType(const BuiltinType *Ty); llvm::DIType CreateType(const ComplexType *Ty); llvm::DIType CreateQualifiedType(QualType Ty, llvm::DIFile F); @@ -299,6 +306,10 @@ private: /// CreateTypeNode - Create type metadata for a source language type. llvm::DIType CreateTypeNode(QualType Ty, llvm::DIFile F); + /// getObjCInterfaceDecl - return the underlying ObjCInterfaceDecl + /// if Ty is an ObjCInterface or a pointer to one. + ObjCInterfaceDecl* getObjCInterfaceDecl(QualType Ty); + /// CreateLimitedTypeNode - Create type metadata for a source language /// type, but only partial types for records. llvm::DIType CreateLimitedTypeNode(QualType Ty, llvm::DIFile F); diff --git a/clang/test/CodeGenObjC/debug-info-ivars-extension.m b/clang/test/CodeGenObjC/debug-info-ivars-extension.m new file mode 100644 index 0000000..aca1917 --- /dev/null +++ b/clang/test/CodeGenObjC/debug-info-ivars-extension.m @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -g %s -o - | FileCheck %s + +// Make sure we generate debug symbols for ivars added by a class extension. + +@interface I +{ + @public int a; +} +@end + +void foo(I* pi) { + // poking into pi for primary class ivars. + int _a = pi->a; +} + +@interface I() +{ + @public int b; +} +@end + +void gorf (I* pg) { + // poking into pg for ivars for class extension + int _b = pg->b; +} + +// CHECK: metadata !{i32 {{[0-9]*}}, metadata !{{[0-9]*}}, metadata !"a", metadata !{{[0-9]*}}, i32 7, i64 32, i64 32, i64 0, i32 0, metadata !{{[0-9]*}}, null} ; [ DW_TAG_member ] [a] [line 7, size 32, align 32, offset 0] [from int] +// CHECK: metadata !{i32 {{[0-9]*}}, metadata !{{[0-9]*}}, metadata !"b", metadata !{{[0-9]*}}, i32 18, i64 32, i64 32, i64 0, i32 0, metadata !{{[0-9]*}}, null} ; [ DW_TAG_member ] [b] [line 18, size 32, align 32, offset 0] [from int] diff --git a/clang/test/CodeGenObjC/debug-info-ivars-indirect.m b/clang/test/CodeGenObjC/debug-info-ivars-indirect.m new file mode 100644 index 0000000..9f7f940 --- /dev/null +++ b/clang/test/CodeGenObjC/debug-info-ivars-indirect.m @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -g %s -o - | FileCheck %s + +// Make sure we generate debug symbols for an indirectly referenced +// extension to an interface. + +@interface I +{ + @public int a; +} +@end + +void foo(I* pi) { + int _a = pi->a; +} + +// another layer of indirection +struct S +{ + I* i; +}; + +@interface I() +{ + @public int b; +} +@end + +void gorf (struct S* s) { + int _b = s->i->b; +} + +// CHECK: metadata !{i32 {{[0-9]*}}, metadata !{{[0-9]*}}, metadata !"b", metadata !{{[0-9]*}}, i32 24, i64 32, i64 32, i64 0, i32 0, metadata !{{[0-9]*}}, null} ; [ DW_TAG_member ] [b] [line 24, size 32, align 32, offset 0] [from int] diff --git a/clang/test/CodeGenObjC/debug-info-ivars-private.m b/clang/test/CodeGenObjC/debug-info-ivars-private.m new file mode 100644 index 0000000..0a555c2 --- /dev/null +++ b/clang/test/CodeGenObjC/debug-info-ivars-private.m @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -g %s -o - | FileCheck %s + +// Debug symbols for private ivars. This test ensures that we are +// generating debug info for ivars added by the implementation. +__attribute((objc_root_class)) @interface NSObject { + id isa; +} +@end + +@protocol Protocol +@end + +@interface Delegate : NSObject { + @protected int foo; +} +@end + +@interface Delegate(NSObject) +- (void)f; +@end + +@implementation Delegate(NSObject) +- (void)f { return; } +@end + +@implementation Delegate { + int bar; +} + +- (void)g:(NSObject*) anObject { + bar = foo; +} +@end + +// CHECK: metadata !{i32 {{[0-9]*}}, metadata !{{[0-9]*}}, metadata !"foo", metadata !{{[0-9]*}}, i32 14, i64 32, i64 32, i64 0, i32 2, metadata !{{[0-9]*}}, null} ; [ DW_TAG_member ] [foo] [line 14, size 32, align 32, offset 0] [protected] [from int] +// CHECK: metadata !{i32 {{[0-9]*}}, metadata !{{[0-9]*}}, metadata !"bar", metadata !{{[0-9]*}}, i32 27, i64 32, i64 32, i64 0, i32 1, metadata !{{[0-9]*}}, null} ; [ DW_TAG_member ] [bar] [line 27, size 32, align 32, offset 0] [private] [from int] -- 2.7.4