@@ -631,16 +631,98 @@ autorelease in the callee.
631
631
type behaves like a non-generic type, as if the substitutions were
632
632
actually applied to the underlying function type.
633
633
634
+ Async Functions
635
+ ```````````````
636
+
637
+ SIL function types may be ``@async ``. ``@async `` functions run inside async
638
+ tasks, and can have explicit *suspend points * where they suspend execution.
639
+ ``@async `` functions can only be called from other ``@async `` functions, but
640
+ otherwise can be invoked with the normal ``apply `` and ``try_apply ``
641
+ instructions (or ``begin_apply `` if they are coroutines).
642
+
643
+ In Swift, the ``withUnsafeContinuation `` primitive is used to implement
644
+ primitive suspend points. In SIL, ``@async `` functions represent this
645
+ abstraction using the ``get_async_continuation[_addr] `` and
646
+ ``await_async_continuation `` instructions. ``get_async_continuation[_addr] ``
647
+ accesses a *continuation * value that can be used to resume the coroutine after
648
+ it suspends. The resulting continuation value can then be passed into a
649
+ completion handler, registered with an event loop, or scheduled by some other
650
+ mechanism. Operations on the continuation can resume the async function's
651
+ execution by passing a value back to the async function, or passing in an error
652
+ that propagates as an error in the async function's context.
653
+ The ``await_async_continuation `` instruction suspends execution of
654
+ the coroutine until the continuation is invoked to resume it. A use of
655
+ ``withUnsafeContinuation `` in Swift::
656
+
657
+ func waitForCallback() async -> Int {
658
+ return await withUnsafeContinuation { cc in
659
+ registerCallback { cc.resume($0) }
660
+ }
661
+ }
662
+
663
+ might lower to the following SIL::
664
+
665
+ sil @waitForCallback : $@convention(thin) @async () -> Int {
666
+ entry:
667
+ %cc = get_async_continuation $Int
668
+ %closure = function_ref @waitForCallback_closure
669
+ : $@convention(thin) (UnsafeContinuation<Int>) -> ()
670
+ apply %closure(%cc)
671
+ await_async_continuation %cc, resume resume_cc
672
+
673
+ resume_cc(%result : $Int):
674
+ return %result
675
+ }
676
+
677
+ The closure may then be inlined into the ``waitForCallback `` function::
678
+
679
+ sil @waitForCallback : $@convention(thin) @async () -> Int {
680
+ entry:
681
+ %cc = get_async_continuation $Int
682
+ %registerCallback = function_ref @registerCallback
683
+ : $@convention(thin) (@convention(thick) () -> ()) -> ()
684
+ %callback_fn = function_ref @waitForCallback_callback
685
+ %callback = partial_apply %callback_fn(%cc)
686
+ apply %registerCallback(%callback)
687
+ await_async_continuation %cc, resume resume_cc
688
+
689
+ resume_cc(%result : $Int):
690
+ return %result
691
+ }
692
+
693
+ Every continuation value must be used exactly once to resume its associated
694
+ async coroutine once. It is undefined behavior to attempt to resume the same
695
+ continuation more than once. On the flip side, failing to resume a continuation
696
+ will leave the async task stuck in the suspended state, leaking any memory or
697
+ other resources it owns.
698
+
634
699
Coroutine Types
635
700
```````````````
636
701
637
702
A coroutine is a function which can suspend itself and return control to
638
703
its caller without terminating the function. That is, it does not need to
639
- obey a strict stack discipline.
640
-
641
- SIL supports two kinds of coroutine: ``@yield_many `` and ``@yield_once ``.
642
- Either of these attributes may be written before a function type to
643
- indicate that it is a coroutine type.
704
+ obey a strict stack discipline. SIL coroutines have control flow that is
705
+ tightly integrated with their callers, and they pass information back and forth
706
+ between caller and callee in a structured way through yield points.
707
+ *Generalized accessors * and *generators * in Swift fit this description: a
708
+ ``read `` or ``modify `` accessor coroutine projects a single value, yields
709
+ ownership of that one value temporarily to the caller, and then takes ownership
710
+ back when resumed, allowing the coroutine to clean up resources or otherwise
711
+ react to mutations done by the caller. *Generators * similarly yield a stream of
712
+ values one at a time to their caller, temporarily yielding ownership of each
713
+ value in turn to the caller. The tight coupling of the caller's control flow
714
+ with these coroutines allows the caller to *borrow * values produced by the
715
+ coroutine, where a normal function return would need to transfer ownership of
716
+ its return value, since a normal function's context ceases to exist and be able
717
+ to maintain ownership of the value after it returns.
718
+
719
+ To support these concepts, SIL supports two kinds of coroutine:
720
+ ``@yield_many `` and ``@yield_once ``. Either of these attributes may be
721
+ written before a function type to indicate that it is a coroutine type.
722
+ ``@yield_many `` and ``@yield_once `` coroutines are allowed to also be
723
+ ``@async ``. (Note that ``@async `` functions are not themselves modeled
724
+ explicitly as coroutines in SIL, although the implementation may use a coroutine
725
+ lowering strategy.)
644
726
645
727
A coroutine type may declare any number of *yielded values *, which is to
646
728
say, values which are provided to the caller at a yield point. Yielded
@@ -658,19 +740,13 @@ coroutine can be called with the ``begin_apply`` instruction. There
658
740
is no support yet for calling a throwing yield-once coroutine or for
659
741
calling a yield-many coroutine of any kind.
660
742
661
- Coroutines may contain the special ``yield `` and ``unwind `` instructions.
743
+ Coroutines may contain the special ``yield `` and ``unwind ``
744
+ instructions.
662
745
663
746
A ``@yield_many `` coroutine may yield as many times as it desires.
664
747
A ``@yield_once `` coroutine may yield exactly once before returning,
665
748
although it may also ``throw `` before reaching that point.
666
749
667
- This coroutine representation is well-suited to coroutines whose control
668
- flow is tightly integrated with their callers and which intend to pass
669
- information back and forth. This matches the needs of generalized
670
- accessor and generator features in Swift. It is not a particularly good
671
- match for ``async ``/``await ``-style features; a simpler representation
672
- would probably do fine for that.
673
-
674
750
Properties of Types
675
751
```````````````````
676
752
@@ -2608,6 +2684,76 @@ undefined behavior if the global variable has already been initialized.
2608
2684
2609
2685
The type operand must be a lowered object type.
2610
2686
2687
+ get_async_continuation
2688
+ ``````````````````````
2689
+
2690
+ ::
2691
+
2692
+ sil-instruction ::= 'get_async_continuation' '[throws]'? sil-type
2693
+
2694
+ %0 = get_async_continuation $T
2695
+ %0 = get_async_continuation [throws] $U
2696
+
2697
+ Begins a suspension of an ``@async `` function. This instruction can only be
2698
+ used inside an ``@async `` function. The result of the instruction is an
2699
+ ``UnsafeContinuation<T> `` value, where ``T `` is the formal type argument to the
2700
+ instruction, or an ``UnsafeThrowingContinuation<T> `` if the instruction
2701
+ carries the ``[throws] `` attribute. ``T `` must be a loadable type.
2702
+ The continuation must be consumed by a ``await_async_continuation `` terminator
2703
+ on all paths. Between ``get_async_continuation `` and
2704
+ ``await_async_continuation ``, the following restrictions apply:
2705
+
2706
+ - The function cannot ``return ``, ``throw ``, ``yield ``, or ``unwind ``.
2707
+ - There cannot be nested suspend points; namely, the function cannot call
2708
+ another ``@async `` function, nor can it initiate another suspend point with
2709
+ ``get_async_continuation ``.
2710
+
2711
+ The function suspends execution when the matching ``await_async_continuation ``
2712
+ terminator is reached, and resumes execution when the continuation is resumed.
2713
+ The continuation resumption operation takes a value of type ``T `` which is
2714
+ passed back into the function when it resumes execution in the ``await_async_continuation `` instruction's
2715
+ ``resume `` successor block. If the instruction
2716
+ has the ``[throws] `` attribute, it can also be resumed in an error state, in
2717
+ which case the matching ``await_async_continuation `` instruction must also
2718
+ have an ``error `` successor.
2719
+
2720
+ Within the enclosing SIL function, the result continuation is consumed by the
2721
+ ``await_async_continuation ``, and cannot be referenced after the
2722
+ ``await_async_continuation `` executes. Dynamically, the continuation value must
2723
+ be resumed exactly once in the course of the program's execution; it is
2724
+ undefined behavior to resume the continuation more than once. Conversely,
2725
+ failing to resume the continuation will leave the suspended async coroutine
2726
+ hung in its suspended state, leaking any resources it may be holding.
2727
+
2728
+ get_async_continuation_addr
2729
+ ```````````````````````````
2730
+
2731
+ ::
2732
+
2733
+ sil-instruction ::= 'get_async_continuation_addr' '[throws]'? sil-type ',' sil-operand
2734
+
2735
+ %1 = get_async_continuation_addr $T, %0 : $*T
2736
+ %1 = get_async_continuation_addr [throws] $U, %0 : $*U
2737
+
2738
+ Begins a suspension of an ``@async `` function, like ``get_async_continuation ``,
2739
+ additionally binding a specific memory location for receiving the value
2740
+ when the result continuation is resumed. The operand must be an address whose
2741
+ type is the maximally-abstracted lowered type of the formal resume type. The
2742
+ memory must be uninitialized, and must remain allocated until the matching
2743
+ ``await_async_continuation `` instruction(s) consuming the result continuation
2744
+ have executed. The behavior is otherwise the same as
2745
+ ``get_async_continuation ``, and the same restrictions apply on code appearing
2746
+ between ``get_async_continuation_addr `` and ``await_async_continuation `` as
2747
+ apply between ``get_async_continuation `` and ``await_async_continuation ``.
2748
+ Additionally, the state of the memory referenced by the operand is indefinite
2749
+ between the execution of ``get_async_continuation_addr `` and
2750
+ ``await_async_continuation ``, and it is undefined behavior to read or modify
2751
+ the memory during this time. After the ``await_async_continuation `` resumes
2752
+ normally to its ``resume `` successor, the memory referenced by the operand is
2753
+ initialized with the resume value, and that value is then owned by the current
2754
+ function. If ``await_async_continuation `` instead resumes to its ``error ``
2755
+ successor, then the memory remains uninitialized.
2756
+
2611
2757
dealloc_stack
2612
2758
`````````````
2613
2759
::
@@ -6230,6 +6376,55 @@ destination (if it returns with ``throw``).
6230
6376
6231
6377
The rules on generic substitutions are identical to those of ``apply ``.
6232
6378
6379
+ await_async_continuation
6380
+ ````````````````````````
6381
+
6382
+ ::
6383
+
6384
+ sil-terminator ::= 'await_async_continuation' sil-value
6385
+ ',' 'resume' sil-identifier
6386
+ (',' 'error' sil-identifier)?
6387
+
6388
+ await_async_continuation %0 : $UnsafeContinuation<T>, resume bb1
6389
+ await_async_continuation %0 : $UnsafeThrowingContinuation<T>, resume bb1, error bb2
6390
+
6391
+ bb1(%1 : @owned $T):
6392
+ bb2(%2 : @owned $Error):
6393
+
6394
+ Suspends execution of an ``@async `` function until the continuation
6395
+ is resumed. The continuation must be the result of a
6396
+ ``get_async_continuation `` or ``get_async_continuation_addr ``
6397
+ instruction within the same function; see the documentation for
6398
+ ``get_async_continuation `` for discussion of further constraints on the
6399
+ IR between ``get_async_continuation[_addr] `` and ``await_async_continuation ``.
6400
+ This terminator can only appear inside an ``@async `` function. The
6401
+ instruction must always have a ``resume `` successor, but must have an
6402
+ ``error `` successor if and only if the operand is an
6403
+ ``UnsafeThrowingContinuation<T> ``.
6404
+
6405
+ If the operand is the result of a ``get_async_continuation `` instruction,
6406
+ then the ``resume `` successor block must take an argument whose type is the
6407
+ maximally-abstracted lowered type of ``T ``, matching the type argument of the
6408
+ ``Unsafe[Throwing]Continuation<T> `` operand. The value of the ``resume ``
6409
+ argument is owned by the current function. If the operand is the result of a
6410
+ ``get_async_continuation_addr `` instruction, then the ``resume `` successor
6411
+ block must *not * take an argument; the resume value will be written to the
6412
+ memory referenced by the operand to the ``get_async_continuation_addr ``
6413
+ instruction, after which point the value in that memory becomes owned by the
6414
+ current function. With either variant, if the ``await_async_continuation ``
6415
+ instruction has an ``error `` successor block, the ``error `` block must take a
6416
+ single ``Error `` argument, and that argument is owned by the enclosing
6417
+ function. The memory referenced by a ``get_async_continuation_addr ``
6418
+ instruction remains uninitialized when ``await_async_continuation `` resumes on
6419
+ the ``error `` successor.
6420
+
6421
+ It is possible for a continuation to be resumed before
6422
+ ``await_async_continuation ``. In this case, the resume operation returns
6423
+ immediately to its caller. When the ``await_async_continuation `` instruction
6424
+ later executes, it then immediately transfers control to
6425
+ its ``resume `` or ``error `` successor block, using the resume or error value
6426
+ that the continuation was already resumed with.
6427
+
6233
6428
Differentiable Programming
6234
6429
~~~~~~~~~~~~~~~~~~~~~~~~~~
6235
6430
0 commit comments