Skip to content

[Cherry-pick] [DebugInfo] Generate debug info for specialized types #72442

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
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
6 changes: 4 additions & 2 deletions lib/IRGen/GenEnum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5887,8 +5887,10 @@ namespace {
std::optional<SpareBitsMaskInfo> calculateSpareBitsMask() const override {
SpareBitVector spareBits;
for (auto enumCase : getElementsWithPayload()) {
cast<FixedTypeInfo>(enumCase.ti)
->applyFixedSpareBitsMask(IGM, spareBits);
if (auto fixedTI = llvm::dyn_cast<FixedTypeInfo>(enumCase.ti))
fixedTI->applyFixedSpareBitsMask(IGM, spareBits);
else
return {};
}
// Trim leading/trailing zero bytes, then pad to a multiple of 32 bits
llvm::APInt bits = spareBits.asAPInt();
Expand Down
216 changes: 142 additions & 74 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1157,23 +1157,12 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
return DITy;;
}

llvm::DIType *createFullDebugInfoGenericForStructOrClassType(
BoundGenericType *Type, NominalTypeDecl *Decl, llvm::DIScope *Scope,
llvm::DIFile *File, unsigned Line, unsigned SizeInBits,
unsigned AlignInBits, llvm::DINode::DIFlags Flags,
StringRef MangledName, bool IsClass = false) {
// To emit full debug info for generic types, the strategy is to emit
// full debug info for the type with archetypes, and still emit opaque
// debug information for the specialized type. For example, given:
// struct Pair<T, U> {
// let t : T
// let u: U
// }
// When emitting debug information for a type such as Pair<Int, Double>,
// emit full debug info for Pair<T, U>, and emit the regular debug
// information for Pair<Int, Double>.

// Go from Pair<Int, Double> to Pair<T, U>.
llvm::DIType *
createSpecializedEnumType(NominalOrBoundGenericNominalType *EnumTy,
EnumDecl *Decl, StringRef MangledName,
unsigned SizeInBits, unsigned AlignInBits,
llvm::DIScope *Scope, llvm::DIFile *File,
unsigned Line, llvm::DINode::DIFlags Flags) {
auto UnsubstitutedTy = Decl->getDeclaredInterfaceType();
UnsubstitutedTy = Decl->mapTypeIntoContext(UnsubstitutedTy);

Expand All @@ -1183,39 +1172,99 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
std::string DeclTypeMangledName = Mangler.mangleTypeForDebugger(
UnsubstitutedTy->mapTypeOutOfContext(), {});
if (DeclTypeMangledName == MangledName) {
return createUnsubstitutedGenericStructOrClassType(
DbgTy, Decl, UnsubstitutedTy, Scope, File, Line, SizeInBits,
AlignInBits, Flags, nullptr, llvm::dwarf::DW_LANG_Swift,
DeclTypeMangledName);
return createUnsubstitutedVariantType(DbgTy, Decl, MangledName,
SizeInBits, AlignInBits, Scope,
File, 0, Flags);
}
auto FwdDecl = llvm::TempDIType(DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_structure_type, "", Scope, File, 0,
llvm::dwarf::DW_LANG_Swift, 0, 0, llvm::DINode::FlagZero, MangledName));

auto TH = llvm::TrackingMDNodeRef(FwdDecl.get());
DITypeCache[EnumTy] = TH;
// Force the creation of the unsubstituted type, don't create it
// directly so it goes through all the caching/verification logic.
auto UnsubstitutedType = getOrCreateType(DbgTy);

if (auto *ClassTy = llvm::dyn_cast<BoundGenericClassType>(Type)) {
auto SuperClassTy = ClassTy->getSuperclass();
if (SuperClassTy) {
auto SuperClassDbgTy = DebugTypeInfo::getFromTypeInfo(
SuperClassTy, IGM.getTypeInfoForUnlowered(SuperClassTy), IGM);

llvm::DIType *SuperClassDITy = getOrCreateType(SuperClassDbgTy);
assert(SuperClassDITy && "getOrCreateType should never return null!");
DBuilder.createInheritance(UnsubstitutedType, SuperClassDITy, 0, 0,
llvm::DINode::FlagZero);
}

auto *OpaqueType = createPointerSizedStruct(
Scope, Decl ? Decl->getNameStr() : MangledName, File, 0, Flags,
MangledName, UnsubstitutedType);
return OpaqueType;
}

auto *OpaqueType = createOpaqueStruct(
Scope, "", File, Line, SizeInBits, AlignInBits, Flags, MangledName,
collectGenericParams(Type), UnsubstitutedType);
auto unsubstitutedDbgTy = getOrCreateType(DbgTy);
auto DIType = createOpaqueStruct(
Scope, "", File, 0, SizeInBits, AlignInBits, Flags, MangledName,
collectGenericParams(EnumTy), unsubstitutedDbgTy);
DBuilder.replaceTemporary(std::move(FwdDecl), DIType);
return DIType;
}

/// Create a DICompositeType from a specialized struct. A specialized type
/// is a generic type, or a child type whose parent is generic.
llvm::DIType *
createSpecializedStructOrClassType(NominalOrBoundGenericNominalType *Type,
NominalTypeDecl *Decl, llvm::DIScope *Scope,
llvm::DIFile *File, unsigned Line,
unsigned SizeInBits, unsigned AlignInBits,
llvm::DINode::DIFlags Flags,
StringRef MangledName,
bool IsClass = false) {
// To emit debug info of the DwarfTypes level for generic types, the strategy
// is to emit a description of all the fields for the type with archetypes,
// and still the same debug info as the ASTTypes level for the specialized
// type. For example, given:
// struct Pair<T, U> {
// let t: T
// let u: U
// }
// When emitting debug information for a type such as Pair<Int, Double>,
// emit a description of all the fields for Pair<T, U>, and emit the regular
// debug information for Pair<Int, Double>.

auto FwdDecl = llvm::TempDIType(DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_structure_type, "", Scope, File, Line,
llvm::dwarf::DW_LANG_Swift, SizeInBits, 0, Flags, MangledName));

auto TH = llvm::TrackingMDNodeRef(FwdDecl.get());
DITypeCache[Type] = TH;

// Go from Pair<Int, Double> to Pair<T, U>.
auto UnsubstitutedTy = Decl->getDeclaredInterfaceType();
UnsubstitutedTy = Decl->mapTypeIntoContext(UnsubstitutedTy);

auto DbgTy = DebugTypeInfo::getFromTypeInfo(
UnsubstitutedTy, IGM.getTypeInfoForUnlowered(UnsubstitutedTy), IGM);
Mangle::ASTMangler Mangler;
std::string DeclTypeMangledName =
Mangler.mangleTypeForDebugger(UnsubstitutedTy->mapTypeOutOfContext(), {});
if (DeclTypeMangledName == MangledName) {
return createUnsubstitutedGenericStructOrClassType(
DbgTy, Decl, UnsubstitutedTy, Scope, File, Line, SizeInBits,
AlignInBits, Flags, nullptr, llvm::dwarf::DW_LANG_Swift,
DeclTypeMangledName);
}
// Force the creation of the unsubstituted type, don't create it
// directly so it goes through all the caching/verification logic.
auto UnsubstitutedType = getOrCreateType(DbgTy);

if (auto *ClassTy = llvm::dyn_cast<BoundGenericClassType>(Type)) {
auto SuperClassTy = ClassTy->getSuperclass();
if (SuperClassTy) {
auto SuperClassDbgTy = DebugTypeInfo::getFromTypeInfo(
SuperClassTy, IGM.getTypeInfoForUnlowered(SuperClassTy), IGM);

llvm::DIType *SuperClassDITy = getOrCreateType(SuperClassDbgTy);
assert(SuperClassDITy && "getOrCreateType should never return null!");
DBuilder.createInheritance(UnsubstitutedType, SuperClassDITy, 0, 0,
llvm::DINode::FlagZero);
}

auto *OpaqueType = createPointerSizedStruct(
Scope, Decl ? Decl->getNameStr() : MangledName, File, 0, Flags,
MangledName, UnsubstitutedType);
return OpaqueType;
}

auto *OpaqueType = createOpaqueStruct(
Scope, "", File, Line, SizeInBits, AlignInBits, Flags, MangledName,
collectGenericParams(Type), UnsubstitutedType);
DBuilder.replaceTemporary(std::move(FwdDecl), OpaqueType);
return OpaqueType;
}

