Skip to content

Commit c0519c7

Browse files
committed
[ABI] Put mangled inherited protocol witnesses into the resilient witness table
Emit mangled inherited protocol witnesses into the resilient witness table, and realize them when we instantiate the resilient witness table. Don’t put mangled inherited protocol witnesses into non-resilient witness tables: there is no efficient way to make sure they are realized, so keep the previous instantiation-function approach. Implements the rest of rdar://problem/46282080.
1 parent 7679433 commit c0519c7

File tree

5 files changed

+79
-52
lines changed

5 files changed

+79
-52
lines changed

lib/IRGen/GenProto.cpp

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,17 +1320,23 @@ class AccessorConformanceInfo : public ConformanceInfo {
13201320
void addOutOfLineBaseProtocol(ProtocolDecl *baseProto) {
13211321
#ifndef NDEBUG
13221322
auto &entry = SILEntries.front();
1323+
#endif
1324+
SILEntries = SILEntries.slice(1);
1325+
1326+
// Resilient conformances get a resilient witness table.
1327+
if (ResilientConformance)
1328+
return;
1329+
1330+
#ifndef NDEBUG
13231331
assert(entry.getKind() == SILWitnessTable::BaseProtocol
13241332
&& "sil witness table does not match protocol");
13251333
assert(entry.getBaseProtocolWitness().Requirement == baseProto
13261334
&& "sil witness table does not match protocol");
13271335
auto piIndex = PI.getBaseIndex(baseProto);
13281336
assert((size_t)piIndex.getValue() ==
1329-
Table.size() - WitnessTableFirstRequirementOffset &&
1337+
Table.size() - WitnessTableFirstRequirementOffset &&
13301338
"offset doesn't match ProtocolInfo layout");
13311339
#endif
1332-
1333-
SILEntries = SILEntries.slice(1);
13341340

13351341
// TODO: Use the witness entry instead of falling through here.
13361342

@@ -1346,18 +1352,10 @@ class AccessorConformanceInfo : public ConformanceInfo {
13461352
return;
13471353
}
13481354

1349-
// Emit a mangled name.
1350-
if (isDependentConformance(astConf->getRootConformance(),
1351-
/*considerResilience=*/false))
1352-
RequiresSpecialization = true;
1353-
1354-
auto proto = Conformance.getProtocol();
1355-
CanType selfType = proto->getProtocolSelfType()->getCanonicalType();
1356-
AssociatedConformance requirement(proto, selfType, baseProto);
1357-
llvm::Constant *witnessEntry =
1358-
getAssociatedConformanceWitness(requirement, ConcreteType,
1359-
ProtocolConformanceRef(astConf));
1360-
Table.addBitCast(witnessEntry, IGM.Int8PtrTy);
1355+
// Otherwise, we'll need to derive it at instantiation time.
1356+
RequiresSpecialization = true;
1357+
SpecializedBaseConformances.push_back({Table.size(), &conf});
1358+
Table.addNullPointer(IGM.Int8PtrTy);
13611359
}
13621360

13631361
void addMethod(SILDeclRef requirement) {
@@ -1761,6 +1759,22 @@ void WitnessTableBuilder::collectResilientWitnesses(
17611759
continue;
17621760
}
17631761

1762+
// Inherited conformance witnesses.
1763+
if (entry.getKind() == SILWitnessTable::BaseProtocol) {
1764+
const auto &witness = entry.getBaseProtocolWitness();
1765+
auto baseProto = witness.Requirement;
1766+
auto proto = SILWT->getProtocol();
1767+
CanType selfType = proto->getProtocolSelfType()->getCanonicalType();
1768+
AssociatedConformance requirement(proto, selfType, baseProto);
1769+
ProtocolConformanceRef inheritedConformance =
1770+
ConformanceInContext.getAssociatedConformance(selfType, baseProto);
1771+
llvm::Constant *witnessEntry =
1772+
getAssociatedConformanceWitness(requirement, ConcreteType,
1773+
inheritedConformance);
1774+
resilientWitnesses.push_back(witnessEntry);
1775+
continue;
1776+
}
1777+
17641778
if (entry.getKind() != SILWitnessTable::Method)
17651779
continue;
17661780

@@ -1978,6 +1992,18 @@ namespace {
19781992
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
19791993
LinkEntity::forAssociatedConformanceDescriptor(requirement));
19801994
B.addRelativeAddress(assocConformanceDescriptor);
1995+
} else if (entry.getKind() == SILWitnessTable::BaseProtocol) {
1996+
// Associated conformance descriptor for a base protocol.
1997+
const auto &witness = entry.getBaseProtocolWitness();
1998+
auto proto = SILWT->getProtocol();
1999+
AssociatedConformance requirement(proto,
2000+
proto->getSelfInterfaceType()
2001+
->getCanonicalType(),
2002+
witness.Requirement);
2003+
auto baseConformanceDescriptor =
2004+
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
2005+
LinkEntity::forAssociatedConformanceDescriptor(requirement));
2006+
B.addRelativeAddress(baseConformanceDescriptor);
19812007
} else if (entry.getKind() == SILWitnessTable::Method) {
19822008
// Method descriptor.
19832009
auto declRef = entry.getMethodWitness().Requirement;

stdlib/public/runtime/Metadata.cpp

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3981,6 +3981,7 @@ static bool doesNotRequireInstantiation(
39813981
/// witnesses stored in the generic witness table structure itself.
39823982
static void initializeResilientWitnessTable(
39833983
const ProtocolConformanceDescriptor *conformance,
3984+
const Metadata *conformingType,
39843985
const GenericWitnessTable *genericTable,
39853986
void **table) {
39863987
auto protocol = conformance->getProtocol();
@@ -3994,7 +3995,7 @@ static void initializeResilientWitnessTable(
39943995
auto reqDescriptor = witness.Requirement.get();
39953996

39963997
// The requirement descriptor may be NULL, in which case this is a
3997-
// requirement introduced in a later version of the protocol./
3998+
// requirement introduced in a later version of the protocol.
39983999
if (!reqDescriptor) continue;
39994000

40004001
// If the requirement descriptor doesn't land within the bounds of the
@@ -4017,35 +4018,24 @@ static void initializeResilientWitnessTable(
40174018
for (size_t i = 0, e = protocol->NumRequirements; i < e; ++i) {
40184019
unsigned witnessIndex = WitnessTableFirstRequirementOffset + i;
40194020

4021+
// If we don't have a witness, fill in the default implementation.
40204022
// If we already have a witness, there's nothing to do.
4021-
if (table[witnessIndex])
4022-
continue;
4023-
4024-
// Otherwise, fill in a default implementation.
40254023
auto &reqt = requirements[i];
4026-
void *impl = reqt.DefaultImplementation.get();
4027-
table[witnessIndex] = impl;
4028-
}
4029-
}
4030-
4031-
/// Realize witness tables for base protocols.
4032-
static void realizeBaseProtocolWitnessTables(const ProtocolDescriptor *protocol,
4033-
const Metadata *conformingType,
4034-
WitnessTable *witnessTable) {
4035-
// Loop over the requirements, realizing witness tables for base class
4036-
// conformances.
4037-
auto requirements = protocol->getRequirements();
4038-
auto baseReq = protocol->getRequirementBaseDescriptor();
4039-
for (size_t i = 0, e = protocol->NumRequirements; i < e; ++i) {
4040-
// Once we hit a non-base protocol requirement, we're done.
4041-
auto &reqt = requirements[i];
4042-
if (reqt.Flags.getKind() != ProtocolRequirementFlags::Kind::BaseProtocol)
4043-
break;
4024+
if (!table[witnessIndex]) {
4025+
void *impl = reqt.DefaultImplementation.get();
4026+
table[witnessIndex] = impl;
4027+
}
40444028

4045-
// Realize the base protocol witness table.
4046-
(void)swift_getAssociatedConformanceWitness(witnessTable,
4047-
conformingType, conformingType,
4048-
baseReq, &reqt);
4029+
// Realize base protocol witnesses.
4030+
if (reqt.Flags.getKind() == ProtocolRequirementFlags::Kind::BaseProtocol &&
4031+
table[witnessIndex]) {
4032+
// Realize the base protocol witness table.
4033+
auto baseReq = protocol->getRequirementBaseDescriptor();
4034+
(void)swift_getAssociatedConformanceWitness((WitnessTable *)table,
4035+
conformingType,
4036+
conformingType,
4037+
baseReq, &reqt);
4038+
}
40494039
}
40504040
}
40514041

@@ -4108,9 +4098,8 @@ WitnessTableCacheEntry::allocate(
41084098
}
41094099

41104100
// Fill in any default requirements.
4111-
initializeResilientWitnessTable(conformance, genericTable, table);
4101+
initializeResilientWitnessTable(conformance, Type, genericTable, table);
41124102
auto castTable = reinterpret_cast<WitnessTable*>(table);
4113-
realizeBaseProtocolWitnessTables(protocol, Type, castTable);
41144103

41154104
// Call the instantiation function if present.
41164105
if (!genericTable->Instantiator.isNull()) {
@@ -4143,10 +4132,7 @@ swift::swift_getWitnessTable(const ProtocolConformanceDescriptor *conformance,
41434132
// accessor directly.
41444133
auto genericTable = conformance->getGenericWitnessTable();
41454134
if (!genericTable || doesNotRequireInstantiation(conformance, genericTable)) {
4146-
auto pattern = conformance->getWitnessTablePattern();
4147-
realizeBaseProtocolWitnessTables(conformance->getProtocol(), type,
4148-
const_cast<WitnessTable *>(pattern));
4149-
return uniqueForeignWitnessTableRef(pattern);
4135+
return uniqueForeignWitnessTableRef(conformance->getWitnessTablePattern());
41504136
}
41514137

41524138
auto &cache = getCache(genericTable);

test/IRGen/associated_type_witness.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,15 @@ struct Computed<T, U> : Assocked {
9797
typealias Assoc = Pair<T, U>
9898
}
9999

100+
// Instantiation function for GenericComputed : DerivedFromSimpleAssoc.
101+
// CHECK-LABEL: define internal void @"$s23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWI"(i8**, %swift.type* %"GenericComputed<T>", i8**)
102+
// CHECK: [[T0:%.*]] = call i8** @swift_getWitnessTable({{.*}}@"$s23associated_type_witness15GenericComputedVyxGAA14HasSimpleAssocAAMc"
103+
// CHECK-NEXT: [[T1:%.*]] = bitcast i8** [[T0]] to i8*
104+
// CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i8*, i8** %0, i32 1
105+
// CHECK-NEXT: store i8* [[T1]], i8** [[T2]], align 8
106+
// CHECK-NEXT: ret void
107+
108+
100109
struct PBox<T: P> {}
101110
protocol HasSimpleAssoc {
102111
associatedtype Assoc
@@ -106,7 +115,7 @@ protocol DerivedFromSimpleAssoc : HasSimpleAssoc {}
106115
// Generic witness table pattern for GenericComputed : DerivedFromSimpleAssoc.
107116
// GLOBAL-LABEL: @"$s23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWp" = internal constant [2 x i8*]
108117
// GLOBAL-SAME: @"$s23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAMc"
109-
// GLOBAL-SAME: @"associated conformance 23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocA2A03HashI0"
118+
// GLOBAL-SAME: i8* null
110119

111120
// Relative reference to private data
112121
struct GenericComputed<T: P> : DerivedFromSimpleAssoc {
@@ -139,5 +148,5 @@ struct UsesVoid : HasSimpleAssoc {
139148
// GLOBAL-SAME: i16 2,
140149
// GLOBAL-SAME: i16 1,
141150

142-
// No instantiator function needed
143-
// GLOBAL-SAME: i32 0
151+
// Relative reference to instantiator function
152+
// GLOBAL-SAME: i32 trunc (i64 sub (i64 ptrtoint (void (i8**, %swift.type*, i8**)* @"$s23associated_type_witness15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWI" to i64),

test/IRGen/generic_wt_linkage.sil

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ sil_default_witness_table hidden DerivedFromSimpleAssoc {
4444
no_default
4545
}
4646

47-
// CHECK: define internal swiftcc i8** @"$s4test15GenericComputedVyxGAA22DerivedFromSimpleAssocAAxAA03HasfG0PWT"
47+
// CHECK: define internal void @"$s4test15GenericComputedVyxGAA22DerivedFromSimpleAssocAAWI"
4848

4949

test/IRGen/protocol_resilience_descriptors.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ where Element: ProtocolWithRequirements, Element.T == Y {
7979
public func second() { }
8080
}
8181

82+
// CHECK-USAGE: @"$s31protocol_resilience_descriptors17ConformsToDerivedV010resilient_A009ResilientF8ProtocolAAMc" =
83+
// CHECK-SAME: @"associated conformance 31protocol_resilience_descriptors17ConformsToDerivedV010resilient_A009ResilientF8ProtocolAaD0h4BaseI0"
84+
public struct ConformsToDerived : ResilientDerivedProtocol {
85+
public func requirement() -> Int { return 0 }
86+
}
87+
8288
// ----------------------------------------------------------------------------
8389
// Resilient protocol usage
8490
// ----------------------------------------------------------------------------

0 commit comments

Comments
 (0)