Skip to content

Commit e7ec8c3

Browse files
committed
SILGen: Caller-side codegen for invoking foreign async functions
Immediately before invoking the ObjC API, get the current continuation, capture it into a block to pass as the completion handler, and then await the continuation, whose resume/error successors serve as the semantic return/throw result of the call. This should complete the caller-side part of SILGen; the completion handler block implementation is however still only a stub.
1 parent 6c46841 commit e7ec8c3

File tree

11 files changed

+238
-31
lines changed

11 files changed

+238
-31
lines changed

docs/ABI/Mangling.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ types where the metadata itself has unknown layout.)
219219
global ::= entity // some identifiable thing
220220
global ::= from-type to-type generic-signature? 'TR' // reabstraction thunk
221221
global ::= from-type to-type generic-signature? 'TR' // reabstraction thunk
222-
global ::= impl-function-type 'Tz' // objc-to-swift-async completion handler block implementation
222+
global ::= impl-function-type type 'Tz' // objc-to-swift-async completion handler block implementation
223223
global ::= from-type to-type self-type generic-signature? 'Ty' // reabstraction thunk with dynamic 'Self' capture
224224
global ::= from-type to-type generic-signature? 'Tr' // obsolete mangling for reabstraction thunk
225225
global ::= entity generic-signature? type type* 'TK' // key path getter

include/swift/AST/ASTMangler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ class ASTMangler : public Mangler {
155155
Type SelfType,
156156
ModuleDecl *Module);
157157

158+
std::string mangleObjCAsyncCompletionHandlerImpl(CanSILFunctionType BlockType,
159+
CanType ResultType);
160+
158161
/// Mangle the derivative function (JVP/VJP) for the given:
159162
/// - Mangled original function name.
160163
/// - Derivative function kind.

lib/AST/ASTMangler.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,16 @@ std::string ASTMangler::mangleReabstractionThunkHelper(
394394
return finalize();
395395
}
396396

