Skip to content

Commit d262acf

Browse files
committed
[Runtime] Support precomputed protocol conformances outside the shared cache.
Extend the support for precomputed protocol conformances in the shared cache. When available, we'll query dyld for conformances in optimized images loaded outside the shared cache. This is a relatively small addition; where we previously checked the shared cache, we now check both. The order is conditionalized on `scanSectionsBackwards` to preserve that behavior. We also rename some identifiers to fit this more expansive use of preoptimized conformances. rdar://87425446
1 parent f05f231 commit d262acf

File tree

1 file changed

+113
-22
lines changed

1 file changed

+113
-22
lines changed

stdlib/public/runtime/ProtocolConformance.cpp

Lines changed: 113 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,25 @@ _dyld_find_foreign_type_protocol_conformance(const void *protocol,
5353

5454
LLVM_ATTRIBUTE_WEAK
5555
uint32_t _dyld_swift_optimizations_version(void);
56-
#endif
56+
57+
#if DYLD_FIND_PROTOCOL_ON_DISK_CONFORMANCE_DEFINED
58+
// Redeclare these functions as weak as well.
59+
LLVM_ATTRIBUTE_WEAK bool _dyld_has_preoptimized_swift_protocol_conformances(
60+
const struct mach_header *mh);
61+
62+
LLVM_ATTRIBUTE_WEAK struct _dyld_protocol_conformance_result
63+
_dyld_find_protocol_conformance_on_disk(const void *protocolDescriptor,
64+
const void *metadataType,
65+
const void *typeDescriptor,
66+
uint32_t flags);
67+
68+
LLVM_ATTRIBUTE_WEAK struct _dyld_protocol_conformance_result
69+
_dyld_find_foreign_type_protocol_conformance_on_disk(
70+
const void *protocol, const char *foreignTypeIdentityStart,
71+
size_t foreignTypeIdentityLength, uint32_t flags);
72+
#endif // DYLD_FIND_PROTOCOL_ON_DISK_CONFORMANCE_DEFINED
73+
74+
#endif // __has_include(<mach-o/dyld_priv.h>)
5775

5876
// Set this to 1 to enable logging of calls to the dyld shared cache conformance
5977
// table
@@ -411,19 +429,20 @@ struct ConformanceState {
411429
uintptr_t dyldSharedCacheStart;
412430
uintptr_t dyldSharedCacheEnd;
413431
bool hasOverriddenImage;
414-
bool validateSharedCacheResults;
432+
bool validateDyldResults;
415433

416-
// Only populated when validateSharedCacheResults is enabled.
417-
ConcurrentReadableArray<ConformanceSection> SharedCacheSections;
434+
// Only populated when validateDyldResults is enabled.
435+
ConcurrentReadableArray<ConformanceSection> DyldOptimizedSections;
418436

419437
bool inSharedCache(const void *ptr) {
420438
auto uintPtr = reinterpret_cast<uintptr_t>(ptr);
421439
return dyldSharedCacheStart <= uintPtr && uintPtr < dyldSharedCacheEnd;
422440
}
423441

424-
bool sharedCacheOptimizationsActive() { return dyldSharedCacheStart != 0; }
442+
bool dyldOptimizationsActive() { return dyldSharedCacheStart != 0; }
425443
#else
426-
bool sharedCacheOptimizationsActive() { return false; }
444+
bool dyldOptimizationsActive() { return false; }
445+
427446
#endif
428447

429448
ConformanceState() {
@@ -441,7 +460,7 @@ struct ConformanceState {
441460
(uintptr_t)_dyld_get_shared_cache_range(&length);
442461
dyldSharedCacheEnd =
443462
dyldSharedCacheStart ? dyldSharedCacheStart + length : 0;
444-
validateSharedCacheResults = runtime::environment::
463+
validateDyldResults = runtime::environment::
445464
SWIFT_DEBUG_VALIDATE_SHARED_CACHE_PROTOCOL_CONFORMANCES();
446465
SHARED_CACHE_LOG("Shared cache range is %#lx-%#lx",
447466
dyldSharedCacheStart, dyldSharedCacheEnd);
@@ -557,10 +576,21 @@ void swift::addImageProtocolConformanceBlockCallbackUnsafe(
557576
if (C.inSharedCache(conformances)) {
558577
SHARED_CACHE_LOG("Skipping conformances section %p in the shared cache",
559578
conformances);
560-
if (C.validateSharedCacheResults)
561-
C.SharedCacheSections.push_back(
579+
if (C.validateDyldResults)
580+
C.DyldOptimizedSections.push_back(
581+
ConformanceSection{conformances, conformancesSize});
582+
return;
583+
#if DYLD_FIND_PROTOCOL_ON_DISK_CONFORMANCE_DEFINED
584+
} else if (_dyld_has_preoptimized_swift_protocol_conformances(
585+
reinterpret_cast<const mach_header *>(baseAddress))) {
586+
// dyld may optimize images outside the shared cache. Skip those too.
587+
SHARED_CACHE_LOG("Skipping conformances section %p optimized by dyld",
588+
conformances);
589+
if (C.validateDyldResults)
590+
C.DyldOptimizedSections.push_back(
562591
ConformanceSection{conformances, conformancesSize});
563592
return;
593+
#endif
564594
} else {
565595
SHARED_CACHE_LOG(
566596
"Adding conformances section %p outside the shared cache",
@@ -705,18 +735,18 @@ namespace {
705735
};
706736
}
707737

708-
static void validateSharedCacheResults(
738+
static void validateDyldResults(
709739
ConformanceState &C, const Metadata *type,
710740
const ProtocolDescriptor *protocol,
711741
const WitnessTable *dyldCachedWitnessTable,
712742
const ProtocolConformanceDescriptor *dyldCachedConformanceDescriptor,
713743
bool instantiateSuperclassMetadata) {
714744
#if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES
715-
if (!C.sharedCacheOptimizationsActive() || !C.validateSharedCacheResults)
745+
if (!C.dyldOptimizationsActive() || !C.validateDyldResults)
716746
return;
717747

718748
llvm::SmallVector<const ProtocolConformanceDescriptor *, 8> conformances;
719-
for (auto &section : C.SharedCacheSections.snapshot()) {
749+
for (auto &section : C.DyldOptimizedSections.snapshot()) {
720750
for (const auto &record : section) {
721751
auto &descriptor = *record.get();
722752
if (descriptor.getProtocol() != protocol)
@@ -765,15 +795,61 @@ static void validateSharedCacheResults(
765795
#endif
766796
}
767797

768-
/// Query the shared cache for a protocol conformance, if supported. The return
798+
#if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES
799+
static _dyld_protocol_conformance_result getDyldSharedCacheConformance(
800+
ConformanceState &C, const ProtocolDescriptor *protocol,
801+
const ClassMetadata *objcClassMetadata,
802+
const ContextDescriptor *description, llvm::StringRef foreignTypeIdentity) {
803+
if (!foreignTypeIdentity.empty()) {
804+
SHARED_CACHE_LOG(
805+
"_dyld_find_foreign_type_protocol_conformance(%p, %.*s, %zu)", protocol,
806+
(int)foreignTypeIdentity.size(), foreignTypeIdentity.data(),
807+
foreignTypeIdentity.size());
808+
return _dyld_find_foreign_type_protocol_conformance(
809+
protocol, foreignTypeIdentity.data(), foreignTypeIdentity.size());
810+
} else {
811+
SHARED_CACHE_LOG("_dyld_find_protocol_conformance(%p, %p, %p)", protocol,
812+
objcClassMetadata, description);
813+
return _dyld_find_protocol_conformance(protocol, objcClassMetadata,
814+
description);
815+
}
816+
}
817+
818+
static _dyld_protocol_conformance_result getDyldOnDiskConformance(
819+
ConformanceState &C, const ProtocolDescriptor *protocol,
820+
const ClassMetadata *objcClassMetadata,
821+
const ContextDescriptor *description, llvm::StringRef foreignTypeIdentity) {
822+
#if DYLD_FIND_PROTOCOL_ON_DISK_CONFORMANCE_DEFINED
823+
if (&_dyld_find_foreign_type_protocol_conformance_on_disk &&
824+
&_dyld_find_protocol_conformance_on_disk) {
825+
if (!foreignTypeIdentity.empty()) {
826+
SHARED_CACHE_LOG("_dyld_find_foreign_type_protocol_conformance_on_disk(%"
827+
"p, %.*s, %zu, 0)",
828+
protocol, (int)foreignTypeIdentity.size(),
829+
foreignTypeIdentity.data(), foreignTypeIdentity.size());
830+
return _dyld_find_foreign_type_protocol_conformance_on_disk(
831+
protocol, foreignTypeIdentity.data(), foreignTypeIdentity.size(), 0);
832+
} else {
833+
SHARED_CACHE_LOG("_dyld_find_protocol_conformance_on_disk(%p, %p, %p, 0)",
834+
protocol, objcClassMetadata, description);
835+
return _dyld_find_protocol_conformance_on_disk(
836+
protocol, objcClassMetadata, description, 0);
837+
}
838+
}
839+
#endif
840+
return {_dyld_protocol_conformance_result_kind_not_found, nullptr};
841+
}
842+
#endif
843+
844+
/// Query dyld for a protocol conformance, if supported. The return
769845
/// value is a tuple consisting of the found witness table (if any), the found
770846
/// conformance descriptor (if any), and a bool that's true if a failure is
771847
/// definitive.
772848
static std::tuple<const WitnessTable *, const ProtocolConformanceDescriptor *,
773849
bool>
774-
findSharedCacheConformance(ConformanceState &C, const Metadata *type,
775-
const ProtocolDescriptor *protocol,
776-
bool instantiateSuperclassMetadata) {
850+
findConformanceWithDyld(ConformanceState &C, const Metadata *type,
851+
const ProtocolDescriptor *protocol,
852+
bool instantiateSuperclassMetadata) {
777853
#if USE_DYLD_SHARED_CACHE_CONFORMANCE_TABLES
778854
const ContextDescriptor *description;
779855
llvm::StringRef foreignTypeIdentity;
@@ -787,6 +863,21 @@ findSharedCacheConformance(ConformanceState &C, const Metadata *type,
787863
typeName.data, protocol->Name.get());
788864
#endif
789865
_dyld_protocol_conformance_result dyldResult;
866+
if (C.scanSectionsBackwards) {
867+
// Search "on disk" first, then shared cache.
868+
dyldResult = getDyldOnDiskConformance(C, protocol, objcClassMetadata,
869+
description, foreignTypeIdentity);
870+
if (dyldResult.kind == _dyld_protocol_conformance_result_kind_not_found)
871+
dyldResult = getDyldSharedCacheConformance(
872+
C, protocol, objcClassMetadata, description, foreignTypeIdentity);
873+
} else {
874+
// In normal operation, search the shared cache first.
875+
dyldResult = getDyldSharedCacheConformance(
876+
C, protocol, objcClassMetadata, description, foreignTypeIdentity);
877+
if (dyldResult.kind == _dyld_protocol_conformance_result_kind_not_found)
878+
dyldResult = getDyldOnDiskConformance(C, protocol, objcClassMetadata,
879+
description, foreignTypeIdentity);
880+
}
790881
if (!foreignTypeIdentity.empty()) {
791882
SHARED_CACHE_LOG(
792883
"_dyld_find_foreign_type_protocol_conformance(%p, %.*s, %zu)", protocol,
@@ -876,14 +967,14 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
876967

877968
// Search the shared cache tables for a conformance for this type, and for
878969
// superclasses (if it's a class).
879-
if (C.sharedCacheOptimizationsActive()) {
970+
if (C.dyldOptimizationsActive()) {
880971
for (auto dyldSearchType : iterateMaybeIncompleteSuperclasses(
881972
type, instantiateSuperclassMetadata)) {
882973
bool definitiveFailure;
883974
std::tie(dyldCachedWitnessTable, dyldCachedConformanceDescriptor,
884975
definitiveFailure) =
885-
findSharedCacheConformance(C, dyldSearchType, protocol,
886-
instantiateSuperclassMetadata);
976+
findConformanceWithDyld(C, dyldSearchType, protocol,
977+
instantiateSuperclassMetadata);
887978

888979
if (definitiveFailure)
889980
return {nullptr, false};
@@ -892,9 +983,9 @@ swift_conformsToProtocolMaybeInstantiateSuperclasses(
892983
break;
893984
}
894985

895-
validateSharedCacheResults(C, type, protocol, dyldCachedWitnessTable,
896-
dyldCachedConformanceDescriptor,
897-
instantiateSuperclassMetadata);
986+
validateDyldResults(C, type, protocol, dyldCachedWitnessTable,
987+
dyldCachedConformanceDescriptor,
988+
instantiateSuperclassMetadata);
898989
// Return a cached result if we got a witness table. We can't do this if
899990
// scanSectionsBackwards is set, since a scanned conformance can override a
900991
// cached result in that case.

0 commit comments

Comments
 (0)