Skip to content

Commit 48a28ab

Browse files
authored
Merge pull request #34882 from DougGregor/async-let-silgen
[Concurrency] Implement SIL generation for "async let".
2 parents 220757b + dab6fb7 commit 48a28ab

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)