Skip to content

Commit bac92e9

Browse files
authored
Merge pull request #32894 from jckarter/singleton-witness-table-instantiation
[WIP] Avoid creating a ConcurrentMap for nongeneric instantiated witness tables
2 parents 1db4120 + 88d9641 commit bac92e9

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
@@ -4596,12 +4596,13 @@ static void initializeResilientWitnessTable(
45964596
}
45974597
}
45984598

4599-
/// Instantiate a brand new witness table for a resilient or generic
4600-
/// protocol conformance.
4601-
WitnessTable *
4602-
WitnessTableCacheEntry::allocate(
4603-
const ProtocolConformanceDescriptor *conformance,
4604-
const void * const *instantiationArgs) {
4599+
// Instantiate a generic or resilient witness table into a `buffer`
4600+
// that has already been allocated of the appropriate size and zeroed out.
4601+
static WitnessTable *
4602+
instantiateWitnessTable(const Metadata *Type,
4603+
const ProtocolConformanceDescriptor *conformance,
4604+
const void * const *instantiationArgs,
4605+
void **fullTable) {
46054606
auto protocol = conformance->getProtocol();
46064607
auto genericTable = conformance->getGenericWitnessTable();
46074608

@@ -4617,12 +4618,6 @@ WitnessTableCacheEntry::allocate(
46174618
// Number of bytes for any private storage used by the conformance itself.
46184619
size_t privateSizeInWords = genericTable->getWitnessTablePrivateSizeInWords();
46194620

4620-
// Find the allocation.
4621-
void **fullTable = reinterpret_cast<void**>(this + 1);
4622-
4623-
// Zero out the witness table.
4624-
memset(fullTable, 0, getWitnessTableSize(conformance));
4625-
46264621
// Advance the address point; the private storage area is accessed via
46274622
// negative offsets.
46284623
auto table = fullTable + privateSizeInWords;
@@ -4678,6 +4673,57 @@ WitnessTableCacheEntry::allocate(
46784673

46794674
return castTable;
46804675
}
4676+
/// Instantiate a brand new witness table for a resilient or generic
4677+
/// protocol conformance.
4678+
WitnessTable *
4679+
WitnessTableCacheEntry::allocate(
4680+
const ProtocolConformanceDescriptor *conformance,
4681+
const void * const *instantiationArgs) {
4682+
// Find the allocation.
4683+
void **fullTable = reinterpret_cast<void**>(this + 1);
4684+
4685+
// Zero out the witness table.
4686+
memset(fullTable, 0, getWitnessTableSize(conformance));
4687+
4688+
// Instantiate the table.
4689+
return instantiateWitnessTable(Type, Conformance, instantiationArgs, fullTable);
4690+
}
4691+
4692+
/// Instantiate the witness table for a nondependent conformance that only has
4693+
/// one possible instantiation.
4694+
static WitnessTable *
4695+
getNondependentWitnessTable(const ProtocolConformanceDescriptor *conformance,
4696+
const Metadata *type) {
4697+
// Check whether the table has already been instantiated.
4698+
auto tablePtr = reinterpret_cast<std::atomic<WitnessTable*> *>(
4699+
conformance->getGenericWitnessTable()->PrivateData.get());
4700+
4701+
auto existingTable = tablePtr->load(SWIFT_MEMORY_ORDER_CONSUME);
4702+
if (existingTable) {
4703+
return existingTable;
4704+
}
4705+
4706+
// Allocate space for the table.
4707+
auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(conformance);
4708+
TaggedMetadataAllocator<SingletonGenericWitnessTableCacheTag> allocator;
4709+
auto buffer = (void **)allocator.Allocate(tableSize, alignof(void*));
4710+
memset(buffer, 0, tableSize);
4711+
4712+
// Instantiate the table.
4713+
auto table = instantiateWitnessTable(type, conformance, nullptr, buffer);
4714+
4715+
// See whether we can claim to be the one true table.
4716+
WitnessTable *orig = nullptr;
4717+
if (!tablePtr->compare_exchange_strong(orig, table, std::memory_order_release,
4718+
SWIFT_MEMORY_ORDER_CONSUME)) {
4719+
// Someone beat us to the punch. Throw away our table and return the
4720+
// existing one.
4721+
allocator.Deallocate(buffer);
4722+
return orig;
4723+
}
4724+
4725+
return table;
4726+
}
46814727

46824728
const WitnessTable *
46834729
swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
@@ -4699,11 +4745,24 @@ swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
46994745

47004746
// When there is no generic table, or it doesn't require instantiation,
47014747
// use the pattern directly.
4702-
// accessor directly.
47034748
auto genericTable = conformance->getGenericWitnessTable();
47044749
if (!genericTable || doesNotRequireInstantiation(conformance, genericTable)) {
47054750
return uniqueForeignWitnessTableRef(conformance->getWitnessTablePattern());
47064751
}
4752+
4753+
// If the conformance is not dependent on generic arguments in the conforming
4754+
// type, then there is only one instantiation possible, so we can try to
4755+
// allocate only the table without the concurrent map structure.
4756+
//
4757+
// TODO: There is no metadata flag that directly encodes the "nondependent"
4758+
// as of the Swift 5.3 ABI. However, we can check whether the conforming
4759+
// type is generic; a nongeneric type's conformance can never be dependent (at
4760+
// least, not today). However, a generic type conformance may also be
4761+
// nondependent if it
4762+
auto typeDescription = conformance->getTypeDescriptor();
4763+
if (typeDescription && !typeDescription->isGeneric()) {
4764+
return getNondependentWitnessTable(conformance, type);
4765+
}
47074766

47084767
auto &cache = getCache(genericTable);
47094768
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)