/// Create debug information for an enum with a raw type (enum E : Int {}).
llvm::DICompositeType *createRawEnumType(CompletedDebugTypeInfo DbgTy,
EnumDecl *Decl,
Expand Down Expand Up @@ -1441,17 +1490,29 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
/// 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) {
llvm::DINodeArray
collectGenericParams(NominalOrBoundGenericNominalType *BGT) {

// Collect the generic args from the type and its parent.
std::vector<Type> GenericArgs;
Type CurrentType = BGT;
while (CurrentType && CurrentType->getAnyNominal()) {
if (auto *BGT = llvm::dyn_cast<BoundGenericType>(CurrentType))
GenericArgs.insert(GenericArgs.end(), BGT->getGenericArgs().begin(),
BGT->getGenericArgs().end());
CurrentType = CurrentType->getNominalParent();
}

SmallVector<llvm::Metadata *, 16> TemplateParams;
for (auto Param : BGT->getGenericArgs()) {
for (auto Arg : GenericArgs) {
DebugTypeInfo ParamDebugType;
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes)
// For full debug info don't generate just a forward declaration for
// the generic type parameters.
// For the DwarfTypes level don't generate just a forward declaration
// for the generic type parameters.
ParamDebugType = DebugTypeInfo::getFromTypeInfo(
Param, IGM.getTypeInfoForUnlowered(Param), IGM);
Arg, IGM.getTypeInfoForUnlowered(Arg), IGM);
else
ParamDebugType = DebugTypeInfo::getForwardDecl(Param);
ParamDebugType = DebugTypeInfo::getForwardDecl(Arg);

TemplateParams.push_back(DBuilder.createTemplateTypeParameter(
TheCU, "", getOrCreateType(ParamDebugType), false));
Expand Down Expand Up @@ -1625,7 +1686,6 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
createMemberType(DbgTy, "", OffsetInBits, Scope, MainFile, Flags));
}
// FIXME: assert that SizeInBits == OffsetInBits.
SizeInBits = OffsetInBits;

