Skip to content

[embedded] Initial support for generic classes in embedded Swift #68461

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,8 @@ ERROR(bad_attr_on_non_const_global,none,
"global variable must be a compile-time constant to use %0 attribute", (StringRef))
ERROR(global_must_be_compile_time_const,none,
"global variable must be a compile-time constant", ())
ERROR(non_final_generic_class_function,none,
"classes cannot have non-final generic fuctions in embedded Swift", ())
NOTE(performance_called_from,none,
"called from here", ())

Expand Down
2 changes: 1 addition & 1 deletion include/swift/IRGen/Linking.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ inline bool isEmbedded(CanType t) {
}

inline bool isMetadataAllowedInEmbedded(CanType t) {
return isa<ClassType>(t);
return isa<ClassType>(t) || isa<BoundGenericClassType>(t);
}

inline bool isEmbedded(Decl *d) {
Expand Down
6 changes: 6 additions & 0 deletions include/swift/SIL/SILModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ class SILModule {
/// Lookup table for SIL vtables from class decls.
llvm::DenseMap<const ClassDecl *, SILVTable *> VTableMap;

/// Lookup table for specialized SIL vtables from types.
llvm::DenseMap<SILType, SILVTable *> SpecializedVTableMap;

/// The list of SILVTables in the module.
std::vector<SILVTable*> vtables;

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

/// Look up a specialized VTable
SILVTable *lookUpSpecializedVTable(SILType classTy);

/// Attempt to lookup the function corresponding to \p Member in the class
/// hierarchy of \p Class.
SILFunction *lookUpFunctionInVTable(ClassDecl *Class, SILDeclRef Member);
Expand Down
20 changes: 18 additions & 2 deletions include/swift/SIL/SILVTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ class SILVTableEntry {
void setNonOverridden(bool value) { IsNonOverridden = value; }

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

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

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

SILType classType;

/// Whether or not this vtable is serialized, which allows
/// devirtualization from another module.
bool Serialized : 1;
Expand All @@ -122,11 +125,19 @@ class SILVTable final : public SILAllocated<SILVTable>,
unsigned NumEntries : 31;

/// Private constructor. Create SILVTables by calling SILVTable::create.
SILVTable(ClassDecl *c, IsSerialized_t serialized, ArrayRef<Entry> entries);
SILVTable(ClassDecl *c, SILType classType, IsSerialized_t serialized,
ArrayRef<Entry> entries);

public:
public:
~SILVTable();

/// Create a new SILVTable with the given method-to-implementation mapping.
/// The SILDeclRef keys should reference the most-overridden members available
/// through the class.
static SILVTable *create(SILModule &M, ClassDecl *Class, SILType classType,
IsSerialized_t Serialized,
ArrayRef<Entry> Entries);

/// Create a new SILVTable with the given method-to-implementation mapping.
/// The SILDeclRef keys should reference the most-overridden members available
/// through the class.
Expand All @@ -137,6 +148,11 @@ class SILVTable final : public SILAllocated<SILVTable>,
/// Return the class that the vtable represents.
ClassDecl *getClass() const { return Class; }

bool isSpecialized() const {
return !classType.isNull();
}
SILType getClassType() const { return classType; }

/// Returns true if this vtable is going to be (or was) serialized.
IsSerialized_t isSerialized() const {
return Serialized ? IsSerialized : IsNotSerialized;
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SILOptimizer/PassManager/Passes.def
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ SWIFT_FUNCTION_PASS(DeadStoreElimination, "dead-store-elimination",
"Dead Store Elimination")
PASS(GenericSpecializer, "generic-specializer",
"Generic Function Specialization on Static Types")
PASS(VTableSpecializer, "vtable-specializer",
"Generic Specialization on VTables")
PASS(ExistentialSpecializer, "existential-specializer",
"Existential Specializer")
PASS(SILSkippingChecker, "check-sil-skipping",
Expand Down
78 changes: 72 additions & 6 deletions include/swift/SILOptimizer/Utils/Generics.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,15 @@ class ReabstractionInfo {

/// If set, indirect to direct conversions should be performed by the generic
/// specializer.
bool ConvertIndirectToDirect;
bool ConvertIndirectToDirect = true;

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

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

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

/// The generic environment to be used by the specialization.
GenericEnvironment *SpecializedGenericEnv;
GenericEnvironment *SpecializedGenericEnv = nullptr;

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

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

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

// Set if a specialized function has unbound generic parameters.
bool HasUnboundGenericParams;
bool HasUnboundGenericParams = false;

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

// Is the generated specialization going to be serialized?
IsSerialized_t Serialized;
IsSerialized_t Serialized = IsNotSerialized;

enum TypeCategory {
NotLoadable,
Expand All @@ -162,7 +162,9 @@ class ReabstractionInfo {
SubstitutionMap SubstMap,
bool HasUnboundGenericParams);

public:
void createSubstitutedAndSpecializedTypes();
private:

TypeCategory getReturnTypeCategory(const SILResultInfo &RI,
const SILFunctionConventions &substConv,
Expand Down Expand Up @@ -205,6 +207,12 @@ class ReabstractionInfo {
SILFunction *Callee, GenericSignature SpecializedSig,
bool isPrespecialization = false);

ReabstractionInfo(CanSILFunctionType substitutedType,
SILModule &M) :
SubstitutedType(substitutedType),
isWholeModule(M.isWholeModule()) {}


bool isPrespecialized() const { return isPrespecialization; }

IsSerialized_t isSerialized() const {
Expand Down Expand Up @@ -400,6 +408,64 @@ class GenericFuncSpecializer {
/// prespecialization for -Onone support.
bool isKnownPrespecialization(StringRef SpecName);

class TypeReplacements {
private:
llvm::Optional<SILType> resultType;
llvm::MapVector<unsigned, CanType> indirectResultTypes;
llvm::MapVector<unsigned, CanType> paramTypeReplacements;
llvm::MapVector<unsigned, CanType> yieldTypeReplacements;

public:
llvm::Optional<SILType> getResultType() const { return resultType; }

void setResultType(SILType type) { resultType = type; }

bool hasResultType() const { return resultType.has_value(); }

const llvm::MapVector<unsigned, CanType> &getIndirectResultTypes() const {
return indirectResultTypes;
}

void addIndirectResultType(unsigned index, CanType type) {
indirectResultTypes.insert(std::make_pair(index, type));
}

bool hasIndirectResultTypes() const { return !indirectResultTypes.empty(); }

const llvm::MapVector<unsigned, CanType> &getParamTypeReplacements() const {
return paramTypeReplacements;
}

void addParameterTypeReplacement(unsigned index, CanType type) {
paramTypeReplacements.insert(std::make_pair(index, type));
}

bool hasParamTypeReplacements() const {
return !paramTypeReplacements.empty();
}

const llvm::MapVector<unsigned, CanType> &getYieldTypeReplacements() const {
return yieldTypeReplacements;
}

void addYieldTypeReplacement(unsigned index, CanType type) {
yieldTypeReplacements.insert(std::make_pair(index, type));
}

bool hasYieldTypeReplacements() const {
return !yieldTypeReplacements.empty();
}

bool hasTypeReplacements() const {
return hasResultType() || hasParamTypeReplacements() ||
hasIndirectResultTypes() || hasYieldTypeReplacements();
}
};

ApplySite replaceWithSpecializedCallee(
ApplySite applySite, SILValue callee, const ReabstractionInfo &reInfo,
const TypeReplacements &typeReplacements = {});

/// Checks if all OnoneSupport pre-specializations are included in the module
/// as public functions.
///
Expand Down
3 changes: 3 additions & 0 deletions lib/IRGen/ClassMetadataVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ template <class Impl> class ClassMetadataVisitor
: super(IGM), Target(target),
VTable(IGM.getSILModule().lookUpVTable(target, /*deserialize*/ false)) {}

ClassMetadataVisitor(IRGenModule &IGM, ClassDecl *target, SILVTable *vtable)
: super(IGM), Target(target), VTable(vtable) {}

public:
void layout() {
static_assert(MetadataAdjustmentIndex::Class == 3,
Expand Down
4 changes: 3 additions & 1 deletion lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,7 +1079,9 @@ void IRGenModule::emitClassDecl(ClassDecl *D) {
emitClassMetadata(*this, D, fragileLayout, resilientLayout);
emitFieldDescriptor(D);
} else {
emitEmbeddedClassMetadata(*this, D, fragileLayout);
if (!D->isGenericContext()) {
emitEmbeddedClassMetadata(*this, D, fragileLayout);
}
}

IRGen.addClassForEagerInitialization(D);
Expand Down
54 changes: 43 additions & 11 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,7 @@ void IRGenModule::emitGlobalLists() {
// Eagerly emit functions that are externally visible. Functions that are
// dynamic replacements must also be eagerly emitted.
static bool isLazilyEmittedFunction(SILFunction &f, SILModule &m) {
// Embedded Swift only emits specialized function, so don't emit genreic
// Embedded Swift only emits specialized function, so don't emit generic
// functions, even if they're externally visible.
if (f.getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
f.getLoweredFunctionType()->getSubstGenericSignature()) {
Expand Down Expand Up @@ -1412,13 +1412,19 @@ deleteAndReenqueueForEmissionValuesDependentOnCanonicalPrespecializedMetadataRec
void IRGenerator::emitLazyDefinitions() {
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
// In embedded Swift, the compiler cannot emit any metadata, etc.
LazyTypeMetadata.clear();
LazySpecializedTypeMetadataRecords.clear();
LazyTypeContextDescriptors.clear();
LazyOpaqueTypeDescriptors.clear();
LazyCanonicalSpecializedMetadataAccessors.clear();
LazyMetadataAccessors.clear();
LazyWitnessTables.clear();
assert(LazyTypeMetadata.empty());
assert(LazySpecializedTypeMetadataRecords.empty());
assert(LazyTypeContextDescriptors.empty());
assert(LazyOpaqueTypeDescriptors.empty());
assert(LazyFieldDescriptors.empty());
// LazyFunctionDefinitions are allowed, but they must not be generic
for (SILFunction *f : LazyFunctionDefinitions) {
assert(!f->getLoweredFunctionType()->getSubstGenericSignature());
}
assert(LazyWitnessTables.empty());
assert(LazyCanonicalSpecializedMetadataAccessors.empty());
assert(LazyMetadataAccessors.empty());
// LazySpecializedClassMetadata is allowed
}

while (!LazyTypeMetadata.empty() ||
Expand All @@ -1427,7 +1433,8 @@ void IRGenerator::emitLazyDefinitions() {
!LazyOpaqueTypeDescriptors.empty() || !LazyFieldDescriptors.empty() ||
!LazyFunctionDefinitions.empty() || !LazyWitnessTables.empty() ||
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
!LazyMetadataAccessors.empty()) {
!LazyMetadataAccessors.empty() ||
!LazySpecializedClassMetadata.empty()) {
// Emit any lazy type metadata we require.
while (!LazyTypeMetadata.empty()) {
NominalTypeDecl *type = LazyTypeMetadata.pop_back_val();
Expand Down Expand Up @@ -1519,6 +1526,12 @@ void IRGenerator::emitLazyDefinitions() {
CurrentIGMPtr IGM = getGenModule(nominal->getDeclContext());
emitLazyMetadataAccessor(*IGM.get(), nominal);
}

while (!LazySpecializedClassMetadata.empty()) {
CanType classType = LazySpecializedClassMetadata.pop_back_val();
CurrentIGMPtr IGM = getGenModule(classType->getClassOrBoundGenericClass());
emitLazySpecializedClassMetadata(*IGM.get(), classType);
}
}

FinishedEmittingLazyDefinitions = true;
Expand All @@ -1528,6 +1541,10 @@ void IRGenerator::addLazyFunction(SILFunction *f) {
// Add it to the queue if it hasn't already been put there.
if (!LazilyEmittedFunctions.insert(f).second)
return;

if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
assert(!f->getLoweredFunctionType()->getSubstGenericSignature());
}

assert(!FinishedEmittingLazyDefinitions);
LazyFunctionDefinitions.push_back(f);
Expand Down Expand Up @@ -1601,6 +1618,12 @@ bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
return isLazy;
}

void IRGenerator::noteUseOfSpecializedClassMetadata(CanType classType) {
if (LazilyEmittedSpecializedClassMetadata.insert(classType.getPointer()).second) {
LazySpecializedClassMetadata.push_back(classType);
}
}

void IRGenerator::noteUseOfTypeGlobals(NominalTypeDecl *type,
bool isUseOfMetadata,
RequireMetadata_t requireMetadata) {
Expand Down Expand Up @@ -5061,8 +5084,9 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
// Foreign classes and prespecialized generic types do not use an alias into
// the full metadata and therefore require a GEP.
bool fullMetadata =
foreign || (concreteType->getAnyGeneric() &&
concreteType->getAnyGeneric()->isGenericContext());
!Context.LangOpts.hasFeature(Feature::Embedded) &&
(foreign || (concreteType->getAnyGeneric() &&
concreteType->getAnyGeneric()->isGenericContext()));

llvm::Type *defaultVarTy;
unsigned adjustmentIndex;
Expand Down Expand Up @@ -5099,6 +5123,14 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
// trigger lazy emission of the metadata.
if (NominalTypeDecl *nominal = concreteType->getAnyNominal()) {
IRGen.noteUseOfTypeMetadata(nominal);

if (Context.LangOpts.hasFeature(Feature::Embedded)) {
if (auto *classDecl = dyn_cast<ClassDecl>(nominal)) {
if (classDecl->isGenericContext()) {
IRGen.noteUseOfSpecializedClassMetadata(concreteType);
}
}
}
}

if (shouldPrespecializeGenericMetadata()) {
Expand Down
Loading