Skip to content

Store the type parameters of bound generics in the debug info. #31118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions lib/IRGen/DebugTypeInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ DebugTypeInfo::DebugTypeInfo(swift::Type Ty, llvm::Type *StorageTy, Size size,
bool IsMetadata)
: Type(Ty.getPointer()), StorageType(StorageTy), size(size), align(align),
DefaultAlignment(HasDefaultAlignment), IsMetadataType(IsMetadata) {
assert(StorageType && "StorageType is a nullptr");
assert(align.getValue() != 0);
}

Expand All @@ -53,8 +52,9 @@ DebugTypeInfo DebugTypeInfo::getFromTypeInfo(swift::Type Ty,
// encounter one.
size = Size(0);
}
assert(Info.getStorageType() && "StorageType is a nullptr");
return DebugTypeInfo(Ty.getPointer(), Info.getStorageType(), size,
Info.getBestKnownAlignment(), hasDefaultAlignment(Ty),
Info.getBestKnownAlignment(), ::hasDefaultAlignment(Ty),
false);
}

Expand All @@ -80,6 +80,7 @@ DebugTypeInfo DebugTypeInfo::getMetadata(swift::Type Ty, llvm::Type *StorageTy,
Size size, Alignment align) {
DebugTypeInfo DbgTy(Ty.getPointer(), StorageTy, size,
align, true, false);
assert(StorageTy && "StorageType is a nullptr");
assert(!DbgTy.isContextArchetype() && "type metadata cannot contain an archetype");
return DbgTy;
}
Expand All @@ -88,10 +89,17 @@ DebugTypeInfo DebugTypeInfo::getArchetype(swift::Type Ty, llvm::Type *StorageTy,
Size size, Alignment align) {
DebugTypeInfo DbgTy(Ty.getPointer(), StorageTy, size,
align, true, true);
assert(StorageTy && "StorageType is a nullptr");
assert(!DbgTy.isContextArchetype() && "type metadata cannot contain an archetype");
return DbgTy;
}

DebugTypeInfo DebugTypeInfo::getForwardDecl(swift::Type Ty) {
DebugTypeInfo DbgTy(Ty.getPointer(), nullptr, {}, Alignment(1), true,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wdyt of making StorageType private in DebugTypeInfo and add a getter for it? This could assert if the field is null and the size is known?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the second commit.

false);
return DbgTy;
}

