Skip to content

[IRGen] Emit a pointer from nominal type descriptor to concrete metadata. #79302

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
Feb 28, 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
74 changes: 67 additions & 7 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -3879,6 +3879,13 @@ struct TargetCanonicalSpecializedMetadatasCachingOnceToken {
TargetRelativeDirectPointer<Runtime, swift_once_t, /*Nullable*/ false> token;
};

template <typename Runtime>
struct TargetSingletonMetadataPointer {
TargetRelativeDirectPointer<Runtime, TargetMetadata<Runtime>,
/*Nullable*/ false>
metadata;
};

template <typename Runtime>
class swift_ptrauth_struct_context_descriptor(TypeContextDescriptor)
TargetTypeContextDescriptor : public TargetContextDescriptor<Runtime> {
Expand Down Expand Up @@ -3931,7 +3938,15 @@ class swift_ptrauth_struct_context_descriptor(TypeContextDescriptor)
}

bool hasCanonicalMetadataPrespecializations() const {
return getTypeContextDescriptorFlags().hasCanonicalMetadataPrespecializations();
return this->isGeneric() &&
getTypeContextDescriptorFlags()
.hasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer();
}

bool hasSingletonMetadataPointer() const {
return !this->isGeneric() &&
getTypeContextDescriptorFlags()
.hasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer();
}

bool hasLayoutString() const {
Expand Down Expand Up @@ -4123,7 +4138,8 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor)
TargetCanonicalSpecializedMetadatasListEntry<Runtime>,
TargetCanonicalSpecializedMetadataAccessorsListEntry<Runtime>,
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>,
InvertibleProtocolSet> {
InvertibleProtocolSet,
TargetSingletonMetadataPointer<Runtime>> {
private:
using TrailingGenericContextObjects =
swift::TrailingGenericContextObjects<TargetClassDescriptor<Runtime>,
Expand All @@ -4140,7 +4156,8 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor)
TargetCanonicalSpecializedMetadatasListEntry<Runtime>,
TargetCanonicalSpecializedMetadataAccessorsListEntry<Runtime>,
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>,
InvertibleProtocolSet>;
InvertibleProtocolSet,
TargetSingletonMetadataPointer<Runtime>>;

using TrailingObjects =
typename TrailingGenericContextObjects::TrailingObjects;
Expand Down Expand Up @@ -4170,6 +4187,7 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor)
TargetCanonicalSpecializedMetadataAccessorsListEntry<Runtime>;
using MetadataCachingOnceToken =
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>;
using SingletonMetadataPointer = TargetSingletonMetadataPointer<Runtime>;

using StoredPointer = typename Runtime::StoredPointer;
using StoredPointerDifference = typename Runtime::StoredPointerDifference;
Expand Down Expand Up @@ -4311,6 +4329,10 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor)
return this->hasCanonicalMetadataPrespecializations() ? 1 : 0;
}

size_t numTrailingObjects(OverloadToken<SingletonMetadataPointer>) const {
return this->hasSingletonMetadataPointer() ? 1 : 0;
}

public:
const TargetRelativeDirectPointer<Runtime, const void, /*nullable*/true> &
getResilientSuperclass() const {
Expand Down Expand Up @@ -4490,6 +4512,14 @@ class swift_ptrauth_struct_context_descriptor(ClassDescriptor)
return box->token.get();
}

TargetMetadata<Runtime> *getSingletonMetadata() const {
if (!this->hasSingletonMetadataInitialization())
return nullptr;

auto box = this->template getTrailingObjects<SingletonMetadataPointer>();
return box->token.get();
}

