Skip to content

Resolve cyclic dependencies in in-place metadata initialization of value types #18214

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
1 change: 1 addition & 0 deletions docs/ABI/Mangling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Globals
global ::= nominal-type 'Mr' // generic type completion function
global ::= nominal-type 'Mi' // generic type instantiation function
global ::= nominal-type 'MI' // generic type instantiation cache
global ::= nominal-type 'Ml' // in-place type initialization cache
global ::= nominal-type 'Mm' // class metaclass
global ::= nominal-type 'Mn' // nominal type descriptor
global ::= module 'MXM' // module descriptor
Expand Down
108 changes: 104 additions & 4 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -3364,6 +3364,14 @@ class TargetTypeContextDescriptor
return TypeContextDescriptorFlags(this->Flags.getKindSpecificFlags());
}

/// Does this type have non-trivial "in place" metadata initialization?
///
/// The type of the initialization-control structure differs by subclass,
/// so it doesn't appear here.
bool hasInPlaceMetadataInitialization() const {
return getTypeContextDescriptorFlags().hasInPlaceMetadataInitialization();
}

const TargetTypeGenericContextDescriptorHeader<Runtime> &
getFullGenericContextHeader() const;

Expand Down Expand Up @@ -3709,10 +3717,49 @@ class TargetClassDescriptor final

using ClassDescriptor = TargetClassDescriptor<InProcess>;

/// The cache structure for non-trivial initialization of singleton value
/// metadata.
template <typename Runtime>
struct TargetInPlaceValueMetadataCache {
/// The metadata pointer. Clients can do dependency-ordered loads
/// from this, and if they see a non-zero value, it's a Complete
/// metadata.
std::atomic<TargetMetadataPointer<Runtime, TargetMetadata>> Metadata;

/// The private cache data.
std::atomic<TargetPointer<Runtime, void>> Private;
};
using InPlaceValueMetadataCache =
TargetInPlaceValueMetadataCache<InProcess>;

/// The control structure for performing non-trivial initialization of
/// singleton value metadata, which is required when e.g. a non-generic
/// value type has a resilient component type.
template <typename Runtime>
struct TargetInPlaceValueMetadataInitialization {
/// The initialization cache. Out-of-line because mutable.
TargetRelativeDirectPointer<Runtime,
TargetInPlaceValueMetadataCache<Runtime>>
InitializationCache;

/// The incomplete metadata.
TargetRelativeDirectPointer<Runtime, TargetMetadata<Runtime>>
IncompleteMetadata;

/// The completion function. The pattern will always be null.
TargetRelativeDirectPointer<Runtime, MetadataCompleter>
CompletionFunction;
};

