Skip to content

Commit 7131885

Browse files
authored
Merge pull request #31780 from rjmccall/associated-witness-atomics-5.3
[5.3] Make the lazy assignment of an associated type/wtable atomic
2 parents f329eec + 29d8bfd commit 7131885

File tree

2 files changed

+80
-13
lines changed

2 files changed

+80
-13
lines changed

stdlib/public/runtime/Metadata.cpp

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4654,6 +4654,8 @@ static StringRef findAssociatedTypeName(const ProtocolDescriptor *protocol,
46544654
return StringRef();
46554655
}
46564656

4657+
using AssociatedTypeWitness = std::atomic<const Metadata *>;
4658+
46574659
SWIFT_CC(swift)
46584660
static MetadataResponse
46594661
swift_getAssociatedTypeWitnessSlowImpl(
@@ -4677,8 +4679,8 @@ swift_getAssociatedTypeWitnessSlowImpl(
46774679

46784680
// Retrieve the witness.
46794681
unsigned witnessIndex = assocType - reqBase;
4680-
auto *witnessAddr = &((const Metadata **)wtable)[witnessIndex];
4681-
auto witness = *witnessAddr;
4682+
auto *witnessAddr = &((AssociatedTypeWitness*)wtable)[witnessIndex];
4683+
auto witness = witnessAddr->load(std::memory_order_acquire);
46824684

46834685
#if SWIFT_PTRAUTH
46844686
uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator();
@@ -4781,8 +4783,14 @@ swift_getAssociatedTypeWitnessSlowImpl(
47814783
if (response.State == MetadataState::Complete) {
47824784
// We pass type metadata around as unsigned pointers, but we sign them
47834785
// in witness tables, which doesn't provide all that much extra security.
4784-
initAssociatedTypeProtocolWitness(witnessAddr, assocTypeMetadata,
4785-
*assocType);
4786+
auto valueToStore = assocTypeMetadata;
4787+
#if SWIFT_PTRAUTH
4788+
valueToStore = ptrauth_sign_unauthenticated(valueToStore,
4789+
swift_ptrauth_key_associated_type,
4790+
ptrauth_blend_discriminator(witnessAddr,
4791+
extraDiscriminator));
4792+
#endif
4793+
witnessAddr->store(valueToStore, std::memory_order_release);
47864794
}
47874795

47884796
return response;
@@ -4799,8 +4807,8 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,
47994807

48004808
// If the low bit of the witness is clear, it's already a metadata pointer.
48014809
unsigned witnessIndex = assocType - reqBase;
4802-
auto *witnessAddr = &((const void* *)wtable)[witnessIndex];
4803-
auto witness = *witnessAddr;
4810+
auto *witnessAddr = &((const AssociatedTypeWitness *)wtable)[witnessIndex];
4811+
auto witness = witnessAddr->load(std::memory_order_acquire);
48044812

48054813
#if SWIFT_PTRAUTH
48064814
uint16_t extraDiscriminator = assocType->Flags.getExtraDiscriminator();
@@ -4819,6 +4827,8 @@ swift::swift_getAssociatedTypeWitness(MetadataRequest request,
48194827
reqBase, assocType);
48204828
}
48214829

4830+
using AssociatedConformanceWitness = std::atomic<void *>;
4831+
48224832
SWIFT_CC(swift)
48234833
static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(
48244834
WitnessTable *wtable,
@@ -4844,8 +4854,8 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(
48444854

48454855
// Retrieve the witness.
48464856
unsigned witnessIndex = assocConformance - reqBase;
4847-
auto *witnessAddr = &((void**)wtable)[witnessIndex];
4848-
auto witness = *witnessAddr;
4857+
auto *witnessAddr = &((AssociatedConformanceWitness*)wtable)[witnessIndex];
4858+
auto witness = witnessAddr->load(std::memory_order_acquire);
48494859

48504860
#if SWIFT_PTRAUTH
48514861
// For associated protocols, the witness is signed with address
@@ -4903,9 +4913,18 @@ static const WitnessTable *swift_getAssociatedConformanceWitnessSlowImpl(
49034913

49044914
// The access function returns an unsigned pointer for now.
49054915

4906-
// We can't just use initAssociatedConformanceProtocolWitness because we
4907-
// also use this function for base protocols.
4908-
initProtocolWitness(witnessAddr, assocWitnessTable, *assocConformance);
4916+
auto valueToStore = assocWitnessTable;
4917+
#if SWIFT_PTRAUTH
4918+
if (assocConformance->Flags.isSignedWithAddress()) {
4919+
uint16_t extraDiscriminator =
4920+
assocConformance->Flags.getExtraDiscriminator();
4921+
valueToStore = ptrauth_sign_unauthenticated(valueToStore,
4922+
swift_ptrauth_key_associated_conformance,
4923+
ptrauth_blend_discriminator(witnessAddr,
4924+
extraDiscriminator));
4925+
}
4926+
#endif
4927+
witnessAddr->store(valueToStore, std::memory_order_release);
49094928

49104929
return assocWitnessTable;
49114930
}
@@ -4926,8 +4945,8 @@ const WitnessTable *swift::swift_getAssociatedConformanceWitness(
49264945

49274946
// Retrieve the witness.
49284947
unsigned witnessIndex = assocConformance - reqBase;
4929-
auto *witnessAddr = &((const void* *)wtable)[witnessIndex];
4930-
auto witness = *witnessAddr;
4948+
auto *witnessAddr = &((AssociatedConformanceWitness*)wtable)[witnessIndex];
4949+
auto witness = witnessAddr->load(std::memory_order_acquire);
49314950

49324951
#if SWIFT_PTRAUTH
49334952
uint16_t extraDiscriminator = assocConformance->Flags.getExtraDiscriminator();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// RUN: %target-swiftc_driver %s -g %import-libdispatch -o %t
2+
// RUN: %target-codesign %t
3+
// RUN: %target-run %t
4+
// REQUIRES: libdispatch
5+
// REQUIRES: executable_test
6+
7+
import Dispatch
8+
9+
protocol P {
10+
associatedtype Unused
11+
}
12+
struct A<T> : P {
13+
// We never actually read this associated type, but its presence in the
14+
// wtable should force it to be instantiated rather than shared for
15+
// all specializations of A.
16+
typealias Unused = A<A<T>>
17+
}
18+
19+
protocol Q {
20+
associatedtype Assoc: P
21+
22+
func foo()
23+
}
24+
struct B<T: P> : Q {
25+
// Both the metadata and the wtable for this associated type require
26+
// runtime instantiation and must be fetched lazily.
27+
typealias Assoc = A<T>
28+
29+
func foo() {}
30+
}
31+
32+
// It's possible that the optimizer might someday be able to recognize
33+
// that this is a no-op.
34+
func rundown<T: Q>(value: T, count: Int) {
35+
guard count > 0 else { return }
36+
37+
value.foo()
38+
39+
// Assuming that T is B<U> for some U, this constructs B<A<U>>,
40+
// which will be T in the recursive call; i.e. we should have a
41+
// different metadata/wtable pair each time. In order to construct
42+
// that, we have to read the associated metadata and wtable for T.Assoc.
43+
rundown(value: B<T.Assoc>(), count: count - 1)
44+
}
45+
46+
DispatchQueue.concurrentPerform(iterations: 5) { n in
47+
rundown(value: B<A<()>>(), count: 1000)
48+
}

0 commit comments

Comments
 (0)