auto FwdDecl = llvm::TempDINode(DBuilder.createReplaceableCompositeType(
llvm::dwarf::DW_TAG_structure_type, MangledName, Scope, MainFile, 0,
Expand Down Expand Up @@ -1786,10 +1846,15 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
// don't want a whitespace change to an secondary file trigger a
// recompilation of the debug info of a primary source file.
unsigned FwdDeclLine = 0;
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes)
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) {
if (StructTy->isSpecialized())
return createSpecializedStructOrClassType(
StructTy, Decl, Scope, L.File, L.Line, SizeInBits, AlignInBits,
Flags, MangledName);
return createStructType(DbgTy, Decl, StructTy, Scope, L.File, L.Line,
SizeInBits, AlignInBits, Flags, nullptr,
llvm::dwarf::DW_LANG_Swift, MangledName);
}
StringRef Name = Decl->getName().str();
if (!SizeInBitsOrNull)
return DBuilder.createForwardDecl(
Expand All @@ -1814,6 +1879,11 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
assert(SizeInBits ==
CI.getTargetInfo().getPointerWidth(clang::LangAS::Default));
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) {
if (ClassTy->isSpecialized())
return createSpecializedStructOrClassType(
ClassTy, Decl, Scope, L.File, L.Line, SizeInBits, AlignInBits,
Flags, MangledName);

auto *DIType = createStructType(
DbgTy, Decl, ClassTy, Scope, File, L.Line, SizeInBits, AlignInBits,
Flags, nullptr, llvm::dwarf::DW_LANG_Swift, MangledName);
Expand Down Expand Up @@ -1875,7 +1945,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
auto L = getFileAndLocation(Decl);
unsigned FwdDeclLine = 0;
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes)
return createFullDebugInfoGenericForStructOrClassType(
return createSpecializedStructOrClassType(
StructTy, Decl, Scope, L.File, L.Line, SizeInBits, AlignInBits,
Flags, MangledName);

Expand All @@ -1892,7 +1962,7 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
unsigned FwdDeclLine = 0;

if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes)
return createFullDebugInfoGenericForStructOrClassType(
return createSpecializedStructOrClassType(
ClassTy, Decl, Scope, L.File, L.Line, SizeInBits, AlignInBits,
Flags, MangledName);

Expand Down Expand Up @@ -2011,10 +2081,16 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
auto *Decl = EnumTy->getDecl();
auto L = getFileAndLocation(Decl);
unsigned FwdDeclLine = 0;
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes)
if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) {
if (EnumTy->isSpecialized())
return createSpecializedEnumType(EnumTy, Decl, MangledName,
SizeInBits, AlignInBits, Scope, File,
FwdDeclLine, Flags);

if (CompletedDbgTy)
return createEnumType(*CompletedDbgTy, Decl, MangledName, AlignInBits,
Scope, L.File, L.Line, Flags);
}
return createOpaqueStruct(Scope, Decl->getName().str(), L.File,
FwdDeclLine, SizeInBits, AlignInBits, Flags,
MangledName);
Expand All @@ -2027,25 +2103,13 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
unsigned FwdDeclLine = 0;