template <typename Runtime>
class TargetValueTypeDescriptor
: public TargetTypeContextDescriptor<Runtime> {
public:
using InPlaceMetadataInitialization =
TargetInPlaceValueMetadataInitialization<Runtime>;

const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const;

static bool classof(const TargetContextDescriptor<Runtime> *cd) {
return cd->getKind() == ContextDescriptorKind::Struct ||
cd->getKind() == ContextDescriptorKind::Enum;
Expand All @@ -3724,16 +3771,30 @@ template <typename Runtime>
class TargetStructDescriptor final
: public TargetValueTypeDescriptor<Runtime>,
public TrailingGenericContextObjects<TargetStructDescriptor<Runtime>,
TargetTypeGenericContextDescriptorHeader> {
TargetTypeGenericContextDescriptorHeader,
TargetInPlaceValueMetadataInitialization<Runtime>> {
public:
using InPlaceMetadataInitialization =
TargetInPlaceValueMetadataInitialization<Runtime>;

private:
using TrailingGenericContextObjects =
TrailingGenericContextObjects<TargetStructDescriptor<Runtime>,
TargetTypeGenericContextDescriptorHeader>;
TargetTypeGenericContextDescriptorHeader,
InPlaceMetadataInitialization>;

using TrailingObjects =
typename TrailingGenericContextObjects::TrailingObjects;
friend TrailingObjects;

template<typename T>
using OverloadToken = typename TrailingObjects::template OverloadToken<T>;

using TrailingGenericContextObjects::numTrailingObjects;
size_t numTrailingObjects(OverloadToken<InPlaceMetadataInitialization>) const{
return this->hasInPlaceMetadataInitialization() ? 1 : 0;
}

public:
using TrailingGenericContextObjects::getGenericContext;
using TrailingGenericContextObjects::getGenericContextHeader;
Expand All @@ -3752,6 +3813,11 @@ class TargetStructDescriptor final
/// its stored properties.
bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; }

const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const{
assert(this->hasInPlaceMetadataInitialization());
return *this->template getTrailingObjects<InPlaceMetadataInitialization>();
}

static constexpr int32_t getGenericArgumentOffset() {
return TargetStructMetadata<Runtime>::getGenericArgumentOffset();
}
Expand All @@ -3767,16 +3833,30 @@ template <typename Runtime>
class TargetEnumDescriptor final
: public TargetValueTypeDescriptor<Runtime>,
public TrailingGenericContextObjects<TargetEnumDescriptor<Runtime>,
TargetTypeGenericContextDescriptorHeader> {
TargetTypeGenericContextDescriptorHeader,
TargetInPlaceValueMetadataInitialization<Runtime>> {
public:
using InPlaceMetadataInitialization =
TargetInPlaceValueMetadataInitialization<Runtime>;

private:
using TrailingGenericContextObjects =
TrailingGenericContextObjects<TargetEnumDescriptor<Runtime>,
TargetTypeGenericContextDescriptorHeader>;
TargetTypeGenericContextDescriptorHeader,
InPlaceMetadataInitialization>;

using TrailingObjects =
typename TrailingGenericContextObjects::TrailingObjects;
friend TrailingObjects;

template<typename T>
using OverloadToken = typename TrailingObjects::template OverloadToken<T>;

using TrailingGenericContextObjects::numTrailingObjects;
size_t numTrailingObjects(OverloadToken<InPlaceMetadataInitialization>) const{
return this->hasInPlaceMetadataInitialization() ? 1 : 0;
}

public:
using TrailingGenericContextObjects::getGenericContext;
using TrailingGenericContextObjects::getGenericContextHeader;
Expand Down Expand Up @@ -3813,6 +3893,11 @@ class TargetEnumDescriptor final
return TargetEnumMetadata<Runtime>::getGenericArgumentOffset();
}

const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const{
assert(this->hasInPlaceMetadataInitialization());
return *this->template getTrailingObjects<InPlaceMetadataInitialization>();
}

static bool classof(const TargetContextDescriptor<Runtime> *cd) {
return cd->getKind() == ContextDescriptorKind::Enum;
}
Expand Down Expand Up @@ -3901,6 +3986,21 @@ TargetTypeContextDescriptor<Runtime>::getGenericParams() const {
}
}

template<typename Runtime>
inline const TargetInPlaceValueMetadataInitialization<Runtime> &
TargetValueTypeDescriptor<Runtime>::getInPlaceMetadataInitialization() const {
switch (this->getKind()) {
case ContextDescriptorKind::Enum:
return llvm::cast<TargetEnumDescriptor<Runtime>>(this)
->getInPlaceMetadataInitialization();
case ContextDescriptorKind::Struct:
return llvm::cast<TargetStructDescriptor<Runtime>>(this)
->getInPlaceMetadataInitialization();
default:
swift_runtime_unreachable("Not a value type descriptor.");
}
}

} // end namespace swift

#pragma clang diagnostic pop
Expand Down
12 changes: 12 additions & 0 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,14 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
/// declarations associated with the same declaration.
IsSynthesizedRelatedEntity = 3,

/// Set if the type requires non-trivial but non-generic metadata
/// initialization. It may or may not be truly "in place" depending
/// on the kind of metadata.
///
/// Currently only meaningful for value descriptors, but will be
/// extended to class descriptors.
HasInPlaceMetadataInitialization = 4,

/// Set if the context descriptor is includes metadata for dynamically
/// constructing a class's vtables at metadata instantiation time.
///
Expand Down Expand Up @@ -1237,6 +1245,10 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
isSynthesizedRelatedEntity,
setIsSynthesizedRelatedEntity)

FLAGSET_DEFINE_FLAG_ACCESSORS(HasInPlaceMetadataInitialization,
hasInPlaceMetadataInitialization,
setHasInPlaceMetadataInitialization)

FLAGSET_DEFINE_FLAG_ACCESSORS(Class_HasVTable,
class_hasVTable,
class_setHasVTable)
Expand Down
1 change: 1 addition & 0 deletions include/swift/Demangling/DemangleNodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ NODE(TypeMetadataAccessFunction)
NODE(TypeMetadataCompletionFunction)
NODE(TypeMetadataInstantiationCache)
NODE(TypeMetadataInstantiationFunction)
NODE(TypeMetadataInPlaceInitializationCache)
NODE(TypeMetadataLazyCache)
NODE(UncurriedFunctionType)
#define REF_STORAGE(Name, ...) NODE(Name)
Expand Down
13 changes: 12 additions & 1 deletion include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ class LinkEntity {
/// The pointer is a NominalTypeDecl*.
TypeMetadataInstantiationFunction,

/// The in-place initialization cache for a generic nominal type.
/// The pointer is a NominalTypeDecl*.
TypeMetadataInPlaceInitializationCache,

/// The completion function for a generic or resilient nominal type.
/// The pointer is a NominalTypeDecl*.
TypeMetadataCompletionFunction,
Expand Down Expand Up @@ -503,12 +507,19 @@ class LinkEntity {
return entity;
}

static LinkEntity forTypeMetadataInstantiationFunction(NominalTypeDecl *decl){
static LinkEntity forTypeMetadataInstantiationFunction(NominalTypeDecl *decl) {
LinkEntity entity;
entity.setForDecl(Kind::TypeMetadataInstantiationFunction, decl);
return entity;
}

static LinkEntity forTypeMetadataInPlaceInitializationCache(
NominalTypeDecl *decl) {
LinkEntity entity;
entity.setForDecl(Kind::TypeMetadataInPlaceInitializationCache, decl);
return entity;
}

static LinkEntity forTypeMetadataCompletionFunction(NominalTypeDecl *decl) {
LinkEntity entity;
entity.setForDecl(Kind::TypeMetadataCompletionFunction, decl);
Expand Down
15 changes: 12 additions & 3 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -1050,8 +1050,10 @@ class MetadataReader {
sizeof(flags)))
return nullptr;

TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags());
unsigned baseSize = 0;
unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader);
unsigned inPlaceInitSize = 0;
bool hasVTable = false;
switch (auto kind = flags.getKind()) {
case ContextDescriptorKind::Module:
Expand All @@ -1067,16 +1069,23 @@ class MetadataReader {
case ContextDescriptorKind::Class:
baseSize = sizeof(TargetClassDescriptor<Runtime>);
genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader);
hasVTable = TypeContextDescriptorFlags(flags.getKindSpecificFlags())
.class_hasVTable();
hasVTable = typeFlags.class_hasVTable();
break;
case ContextDescriptorKind::Enum:
baseSize = sizeof(TargetEnumDescriptor<Runtime>);
genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader);
if (typeFlags.hasInPlaceMetadataInitialization()) {
inPlaceInitSize =
sizeof(TargetInPlaceValueMetadataInitialization<Runtime>);
}
break;
case ContextDescriptorKind::Struct:
baseSize = sizeof(TargetStructDescriptor<Runtime>);
genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader);
if (typeFlags.hasInPlaceMetadataInitialization()) {
inPlaceInitSize =
sizeof(TargetInPlaceValueMetadataInitialization<Runtime>);
}
break;
case ContextDescriptorKind::Protocol:
baseSize = sizeof(TargetProtocolDescriptorRef<Runtime>);
Expand Down Expand Up @@ -1122,7 +1131,7 @@ class MetadataReader {
+ header.VTableSize * sizeof(TargetMethodDescriptor<Runtime>);
}

