[llvm-dev] [RFC] LLVM Coroutines

Eli Friedman via llvm-dev llvm-dev at lists.llvm.org
Fri Jun 10 16:08:16 PDT 2016


On Fri, Jun 10, 2016 at 6:36 AM, Gor Nishanov <gornishanov at gmail.com> wrote:

> >> If you're going down that route, that still leaves the question of the
> >> semantics of the fork intrinsic... thinking about it a bit more, I think
> >> you're going to run into problems with trying to keep around a return
> block
> >> through optimizations:
>
> Thinking about it a bit more, it is even simpler, I don't have to involve
> coro.end in this case at all and should not worry about branches, RAUW-ing
> coro.suspend with an appropriate constant takes care of it all.
>
> Here is an updated version.
> 1) coro.fork is gone, gone gone!!!!
> 2) coro.suspend is changed as follows:
>
>     declare i8 coro.suspend([...])
>
>     returns:
>       0 - in resume clone
>       1 - in destroy clone
>       -1 - in the original function
>
> Usage:
>
>     [...]
>     %0 = call i8 @llvm.coro.suspend([...])
>     switch i8 %0, label %return [ i32 0, label %resume,
>                                   i32 1, label %cleanup]
>
> Lowering of coro.suspend X
>
>   In the original function:
>     * RAUW coro.suspend to -1
>     * Remove coro.suspend
>
>   In both clones:
>     * RAUW coro.suspend to 0 in `f.resume` and 1 in `f.destroy`
>     * Split block after coro.suspend, new block becomes the resume label X
>     * replace coro.suspend with
>       - `ret void` in resume clone
>       - @llvm.trap() in destroy clone
>         (in destroy clone none of the suspends points should be reachable,
>          if they are it is a front end bug)
>
> Much prettier than before :-)
>
>
>
I'm not sure this quite works the way you want it to in terms of the
optimizer.  For example:

block1:
suspend
switch (resume, destroy, return)

resume:
store zero to global @g
[...]

destroy:
store zero to global @g
[...]

return:
store zero to global @g
[...]

Naively, you would expect that it would be legal to hoist the store... but
that breaks your coroutine semantics because the global could be
mutated between
the first return and the resume.

Probably the most natural way to solve this is the two-function approach I
suggested earlier... that way, the first call to suspend isn't special, so
everything just works.

There might be some other way to solve it, but I think it just becomes more
awkward. For example, you could add a boolean that tracks whether the
function has been suspended yet, and explicitly reroute the control flow to
conditionally perform one-time operations before each call to suspend.  But
then you need a giant switch statement or a separate one-time cleanup
function to actually route the control flow correctly.

-Eli
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20160610/4344a44c/attachment.html>


More information about the llvm-dev mailing list