Skip to content

Commit 88d9641

Browse files
committed
Avoid creating a ConcurrentMap for nongeneric instantiated witness tables
1 parent e74e914 commit 88d9641

File tree

2 files changed

+73
-13
lines changed

2 files changed

+73
-13
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4535,12 +4535,13 @@ static void initializeResilientWitnessTable(
45354535
}
45364536
}
45374537

4538-
/// Instantiate a brand new witness table for a resilient or generic
4539-
/// protocol conformance.
4540-
WitnessTable *
4541-
WitnessTableCacheEntry::allocate(
4542-
const ProtocolConformanceDescriptor *conformance,
4543-
const void * const *instantiationArgs) {
4538+
// Instantiate a generic or resilient witness table into a `buffer`
4539+
// that has already been allocated of the appropriate size and zeroed out.
4540+
static WitnessTable *
4541+
instantiateWitnessTable(const Metadata *Type,
4542+
const ProtocolConformanceDescriptor *conformance,
4543+
const void * const *instantiationArgs,
4544+
void **fullTable) {
45444545
auto protocol = conformance->getProtocol();
45454546
auto genericTable = conformance->getGenericWitnessTable();
45464547

@@ -4556,12 +4557,6 @@ WitnessTableCacheEntry::allocate(
45564557
// Number of bytes for any private storage used by the conformance itself.
45574558
size_t privateSizeInWords = genericTable->getWitnessTablePrivateSizeInWords();
45584559

4559-
// Find the allocation.
4560-
void **fullTable = reinterpret_cast<void**>(this + 1);
4561-
4562-
// Zero out the witness table.
4563-
memset(fullTable, 0, getWitnessTableSize(conformance));
4564-
45654560
// Advance the address point; the private storage area is accessed via
45664561
// negative offsets.
45674562
auto table = fullTable + privateSizeInWords;
@@ -4617,6 +4612,57 @@ WitnessTableCacheEntry::allocate(
46174612

46184613
return castTable;
46194614
}
4615+
/// Instantiate a brand new witness table for a resilient or generic
4616+
/// protocol conformance.
4617+
WitnessTable *
4618+
WitnessTableCacheEntry::allocate(
4619+
const ProtocolConformanceDescriptor *conformance,
4620+
const void * const *instantiationArgs) {
4621+
// Find the allocation.
4622+
void **fullTable = reinterpret_cast<void**>(this + 1);
4623+
4624+
// Zero out the witness table.
4625+
memset(fullTable, 0, getWitnessTableSize(conformance));
4626+
4627+
// Instantiate the table.
4628+
return instantiateWitnessTable(Type, Conformance, instantiationArgs, fullTable);
4629+
}
4630+
4631+
/// Instantiate the witness table for a nondependent conformance that only has
4632+
/// one possible instantiation.
4633+
static WitnessTable *
4634+
getNondependentWitnessTable(const ProtocolConformanceDescriptor *conformance,
4635+
const Metadata *type) {
4636+
// Check whether the table has already been instantiated.
4637+
auto tablePtr = reinterpret_cast<std::atomic<WitnessTable*> *>(
4638+
conformance->getGenericWitnessTable()->PrivateData.get());
4639+
4640+
auto existingTable = tablePtr->load(SWIFT_MEMORY_ORDER_CONSUME);
4641+
if (existingTable) {
4642+
return existingTable;
4643+
}
4644+
4645+
// Allocate space for the table.
4646+
auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(conformance);
4647+
TaggedMetadataAllocator<SingletonGenericWitnessTableCacheTag> allocator;
4648+
auto buffer = (void **)allocator.Allocate(tableSize, alignof(void*));
4649+
memset(buffer, 0, tableSize);
4650+
4651+
// Instantiate the table.
4652+
auto table = instantiateWitnessTable(type, conformance, nullptr, buffer);
4653+
4654+
// See whether we can claim to be the one true table.
4655+
WitnessTable *orig = nullptr;
4656+
if (!tablePtr->compare_exchange_strong(orig, table, std::memory_order_release,
4657+
SWIFT_MEMORY_ORDER_CONSUME)) {
4658+
// Someone beat us to the punch. Throw away our table and return the
4659+
// existing one.
4660+
allocator.Deallocate(buffer);
4661+
return orig;
4662+
}
4663+
4664+
return table;
4665+
}
46204666

46214667
const WitnessTable *
46224668
swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
@@ -4638,11 +4684,24 @@ swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
46384684

46394685
// When there is no generic table, or it doesn't require instantiation,
46404686
// use the pattern directly.
4641-
// accessor directly.
46424687
auto genericTable = conformance->getGenericWitnessTable();
46434688
if (!genericTable || doesNotRequireInstantiation(conformance, genericTable)) {
46444689
return uniqueForeignWitnessTableRef(conformance->getWitnessTablePattern());
46454690
}
4691+
4692+
// If the conformance is not dependent on generic arguments in the conforming
4693+
// type, then there is only one instantiation possible, so we can try to
4694+
// allocate only the table without the concurrent map structure.
4695+
//
4696+
// TODO: There is no metadata flag that directly encodes the "nondependent"
4697+
// as of the Swift 5.3 ABI. However, we can check whether the conforming
4698+
// type is generic; a nongeneric type's conformance can never be dependent (at
4699+
// least, not today). However, a generic type conformance may also be
4700+
// nondependent if it
4701+
auto typeDescription = conformance->getTypeDescriptor();
4702+
if (typeDescription && !typeDescription->isGeneric()) {
4703+
return getNondependentWitnessTable(conformance, type);
4704+
}
46464705

46474706
auto &cache = getCache(genericTable);
46484707
auto result = cache.getOrInsert(type, conformance, instantiationArgs);

stdlib/public/runtime/MetadataAllocatorTags.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ TAG(ForeignMetadataCache, 15)
4141
TAG(GenericWitnessTableCache, 16)
4242
TAG(GenericClassMetadata, 17)
4343
TAG(GenericValueMetadata, 18)
44+
TAG(SingletonGenericWitnessTableCache, 19)
4445

4546
#undef TAG

0 commit comments

Comments
 (0)