Skip to content

Commit 0b6fcb2

Browse files
committed
Stage the swift_continuation_await ABI change.
Introduce a fake (but non-ABI) declaration to the swiftinterface which marks that an SDK support swift_continuation_await, and then only call it if that declaration exists, otherwise falling back on the old atomic sequence. Using that sequence will badly mess up the runtime's tracking of task state, but it might still work, and more importantly things will still build, which solves the short-term problem. Hopefully we can remove this hack soon. Fixes rdar://problem/80787731.
1 parent 75a48d8 commit 0b6fcb2

File tree

2 files changed

+75
-4
lines changed

2 files changed

+75
-4
lines changed

lib/IRGen/IRGenFunction.cpp

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,17 @@ void IRGenFunction::emitGetAsyncContinuation(SILType resumeTy,
658658
out.add(unsafeContinuation);
659659
}
660660

661+
static bool shouldUseContinuationAwait(IRGenModule &IGM) {
662+
auto &ctx = IGM.Context;
663+
auto module = ctx.getLoadedModule(ctx.Id_Concurrency);
664+
assert(module && "building async code without concurrency library");
665+
SmallVector<ValueDecl *, 1> results;
666+
module->lookupValue(ctx.getIdentifier("_abiEnableAwaitContinuation"),
667+
NLKind::UnqualifiedLookup, results);
668+
assert(results.size() <= 1);
669+
return !results.empty();
670+
}
671+
661672
void IRGenFunction::emitAwaitAsyncContinuation(
662673
SILType resumeTy, bool isIndirectResult,
663674
Explosion &outDirectResult, llvm::BasicBlock *&normalBB,
@@ -667,6 +678,43 @@ void IRGenFunction::emitAwaitAsyncContinuation(
667678

668679
// Call swift_continuation_await to check whether the continuation
669680
// has already been resumed.
681+
bool useContinuationAwait = shouldUseContinuationAwait(IGM);
682+
683+
// As a temporary hack for compatibility with SDKs that don't provide
684+
// swift_continuation_await, emit the old inline sequence. This can
685+
// be removed as soon as we're sure that such SDKs don't exist.
686+
if (!useContinuationAwait) {
687+
auto contAwaitSyncAddr =
688+
Builder.CreateStructGEP(AsyncCoroutineCurrentContinuationContext, 1);
689+
690+
auto pendingV = llvm::ConstantInt::get(
691+
contAwaitSyncAddr->getType()->getPointerElementType(),
692+
unsigned(ContinuationStatus::Pending));
693+
auto awaitedV = llvm::ConstantInt::get(
694+
contAwaitSyncAddr->getType()->getPointerElementType(),
695+
unsigned(ContinuationStatus::Awaited));
696+
auto results = Builder.CreateAtomicCmpXchg(
697+
contAwaitSyncAddr, pendingV, awaitedV,
698+
llvm::AtomicOrdering::Release /*success ordering*/,
699+
llvm::AtomicOrdering::Acquire /* failure ordering */,
700+
llvm::SyncScope::System);
701+
auto firstAtAwait = Builder.CreateExtractValue(results, 1);
702+
auto contBB = createBasicBlock("await.async.resume");
703+
auto abortBB = createBasicBlock("await.async.abort");
704+
Builder.CreateCondBr(firstAtAwait, abortBB, contBB);
705+
Builder.emitBlock(abortBB);
706+
{
707+
// We were the first to the sync point. "Abort" (return from the
708+
// coroutine partial function, without making a tail call to anything)
709+
// because the continuation result is not available yet. When the
710+
// continuation is later resumed, the task will get scheduled
711+
// starting from the suspension point.
712+
emitCoroutineOrAsyncExit();
713+
}
714+
715+
Builder.emitBlock(contBB);
716+
}
717+
670718
{
671719
// Set up the suspend point.
672720
SmallVector<llvm::Value *, 8> arguments;
@@ -676,10 +724,26 @@ void IRGenFunction::emitAwaitAsyncContinuation(
676724
auto resumeProjFn = getOrCreateResumePrjFn();
677725
arguments.push_back(
678726
Builder.CreateBitOrPointerCast(resumeProjFn, IGM.Int8PtrTy));
679-
arguments.push_back(Builder.CreateBitOrPointerCast(
680-
IGM.getAwaitAsyncContinuationFn(),
681-
IGM.Int8PtrTy));
682-
arguments.push_back(AsyncCoroutineCurrentContinuationContext);
727+
728+
llvm::Constant *awaitFnPtr;
729+
if (useContinuationAwait) {
730+
awaitFnPtr = IGM.getAwaitAsyncContinuationFn();
731+
} else {
732+
auto resumeFnPtr =
733+
getFunctionPointerForResumeIntrinsic(AsyncCoroutineCurrentResume);
734+
awaitFnPtr = createAsyncDispatchFn(resumeFnPtr, {IGM.Int8PtrTy});
735+
}
736+
arguments.push_back(
737+
Builder.CreateBitOrPointerCast(awaitFnPtr, IGM.Int8PtrTy));
738+
739+
if (useContinuationAwait) {
740+
arguments.push_back(AsyncCoroutineCurrentContinuationContext);
741+
} else {
742+
arguments.push_back(AsyncCoroutineCurrentResume);
743+
arguments.push_back(Builder.CreateBitOrPointerCast(
744+
AsyncCoroutineCurrentContinuationContext, IGM.Int8PtrTy));
745+
}
746+
683747
auto resultTy =
684748
llvm::StructType::get(IGM.getLLVMContext(), {IGM.Int8PtrTy}, false /*packed*/);
685749
emitSuspendAsyncCall(swiftAsyncContextIndex, resultTy, arguments);

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,10 @@ public func withUnsafeThrowingContinuation<T>(
271271
fn(UnsafeContinuation<T, Error>($0))
272272
}
273273
}
274+
275+
/// A hack to mark an SDK that supports swift_continuation_await.
276+
@available(SwiftStdlib 5.5, *)
277+
@_alwaysEmitIntoClient
278+
public func _abiEnableAwaitContinuation() {
279+
fatalError("never use this function")
280+
}

0 commit comments

Comments
 (0)