Skip to content

Commit 48aa75d

Browse files
committed
[Isolated conformances] Cache resolved global actor for conformances
In the prior implementation of runtime resolution of isolated conformances, the runtime had to look in both the protocol conformance descriptor and in all conditional conformance requirements (recursively) to find any isolated conformances. If it found one, it had to demangle the global actor type to metadata. Since swift_conformsToProtocol is a hot path through the runtime, we can't afford this non-constant-time work in the common case. Instead, cache the resolved global actor and witness table as part of the conformance cache, so that we have access to this information every time we look up a witness table for a conformance. Propagate this up through various callers (e.g., generic requirement checking) to the point where we either stash it in the cache or check it at runtime. This gets us down to a very quick check (basically, NULL-or-not) for nonisolated conformances, and just one check for isolated conformances.
1 parent 6dd141a commit 48aa75d

File tree

8 files changed

+346
-272
lines changed

8 files changed

+346
-272
lines changed

include/swift/ABI/Metadata.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2884,8 +2884,16 @@ struct TargetProtocolConformanceDescriptor final
28842884
/// Get the witness table for the specified type, realizing it if
28852885
/// necessary, or return null if the conformance does not apply to the
28862886
/// type.
2887+
///
2888+
/// The globalActorIsolationType will be populated with the type of the global
2889+
/// actor to which this conformance is isolated, or NULL if this is a
2890+
/// nonisolated conformances. When it is isolated,
2891+
/// globalActorIsolationConformance is the conformance of
2892+
/// globalActorIsolationType to the GlobalActor protocol.
28872893
const swift::TargetWitnessTable<Runtime> *
2888-
getWitnessTable(const TargetMetadata<Runtime> *type) const;
2894+
getWitnessTable(const TargetMetadata<Runtime> *type,
2895+
const Metadata *&globalActorIsolationType,
2896+
const WitnessTable *&globalActorIsolationConformance) const;
28892897

28902898
/// Retrieve the resilient witnesses.
28912899
llvm::ArrayRef<ResilientWitness> getResilientWitnesses() const {

include/swift/Runtime/Casting.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,18 @@ const WitnessTable *
260260
swift_conformsToProtocolCommon(const Metadata *type,
261261
const ProtocolDescriptor *protocol);
262262

263+
/// Check whether a type conforms to a given native Swift protocol. This
264+
/// is similar to swift_conformsToProtocolCommon, but allows the caller to
265+
/// capture the global actor isolation of the conformance rather than
266+
/// checking that the code is currently executing on that global actor.
267+
SWIFT_RUNTIME_EXPORT
268+
const WitnessTable *
269+
swift_conformsToProtocolCommonIsolated(
270+
const Metadata *type,
271+
const ProtocolDescriptor *protocol,
272+
const Metadata **globalActorIsolationType,
273+
const WitnessTable **globalActorIsolationWitnessTable);
274+
263275
} // end namespace swift
264276

265277
#endif // SWIFT_RUNTIME_CASTING_H

stdlib/public/CompatibilityOverride/CompatibilityOverrideRuntime.def

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,16 @@ OVERRIDE_PROTOCOLCONFORMANCE(conformsToProtocolCommon, const WitnessTable *, , ,
193193
const ProtocolDescriptor *protocol),
194194
(type, protocol))
195195

196+
OVERRIDE_PROTOCOLCONFORMANCE(conformsToProtocolCommonIsolated,
197+
const WitnessTable *, , , swift::,
198+
(const Metadata * const type,
199+
const ProtocolDescriptor *protocol,
200+
const Metadata **globalActorIsolationType,
201+
const WitnessTable **
202+
globalActorIsolationWitnessTable),
203+
(type, protocol, globalActorIsolationType,
204+
globalActorIsolationWitnessTable))
205+
196206
OVERRIDE_KEYPATH(getKeyPath, const HeapObject *, , , swift::,
197207
(const void *pattern, const void *arguments),
198208
(pattern, arguments))

stdlib/public/runtime/Casting.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -529,13 +529,18 @@ static bool _unknownClassConformsToObjCProtocol(const OpaqueValue *value,
529529
}
530530
#endif
531531

