Skip to content

Commit 98711fd

Browse files
committed
Revise the continuation ABI.
The immediate desire is to minimize the set of ABI dependencies on the layout of an ExecutorRef. In addition to that, however, I wanted to generally reduce the code size impact of an unsafe continuation since it now requires accessing thread-local state, and I wanted resumption to not have to create unnecessary type metadata for the value type just to do the initialization. Therefore, I've introduced a swift_continuation_init function which handles the default initialization of a continuation and returns a reference to the current task. I've also moved the initialization of the normal continuation result into the caller (out of the runtime), and I've moved the resumption-side cmpxchg into the runtime (and prior to the task being enqueued).
1 parent 0f152af commit 98711fd

25 files changed

+634
-268
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2052,6 +2052,9 @@ enum class AsyncContextKind {
20522052
/// A context which can yield to its caller.
20532053
Yielding = 1,
20542054

2055+
/// A continuation context.
2056+
Continuation = 2,
2057+
20552058
// Other kinds are reserved for interesting special
20562059
// intermediate contexts.
20572060

@@ -2096,6 +2099,55 @@ class AsyncContextFlags : public FlagSet<uint32_t> {
20962099
setShouldNotDeallocateInCallee)
20972100
};
20982101

2102+
/// Flags passed to swift_continuation_init.
2103+
class AsyncContinuationFlags : public FlagSet<size_t> {
2104+
public:
2105+
enum {
2106+
CanThrow = 0,
2107+
HasExecutorOverride = 1,
2108+
IsPreawaited = 2,
2109+
};
2110+
2111+
explicit AsyncContinuationFlags(size_t bits) : FlagSet(bits) {}
2112+
constexpr AsyncContinuationFlags() {}
2113+
2114+
/// Whether the continuation is permitted to throw.
2115+
FLAGSET_DEFINE_FLAG_ACCESSORS(CanThrow, canThrow, setCanThrow)
2116+
2117+
/// Whether the continuation should be resumed on a different
2118+
/// executor than the current one. swift_continuation_init
2119+
/// will not initialize ResumeToExecutor if this is set.
2120+
FLAGSET_DEFINE_FLAG_ACCESSORS(HasExecutorOverride,
2121+
hasExecutorOverride,
2122+
setHasExecutorOverride)
2123+
2124+
/// Whether the continuation is "pre-awaited". If so, it should
2125+
/// be set up in the already-awaited state, and so resumptions
2126+
/// will immediately schedule the continuation to begin
2127+
/// asynchronously.
2128+
FLAGSET_DEFINE_FLAG_ACCESSORS(IsPreawaited,
2129+
isPreawaited,
2130+
setIsPreawaited)
2131+
};
2132+
2133+
/// Status values for a continuation. Note that the "not yet"s in
2134+
/// the description below aren't quite right because the system
2135+
/// does not actually promise to update the status before scheduling
2136+
/// the task. This is because the continuation context is immediately
2137+
/// invalidated once the task starts running again, so the window in
2138+
/// which we can usefully protect against (say) double-resumption may
2139+
/// be very small.
2140+
enum class ContinuationStatus : size_t {
2141+
/// The continuation has not yet been awaited or resumed.
2142+
Pending = 0,
2143+
2144+
/// The continuation has already been awaited, but not yet resumed.
2145+
Awaited = 1,
2146+
2147+
/// The continuation has already been resumed, but not yet awaited.
2148+
Resumed = 2
2149+
};
2150+
20992151
} // end namespace swift
21002152

21012153
#endif // SWIFT_ABI_METADATAVALUES_H

include/swift/ABI/Task.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,31 @@ class YieldingAsyncContext : public AsyncContext {
536536
}
537537
};
538538

