From f9eb78c982087641da94af73e302ddc2676095b0 Mon Sep 17 00:00:00 2001 From: John McCall Date: Mon, 30 Jul 2018 20:08:14 -0700 Subject: [PATCH 1/8] Refactor `LockingConcurrentMap` to allow it to not use a map; NFC. --- stdlib/public/runtime/Metadata.cpp | 11 ++- stdlib/public/runtime/MetadataCache.h | 105 ++++++++++++++++---------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 05d43d88b52db..4122080f1596f 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -875,7 +875,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 +893,10 @@ class TupleCache : public MetadataCache { #pragma clang diagnostic pop }; +class TupleCache : + public LockingConcurrentMap { +}; + } // end anonymous namespace /// The uniquing structure for tuple type metadata. @@ -1269,7 +1274,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 @@ -3297,7 +3302,7 @@ class WitnessTableCacheEntry : } // end anonymous namespace using GenericWitnessTableCache = - LockingConcurrentMap; + MetadataCache; using LazyGenericWitnessTableCache = Lazy; /// Fetch the cache for a generic witness-table structure. diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index 1f764ab7fbfa6..fad75ec4ffc3c 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)...); } }; @@ -1255,9 +1277,10 @@ class VariadicMetadataCacheEntryBase : } }; -template +template class MetadataCache : - public LockingConcurrentMap { + public LockingConcurrentMap> { }; } // namespace swift From f029c5f119347485060a4a61e7f739671fc9d6a8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 24 Jul 2018 18:27:42 -0400 Subject: [PATCH 2/8] Support in-place value metadata initialization in the runtime. --- include/swift/ABI/Metadata.h | 108 ++++++++++- include/swift/ABI/MetadataValues.h | 12 ++ include/swift/Remote/MetadataReader.h | 15 +- include/swift/Runtime/Metadata.h | 7 + include/swift/Runtime/RuntimeFunctions.def | 7 + stdlib/public/runtime/Metadata.cpp | 206 ++++++++++++++++++++- 6 files changed, 340 insertions(+), 15 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index ab3cb49e91418..37c673ad29b4c 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -3158,6 +3158,14 @@ class TargetTypeContextDescriptor return TypeContextDescriptorFlags(this->Flags.getKindSpecificFlags()); } + /// Does this type have non-trivial "in place" metadata initialization? + /// + /// The type of the initialization-control structure differs by subclass, + /// so it doesn't appear here. + bool hasInPlaceMetadataInitialization() const { + return getTypeContextDescriptorFlags().hasInPlaceMetadataInitialization(); + } + const TargetTypeGenericContextDescriptorHeader & getFullGenericContextHeader() const; @@ -3503,10 +3511,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 +3565,30 @@ template class TargetStructDescriptor final : public TargetValueTypeDescriptor, public TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader> { + TargetTypeGenericContextDescriptorHeader, + TargetInPlaceValueMetadataInitialization> { +public: + using InPlaceMetadataInitialization = + TargetInPlaceValueMetadataInitialization; + private: using TrailingGenericContextObjects = TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader>; + TargetTypeGenericContextDescriptorHeader, + 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->hasInPlaceMetadataInitialization() ? 1 : 0; + } + public: using TrailingGenericContextObjects::getGenericContext; using TrailingGenericContextObjects::getGenericContextHeader; @@ -3546,6 +3607,11 @@ class TargetStructDescriptor final /// its stored properties. bool hasFieldOffsetVector() const { return FieldOffsetVectorOffset != 0; } + const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const{ + assert(this->hasInPlaceMetadataInitialization()); + return *this->template getTrailingObjects(); + } + static constexpr int32_t getGenericArgumentOffset() { return TargetStructMetadata::getGenericArgumentOffset(); } @@ -3561,16 +3627,30 @@ template class TargetEnumDescriptor final : public TargetValueTypeDescriptor, public TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader> { + TargetTypeGenericContextDescriptorHeader, + TargetInPlaceValueMetadataInitialization> { +public: + using InPlaceMetadataInitialization = + TargetInPlaceValueMetadataInitialization; + private: using TrailingGenericContextObjects = TrailingGenericContextObjects, - TargetTypeGenericContextDescriptorHeader>; + TargetTypeGenericContextDescriptorHeader, + 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->hasInPlaceMetadataInitialization() ? 1 : 0; + } + public: using TrailingGenericContextObjects::getGenericContext; using TrailingGenericContextObjects::getGenericContextHeader; @@ -3607,6 +3687,11 @@ class TargetEnumDescriptor final return TargetEnumMetadata::getGenericArgumentOffset(); } + const InPlaceMetadataInitialization &getInPlaceMetadataInitialization() const{ + assert(this->hasInPlaceMetadataInitialization()); + return *this->template getTrailingObjects(); + } + static bool classof(const TargetContextDescriptor *cd) { return cd->getKind() == ContextDescriptorKind::Enum; } @@ -3695,6 +3780,21 @@ TargetTypeContextDescriptor::getGenericParams() const { } } +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..8c833369a8edf 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1196,6 +1196,14 @@ class TypeContextDescriptorFlags : public FlagSet { /// declarations associated with the same declaration. IsSynthesizedRelatedEntity = 3, + /// Set if the type requires non-trivial but non-generic metadata + /// initialization. It may or may not be truly "in place" depending + /// on the kind of metadata. + /// + /// Currently only meaningful for value descriptors, but will be + /// extended to class descriptors. + HasInPlaceMetadataInitialization = 4, + /// Set if the context descriptor is includes metadata for dynamically /// constructing a class's vtables at metadata instantiation time. /// @@ -1231,6 +1239,10 @@ class TypeContextDescriptorFlags : public FlagSet { isSynthesizedRelatedEntity, setIsSynthesizedRelatedEntity) + FLAGSET_DEFINE_FLAG_ACCESSORS(HasInPlaceMetadataInitialization, + hasInPlaceMetadataInitialization, + setHasInPlaceMetadataInitialization) + FLAGSET_DEFINE_FLAG_ACCESSORS(Class_HasVTable, class_hasVTable, class_setHasVTable) diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index a87f538eeab01..d03aaddadf38e 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -1038,8 +1038,10 @@ class MetadataReader { sizeof(flags))) return nullptr; + TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags()); unsigned baseSize = 0; unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader); + unsigned inPlaceInitSize = 0; bool hasVTable = false; switch (auto kind = flags.getKind()) { case ContextDescriptorKind::Module: @@ -1055,16 +1057,23 @@ class MetadataReader { case ContextDescriptorKind::Class: baseSize = sizeof(TargetClassDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); - hasVTable = TypeContextDescriptorFlags(flags.getKindSpecificFlags()) - .class_hasVTable(); + hasVTable = typeFlags.class_hasVTable(); break; case ContextDescriptorKind::Enum: baseSize = sizeof(TargetEnumDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); + if (typeFlags.hasInPlaceMetadataInitialization()) { + inPlaceInitSize = + sizeof(TargetInPlaceValueMetadataInitialization); + } break; case ContextDescriptorKind::Struct: baseSize = sizeof(TargetStructDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); + if (typeFlags.hasInPlaceMetadataInitialization()) { + inPlaceInitSize = + sizeof(TargetInPlaceValueMetadataInitialization); + } break; default: // We don't know about this kind of context. @@ -1107,7 +1116,7 @@ class MetadataReader { + header.VTableSize * sizeof(TargetMethodDescriptor); } - unsigned size = baseSize + genericsSize + vtableSize; + unsigned size = baseSize + genericsSize + vtableSize + inPlaceInitSize; auto buffer = (uint8_t *)malloc(size); if (!Reader->readBytes(RemoteAddress(address), buffer, size)) { free(buffer); diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index ccc1283211b46..3f20380c31316 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 diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 24fa49f81b7da..0570cc2ad038d 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -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); diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 4122080f1596f..27f2e2d65934c 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -175,6 +175,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,14 +220,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(); @@ -533,6 +533,169 @@ 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 }; + } + + static const TypeContextDescriptor *getDescription(Metadata *type) { + return cast(type)->getDescription(); + } + + 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 ********************************************/ /***************************************************************************/ @@ -3474,6 +3637,10 @@ static Result performOnMetadataCache(const Metadata *metadata, } if (!description->isGeneric()) { + if (description->hasInPlaceMetadataInitialization()) { + return std::move(callbacks).forInPlaceMetadata(description); + } + return std::move(callbacks).forOtherMetadata(metadata); } @@ -3502,6 +3669,10 @@ bool swift::addToMetadataQueue(MetadataCompletionQueueEntry *queueEntry, return cache.enqueue(key, 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); } @@ -3525,6 +3696,10 @@ void swift::resumeMetadataCompletion(MetadataCompletionQueueEntry *queueEntry) { cache.resumeInitialization(key, QueueEntry); } + void forInPlaceMetadata(const TypeContextDescriptor *description) && { + InPlaceMetadata.get().resumeInitialization(description, QueueEntry); + } + void forTupleMetadata(const TupleTypeMetadata *metadata) { TupleTypes.get().resumeInitialization(metadata, QueueEntry); } @@ -3551,6 +3726,11 @@ MetadataResponse swift::swift_checkMetadataState(MetadataRequest request, return cache.await(key, Request); } + MetadataResponse forInPlaceMetadata( + const TypeContextDescriptor *description) && { + return InPlaceMetadata.get().await(description, Request); + } + MetadataResponse forTupleMetadata(const TupleTypeMetadata *metadata) { return TupleTypes.get().await(metadata, Request); } @@ -3634,6 +3814,11 @@ areAllTransitiveMetadataComplete_cheap(const Metadata *type) { return true; } + 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; @@ -3791,6 +3976,11 @@ checkMetadataDependency(MetadataDependency dependency) { return cache.checkDependency(key, Requirement); } + MetadataDependency forInPlaceMetadata( + const TypeContextDescriptor *description) && { + return InPlaceMetadata.get().checkDependency(description, Requirement); + } + MetadataDependency forTupleMetadata(const TupleTypeMetadata *metadata) { return TupleTypes.get().checkDependency(metadata, Requirement); } From 483e556d2f06717d4b5f9c283892bd90efc15ec8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Wed, 25 Jul 2018 03:39:04 -0400 Subject: [PATCH 3/8] Add a test case for an unresolvable dynamic metadata cycle. --- .../Inputs/resilient_generic_struct_v1.swift | 6 +++ .../Inputs/resilient_generic_struct_v2.swift | 8 ++++ ...unresolvable_dynamic_metadata_cycles.swift | 47 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 test/Interpreter/Inputs/resilient_generic_struct_v1.swift create mode 100644 test/Interpreter/Inputs/resilient_generic_struct_v2.swift create mode 100644 test/Interpreter/unresolvable_dynamic_metadata_cycles.swift 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/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 Date: Wed, 25 Jul 2018 02:40:06 -0400 Subject: [PATCH 4/8] Resolve metadata cycles through non-generic value types with resilient layout. The central thrust of this patch is to get these metadata initializations off of `swift_once` and onto the metadata-request system where we can properly detect and resolve dependencies. We do this by first introducing runtime support for resolving metadata requests for "in-place" initializations (committed previously) and then teaching IRGen to actually generate code to use them (this patch). A non-trivial amount of this patch is just renaming and refactoring some of existing infrastructure that was being used for in-place initializations to try to avoid unnecessary confusion. The remaining cases that are still using `swift_once` resolution of metadata initialization are: - non-generic classes that can't statically fill their superclass or have resilient internal layout - foreign type metadata Classes require more work because I'd like to switch at least the resilient-superclass case over to using a pattern much more like what we do with generic class instantiation. That is, I'd like in-place initialization to be reserved for classes that actually don't need relocation. Foreign metadata should also be updated to the request/dependency scheme before we declare ABI stability. I'm not sure why foreign metadata would ever require a type to be resolved, but let's assume it's possible. Fixes part of SR-7876. --- docs/ABI/Mangling.rst | 1 + include/swift/Demangling/DemangleNodes.def | 1 + include/swift/IRGen/Linking.h | 13 +- lib/Demangling/Demangler.cpp | 3 + lib/Demangling/NodePrinter.cpp | 5 + lib/Demangling/OldRemangler.cpp | 5 + lib/Demangling/Remangler.cpp | 5 + lib/IRGen/GenDecl.cpp | 61 ++- lib/IRGen/GenMeta.cpp | 357 +++++++++++++----- lib/IRGen/GenProto.cpp | 4 +- lib/IRGen/IRGenMangler.h | 5 + lib/IRGen/IRGenModule.h | 3 + lib/IRGen/Linking.cpp | 4 + lib/IRGen/MetadataRequest.cpp | 160 +++++--- lib/IRGen/MetadataRequest.h | 35 +- test/IRGen/enum_resilience.swift | 48 ++- test/IRGen/struct_resilience.swift | 7 +- .../resilient_metadata_cycles.swift | 28 ++ 18 files changed, 551 insertions(+), 194 deletions(-) create mode 100644 test/Interpreter/resilient_metadata_cycles.swift 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/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/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/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 0fce67f652f94..7287d91c9c66e 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -1498,6 +1498,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; @@ -1719,6 +1720,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 +2506,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 +2552,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(); } @@ -3275,8 +3275,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 +4323,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..8d0f5ad3f1fa4 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -121,6 +121,73 @@ 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); +} + /*****************************************************************************/ /** Nominal Type Descriptor Emission *****************************************/ /*****************************************************************************/ @@ -457,6 +524,7 @@ namespace { protected: NominalTypeDecl *Type; RequireMetadata_t HasMetadata; + bool HasInPlaceMetadataInitialization; using super::IGM; using super::B; @@ -468,8 +536,10 @@ namespace { TypeContextDescriptorBuilderBase(IRGenModule &IGM, NominalTypeDecl *Type, RequireMetadata_t requireMetadata) : super(IGM), Type(Type), - HasMetadata(requireMetadata) - {} + HasMetadata(requireMetadata), + HasInPlaceMetadataInitialization( + computeHasInPlaceMetadataInitialization()) { + } void layout() { super::layout(); @@ -478,6 +548,7 @@ namespace { // ABI TODO: layout info should be superseded by remote mirror metadata asImpl().addLayoutInfo(); asImpl().addGenericSignature(); + asImpl().maybeAddInPlaceMetadataInitialization(); } void addName() { @@ -589,6 +660,61 @@ namespace { } } + bool computeHasInPlaceMetadataInitialization() { + // Not if we don't have metadata. + if (!HasMetadata) + return false; + + // 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 false; + + return needsInPlaceMetadataInitialization(IGM, Type); + } + + bool hasInPlaceMetadataInitialization() { + return HasInPlaceMetadataInitialization; + } + + void setHasInPlaceMetadataInitialization(TypeContextDescriptorFlags &flags){ + flags.setHasInPlaceMetadataInitialization( + HasInPlaceMetadataInitialization); + } + + void maybeAddInPlaceMetadataInitialization() { + if (!HasInPlaceMetadataInitialization) + return; + + if (isa(Type) || isa(Type)) { + asImpl().addInPlaceValueMetadataInitialization(); + } else { + llvm_unreachable("unexpected type allowing in-place initialization"); + } + } + + /// 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 @@ -705,6 +831,8 @@ namespace { flags.setIsReflectable( !IGM.shouldEmitOpaqueTypeMetadataRecord(getType())); + setHasInPlaceMetadataInitialization(flags); + getClangImportedFlags(flags); return flags.getOpaqueValue(); } @@ -763,6 +891,8 @@ namespace { flags.setIsReflectable(Strategy.isReflectable()); + setHasInPlaceMetadataInitialization(flags); + getClangImportedFlags(flags); return flags.getOpaqueValue(); } @@ -1167,53 +1297,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 @@ -2080,6 +2183,7 @@ namespace { auto type =cast(Target->getDeclaredType()->getCanonicalType()); (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, + CacheStrategy::Lazy, [&](IRGenFunction &IGF, DynamicMetadataRequest request, llvm::Constant *cacheVar) -> MetadataResponse { // There's an interesting special case where we can do the @@ -2095,17 +2199,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,10 +2631,38 @@ IRGenFunction::emitValueWitnessTableRef(SILType type, // Value types (structs and enums) //===----------------------------------------------------------------------===// +namespace { + /// A helper class for laying out value metadata. + template + class ValueMetadataBuilderBase : public Base { + protected: + using Base::IGM; + using Base::Target; + using Base::asImpl; + + 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); + }); + } + }; +} + static llvm::Value * -emitInPlaceValueTypeMetadataInitialization(IRGenFunction &IGF, - CanNominalType type, - llvm::Value *metadata, +emitOnceValueTypeMetadataInitialization(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) || @@ -2553,27 +2685,46 @@ emitInPlaceValueTypeMetadataInitialization(IRGenFunction &IGF, return metadata; } -/// 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, + CacheStrategy::InPlaceInitialization, [&](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); - }); + 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. + (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition); +} + + //===----------------------------------------------------------------------===// // Structs //===----------------------------------------------------------------------===// @@ -2581,8 +2732,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 +2809,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 +2841,8 @@ namespace { } void createMetadataAccessFunction() { - createInPlaceValueTypeMetadataAccessFunction(IGM, Target); + createNonGenericMetadataAccessFunction(IGM, Target); + maybeCreateInPlaceMetadataInitialization(); } }; @@ -2784,18 +2949,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 +3004,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 +3074,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 +3097,6 @@ namespace { ConstantStructBuilder &B) : EnumMetadataBuilderBase(IGM, theEnum, B) {} - - void addPayloadSize() { auto payloadSize = getConstantPayloadSize(); if (!payloadSize) { @@ -2949,7 +3113,8 @@ namespace { } void createMetadataAccessFunction() { - createInPlaceValueTypeMetadataAccessFunction(IGM, Target); + createNonGenericMetadataAccessFunction(IGM, Target); + maybeCreateInPlaceMetadataInitialization(); } }; @@ -3001,18 +3166,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 @@ -3166,15 +3319,15 @@ namespace { auto type = cast(asImpl().getTargetType()); (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, + CacheStrategy::Lazy, [&](IRGenFunction &IGF, DynamicMetadataRequest request, llvm::Constant *cacheVariable) { - return emitInPlaceTypeMetadataAccessFunctionBody(IGF, type, - cacheVariable, + return emitOnceTypeMetadataAccessFunctionBody(IGF, type, cacheVariable, [&](IRGenFunction &IGF, llvm::Value *candidate) { MetadataDependencyCollector *collector = nullptr; auto metadata = uniqueForeignTypeMetadataRef(IGF, candidate); - return emitInPlaceValueTypeMetadataInitialization(IGF, type, + return emitOnceValueTypeMetadataInitialization(IGF, type, metadata, collector); }); diff --git a/lib/IRGen/GenProto.cpp b/lib/IRGen/GenProto.cpp index ea363780ea76e..f7dfce7f5be68 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, 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.h b/lib/IRGen/IRGenModule.h index 6d7621371545a..9e3256ffab6d1 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1156,6 +1156,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( 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/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 91600c4cb2cff..36cd55cc8f705 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -1283,16 +1283,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 +1311,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 +1328,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 +1393,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 +1444,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 +1587,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,12 +1625,12 @@ 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) { +irgen::emitOnceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, + CanNominalType type, + llvm::Constant *cacheVariable, + OnceMetadataInitializer initializer) { llvm::Constant *metadata = IGF.IGM.requiresForeignTypeMetadata(type) ? IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type) @@ -1634,9 +1657,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 +1675,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); } @@ -1727,6 +1750,7 @@ llvm::Function * irgen::getTypeMetadataAccessFunction(IRGenModule &IGM, CanType type, ForDefinition_t shouldDefine, + CacheStrategy cacheStrategy, MetadataAccessGenerator generator) { assert(!type->hasArchetype()); // Type should be bound unless it's type erased. @@ -1743,20 +1767,37 @@ irgen::getTypeMetadataAccessFunction(IRGenModule &IGM, 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); }); @@ -1769,6 +1810,7 @@ llvm::Function *irgen::getTypeMetadataAccessFunction(IRGenModule &IGM, CanType type, ForDefinition_t shouldDefine) { return getTypeMetadataAccessFunction(IGM, type, shouldDefine, + CacheStrategy::Lazy, [&](IRGenFunction &IGF, DynamicMetadataRequest request, llvm::Constant *cacheVariable) { @@ -1804,12 +1846,12 @@ 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; } diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index ec1beba276d01..ec5d1a83fed4c 100644 --- a/lib/IRGen/MetadataRequest.h +++ b/lib/IRGen/MetadataRequest.h @@ -476,9 +476,21 @@ using MetadataAccessGenerator = DynamicMetadataRequest request, llvm::Constant *cache)>; +enum class CacheStrategy { + /// No cache. + None, + + /// A simple lazy cache. + Lazy, + + /// An InPlaceValueMetadataCache initialization cache. + InPlaceInitialization, +}; + llvm::Function *getTypeMetadataAccessFunction(IRGenModule &IGM, CanType type, ForDefinition_t shouldDefine, + CacheStrategy cacheStrategy, MetadataAccessGenerator generator); llvm::Function * @@ -491,27 +503,28 @@ 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); +emitOnceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, + CanNominalType type, + llvm::Constant *cacheVariable, + OnceMetadataInitializer initializer); llvm::Value *uniqueForeignTypeMetadataRef(IRGenFunction &IGF, llvm::Value *candidate); -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/test/IRGen/enum_resilience.swift b/test/IRGen/enum_resilience.swift index 692f7c67214f9..60e4964ea7afa 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 +// 1310802 == 0x00140052 +// 0x0010 - HasInPlaceMetadataInitialization +// 0x0014 - IsReflectable +// 0x 0040 - IsUnique +// 0x 0012 - Enum +// CHECK-SAME: i32 1310802, +// 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,22 @@ 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: [[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: [[TUPLE_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata2([[INT]] 319, %swift.type* [[SIZE_METADATA]], %swift.type* [[SIZE_METADATA]], i8* null, i8** null) +// CHECK-NEXT: [[TUPLE_METADATA:%.*]] = extractvalue %swift.metadata_response [[TUPLE_RESPONSE]], 0 +// CHECK-NEXT: [[TUPLE_STATE:%.*]] = extractvalue %swift.metadata_response [[TUPLE_RESPONSE]], 1 +// CHECK-NEXT: [[T0:%.*]] = icmp ule [[INT]] [[TUPLE_STATE]], 63 +// CHECK-NEXT: br i1 [[T0]], label %[[SATISFIED2:.*]], label +// CHECK: [[SATISFIED2]]: +// CHECK: call void @swift_initEnumMetadataMultiPayload +// CHECK: phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ [[TUPLE_METADATA]], %[[SATISFIED1]] ], [ null, %[[SATISFIED2]] ] +// CHECK: phi [[INT]] [ 63, %entry ], [ 63, %[[SATISFIED1]] ], [ 0, %[[SATISFIED2]] ] public protocol Prot { diff --git a/test/IRGen/struct_resilience.swift b/test/IRGen/struct_resilience.swift index 9fec84ccbec34..61f5841fc2606 100644 --- a/test/IRGen/struct_resilience.swift +++ b/test/IRGen/struct_resilience.swift @@ -196,18 +196,20 @@ 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: [[FIELDS_ADDR:%.*]] = getelementptr inbounds [4 x i8**], [4 x i8**]* [[FIELDS]], i32 0, i32 0 // public let s: Size +// CHECK: call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319) // CHECK: [[FIELD_1:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 0 // CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_1]] // public let ss: (Size, Size) +// CHECK: call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata2([[INT]] 319, // CHECK: [[FIELD_2:%.*]] = getelementptr inbounds i8**, i8*** [[FIELDS_ADDR]], i32 1 // CHECK: store i8** [[SIZE_AND_ALIGNMENT:%.*]], i8*** [[FIELD_2]] @@ -219,9 +221,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/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() From c26012d75b49bf2c7d4fcfaf79e16ca6b94019e8 Mon Sep 17 00:00:00 2001 From: John McCall Date: Thu, 26 Jul 2018 04:01:57 -0400 Subject: [PATCH 5/8] Reorganize TypeContextDescriptorFlags to be a bit more semantic. Leave space for new kinds of non-generic metadata initialization (one of which I'm about to claim for "foreign") and non-default type namespaces. --- include/swift/ABI/MetadataValues.h | 124 +++++++++++++++++++++++------ lib/IRGen/GenMeta.cpp | 31 ++++---- test/IRGen/cf.sil | 2 +- test/IRGen/class_metadata.swift | 16 ++-- test/IRGen/class_resilience.swift | 2 +- test/IRGen/enum_resilience.swift | 8 +- test/IRGen/generic_classes.sil | 4 +- test/IRGen/generic_structs.sil | 4 +- test/IRGen/generic_types.swift | 2 +- test/IRGen/generic_vtable.swift | 6 +- 10 files changed, 137 insertions(+), 62 deletions(-) diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 8c833369a8edf..5d68b6e1cf93b 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1174,35 +1174,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 requires non-trivial but non-generic metadata - /// initialization. It may or may not be truly "in place" depending - /// on the kind of metadata. + /// 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. /// - /// Currently only meaningful for value descriptors, but will be - /// extended to class descriptors. - HasInPlaceMetadataInitialization = 4, + /// 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. @@ -1231,18 +1247,74 @@ class TypeContextDescriptorFlags : public FlagSet { 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, + + // 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; + } + + 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) - FLAGSET_DEFINE_FLAG_ACCESSORS(HasInPlaceMetadataInitialization, - hasInPlaceMetadataInitialization, - setHasInPlaceMetadataInitialization) - FLAGSET_DEFINE_FLAG_ACCESSORS(Class_HasVTable, class_hasVTable, class_setHasVTable) diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 8d0f5ad3f1fa4..2b74ca2674106 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -642,20 +642,25 @@ 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 { + void setClangImportedFlags(TypeContextDescriptorFlags &flags) { if (Type->getAttrs().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); } } } @@ -679,9 +684,11 @@ namespace { return HasInPlaceMetadataInitialization; } - void setHasInPlaceMetadataInitialization(TypeContextDescriptorFlags &flags){ - flags.setHasInPlaceMetadataInitialization( - HasInPlaceMetadataInitialization); + void setMetadataInitializationKind(TypeContextDescriptorFlags &flags) { + if (HasInPlaceMetadataInitialization) { + flags.setMetadataInitialization( + TypeContextDescriptorFlags::InPlaceMetadataInitialization); + } } void maybeAddInPlaceMetadataInitialization() { @@ -831,9 +838,7 @@ namespace { flags.setIsReflectable( !IGM.shouldEmitOpaqueTypeMetadataRecord(getType())); - setHasInPlaceMetadataInitialization(flags); - - getClangImportedFlags(flags); + setCommonFlags(flags); return flags.getOpaqueValue(); } }; @@ -891,9 +896,7 @@ namespace { flags.setIsReflectable(Strategy.isReflectable()); - setHasInPlaceMetadataInitialization(flags); - - getClangImportedFlags(flags); + setCommonFlags(flags); return flags.getOpaqueValue(); } }; @@ -950,6 +953,8 @@ namespace { // Classes are always reflectable. flags.setIsReflectable(true); + setCommonFlags(flags); + if (!getType()->isForeign()) { if (MetadataLayout->areImmediateMembersNegative()) flags.class_setAreImmediateMembersNegative(true); @@ -965,8 +970,6 @@ namespace { flags.class_setSuperclassReferenceKind(SuperClassRef->getKind()); } - getClangImportedFlags(flags); - return flags.getOpaqueValue(); } diff --git a/test/IRGen/cf.sil b/test/IRGen/cf.sil index 7916e58514bd4..3dedb898477a2 100644 --- a/test/IRGen/cf.sil +++ b/test/IRGen/cf.sil @@ -21,7 +21,7 @@ // CHECK-64: @"$SSo24CCMutableRefrigeratorRefaMn" = linkonce_odr hidden constant // -- is imported C typedef, is class, is nonunique -// CHECK-64-SAME: +// CHECK-64-SAME: // CHECK-64-SAME: [[MUTABLE_REFRIGERATOR_NAME]] // CHECK-64: @"$SSo24CCMutableRefrigeratorRefaN" = linkonce_odr hidden global <{ {{.*}} }> <{ diff --git a/test/IRGen/class_metadata.swift b/test/IRGen/class_metadata.swift index e1c978b076350..f88554360282d 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. 268501072 == 0x1001_0050 == Reflectable | IndirectSuperclass | Unique | Class +// CHECK-SAME: i32 268501072, // Parent. // CHECK-SAME: i32 {{.*}} @"$S14class_metadataMXM" // Name. diff --git a/test/IRGen/class_resilience.swift b/test/IRGen/class_resilience.swift index cb55adb24102f..e53779abbb8d9 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 60e4964ea7afa..eeefa58ecb0f0 100644 --- a/test/IRGen/enum_resilience.swift +++ b/test/IRGen/enum_resilience.swift @@ -48,12 +48,12 @@ import resilient_struct // CHECK-SAME: internal global { %swift.type*, i8* } zeroinitializer, align // CHECK: @"$S15enum_resilience24EnumWithResilientPayloadOMn" = {{.*}}constant -// 1310802 == 0x00140052 -// 0x0010 - HasInPlaceMetadataInitialization -// 0x0014 - IsReflectable +// 196690 == 0x00030052 +// 0x0002 - InPlaceMetadataInitialization +// 0x0001 - IsReflectable // 0x 0040 - IsUnique // 0x 0012 - Enum -// CHECK-SAME: i32 1310802, +// CHECK-SAME: i32 196690, // CHECK-SAME: @"$S15enum_resilience24EnumWithResilientPayloadOMl" // CHECK-SAME: @"$S15enum_resilience24EnumWithResilientPayloadOMf", i32 0, i32 1) // CHECK-SAME: @"$S15enum_resilience24EnumWithResilientPayloadOMr" 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 From 80b5ab80dce9a6619cb2dd9325672ea34ac5a48d Mon Sep 17 00:00:00 2001 From: John McCall Date: Fri, 27 Jul 2018 21:25:30 -0400 Subject: [PATCH 6/8] Reference runtime-only ObjC classes with bare strings. As part of this, rename TypeMetadataRecordKind to TypeReferenceKind and consistently give it three bits of storage. The better modelling of these type references appears to have been sufficient to make dynamic conformance checks succeed, which is good but unexpected. --- include/swift/ABI/Metadata.h | 141 +++++++++++------- include/swift/ABI/MetadataValues.h | 33 ++-- include/swift/Remote/MetadataReader.h | 27 ++-- lib/IRGen/GenDecl.cpp | 91 +++++++---- lib/IRGen/GenMeta.cpp | 9 +- lib/IRGen/IRGenModule.h | 8 +- stdlib/public/runtime/Metadata.cpp | 35 +++-- stdlib/public/runtime/ProtocolConformance.cpp | 71 +++++---- test/IRGen/class_metadata.swift | 4 +- test/IRGen/class_resilience.swift | 2 +- .../objc_runtime_visible_conformance.swift | 13 +- test/Interpreter/objc_runtime_visible.swift | 1 - 12 files changed, 277 insertions(+), 158 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index 37c673ad29b4c..c8cc685408381 100644 --- a/include/swift/ABI/Metadata.h +++ b/include/swift/ABI/Metadata.h @@ -2062,32 +2062,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 +2122,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 +2219,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 +2239,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 +2318,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 @@ -3344,7 +3375,7 @@ class TargetClassDescriptor final return !Superclass.isNull(); } - TypeMetadataRecordKind getSuperclassReferenceKind() const { + TypeReferenceKind getSuperclassReferenceKind() const { return getTypeContextDescriptorFlags().class_getSuperclassReferenceKind(); } diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 5d68b6e1cf93b..063a5a9b4059e 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); } @@ -1231,16 +1234,16 @@ 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: @@ -1327,7 +1330,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/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index d03aaddadf38e..b37688c9ba4d6 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 diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index 7287d91c9c66e..df279e6bc72fe 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -2684,40 +2684,77 @@ IRGenModule::getFunctionGOTEquivalent(LinkEntity entity, return {gotEquivalent, ConstantReference::Indirect}; } +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 ref = IGM.getAddrOfLLVMVariableOrGOTEquivalent( + entity, IGM.getPointerAlignment(), defaultTy); + + if (ref.isIndirect()) { + kind = TypeReferenceKind::IndirectNominalTypeDescriptor; + } + + 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"); + + 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) { - TypeMetadataRecordKind kind; - Optional entity; - llvm::Type *defaultTy; - 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"); - - 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 (!clas) { + return getTypeContextDescriptorEntityReference(*this, decl); } - auto ref = getAddrOfLLVMVariableOrGOTEquivalent( - *entity, getPointerAlignment(), defaultTy); + switch (clas->getForeignClassKind()) { + case ClassDecl::ForeignKind::RuntimeOnly: + return getRuntimeOnlyClassEntityReference(*this, clas); - // 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; - } + case ClassDecl::ForeignKind::CFType: + return getTypeContextDescriptorEntityReference(*this, clas); - return TypeEntityReference(kind, ref.getValue()); + 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 diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 2b74ca2674106..d22082be97599 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -3482,7 +3482,14 @@ namespace { bool IRGenModule::requiresForeignTypeMetadata(CanType type) { if (NominalTypeDecl *nominal = type->getAnyNominal()) { if (auto *clas = dyn_cast(nominal)) { - return clas->isForeign(); + 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(nominal->getModuleScopeContext()); diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index 9e3256ffab6d1..c2d6d1e41e898 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; } }; diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index 27f2e2d65934c..e9708f5c8138d 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"); } 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/class_metadata.swift b/test/IRGen/class_metadata.swift index f88554360282d..e958903124096 100644 --- a/test/IRGen/class_metadata.swift +++ b/test/IRGen/class_metadata.swift @@ -104,8 +104,8 @@ class D : E {} // CHECK: [[D_NAME:@.*]] = private constant [2 x i8] c"D\00" // CHECK-LABEL: @"$S14class_metadata1DCMn" = -// Flags. 268501072 == 0x1001_0050 == Reflectable | IndirectSuperclass | Unique | Class -// CHECK-SAME: i32 268501072, +// 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 e53779abbb8d9..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/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/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 From ce23fd40ef22c37814829ac2da10f3b1373b3c3f Mon Sep 17 00:00:00 2001 From: John McCall Date: Sat, 28 Jul 2018 04:21:26 -0400 Subject: [PATCH 7/8] Update the ABI for uniquing foreign type metadata. - `swift_getForeignTypeMetadata` is now a request/response function. - The initialization function is now a completion function, and the pointer to it has moved into the type descriptor. - The cache variable is no longer part of the ABI; it's an implementation detail of the access function. - The two points above mean that there is no special header on foreign type metadata and therefore that they can be marked constant when there isn't something about them that needs to be initialized. The only foreign-metadata initialization we actually do right now is of the superclass field of a foreign class, and since that relationship is a proper DAG, it's not actually possible to have recursive initialization problems. But this is the right long-term thing to do, and it removes one of the last two clients of once-based initialization. --- include/swift/ABI/Metadata.h | 241 ++++++------- include/swift/ABI/MetadataValues.h | 8 + include/swift/Remote/MetadataReader.h | 34 +- include/swift/Runtime/Metadata.h | 7 +- include/swift/Runtime/RuntimeFunctions.def | 6 +- lib/IRGen/ForeignClassMetadataVisitor.h | 8 - lib/IRGen/GenDecl.cpp | 7 +- lib/IRGen/GenMeta.cpp | 374 ++++++++++----------- lib/IRGen/GenMeta.h | 3 + lib/IRGen/GenProto.cpp | 14 +- lib/IRGen/IRGenModule.h | 3 - lib/IRGen/MetadataLayout.cpp | 3 +- lib/IRGen/MetadataRequest.cpp | 157 ++++----- lib/IRGen/MetadataRequest.h | 76 ++++- stdlib/public/runtime/Metadata.cpp | 335 +++++++++--------- stdlib/public/runtime/MetadataCache.h | 42 +++ test/IRGen/cf.sil | 43 ++- test/IRGen/foreign_types.sil | 9 +- test/IRGen/objc.swift | 4 +- test/IRGen/objc_ns_enum.swift | 8 +- 20 files changed, 704 insertions(+), 678 deletions(-) diff --git a/include/swift/ABI/Metadata.h b/include/swift/ABI/Metadata.h index c8cc685408381..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; @@ -3166,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 { @@ -3189,6 +3066,13 @@ 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, @@ -3197,6 +3081,16 @@ class TargetTypeContextDescriptor 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; @@ -3335,12 +3229,14 @@ class TargetClassDescriptor final public TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, /*additional trailing objects:*/ + TargetForeignMetadataInitialization, TargetVTableDescriptorHeader, TargetMethodDescriptor> { private: using TrailingGenericContextObjects = TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, + TargetForeignMetadataInitialization, TargetVTableDescriptorHeader, TargetMethodDescriptor>; @@ -3351,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; @@ -3438,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; } @@ -3450,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; } @@ -3597,8 +3504,12 @@ class TargetStructDescriptor final : public TargetValueTypeDescriptor, public TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, + /*additional trailing objects*/ + TargetForeignMetadataInitialization, TargetInPlaceValueMetadataInitialization> { public: + using ForeignMetadataInitialization = + TargetForeignMetadataInitialization; using InPlaceMetadataInitialization = TargetInPlaceValueMetadataInitialization; @@ -3606,6 +3517,7 @@ class TargetStructDescriptor final using TrailingGenericContextObjects = TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, InPlaceMetadataInitialization>; using TrailingObjects = @@ -3614,8 +3526,12 @@ class TargetStructDescriptor final 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; } @@ -3638,6 +3554,11 @@ 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(); @@ -3659,15 +3580,20 @@ class TargetEnumDescriptor final : public TargetValueTypeDescriptor, public TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, + /*additional trailing objects*/ + TargetForeignMetadataInitialization, TargetInPlaceValueMetadataInitialization> { public: using InPlaceMetadataInitialization = TargetInPlaceValueMetadataInitialization; + using ForeignMetadataInitialization = + TargetForeignMetadataInitialization; private: using TrailingGenericContextObjects = TrailingGenericContextObjects, TargetTypeGenericContextDescriptorHeader, + ForeignMetadataInitialization, InPlaceMetadataInitialization>; using TrailingObjects = @@ -3676,8 +3602,12 @@ class TargetEnumDescriptor final 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; } @@ -3718,6 +3648,11 @@ 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(); @@ -3811,6 +3746,24 @@ 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 { diff --git a/include/swift/ABI/MetadataValues.h b/include/swift/ABI/MetadataValues.h index 063a5a9b4059e..d4a19c6260ed5 100644 --- a/include/swift/ABI/MetadataValues.h +++ b/include/swift/ABI/MetadataValues.h @@ -1262,6 +1262,10 @@ class TypeContextDescriptorFlags : public FlagSet { /// "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. }; @@ -1276,6 +1280,10 @@ class TypeContextDescriptorFlags : public FlagSet { return getMetadataInitialization() == InPlaceMetadataInitialization; } + bool hasForeignMetadataInitialization() const { + return getMetadataInitialization() == ForeignMetadataInitialization; + } + enum ImportNamespaceKind { /// The type comes the default namespace for its language. DefaultNamespace = 0, diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index b37688c9ba4d6..360af5c1a0045 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -1050,8 +1050,22 @@ class MetadataReader { TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags()); unsigned baseSize = 0; unsigned genericHeaderSize = sizeof(GenericContextDescriptorHeader); - unsigned inPlaceInitSize = 0; + 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); @@ -1067,28 +1081,23 @@ class MetadataReader { baseSize = sizeof(TargetClassDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); hasVTable = typeFlags.class_hasVTable(); + metadataInitSize = readMetadataInitSize(); break; case ContextDescriptorKind::Enum: baseSize = sizeof(TargetEnumDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); - if (typeFlags.hasInPlaceMetadataInitialization()) { - inPlaceInitSize = - sizeof(TargetInPlaceValueMetadataInitialization); - } + metadataInitSize = readMetadataInitSize(); break; case ContextDescriptorKind::Struct: baseSize = sizeof(TargetStructDescriptor); genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader); - if (typeFlags.hasInPlaceMetadataInitialization()) { - inPlaceInitSize = - sizeof(TargetInPlaceValueMetadataInitialization); - } + 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... @@ -1115,7 +1124,8 @@ class MetadataReader { TargetVTableDescriptorHeader header; auto headerAddr = address + baseSize - + genericsSize; + + genericsSize + + metadataInitSize; if (!Reader->readBytes(RemoteAddress(headerAddr), (uint8_t*)&header, sizeof(header))) @@ -1125,7 +1135,7 @@ class MetadataReader { + header.VTableSize * sizeof(TargetMethodDescriptor); } - unsigned size = baseSize + genericsSize + vtableSize + inPlaceInitSize; + 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 3f20380c31316..d383a7e22f292 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -475,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 diff --git a/include/swift/Runtime/RuntimeFunctions.def b/include/swift/Runtime/RuntimeFunctions.def index 0570cc2ad038d..ac5b6fab8b041 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, 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 df279e6bc72fe..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. @@ -1507,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. @@ -1548,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; } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index d22082be97599..1f5fce6acca62 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -188,6 +188,20 @@ static void emitMetadataCompletionFunction(IRGenModule &IGM, 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 *****************************************/ /*****************************************************************************/ @@ -514,17 +528,18 @@ namespace { IGM.setTrueConstGlobal(var); } }; - - template + + template class TypeContextDescriptorBuilderBase : public ContextDescriptorBuilderBase { using super = ContextDescriptorBuilderBase; protected: - NominalTypeDecl *Type; + DeclType *Type; RequireMetadata_t HasMetadata; - bool HasInPlaceMetadataInitialization; + TypeContextDescriptorFlags::MetadataInitializationKind + MetadataInitialization; using super::IGM; using super::B; @@ -533,12 +548,11 @@ namespace { public: using super::addGenericSignature; - TypeContextDescriptorBuilderBase(IRGenModule &IGM, NominalTypeDecl *Type, + TypeContextDescriptorBuilderBase(IRGenModule &IGM, DeclType *Type, RequireMetadata_t requireMetadata) : super(IGM), Type(Type), HasMetadata(requireMetadata), - HasInPlaceMetadataInitialization( - computeHasInPlaceMetadataInitialization()) { + MetadataInitialization(computeMetadataInitialization()) { } void layout() { @@ -548,7 +562,7 @@ namespace { // ABI TODO: layout info should be superseded by remote mirror metadata asImpl().addLayoutInfo(); asImpl().addGenericSignature(); - asImpl().maybeAddInPlaceMetadataInitialization(); + asImpl().maybeAddMetadataInitialization(); } void addName() { @@ -558,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()); @@ -578,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() { @@ -651,7 +678,8 @@ namespace { /// Flags to indicate Clang-imported declarations so we mangle them /// consistently at runtime. void setClangImportedFlags(TypeContextDescriptorFlags &flags) { - if (Type->getAttrs().getAttribute()) { + if (Type->getAttrs() + .template getAttribute()) { flags.setIsSynthesizedRelatedEntity(true); } @@ -665,36 +693,56 @@ namespace { } } - bool computeHasInPlaceMetadataInitialization() { + TypeContextDescriptorFlags::MetadataInitializationKind + computeMetadataInitialization() { // Not if we don't have metadata. if (!HasMetadata) - return false; + 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 false; + return TypeContextDescriptorFlags::NoMetadataInitialization; - return needsInPlaceMetadataInitialization(IGM, Type); - } + if (needsInPlaceMetadataInitialization(IGM, Type)) + return TypeContextDescriptorFlags::InPlaceMetadataInitialization; - bool hasInPlaceMetadataInitialization() { - return HasInPlaceMetadataInitialization; + return TypeContextDescriptorFlags::NoMetadataInitialization; } void setMetadataInitializationKind(TypeContextDescriptorFlags &flags) { - if (HasInPlaceMetadataInitialization) { - flags.setMetadataInitialization( - TypeContextDescriptorFlags::InPlaceMetadataInitialization); - } + flags.setMetadataInitialization(MetadataInitialization); } - void maybeAddInPlaceMetadataInitialization() { - if (!HasInPlaceMetadataInitialization) + 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 { @@ -702,6 +750,20 @@ namespace { } } + /// 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. @@ -795,7 +857,8 @@ namespace { class StructContextDescriptorBuilder - : public TypeContextDescriptorBuilderBase + : public TypeContextDescriptorBuilderBase { using super = TypeContextDescriptorBuilderBase; @@ -844,7 +907,8 @@ namespace { }; class EnumContextDescriptorBuilder - : public TypeContextDescriptorBuilderBase + : public TypeContextDescriptorBuilderBase { using super = TypeContextDescriptorBuilderBase; @@ -902,7 +966,8 @@ namespace { }; class ClassContextDescriptorBuilder - : public TypeContextDescriptorBuilderBase, + : public TypeContextDescriptorBuilderBase, public SILVTableVisitor { using super = TypeContextDescriptorBuilderBase; @@ -2185,8 +2250,7 @@ namespace { assert(!Target->isGenericContext()); auto type =cast(Target->getDeclaredType()->getCanonicalType()); - (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, - CacheStrategy::Lazy, + (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 @@ -2662,32 +2726,6 @@ namespace { }; } -static llvm::Value * -emitOnceValueTypeMetadataInitialization(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); - } - } - - return metadata; -} - /// Create an access function for the given type which triggers the /// in-place initialization path. static void @@ -2696,11 +2734,11 @@ createInPlaceInitializationMetadataAccessFunction(IRGenModule &IGM, CanType type) { assert(!typeDecl->isGenericContext()); - (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition, - CacheStrategy::InPlaceInitialization, - [&](IRGenFunction &IGF, - DynamicMetadataRequest request, - llvm::Constant *cacheVariable) { + (void) createTypeMetadataAccessFunction(IGM, type, + CacheStrategy::InPlaceInitialization, + [&](IRGenFunction &IGF, + DynamicMetadataRequest request, + llvm::Constant *cacheVariable) { llvm::Value *descriptor = IGF.IGM.getAddrOfTypeContextDescriptor(typeDecl, RequireMetadata); auto responsePair = @@ -2724,7 +2762,7 @@ static void createNonGenericMetadataAccessFunction(IRGenModule &IGM, // Otherwise, use the lazy pattern, which should be emitted using a // direct reference to the metadata. - (void) getTypeMetadataAccessFunction(IGM, type, ForDefinition); + createDirectTypeMetadataAccessFunction(IGM, type, /*allow existing*/ false); } @@ -3243,6 +3281,7 @@ namespace { protected: using super::IGM; + using super::Target; using super::asImpl; using super::B; @@ -3250,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, - CacheStrategy::Lazy, - [&](IRGenFunction &IGF, - DynamicMetadataRequest request, - llvm::Constant *cacheVariable) { - return emitOnceTypeMetadataAccessFunctionBody(IGF, type, cacheVariable, - [&](IRGenFunction &IGF, llvm::Value *candidate) { - MetadataDependencyCollector *collector = nullptr; - auto metadata = uniqueForeignTypeMetadataRef(IGF, candidate); - return emitOnceValueTypeMetadataInitialization(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); }); } }; @@ -3358,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. @@ -3400,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() { @@ -3436,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()); @@ -3464,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()); @@ -3479,25 +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)) { - 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(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. @@ -3512,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 = [&] { @@ -3523,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); @@ -3544,6 +3540,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { ForeignClassMetadataBuilder builder(*this, classDecl, init); builder.layout(); addressPoint = builder.getOffsetOfAddressPoint(); + canBeConstant = builder.canBeConstant(); createCandidateVariable(); builder.createMetadataAccessFunction(); @@ -3556,6 +3553,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { ForeignStructMetadataBuilder builder(*this, structDecl, init); builder.layout(); addressPoint = builder.getOffsetOfAddressPoint(); + canBeConstant = builder.canBeConstant(); createCandidateVariable(); builder.createMetadataAccessFunction(); @@ -3566,6 +3564,7 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) { ForeignEnumMetadataBuilder builder(*this, enumDecl, init); builder.layout(); addressPoint = builder.getOffsetOfAddressPoint(); + canBeConstant = builder.canBeConstant(); createCandidateVariable(); builder.createMetadataAccessFunction(); @@ -3580,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 f7dfce7f5be68..2c7d93c388c7f 100644 --- a/lib/IRGen/GenProto.cpp +++ b/lib/IRGen/GenProto.cpp @@ -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/IRGenModule.h b/lib/IRGen/IRGenModule.h index c2d6d1e41e898..be5b9e1ebbb0e 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -1169,9 +1169,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/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 36cd55cc8f705..9edbcc8199c3f 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. @@ -1631,10 +1615,8 @@ irgen::emitOnceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, CanNominalType type, llvm::Constant *cacheVariable, OnceMetadataInitializer initializer) { - llvm::Constant *metadata = - IGF.IGM.requiresForeignTypeMetadata(type) - ? IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type) - : IGF.IGM.getAddrOfTypeMetadata(type); + assert(!requiresForeignTypeMetadata(type)); + llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type); // We might not have interesting initialization to do. assert((cacheVariable == nullptr) == @@ -1687,9 +1669,9 @@ irgen::emitOnceTypeMetadataAccessFunctionBody(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"); @@ -1717,54 +1699,61 @@ 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, - CacheStrategy cacheStrategy, - 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::Constant *cacheVariable = nullptr; @@ -1805,19 +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, - CacheStrategy::Lazy, - [&](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. @@ -1856,32 +1844,16 @@ irgen::getGenericTypeMetadataAccessFunction(IRGenModule &IGM, 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(); @@ -1916,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 @@ -1939,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"); } diff --git a/lib/IRGen/MetadataRequest.h b/lib/IRGen/MetadataRequest.h index ec5d1a83fed4c..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,9 +486,16 @@ 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 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 OnceMetadataInitializer = llvm::function_ref; @@ -512,9 +557,6 @@ emitOnceTypeMetadataAccessFunctionBody(IRGenFunction &IGF, llvm::Constant *cacheVariable, OnceMetadataInitializer initializer); -llvm::Value *uniqueForeignTypeMetadataRef(IRGenFunction &IGF, - llvm::Value *candidate); - using CacheEmitter = llvm::function_ref; diff --git a/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index e9708f5c8138d..e5ce35c7395b4 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -231,13 +231,6 @@ namespace { return { metadata, state }; } - 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) { @@ -246,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. @@ -582,10 +576,6 @@ namespace { return { metadata, state }; } - static const TypeContextDescriptor *getDescription(Metadata *type) { - return cast(type)->getDescription(); - } - TryInitializeResult tryInitialize(Metadata *metadata, PrivateMetadataState state, PrivateMetadataCompletionContext *context) { @@ -1611,6 +1601,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) { @@ -1651,20 +1672,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. @@ -3112,147 +3120,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. @@ -3643,16 +3626,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()) { - if (description->hasInPlaceMetadataInitialization()) { + 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); } - - return std::move(callbacks).forOtherMetadata(metadata); + swift_runtime_unreachable("bad metadata initialization kind"); } auto &generics = description->getFullGenericContextHeader(); @@ -3680,6 +3672,11 @@ 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); } @@ -3707,6 +3704,11 @@ 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); } @@ -3737,6 +3739,11 @@ 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); @@ -3782,6 +3789,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; @@ -3813,7 +3828,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, @@ -3825,6 +3840,17 @@ 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; @@ -3987,6 +4013,11 @@ 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); diff --git a/stdlib/public/runtime/MetadataCache.h b/stdlib/public/runtime/MetadataCache.h index fad75ec4ffc3c..e87d4d996ba1e 100644 --- a/stdlib/public/runtime/MetadataCache.h +++ b/stdlib/public/runtime/MetadataCache.h @@ -787,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)...); @@ -804,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, diff --git a/test/IRGen/cf.sil b/test/IRGen/cf.sil index 3dedb898477a2..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/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/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 { From f56af9b0b7455bbaa2475d8e9dbdc72ef63446dc Mon Sep 17 00:00:00 2001 From: John McCall Date: Sun, 29 Jul 2018 16:36:02 -0400 Subject: [PATCH 8/8] Add runtime functions to compute tuple layouts from element layouts. Previously, when a tuple type had non-fixed layout, we would compute a layout by building the metadata for that tuple type and then extracting the layout from the VWT. This can be quite expensive because it involves constructing the exact metadata for types like arrays and functions despite those types being fixed-layout across all instantiations. It also tends to cause unnecessary recursive-type issues, especially with enums where tuples are currently used to model cases with mutliple payloads. Since we just need a layout, computing it directly from element layouts instead of constructing metadata for the formal type lets us take advantage of all the other fast paths for layout construction, e.g. for fixed types and single-field aggregates. This is a good improvement overall, but it also serves to alleviate some of the problems of rdar://40810002 / SR-7876 in a way that might be suitable for integration to 4.2. --- include/swift/Runtime/Metadata.h | 40 ++++++ include/swift/Runtime/RuntimeFunctions.def | 28 +++++ lib/IRGen/IRGenModule.cpp | 10 ++ lib/IRGen/IRGenModule.h | 2 + lib/IRGen/MetadataRequest.cpp | 116 +++++++++++++++++- stdlib/public/runtime/Metadata.cpp | 37 ++++++ test/IRGen/enum_resilience.swift | 22 ++-- test/IRGen/struct_resilience.swift | 17 ++- test/IRGen/type_layout.swift | 35 ++++++ .../IRGen/type_layout_reference_storage.swift | 10 +- 10 files changed, 300 insertions(+), 17 deletions(-) diff --git a/include/swift/Runtime/Metadata.h b/include/swift/Runtime/Metadata.h index d383a7e22f292..59ff64a9f96f0 100644 --- a/include/swift/Runtime/Metadata.h +++ b/include/swift/Runtime/Metadata.h @@ -531,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 ac5b6fab8b041..9bec39c2662f8 100644 --- a/include/swift/Runtime/RuntimeFunctions.def +++ b/include/swift/Runtime/RuntimeFunctions.def @@ -765,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/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 be5b9e1ebbb0e..38ad05aa67010 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -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* diff --git a/lib/IRGen/MetadataRequest.cpp b/lib/IRGen/MetadataRequest.cpp index 9edbcc8199c3f..c1810ef5d184e 100644 --- a/lib/IRGen/MetadataRequest.cpp +++ b/lib/IRGen/MetadataRequest.cpp @@ -2126,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. @@ -2233,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; @@ -2340,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/stdlib/public/runtime/Metadata.cpp b/stdlib/public/runtime/Metadata.cpp index e5ce35c7395b4..55ba5647bfba0 100644 --- a/stdlib/public/runtime/Metadata.cpp +++ b/stdlib/public/runtime/Metadata.cpp @@ -1378,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, diff --git a/test/IRGen/enum_resilience.swift b/test/IRGen/enum_resilience.swift index eeefa58ecb0f0..7fef35f43cc6d 100644 --- a/test/IRGen/enum_resilience.swift +++ b/test/IRGen/enum_resilience.swift @@ -331,21 +331,27 @@ public func constructFullyFixed() -> FullyFixedLayout { } // 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: [[TUPLE_RESPONSE:%.*]] = call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata2([[INT]] 319, %swift.type* [[SIZE_METADATA]], %swift.type* [[SIZE_METADATA]], i8* null, i8** null) -// CHECK-NEXT: [[TUPLE_METADATA:%.*]] = extractvalue %swift.metadata_response [[TUPLE_RESPONSE]], 0 -// CHECK-NEXT: [[TUPLE_STATE:%.*]] = extractvalue %swift.metadata_response [[TUPLE_RESPONSE]], 1 -// CHECK-NEXT: [[T0:%.*]] = icmp ule [[INT]] [[TUPLE_STATE]], 63 -// CHECK-NEXT: br i1 [[T0]], label %[[SATISFIED2:.*]], label -// CHECK: [[SATISFIED2]]: +// 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 ], [ [[TUPLE_METADATA]], %[[SATISFIED1]] ], [ null, %[[SATISFIED2]] ] -// CHECK: phi [[INT]] [ 63, %entry ], [ 63, %[[SATISFIED1]] ], [ 0, %[[SATISFIED2]] ] +// CHECK: phi %swift.type* [ [[SIZE_METADATA]], %entry ], [ null, %[[SATISFIED1]] ] +// CHECK: phi [[INT]] [ 63, %entry ], [ 0, %[[SATISFIED1]] ] public protocol Prot { diff --git a/test/IRGen/struct_resilience.swift b/test/IRGen/struct_resilience.swift index 61f5841fc2606..20c8e85c41be5 100644 --- a/test/IRGen/struct_resilience.swift +++ b/test/IRGen/struct_resilience.swift @@ -198,20 +198,29 @@ public func resilientAny(s : ResilientWeakRef) { // 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: call swiftcc %swift.metadata_response @"$S16resilient_struct4SizeVMa"([[INT]] 319) +// 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: call swiftcc %swift.metadata_response @swift_getTupleTypeMetadata2([[INT]] 319, +// 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 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