Skip to content

Commit

Permalink
[Coroutines] Documentation for plugin libraries and custom ABIs
Browse files Browse the repository at this point in the history
  • Loading branch information
tnowicki committed Oct 10, 2024
1 parent e82fcda commit e0ef787
Showing 1 changed file with 90 additions and 0 deletions.
90 changes: 90 additions & 0 deletions llvm/docs/Coroutines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ lowered to a constant representing the size required for the coroutine frame.
The `coro.begin`_ intrinsic initializes the coroutine frame and returns the
coroutine handle. The second parameter of `coro.begin` is given a block of memory
to be used if the coroutine frame needs to be allocated dynamically.

The `coro.id`_ intrinsic serves as coroutine identity useful in cases when the
`coro.begin`_ intrinsic get duplicated by optimization passes such as
jump-threading.
Expand Down Expand Up @@ -749,6 +750,65 @@ and python iterator `__next__` would look like:
return *(int*)coro.promise(hdl, 4, false);
}

Custom ABIs and Plugin Libraries
--------------------------------

Plugin libraries can extend coroutine lowering enabling a wide variety of users
to utilize the coroutine transformation passes. An existing coroutine lowering
is extended by: 1. defining custom ABIs that inherit from the existing ABIs,
2. give a list of generators for the custom ABIs when constructing the
`CoroSplit`_ pass, and 3. use `coro.begin.custom.abi` in place of `coro.begin`
with an additional parameter for the index of the generator/ABI to be used for
the coroutine.

A custom ABI overriding the SwitchABI's materialization looks like:

.. code-block:: c++

class CustomSwitchABI : public coro::SwitchABI {
public:
CustomSwitchABI(Function &F, coro::Shape &S)
: coro::SwitchABI(F, S, ExtraMaterializable) {}
};

Giving a list of custom ABI generators while constructing the `CoroSplit`
pass looks like:

.. code-block:: c++

CoroSplitPass::BaseABITy GenCustomABI = [](Function &F, coro::Shape &S) {
return new CustomSwitchABI(F, S);
};

CGSCCPassManager CGPM;
CGPM.addPass(CoroSplitPass({GenCustomABI}));

The LLVM IR for a coroutine using a Coroutine with a custom ABI looks like:

.. code-block:: llvm
define ptr @f(i32 %n) presplitcoroutine_custom_abi {
entry:
%id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
%size = call i32 @llvm.coro.size.i32()
%alloc = call ptr @malloc(i32 %size)
%hdl = call noalias ptr @llvm.coro.begin.custom.abi(token %id, ptr %alloc, i32 0)
br label %loop
loop:
%n.val = phi i32 [ %n, %entry ], [ %inc, %loop ]
%inc = add nsw i32 %n.val, 1
call void @print(i32 %n.val)
%0 = call i8 @llvm.coro.suspend(token none, i1 false)
switch i8 %0, label %suspend [i8 0, label %loop
i8 1, label %cleanup]
cleanup:
%mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
call void @free(ptr %mem)
br label %suspend
suspend:
%unused = call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
ret ptr %hdl
}
Intrinsics
==========
Expand Down Expand Up @@ -1007,6 +1067,36 @@ with small positive and negative offsets).

A frontend should emit exactly one `coro.begin` intrinsic per coroutine.

.. _coro.begin.custom.abi:

'llvm.coro.begin.custom.abi' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::

declare ptr @llvm.coro.begin.custom.abi(token <id>, ptr <mem>, i32)

Overview:
"""""""""

The '``llvm.coro.begin.custom.abi``' intrinsic is used in place of the
`coro.begin` intrinsic with an additional parameter to specify the custom ABI
for the coroutine. The return is identical to that of the `coro.begin`
intrinsic.

Arguments:
""""""""""

The first and second arguments are identical to those of the `coro.begin`
intrinsic.

The third argument is an i32 index of the generator list given to the
`CoroSplit` pass specifying the custom ABI generator lor this coroutine.

Semantics:
""""""""""

The semantics are identical to those of the `coro.begin` intrinsic.

.. _coro.free:

'llvm.coro.free' Intrinsic
Expand Down

0 comments on commit e0ef787

Please sign in to comment.