DebugTypeInfo DebugTypeInfo::getGlobal(SILGlobalVariable *GV,
llvm::Type *StorageTy, Size size,
Alignment align) {
Expand All @@ -104,7 +112,7 @@ DebugTypeInfo DebugTypeInfo::getGlobal(SILGlobalVariable *GV,
if (DeclType->isEqual(LowTy))
Type = DeclType.getPointer();
}
DebugTypeInfo DbgTy(Type, StorageTy, size, align, hasDefaultAlignment(Type),
DebugTypeInfo DbgTy(Type, StorageTy, size, align, ::hasDefaultAlignment(Type),
false);
assert(StorageTy && "StorageType is a nullptr");
assert(!DbgTy.isContextArchetype() &&
Expand All @@ -118,6 +126,7 @@ DebugTypeInfo DebugTypeInfo::getObjCClass(ClassDecl *theClass,
Alignment align) {
DebugTypeInfo DbgTy(theClass->getInterfaceType().getPointer(), StorageType,
size, align, true, false);
assert(StorageType && "StorageType is a nullptr");
assert(!DbgTy.isContextArchetype() && "type of objc class cannot be an archetype");
return DbgTy;
}
Expand Down Expand Up @@ -158,6 +167,7 @@ LLVM_DUMP_METHOD void DebugTypeInfo::dump() const {
if (StorageType) {
llvm::errs() << "StorageType=";
StorageType->dump();
}
} else
llvm::errs() << "forward-declared\n";
}
#endif
24 changes: 20 additions & 4 deletions lib/IRGen/DebugTypeInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ class TypeInfo;
/// This data structure holds everything needed to emit debug info
/// for a type.
class DebugTypeInfo {
public:
/// The type we need to emit may be different from the type
/// mentioned in the Decl, for example, stripped of qualifiers.
TypeBase *Type = nullptr;
Expand All @@ -48,7 +47,8 @@ class DebugTypeInfo {
bool DefaultAlignment = true;
bool IsMetadataType = false;

DebugTypeInfo() {}
public:
DebugTypeInfo() = default;
DebugTypeInfo(swift::Type Ty, llvm::Type *StorageTy, Size SizeInBytes,
Alignment AlignInBytes, bool HasDefaultAlignment,
bool IsMetadataType);
Expand All @@ -63,6 +63,9 @@ class DebugTypeInfo {
static DebugTypeInfo getArchetype(swift::Type Ty, llvm::Type *StorageTy,
Size size, Alignment align);

/// Create a forward declaration for a type whose size is unknown.
static DebugTypeInfo getForwardDecl(swift::Type Ty);

/// Create a standalone type from a TypeInfo object.
static DebugTypeInfo getFromTypeInfo(swift::Type Ty, const TypeInfo &Info);
/// Global variables.
Expand All @@ -88,11 +91,24 @@ class DebugTypeInfo {
return false;
}

llvm::Type *getStorageType() const {
assert((StorageType || size.isZero()) &&
"only defined types may have a size");
return StorageType;
}
Size getSize() const { return size; }
void setSize(Size NewSize) { size = NewSize; }
Alignment getAlignment() const { return align; }
bool isNull() const { return Type == nullptr; }
bool isForwardDecl() const { return StorageType == nullptr; }
bool isMetadataType() const { return IsMetadataType; }
bool hasDefaultAlignment() const { return DefaultAlignment; }

bool operator==(DebugTypeInfo T) const;
bool operator!=(DebugTypeInfo T) const;

void dump() const;
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void dump() const;
#endif
};
}
}
Expand Down
114 changes: 78 additions & 36 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
llvm::DenseMap<const void *, llvm::TrackingMDNodeRef> DIModuleCache;
llvm::StringMap<llvm::TrackingMDNodeRef> DIFileCache;
TrackingDIRefMap DIRefMap;
TrackingDIRefMap InnerTypeCache;
/// @}

/// A list of replaceable fwddecls that need to be RAUWed at the end.
Expand Down Expand Up @@ -765,7 +766,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
}

StringRef getMangledName(DebugTypeInfo DbgTy) {
if (DbgTy.IsMetadataType)
if (DbgTy.isMetadataType())
return MetadataTypeDeclCache.find(DbgTy.getDecl()->getName().str())
->getKey();

Expand Down Expand Up @@ -829,12 +830,12 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
llvm::DINode::DIFlags Flags) {
unsigned SizeOfByte = CI.getTargetInfo().getCharWidth();
auto *Ty = getOrCreateType(DbgTy);
auto *DITy = DBuilder.createMemberType(Scope, Name, File, 0,
SizeOfByte * DbgTy.size.getValue(),
0, OffsetInBits, Flags, Ty);
auto *DITy = DBuilder.createMemberType(
Scope, Name, File, 0, SizeOfByte * DbgTy.getSize().getValue(), 0,
OffsetInBits, Flags, Ty);
OffsetInBits += getSizeInBits(Ty);
OffsetInBits =
llvm::alignTo(OffsetInBits, SizeOfByte * DbgTy.align.getValue());
OffsetInBits = llvm::alignTo(OffsetInBits,
SizeOfByte * DbgTy.getAlignment().getValue());
return DITy;
}

Expand Down Expand Up @@ -929,8 +930,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
// one of the raw type as long as it is large enough to hold
// all enum values. Use the raw type for the debug type, but
// the storage size from the enum.
ElemDbgTy = DebugTypeInfo(ED->getRawType(), DbgTy.StorageType,
DbgTy.size, DbgTy.align, true, false);
ElemDbgTy =
DebugTypeInfo(ED->getRawType(), DbgTy.getStorageType(),
DbgTy.getSize(), DbgTy.getAlignment(), true, false);
else if (auto ArgTy = ElemDecl->getArgumentInterfaceType()) {
// A discriminated union. This should really be described as a
// DW_TAG_variant_type. For now only describing the data.
Expand All @@ -941,12 +943,13 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
// Discriminated union case without argument. Fallback to Int
// as the element type; there is no storage here.
Type IntTy = IGM.Context.getIntDecl()->getDeclaredType();
ElemDbgTy = DebugTypeInfo(IntTy, DbgTy.StorageType, Size(0),
ElemDbgTy = DebugTypeInfo(IntTy, DbgTy.getStorageType(), Size(0),
Alignment(1), true, false);
}
unsigned Offset = 0;
auto MTy = createMemberType(ElemDbgTy, ElemDecl->getBaseIdentifier().str(),
Offset, Scope, File, Flags);
auto MTy =
createMemberType(ElemDbgTy, ElemDecl->getBaseIdentifier().str(),
Offset, Scope, File, Flags);
Elements.push_back(MTy);
}
return DBuilder.getOrCreateArray(Elements);
Expand All @@ -958,7 +961,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
llvm::DIFile *File, unsigned Line,
llvm::DINode::DIFlags Flags) {
unsigned SizeOfByte = CI.getTargetInfo().getCharWidth();
unsigned SizeInBits = DbgTy.size.getValue() * SizeOfByte;
unsigned SizeInBits = DbgTy.getSize().getValue() * SizeOfByte;
// Default, since Swift doesn't allow specifying a custom alignment.
unsigned AlignInBits = 0;

Expand All @@ -982,16 +985,17 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
}

