Skip to content

Commit de1bbf6

Browse files
authored
Avoid creating a ConcurrentMap for nongeneric instantiated witness tables (swiftlang#32936)
1 parent 8f0ea35 commit de1bbf6

File tree

1 file changed

+72
-13
lines changed

1 file changed

+72
-13
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4512,12 +4512,13 @@ static void initializeResilientWitnessTable(
45124512
}
45134513
}
45144514

4515-
/// Instantiate a brand new witness table for a resilient or generic
4516-
/// protocol conformance.
4517-
WitnessTable *
4518-
WitnessTableCacheEntry::allocate(
4519-
const ProtocolConformanceDescriptor *conformance,
4520-
const void * const *instantiationArgs) {
4515+
// Instantiate a generic or resilient witness table into a `buffer`
4516+
// that has already been allocated of the appropriate size and zeroed out.
4517+
static WitnessTable *
4518+
instantiateWitnessTable(const Metadata *Type,
4519+
const ProtocolConformanceDescriptor *conformance,
4520+
const void * const *instantiationArgs,
4521+
void **fullTable) {
45214522
auto protocol = conformance->getProtocol();
45224523
auto genericTable = conformance->getGenericWitnessTable();
45234524

@@ -4533,12 +4534,6 @@ WitnessTableCacheEntry::allocate(
45334534
// Number of bytes for any private storage used by the conformance itself.
45344535
size_t privateSizeInWords = genericTable->getWitnessTablePrivateSizeInWords();
45354536

4536-
// Find the allocation.
4537-
void **fullTable = reinterpret_cast<void**>(this + 1);
4538-
4539-
// Zero out the witness table.
4540-
memset(fullTable, 0, getWitnessTableSize(conformance));
4541-
45424537
// Advance the address point; the private storage area is accessed via
45434538
// negative offsets.
45444539
auto table = fullTable + privateSizeInWords;
@@ -4594,6 +4589,57 @@ WitnessTableCacheEntry::allocate(
45944589

45954590
return castTable;
45964591
}
4592+
/// Instantiate a brand new witness table for a resilient or generic
4593+
/// protocol conformance.
4594+
WitnessTable *
4595+
WitnessTableCacheEntry::allocate(
4596+
const ProtocolConformanceDescriptor *conformance,
4597+
const void * const *instantiationArgs) {
4598+
// Find the allocation.
4599+
void **fullTable = reinterpret_cast<void**>(this + 1);
4600+
4601+
// Zero out the witness table.
4602+
memset(fullTable, 0, getWitnessTableSize(conformance));
4603+
4604+
// Instantiate the table.
4605+
return instantiateWitnessTable(Type, Conformance, instantiationArgs, fullTable);
4606+
}
4607+
4608+
/// Instantiate the witness table for a nondependent conformance that only has
4609+
/// one possible instantiation.
4610+
static WitnessTable *
4611+
getNondependentWitnessTable(const ProtocolConformanceDescriptor *conformance,
4612+
const Metadata *type) {
4613+
// Check whether the table has already been instantiated.
4614+
auto tablePtr = reinterpret_cast<std::atomic<WitnessTable*> *>(
4615+
conformance->getGenericWitnessTable()->PrivateData.get());
4616+
4617+
auto existingTable = tablePtr->load(SWIFT_MEMORY_ORDER_CONSUME);
4618+
if (existingTable) {
4619+
return existingTable;
4620+
}
4621+
4622+
// Allocate space for the table.
4623+
auto tableSize = WitnessTableCacheEntry::getWitnessTableSize(conformance);
4624+
MetadataAllocator allocator;
4625+
auto buffer = (void **)allocator.Allocate(tableSize, alignof(void*));
4626+
memset(buffer, 0, tableSize);
4627+
4628+
// Instantiate the table.
4629+
auto table = instantiateWitnessTable(type, conformance, nullptr, buffer);
4630+
4631+
// See whether we can claim to be the one true table.
4632+
WitnessTable *orig = nullptr;
4633+
if (!tablePtr->compare_exchange_strong(orig, table, std::memory_order_release,
4634+
SWIFT_MEMORY_ORDER_CONSUME)) {
4635+
// Someone beat us to the punch. Throw away our table and return the
4636+
// existing one.
4637+
allocator.Deallocate(buffer);
4638+
return orig;
4639+
}
4640+
4641+
return table;
4642+
}
45974643

45984644
const WitnessTable *
45994645
swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
@@ -4615,11 +4661,24 @@ swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
46154661

46164662
// When there is no generic table, or it doesn't require instantiation,
46174663
// use the pattern directly.
4618-
// accessor directly.
46194664
auto genericTable = conformance->getGenericWitnessTable();
46204665
if (!genericTable || doesNotRequireInstantiation(conformance, genericTable)) {
46214666
return uniqueForeignWitnessTableRef(conformance->getWitnessTablePattern());
46224667
}
4668+
4669+
// If the conformance is not dependent on generic arguments in the conforming
4670+
// type, then there is only one instantiation possible, so we can try to
4671+
// allocate only the table without the concurrent map structure.
4672+
//
4673+
// TODO: There is no metadata flag that directly encodes the "nondependent"
4674+
// as of the Swift 5.3 ABI. However, we can check whether the conforming
4675+
// type is generic; a nongeneric type's conformance can never be dependent (at
4676+
// least, not today). However, a generic type conformance may also be
4677+
// nondependent if it
4678+
auto typeDescription = conformance->getTypeDescriptor();
4679+
if (typeDescription && !typeDescription->isGeneric()) {
4680+
return getNondependentWitnessTable(conformance, type);
4681+
}
46234682

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

0 commit comments

Comments
 (0)