Skip to content

Commit 4a51cfa

Browse files
authored
Merge pull request #76572 from kubamracek/embedded-class-bound-existentials
[embedded] Introduce class-bound existentials into Embedded Swift
2 parents 5d5627a + a6c5e62 commit 4a51cfa

16 files changed

+314
-33
lines changed

include/swift/IRGen/Linking.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,23 @@ inline bool isEmbedded(CanType t) {
8989
return t->getASTContext().LangOpts.hasFeature(Feature::Embedded);
9090
}
9191

92+
// Metadata is not generated and not allowed to be referenced in Embedded Swift,
93+
// expect for classes (both generic and non-generic), dynamic self, and
94+
// class-bound existentials.
9295
inline bool isMetadataAllowedInEmbedded(CanType t) {
93-
return isa<ClassType>(t) || isa<BoundGenericClassType>(t) ||
94-
isa<DynamicSelfType>(t);
96+
if (isa<ClassType>(t) || isa<BoundGenericClassType>(t) ||
97+
isa<DynamicSelfType>(t)) {
98+
return true;
99+
}
100+
if (auto existentialTy = dyn_cast<ExistentialType>(t)) {
101+
if (existentialTy->requiresClass())
102+
return true;
103+
}
104+
if (auto archeTy = dyn_cast<ArchetypeType>(t)) {
105+
if (archeTy->requiresClass())
106+
return true;
107+
}
108+
return false;
95109
}
96110

