Skip to content

Commit d6e8eb2

Browse files
authored
Merge pull request swiftlang#76874 from eeckstein/witnesstable-specialization2
Embedded: specialized witness tables, part2: support remaining cases of class existentials
2 parents 064576b + a3e6e86 commit d6e8eb2

32 files changed

+479
-64
lines changed

SwiftCompilerSources/Sources/AST/Conformance.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ public struct Conformance: CustomStringConvertible, NoReflectionChildren {
4747
return bridged.getGenericConformance().conformance
4848
}
4949

50+
public var isInherited: Bool {
51+
assert(isConcrete)
52+
return bridged.isInheritedConformance()
53+
}
54+
55+
public var inheritedConformance: Conformance {
56+
assert(isInherited)
57+
return bridged.getInheritedConformance().conformance
58+
}
59+
5060
public var specializedSubstitutions: SubstitutionMap {
5161
assert(isSpecialized)
5262
return SubstitutionMap(bridged: bridged.getSpecializedSubstitutions())

SwiftCompilerSources/Sources/AST/SubstitutionMap.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import ASTBridging
1818
///
1919
/// Substitution maps are primarily used when performing substitutions into any entity that
2020
/// can reference type parameters and conformances.
21-
public struct SubstitutionMap {
21+
public struct SubstitutionMap: CustomStringConvertible {
2222
public let bridged: BridgedSubstitutionMap
2323

2424
public init(bridged: BridgedSubstitutionMap) {
@@ -29,6 +29,10 @@ public struct SubstitutionMap {
2929
self.bridged = BridgedSubstitutionMap()
3030
}
3131

32+
public var description: String {
33+
return String(taking: bridged.getDebugDescription())
34+
}
35+
3236
public var isEmpty: Bool { bridged.isEmpty() }
3337

3438
public var hasAnySubstitutableParams: Bool { bridged.hasAnySubstitutableParams() }

SwiftCompilerSources/Sources/AST/Type.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ public struct Type: CustomStringConvertible, NoReflectionChildren {
3737
public var isEscapable: Bool { bridged.isEscapable() }
3838
public var isNoEscape: Bool { bridged.isNoEscape() }
3939
public var isInteger: Bool { bridged.isInteger() }
40+
41+
public func subst(with substitutionMap: SubstitutionMap) -> Type {
42+
return Type(bridged: bridged.subst(substitutionMap.bridged))
43+
}
4044
}
4145

4246
/// A Type that is statically known to be canonical.
@@ -55,4 +59,8 @@ public struct CanonicalType: CustomStringConvertible, NoReflectionChildren {
5559
public var isEscapable: Bool { type.isEscapable }
5660
public var isNoEscape: Bool { type.isNoEscape }
5761
public var isInteger: Bool { type.isInteger }
62+
63+
public func subst(with substitutionMap: SubstitutionMap) -> CanonicalType {
64+
return type.subst(with: substitutionMap).canonical
65+
}
5866
}

SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu
116116
if context.options.enableEmbeddedSwift {
117117
_ = context.specializeClassMethodInst(classMethod)
118118
}
119+
case let witnessMethod as WitnessMethodInst:
120+
if context.options.enableEmbeddedSwift {
121+
_ = context.specializeWitnessMethodInst(witnessMethod)
122+
}
119123

120124
case let initExRef as InitExistentialRefInst:
121125
if context.options.enableEmbeddedSwift {
@@ -267,12 +271,14 @@ private func shouldInline(apply: FullApplySite, callee: Function, alreadyInlined
267271
private func specializeWitnessTables(for initExRef: InitExistentialRefInst, _ context: ModulePassContext,
268272
_ worklist: inout FunctionWorklist)
269273
{
270-
for conformance in initExRef.conformances where conformance.isConcrete {
274+
for c in initExRef.conformances where c.isConcrete {
275+
let conformance = c.isInherited ? c.inheritedConformance : c
271276
let origWitnessTable = context.lookupWitnessTable(for: conformance)
272277
if conformance.isSpecialized {
273278
if origWitnessTable == nil {
274-
let wt = specializeWitnessTable(forConformance: conformance, errorLocation: initExRef.location, context)
275-
worklist.addWitnessMethods(of: wt)
279+
specializeWitnessTable(forConformance: conformance, errorLocation: initExRef.location, context) {
280+
worklist.addWitnessMethods(of: $0)
281+
}
276282
}
277283
} else if let origWitnessTable {
278284
checkForGenericMethods(in: origWitnessTable, errorLocation: initExRef.location, context)

SwiftCompilerSources/Sources/Optimizer/PassManager/Context.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ extension Context {
7474
return _bridged.lookupSpecializedVTable(classType.bridged).vTable
7575
}
7676

77+
func getSpecializedConformance(of genericConformance: Conformance,
78+
for type: AST.`Type`,
79+
substitutions: SubstitutionMap) -> Conformance
80+
{
81+
let c = _bridged.getSpecializedConformance(genericConformance.bridged, type.bridged, substitutions.bridged)
82+
return Conformance(bridged: c)
83+
}
84+
7785
func notifyNewFunction(function: Function, derivedFrom: Function) {
7886
_bridged.addFunctionToPassManagerWorklist(function.bridged, derivedFrom.bridged)
7987
}
@@ -349,6 +357,15 @@ struct FunctionPassContext : MutatingContext {
349357
return false
350358
}
351359

360+
func specializeWitnessMethodInst(_ wm: WitnessMethodInst) -> Bool {
361+
if _bridged.specializeWitnessMethodInst(wm.bridged) {
362+
notifyInstructionsChanged()
363+
notifyCallsChanged()
364+
return true
365+
}
366+
return false
367+
}
368+
352369
func specializeApplies(in function: Function, isMandatory: Bool) -> Bool {
353370
if _bridged.specializeAppliesInFunction(function.bridged, isMandatory) {
354371
notifyInstructionsChanged()

SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ func specializeVTablesOfSuperclasses(_ moduleContext: ModulePassContext) {
7878

7979
func specializeWitnessTable(forConformance conformance: Conformance,
8080
errorLocation: Location,
81-
_ context: ModulePassContext) -> WitnessTable
81+
_ context: ModulePassContext,
82+
_ notifyNewWitnessTable: (WitnessTable) -> ())
8283
{
8384
let genericConformance = conformance.genericConformance
8485
guard let witnessTable = context.lookupWitnessTable(for: genericConformance) else {
@@ -88,6 +89,8 @@ func specializeWitnessTable(forConformance conformance: Conformance,
8889

8990
let newEntries = witnessTable.entries.map { origEntry in
9091
switch origEntry {
92+
case .invalid:
93+
return WitnessTable.Entry.invalid
9194
case .method(let requirement, let witness):
9295
guard let origMethod = witness else {
9396
return origEntry
@@ -101,11 +104,23 @@ func specializeWitnessTable(forConformance conformance: Conformance,
101104
return origEntry
102105
}
103106
return .method(requirement: requirement, witness: specializedMethod)
104-
default:
105-
// TODO: handle other witness table entry kinds
106-
fatalError("unsupported witness table etnry")
107+
case .baseProtocol(let requirement, let witness):
108+
let baseConf = context.getSpecializedConformance(of: witness,
109+
for: conformance.type,
110+
substitutions: conformance.specializedSubstitutions)
111+
specializeWitnessTable(forConformance: baseConf, errorLocation: errorLocation, context, notifyNewWitnessTable)
112+
return .baseProtocol(requirement: requirement, witness: baseConf)
113+
case .associatedType(let requirement, let witness):
114+
let substType = witness.subst(with: conformance.specializedSubstitutions)
115+
return .associatedType(requirement: requirement, witness: substType)
116+
case .associatedConformance(let requirement, let proto, let witness):
117+
if witness.isSpecialized {
118+
specializeWitnessTable(forConformance: witness, errorLocation: errorLocation, context, notifyNewWitnessTable)
119+
}
120+
return .associatedConformance(requirement: requirement, protocol: proto, witness: witness)
107121
}
108-
return origEntry
109122
}
110-
return context.createWitnessTable(entries: newEntries, conformance: conformance, linkage: .shared, serialized: false)
123+
let newWT = context.createWitnessTable(entries: newEntries,conformance: conformance,
124+
linkage: .shared, serialized: false)
125+
notifyNewWitnessTable(newWT)
111126
}

docs/EmbeddedSwift/ABI.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,31 @@ The compiler respects the ABIs and calling conventions of C and C++ when interop
2020

2121
## Metadata ABI of Embedded Swift
2222

23-
Embedded Swift eliminates almost all metadata compared to full Swift. However, class metadata is still used, because those serve as vtables for dynamic dispatch of methods to implement runtime polymorphism. The layout of Embedded Swift's class metadata is *different* from full Swift:
23+
Embedded Swift eliminates almost all metadata compared to full Swift. However, class and existential metadata are still used, because those serve as vtables and witness tables for dynamic dispatch of methods to implement runtime polymorphism with classes and existentials.
24+
25+
### Class Metadata ABI
26+
27+
The layout of Embedded Swift's class metadata is *different* from full Swift:
2428

2529
- The **super pointer** pointing to the class metadata record for the superclass is stored at **offset 0**. If the class is a root class, it is null.
2630
- The **destructor pointer** is stored at **offset 1**. This function is invoked by Swift's deallocator when the class instance is destroyed.
2731
- The **ivar destroyer** is stored at **offset 2**. This function is invoked to destroy instance members when creation of the object is cancelled (e.g. in a failable initializer).
2832
- Lastly, the **vtable** is stored at **offset 3**: For each Swift class in the class's inheritance hierarchy, in order starting
2933
from the root class and working down to the most derived class, the function pointers to the implementation of every method of the class in declaration order in stored.
3034

35+
### Witness Tables ABI
36+
37+
The layout of Embedded Swift's witness tables is *different* from full Swift:
38+
39+
- The first word is always a null pointer (TODO: it can be eliminated)
40+
- The following words are witness table entries which can be one of the following:
41+
- A method witness: a pointer to the witness function.
42+
- An associated conformance witness: a pointer to the witness table of the associated conformance
43+
44+
Note that witness tables in Embedded Swift do not contain associated type entries.
45+
46+
Witness functions are always specialized for concrete types. This also means that parameters and return values are passed directly (if possible).
47+
3148
## Heap object layout in Embedded Swift
3249

3350
Heap objects have the following layout in Embedded Swift:

include/swift/AST/ASTBridging.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2014,6 +2014,7 @@ struct BridgedASTType {
20142014
BRIDGED_INLINE bool isEscapable() const;
20152015
BRIDGED_INLINE bool isNoEscape() const;
20162016
BRIDGED_INLINE bool isInteger() const;
2017+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType subst(BridgedSubstitutionMap substMap) const;
20172018
};
20182019

20192020
class BridgedCanType {
@@ -2041,8 +2042,10 @@ struct BridgedConformance {
20412042
BRIDGED_INLINE bool isConcrete() const;
20422043
BRIDGED_INLINE bool isValid() const;
20432044
BRIDGED_INLINE bool isSpecializedConformance() const;
2045+
BRIDGED_INLINE bool isInheritedConformance() const;
20442046
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType getType() const;
20452047
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformance getGenericConformance() const;
2048+
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformance getInheritedConformance() const;
20462049
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedSubstitutionMap getSpecializedSubstitutions() const;
20472050
};
20482051

@@ -2070,6 +2073,7 @@ struct BridgedSubstitutionMap {
20702073
BRIDGED_INLINE BridgedSubstitutionMap(swift::SubstitutionMap map);
20712074
BRIDGED_INLINE swift::SubstitutionMap unbridged() const;
20722075
BRIDGED_INLINE BridgedSubstitutionMap();
2076+
BridgedOwnedString getDebugDescription() const;
20732077
BRIDGED_INLINE bool isEmpty() const;
20742078
BRIDGED_INLINE bool hasAnySubstitutableParams() const;
20752079
BRIDGED_INLINE SwiftInt getNumConformances() const;

include/swift/AST/ASTBridgingImpl.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ bool BridgedASTType::isInteger() const {
126126
return unbridged()->is<swift::IntegerType>();
127127
}
128128

129+
BridgedASTType BridgedASTType::subst(BridgedSubstitutionMap substMap) const {
130+
return {unbridged().subst(substMap.unbridged()).getPointer()};
131+
}
132+
129133
//===----------------------------------------------------------------------===//
130134
// MARK: BridgedCanType
131135
//===----------------------------------------------------------------------===//
@@ -159,6 +163,10 @@ bool BridgedConformance::isSpecializedConformance() const {
159163
return swift::isa<swift::SpecializedProtocolConformance>(unbridged().getConcrete());
160164
}
161165

166+
bool BridgedConformance::isInheritedConformance() const {
167+
return swift::isa<swift::InheritedProtocolConformance>(unbridged().getConcrete());
168+
}
169+
162170
BridgedASTType BridgedConformance::getType() const {
163171
return {unbridged().getConcrete()->getType().getPointer()};
164172
}
@@ -168,6 +176,11 @@ BridgedConformance BridgedConformance::getGenericConformance() const {
168176
return {swift::ProtocolConformanceRef(specPC->getGenericConformance())};
169177
}
170178

179+
BridgedConformance BridgedConformance::getInheritedConformance() const {
180+
auto *inheritedConf = swift::cast<swift::InheritedProtocolConformance>(unbridged().getConcrete());
181+
return {swift::ProtocolConformanceRef(inheritedConf->getInheritedConformance())};
182+
}
183+
171184
BridgedSubstitutionMap BridgedConformance::getSpecializedSubstitutions() const {
172185
auto *specPC = swift::cast<swift::SpecializedProtocolConformance>(unbridged().getConcrete());
173186
return {specPC->getSubstitutionMap()};

include/swift/AST/IRGenOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ struct PointerAuthOptions : clang::PointerAuthOptions {
120120

121121
/// Swift protocol witness table associated conformance witness table
122122
/// access functions.
123+
/// In Embedded Swift used for associated conformance witness table
124+
/// pointers.
123125
PointerAuthSchema ProtocolAssociatedTypeWitnessTableAccessFunctions;
124126

125127
/// Swift class v-table functions.

0 commit comments

Comments
 (0)