diff --git a/docs/ABI/Mangling.rst b/docs/ABI/Mangling.rst index 9d6b204435bd2..4851aa97e0b23 100644 --- a/docs/ABI/Mangling.rst +++ b/docs/ABI/Mangling.rst @@ -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 diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index ab3cb49e91418..f7a4f8b23b06c 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -1175,144 +1175,6 @@ using ObjCClassWrapperMetadata /// us to use. template struct TargetForeignTypeMetadata : public TargetMetadata { - using StoredPointer = typename Runtime::StoredPointer; - using StoredSize = typename Runtime::StoredSize; - using InitializationFunction_t = - void (TargetForeignTypeMetadata *selectedMetadata); - using RuntimeMetadataPointer = - ConstTargetMetadataPointer; - - /// An invasive cache for the runtime-uniqued lookup structure that is stored - /// in the header prefix of foreign metadata records. - /// - /// Prior to initialization, as emitted by the compiler, this contains the - /// initialization flags. - /// After initialization, it holds a pointer to the actual, runtime-uniqued - /// metadata for this type. - struct CacheValue { - StoredSize Value; - - /// Work around a bug in libstdc++'s std::atomic that requires the type to - /// be default-constructible. - CacheValue() = default; - - explicit CacheValue(RuntimeMetadataPointer p) - : Value(reinterpret_cast(p)) - {} - - /// Various flags. The largest flag bit should be less than 4096 so that - /// a flag set is distinguishable from a valid pointer. - enum : StoredSize { - /// This metadata has an initialization callback function. If - /// this flag is not set, the metadata object needn't actually - /// have a InitializationFunction field, and that field will be - /// undefined. - HasInitializationFunction = 0x1, - - LargestFlagMask = 0xFFF, - }; - - /// True if the metadata record associated with this cache has not been - /// initialized, so contains a flag set describing parameters to the - /// initialization operation. isFlags() == !isInitialized() - bool isFlags() const { - return Value <= LargestFlagMask; - } - /// True if the metadata record associated with this cache has an - /// initialization function which must be run if it is picked as the - /// canonical metadata record for its key. - /// - /// Undefined if !isFlags(). - bool hasInitializationFunction() const { - assert(isFlags()); - return Value & HasInitializationFunction; - } - - /// True if the metadata record associated with this cache has been - /// initialized, so the cache contains an absolute pointer to the - /// canonical metadata record for its key. isInitialized() == !isFlags() - bool isInitialized() const { - return !isFlags(); - } - - /// Gets the cached pointer to the unique canonical metadata record for - /// this metadata record's key. - /// - /// Undefined if !isInitialized(). - RuntimeMetadataPointer getCachedUniqueMetadata() const { - assert(isInitialized()); - return RuntimeMetadataPointer(Value); - } - }; - - - /// Foreign type metadata may have extra header fields depending on - /// the flags. - struct HeaderPrefix { - /// An optional callback performed when a particular metadata object - /// is chosen as the unique structure. - /// - /// If there is no initialization function, this metadata record can be - /// assumed to be immutable (except for the \c Unique invasive cache - /// field). The field is not present unless the HasInitializationFunction - /// flag is set. - RelativeDirectPointer InitializationFunction; - - mutable std::atomic Cache; - }; - - struct HeaderType : HeaderPrefix, TargetTypeMetadataHeader {}; - - CacheValue getCacheValue() const { - /// NB: This can be a relaxed-order load if there is no initialization - /// function. On platforms Swift currently targets, consume is no more - /// expensive than relaxed, so there's no reason to branch here (and LLVM - /// isn't smart enough to eliminate it when it's not needed). - /// - /// A port to a platform where relaxed is significantly less expensive than - /// consume (historically, Alpha) would probably want to preserve the - /// 'hasInitializationFunction' bit in its own word to be able to avoid - /// the consuming load when not needed. - return asFullMetadata(this)->Cache - .load(SWIFT_MEMORY_ORDER_CONSUME); - } - - void setCachedUniqueMetadata(RuntimeMetadataPointer unique) const { - auto cache = getCacheValue(); - - // If the cache was already set to a pointer, we're done. We ought to - // converge on a single unique pointer. - if (cache.isInitialized()) { - assert(cache.getCachedUniqueMetadata() == unique - && "already set unique metadata to something else"); - return; - } - - auto newCache = CacheValue(unique); - - // If there is no initialization function, this can be a relaxed store. - if (cache.hasInitializationFunction()) - asFullMetadata(this)->Cache.store(newCache, std::memory_order_relaxed); - - // Otherwise, we need a release store to publish the result of - // initialization. - else - asFullMetadata(this)->Cache.store(newCache, std::memory_order_release); - } - - /// Return the initialization function for this metadata record. - /// - /// As a prerequisite, the metadata record must not have been initialized yet, - /// and must have an initialization function to begin with, otherwise the - /// result is undefined. - InitializationFunction_t *getInitializationFunction() const { -#ifndef NDEBUG - auto cache = getCacheValue(); - assert(cache.hasInitializationFunction()); -#endif - - return asFullMetadata(this)->InitializationFunction; - } }; using ForeignTypeMetadata = TargetForeignTypeMetadata; @@ -1324,8 +1186,7 @@ using ForeignTypeMetadata = TargetForeignTypeMetadata; /// We assume for now that foreign classes are entirely opaque /// to Swift introspection. template -struct TargetForeignClassMetadata - : public TargetForeignTypeMetadata { +struct TargetForeignClassMetadata : public TargetForeignTypeMetadata { using StoredPointer = typename Runtime::StoredPointer; /// An out-of-line description of the type. @@ -1335,8 +1196,15 @@ struct TargetForeignClassMetadata ConstTargetMetadataPointer Superclass; - /// Reserved space. For now, these should be zero-initialized. - StoredPointer Reserved[3]; + /// Reserved space. For now, this should be zero-initialized. + /// If this is used for anything in the future, at least some of these + /// first bits should be flags. + StoredPointer Reserved[1]; + + ConstTargetMetadataPointer + getDescription() const { + return Description; + } static bool classof(const TargetMetadata *metadata) { return metadata->getKind() == MetadataKind::ForeignClass; @@ -2062,32 +1930,37 @@ struct TargetTypeMetadataRecord { union { /// A direct reference to a nominal type descriptor. RelativeDirectPointerIntPair, - TypeMetadataRecordKind> + TypeReferenceKind> DirectNominalTypeDescriptor; /// An indirect reference to a nominal type descriptor. RelativeDirectPointerIntPair * const, - TypeMetadataRecordKind> + TypeReferenceKind> IndirectNominalTypeDescriptor; + + // We only allow a subset of the TypeReferenceKinds here. + // Should we just acknowledge that this is a different enum? }; public: - TypeMetadataRecordKind getTypeKind() const { + TypeReferenceKind getTypeKind() const { return DirectNominalTypeDescriptor.getInt(); } const TargetTypeContextDescriptor * getTypeContextDescriptor() const { switch (getTypeKind()) { - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: + case TypeReferenceKind::DirectNominalTypeDescriptor: return DirectNominalTypeDescriptor.getPointer(); - case TypeMetadataRecordKind::Reserved: - case TypeMetadataRecordKind::IndirectObjCClass: - return nullptr; - - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: + case TypeReferenceKind::IndirectNominalTypeDescriptor: return *IndirectNominalTypeDescriptor.getPointer(); + + // These types (and any others we might add to TypeReferenceKind + // in the future) are just never used in these lists. + case TypeReferenceKind::DirectObjCClassName: + case TypeReferenceKind::IndirectObjCClass: + return nullptr; } return nullptr; @@ -2117,6 +1990,67 @@ using ProtocolRecord = TargetProtocolRecord; template class TargetGenericRequirementDescriptor; + +/// A referenc to a type. +template +struct TargetTypeReference { + union { + /// A direct reference to a nominal type descriptor. + RelativeDirectPointer> + DirectNominalTypeDescriptor; + + /// An indirect reference to a nominal type descriptor. + RelativeDirectPointer< + ConstTargetMetadataPointer> + IndirectNominalTypeDescriptor; + + /// An indirect reference to an Objective-C class. + RelativeDirectPointer< + ConstTargetMetadataPointer> + IndirectObjCClass; + + /// A direct reference to an Objective-C class name. + RelativeDirectPointer + DirectObjCClassName; + }; + + const TargetTypeContextDescriptor * + getTypeContextDescriptor(TypeReferenceKind kind) const { + switch (kind) { + case TypeReferenceKind::DirectNominalTypeDescriptor: + return DirectNominalTypeDescriptor; + + case TypeReferenceKind::IndirectNominalTypeDescriptor: + return *IndirectNominalTypeDescriptor; + + case TypeReferenceKind::DirectObjCClassName: + case TypeReferenceKind::IndirectObjCClass: + return nullptr; + } + + return nullptr; + } + +#if SWIFT_OBJC_INTEROP + /// If this type reference is one of the kinds that supports ObjC + /// references, + const TargetClassMetadata * + getObjCClass(TypeReferenceKind kind) const; +#endif + + const TargetClassMetadata * const * + getIndirectObjCClass(TypeReferenceKind kind) const { + assert(kind == TypeReferenceKind::IndirectObjCClass); + return IndirectObjCClass.get(); + } + + const char *getDirectObjCClassName(TypeReferenceKind kind) const { + assert(kind == TypeReferenceKind::DirectObjCClassName); + return DirectObjCClassName.get(); + } +}; +using TypeReference = TargetTypeReference; + /// The structure of a protocol conformance. /// /// This contains enough static information to recover the witness table for a @@ -2153,21 +2087,7 @@ struct TargetProtocolConformanceDescriptor final RelativeIndirectablePointer Protocol; // Some description of the type that conforms to the protocol. - union { - /// A direct reference to a nominal type descriptor. - RelativeDirectPointer> - DirectNominalTypeDescriptor; - - /// An indirect reference to a nominal type descriptor. - RelativeDirectPointer< - ConstTargetMetadataPointer> - IndirectNominalTypeDescriptor; - - /// An indirect reference to the metadata. - RelativeDirectPointer< - ConstTargetMetadataPointer> - IndirectObjCClass; - }; + TargetTypeReference TypeRef; // The conformance, or a generator function for the conformance. union { @@ -2187,45 +2107,24 @@ struct TargetProtocolConformanceDescriptor final return Protocol; } - TypeMetadataRecordKind getTypeKind() const { + TypeReferenceKind getTypeKind() const { return Flags.getTypeReferenceKind(); } typename ConformanceFlags::ConformanceKind getConformanceKind() const { return Flags.getConformanceKind(); } - - const TargetClassMetadata * const *getIndirectObjCClass() const { - switch (getTypeKind()) { - case TypeMetadataRecordKind::IndirectObjCClass: - break; - case TypeMetadataRecordKind::Reserved: - return nullptr; + const char *getDirectObjCClassName() const { + return TypeRef.getDirectObjCClassName(getTypeKind()); + } - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: - assert(false && "not indirect class object"); - } - - return IndirectObjCClass.get(); + const TargetClassMetadata * const *getIndirectObjCClass() const { + return TypeRef.getIndirectObjCClass(getTypeKind()); } - const TargetTypeContextDescriptor * - getTypeContextDescriptor() const { - switch (getTypeKind()) { - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: - return DirectNominalTypeDescriptor; - - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: - return *IndirectNominalTypeDescriptor; - - case TypeMetadataRecordKind::Reserved: - case TypeMetadataRecordKind::IndirectObjCClass: - return nullptr; - } - - return nullptr; + const TargetTypeContextDescriptor *getTypeContextDescriptor() const { + return TypeRef.getTypeContextDescriptor(getTypeKind()); } /// Retrieve the context of a retroactive conformance. @@ -2287,7 +2186,7 @@ struct TargetProtocolConformanceDescriptor final /// /// We currently check that the descriptor: /// - /// 1. Has a valid TypeMetadataRecordKind. + /// 1. Has a valid TypeReferenceKind. /// 2. Has a valid conformance kind. void verify() const LLVM_ATTRIBUTE_USED; #endif @@ -3135,6 +3034,15 @@ class MetadataAccessFunction { } }; +/// The control structure for performing non-trivial initialization of +/// singleton foreign metadata. +template +struct TargetForeignMetadataInitialization { + /// The completion function. The pattern will always be null. + TargetRelativeDirectPointer + CompletionFunction; +}; + template class TargetTypeContextDescriptor : public TargetContextDescriptor { @@ -3158,6 +3066,31 @@ class TargetTypeContextDescriptor return TypeContextDescriptorFlags(this->Flags.getKindSpecificFlags()); } + /// Return the kind of metadata initialization required by this type. + /// Note that this is only meaningful for non-generic types. + TypeContextDescriptorFlags::MetadataInitializationKind + getMetadataInitialization() const { + return getTypeContextDescriptorFlags().getMetadataInitialization(); + } + + /// 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(); + } + + /// Does this type have "foreign" metadata initialiation? + bool hasForeignMetadataInitialization() const { + return getTypeContextDescriptorFlags().hasForeignMetadataInitialization(); + } + + /// Given that this type has foreign metadata initialization, return the + /// control structure for it. + const TargetForeignMetadataInitialization & + getForeignMetadataInitialization() const; + const TargetTypeGenericContextDescriptorHeader & getFullGenericContextHeader() const; @@ -3296,12 +3229,14 @@ class TargetClassDescriptor final public TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, /*additional trailing objects:*/ + TargetForeignMetadataInitialization, TargetVTableDescriptorHeader, TargetMethodDescriptor> { private: using TrailingGenericContextObjects = TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, + TargetForeignMetadataInitialization, TargetVTableDescriptorHeader, TargetMethodDescriptor>; @@ -3312,6 +3247,8 @@ class TargetClassDescriptor final public: using MethodDescriptor = TargetMethodDescriptor; using VTableDescriptorHeader = TargetVTableDescriptorHeader; + using ForeignMetadataInitialization = + TargetForeignMetadataInitialization; using StoredPointer = typename Runtime::StoredPointer; using StoredPointerDifference = typename Runtime::StoredPointerDifference; @@ -3336,7 +3273,7 @@ class TargetClassDescriptor final return !Superclass.isNull(); } - TypeMetadataRecordKind getSuperclassReferenceKind() const { + TypeReferenceKind getSuperclassReferenceKind() const { return getTypeContextDescriptorFlags().class_getSuperclassReferenceKind(); } @@ -3399,6 +3336,10 @@ class TargetClassDescriptor final using TrailingGenericContextObjects::numTrailingObjects; + size_t numTrailingObjects(OverloadToken) const{ + return this->hasForeignMetadataInitialization() ? 1 : 0; + } + size_t numTrailingObjects(OverloadToken) const { return hasVTable() ? 1 : 0; } @@ -3411,6 +3352,11 @@ class TargetClassDescriptor final } public: + const ForeignMetadataInitialization &getForeignMetadataInitialization() const{ + assert(this->hasForeignMetadataInitialization()); + return *this->template getTrailingObjects(); + } + /// True if metadata records for this type have a field offset vector for /// its stored properties. bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; } @@ -3503,10 +3449,49 @@ class TargetClassDescriptor final using ClassDescriptor = TargetClassDescriptor; +/// The cache structure for non-trivial initialization of singleton value +/// metadata. +template +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> Metadata; + + /// The private cache data. + std::atomic> Private; +}; +using InPlaceValueMetadataCache = + TargetInPlaceValueMetadataCache; + +/// 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 +struct TargetInPlaceValueMetadataInitialization { + /// The initialization cache. Out-of-line because mutable. + TargetRelativeDirectPointer> + InitializationCache; + + /// The incomplete metadata. + TargetRelativeDirectPointer> + IncompleteMetadata; + + /// The completion function. The pattern will always be null. + TargetRelativeDirectPointer + CompletionFunction; +}; + template class TargetValueTypeDescriptor : public TargetTypeContextDescriptor { public: + using InPlaceMetadataInitialization = + TargetInPlaceValueMetadataInitialization; + + const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const; + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::Struct || cd->getKind() == ContextDescriptorKind::Enum; @@ -3518,16 +3503,39 @@ template class TargetStructDescriptor final : public TargetValueTypeDescriptor, public TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader> { + TargetTypeGenericContextDescriptorHeader, + /*additional trailing objects*/ + TargetForeignMetadataInitialization, + TargetInPlaceValueMetadataInitialization> { +public: + using ForeignMetadataInitialization = + TargetForeignMetadataInitialization; + using InPlaceMetadataInitialization = + TargetInPlaceValueMetadataInitialization; + private: using TrailingGenericContextObjects = TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader>; + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + InPlaceMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; + template + using OverloadToken = typename TrailingObjects::template OverloadToken; + using TrailingGenericContextObjects::numTrailingObjects; + + size_t numTrailingObjects(OverloadToken) const{ + return this->hasForeignMetadataInitialization() ? 1 : 0; + } + + size_t numTrailingObjects(OverloadToken) const{ + return this->hasInPlaceMetadataInitialization() ? 1 : 0; + } + public: using TrailingGenericContextObjects::getGenericContext; using TrailingGenericContextObjects::getGenericContextHeader; @@ -3546,6 +3554,16 @@ class TargetStructDescriptor final /// its stored properties. bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; } + const ForeignMetadataInitialization &getForeignMetadataInitialization() const{ + assert(this->hasForeignMetadataInitialization()); + return *this->template getTrailingObjects(); + } + + const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const{ + assert(this->hasInPlaceMetadataInitialization()); + return *this->template getTrailingObjects(); + } + static constexpr int32_t getGenericArgumentOffset() { return TargetStructMetadata::getGenericArgumentOffset(); } @@ -3561,16 +3579,39 @@ template class TargetEnumDescriptor final : public TargetValueTypeDescriptor, public TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader> { + TargetTypeGenericContextDescriptorHeader, + /*additional trailing objects*/ + TargetForeignMetadataInitialization, + TargetInPlaceValueMetadataInitialization> { +public: + using InPlaceMetadataInitialization = + TargetInPlaceValueMetadataInitialization; + using ForeignMetadataInitialization = + TargetForeignMetadataInitialization; + private: using TrailingGenericContextObjects = TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader>; + TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, + InPlaceMetadataInitialization>; using TrailingObjects = typename TrailingGenericContextObjects::TrailingObjects; friend TrailingObjects; + template + using OverloadToken = typename TrailingObjects::template OverloadToken; + using TrailingGenericContextObjects::numTrailingObjects; + + size_t numTrailingObjects(OverloadToken) const{ + return this->hasForeignMetadataInitialization() ? 1 : 0; + } + + size_t numTrailingObjects(OverloadToken) const{ + return this->hasInPlaceMetadataInitialization() ? 1 : 0; + } + public: using TrailingGenericContextObjects::getGenericContext; using TrailingGenericContextObjects::getGenericContextHeader; @@ -3607,6 +3648,16 @@ class TargetEnumDescriptor final return TargetEnumMetadata::getGenericArgumentOffset(); } + const ForeignMetadataInitialization &getForeignMetadataInitialization() const{ + assert(this->hasForeignMetadataInitialization()); + return *this->template getTrailingObjects(); + } + + const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const{ + assert(this->hasInPlaceMetadataInitialization()); + return *this->template getTrailingObjects(); + } + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::Enum; } @@ -3695,6 +3746,39 @@ TargetTypeContextDescriptor::getGenericParams() const { } } +template +const TargetForeignMetadataInitialization & +TargetTypeContextDescriptor::getForeignMetadataInitialization() const { + switch (this->getKind()) { + case ContextDescriptorKind::Class: + return llvm::cast>(this) + ->getForeignMetadataInitialization(); + case ContextDescriptorKind::Enum: + return llvm::cast>(this) + ->getForeignMetadataInitialization(); + case ContextDescriptorKind::Struct: + return llvm::cast>(this) + ->getForeignMetadataInitialization(); + default: + swift_runtime_unreachable("Not a type context descriptor."); + } +} + +template +inline const TargetInPlaceValueMetadataInitialization & +TargetValueTypeDescriptor::getInPlaceMetadataInitialization() const { + switch (this->getKind()) { + case ContextDescriptorKind::Enum: + return llvm::cast>(this) + ->getInPlaceMetadataInitialization(); + case ContextDescriptorKind::Struct: + return llvm::cast>(this) + ->getInPlaceMetadataInitialization(); + default: + swift_runtime_unreachable("Not a value type descriptor."); + } +} + } // end namespace swift #pragma clang diagnostic pop diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index e59acdae0aceb..d4a19c6260ed5 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -350,7 +350,7 @@ enum : unsigned { }; /// Kinds of type metadata/protocol conformance records. -enum class TypeMetadataRecordKind : unsigned { +enum class TypeReferenceKind : unsigned { /// The conformance is for a nominal type referenced directly; /// getNominalTypeDescriptor() points to the nominal type descriptor. DirectNominalTypeDescriptor = 0x00, @@ -359,9 +359,10 @@ enum class TypeMetadataRecordKind : unsigned { /// getNominalTypeDescriptor() points to the nominal type descriptor. IndirectNominalTypeDescriptor = 0x01, - /// Reserved for future use. - Reserved = 0x02, - + /// The conformance is for an Objective-C class that should be looked up + /// by class name. + DirectObjCClassName = 0x02, + /// The conformance is for an Objective-C class that has no nominal type /// descriptor. /// getIndirectObjCClass() points to a variable that contains the pointer to @@ -371,6 +372,8 @@ enum class TypeMetadataRecordKind : unsigned { /// unused. IndirectObjCClass = 0x03, + // We only reserve three bits for this in the various places we store it. + First_Kind = DirectNominalTypeDescriptor, Last_Kind = IndirectObjCClass, }; @@ -593,7 +596,7 @@ class ConformanceFlags { return ConformanceFlags((Value & ~ConformanceKindMask) | int_type(kind)); } - ConformanceFlags withTypeReferenceKind(TypeMetadataRecordKind kind) const { + ConformanceFlags withTypeReferenceKind(TypeReferenceKind kind) const { return ConformanceFlags((Value & ~TypeMetadataKindMask) | (int_type(kind) << TypeMetadataKindShift)); } @@ -621,8 +624,8 @@ class ConformanceFlags { } /// Retrieve the type reference kind kind. - TypeMetadataRecordKind getTypeReferenceKind() const { - return TypeMetadataRecordKind( + TypeReferenceKind getTypeReferenceKind() const { + return TypeReferenceKind( (Value & TypeMetadataKindMask) >> TypeMetadataKindShift); } @@ -1174,27 +1177,51 @@ class TypeContextDescriptorFlags : public FlagSet { // Generic flags build upwards from 0. // Type-specific flags build downwards from 15. - /// Set if the type represents an imported C tag type. + /// Set if the type supports reflection. C and Objective-C enums + /// currently don't. /// /// Meaningful for all type-descriptor kinds. - IsCTag = 0, + IsReflectable = 0, - /// Set if the type represents an imported C typedef type. + /// Whether there's something unusual about how the metadata is + /// initialized. /// /// Meaningful for all type-descriptor kinds. - IsCTypedef = 1, - - /// Set if the type supports reflection. C and Objective-C enums - /// currently don't. + MetadataInitialization = 1, + MetadataInitialization_width = 2, + + /// The namespace of the imported declaration that gave rise to this type. + /// Some languages (most importantly, C/C++/Objective-C) have different + /// symbol namespaces in which types can be declared; for example, + /// `struct A` and `typedef ... A` can be declared in the same scope and + /// resolve to unrelated types. When these declarations are imported, + /// there are several possible ways to distinguish them in Swift, e.g. + /// by implicitly renaming them; however, the external name used for + /// mangling and metadata must be stable and so is based on the original + /// declared name. Therefore, in these languages, we privilege one + /// symbol namespace as the default (although which may depend on the + /// type kind), and declarations from the other(s) must be marked in + /// order to differentiate them. /// /// Meaningful for all type-descriptor kinds. - IsReflectable = 2, + ImportNamespace = 3, + ImportNamespace_width = 3, - /// Set if the type is a Clang-importer-synthesized related entity. After - /// the null terminator for the type name is another null-terminated string - /// containing the tag that discriminates the entity from other synthesized - /// declarations associated with the same declaration. - IsSynthesizedRelatedEntity = 3, + /// Set if the type is an importer-synthesized related entity. + /// A related entity is an entity synthesized in response to an imported + /// type which is not the type itself; for example, when the importer + /// sees an ObjC error domain, it creates an error-wrapper type (a + /// related entity) and a Code enum (not a related entity because it's + /// exactly the original type). + /// + /// The name and import namespace (together with the parent context) + /// identify the original declaration. + /// + /// If this flag is set, then after the null terminator for the type name + /// is another null-terminated string containing the tag that discriminates + /// the entity from other synthesized declarations associated with the + /// same declaration. + IsSynthesizedRelatedEntity = 6, /// Set if the context descriptor is includes metadata for dynamically /// constructing a class's vtables at metadata instantiation time. @@ -1207,26 +1234,94 @@ class TypeContextDescriptorFlags : public FlagSet { /// Only meaningful for class descriptors. Class_HasResilientSuperclass = 14, + /// Whether the immediate class members in this metadata are allocated + /// at negative offsets. For now, we don't use this. + Class_AreImmediateMembersNegative = 13, + /// The kind of reference that this class makes to its superclass - /// descriptor. A TypeMetadataRecordKind. + /// descriptor. A TypeReferenceKind. /// /// Only meaningful for class descriptors. - Class_SuperclassReferenceKind = 12, - Class_SuperclassReferenceKind_width = 2, - - /// Whether the immediate class members in this metadata are allocated - /// at negative offsets. For now, we don't use this. - Class_AreImmediateMembersNegative = 11, + Class_SuperclassReferenceKind = 10, + Class_SuperclassReferenceKind_width = 3, }; public: explicit TypeContextDescriptorFlags(uint16_t bits) : FlagSet(bits) {} constexpr TypeContextDescriptorFlags() {} - FLAGSET_DEFINE_FLAG_ACCESSORS(IsCTag, isCTag, setIsCTag) - FLAGSET_DEFINE_FLAG_ACCESSORS(IsCTypedef, isCTypedef, setIsCTypedef) FLAGSET_DEFINE_FLAG_ACCESSORS(IsReflectable, isReflectable, setIsReflectable) + enum MetadataInitializationKind { + /// There are either no special rules for initializing the metadata + /// or the metadata is generic. (Genericity is set in the + /// non-kind-specific descriptor flags.) + NoMetadataInitialization = 0, + + /// The type requires non-trivial singleton initialization using the + /// "in-place" code pattern. + InPlaceMetadataInitialization = 1, + + /// The type requires non-trivial singleton initialization using the + /// "foreign" code pattern. + ForeignMetadataInitialization = 2, + + // We only have two bits here, so if you add a third special kind, + // include more flag bits in its out-of-line storage. + }; + + FLAGSET_DEFINE_FIELD_ACCESSORS(MetadataInitialization, + MetadataInitialization_width, + MetadataInitializationKind, + getMetadataInitialization, + setMetadataInitialization) + + bool hasInPlaceMetadataInitialization() const { + return getMetadataInitialization() == InPlaceMetadataInitialization; + } + + bool hasForeignMetadataInitialization() const { + return getMetadataInitialization() == ForeignMetadataInitialization; + } + + enum ImportNamespaceKind { + /// The type comes the default namespace for its language. + DefaultNamespace = 0, + + // The behavior for C imported types is complicated in ways that don't + // entirely make sense according to the design laid out in the comment + // on the ImportNamespace field. The rules are basically: + // - Classes are assumed to come from Objective-C by default. + // ObjC classes are in the ordinary namespace in C. + // - Protocols are assumed to come from Objective-C by default. + // ObjC protocols are in their own namespace in C. + // - Structs and enums seem to always get either CTag or CTypedef. + // It would probably make more sense to assume they come from the + // tag namespace in C and then just use CTypedef as an override. + + /// The type comes from an imported C tag type. + CTag = 1, + + /// The type comes from an imported C typedef type. + CTypedef = 2, + + // We only have three bits here, so be judicious about adding new + // namespaces. + }; + + FLAGSET_DEFINE_FIELD_ACCESSORS(ImportNamespace, + ImportNamespace_width, + ImportNamespaceKind, + getImportNamespace, + setImportNamespace) + + bool isCTag() const { + return getImportNamespace() == CTag; + } + bool isCTypedef() const { + return getImportNamespace() == CTypedef; + } + FLAGSET_DEFINE_FLAG_ACCESSORS(IsSynthesizedRelatedEntity, isSynthesizedRelatedEntity, setIsSynthesizedRelatedEntity) @@ -1243,7 +1338,7 @@ class TypeContextDescriptorFlags : public FlagSet { FLAGSET_DEFINE_FIELD_ACCESSORS(Class_SuperclassReferenceKind, Class_SuperclassReferenceKind_width, - TypeMetadataRecordKind, + TypeReferenceKind, class_getSuperclassReferenceKind, class_setSuperclassReferenceKind) }; diff --git a/include/swift/Demangling/DemangleNodes.def b/include/swift/Demangling/DemangleNodes.def index ff556c94bf3f7..8f0fe0020baaf 100644 --- a/include/swift/Demangling/DemangleNodes.def +++ b/include/swift/Demangling/DemangleNodes.def @@ -182,6 +182,7 @@ NODE(TypeMetadataAccessFunction) NODE(TypeMetadataCompletionFunction) NODE(TypeMetadataInstantiationCache) NODE(TypeMetadataInstantiationFunction) +NODE(TypeMetadataInPlaceInitializationCache) NODE(TypeMetadataLazyCache) NODE(UncurriedFunctionType) #define REF_STORAGE(Name, ...) NODE(Name) diff --git a/include/swift/IRGen/Linking.h b/include/swift/IRGen/Linking.h index 3fb1be9551bec..cd993184c1905 100644 --- a/include/swift/IRGen/Linking.h +++ b/include/swift/IRGen/Linking.h @@ -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, @@ -507,12 +511,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); diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index a87f538eeab01..360af5c1a0045 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -603,16 +603,23 @@ class MetadataReader { return llvm::None; return cls->getClassBoundsAsSwiftSuperclass(); + }, + [](StoredPointer objcClassName) -> llvm::Optional { + // We have no ability to look up an ObjC class by name. + // FIXME: add a query for this; clients may have a way to do it. + return llvm::None; }); } - template + template llvm::Optional - forTypeReference(TypeMetadataRecordKind refKind, StoredPointer ref, + forTypeReference(TypeReferenceKind refKind, StoredPointer ref, const DescriptorFn &descriptorFn, - const MetadataFn &metadataFn) { + const MetadataFn &metadataFn, + const ClassNameFn &classNameFn) { switch (refKind) { - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: { + case TypeReferenceKind::IndirectNominalTypeDescriptor: { StoredPointer descriptorAddress = 0; if (!Reader->readInteger(RemoteAddress(ref), &descriptorAddress)) return llvm::None; @@ -621,7 +628,7 @@ class MetadataReader { LLVM_FALLTHROUGH; } - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: { + case TypeReferenceKind::DirectNominalTypeDescriptor: { auto descriptor = readContextDescriptor(ref); if (!descriptor) return llvm::None; @@ -629,7 +636,10 @@ class MetadataReader { return descriptorFn(descriptor); } - case TypeMetadataRecordKind::IndirectObjCClass: { + case TypeReferenceKind::DirectObjCClassName: + return classNameFn(ref); + + case TypeReferenceKind::IndirectObjCClass: { StoredPointer classRef = 0; if (!Reader->readInteger(RemoteAddress(ref), &classRef)) return llvm::None; @@ -640,10 +650,9 @@ class MetadataReader { return metadataFn(metadata); } - - default: - return llvm::None; } + + return llvm::None; } /// Read a single generic type argument from a bound generic type @@ -1038,9 +1047,25 @@ class MetadataReader { sizeof(flags))) return nullptr; + TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags()); unsigned baseSize = 0; unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader); + unsigned metadataInitSize = 0; bool hasVTable = false; + + auto readMetadataInitSize = [&]() -> unsigned { + switch (typeFlags.getMetadataInitialization()) { + case TypeContextDescriptorFlags::NoMetadataInitialization: + return 0; + case TypeContextDescriptorFlags::InPlaceMetadataInitialization: + // FIXME: classes + return sizeof(TargetInPlaceValueMetadataInitialization); + case TypeContextDescriptorFlags::ForeignMetadataInitialization: + return sizeof(TargetForeignMetadataInitialization); + } + return 0; + }; + switch (auto kind = flags.getKind()) { case ContextDescriptorKind::Module: baseSize = sizeof(TargetModuleContextDescriptor); @@ -1055,22 +1080,24 @@ class MetadataReader { case ContextDescriptorKind::Class: baseSize = sizeof(TargetClassDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); - hasVTable = TypeContextDescriptorFlags(flags.getKindSpecificFlags()) - .class_hasVTable(); + hasVTable = typeFlags.class_hasVTable(); + metadataInitSize = readMetadataInitSize(); break; case ContextDescriptorKind::Enum: baseSize = sizeof(TargetEnumDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); + metadataInitSize = readMetadataInitSize(); break; case ContextDescriptorKind::Struct: baseSize = sizeof(TargetStructDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); + metadataInitSize = readMetadataInitSize(); break; default: // We don't know about this kind of context. return nullptr; } - + // Determine the full size of the descriptor. This is reimplementing a fair // bit of TrailingObjects but for out-of-process; maybe there's a way to // factor the layout stuff out... @@ -1097,7 +1124,8 @@ class MetadataReader { TargetVTableDescriptorHeader header; auto headerAddr = address + baseSize - + genericsSize; + + genericsSize + + metadataInitSize; if (!Reader->readBytes(RemoteAddress(headerAddr), (uint8_t*)&header, sizeof(header))) @@ -1107,7 +1135,7 @@ class MetadataReader { + header.VTableSize * sizeof(TargetMethodDescriptor); } - unsigned size = baseSize + genericsSize + vtableSize; + unsigned size = baseSize + genericsSize + metadataInitSize + vtableSize; auto buffer = (uint8_t *)malloc(size); if (!Reader->readBytes(RemoteAddress(address), buffer, size)) { free(buffer); diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index ccc1283211b46..59ff64a9f96f0 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -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 @@ -468,9 +475,10 @@ swift_getObjCClassFromObject(HeapObject *object); #endif /// \brief Fetch a unique type metadata object for a foreign type. -SWIFT_RUNTIME_EXPORT -const ForeignTypeMetadata * -swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique); +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +MetadataResponse +swift_getForeignTypeMetadata(MetadataRequest request, + ForeignTypeMetadata *nonUnique); /// \brief Fetch a unique witness table for a foreign witness table. SWIFT_RUNTIME_EXPORT @@ -523,6 +531,46 @@ swift_getTupleTypeMetadata3(MetadataRequest request, const Metadata *elt2, const char *labels, const ValueWitnessTable *proposedWitnesses); +/// Perform layout as if for a tuple whose elements have the given layouts. +/// +/// \param tupleLayout - A structure into which to write the tuple layout. +/// Must be non-null. +/// \param elementOffsets - An array into which to write the offsets of +/// the elements. May be null. Must have space for all elements, +/// including element 0 (which will always have offset 0). +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +void swift_getTupleTypeLayout(TypeLayout *tupleLayout, + uint32_t *elementOffsets, + TupleTypeFlags flags, + const TypeLayout * const *elements); + +/// Perform layout as if for a two-element tuple whose elements have +/// the given layouts. +/// +/// \param tupleLayout - A structure into which to write the tuple layout. +/// Must be non-null. +/// \returns The offset of the second element. +/// The first element always has offset 0. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +size_t swift_getTupleTypeLayout2(TypeLayout *tupleLayout, + const TypeLayout *elt0, + const TypeLayout *elt1); + +struct OffsetPair { size_t First; size_t Second; }; + +/// Perform layout as if for a three-element tuple whose elements have +/// the given layouts. +/// +/// \param tupleLayout - A structure into which to write the tuple layout. +/// Must be non-null. +/// \returns The offsets of the second and third elements. +/// The first element always has offset 0. +SWIFT_RUNTIME_EXPORT SWIFT_CC(swift) +OffsetPair swift_getTupleTypeLayout3(TypeLayout *tupleLayout, + const TypeLayout *elt0Layout, + const TypeLayout *elt1Layout, + const TypeLayout *elt2Layout); + /// Initialize the value witness table and struct field offset vector for a /// struct, using the "Universal" layout strategy. SWIFT_RUNTIME_EXPORT diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 24fa49f81b7da..9bec39c2662f8 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -638,9 +638,9 @@ FUNCTION(GetFunctionMetadata3, swift_getFunctionTypeMetadata3, ATTRS(NoUnwind, ReadNone)) // Metadata *swift_getForeignTypeMetadata(Metadata *nonUnique); -FUNCTION(GetForeignTypeMetadata, swift_getForeignTypeMetadata, C_CC, - RETURNS(TypeMetadataPtrTy), - ARGS(TypeMetadataPtrTy), +FUNCTION(GetForeignTypeMetadata, swift_getForeignTypeMetadata, SwiftCC, + RETURNS(TypeMetadataResponseTy), + ARGS(SizeTy, TypeMetadataPtrTy), ATTRS(NoUnwind, ReadNone)) // only writes to runtime-private fields // WitnessTable *swift_getForeignWitnessTable(const WitnessTable *candidate, @@ -652,6 +652,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); @@ -758,6 +765,34 @@ FUNCTION(GetTupleMetadata3, swift_getTupleTypeMetadata3, SwiftCC, Int8PtrTy, WitnessTablePtrTy), ATTRS(NoUnwind, ReadOnly)) +// void swift_getTupleTypeLayout(TypeLayout *result, +// uint32_t offsets, +// TupleTypeFlags flags, +// const TypeLayout * const *elts); +FUNCTION(GetTupleLayout, swift_getTupleTypeLayout, SwiftCC, + RETURNS(VoidTy), + ARGS(FullTypeLayoutTy->getPointerTo(0), Int32Ty->getPointerTo(0), + SizeTy, Int8PtrPtrTy->getPointerTo(0)), + ATTRS(NoUnwind)) + +// size_t swift_getTupleTypeLayout2(TypeLayout *layout, +// const TypeLayout *elt0, +// const TypeLayout *elt1); +FUNCTION(GetTupleLayout2, swift_getTupleTypeLayout2, SwiftCC, + RETURNS(SizeTy), + ARGS(FullTypeLayoutTy->getPointerTo(0), Int8PtrPtrTy, Int8PtrPtrTy), + ATTRS(NoUnwind)) + +// OffsetPair swift_getTupleTypeLayout3(TypeLayout *layout, +// const TypeLayout *elt0, +// const TypeLayout *elt1, +// const TypeLayout *elt2); +FUNCTION(GetTupleLayout3, swift_getTupleTypeLayout3, SwiftCC, + RETURNS(OffsetPairTy), + ARGS(FullTypeLayoutTy->getPointerTo(0), + Int8PtrPtrTy, Int8PtrPtrTy, Int8PtrPtrTy), + ATTRS(NoUnwind)) + // Metadata *swift_getExistentialTypeMetadata( // ProtocolClassConstraint classConstraint, // const Metadata *superclassConstraint, diff --git a/lib/Demangling/Demangler.cpp b/lib/Demangling/Demangler.cpp index 253ba5253344a..1731da3672e58 100644 --- a/lib/Demangling/Demangler.cpp +++ b/lib/Demangling/Demangler.cpp @@ -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': diff --git a/lib/Demangling/NodePrinter.cpp b/lib/Demangling/NodePrinter.cpp index 4777a3f470619..6731e3802e8b5 100644 --- a/lib/Demangling/NodePrinter.cpp +++ b/lib/Demangling/NodePrinter.cpp @@ -428,6 +428,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, ...) \ @@ -1512,6 +1513,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)); diff --git a/lib/Demangling/OldRemangler.cpp b/lib/Demangling/OldRemangler.cpp index c17c77885e485..82fb9ecea770b 100644 --- a/lib/Demangling/OldRemangler.cpp +++ b/lib/Demangling/OldRemangler.cpp @@ -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 diff --git a/lib/Demangling/Remangler.cpp b/lib/Demangling/Remangler.cpp index 40db2eda4746a..9aae9ae2c9b78 100644 --- a/lib/Demangling/Remangler.cpp +++ b/lib/Demangling/Remangler.cpp @@ -1763,6 +1763,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"; diff --git a/lib/IRGen/ForeignClassMetadataVisitor.h b/lib/IRGen/ForeignClassMetadataVisitor.h index cce48b0d2222c..6452fc004c078 100644 --- a/lib/IRGen/ForeignClassMetadataVisitor.h +++ b/lib/IRGen/ForeignClassMetadataVisitor.h @@ -37,16 +37,8 @@ class ForeignClassMetadataVisitor void layout() { super::layout(); asImpl().addNominalTypeDescriptor(); - asImpl().noteStartOfSuperClass(); asImpl().addSuperclass(); asImpl().addReservedWord(); - asImpl().addReservedWord(); - asImpl().addReservedWord(); - } - - bool requiresInitializationFunction() { - return Target->getSuperclassDecl() && - Target->getSuperclassDecl()->isForeign(); } CanType getTargetType() const { diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 0fce67f652f94..c4ccc619623df 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1488,8 +1488,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { return getSILLinkage(FormalLinkage::PublicUnique, forDefinition); // Imported types. - if (getTypeMetadataAccessStrategy(type) == - MetadataAccessStrategy::NonUniqueAccessor) + if (isAccessorLazilyGenerated(getTypeMetadataAccessStrategy(type))) return SILLinkage::Shared; // Everything else is only referenced inside its module. @@ -1498,6 +1497,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { case Kind::TypeMetadataInstantiationCache: case Kind::TypeMetadataInstantiationFunction: + case Kind::TypeMetadataInPlaceInitializationCache: case Kind::TypeMetadataCompletionFunction: case Kind::TypeMetadataPattern: return SILLinkage::Private; @@ -1506,8 +1506,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { auto type = getType(); // Imported types, non-primitive structural types. - if (getTypeMetadataAccessStrategy(type) == - MetadataAccessStrategy::NonUniqueAccessor) + if (isAccessorLazilyGenerated(getTypeMetadataAccessStrategy(type))) return SILLinkage::Shared; // Everything else is only referenced inside its module. @@ -1547,6 +1546,7 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const { return getSILLinkage(FormalLinkage::HiddenUnique, forDefinition); case MetadataAccessStrategy::PrivateAccessor: return getSILLinkage(FormalLinkage::Private, forDefinition); + case MetadataAccessStrategy::ForeignAccessor: case MetadataAccessStrategy::NonUniqueAccessor: return SILLinkage::Shared; } @@ -1719,6 +1719,7 @@ bool LinkEntity::isAvailableExternally(IRGenModule &IGM) const { case Kind::AnonymousDescriptor: case Kind::TypeMetadataInstantiationCache: case Kind::TypeMetadataInstantiationFunction: + case Kind::TypeMetadataInPlaceInitializationCache: case Kind::TypeMetadataCompletionFunction: case Kind::TypeMetadataPattern: return false; @@ -2504,8 +2505,6 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity, Alignment alignment, // forward declaration. if (definitionType) { assert(existing->isDeclaration() && "already defined"); - assert(entry->getType()->getPointerElementType() == defaultType - || entry->getType()->getPointerElementType() == definition.getType()); updateLinkageForDefinition(*this, existing, entity); // If the existing entry is a variable of the right type, @@ -2552,7 +2551,7 @@ IRGenModule::getAddrOfLLVMVariable(LinkEntity entity, Alignment alignment, // new variable. if (entry) { auto existing = cast(entry); - auto castVar = getElementBitCast(var, defaultType); + auto castVar = llvm::ConstantExpr::getBitCast(var, entry->getType()); existing->replaceAllUsesWith(castVar); existing->eraseFromParent(); } @@ -2684,42 +2683,79 @@ IRGenModule::getFunctionGOTEquivalent(LinkEntity entity, return {gotEquivalent, ConstantReference::Indirect}; } -TypeEntityReference -IRGenModule::getTypeEntityReference(NominalTypeDecl *decl) { - TypeMetadataRecordKind kind; - Optional entity; - llvm::Type *defaultTy; +static TypeEntityReference +getTypeContextDescriptorEntityReference(IRGenModule &IGM, + NominalTypeDecl *decl) { + // A reference to a concrete type. + // TODO: consider using a symbolic reference (i.e. a symbol string + // to be looked up dynamically) for types defined outside the module. + auto kind = TypeReferenceKind::DirectNominalTypeDescriptor; + auto entity = LinkEntity::forNominalTypeDescriptor(decl); + auto defaultTy = IGM.TypeContextDescriptorTy; - auto clas = dyn_cast(decl); - if (clas && !clas->isForeign() && !hasKnownSwiftMetadata(*this, clas)) { - // A reference to an Objective-C class object. - assert(clas->isObjC() && "Must have an Objective-C class here"); + auto ref = IGM.getAddrOfLLVMVariableOrGOTEquivalent( + entity, IGM.getPointerAlignment(), defaultTy); - kind = TypeMetadataRecordKind::IndirectObjCClass; - defaultTy = TypeMetadataPtrTy; - entity = LinkEntity::forObjCClassRef(clas); - } else { - // A reference to a concrete type. - // TODO: consider using a symbolic reference (i.e. a symbol string - // to be looked up dynamically) for types defined outside the module. - kind = TypeMetadataRecordKind::DirectNominalTypeDescriptor; - entity = LinkEntity::forNominalTypeDescriptor(decl); - defaultTy = TypeContextDescriptorTy; + if (ref.isIndirect()) { + kind = TypeReferenceKind::IndirectNominalTypeDescriptor; } - auto ref = getAddrOfLLVMVariableOrGOTEquivalent( - *entity, getPointerAlignment(), defaultTy); + return TypeEntityReference(kind, ref.getValue()); +} + +static TypeEntityReference +getObjCClassRefEntityReference(IRGenModule &IGM, ClassDecl *cls) { + // A reference to an Objective-C class object. + assert(cls->isObjC() && "Must have an Objective-C class here"); - // Adjust the flags now that we know whether the reference to this - // entity will be indirect. - if (ref.isIndirect()) { - assert(kind == TypeMetadataRecordKind::DirectNominalTypeDescriptor); - kind = TypeMetadataRecordKind::IndirectNominalTypeDescriptor; - } + auto kind = TypeReferenceKind::IndirectObjCClass; + auto entity = LinkEntity::forObjCClassRef(cls); + auto defaultTy = IGM.TypeMetadataPtrTy; + + auto ref = IGM.getAddrOfLLVMVariableOrGOTEquivalent( + entity, IGM.getPointerAlignment(), defaultTy); + assert(!ref.isIndirect()); return TypeEntityReference(kind, ref.getValue()); } +static TypeEntityReference +getRuntimeOnlyClassEntityReference(IRGenModule &IGM, ClassDecl *cls) { + assert(cls->getForeignClassKind() == ClassDecl::ForeignKind::RuntimeOnly); + + auto namedClangDecl = Mangle::ASTMangler::getClangDeclForMangling(cls); + assert(namedClangDecl); + + auto kind = TypeReferenceKind::DirectObjCClassName; + auto ref = IGM.getAddrOfGlobalString(namedClangDecl->getName()); + + return TypeEntityReference(kind, ref); +} + +TypeEntityReference +IRGenModule::getTypeEntityReference(NominalTypeDecl *decl) { + auto clas = dyn_cast(decl); + if (!clas) { + return getTypeContextDescriptorEntityReference(*this, decl); + } + + switch (clas->getForeignClassKind()) { + case ClassDecl::ForeignKind::RuntimeOnly: + return getRuntimeOnlyClassEntityReference(*this, clas); + + case ClassDecl::ForeignKind::CFType: + return getTypeContextDescriptorEntityReference(*this, clas); + + case ClassDecl::ForeignKind::Normal: + if (hasKnownSwiftMetadata(*this, clas)) { + return getTypeContextDescriptorEntityReference(*this, clas); + } else { + return getObjCClassRefEntityReference(*this, clas); + } + } + llvm_unreachable("bad foreign type kind"); +} + /// Form an LLVM constant for the relative distance between a reference /// (appearing at gep (0, indices) of `base`) and `target`. llvm::Constant * @@ -3275,8 +3311,45 @@ IRGenModule::getAddrOfTypeMetadataLazyCacheVariable(CanType type, ForDefinition_t forDefinition) { assert(!type->hasArchetype() && !type->hasTypeParameter()); LinkEntity entity = LinkEntity::forTypeMetadataLazyCacheVariable(type); - return getAddrOfLLVMVariable(entity, getPointerAlignment(), forDefinition, - TypeMetadataPtrTy, DebugTypeInfo()); + auto variable = + getAddrOfLLVMVariable(entity, getPointerAlignment(), forDefinition, + TypeMetadataPtrTy, DebugTypeInfo()); + + // Zero-initialize if we're asking for a definition. + if (forDefinition) { + cast(variable)->setInitializer( + llvm::ConstantPointerNull::get(TypeMetadataPtrTy)); + } + + return variable; +} + +llvm::Constant * +IRGenModule::getAddrOfTypeMetadataInPlaceInitializationCache( + NominalTypeDecl *D, + ForDefinition_t forDefinition) { + // Build the cache type. + llvm::Type *cacheTy; + if (isa(D) || isa(D)) { + // This is struct InPlaceValueMetadataCache. + cacheTy = llvm::StructType::get(getLLVMContext(), + {TypeMetadataPtrTy, Int8PtrTy}); + } else { + llvm_unreachable("in-place initialization for classes not yet supported"); + } + + LinkEntity entity = LinkEntity::forTypeMetadataInPlaceInitializationCache(D); + auto variable = + getAddrOfLLVMVariable(entity, getPointerAlignment(), forDefinition, + cacheTy, DebugTypeInfo()); + + // Zero-initialize if we're asking for a definition. + if (forDefinition) { + cast(variable)->setInitializer( + llvm::Constant::getNullValue(cacheTy)); + } + + return variable; } /// Define the metadata for a type. @@ -4286,9 +4359,17 @@ IRGenModule::getAddrOfWitnessTableLazyCacheVariable( assert(!conformingType->hasArchetype()); LinkEntity entity = LinkEntity::forProtocolWitnessTableLazyCacheVariable(conf, conformingType); - return getAddrOfLLVMVariable(entity, getPointerAlignment(), - forDefinition, WitnessTablePtrTy, - DebugTypeInfo()); + auto variable = getAddrOfLLVMVariable(entity, getPointerAlignment(), + forDefinition, WitnessTablePtrTy, + DebugTypeInfo()); + + // Zero-initialize if we're asking for a definition. + if (forDefinition) { + cast(variable)->setInitializer( + llvm::ConstantPointerNull::get(WitnessTablePtrTy)); + } + + return variable; } /// Look up the address of a witness table. diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 6022ec5d81bdf..1f5fce6acca62 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -121,6 +121,87 @@ void IRGenModule::setTrueConstGlobal(llvm::GlobalVariable *var) { } } +/*****************************************************************************/ +/** Metadata completion ******************************************************/ +/*****************************************************************************/ + +/// Does the metadata for the given type, which we are currently emitting, +/// require in-place metadata initialiation structures and functions? +static bool needsInPlaceMetadataInitialization(IRGenModule &IGM, + NominalTypeDecl *typeDecl) { + // Generic types never have in-place metadata initialization. + if (typeDecl->isGenericContext()) + return false; + + assert(isa(typeDecl) || isa(typeDecl)); + + // If the type is known to be fixed-layout, we can emit its metadata such + // that it doesn't need dynamic initialization. + auto &ti = IGM.getTypeInfoForUnlowered(typeDecl->getDeclaredTypeInContext()); + if (ti.isFixedSize(ResilienceExpansion::Maximal)) + return false; + + return true; +} + +using MetadataCompletionBodyEmitter = + void (IRGenFunction &IGF, + llvm::Value *metadata, + MetadataDependencyCollector *collector); + +static void emitMetadataCompletionFunction(IRGenModule &IGM, + NominalTypeDecl *typeDecl, + llvm::function_ref body) { + llvm::Function *f = + IGM.getAddrOfTypeMetadataCompletionFunction(typeDecl, ForDefinition); + f->setAttributes(IGM.constructInitialAttributes()); + + IRGenFunction IGF(IGM, f); + + // Skip instrumentation when building for TSan to avoid false positives. + // The synchronization for this happens in the Runtime and we do not see it. + if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) + f->removeFnAttr(llvm::Attribute::SanitizeThread); + + if (IGM.DebugInfo) + IGM.DebugInfo->emitArtificialFunction(IGF, f); + + Explosion params = IGF.collectParameters(); + llvm::Value *metadata = params.claimNext(); + llvm::Value *context = params.claimNext(); + llvm::Value *templatePointer = params.claimNext(); + + // TODO: use these? + (void) context; + (void) templatePointer; + + MetadataDependencyCollector collector; + + body(IGF, metadata, &collector); + + // At the current insertion point, the metadata is now complete. + + // Merge with any metadata dependencies we may have collected. + auto dependency = collector.finish(IGF); + auto returnValue = dependency.combine(IGF); + + IGF.Builder.CreateRet(returnValue); +} + +static bool needsForeignMetadataCompletionFunction(StructDecl *decl) { + // Currently, foreign structs never need a completion function. + return false; +} + +static bool needsForeignMetadataCompletionFunction(EnumDecl *decl) { + // Currently, foreign enums never need a completion function. + return false; +} + +static bool needsForeignMetadataCompletionFunction(ClassDecl *decl) { + return decl->hasSuperclass(); +} + /*****************************************************************************/ /** Nominal Type Descriptor Emission *****************************************/ /*****************************************************************************/ @@ -447,16 +528,18 @@ namespace { IGM.setTrueConstGlobal(var); } }; - - template + + template class TypeContextDescriptorBuilderBase : public ContextDescriptorBuilderBase { using super = ContextDescriptorBuilderBase; protected: - NominalTypeDecl *Type; + DeclType *Type; RequireMetadata_t HasMetadata; + TypeContextDescriptorFlags::MetadataInitializationKind + MetadataInitialization; using super::IGM; using super::B; @@ -465,11 +548,12 @@ namespace { public: using super::addGenericSignature; - TypeContextDescriptorBuilderBase(IRGenModule &IGM, NominalTypeDecl *Type, + TypeContextDescriptorBuilderBase(IRGenModule &IGM, DeclType *Type, RequireMetadata_t requireMetadata) : super(IGM), Type(Type), - HasMetadata(requireMetadata) - {} + HasMetadata(requireMetadata), + MetadataInitialization(computeMetadataInitialization()) { + } void layout() { super::layout(); @@ -478,6 +562,7 @@ namespace { // ABI TODO: layout info should be superseded by remote mirror metadata asImpl().addLayoutInfo(); asImpl().addGenericSignature(); + asImpl().maybeAddMetadataInitialization(); } void addName() { @@ -487,7 +572,8 @@ namespace { // Use the original name with tag for synthesized decls. The tag comes // after the null terminator for the name. if (auto *synthesizedTypeAttr = - Type->getAttrs().getAttribute()) { + Type->getAttrs() + .template getAttribute()) { nameBuf.append(synthesizedTypeAttr->originalTypeName); nameBuf.push_back('\0'); nameBuf.append(synthesizedTypeAttr->getManglingName()); @@ -507,16 +593,28 @@ namespace { } void addAccessFunction() { - // Don't emit the access function if we're only lazily emitting the - // context descriptor. + llvm::Constant *accessor; + + // Don't include an access function if we're emitting the context + // descriptor without metadata. if (!HasMetadata) { - B.addInt32(0); - return; + accessor = nullptr; + + // If it's a generic type, use the generic access function. + // This has a different prototype from an ordinary function, but + // the runtime knows to check for that. + } else if (Type->isGenericContext()) { + accessor = getGenericTypeMetadataAccessFunction(IGM, Type, + NotForDefinition); + + // Otherwise, use the ordinary access function, which we'll define + // when we emit the metadata. + } else { + CanType type = Type->getDeclaredType()->getCanonicalType(); + accessor = getOtherwiseDefinedTypeMetadataAccessFunction(IGM, type); } - llvm::Constant *accessFn = - getRequiredTypeMetadataAccessFunction(IGM, Type, NotForDefinition); - B.addRelativeAddressOrNull(accessFn); + B.addRelativeAddressOrNull(accessor); } ConstantReference getParent() { @@ -571,24 +669,121 @@ namespace { IGM.setTrueConstGlobal(var); return var; } + + void setCommonFlags(TypeContextDescriptorFlags &flags) { + setClangImportedFlags(flags); + setMetadataInitializationKind(flags); + } /// Flags to indicate Clang-imported declarations so we mangle them /// consistently at runtime. - void getClangImportedFlags(TypeContextDescriptorFlags &flags) const { - if (Type->getAttrs().getAttribute()) { + void setClangImportedFlags(TypeContextDescriptorFlags &flags) { + if (Type->getAttrs() + .template getAttribute()) { flags.setIsSynthesizedRelatedEntity(true); } if (auto clangDecl = Mangle::ASTMangler::getClangDeclForMangling(Type)) { if (isa(clangDecl)) { - flags.setIsCTag(true); + flags.setImportNamespace(TypeContextDescriptorFlags::CTag); } else if (isa(clangDecl) || isa(clangDecl)) { - flags.setIsCTypedef(true); + flags.setImportNamespace(TypeContextDescriptorFlags::CTypedef); } } } + TypeContextDescriptorFlags::MetadataInitializationKind + computeMetadataInitialization() { + // Not if we don't have metadata. + if (!HasMetadata) + return TypeContextDescriptorFlags::NoMetadataInitialization; + + // Generic types use their own system. + if (Type->isGenericContext()) + return TypeContextDescriptorFlags::NoMetadataInitialization; + + // Check for foreign metadata. + if (requiresForeignTypeMetadata(Type)) + return TypeContextDescriptorFlags::ForeignMetadataInitialization; + + // The only other option is in-place initialization. + + // Only struct and enums for now. Classes currently use an eager + // mechanism that doesn't properly support recursive dependencies, so + // their equivalent of in-place initialization does not yet use this + // infrastructure. + if (!isa(Type) && !isa(Type)) + return TypeContextDescriptorFlags::NoMetadataInitialization; + + if (needsInPlaceMetadataInitialization(IGM, Type)) + return TypeContextDescriptorFlags::InPlaceMetadataInitialization; + + return TypeContextDescriptorFlags::NoMetadataInitialization; + } + + void setMetadataInitializationKind(TypeContextDescriptorFlags &flags) { + flags.setMetadataInitialization(MetadataInitialization); + } + + void maybeAddMetadataInitialization() { + switch (MetadataInitialization) { + case TypeContextDescriptorFlags::NoMetadataInitialization: + return; + + case TypeContextDescriptorFlags::ForeignMetadataInitialization: + asImpl().addForeignMetadataInitialization(); + return; + + case TypeContextDescriptorFlags::InPlaceMetadataInitialization: + asImpl().addInPlaceMetadataInitialization(); + return; + } + llvm_unreachable("bad kind"); + } + + void addInPlaceMetadataInitialization() { + if (isa(Type) || isa(Type)) { + asImpl().addInPlaceValueMetadataInitialization(); + } else { + llvm_unreachable("unexpected type allowing in-place initialization"); + } + } + + /// Add a ForeignMetadataInitialization structure to the descriptor. + void addForeignMetadataInitialization() { + llvm::Constant *completionFunction = nullptr; + if (asImpl().needsForeignMetadataCompletionFunction()) { + completionFunction = + IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition); + } + B.addRelativeAddressOrNull(completionFunction); + } + + bool needsForeignMetadataCompletionFunction() { + return ::needsForeignMetadataCompletionFunction(Type); + } + + /// Add an InPlaceValueMetadataInitialization structure to the descriptor. + void addInPlaceValueMetadataInitialization() { + // Relative pointer to the initialization cache. + // Note that we trigger the definition of it when emitting the + // completion function. + auto cache = IGM.getAddrOfTypeMetadataInPlaceInitializationCache(Type, + NotForDefinition); + B.addRelativeAddress(cache); + + // Relative pointer to the metadata. + auto type = Type->getDeclaredTypeInContext()->getCanonicalType(); + auto metadata = IGM.getAddrOfTypeMetadata(type); + B.addRelativeAddress(metadata); + + // Completion function. + auto completionFunction = + IGM.getAddrOfTypeMetadataCompletionFunction(Type, NotForDefinition); + B.addRelativeAddress(completionFunction); + } + // Subclasses should provide: // ContextDescriptorKind getContextKind(); // void addLayoutInfo(); // ABI TODO: should be superseded @@ -662,7 +857,8 @@ namespace { class StructContextDescriptorBuilder - : public TypeContextDescriptorBuilderBase + : public TypeContextDescriptorBuilderBase { using super = TypeContextDescriptorBuilderBase; @@ -705,13 +901,14 @@ namespace { flags.setIsReflectable( !IGM.shouldEmitOpaqueTypeMetadataRecord(getType())); - getClangImportedFlags(flags); + setCommonFlags(flags); return flags.getOpaqueValue(); } }; class EnumContextDescriptorBuilder - : public TypeContextDescriptorBuilderBase + : public TypeContextDescriptorBuilderBase { using super = TypeContextDescriptorBuilderBase; @@ -763,13 +960,14 @@ namespace { flags.setIsReflectable(Strategy.isReflectable()); - getClangImportedFlags(flags); + setCommonFlags(flags); return flags.getOpaqueValue(); } }; class ClassContextDescriptorBuilder - : public TypeContextDescriptorBuilderBase, + : public TypeContextDescriptorBuilderBase, public SILVTableVisitor { using super = TypeContextDescriptorBuilderBase; @@ -820,6 +1018,8 @@ namespace { // Classes are always reflectable. flags.setIsReflectable(true); + setCommonFlags(flags); + if (!getType()->isForeign()) { if (MetadataLayout->areImmediateMembersNegative()) flags.class_setAreImmediateMembersNegative(true); @@ -835,8 +1035,6 @@ namespace { flags.class_setSuperclassReferenceKind(SuperClassRef->getKind()); } - getClangImportedFlags(flags); - return flags.getOpaqueValue(); } @@ -1167,53 +1365,26 @@ namespace { // MetadataDependency(Metadata *type, // MetadataCompletionContext *context, // const GenericMetadataPattern *pattern); - llvm::Function *f = - IGM.getAddrOfTypeMetadataCompletionFunction(Target, ForDefinition); - f->setAttributes(IGM.constructInitialAttributes()); - - IRGenFunction IGF(IGM, f); - - // Skip instrumentation when building for TSan to avoid false positives. - // The synchronization for this happens in the Runtime and we do not see it. - if (IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) - f->removeFnAttr(llvm::Attribute::SanitizeThread); - - if (IGM.DebugInfo) - IGM.DebugInfo->emitArtificialFunction(IGF, f); - - Explosion params = IGF.collectParameters(); - llvm::Value *metadata = params.claimNext(); - llvm::Value *context = params.claimNext(); - llvm::Value *templatePointer = params.claimNext(); - - (void) context; - (void) templatePointer; - - // Bind the generic arguments. - // FIXME: this will be problematic if we ever try to bind superclass - // types from type metadata! - if (Target->isGenericContext()) { - auto type = Target->getDeclaredTypeInContext()->getCanonicalType(); - IGF.bindLocalTypeDataFromTypeMetadata(type, IsExact, metadata, - MetadataState::Abstract); - } - - // A dependent VWT means that we have dependent metadata. - if (HasDependentVWT) - HasDependentMetadata = true; - - MetadataDependencyCollector collector; + emitMetadataCompletionFunction(IGM, Target, + [&](IRGenFunction &IGF, llvm::Value *metadata, + MetadataDependencyCollector *collector) { + // Bind the generic arguments. + // FIXME: this will be problematic if we ever try to bind superclass + // types from type metadata! + if (Target->isGenericContext()) { + auto type = Target->getDeclaredTypeInContext()->getCanonicalType(); + IGF.bindLocalTypeDataFromTypeMetadata(type, IsExact, metadata, + MetadataState::Abstract); + } - if (HasDependentMetadata) { - asImpl().emitInitializeMetadata(IGF, metadata, false, &collector); - } - - // The metadata is now complete. Finalize any metadata dependencies - // we may have collected. - auto dependency = collector.finish(IGF); - auto returnValue = dependency.combine(IGF); + // A dependent VWT means that we have dependent metadata. + if (HasDependentVWT) + HasDependentMetadata = true; - IGF.Builder.CreateRet(returnValue); + if (HasDependentMetadata) { + asImpl().emitInitializeMetadata(IGF, metadata, false, collector); + } + }); } /// The information necessary to fill in a GenericMetadataPartialPattern @@ -2079,7 +2250,7 @@ namespace { assert(!Target->isGenericContext()); auto type =cast(Target->getDeclaredType()->getCanonicalType()); - (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, + (void) createTypeMetadataAccessFunction(IGM, type, CacheStrategy::Lazy, [&](IRGenFunction &IGF, DynamicMetadataRequest request, llvm::Constant *cacheVar) -> MetadataResponse { // There's an interesting special case where we can do the @@ -2095,17 +2266,17 @@ namespace { } // Otherwise, use the generic path. - return emitInPlaceTypeMetadataAccessFunctionBody(IGF, type, cacheVar, + return emitOnceTypeMetadataAccessFunctionBody(IGF, type, cacheVar, [&](IRGenFunction &IGF, llvm::Value *metadata) { - return emitInPlaceMetadataInitialization(IGF, type, metadata); + return emitOnceMetadataInitialization(IGF, type, metadata); }); }); } private: - llvm::Value *emitInPlaceMetadataInitialization(IRGenFunction &IGF, - CanClassType type, - llvm::Value *metadata) { + llvm::Value *emitOnceMetadataInitialization(IRGenFunction &IGF, + CanClassType type, + llvm::Value *metadata) { // Many of the things done by generic instantiation are unnecessary here: // initializing the metaclass pointer // initializing the ro-data pointer @@ -2527,53 +2698,74 @@ IRGenFunction::emitValueWitnessTableRef(SILType type, // Value types (structs and enums) //===----------------------------------------------------------------------===// -static llvm::Value * -emitInPlaceValueTypeMetadataInitialization(IRGenFunction &IGF, - CanNominalType type, - llvm::Value *metadata, - MetadataDependencyCollector *collector) { - // All the value types are basically similar, as are foreign types. - assert(isa(type) || isa(type) || - IGF.IGM.requiresForeignTypeMetadata(type)); - - // Set up the value witness table if it's dependent. - SILType loweredType = IGF.IGM.getLoweredType(AbstractionPattern(type), type); - auto &ti = IGF.IGM.getTypeInfo(loweredType); - if (!ti.isFixedSize()) { - loweredType = loweredType.getAddressType(); - if (isa(type)) { - emitInitializeFieldOffsetVector(IGF, loweredType, metadata, true, - collector); - } else if (isa(type)) { - auto &strategy = getEnumImplStrategy(IGF.IGM, loweredType); - strategy.initializeMetadata(IGF, metadata, true, loweredType, collector); - } - } +namespace { + /// A helper class for laying out value metadata. + template + class ValueMetadataBuilderBase : public Base { + protected: + using Base::IGM; + using Base::Target; + using Base::asImpl; - return metadata; + using Base::Base; + + public: + /// Create the runtime data structures and functions necessary to + /// support in-place metadata initialization on this type. + void maybeCreateInPlaceMetadataInitialization() { + if (!needsInPlaceMetadataInitialization(IGM, Target)) + return; + + emitMetadataCompletionFunction(IGM, Target, + [&](IRGenFunction &IGF, llvm::Value *metadata, + MetadataDependencyCollector *collector) { + asImpl().emitInitializeMetadata(IGF, metadata, /*vwt mutable*/true, + collector); + }); + } + }; } -/// Create an access function for the type metadata of the given -/// non-generic nominal type. -static void createInPlaceValueTypeMetadataAccessFunction(IRGenModule &IGM, - NominalTypeDecl *typeDecl) { +/// Create an access function for the given type which triggers the +/// in-place initialization path. +static void +createInPlaceInitializationMetadataAccessFunction(IRGenModule &IGM, + NominalTypeDecl *typeDecl, + CanType type) { assert(!typeDecl->isGenericContext()); - auto type = - cast(typeDecl->getDeclaredType()->getCanonicalType()); - - (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, - [&](IRGenFunction &IGF, - DynamicMetadataRequest request, - llvm::Constant *cacheVariable) { - return emitInPlaceTypeMetadataAccessFunctionBody(IGF, type, cacheVariable, - [&](IRGenFunction &IGF, llvm::Value *metadata) { - MetadataDependencyCollector *collector = nullptr; // FIXME - return emitInPlaceValueTypeMetadataInitialization(IGF, type, metadata, - collector); - }); + + (void) createTypeMetadataAccessFunction(IGM, type, + CacheStrategy::InPlaceInitialization, + [&](IRGenFunction &IGF, + DynamicMetadataRequest request, + llvm::Constant *cacheVariable) { + llvm::Value *descriptor = + IGF.IGM.getAddrOfTypeContextDescriptor(typeDecl, RequireMetadata); + auto responsePair = + IGF.Builder.CreateCall(IGF.IGM.getGetInPlaceMetadataFn(), + {request.get(IGF), descriptor}); + return MetadataResponse::handle(IGF, request, responsePair); }); } +/// Create an access function for the given non-generic type. +static void createNonGenericMetadataAccessFunction(IRGenModule &IGM, + NominalTypeDecl *typeDecl) { + assert(!typeDecl->isGenericContext()); + auto type = typeDecl->getDeclaredType()->getCanonicalType(); + + // If the type requires the in-place initialization pattern, use it. + if (needsInPlaceMetadataInitialization(IGM, typeDecl)) { + createInPlaceInitializationMetadataAccessFunction(IGM, typeDecl, type); + return; + } + + // Otherwise, use the lazy pattern, which should be emitted using a + // direct reference to the metadata. + createDirectTypeMetadataAccessFunction(IGM, type, /*allow existing*/ false); +} + + //===----------------------------------------------------------------------===// // Structs //===----------------------------------------------------------------------===// @@ -2581,8 +2773,9 @@ static void createInPlaceValueTypeMetadataAccessFunction(IRGenModule &IGM, namespace { /// An adapter for laying out struct metadata. template - class StructMetadataBuilderBase : public StructMetadataVisitor { - using super = StructMetadataVisitor; + class StructMetadataBuilderBase + : public ValueMetadataBuilderBase> { + using super = ValueMetadataBuilderBase>; protected: ConstantStructBuilder &B; @@ -2657,6 +2850,18 @@ namespace { void addGenericWitnessTable(CanType type, ProtocolConformanceRef conf) { B.addNullPointer(IGM.WitnessTablePtrTy); } + + void emitInitializeMetadata(IRGenFunction &IGF, + llvm::Value *metadata, + bool isVWTMutable, + MetadataDependencyCollector *collector) { + auto loweredTy = getLoweredType(); + auto &fixedTI = IGM.getTypeInfo(loweredTy); + if (isa(fixedTI)) return; + + emitInitializeFieldOffsetVector(IGF, loweredTy, metadata, isVWTMutable, + collector); + } }; class StructMetadataBuilder : @@ -2677,7 +2882,8 @@ namespace { } void createMetadataAccessFunction() { - createInPlaceValueTypeMetadataAccessFunction(IGM, Target); + createNonGenericMetadataAccessFunction(IGM, Target); + maybeCreateInPlaceMetadataInitialization(); } }; @@ -2784,18 +2990,6 @@ namespace { bool hasCompletionFunction() { return !isa(IGM.getTypeInfo(getLoweredType())); } - - void emitInitializeMetadata(IRGenFunction &IGF, - llvm::Value *metadata, - bool isVWTMutable, - MetadataDependencyCollector *collector) { - auto loweredTy = getLoweredType(); - auto &fixedTI = IGM.getTypeInfo(loweredTy); - if (isa(fixedTI)) return; - - emitInitializeFieldOffsetVector(IGF, loweredTy, metadata, isVWTMutable, - collector); - } }; } // end anonymous namespace @@ -2851,8 +3045,9 @@ void IRGenerator::noteUseOfAnyParentTypeMetadata(NominalTypeDecl *type) { namespace { template - class EnumMetadataBuilderBase : public EnumMetadataVisitor { - using super = EnumMetadataVisitor; + class EnumMetadataBuilderBase + : public ValueMetadataBuilderBase> { + using super = ValueMetadataBuilderBase>; protected: ConstantStructBuilder &B; @@ -2920,6 +3115,18 @@ namespace { auto &strategy = getEnumImplStrategy(IGM, enumTy); return Size(strategy.getPayloadSizeForMetadata()); } + + void emitInitializeMetadata(IRGenFunction &IGF, + llvm::Value *metadata, + bool isVWTMutable, + MetadataDependencyCollector *collector) { + // Nominal types are always preserved through SIL lowering. + auto enumTy = getLoweredType(); + + auto &strategy = getEnumImplStrategy(IGF.IGM, enumTy); + strategy.initializeMetadata(IGF, metadata, isVWTMutable, enumTy, + collector); + } }; class EnumMetadataBuilder @@ -2931,8 +3138,6 @@ namespace { ConstantStructBuilder &B) : EnumMetadataBuilderBase(IGM, theEnum, B) {} - - void addPayloadSize() { auto payloadSize = getConstantPayloadSize(); if (!payloadSize) { @@ -2949,7 +3154,8 @@ namespace { } void createMetadataAccessFunction() { - createInPlaceValueTypeMetadataAccessFunction(IGM, Target); + createNonGenericMetadataAccessFunction(IGM, Target); + maybeCreateInPlaceMetadataInitialization(); } }; @@ -3001,18 +3207,6 @@ namespace { bool hasCompletionFunction() { return !isa(IGM.getTypeInfo(getLoweredType())); } - - void emitInitializeMetadata(IRGenFunction &IGF, - llvm::Value *metadata, - bool isVWTMutable, - MetadataDependencyCollector *collector) { - // Nominal types are always preserved through SIL lowering. - auto enumTy = getLoweredType(); - - auto &strategy = getEnumImplStrategy(IGF.IGM, enumTy); - strategy.initializeMetadata(IGF, metadata, isVWTMutable, enumTy, - collector); - } }; } // end anonymous namespace @@ -3087,6 +3281,7 @@ namespace { protected: using super::IGM; + using super::Target; using super::asImpl; using super::B; @@ -3094,90 +3289,53 @@ namespace { ForeignMetadataBuilderBase(T &&...args) : super(std::forward(args)...) {} Size AddressPoint = Size::invalid(); + bool CanBeConstant = true; public: - void layout() { - if (asImpl().requiresInitializationFunction()) - asImpl().addInitializationFunction(); - asImpl().addForeignFlags(); - super::layout(); - } - - void addForeignFlags() { - int64_t flags = 0; - if (asImpl().requiresInitializationFunction()) flags |= 1; - B.addInt(IGM.IntPtrTy, flags); + void noteAddressPoint() { + AddressPoint = B.getNextOffsetFromGlobal(); } - void addForeignName() { - CanType targetType = asImpl().getTargetType(); - IRGenMangler mangler; - std::string Name = - mangler.mangleTypeForForeignMetadataUniquing(targetType); - llvm::Constant *nameStr = IGM.getAddrOfGlobalString(Name, - /*relatively addressed*/ true); - B.addRelativeAddress(nameStr); + bool canBeConstant() { + return CanBeConstant; } - void addInitializationFunction() { - auto type = cast(asImpl().getTargetType()); - - auto fnTy = llvm::FunctionType::get(IGM.VoidTy, {IGM.TypeMetadataPtrTy}, - /*variadic*/ false); - llvm::Function *fn = llvm::Function::Create(fnTy, - llvm::GlobalValue::PrivateLinkage, - Twine("initialize_metadata_") - + type->getDecl()->getName().str(), - &IGM.Module); - fn->setAttributes(IGM.constructInitialAttributes()); - - // Set up the function. - IRGenFunction IGF(IGM, fn); - if (IGM.DebugInfo) - IGM.DebugInfo->emitArtificialFunction(IGF, fn); + Size getOffsetOfAddressPoint() const { return AddressPoint; } - // Emit the initialization. - llvm::Value *metadata = IGF.collectParameters().claimNext(); - asImpl().emitInitialization(IGF, metadata); + void createMetadataAccessFunction() { + if (asImpl().needsMetadataCompletionFunction()) + asImpl().createMetadataCompletionFunction(); - IGF.Builder.CreateRetVoid(); + auto type = cast(asImpl().getTargetType()); - B.addRelativeAddress(fn); - - // Keep pointer alignment on 64-bit platforms for further fields. - switch (IGM.getPointerSize().getValue()) { - case 4: - break; - case 8: - B.addInt32(0); - break; - default: - llvm_unreachable("unsupported word size"); - } - } - - void noteAddressPoint() { - AddressPoint = B.getNextOffsetFromGlobal(); + (void) createTypeMetadataAccessFunction(IGM, type, CacheStrategy::Lazy, + [&](IRGenFunction &IGF, + DynamicMetadataRequest request, + llvm::Constant *cacheVariable) { + auto candidate = IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type); + auto call = IGF.Builder.CreateCall(IGF.IGM.getGetForeignTypeMetadataFn(), + {request.get(IGF), candidate}); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoUnwind); + call->addAttribute(llvm::AttributeList::FunctionIndex, + llvm::Attribute::ReadNone); + + return MetadataResponse::handle(IGF, request, call); + }); } - Size getOffsetOfAddressPoint() const { return AddressPoint; } - - void createMetadataAccessFunction() { - auto type = cast(asImpl().getTargetType()); + bool needsMetadataCompletionFunction() { + return needsForeignMetadataCompletionFunction(Target); + } - (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, - [&](IRGenFunction &IGF, - DynamicMetadataRequest request, - llvm::Constant *cacheVariable) { - return emitInPlaceTypeMetadataAccessFunctionBody(IGF, type, - cacheVariable, - [&](IRGenFunction &IGF, llvm::Value *candidate) { - MetadataDependencyCollector *collector = nullptr; - auto metadata = uniqueForeignTypeMetadataRef(IGF, candidate); - return emitInPlaceValueTypeMetadataInitialization(IGF, type, - metadata, - collector); - }); + void createMetadataCompletionFunction() { + // Note that we can't call this until we've finished laying out the + // metadata because otherwise we'll try to reenter when we ask for + // the metadata candidate. + emitMetadataCompletionFunction(IGM, Target, + [&](IRGenFunction &IGF, llvm::Value *metadata, + MetadataDependencyCollector *collector) { + asImpl().emitInitializeMetadata(IGF, metadata, collector); }); } }; @@ -3202,24 +3360,20 @@ namespace { ConstantStructBuilder &B) : ForeignMetadataBuilderBase(IGM, target, B) {} - void emitInitialization(IRGenFunction &IGF, llvm::Value *metadata) { - // Dig out the address of the superclass field. + void emitInitializeMetadata(IRGenFunction &IGF, llvm::Value *metadata, + MetadataDependencyCollector *collector) { + // Emit a reference to the superclass. + auto superclass = IGF.emitAbstractTypeMetadataRef( + Target->getSuperclass()->getCanonicalType()); + + // Dig out the address of the superclass field and store. auto &layout = IGF.IGM.getForeignMetadataLayout(Target); - Address metadataWords(IGF.Builder.CreateBitCast(metadata, - IGM.Int8PtrPtrTy), - IGM.getPointerAlignment()); + Address addr(metadata, IGM.getPointerAlignment()); + addr = IGF.Builder.CreateElementBitCast(addr, IGM.TypeMetadataPtrTy); auto superclassField = - createPointerSizedGEP(IGF, metadataWords, + createPointerSizedGEP(IGF, addr, layout.getSuperClassOffset().getStaticOffset()); - superclassField = - IGF.Builder.CreateBitCast( - superclassField, - llvm::PointerType::get(IGM.TypeMetadataPtrTy, 0)); - - // Unique the superclass field and write it back. - auto superclass = IGF.Builder.CreateLoad(superclassField); - auto uniquedSuperclass = uniqueForeignTypeMetadataRef(IGF, superclass); - IGF.Builder.CreateStore(uniquedSuperclass, superclassField); + IGF.Builder.CreateStore(superclass, superclassField); } // Visitor methods. @@ -3244,21 +3398,15 @@ namespace { B.add(descriptor); } - void noteStartOfSuperClass() { } - void addSuperclass() { - auto superclassDecl = Target->getSuperclassDecl(); - if (!superclassDecl || !superclassDecl->isForeign()) { - B.addNullPointer(IGM.TypeMetadataPtrTy); - return; - } + // Always leave the superclass pointer unfilled. We'll have to + // unique it during initialization anyway, so we might as well spare + // ourselves the load-time work. + B.addNullPointer(IGM.TypeMetadataPtrTy); - auto superclassType = - superclassDecl->swift::TypeDecl::getDeclaredInterfaceType() - ->getCanonicalType(); - auto superclass = - IGM.getAddrOfForeignTypeMetadataCandidate(superclassType); - B.add(superclass); + // But remember if we might need to change it. + if (Target->hasSuperclass()) + CanBeConstant = false; } void addReservedWord() { @@ -3280,10 +3428,9 @@ namespace { return Target->getDeclaredType()->getCanonicalType(); } - bool requiresInitializationFunction() const { - return false; + void createMetadataCompletionFunction() { + llvm_unreachable("foreign structs never require completion"); } - void emitInitialization(IRGenFunction &IGF, llvm::Value *metadata) {} void addValueWitnessTable() { B.add(emitValueWitnessTable()); @@ -3308,10 +3455,9 @@ namespace { return Target->getDeclaredType()->getCanonicalType(); } - bool requiresInitializationFunction() const { - return false; + void createMetadataCompletionFunction() { + llvm_unreachable("foreign enums never require completion"); } - void emitInitialization(IRGenFunction &IGF, llvm::Value *metadata) {} void addValueWitnessTable() { B.add(emitValueWitnessTable()); @@ -3323,18 +3469,29 @@ namespace { }; } // end anonymous namespace -bool IRGenModule::requiresForeignTypeMetadata(CanType type) { +bool irgen::requiresForeignTypeMetadata(CanType type) { if (NominalTypeDecl *nominal = type->getAnyNominal()) { - if (auto *clas = dyn_cast(nominal)) { - return clas->isForeign(); - } - - return isa(nominal->getModuleScopeContext()); + return requiresForeignTypeMetadata(nominal); } return false; } +bool irgen::requiresForeignTypeMetadata(NominalTypeDecl *decl) { + if (auto *clas = dyn_cast(decl)) { + switch (clas->getForeignClassKind()) { + case ClassDecl::ForeignKind::Normal: + case ClassDecl::ForeignKind::RuntimeOnly: + return false; + case ClassDecl::ForeignKind::CFType: + return true; + } + llvm_unreachable("bad foreign class kind"); + } + + return isa(decl->getModuleScopeContext()); +} + llvm::Constant * IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { // What we save in GlobalVars is actually the offsetted value. @@ -3349,6 +3506,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { // Local function to create the global variable for the foreign type // metadata candidate. + bool canBeConstant; Size addressPoint; llvm::Constant *result = nullptr; auto createCandidateVariable = [&] { @@ -3360,6 +3518,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { createVariable(*this, link, definition.getType(), getPointerAlignment()); definition.installInGlobal(var); + var->setConstant(canBeConstant); // Apply the offset. result = llvm::ConstantExpr::getBitCast(var, Int8PtrTy); @@ -3381,6 +3540,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { ForeignClassMetadataBuilder builder(*this, classDecl, init); builder.layout(); addressPoint = builder.getOffsetOfAddressPoint(); + canBeConstant = builder.canBeConstant(); createCandidateVariable(); builder.createMetadataAccessFunction(); @@ -3393,6 +3553,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { ForeignStructMetadataBuilder builder(*this, structDecl, init); builder.layout(); addressPoint = builder.getOffsetOfAddressPoint(); + canBeConstant = builder.canBeConstant(); createCandidateVariable(); builder.createMetadataAccessFunction(); @@ -3403,6 +3564,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { ForeignEnumMetadataBuilder builder(*this, enumDecl, init); builder.layout(); addressPoint = builder.getOffsetOfAddressPoint(); + canBeConstant = builder.canBeConstant(); createCandidateVariable(); builder.createMetadataAccessFunction(); @@ -3417,8 +3579,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { if (auto enclosing = type->getNominalParent()) { auto canonicalEnclosing = enclosing->getCanonicalType(); if (requiresForeignTypeMetadata(canonicalEnclosing)) { - (void)getTypeMetadataAccessFunction(*this, canonicalEnclosing, - ForDefinition); + (void)getAddrOfForeignTypeMetadataCandidate(canonicalEnclosing); } } diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index f0f9e55f4902d..cf3975d4d170a 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -47,6 +47,9 @@ namespace irgen { class StructLayout; struct ClassLayout; + bool requiresForeignTypeMetadata(CanType type); + bool requiresForeignTypeMetadata(NominalTypeDecl *decl); + /// Emit the metadata associated with the given class declaration. void emitClassMetadata(IRGenModule &IGM, ClassDecl *theClass, const StructLayout &layout, diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index ea363780ea76e..2c7d93c388c7f 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -1091,8 +1091,8 @@ getWitnessTableLazyAccessFunction(IRGenModule &IGM, auto cacheVariable = cast(IGM.getAddrOfWitnessTableLazyCacheVariable( rootConformance, conformingType, ForDefinition)); - emitLazyCacheAccessFunction(IGM, accessor, cacheVariable, - [&](IRGenFunction &IGF, Explosion ¶ms) { + emitCacheAccessFunction(IGM, accessor, cacheVariable, CacheStrategy::Lazy, + [&](IRGenFunction &IGF, Explosion ¶ms) { llvm::Value *conformingMetadataCache = nullptr; return MetadataResponse::forComplete( emitWitnessTableAccessorCall(IGF, conformance, @@ -2225,19 +2225,7 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) { // Trigger the lazy emission of the foreign type metadata. CanType conformingType = conf->getType()->getCanonicalType(); if (requiresForeignTypeMetadata(conformingType)) { - // Make sure we emit the metadata access function. - (void)getTypeMetadataAccessFunction(*this, conformingType, - ForDefinition); - - // Make sure we emit the nominal type descriptor. - auto *nominal = conformingType->getAnyNominal(); - auto entity = LinkEntity::forNominalTypeDescriptor(nominal); - if (auto entry = GlobalVars[entity]) { - if (!cast(entry)->isDeclaration()) - return; - } - - emitLazyTypeContextDescriptor(*this, nominal, RequireMetadata); + (void)getAddrOfForeignTypeMetadataCandidate(conformingType); } } diff --git a/lib/IRGen/IRGenMangler.h b/lib/IRGen/IRGenMangler.h index 77d3438b0b53c..a513c494ed8eb 100644 --- a/lib/IRGen/IRGenMangler.h +++ b/lib/IRGen/IRGenMangler.h @@ -82,6 +82,11 @@ class IRGenMangler : public Mangle::ASTMangler { return mangleNominalTypeSymbol(Decl, "Mi"); } + std::string mangleTypeMetadataInPlaceInitializationCache( + const NominalTypeDecl *Decl) { + return mangleNominalTypeSymbol(Decl, "Ml"); + } + std::string mangleTypeMetadataCompletionFunction(const NominalTypeDecl *Decl){ return mangleNominalTypeSymbol(Decl, "Mr"); } diff --git a/lib/IRGen/IRGenModule.cpp b/lib/IRGen/IRGenModule.cpp index 7c70497bed448..465457289b370 100644 --- a/lib/IRGen/IRGenModule.cpp +++ b/lib/IRGen/IRGenModule.cpp @@ -199,6 +199,16 @@ IRGenModule::IRGenModule(IRGenerator &irgen, SizeTy }); + OffsetPairTy = llvm::StructType::get(getLLVMContext(), { SizeTy, SizeTy }); + + // The TypeLayout structure, including all possible trailing components. + FullTypeLayoutTy = createStructType(*this, "swift.full_type_layout", { + SizeTy, // size + SizeTy, // flags + SizeTy, // alignment + SizeTy // extra inhabitant flags (optional) + }); + // A protocol descriptor describes a protocol. It is not type metadata in // and of itself, but is referenced in the structure of existential type // metadata records. diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 6d7621371545a..38ad05aa67010 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -98,7 +98,7 @@ namespace swift { class SourceLoc; class SourceFile; class Type; - enum class TypeMetadataRecordKind : unsigned; + enum class TypeReferenceKind : unsigned; namespace Lowering { class TypeConverter; @@ -424,13 +424,13 @@ class ConstantReference { /// A reference to a declared type entity. class TypeEntityReference { - TypeMetadataRecordKind Kind; + TypeReferenceKind Kind; llvm::Constant *Value; public: - TypeEntityReference(TypeMetadataRecordKind kind, llvm::Constant *value) + TypeEntityReference(TypeReferenceKind kind, llvm::Constant *value) : Kind(kind), Value(value) {} - TypeMetadataRecordKind getKind() const { return Kind; } + TypeReferenceKind getKind() const { return Kind; } llvm::Constant *getValue() const { return Value; } }; @@ -530,6 +530,8 @@ class IRGenModule { llvm::StructType *TypeMetadataResponseTy; /// { %swift.type*, iSize } llvm::StructType *TypeMetadataDependencyTy; /// { %swift.type*, iSize } }; + llvm::StructType *OffsetPairTy; /// { iSize, iSize } + llvm::StructType *FullTypeLayoutTy; /// %swift.full_type_layout = { ... } llvm::PointerType *TupleTypeMetadataPtrTy; /// %swift.tuple_type* llvm::StructType *FullHeapMetadataStructTy; /// %swift.full_heapmetadata = type { ... } llvm::PointerType *FullHeapMetadataPtrTy;/// %swift.full_heapmetadata* @@ -1156,6 +1158,9 @@ private: \ ForDefinition_t forDefinition); llvm::Constant *getAddrOfTypeMetadataInstantiationCache(NominalTypeDecl *D, ForDefinition_t forDefinition); + llvm::Constant *getAddrOfTypeMetadataInPlaceInitializationCache( + NominalTypeDecl *D, + ForDefinition_t forDefinition); llvm::Function *getAddrOfTypeMetadataAccessFunction(CanType type, ForDefinition_t forDefinition); llvm::Function *getAddrOfGenericTypeMetadataAccessFunction( @@ -1166,9 +1171,6 @@ private: \ ForDefinition_t forDefinition); llvm::Constant *getAddrOfForeignTypeMetadataCandidate(CanType concreteType); - /// Determine whether the given type requires foreign type metadata. - bool requiresForeignTypeMetadata(CanType type); - llvm::Constant *getAddrOfClassMetadataBounds(ClassDecl *D, ForDefinition_t forDefinition); llvm::Constant *getAddrOfTypeContextDescriptor(NominalTypeDecl *D, diff --git a/lib/IRGen/Linking.cpp b/lib/IRGen/Linking.cpp index 3c6981fe7dfaa..8602438909202 100644 --- a/lib/IRGen/Linking.cpp +++ b/lib/IRGen/Linking.cpp @@ -96,6 +96,10 @@ std::string LinkEntity::mangleAsString() const { return mangler.mangleTypeMetadataInstantiationFunction( cast(getDecl())); + case Kind::TypeMetadataInPlaceInitializationCache: + return mangler.mangleTypeMetadataInPlaceInitializationCache( + cast(getDecl())); + case Kind::TypeMetadataCompletionFunction: return mangler.mangleTypeMetadataCompletionFunction( cast(getDecl())); diff --git a/lib/IRGen/MetadataLayout.cpp b/lib/IRGen/MetadataLayout.cpp index bb8eeb8093f13..4c4fc217c91f5 100644 --- a/lib/IRGen/MetadataLayout.cpp +++ b/lib/IRGen/MetadataLayout.cpp @@ -613,8 +613,9 @@ ForeignClassMetadataLayout::ForeignClassMetadataLayout(IRGenModule &IGM, ForeignClassMetadataLayout &layout) : super(IGM, decl), Layout(layout) {} - void noteStartOfSuperClass() { + void addSuperclass() { Layout.SuperClassOffset = getNextOffset(); + super::addSuperclass(); } void layout() { diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 91600c4cb2cff..c1810ef5d184e 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -469,25 +469,6 @@ llvm::Value *irgen::emitObjCHeapMetadataRef(IRGenFunction &IGF, classObject); } -/// Emit a reference to the type metadata for a foreign type. -llvm::Value *irgen::uniqueForeignTypeMetadataRef(IRGenFunction &IGF, - llvm::Value *candidate) { - auto call = IGF.Builder.CreateCall(IGF.IGM.getGetForeignTypeMetadataFn(), - candidate); - call->addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::NoUnwind); - call->addAttribute(llvm::AttributeList::FunctionIndex, - llvm::Attribute::ReadNone); - return call; -} - -/// Emit a reference to the type metadata for a foreign type. -static llvm::Value *emitForeignTypeMetadataRef(IRGenFunction &IGF, - CanType type) { - llvm::Value *candidate = IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type); - return uniqueForeignTypeMetadataRef(IGF, candidate); -} - /// Returns a metadata reference for a nominal type. /// /// This is only valid in a couple of special cases: @@ -634,6 +615,9 @@ MetadataAccessStrategy irgen::getTypeMetadataAccessStrategy(CanType type) { assert(type->hasUnboundGenericType()); } + if (requiresForeignTypeMetadata(nominal)) + return MetadataAccessStrategy::ForeignAccessor; + // If the type doesn't guarantee that it has an access function, // we might have to use a non-unique accessor. @@ -1283,16 +1267,18 @@ static bool isLoadFrom(llvm::Value *value, Address address) { return false; } -/// Emit the body of a lazy cache accessor. +/// Emit the body of a cache accessor. /// /// If cacheVariable is null, we perform the direct access every time. /// This is used for metadata accessors that come about due to resilience, /// where the direct access is completely trivial. -void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM, - llvm::Function *accessor, - llvm::GlobalVariable *cacheVariable, - LazyCacheEmitter getValue, - bool isReadNone) { +void irgen::emitCacheAccessFunction(IRGenModule &IGM, + llvm::Function *accessor, + llvm::Constant *cacheVariable, + CacheStrategy cacheStrategy, + CacheEmitter getValue, + bool isReadNone) { + assert((cacheStrategy == CacheStrategy::None) == (cacheVariable == nullptr)); accessor->setDoesNotThrow(); // This function is logically 'readnone': the caller does not need @@ -1309,8 +1295,10 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM, bool returnsResponse = (accessor->getReturnType() == IGM.TypeMetadataResponseTy); + switch (cacheStrategy) { + // If there's no cache variable, just perform the direct access. - if (cacheVariable == nullptr) { + case CacheStrategy::None: { auto response = getValue(IGF, parameters); llvm::Value *ret; if (returnsResponse) { @@ -1324,13 +1312,22 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM, return; } - // Set up the cache variable. + // For in-place initialization, drill to the first element of the cache. + case CacheStrategy::InPlaceInitialization: + cacheVariable = + llvm::ConstantExpr::getBitCast(cacheVariable, + IGM.TypeMetadataPtrTy->getPointerTo()); + break; + + case CacheStrategy::Lazy: + break; + } + llvm::Constant *null = llvm::ConstantPointerNull::get( - cast(cacheVariable->getValueType())); + cast( + cacheVariable->getType()->getPointerElementType())); - cacheVariable->setInitializer(null); - cacheVariable->setAlignment(IGM.getPointerAlignment().getValue()); Address cache(cacheVariable, IGM.getPointerAlignment()); // Okay, first thing, check the cache variable. @@ -1380,33 +1377,41 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM, // Emit a branch around the caching code if we're working with responses // and the fetched result is not complete. We can avoid doing this if - // the response is statically known to be complete. + // the response is statically known to be complete, and we don't need to + // do it if this is an in-place initiazation cache because the store + // is done within the runtime. llvm::BasicBlock *completionCheckBB = nullptr; llvm::Value *directState = nullptr; - if (returnsResponse && !response.isStaticallyKnownComplete()) { - completionCheckBB = IGF.Builder.GetInsertBlock(); + if (cacheStrategy == CacheStrategy::InPlaceInitialization) { directState = response.getDynamicState(); + completionCheckBB = IGF.Builder.GetInsertBlock(); + } else { + if (returnsResponse && + !response.isStaticallyKnownComplete()) { + completionCheckBB = IGF.Builder.GetInsertBlock(); + directState = response.getDynamicState(); - auto isCompleteBB = IGF.createBasicBlock("is_complete"); - auto isComplete = - IGF.Builder.CreateICmpEQ(directState, completedState); + auto isCompleteBB = IGF.createBasicBlock("is_complete"); + auto isComplete = + IGF.Builder.CreateICmpEQ(directState, completedState); - IGF.Builder.CreateCondBr(isComplete, isCompleteBB, contBB); - IGF.Builder.emitBlock(isCompleteBB); - } + IGF.Builder.CreateCondBr(isComplete, isCompleteBB, contBB); + IGF.Builder.emitBlock(isCompleteBB); + } - // Store it back to the cache variable. This needs to be a store-release - // because it needs to propagate memory visibility to the other threads - // that can access the cache: the initializing stores might be visible - // to this thread, but they aren't transitively guaranteed to be visible - // to other threads unless this is a store-release. - // - // However, we can skip this if the value was actually loaded from the - // cache. This is a simple, if hacky, peephole that's useful for the - // code in emitInPlaceTypeMetadataAccessFunctionBody. - if (!isLoadFrom(directResult, cache)) { - IGF.Builder.CreateStore(directResult, cache) - ->setAtomic(llvm::AtomicOrdering::Release); + // Store it back to the cache variable. This needs to be a store-release + // because it needs to propagate memory visibility to the other threads + // that can access the cache: the initializing stores might be visible + // to this thread, but they aren't transitively guaranteed to be visible + // to other threads unless this is a store-release. + // + // However, we can skip this if the value was actually loaded from the + // cache. This is a simple, if hacky, peephole that's useful for the + // code in emitOnceTypeMetadataAccessFunctionBody. + if (!isLoadFrom(directResult, cache)) { + IGF.Builder.CreateStore(directResult, cache) + ->setAtomic(llvm::AtomicOrdering::Release); + } } IGF.Builder.CreateBr(contBB); @@ -1423,12 +1428,14 @@ void irgen::emitLazyCacheAccessFunction(IRGenModule &IGM, // Add a phi for the metadata state if we're returning a response. llvm::Value *stateToReturn = nullptr; if (directState) { - phi->addIncoming(directResult, completionCheckBB); + if (storeBB != completionCheckBB) + phi->addIncoming(directResult, completionCheckBB); auto completionStatePHI = IGF.Builder.CreatePHI(IGM.SizeTy, 3); completionStatePHI->addIncoming(completedState, loadBB); completionStatePHI->addIncoming(directState, completionCheckBB); - completionStatePHI->addIncoming(completedState, storeBB); + if (storeBB != completionCheckBB) + completionStatePHI->addIncoming(completedState, storeBB); stateToReturn = completionStatePHI; } else if (returnsResponse) { stateToReturn = completedState; @@ -1564,11 +1571,11 @@ emitGenericTypeMetadataAccessFunction(IRGenFunction &IGF, /// Emit a helper function for swift_once that performs in-place /// initialization of the given nominal type. static llvm::Constant * -createInPlaceMetadataInitializationFunction(IRGenModule &IGM, - CanNominalType type, - llvm::Constant *metadata, - llvm::Constant *cacheVariable, - InPlaceMetadataInitializer &&initialize) { +createOnceMetadataInitializationFunction(IRGenModule &IGM, + CanNominalType type, + llvm::Constant *metadata, + llvm::Constant *cacheVariable, + OnceMetadataInitializer initialize) { // There's an ignored i8* parameter. auto fnTy = llvm::FunctionType::get(IGM.VoidTy, {IGM.Int8PtrTy}, /*variadic*/ false); @@ -1602,16 +1609,14 @@ createInPlaceMetadataInitializationFunction(IRGenModule &IGM, } /// Emit the function body for the type metadata accessor of a nominal type -/// that might require in-place initialization. +/// that uses swift_once to control in-place initialization. MetadataResponse -irgen::emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, - CanNominalType type, - llvm::Constant *cacheVariable, - InPlaceMetadataInitializer &&initializer) { - llvm::Constant *metadata = - IGF.IGM.requiresForeignTypeMetadata(type) - ? IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type) - : IGF.IGM.getAddrOfTypeMetadata(type); +irgen::emitOnceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, + CanNominalType type, + llvm::Constant *cacheVariable, + OnceMetadataInitializer initializer) { + assert(!requiresForeignTypeMetadata(type)); + llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type); // We might not have interesting initialization to do. assert((cacheVariable == nullptr) == @@ -1634,9 +1639,9 @@ irgen::emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, // Create the protected function. swift_once wants this as an i8*. llvm::Value *onceFn = - createInPlaceMetadataInitializationFunction(IGF.IGM, type, metadata, - cacheVariable, - std::move(initializer)); + createOnceMetadataInitializationFunction(IGF.IGM, type, metadata, + cacheVariable, + std::move(initializer)); onceFn = IGF.Builder.CreateBitCast(onceFn, IGF.IGM.Int8PtrTy); auto context = llvm::UndefValue::get(IGF.IGM.Int8PtrTy); @@ -1652,7 +1657,7 @@ irgen::emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, if (IGF.IGM.IRGen.Opts.Sanitizers & SanitizerKind::Thread) relocatedMetadata->setOrdering(llvm::AtomicOrdering::Acquire); - // emitLazyCacheAccessFunction will see that the value was loaded from + // emitCacheAccessFunction will see that the value was loaded from // the guard variable and skip the redundant store back. return MetadataResponse::forComplete(relocatedMetadata); } @@ -1664,9 +1669,9 @@ irgen::emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, /// metadata-construction functions. It is not used for the in-place /// initialization of non-generic nominal type metadata. static MetadataResponse -emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF, - DynamicMetadataRequest request, - CanType type) { +emitDirectTypeMetadataAccessFunctionBody(IRGenFunction &IGF, + DynamicMetadataRequest request, + CanType type) { assert(!type->hasArchetype() && "cannot emit metadata accessor for context-dependent type"); @@ -1694,69 +1699,94 @@ emitTypeMetadataAccessFunctionBody(IRGenFunction &IGF, // metadata for it.) assert(!IGF.IGM.isResilient(typeDecl, ResilienceExpansion::Maximal)); - // Non-native types are just wrapped in various ways. - if (auto classDecl = dyn_cast(typeDecl)) { - // We emit a completely different pattern for foreign classes. - if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) { - return MetadataResponse::forComplete( - emitForeignTypeMetadataRef(IGF, type)); - } + // We should never be emitting a metadata accessor for foreign type + // metadata using this function. + assert(!requiresForeignTypeMetadata(typeDecl)); - // Classes that might not have Swift metadata use a different - // symbol name. + // Classes that might not have Swift metadata use a different + // access pattern. + if (auto classDecl = dyn_cast(typeDecl)) { if (!hasKnownSwiftMetadata(IGF.IGM, classDecl)) { return MetadataResponse::forComplete(emitObjCMetadataRef(IGF, classDecl)); } - - // Imported value types require foreign metadata uniquing. - } else if (isa(typeDecl->getModuleScopeContext())) { - return MetadataResponse::forComplete(emitForeignTypeMetadataRef(IGF, type)); } - // Okay, everything else is built from a Swift metadata object. - llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type); - // We should not be doing more serious work along this path. assert(isTypeMetadataAccessTrivial(IGF.IGM, type)); + // Okay, everything else is built from a Swift metadata object. + llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type); + return MetadataResponse::forComplete(metadata); } -/// Get or create an accessor function to the given non-dependent type. -llvm::Function * -irgen::getTypeMetadataAccessFunction(IRGenModule &IGM, - CanType type, - ForDefinition_t shouldDefine, - MetadataAccessGenerator generator) { +static llvm::Function *getAccessFunctionPrototype(IRGenModule &IGM, + CanType type, + ForDefinition_t forDefinition) { assert(!type->hasArchetype()); // Type should be bound unless it's type erased. assert(isTypeErasedGenericClassType(type) ? !isa(type) : !isa(type)); - llvm::Function *accessor = - IGM.getAddrOfTypeMetadataAccessFunction(type, shouldDefine); + return IGM.getAddrOfTypeMetadataAccessFunction(type, forDefinition); +} + +llvm::Function * +irgen::getOtherwiseDefinedTypeMetadataAccessFunction(IRGenModule &IGM, + CanType type) { + return getAccessFunctionPrototype(IGM, type, NotForDefinition); +} + +/// Get or create an accessor function to the given non-dependent type. +llvm::Function * +irgen::createTypeMetadataAccessFunction(IRGenModule &IGM, CanType type, + CacheStrategy cacheStrategy, + MetadataAccessGenerator generator, + bool allowExistingDefinition) { + // Get the prototype. + auto accessor = getAccessFunctionPrototype(IGM, type, ForDefinition); // If we're not supposed to define the accessor, or if we already // have defined it, just return the pointer. - if (!shouldDefine || !accessor->empty()) + if (!accessor->empty()) { + assert(allowExistingDefinition && + "repeat definition of access function!"); return accessor; + } // Okay, define the accessor. - llvm::GlobalVariable *cacheVariable = nullptr; + llvm::Constant *cacheVariable = nullptr; // If our preferred access method is to go via an accessor, it means // there is some non-trivial computation that needs to be cached. - if (!isTypeMetadataAccessTrivial(IGM, type)) { - cacheVariable = cast( - IGM.getAddrOfTypeMetadataLazyCacheVariable(type, ForDefinition)); + if (isTypeMetadataAccessTrivial(IGM, type)) { + cacheStrategy = CacheStrategy::None; + } else { + switch (cacheStrategy) { + // Nothing to do. + case CacheStrategy::None: + break; + + // For lazy initialization, the cache variable is just a pointer. + case CacheStrategy::Lazy: + cacheVariable = + IGM.getAddrOfTypeMetadataLazyCacheVariable(type, ForDefinition); + break; + + // For in-place initialization, drill down to the first element. + case CacheStrategy::InPlaceInitialization: + cacheVariable = IGM.getAddrOfTypeMetadataInPlaceInitializationCache( + type->getAnyNominal(), ForDefinition); + break; + } if (IGM.getOptions().optimizeForSize()) accessor->addFnAttr(llvm::Attribute::NoInline); } - emitLazyCacheAccessFunction(IGM, accessor, cacheVariable, - [&](IRGenFunction &IGF, Explosion ¶ms) { + emitCacheAccessFunction(IGM, accessor, cacheVariable, cacheStrategy, + [&](IRGenFunction &IGF, Explosion ¶ms) { auto request = DynamicMetadataRequest(params.claimNext()); return generator(IGF, request, cacheVariable); }); @@ -1764,18 +1794,18 @@ irgen::getTypeMetadataAccessFunction(IRGenModule &IGM, return accessor; } -/// Get or create an accessor function to the given non-dependent type. -llvm::Function *irgen::getTypeMetadataAccessFunction(IRGenModule &IGM, - CanType type, - ForDefinition_t shouldDefine) { - return getTypeMetadataAccessFunction(IGM, type, shouldDefine, - [&](IRGenFunction &IGF, - DynamicMetadataRequest request, - llvm::Constant *cacheVariable) { +/// Emit a standard accessor function to the given non-dependent type. +llvm::Function * +irgen::createDirectTypeMetadataAccessFunction(IRGenModule &IGM, CanType type, + bool allowExistingDefinition) { + return createTypeMetadataAccessFunction(IGM, type, CacheStrategy::Lazy, + [&](IRGenFunction &IGF, + DynamicMetadataRequest request, + llvm::Constant *cacheVariable) { // We should not be called with ForDefinition for nominal types // that require in-place initialization. - return emitTypeMetadataAccessFunctionBody(IGF, request, type); - }); + return emitDirectTypeMetadataAccessFunctionBody(IGF, request, type); + }, allowExistingDefinition); } /// Get or create an accessor function to the given generic type. @@ -1804,42 +1834,26 @@ irgen::getGenericTypeMetadataAccessFunction(IRGenModule &IGM, bool isReadNone = (genericArgs.Types.size() <= NumDirectGenericTypeMetadataAccessFunctionArgs); - emitLazyCacheAccessFunction(IGM, accessor, /*cacheVariable=*/nullptr, - [&](IRGenFunction &IGF, Explosion ¶ms) { - return emitGenericTypeMetadataAccessFunction( + emitCacheAccessFunction(IGM, accessor, /*cache*/nullptr, CacheStrategy::None, + [&](IRGenFunction &IGF, Explosion ¶ms) { + return emitGenericTypeMetadataAccessFunction( IGF, params, nominal, genericArgs); - }, - isReadNone); + }, + isReadNone); return accessor; } -/// Return the type metadata access function for the given type, if it -/// is guaranteed to exist. -llvm::Constant * -irgen::getRequiredTypeMetadataAccessFunction(IRGenModule &IGM, - NominalTypeDecl *theDecl, - ForDefinition_t shouldDefine) { - if (theDecl->isGenericContext()) { - return getGenericTypeMetadataAccessFunction(IGM, theDecl, shouldDefine); - } - - CanType declaredType = theDecl->getDeclaredType()->getCanonicalType(); - return getTypeMetadataAccessFunction(IGM, declaredType, shouldDefine); -} - /// Emit a call to the type metadata accessor for the given function. static MetadataResponse -emitCallToTypeMetadataAccessFunction(IRGenFunction &IGF, - CanType type, - DynamicMetadataRequest request, - ForDefinition_t shouldDefine) { +emitCallToTypeMetadataAccessFunction(IRGenFunction &IGF, CanType type, + DynamicMetadataRequest request) { // If we already cached the metadata, use it. if (auto local = IGF.tryGetLocalTypeMetadata(type, request)) return local; llvm::Constant *accessor = - getTypeMetadataAccessFunction(IGF.IGM, type, shouldDefine); + getOrCreateTypeMetadataAccessFunction(IGF.IGM, type); llvm::CallInst *call = IGF.Builder.CreateCall(accessor, { request.get(IGF) }); call->setCallingConv(IGF.IGM.SwiftCC); call->setDoesNotAccessMemory(); @@ -1874,17 +1888,7 @@ IRGenFunction::emitTypeMetadataRef(CanType type, return emitDirectTypeMetadataRef(*this, type, request); } - switch (getTypeMetadataAccessStrategy(type)) { - case MetadataAccessStrategy::PublicUniqueAccessor: - case MetadataAccessStrategy::HiddenUniqueAccessor: - case MetadataAccessStrategy::PrivateAccessor: - return emitCallToTypeMetadataAccessFunction(*this, type, request, - NotForDefinition); - case MetadataAccessStrategy::NonUniqueAccessor: - return emitCallToTypeMetadataAccessFunction(*this, type, request, - ForDefinition); - } - llvm_unreachable("bad type metadata access strategy"); + return emitCallToTypeMetadataAccessFunction(*this, type, request); } /// Return the address of a function that will return type metadata @@ -1897,12 +1901,17 @@ llvm::Function *irgen::getOrCreateTypeMetadataAccessFunction(IRGenModule &IGM, "cannot create global function to return dependent type metadata"); switch (getTypeMetadataAccessStrategy(type)) { + case MetadataAccessStrategy::ForeignAccessor: + // Force the foreign candidate to exist. + (void) IGM.getAddrOfForeignTypeMetadataCandidate(type); + LLVM_FALLTHROUGH; case MetadataAccessStrategy::PublicUniqueAccessor: case MetadataAccessStrategy::HiddenUniqueAccessor: case MetadataAccessStrategy::PrivateAccessor: - return getTypeMetadataAccessFunction(IGM, type, NotForDefinition); + return getOtherwiseDefinedTypeMetadataAccessFunction(IGM, type); case MetadataAccessStrategy::NonUniqueAccessor: - return getTypeMetadataAccessFunction(IGM, type, ForDefinition); + return createDirectTypeMetadataAccessFunction(IGM, type, + /*allow existing*/true); } llvm_unreachable("bad type metadata access strategy"); } @@ -2117,6 +2126,23 @@ namespace { return emitFromValueWitnessTablePointer(vwtable); } + /// Given that the type is fixed-layout, emit the type layout by + /// emitting a global layout for it. + llvm::Value *emitFromFixedLayout(CanType t) { + auto layout = tryEmitFromFixedLayout(t); + assert(layout && "type must be fixed-size to call emitFromFixedLayout"); + return layout; + } + + /// If the type is fixed-layout, emit the type layout by + /// emitting a global layout for it. + llvm::Value *tryEmitFromFixedLayout(CanType t) { + auto &ti = IGF.getTypeInfo(SILType::getPrimitiveObjectType(t)); + if (auto fixedTI = dyn_cast(&ti)) + return IGF.IGM.emitFixedTypeLayout(t, *fixedTI); + return nullptr; + } + bool hasVisibleValueWitnessTable(CanType t) const { // Some builtin and structural types have value witnesses exported from // the runtime. @@ -2224,7 +2250,7 @@ namespace { } case MetatypeRepresentation::Thick: if (isa(type)) { - return emitFromTypeMetadata(type, request); + return emitFromFixedLayout(type); } // Otherwise, this is a metatype that looks like a pointer. LLVM_FALLTHROUGH; @@ -2331,6 +2357,103 @@ namespace { type->getOwnership()); return emitFromValueWitnessTable(valueWitnessType); } + + llvm::Value *visitTupleType(CanTupleType type, + DynamicMetadataRequest request) { + // Single-element tuples have exactly the same layout as their elements. + if (type->getNumElements() == 1) { + return visit(type.getElementType(0), request); + } + + // If the type is fixed-layout, use a global layout. + if (auto layout = tryEmitFromFixedLayout(type)) + return layout; + + // TODO: check for cached VWT / metadata for the type. + + // Use swift_getTupleTypeLayout to compute a layout. + + // Create a buffer to hold the result. We don't have any reasonable + // way to scope the lifetime of this. + auto resultPtr = IGF.createAlloca(IGF.IGM.FullTypeLayoutTy, + IGF.IGM.getPointerAlignment()) + .getAddress(); + + switch (type->getNumElements()) { + case 0: + case 1: + llvm_unreachable("filtered out above"); + + case 2: { + auto elt0 = visit(type.getElementType(0), request); + auto elt1 = visit(type.getElementType(1), request); + + // Ignore the offset. + auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout2Fn(), + {resultPtr, elt0, elt1}); + call->setDoesNotThrow(); + + break; + } + + case 3: { + auto elt0 = visit(type.getElementType(0), request); + auto elt1 = visit(type.getElementType(1), request); + auto elt2 = visit(type.getElementType(2), request); + + // Ignore the offsets. + auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayout3Fn(), + {resultPtr, elt0, elt1, elt2}); + call->setDoesNotThrow(); + + break; + } + + default: { + // Allocate a temporary array for the element layouts. + auto eltLayoutsArraySize = + IGF.IGM.getPointerSize() * type->getNumElements(); + auto eltLayoutsArray = + IGF.createAlloca(IGF.IGM.Int8PtrPtrTy, + IGF.IGM.getSize(Size(type->getNumElements())), + IGF.IGM.getPointerAlignment()); + IGF.Builder.CreateLifetimeStart(eltLayoutsArray, eltLayoutsArraySize); + + // Emit layouts for all the elements and store them into the array. + for (auto i : indices(type.getElementTypes())) { + auto eltLayout = visit(type.getElementType(i), request); + auto eltLayoutSlot = + i == 0 ? eltLayoutsArray + : IGF.Builder.CreateConstArrayGEP(eltLayoutsArray, i, + IGF.IGM.getPointerSize()); + IGF.Builder.CreateStore(eltLayout, eltLayoutSlot); + } + + // Ignore the offsets. + auto offsetsPtr = + llvm::ConstantPointerNull::get(IGF.IGM.Int32Ty->getPointerTo()); + + // Flags. + auto flags = TupleTypeFlags().withNumElements(type->getNumElements()); + auto flagsValue = IGF.IGM.getSize(Size(flags.getIntValue())); + + // Compute the layout. + auto call = IGF.Builder.CreateCall(IGF.IGM.getGetTupleLayoutFn(), + {resultPtr, offsetsPtr, flagsValue, + eltLayoutsArray.getAddress()}); + call->setDoesNotThrow(); + + // We're done with the buffer. + IGF.Builder.CreateLifetimeEnd(eltLayoutsArray, eltLayoutsArraySize); + + break; + } + } + + // Cast resultPtr to i8**, our general currency type for type layouts. + resultPtr = IGF.Builder.CreateBitCast(resultPtr, IGF.IGM.Int8PtrPtrTy); + return resultPtr; + } }; } // end anonymous namespace diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index ec1beba276d01..8b3dc07877a24 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -450,11 +450,30 @@ enum class MetadataAccessStrategy { /// There is a unique private accessor function for the given type metadata. PrivateAccessor, + /// The given type metadata is for a foreign type; its accessor function + /// is built as a side-effect of emitting a metadata candidate. + ForeignAccessor, + /// There is no unique accessor function for the given type metadata, but /// one should be made automatically. NonUniqueAccessor }; +/// Does the given access strategy rely on an accessor that's generated +/// on-demand and thus may be shared across object files? +static inline bool isAccessorLazilyGenerated(MetadataAccessStrategy strategy) { + switch (strategy) { + case MetadataAccessStrategy::PublicUniqueAccessor: + case MetadataAccessStrategy::HiddenUniqueAccessor: + case MetadataAccessStrategy::PrivateAccessor: + return false; + case MetadataAccessStrategy::ForeignAccessor: + case MetadataAccessStrategy::NonUniqueAccessor: + return true; + } + llvm_unreachable("bad kind"); +} + /// Is it basically trivial to access the given metadata? If so, we don't /// need a cache variable in its accessor. bool isTypeMetadataAccessTrivial(IRGenModule &IGM, CanType type); @@ -467,51 +486,87 @@ MetadataAccessStrategy getTypeMetadataAccessStrategy(CanType type); llvm::Function *getOrCreateTypeMetadataAccessFunction(IRGenModule &IGM, CanType type); -llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM, - CanType type, - ForDefinition_t shouldDefine); +/// Return the type metadata access function for the given type, given that +/// some other code will be defining it. +llvm::Function * +getOtherwiseDefinedTypeMetadataAccessFunction(IRGenModule &IGM, CanType type); + +/// Emit a type metadata access function that just directly accesses +/// the metadata. +llvm::Function * +createDirectTypeMetadataAccessFunction(IRGenModule &IGM, CanType type, + bool allowExistingDefinition); using MetadataAccessGenerator = llvm::function_ref; -llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM, - CanType type, - ForDefinition_t shouldDefine, - MetadataAccessGenerator generator); +enum class CacheStrategy { + /// No cache. + None, + /// A simple lazy cache. + Lazy, + + /// An InPlaceValueMetadataCache initialization cache. + InPlaceInitialization, +}; + +/// Emit a type metadata access function using the given generator function. +llvm::Function * +createTypeMetadataAccessFunction(IRGenModule &IGM, + CanType type, + CacheStrategy cacheStrategy, + MetadataAccessGenerator generator, + bool allowExistingDefinition = false); + +/// Either create or return a reference to a generic type metadata +/// access function. +/// +/// Note that a generic type metadata access function is a somewhat +/// different kind of thing from an ordinary type metadata access function: +/// +/// - A generic type metadata access function is associated with a type +/// object of kind `(...) -> type` --- typically, an unapplied +/// generic type (like `Dictionary`, without any type arguments). +/// It takes the concrete witnesses to the generic signature as +/// parameters and builds an appropriately instantiated type for those +/// arguments. +/// +/// - An ordinary type metadata access function is associated with +/// a type object of kind `type` --- which is to say, an ordinary type +/// like `Float` or `Dictionary`. There may be ordinary +/// access functions for various specializations of generic types; +/// these will be created on demand. +/// +/// The definitions of generic type metadata access functions currently +/// always follow the same pattern, so we don't need to take a closure to +/// define the body. llvm::Function * getGenericTypeMetadataAccessFunction(IRGenModule &IGM, NominalTypeDecl *nominal, ForDefinition_t shouldDefine); -llvm::Constant * -getRequiredTypeMetadataAccessFunction(IRGenModule &IGM, - NominalTypeDecl *theDecl, - ForDefinition_t shouldDefine); - -using InPlaceMetadataInitializer = +using OnceMetadataInitializer = llvm::function_ref; MetadataResponse -emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, - CanNominalType type, - llvm::Constant *cacheVariable, - InPlaceMetadataInitializer &&initializer); - -llvm::Value *uniqueForeignTypeMetadataRef(IRGenFunction &IGF, - llvm::Value *candidate); +emitOnceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, + CanNominalType type, + llvm::Constant *cacheVariable, + OnceMetadataInitializer initializer); -using LazyCacheEmitter = +using CacheEmitter = llvm::function_ref; /// Emit the body of a lazy cache access function. -void emitLazyCacheAccessFunction(IRGenModule &IGM, - llvm::Function *accessor, - llvm::GlobalVariable *cache, - LazyCacheEmitter getValue, - bool isReadNone = true); +void emitCacheAccessFunction(IRGenModule &IGM, + llvm::Function *accessor, + llvm::Constant *cache, + CacheStrategy cacheStrategy, + CacheEmitter getValue, + bool isReadNone = true); /// Emit a declaration reference to a metatype object. void emitMetatypeRef(IRGenFunction &IGF, CanMetatypeType type, diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 05d43d88b52db..55ba5647bfba0 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -83,11 +83,19 @@ static void installGenericArguments(Metadata *metadata, generics.Base.getNumArguments() * sizeof(void*)); } +#if SWIFT_OBJC_INTEROP +static ClassMetadataBounds computeMetadataBoundsForObjCClass(Class cls) { + cls = swift_getInitializedObjCClass(cls); + auto metadata = reinterpret_cast(cls); + return metadata->getClassBoundsAsSwiftSuperclass(); +} +#endif + static ClassMetadataBounds computeMetadataBoundsForSuperclass(const void *ref, - TypeMetadataRecordKind refKind) { + TypeReferenceKind refKind) { switch (refKind) { - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: { + case TypeReferenceKind::IndirectNominalTypeDescriptor: { auto description = *reinterpret_cast(ref); if (!description) { swift::fatalError(0, "instantiating class metadata for class with " @@ -96,25 +104,28 @@ computeMetadataBoundsForSuperclass(const void *ref, return description->getMetadataBounds(); } - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: { + case TypeReferenceKind::DirectNominalTypeDescriptor: { auto description = reinterpret_cast(ref); return description->getMetadataBounds(); } - case TypeMetadataRecordKind::IndirectObjCClass: + case TypeReferenceKind::DirectObjCClassName: { #if SWIFT_OBJC_INTEROP - { - auto cls = *reinterpret_cast(ref); - cls = swift_getInitializedObjCClass(cls); - auto metadata = reinterpret_cast(cls); - return metadata->getClassBoundsAsSwiftSuperclass(); - } + auto cls = objc_lookUpClass(reinterpret_cast(ref)); + return computeMetadataBoundsForObjCClass(cls); #else - // fallthrough + break; #endif + } - case TypeMetadataRecordKind::Reserved: + case TypeReferenceKind::IndirectObjCClass: { +#if SWIFT_OBJC_INTEROP + auto cls = *reinterpret_cast(ref); + return computeMetadataBoundsForObjCClass(cls); +#else break; +#endif + } } swift_runtime_unreachable("unsupported superclass reference kind"); } @@ -175,6 +186,14 @@ areAllTransitiveMetadataComplete_cheap(const Metadata *metadata); static MetadataDependency checkTransitiveCompleteness(const Metadata *metadata); +static PrivateMetadataState inferStateForMetadata(Metadata *metadata) { + if (metadata->getValueWitnesses()->isIncomplete()) + return PrivateMetadataState::Abstract; + + // TODO: internal vs. external layout-complete? + return PrivateMetadataState::LayoutComplete; +} + namespace { struct GenericCacheEntry final : VariadicMetadataCacheEntryBase { @@ -212,21 +231,6 @@ namespace { return { metadata, state }; } - PrivateMetadataState inferStateForMetadata(Metadata *metadata) { - if (metadata->getValueWitnesses()->isIncomplete()) - return PrivateMetadataState::Abstract; - - // TODO: internal vs. external layout-complete? - return PrivateMetadataState::LayoutComplete; - } - - static const TypeContextDescriptor *getDescription(Metadata *type) { - if (auto classType = dyn_cast(type)) - return classType->getDescription(); - else - return cast(type)->getDescription(); - } - TryInitializeResult tryInitialize(Metadata *metadata, PrivateMetadataState state, PrivateMetadataCompletionContext *context) { @@ -235,7 +239,8 @@ namespace { // Finish the completion function. if (state < PrivateMetadataState::NonTransitiveComplete) { // Find a pattern. Currently we always use the default pattern. - auto &generics = getDescription(metadata)->getFullGenericContextHeader(); + auto &generics = metadata->getTypeContextDescriptor() + ->getFullGenericContextHeader(); auto pattern = generics.DefaultInstantiationPattern.get(); // Complete the metadata's instantiation. @@ -533,6 +538,165 @@ swift::swift_getGenericMetadata(MetadataRequest request, return result.second; } +/***************************************************************************/ +/*** In-place metadata initialization **************************************/ +/***************************************************************************/ + +namespace { + /// A cache entry for "in-place" metadata initializations. + class InPlaceMetadataCacheEntry final + : public MetadataCacheEntryBase { + ValueType Value = nullptr; + + friend MetadataCacheEntryBase; + ValueType getValue() { + return Value; + } + void setValue(ValueType value) { + Value = value; + } + + public: + // We have to give MetadataCacheEntryBase a non-empty list of trailing + // objects or else it gets annoyed. + static size_t numTrailingObjects(OverloadToken) { return 0; } + + static const char *getName() { return "InPlaceMetadataCache"; } + + InPlaceMetadataCacheEntry() {} + + AllocationResult allocate(const TypeContextDescriptor *description) { + auto valueTypeDescriptor = cast(description); + auto &initialization = + valueTypeDescriptor->getInPlaceMetadataInitialization(); + + auto metadata = initialization.IncompleteMetadata.get(); + + auto state = inferStateForMetadata(metadata); + return { metadata, state }; + } + + TryInitializeResult tryInitialize(Metadata *metadata, + PrivateMetadataState state, + PrivateMetadataCompletionContext *context) { + assert(state != PrivateMetadataState::Complete); + + // Finish the completion function. + if (state < PrivateMetadataState::NonTransitiveComplete) { + // Find a pattern. Currently we always use the default pattern. + auto &initialization = + cast(metadata)->getDescription() + ->getInPlaceMetadataInitialization(); + + // Complete the metadata's instantiation. + auto dependency = + initialization.CompletionFunction(metadata, &context->Public, + /*pattern*/ nullptr); + + // If this failed with a dependency, infer the current metadata state + // and return. + if (dependency) { + return { inferStateForMetadata(metadata), dependency }; + } + } + + // Check for transitive completeness. + if (auto dependency = checkTransitiveCompleteness(metadata)) { + return { PrivateMetadataState::NonTransitiveComplete, dependency }; + } + + // We're done. + publishCompleteMetadata(metadata); + return { PrivateMetadataState::Complete, MetadataDependency() }; + } + + void publishCompleteMetadata(Metadata *metadata) { + auto &init = cast(metadata)->getDescription() + ->getInPlaceMetadataInitialization(); + auto &cache = *init.InitializationCache.get(); + cache.Metadata.store(metadata, std::memory_order_release); + } + }; + + /// An implementation of LockingConcurrentMapStorage that's more + /// appropriate for the in-place metadata cache. + /// + /// TODO: delete the cache entry when initialization is complete. + class InPlaceMetadataCacheStorage { + ConcurrencyControl Concurrency; + + public: + using KeyType = const TypeContextDescriptor *; + using EntryType = InPlaceMetadataCacheEntry; + + ConcurrencyControl &getConcurrency() { return Concurrency; } + + template + std::pair + getOrInsert(KeyType key, ArgTys &&...args) { + auto &init = + cast(key)->getInPlaceMetadataInitialization(); + auto &cache = *init.InitializationCache.get(); + + // Check for an existing entry. + auto existingEntry = cache.Private.load(std::memory_order_acquire); + + // If there isn't one there, optimistically create an entry and + // try to swap it in. + if (!existingEntry) { + auto allocatedEntry = new InPlaceMetadataCacheEntry(); + if (cache.Private.compare_exchange_strong(existingEntry, + allocatedEntry, + std::memory_order_acq_rel, + std::memory_order_acquire)) { + // If that succeeded, return the entry we allocated and tell the + // caller we allocated it. + return { allocatedEntry, true }; + } + + // Otherwise, use the new entry and destroy the one we allocated. + assert(existingEntry && "spurious failure of strong compare-exchange?"); + delete allocatedEntry; + } + + return { static_cast(existingEntry), false }; + } + + EntryType *find(KeyType key) { + auto &init = + cast(key)->getInPlaceMetadataInitialization(); + + return static_cast( + init.InitializationCache->Private.load(std::memory_order_acquire)); + } + + /// A default implementation for resolveEntry that assumes that the + /// key type is a lookup key for the map. + EntryType *resolveExistingEntry(KeyType key) { + auto entry = find(key); + assert(entry && "entry doesn't already exist!"); + return entry; + } + }; + + class InPlaceMetadataCache + : public LockingConcurrentMap { + }; +} // end anonymous namespace + +/// The cache of all in-place metadata initializations. +static Lazy InPlaceMetadata; + +MetadataResponse +swift::swift_getInPlaceMetadata(MetadataRequest request, + const TypeContextDescriptor *description) { + auto result = InPlaceMetadata.get().getOrInsert(description, request, + description); + + return result.second; +} + /***************************************************************************/ /*** Objective-C class wrappers ********************************************/ /***************************************************************************/ @@ -875,7 +1039,8 @@ class TupleCacheEntry } }; -class TupleCache : public MetadataCache { +class TupleCacheStorage : + public LockingConcurrentMapStorage { public: // FIXME: https://bugs.swift.org/browse/SR-1155 #pragma clang diagnostic push @@ -892,6 +1057,10 @@ class TupleCache : public MetadataCache { #pragma clang diagnostic pop }; +class TupleCache : + public LockingConcurrentMap { +}; + } // end anonymous namespace /// The uniquing structure for tuple type metadata. @@ -1209,6 +1378,43 @@ static void performBasicLayout(TypeLayout &layout, layout.stride = std::max(size_t(1), roundUpToAlignMask(size, alignMask)); } + +size_t swift::swift_getTupleTypeLayout2(TypeLayout *result, + const TypeLayout *elt0, + const TypeLayout *elt1) { + const TypeLayout *elts[] = { elt0, elt1 }; + uint32_t offsets[2]; + swift_getTupleTypeLayout(result, offsets, + TupleTypeFlags().withNumElements(2), elts); + assert(offsets[0] == 0); + return offsets[1]; +} + +OffsetPair swift::swift_getTupleTypeLayout3(TypeLayout *result, + const TypeLayout *elt0, + const TypeLayout *elt1, + const TypeLayout *elt2) { + const TypeLayout *elts[] = { elt0, elt1, elt2 }; + uint32_t offsets[3]; + swift_getTupleTypeLayout(result, offsets, + TupleTypeFlags().withNumElements(3), elts); + assert(offsets[0] == 0); + return {offsets[1], offsets[2]}; +} + +void swift::swift_getTupleTypeLayout(TypeLayout *result, + uint32_t *elementOffsets, + TupleTypeFlags flags, + const TypeLayout * const *elements) { + *result = TypeLayout(); + performBasicLayout(*result, elements, flags.getNumElements(), + [](const TypeLayout *elt) { return elt; }, + [elementOffsets](size_t i, const TypeLayout *elt, size_t offset) { + if (elementOffsets) + elementOffsets[i] = uint32_t(offset); + }); +} + MetadataResponse swift::swift_getTupleTypeMetadata(MetadataRequest request, TupleTypeFlags flags, @@ -1269,7 +1475,7 @@ TupleCacheEntry::TupleCacheEntry(const Key &key, MetadataRequest request, for (size_t i = 0, e = key.NumElements; i != e; ++i) Data.getElement(i).Type = key.Elements[i]; - assert(TupleCache::resolveExistingEntry(&Data) == this); + assert(TupleCacheStorage::resolveExistingEntry(&Data) == this); } TupleCacheEntry::AllocationResult @@ -1432,6 +1638,37 @@ swift::swift_getTupleTypeMetadata3(MetadataRequest request, /***************************************************************************/ /*** Nominal type descriptors **********************************************/ /***************************************************************************/ + +namespace { + /// A class encapsulating everything interesting about the identity of + /// a type context *except* the identity of the parent context. + class TypeContextIdentity { + StringRef Name; + public: + explicit TypeContextIdentity(const TypeContextDescriptor *type) { + // Use the name of the type context. + Name = type->Name.get(); + + // If this is a synthesized entity, include the related entity tag. + if (type->isSynthesizedRelatedEntity()) { + auto tag = type->getSynthesizedDeclRelatedEntityTag(); + assert(Name.end() + 1 == tag.begin()); + + // The length computation needs to include the \0 at the + // end of the name. + Name = StringRef(Name.begin(), Name.size() + tag.size() + 1); + } + } + + bool operator==(const TypeContextIdentity &other) const { + return Name == other.Name; + } + int compare(const TypeContextIdentity &other) const { + return Name.compare(other.Name); + } + }; +} + bool swift::equalContexts(const ContextDescriptor *a, const ContextDescriptor *b) { @@ -1472,20 +1709,7 @@ bool swift::equalContexts(const ContextDescriptor *a, && kind <= ContextDescriptorKind::Type_Last) { auto typeA = cast(a); auto typeB = cast(b); - if (strcmp(typeA->Name.get(), typeB->Name.get()) != 0) - return false; - - // A synthesized entity has to match the related entity tag too. - if (typeA->isSynthesizedRelatedEntity()) { - if (!typeB->isSynthesizedRelatedEntity()) - return false; - - if (typeA->getSynthesizedDeclRelatedEntityTag() - != typeB->getSynthesizedDeclRelatedEntityTag()) - return false; - } - - return true; + return TypeContextIdentity(typeA) == TypeContextIdentity(typeB); } // Otherwise, this runtime doesn't know anything about this context kind. @@ -2933,147 +3157,122 @@ OpaqueValue *swift::swift_assignExistentialWithCopy(OpaqueValue *dest, /*** Foreign types *********************************************************/ /***************************************************************************/ +// We use a DenseMap over what are essentially StringRefs instead of a +// StringMap because we don't need to actually copy the string. namespace { - /// A reference to a context descriptor, used as a uniquing key. - struct ContextDescriptorKey { - const TypeContextDescriptor *Data; - }; -} // end anonymous namespace -template <> -struct llvm::DenseMapInfo { - static ContextDescriptorKey getEmptyKey() { - return ContextDescriptorKey{(const TypeContextDescriptor*) 0}; +static const TypeContextDescriptor * +getForeignTypeDescription(Metadata *metadata) { + if (auto foreignClass = dyn_cast(metadata)) + return foreignClass->getDescription(); + return cast(metadata)->getDescription(); +} + +class ForeignMetadataCacheEntry + : public MetadataCacheEntryBase { + + Metadata *Value; + + friend MetadataCacheEntryBase; + ValueType getValue() { + return Value; } - static ContextDescriptorKey getTombstoneKey() { - return ContextDescriptorKey{(const TypeContextDescriptor*) 1}; + void setValue(ValueType value) { + swift_runtime_unreachable("should never be called"); } - static unsigned getHashValue(ContextDescriptorKey val) { - if ((uintptr_t)val.Data <= 1) { - return llvm::hash_value(val.Data); - } - // Hash by name. - // In full generality, we'd get a better hash by walking up the entire - // descriptor tree and hashing names all along the way, and we'd be faster - // if we special cased unique keys by hashing pointers. In practice, this - // is only used to unique foreign metadata records, which only ever appear - // in the "C" or "ObjC" special context, and are never unique. - - // llvm::hash_value(StringRef) is, unfortunately, defined out of - // line in a library we otherwise would not need to link against. - StringRef name(val.Data->Name.get()); - return llvm::hash_combine_range(name.begin(), name.end()); - } - static bool isEqual(ContextDescriptorKey lhs, ContextDescriptorKey rhs) { - if ((uintptr_t)lhs.Data <= 1 || (uintptr_t)rhs.Data <= 1) { - return lhs.Data == rhs.Data; +public: + static const char *getName() { return "ForeignMetadataCache"; } + + template + ForeignMetadataCacheEntry(const TypeContextDescriptor *description, + MetadataRequest request, Metadata *candidate) + : Value(candidate) { + // Remember that the metadata is still just a candidate until this + // is actually successfully installed in the concurrent map. + + auto &init = description->getForeignMetadataInitialization(); + + PrivateMetadataState state; + if (!init.CompletionFunction) { + if (areAllTransitiveMetadataComplete_cheap(candidate)) { + state = PrivateMetadataState::Complete; + } else { + state = PrivateMetadataState::NonTransitiveComplete; + } + } else { + state = inferStateForMetadata(candidate); } - return equalContexts(lhs.Data, rhs.Data); + + flagAllocatedDuringConstruction(state); } -}; -// We use a DenseMap over what are essentially StringRefs instead of a -// StringMap because we don't need to actually copy the string. -namespace { -struct ForeignTypeState { - Mutex Lock; - ConditionVariable InitializationWaiters; - llvm::DenseMap Types; -}; -} // end anonymous namespace + enum : bool { MayFlagAllocatedDuringConstruction = true }; -static Lazy ForeignTypes; - -const ForeignTypeMetadata * -swift::swift_getForeignTypeMetadata(ForeignTypeMetadata *nonUnique) { - // Fast path: check the invasive cache. - auto cache = nonUnique->getCacheValue(); - if (cache.isInitialized()) { - return cache.getCachedUniqueMetadata(); - } - - // Okay, check the global map. - auto &foreignTypes = ForeignTypes.get(); - ContextDescriptorKey key{nonUnique->getTypeContextDescriptor()}; - assert(key.Data - && "all foreign metadata should have a type context descriptor"); - bool hasInit = cache.hasInitializationFunction(); - - const ForeignTypeMetadata *uniqueMetadata; - bool inserted; - - // A helper function to find the current entry for the key using the - // saved iterator if it's still valid. This should only be called - // while the lock is held. - decltype(foreignTypes.Types.begin()) savedIterator; - size_t savedSize = 0; - auto getCurrentEntry = [&]() -> const ForeignTypeMetadata *& { - // The iterator may have been invalidated if the size of the map - // has changed since the last lookup. - if (foreignTypes.Types.size() != savedSize) { - savedSize = foreignTypes.Types.size(); - savedIterator = foreignTypes.Types.find(key); - assert(savedIterator != foreignTypes.Types.end() && - "entries cannot be removed from foreign types metadata map"); - } - return savedIterator->second; - }; + const TypeContextDescriptor *getDescription() const { + return getForeignTypeDescription(Value); + } - { - ScopedLock guard(foreignTypes.Lock); - - // Try to create an entry in the map. The initial value of the entry - // is our copy of the metadata unless it has an initialization function, - // in which case we have to insert null as a placeholder to tell others - // to wait while we call the initializer. - auto valueToInsert = (hasInit ? nullptr : nonUnique); - auto insertResult = foreignTypes.Types.insert({key, valueToInsert}); - inserted = insertResult.second; - savedIterator = insertResult.first; - savedSize = foreignTypes.Types.size(); - uniqueMetadata = savedIterator->second; - - // If we created the entry, then the unique metadata is our copy. - if (inserted) { - uniqueMetadata = nonUnique; - - // If we didn't create the entry, but it's null, then we have to wait - // until it becomes non-null. - } else { - while (uniqueMetadata == nullptr) { - foreignTypes.Lock.wait(foreignTypes.InitializationWaiters); - uniqueMetadata = getCurrentEntry(); - } - } + template + static size_t numTrailingObjects(OverloadToken, Args &&...) { + return 0; } - // If we inserted the entry and there's an initialization function, - // call it. This has to be done with the lock dropped. - if (inserted && hasInit) { - nonUnique->getInitializationFunction()(nonUnique); + intptr_t getKeyIntValueForDump() const { + return reinterpret_cast(getDescription()->Name.get()); + } - // Update the cache entry: + int compareWithKey(const TypeContextDescriptor *key) const { + // We can just compare unparented type-context identities because + // we assume that foreign types don't have interesting parenting + // structure. + return TypeContextIdentity(key) + .compare(TypeContextIdentity(getDescription())); + } - // - Reacquire the lock. - ScopedLock guard(foreignTypes.Lock); + AllocationResult allocate(Metadata *candidate) { + swift_runtime_unreachable( + "always flags allocation complete during construction"); + } - // - Change the entry. - auto &entry = getCurrentEntry(); - assert(entry == nullptr); - entry = nonUnique; + TryInitializeResult tryInitialize(Metadata *metadata, + PrivateMetadataState state, + PrivateMetadataCompletionContext *ctxt) { + assert(state != PrivateMetadataState::Complete); + + // Finish the completion function. + auto &init = getDescription()->getForeignMetadataInitialization(); + if (init.CompletionFunction) { + // Try to complete the metadata's instantiation. + auto dependency = + init.CompletionFunction(metadata, &ctxt->Public, nullptr); + + // If this failed with a dependency, infer the current metadata state + // and return. + if (dependency) { + return { inferStateForMetadata(metadata), dependency }; + } + } - // - Notify waiters. - foreignTypes.InitializationWaiters.notifyAll(); + // Check for transitive completeness. + if (auto dependency = checkTransitiveCompleteness(metadata)) { + return { PrivateMetadataState::NonTransitiveComplete, dependency }; + } + + // We're done. + return { PrivateMetadataState::Complete, MetadataDependency() }; } +}; + +} // end anonymous namespace - // Remember the unique result in the invasive cache. We don't want - // to do this until after the initialization completes; otherwise, - // it will be possible for code to fast-path through this function - // too soon. - nonUnique->setCachedUniqueMetadata(uniqueMetadata); +static Lazy> ForeignMetadata; - return uniqueMetadata; +MetadataResponse +swift::swift_getForeignTypeMetadata(MetadataRequest request, + ForeignTypeMetadata *candidate) { + auto description = getForeignTypeDescription(candidate); + return ForeignMetadata->getOrInsert(description, request, candidate).second; } /// Unique-ing of foreign types' witness tables. @@ -3297,7 +3496,7 @@ class WitnessTableCacheEntry : } // end anonymous namespace using GenericWitnessTableCache = - LockingConcurrentMap; + MetadataCache; using LazyGenericWitnessTableCache = Lazy; /// Fetch the cache for a generic witness-table structure. @@ -3464,12 +3663,25 @@ static Result performOnMetadataCache(const Metadata *metadata, if (tupleMetadata->NumElements == 0) return std::move(callbacks).forOtherMetadata(tupleMetadata); return std::move(callbacks).forTupleMetadata(tupleMetadata); + } else if (auto foreignClass = dyn_cast(metadata)) { + return std::move(callbacks).forForeignMetadata(foreignClass, + foreignClass->getDescription()); } else { return std::move(callbacks).forOtherMetadata(metadata); } if (!description->isGeneric()) { - return std::move(callbacks).forOtherMetadata(metadata); + switch (description->getMetadataInitialization()) { + case TypeContextDescriptorFlags::NoMetadataInitialization: + return std::move(callbacks).forOtherMetadata(metadata); + + case TypeContextDescriptorFlags::ForeignMetadataInitialization: + return std::move(callbacks).forForeignMetadata(metadata, description); + + case TypeContextDescriptorFlags::InPlaceMetadataInitialization: + return std::move(callbacks).forInPlaceMetadata(description); + } + swift_runtime_unreachable("bad metadata initialization kind"); } auto &generics = description->getFullGenericContextHeader(); @@ -3497,6 +3709,15 @@ bool swift::addToMetadataQueue(MetadataCompletionQueueEntry *queueEntry, return cache.enqueue(key, QueueEntry, Dependency); } + bool forForeignMetadata(const Metadata *metadata, + const TypeContextDescriptor *description) { + return ForeignMetadata.get().enqueue(description, QueueEntry, Dependency); + } + + bool forInPlaceMetadata(const TypeContextDescriptor *description) && { + return InPlaceMetadata.get().enqueue(description, QueueEntry, Dependency); + } + bool forTupleMetadata(const TupleTypeMetadata *metadata) { return TupleTypes.get().enqueue(metadata, QueueEntry, Dependency); } @@ -3520,6 +3741,15 @@ void swift::resumeMetadataCompletion(MetadataCompletionQueueEntry *queueEntry) { cache.resumeInitialization(key, QueueEntry); } + void forForeignMetadata(const Metadata *metadata, + const TypeContextDescriptor *description) { + ForeignMetadata.get().resumeInitialization(description, QueueEntry); + } + + void forInPlaceMetadata(const TypeContextDescriptor *description) && { + InPlaceMetadata.get().resumeInitialization(description, QueueEntry); + } + void forTupleMetadata(const TupleTypeMetadata *metadata) { TupleTypes.get().resumeInitialization(metadata, QueueEntry); } @@ -3546,6 +3776,16 @@ MetadataResponse swift::swift_checkMetadataState(MetadataRequest request, return cache.await(key, Request); } + MetadataResponse forForeignMetadata(const Metadata *metadata, + const TypeContextDescriptor *description) { + return ForeignMetadata.get().await(description, Request); + } + + MetadataResponse forInPlaceMetadata( + const TypeContextDescriptor *description) && { + return InPlaceMetadata.get().await(description, Request); + } + MetadataResponse forTupleMetadata(const TupleTypeMetadata *metadata) { return TupleTypes.get().await(metadata, Request); } @@ -3586,6 +3826,14 @@ static bool findAnyTransitiveMetadata(const Metadata *type, T &&predicate) { return false; + // Foreign classes require their superclass to be transitively complete. + } else if (auto foreignClassType = dyn_cast(type)) { + if (auto super = foreignClassType->Superclass) { + if (predicate(super)) + return true; + } + return false; + // Other types do not have transitive completeness requirements. } else { return false; @@ -3617,7 +3865,7 @@ static bool findAnyTransitiveMetadata(const Metadata *type, T &&predicate) { /// Do a quick check to see if all the transitive type metadata are complete. static bool areAllTransitiveMetadataComplete_cheap(const Metadata *type) { - // Look for any transitive metadata that's incomplete. + // Look for any transitive metadata that's *incomplete*. return !findAnyTransitiveMetadata(type, [](const Metadata *type) { struct IsIncompleteCallbacks { bool forGenericMetadata(const Metadata *type, @@ -3629,6 +3877,22 @@ areAllTransitiveMetadataComplete_cheap(const Metadata *type) { return true; } + bool forForeignMetadata(const Metadata *metadata, + const TypeContextDescriptor *description) { + // If the type doesn't have a completion function, we can assume + // it's transitively complete by construction. + if (!description->getForeignMetadataInitialization().CompletionFunction) + return false; + + // TODO: it might be worth doing a quick check against the cache here. + return false; + } + + bool forInPlaceMetadata(const TypeContextDescriptor *description) && { + // TODO: this could be cheap enough. + return true; + } + bool forTupleMetadata(const TupleTypeMetadata *metadata) { // TODO: this could be cheap enough. return true; @@ -3786,6 +4050,16 @@ checkMetadataDependency(MetadataDependency dependency) { return cache.checkDependency(key, Requirement); } + MetadataDependency forForeignMetadata(const Metadata *metadata, + const TypeContextDescriptor *description) { + return ForeignMetadata.get().checkDependency(description, Requirement); + } + + MetadataDependency forInPlaceMetadata( + const TypeContextDescriptor *description) && { + return InPlaceMetadata.get().checkDependency(description, Requirement); + } + MetadataDependency forTupleMetadata(const TupleTypeMetadata *metadata) { return TupleTypes.get().checkDependency(metadata, Requirement); } diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index 1f764ab7fbfa6..e87d4d996ba1e 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -83,6 +83,41 @@ enum class ConcurrencyRequest { struct ConcurrencyControl { Mutex Lock; ConditionVariable Queue; + + ConcurrencyControl() = default; +}; + +template +class LockingConcurrentMapStorage { + ConcurrentMap Map; + StaticOwningPointer Concurrency; + +public: + LockingConcurrentMapStorage() : Concurrency(new ConcurrencyControl()) {} + + MetadataAllocator &getAllocator() { return Map.getAllocator(); } + + ConcurrencyControl &getConcurrency() { return *Concurrency; } + + template + std::pair + getOrInsert(KeyType key, ArgTys &&...args) { + return Map.getOrInsert(key, args...); + } + + template + EntryType *find(KeyType key) { + return Map.find(key); + } + + /// A default implementation for resolveEntry that assumes that the + /// key type is a lookup key for the map. + template + EntryType *resolveExistingEntry(KeyType key) { + auto entry = Map.find(key); + assert(entry && "entry doesn't already exist!"); + return entry; + } }; /// A map for which there is a phase of initialization that is guaranteed @@ -122,38 +157,28 @@ struct ConcurrencyControl { /// /// implemented if checkDependency is called on the map. /// MetadataDependency checkDependency(ConcurrencyControl &concurrency, /// ArgTys...); -template +template > class LockingConcurrentMap { - ConcurrentMap Map; - - StaticOwningPointer Concurrency; - -protected: - using Impl = - typename std::conditional::value, - LockingConcurrentMap, - OptImpl>::type; - Impl &asImpl() { return static_cast(*this); } - + StorageType Storage; using Status = typename EntryType::Status; public: - LockingConcurrentMap() : Concurrency(new ConcurrencyControl()) {} + LockingConcurrentMap() = default; - MetadataAllocator &getAllocator() { return Map.getAllocator(); } + MetadataAllocator &getAllocator() { return Storage.getAllocator(); } template std::pair getOrInsert(KeyType key, ArgTys &&...args) { - auto result = Map.getOrInsert(key, args...); + auto result = Storage.getOrInsert(key, args...); auto entry = result.first; // If we are not inserting the entry, we need to potentially block on // currently satisfies our conditions. if (!result.second) { auto status = - entry->await(*Concurrency, std::forward(args)...); + entry->await(Storage.getConcurrency(), std::forward(args)...); return { entry, status }; } @@ -162,66 +187,63 @@ class LockingConcurrentMap { // Allocation. This can fast-path and bypass initialization by returning // a status. - if (auto status = entry->beginAllocation(*Concurrency, args...)) { + if (auto status = + entry->beginAllocation(Storage.getConcurrency(), args...)) { return { entry, *status }; } // Initialization. - auto status = entry->beginInitialization(*Concurrency, + auto status = entry->beginInitialization(Storage.getConcurrency(), std::forward(args)...); return { entry, status }; } template EntryType *find(KeyType key) { - return Map.find(key); + return Storage.find(key); } template std::pair resumeInitialization(KeyType key, ArgTys &&...args) { - EntryType *entry = asImpl().resolveExistingEntry(key); + EntryType *entry = Storage.resolveExistingEntry(key); auto status = - entry->resumeInitialization(*Concurrency, std::forward(args)...); + entry->resumeInitialization(Storage.getConcurrency(), + std::forward(args)...); return { entry, status }; } template bool enqueue(KeyType key, ArgTys &&...args) { - EntryType *entry = asImpl().resolveExistingEntry(key); - return entry->enqueue(*Concurrency, std::forward(args)...); + EntryType *entry = Storage.resolveExistingEntry(key); + return entry->enqueue(Storage.getConcurrency(), + std::forward(args)...); } /// Given that an entry already exists, await it. template Status await(KeyType key, ArgTys &&...args) { - EntryType *entry = asImpl().resolveExistingEntry(key); - return entry->await(*Concurrency, std::forward(args)...); + EntryType *entry = Storage.resolveExistingEntry(key); + return entry->await(Storage.getConcurrency(), + std::forward(args)...); } /// If an entry already exists, await it; otherwise report failure. template Optional tryAwaitExisting(KeyType key, ArgTys &&...args) { - EntryType *entry = Map.find(key); + EntryType *entry = Storage.find(key); if (!entry) return None; - return entry->await(*Concurrency, std::forward(args)...); + return entry->await(Storage.getConcurrency(), + std::forward(args)...); } /// Given that an entry already exists, check whether it has an active /// dependency. template MetadataDependency checkDependency(KeyType key, ArgTys &&...args) { - EntryType *entry = asImpl().resolveExistingEntry(key); - return entry->checkDependency(*Concurrency, std::forward(args)...); - } - - /// A default implementation for resolveEntry that assumes that the - /// key type is a lookup key for the map. - template - EntryType *resolveExistingEntry(KeyType key) { - auto entry = Map.find(key); - assert(entry && "entry doesn't already exist!"); - return entry; + EntryType *entry = Storage.resolveExistingEntry(key); + return entry->checkDependency(Storage.getConcurrency(), + std::forward(args)...); } }; @@ -765,6 +787,31 @@ class MetadataCacheEntryBase Optional beginAllocation(ConcurrencyControl &concurrency, MetadataRequest request, Args &&...args) { + // Returning a non-None value here will preempt initialization, so we + // should only do it if we're reached PrivateMetadataState::Complete. + + // Fast-track out if flagAllocatedDuringConstruction was called. + if (Impl::MayFlagAllocatedDuringConstruction) { + // This can be a relaxed load because beginAllocation is called on the + // same thread that called the constructor. + auto trackingInfo = + PrivateMetadataTrackingInfo( + TrackingInfo.load(std::memory_order_relaxed)); + + // If we've already allocated metadata, we can skip the rest of + // allocation. + if (trackingInfo.hasAllocatedMetadata()) { + // Skip initialization, too, if we're fully complete. + if (trackingInfo.isComplete()) { + return Status{asImpl().getValue(), MetadataState::Complete}; + + // Otherwise go directly to the initialization phase. + } else { + return None; + } + } + } + // Allocate the metadata. AllocationResult allocationResult = asImpl().allocate(std::forward(args)...); @@ -782,6 +829,23 @@ class MetadataCacheEntryBase return None; } + enum : bool { MayFlagAllocatedDuringConstruction = false }; + + /// As an alternative to allocate(), flag that allocation was + /// completed within the entry's constructor. This should only be + /// called from within the constructor. + /// + /// If this is called, allocate() will not be called. + /// + /// If this is called, the subclass must define + /// enum { MayFlagAllocatedDuringConstruction = true }; + void flagAllocatedDuringConstruction(PrivateMetadataState state) { + assert(Impl::MayFlagAllocatedDuringConstruction); + assert(state != PrivateMetadataState::Allocating); + TrackingInfo.store(PrivateMetadataTrackingInfo(state).getRawValue(), + std::memory_order_relaxed); + } + /// Begin initialization immediately after allocation. template Status beginInitialization(ConcurrencyControl &concurrency, @@ -1255,9 +1319,10 @@ class VariadicMetadataCacheEntryBase : } }; -template +template class MetadataCache : - public LockingConcurrentMap { + public LockingConcurrentMap> { }; } // namespace swift diff --git a/stdlib/public/runtime/ProtocolConformance.cpp b/stdlib/public/runtime/ProtocolConformance.cpp index 0877082c27b5f..c1203d46f5829 100644 --- a/stdlib/public/runtime/ProtocolConformance.cpp +++ b/stdlib/public/runtime/ProtocolConformance.cpp @@ -84,19 +84,19 @@ template<> void ProtocolConformanceDescriptor::dump() const { }; switch (auto kind = getTypeKind()) { - case TypeMetadataRecordKind::Reserved: - printf("unknown (reserved)"); - break; + case TypeReferenceKind::DirectObjCClassName: + printf("direct Objective-C class name %s", getDirectObjCClassName()); + break; - case TypeMetadataRecordKind::IndirectObjCClass: - printf("indirect Objective-C class %s", - class_getName(*getIndirectObjCClass())); - break; - - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: - printf("unique nominal type descriptor %s", symbolName(getTypeContextDescriptor())); - break; + case TypeReferenceKind::IndirectObjCClass: + printf("indirect Objective-C class %s", + class_getName(*getIndirectObjCClass())); + break; + + case TypeReferenceKind::DirectNominalTypeDescriptor: + case TypeReferenceKind::IndirectNominalTypeDescriptor: + printf("unique nominal type descriptor %s", symbolName(getTypeContextDescriptor())); + break; } printf(" => "); @@ -120,8 +120,8 @@ template<> void ProtocolConformanceDescriptor::dump() const { #ifndef NDEBUG template<> void ProtocolConformanceDescriptor::verify() const { auto typeKind = unsigned(getTypeKind()); - assert(((unsigned(TypeMetadataRecordKind::First_Kind) <= typeKind) && - (unsigned(TypeMetadataRecordKind::Last_Kind) >= typeKind)) && + assert(((unsigned(TypeReferenceKind::First_Kind) <= typeKind) && + (unsigned(TypeReferenceKind::Last_Kind) >= typeKind)) && "Corrupted type metadata record kind"); auto confKind = unsigned(getConformanceKind()); @@ -132,6 +132,26 @@ template<> void ProtocolConformanceDescriptor::verify() const { } #endif +#if SWIFT_OBJC_INTEROP +template <> +const ClassMetadata *TypeReference::getObjCClass(TypeReferenceKind kind) const { + switch (kind) { + case TypeReferenceKind::IndirectObjCClass: + return *getIndirectObjCClass(kind); + + case TypeReferenceKind::DirectObjCClassName: + return reinterpret_cast( + objc_lookUpClass(getDirectObjCClassName(kind))); + + case TypeReferenceKind::DirectNominalTypeDescriptor: + case TypeReferenceKind::IndirectNominalTypeDescriptor: + return nullptr; + } + + swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); +} +#endif + /// Take the type reference inside a protocol conformance record and fetch the /// canonical metadata pointer for the type it refers to. /// Returns nil for universal or generic type references. @@ -139,22 +159,23 @@ template <> const Metadata * ProtocolConformanceDescriptor::getCanonicalTypeMetadata() const { switch (getTypeKind()) { - case TypeMetadataRecordKind::Reserved: - return nullptr; - case TypeMetadataRecordKind::IndirectObjCClass: + case TypeReferenceKind::IndirectObjCClass: + case TypeReferenceKind::DirectObjCClassName: +#if SWIFT_OBJC_INTEROP // The class may be ObjC, in which case we need to instantiate its Swift // metadata. The class additionally may be weak-linked, so we have to check // for null. - if (auto *ClassMetadata = *getIndirectObjCClass()) - return getMetadataForClass(ClassMetadata); + if (auto cls = TypeRef.getObjCClass(getTypeKind())) + return getMetadataForClass(cls); +#endif return nullptr; - - case TypeMetadataRecordKind::DirectNominalTypeDescriptor: - case TypeMetadataRecordKind::IndirectNominalTypeDescriptor: + + case TypeReferenceKind::DirectNominalTypeDescriptor: + case TypeReferenceKind::IndirectNominalTypeDescriptor: return nullptr; } - swift_runtime_unreachable("Unhandled TypeMetadataRecordKind in switch."); + swift_runtime_unreachable("Unhandled TypeReferenceKind in switch."); } template<> @@ -614,9 +635,9 @@ swift_conformsToProtocolImpl(const Metadata * const type, // An accessor function might still be necessary even if the witness table // can be shared. } else if (descriptor.getTypeKind() - == TypeMetadataRecordKind::DirectNominalTypeDescriptor || + == TypeReferenceKind::DirectNominalTypeDescriptor || descriptor.getTypeKind() - == TypeMetadataRecordKind::IndirectNominalTypeDescriptor) { + == TypeReferenceKind::IndirectNominalTypeDescriptor) { auto R = descriptor.getTypeContextDescriptor(); auto P = descriptor.getProtocol(); diff --git a/test/IRGen/cf.sil b/test/IRGen/cf.sil index 7916e58514bd4..7ad9c7ffa9af4 100644 --- a/test/IRGen/cf.sil +++ b/test/IRGen/cf.sil @@ -9,26 +9,19 @@ // CHECK: [[MUTABLE_REFRIGERATOR:%TSo24CCMutableRefrigeratorRefa]] = type // CHECK: [[OBJC:%objc_object]] = type -// CHECK-32: @"$SSo17CCRefrigeratorRefaN" = linkonce_odr hidden global <{ {{.*}} }> <{ -// CHECK-32-SAME: i32 0, -// CHECK-32-SAME: i8** @"$SBOWV", i32 16, {{.*}}"$SSo17CCRefrigeratorRefaMn", [[TYPE]]* null, i8* null, i8* null, i8* null }> - -// CHECK-64: @"$SSo17CCRefrigeratorRefaN" = linkonce_odr hidden global <{ {{.*}} }> <{ -// CHECK-64-SAME: i64 0, -// CHECK-64-SAME: i8** @"$SBOWV", i64 16, {{.*}}"$SSo17CCRefrigeratorRefaMn", [[TYPE]]* null, i8* null, i8* null, i8* null }> +// CHECK: @"$SSo17CCRefrigeratorRefaN" = linkonce_odr hidden constant <{ {{.*}} }> <{ i8** @"$SBOWV", [[INT]] 16, {{.*}}"$SSo17CCRefrigeratorRefaMn", [[TYPE]]* null, i8* null }> // CHECK: [[MUTABLE_REFRIGERATOR_NAME:@.*]] = private constant [25 x i8] c"CCMutableRefrigeratorRef\00" // CHECK-64: @"$SSo24CCMutableRefrigeratorRefaMn" = linkonce_odr hidden constant -// -- is imported C typedef, is class, is nonunique -// CHECK-64-SAME: +// -- is imported C typedef, foreign init, reflectable, is class, is nonunique +// CHECK-64-SAME: // CHECK-64-SAME: [[MUTABLE_REFRIGERATOR_NAME]] +// CHECK-64-SAME: @"$SSo24CCMutableRefrigeratorRefaMa" +// CHECK-64-SAME: @"$SSo24CCMutableRefrigeratorRefaMr" // CHECK-64: @"$SSo24CCMutableRefrigeratorRefaN" = linkonce_odr hidden global <{ {{.*}} }> <{ -// CHECK-64-SAME: @initialize_metadata_CCMutableRefrigerator -// CHECK-64-SAME: i32 0, -// CHECK-64-SAME: i64 1, -// CHECK-64-SAME: i8** @"$SBOWV", i64 16, {{.*}}"$SSo24CCMutableRefrigeratorRefaMn", [[TYPE]]* bitcast{{.*}}@"$SSo17CCRefrigeratorRefaN{{.*}} to %swift.type*), i8* null, i8* null, i8* null }> +// CHECK-64-SAME: i8** @"$SBOWV", i64 16, {{.*}}"$SSo24CCMutableRefrigeratorRefaMn", %swift.type* null, i8* null }> sil_stage canonical @@ -46,7 +39,8 @@ bb0(%0 : $CCRefrigerator, %1: $CCMutableRefrigerator): return %5 : $() } -// CHECK: define{{( dllexport)?}}{{( protected)?}} swiftcc void @call_generic([[REFRIGERATOR]]*, [[MUTABLE_REFRIGERATOR]]*) {{.*}} { +// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc void @call_generic +// CHECK-SAME: ([[REFRIGERATOR]]*, [[MUTABLE_REFRIGERATOR]]*) {{.*}} { // CHECK: [[T0:%.*]] = bitcast [[REFRIGERATOR]]* %0 to [[OBJC]]* // CHECK-NEXT: [[T1:%.*]] = call swiftcc %swift.metadata_response @"$SSo17CCRefrigeratorRefaMa"([[INT]] 0) // CHECK-NEXT: [[T2:%.*]] = extractvalue %swift.metadata_response [[T1]], 0 @@ -57,14 +51,15 @@ bb0(%0 : $CCRefrigerator, %1: $CCMutableRefrigerator): // CHECK-NEXT: call swiftcc void @generic_function([[OBJC]]* [[T0]], [[TYPE]]* [[T2]]) // CHECK-NEXT: ret void -// CHECK: define linkonce_odr hidden swiftcc %swift.metadata_response @"$SSo17CCRefrigeratorRefaMa"( -// CHECK-32: call [[TYPE]]* @swift_getForeignTypeMetadata([[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @"$SSo17CCRefrigeratorRefaN" to i8*), i32 8) to [[TYPE]]*)) -// CHECK-64: call [[TYPE]]* @swift_getForeignTypeMetadata([[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @"$SSo17CCRefrigeratorRefaN" to i8*), i64 16) to [[TYPE]]*)) +// CHECK-LABEL: define linkonce_odr hidden swiftcc %swift.metadata_response @"$SSo17CCRefrigeratorRefaMa"( +// CHECK-32: call swiftcc %swift.metadata_response @swift_getForeignTypeMetadata([[INT]] %0, [[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @"$SSo17CCRefrigeratorRefaN" to i8*), [[INT]] 4) to [[TYPE]]*)) +// CHECK-64: call swiftcc %swift.metadata_response @swift_getForeignTypeMetadata([[INT]] %0, [[TYPE]]* bitcast (i8* getelementptr inbounds (i8, i8* bitcast ({{.*}}* @"$SSo17CCRefrigeratorRefaN" to i8*), [[INT]] 8) to [[TYPE]]*)) -// CHECK: define private void @initialize_metadata_CCMutableRefrigerator(%swift.type*) -// CHECK-64: [[T0:%.*]] = bitcast %swift.type* %0 to i8** -// CHECK-64: [[T1:%.*]] = getelementptr inbounds i8*, i8** [[T0]], i32 2 -// CHECK-64: [[T2:%.*]] = bitcast i8** [[T1]] to %swift.type** -// CHECK-64: [[T3:%.*]] = load %swift.type*, %swift.type** [[T2]] -// CHECK-64: [[T4:%.*]] = call %swift.type* @swift_getForeignTypeMetadata(%swift.type* [[T3]]) -// CHECK-64: store %swift.type* [[T4]], %swift.type** [[T2]] +// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$SSo24CCMutableRefrigeratorRefaMr"(%swift.type*, i8*, i8**) +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$SSo17CCRefrigeratorRefaMa"([[INT]] 255) +// CHECK-NEXT: [[T1:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK-NEXT: extractvalue %swift.metadata_response [[T0]], 1 +// CHECK-NEXT: [[T2:%.*]] = bitcast %swift.type* %0 to %swift.type** +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds %swift.type*, %swift.type** [[T2]], i32 2 +// CHECK-NEXT: store %swift.type* [[T1]], %swift.type** [[T3]], align +// CHECK-NEXT: ret %swift.metadata_response zeroinitializer diff --git a/test/IRGen/class_metadata.swift b/test/IRGen/class_metadata.swift index e1c978b076350..e958903124096 100644 --- a/test/IRGen/class_metadata.swift +++ b/test/IRGen/class_metadata.swift @@ -4,8 +4,8 @@ class A {} // CHECK: [[A_NAME:@.*]] = private constant [2 x i8] c"A\00" // CHECK-LABEL: @"$S14class_metadata1ACMn" = -// Flags. -2147221424 == 0x8004_0050 == HasVTable | Reflectable | Unique | Class -// CHECK-SAME: i32 -2147221424, +// Flags. -2147418032 == 0x8001_0050 == HasVTable | Reflectable | Unique | Class +// CHECK-SAME: i32 -2147418032, // Parent. // CHECK-SAME: i32 {{.*}} @"$S14class_metadataMXM" // Name. @@ -33,8 +33,8 @@ class B : A {} // CHECK: [[B_NAME:@.*]] = private constant [2 x i8] c"B\00" // CHECK-LABEL: @"$S14class_metadata1BCMn" = -// Flags. 262224 == 0x0004_0050 == Reflectable | Unique | Class -// CHECK-SAME: i32 262224, +// Flags. 65616 == 0x0001_0050 == Reflectable | Unique | Class +// CHECK-SAME: i32 65616, // Parent. // CHECK-SAME: i32 {{.*}} @"$S14class_metadataMXM" // Name. @@ -53,8 +53,8 @@ class C : B {} // CHECK: [[C_NAME:@.*]] = private constant [2 x i8] c"C\00" // CHECK-LABEL: @"$S14class_metadata1CCMn" = -// Flags. 262352 == 0x0004_00d0 == Reflectable | Generic | Unique | Class -// CHECK-SAME: i32 262352, +// Flags. 65744 == 0x0001_00d0 == Reflectable | Generic | Unique | Class +// CHECK-SAME: i32 65744, // Parent. // CHECK-SAME: i32 {{.*}} @"$S14class_metadataMXM" // Name. @@ -104,8 +104,8 @@ class D : E {} // CHECK: [[D_NAME:@.*]] = private constant [2 x i8] c"D\00" // CHECK-LABEL: @"$S14class_metadata1DCMn" = -// Flags. 268697680 == 0x1004_0050 == Reflectable | IndirectSuperclass | Unique | Class -// CHECK-SAME: i32 268697680, +// Flags. 67174480 == 0x0401_0050 == Reflectable | IndirectSuperclass | Unique | Class +// CHECK-SAME: i32 67174480, // Parent. // CHECK-SAME: i32 {{.*}} @"$S14class_metadataMXM" // Name. diff --git a/test/IRGen/class_resilience.swift b/test/IRGen/class_resilience.swift index cb55adb24102f..5939a0691ebf3 100644 --- a/test/IRGen/class_resilience.swift +++ b/test/IRGen/class_resilience.swift @@ -32,7 +32,7 @@ // CHECK: @"$S16class_resilience14ResilientChildCMn" = {{(protected )?}}{{(dllexport )?}}constant <{{.*}}> <{ // -- flags: class, unique, reflectable, has vtable, has resilient superclass -// CHECK-SAME: +// CHECK-SAME: // -- name: // CHECK-SAME: [15 x i8]* [[RESILIENTCHILD_NAME]] // -- num fields diff --git a/test/IRGen/enum_resilience.swift b/test/IRGen/enum_resilience.swift index 692f7c67214f9..7fef35f43cc6d 100644 --- a/test/IRGen/enum_resilience.swift +++ b/test/IRGen/enum_resilience.swift @@ -44,6 +44,20 @@ import resilient_struct // CHECK: %T15enum_resilience10EitherFastO = type <{ [[REFERENCE_TYPE]] }> +// CHECK: @"$S15enum_resilience24EnumWithResilientPayloadOMl" = +// CHECK-SAME: internal global { %swift.type*, i8* } zeroinitializer, align + +// CHECK: @"$S15enum_resilience24EnumWithResilientPayloadOMn" = {{.*}}constant +// 196690 == 0x00030052 +// 0x0002 - InPlaceMetadataInitialization +// 0x0001 - IsReflectable +// 0x 0040 - IsUnique +// 0x 0012 - Enum +// CHECK-SAME: i32 196690, +// CHECK-SAME: @"$S15enum_resilience24EnumWithResilientPayloadOMl" +// CHECK-SAME: @"$S15enum_resilience24EnumWithResilientPayloadOMf", i32 0, i32 1) +// CHECK-SAME: @"$S15enum_resilience24EnumWithResilientPayloadOMr" + public class Class {} public struct Reference { @@ -267,17 +281,22 @@ public func getResilientEnumType() -> Any.Type { // Public metadata accessor for our resilient enum // CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} swiftcc %swift.metadata_response @"$S15enum_resilience24EnumWithResilientPayloadOMa"( -// CHECK: [[METADATA:%.*]] = load %swift.type*, %swift.type** @"$S15enum_resilience24EnumWithResilientPayloadOML" -// CHECK-NEXT: [[COND:%.*]] = icmp eq %swift.type* [[METADATA]], null +// CHECK: [[LOAD_METADATA:%.*]] = load %swift.type*, %swift.type** getelementptr inbounds ({ %swift.type*, i8* }, { %swift.type*, i8* }* @"$S15enum_resilience24EnumWithResilientPayloadOMl", i32 0, i32 0), align +// CHECK-NEXT: [[COND:%.*]] = icmp eq %swift.type* [[LOAD_METADATA]], null // CHECK-NEXT: br i1 [[COND]], label %cacheIsNull, label %cont // CHECK: cacheIsNull: -// CHECK-NEXT: call void @swift_once([[INT]]* @"$S15enum_resilience24EnumWithResilientPayloadOMa.once_token", i8* bitcast (void (i8*)* @initialize_metadata_EnumWithResilientPayload to i8*), i8* undef) -// CHECK-NEXT: [[METADATA2:%.*]] = load %swift.type*, %swift.type** @"$S15enum_resilience24EnumWithResilientPayloadOML" +// CHECK-NEXT: [[RESPONSE:%.*]] = call swiftcc %swift.metadata_response @swift_getInPlaceMetadata([[INT]] %0, %swift.type_descriptor* bitcast ({{.*}} @"$S15enum_resilience24EnumWithResilientPayloadOMn" to %swift.type_descriptor*)) +// CHECK-NEXT: [[RESPONSE_METADATA:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 0 +// CHECK-NEXT: [[RESPONSE_STATE:%.*]] = extractvalue %swift.metadata_response [[RESPONSE]], 1 // CHECK-NEXT: br label %cont // CHECK: cont: -// CHECK-NEXT: [[RESULT:%.*]] = phi %swift.type* [ [[METADATA]], %entry ], [ [[METADATA2]], %cacheIsNull ] +// CHECK-NEXT: [[RESULT_METADATA:%.*]] = phi %swift.type* [ [[LOAD_METADATA]], %entry ], [ [[RESPONSE_METADATA]], %cacheIsNull ] +// CHECK-NEXT: [[RESULT_STATE:%.*]] = phi [[INT]] [ 0, %entry ], [ [[RESPONSE_STATE]], %cacheIsNull ] +// CHECK-NEXT: [[T0:%.*]] = insertvalue %swift.metadata_response undef, %swift.type* [[RESULT_METADATA]], 0 +// CHECK-NEXT: [[T1:%.*]] = insertvalue %swift.metadata_response [[T0]], [[INT]] [[RESULT_STATE]], 1 +// CHECK-NEXT: ret %swift.metadata_response [[T1]] // Methods inside extensions of resilient enums fish out type parameters // from metadata -- make sure we can do that @@ -311,9 +330,28 @@ public func constructFullyFixed() -> FullyFixedLayout { return .noPayload } -// CHECK-LABEL: define private void @initialize_metadata_EnumWithResilientPayload(i8*) -// CHECK: call void @swift_initEnumMetadataMultiPayload(%swift.type* {{.*}}, [[INT]] 256, [[INT]] 2, i8*** {{.*}}) - +// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$S15enum_resilience24EnumWithResilientPayloadOMr"(%swift.type*, i8*, i8**) +// CHECK: [[TUPLE_LAYOUT:%.*]] = alloca %swift.full_type_layout +// CHECK: [[SIZE_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319) +// CHECK-NEXT: [[SIZE_METADATA:%.*]] = extractvalue %swift.metadata_response [[SIZE_RESPONSE]], 0 +// CHECK-NEXT: [[SIZE_STATE:%.*]] = extractvalue %swift.metadata_response [[SIZE_RESPONSE]], 1 +// CHECK-NEXT: [[T0:%.*]] = icmp ule [[INT]] [[SIZE_STATE]], 63 +// CHECK-NEXT: br i1 [[T0]], label %[[SATISFIED1:.*]], label +// CHECK: [[SATISFIED1]]: +// CHECK-NEXT: [[T0:%.*]] = bitcast %swift.type* [[SIZE_METADATA]] to i8*** +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], [[INT]] -1 +// CHECK-NEXT: [[SIZE_VWT:%.*]] = load i8**, i8*** [[T1]], +// CHECK-NEXT: [[SIZE_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8 +// CHECK-NEXT: store i8** [[SIZE_LAYOUT_1]], +// CHECK-NEXT: getelementptr +// CHECK-NEXT: [[SIZE_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8 +// CHECK-NEXT: [[SIZE_LAYOUT_3:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8 +// CHECK-NEXT: call swiftcc [[INT]] @swift_getTupleTypeLayout2(%swift.full_type_layout* [[TUPLE_LAYOUT]], i8** [[SIZE_LAYOUT_2]], i8** [[SIZE_LAYOUT_3]]) +// CHECK-NEXT: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT]] to i8** +// CHECK-NEXT: store i8** [[T0]], +// CHECK: call void @swift_initEnumMetadataMultiPayload +// CHECK: phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ null, %[[SATISFIED1]] ] +// CHECK: phi [[INT]] [ 63, %entry ], [ 0, %[[SATISFIED1]] ] public protocol Prot { diff --git a/test/IRGen/foreign_types.sil b/test/IRGen/foreign_types.sil index c4130158ff39e..58159d9851d87 100644 --- a/test/IRGen/foreign_types.sil +++ b/test/IRGen/foreign_types.sil @@ -1,4 +1,4 @@ -// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -I %S/Inputs/abi %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -I %S/Inputs/abi %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize sil_stage canonical import c_layout @@ -15,8 +15,7 @@ import c_layout // CHECK-SAME: [[AMAZING_COLOR_NAME]] // CHECK-SAME: @"$SSo12AmazingColorVMa" -// CHECK-LABEL: @"$SSo14HasNestedUnionV18__Unnamed_struct_sVN" = linkonce_odr hidden global -// CHECK-SAME: [[INT:i[0-9]+]] 0, +// CHECK-LABEL: @"$SSo14HasNestedUnionV18__Unnamed_struct_sVN" = linkonce_odr hidden constant // CHECK-SAME: @"$SSo14HasNestedUnionV18__Unnamed_struct_sVWV" // CHECK-SAME: [[INT]] 1, // CHECK-SAME: @"$SSo14HasNestedUnionV18__Unnamed_struct_sVMn" @@ -38,7 +37,7 @@ bb0: } // CHECK-LABEL: define linkonce_odr hidden swiftcc %swift.metadata_response @"$SSo14HasNestedUnionVMa"( -// CHECK: call %swift.type* @swift_getForeignTypeMetadata{{.*}}$SSo14HasNestedUnionVN +// CHECK: call swiftcc %swift.metadata_response @swift_getForeignTypeMetadata([[INT]] %0, {{.*}}$SSo14HasNestedUnionVN // CHECK-LABEL: define linkonce_odr hidden swiftcc %swift.metadata_response @"$SSo12AmazingColorVMa"( -// CHECK: call %swift.type* @swift_getForeignTypeMetadata{{.*}}@"$SSo12AmazingColorVN" +// CHECK: call swiftcc %swift.metadata_response @swift_getForeignTypeMetadata([[INT]] %0, {{.*}}@"$SSo12AmazingColorVN" diff --git a/test/IRGen/generic_classes.sil b/test/IRGen/generic_classes.sil index e7f9dcb198092..180d745d9b6ac 100644 --- a/test/IRGen/generic_classes.sil +++ b/test/IRGen/generic_classes.sil @@ -16,7 +16,7 @@ import Swift // CHECK-LABEL: @"$S15generic_classes11RootGenericCMn" = // -- flags: class, generic, unique, reflectable, has vtable -// CHECK-SAME: +// CHECK-SAME: // -- name // CHECK-SAME: [12 x i8]* [[ROOTGENERIC_NAME]] // -- negative size in words @@ -80,7 +80,7 @@ import Swift // CHECK: [[ROOTNONGENERIC_NAME:@.*]] = private constant [15 x i8] c"RootNonGeneric\00" // CHECK: @"$S15generic_classes14RootNonGenericCMn" = hidden constant <{ {{.*}} %swift.method_descriptor }> <{ // -- flags: class, unique, has vtable, reflectable -// CHECK-SAME: +// CHECK-SAME: // -- name // CHECK-SAME: [15 x i8]* [[ROOTNONGENERIC_NAME]] // -- num fields diff --git a/test/IRGen/generic_structs.sil b/test/IRGen/generic_structs.sil index 1728ab5e79222..1eec3f9d82292 100644 --- a/test/IRGen/generic_structs.sil +++ b/test/IRGen/generic_structs.sil @@ -39,7 +39,7 @@ import Builtin // CHECK: [[SINGLEDYNAMIC_NAME:@.*]] = private constant [14 x i8] c"SingleDynamic\00" // CHECK: @"$S15generic_structs13SingleDynamicVMn" = hidden constant // -- flags: struct, unique, generic, reflectable -// CHECK-SAME: +// CHECK-SAME: // -- name // CHECK-SAME: [14 x i8]* [[SINGLEDYNAMIC_NAME]] // -- field count @@ -65,7 +65,7 @@ import Builtin // CHECK: [[DYNAMICWITHREQUIREMENTS_NAME:@.*]] = private constant [24 x i8] c"DynamicWithRequirements\00" // CHECK: @"$S15generic_structs23DynamicWithRequirementsVMn" = hidden constant <{ {{.*}} i32 }> <{ // -- flags: struct, unique, generic, reflectable -// CHECK-SAME: +// CHECK-SAME: // -- name // CHECK-SAME: [24 x i8]* [[DYNAMICWITHREQUIREMENTS_NAME]] // -- field count diff --git a/test/IRGen/generic_types.swift b/test/IRGen/generic_types.swift index 7902ae4848682..f0f3fe646b543 100644 --- a/test/IRGen/generic_types.swift +++ b/test/IRGen/generic_types.swift @@ -11,7 +11,7 @@ // CHECK-LABEL: @"$S13generic_types1ACMI" = internal global [16 x i8*] zeroinitializer, align 8 // CHECK-LABEL: @"$S13generic_types1ACMn" = hidden constant -// CHECK-SAME: i32 -2147221296, +// CHECK-SAME: i32 -2147417904, // CHECK-SAME: @"$S13generic_typesMXM" // // CHECK-SAME: @"$S13generic_types1ACMa" diff --git a/test/IRGen/generic_vtable.swift b/test/IRGen/generic_vtable.swift index f0b2800b3f654..298789aade127 100644 --- a/test/IRGen/generic_vtable.swift +++ b/test/IRGen/generic_vtable.swift @@ -24,7 +24,7 @@ public class Concrete : Derived { // CHECK-LABEL: @"$S14generic_vtable4BaseCMn" = {{(dllexport )?}}{{(protected )?}}constant // -- flags: has vtable, reflectable, is class, is unique -// CHECK-SAME: , +// CHECK-SAME: , // -- vtable offset // CHECK-SAME: i32 10, // -- vtable size @@ -49,7 +49,7 @@ public class Concrete : Derived { // CHECK-LABEL: @"$S14generic_vtable7DerivedCMn" = {{(dllexport )?}}{{(protected )?}}constant // -- flags: has vtable, reflectable, is class, is unique, is generic -// CHECK-SAME: , +// CHECK-SAME: , // -- vtable offset // CHECK-SAME: i32 14, // -- vtable size @@ -73,7 +73,7 @@ public class Concrete : Derived { // CHECK-LABEL: @"$S14generic_vtable8ConcreteCMn" = {{(dllexport )?}}{{(protected )?}}constant // -- flags: has vtable, reflectable, is class, is unique -// CHECK-SAME: , +// CHECK-SAME: , // -- vtable offset // CHECK-SAME: i32 15, // -- vtable size diff --git a/test/IRGen/objc.swift b/test/IRGen/objc.swift index ef0d59fe04781..934270a5df696 100644 --- a/test/IRGen/objc.swift +++ b/test/IRGen/objc.swift @@ -23,7 +23,7 @@ import gizmo // CHECK: @"\01L_selector(bar)" = private externally_initialized global i8* getelementptr inbounds ([4 x i8], [4 x i8]* @"\01L_selector_data(bar)", i64 0, i64 0), section "__DATA,__objc_selrefs,literal_pointers,no_dead_strip", align 8 // CHECK: @"$SSo4RectVMn" = linkonce_odr hidden constant -// CHECK: @"$SSo4RectVN" = linkonce_odr hidden global +// CHECK: @"$SSo4RectVN" = linkonce_odr hidden constant // CHECK: @"\01L_selector_data(acquiesce)" // CHECK-NOT: @"\01L_selector_data(disharmonize)" @@ -130,7 +130,7 @@ func test10(_ g: Gizmo, r: Rect) { func test11_helper(_ t: T) {} // NSRect's metadata needs to be uniqued at runtime using getForeignTypeMetadata. // CHECK-LABEL: define hidden swiftcc void @"$S4objc6test11yySo4RectVF" -// CHECK: call %swift.type* @swift_getForeignTypeMetadata({{.*}} @"$SSo4RectVN" +// CHECK: call swiftcc %swift.metadata_response @swift_getForeignTypeMetadata(i64 %0, {{.*}} @"$SSo4RectVN" func test11(_ r: Rect) { test11_helper(r) } class WeakObjC { diff --git a/test/IRGen/objc_ns_enum.swift b/test/IRGen/objc_ns_enum.swift index 786a45f82636b..e9ad4166f2bdb 100644 --- a/test/IRGen/objc_ns_enum.swift +++ b/test/IRGen/objc_ns_enum.swift @@ -1,6 +1,6 @@ // RUN: %empty-directory(%t) // RUN: %build-irgen-test-overlays -// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir | %FileCheck %s +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir | %FileCheck %s -DINT=i%target-ptrsize // REQUIRES: CPU=x86_64 // REQUIRES: objc_interop @@ -10,7 +10,7 @@ import gizmo // CHECK: @"$SSo16NSRuncingOptionsVWV" = linkonce_odr hidden constant // CHECK: @"$SSo16NSRuncingOptionsVMn" = linkonce_odr hidden constant -// CHECK: @"$SSo16NSRuncingOptionsVN" = linkonce_odr hidden global +// CHECK: @"$SSo16NSRuncingOptionsVN" = linkonce_odr hidden constant // CHECK: @"$SSo16NSRuncingOptionsVSQSCMc" = linkonce_odr hidden constant %swift.protocol_conformance_descriptor { {{.*}}@"$SSo16NSRuncingOptionsVSQSCWa // CHECK: @"$SSo28NeverActuallyMentionedByNameVSQSCWp" = linkonce_odr hidden constant @@ -87,11 +87,11 @@ func use_metadata(_ t:T){} use_metadata(NSRuncingOptions.mince) // CHECK-LABEL: define linkonce_odr hidden swiftcc %swift.metadata_response @"$SSo16NSRuncingOptionsVMa"(i64) -// CHECK: call %swift.type* @swift_getForeignTypeMetadata({{.*}} @"$SSo16NSRuncingOptionsVN" {{.*}}) [[NOUNWIND_READNONE:#[0-9]+]] +// CHECK: call swiftcc %swift.metadata_response @swift_getForeignTypeMetadata([[INT]] %0, {{.*}} @"$SSo16NSRuncingOptionsVN" {{.*}}) [[NOUNWIND_READNONE:#[0-9]+]] // CHECK-LABEL: define linkonce_odr hidden i8** @"$SSo16NSRuncingOptionsVSQSCWa"() // CHECK: [[NONUNIQUE:%.*]] = call i8** @swift_getGenericWitnessTable(%swift.generic_witness_table_cache* @"$SSo16NSRuncingOptionsVSQSCWG", %swift.type* null, i8*** null) -// CHECK: [[UNIQUE:%.*]] = call i8** @swift_getForeignWitnessTable(i8** [[NONUNIQUE]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32 }>* @"$SSo16NSRuncingOptionsVMn" to %swift.type_descriptor*), %swift.protocol* @"$SSQMp") +// CHECK: [[UNIQUE:%.*]] = call i8** @swift_getForeignWitnessTable(i8** [[NONUNIQUE]], %swift.type_descriptor* bitcast (<{ i32, i32, i32, i32, i32, i32, i32 }>* @"$SSo16NSRuncingOptionsVMn" to %swift.type_descriptor*), %swift.protocol* @"$SSQMp") // CHECK: ret i8** [[UNIQUE]] @objc enum ExportedToObjC: Int { diff --git a/test/IRGen/objc_runtime_visible_conformance.swift b/test/IRGen/objc_runtime_visible_conformance.swift index 530a9a299a527..dbe21242ac0bd 100644 --- a/test/IRGen/objc_runtime_visible_conformance.swift +++ b/test/IRGen/objc_runtime_visible_conformance.swift @@ -8,9 +8,10 @@ protocol YourProtocol {} extension A : MyProtocol {} extension A : YourProtocol {} -// CHECK-LABEL: @"$SSo1ACMn" = linkonce_odr hidden constant <{ {{.*}} }> - -// CHECK-LABEL: define linkonce_odr hidden swiftcc %swift.metadata_response @"$SSo1ACMa"({{i32|i64}}) {{.*}} { -// CHECK: call %objc_class* @objc_lookUpClass -// CHECK: call %swift.type* @swift_getObjCClassMetadata -// CHECK: ret +// CHECK-LABEL: @"$SSo1AC32objc_runtime_visible_conformance10MyProtocolACMc" +// CHECK-SAME: @"$S32objc_runtime_visible_conformance10MyProtocolMp" +// CHECK-SAME: [2 x i8]* [[STRING_A:@[0-9]+]] +// CHECK-SAME: @"$SSo1AC32objc_runtime_visible_conformance10MyProtocolACWP" +// DirectObjCClassName +// CHECK-SAME: i32 16 +// CHECK: [[STRING_A]] = private unnamed_addr constant [2 x i8] c"A\00" diff --git a/test/IRGen/struct_resilience.swift b/test/IRGen/struct_resilience.swift index 9fec84ccbec34..20c8e85c41be5 100644 --- a/test/IRGen/struct_resilience.swift +++ b/test/IRGen/struct_resilience.swift @@ -196,20 +196,31 @@ public func resilientAny(s : ResilientWeakRef) { // CHECK: ret %swift.metadata_response { %swift.type* bitcast ([[INT]]* getelementptr inbounds {{.*}} @"$S17struct_resilience6MySizeVMf", i32 0, i32 1) to %swift.type*), [[INT]] 0 } -// CHECK-LABEL: define{{( dllexport)?}}{{( protected)?}} private void @initialize_metadata_StructWithResilientStorage(i8*) +// CHECK-LABEL: define internal swiftcc %swift.metadata_response @"$S17struct_resilience26StructWithResilientStorageVMr"(%swift.type*, i8*, i8**) // CHECK: [[FIELDS:%.*]] = alloca [4 x i8**] +// CHECK: [[TUPLE_LAYOUT:%.*]] = alloca %swift.full_type_layout, // CHECK: [[FIELDS_ADDR:%.*]] = getelementptr inbounds [4 x i8**], [4 x i8**]* [[FIELDS]], i32 0, i32 0 // public let s: Size +// CHECK: [[T0:%.*]] = call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319) +// CHECK: [[SIZE_METADATA:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 +// CHECK: [[T0:%.*]] = bitcast %swift.type* [[SIZE_METADATA]] to i8*** +// CHECK: [[T1:%.*]] = getelementptr inbounds i8**, i8*** [[T0]], [[INT]] -1 +// CHECK: [[SIZE_VWT:%.*]] = load i8**, i8*** [[T1]], +// CHECK: [[SIZE_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8 // CHECK: [[FIELD_1:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 0 -// CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_1]] +// CHECK: store i8** [[SIZE_LAYOUT_1:%.*]], i8*** [[FIELD_1]] // public let ss: (Size, Size) +// CHECK: [[SIZE_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8 +// CHECK: [[SIZE_LAYOUT_3:%.*]] = getelementptr inbounds i8*, i8** [[SIZE_VWT]], i32 8 +// CHECK: call swiftcc [[INT]] @swift_getTupleTypeLayout2(%swift.full_type_layout* [[TUPLE_LAYOUT]], i8** [[SIZE_LAYOUT_2]], i8** [[SIZE_LAYOUT_3]]) +// CHECK: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT]] to i8** // CHECK: [[FIELD_2:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 1 -// CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_2]] +// CHECK: store i8** [[T0]], i8*** [[FIELD_2]] // Fixed-layout aggregate -- we can reference a static value witness table // public let n: Int @@ -219,9 +230,8 @@ public func resilientAny(s : ResilientWeakRef) { // Resilient aggregate with one field -- make sure we don't look inside it // public let i: ResilientInt +// CHECK: call swiftcc %swift.metadata_response @"$S16resilient_struct12ResilientIntVMa"([[INT]] 319) // CHECK: [[FIELD_4:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 3 // CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_4]] // CHECK: call void @swift_initStructMetadata(%swift.type* {{.*}}, [[INT]] 256, [[INT]] 4, i8*** [[FIELDS_ADDR]], i32* {{.*}}) -// CHECK: store atomic %swift.type* {{.*}} @"$S17struct_resilience26StructWithResilientStorageVMf{{.*}}, %swift.type** @"$S17struct_resilience26StructWithResilientStorageVML" release, -// CHECK: ret void diff --git a/test/IRGen/type_layout.swift b/test/IRGen/type_layout.swift index b9fd828c5cbee..f850c0318fb06 100644 --- a/test/IRGen/type_layout.swift +++ b/test/IRGen/type_layout.swift @@ -25,6 +25,10 @@ struct AlignedFourInts { var x: FourInts } // CHECK: define internal %swift.type* @"$S11type_layout14TypeLayoutTestVMi" // CHECK: define internal swiftcc %swift.metadata_response @"$S11type_layout14TypeLayoutTestVMr" struct TypeLayoutTest { + // CHECK: [[TUPLE_LAYOUT_M:%.*]] = alloca %swift.full_type_layout, + // CHECK: [[TUPLE_LAYOUT_N:%.*]] = alloca %swift.full_type_layout, + // CHECK: [[TUPLE_LAYOUT_O:%.*]] = alloca %swift.full_type_layout, + // CHECK: [[TUPLE_ELT_LAYOUTS_O:%.*]] = alloca i8**, [[INT]] 4, // -- dynamic layout, projected from metadata // CHECK: [[T0:%.*]] = call{{( tail)?}} swiftcc %swift.metadata_response @swift_checkMetadataState([[INT]] 319, %swift.type* %T) // CHECK: [[T_CHECKED:%.*]] = extractvalue %swift.metadata_response [[T0]], 0 @@ -80,4 +84,35 @@ struct TypeLayoutTest { // -- Single-field aggregate with alignment // CHECK: store i8** getelementptr (i8*, i8** @"$SBi128_WV", i32 8) var l: AlignedFourInts + // -- Tuple with two elements + // CHECK: [[T_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: [[T_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: call swiftcc [[INT]] @swift_getTupleTypeLayout2(%swift.full_type_layout* [[TUPLE_LAYOUT_M]], i8** [[T_LAYOUT_1]], i8** [[T_LAYOUT_2]]) + // CHECK: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT_M]] to i8** + // CHECK: store i8** [[T0]] + var m: (T, T) + // -- Tuple with three elements + // CHECK: [[T_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: [[T_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: [[T_LAYOUT_3:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: call swiftcc { [[INT]], [[INT]] } @swift_getTupleTypeLayout3(%swift.full_type_layout* [[TUPLE_LAYOUT_N]], i8** [[T_LAYOUT_1]], i8** [[T_LAYOUT_2]], i8** [[T_LAYOUT_3]]) + // CHECK: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT_N]] to i8** + // CHECK: store i8** [[T0]] + var n: (T, T, T) + // -- Tuple with four elements + // CHECK: [[T_LAYOUT_1:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: store i8** [[T_LAYOUT_1]], i8*** [[TUPLE_ELT_LAYOUTS_O]], + // CHECK: [[T_LAYOUT_2:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8**, i8*** [[TUPLE_ELT_LAYOUTS_O]], i32 1 + // CHECK: store i8** [[T_LAYOUT_2]], i8*** [[T0]], + // CHECK: [[T_LAYOUT_3:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8**, i8*** [[TUPLE_ELT_LAYOUTS_O]], i32 2 + // CHECK: store i8** [[T_LAYOUT_3]], i8*** [[T0]], + // CHECK: [[T_LAYOUT_4:%.*]] = getelementptr inbounds i8*, i8** [[T_VALUE_WITNESSES]], i32 8 + // CHECK: [[T0:%.*]] = getelementptr inbounds i8**, i8*** [[TUPLE_ELT_LAYOUTS_O]], i32 3 + // CHECK: store i8** [[T_LAYOUT_4]], i8*** [[T0]], + // CHECK: call swiftcc void @swift_getTupleTypeLayout(%swift.full_type_layout* [[TUPLE_LAYOUT_O]], i32* null, [[INT]] 4, i8*** [[TUPLE_ELT_LAYOUTS_O]]) + // CHECK: [[T0:%.*]] = bitcast %swift.full_type_layout* [[TUPLE_LAYOUT_O]] to i8** + // CHECK: store i8** [[T0]] + var o: (T, T, T, T) } diff --git a/test/IRGen/type_layout_reference_storage.swift b/test/IRGen/type_layout_reference_storage.swift index ef53e2436dfba..10424c15cb07c 100644 --- a/test/IRGen/type_layout_reference_storage.swift +++ b/test/IRGen/type_layout_reference_storage.swift @@ -105,10 +105,12 @@ struct ReferenceStorageTypeLayout { public class Base { var a: UInt32 = 0 } -// CHECK-LABEL: %swift.type* @{{.*}}7DerivedCMi"(%swift.type_descriptor*, i8**, i8**) -// CHECK-NOT: store {{.*}}getelementptr{{.*}}SBomWV -// CHECK: call swiftcc %swift.metadata_response @"$S29type_layout_reference_storage1P_pXmTMa"([[INT]] 0) -// CHECK: store {{.*}}getelementptr{{.*}}SBoWV +// CHECK-LABEL: %swift.metadata_response @{{.*}}7DerivedCMr"( +// CHECK: call swiftcc %swift.metadata_response @"$S29type_layout_reference_storage4BaseCMa" +// CHECK-64: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_16_8_{{.*}}_pod, i32 0, i32 0), +// CHECK-32: store i8** getelementptr inbounds ([4 x i8*], [4 x i8*]* @type_layout_8_4_{{.*}}_pod, i32 0, i32 0), +// CHECK: store i8** getelementptr inbounds (i8*, i8** @"$SBoWV", i32 8), +// CHECK: call void @swift_initClassMetadata // CHECK: ret public class Derived : Base { var type : P.Type diff --git a/test/Interpreter/Inputs/resilient_generic_struct_v1.swift b/test/Interpreter/Inputs/resilient_generic_struct_v1.swift new file mode 100644 index 0000000000000..55a0dd5ec0244 --- /dev/null +++ b/test/Interpreter/Inputs/resilient_generic_struct_v1.swift @@ -0,0 +1,6 @@ +public struct ResilientGenericStruct { + public init(value: T) { + size = MemoryLayout.size + } + public var size: Int +} diff --git a/test/Interpreter/Inputs/resilient_generic_struct_v2.swift b/test/Interpreter/Inputs/resilient_generic_struct_v2.swift new file mode 100644 index 0000000000000..dcae19f4ef1a0 --- /dev/null +++ b/test/Interpreter/Inputs/resilient_generic_struct_v2.swift @@ -0,0 +1,8 @@ +public struct ResilientGenericStruct { + public init(value: T) { + size = MemoryLayout.size + storage = value + } + public var size: Int + private var storage: T +} diff --git a/test/Interpreter/objc_runtime_visible.swift b/test/Interpreter/objc_runtime_visible.swift index af79a34de96a7..7698760275e65 100644 --- a/test/Interpreter/objc_runtime_visible.swift +++ b/test/Interpreter/objc_runtime_visible.swift @@ -62,7 +62,6 @@ ObjCRuntimeVisibleTestSuite.test("protocols") { } ObjCRuntimeVisibleTestSuite.test("protocols/downcast") - .xfail(.always("unimplemented")) .code { let obj = HiddenClass.create() let opaque: AnyObject = obj diff --git a/test/Interpreter/resilient_metadata_cycles.swift b/test/Interpreter/resilient_metadata_cycles.swift new file mode 100644 index 0000000000000..c0fc4c4571e80 --- /dev/null +++ b/test/Interpreter/resilient_metadata_cycles.swift @@ -0,0 +1,28 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift-dylib(%t/libresilient_struct.%target-dylib-extension) -Xfrontend -enable-resilience %S/../Inputs/resilient_struct.swift -emit-module -emit-module-path %t/resilient_struct.swiftmodule -module-name resilient_struct +// RUN: %target-codesign %t/libresilient_struct.%target-dylib-extension + +// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -o %t/main -Xlinker -rpath -Xlinker %t + +// RUN: %target-run %t/main %t/libresilient_struct.%target-dylib-extension + +import StdlibUnittest + +import resilient_struct + +var ResilientMetadataCycleTests = TestSuite("Resilient metadata cycle tests") + +// SR-7876 +enum test0_Node { + case link(size: Size, children: [test0_Node]) + + static func test() -> [test0_Node] { + return [] + } +} +ResilientMetadataCycleTests.test("SR-7876") { + _ = test0_Node.test() +} + +runAllTests() diff --git a/test/Interpreter/unresolvable_dynamic_metadata_cycles.swift b/test/Interpreter/unresolvable_dynamic_metadata_cycles.swift new file mode 100644 index 0000000000000..56c69e02b6db6 --- /dev/null +++ b/test/Interpreter/unresolvable_dynamic_metadata_cycles.swift @@ -0,0 +1,47 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-build-swift-dylib(%t/libresil.%target-dylib-extension) -Xfrontend -enable-resilience %S/Inputs/resilient_generic_struct_v1.swift -emit-module -emit-module-path %t/resil.swiftmodule -module-name resil +// RUN: %target-codesign %t/libresil.%target-dylib-extension + +// RUN: %target-build-swift %s -L %t -I %t -lresil -o %t/main -Xlinker -rpath -Xlinker %t + +// RUN: %target-build-swift-dylib(%t/libresil.%target-dylib-extension) -Xfrontend -enable-resilience %S/Inputs/resilient_generic_struct_v2.swift -emit-module -emit-module-path %t/resil.swiftmodule -module-name resil +// RUN: %target-codesign %t/libresil.%target-dylib-extension + +// RUN: %target-run %t/main %t/libresil.%target-dylib-extension + +import StdlibUnittest + +// We build this code against a version of 'resil' where +// ResilientGenericStruct doesn't store a T, then switch the +// dynamic library to a new version where it does, introducing +// an unresolvable dynamic cycle. +// +// It would also be sufficient to demonstrate this crash if the +// compiler *actually* didn't know about the internal implementation +// details of 'resil' when building this file, but since it currently +// still does, it'll report a cycle immediately if we don't pull +// this switcharoo. +import resil + +var DynamicMetadataCycleTests = + TestSuite("Unresolvable dynamic metadata cycle tests") + +enum test0_Node { + case link(ResilientGenericStruct) + + static func test() -> [test0_Node] { + return [] + } +} +DynamicMetadataCycleTests.test("cycle through enum") + .crashOutputMatches("runtime error: unresolvable type metadata dependency cycle detected") + .crashOutputMatches(" main.test0_Node") + .crashOutputMatches(" depends on layout of resil.ResilientGenericStruct