Skip to content

Commit 0ca29b5

Browse files
authored
Merge pull request #34525 from jckarter/foreign-async-silgen
SILGen: Caller-side codegen for invoking foreign async functions
2 parents f677d19 + 368dc0f commit 0ca29b5

File tree

16 files changed

+518
-36
lines changed

16 files changed

+518
-36
lines changed

docs/ABI/Mangling.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ types where the metadata itself has unknown layout.)
220220
global ::= entity // some identifiable thing
221221
global ::= from-type to-type generic-signature? 'TR' // reabstraction thunk
222222
global ::= from-type to-type generic-signature? 'TR' // reabstraction thunk
223-
global ::= impl-function-type 'Tz' // objc-to-swift-async completion handler block implementation
223+
global ::= impl-function-type type 'Tz' // objc-to-swift-async completion handler block implementation
224+
global ::= impl-function-type type 'TZ' // objc-to-swift-async completion handler block implementation (predefined by runtime)
224225
global ::= from-type to-type self-type generic-signature? 'Ty' // reabstraction thunk with dynamic 'Self' capture
225226
global ::= from-type to-type generic-signature? 'Tr' // obsolete mangling for reabstraction thunk
226227
global ::= entity generic-signature? type type* 'TK' // key path getter

include/swift/AST/ASTMangler.h

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

158+
/// Mangle a completion handler block implementation function, used for importing ObjC
159+
/// APIs as async.
160+
///
161+
/// - If `predefined` is true, this mangles the symbol name of the completion handler
162+
/// predefined in the Swift runtime for the given type signature.
163+
std::string mangleObjCAsyncCompletionHandlerImpl(CanSILFunctionType BlockType,
164+
CanType ResultType,
165+
bool predefined);
166+
158167
/// Mangle the derivative function (JVP/VJP) for the given:
159168
/// - Mangled original function name.
160169
/// - Derivative function kind.

include/swift/Demangling/DemangleNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ NODE(NominalTypeDescriptor)
160160
NODE(NonObjCAttribute)
161161
NODE(Number)
162162
NODE(ObjCAsyncCompletionHandlerImpl)
163+
NODE(PredefinedObjCAsyncCompletionHandlerImpl)
163164
NODE(ObjCAttribute)
164165
NODE(ObjCBlock)
165166
NODE(EscapingObjCBlock)

lib/AST/ASTMangler.cpp

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

