Skip to content
Open
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
12 changes: 9 additions & 3 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1051,16 +1051,22 @@ void IRGenModule::emitClassDecl(ClassDecl *D) {
auto &resilientLayout =
classTI.getClassLayout(*this, selfType, /*forBackwardDeployment=*/false);

auto isEmbeddedWithExistentials =
Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);

// As a matter of policy, class metadata is never emitted lazily for now.
assert(!IRGen.hasLazyMetadata(D));
assert(isEmbeddedWithExistentials || !IRGen.hasLazyMetadata(D));

// Emit the class metadata.
if (!D->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
emitClassMetadata(*this, D, fragileLayout, resilientLayout);
emitFieldDescriptor(D);
} else {
if (!D->isGenericContext()) {
emitEmbeddedClassMetadata(*this, D, fragileLayout);
if (!isEmbeddedWithExistentials && !D->isGenericContext()) {
emitEmbeddedClassMetadata(*this, D);
} else {
// We create all metadata lazyly in embedded with existentials mode.
return;
}
}

Expand Down
13 changes: 10 additions & 3 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5398,6 +5398,7 @@ diagnoseUnsupportedObjCImplLayout(IRGenModule &IGM, ClassDecl *classDecl,
void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
const ClassLayout &fragileLayout,
const ClassLayout &resilientLayout) {

assert(!classDecl->isForeign());
PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl);

Expand Down Expand Up @@ -5565,8 +5566,7 @@ static void emitEmbeddedVTable(IRGenModule &IGM, CanType classTy,
(void)var;
}

void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
const ClassLayout &fragileLayout) {
void irgen::emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *classDecl) {
PrettyStackTraceDecl stackTraceRAII("emitting metadata for", classDecl);
assert(!classDecl->isForeign());
CanType declaredType = classDecl->getDeclaredType()->getCanonicalType();
Expand All @@ -5578,7 +5578,9 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
// Might already be emitted, skip if that's the case.
auto entity =
LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::AddressPoint);
if (IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {

auto isEmbeddedWithExistentials = IGM.Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
if (isEmbeddedWithExistentials) {
entity = LinkEntity::forTypeMetadata(classTy, TypeMetadataAddress::FullMetadata);
}
auto *existingVar = cast<llvm::GlobalVariable>(
Expand All @@ -5587,6 +5589,11 @@ void irgen::emitLazyClassMetadata(IRGenModule &IGM, CanType classTy) {
return;
}

if (isEmbeddedWithExistentials) {
emitEmbeddedClassMetadata(IGM, classTy->getClassOrBoundGenericClass());
return;
}

auto &context = classTy->getNominalOrBoundGenericNominal()->getASTContext();
PrettyStackTraceType stackTraceRAII(
context, "emitting lazy class metadata for", classTy);
Expand Down
3 changes: 1 addition & 2 deletions lib/IRGen/GenMeta.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ namespace irgen {

/// Emit "embedded Swift" class metadata (a simple vtable) for the given class
/// declaration.
void emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *theClass,
const ClassLayout &fragileLayout);
void emitEmbeddedClassMetadata(IRGenModule &IGM, ClassDecl *theClass);

/// Emit the constant initializer of the type metadata candidate for
/// the given foreign class declaration.
Expand Down
28 changes: 23 additions & 5 deletions lib/IRGen/Linking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,21 @@ SILDeclRef LinkEntity::getSILDeclRef() const {
return ref;
}

static bool isLazyEmissionOfPublicSymbolInMultipleModulesPossible(CanType ty) {
// In embedded existenitals mode we generate lazy public metadata on demand
// which makes it non unique.
if (ty->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
if (auto nominal = ty->getAnyNominal()) {
if (SILDeclRef::declHasNonUniqueDefinition(nominal)) {
return true;
}
} else {
return true;
}
}
return false;
}

SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
// For when `this` is a protocol conformance of some kind.
auto getLinkageAsConformance = [&] {
Expand Down Expand Up @@ -658,6 +673,11 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
case Kind::ValueWitnessTable: {
auto type = getType();

// In embedded existenitals mode we generate lazy public metadata on demand
// which makes it non unique.
if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(type))
return SILLinkage::Shared;

// Builtin types, (), () -> () and so on are in the runtime.
if (!type.getAnyNominal())
return getSILLinkage(FormalLinkage::PublicUnique, forDefinition);
Expand Down Expand Up @@ -696,12 +716,10 @@ SILLinkage LinkEntity::getLinkage(ForDefinition_t forDefinition) const {
if (isForcedShared())
return SILLinkage::Shared;

// In embedded existenitals mode we generate metadata for tuple types.
if (getType()->getASTContext().LangOpts.hasFeature(Feature::EmbeddedExistentials) &&
(isa<TupleType>(getType()) ||
isa<FunctionType>(getType()))) {
// In embedded existenitals mode we generate lazy public metadata on demand
// which makes it non unique.
if (isLazyEmissionOfPublicSymbolInMultipleModulesPossible(getType()))
return SILLinkage::Shared;
}

auto *nominal = getType().getAnyNominal();
switch (getMetadataAddress()) {
Expand Down
109 changes: 109 additions & 0 deletions test/embedded/lazy_metadata.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// RUN: %empty-directory(%t)
// RUN: split-file %s %t

// RUN: %target-swift-frontend -wmo -module-name A -emit-irgen -o %t/A1.ll %t/A.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library
// RUN: %FileCheck --check-prefix=A1 %s < %t/A1.ll

// RUN: %target-swift-frontend -wmo -module-name A -num-threads 1 -emit-ir -o %t/A2.ll -o %t/B2.ll %t/A.swift %t/B.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library
// RUN: %FileCheck --check-prefix=A2 %s < %t/A2.ll
// RUN: %FileCheck --check-prefix=B2 %s < %t/B2.ll

// RUN: %target-swift-frontend -emit-module -emit-module-path %t/A.swiftmodule -wmo -module-name A -emit-ir -o %t/A3.ll %t/A.swift %t/B.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library
// RUN: %FileCheck --check-prefix=A3 %s < %t/A3.ll

// RUN: %target-swift-frontend -wmo -I %t -module-name C -emit-irgen -o %t/C4.ll %t/C.swift -enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library
// RUN: %FileCheck --check-prefix=C4 %s < %t/C4.ll

// REQUIRES: swift_in_compiler
// REQUIRES: optimized_stdlib
// REQUIRES: swift_feature_Embedded
// REQUIRES: swift_feature_EmbeddedExistentials


//--- A.swift
public class SomeClass {
}

public struct SomeStruct {
var x = 1
var y = 2
}

public enum SomeEnum {
case a(Int)
case b(Float)
}

//--- B.swift
public func getSomeClass() -> SomeClass {
return SomeClass()
}

public func getSomeStructAsAny() -> Any {
return SomeStruct()
}

public func getSomeStruct() -> SomeStruct {
return SomeStruct()
}

public func getSomeEnumAsAny() -> Any {
return SomeEnum.a(1)
}

public func getSomeEnum()-> SomeEnum {
return SomeEnum.b(2.0)
}

//--- C.swift
import A
public func useMetadata() -> Any {
return getSomeClass()
}

public func useMetadata2() -> Any {
return getSomeStruct()
}

public func useMetadata3() -> Any {
return getSomeEnum()
}

// Test no reference of metadata.
// A1-NOT: CMf

// Test reference of metadata. Because we are the defining module metadata is
// visible outside of the image per current policy.
// In multiple llvm modules per SIL module mode "Private" SIL linkage becomes
// hidden linkonce_odr.
// A2: @"$e1A8SomeEnumOMf" = linkonce_odr hidden constant
// A2: @"$e1A10SomeStructVMf" = linkonce_odr hidden constant
// A2: @"$e1A9SomeClassCMf" = linkonce_odr hidden global

// A2: @"$e1A8SomeEnumON" = alias{{.*}}e1A8SomeEnumOMf
// A2: @"$e1A10SomeStructVN" = alias{{.*}}e1A10SomeStructVMf
// A2: @"$e1A9SomeClassCN" = alias{{.*}}e1A9SomeClassCMf

// B2: @"$e1A9SomeClassCN" = {{.*}}external{{.*}} global
// B2: @"$e1A10SomeStructVN" = {{.*}}external{{.*}} global
// B2: @"$e1A8SomeEnumON" = {{.*}}external{{.*}} global
// B2: call {{.*}} @"$e1A9SomeClassCACycfC"(ptr swiftself @"$e1A9SomeClassCN")
// B2: store{{.*}}e1A10SomeStructVN
// B2: store{{.*}}e1A8SomeEnumON

// Test reference of metadata. Because we are the defining module metadata is
// visible outside of the image per current policy.
// In single llvm module per SIL module "Private" SIL linkage makes more
// intuitive sense.
// A3: @"$e1A8SomeEnumOMf" = internal constant
// A3: @"$e1A10SomeStructVMf" = internal constant
// A3: @"$e1A9SomeClassCMf" = internal global
// A3: @"$e1A9SomeClassCN" = alias{{.*}}e1A9SomeClassCMf
// A3: call {{.*}} @"$e1A9SomeClassCACycfC"({{.*}}getelementptr{{.*}}@"$e1A9SomeClassCMf"


// Test "external" reference of metadata (defines metadata from another module
// in current module as linkonce).
// C4: @"$e1A8SomeEnumOMf" = linkonce_odr hidden constant
// C4: @"$e1A10SomeStructVMf" = linkonce_odr hidden constant
// C4: @"$e1A9SomeClassCMf" = linkonce_odr hidden global