llvm::DIType *getOrCreateDesugaredType(Type Ty, DebugTypeInfo DbgTy) {
DebugTypeInfo BlandDbgTy(Ty, DbgTy.StorageType, DbgTy.size, DbgTy.align,
DbgTy.DefaultAlignment, DbgTy.IsMetadataType);
DebugTypeInfo BlandDbgTy(Ty, DbgTy.getStorageType(), DbgTy.getSize(),
DbgTy.getAlignment(), DbgTy.hasDefaultAlignment(),
DbgTy.isMetadataType());
return getOrCreateType(BlandDbgTy);
}

uint64_t getSizeOfBasicType(DebugTypeInfo DbgTy) {
uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth();
uint64_t BitWidth = DbgTy.size.getValue() * SizeOfByte;
llvm::Type *StorageType = DbgTy.StorageType
? DbgTy.StorageType
uint64_t BitWidth = DbgTy.getSize().getValue() * SizeOfByte;
llvm::Type *StorageType = DbgTy.getStorageType()
? DbgTy.getStorageType()
: IGM.DataLayout.getSmallestLegalIntType(
IGM.getLLVMContext(), BitWidth);

Expand All @@ -1003,23 +1007,44 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
return BitWidth;
}

/// Collect the type parameters of a bound generic type. This is needed to
/// anchor any typedefs that may appear in parameters so they can be
/// resolved in the debugger without needing to query the Swift module.
llvm::DINodeArray collectGenericParams(BoundGenericType *BGT) {
SmallVector<llvm::Metadata *, 16> TemplateParams;
for (auto Param : BGT->getGenericArgs()) {
TemplateParams.push_back(DBuilder.createTemplateTypeParameter(
TheCU, "", getOrCreateType(DebugTypeInfo::getForwardDecl(Param))));
Comment on lines +1016 to +1017
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On master-next createTemplateTypeParameter has a fourth parameter IsDefault (added in https://reviews.llvm.org/D73462 / swiftlang/llvm-project@7a42bab).

What's the right value to pass there to fix the master-next build?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false. Sorry!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries. Thanks for the confirmation. :-)

}
return DBuilder.getOrCreateArray(TemplateParams);
}

/// Create a sized container for a sizeless type. Used to represent
/// BoundGenericEnums that may have different sizes depending on what they are
/// bound to, but still share a mangled name.
llvm::DIType *createOpaqueStructWithSizedContainer(
llvm::DIScope *Scope, StringRef Name, llvm::DIFile *File, unsigned Line,
unsigned SizeInBits, unsigned AlignInBits, llvm::DINode::DIFlags Flags,
StringRef MangledName) {
// Let the MDNode folding set do the work of uniquing the inner type. This
// should be cheap.
llvm::DICompositeType *UniqueType = DBuilder.createStructType(
Scope, Name, File, Line, 0, 0, Flags, nullptr,
DBuilder.getOrCreateArray(ArrayRef<llvm::Metadata *>()),
llvm::dwarf::DW_LANG_Swift, nullptr, MangledName);
StringRef MangledName, llvm::DINodeArray BoundParams) {
// This uses a separate cache and not DIRefMap for the inner type to avoid
// associating the anonymous container (which is specific to the
// variable/storage and not the type) with the MangledName.
llvm::DICompositeType *UniqueType = nullptr;
auto *UID = llvm::MDString::get(IGM.getLLVMContext(), MangledName);
if (llvm::Metadata *V = InnerTypeCache.lookup(UID))
UniqueType = cast<llvm::DICompositeType>(V);
else {
UniqueType = DBuilder.createStructType(
Scope, Name, File, Line, 0, 0, Flags, nullptr, nullptr,
llvm::dwarf::DW_LANG_Swift, nullptr, MangledName);
if (BoundParams)
DBuilder.replaceArrays(UniqueType, nullptr, BoundParams);
InnerTypeCache[UID] = llvm::TrackingMDNodeRef(UniqueType);
}

llvm::Metadata *Elements[] = {
DBuilder.createMemberType(Scope, "", File, 0, SizeInBits,
AlignInBits, 0, Flags, UniqueType)};

return DBuilder.createStructType(
Scope, "", File, Line, SizeInBits, AlignInBits, Flags,
/* DerivedFrom */ nullptr, DBuilder.getOrCreateArray(Elements),
Expand Down Expand Up @@ -1188,9 +1213,10 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
// emitting the storage size of the struct, but it may be necessary
// to emit the (target!) size of the underlying basic type.
uint64_t SizeOfByte = CI.getTargetInfo().getCharWidth();
uint64_t SizeInBits = DbgTy.size.getValue() * SizeOfByte;
unsigned AlignInBits =
DbgTy.DefaultAlignment ? 0 : DbgTy.align.getValue() * SizeOfByte;
uint64_t SizeInBits = DbgTy.getSize().getValue() * SizeOfByte;
unsigned AlignInBits = DbgTy.hasDefaultAlignment()
? 0
: DbgTy.getAlignment().getValue() * SizeOfByte;
unsigned Encoding = 0;
llvm::DINode::DIFlags Flags = llvm::DINode::FlagZero;

Expand Down Expand Up @@ -1332,7 +1358,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
auto L = getDebugLoc(*this, Decl);
return createOpaqueStructWithSizedContainer(
Scope, Decl ? Decl->getNameStr() : "", File, L.Line, SizeInBits,
AlignInBits, Flags, MangledName);
AlignInBits, Flags, MangledName, collectGenericParams(StructTy));
}

case TypeKind::BoundGenericClass: {
Expand Down Expand Up @@ -1441,7 +1467,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
auto *File = getOrCreateFile(L.Filename);
return createOpaqueStructWithSizedContainer(
Scope, Decl->getName().str(), File, L.Line, SizeInBits, AlignInBits,
Flags, MangledName);
Flags, MangledName, collectGenericParams(EnumTy));
}

