Skip to content

[DebugInfo] Emit debug info for witness tables #79171

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 1 commit into from
May 21, 2025
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
5 changes: 4 additions & 1 deletion include/swift/AST/Types.h
Original file line number Diff line number Diff line change
Expand Up @@ -7311,7 +7311,10 @@ class GenericTypeParamType : public SubstitutableType,

/// Get the name of the generic type parameter.
Identifier getName() const;


/// Get the canonical <tau>_n_n name;
Identifier getCanonicalName() const;

/// The depth of this generic type parameter, i.e., the number of outer
/// levels of generic parameter lists that enclose this type parameter.
///
Expand Down
4 changes: 3 additions & 1 deletion lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2124,8 +2124,10 @@ Identifier GenericTypeParamType::getName() const {
if (!isCanonical())
return Name;

// Otherwise, we're canonical. Produce an anonymous '<tau>_n_n' name.
return getCanonicalName();
}

Identifier GenericTypeParamType::getCanonicalName() const {
// getASTContext() doesn't actually mutate an already-canonical type.
auto &C = const_cast<GenericTypeParamType*>(this)->getASTContext();
auto &names = C.CanonicalGenericTypeParamTypeNames;
Expand Down
73 changes: 54 additions & 19 deletions lib/IRGen/IRGenDebugInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,16 @@ class IRGenDebugInfoImpl : public IRGenDebugInfo {
DebugTypeInfo DebugType,
bool IsLocalToUnit,
std::optional<SILLocation> Loc);

void emitArtificialVariable(IRGenFunction &IGF, llvm::Value *Metadata,
StringRef Name, StringRef Identifier);

void emitTypeMetadata(IRGenFunction &IGF, llvm::Value *Metadata,
unsigned Depth, unsigned Index, StringRef Name);
GenericTypeParamType *Type);

void emitWitnessTable(IRGenFunction &IGF, llvm::Value *Metadata,
StringRef Name, ProtocolDecl *protocol);

void emitPackCountParameter(IRGenFunction &IGF, llvm::Value *Metadata,
SILDebugVariable VarInfo);

Expand Down Expand Up @@ -3904,9 +3912,10 @@ void IRGenDebugInfoImpl::emitGlobalVariableDeclaration(
Var->addDebugInfo(GV);
}

