Skip to content

embedded: fix two problems in the SILLinker #80530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 4, 2025
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
22 changes: 21 additions & 1 deletion lib/SIL/IR/Linker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,10 @@ void SILLinkerVisitor::visitProtocolConformance(
// reading in most conformances until we need them for devirtualization.
// However, we *must* pull in shared clang-importer-derived conformances
// we potentially use, since we may not otherwise have a local definition.
if (mustDeserializeProtocolConformance(Mod, c))
if ((isEmbedded && referencedFromInitExistential) ||
mustDeserializeProtocolConformance(Mod, c)) {
visitProtocolConformance(c, referencedFromInitExistential);
}
};

// For each entry in the witness table...
Expand Down Expand Up @@ -427,6 +429,24 @@ void SILLinkerVisitor::visitInitExistentialRefInst(
}
}

void SILLinkerVisitor::visitBuiltinInst(BuiltinInst *bi) {
switch (bi->getBuiltinInfo().ID) {
case BuiltinValueKind::BuildOrdinaryTaskExecutorRef:
case BuiltinValueKind::BuildOrdinarySerialExecutorRef:
case BuiltinValueKind::BuildComplexEqualitySerialExecutorRef:
if (Mod.getOptions().EmbeddedSwift) {
// Those builtins act like init_existential_ref instructions and therefore
// it's important to have the Executor witness tables available in embedded
// mode.
auto executorConf = bi->getSubstitutions().getConformances()[0];
visitProtocolConformance(executorConf, true);
}
break;
default:
break;
}
}

void SILLinkerVisitor::visitAllocRefDynamicInst(AllocRefDynamicInst *ARI) {
if (!isLinkAll())
return;
Expand Down
1 change: 1 addition & 0 deletions lib/SIL/IR/Linker.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class SILLinkerVisitor : public SILInstructionVisitor<SILLinkerVisitor, void> {
}
void visitInitExistentialAddrInst(InitExistentialAddrInst *IEI);
void visitInitExistentialRefInst(InitExistentialRefInst *IERI);
void visitBuiltinInst(BuiltinInst *bi);
void visitAllocRefInst(AllocRefInst *ARI);
void visitAllocRefDynamicInst(AllocRefDynamicInst *ARI);
void visitMetatypeInst(MetatypeInst *MI);
Expand Down
62 changes: 62 additions & 0 deletions test/embedded/concurrency-modules.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 -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 -parse-as-library
// RUN: %target-clang %t/a.o -o %t/a.out -L%swift_obj_root/lib/swift/embedded/%target-cpu-apple-macos -lswift_Concurrency -lswift_ConcurrencyDefaultExecutor -dead_strip
// RUN: %target-run %t/a.out | %FileCheck %s

// REQUIRES: swift_in_compiler
// REQUIRES: executable_test
// REQUIRES: OS=macosx
// REQUIRES: swift_feature_Embedded

// BEGIN MyModule.swift

import _Concurrency

nonisolated(unsafe) var glob: UnsafeMutableRawPointer? = nil
nonisolated(unsafe) var job: UnownedJob? = nil

public final class MyCustomExecutor: SerialExecutor, @unchecked Sendable {
private init() {}

nonisolated(unsafe)
public static var shared: MyCustomExecutor = MyCustomExecutor()

public static func installGlobalExecutor() {
let fn: @convention(thin) () -> () = {
MyCustomExecutor.shared.unsafeEnqueue(job!)
}
glob = unsafeBitCast(fn, to: UnsafeMutableRawPointer?.self)
}

private func enqueue(_ job: UnownedJob, withDelay nanoseconds: UInt64) {}

private func unsafeEnqueue(_ job: UnownedJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}

public func enqueue(_ job: consuming ExecutorJob) {
unsafeEnqueue(UnownedJob(job))
}

public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
return UnownedSerialExecutor(ordinary: self)
}
}

// BEGIN Main.swift

import MyModule
import _Concurrency

@main
struct Entrypoint {
static func main() async {
MyCustomExecutor.installGlobalExecutor()
print("OK!")
}
}

// CHECK: OK!
9 changes: 8 additions & 1 deletion test/embedded/existential-class-bound8.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

// BEGIN MyModule.swift

public protocol ClassBound: AnyObject {
public protocol Base: AnyObject {
func bar()
}

public protocol ClassBound: Base {
func foo()
}

Expand All @@ -18,6 +22,7 @@ class MyGenericClass<T> {
init(typ: String) { self.typ = typ }
}
extension MyGenericClass: ClassBound {
func bar() { print("MyGenericClass<\(typ)>.bar()") }
func foo() { print("MyGenericClass<\(typ)>.foo()") }
}

Expand All @@ -32,3 +37,5 @@ import MyModule
var arr: [any ClassBound] = [factory()]
arr[0].foo()
// CHECK: MyGenericClass<String>.foo()
arr[0].foo()
// CHECK: MyGenericClass<String>.bar()