397+
std::string ASTMangler::mangleObjCAsyncCompletionHandlerImpl(
398+
CanSILFunctionType BlockType,
399+
CanType ResultType) {
400+
beginMangling();
401+
appendType(BlockType);
402+
appendType(ResultType);
403+
appendOperator("Tz");
404+
return finalize();
405+
}
406+
397407
std::string ASTMangler::mangleAutoDiffDerivativeFunctionHelper(
398408
StringRef name, AutoDiffDerivativeFunctionKind kind,
399409
AutoDiffConfig config) {

lib/Demangling/Demangler.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,8 +2264,10 @@ NodePointer Demangler::popProtocolConformance() {
22642264
return createWithChild(Node::Kind::CoroutineContinuationPrototype, type);
22652265
}
22662266
case 'z': {
2267-
NodePointer implType = popNode(Node::Kind::ImplFunctionType);
2268-
return createWithChild(Node::Kind::ObjCAsyncCompletionHandlerImpl, implType);
2267+
NodePointer resultType = popNode(Node::Kind::Type);
2268+
NodePointer implType = popNode(Node::Kind::Type);
2269+
return createWithChildren(Node::Kind::ObjCAsyncCompletionHandlerImpl,
2270+
implType, resultType);
22692271
}
22702272
case 'V': {
22712273
NodePointer Base = popNode(isEntity);

lib/Demangling/NodePrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2536,6 +2536,8 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
25362536
case Node::Kind::ObjCAsyncCompletionHandlerImpl:
25372537
Printer << "@objc completion handler block implementation for ";
25382538
print(Node->getChild(0));
2539+
Printer << " with result type ";
2540+
print(Node->getChild(1));
25392541
return nullptr;
25402542
}
25412543
printer_unreachable("bad node kind!");

lib/SILGen/ResultPlan.cpp

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,25 +453,115 @@ class TupleInitializationResultPlan final : public ResultPlan {
453453

454454
class ForeignAsyncInitializationPlan final : public ResultPlan {
455455
SILLocation loc;
456+
CalleeTypeInfo calleeTypeInfo;
457+
SILType opaqueResumeType;
458+
SILValue resumeBuf;
459+
SILValue continuation;
460+
456461
public:
457-
ForeignAsyncInitializationPlan(SILLocation loc) : loc(loc) {}
462+
ForeignAsyncInitializationPlan(SILGenFunction &SGF, SILLocation loc,
463+
const CalleeTypeInfo &calleeTypeInfo)
464+
: loc(loc), calleeTypeInfo(calleeTypeInfo)
465+
{
466+
// Allocate space to receive the resume value when the continuation is
467+
// resumed.
468+
opaqueResumeType = SGF.getLoweredType(AbstractionPattern::getOpaque(),
469+
calleeTypeInfo.substResultType);
470+
resumeBuf = SGF.emitTemporaryAllocation(loc, opaqueResumeType);
471+
}
458472

459473
void
460474
gatherIndirectResultAddrs(SILGenFunction &SGF, SILLocation loc,
461475
SmallVectorImpl<SILValue> &outList) const override {
462-
// TODO: Move values from the continuation result buffer to the individual
463-
// out argument buffers, unless we were able to emit the resume buffer
464-
// in-place.
476+
// A foreign async function shouldn't have any indirect results.
477+
}
478+
479+
ManagedValue
480+
emitForeignAsyncCompletionHandler(SILGenFunction &SGF, SILLocation loc)
481+
override {
482+
// Get the current continuation for the task.
483+
auto continuationDecl = calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex()
484+
? SGF.getASTContext().getUnsafeThrowingContinuationDecl()
485+
: SGF.getASTContext().getUnsafeContinuationDecl();
486+
487+
auto continuationTy = BoundGenericType::get(continuationDecl, Type(),
488+
calleeTypeInfo.substResultType)
489+
->getCanonicalType();
490+
491+
492+
continuation = SGF.B.createGetAsyncContinuationAddr(loc, resumeBuf,
493+
SILType::getPrimitiveObjectType(continuationTy));
494+
495+
// Stash it in a buffer for a block object.
496+
auto blockStorageTy = SILType::getPrimitiveAddressType(SILBlockStorageType::get(continuationTy));
497+
auto blockStorage = SGF.emitTemporaryAllocation(loc, blockStorageTy);
498+
auto continuationAddr = SGF.B.createProjectBlockStorage(loc, blockStorage);
499+
SGF.B.createStore(loc, continuation, continuationAddr,
500+
StoreOwnershipQualifier::Trivial);
501+
502+
// Get the block invocation function for the given completion block type.
503+
auto completionHandlerIndex = calleeTypeInfo.foreign.async
504+
->completionHandlerParamIndex();
505+
auto implTy = cast<SILFunctionType>(calleeTypeInfo.substFnType
506+
->getParameters()[completionHandlerIndex]
507+
.getInterfaceType());
508+
SILFunction *impl = SGF.SGM
509+
.getOrCreateForeignAsyncCompletionHandlerImplFunction(implTy,
510+
continuationTy);
511+
auto implRef = SGF.B.createFunctionRef(loc, impl);
512+
513+
// Initialize the block object for the completion handler.
514+
auto block = SGF.B.createInitBlockStorageHeader(loc, blockStorage, implRef,
515+
SILType::getPrimitiveObjectType(implTy), {});
516+
// We don't need to manage the block because it's still on the stack. We
517+
// know we won't escape it locally so the callee can be responsible for
518+
// _Block_copy-ing it.
519+
return ManagedValue::forUnmanaged(block);
465520
}
466521

467522
RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
468523
ArrayRef<ManagedValue> &directResults) override {
469524
// There should be no direct results from the call.
470525
assert(directResults.empty());
471526

472-
// TODO: Get the actual result values from the awaited continuation.
473-
// For now, produce an undef RValue.
474-
return SGF.emitUndefRValue(loc, substType);
527+
// Await the continuation we handed off to the completion handler.
528+
SILBasicBlock *resumeBlock = SGF.createBasicBlock();
529+
SILBasicBlock *errorBlock = nullptr;
530+
auto errorParamIndex = calleeTypeInfo.foreign.async->completionHandlerErrorParamIndex();
531+
if (errorParamIndex) {
532+
errorBlock = SGF.createBasicBlock(FunctionSection::Postmatter);
533+
}
534+
535+
SGF.B.createAwaitAsyncContinuation(loc, continuation, resumeBlock, errorBlock);
536+
537+
// Propagate an error if we have one.
538+
if (errorBlock) {
539+
SGF.B.emitBlock(errorBlock);
540+
541+
Scope errorScope(SGF, loc);
542+
543+
auto errorTy = SGF.getASTContext().getErrorDecl()->getDeclaredType()
544+
->getCanonicalType();
545+
auto errorVal
546+
= SGF.B.createOwnedPhiArgument(SILType::getPrimitiveObjectType(errorTy));
547+
548+
SGF.emitThrow(loc, errorVal, true);
549+
}
550+
551+
SGF.B.emitBlock(resumeBlock);
552+
553+
// The incoming value is the maximally-abstracted result type of the
554+
// continuation. Move it out of the resume buffer and reabstract it if
555+
// necessary.
556+
auto resumeResult = SGF.emitLoad(loc, resumeBuf,
557+
calleeTypeInfo.origResultType
558+
? *calleeTypeInfo.origResultType
559+
: AbstractionPattern(calleeTypeInfo.substResultType),
560+
calleeTypeInfo.substResultType,
561+
SGF.getTypeLowering(calleeTypeInfo.substResultType),
562+
SGFContext(), IsTake);
563+
564+
return RValue(SGF, loc, calleeTypeInfo.substResultType, resumeResult);
475565
}
476566
};
477567

@@ -572,8 +662,7 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init,
572662
// Create a result plan that gets the result schema from the completion
573663
// handler callback's arguments.
574664
// completion handler.
575-
return ResultPlanPtr(new ForeignAsyncInitializationPlan(loc));
576-
665+
return ResultPlanPtr(new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo));
577666
} else if (auto foreignError = calleeTypeInfo.foreign.error) {
578667
// Handle the foreign error first.
579668
//

lib/SILGen/ResultPlan.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ class ResultPlan {
4949
emitForeignErrorArgument(SILGenFunction &SGF, SILLocation loc) {
5050
return None;
5151
}
52+
53+
virtual ManagedValue
54+
emitForeignAsyncCompletionHandler(SILGenFunction &SGF, SILLocation loc) {
55+
return {};
56+
}
5257
};
5358

5459
using ResultPlanPtr = std::unique_ptr<ResultPlan>;

lib/SILGen/SILGen.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
163163
CanSILFunctionType fromType,
164164
CanSILFunctionType toType,
165165
CanType dynamicSelfType);
166+
167+
/// Get or create the declaration of a completion handler block
168+
/// implementation function for an ObjC API that was imported
169+
/// as `async` in Swift.
170+
SILFunction *getOrCreateForeignAsyncCompletionHandlerImplFunction(
171+
CanSILFunctionType blockType,
172+
CanType continuationTy);
166173