unsigned size = baseSize + genericsSize + vtableSize;
unsigned size = baseSize + genericsSize + vtableSize + inPlaceInitSize;
auto buffer = (uint8_t *)malloc(size);
if (!Reader->readBytes(RemoteAddress(address), buffer, size)) {
free(buffer);
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,13 @@ ClassMetadataBounds getResilientMetadataBounds(
const ClassDescriptor *descriptor);
int32_t getResilientImmediateMembersOffset(const ClassDescriptor *descriptor);

/// \brief Fetch a uniqued metadata object for a nominal type which requires
/// in-place metadata initialization.
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
MetadataResponse
swift_getInPlaceMetadata(MetadataRequest request,
const TypeContextDescriptor *description);

/// \brief Fetch a uniqued metadata object for a generic nominal type.
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
MetadataResponse
Expand Down
7 changes: 7 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ FUNCTION(GetForeignWitnessTable, swift_getForeignWitnessTable, C_CC,
ProtocolDescriptorPtrTy),
ATTRS(NoUnwind, ReadNone))

// MetadataResponse swift_getInPlaceMetadata(MetadataRequest request,
// TypeContextDescriptor *type);
FUNCTION(GetInPlaceMetadata, swift_getInPlaceMetadata, SwiftCC,
RETURNS(TypeMetadataResponseTy),
ARGS(SizeTy, TypeContextDescriptorPtrTy),
ATTRS(NoUnwind, ReadNone))