97111
inline bool isEmbedded(Decl *d) {
@@ -1062,7 +1076,10 @@ class LinkEntity {
10621076
}
10631077

10641078
static LinkEntity forProtocolWitnessTable(const RootProtocolConformance *C) {
1065-
assert(!isEmbedded(C));
1079+
if (isEmbedded(C)) {
1080+
assert(C->getProtocol()->requiresClass());
1081+
}
1082+
10661083
LinkEntity entity;
10671084
entity.setForProtocolConformance(Kind::ProtocolWitnessTable, C);
10681085
return entity;

include/swift/SIL/RuntimeEffect.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ enum class RuntimeEffect : unsigned {
5454

5555
/// Witness methods, boxing, unboxing, initializing, etc.
5656
Existential = 0x80,
57+
58+
/// Class-bound only existential
59+
ExistentialClassBound = 0x200,
5760

5861
/// Not modelled currently.
5962
Concurrency = 0x0,

lib/IRGen/GenDecl.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,8 +1142,9 @@ void IRGenModule::emitGlobalLists() {
11421142
// Eagerly emit functions that are externally visible. Functions that are
11431143
// dynamic replacements must also be eagerly emitted.
11441144
static bool isLazilyEmittedFunction(SILFunction &f, SILModule &m) {
1145-
// Embedded Swift only emits specialized function, so don't emit generic
1146-
// functions, even if they're externally visible.
1145+
// Embedded Swift only emits specialized function (except when they are
1146+
// protocol witness methods). So don't emit generic functions, even if they're
1147+
// externally visible.
11471148
if (f.getASTContext().LangOpts.hasFeature(Feature::Embedded) &&
11481149
f.getLoweredFunctionType()->getSubstGenericSignature()) {
11491150
return true;
@@ -1332,7 +1333,7 @@ void IRGenerator::emitLazyDefinitions() {
13321333
assert(LazyFieldDescriptors.empty());
13331334
// LazyFunctionDefinitions are allowed, but they must not be generic
13341335
for (SILFunction *f : LazyFunctionDefinitions) {
1335-
assert(!f->isGeneric());
1336+
assert(hasValidSignatureForEmbedded(f));
13361337
}
13371338
assert(LazyWitnessTables.empty());
13381339
assert(LazyCanonicalSpecializedMetadataAccessors.empty());
@@ -1482,7 +1483,7 @@ void IRGenerator::addLazyFunction(SILFunction *f) {
14821483

14831484
// Embedded Swift doesn't expect any generic functions to be referenced.
14841485
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
1485-
assert(!f->isGeneric());
1486+
assert(hasValidSignatureForEmbedded(f));
14861487
}
14871488

14881489
assert(!FinishedEmittingLazyDefinitions);
@@ -3472,6 +3473,24 @@ llvm::CallBase *swift::irgen::emitCXXConstructorCall(
34723473
return result;
34733474
}
34743475

3476+
// For a SILFunction to be legal in Embedded Swift, it must be either
3477+
// - non-generic
3478+
// - generic with parameters thar are either
3479+
// - fully specialized (concrete)
3480+
// - a class-bound archetype (class-bound existential)
3481+
bool swift::irgen::hasValidSignatureForEmbedded(SILFunction *f) {
3482+
auto s = f->getLoweredFunctionType()->getInvocationGenericSignature();
3483+
for (auto genParam : s.getGenericParams()) {
3484+
auto mappedParam = f->getGenericEnvironment()->mapTypeIntoContext(genParam);
3485+
if (auto archeTy = dyn_cast<ArchetypeType>(mappedParam)) {
3486+
if (archeTy->requiresClass())
3487+
continue;
3488+
}
3489+
return false;
3490+
}
3491+
return true;
3492+
}
3493+
34753494
StackProtectorMode IRGenModule::shouldEmitStackProtector(SILFunction *f) {
34763495
const SILOptions &opts = IRGen.SIL.getOptions();
34773496
return (opts.EnableStackProtection && f->needsStackProtection()) ?
@@ -4351,7 +4370,10 @@ static bool conformanceIsVisibleViaMetadata(
43514370

43524371

43534372
void IRGenModule::addProtocolConformance(ConformanceDescription &&record) {
4354-
4373+
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
4374+
return;
4375+
}
4376+
43554377
emitProtocolConformance(record);
43564378

43574379
if (conformanceIsVisibleViaMetadata(record.conformance)) {

lib/IRGen/GenDecl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ namespace irgen {
7676
llvm::FunctionType *ctorFnType,
7777
llvm::Constant *ctorAddress,
7878
llvm::ArrayRef<llvm::Value *> args);
79+
80+
bool hasValidSignatureForEmbedded(SILFunction *f);
7981
}
8082
}
8183

lib/IRGen/GenExistential.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1791,11 +1791,6 @@ static void forEachProtocolWitnessTable(
17911791
assert(protocols.size() == witnessConformances.size() &&
17921792
"mismatched protocol conformances");
17931793

1794-
// Don't emit witness tables in embedded Swift.
1795-
if (srcType->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
1796-
return;
1797-
}
1798-
17991794
for (unsigned i = 0, e = protocols.size(); i < e; ++i) {
18001795
assert(protocols[i] == witnessConformances[i].getRequirement());
18011796
auto table = emitWitnessTableRef(IGF, srcType, srcMetadataCache,

lib/IRGen/GenProto.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,13 @@ class AccessorConformanceInfo : public ConformanceInfo {
15161516
/// Add reference to the protocol conformance descriptor that generated
15171517
/// this table.
15181518
void addProtocolConformanceDescriptor() {
1519+
// In Embedded Swift, there are no protocol conformance descriptors. Emit
1520+
// a null pointer instead to keep the same layout as regular Swift.
1521+
if (IGM.Context.LangOpts.hasFeature(Feature::Embedded)) {
1522+
Table.addNullPointer(IGM.Int8PtrTy);
1523+
return;
1524+
}
1525+
15191526
auto descriptor =
15201527
IGM.getAddrOfProtocolConformanceDescriptor(&Conformance);
15211528
if (isRelative)
@@ -2564,7 +2571,9 @@ static void addWTableTypeMetadata(IRGenModule &IGM,
25642571

25652572
void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) {
25662573
if (Context.LangOpts.hasFeature(Feature::Embedded)) {
2567-
return;
2574+
// In Embedded Swift, only class-bound wtables are allowed.
2575+
if (!wt->getConformance()->getProtocol()->requiresClass())
2576+
return;
25682577
}
25692578

25702579
// Don't emit a witness table if it is a declaration.
@@ -3564,9 +3573,13 @@ llvm::Value *irgen::emitWitnessTableRef(IRGenFunction &IGF,
35643573
CanType srcType,
35653574
llvm::Value **srcMetadataCache,
35663575
ProtocolConformanceRef conformance) {
3567-
assert(!srcType->getASTContext().LangOpts.hasFeature(Feature::Embedded));
3568-
35693576
auto proto = conformance.getRequirement();
3577+
3578+
// In Embedded Swift, only class-bound wtables are allowed.
3579+
if (srcType->getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
3580+
assert(proto->requiresClass());
3581+
}
3582+
35703583
assert(Lowering::TypeConverter::protocolRequiresWitnessTable(proto)
35713584
&& "protocol does not have witness tables?!");
35723585

lib/IRGen/IRGenModule.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1407,7 +1407,10 @@ bool IRGenerator::canEmitWitnessTableLazily(SILWitnessTable *wt) {
14071407
}
14081408

14091409
void IRGenerator::addLazyWitnessTable(const ProtocolConformance *Conf) {
1410-
assert(!SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded));
1410+
// In Embedded Swift, only class-bound wtables are allowed.
1411+
if (SIL.getASTContext().LangOpts.hasFeature(Feature::Embedded)) {
1412+
assert(Conf->getProtocol()->requiresClass());
1413+
}
14111414

14121415
if (auto *wt = SIL.lookUpWitnessTable(Conf)) {
14131416
// Add it to the queue if it hasn't already been put there.

lib/IRGen/IRGenSIL.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2478,10 +2478,6 @@ void IRGenModule::emitSILFunction(SILFunction *f) {
24782478
if (f->isExternalDeclaration())
24792479
return;
24802480

2481-
if (Context.LangOpts.hasFeature(Feature::Embedded) &&
2482-
f->getLoweredFunctionType()->isPolymorphic())
2483-
return;
2484-
24852481
// Do not emit bodies of public_external or package_external functions.
24862482
if (hasPublicOrPackageVisibility(f->getLinkage(),
24872483
f->getASTContext().SILOpts.EnableSerializePackage) &&

lib/SIL/Utils/InstructionUtils.cpp

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -671,9 +671,13 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
671671
RuntimeEffect::MetaData | RuntimeEffect::Existential;
672672

673673
case SILInstructionKind::InitExistentialRefInst:
674+
impactType = inst->getOperand(0)->getType();
675+
return RuntimeEffect::MetaData | RuntimeEffect::ExistentialClassBound;
676+
674677
case SILInstructionKind::InitExistentialMetatypeInst:
675678
impactType = inst->getOperand(0)->getType();
676679
return RuntimeEffect::MetaData | RuntimeEffect::Existential;
680+
677681
case SILInstructionKind::ObjCToThickMetatypeInst:
678682
impactType = inst->getOperand(0)->getType();
679683
return RuntimeEffect::MetaData;
@@ -693,14 +697,8 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
693697
return RuntimeEffect::Existential;
694698

695699
case SILInstructionKind::OpenExistentialRefInst: {
696-
SILType opType = cast<OpenExistentialRefInst>(inst)->getOperand()->getType();
697-
impactType = opType;
698-
if (opType.getASTType()->isObjCExistentialType()) {
699-
return RuntimeEffect::MetaData | RuntimeEffect::Existential;
700-
}
701-
return RuntimeEffect::MetaData | RuntimeEffect::Existential;
702-
// TODO: should be Existential
703-
//return RuntimeEffect::Existential;
700+
impactType = inst->getOperand(0)->getType();
701+
return RuntimeEffect::MetaData | RuntimeEffect::ExistentialClassBound;
704702
}
705703

706704
case SILInstructionKind::UnconditionalCheckedCastInst:
@@ -962,9 +960,16 @@ RuntimeEffect swift::getRuntimeEffect(SILInstruction *inst, SILType &impactType)
962960
case SILFunctionTypeRepresentation::Block:
963961
rt |= RuntimeEffect::ObjectiveC | RuntimeEffect::MetaData;
964962
break;
965-
case SILFunctionTypeRepresentation::WitnessMethod:
966-
rt |= RuntimeEffect::MetaData | RuntimeEffect::Existential;
963+
case SILFunctionTypeRepresentation::WitnessMethod: {
964+
auto conformance =
965+
as.getOrigCalleeType()->getWitnessMethodConformanceOrInvalid();
966+
if (conformance.getRequirement()->requiresClass()) {
967+
rt |= RuntimeEffect::MetaData | RuntimeEffect::ExistentialClassBound;
968+
} else {
969+
rt |= RuntimeEffect::MetaData | RuntimeEffect::Existential;
970+
}
967971
break;
972+
}
968973
case SILFunctionTypeRepresentation::CFunctionPointer:
969974
case SILFunctionTypeRepresentation::CXXMethod:
970975
case SILFunctionTypeRepresentation::Thin:

lib/SILOptimizer/Mandatory/PerformanceDiagnostics.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,8 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
534534
LocWithParent loc(inst->getLoc().getSourceLoc(), parentLoc);
535535

536536
if (perfConstr == PerformanceConstraints::NoExistentials &&
537-
(impact & RuntimeEffect::Existential)) {
537+
((impact & RuntimeEffect::Existential) ||
538+
(impact & RuntimeEffect::ExistentialClassBound))) {
538539
PrettyStackTracePerformanceDiagnostics stackTrace("existential", inst);
539540
if (impactType) {
540541
diagnose(loc, diag::perf_diag_existential_type, impactType.getASTType());
@@ -556,6 +557,8 @@ bool PerformanceDiagnostics::visitInst(SILInstruction *inst,
556557
}
557558

558559
if (module.getOptions().EmbeddedSwift) {
560+
// Explicitly don't detect RuntimeEffect::ExistentialClassBound - those are
561+
// allowed in Embedded Swift.
559562
if (impact & RuntimeEffect::Existential) {
560563
PrettyStackTracePerformanceDiagnostics stackTrace("existential", inst);
561564
if (impactType) {

lib/SILOptimizer/PassManager/PassPipeline.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,13 @@ void addFunctionPasses(SILPassPipelinePlan &P,
457457
P.addMem2Reg();
458458

459459
// Run the existential specializer Pass.
460-
P.addExistentialSpecializer();
460+
if (!P.getOptions().EmbeddedSwift) {
461+
// MandatoryPerformanceOptimizations already took care of all specializations
462+
// in embedded Swift mode, running the existential specializer might introduce
463+
// more generic calls from non-generic functions, which breaks the assumptions
464+
// of embedded Swift.
465+
P.addExistentialSpecializer();
466+
}
461467

462468
// Cleanup, which is important if the inliner has restarted the pass pipeline.
463469
P.addPerformanceConstantPropagation();
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s
2+
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -O) | %FileCheck %s
3+
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo -Osize) | %FileCheck %s
4+
5+
// REQUIRES: swift_in_compiler
6+
// REQUIRES: executable_test
7+
// REQUIRES: optimized_stdlib
8+
// REQUIRES: OS=macosx || OS=linux-gnu
9+
10+
protocol ClassBound: AnyObject {
11+
func foo()
12+
func bar()
13+
}
14+
15+
class MyClass {}
16+
extension MyClass: ClassBound {
17+
func foo() { print("MyClass.foo()") }
18+
func bar() { print("MyClass.bar()") }
19+
}
20+
21+
class MyOtherClass {}
22+
extension MyOtherClass: ClassBound {
23+
func foo() { print("MyOtherClass.foo()") }
24+
func bar() { print("MyOtherClass.bar()") }
25+
}
26+
27+
func test(existential: any ClassBound) {
28+
existential.foo()
29+
existential.bar()
30+
}
31+
32+
@main
33+
struct Main {
34+
static func main() {
35+
test(existential: MyClass())
36+
// CHECK: MyClass.foo()
37+
// CHECK: MyClass.bar()
38+
test(existential: MyOtherClass())
39+
// CHECK: MyOtherClass.foo()
40+
// CHECK: MyOtherClass.bar()
41+
}
42+
}
43+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -parse-as-library -wmo) | %FileCheck %s
2+
3+
// REQUIRES: swift_in_compiler
4+
// REQUIRES: executable_test
5+
// REQUIRES: optimized_stdlib
6+
// REQUIRES: OS=macosx || OS=linux-gnu
7+
8+
protocol ClassBound: AnyObject {
9+
func foo()
10+
func bar()
11+
}
12+
13+
extension ClassBound {
14+
func extensionMethod() {
15+
self.foo()
16+
self.bar()
17+
}
18+
}
19+
20+
class MyClass {}
21+
extension MyClass: ClassBound {
22+
func foo() { print("MyClass.foo()") }
23+
func bar() { print("MyClass.bar()") }
24+
}
25+
26+
class MyOtherClass {}
27+
extension MyOtherClass: ClassBound {
28+
func foo() { print("MyOtherClass.foo()") }
29+
func bar() { print("MyOtherClass.bar()") }
30+
}
31+
32+
@main
33+
struct Main {
34+
static func main() {
35+
var array: [any ClassBound] = []
36+
array.append(MyClass())
37+
array.append(MyOtherClass())
38+
39+
for e in array {
40+
e.extensionMethod()
41+
}
42+
43+
// CHECK: MyClass.foo()
44+
// CHECK: MyClass.bar()
45+
46+
// CHECK: MyOtherClass.foo()
47+
// CHECK: MyOtherClass.bar()
48+
}
49+
}
50+

0 commit comments

Comments
 (0)