532-
bool swift::_conformsToProtocol(const OpaqueValue *value,
533-
const Metadata *type,
534-
ProtocolDescriptorRef protocol,
535-
const WitnessTable **conformance) {
532+
bool swift::_conformsToProtocol(
533+
const OpaqueValue *value,
534+
const Metadata *type,
535+
ProtocolDescriptorRef protocol,
536+
const WitnessTable **conformance,
537+
const Metadata **globalActorIsolationType,
538+
const WitnessTable **globalActorIsolationWitnessTable) {
536539
// Look up the witness table for protocols that need them.
537540
if (protocol.needsWitnessTable()) {
538-
auto witness = swift_conformsToProtocolCommon(type, protocol.getSwiftProtocol());
541+
auto witness = swift_conformsToProtocolCommonIsolated(
542+
type, protocol.getSwiftProtocol(), globalActorIsolationType,
543+
globalActorIsolationWitnessTable);
539544
if (!witness)
540545
return false;
541546
if (conformance)
@@ -624,7 +629,8 @@ static bool _conformsToProtocols(const OpaqueValue *value,
624629
}
625630

626631
for (auto protocol : existentialType->getProtocols()) {
627-
if (!_conformsToProtocol(value, type, protocol, conformances))
632+
if (!_conformsToProtocol(
633+
value, type, protocol, conformances, nullptr, nullptr))
628634
return false;
629635
if (conformances != nullptr && protocol.needsWitnessTable()) {
630636
assert(*conformances != nullptr);

stdlib/public/runtime/DynamicCast.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,7 +1384,8 @@ static bool _conformsToProtocols(const OpaqueValue *value,
13841384
}
13851385

13861386
for (auto protocol : existentialType->getProtocols()) {
1387-
if (!swift::_conformsToProtocol(value, type, protocol, conformances))
1387+
if (!swift::_conformsToProtocol(value, type, protocol, conformances,
1388+
nullptr, nullptr))
13881389
return false;
13891390
if (conformances != nullptr && protocol.needsWitnessTable()) {
13901391
assert(*conformances != nullptr);
@@ -1879,7 +1880,8 @@ static DynamicCastResult tryCastToExtendedExistential(
18791880
},
18801881
[](const Metadata *type, unsigned index) -> const WitnessTable * {
18811882
swift_unreachable("Resolution of witness tables is not supported");
1882-
});
1883+
},
1884+
nullptr, nullptr);
18831885
if (error)
18841886
return DynamicCastResult::Failure;
18851887
}

stdlib/public/runtime/MetadataLookup.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,7 +1602,8 @@ _gatherGenericParameters(const ContextDescriptor *context,
16021602
},
16031603
[&substitutions](const Metadata *type, unsigned index) {
16041604
return substitutions.getWitnessTable(type, index);
1605-
});
1605+
},
1606+
nullptr, nullptr);
16061607
if (error)
16071608
return *error;
16081609

@@ -2043,7 +2044,8 @@ class DecodedMetadataBuilder {
20432044
},
20442045
[](const Metadata *type, unsigned index) -> const WitnessTable * {
20452046
swift_unreachable("never called");
2046-
});
2047+
},
2048+
nullptr, nullptr);
20472049
if (error)
20482050
return *error;
20492051

@@ -3056,7 +3058,8 @@ swift_distributed_getWitnessTables(GenericEnvironmentDescriptor *genericEnv,
30563058
},
30573059
[&substFn](const Metadata *type, unsigned index) {
30583060
return substFn.getWitnessTable(type, index);
3059-
});
3061+
},
3062+
nullptr, nullptr);
30603063

30613064
if (error) {
30623065
return {/*ptr=*/nullptr, -1};

stdlib/public/runtime/Private.h

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -567,14 +567,20 @@ class TypeInfo {
567567
/// generic requirements (e.g., those that need to be
568568
/// passed to an instantiation function) will be added to this vector.
569569
///
570+
/// \param globalActorIsolationType When non-NULL, the global actor isolation
571+
/// of these requirements will be reported through this OUT parameter.
572+
/// When NULL, any global actor isolation will be checked dynamically.
573+
///
570574
/// \returns the error if an error occurred, None otherwise.
571575
std::optional<TypeLookupError> _checkGenericRequirements(
572576
llvm::ArrayRef<GenericParamDescriptor> genericParams,
573577
llvm::ArrayRef<GenericRequirementDescriptor> requirements,
574578
llvm::SmallVectorImpl<const void *> &extraArguments,
575579
SubstGenericParameterFn substGenericParam,
576580
SubstGenericParameterOrdinalFn substGenericParamOrdinal,
577-
SubstDependentWitnessTableFn substWitnessTable);
581+
SubstDependentWitnessTableFn substWitnessTable,
582+
const Metadata **globalActorIsolationType,
583+
const WitnessTable **globalActorIsolationWitnessTable);
578584

579585
/// A helper function which avoids performing a store if the destination
580586
/// address already contains the source value. This is useful when
@@ -686,10 +692,19 @@ class TypeInfo {
686692
/// \param conformance - if non-null, and the protocol requires a
687693
/// witness table, and the type implements the protocol, the witness
688694
/// table will be placed here
689-
bool _conformsToProtocol(const OpaqueValue *value,
690-
const Metadata *type,
691-
ProtocolDescriptorRef protocol,
692-
const WitnessTable **conformance);
695+
/// \param globalActorIsolationType - when non-NULL and the conformance is
696+
/// global-actor-isolated, capture the global actor isolation type in this
697+
/// out variable rather than checking when we are executing on that global
698+
/// actor.
699+
/// \param globalActorIsolationWitnessTable - receives the witness table for
700+
/// *globalActorIsolationType's conformance to GlobalActor.
701+
bool _conformsToProtocol(
702+
const OpaqueValue *value,
703+
const Metadata *type,
704+
ProtocolDescriptorRef protocol,
705+
const WitnessTable **conformance,
706+
const Metadata **globalActorIsolationType,
707+
const WitnessTable **globalActorIsolationWitnessTable);
693708

694709
/// Construct type metadata for the given protocol.
695710
const Metadata *

0 commit comments

Comments
 (0)