[llvm-dev] [RFC] LLVM Coroutines

Sanjoy Das via llvm-dev llvm-dev at lists.llvm.org
Sat Jun 11 18:09:26 PDT 2016


Hi Gor,

How will you handle (potentially variably sized) alloca instructions
that cannot be elided by promotion to SSA?  E.g.

void cor() {
  int a = 0;
  escape(&a);
  for (;;) yield(a++);
}

-- Sanjoy

On Sat, Jun 11, 2016 at 5:43 PM, Gor Nishanov via llvm-dev
<llvm-dev at lists.llvm.org> wrote:
> (Dropped llvm-dev by accident. Putting it back)
>
>
> HI Eli:
>
>>> coro.barrier() doesn't work: if the address of the alloca doesn't escape,
>>> alias analysis will assume the barrier can't read or write the value of
>>> the alloca, so the barrier doesn't actually block code movement.
>
> Got it. I am new to this and learning a lot over the course
> of this thread. Thank you for being patient with me.
>
> Two questions and one clarification:
>
> Q1: Do we have to have a load here?
> ===================================
>
>>> block1:
>>>    %first_time = load... <--- What are we loading here?
>>>    br i1 %first_time, label return, label suspend1
>>>
>>> supend1:
>>>    %0 = coro.suspend()
>>>    switch %0 (resume1, destroy1)
>
> Can we use three way coro.suspend instead?
>
>   Block1:
>     %0 = call i8 coro.suspend()
>     switch i8 %0, label suspend1 [i8 0 %return] ; or icmp + br i1
>   Suspend1:
>     switch i8 %0, label %resume1 [i8 1 %destroy1] ; or icmp + br i1
>
> One problem I can see is that someone can write a pass that might merge
> two branches / switches into one switch and we are back where we were.
> I guess what you meant by load, is to call some coro.is.first.time() intrinsic.
> So it looks like:
>
>>> block1:
>>>    %first_time = call i1 coro.is.first.time()
>>>    br i1 %first_time, label return, label suspend1
>>>
>>> supend1:
>>>    %0 = coro.suspend()
>>>    switch %0 (resume1, destroy1)
>
> This looks fine, there may be more uses for this intrinsic in the frontend.
> Killing two birds with one stone. Good.
>
> Question 2: Why the switch in the return block?
> ===============================================
>
> I would think that **pre-split** return block would be simply:
>
> return:
>    <run dtors for parameters, if required>
>    <conversion ops for ret value, if required>
>    <ret void> or <ret whatever>
>
> Where and why I should put a switch that you mentioned in this return block?
>
> BTW, I am speaking of the return block as if it is one block,
> but, it could be a dominating block over all the blocks that together
> run the destructors, do return value conversion, etc.
>
> Clarification:
> ==============
>
>>> Also, if some non-C++ language wants to generate coroutines,
>>> it might not have to generate the return block at all.
>
> C++ coroutines are flexible. The semantic of a coroutine is defined via
> traits, so you may define a coroutine that returns void. It does not have
> to return coroutine handle or some struct that wraps the coroutine handle.
>
> For example, a developer may have a queue of suspended coroutines that is
> processed by a scheduler, so a coroutine can queue its handle on suspension
> either into a global queue or thread_local queue. If there are no destructors
> to run for parameters, the return block would be simply:
>
>   return:
>     ret void
>
>
> Cheers,
> Gor
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev



-- 
Sanjoy Das
http://playingwithpointers.com


More information about the llvm-dev mailing list