Skip to content

In-place class metadata initialization #18694

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 10 commits into from
Aug 21, 2018
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
127 changes: 85 additions & 42 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -3301,6 +3301,69 @@ struct TargetForeignMetadataInitialization {
CompletionFunction;
};

/// 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>;

/// An instantiation pattern for non-generic resilient class metadata.
/// Used in conjunction with InPlaceValueMetadataInitialization.
using MetadataRelocator =
Metadata *(const TargetTypeContextDescriptor<InProcess> *description);

/// 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;

union {
/// The incomplete metadata, for structs, enums and classes without
/// resilient ancestry.
TargetRelativeDirectPointer<Runtime, TargetMetadata<Runtime>>
IncompleteMetadata;

/// If the class descriptor's hasResilientSuperclass() flag is set,
/// this field instead points at a function that allocates metadata
/// with the correct size at runtime.
TargetRelativeDirectPointer<Runtime, MetadataRelocator>
RelocationFunction;
};

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

bool hasRelocationFunction(
const TargetTypeContextDescriptor<Runtime> *description) const {
auto *classDescription =
dyn_cast<TargetClassDescriptor<Runtime>>(description);
return (classDescription != nullptr &&
classDescription->hasResilientSuperclass());
}

TargetMetadata<Runtime> *allocate(
const TargetTypeContextDescriptor<Runtime> *description) const {
if (hasRelocationFunction(description))
return RelocationFunction(description);
return IncompleteMetadata.get();
}
};

