Skip to content

Commit c367c8e

Browse files
committed
[Runtime] Adjust "conforming type" based on a given conformance descriptor.
When we are looking for the specific type for a protocol conformance (e.g., because we may have a subclass of the type that declared conformances), don't go back through swift_conformsToProtocol() multiple times, which requires more lookups in the global conformance table. Instead, use the (known) protocol conformance descriptor.
1 parent 32749f3 commit c367c8e

File tree

4 files changed

+90
-81
lines changed

4 files changed

+90
-81
lines changed

stdlib/public/runtime/AnyHashableSupport.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ struct HashableConformanceEntry {
7070
// FIXME(performance): consider merging this cache into the regular
7171
// protocol conformance cache.
7272
static ConcurrentMap<HashableConformanceEntry, /*Destructor*/ false>
73-
HashableConformances;
73+
HashableConformances;
7474

7575
template<bool KnownToConformToHashable>
7676
LLVM_ATTRIBUTE_ALWAYS_INLINE
@@ -80,16 +80,18 @@ static const Metadata *findHashableBaseTypeImpl(const Metadata *type) {
8080
HashableConformances.find(HashableConformanceKey{type})) {
8181
return entry->baseTypeThatConformsToHashable;
8282
}
83-
if (!KnownToConformToHashable &&
84-
!swift_conformsToProtocol(type, &HashableProtocolDescriptor)) {
83+
84+
auto witnessTable =
85+
swift_conformsToProtocol(type, &HashableProtocolDescriptor);
86+
if (!KnownToConformToHashable && !witnessTable) {
8587
// Don't cache the negative response because we don't invalidate
8688
// this cache when a new conformance is loaded dynamically.
8789
return nullptr;
8890
}
8991
// By this point, `type` is known to conform to `Hashable`.
90-
92+
const auto *conformance = witnessTable->Description;
9193
const Metadata *baseTypeThatConformsToHashable =
92-
findConformingSuperclass(type, &HashableProtocolDescriptor);
94+
findConformingSuperclass(type, conformance);
9395
HashableConformances.getOrInsert(HashableConformanceKey{type},
9496
baseTypeThatConformsToHashable);
9597
return baseTypeThatConformsToHashable;

stdlib/public/runtime/Metadata.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4124,7 +4124,7 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,
41244124
// For a class, chase the superclass chain up until we hit the
41254125
// type that specified the conformance.
41264126
auto originalConformingType = findConformingSuperclass(conformingType,
4127-
protocol);
4127+
conformance);
41284128
SubstGenericParametersFromMetadata substitutions(originalConformingType);
41294129
assocTypeMetadata = _getTypeByMangledName(mangledName, substitutions);
41304130
}

stdlib/public/runtime/Private.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -452,11 +452,11 @@ class TypeInfo {
452452
ProtocolDescriptorRef protocol,
453453
const WitnessTable **conformance);
454454

455-
/// Given a type that we know conforms to the given protocol, find the
456-
/// superclass that introduced the conformance.
457-
const Metadata *findConformingSuperclass(const Metadata *type,
458-
const ProtocolDescriptor *protocol);
459-
455+
/// Given a type that we know can be used with the given conformance, find
456+
/// the superclass that introduced the conformance.
457+
const Metadata *findConformingSuperclass(
458+
const Metadata *type,
459+
const ProtocolConformanceDescriptor *conformance);
460460
} // end namespace swift
461461

462462
#endif /* SWIFT_RUNTIME_PRIVATE_H */

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 77 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -456,42 +456,74 @@ searchInConformanceCache(const Metadata *type,
456456
return ConformanceCacheResult::cacheMiss();
457457
}
458458