/// Retrieve the set of protocols that are inverted by this type's
/// primary definition.
///
Expand Down Expand Up @@ -4537,7 +4567,8 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor)
TargetCanonicalSpecializedMetadatasListCount<Runtime>,
TargetCanonicalSpecializedMetadatasListEntry<Runtime>,
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>,
InvertibleProtocolSet> {
InvertibleProtocolSet,
TargetSingletonMetadataPointer<Runtime>> {
public:
using ForeignMetadataInitialization =
TargetForeignMetadataInitialization<Runtime>;
Expand All @@ -4551,6 +4582,7 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor)
TargetCanonicalSpecializedMetadatasListEntry<Runtime>;
using MetadataCachingOnceToken =
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>;
using SingletonMetadataPointer = TargetSingletonMetadataPointer<Runtime>;

private:
using TrailingGenericContextObjects =
Expand All @@ -4561,7 +4593,8 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor)
MetadataListCount,
MetadataListEntry,
MetadataCachingOnceToken,
InvertibleProtocolSet>;
InvertibleProtocolSet,
TargetSingletonMetadataPointer<Runtime>>;

using TrailingObjects =
typename TrailingGenericContextObjects::TrailingObjects;
Expand Down Expand Up @@ -4595,6 +4628,10 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor)
return this->hasCanonicalMetadataPrespecializations() ? 1 : 0;
}

size_t numTrailingObjects(OverloadToken<SingletonMetadataPointer>) const {
return this->hasSingletonMetadataPointer() ? 1 : 0;
}

public:
using TrailingGenericContextObjects::getGenericContext;
using TrailingGenericContextObjects::getGenericContextHeader;
Expand Down Expand Up @@ -4648,6 +4685,14 @@ class swift_ptrauth_struct_context_descriptor(StructDescriptor)
return box->token.get();
}

TargetMetadata<Runtime> *getSingletonMetadata() const {
if (!this->hasSingletonMetadataInitialization())
return nullptr;

auto box = this->template getTrailingObjects<SingletonMetadataPointer>();
return box->token.get();
}

/// Retrieve the set of protocols that are inverted by this type's
/// primary definition.
///
Expand Down Expand Up @@ -4684,7 +4729,8 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor)
TargetCanonicalSpecializedMetadatasListCount<Runtime>,
TargetCanonicalSpecializedMetadatasListEntry<Runtime>,
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>,
InvertibleProtocolSet> {
InvertibleProtocolSet,
TargetSingletonMetadataPointer<Runtime>> {
public:
using SingletonMetadataInitialization =
TargetSingletonMetadataInitialization<Runtime>;
Expand All @@ -4698,6 +4744,7 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor)
TargetCanonicalSpecializedMetadatasListEntry<Runtime>;
using MetadataCachingOnceToken =
TargetCanonicalSpecializedMetadatasCachingOnceToken<Runtime>;
using SingletonMetadataPointer = TargetSingletonMetadataPointer<Runtime>;

private:
using TrailingGenericContextObjects =
Expand All @@ -4708,7 +4755,8 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor)
MetadataListCount,
MetadataListEntry,
MetadataCachingOnceToken,
InvertibleProtocolSet>;
InvertibleProtocolSet,
TargetSingletonMetadataPointer<Runtime>>;

using TrailingObjects =
typename TrailingGenericContextObjects::TrailingObjects;
Expand Down Expand Up @@ -4742,6 +4790,10 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor)
return this->hasCanonicalMetadataPrespecializations() ? 1 : 0;
}

size_t numTrailingObjects(OverloadToken<SingletonMetadataPointer>) const {
return this->hasSingletonMetadataPointer() ? 1 : 0;
}

public:
using TrailingGenericContextObjects::getGenericContext;
using TrailingGenericContextObjects::getGenericContextHeader;
Expand Down Expand Up @@ -4809,6 +4861,14 @@ class swift_ptrauth_struct_context_descriptor(EnumDescriptor)
return box->token.get();
}

TargetMetadata<Runtime> *getSingletonMetadata() const {
if (!this->hasSingletonMetadataInitialization())
return nullptr;

auto box = this->template getTrailingObjects<SingletonMetadataPointer>();
return box->token.get();
}

/// Retrieve the set of protocols that are inverted by this type's
/// primary definition.
///
Expand Down
12 changes: 8 additions & 4 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -1861,9 +1861,10 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
/// Meaningful for all type-descriptor kinds.
HasImportInfo = 2,