if (Opts.DebugInfoLevel > IRGenDebugInfoLevel::ASTTypes) {
auto UnsubstitutedTy = Decl->getDeclaredInterfaceType();
UnsubstitutedTy = Decl->mapTypeIntoContext(UnsubstitutedTy);

auto DbgTy = DebugTypeInfo::getFromTypeInfo(
UnsubstitutedTy, IGM.getTypeInfoForUnlowered(UnsubstitutedTy), IGM);
Mangle::ASTMangler Mangler;
std::string DeclTypeMangledName = Mangler.mangleTypeForDebugger(
UnsubstitutedTy->mapTypeOutOfContext(), {});
if (DeclTypeMangledName == MangledName) {
return createUnsubstitutedVariantType(DbgTy, Decl, MangledName,
SizeInBits, AlignInBits, Scope, File,
FwdDeclLine, Flags);
}
// Force the creation of the unsubstituted type, don't create it
// directly so it goes through all the caching/verification logic.
auto unsubstitutedDbgTy = getOrCreateType(DbgTy);
return createOpaqueStruct(
Scope, "", L.File, FwdDeclLine, SizeInBits, AlignInBits, Flags,
MangledName, collectGenericParams(EnumTy), unsubstitutedDbgTy);
if (EnumTy->isSpecialized())
return createSpecializedEnumType(EnumTy, Decl, MangledName,
SizeInBits, AlignInBits, Scope, File,
FwdDeclLine, Flags);
if (CompletedDbgTy)
return createEnumType(*CompletedDbgTy, Decl, MangledName, AlignInBits,
Scope, L.File, L.Line, Flags);
}
return createOpaqueStructWithSizedContainer(
Scope, Decl->getName().str(), L.File, FwdDeclLine, SizeInBits,
Expand Down Expand Up @@ -2184,6 +2248,10 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
#ifndef NDEBUG
/// Verify that the size of this type matches the one of the cached type.
bool sanityCheckCachedType(DebugTypeInfo DbgTy, llvm::DIType *CachedType) {
// If this is a temporary, we're in the middle of creating a recursive type,
// so skip the sanity check.
if (CachedType->isTemporary())
return true;
if (DbgTy.isForwardDecl())
return true;
auto CompletedDbgTy = CompletedDebugTypeInfo::getFromTypeInfo(
Expand Down
31 changes: 30 additions & 1 deletion test/DebugInfo/BoundGenericStruct.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,35 @@ public let s = S<Int>(t: 0)
// DWARF: !DICompositeType(tag: DW_TAG_structure_type, name: "S",
// DWARF-SAME: identifier: "$s18BoundGenericStruct1SVyxGD")
// DWARF: !DIDerivedType(tag: DW_TAG_member, name: "t"
// DWARF: !DICompositeType(tag: DW_TAG_structure_type, name: "$sxD"
// DWARF: ![[GENERIC_PARAM_TYPE:[0-9]+]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$sxD"


public struct S2<T> {
public struct Inner {
let t: T
}
}
public let inner = S2<Double>.Inner(t:4.2)

// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "Inner",
// CHECK-SAME: size: 64, {{.*}}identifier: "$s18BoundGenericStruct2S2V5InnerVySd_GD")
// CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "$s18BoundGenericStruct2S2VyxGD",
// CHECK-SAME: flags: DIFlagFwdDecl, runtimeLang: DW_LANG_Swift)

// DWARF: !DICompositeType(tag: DW_TAG_structure_type, scope: ![[SCOPE1:[0-9]+]],
// DWARF-SAME: size: 64, {{.*}}, templateParams: ![[PARAMS2:[0-9]+]], identifier: "$s18BoundGenericStruct2S2V5InnerVySd_GD"
// DWARF-SAME: specification_of: ![[SPECIFICATION:[0-9]+]]

// DWARF: ![[SCOPE1]] = !DICompositeType(tag: DW_TAG_structure_type, name: "$s18BoundGenericStruct2S2VyxGD",

// DWARF: ![[PARAMS2]] = !{![[PARAMS3:[0-9]+]]}
// DWARF: ![[PARAMS3]] = !DITemplateTypeParameter(type: ![[PARAMS4:[0-9]+]])
// DWARF: ![[PARAMS4]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Double",
// DWARF-SAME: size: 64, {{.*}}runtimeLang: DW_LANG_Swift, identifier: "$sSdD")

// DWARF: [[SPECIFICATION]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Inner",
// DWARF-SAME: elements: ![[ELEMENTS1:[0-9]+]], runtimeLang: DW_LANG_Swift, identifier: "$s18BoundGenericStruct2S2V5InnerVyx_GD")

// DWARF: ![[ELEMENTS1]] = !{![[ELEMENTS2:[0-9]+]]}

// DWARF: ![[ELEMENTS2]] = !DIDerivedType(tag: DW_TAG_member, name: "t", scope: !27, file: !3, baseType: ![[GENERIC_PARAM_TYPE]])