void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF,
llvm::Value *Metadata, unsigned Depth,
unsigned Index, StringRef Name) {
void IRGenDebugInfoImpl::emitArtificialVariable(IRGenFunction &IGF,
llvm::Value *Metadata,
StringRef Name,
StringRef Identifier) {
if (Opts.DebugInfoLevel <= IRGenDebugInfoLevel::LineTables)
return;

Expand All @@ -3915,23 +3924,44 @@ void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF,
if (!DS || DS->getInlinedFunction()->isTransparent())
return;

llvm::SmallString<8> Buf;
static const char *Tau = SWIFT_UTF8("\u03C4");
llvm::raw_svector_ostream OS(Buf);
OS << '$' << Tau << '_' << Depth << '_' << Index;
uint64_t PtrWidthInBits = CI.getTargetInfo().getPointerWidth(clang::LangAS::Default);
uint64_t PtrWidthInBits =
CI.getTargetInfo().getPointerWidth(clang::LangAS::Default);
assert(PtrWidthInBits % 8 == 0);
auto DbgTy = DebugTypeInfo::getTypeMetadata(
getMetadataType(Name)->getDeclaredInterfaceType().getPointer(),
Size(PtrWidthInBits / 8),
Alignment(CI.getTargetInfo().getPointerAlign(clang::LangAS::Default)));
emitVariableDeclaration(IGF.Builder, Metadata, DbgTy, IGF.getDebugScope(),
{}, {OS.str().str(), 0, false},
// swift.type is already a pointer type,
// having a shadow copy doesn't add another
// layer of indirection.
IGF.isAsync() ? CoroDirectValue : DirectValue,
ArtificialValue);
emitVariableDeclaration(
IGF.Builder, Metadata, DbgTy, IGF.getDebugScope(), {},
{Identifier, 0, false}, // swift.type is already a pointer type,
// having a shadow copy doesn't add another
// layer of indirection.
IGF.isAsync() ? CoroDirectValue : DirectValue, ArtificialValue);
}

void IRGenDebugInfoImpl::emitTypeMetadata(IRGenFunction &IGF,
llvm::Value *Metadata,
GenericTypeParamType *Type) {
llvm::SmallString<8> Buf;
llvm::raw_svector_ostream OS(Buf);
OS << "$" << Type->getCanonicalName().str();
auto Name = Type->getName().str();

emitArtificialVariable(IGF, Metadata, Name, OS.str());
}

void IRGenDebugInfoImpl::emitWitnessTable(IRGenFunction &IGF,
llvm::Value *Metadata, StringRef Name,
ProtocolDecl *protocol) {
llvm::SmallString<32> Buf;
llvm::raw_svector_ostream OS(Buf);
DebugTypeInfo DbgTy(protocol->getDeclaredType());
auto MangledName = getMangledName(DbgTy).Canonical;
OS << "$WT" << Name << "$$" << MangledName;;
// Make sure this ID lives long enough.
auto Id = IGF.getSwiftModule()->getASTContext().getIdentifier(OS.str());

emitArtificialVariable(IGF, Metadata, Name, Id.str());
}

void IRGenDebugInfoImpl::emitPackCountParameter(IRGenFunction &IGF,
Expand Down Expand Up @@ -4070,10 +4100,15 @@ void IRGenDebugInfo::emitGlobalVariableDeclaration(
}

void IRGenDebugInfo::emitTypeMetadata(IRGenFunction &IGF, llvm::Value *Metadata,
unsigned Depth, unsigned Index,
StringRef Name) {
GenericTypeParamType *Type) {
static_cast<IRGenDebugInfoImpl *>(this)->emitTypeMetadata(IGF, Metadata,
Depth, Index, Name);
Type);
}

void IRGenDebugInfo::emitWitnessTable(IRGenFunction &IGF, llvm::Value *Metadata,
StringRef Name, ProtocolDecl *protocol) {
static_cast<IRGenDebugInfoImpl *>(this)->emitWitnessTable(IGF, Metadata, Name,
protocol);
}

void IRGenDebugInfo::emitPackCountParameter(IRGenFunction &IGF,
Expand Down
6 changes: 5 additions & 1 deletion lib/IRGen/IRGenDebugInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,11 @@ class IRGenDebugInfo {

/// Emit debug metadata for type metadata (for generic types). So meta.
void emitTypeMetadata(IRGenFunction &IGF, llvm::Value *Metadata,
unsigned Depth, unsigned Index, StringRef Name);
GenericTypeParamType *Type);

/// Emit debug metadata for a (protocol) witness table.
void emitWitnessTable(IRGenFunction &IGF, llvm::Value *Metadata,
StringRef Name, ProtocolDecl *protocol);

/// Emit debug info for the IR function parameter holding the size of one or
/// more parameter / type packs.
Expand Down
36 changes: 35 additions & 1 deletion lib/IRGen/IRGenSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1168,8 +1168,42 @@ class IRGenSILFunction :
emitPackCountDebugVariable(Shape);
}
});

if (auto *BGT = llvm::dyn_cast<BoundGenericType>(Ty)) {
auto Decl = BGT->getDecl();
auto GE = Decl->getGenericEnvironment();
auto Requirements = BGT->getDecl()
->getGenericEnvironment()
->getGenericSignature()
.getRequirements();
for (auto Requirement : Requirements) {
if (Requirement.getKind() == RequirementKind::Conformance) {
auto ProtocolDecl = Requirement.getProtocolDecl();
auto ConformingType = Requirement.getFirstType();
Type Archetype;
if (auto GTPT = llvm::dyn_cast<GenericTypeParamType>(
ConformingType.getPointer()))
Archetype = GE->mapTypeIntoContext(GTPT);
else if (auto DMT = llvm::dyn_cast<DependentMemberType>(
ConformingType.getPointer()))
Archetype = GE->mapTypeIntoContext(DMT);

if (Lowering::TypeConverter::protocolRequiresWitnessTable(
ProtocolDecl) &&
tryGetLocalTypeData(
Archetype->getCanonicalType(),
LocalTypeDataKind::forAbstractProtocolWitnessTable(
ProtocolDecl))) {
auto Conformance =
ProtocolConformanceRef::forAbstract(Archetype, ProtocolDecl);

emitWitnessTableRef(*this, Archetype->getCanonicalType(),
Conformance);
}
}
}
}
}

