From 7da86b1148fa320d1d508f58ced6d74f395fda28 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 5 Oct 2023 10:45:49 -0700 Subject: [PATCH 1/3] [embedded] Serialize+deserialize vtables, fix using non-generic classes from other modules in embedded Swift --- lib/IRGen/GenClass.cpp | 3 + lib/IRGen/GenDecl.cpp | 19 ++++- lib/IRGen/GenMeta.cpp | 84 +++++++++++-------- lib/IRGen/GenMeta.h | 2 + lib/IRGen/IRGenModule.h | 5 ++ lib/SIL/IR/SILModule.cpp | 11 +++ .../IPO/CrossModuleOptimization.cpp | 24 ++++++ test/embedded/modules-classes.swift | 39 +++++++++ 8 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 test/embedded/modules-classes.swift diff --git a/lib/IRGen/GenClass.cpp b/lib/IRGen/GenClass.cpp index 9fe95aa04a907..da8b1af5d570a 100644 --- a/lib/IRGen/GenClass.cpp +++ b/lib/IRGen/GenClass.cpp @@ -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(); diff --git a/lib/IRGen/GenDecl.cpp b/lib/IRGen/GenDecl.cpp index b68977682516f..21311df36f9d3 100644 --- a/lib/IRGen/GenDecl.cpp +++ b/lib/IRGen/GenDecl.cpp @@ -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(nominal)) { if (classDecl->isGenericContext()) { IRGen.noteUseOfSpecializedClassMetadata(concreteType); + } else { + IRGen.noteUseOfClassMetadata(concreteType); } } } diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 2e22ec2b715ab..cb48dfc88660d 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -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(); - // 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( + 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(); - - 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, diff --git a/lib/IRGen/GenMeta.h b/lib/IRGen/GenMeta.h index 7190ec9fb02a9..2b3880eca1331 100644 --- a/lib/IRGen/GenMeta.h +++ b/lib/IRGen/GenMeta.h @@ -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, diff --git a/lib/IRGen/IRGenModule.h b/lib/IRGen/IRGenModule.h index cb504378eec3a..57e4f9e7a2509 100644 --- a/lib/IRGen/IRGenModule.h +++ b/lib/IRGen/IRGenModule.h @@ -327,6 +327,10 @@ class IRGenerator { /// The queue of lazy witness tables to emit. llvm::SmallVector LazyWitnessTables; + llvm::SmallVector LazyClassMetadata; + + llvm::SmallPtrSet LazilyEmittedClassMetadata; + llvm::SmallVector LazySpecializedClassMetadata; llvm::SmallPtrSet LazilyEmittedSpecializedClassMetadata; @@ -476,6 +480,7 @@ class IRGenerator { } } + void noteUseOfClassMetadata(CanType classType); void noteUseOfSpecializedClassMetadata(CanType classType); void noteUseOfTypeMetadata(NominalTypeDecl *type) { diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 85c551a03a11a..35099caaa8255 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -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; diff --git a/lib/SILOptimizer/IPO/CrossModuleOptimization.cpp b/lib/SILOptimizer/IPO/CrossModuleOptimization.cpp index 658316b73d2b3..cc4818fcd73ad 100644 --- a/lib/SILOptimizer/IPO/CrossModuleOptimization.cpp +++ b/lib/SILOptimizer/IPO/CrossModuleOptimization.cpp @@ -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(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(decl->getDeclContext())) { makeDeclUsableFromInline(nominalCtx); diff --git a/test/embedded/modules-classes.swift b/test/embedded/modules-classes.swift new file mode 100644 index 0000000000000..fd58b37c68ac5 --- /dev/null +++ b/test/embedded/modules-classes.swift @@ -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 From f144bf0bb4b638ba3ac0d5413d1c5396120d45fd Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Thu, 5 Oct 2023 13:48:03 -0700 Subject: [PATCH 2/3] [embedded] Avoid overwriting entries in the VTableMap (fix typo) --- lib/SIL/IR/SILModule.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 35099caaa8255..345fb3fc8f3ac 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -523,7 +523,7 @@ SILVTable *SILModule::lookUpVTable(const ClassDecl *C, if (!Vtbl) { return TypeWalker::Action::Stop; } - VTableMap[C] = Vtbl; + VTableMap[S] = Vtbl; return TypeWalker::Action::Continue; })) { return nullptr; From c5dd11a3585d021e89c61e198c0c211f6e595726 Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Fri, 6 Oct 2023 11:12:12 -0700 Subject: [PATCH 3/3] [embedded] Don't deserialize alraedy existin superclass vtables in SILModule::lookUpVTable --- lib/SIL/IR/SILModule.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/SIL/IR/SILModule.cpp b/lib/SIL/IR/SILModule.cpp index 345fb3fc8f3ac..21b39a2407c11 100644 --- a/lib/SIL/IR/SILModule.cpp +++ b/lib/SIL/IR/SILModule.cpp @@ -519,6 +519,9 @@ SILVTable *SILModule::lookUpVTable(const ClassDecl *C, 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;