Skip to content

Commit d0c2a4c

Browse files
committed
[embedded] Initial support for generic classes in embedded Swift
- VTableSpecializer, a new pass that synthesizes a new vtable per each observed concrete type used - Don't use full type metadata refs in embedded Swift - Lazily emit specialized class metadata (LazySpecializedClassMetadata) in IRGen - Don't emit regular class metadata for a class decl if it's generic (only emit the specialized metadata)
1 parent dc6d784 commit d0c2a4c

23 files changed

+513
-79
lines changed

include/swift/IRGen/Linking.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ inline bool isEmbedded(CanType t) {
9090
}
9191

9292
inline bool isMetadataAllowedInEmbedded(CanType t) {
93-
return isa<ClassType>(t);
93+
return isa<ClassType>(t) || isa<BoundGenericClassType>(t);
9494
}
9595

9696
inline bool isEmbedded(Decl *d) {

include/swift/SIL/SILModule.h

+6
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ class SILModule {
243243
/// Lookup table for SIL vtables from class decls.
244244
llvm::DenseMap<const ClassDecl *, SILVTable *> VTableMap;
245245

246+
/// Lookup table for specialized SIL vtables from types.
247+
llvm::DenseMap<SILType, SILVTable *> SpecializedVTableMap;
248+
246249
/// The list of SILVTables in the module.
247250
std::vector<SILVTable*> vtables;
248251

@@ -827,6 +830,9 @@ class SILModule {
827830
/// Look up the VTable mapped to the given ClassDecl. Returns null on failure.
828831
SILVTable *lookUpVTable(const ClassDecl *C, bool deserializeLazily = true);
829832

833+
/// Look up a specialized VTable
834+
SILVTable *lookUpSpecializedVTable(SILType classTy);
835+
830836
/// Attempt to lookup the function corresponding to \p Member in the class
831837
/// hierarchy of \p Class.
832838
SILFunction *lookUpFunctionInVTable(ClassDecl *Class, SILDeclRef Member);

include/swift/SIL/SILVTable.h

+18-2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class SILVTableEntry {
8181
void setNonOverridden(bool value) { IsNonOverridden = value; }
8282

8383
SILFunction *getImplementation() const { return ImplAndKind.getPointer(); }
84+
void setImplementation(SILFunction *f);
8485

8586
void print(llvm::raw_ostream &os) const;
8687

@@ -114,6 +115,8 @@ class SILVTable final : public SILAllocated<SILVTable>,
114115
/// The ClassDecl mapped to this VTable.
115116
ClassDecl *Class;
116117

118+
SILType classType;
119+
117120
/// Whether or not this vtable is serialized, which allows
118121
/// devirtualization from another module.
119122
bool Serialized : 1;
@@ -122,11 +125,19 @@ class SILVTable final : public SILAllocated<SILVTable>,
122125
unsigned NumEntries : 31;
123126

124127
/// Private constructor. Create SILVTables by calling SILVTable::create.
125-
SILVTable(ClassDecl *c, IsSerialized_t serialized, ArrayRef<Entry> entries);
128+
SILVTable(ClassDecl *c, SILType classType, IsSerialized_t serialized,
129+
ArrayRef<Entry> entries);
126130

127-
public:
131+
public:
128132
~SILVTable();
129133

134+
/// Create a new SILVTable with the given method-to-implementation mapping.
135+
/// The SILDeclRef keys should reference the most-overridden members available
136+
/// through the class.
137+
static SILVTable *create(SILModule &M, ClassDecl *Class, SILType classType,
138+
IsSerialized_t Serialized,
139+
ArrayRef<Entry> Entries);
140+
130141
/// Create a new SILVTable with the given method-to-implementation mapping.
131142
/// The SILDeclRef keys should reference the most-overridden members available
132143
/// through the class.
@@ -137,6 +148,11 @@ class SILVTable final : public SILAllocated<SILVTable>,
137148
/// Return the class that the vtable represents.
138149
ClassDecl *getClass() const { return Class; }
139150

151+
bool isSpecialized() const {
152+
return !classType.isNull();
153+
}
154+
SILType getClassType() const { return classType; }
155+
140156
/// Returns true if this vtable is going to be (or was) serialized.
141157
IsSerialized_t isSerialized() const {
142158
return Serialized ? IsSerialized : IsNotSerialized;

include/swift/SILOptimizer/PassManager/Passes.def

+2
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ SWIFT_FUNCTION_PASS(DeadStoreElimination, "dead-store-elimination",
247247
"Dead Store Elimination")
248248
PASS(GenericSpecializer, "generic-specializer",
249249
"Generic Function Specialization on Static Types")
250+
PASS(VTableSpecializer, "vtable-specializer",
251+
"Generic Specialization on VTables")
250252
PASS(ExistentialSpecializer, "existential-specializer",
251253
"Existential Specializer")
252254
PASS(SILSkippingChecker, "check-sil-skipping",

include/swift/SILOptimizer/Utils/Generics.h

+72-6
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ class ReabstractionInfo {
8282

8383
/// If set, indirect to direct conversions should be performed by the generic
8484
/// specializer.
85-
bool ConvertIndirectToDirect;
85+
bool ConvertIndirectToDirect = true;
8686

8787
/// If true, drop metatype arguments.
8888
/// See `droppedMetatypeArgs`.
8989
bool dropMetatypeArgs = false;
9090

9191
/// The first NumResults bits in Conversions refer to formal indirect
9292
/// out-parameters.
93-
unsigned NumFormalIndirectResults;
93+
unsigned NumFormalIndirectResults = 0;
9494

9595
/// The function type after applying the substitutions used to call the
9696
/// specialized function.
@@ -101,7 +101,7 @@ class ReabstractionInfo {
101101
CanSILFunctionType SpecializedType;
102102

103103
/// The generic environment to be used by the specialization.
104-
GenericEnvironment *SpecializedGenericEnv;
104+
GenericEnvironment *SpecializedGenericEnv = nullptr;
105105

106106
/// The generic signature of the specialization.
107107
/// It is nullptr if the specialization is not polymorphic.
@@ -125,7 +125,7 @@ class ReabstractionInfo {
125125
SubstitutionMap ClonerParamSubMap;
126126

127127
// Reference to the original generic non-specialized callee function.
128-
SILFunction *Callee;
128+
SILFunction *Callee = nullptr;
129129

130130
// The module the specialization is created in.
131131
ModuleDecl *TargetModule = nullptr;
@@ -136,7 +136,7 @@ class ReabstractionInfo {
136136
ApplySite Apply;
137137

138138
// Set if a specialized function has unbound generic parameters.
139-
bool HasUnboundGenericParams;
139+
bool HasUnboundGenericParams = false;
140140

141141
// Substitutions to be used for creating a new function type
142142
// for the specialized function.
@@ -149,7 +149,7 @@ class ReabstractionInfo {
149149
bool isPrespecialization = false;
150150

151151
// Is the generated specialization going to be serialized?
152-
IsSerialized_t Serialized;
152+
IsSerialized_t Serialized = IsNotSerialized;
153153

154154
enum TypeCategory {
155155
NotLoadable,
@@ -162,7 +162,9 @@ class ReabstractionInfo {
162162
SubstitutionMap SubstMap,
163163
bool HasUnboundGenericParams);
164164

165+
public:
165166
void createSubstitutedAndSpecializedTypes();
167+
private:
166168

167169
TypeCategory getReturnTypeCategory(const SILResultInfo &RI,
168170
const SILFunctionConventions &substConv,
@@ -205,6 +207,12 @@ class ReabstractionInfo {
205207
SILFunction *Callee, GenericSignature SpecializedSig,
206208
bool isPrespecialization = false);
207209

210+
ReabstractionInfo(CanSILFunctionType substitutedType,
211+
SILModule &M) :
212+
SubstitutedType(substitutedType),
213+
isWholeModule(M.isWholeModule()) {}
214+
215+
208216
bool isPrespecialized() const { return isPrespecialization; }
209217

210218
IsSerialized_t isSerialized() const {
@@ -400,6 +408,64 @@ class GenericFuncSpecializer {
400408
/// prespecialization for -Onone support.
401409
bool isKnownPrespecialization(StringRef SpecName);
402410

411+
class TypeReplacements {
412+
private:
413+
llvm::Optional<SILType> resultType;
414+
llvm::MapVector<unsigned, CanType> indirectResultTypes;
415+
llvm::MapVector<unsigned, CanType> paramTypeReplacements;
416+
llvm::MapVector<unsigned, CanType> yieldTypeReplacements;
417+
418+
public:
419+
llvm::Optional<SILType> getResultType() const { return resultType; }
420+
421+
void setResultType(SILType type) { resultType = type; }
422+
423+
bool hasResultType() const { return resultType.has_value(); }
424+
425+
const llvm::MapVector<unsigned, CanType> &getIndirectResultTypes() const {
426+
return indirectResultTypes;
427+
}
428+
429+
void addIndirectResultType(unsigned index, CanType type) {
430+
indirectResultTypes.insert(std::make_pair(index, type));
431+
}
432+
433+
bool hasIndirectResultTypes() const { return !indirectResultTypes.empty(); }
434+
435+
const llvm::MapVector<unsigned, CanType> &getParamTypeReplacements() const {
436+
return paramTypeReplacements;
437+
}
438+
439+
void addParameterTypeReplacement(unsigned index, CanType type) {
440+
paramTypeReplacements.insert(std::make_pair(index, type));
441+
}
442+
443+
bool hasParamTypeReplacements() const {
444+
return !paramTypeReplacements.empty();
445+
}
446+
447+
const llvm::MapVector<unsigned, CanType> &getYieldTypeReplacements() const {
448+
return yieldTypeReplacements;
449+
}
450+
451+
void addYieldTypeReplacement(unsigned index, CanType type) {
452+
yieldTypeReplacements.insert(std::make_pair(index, type));
453+
}
454+
455+
bool hasYieldTypeReplacements() const {
456+
return !yieldTypeReplacements.empty();
457+
}
458+
459+
bool hasTypeReplacements() const {
460+
return hasResultType() || hasParamTypeReplacements() ||
461+
hasIndirectResultTypes() || hasYieldTypeReplacements();
462+
}
463+
};
464+
465+
ApplySite replaceWithSpecializedCallee(
466+
ApplySite applySite, SILValue callee, const ReabstractionInfo &reInfo,
467+
const TypeReplacements &typeReplacements = {});
468+
403469
/// Checks if all OnoneSupport pre-specializations are included in the module
404470
/// as public functions.
405471
///

lib/IRGen/ClassMetadataVisitor.h

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ template <class Impl> class ClassMetadataVisitor
6363
: super(IGM), Target(target),
6464
VTable(IGM.getSILModule().lookUpVTable(target, /*deserialize*/ false)) {}
6565

66+
ClassMetadataVisitor(IRGenModule &IGM, ClassDecl *target, SILVTable *vtable)
67+
: super(IGM), Target(target), VTable(vtable) {}
68+
6669
public:
6770
void layout() {
6871
static_assert(MetadataAdjustmentIndex::Class == 3,

lib/IRGen/GenClass.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,9 @@ void IRGenModule::emitClassDecl(ClassDecl *D) {
10791079
emitClassMetadata(*this, D, fragileLayout, resilientLayout);
10801080
emitFieldDescriptor(D);
10811081
} else {
1082-
emitEmbeddedClassMetadata(*this, D, fragileLayout);
1082+
if (!D->isGenericContext()) {
1083+
emitEmbeddedClassMetadata(*this, D, fragileLayout);
1084+
}
10831085
}
10841086

10851087
IRGen.addClassForEagerInitialization(D);

lib/IRGen/GenDecl.cpp

+25-5
Original file line numberDiff line numberDiff line change
@@ -1427,7 +1427,8 @@ void IRGenerator::emitLazyDefinitions() {
14271427
!LazyOpaqueTypeDescriptors.empty() || !LazyFieldDescriptors.empty() ||
14281428
!LazyFunctionDefinitions.empty() || !LazyWitnessTables.empty() ||
14291429
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
1430-
!LazyMetadataAccessors.empty()) {
1430+
!LazyMetadataAccessors.empty() ||
1431+
!LazySpecializedClassMetadata.empty()) {
14311432
// Emit any lazy type metadata we require.
14321433
while (!LazyTypeMetadata.empty()) {
14331434
NominalTypeDecl *type = LazyTypeMetadata.pop_back_val();
@@ -1492,8 +1493,9 @@ void IRGenerator::emitLazyDefinitions() {
14921493
while (!LazyFunctionDefinitions.empty()) {
14931494
SILFunction *f = LazyFunctionDefinitions.pop_back_val();
14941495
CurrentIGMPtr IGM = getGenModule(f);
1495-
assert(!f->isPossiblyUsedExternally()
1496-
&& "function with externally-visible linkage emitted lazily?");
1496+
// XXX TODO
1497+
//assert(!f->isPossiblyUsedExternally()
1498+
// && "function with externally-visible linkage emitted lazily?");
14971499
IGM->emitSILFunction(f);
14981500
}
14991501

@@ -1519,6 +1521,12 @@ void IRGenerator::emitLazyDefinitions() {
15191521
CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext());
15201522
emitLazyMetadataAccessor(*IGM.get(), nominal);
15211523
}
1524+
1525+
while (!LazySpecializedClassMetadata.empty()) {
1526+
CanType classType = LazySpecializedClassMetadata.pop_back_val();
1527+
CurrentIGMPtr IGM = getGenModule(classType->getClassOrBoundGenericClass());
1528+
emitLazySpecializedClassMetadata(*IGM.get(), classType);
1529+
}
15221530
}
15231531

15241532
FinishedEmittingLazyDefinitions = true;
@@ -1601,6 +1609,12 @@ bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
16011609
return isLazy;
16021610
}
16031611

1612+
void IRGenerator::noteUseOfSpecializedClassMetadata(CanType classType) {
1613+
if (LazilyEmittedSpecializedClassMetadata.insert(classType.getPointer()).second) {
1614+
LazySpecializedClassMetadata.push_back(classType);
1615+
}
1616+
}
1617+
16041618
void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type,
16051619
bool isUseOfMetadata,
16061620
RequireMetadata_t requireMetadata) {
@@ -5061,8 +5075,9 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
50615075
// Foreign classes and prespecialized generic types do not use an alias into
50625076
// the full metadata and therefore require a GEP.
50635077
bool fullMetadata =
5064-
foreign || (concreteType->getAnyGeneric() &&
5065-
concreteType->getAnyGeneric()->isGenericContext());
5078+
!Context.LangOpts.hasFeature(Feature::Embedded) &&
5079+
(foreign || (concreteType->getAnyGeneric() &&
5080+
concreteType->getAnyGeneric()->isGenericContext()));
50665081

50675082
llvm::Type *defaultVarTy;
50685083
unsigned adjustmentIndex;
@@ -5099,6 +5114,11 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
50995114
// trigger lazy emission of the metadata.
51005115
if (NominalTypeDecl *nominal = concreteType->getAnyNominal()) {
51015116
IRGen.noteUseOfTypeMetadata(nominal);
5117+
if (auto *classDecl = dyn_cast<ClassDecl>(nominal)) {
5118+
if (classDecl->isGenericContext()) {
5119+
IRGen.noteUseOfSpecializedClassMetadata(concreteType);
5120+
}
5121+
}
51025122
}
51035123

51045124
if (shouldPrespecializeGenericMetadata()) {

lib/IRGen/GenMeta.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -3772,6 +3772,14 @@ namespace {
37723772
FieldLayout(fieldLayout),
37733773
MetadataLayout(IGM.getClassMetadataLayout(theClass)) {}
37743774

3775+
ClassMetadataBuilderBase(IRGenModule &IGM, ClassDecl *theClass,
3776+
ConstantStructBuilder &builder,
3777+
const ClassLayout &fieldLayout,
3778+
SILVTable *vtable)
3779+
: super(IGM, theClass, vtable), B(builder),
3780+
FieldLayout(fieldLayout),
3781+
MetadataLayout(IGM.getClassMetadataLayout(theClass)) {}
3782+
37753783
public:
37763784
const ClassLayout &getFieldLayout() const { return FieldLayout; }
37773785

@@ -4209,6 +4217,12 @@ namespace {
42094217
const ClassLayout &fieldLayout)
42104218
: super(IGM, theClass, builder, fieldLayout) {}
42114219

4220+
FixedClassMetadataBuilder(IRGenModule &IGM, ClassDecl *theClass,
4221+
ConstantStructBuilder &builder,
4222+
const ClassLayout &fieldLayout,
4223+
SILVTable *vtable)
4224+
: super(IGM, theClass, builder, fieldLayout, vtable) {}
4225+
42124226
void addFieldOffset(VarDecl *var) {
42134227
if (asImpl().getFieldLayout().hasObjCImplementation())
42144228
return;
@@ -5003,6 +5017,37 @@ void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
50035017
(void)var;
50045018
}
50055019

5020+
void irgen::emitLazySpecializedClassMetadata(IRGenModule &IGM,
5021+
CanType classTy) {
5022+
auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext();
5023+
PrettyStackTraceType stackTraceRAII(
5024+
context, "emitting lazy specialized class metadata for", classTy);
5025+
5026+
SILType classType = SILType::getPrimitiveObjectType(classTy);
5027+
auto &classTI = IGM.getTypeInfo(classType).as<ClassTypeInfo>();
5028+
5029+
auto &fragileLayout =
5030+
classTI.getClassLayout(IGM, classType, /*forBackwardDeployment=*/true);
5031+
5032+
ClassDecl *classDecl = classType.getClassOrBoundGenericClass();
5033+
5034+
ConstantInitBuilder initBuilder(IGM);
5035+
auto init = initBuilder.beginStruct();
5036+
init.setPacked(true);
5037+
5038+
SILVTable *vtable = IGM.getSILModule().lookUpSpecializedVTable(classType);
5039+
assert(vtable);
5040+
5041+
FixedClassMetadataBuilder builder(IGM, classDecl, init, fragileLayout, vtable);
5042+
builder.layout();
5043+
bool canBeConstant = builder.canBeConstant();
5044+
5045+
StringRef section{};
5046+
auto var = IGM.defineTypeMetadata(classTy, false, canBeConstant,
5047+
init.finishAndCreateFuture(), section);
5048+
(void)var;
5049+
}
5050+
50065051
void irgen::emitSpecializedGenericClassMetadata(IRGenModule &IGM, CanType type,
50075052
ClassDecl &decl) {
50085053
assert(decl.isGenericContext());

lib/IRGen/GenMeta.h

+2
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ namespace irgen {
8383
/// Emit the type metadata accessor for a type for which it might be used.
8484
void emitLazyMetadataAccessor(IRGenModule &IGM, NominalTypeDecl *type);
8585

86+
void emitLazySpecializedClassMetadata(IRGenModule &IGM, CanType classType);
87+
8688
void emitLazyCanonicalSpecializedMetadataAccessor(IRGenModule &IGM,
8789
CanType theType);
8890

0 commit comments

Comments
 (0)