[llvm-dev] [RFC] LLVM Coroutines

Gor Nishanov via llvm-dev llvm-dev at lists.llvm.org
Sat Jun 11 17:43:45 PDT 2016


(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


More information about the llvm-dev mailing list