459-
/// Checks if a given candidate is a type itself, one of its
460-
/// superclasses or a related generic type.
461-
///
462-
/// This check is supposed to use the same logic that is used
463-
/// by searchInConformanceCache.
464-
///
465-
/// \param candidate Pointer to a Metadata or a NominalTypeDescriptor.
466-
///
467-
static
468-
bool isRelatedType(const Metadata *type, const void *candidate,
469-
bool candidateIsMetadata) {
459+
namespace {
460+
/// Describes a protocol conformance "candidate" that can be checked
461+
/// against the
462+
class ConformanceCandidate {
463+
const void *candidate;
464+
bool candidateIsMetadata;
470465

471-
while (true) {
472-
// Check whether the types match.
473-
if (candidateIsMetadata && type == candidate)
474-
return true;
466+
public:
467+
ConformanceCandidate() : candidate(0), candidateIsMetadata(false) { }
468+
469+
ConformanceCandidate(const ProtocolConformanceDescriptor &conformance)
470+
: ConformanceCandidate()
471+
{
472+
if (auto metadata = conformance.getCanonicalTypeMetadata()) {
473+
candidate = metadata;
474+
candidateIsMetadata = true;
475+
return;
476+
}
475477

476-
// Check whether the nominal type descriptors match.
477-
if (!candidateIsMetadata) {
478-
const auto *description = type->getTypeContextDescriptor();
479-
auto candidateDescription =
480-
static_cast<const TypeContextDescriptor *>(candidate);
481-
if (description && equalContexts(description, candidateDescription))
482-
return true;
478+
if (auto description = conformance.getTypeContextDescriptor()) {
479+
candidate = description;
480+
candidateIsMetadata = false;
481+
return;
482+
}
483483
}
484484

485-
// If there is a superclass, look there.
486-
if (auto superclass = _swift_class_getSuperclass(type)) {
487-
type = superclass;
488-
continue;
485+
/// Retrieve the conforming type as metadata, or NULL if the candidate's
486+
/// conforming type is described in another way (e.g., a nominal type
487+
/// descriptor).
488+
const Metadata *getConformingTypeAsMetadata() const {
489+
return candidateIsMetadata ? static_cast<const Metadata *>(candidate)
490+
: nullptr;
489491
}
490492

491-
break;
492-
}
493+
/// Whether the conforming type exactly matches the conformance candidate.
494+
bool matches(const Metadata *conformingType) const {
495+
// Check whether the types match.
496+
if (candidateIsMetadata && conformingType == candidate)
497+
return true;
493498

494-
return false;
499+
// Check whether the nominal type descriptors match.
500+
if (!candidateIsMetadata) {
501+
const auto *description = conformingType->getTypeContextDescriptor();
502+
auto candidateDescription =
503+
static_cast<const TypeContextDescriptor *>(candidate);
504+
if (description && equalContexts(description, candidateDescription))
505+
return true;
506+
}
507+
508+
return false;
509+
}
510+
511+
/// Retrieve the type that matches the conformance candidate, which may
512+
/// be a superclass of the given type. Returns null if this type does not
513+
/// match this conformance.
514+
const Metadata *getMatchingType(const Metadata *conformingType) const {
515+
while (conformingType) {
516+
// Check for a match.
517+
if (matches(conformingType))
518+
return conformingType;
519+
520+
// Look for a superclass.
521+
conformingType = _swift_class_getSuperclass(conformingType);
522+
}
523+
524+
return nullptr;
525+
}
526+
};
495527
}
496528

497529
static const WitnessTable *
@@ -541,38 +573,18 @@ swift_conformsToProtocolImpl(const Metadata * const type,
541573
for (const auto &record : section) {
542574
auto &descriptor = *record.get();
543575

544-
// If the record applies to a specific type, cache it.
545-
if (auto metadata = descriptor.getCanonicalTypeMetadata()) {
546-
auto P = descriptor.getProtocol();
547-
548-
// Look for an exact match.
549-
if (protocol != P)
550-
continue;
551-
552-
if (!isRelatedType(type, metadata, /*candidateIsMetadata=*/true))
553-
continue;
554-
555-
// Record the witness table.
556-
recordWitnessTable(descriptor, metadata);
557-
558-
// TODO: "Nondependent witness table" probably deserves its own flag.
559-
// An accessor function might still be necessary even if the witness table
560-
// can be shared.
561-
} else if (descriptor.getTypeKind()
562-
== TypeReferenceKind::DirectNominalTypeDescriptor ||
563-
descriptor.getTypeKind()
564-
== TypeReferenceKind::IndirectNominalTypeDescriptor) {
565-
auto R = descriptor.getTypeContextDescriptor();
566-
auto P = descriptor.getProtocol();
567-
568-
// Look for an exact match.
569-
if (protocol != P)
570-
continue;
576+
// We only care about conformances for this protocol.
577+
if (descriptor.getProtocol() != protocol)
578+
continue;
571579

572-
if (!isRelatedType(type, R, /*candidateIsMetadata=*/false))
573-
continue;
580+
// If there's a matching type, record the positive result.
581+
ConformanceCandidate candidate(descriptor);
582+
if (candidate.getMatchingType(type)) {
583+
const Metadata *matchingType = candidate.getConformingTypeAsMetadata();
584+
if (!matchingType)
585+
matchingType = type;
574586

575-
recordWitnessTable(descriptor, type);
587+
recordWitnessTable(descriptor, matchingType);
576588
}
577589
}
578590
}
@@ -692,18 +704,13 @@ bool swift::_checkGenericRequirements(
692704
}
693705

694706
const Metadata *swift::findConformingSuperclass(
695-
const Metadata *type,
696-
const ProtocolDescriptor *protocol) {
697-
const Metadata *conformingType = type;
698-
while (true) {
699-
const Metadata *superclass = _swift_class_getSuperclass(conformingType);
700-
if (!superclass)
701-
break;
702-
if (!swift_conformsToProtocol(superclass, protocol))
703-
break;
704-
conformingType = superclass;
705-
}
707+
const Metadata *type,
708+
const ProtocolConformanceDescriptor *conformance) {
709+
// Figure out which type we're looking for.
710+
ConformanceCandidate candidate(*conformance);
706711

712+
const Metadata *conformingType = candidate.getMatchingType(type);
713+
assert(conformingType);
707714
return conformingType;
708715
}
709716

0 commit comments

Comments
 (0)