From cedc0ef25b895c864079b34ea026eb3e1169ef9f Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Nov 2025 10:17:23 +0100 Subject: [PATCH 1/4] embedded: don't re-abstract witness method calls for existentials Don't convert indirect to direct arguments. This is needed to support general existentials in embedded swift. --- .../Sources/Optimizer/Utilities/GenericSpecialization.swift | 4 ++-- include/swift/SILOptimizer/Utils/Generics.h | 2 ++ lib/SILOptimizer/Utils/Generics.cpp | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift b/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift index ef8dbc679afc8..d402190c05a4a 100644 --- a/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift +++ b/SwiftCompilerSources/Sources/Optimizer/Utilities/GenericSpecialization.swift @@ -148,7 +148,7 @@ func specializeWitnessTable(for conformance: Conformance, _ context: ModulePassC guard !methodSubs.conformances.contains(where: {!$0.isValid}), context.loadFunction(function: origMethod, loadCalleesRecursively: true), let specializedMethod = context.specialize(function: origMethod, for: methodSubs, - convertIndirectToDirect: true, isMandatory: true) + convertIndirectToDirect: false, isMandatory: true) else { return origEntry } @@ -215,7 +215,7 @@ private func specializeDefaultMethods(for conformance: Conformance, guard !methodSubs.conformances.contains(where: {!$0.isValid}), context.loadFunction(function: origMethod, loadCalleesRecursively: true), let specializedMethod = context.specialize(function: origMethod, for: methodSubs, - convertIndirectToDirect: true, isMandatory: true) + convertIndirectToDirect: false, isMandatory: true) else { return origEntry } diff --git a/include/swift/SILOptimizer/Utils/Generics.h b/include/swift/SILOptimizer/Utils/Generics.h index 8b320ce92da27..11c914d46c8d6 100644 --- a/include/swift/SILOptimizer/Utils/Generics.h +++ b/include/swift/SILOptimizer/Utils/Generics.h @@ -216,7 +216,9 @@ class ReabstractionInfo { ReabstractionInfo(CanSILFunctionType substitutedType, SILDeclRef methodDecl, + bool convertIndirectToDirect, SILModule &M) : + ConvertIndirectToDirect(convertIndirectToDirect), SubstitutedType(substitutedType), methodDecl(methodDecl), M(&M), isWholeModule(M.isWholeModule()) {} diff --git a/lib/SILOptimizer/Utils/Generics.cpp b/lib/SILOptimizer/Utils/Generics.cpp index 9ef5e942708cf..b12bf8c36a0b8 100644 --- a/lib/SILOptimizer/Utils/Generics.cpp +++ b/lib/SILOptimizer/Utils/Generics.cpp @@ -2413,7 +2413,8 @@ bool swift::specializeClassMethodInst(ClassMethodInst *cm) { SILType substitutedType = funcTy.substGenericArgs(m, subs, TypeExpansionContext::minimal()); - ReabstractionInfo reInfo(substitutedType.getAs(), cm->getMember(), m); + ReabstractionInfo reInfo(substitutedType.getAs(), cm->getMember(), + /*convertIndirectToDirect=*/ true, m); reInfo.createSubstitutedAndSpecializedTypes(); CanSILFunctionType finalFuncTy = reInfo.getSpecializedType(); SILType finalSILTy = SILType::getPrimitiveObjectType(finalFuncTy); @@ -2465,7 +2466,8 @@ bool swift::specializeWitnessMethodInst(WitnessMethodInst *wm) { SILType substitutedType = funcTy.substGenericArgs(m, subs, TypeExpansionContext::minimal()); - ReabstractionInfo reInfo(substitutedType.getAs(), wm->getMember(), m); + ReabstractionInfo reInfo(substitutedType.getAs(), wm->getMember(), + /*convertIndirectToDirect=*/ false, m); reInfo.createSubstitutedAndSpecializedTypes(); CanSILFunctionType finalFuncTy = reInfo.getSpecializedType(); SILType finalSILTy = SILType::getPrimitiveObjectType(finalFuncTy); From 181051b9f506628cb243356b1d5a78e0e9be6087 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Nov 2025 10:19:13 +0100 Subject: [PATCH 2/4] SIL: add some APIs for `InitExistentialAddrInst` * `var conformances: ConformanceArray` * `var formalConcreteType: CanonicalType` --- SwiftCompilerSources/Sources/SIL/Instruction.swift | 10 +++++++++- include/swift/SIL/SILBridging.h | 2 ++ include/swift/SIL/SILBridgingImpl.h | 8 ++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/SwiftCompilerSources/Sources/SIL/Instruction.swift b/SwiftCompilerSources/Sources/SIL/Instruction.swift index 2e0ee31ebab7a..23552f8aadc55 100644 --- a/SwiftCompilerSources/Sources/SIL/Instruction.swift +++ b/SwiftCompilerSources/Sources/SIL/Instruction.swift @@ -879,7 +879,15 @@ final public class OpenExistentialValueInst : SingleValueInstruction, UnaryInstruction {} final public -class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction {} +class InitExistentialAddrInst : SingleValueInstruction, UnaryInstruction { + public var conformances: ConformanceArray { + ConformanceArray(bridged: bridged.InitExistentialAddrInst_getConformances()) + } + + public var formalConcreteType: CanonicalType { + CanonicalType(bridged: bridged.InitExistentialAddrInst_getFormalConcreteType()) + } +} final public class DeinitExistentialAddrInst : Instruction, UnaryInstruction {} diff --git a/include/swift/SIL/SILBridging.h b/include/swift/SIL/SILBridging.h index 00eae623f724c..7f067205ab1a3 100644 --- a/include/swift/SIL/SILBridging.h +++ b/include/swift/SIL/SILBridging.h @@ -808,6 +808,8 @@ struct BridgedInstruction { BRIDGED_INLINE bool IndexAddrInst_needsStackProtection() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialRefInst_getConformances() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialRefInst_getFormalConcreteType() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialAddrInst_getConformances() const; + SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialAddrInst_getFormalConcreteType() const; BRIDGED_INLINE bool OpenExistentialAddr_isImmutable() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar GlobalAccessInst_getGlobal() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar AllocGlobalInst_getGlobal() const; diff --git a/include/swift/SIL/SILBridgingImpl.h b/include/swift/SIL/SILBridgingImpl.h index 13edc89c48d8d..36b256bdd1ddf 100644 --- a/include/swift/SIL/SILBridgingImpl.h +++ b/include/swift/SIL/SILBridgingImpl.h @@ -1288,6 +1288,14 @@ BridgedCanType BridgedInstruction::InitExistentialRefInst_getFormalConcreteType( return getAs()->getFormalConcreteType(); } +BridgedConformanceArray BridgedInstruction::InitExistentialAddrInst_getConformances() const { + return {getAs()->getConformances()}; +} + +BridgedCanType BridgedInstruction::InitExistentialAddrInst_getFormalConcreteType() const { + return getAs()->getFormalConcreteType(); +} + bool BridgedInstruction::OpenExistentialAddr_isImmutable() const { switch (getAs()->getAccessKind()) { case swift::OpenedExistentialAccess::Immutable: return true; From 774c8098627da366f9edf865a23d48628fde03ff Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Thu, 20 Nov 2025 19:45:23 +0100 Subject: [PATCH 3/4] AST: add `var ProtocolDecl.isMarkerProtocol` --- SwiftCompilerSources/Sources/AST/Declarations.swift | 1 + include/swift/AST/ASTBridging.h | 1 + include/swift/AST/ASTBridgingImpl.h | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/SwiftCompilerSources/Sources/AST/Declarations.swift b/SwiftCompilerSources/Sources/AST/Declarations.swift index a585fe5ff76d2..dff2d326823f2 100644 --- a/SwiftCompilerSources/Sources/AST/Declarations.swift +++ b/SwiftCompilerSources/Sources/AST/Declarations.swift @@ -132,6 +132,7 @@ final public class ClassDecl: NominalTypeDecl { final public class ProtocolDecl: NominalTypeDecl { public var requiresClass: Bool { bridged.ProtocolDecl_requiresClass() } + public var isMarkerProtocol: Bool { bridged.ProtocolDecl_isMarkerProtocol() } } final public class BuiltinTupleDecl: NominalTypeDecl {} diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 9f8c1ccfaa20a..ebb7e8a0365ef 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -347,6 +347,7 @@ struct BridgedDeclObj { SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType Class_getSuperclass() const; SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj Class_getDestructor() const; BRIDGED_INLINE bool ProtocolDecl_requiresClass() const; + BRIDGED_INLINE bool ProtocolDecl_isMarkerProtocol() const; BRIDGED_INLINE bool AbstractFunction_isOverridden() const; BRIDGED_INLINE bool Destructor_isIsolated() const; BRIDGED_INLINE bool EnumElementDecl_hasAssociatedValues() const; diff --git a/include/swift/AST/ASTBridgingImpl.h b/include/swift/AST/ASTBridgingImpl.h index 2e236d083837d..5e5f5397e92e8 100644 --- a/include/swift/AST/ASTBridgingImpl.h +++ b/include/swift/AST/ASTBridgingImpl.h @@ -260,6 +260,10 @@ bool BridgedDeclObj::ProtocolDecl_requiresClass() const { return getAs()->requiresClass(); } +bool BridgedDeclObj::ProtocolDecl_isMarkerProtocol() const { + return getAs()->isMarkerProtocol(); +} + bool BridgedDeclObj::AbstractFunction_isOverridden() const { return getAs()->isOverridden(); } From a126e781e5cd1ee84509475a2aa8f0b6b7878775 Mon Sep 17 00:00:00 2001 From: Erik Eckstein Date: Tue, 11 Nov 2025 10:22:47 +0100 Subject: [PATCH 4/4] MandatoryPerformanceOptimizations: specialize witness tables for general existentials --- .../MandatoryPerformanceOptimizations.swift | 8 + test/embedded/general-existentials1.swift | 300 ++++++++++++++++++ 2 files changed, 308 insertions(+) create mode 100644 test/embedded/general-existentials1.swift diff --git a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift index 11616d84335bb..b62f6c5d8c180 100644 --- a/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift +++ b/SwiftCompilerSources/Sources/Optimizer/ModulePasses/MandatoryPerformanceOptimizations.swift @@ -143,6 +143,14 @@ private func optimize(function: Function, _ context: FunctionPassContext, _ modu } } + case let initExAddr as InitExistentialAddrInst: + if context.options.enableEmbeddedSwift { + for c in initExAddr.conformances where c.isConcrete && !c.protocol.isMarkerProtocol { + specializeWitnessTable(for: c, moduleContext) + worklist.addWitnessMethods(of: c, moduleContext) + } + } + case let bi as BuiltinInst: switch bi.id { case .BuildOrdinaryTaskExecutorRef, diff --git a/test/embedded/general-existentials1.swift b/test/embedded/general-existentials1.swift new file mode 100644 index 0000000000000..a39b48ab77c1b --- /dev/null +++ b/test/embedded/general-existentials1.swift @@ -0,0 +1,300 @@ +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library -wmo) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library -wmo -O) | %FileCheck %s +// RUN: %target-run-simple-swift(-enable-experimental-feature Embedded -enable-experimental-feature EmbeddedExistentials -parse-as-library -wmo -Osize) | %FileCheck %s + +// REQUIRES: swift_in_compiler +// REQUIRES: executable_test +// REQUIRES: optimized_stdlib +// REQUIRES: swift_feature_Embedded +// REQUIRES: swift_feature_EmbeddedExistentials + +protocol P { + func foo() + func bar() + func predicate() -> Bool +} + +extension P { + public func predicate() -> Bool { true } +} + +struct MyStruct : P { + var i: Int + + func foo() { print("MyStruct.foo: \(i)") } + func bar() { print("MyStruct.bar: \(i)") } +} + +struct LargeStruct : P { + var a: Int + var b: Int + var c: Int + var d: Int + + func foo() { print("LargeStruct.foo: \(a), \(b), \(c), \(d)") } + func bar() { print("LargeStruct.bar: \(a), \(b), \(c), \(d)") } +} + +struct GenericStruct : P { + var t: T + + func foo() { print("GenericStruct.foo: \(t)") } + func bar() { print("GenericStruct.bar: \(t)") } +} + +func test(existential: any P) { + existential.foo() + existential.bar() +} + +/* +public protocol ProtoWithAssocType { + associatedtype T + func foo(t: T) +} + +final public class GenClass: ProtoWithAssocType { + public func foo(t: T) { + print(t) + } +} + +public func createExWithAssocType() -> any ProtoWithAssocType { + return GenClass() +} + +public func callExWithAssocType(_ p: any ProtoWithAssocType) { + p.foo(t: 27) +} + +public protocol Q { + func bar() +} + +public protocol ProtoWithAssocConf { + associatedtype A: Q + func foo() -> A +} + +public class GenClass2: Q { + final var t: T + + init(t : T) { self.t = t } + + public func bar() { + print("bar") + } +} + +public class DerivedFromGenClass2: GenClass2 { + init() { super.init(t: 42) } + + public override func bar() { + print("derived-bar") + } +} + +final public class GenClass3: ProtoWithAssocConf { + public func foo() -> GenClass2 { + print("foo") + return GenClass2(t: 27) + } +} + +final public class OtherClass: ProtoWithAssocConf { + public func foo() -> GenClass2 { + print("other-foo") + return DerivedFromGenClass2() + } +} + + +public func createExWithAssocConf() -> any ProtoWithAssocConf { + return GenClass3() +} + +public func callExWithAssocConf(_ p: any ProtoWithAssocConf) { + let x = p.foo() + x.bar() +} + +public class Base: P { + public func foo() { print("Base.foo()") } + public func bar() { print("Base.bar()") } +} + +public class Derived1: Base { + public override func foo() { print("Derived1.foo()") } + public override func bar() { print("Derived1.bar()") } +} + +public class Derived2: Base { + public override func foo() { print("Derived2.foo()") } + public override func bar() { print("Derived2.bar()") } +} + +public func takes_p1(_ p: P1) { + p.normal() +} + +public protocol P1 { + func normal() +} + +public protocol P2 { + func foo() +} + +public class ConditionalConformanceBase { + final var a: A + + init(a: A) { self.a = a } +} + +extension ConditionalConformanceBase: P1 where A: P2 { + public func normal() { + a.foo() + } +} + +public class ConditionalConformanceDerived: ConditionalConformanceBase { + init(t: T) { super.init(a: t) } +} + + +public func testConditionalConformance(t: T) { + takes_p1(ConditionalConformanceDerived(t: t)) +} + + +struct S: P2 { + var i: Int + + func foo() { + print(i) + } +} + +protocol Q3 { + func bar() +} + +protocol P3 { + associatedtype T: Q3 + + var t: T { get } + + func foo() +} + +extension P3 { + func foo() { + t.bar() + } +} + +struct C3: P3 { + var t: T + + + init(t: T) { self.t = t } +} + +struct S3: Q3 { + var x: I + + func bar() { + print(x) + } +} + +@inline(never) +func testP3() -> any P3 { + return C3(t: S3(x: 102)) +} + +protocol P4 { + associatedtype T: Q + + var t: T { get } + + func foo() +} + +extension P4 { + func foo() { + t.bar() + } +} + +struct C4: P4 { + var t: T + + + init(t: T) { self.t = t } +} + +struct K4: Q { + var x: I + + init(x: I) { self.x = x } + + func bar() { + print(x) + } +} + +@inline(never) +func testP4() -> any P4 { + return C4(t: K4(x: 437)) +} +*/ + +@main +struct Main { + static func main() { + test(existential: MyStruct(i: 27)) + // CHECK: MyStruct.foo: 27 + // CHECK: MyStruct.bar: 27 + + test(existential: LargeStruct(a: 10, b: 11, c: 12, d: 13)) + // CHECK: LargeStruct.foo: 10, 11, 12, 13 + // CHECK: LargeStruct.bar: 10, 11, 12, 13 + + test(existential: GenericStruct(t: 28)) + // CHECK: GenericStruct.foo: 28 + // CHECK: GenericStruct.bar: 28 + + /* + callExWithAssocType(createExWithAssocType()) + // xCHECK: 27 + + callExWithAssocConf(createExWithAssocConf()) + // xCHECK: foo + // xCHECK: bar + + callExWithAssocConf(OtherClass()) + // xCHECK: other-foo + // xCHECK: derived-bar + + test(existential: Derived1()) + // xCHECK: Derived1.foo() + // xCHECK: Derived1.bar() + + test(existential: Derived2()) + // xCHECK: Derived2.foo() + // xCHECK: Derived2.bar() + + testConditionalConformance(t: S(i: 27)) + // xCHECK: 27 + + testP3().foo() + // xCHECK: 102 + + testP4().foo() + // xCHECK: 437 + */ + } +} + +