167174
/// Determine whether the given class has any instance variables that
168175
/// need to be destroyed.

lib/SILGen/SILGenApply.cpp

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3276,14 +3276,15 @@ class ArgEmitter {
32763276
if (Foreign.async
32773277
&& Foreign.async->completionHandlerParamIndex() == Args.size()) {
32783278
SILParameterInfo param = claimNextParameters(1).front();
3279-
3280-
// TODO: Get or create the completion handler block implementation
3281-
// function for the given argument type, then create a block containing
3282-
// the current continuation. (This probably needs to be deferred to right
3283-
// before the actual call, since evaluating other arguments to the call
3284-
// may suspend the task)
3285-
auto argTy = SILType::getPrimitiveObjectType(param.getInterfaceType());
3286-
Args.push_back(ManagedValue::forUnmanaged(SILUndef::get(argTy, SGF.F)));
3279+
(void)param;
3280+
3281+
// Leave a placeholder in the position. We'll fill this in with a block
3282+
// capturing the current continuation right before we invoke the
3283+
// function.
3284+
// (We can't do this immediately, because evaluating other arguments
3285+
// may require suspending the async task, which is not allowed while its
3286+
// continuation is active.)
3287+
Args.push_back(ManagedValue::forInContext());
32873288
} else if (Foreign.error
32883289
&& Foreign.error->getErrorParameterIndex() == Args.size()) {
32893290
SILParameterInfo param = claimNextParameters(1).front();
@@ -4327,14 +4328,21 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
43274328

43284329
// If there's a foreign error or async parameter, fill it in.
43294330
ManagedValue errorTemp;
4330-
if (calleeTypeInfo.foreign.async) {
4331-
// TODO: prepare the callback continuation and block here.
4332-
4333-
} else if (calleeTypeInfo.foreign.error) {
4331+
if (auto foreignAsync = calleeTypeInfo.foreign.async) {
4332+
unsigned completionIndex = foreignAsync->completionHandlerParamIndex();
4333+
4334+
// Ram the emitted error into the argument list, over the placeholder
4335+
// we left during the first pass.
4336+
auto &completionArgSlot = const_cast<ManagedValue &>(args[completionIndex]);
4337+
4338+
completionArgSlot = resultPlan->emitForeignAsyncCompletionHandler(*this, loc);
4339+
4340+
} else if (auto foreignError = calleeTypeInfo.foreign.error) {
43344341
unsigned errorParamIndex =
4335-
calleeTypeInfo.foreign.error->getErrorParameterIndex();
4342+
foreignError->getErrorParameterIndex();
43364343

4337-
// This is pretty evil.
4344+
// Ram the emitted error into the argument list, over the placeholder
4345+
// we left during the first pass.
43384346
auto &errorArgSlot = const_cast<ManagedValue &>(args[errorParamIndex]);
43394347

43404348
std::tie(errorTemp, errorArgSlot) =
@@ -4442,9 +4450,6 @@ RValue SILGenFunction::emitApply(ResultPlanPtr &&resultPlan,
44424450
*foreignError);
44434451
}
44444452

4445-
// TODO(async): If there's a foreign async convention, await the continuation
4446-
// to get the result from the completion callback.
4447-
44484453
auto directResultsArray = makeArrayRef(directResults);
44494454
RValue result =
44504455
resultPlan->finish(*this, loc, substResultType, directResultsArray);

lib/SILGen/SILGenThunk.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,60 @@ SILGenFunction::emitGlobalFunctionRef(SILLocation loc, SILDeclRef constant,
138138
return B.createFunctionRefFor(loc, f);
139139
}
140140

141+
SILFunction *
142+
SILGenModule::getOrCreateForeignAsyncCompletionHandlerImplFunction(
143+
CanSILFunctionType blockType,
144+
CanType continuationTy) {
145+
// Extract the result type from the continuation type.
146+
auto resumeType = cast<BoundGenericType>(continuationTy).getGenericArgs()[0];
147+
148+
// Build up the implementation function type, which matches the
149+
// block signature with an added block storage argument that points at the
150+
// block buffer. The block storage holds the continuation we feed the
151+
// result values into.
152+
SmallVector<SILParameterInfo, 4> implArgs;
153+
auto blockStorageTy = SILBlockStorageType::get(continuationTy);
154+
implArgs.push_back(SILParameterInfo(blockStorageTy,
155+
ParameterConvention::Indirect_InoutAliasable));
156+
157+
std::copy(blockType->getParameters().begin(),
158+
blockType->getParameters().end(),
159+
std::back_inserter(implArgs));
160+
161+
auto implTy = SILFunctionType::get(GenericSignature(),
162+
blockType->getExtInfo()
163+
.withRepresentation(SILFunctionTypeRepresentation::CFunctionPointer),
164+
SILCoroutineKind::None,
165+
ParameterConvention::Direct_Unowned,
166+
implArgs, {}, blockType->getResults(),
167+
None,
168+
SubstitutionMap(), SubstitutionMap(), getASTContext());
169+
170+
auto loc = RegularLocation::getAutoGeneratedLocation();
171+
172+
Mangle::ASTMangler Mangler;
173+
auto name = Mangler.mangleObjCAsyncCompletionHandlerImpl(blockType,
174+
resumeType);
175+
176+
SILGenFunctionBuilder builder(*this);
177+
auto F = builder.getOrCreateSharedFunction(loc, name, implTy,
178+
IsBare, IsTransparent, IsSerializable,
179+
ProfileCounter(),
180+
IsThunk,
181+
IsNotDynamic);
182+
183+
if (F->empty()) {
184+
// TODO: Emit the implementation.
185+
SILGenFunction SGF(*this, *F, SwiftModule);
186+
SmallVector<ManagedValue, 4> params;
187+
SGF.collectThunkParams(loc, params);
188+
189+
SGF.B.createUnreachable(loc);
190+
}
191+
192+
return F;
193+
}
194+
141195
SILFunction *SILGenModule::
142196
getOrCreateReabstractionThunk(CanSILFunctionType thunkType,
143197
CanSILFunctionType fromType,

0 commit comments

Comments
 (0)