Skip to content

Commit f368364

Browse files
authored
Merge pull request swiftlang#35565 from slavapestov/resilient-async-dispatch-thunks
Support async calling convention for resilient class and protocol method dispatch thunks
2 parents edd0c47 + 6864c3a commit f368364

File tree

10 files changed

+203
-9
lines changed

10 files changed

+203
-9
lines changed

lib/IRGen/GenMeta.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -289,8 +289,14 @@ static void buildMethodDescriptorFields(IRGenModule &IGM,
289289

290290
if (auto entry = VTable->getEntry(IGM.getSILModule(), fn)) {
291291
assert(entry->getKind() == SILVTable::Entry::Kind::Normal);
292-
auto *implFn = IGM.getAddrOfSILFunction(entry->getImplementation(),
293-
NotForDefinition);
292+
293+
auto *impl = entry->getImplementation();
294+
llvm::Constant *implFn;
295+
if (impl->isAsync())
296+
implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
297+
else
298+
implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
299+
294300
descriptor.addRelativeAddress(implFn);
295301
} else {
296302
// The method is removed by dead method elimination.
@@ -1757,8 +1763,14 @@ namespace {
17571763
// The implementation of the override.
17581764
if (auto entry = VTable->getEntry(IGM.getSILModule(), baseRef)) {
17591765
assert(entry->getKind() == SILVTable::Entry::Kind::Override);
1760-
auto *implFn = IGM.getAddrOfSILFunction(entry->getImplementation(),
1761-
NotForDefinition);
1766+
1767+
auto *impl = entry->getImplementation();
1768+
llvm::Constant *implFn;
1769+
if (impl->isAsync())
1770+
implFn = IGM.getAddrOfAsyncFunctionPointer(impl);
1771+
else
1772+
implFn = IGM.getAddrOfSILFunction(impl, NotForDefinition);
1773+
17621774
descriptor.addRelativeAddress(implFn);
17631775
} else {
17641776
// The method is removed by dead method elimination.

lib/IRGen/GenProto.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1678,7 +1678,10 @@ void ResilientWitnessTableBuilder::collectResilientWitnesses(
16781678
SILFunction *Func = entry.getMethodWitness().Witness;
16791679
llvm::Constant *witness;
16801680
if (Func) {
1681-
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
1681+
if (Func->isAsync())
1682+
witness = IGM.getAddrOfAsyncFunctionPointer(Func);
1683+
else
1684+
witness = IGM.getAddrOfSILFunction(Func, NotForDefinition);
16821685
} else {
16831686
// The method is removed by dead method elimination.
16841687
// It should be never called. We add a null pointer.

lib/IRGen/IRGenSIL.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6133,14 +6133,22 @@ void IRGenSILFunction::visitWitnessMethodInst(swift::WitnessMethodInst *i) {
61336133
CanType baseTy = i->getLookupType();
61346134
ProtocolConformanceRef conformance = i->getConformance();
61356135
SILDeclRef member = i->getMember();
6136+
auto fnType = IGM.getSILTypes().getConstantFunctionType(
6137+
IGM.getMaximalTypeExpansionContext(), member);
61366138

61376139
assert(member.requiresNewWitnessTableEntry());
61386140

61396141
if (IGM.isResilient(conformance.getRequirement(),
61406142
ResilienceExpansion::Maximal)) {
6141-
auto *fnPtr = IGM.getAddrOfDispatchThunk(member, NotForDefinition);
6142-
auto fnType = IGM.getSILTypes().getConstantFunctionType(
6143-
IGM.getMaximalTypeExpansionContext(), member);
6143+
llvm::Constant *fnPtr = IGM.getAddrOfDispatchThunk(member, NotForDefinition);
6144+
6145+
if (fnType->isAsync()) {
6146+
auto *fnPtrType = fnPtr->getType();
6147+
fnPtr = IGM.getAddrOfAsyncFunctionPointer(
6148+
LinkEntity::forDispatchThunk(member));
6149+
fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, fnPtrType);
6150+
}
6151+
61446152
auto sig = IGM.getSignature(fnType);
61456153
auto fn = FunctionPointer::forDirect(fnType, fnPtr, sig);
61466154

@@ -6340,7 +6348,15 @@ void IRGenSILFunction::visitClassMethodInst(swift::ClassMethodInst *i) {
63406348
auto *classDecl = cast<ClassDecl>(method.getDecl()->getDeclContext());
63416349
if (IGM.hasResilientMetadata(classDecl,
63426350
ResilienceExpansion::Maximal)) {
6343-
auto *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition);
6351+
llvm::Constant *fnPtr = IGM.getAddrOfDispatchThunk(method, NotForDefinition);
6352+
6353+
if (methodType->isAsync()) {
6354+
auto *fnPtrType = fnPtr->getType();
6355+
fnPtr = IGM.getAddrOfAsyncFunctionPointer(
6356+
LinkEntity::forDispatchThunk(method));
6357+
fnPtr = llvm::ConstantExpr::getBitCast(fnPtr, fnPtrType);
6358+
}
6359+
63446360
auto sig = IGM.getSignature(methodType);
63456361
FunctionPointer fn(methodType, fnPtr, sig);
63466362

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
open class BaseClass<T> {
2+
let value: T
3+
4+
public init(value: T) {
5+
self.value = value
6+
}
7+
8+
open func waitForNothing() async {}
9+
open func wait() async -> T {
10+
return value
11+
}
12+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public protocol Awaitable {
2+
associatedtype Result
3+
func waitForNothing() async
4+
func wait() async -> Result
5+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_class)) -Xfrontend -enable-experimental-concurrency -enable-library-evolution %S/Inputs/resilient_class.swift -emit-module -emit-module-path %t/resilient_class.swiftmodule -module-name resilient_class
4+
// RUN: %target-codesign %t/%target-library-name(resilient_class)
5+
6+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -lresilient_class -I %t -L %t -o %t/main %target-rpath(%t)
7+
// RUN: %target-codesign %t/main
8+
9+
// RUN: %target-run %t/main %t/%target-library-name(resilient_class)
10+
11+
// REQUIRES: executable_test
12+
// REQUIRES: concurrency
13+
14+
// REQUIRES: OS=macosx
15+
16+
import StdlibUnittest
17+
import resilient_class
18+
19+
class MyDerived : BaseClass<Int> {
20+
override func waitForNothing() async {
21+
await super.waitForNothing()
22+
}
23+
24+
override func wait() async -> Int {
25+
return await super.wait() * 2
26+
}
27+
}
28+
29+
func virtualWaitForNothing<T>(_ c: BaseClass<T>) async {
30+
await c.waitForNothing()
31+
}
32+
33+
func virtualWait<T>(_ t: BaseClass<T>) async -> T {
34+
return await t.wait()
35+
}
36+
37+
var AsyncVTableMethodSuite = TestSuite("ResilientClass")
38+
39+
AsyncVTableMethodSuite.test("AsyncVTableMethod") {
40+
runAsyncAndBlock {
41+
let x = MyDerived(value: 321)
42+
43+
await virtualWaitForNothing(x)
44+
45+
expectEqual(642, await virtualWait(x))
46+
}
47+
}
48+
49+
runAllTests()
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_protocol)) -Xfrontend -enable-experimental-concurrency -enable-library-evolution %S/Inputs/resilient_protocol.swift -emit-module -emit-module-path %t/resilient_protocol.swiftmodule -module-name resilient_protocol
4+
// RUN: %target-codesign %t/%target-library-name(resilient_protocol)
5+
6+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -lresilient_protocol -I %t -L %t -o %t/main %target-rpath(%t)
7+
// RUN: %target-codesign %t/main
8+
9+
// RUN: %target-run %t/main %t/%target-library-name(resilient_protocol)
10+
11+
// REQUIRES: executable_test
12+
// REQUIRES: concurrency
13+
14+
// REQUIRES: OS=macosx
15+
16+
import StdlibUnittest
17+
import resilient_protocol
18+
19+
struct IntAwaitable : Awaitable {
20+
func waitForNothing() async {}
21+
22+
func wait() async -> Int {
23+
return 123
24+
}
25+
}
26+
27+
func genericWaitForNothing<T : Awaitable>(_ t: T) async {
28+
await t.waitForNothing()
29+
}
30+
31+
func genericWait<T : Awaitable>(_ t: T) async -> T.Result {
32+
return await t.wait()
33+
}
34+
35+
var AsyncProtocolRequirementSuite = TestSuite("ResilientProtocol")
36+
37+
AsyncProtocolRequirementSuite.test("AsyncProtocolRequirement") {
38+
runAsyncAndBlock {
39+
let x = IntAwaitable()
40+
41+
await genericWaitForNothing(x)
42+
43+
expectEqual(123, await genericWait(x))
44+
}
45+
}
46+
47+
runAllTests()

test/IRGen/async/class_resilience.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,23 @@ open class MyBaseClass<T> {
2727

2828
// CHECK-LABEL: @"$s16class_resilience11MyBaseClassC4waitxyYFTjTu" = {{(dllexport )?}}{{(protected )?}}global %swift.async_func_pointer
2929

30+
// CHECK-LABEL: @"$s16class_resilience11MyBaseClassCMn" = {{(dllexport )?}}{{(protected )?}}constant
31+
// CHECK-SAME: %swift.async_func_pointer* @"$s16class_resilience11MyBaseClassC4waitxyYFTu"
32+
33+
// CHECK-LABEL: @"$s16class_resilience9MyDerivedCMn" = hidden constant
34+
// CHECK-SAME: %swift.async_func_pointer* @"$s16class_resilience9MyDerivedC4waitSiyYF010resilient_A09BaseClassCADxyYFTVTu"
35+
36+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s16class_resilience14callsAwaitableyx010resilient_A09BaseClassCyxGYlF"(%swift.task* %0, %swift.executor* %1, %swift.context* swiftasync %2)
37+
// CHECK: %swift.async_func_pointer* @"$s15resilient_class9BaseClassC4waitxyYFTjTu"
38+
// CHECK: ret void
39+
public func callsAwaitable<T>(_ c: BaseClass<T>) async -> T {
40+
return await c.wait()
41+
}
42+
3043
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s16class_resilience11MyBaseClassC4waitxyYFTj"(%swift.task* %0, %swift.executor* %1, %swift.context* swiftasync %2) #0 {
44+
45+
class MyDerived : BaseClass<Int> {
46+
override func wait() async -> Int {
47+
return await super.wait()
48+
}
49+
}

test/IRGen/async/protocol_resilience.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,22 @@ public protocol MyAwaitable {
1818

1919
// CHECK-LABEL: @"$s19protocol_resilience11MyAwaitableP4wait6ResultQzyYFTjTu" = {{(dllexport )?}}{{(protected )?}}global %swift.async_func_pointer
2020

21+
// CHECK-LABEL: @"$s19protocol_resilience19ConformsToAwaitableVyxG010resilient_A00E0AAMc" = hidden constant
22+
// CHECK-SAME: %swift.async_func_pointer* @"$s19protocol_resilience19ConformsToAwaitableVyxG010resilient_A00E0AaeFP4wait6ResultQzyYFTWTu"
23+
24+
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s19protocol_resilience14callsAwaitabley6ResultQzxY010resilient_A00D0RzlF"(%swift.task* %0, %swift.executor* %1, %swift.context* swiftasync %2)
25+
// CHECK: %swift.async_func_pointer* @"$s18resilient_protocol9AwaitableP4wait6ResultQzyYFTjTu"
26+
// CHECK: ret void
27+
public func callsAwaitable<T : Awaitable>(_ t: T) async -> T.Result {
28+
return await t.wait()
29+
}
30+
2131
// CHECK-LABEL: define {{(dllexport )?}}{{(protected )?}}swiftcc void @"$s19protocol_resilience11MyAwaitableP4wait6ResultQzyYFTj"(%swift.task* %0, %swift.executor* %1, %swift.context* swiftasync %2)
32+
33+
struct ConformsToAwaitable<T> : Awaitable {
34+
var value: T
35+
36+
func wait() async -> T {
37+
return value
38+
}
39+
}

test/SILGen/async_vtable_thunk.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// RUN: %target-swift-frontend -emit-silgen %s -enable-experimental-concurrency | %FileCheck %s
2+
// REQUIRES: concurrency
3+
4+
class BaseClass<T> {
5+
func wait() async -> T {}
6+
}
7+
8+
class Derived : BaseClass<Int> {
9+
override func wait() async -> Int {}
10+
}
11+
12+
// CHECK-LABEL: sil private [thunk] [ossa] @$s18async_vtable_thunk7DerivedC4waitSiyYFAA9BaseClassCADxyYFTV : $@convention(method) @async (@guaranteed Derived) -> @out Int {
13+

0 commit comments

Comments
 (0)