[embedded] Serialize+deserialize vtables, fix using non-generic classes from other modules in embedded Swift

This commit is contained in:
Kuba Mracek
2023-10-05 10:45:49 -07:00
parent 2278098c21
commit 7da86b1148
8 changed files with 149 additions and 38 deletions

View File

@@ -2901,6 +2901,9 @@ CanType irgen::getSuperclassForMetadata(IRGenModule &IGM, CanType type,
ClassMetadataStrategy
IRGenModule::getClassMetadataStrategy(const ClassDecl *theClass) {
if (Context.LangOpts.hasFeature(Feature::Embedded))
return ClassMetadataStrategy::Fixed;
SILType selfType = getSelfType(theClass);
auto &selfTI = getTypeInfo(selfType).as<ClassTypeInfo>();

View File

@@ -1436,6 +1436,7 @@ void IRGenerator::emitLazyDefinitions() {
assert(LazyWitnessTables.empty());
assert(LazyCanonicalSpecializedMetadataAccessors.empty());
assert(LazyMetadataAccessors.empty());
// LazyClassMetadata is allowed
// LazySpecializedClassMetadata is allowed
}
@@ -1446,7 +1447,9 @@ void IRGenerator::emitLazyDefinitions() {
!LazyFunctionDefinitions.empty() || !LazyWitnessTables.empty() ||
!LazyCanonicalSpecializedMetadataAccessors.empty() ||
!LazyMetadataAccessors.empty() ||
!LazySpecializedClassMetadata.empty()) {
!LazyClassMetadata.empty() ||
!LazySpecializedClassMetadata.empty()
) {
// Emit any lazy type metadata we require.
while (!LazyTypeMetadata.empty()) {
NominalTypeDecl *type = LazyTypeMetadata.pop_back_val();
@@ -1544,6 +1547,12 @@ void IRGenerator::emitLazyDefinitions() {
emitLazyMetadataAccessor(*IGM.get(), nominal);
}
while (!LazyClassMetadata.empty()) {
CanType classType = LazyClassMetadata.pop_back_val();
CurrentIGMPtr IGM = getGenModule(classType->getClassOrBoundGenericClass());
emitLazyClassMetadata(*IGM.get(), classType);
}
while (!LazySpecializedClassMetadata.empty()) {
CanType classType = LazySpecializedClassMetadata.pop_back_val();
CurrentIGMPtr IGM = getGenModule(classType->getClassOrBoundGenericClass());
@@ -1636,6 +1645,12 @@ bool IRGenerator::hasLazyMetadata(TypeDecl *type) {
return isLazy;
}
void IRGenerator::noteUseOfClassMetadata(CanType classType) {
if (LazilyEmittedClassMetadata.insert(classType.getPointer()).second) {
LazyClassMetadata.push_back(classType);
}
}
void IRGenerator::noteUseOfSpecializedClassMetadata(CanType classType) {
if (LazilyEmittedSpecializedClassMetadata.insert(classType.getPointer()).second) {
LazySpecializedClassMetadata.push_back(classType);
@@ -5151,6 +5166,8 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
if (auto *classDecl = dyn_cast<ClassDecl>(nominal)) {
if (classDecl->isGenericContext()) {
IRGen.noteUseOfSpecializedClassMetadata(concreteType);
} else {
IRGen.noteUseOfClassMetadata(concreteType);
}
}
}

View File

@@ -4996,35 +4996,65 @@ void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
}
}
void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
const ClassLayout &fragileLayout) {
PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl);
assert(!classDecl->isForeign());
static void emitEmbeddedVTable(IRGenModule &IGM, CanType classTy,
SILVTable *vtable) {
SILType classType = SILType::getPrimitiveObjectType(classTy);
auto &classTI = IGM.getTypeInfo(classType).as<ClassTypeInfo>();
// Set up a dummy global to stand in for the metadata object while we produce
// relative references.
ConstantInitBuilder builder(IGM);
auto init = builder.beginStruct();
init.setPacked(true);
auto &fragileLayout =
classTI.getClassLayout(IGM, classType, /*forBackwardDeployment=*/true);
ClassDecl *classDecl = classType.getClassOrBoundGenericClass();
auto strategy = IGM.getClassMetadataStrategy(classDecl);
assert(strategy == ClassMetadataStrategy::FixedOrUpdate ||
strategy == ClassMetadataStrategy::Fixed);
FixedClassMetadataBuilder metadataBuilder(IGM, classDecl, init,
fragileLayout);
metadataBuilder.layout();
bool canBeConstant = metadataBuilder.canBeConstant();
ConstantInitBuilder initBuilder(IGM);
auto init = initBuilder.beginStruct();
init.setPacked(true);
CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();
assert(vtable);
FixedClassMetadataBuilder builder(IGM, classDecl, init, fragileLayout,
vtable);
builder.layout();
bool canBeConstant = builder.canBeConstant();
StringRef section{};
bool isPattern = false;
auto var = IGM.defineTypeMetadata(declaredType, isPattern, canBeConstant,
auto var = IGM.defineTypeMetadata(classTy, /*isPattern*/ false, canBeConstant,
init.finishAndCreateFuture(), section);
(void)var;
}
void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
const ClassLayout &fragileLayout) {
PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl);
assert(!classDecl->isForeign());
CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();
SILVTable *vtable = IGM.getSILModule().lookUpVTable(classDecl);
emitEmbeddedVTable(IGM, declaredType, vtable);
}
void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
// Might already be emitted, skip if that's the case.
auto entity =
LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
auto *existingVar = cast<llvm::GlobalVariable>(
IGM.getAddrOfLLVMVariable(entity, ConstantInit(), DebugTypeInfo()));
if (!existingVar->isDeclaration()) {
return;
}
auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext();
PrettyStackTraceType stackTraceRAII(
context, "emitting lazy class metadata for", classTy);
SILType classType = SILType::getPrimitiveObjectType(classTy);
ClassDecl *classDecl = classType.getClassOrBoundGenericClass();
SILVTable *vtable = IGM.getSILModule().lookUpVTable(classDecl);
emitEmbeddedVTable(IGM, classTy, vtable);
}
void irgen::emitLazySpecializedClassMetadata(IRGenModule &IGM,
CanType classTy) {
auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext();
@@ -5032,28 +5062,8 @@ void irgen::emitLazySpecializedClassMetadata(IRGenModule &IGM,
context, "emitting lazy specialized class metadata for", classTy);
SILType classType = SILType::getPrimitiveObjectType(classTy);
auto &classTI = IGM.getTypeInfo(classType).as<ClassTypeInfo>();
auto &fragileLayout =
classTI.getClassLayout(IGM, classType, /*forBackwardDeployment=*/true);
ClassDecl *classDecl = classType.getClassOrBoundGenericClass();
ConstantInitBuilder initBuilder(IGM);
auto init = initBuilder.beginStruct();
init.setPacked(true);
SILVTable *vtable = IGM.getSILModule().lookUpSpecializedVTable(classType);
assert(vtable);
FixedClassMetadataBuilder builder(IGM, classDecl, init, fragileLayout, vtable);
builder.layout();
bool canBeConstant = builder.canBeConstant();
StringRef section{};
auto var = IGM.defineTypeMetadata(classTy, false, canBeConstant,
init.finishAndCreateFuture(), section);
(void)var;
emitEmbeddedVTable(IGM, classTy, vtable);
}
void irgen::emitSpecializedGenericClassMetadata(IRGenModule &IGM, CanType type,

View File

@@ -83,6 +83,8 @@ namespace irgen {
/// Emit the type metadata accessor for a type for which it might be used.
void emitLazyMetadataAccessor(IRGenModule &IGM, NominalTypeDecl *type);
void emitLazyClassMetadata(IRGenModule &IGM, CanType classType);
void emitLazySpecializedClassMetadata(IRGenModule &IGM, CanType classType);
void emitLazyCanonicalSpecializedMetadataAccessor(IRGenModule &IGM,

View File

@@ -327,6 +327,10 @@ private:
/// The queue of lazy witness tables to emit.
llvm::SmallVector<SILWitnessTable *, 4> LazyWitnessTables;
llvm::SmallVector<CanType, 4> LazyClassMetadata;
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedClassMetadata;
llvm::SmallVector<CanType, 4> LazySpecializedClassMetadata;
llvm::SmallPtrSet<TypeBase *, 4> LazilyEmittedSpecializedClassMetadata;
@@ -476,6 +480,7 @@ public:
}
}
void noteUseOfClassMetadata(CanType classType);
void noteUseOfSpecializedClassMetadata(CanType classType);
void noteUseOfTypeMetadata(NominalTypeDecl *type) {

View File

@@ -518,6 +518,17 @@ SILVTable *SILModule::lookUpVTable(const ClassDecl *C,
if (!Vtbl)
return nullptr;
if (C->walkSuperclasses([&](ClassDecl *S) {
SILVTable *Vtbl = getSILLoader()->lookupVTable(S);
if (!Vtbl) {
return TypeWalker::Action::Stop;
}
VTableMap[C] = Vtbl;
return TypeWalker::Action::Continue;
})) {
return nullptr;
}
// If we succeeded, map C -> VTbl in the table and return VTbl.
VTableMap[C] = Vtbl;
return Vtbl;

View File

@@ -611,6 +611,30 @@ void CrossModuleOptimization::makeDeclUsableFromInline(ValueDecl *decl) {
auto &ctx = decl->getASTContext();
auto *attr = new (ctx) UsableFromInlineAttr(/*implicit=*/true);
decl->getAttrs().add(attr);
if (everything) {
// Serialize vtables, their superclass vtables, and make all vfunctions
// usable from inline.
if (auto *classDecl = dyn_cast<ClassDecl>(decl)) {
auto *vTable = M.lookUpVTable(classDecl);
vTable->setSerialized(IsSerialized);
for (auto &entry : vTable->getEntries()) {
makeFunctionUsableFromInline(entry.getImplementation());
}
classDecl->walkSuperclasses([&](ClassDecl *superClassDecl) {
auto *vTable = M.lookUpVTable(superClassDecl);
if (!vTable) {
return TypeWalker::Action::Stop;
}
vTable->setSerialized(IsSerialized);
for (auto &entry : vTable->getEntries()) {
makeFunctionUsableFromInline(entry.getImplementation());
}
return TypeWalker::Action::Continue;
});
}
}
}
if (auto *nominalCtx = dyn_cast<NominalTypeDecl>(decl->getDeclContext())) {
makeDeclUsableFromInline(nominalCtx);

View File

@@ -0,0 +1,39 @@
// RUN: %empty-directory(%t)
// RUN: %{python} %utils/split_file.py -o %t %s
// RUN: %target-swift-frontend -emit-module -o %t/MyModule.swiftmodule %t/MyModule.swift %S/Inputs/print.swift -enable-experimental-feature Embedded -parse-as-library
// RUN: %target-swift-frontend -c -I %t %t/Main.swift -enable-experimental-feature Embedded -o %t/a.o
// RUN: %target-clang -x c -c %S/Inputs/print.c -o %t/print.o
// RUN: %target-clang %t/a.o %t/print.o -o %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s
// REQUIRES: executable_test
// REQUIRES: VENDOR=apple
// REQUIRES: OS=macosx
// BEGIN MyModule.swift
internal class BaseClass {
}
final internal class MyClass: BaseClass {
func foo() { print("MyClass.foo") }
}
public func foo() {
let o = MyClass()
o.foo()
}
// BEGIN Main.swift
import MyModule
func test() {
foo()
}
test()
// CHECK: MyClass.foo