<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, Jun 10, 2016 at 5:25 PM, Gor Nishanov <span dir="ltr"><<a href="mailto:gornishanov@gmail.com" target="_blank">gornishanov@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Eli:<br>
<span class=""><br>
>> Naively, you would expect that it would be legal to hoist the store...<br>
>> but that breaks your coroutine semantics because the global could be mutated<br>
>> between the first return and the resume.<br>
<br>
</span>Hmmm... I don't see the problem. I think hoisting the store is perfectly legal<br>
transformation with all semantics preserved.<br>
<br>
Let's look at your example:<br>
<span class=""><br>
>> block1:<br>
>> suspend<br>
>> switch (resume, destroy, return)<br>
>><br>
>> resume:<br>
>> store zero to global @g<br>
</span>>> doA()<br>
<span class="">>> [...]<br>
>><br>
>> destroy:<br>
>> store zero to global @g<br>
</span>>> doB()<br>
<span class="">>> [...]<br>
>><br>
>> return:<br>
>> store zero to global @g<br>
</span>>> doC<br>
>> [...]<br>
<br>
As written the behavior is:<br>
<br>
1) when we encounter a suspend during the first pass through the function,<br>
store 0 to @g and doC()<br>
<br>
2) when we encounter a suspend after coroutine was resumed<br>
ret void<br>
<br>
3) When coroutine is resumed:<br>
store 0 to @g and doA()<br>
<br>
4) When coroutine is destroyed:<br>
store 0 to @g and doB()<br>
<br>
If this is a coroutine that can be resumed asynchronously from a different<br>
thread there is a data race. For example, if resume happens before 'f' returns,<br>
doA() can write something useful to @g, and 'f' will clobber it with zero.<br>
But, this race is already present in the code and is not introduced by LLVM.<br>
<br>
Let's hoist the store and see what happens:<br>
<br>
>> block1:<br>
>> suspend<br>
<span class="">>> store zero to global @g<br>
>> switch (resume, destroy, return)<br>
>><br>
>> resume:<br>
</span>>> doA()<br>
>> [...]<br>
>><br>
>> destroy:<br>
>> doB()<br>
>> [...]<br>
>><br>
>> return:<br>
>> doC()<br>
>> [...]<br>
<br>
Now, let's split it up:<br>
1. RAUW coro.suspend with -1,0,1 in f, f.resume and f.destroy<br>
2. remove coro.suspend in f, replace with ret void in f.resume<br>
<br>
void f() {<br>
[...]<br>
<span class=""> store zero to global @g<br>
</span> doC();<br>
[...]<br>
}<br>
<br>
void @f.resume() {<br>
entry:<br>
<span class=""> store zero to global @g<br>
</span> doA();<br>
[....]<br>
}<br>
<br>
void @f.destroy() {<br>
entry:<br>
<span class=""> store zero to global @g<br>
</span> doB();<br>
[....]<br>
}<br>
<br>
Behavior looks exactly as before. What am I missing?<br>
<br></blockquote><div><br></div><div>Err... I guess nothing; sorry, I got myself confused. Essentially executing a return statement, then jumping back, seems wrong, but I'm having trouble coming up with a good example of how it would actually break something. I guess there aren't really any issues on a pure control-flow basis: you can't execute a global side-effect a different number of times than it would otherwise happen.<br><br>You might run into problems with allocas: LLVM's optimizer will assume the lifetime of any alloca in the current function ends when you hit a "ret" instruction, so jumping back from the ret to the middle of the function could lead to wrong results. Silly example:<br><br></div><div>x = alloca...<br><br>block1:<br>
suspend<br> ; increment could get moved here.<br></div><div><span class=""> switch (resume, destroy, return)<br>
<br>resume:<br>
</span> x += 1<br>
[...]<br>
<br>destroy:<br>
x += 1<br>
[...]<br>
<br>return:<br></div><div> (don't touch x)<br></div><div> [...]<br></div><div><br></div><div>The increment is only supposed to execute once, but instead executes twice.<br></div><div><br></div><div>This isn't a great example... and I'm not sure issues are limited to allocas... but that's the idea, anyway.<br></div><div><br></div><div>-Eli<br></div></div><br></div></div>