Skip to content

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

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 3 commits into from
Oct 7, 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
3 changes: 3 additions & 0 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>();

Expand Down
19 changes: 18 additions & 1 deletion lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1436,6 +1436,7 @@ void IRGenerator::emitLazyDefinitions() {
assert(LazyWitnessTables.empty());
assert(LazyCanonicalSpecializedMetadataAccessors.empty());
assert(LazyMetadataAccessors.empty());
// LazyClassMetadata is allowed
// LazySpecializedClassMetadata is allowed
}

Expand All @@ -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();
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}
}
Expand Down
84 changes: 47 additions & 37 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4996,64 +4996,74 @@ 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);

assert(vtable);

CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();
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::emitLazySpecializedClassMetadata(IRGenModule &IGM,
CanType classTy) {
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 specialized class metadata for", classTy);
context, "emitting lazy 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();
SILVTable *vtable = IGM.getSILModule().lookUpVTable(classDecl);
emitEmbeddedVTable(IGM, classTy, vtable);
}

ConstantInitBuilder initBuilder(IGM);
auto init = initBuilder.beginStruct();
init.setPacked(true);
void irgen::emitLazySpecializedClassMetadata(IRGenModule &IGM,
CanType classTy) {
auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext();
PrettyStackTraceType stackTraceRAII(
context, "emitting lazy specialized class metadata for", classTy);

SILType classType = SILType::getPrimitiveObjectType(classTy);
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,
Expand Down
2 changes: 2 additions & 0 deletions lib/IRGen/GenMeta.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ class IRGenerator {
/// 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;
Expand Down Expand Up @@ -476,6 +480,7 @@ class IRGenerator {
}
}

void noteUseOfClassMetadata(CanType classType);
void noteUseOfSpecializedClassMetadata(CanType classType);

void noteUseOfTypeMetadata(NominalTypeDecl *type) {
Expand Down
14 changes: 14 additions & 0 deletions lib/SIL/IR/SILModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,20 @@ SILVTable *SILModule::lookUpVTable(const ClassDecl *C,
if (!Vtbl)
return nullptr;

if (C->walkSuperclasses([&](ClassDecl *S) {
auto R = VTableMap.find(S);
if (R != VTableMap.end())
return TypeWalker::Action::Continue;
SILVTable *Vtbl = getSILLoader()->lookupVTable(S);
if (!Vtbl) {
return TypeWalker::Action::Stop;
}
VTableMap[S] = Vtbl;
return TypeWalker::Action::Continue;
})) {
return nullptr;
}

// If we succeeded, map C -> VTbl in the table and return VTbl.
VTableMap[C] = Vtbl;
return Vtbl;
Expand Down
24 changes: 24 additions & 0 deletions lib/SILOptimizer/IPO/CrossModuleOptimization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
39 changes: 39 additions & 0 deletions test/embedded/modules-classes.swift
Original file line number Diff line number Diff line change
@@ -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