case TypeKind::BuiltinVector: {
Expand Down Expand Up @@ -1480,9 +1506,9 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
auto File = getOrCreateFile(L.Filename);
// For TypeAlias types, the DeclContext for the aliased type is
// in the decl of the alias type.
DebugTypeInfo AliasedDbgTy(AliasedTy, DbgTy.StorageType, DbgTy.size,
DbgTy.align, DbgTy.DefaultAlignment,
false);
DebugTypeInfo AliasedDbgTy(AliasedTy, DbgTy.getStorageType(),
DbgTy.getSize(), DbgTy.getAlignment(),
DbgTy.hasDefaultAlignment(), false);
return DBuilder.createTypedef(getOrCreateType(AliasedDbgTy), MangledName,
File, L.Line, Scope);
}
Expand Down Expand Up @@ -1637,8 +1663,24 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
if (Decl->isOutermostPrivateOrFilePrivateScope())
Scope = getFilePrivateScope(Scope, Decl);

// If this is a forward decl, create one for this mangled name and don't
// cache it.
if (DbgTy.isForwardDecl() && !isa<TypeAliasType>(DbgTy.getType())) {
auto *FwdDecl = DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, 0, 0,
llvm::dwarf::DW_LANG_Swift, 0, 0, llvm::DINode::FlagFwdDecl,
MangledName);
ReplaceMap.emplace_back(
std::piecewise_construct, std::make_tuple(DbgTy.getType()),
std::make_tuple(static_cast<llvm::Metadata *>(FwdDecl)));
return FwdDecl;
}
llvm::DIType *DITy = createType(DbgTy, MangledName, Scope, getFile(Scope));

