Skip to content

Commit e06d316

Browse files
committed
[Async CC] Find wtable in async context in thunk.
Previously, when looking up a protocol method, the witness table was always exepcted to be the final argument passed to the function. That is true for sync protocol witnesses but not for async witnesses. In the async case, the witness table is embedded in the async context. Here, the witness table is dug out of the async context. rdar://problem/71491604
1 parent 9e4bdb6 commit e06d316

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

lib/IRGen/GenThunk.cpp

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,34 +64,49 @@ IRGenModule::getAddrOfDispatchThunk(SILDeclRef declRef,
6464
static FunctionPointer lookupMethod(IRGenFunction &IGF, SILDeclRef declRef) {
6565
auto expansionContext = IGF.IGM.getMaximalTypeExpansionContext();
6666
auto *decl = cast<AbstractFunctionDecl>(declRef.getDecl());
67+
auto funcTy = IGF.IGM.getSILModule().Types.getConstantFunctionType(
68+
expansionContext, declRef);
69+
70+
auto getAsyncContextLayout = [&]() {
71+
auto originalType = funcTy;
72+
auto forwardingSubstitutionMap =
73+
decl->getGenericEnvironment()
74+
? decl->getGenericEnvironment()->getForwardingSubstitutionMap()
75+
: SubstitutionMap();
76+
auto substitutedType = originalType->substGenericArgs(
77+
IGF.IGM.getSILModule(), forwardingSubstitutionMap,
78+
IGF.IGM.getMaximalTypeExpansionContext());
79+
auto layout = irgen::getAsyncContextLayout(
80+
IGF.IGM, originalType, substitutedType, forwardingSubstitutionMap);
81+
return layout;
82+
};
6783

6884
// Protocol case.
6985
if (isa<ProtocolDecl>(decl->getDeclContext())) {
7086
// Find the witness table.
71-
llvm::Value *wtable = (IGF.CurFn->arg_end() - 1);
87+
llvm::Value *wtable;
88+
if (funcTy->isAsync()) {
89+
auto layout = getAsyncContextLayout();
90+
assert(layout.hasTrailingWitnesses());
91+
auto context = layout.emitCastTo(IGF, IGF.getAsyncContext());
92+
auto wtableAddr =
93+
layout.getSelfWitnessTableLayout().project(IGF, context, llvm::None);
94+
wtable = IGF.Builder.CreateLoad(wtableAddr);
95+
} else {
96+
wtable = (IGF.CurFn->arg_end() - 1);
97+
}
7298

7399
// Find the witness we're interested in.
74100
return emitWitnessMethodValue(IGF, wtable, declRef);
75101
}
76102

77103
// Class case.
78-
auto funcTy = IGF.IGM.getSILModule().Types.getConstantFunctionType(
79-
expansionContext, declRef);
80104

81105
// Load the metadata, or use the 'self' value if we have a static method.
82106
llvm::Value *self;
83107

84108
if (funcTy->isAsync()) {
85-
auto originalType = funcTy;
86-
auto forwardingSubstitutionMap =
87-
decl->getGenericEnvironment()
88-
? decl->getGenericEnvironment()->getForwardingSubstitutionMap()
89-
: SubstitutionMap();
90-
auto substitutedType = originalType->substGenericArgs(
91-
IGF.IGM.getSILModule(), forwardingSubstitutionMap,
92-
IGF.IGM.getMaximalTypeExpansionContext());
93-
auto layout = getAsyncContextLayout(IGF.IGM, originalType, substitutedType,
94-
forwardingSubstitutionMap);
109+
auto layout = getAsyncContextLayout();
95110
assert(layout.hasLocalContext());
96111
auto context = layout.emitCastTo(IGF, IGF.getAsyncContext());
97112
auto localContextAddr =
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import _Concurrency
2+
3+
public protocol Protokol {
4+
func protocolinstanceVoidToVoid() async
5+
}
6+
7+
public struct Impl : Protokol {
8+
public init() {}
9+
public func protocolinstanceVoidToVoid() async {
10+
print(self)
11+
}
12+
}
13+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift-dylib(%t/%target-library-name(PrintShims)) %S/../../Inputs/print-shims.swift -module-name PrintShims -emit-module -emit-module-path %t/PrintShims.swiftmodule
3+
// RUN: %target-codesign %t/%target-library-name(PrintShims)
4+
// RUN: %target-build-swift-dylib(%t/%target-library-name(ResilientProtocol)) %S/Inputs/protocol-1instance-void_to_void.swift -Xfrontend -enable-experimental-concurrency -module-name ResilientProtocol -emit-module -emit-module-path %t/ResilientProtocol.swiftmodule
5+
// RUN: %target-codesign %t/%target-library-name(ResilientProtocol)
6+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -emit-ir -I %t -L %t -lPrintShim -lResilientProtocol | %FileCheck %s --check-prefix=CHECK-LL
7+
// RUN: %target-build-swift -Xfrontend -enable-experimental-concurrency %s -module-name main -o %t/main -I %t -L %t -lPrintShims -lResilientProtocol %target-rpath(%t)
8+
// RUN: %target-codesign %t/main
9+
// RUN: %target-run %t/main %t/%target-library-name(PrintShims) %t/%target-library-name(ResilientProtocol) | %FileCheck %s
10+
11+
// REQUIRES: executable_test
12+
// REQUIRES: swift_test_mode_optimize_none
13+
// REQUIRES: concurrency
14+
// UNSUPPORTED: use_os_stdlib
15+
// UNSUPPORTED: CPU=arm64e
16+
17+
import _Concurrency
18+
import ResilientProtocol
19+
20+
func call<T : Protokol>(_ t: T) async {
21+
await t.protocolinstanceVoidToVoid()
22+
}
23+
24+
// CHECK-LL: define hidden swiftcc void @"$s4main4callyyxY17ResilientProtocol8ProtokolRzlF"(%swift.task* {{%[0-9]+}}, %swift.executor* {{%[0-9]+}}, %swift.context* {{%[0-9]+}}) {{#[0-9]*}} {
25+
func test_case() async {
26+
let impl = Impl()
27+
await call(impl) // CHECK: Impl()
28+
}
29+
30+
_Concurrency.runAsync(test_case)
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// RUN: %target-swift-frontend %s -emit-ir -enable-library-evolution -enable-experimental-concurrency
2+
// REQUIRES: concurrency
3+
4+
public protocol P {
5+
func f() async
6+
}

0 commit comments

Comments
 (0)