397+
std::string ASTMangler::mangleObjCAsyncCompletionHandlerImpl(
398+
CanSILFunctionType BlockType,
399+
CanType ResultType,
400+
bool predefined) {
401+
beginMangling();
402+
appendType(BlockType);
403+
appendType(ResultType);
404+
appendOperator(predefined ? "TZ" : "Tz");
405+
return finalize();
406+
}
407+
397408
std::string ASTMangler::mangleAutoDiffDerivativeFunctionHelper(
398409
StringRef name, AutoDiffDerivativeFunctionKind kind,
399410
AutoDiffConfig config) {

lib/Demangling/Demangler.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,9 +2266,14 @@ NodePointer Demangler::popProtocolConformance() {
22662266
NodePointer type = popNode(Node::Kind::Type);
22672267
return createWithChild(Node::Kind::CoroutineContinuationPrototype, type);
22682268
}
2269-
case 'z': {
2270-
NodePointer implType = popNode(Node::Kind::ImplFunctionType);
2271-
return createWithChild(Node::Kind::ObjCAsyncCompletionHandlerImpl, implType);
2269+
case 'z':
2270+
case 'Z': {
2271+
NodePointer resultType = popNode(Node::Kind::Type);
2272+
NodePointer implType = popNode(Node::Kind::Type);
2273+
return createWithChildren(c == 'z'
2274+
? Node::Kind::ObjCAsyncCompletionHandlerImpl
2275+
: Node::Kind::PredefinedObjCAsyncCompletionHandlerImpl,
2276+
implType, resultType);
22722277
}
22732278
case 'V': {
22742279
NodePointer Base = popNode(isEntity);

lib/Demangling/NodePrinter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ class NodePrinter {
455455
case Node::Kind::PartialApplyForwarder:
456456
case Node::Kind::PartialApplyObjCForwarder:
457457
case Node::Kind::PostfixOperator:
458+
case Node::Kind::PredefinedObjCAsyncCompletionHandlerImpl:
458459
case Node::Kind::PrefixOperator:
459460
case Node::Kind::PrivateDeclName:
460461
case Node::Kind::PropertyDescriptor:
@@ -2534,9 +2535,14 @@ NodePointer NodePrinter::print(NodePointer Node, bool asPrefixContext) {
25342535
Printer << ')';
25352536
}
25362537
return nullptr;
2538+
case Node::Kind::PredefinedObjCAsyncCompletionHandlerImpl:
2539+
Printer << "predefined ";
2540+
LLVM_FALLTHROUGH;
25372541
case Node::Kind::ObjCAsyncCompletionHandlerImpl:
25382542
Printer << "@objc completion handler block implementation for ";
25392543
print(Node->getChild(0));
2544+
Printer << " with result type ";
2545+
print(Node->getChild(1));
25402546
return nullptr;
25412547
case Node::Kind::CanonicalPrespecializedGenericTypeCachingOnceToken:
25422548
Printer << "flag for loading of canonical specialized generic type "

lib/Demangling/OldRemangler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2166,6 +2166,9 @@ void Remangler::mangleGlobalVariableOnceFunction(Node *node) {
21662166
void Remangler::mangleGlobalVariableOnceDeclList(Node *node) {
21672167
unreachable("unsupported");
21682168
}
2169+
void Remangler::manglePredefinedObjCAsyncCompletionHandlerImpl(Node *node) {
2170+
unreachable("unsupported");
2171+
}
21692172
void Remangler::mangleObjCAsyncCompletionHandlerImpl(Node *node) {
21702173
unreachable("unsupported");
21712174
}

lib/Demangling/Remangler.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,11 @@ void Remangler::mangleCoroutineContinuationPrototype(Node *node) {
805805
Buffer << "TC";
806806
}
807807

808+
void Remangler::manglePredefinedObjCAsyncCompletionHandlerImpl(Node *node) {
809+
mangleChildNodes(node);
810+
Buffer << "TZ";
811+
}
812+
808813
void Remangler::mangleObjCAsyncCompletionHandlerImpl(Node *node) {
809814
mangleChildNodes(node);
810815
Buffer << "Tz";

lib/SILGen/ResultPlan.cpp

Lines changed: 99 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -453,25 +453,116 @@ 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+
*calleeTypeInfo.foreign.async);
512+
auto implRef = SGF.B.createFunctionRef(loc, impl);
513+
514+
// Initialize the block object for the completion handler.
515+
auto block = SGF.B.createInitBlockStorageHeader(loc, blockStorage, implRef,
516+
SILType::getPrimitiveObjectType(implTy), {});
517+
// We don't need to manage the block because it's still on the stack. We
518+
// know we won't escape it locally so the callee can be responsible for
519+
// _Block_copy-ing it.
520+
return ManagedValue::forUnmanaged(block);
465521
}
466522

467523
RValue finish(SILGenFunction &SGF, SILLocation loc, CanType substType,
468524
ArrayRef<ManagedValue> &directResults) override {
469525
// There should be no direct results from the call.
470526
assert(directResults.empty());
471527

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

@@ -572,8 +663,7 @@ ResultPlanPtr ResultPlanBuilder::buildTopLevelResult(Initialization *init,
572663
// Create a result plan that gets the result schema from the completion
573664
// handler callback's arguments.
574665
// completion handler.
575-
return ResultPlanPtr(new ForeignAsyncInitializationPlan(loc));
576-
666+
return ResultPlanPtr(new ForeignAsyncInitializationPlan(SGF, loc, calleeTypeInfo));
577667
} else if (auto foreignError = calleeTypeInfo.foreign.error) {
578668
// Handle the foreign error first.
579669
//

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.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: 20 additions & 0 deletions
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

@@ -163,6 +168,14 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
163168
CanSILFunctionType fromType,
164169
CanSILFunctionType toType,
165170
CanType dynamicSelfType);
171+
172+
/// Get or create the declaration of a completion handler block
173+
/// implementation function for an ObjC API that was imported
174+
/// as `async` in Swift.
175+
SILFunction *getOrCreateForeignAsyncCompletionHandlerImplFunction(
176+
CanSILFunctionType blockType,
177+
CanType continuationTy,
178+
ForeignAsyncConvention convention);
166179

167180
/// Determine whether the given class has any instance variables that
168181
/// need to be destroyed.
@@ -460,6 +473,13 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
460473
/// Retrieve the conformance of NSError to the Error protocol.
461474
ProtocolConformance *getNSErrorConformanceToError();
462475

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+
463483
SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess,
464484
KeyPathTypeKind typeKind);
465485

0 commit comments

Comments
 (0)