// Don't cache a type alias to a forward declaration either.
if (DbgTy.isForwardDecl())
return DITy;

// Incrementally build the DIRefMap.
if (auto *CTy = dyn_cast<llvm::DICompositeType>(DITy)) {
#ifndef NDEBUG
Expand Down Expand Up @@ -2178,11 +2220,11 @@ void IRGenDebugInfoImpl::emitVariableDeclaration(
return;

// We cannot yet represent opened existentials.
if (DbgTy.Type->hasOpenedExistential())
if (DbgTy.getType()->hasOpenedExistential())
return;

if (!DbgTy.size)
DbgTy.size = getStorageSize(IGM.DataLayout, Storage);
if (!DbgTy.getSize())
DbgTy.setSize(getStorageSize(IGM.DataLayout, Storage));

auto *Scope = dyn_cast_or_null<llvm::DILocalScope>(getOrCreateScope(DS));
assert(Scope && "variable has no local scope");
Expand Down
12 changes: 12 additions & 0 deletions test/DebugInfo/BoundGenericStruct.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %target-swift-frontend %s -O -emit-ir -g -o - | %FileCheck %s
public struct S<T> {
let t : T
}

public let s = S<Int>(t: 0)

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "S",
// CHECK-SAME: templateParams: ![[PARAMS:[0-9]+]], identifier:
// CHECK: ![[PARAMS]] = !{![[INTPARAM:[0-9]+]]}
// CHECK: ![[INTPARAM]] = !DITemplateTypeParameter(type: ![[INT:[0-9]+]])
// CHECK: ![[INT]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Int",
6 changes: 3 additions & 3 deletions test/DebugInfo/enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public enum Nothing { }
public func foo(_ empty : Nothing) { }
// CHECK: !DICompositeType({{.*}}name: "Nothing", {{.*}}elements: ![[EMPTY]]

// CHECK: !DICompositeType({{.*}}name: "Rose", {{.*}}elements: ![[ELTS:[0-9]+]],
// CHECK: !DICompositeType({{.*}}name: "Rose",
// CHECK-SAME: {{.*}}identifier: "$s4enum4RoseOyxG{{z?}}D")
enum Rose<A> {
case MkRose(() -> A, () -> [Rose<A>])
Expand All @@ -69,8 +69,8 @@ enum Rose<A> {

func foo<T>(_ x : Rose<T>) -> Rose<T> { return x }

// CHECK: !DICompositeType({{.*}}name: "Tuple", {{.*}}elements: ![[ELTS:[0-9]+]], {{.*}}identifier: "$s4enum5TupleOyxGD")
// DWARF: !DICompositeType({{.*}}name: "Tuple", {{.*}}elements: ![[ELTS:[0-9]+]],
// CHECK: !DICompositeType({{.*}}name: "Tuple", {{.*}}identifier: "$s4enum5TupleOyxGD")
// DWARF: !DICompositeType({{.*}}name: "Tuple",
// DWARF-SAME: {{.*}}identifier: "$s4enum5TupleOyxG{{z?}}D")
public enum Tuple<P> {
case C(P, () -> Tuple)
Expand Down