/// Emit debug info for a function argument or a local variable.
template <typename StorageType>
void emitDebugVariableDeclaration(
Expand Down
59 changes: 47 additions & 12 deletions lib/IRGen/LocalTypeData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -408,26 +408,59 @@ static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF,
// functions that were inlined into transparent functions. Correct would be to
// check which instruction requests the type metadata and see whether its
// inlined function is transparent.
auto * DS = IGF.getDebugScope();
auto *DS = IGF.getDebugScope();
if (DS && DS->getInlinedFunction() &&
DS->getInlinedFunction()->isTransparent())
return;

// Only for formal type metadata.
if (key.Kind != LocalTypeDataKind::forFormalTypeMetadata())
// For formal type metadata and witness tables.
ProtocolDecl *proto = nullptr;

if (key.Kind.isAbstractProtocolConformance())
proto = key.Kind.getAbstractProtocolConformance();
else if (key.Kind.isConcreteProtocolConformance())
proto = key.Kind.getConcreteProtocolConformance()->getProtocol();
else if (key.Kind != LocalTypeDataKind::forFormalTypeMetadata())
return;

// Only for archetypes, and not for opened/opaque archetypes.
auto type = dyn_cast<ArchetypeType>(key.Type);
if (!type)
return;
if (!type->isRoot())
if (!type->isRoot() && !proto)
return;
if (!isa<PrimaryArchetypeType>(type) && !isa<PackArchetypeType>(type))
return;

auto *typeParam = type->getInterfaceType()->castTo<GenericTypeParamType>();
auto name = typeParam->getName().str();
auto interfaceType = type->getInterfaceType();
llvm::SmallString<16> name;
llvm::SmallString<16> displayName;
if (auto DMT =
llvm::dyn_cast<DependentMemberType>(interfaceType.getPointer())) {
std::function<void(DependentMemberType *)> visitDependentMembers =
[&](DependentMemberType *member) {
if (member == nullptr)
return;
if (auto *parent =
llvm::dyn_cast<DependentMemberType>(member->getBase())) {
visitDependentMembers(parent);
name.append("$");
displayName.append(".");
}
name.append(member->getName().str());
displayName.append(member->getName().str());
};
name.append(DMT->getRootGenericParam()->getCanonicalName().str());
name.append("$");
displayName.append(DMT->getRootGenericParam()->getName().str());
displayName.append(".");
visitDependentMembers(DMT);
} else if (auto GTPT = llvm::dyn_cast<GenericTypeParamType>(
interfaceType.getPointer())) {
name = GTPT->getCanonicalName().str();
displayName = GTPT->getName().str();
} else {
return;
}

llvm::Value *data = value.getMetadata();

Expand All @@ -438,7 +471,7 @@ static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF,
// though; see the comment in IRGenFunctionSIL::emitShadowCopyIfNeeded().
if (!IGF.IGM.IRGen.Opts.shouldOptimize() && !IGF.isAsync()) {
auto alloca =
IGF.createAlloca(data->getType(), IGF.IGM.getPointerAlignment(), name);
IGF.createAlloca(data->getType(), IGF.IGM.getPointerAlignment(), displayName);
IGF.Builder.CreateStore(data, alloca);
data = alloca.getAddress();
}
Expand All @@ -447,10 +480,12 @@ static void maybeEmitDebugInfoForLocalTypeData(IRGenFunction &IGF,
if (!IGF.IGM.DebugInfo)
return;

IGF.IGM.DebugInfo->emitTypeMetadata(IGF, data,
typeParam->getDepth(),
typeParam->getIndex(),
name);
if (proto) {
IGF.IGM.DebugInfo->emitWitnessTable(IGF, data, name, proto);
} else {
auto *typeParam = type->getInterfaceType()->castTo<GenericTypeParamType>();
IGF.IGM.DebugInfo->emitTypeMetadata(IGF, data, typeParam);
}
}

void
Expand Down
68 changes: 6 additions & 62 deletions test/DebugInfo/move_function_dbginfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,19 +237,19 @@ public func copyableVarArgTest(_ k: inout Klass) {
// DWARF-NEXT: DW_AT_artificial (true)
//
// DWARF: DW_TAG_variable
// DWARF: DW_AT_location
// DWARF: DW_AT_name ("$WT\317\204_0_0$$$s3out1P_pD")
// DWARF: DW_AT_type (
// DWARF: DW_AT_artificial (true)
//
// DWARF: DW_TAG_variable
// DWARF-NEXT: DW_AT_location (0x{{[a-z0-9]+}}:
// DWARF-NEXT: [0x{{[a-z0-9]+}}, 0x{{[a-z0-9]+}}):
// DWARF-NEXT: DW_AT_name ("k")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
//
// DWARF: DW_TAG_variable
// DWARF-NEXT: DW_AT_location (
// DWARF-NEXT: DW_AT_name ("m")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
public func addressOnlyValueTest<T : P>(_ x: T) {
let k = x
k.doSomething()
Expand All @@ -265,33 +265,6 @@ public func addressOnlyValueTest<T : P>(_ x: T) {
// CHECK: ret void
// CHECK-NEXT: }
//
// DWARF: DW_AT_linkage_name ("$s3out23addressOnlyValueArgTestyyxnAA1PRzlF")
// DWARF-NEXT: DW_AT_name ("addressOnlyValueArgTest")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
// DWARF-NEXT: DW_AT_external (
//
// DWARF: DW_TAG_formal_parameter
// DWARF-NEXT: DW_AT_location (0x{{[a-z0-9]+}}:
// DWARF-NEXT: [0x{{[a-z0-9]+}}, 0x{{[a-z0-9]+}}):
// DWARF-NEXT: DW_AT_name ("k")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
//
// DWARF: DW_TAG_variable
// DWARF-NEXT: DW_AT_location (
// DWARF-NEXT: DW_AT_name ("$\317\204_0_0")
// DWARF-NEXT: DW_AT_type (
// DWARF-NEXT: DW_AT_artificial (true)
//
// DWARF: DW_TAG_variable
// DWARF-NEXT: DW_AT_location (
// DWARF-NEXT: DW_AT_name ("m")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
public func addressOnlyValueArgTest<T : P>(_ k: __owned T) {
k.doSomething()
let m = consume k
Expand All @@ -306,35 +279,6 @@ public func addressOnlyValueArgTest<T : P>(_ k: __owned T) {
// CHECK: ret void
// CHECK-NEXT: }
//
// DWARF: DW_AT_linkage_name ("$s3out18addressOnlyVarTestyyxAA1PRzlF")
// DWARF-NEXT: DW_AT_name ("addressOnlyVarTest")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
// DWARF-NEXT: DW_AT_external (
//
// DWARF: DW_TAG_formal_parameter
// DWARF-NEXT: DW_AT_location (
// DWARF-NEXT: DW_AT_name ("x")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
//
// DWARF: DW_TAG_variable
// DWARF-NEXT: DW_AT_location (
// DWARF-NEXT: DW_AT_name ("$\317\204_0_0")
// DWARF-NEXT: DW_AT_type (
// DWARF-NEXT: DW_AT_artificial (true)
//
// DWARF: DW_TAG_variable
// DWARF-NEXT: DW_AT_location (0x{{[a-z0-9]+}}:
// DWARF-NEXT: [0x{{[a-z0-9]+}}, 0x{{[a-z0-9]+}}):
// TODO: Missing def in dbg info here.
// DWARF-NEXT: [0x{{[a-z0-9]+}}, 0x{{[a-z0-9]+}}):
// DWARF-NEXT: DW_AT_name ("k")
// DWARF-NEXT: DW_AT_decl_file (
// DWARF-NEXT: DW_AT_decl_line (
// DWARF-NEXT: DW_AT_type (
public func addressOnlyVarTest<T : P>(_ x: T) {
var k = x // << this
k.doSomething()
Expand Down
Loading