/// Set if the type descriptor has a pointer to a list of canonical
/// prespecializations.
HasCanonicalMetadataPrespecializations = 3,
/// Set if the generic type descriptor has a pointer to a list of canonical
/// prespecializations, or the non-generic type descriptor has a pointer to
/// its singleton metadata.
HasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer = 3,

/// Set if the metadata contains a pointer to a layout string
HasLayoutString = 4,
Expand Down Expand Up @@ -1948,7 +1949,10 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {

FLAGSET_DEFINE_FLAG_ACCESSORS(HasImportInfo, hasImportInfo, setHasImportInfo)

FLAGSET_DEFINE_FLAG_ACCESSORS(HasCanonicalMetadataPrespecializations, hasCanonicalMetadataPrespecializations, setHasCanonicalMetadataPrespecializations)
FLAGSET_DEFINE_FLAG_ACCESSORS(
HasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer,
hasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer,
setHasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer)

FLAGSET_DEFINE_FLAG_ACCESSORS(HasLayoutString,
hasLayoutString,
Expand Down
7 changes: 6 additions & 1 deletion include/swift/AST/IRGenOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@ class IRGenOptions {
/// arguments.
unsigned PrespecializeGenericMetadata : 1;

/// Emit pointers to the corresponding type metadata in non-public non-generic
/// type descriptors.
unsigned EmitSingletonMetadataPointers : 1;

/// The path to load legacy type layouts from.
StringRef ReadLegacyTypeInfoPath;

Expand Down Expand Up @@ -576,7 +580,8 @@ class IRGenOptions {
LazyInitializeProtocolConformances(false),
IndirectAsyncFunctionPointer(false),
CompactAbsoluteFunctionPointer(false), DisableLegacyTypeInfo(false),
PrespecializeGenericMetadata(false), UseIncrementalLLVMCodeGen(true),
PrespecializeGenericMetadata(false),
EmitSingletonMetadataPointers(false), UseIncrementalLLVMCodeGen(true),
UseTypeLayoutValueHandling(true), ForceStructTypeLayouts(false),
EnableLargeLoadableTypesReg2Mem(true),
EnableLayoutStringValueWitnesses(false),
Expand Down
4 changes: 4 additions & 0 deletions include/swift/Option/FrontendOptions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,10 @@ def prespecialize_generic_metadata : Flag<["-"], "prespecialize-generic-metadata
HelpText<"Statically specialize metadata for generic types at types that "
"are known to be used in source.">;

def emit_singleton_metadata_pointer : Flag<["-"], "emit-singleton-metadata-pointer">,
HelpText<"Emit a pointer to the corresponding type metadata into non-public "
"non-generic type descriptors.">;

def read_legacy_type_info_path_EQ : Joined<["-"], "read-legacy-type-info-path=">,
HelpText<"Read legacy type layout from the given path instead of default path">;

Expand Down
4 changes: 4 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3436,6 +3436,10 @@ static bool ParseIRGenArgs(IRGenOptions &Opts, ArgList &Args,
Opts.PrespecializeGenericMetadata = true;
}

if (Args.hasArg(OPT_emit_singleton_metadata_pointer)) {
Opts.EmitSingletonMetadataPointers = true;
}

if (const Arg *A = Args.getLastArg(OPT_read_legacy_type_info_path_EQ)) {
Opts.ReadLegacyTypeInfoPath = A->getValue();
}
Expand Down
45 changes: 42 additions & 3 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1589,7 +1589,8 @@ namespace {
void setCommonFlags(TypeContextDescriptorFlags &flags) {
setClangImportedFlags(flags);
setMetadataInitializationKind(flags);
setHasCanonicalMetadataPrespecializations(flags);
setHasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer(
flags);
}

void setClangImportedFlags(TypeContextDescriptorFlags &flags) {
Expand Down Expand Up @@ -1623,8 +1624,11 @@ namespace {
flags.setMetadataInitialization(MetadataInitialization);
}

void setHasCanonicalMetadataPrespecializations(TypeContextDescriptorFlags &flags) {
flags.setHasCanonicalMetadataPrespecializations(hasCanonicalMetadataPrespecializations());
void setHasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer(
TypeContextDescriptorFlags &flags) {
flags.setHasCanonicalMetadataPrespecializationsOrSingletonMetadataPointer(
hasCanonicalMetadataPrespecializations() ||
hasSingletonMetadataPointer());
}

bool hasCanonicalMetadataPrespecializations() {
Expand All @@ -1636,6 +1640,30 @@ namespace {
});
}

bool hasSingletonMetadataPointer() {
if (!IGM.IRGen.Opts.EmitSingletonMetadataPointers)
return false;

bool isGeneric = Type->isGenericContext();
bool noInitialization =
MetadataInitialization ==
TypeContextDescriptorFlags::NoMetadataInitialization;
auto isPublic = Type->getFormalAccessScope().isPublic();

auto kind = asImpl().getContextKind();
auto isSupportedKind = kind == ContextDescriptorKind::Class ||
kind == ContextDescriptorKind::Struct ||
kind == ContextDescriptorKind::Enum;

// Only emit a singleton metadata pointer if:
// The type is not generic (there's no single metadata if it's generic).
// The metadata doesn't require runtime initialization. (The metadata
// can't safely be accessed directly if it does.)
// The type is not public. (If it's public it can be found by symbol.)
// It's a class, struct, or enum.
return !isGeneric && noInitialization && !isPublic && isSupportedKind;
}

void maybeAddMetadataInitialization() {
switch (MetadataInitialization) {
case TypeContextDescriptorFlags::NoMetadataInitialization:
Expand Down Expand Up @@ -1724,6 +1752,14 @@ namespace {
B.addRelativeAddress(cachingOnceToken);
}

void maybeAddSingletonMetadataPointer() {
if (hasSingletonMetadataPointer()) {
auto type = Type->getDeclaredTypeInContext()->getCanonicalType();
auto metadata = IGM.getAddrOfTypeMetadata(type);
B.addRelativeAddress(metadata);
}
}

// Subclasses should provide:
// ContextDescriptorKind getContextKind();
// void addLayoutInfo();
Expand Down Expand Up @@ -1758,6 +1794,7 @@ namespace {
super::layout();
maybeAddCanonicalMetadataPrespecializations();
addInvertedProtocols();
maybeAddSingletonMetadataPointer();
}

ContextDescriptorKind getContextKind() {
Expand Down Expand Up @@ -1832,6 +1869,7 @@ namespace {
super::layout();
maybeAddCanonicalMetadataPrespecializations();
addInvertedProtocols();
maybeAddSingletonMetadataPointer();
}

ContextDescriptorKind getContextKind() {
Expand Down Expand Up @@ -1965,6 +2003,7 @@ namespace {
addObjCResilientClassStubInfo();
maybeAddCanonicalMetadataPrespecializations();
addInvertedProtocols();
maybeAddSingletonMetadataPointer();
}

void addIncompleteMetadataOrRelocationFunction() {
Expand Down
11 changes: 11 additions & 0 deletions test/IRGen/emit-singleton-metadata-pointer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// RUN: %target-swift-frontend -prespecialize-generic-metadata -target %module-target-future -emit-ir %s -emit-singleton-metadata-pointer | %FileCheck %s -DINT=i%target-ptrsize -DALIGNMENT=%target-alignment --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK --check-prefix=CHECK-%target-vendor --dump-input=always

// CHECK: @"$s4main23PrivateNongenericStructVMn" =
// CHECK-SAME: hidden constant <{ i32, i32, i32, i32, i32, i32, i32, i32 }>
// -- flags: struct, unique, has singleton metadata pointer
// CHECK-SAME: <{ i32 524369,
// -- 32-bit relative pointer to metadata
// CHECK-64-SAME: i32 trunc (i64 sub (i64 ptrtoint (ptr getelementptr inbounds (<{ {{.*}} }>, ptr @"$s4main23PrivateNongenericStructVMf", i32 0, i32 2) to i64)
// CHECK-32-SAME: i32 sub (i32 ptrtoint (ptr getelementptr inbounds (<{ {{.*}} }>, ptr @"$s4main23PrivateNongenericStructVMf", i32 0, i32 2) to i32)

internal struct PrivateNongenericStruct {}