539+
/// An async context that can be resumed as a continuation.
540+
class ContinuationAsyncContext : public AsyncContext {
541+
public:
542+
/// An atomic object used to ensure that a continuation is not
543+
/// scheduled immediately during a resume if it hasn't yet been
544+
/// awaited by the function which set it up.
545+
std::atomic<ContinuationStatus> AwaitSynchronization;
546+
547+
/// The error result value of the continuation.
548+
/// This should be null-initialized when setting up the continuation.
549+
/// Throwing resumers must overwrite this with a non-null value.
550+
SwiftError *ErrorResult;
551+
552+
/// A pointer to the normal result value of the continuation.
553+
/// Normal resumers must initialize this before resuming.
554+
OpaqueValue *NormalResult;
555+
556+
/// The executor that should be resumed to.
557+
ExecutorRef ResumeToExecutor;
558+
559+
static bool classof(const AsyncContext *context) {
560+
return context->Flags.getKind() == AsyncContextKind::Continuation;
561+
}
562+
};
563+
539564
/// An asynchronous context within a task that describes a general "Future".
540565
/// task.
541566
///

include/swift/AST/ASTSynthesis.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ enum SingletonTypeSynthesizer {
4646
_nativeObject,
4747
_never,
4848
_rawPointer,
49+
_rawUnsafeContinuation,
4950
_void,
5051
_word,
5152
};
@@ -60,6 +61,7 @@ inline Type synthesizeType(SynthesisContext &SC,
6061
case _nativeObject: return SC.Context.TheNativeObjectType;
6162
case _never: return SC.Context.getNeverType();
6263
case _rawPointer: return SC.Context.TheRawPointerType;
64+
case _rawUnsafeContinuation: return SC.Context.TheRawUnsafeContinuationType;
6365
case _void: return SC.Context.TheEmptyTupleType;
6466
case _word: return BuiltinIntegerType::get(BuiltinIntegerWidth::pointer(),
6567
SC.Context);

include/swift/AST/Builtins.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,18 @@ BUILTIN_MISC_OPERATION(InitializeDefaultActor, "initializeDefaultActor", "", Spe
723723
/// Destroy the default-actor instance in a default actor object.
724724
BUILTIN_MISC_OPERATION(DestroyDefaultActor, "destroyDefaultActor", "", Special)
725725

726+
/// Resume a non-throwing continuation normally with the given result.
727+
BUILTIN_MISC_OPERATION(ResumeNonThrowingContinuationReturning,
728+
"resumeNonThrowingContinuationReturning", "", Special)
729+
730+
/// Resume a throwing continuation normally with the given result.
731+
BUILTIN_MISC_OPERATION(ResumeThrowingContinuationReturning,
732+
"resumeThrowingContinuationReturning", "", Special)
733+
734+
/// Resume a throwing continuation abnormally with the given error.
735+
BUILTIN_MISC_OPERATION(ResumeThrowingContinuationThrowing,
736+
"resumeThrowingContinuationThrowing", "", Special)
737+
726738
// BUILTIN_MISC_OPERATION_WITH_SILGEN - Miscellaneous operations that are
727739
// specially emitted during SIL generation.
728740
//

include/swift/Runtime/Concurrency.h

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -523,23 +523,35 @@ void swift_defaultActor_destroy(DefaultActor *actor);
523523
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
524524
void swift_defaultActor_enqueue(Job *job, DefaultActor *actor);
525525

526-
/// Resume a task from its continuation, given a normal result value.
526+
/// Prepare a continuation in the current task.
527+
///
528+
/// The caller should initialize the Parent, ResumeParent,
529+
/// and NormalResult fields. This function will initialize the other
530+
/// fields with appropriate defaaults; the caller may then overwrite
531+
/// them if desired.
532+
///
533+
/// This function is provided as a code-size and runtime-usage
534+
/// optimization; calling it is not required if code is willing to
535+
/// do all its work inline.
536+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
537+
AsyncTask *swift_continuation_init(ContinuationAsyncContext *context,
538+
AsyncContinuationFlags flags);
539+
540+
/// Resume a task from a non-throwing continuation, given a normal
541+
/// result which has already been stored into the continuation.
527542
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
528-
void swift_continuation_resume(/* +1 */ OpaqueValue *result,
529-
void *continuation,
530-
const Metadata *resumeType);
543+
void swift_continuation_resume(AsyncTask *continuation);
531544

532-
/// Resume a task from its throwing continuation, given a normal result value.
545+
/// Resume a task from a potentially-throwing continuation, given a
546+
/// normal result which has already been stored into the continuation.
533547
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
534-
void swift_continuation_throwingResume(/* +1 */ OpaqueValue *result,
535-
void *continuation,
536-
const Metadata *resumeType);
548+
void swift_continuation_throwingResume(AsyncTask *continuation);
537549

538-
/// Resume a task from its throwing continuation by throwing an error.
550+
/// Resume a task from a potentially-throwing continuation by throwing
551+
/// an error.
539552
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
540-
void swift_continuation_throwingResumeWithError(/* +1 */ SwiftError *error,
541-
void *continuation,
542-
const Metadata *resumeType);
553+
void swift_continuation_throwingResumeWithError(AsyncTask *continuation,
554+
/* +1 */ SwiftError *error);
543555

544556
/// SPI helper to log a misuse of a `CheckedContinuation` to the appropriate places in the OS.
545557
extern "C" SWIFT_CC(swift)

include/swift/Runtime/RuntimeFunctions.def

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1581,6 +1581,40 @@ FUNCTION(TaskSwitchFunc,
15811581
ARGS(SwiftContextPtrTy, Int8PtrTy, SwiftExecutorPtrTy),
15821582
ATTRS(NoUnwind))
15831583

1584+
// AsyncTask *swift_continuation_init(AsyncContext *continuationContext,
1585+
// AsyncContinuationFlags);
1586+
FUNCTION(ContinuationInit,
1587+
swift_continuation_init, SwiftCC,
1588+
ConcurrencyAvailability,
1589+
RETURNS(SwiftTaskPtrTy),
1590+
ARGS(ContinuationAsyncContextPtrTy, SizeTy),
1591+
ATTRS(NoUnwind))
1592+
1593+
// void swift_continuation_resume(AsyncTask *continuation);
1594+
FUNCTION(ContinuationResume,
1595+
swift_continuation_resume, SwiftCC,
1596+
ConcurrencyAvailability,
1597+
RETURNS(VoidTy),
1598+
ARGS(SwiftTaskPtrTy),
1599+
ATTRS(NoUnwind))
1600+
1601+
// void swift_continuation_throwingResume(AsyncTask *continuation);
1602+
FUNCTION(ContinuationThrowingResume,
1603+
swift_continuation_throwingResume, SwiftCC,
1604+
ConcurrencyAvailability,
1605+
RETURNS(VoidTy),
1606+
ARGS(SwiftTaskPtrTy),
1607+
ATTRS(NoUnwind))
1608+
1609+
// void swift_continuation_throwingResumeWithError(AsyncTask *continuation,
1610+
// SwiftError *error);
1611+
FUNCTION(ContinuationThrowingResumeWithError,
1612+
swift_continuation_throwingResumeWithError, SwiftCC,
1613+
ConcurrencyAvailability,
1614+
RETURNS(VoidTy),
1615+
ARGS(SwiftTaskPtrTy, ErrorPtrTy),
1616+
ATTRS(NoUnwind))
1617+
15841618
// ExecutorRef swift_task_getCurrentExecutor();
15851619
FUNCTION(TaskGetCurrentExecutor,
15861620
swift_task_getCurrentExecutor, SwiftCC,

lib/AST/Builtins.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,23 @@ static ValueDecl *getDefaultActorInitDestroy(ASTContext &ctx,
14101410
_void);
14111411
}
14121412

1413+
static ValueDecl *getResumeContinuationReturning(ASTContext &ctx,
1414+
Identifier id) {
1415+
return getBuiltinFunction(ctx, id, _thin,
1416+
_generics(_unrestricted),
1417+
_parameters(_rawUnsafeContinuation,
1418+
_owned(_typeparam(0))),
1419+
_void);
1420+
}
1421+
1422+
static ValueDecl *getResumeContinuationThrowing(ASTContext &ctx,
1423+
Identifier id) {
1424+
return getBuiltinFunction(ctx, id, _thin,
1425+
_parameters(_rawUnsafeContinuation,
1426+
_owned(_error)),
1427+
_void);
1428+
}
1429+
14131430
static ValueDecl *getAutoDiffCreateLinearMapContext(ASTContext &ctx,
14141431
Identifier id) {
14151432
return getBuiltinFunction(
@@ -2620,6 +2637,13 @@ ValueDecl *swift::getBuiltinValueDecl(ASTContext &Context, Identifier Id) {
26202637
case BuiltinValueKind::DestroyDefaultActor:
26212638
return getDefaultActorInitDestroy(Context, Id);
26222639

2640+
case BuiltinValueKind::ResumeNonThrowingContinuationReturning:
2641+
case BuiltinValueKind::ResumeThrowingContinuationReturning:
2642+
return getResumeContinuationReturning(Context, Id);
2643+
2644+
case BuiltinValueKind::ResumeThrowingContinuationThrowing:
2645+
return getResumeContinuationThrowing(Context, Id);
2646+
26232647
case BuiltinValueKind::WithUnsafeContinuation:
26242648
return getWithUnsafeContinuation(Context, Id, /*throws=*/false);
26252649

lib/IRGen/GenBuiltin.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,23 @@ void irgen::emitBuiltinCall(IRGenFunction &IGF, const BuiltinInfo &Builtin,
287287
return;
288288
}
289289

290-
if (Builtin.ID == BuiltinValueKind::DestroyDefaultActor) {
290+
if (Builtin.ID == BuiltinValueKind::ResumeThrowingContinuationReturning ||
291+
Builtin.ID == BuiltinValueKind::ResumeNonThrowingContinuationReturning) {
292+
auto continuation = args.claimNext();
293+
auto valueTy = argTypes[1];
294+
auto valuePtr = args.claimNext();
295+
bool throwing =
296+
(Builtin.ID == BuiltinValueKind::ResumeThrowingContinuationReturning);
297+
IGF.emitResumeAsyncContinuationReturning(continuation, valuePtr, valueTy,
298+
throwing);
299+
return;
300+
}
301+
302+
if (Builtin.ID == BuiltinValueKind::ResumeThrowingContinuationThrowing) {
303+
auto continuation = args.claimNext();
304+
auto error = args.claimNext();
305+
IGF.emitResumeAsyncContinuationThrowing(continuation, error);
306+
return;
291307
}
292308

293309
// If this is an LLVM IR intrinsic, lower it to an intrinsic call.

lib/IRGen/GenCall.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ AsyncContextLayout::AsyncContextLayout(
198198
#endif
199199
}
200200

201-
static Alignment getAsyncContextAlignment(IRGenModule &IGM) {
202-
return IGM.getPointerAlignment();
201+
Alignment IRGenModule::getAsyncContextAlignment() const {
202+
return Alignment(MaximumAlignment);
203203
}
204204

205205
void IRGenFunction::setupAsync(unsigned asyncContextIndex) {
@@ -3815,7 +3815,7 @@ llvm::Value *irgen::emitTaskCreate(
38153815

38163816
Address irgen::emitAllocAsyncContext(IRGenFunction &IGF,
38173817
llvm::Value *sizeValue) {
3818-
auto alignment = getAsyncContextAlignment(IGF.IGM);
3818+
auto alignment = IGF.IGM.getAsyncContextAlignment();
38193819
auto address = IGF.emitTaskAlloc(sizeValue, alignment);
38203820
IGF.Builder.CreateLifetimeStart(address, Size(-1) /*dynamic size*/);
38213821
return address;

0 commit comments

Comments
 (0)