Build debug info for ObjC interface types at the end of the translation unit to ensure all ivars are included.

This takes a different approach than the
completedType/requiresCompleteType work which relies on AST callbacks to
upgrade the type declaration to a definition. Instead, just defer
constructing the definition to the end of the translation unit.

This works because the definition is never needed by other debug info
(so far as I know), whereas the definition of a struct may be needed by
other debug info before the end of the translation unit (such as
emitting the definition of a member function which must refer to that
member function's declaration).

If we had a callback for whenever an IVar was added to an ObjC interface
we could use that, and remove the need for the ObjCInterfaceCache, which
might be nice. (also would need a callback for when it was more than
just a declaration so we could get properties, etc).

A side benefit is that we also don't need the CompletedTypeCache
anymore. Just rely on the declaration-ness of a type to decide whether
its definition is yet to be emitted.

There's still the PR19562 memory leak, but this should hopefully make
that a bit easier to approach.

llvm-svn: 208015
This commit is contained in:
David Blaikie 2014-05-05 23:23:53 +00:00
parent fc55cb6e14
commit ef8a951882
3 changed files with 51 additions and 110 deletions

View File

@ -1453,11 +1453,13 @@ void CGDebugInfo::completeClassData(const RecordDecl *RD) {
return;
QualType Ty = CGM.getContext().getRecordType(RD);
void* TyPtr = Ty.getAsOpaquePtr();
if (CompletedTypeCache.count(TyPtr))
auto I = TypeCache.find(TyPtr);
if (I != TypeCache.end() &&
!llvm::DIType(cast<llvm::MDNode>(static_cast<llvm::Value *>(I->second)))
.isForwardDecl())
return;
llvm::DIType Res = CreateTypeDefinition(Ty->castAs<RecordType>());
assert(!Res.isForwardDecl());
CompletedTypeCache[TyPtr] = Res;
TypeCache[TyPtr] = Res;
}
@ -1545,9 +1547,6 @@ llvm::DIType CGDebugInfo::CreateTypeDefinition(const RecordType *Ty) {
LexicalBlockStack.push_back(&*FwdDecl);
RegionMap[Ty->getDecl()] = llvm::WeakVH(FwdDecl);
// Add this to the completed-type cache while we're completing it recursively.
CompletedTypeCache[QualType(Ty, 0).getAsOpaquePtr()] = FwdDecl;
// Convert all the elements.
SmallVector<llvm::Value *, 16> EltTys;
// what about nested types?
@ -1624,15 +1623,24 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty,
// If this is just a forward declaration return a special forward-declaration
// debug type since we won't be able to lay out the entire type.
ObjCInterfaceDecl *Def = ID->getDefinition();
if (!Def) {
if (!Def || !Def->getImplementation()) {
llvm::DIType FwdDecl =
DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type,
ID->getName(), TheCU, DefUnit, Line,
RuntimeLang);
ObjCInterfaceCache.push_back(ObjCInterfaceCacheEntry(Ty, FwdDecl, Unit));
return FwdDecl;
}
ID = Def;
return CreateTypeDefinition(Ty, Unit);
}
llvm::DIType CGDebugInfo::CreateTypeDefinition(const ObjCInterfaceType *Ty, llvm::DIFile Unit) {
ObjCInterfaceDecl *ID = Ty->getDecl();
llvm::DIFile DefUnit = getOrCreateFile(ID->getLocation());
unsigned Line = getLineNumber(ID->getLocation());
unsigned RuntimeLang = TheCU.getLanguage();
// Bit size, align and offset of the type.
uint64_t Size = CGM.getContext().getTypeSize(Ty);
@ -1647,10 +1655,8 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty,
Line, Size, Align, Flags,
llvm::DIType(), llvm::DIArray(), RuntimeLang);
// Otherwise, insert it into the CompletedTypeCache so that recursive uses
// will find it and we're emitting the complete type.
QualType QualTy = QualType(Ty, 0);
CompletedTypeCache[QualTy.getAsOpaquePtr()] = RealDecl;
QualType QTy(Ty, 0);
TypeCache[QTy.getAsOpaquePtr()] = RealDecl;
// Push the struct on region stack.
LexicalBlockStack.push_back(static_cast<llvm::MDNode*>(RealDecl));
@ -1774,12 +1780,6 @@ llvm::DIType CGDebugInfo::CreateType(const ObjCInterfaceType *Ty,
llvm::DIArray Elements = DBuilder.getOrCreateArray(EltTys);
RealDecl.setTypeArray(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 RealDecl;
}
@ -2002,14 +2002,6 @@ llvm::DIType CGDebugInfo::getTypeOrNull(QualType Ty) {
// Unwrap the type as needed for debug information.
Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext());
// Check for existing entry.
if (Ty->getTypeClass() == Type::ObjCInterface) {
llvm::Value *V = getCachedInterfaceTypeOrNull(Ty);
if (V)
return llvm::DIType(cast<llvm::MDNode>(V));
else return llvm::DIType();
}
llvm::DenseMap<void *, llvm::WeakVH>::iterator it =
TypeCache.find(Ty.getAsOpaquePtr());
if (it != TypeCache.end()) {
@ -2021,27 +2013,6 @@ llvm::DIType CGDebugInfo::getTypeOrNull(QualType Ty) {
return llvm::DIType();
}
/// getCompletedTypeOrNull - Get the type from the cache or return null if it
/// doesn't exist.
llvm::DIType CGDebugInfo::getCompletedTypeOrNull(QualType Ty) {
// Unwrap the type as needed for debug information.
Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext());
// Check for existing entry.
llvm::Value *V = 0;
llvm::DenseMap<void *, llvm::WeakVH>::iterator it =
CompletedTypeCache.find(Ty.getAsOpaquePtr());
if (it != CompletedTypeCache.end())
V = it->second;
else {
V = getCachedInterfaceTypeOrNull(Ty);
}
// Verify that any cached debug info still exists.
return llvm::DIType(cast_or_null<llvm::MDNode>(V));
}
void CGDebugInfo::completeTemplateDefinition(
const ClassTemplateSpecializationDecl &SD) {
if (DebugKind <= CodeGenOptions::DebugLineTablesOnly)
@ -2053,22 +2024,6 @@ void CGDebugInfo::completeTemplateDefinition(
RetainedTypes.push_back(CGM.getContext().getRecordType(&SD).getAsOpaquePtr());
}
/// getCachedInterfaceTypeOrNull - Get the type from the interface
/// cache, unless it needs to regenerated. Otherwise return null.
llvm::Value *CGDebugInfo::getCachedInterfaceTypeOrNull(QualType Ty) {
// Is there a cached interface that hasn't changed?
llvm::DenseMap<void *, std::pair<llvm::WeakVH, unsigned > >
::iterator it1 = ObjCInterfaceCache.find(Ty.getAsOpaquePtr());
if (it1 != ObjCInterfaceCache.end())
if (ObjCInterfaceDecl* Decl = getObjCInterfaceDecl(Ty))
if (Checksum(Decl) == it1->second.second)
// Return cached forward declaration.
return it1->second.first;
return 0;
}
/// getOrCreateType - Get the type from the cache or create a new
/// one if necessary.
llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) {
@ -2078,7 +2033,7 @@ llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) {
// Unwrap the type as needed for debug information.
Ty = UnwrapTypeForDebugInfo(Ty, CGM.getContext());
if (llvm::DIType T = getCompletedTypeOrNull(Ty))
if (llvm::DIType T = getTypeOrNull(Ty))
return T;
// Otherwise create the type.
@ -2088,37 +2043,6 @@ llvm::DIType CGDebugInfo::getOrCreateType(QualType Ty, llvm::DIFile Unit) {
// And update the type cache.
TypeCache[TyPtr] = Res;
// FIXME: this getTypeOrNull call seems silly when we just inserted the type
// into the cache - but getTypeOrNull has a special case for cached interface
// types. We should probably just pull that out as a special case for the
// "else" block below & skip the otherwise needless lookup.
llvm::DIType TC = getTypeOrNull(Ty);
if (ObjCInterfaceDecl *Decl = getObjCInterfaceDecl(Ty)) {
// Interface types may have elements added to them by a
// subsequent implementation or extension, so we keep them in
// the ObjCInterfaceCache together with a checksum. Instead of
// the (possibly) incomplete interface type, we return a forward
// declaration that gets RAUW'd in CGDebugInfo::finalize().
std::pair<llvm::WeakVH, unsigned> &V = ObjCInterfaceCache[TyPtr];
if (V.first)
return llvm::DIType(cast<llvm::MDNode>(V.first));
TC = DBuilder.createForwardDecl(llvm::dwarf::DW_TAG_structure_type,
Decl->getName(), TheCU, Unit,
getLineNumber(Decl->getLocation()),
TheCU.getLanguage());
// Store the forward declaration in the cache.
V.first = TC;
V.second = Checksum(Decl);
// Register the type for replacement in finalize().
ReplaceMap.push_back(std::make_pair(TyPtr, static_cast<llvm::Value*>(TC)));
return TC;
}
if (!Res.isForwardDecl())
CompletedTypeCache[TyPtr] = Res;
return Res;
}
@ -2282,13 +2206,6 @@ llvm::DICompositeType CGDebugInfo::CreateLimitedType(const RecordType *Ty) {
// If we ended up creating the type during the context chain construction,
// just return that.
// FIXME: this could be dealt with better if the type was recorded as
// completed before we started this (see the CompletedTypeCache usage in
// CGDebugInfo::CreateTypeDefinition(const RecordType*) - that would need to
// be pushed to before context creation, but after it was known to be
// destined for completion (might still have an issue if this caller only
// required a declaration but the context construction ended up creating a
// definition)
llvm::DICompositeType T(getTypeOrNull(CGM.getContext().getRecordType(RD)));
if (T && (!T.isForwardDecl() || !RD->getDefinition()))
return T;
@ -3393,6 +3310,11 @@ void CGDebugInfo::finalize() {
Ty.replaceAllUsesWith(RepTy);
}
for (auto E : ObjCInterfaceCache)
E.Decl.replaceAllUsesWith(E.Type->getDecl()->getDefinition()
? CreateTypeDefinition(E.Type, E.Unit)
: E.Decl);
// We keep our own list of retained types, because we need to look
// up the final type in the type cache.
for (std::vector<void *>::const_iterator RI = RetainedTypes.begin(),

View File

@ -67,16 +67,22 @@ class CGDebugInfo {
/// TypeCache - Cache of previously constructed Types.
llvm::DenseMap<void *, llvm::WeakVH> TypeCache;
struct ObjCInterfaceCacheEntry {
const ObjCInterfaceType *Type;
llvm::DIType Decl;
llvm::DIFile Unit;
ObjCInterfaceCacheEntry(const ObjCInterfaceType *Type, llvm::DIType Decl,
llvm::DIFile Unit)
: Type(Type), Decl(Decl), Unit(Unit) {}
};
/// ObjCInterfaceCache - Cache of previously constructed interfaces
/// which may change. Storing a pair of DIType and checksum.
llvm::DenseMap<void *, std::pair<llvm::WeakVH, unsigned> > ObjCInterfaceCache;
/// which may change.
llvm::SmallVector<ObjCInterfaceCacheEntry, 32> ObjCInterfaceCache;
/// RetainedTypes - list of interfaces we want to keep even if orphaned.
std::vector<void *> RetainedTypes;
/// CompleteTypeCache - Cache of previously constructed complete RecordTypes.
llvm::DenseMap<void *, llvm::WeakVH> CompletedTypeCache;
/// ReplaceMap - Cache of forward declared types to RAUW at the end of
/// compilation.
std::vector<std::pair<void *, llvm::WeakVH> >ReplaceMap;
@ -120,6 +126,7 @@ class CGDebugInfo {
llvm::DICompositeType CreateLimitedType(const RecordType *Ty);
void CollectContainingType(const CXXRecordDecl *RD, llvm::DICompositeType CT);
llvm::DIType CreateType(const ObjCInterfaceType *Ty, llvm::DIFile F);
llvm::DIType CreateTypeDefinition(const ObjCInterfaceType *Ty, llvm::DIFile F);
llvm::DIType CreateType(const ObjCObjectType *Ty, llvm::DIFile F);
llvm::DIType CreateType(const VectorType *Ty, llvm::DIFile F);
llvm::DIType CreateType(const ArrayType *Ty, llvm::DIFile F);
@ -130,7 +137,6 @@ class CGDebugInfo {
llvm::DIType CreateEnumType(const EnumType *Ty);
llvm::DIType CreateSelfType(const QualType &QualTy, llvm::DIType Ty);
llvm::DIType getTypeOrNull(const QualType);
llvm::DIType getCompletedTypeOrNull(const QualType);
llvm::DICompositeType getOrCreateMethodType(const CXXMethodDecl *Method,
llvm::DIFile F);
llvm::DICompositeType getOrCreateInstanceMethodType(

View File

@ -3,6 +3,14 @@
// Make sure we generate debug symbols for an indirectly referenced
// extension to an interface.
// This happens to be the order the members are emitted in... I'm assuming it's
// not meaningful/important, so if something causes the order to change, feel
// free to update the test to reflect the new order.
// CHECK: ; [ DW_TAG_member ] [a]
// CHECK: ; [ DW_TAG_member ] [d]
// CHECK: ; [ DW_TAG_member ] [c]
// CHECK: ; [ DW_TAG_member ] [b]
@interface I
{
@public int a;
@ -29,7 +37,6 @@ void gorf (struct S* s) {
int _b = s->i->b;
}
// CHECK: ; [ DW_TAG_member ] [b]
I *source();
@ -39,8 +46,14 @@ I *source();
}
@end
// CHECK: ; [ DW_TAG_member ] [c]
void use() {
int _c = source()->c;
}
@interface I()
{
@public int d;
}
@end
I *x();