Skip to content

Commit 1c32ea8

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 ecd83fa commit 1c32ea8

File tree

4 files changed

+89
-75
lines changed

4 files changed

+89
-75
lines changed

stdlib/public/runtime/AnyHashableSupport.cpp

Lines changed: 6 additions & 4 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 conformance =
85+
_conformsToSwiftProtocol(type, &HashableProtocolDescriptor, StringRef());
86+
if (!KnownToConformToHashable && !conformance) {
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`.
9092

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
@@ -4117,7 +4117,7 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,
41174117
// For a class, chase the superclass chain up until we hit the
41184118
// type that specified the conformance.
41194119
auto originalConformingType = findConformingSuperclass(conformingType,
4120-
protocol);
4120+
conformance);
41214121
SubstGenericParametersFromMetadata substitutions(originalConformingType);
41224122
assocTypeMetadata = _getTypeByMangledName(mangledName, substitutions);
41234123
}

stdlib/public/runtime/Private.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -464,10 +464,11 @@ class TypeInfo {
464464
const ProtocolDescriptor *protocol,
465465
StringRef module);
466466

467-
/// Given a type that we know conforms to the given protocol, find the
468-
/// superclass that introduced the conformance.
469-
const Metadata *findConformingSuperclass(const Metadata *type,
470-
const ProtocolDescriptor *protocol);
467+
/// Given a type that we know can be used with the given conformance, find
468+
/// the superclass that introduced the conformance.
469+
const Metadata *findConformingSuperclass(
470+
const Metadata *type,
471+
const ProtocolConformanceDescriptor *conformance);
471472

472473
} // end namespace swift
473474

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 77 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -513,42 +513,74 @@ searchInConformanceCache(const Metadata *type,
513513
return ConformanceCacheResult::cacheMiss();
514514
}
515515

516-
/// Checks if a given candidate is a type itself, one of its
517-
/// superclasses or a related generic type.
518-
///
519-
/// This check is supposed to use the same logic that is used
520-
/// by searchInConformanceCache.
521-
///
522-
/// \param candidate Pointer to a Metadata or a NominalTypeDescriptor.
523-
///
524-
static
525-
bool isRelatedType(const Metadata *type, const void *candidate,
526-
bool candidateIsMetadata) {
516+
namespace {
517+
/// Describes a protocol conformance "candidate" that can be checked
518+
/// against the
519+
class ConformanceCandidate {
520+
const void *candidate;
521+
bool candidateIsMetadata;
527522

528-
while (true) {
529-
// Check whether the types match.
530-
if (candidateIsMetadata && type == candidate)
531-
return true;
523+
public:
524+
ConformanceCandidate() : candidate(0), candidateIsMetadata(false) { }
525+
526+
ConformanceCandidate(const ProtocolConformanceDescriptor &conformance)
527+
: ConformanceCandidate()
528+
{
529+
if (auto metadata = conformance.getCanonicalTypeMetadata()) {
530+
candidate = metadata;
531+
candidateIsMetadata = true;
532+
return;
533+
}
532534

533-
// Check whether the nominal type descriptors match.
534-
if (!candidateIsMetadata) {
535-
const auto *description = type->getTypeContextDescriptor();
536-
auto candidateDescription =
537-
static_cast<const TypeContextDescriptor *>(candidate);
538-
if (description && equalContexts(description, candidateDescription))
539-
return true;
535+
if (auto description = conformance.getTypeContextDescriptor()) {
536+
candidate = description;
537+
candidateIsMetadata = false;
538+
return;
539+
}
540540
}
541541

542-
// If there is a superclass, look there.
543-
if (auto superclass = _swift_class_getSuperclass(type)) {
544-
type = superclass;
545-
continue;
542+
/// Retrieve the conforming type as metadata, or NULL if the candidate's
543+
/// conforming type is described in another way (e.g., a nominal type
544+
/// descriptor).
545+
const Metadata *getConformingTypeAsMetadata() const {
546+
return candidateIsMetadata ? static_cast<const Metadata *>(candidate)
547+
: nullptr;
546548
}
547549

548-
break;
549-
}
550+
/// Whether the conforming type exactly matches the conformance candidate.
551+
bool matches(const Metadata *conformingType) const {
552+
// Check whether the types match.
553+
if (candidateIsMetadata && conformingType == candidate)
554+
return true;
550555

551-
return false;
556+
// Check whether the nominal type descriptors match.
557+
if (!candidateIsMetadata) {
558+
const auto *description = conformingType->getTypeContextDescriptor();
559+
auto candidateDescription =
560+
static_cast<const TypeContextDescriptor *>(candidate);
561+
if (description && equalContexts(description, candidateDescription))
562+
return true;
563+
}
564+
565+
return false;
566+
}
567+
568+
/// Retrieve the type that matches the conformance candidate, which may
569+
/// be a superclass of the given type. Returns null if this type does not
570+
/// match this conformance.
571+
const Metadata *getMatchingType(const Metadata *conformingType) const {
572+
while (conformingType) {
573+
// Check for a match.
574+
if (matches(conformingType))
575+
return conformingType;
576+
577+
// Look for a superclass.
578+
conformingType = _swift_class_getSuperclass(conformingType);
579+
}
580+
581+
return nullptr;
582+
}
583+
};
552584
}
553585

554586
const ProtocolConformanceDescriptor *
@@ -661,34 +693,18 @@ swift::_conformsToSwiftProtocol(const Metadata * const type,
661693
for (const auto &record : section) {
662694
auto &descriptor = *record.get();
663695

664-
// If the record applies to a specific type, cache it.
665-
if (auto metadata = descriptor.getCanonicalTypeMetadata()) {
666-
auto P = descriptor.getProtocol();
667-
668-
// Look for an exact match.
669-
if (protocol != P)
670-
continue;
671-
672-
if (!isRelatedType(type, metadata, /*candidateIsMetadata=*/true))
673-
continue;
674-
675-
// Record the conformance descriptor.
676-
recordResult(descriptor, metadata);
677-
} else if (descriptor.getTypeKind()
678-
== TypeReferenceKind::DirectNominalTypeDescriptor ||
679-
descriptor.getTypeKind()
680-
== TypeReferenceKind::IndirectNominalTypeDescriptor) {
681-
auto R = descriptor.getTypeContextDescriptor();
682-
auto P = descriptor.getProtocol();
683-
684-
// Look for an exact match.
685-
if (protocol != P)
686-
continue;
696+
// We only care about conformances to the same protocol.
697+
if (descriptor.getProtocol() != protocol)
698+
continue;
687699

688-
if (!isRelatedType(type, R, /*candidateIsMetadata=*/false))
689-
continue;
700+
// If there's a matching type, record the positive result.
701+
ConformanceCandidate candidate(descriptor);
702+
if (candidate.getMatchingType(type)) {
703+
const Metadata *matchingType = candidate.getConformingTypeAsMetadata();
704+
if (!matchingType)
705+
matchingType = type;
690706

691-
recordResult(descriptor, type);
707+
recordResult(descriptor, matchingType);
692708
}
693709
}
694710
}
@@ -928,18 +944,13 @@ bool swift::_checkGenericRequirements(
928944
}
929945

930946
const Metadata *swift::findConformingSuperclass(
931-
const Metadata *type,
932-
const ProtocolDescriptor *protocol) {
933-
const Metadata *conformingType = type;
934-
while (true) {
935-
const Metadata *superclass = _swift_class_getSuperclass(conformingType);
936-
if (!superclass)
937-
break;
938-
if (!swift_conformsToProtocol(superclass, protocol))
939-
break;
940-
conformingType = superclass;
941-
}
947+
const Metadata *type,
948+
const ProtocolConformanceDescriptor *conformance) {
949+
// Figure out which type we're looking for.
950+
ConformanceCandidate candidate(*conformance);
942951

952+
const Metadata *conformingType = candidate.getMatchingType(type);
953+
assert(conformingType);
943954
return conformingType;
944955
}
945956

0 commit comments

Comments
 (0)