Skip to content

Commit ec07330

Browse files
authored
Merge pull request #41185 from mikeash/protocol-conformance-dyld
[Runtime] Support precomputed protocol conformances outside the shared cache.
2 parents 8fc787e + d262acf commit ec07330

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)