Skip to content

Commit 72b56ec

Browse files
committed
Runtime: New mechanism for resilient witness table instantiation
1 parent d1c6e1d commit 72b56ec

File tree

7 files changed

+290
-97
lines changed

7 files changed

+290
-97
lines changed

Diff for: include/swift/Runtime/Metadata.h

+55-4
Original file line numberDiff line numberDiff line change
@@ -2008,10 +2008,6 @@ struct TargetProtocolDescriptor {
20082008
/// as the requirements.
20092009
RelativeDirectPointer<const char, /*Nullable=*/true> AssociatedTypeNames;
20102010

2011-
void *getDefaultWitness(unsigned index) const {
2012-
return Requirements.get()[index].DefaultImplementation.get();
2013-
}
2014-
20152011
// This is only used in unittests/Metadata.cpp.
20162012
constexpr TargetProtocolDescriptor<Runtime>(const char *Name,
20172013
const TargetProtocolDescriptorList<Runtime> *Inherited,
@@ -2273,6 +2269,57 @@ struct TargetGenericBoxHeapMetadata : public TargetBoxHeapMetadata<Runtime> {
22732269
};
22742270
using GenericBoxHeapMetadata = TargetGenericBoxHeapMetadata<InProcess>;
22752271

2272+
/// \brief The control structure of a generic or resilient protocol
2273+
/// conformance witness.
2274+
///
2275+
/// Resilient conformances must use a pattern where new requirements
2276+
/// with default implementations can be added and the order of existing
2277+
/// requirements can be changed.
2278+
///
2279+
/// This is accomplished by emitting an order-independent series of
2280+
/// relative pointer pairs, consisting of a protocol requirement together
2281+
/// with a witness. The requirement is identified by an indirect relative
2282+
/// pointer to the protocol dispatch thunk.
2283+
template <typename Runtime>
2284+
struct TargetResilientWitness {
2285+
RelativeIndirectPointer<void> Function;
2286+
RelativeDirectPointer<void> Witness;
2287+
};
2288+
using ResilientWitness = TargetResilientWitness<InProcess>;
2289+
2290+
template <typename Runtime>
2291+
struct TargetResilientWitnessTable final
2292+
: public swift::ABI::TrailingObjects<
2293+
TargetResilientWitnessTable<Runtime>,
2294+
TargetResilientWitness<Runtime>> {
2295+
uint32_t NumWitnesses;
2296+
2297+
using TrailingObjects = swift::ABI::TrailingObjects<
2298+
TargetResilientWitnessTable<Runtime>,
2299+
TargetResilientWitness<Runtime>>;
2300+
friend TrailingObjects;
2301+
2302+
template<typename T>
2303+
using OverloadToken = typename TrailingObjects::template OverloadToken<T>;
2304+
2305+
size_t numTrailingObjects(
2306+
OverloadToken<TargetResilientWitness<Runtime>>) const {
2307+
return NumWitnesses;
2308+
}
2309+
2310+
llvm::ArrayRef<TargetResilientWitness<Runtime>>
2311+
getWitnesses() const {
2312+
return {this->template getTrailingObjects<TargetResilientWitness<Runtime>>(),
2313+
NumWitnesses};
2314+
}
2315+
2316+
const TargetResilientWitness<Runtime> &
2317+
getWitness(unsigned i) const {
2318+
return getWitnesses()[i];
2319+
}
2320+
};
2321+
using ResilientWitnessTable = TargetResilientWitnessTable<InProcess>;
2322+
22762323
/// \brief The control structure of a generic or resilient protocol
22772324
/// conformance.
22782325
///
@@ -2302,6 +2349,10 @@ struct TargetGenericWitnessTable {
23022349
/// The pattern.
23032350
RelativeDirectPointer<const TargetWitnessTable<Runtime>> Pattern;
23042351

2352+
/// The resilient witness table, if any.
2353+
RelativeDirectPointer<const TargetResilientWitnessTable<Runtime>,
2354+
/*nullable*/ true> ResilientWitnesses;
2355+
23052356
/// The instantiation function, which is called after the template is copied.
23062357
RelativeDirectPointer<void(TargetWitnessTable<Runtime> *instantiatedTable,
23072358
const TargetMetadata<Runtime> *type,

Diff for: lib/IRGen/GenDecl.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -4052,6 +4052,8 @@ llvm::StructType *IRGenModule::getGenericWitnessTableCacheTy() {
40524052
RelativeAddressTy,
40534053
// Pattern
40544054
RelativeAddressTy,
4055+
// ResilientWitnesses
4056+
RelativeAddressTy,
40554057
// Instantiator
40564058
RelativeAddressTy,
40574059
// PrivateData

Diff for: lib/IRGen/GenProto.cpp

+13-3
Original file line numberDiff line numberDiff line change
@@ -1829,20 +1829,28 @@ void WitnessTableBuilder::buildAccessFunction(llvm::Constant *wtable) {
18291829
// /// The size of the witness table in words.
18301830
// uint16_t WitnessTableSizeInWords;
18311831
//
1832-
// /// The amount to copy from the pattern in words. The rest is zeroed.
1833-
// uint16_t WitnessTableSizeInWordsToCopy;
1832+
// /// The amount of private storage to allocate before the address point,
1833+
// /// in words. This memory is zeroed out in the instantiated witness table
1834+
// /// template.
1835+
// uint16_t WitnessTablePrivateSizeInWords;
18341836
//
18351837
// /// The protocol.
18361838
// RelativeIndirectablePointer<ProtocolDescriptor> Protocol;
18371839
//
18381840
// /// The pattern.
18391841
// RelativeDirectPointer<WitnessTable> WitnessTable;
18401842
//
1843+
// /// The resilient witness table, if any.
1844+
// RelativeDirectPointer<const TargetResilientWitnessTable<Runtime>,
1845+
// /*nullable*/ true> ResilientWitnesses;
1846+
//
18411847
// /// The instantiation function, which is called after the template is copied.
18421848
// RelativeDirectPointer<void(WitnessTable *, const Metadata *, void * const *)>
18431849
// Instantiator;
18441850
//
1845-
// void *PrivateData[swift::NumGenericMetadataPrivateDataWords];
1851+
// /// Private data for the instantiator. Out-of-line so that the rest
1852+
// /// of this structure can be constant.
1853+
// RelativeDirectPointer<PrivateDataType> PrivateData;
18461854
// };
18471855

18481856
// First, create the global. We have to build this in two phases because
@@ -1876,6 +1884,8 @@ void WitnessTableBuilder::buildAccessFunction(llvm::Constant *wtable) {
18761884
cacheData.addRelativeAddress(descriptorRef);
18771885
// RelativePointer<WitnessTable>
18781886
cacheData.addRelativeAddress(wtable);
1887+
// RelativePointer<ResilientWitnesses>
1888+
cacheData.addInt(IGM.Int32Ty, 0);
18791889
// Instantiation function
18801890
cacheData.addRelativeAddressOrNull(instantiationFn);
18811891
// Private data

Diff for: stdlib/public/runtime/Metadata.cpp

+57-7
Original file line numberDiff line numberDiff line change
@@ -3176,6 +3176,7 @@ static GenericWitnessTableCache &getCache(GenericWitnessTable *gen) {
31763176
/// conformance.
31773177
static bool doesNotRequireInstantiation(GenericWitnessTable *genericTable) {
31783178
if (genericTable->Instantiator.isNull() &&
3179+
genericTable->ResilientWitnesses.isNull() &&
31793180
genericTable->WitnessTablePrivateSizeInWords == 0 &&
31803181
genericTable->WitnessTableSizeInWords ==
31813182
(genericTable->Protocol->NumRequirements +
@@ -3186,6 +3187,51 @@ static bool doesNotRequireInstantiation(GenericWitnessTable *genericTable) {
31863187
return false;
31873188
}
31883189

3190+
/// Initialize witness table entries from order independent resilient
3191+
/// witnesses stored in the generic witness table structure itself.
3192+
static void initializeResilientWitnessTable(GenericWitnessTable *genericTable,
3193+
void **table) {
3194+
auto protocol = genericTable->Protocol.get();
3195+
3196+
auto requirements = protocol->Requirements.get();
3197+
auto witnesses = genericTable->ResilientWitnesses->getWitnesses();
3198+
3199+
for (size_t i = 0, e = protocol->NumRequirements; i < e; ++i) {
3200+
auto &reqt = requirements[i];
3201+
3202+
// Only function-like requirements are filled in from the
3203+
// resilient witness table.
3204+
switch (reqt.Flags.getKind()) {
3205+
case ProtocolRequirementFlags::Kind::Method:
3206+
case ProtocolRequirementFlags::Kind::Init:
3207+
case ProtocolRequirementFlags::Kind::Getter:
3208+
case ProtocolRequirementFlags::Kind::Setter:
3209+
case ProtocolRequirementFlags::Kind::MaterializeForSet:
3210+
break;
3211+
case ProtocolRequirementFlags::Kind::BaseProtocol:
3212+
case ProtocolRequirementFlags::Kind::AssociatedTypeAccessFunction:
3213+
case ProtocolRequirementFlags::Kind::AssociatedConformanceAccessFunction:
3214+
continue;
3215+
}
3216+
3217+
void *fn = reqt.Function.get();
3218+
void *impl = reqt.DefaultImplementation.get();
3219+
3220+
// Find the witness if there is one, otherwise we use the default.
3221+
for (auto &witness : witnesses) {
3222+
if (witness.Function.get() == fn) {
3223+
impl = witness.Witness.get();
3224+
break;
3225+
}
3226+
}
3227+
3228+
assert(impl != nullptr && "no implementation for witness");
3229+
3230+
unsigned witnessIndex = WitnessTableFirstRequirementOffset + i;
3231+
table[witnessIndex] = impl;
3232+
}
3233+
}
3234+
31893235
/// Instantiate a brand new witness table for a resilient or generic
31903236
/// protocol conformance.
31913237
WitnessTable *
@@ -3222,13 +3268,17 @@ WitnessTableCacheEntry::allocate(GenericWitnessTable *genericTable,
32223268
}
32233269

32243270
// Fill in any default requirements.
3225-
for (size_t i = numPatternWitnesses, e = numRequirements; i < e; ++i) {
3226-
size_t requirementIndex = i - WitnessTableFirstRequirementOffset;
3227-
void *defaultImpl =
3228-
requirements[requirementIndex].DefaultImplementation.get();
3229-
assert(defaultImpl &&
3230-
"no default implementation for missing requirement");
3231-
table[i] = defaultImpl;
3271+
if (genericTable->ResilientWitnesses)
3272+
initializeResilientWitnessTable(genericTable, table);
3273+
else {
3274+
for (size_t i = numPatternWitnesses, e = numRequirements; i < e; ++i) {
3275+
size_t requirementIndex = i - WitnessTableFirstRequirementOffset;
3276+
void *defaultImpl =
3277+
requirements[requirementIndex].DefaultImplementation.get();
3278+
assert(defaultImpl &&
3279+
"no default implementation for missing requirement");
3280+
table[i] = defaultImpl;
3281+
}
32323282
}
32333283

32343284
auto castTable = reinterpret_cast<WitnessTable*>(table);

Diff for: test/IRGen/associated_type_witness.swift

+15-3
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,19 @@ struct Pair<T, U> : P, Q {}
100100
// GLOBAL-LABEL: @"$S23associated_type_witness8ComputedVyxq_GAA8AssockedAAWG" = internal constant %swift.generic_witness_table_cache {
101101
// GLOBAL-SAME: i16 4,
102102
// GLOBAL-SAME: i16 1,
103+
103104
// Relative reference to protocol
104105
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint (%swift.protocol* @"$S23associated_type_witness8AssockedMp" to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness8ComputedVyxq_GAA8AssockedAAWG", i32 0, i32 2) to i64)) to i32
105106

106107
// Relative reference to witness table template
107108
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint ([4 x i8*]* @"$S23associated_type_witness8ComputedVyxq_GAA8AssockedAAWp" to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness8ComputedVyxq_GAA8AssockedAAWG", i32 0, i32 3) to i64)) to i32
108109

110+
// Relative reference to resilient witnesses
111+
// GLOBAL-SAME: i32 0,
112+
109113
// No instantiator function
110114
// GLOBAL-SAME: i32 0,
111-
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint ([16 x i8*]* [[PRIVATE:@.*]] to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness8ComputedVyxq_GAA8AssockedAAWG", i32 0, i32 5) to i64)) to i32)
115+
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint ([16 x i8*]* [[PRIVATE:@.*]] to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness8ComputedVyxq_GAA8AssockedAAWG", i32 0, i32 6) to i64)) to i32)
112116
// GLOBAL-SAME: }
113117
// GLOBAL: [[PRIVATE]] = internal global [16 x i8*] zeroinitializer
114118

@@ -170,13 +174,21 @@ protocol DerivedFromSimpleAssoc : HasSimpleAssoc {}
170174
// GLOBAL-LABEL: @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG" = internal constant %swift.generic_witness_table_cache {
171175
// GLOBAL-SAME: i16 2,
172176
// GLOBAL-SAME: i16 0,
177+
173178
// Relative reference to protocol
174179
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint (%swift.protocol* @"$S23associated_type_witness22DerivedFromSimpleAssocMp" to i64
180+
175181
// Relative reference to witness table template
176182
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint ([2 x i8*]* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWp" to i64
183+
184+
// Relative reference to resilient witnesses
185+
// GLOBAL-SAME: i32 0,
186+
177187
// Relative reference to instantiator function
178-
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint (void (i8**, %swift.type*, i8**)* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWI" to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG", i32 0, i32 4) to i64)) to i32)
179-
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint ([16 x i8*]* @1 to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG", i32 0, i32 5) to i64)) to i32)
188+
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint (void (i8**, %swift.type*, i8**)* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWI" to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG", i32 0, i32 5) to i64)) to i32)
189+
190+
// Relative reference to private data
191+
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint ([16 x i8*]* @1 to i64), i64 ptrtoint (i32* getelementptr inbounds (%swift.generic_witness_table_cache, %swift.generic_witness_table_cache* @"$S23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWG", i32 0, i32 6) to i64)) to i32)
180192
// GLOBAL-SAME: }
181193
struct GenericComputed<T: P> : DerivedFromSimpleAssoc {
182194
typealias Assoc = PBox<T>

Diff for: test/IRGen/protocol_resilience.sil

+30-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,34 @@ import SwiftShims
1212
import resilient_protocol
1313

1414

15-
// Protocol is public -- needs resilient witness table
15+
// Generic witness table pattern for ConformingType : ResilientProtocol
16+
17+
// CHECK: @"$S19protocol_resilience23ResilientConformingTypeV010resilient_A005OtherC8ProtocolAAWG"
18+
19+
// CHECK-SAME: internal constant %swift.generic_witness_table_cache {
20+
21+
// -- number of witness table entries
22+
// CHECK-SAME: i16 1,
23+
24+
// -- size of private area
25+
// CHECK-SAME: i16 0,
26+
27+
// -- the protocol descriptor
28+
// CHECK-SAME: @"got.$S18resilient_protocol22OtherResilientProtocolMp"
29+
30+
// -- the template
31+
// CHECK-SAME: @"$S19protocol_resilience23ResilientConformingTypeV010resilient_A005OtherC8ProtocolAAWP"
32+
33+
// -- resilient witnesses
34+
// CHECK-SAME: i32 0,
35+
36+
// -- instantiator function
37+
// CHECK-SAME: i32 0
38+
39+
// CHECK-SAME: }
40+
41+
42+
// Protocol requirements for ResilientProtocol
1643

1744
// CHECK: [[RP_REQTS:@"\$S19protocol_resilience17ResilientProtocolWR"]] = internal constant [8 x %swift.protocol_requirement]
1845
// CHECK-SAME: [%swift.protocol_requirement { i32 6, i32 0, i32 0 },
@@ -49,13 +76,14 @@ import resilient_protocol
4976
// CHECK-SAME: }
5077
// CHECK-SAME: ]
5178

79+
// Protocol descriptor for ResilientProtocol
80+
5281
// CHECK: @"$S19protocol_resilience17ResilientProtocolMp" = {{(protected )?}}constant %swift.protocol {
5382
// CHECK-SAME: i32 1031,
5483
// CHECK-SAME: i32 8,
5584
// CHECK-SAME: i32{{ | trunc \(i64 }}sub ([[INT]] ptrtoint ([8 x %swift.protocol_requirement]* [[RP_REQTS]] to [[INT]]), [[INT]] ptrtoint (i32* getelementptr inbounds (%swift.protocol, %swift.protocol* @"$S19protocol_resilience17ResilientProtocolMp", i32 0
5685
// CHECK-SAME: }
5786

58-
5987
public protocol ResilientProtocol {
6088
associatedtype T : OtherResilientProtocol
6189

0 commit comments

Comments
 (0)