Skip to content

Commit dab6fb7

Browse files
committed
[Concurrency] Implement SIL generation for "async let".
Implement SIL generation for "async let" constructs, which involves: 1. Creating a child task future at the point of declaration of the "async let", which runs the initializer in an async closure. 2. Entering a cleanup to destroy the child task. 3. Entering a cleanup to cancel the child task. 4. Waiting for the child task when any of the variables is reference. 5. Decomposing the result of the child task to write the results into the appropriate variables. Implements rdar://71123479.
1 parent 489870e commit dab6fb7

File tree

11 files changed

+471
-52
lines changed

11 files changed

+471
-52
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,9 @@ 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) {
330+
static FuncDecl *lookupConcurrencyIntrinsic(ASTContext &C,
331+
Optional<FuncDecl*> &cache,
332+
StringRef name) {
334333
if (cache)
335334
return *cache;
336335

@@ -355,22 +354,43 @@ lookUpResumeContinuationIntrinsic(ASTContext &C,
355354
return func;
356355
}
357356

357+
FuncDecl *
358+
SILGenModule::getRunChildTask() {
359+
return lookupConcurrencyIntrinsic(getASTContext(),
360+
RunChildTask,
361+
"_runChildTask");
362+
}
363+
364+
FuncDecl *
365+
SILGenModule::getTaskFutureGet() {
366+
return lookupConcurrencyIntrinsic(getASTContext(),
367+
TaskFutureGet,
368+
"_taskFutureGet");
369+
}
370+
371+
FuncDecl *
372+
SILGenModule::getTaskFutureGetThrowing() {
373+
return lookupConcurrencyIntrinsic(getASTContext(),
374+
TaskFutureGetThrowing,
375+
"_taskFutureGetThrowing");
376+
}
377+
358378
FuncDecl *
359379
SILGenModule::getResumeUnsafeContinuation() {
360-
return lookUpResumeContinuationIntrinsic(getASTContext(),
361-
ResumeUnsafeContinuation,
362-
"_resumeUnsafeContinuation");
380+
return lookupConcurrencyIntrinsic(getASTContext(),
381+
ResumeUnsafeContinuation,
382+
"_resumeUnsafeContinuation");
363383
}
364384
FuncDecl *
365385
SILGenModule::getResumeUnsafeThrowingContinuation() {
366-
return lookUpResumeContinuationIntrinsic(getASTContext(),
367-
ResumeUnsafeThrowingContinuation,
368-
"_resumeUnsafeThrowingContinuation");
386+
return lookupConcurrencyIntrinsic(getASTContext(),
387+
ResumeUnsafeThrowingContinuation,
388+
"_resumeUnsafeThrowingContinuation");
369389
}
370390
FuncDecl *
371391
SILGenModule::getResumeUnsafeThrowingContinuationWithError() {
372-
return lookUpResumeContinuationIntrinsic(getASTContext(),
373-
ResumeUnsafeThrowingContinuationWithError,
392+
return lookupConcurrencyIntrinsic(getASTContext(),
393+
ResumeUnsafeThrowingContinuationWithError,
374394
"_resumeUnsafeThrowingContinuationWithError");
375395
}
376396

lib/SILGen/SILGen.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
119119

120120
Optional<ProtocolConformance *> NSErrorConformanceToError;
121121

122+
Optional<FuncDecl*> RunChildTask;
123+
Optional<FuncDecl*> TaskFutureGet;
124+
Optional<FuncDecl*> TaskFutureGetThrowing;
125+
122126
Optional<FuncDecl*> ResumeUnsafeContinuation;
123127
Optional<FuncDecl*> ResumeUnsafeThrowingContinuation;
124128
Optional<FuncDecl*> ResumeUnsafeThrowingContinuationWithError;
@@ -473,6 +477,15 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
473477
/// Retrieve the conformance of NSError to the Error protocol.
474478
ProtocolConformance *getNSErrorConformanceToError();
475479

480+
/// Retrieve the _Concurrency._runChildTask intrinsic.
481+
FuncDecl *getRunChildTask();
482+
483+
/// Retrieve the _Concurrency._taskFutureGet intrinsic.
484+
FuncDecl *getTaskFutureGet();
485+
486+
/// Retrieve the _Concurrency._taskFutureGetThrowing intrinsic.
487+
FuncDecl *getTaskFutureGetThrowing();
488+
476489
/// Retrieve the _Concurrency._resumeUnsafeContinuation intrinsic.
477490
FuncDecl *getResumeUnsafeContinuation();
478491
/// Retrieve the _Concurrency._resumeUnsafeThrowingContinuation intrinsic.

lib/SILGen/SILGenApply.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5671,6 +5671,72 @@ SILGenFunction::emitCoroutineAccessor(SILLocation loc, SILDeclRef accessor,
56715671
return endApplyHandle;
56725672
}
56735673

5674+
ManagedValue SILGenFunction::emitRunChildTask(
5675+
SILLocation loc, Type functionType, ManagedValue taskFunction) {
5676+
auto runChildTaskFn = SGM.getRunChildTask();
5677+
5678+
Type resultType = functionType->castTo<FunctionType>()->getResult();
5679+
Type replacementTypes[] = {resultType};
5680+
auto subs = SubstitutionMap::get(runChildTaskFn->getGenericSignature(),
5681+
replacementTypes,
5682+
ArrayRef<ProtocolConformanceRef>{});
5683+
5684+
CanType origParamType = runChildTaskFn->getParameters()->get(0)
5685+
->getInterfaceType()->getCanonicalType();
5686+
CanType substParamType = origParamType.subst(subs)->getCanonicalType();
5687+
5688+
// Ensure that the closure has the appropriate type.
5689+
AbstractionPattern origParam(
5690+
runChildTaskFn->getGenericSignature().getCanonicalSignature(),
5691+
origParamType);
5692+
taskFunction = emitSubstToOrigValue(
5693+
loc, taskFunction, origParam, substParamType);
5694+
5695+
return emitApplyOfLibraryIntrinsic(
5696+
loc, runChildTaskFn, subs, {taskFunction}, SGFContext()
5697+
).getScalarValue();
5698+
}
5699+
5700+
ManagedValue SILGenFunction::emitCancelAsyncTask(
5701+
SILLocation loc, SILValue task) {
5702+
ASTContext &ctx = getASTContext();
5703+
auto apply = B.createBuiltin(
5704+
loc,
5705+
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CancelAsyncTask)),
5706+
getLoweredType(ctx.TheEmptyTupleType), SubstitutionMap(),
5707+
{ task });
5708+
return ManagedValue::forUnmanaged(apply);
5709+
}
5710+
5711+
void SILGenFunction::completeAsyncLetChildTask(
5712+
PatternBindingDecl *patternBinding, unsigned index) {
5713+
SILValue childTask;
5714+
bool isThrowing;
5715+
std::tie(childTask, isThrowing)= AsyncLetChildTasks[{patternBinding, index}];
5716+
5717+
Type childResultType = patternBinding->getPattern(index)->getType();
5718+
5719+
auto taskFutureGetFn = isThrowing
5720+
? SGM.getTaskFutureGetThrowing()
5721+
: SGM.getTaskFutureGet();
5722+
5723+
// Get the result from the future.
5724+
Type replacementTypes[] = {childResultType};
5725+
auto subs = SubstitutionMap::get(taskFutureGetFn->getGenericSignature(),
5726+
replacementTypes,
5727+
ArrayRef<ProtocolConformanceRef>{});
5728+
RValue childResult = emitApplyOfLibraryIntrinsic(
5729+
SILLocation(patternBinding), taskFutureGetFn, subs,
5730+
{ ManagedValue::forBorrowedObjectRValue(childTask) },
5731+
SGFContext());
5732+
5733+
// Write the child result into the pattern variables.
5734+
emitAssignToPatternVars(
5735+
SILLocation(patternBinding), patternBinding->getPattern(index),
5736+
std::move(childResult));
5737+
}
5738+
5739+
56745740
// Create a partial application of a dynamic method, applying bridging thunks
56755741
// if necessary.
56765742
static ManagedValue emitDynamicPartialApply(SILGenFunction &SGF,

lib/SILGen/SILGenBuiltin.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,14 +1409,7 @@ static ManagedValue emitBuiltinGetCurrentAsyncTask(
14091409
static ManagedValue emitBuiltinCancelAsyncTask(
14101410
SILGenFunction &SGF, SILLocation loc, SubstitutionMap subs,
14111411
ArrayRef<ManagedValue> args, SGFContext C) {
1412-
ASTContext &ctx = SGF.getASTContext();
1413-
auto argument = args[0].borrow(SGF, loc).forward(SGF);
1414-
auto apply = SGF.B.createBuiltin(
1415-
loc,
1416-
ctx.getIdentifier(getBuiltinName(BuiltinValueKind::CancelAsyncTask)),
1417-
SGF.getLoweredType(ctx.TheEmptyTupleType), SubstitutionMap(),
1418-
{ argument });
1419-
return ManagedValue::forUnmanaged(apply);
1412+
return SGF.emitCancelAsyncTask(loc, args[0].borrow(SGF, loc).forward(SGF));
14201413
}
14211414

14221415
// Emit SIL for the named builtin: createAsyncTask.

lib/SILGen/SILGenDecl.cpp

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -428,8 +428,8 @@ class LetValueInitialization : public Initialization {
428428
auto &lowering = SGF.getTypeLowering(vd->getType());
429429

430430
// Decide whether we need a temporary stack buffer to evaluate this 'let'.
431-
// There are three cases we need to handle here: parameters, initialized (or
432-
// bound) decls, and uninitialized ones.
431+
// There are four cases we need to handle here: parameters, initialized (or
432+
// bound) decls, uninitialized ones, and async let declarations.
433433
bool needsTemporaryBuffer;
434434
bool isUninitialized = false;
435435

@@ -440,6 +440,13 @@ class LetValueInitialization : public Initialization {
440440
// buffer. DI will make sure it is only assigned to once.
441441
needsTemporaryBuffer = true;
442442
isUninitialized = true;
443+
} else if (vd->isAsyncLet()) {
444+
// If this is an async let, treat it like a let-value without an
445+
// initializer. The initializer runs concurrently in a child task,
446+
// and value will be initialized at the point the variable in the
447+
// async let is used.
448+
needsTemporaryBuffer = true;
449+
isUninitialized = true;
443450
} else {
444451
// If this is a let with an initializer or bound value, we only need a
445452
// buffer if the type is address only.
@@ -1130,12 +1137,53 @@ SILGenFunction::emitInitializationForVarDecl(VarDecl *vd, bool forceImmutable) {
11301137

11311138
void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
11321139
unsigned idx) {
1140+
1141+
11331142
auto initialization = emitPatternBindingInitialization(PBD->getPattern(idx),
11341143
JumpDest::invalid());
11351144

1136-
// If an initial value expression was specified by the decl, emit it into
1137-
// the initialization. Otherwise, mark it uninitialized for DI to resolve.
1138-
if (auto *Init = PBD->getExecutableInit(idx)) {
1145+
// If this is an async let, create a child task to compute the initializer
1146+
// value.
1147+
if (PBD->isAsyncLet()) {
1148+
// Look through the implicit await (if present), try (if present), and
1149+
// call to reach the autoclosure that computes the value.
1150+
auto *init = PBD->getExecutableInit(idx);
1151+
if (auto awaitExpr = dyn_cast<AwaitExpr>(init))
1152+
init = awaitExpr->getSubExpr();
1153+
if (auto tryExpr = dyn_cast<TryExpr>(init))
1154+
init = tryExpr->getSubExpr();
1155+
init = cast<CallExpr>(init)->getFn();
1156+
assert(isa<AutoClosureExpr>(init) &&
1157+
"Could not find async let autoclosure");
1158+
bool isThrowing = init->getType()->castTo<AnyFunctionType>()->isThrowing();
1159+
1160+
// Emit the closure for the child task.
1161+
SILValue childTask;
1162+
{
1163+
FullExpr Scope(Cleanups, CleanupLocation(init));
1164+
SILLocation loc(PBD);
1165+
childTask = emitRunChildTask(
1166+
loc,
1167+
init->getType(),
1168+
emitRValue(init).getScalarValue()
1169+
).forward(*this);
1170+
}
1171+
1172+
// Destroy the task at the end of the scope.
1173+
enterDestroyCleanup(childTask);
1174+
1175+
// Push a cleanup that will cancel the child task at the end of the scope.
1176+
enterCancelAsyncTaskCleanup(childTask);
1177+
1178+
// Save the child task so we can await it as needed.
1179+
AsyncLetChildTasks[{PBD, idx}] = { childTask, isThrowing };
1180+
1181+
// Mark as uninitialized; actual initialization will occur when the
1182+
// variables are referenced.
1183+
initialization->finishUninitialized(*this);
1184+
} else if (auto *Init = PBD->getExecutableInit(idx)) {
1185+
// If an initial value expression was specified by the decl, emit it into
1186+
// the initialization.
11391187
FullExpr Scope(Cleanups, CleanupLocation(Init));
11401188

11411189
auto *var = PBD->getSingleVar();
@@ -1155,6 +1203,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
11551203

11561204
emitExprInto(Init, initialization.get(), SILLocation(PBD));
11571205
} else {
1206+
// Otherwise, mark it uninitialized for DI to resolve.
11581207
initialization->finishUninitialized(*this);
11591208
}
11601209
}
@@ -1413,6 +1462,35 @@ CleanupHandle SILGenFunction::enterDeinitExistentialCleanup(
14131462
return Cleanups.getTopCleanup();
14141463
}
14151464

1465+
namespace {
1466+
/// A cleanup that cancels an asynchronous task.
1467+
class CancelAsyncTaskCleanup: public Cleanup {
1468+
SILValue task;
1469+
public:
1470+
CancelAsyncTaskCleanup(SILValue task) : task(task) { }
1471+
1472+
void emit(SILGenFunction &SGF, CleanupLocation l,
1473+
ForUnwind_t forUnwind) override {
1474+
SILValue borrowedTask = SGF.B.createBeginBorrow(l, task);
1475+
SGF.emitCancelAsyncTask(l, borrowedTask);
1476+
SGF.B.createEndBorrow(l, borrowedTask);
1477+
}
1478+
1479+
void dump(SILGenFunction &) const override {
1480+
#ifndef NDEBUG
1481+
llvm::errs() << "CancelAsyncTaskCleanup\n"
1482+
<< "Task:" << task << "\n";
1483+
#endif
1484+
}
1485+
};
1486+
} // end anonymous namespace
1487+
1488+
CleanupHandle SILGenFunction::enterCancelAsyncTaskCleanup(SILValue task) {
1489+
Cleanups.pushCleanupInState<CancelAsyncTaskCleanup>(
1490+
CleanupState::Active, task);
1491+
return Cleanups.getTopCleanup();
1492+
}
1493+
14161494
/// Create a LocalVariableInitialization for the uninitialized var.
14171495
InitializationPtr SILGenFunction::emitLocalVariableWithCleanup(
14181496
VarDecl *vd, Optional<MarkUninitializedInst::Kind> kind, unsigned ArgNo) {

0 commit comments

Comments
 (0)