template <typename Runtime>
class TargetTypeContextDescriptor
: public TargetContextDescriptor<Runtime> {
Expand Down Expand Up @@ -3355,8 +3418,11 @@ class TargetTypeContextDescriptor
const TargetForeignMetadataInitialization<Runtime> &
getForeignMetadataInitialization() const;

const TargetInPlaceValueMetadataInitialization<Runtime> &
getInPlaceMetadataInitialization() const;
Copy link
Contributor

Choose a reason for hiding this comment

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

The reason I didn't have this here (and called it InPlace*Value*MetadataInitialization in the first place) is that I was anticipating that resilient classes might need more members, like a reference to a pattern. So before you unify them, you might want to consider whether you're just making more work for yourself to split them again in a follow-up patch. (It'd be better if value metadata didn't have to pay for an always-null reference to a pattern.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we can put the pattern in the class context descriptor, and use a trailing object for it so it’s only there for resilient classes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

... or just say that IncompleteMetadata is either a metadata or a pattern (instead of metadata or function). That was sort of my plan here. I wasn’t going to add an always-null field.


const TargetTypeGenericContextDescriptorHeader<Runtime> &
getFullGenericContextHeader() const;
getFullGenericContextHeader() const;

const TargetGenericContextDescriptorHeader<Runtime> &
getGenericContextHeader() const {
Expand Down Expand Up @@ -3479,13 +3545,15 @@ class TargetClassDescriptor final
TargetTypeGenericContextDescriptorHeader,
/*additional trailing objects:*/
TargetForeignMetadataInitialization<Runtime>,
TargetInPlaceValueMetadataInitialization<Runtime>,
TargetVTableDescriptorHeader<Runtime>,
TargetMethodDescriptor<Runtime>> {
private:
using TrailingGenericContextObjects =
TrailingGenericContextObjects<TargetClassDescriptor<Runtime>,
TargetTypeGenericContextDescriptorHeader,
TargetForeignMetadataInitialization<Runtime>,
TargetInPlaceValueMetadataInitialization<Runtime>,
TargetVTableDescriptorHeader<Runtime>,
TargetMethodDescriptor<Runtime>>;

Expand All @@ -3498,6 +3566,8 @@ class TargetClassDescriptor final
using VTableDescriptorHeader = TargetVTableDescriptorHeader<Runtime>;
using ForeignMetadataInitialization =
TargetForeignMetadataInitialization<Runtime>;
using InPlaceMetadataInitialization =
TargetInPlaceValueMetadataInitialization<Runtime>;

using StoredPointer = typename Runtime::StoredPointer;
using StoredPointerDifference = typename Runtime::StoredPointerDifference;
Expand Down Expand Up @@ -3589,6 +3659,10 @@ class TargetClassDescriptor final
return this->hasForeignMetadataInitialization() ? 1 : 0;
}

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

size_t numTrailingObjects(OverloadToken<VTableDescriptorHeader>) const {
return hasVTable() ? 1 : 0;
}
Expand All @@ -3606,6 +3680,11 @@ class TargetClassDescriptor final
return *this->template getTrailingObjects<ForeignMetadataInitialization>();
}

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

/// True if metadata records for this type have a field offset vector for
/// its stored properties.
bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; }
Expand Down Expand Up @@ -3698,49 +3777,10 @@ 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 Down Expand Up @@ -4015,16 +4055,19 @@ TargetTypeContextDescriptor<Runtime>::getForeignMetadataInitialization() const {

template<typename Runtime>
inline const TargetInPlaceValueMetadataInitialization<Runtime> &
TargetValueTypeDescriptor<Runtime>::getInPlaceMetadataInitialization() const {
TargetTypeContextDescriptor<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();
case ContextDescriptorKind::Class:
return llvm::cast<TargetClassDescriptor<Runtime>>(this)
->getInPlaceMetadataInitialization();
default:
swift_runtime_unreachable("Not a value type descriptor.");
swift_runtime_unreachable("Not a enum, struct or class type descriptor.");
}
}

Expand Down
9 changes: 9 additions & 0 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,12 @@ enum class ClassLayoutFlags : uintptr_t {

/// The ABI baseline algorithm, i.e. the algorithm implemented in Swift 5.
Swift5Algorithm = 0x00,

/// If true, the vtable for this class and all of its superclasses was emitted
/// statically in the class metadata. If false, the superclass vtable is
/// copied from superclass metadata, and the immediate class vtable is
/// initialized from the type context descriptor.
HasStaticVTable = 0x100,
};
static inline ClassLayoutFlags operator|(ClassLayoutFlags lhs,
ClassLayoutFlags rhs) {
Expand All @@ -1052,6 +1058,9 @@ static inline ClassLayoutFlags getLayoutAlgorithm(ClassLayoutFlags flags) {
return ClassLayoutFlags(uintptr_t(flags)
& uintptr_t(ClassLayoutFlags::AlgorithmMask));
}
static inline bool hasStaticVTable(ClassLayoutFlags flags) {
return uintptr_t(flags) & uintptr_t(ClassLayoutFlags::HasStaticVTable);
}

/// Flags for enum layout.
enum class EnumLayoutFlags : uintptr_t {
Expand Down
17 changes: 0 additions & 17 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -3625,23 +3625,6 @@ class ClassDecl final : public NominalTypeDecl {
/// might have implicitly @objc members, but will never itself be @objc.
ObjCClassKind checkObjCAncestry() const;

/// \brief Whether this class or its superclasses has some form of generic
/// context.
///
/// For example, given
///
/// class A<X> {}
/// class B : A<Int> {}
/// struct C<T> {
/// struct Inner {}
/// }
/// class D {}
/// class E: D {}
///
/// Calling hasGenericAncestry() on `B` returns `A<Int>`, on `C<T>.Inner`
/// returns `C<T>.Inner`, but on `E` it returns null.
ClassDecl *getGenericAncestor() const;

/// The type of metaclass to use for a class.
enum class MetaclassKind : uint8_t {
ObjC,
Expand Down
10 changes: 6 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1570,9 +1570,9 @@ ERROR(objc_protocol_cannot_have_conditional_conformance,none,
(Type, Type))
ERROR(objc_protocol_in_generic_extension,none,
"conformance of "
"%select{|subclass of a }2%select{class from generic context|generic class}3"
" %0 to @objc protocol %1 cannot be in an extension",
(Type, Type, bool, bool))
"%select{class from generic context|generic class}0 "
"%1 to @objc protocol %2 cannot be in an extension",
(bool, Type, Type))
ERROR(conditional_conformances_cannot_imply_conformances,none,
"conditional conformance of type %0 to protocol %1 does not imply conformance to "
"inherited protocol %2",
Expand Down Expand Up @@ -3539,7 +3539,9 @@ ERROR(invalid_nonobjc_extension,none,
ERROR(objc_in_extension_context,none,
"members of constrained extensions cannot be declared @objc", ())
ERROR(objc_in_generic_extension,none,
"@objc is not supported within extensions of generic classes or classes that inherit from generic classes", ())
"members of extensions of "
"%select{classes from generic context|generic classes}0 "
"cannot be declared @objc", (bool))
ERROR(objc_operator, none,
"operator methods cannot be declared @objc", ())
ERROR(objc_operator_proto, none,
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Basic/RelativePointer.h
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ class RelativeDirectPointer<RetTy (ArgTy...), Nullable, Offset> :
return this->get();
}

RetTy operator()(ArgTy...arg) {
RetTy operator()(ArgTy...arg) const {
return this->get()(std::forward<ArgTy>(arg)...);
}

Expand Down
7 changes: 4 additions & 3 deletions include/swift/Runtime/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -580,14 +580,15 @@ void swift_initStructMetadata(StructMetadata *self,
/// members.
SWIFT_RUNTIME_EXPORT
ClassMetadata *
swift_relocateClassMetadata(ClassMetadata *self,
size_t templateSize,
size_t numImmediateMembers);
swift_relocateClassMetadata(ClassDescriptor *descriptor,
ClassMetadata *pattern,
size_t patternSize);

/// Initialize the field offset vector for a dependent-layout class, using the
/// "Universal" layout strategy.
SWIFT_RUNTIME_EXPORT
void swift_initClassMetadata(ClassMetadata *self,
ClassMetadata *super,
ClassLayoutFlags flags,
size_t numFields,
const TypeLayout * const *fieldTypes,
Expand Down
11 changes: 6 additions & 5 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -808,25 +808,26 @@ FUNCTION(GetExistentialMetadata,
ProtocolDescriptorRefTy->getPointerTo()),
ATTRS(NoUnwind, ReadOnly))

// Metadata *swift_relocateClassMetadata(Metadata *self,
// size_t templateSize,
// size_t numImmediateMembers);
// Metadata *swift_relocateClassMetadata(TypeContextDescriptor *descriptor,
// Metadata *pattern,
// size_t patternSize);
FUNCTION(RelocateClassMetadata,
swift_relocateClassMetadata, C_CC,
RETURNS(TypeMetadataPtrTy),
ARGS(TypeMetadataPtrTy, SizeTy, SizeTy),
ARGS(TypeContextDescriptorPtrTy, TypeMetadataPtrTy, SizeTy),
ATTRS(NoUnwind))

// struct FieldInfo { size_t Size; size_t AlignMask; };
// void swift_initClassMetadata(Metadata *self,
// Metadata *super,
// ClassLayoutFlags flags,
// size_t numFields,
// TypeLayout * const *fieldTypes,
// size_t *fieldOffsets);
FUNCTION(InitClassMetadata,
swift_initClassMetadata, C_CC,
RETURNS(VoidTy),
ARGS(TypeMetadataPtrTy, SizeTy, SizeTy,
ARGS(TypeMetadataPtrTy, TypeMetadataPtrTy, SizeTy, SizeTy,
Int8PtrPtrTy->getPointerTo(),
SizeTy->getPointerTo()),
ATTRS(NoUnwind))
Expand Down
13 changes: 0 additions & 13 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3431,19 +3431,6 @@ ClassDecl::findImplementingMethod(const AbstractFunctionDecl *Method) const {
return nullptr;
}

ClassDecl *ClassDecl::getGenericAncestor() const {
ClassDecl *current = const_cast<ClassDecl *>(this);

while (current) {
if (current->isGenericContext())
return current;

current = current->getSuperclassDecl();
}

return nullptr;
}

EnumCaseDecl *EnumCaseDecl::create(SourceLoc CaseLoc,
ArrayRef<EnumElementDecl *> Elements,
DeclContext *DC) {
Expand Down
6 changes: 4 additions & 2 deletions lib/IRGen/ClassLayout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ using namespace irgen;

ClassLayout::ClassLayout(const StructLayoutBuilder &builder,
bool isFixedSize,
bool metadataRequiresDynamicInitialization,
bool metadataRequiresInitialization,
bool metadataRequiresRelocation,
llvm::Type *classTy,
ArrayRef<VarDecl *> allStoredProps,
ArrayRef<FieldAccess> allFieldAccesses,
Expand All @@ -35,7 +36,8 @@ ClassLayout::ClassLayout(const StructLayoutBuilder &builder,
MinimumSize(builder.getSize()),
IsFixedLayout(builder.isFixedLayout()),
IsFixedSize(isFixedSize),
MetadataRequiresDynamicInitialization(metadataRequiresDynamicInitialization),
MetadataRequiresInitialization(metadataRequiresInitialization),
MetadataRequiresRelocation(metadataRequiresRelocation),
Ty(classTy),
AllStoredProperties(allStoredProps),
AllFieldAccesses(allFieldAccesses),
Expand Down
Loading