// MetadataResponse swift_getGenericMetadata(MetadataRequest request,
// const void * const *arguments,
// TypeContextDescriptor *type);
Expand Down
3 changes: 3 additions & 0 deletions lib/Demangling/Demangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,9 @@ NodePointer Demangler::demangleMetatype() {
return createWithPoppedType(Node::Kind::TypeMetadataInstantiationFunction);
case 'r':
return createWithPoppedType(Node::Kind::TypeMetadataCompletionFunction);
case 'l':
return createWithPoppedType(
Node::Kind::TypeMetadataInPlaceInitializationCache);
case 'L':
return createWithPoppedType(Node::Kind::TypeMetadataLazyCache);
case 'm':
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/NodePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ class NodePrinter {
case Node::Kind::TypeMetadataCompletionFunction:
case Node::Kind::TypeMetadataInstantiationCache:
case Node::Kind::TypeMetadataInstantiationFunction:
case Node::Kind::TypeMetadataInPlaceInitializationCache:
case Node::Kind::TypeMetadataLazyCache:
case Node::Kind::UncurriedFunctionType:
#define REF_STORAGE(Name, ...) \
Expand Down Expand Up @@ -1509,6 +1510,10 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
Printer << "type metadata instantiation function for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::TypeMetadataInPlaceInitializationCache:
Printer << "type metadata in-place initialization cache for ";
print(Node->getChild(0));
return nullptr;
case Node::Kind::TypeMetadataCompletionFunction:
Printer << "type metadata completion function for ";
print(Node->getChild(0));
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/OldRemangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,11 @@ void Remangler::mangleTypeMetadataInstantiationFunction(Node *node) {
mangleSingleChildNode(node); // type
}

void Remangler::mangleTypeMetadataInPlaceInitializationCache(Node *node) {
Out << "Ml";
mangleSingleChildNode(node); // type
}

void Remangler::mangleTypeMetadataCompletionFunction(Node *node) {
Out << "Mr";
mangleSingleChildNode(node); // type
Expand Down
5 changes: 5 additions & 0 deletions lib/Demangling/Remangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,11 @@ void Remangler::mangleTypeMetadataInstantiationFunction(Node *node) {
Buffer << "Mi";
}

void Remangler::mangleTypeMetadataInPlaceInitializationCache(Node *node) {
mangleSingleChildNode(node);
Buffer << "Ml";
}

void Remangler::mangleTypeMetadataCompletionFunction(Node *node) {
mangleSingleChildNode(node);
Buffer << "Mr";
Expand Down
Loading