Skip to content

Commit 368dc0f

Browse files
committed
SILGen: Generate bodies for completion handler block impls
1 parent 4b33f26 commit 368dc0f

File tree

6 files changed

+260
-11
lines changed

6 files changed

+260
-11
lines changed

lib/SILGen/ResultPlan.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,8 @@ class ForeignAsyncInitializationPlan final : public ResultPlan {
507507
.getInterfaceType());
508508
SILFunction *impl = SGF.SGM
509509
.getOrCreateForeignAsyncCompletionHandlerImplFunction(implTy,
510-
continuationTy);
510+
continuationTy,
511+
*calleeTypeInfo.foreign.async);
511512
auto implRef = SGF.B.createFunctionRef(loc, impl);
512513

513514
// Initialize the block object for the completion handler.

lib/SILGen/SILGen.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,53 @@ SILGenModule::getConformanceToBridgedStoredNSError(SILLocation loc, Type type) {
327327
return SwiftModule->lookupConformance(type, proto);
328328
}
329329

330+
static FuncDecl *
331+
lookUpResumeContinuationIntrinsic(ASTContext &C,
332+
Optional<FuncDecl*> &cache,
333+
StringRef name) {
334+
if (cache)
335+
return *cache;
336+
337+
auto *module = C.getLoadedModule(C.Id_Concurrency);
338+
if (!module) {
339+
cache = nullptr;
340+
return nullptr;
341+
}
342+
343+
SmallVector<ValueDecl *, 1> decls;
344+
module->lookupQualified(module,
345+
DeclNameRef(C.getIdentifier(name)),
346+
NL_QualifiedDefault | NL_IncludeUsableFromInline,
347+
decls);
348+
349+
if (decls.size() != 1) {
350+
cache = nullptr;
351+
return nullptr;
352+
}
353+
auto func = dyn_cast<FuncDecl>(decls[0]);
354+
cache = func;
355+
return func;
356+
}
357+
358+
FuncDecl *
359+
SILGenModule::getResumeUnsafeContinuation() {
360+
return lookUpResumeContinuationIntrinsic(getASTContext(),
361+
ResumeUnsafeContinuation,
362+
"_resumeUnsafeContinuation");
363+
}
364+
FuncDecl *
365+
SILGenModule::getResumeUnsafeThrowingContinuation() {
366+
return lookUpResumeContinuationIntrinsic(getASTContext(),
367+
ResumeUnsafeThrowingContinuation,
368+
"_resumeUnsafeThrowingContinuation");
369+
}
370+
FuncDecl *
371+
SILGenModule::getResumeUnsafeThrowingContinuationWithError() {
372+
return lookUpResumeContinuationIntrinsic(getASTContext(),
373+
ResumeUnsafeThrowingContinuationWithError,
374+
"_resumeUnsafeThrowingContinuationWithError");
375+
}
376+
330377
ProtocolConformance *SILGenModule::getNSErrorConformanceToError() {
331378
if (NSErrorConformanceToError)
332379
return *NSErrorConformanceToError;

lib/SILGen/SILGen.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
namespace swift {
2929
class SILBasicBlock;
30+
class ForeignAsyncConvention;
3031

3132
namespace Lowering {
3233
class TypeConverter;
@@ -118,6 +119,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
118119

119120
Optional<ProtocolConformance *> NSErrorConformanceToError;
120121

122+
Optional<FuncDecl*> ResumeUnsafeContinuation;
123+
Optional<FuncDecl*> ResumeUnsafeThrowingContinuation;
124+
Optional<FuncDecl*> ResumeUnsafeThrowingContinuationWithError;
125+
121126
public:
122127
SILGenModule(SILModule &M, ModuleDecl *SM);
123128

@@ -169,7 +174,8 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
169174
/// as `async` in Swift.
170175
SILFunction *getOrCreateForeignAsyncCompletionHandlerImplFunction(
171176
CanSILFunctionType blockType,
172-
CanType continuationTy);
177+
CanType continuationTy,
178+
ForeignAsyncConvention convention);
173179

174180
/// Determine whether the given class has any instance variables that
175181
/// need to be destroyed.
@@ -467,6 +473,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
467473
/// Retrieve the conformance of NSError to the Error protocol.
468474
ProtocolConformance *getNSErrorConformanceToError();
469475

476+
/// Retrieve the _Concurrency._resumeUnsafeContinuation intrinsic.
477+
FuncDecl *getResumeUnsafeContinuation();
478+
/// Retrieve the _Concurrency._resumeUnsafeThrowingContinuation intrinsic.
479+
FuncDecl *getResumeUnsafeThrowingContinuation();
480+
/// Retrieve the _Concurrency._resumeUnsafeThrowingContinuationWithError intrinsic.
481+
FuncDecl *getResumeUnsafeThrowingContinuationWithError();
482+
470483
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
471484
KeyPathTypeKind typeKind);
472485

lib/SILGen/SILGenThunk.cpp

Lines changed: 121 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "swift/AST/ASTMangler.h"
2828
#include "swift/AST/DiagnosticsSIL.h"
2929
#include "swift/AST/FileUnit.h"
30+
#include "swift/AST/ForeignAsyncConvention.h"
3031
#include "swift/AST/GenericEnvironment.h"
3132
#include "swift/SIL/PrettyStackTrace.h"
3233
#include "swift/SIL/SILArgument.h"
@@ -141,7 +142,8 @@ SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant,
141142
SILFunction *
142143
SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction(
143144
CanSILFunctionType blockType,
144-
CanType continuationTy) {
145+
CanType continuationTy,
146+
ForeignAsyncConvention convention) {
145147
// Extract the result type from the continuation type.
146148
auto resumeType = cast<BoundGenericType>(continuationTy).getGenericArgs()[0];
147149

@@ -184,10 +186,125 @@ SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction(
184186
if (F->empty()) {
185187
// TODO: Emit the implementation.
186188
SILGenFunction SGF(*this, *F, SwiftModule);
187-
SmallVector<ManagedValue, 4> params;
188-
SGF.collectThunkParams(loc, params);
189+
{
190+
Scope scope(SGF, loc);
191+
SmallVector<ManagedValue, 4> params;
192+
SGF.collectThunkParams(loc, params);
189193

190-
SGF.B.createUnreachable(loc);
194+
// Get the continuation out of the block object.
195+
auto blockStorage = params[0].getValue();
196+
auto continuationAddr = SGF.B.createProjectBlockStorage(loc, blockStorage);
197+
auto continuationVal = SGF.B.createLoad(loc, continuationAddr,
198+
LoadOwnershipQualifier::Trivial);
199+
auto continuation = ManagedValue::forUnmanaged(continuationVal);
200+
201+
// Check for an error if the convention includes one.
202+
auto errorIndex = convention.completionHandlerErrorParamIndex();
203+
204+
FuncDecl *resumeIntrinsic, *errorIntrinsic;
205+
206+
SILBasicBlock *returnBB = nullptr;
207+
if (errorIndex) {
208+
resumeIntrinsic = getResumeUnsafeThrowingContinuation();
209+
errorIntrinsic = getResumeUnsafeThrowingContinuationWithError();
210+
211+
auto errorArgument = params[*errorIndex + 1];
212+
auto someErrorBB = SGF.createBasicBlock(FunctionSection::Postmatter);
213+
auto noneErrorBB = SGF.createBasicBlock();
214+
returnBB = SGF.createBasicBlockAfter(noneErrorBB);
215+
216+
auto &C = SGF.getASTContext();
217+
std::pair<EnumElementDecl *, SILBasicBlock *> switchErrorBBs[] = {
218+
{C.getOptionalSomeDecl(), someErrorBB},
219+
{C.getOptionalNoneDecl(), noneErrorBB}
220+
};
221+
222+
SGF.B.createSwitchEnum(loc, errorArgument.borrow(SGF, loc).getValue(),
223+
/*default*/ nullptr,
224+
switchErrorBBs);
225+
226+
SGF.B.emitBlock(someErrorBB);
227+
228+
auto matchedErrorTy = errorArgument.getType().getOptionalObjectType();
229+
auto matchedError = SGF.B
230+
.createGuaranteedTransformingTerminatorArgument(matchedErrorTy);
231+
232+
// Resume the continuation as throwing the given error, bridged to a
233+
// native Swift error.
234+
auto nativeError = SGF.emitBridgedToNativeError(loc, matchedError);
235+
Type replacementTypes[] = {resumeType};
236+
auto subs = SubstitutionMap::get(errorIntrinsic->getGenericSignature(),
237+
replacementTypes,
238+
ArrayRef<ProtocolConformanceRef>{});
239+
SGF.emitApplyOfLibraryIntrinsic(loc, errorIntrinsic, subs,
240+
{continuation, nativeError},
241+
SGFContext());
242+
243+
SGF.B.createBranch(loc, returnBB);
244+
SGF.B.emitBlock(noneErrorBB);
245+
} else {
246+
resumeIntrinsic = getResumeUnsafeContinuation();
247+
}
248+
249+
auto loweredResumeTy = SGF.getLoweredType(AbstractionPattern::getOpaque(),
250+
resumeType);
251+
252+
// Prepare the argument for the resume intrinsic, using the non-error
253+
// arguments to the callback.
254+
{
255+
Scope resumeScope(SGF, loc);
256+
unsigned errorIndexBoundary = errorIndex ? *errorIndex : ~0u;
257+
auto resumeArgBuf = SGF.emitTemporaryAllocation(loc,
258+
loweredResumeTy.getAddressType());
259+
260+
auto prepareArgument = [&](SILValue destBuf, ManagedValue arg) {
261+
// Convert the ObjC argument to the bridged Swift representation we
262+
// want.
263+
ManagedValue bridgedArg = SGF.emitBridgedToNativeValue(loc,
264+
arg,
265+
arg.getType().getASTType(),
266+
// FIXME: pass down formal type
267+
destBuf->getType().getASTType(),
268+
destBuf->getType().getObjectType());
269+
bridgedArg.forwardInto(SGF, loc, destBuf);
270+
};
271+
272+
if (auto resumeTuple = dyn_cast<TupleType>(resumeType)) {
273+
assert(params.size() == resumeTuple->getNumElements()
274+
+ 1 + (bool)errorIndex);
275+
for (auto i : indices(resumeTuple.getElementTypes())) {
276+
auto resumeEltBuf = SGF.B.createTupleElementAddr(loc,
277+
resumeArgBuf, i);
278+
auto arg = params[1 + i + (i >= errorIndexBoundary)];
279+
prepareArgument(resumeEltBuf, arg);
280+
}
281+
} else {
282+
assert(params.size() == 2 + (bool)errorIndex);
283+
prepareArgument(resumeArgBuf, params[1 + (errorIndexBoundary == 0)]);
284+
}
285+
286+
287+
// Resume the continuation with the composed bridged result.
288+
ManagedValue resumeArg = SGF.emitManagedBufferWithCleanup(resumeArgBuf);
289+
Type replacementTypes[] = {resumeType};
290+
auto subs = SubstitutionMap::get(resumeIntrinsic->getGenericSignature(),
291+
replacementTypes,
292+
ArrayRef<ProtocolConformanceRef>{});
293+
SGF.emitApplyOfLibraryIntrinsic(loc, resumeIntrinsic, subs,
294+
{continuation, resumeArg},
295+
SGFContext());
296+
}
297+
298+
// Now we've resumed the continuation one way or another. Return from the
299+
// completion callback.
300+
if (returnBB) {
301+
SGF.B.createBranch(loc, returnBB);
302+
SGF.B.emitBlock(returnBB);
303+
}
304+
}
305+
306+
SGF.B.createReturn(loc,
307+
SILUndef::get(SGF.SGM.Types.getEmptyTupleType(), SGF.F));
191308
}
192309

193310
return F;

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,46 @@ public struct PartialAsyncTask {
2424
public struct UnsafeContinuation<T> {
2525
private var context: UnsafeRawPointer
2626

27-
public func resume(_: T) { }
27+
public func resume(_: __owned T) { }
2828
}
2929

3030
@frozen
3131
public struct UnsafeThrowingContinuation<T> {
3232
private var context: UnsafeRawPointer
3333

34-
public func resume(_: T) { }
35-
public func fail(_: Error) { }
34+
public func resume(_: __owned T) { }
35+
public func fail(_: __owned Error) { }
3636
}
3737

38+
#if _runtime(_ObjC)
3839

40+
// Intrinsics used by SILGen to resume or fail continuations
41+
// for
42+
@_alwaysEmitIntoClient
43+
@usableFromInline
44+
internal func _resumeUnsafeContinuation<T>(
45+
_ continuation: UnsafeContinuation<T>,
46+
_ value: __owned T
47+
) {
48+
continuation.resume(value)
49+
}
50+
51+
@_alwaysEmitIntoClient
52+
@usableFromInline
53+
internal func _resumeUnsafeThrowingContinuation<T>(
54+
_ continuation: UnsafeThrowingContinuation<T>,
55+
_ value: __owned T
56+
) {
57+
continuation.resume(value)
58+
}
59+
60+
@_alwaysEmitIntoClient
61+
@usableFromInline
62+
internal func _resumeUnsafeThrowingContinuationWithError<T>(
63+
_ continuation: UnsafeThrowingContinuation<T>,
64+
_ error: __owned Error
65+
) {
66+
continuation.fail(error)
67+
}
68+
69+
#endif

test/SILGen/objc_async.swift

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ func testSlowServer(slowServer: SlowServer) async throws {
1212
// CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage UnsafeContinuation<Int>
1313
// CHECK: [[CONT_SLOT:%.*]] = project_block_storage [[BLOCK_STORAGE]]
1414
// CHECK: store [[CONT]] to [trivial] [[CONT_SLOT]]
15-
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @{{.*}} : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<Int>, Int) -> ()
15+
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[INT_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<Int>, Int) -> ()
1616
// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]]
1717
// CHECK: apply [[METHOD]]({{.*}}, [[BLOCK]], %0)
1818
// CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]]
@@ -27,7 +27,7 @@ func testSlowServer(slowServer: SlowServer) async throws {
2727
// CHECK: [[BLOCK_STORAGE:%.*]] = alloc_stack $@block_storage UnsafeThrowingContinuation<String>
2828
// CHECK: [[CONT_SLOT:%.*]] = project_block_storage [[BLOCK_STORAGE]]
2929
// CHECK: store [[CONT]] to [trivial] [[CONT_SLOT]]
30-
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @{{.*}} : $@convention(c) (@inout_aliasable @block_storage UnsafeThrowingContinuation<String>, Optional<NSString>, Optional<NSError>) -> ()
30+
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[STRING_COMPLETION_THROW_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeThrowingContinuation<String>, Optional<NSString>, Optional<NSError>) -> ()
3131
// CHECK: [[BLOCK:%.*]] = init_block_storage_header [[BLOCK_STORAGE]] {{.*}}, invoke [[BLOCK_IMPL]]
3232
// CHECK: apply [[METHOD]]([[BLOCK]], %0)
3333
// CHECK: await_async_continuation [[CONT]] {{.*}}, resume [[RESUME:bb[0-9]+]], error [[ERROR:bb[0-9]+]]
@@ -38,10 +38,50 @@ func testSlowServer(slowServer: SlowServer) async throws {
3838
let _: String = try await slowServer.findAnswer()
3939

4040
// CHECK: objc_method {{.*}} $@convention(objc_method) (NSString, @convention(block) () -> (), SlowServer) -> ()
41+
// CHECK: [[BLOCK_IMPL:%.*]] = function_ref @[[VOID_COMPLETION_BLOCK:.*]] : $@convention(c) (@inout_aliasable @block_storage UnsafeContinuation<()>) -> ()
4142
await slowServer.serverRestart("somewhere")
4243

4344
// CHECK: [[ERROR]]([[ERROR_VALUE:%.*]] : @owned $Error):
4445
// CHECK: dealloc_stack [[RESUME_BUF]]
4546
// CHECK: throw [[ERROR_VALUE]]
4647

4748
}
49+
50+
// CHECK: sil{{.*}}@[[INT_COMPLETION_BLOCK]]
51+
// CHECK: [[CONT_ADDR:%.*]] = project_block_storage %0
52+
// CHECK: [[CONT:%.*]] = load [trivial] [[CONT_ADDR]]
53+
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $Int
54+
// CHECK: store %1 to [trivial] [[RESULT_BUF]]
55+
// CHECK: [[RESUME:%.*]] = function_ref @{{.*}}resumeUnsafeContinuation
56+
// CHECK: apply [[RESUME]]<Int>([[CONT]], [[RESULT_BUF]])
57+
58+
// CHECK: sil{{.*}}@[[STRING_COMPLETION_THROW_BLOCK]]
59+
// CHECK: [[RESUME_IN:%.*]] = copy_value %1
60+
// CHECK: [[ERROR_IN:%.*]] = copy_value %2
61+
// CHECK: [[CONT_ADDR:%.*]] = project_block_storage %0
62+
// CHECK: [[CONT:%.*]] = load [trivial] [[CONT_ADDR]]
63+
// CHECK: [[ERROR_IN_B:%.*]] = begin_borrow [[ERROR_IN]]
64+
// CHECK: switch_enum [[ERROR_IN_B]] : {{.*}}, case #Optional.some!enumelt: [[ERROR_BB:bb[0-9]+]], case #Optional.none!enumelt: [[RESUME_BB:bb[0-9]+]]
65+
// CHECK: [[RESUME_BB]]:
66+
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $String
67+
// CHECK: [[BRIDGE:%.*]] = function_ref @{{.*}}unconditionallyBridgeFromObjectiveC
68+
// CHECK: [[BRIDGED_RESULT:%.*]] = apply [[BRIDGE]]([[RESUME_IN]]
69+
// CHECK: store [[BRIDGED_RESULT]] to [init] [[RESULT_BUF]]
70+
// CHECK: [[RESUME:%.*]] = function_ref @{{.*}}resumeUnsafeThrowingContinuation
71+
// CHECK: apply [[RESUME]]<String>([[CONT]], [[RESULT_BUF]])
72+
// CHECK: br [[END_BB:bb[0-9]+]]
73+
// CHECK: [[END_BB]]:
74+
// CHECK: return
75+
// CHECK: [[ERROR_BB]]([[ERROR_IN_UNWRAPPED:%.*]] : @guaranteed $NSError):
76+
// CHECK: [[ERROR:%.*]] = init_existential_ref [[ERROR_IN_UNWRAPPED]]
77+
// CHECK: [[RESUME_WITH_ERROR:%.*]] = function_ref @{{.*}}resumeUnsafeThrowingContinuationWithError
78+
// CHECK: [[ERROR_COPY:%.*]] = copy_value [[ERROR]]
79+
// CHECK: apply [[RESUME_WITH_ERROR]]<String>([[CONT]], [[ERROR_COPY]])
80+
// CHECK: br [[END_BB]]
81+
82+
// CHECK: sil {{.*}} @[[VOID_COMPLETION_BLOCK]]
83+
// CHECK: [[CONT_ADDR:%.*]] = project_block_storage %0
84+
// CHECK: [[CONT:%.*]] = load [trivial] [[CONT_ADDR]]
85+
// CHECK: [[RESULT_BUF:%.*]] = alloc_stack $()
86+
// CHECK: [[RESUME:%.*]] = function_ref @{{.*}}resumeUnsafeContinuation
87+
// CHECK: apply [[RESUME]]<()>([[CONT]], [[RESULT_BUF]])

0 commit comments

Comments
 (0)