Skip to content
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
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 lazily in embedded with existentials mode.
return;
}
}

Expand Down
16 changes: 8 additions & 8 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5308,7 +5308,8 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(

return cast<llvm::GlobalValue>(addr);
}

bool hasEmbeddedExistentials =
Context.LangOpts.hasFeature(Feature::EmbeddedExistentials);
auto entity =
(isPrespecialized &&
!irgen::isCanonicalInitializableTypeMetadataStaticallyAddressable(
Expand All @@ -5324,7 +5325,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
entity = LinkEntity::forTypeMetadata(concreteType,
TypeMetadataAddress::AddressPoint);
if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
if (hasEmbeddedExistentials)
entity = LinkEntity::forTypeMetadata(concreteType,
TypeMetadataAddress::FullMetadata);
}
Expand All @@ -5350,7 +5351,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
markGlobalAsUsedBasedOnLinkage(*this, link, var);

if (Context.LangOpts.hasFeature(Feature::Embedded) &&
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
!hasEmbeddedExistentials) {
return var;
}

Expand All @@ -5361,14 +5362,13 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
if (auto nominal = concreteType->getAnyNominal()) {
// Keep type metadata around for all types (except @_objcImplementation,
// since we're using ObjC metadata for that).
if (!isObjCImpl &&
!Context.LangOpts.hasFeature(Feature::EmbeddedExistentials))
if (!isObjCImpl && !hasEmbeddedExistentials)
addRuntimeResolvableType(nominal);

// Don't define the alias for foreign type metadata, prespecialized
// generic metadata, or @_objcImplementation classes, since they're not ABI.
if (requiresForeignTypeMetadata(nominal) ||
(isPrespecialized && !Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) ||
if ((requiresForeignTypeMetadata(nominal) && !hasEmbeddedExistentials) ||
(isPrespecialized && !hasEmbeddedExistentials) ||
isObjCImpl)
return var;

Expand All @@ -5382,7 +5382,7 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
}
}

if (Context.LangOpts.hasFeature(Feature::EmbeddedExistentials)) {
if (hasEmbeddedExistentials) {
adjustmentIndex = MetadataAdjustmentIndex::EmbeddedWithExistentials;
}

Expand Down
41 changes: 31 additions & 10 deletions lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5565,8 +5565,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 +5577,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 +5588,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 Expand Up @@ -7196,46 +7202,61 @@ void irgen::emitForeignTypeMetadata(IRGenModule &IGM, NominalTypeDecl *decl) {
auto init = builder.beginStruct();
init.setPacked(true);

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

if (auto classDecl = dyn_cast<ClassDecl>(decl)) {
if (classDecl->isForeignReferenceType()) {
assert(!isEmbedded && "emitting foregin reference type not supported");
ForeignReferenceTypeMetadataBuilder builder(IGM, classDecl, init);
builder.layout();

IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
if (!isEmbedded)
builder.createMetadataAccessFunction();
} else {
assert(classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType);
assert(!isEmbedded && "emitting foregin cf class type not supported");

ForeignClassMetadataBuilder builder(IGM, classDecl, init);
builder.layout();

IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
if (!isEmbedded)
builder.createMetadataAccessFunction();
}
} else if (auto structDecl = dyn_cast<StructDecl>(decl)) {
assert(isa<ClangModuleUnit>(structDecl->getModuleScopeContext()));

ForeignStructMetadataBuilder builder(IGM, structDecl, init);
builder.layout();
if (isEmbedded)
builder.embeddedLayout();
else
builder.layout();

IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
if (!isEmbedded)
builder.createMetadataAccessFunction();
} else if (auto enumDecl = dyn_cast<EnumDecl>(decl)) {
assert(enumDecl->hasClangNode());

ForeignEnumMetadataBuilder builder(IGM, enumDecl, init);
builder.layout();
if (isEmbedded)
builder.embeddedLayout();
else
builder.layout();

IGM.defineTypeMetadata(type, /*isPattern=*/false,
builder.canBeConstant(),
init.finishAndCreateFuture());
builder.createMetadataAccessFunction();
if (!isEmbedded)
builder.createMetadataAccessFunction();
} else {
llvm_unreachable("foreign metadata for unexpected type?!");
}
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
34 changes: 34 additions & 0 deletions test/embedded/Inputs/existential_foreign.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

typedef struct {
unsigned long long f1;
unsigned long long f2;
} SomeCStruct;

static inline SomeCStruct createSomeCStruct() {
SomeCStruct s;
s.f1 = 1;
s.f2 = 2;
return s;
}

typedef enum {
caseA,
caseB,
caseC
} SomeCEnum;

static inline SomeCEnum createSomeCEnum() {
return caseA;
}

#define SWIFT_ENUM(_type, _name) enum _name : _type

typedef SWIFT_ENUM(unsigned short, SomeNSEnum) {
someCaseA,
someCaseB,
} SomeNSEnum;

static inline SomeNSEnum createSomeNSEnum() {
return someCaseB;
}
102 changes: 102 additions & 0 deletions test/embedded/existential_foreign.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// RUN: %target-run-simple-swift(-import-objc-header %S/Inputs/existential_foreign.h -enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s --check-prefix=OUTPUT
// RUN: %target-run-simple-swift(-import-objc-header %S/Inputs/existential_foreign.h -enable-experimental-feature EmbeddedExistentials -enable-experimental-feature Embedded -parse-as-library -wmo -O) | %FileCheck %s --check-prefix=OUTPUT

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

protocol P {
func printme()
}

protocol Q {
associatedtype Printable : P

func getPrintable() -> Printable
}

extension SomeCStruct : P {
func printme() {
print("SomeCStruct: \(self.f1), \(self.f2)")
}
}

extension SomeCEnum : P {
func printme() {
switch self {
case caseA:
print("SomeCEnum: .caseA")
case caseB:
print("SomeCEnum: .caseB")
case caseC:
print("SomeCEnum: .caseC")
default:
print("SomeCEnum: default")
}
}
}

struct SomeCStructContainer : Q {
let s: SomeCStruct

init() {
self.s = createSomeCStruct()
}

func getPrintable() -> SomeCStruct {
return s
}
}

struct SomeCEnumContainer : Q {
let s: SomeCEnum

init() {
self.s = createSomeCEnum()
}

func getPrintable() -> SomeCEnum {
return s
}
}

extension SomeNSEnum : P {
func printme() {
switch self {
case .someCaseA:
print("SomeNSEnum: .someCaseA")
case .someCaseB:
print("SomeNSEnum: .someCaseB")
}
}
}

struct SomeNSEnumContainer : Q {
let s: SomeNSEnum

init() {
self.s = createSomeNSEnum()
}

func getPrintable() -> SomeNSEnum {
return s
}
}

@main
struct Main {
static func main() {

let a: [any Q] = [ SomeCStructContainer(), SomeCEnumContainer(), SomeNSEnumContainer() ]

for x0 in a {
let x = x0.getPrintable()
x.printme()
// OUTPUT: SomeCStruct: 1, 2
// OUTPUT: SomeCEnum: .caseA
// OUTPUT: SomeNSEnum: .someCaseB
}
}
}
Loading