<html>
<head>
<base href="https://bugs.llvm.org/">
</head>
<body><table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Bug ID</th>
<td><a class="bz_bug_link
bz_status_NEW "
title="NEW - miscompile of coroutines using symmetric control transfer"
href="https://bugs.llvm.org/show_bug.cgi?id=45130">45130</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>miscompile of coroutines using symmetric control transfer
</td>
</tr>
<tr>
<th>Product</th>
<td>libraries
</td>
</tr>
<tr>
<th>Version</th>
<td>trunk
</td>
</tr>
<tr>
<th>Hardware</th>
<td>PC
</td>
</tr>
<tr>
<th>OS</th>
<td>Linux
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>normal
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>Common Code Generator Code
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>richard-llvm@metafoo.co.uk
</td>
</tr>
<tr>
<th>CC</th>
<td>gornishanov@gmail.com, llvm-bugs@lists.llvm.org
</td>
</tr></table>
<p>
<div>
<pre>Testcase:
#include <iostream>
#include <experimental/coroutine>
namespace std { using namespace experimental; }
struct coro {
struct promise_type;
using coro_handle = std::coroutine_handle<promise_type>;
struct promise_type {
coro_handle handle;
auto initial_suspend() {
struct awaitable {
promise_type &promise;
bool await_ready() { return false; }
void await_suspend(coro_handle h) { promise.handle = h; }
void await_resume() {}
};
return awaitable{*this};
}
auto final_suspend() {
struct awaitable {
bool await_ready() { return false; }
void await_suspend(coro_handle h) { h.destroy(); }
void await_resume() { __builtin_unreachable(); }
};
return awaitable{};
}
coro get_return_object() { return {this}; }
void unhandled_exception() { throw; }
void return_void() {}
};
promise_type *promise;
bool await_ready() { return false; }
coro_handle await_suspend(coro_handle) { return promise->handle; }
coro await_resume() { return *this; }
struct self_t {
coro_handle handle;
bool await_ready() { return false; }
bool await_suspend(coro_handle h) { handle = h; return false; }
coro await_resume() { return {&handle.promise()}; }
};
static inline self_t self;
void run() { promise->handle(); }
void destroy() { promise->handle.destroy(); }
};
coro g(coro c) {
std::cout << "g1" << std::endl;
co_await c;
std::cout << "g2" << std::endl;
co_await c;
std::cout << "g3 returning" << std::endl;
c.destroy();
}
coro f() {
std::cout << "f1" << std::endl;
coro c = g(co_await coro::self);
std::cout << "f2" << std::endl;
co_await c;
std::cout << "f3" << std::endl;
co_await c;
std::cout << "f4" << std::endl;
co_await c;
std::cout << "f5 returning" << std::endl;
c.destroy();
}
int main() {
f().run();
}
At -O0, this prints out:
f1
f2
g1
f3
g2
f4
g3 returning
... which I believe to be correct. At -O2, this instead prints:
f1
f2
g1
f3
g2
f4
g2
f1
f2
g1
f3
g2
f4
g2
f1
f2
g1
f3
g2
f4
g2
f1
f2
g1
f3
g2
f4
g2
[... endless cycle ...]
Something seems to be messing up the state number handling in both coroutines.
For example, here's the resume function for g:
define internal fastcc void @_Z1g4coro.resume(%_Z1g4coro.Frame* noalias nonnull
%0) #2 {
%2 = getelementptr inbounds %_Z1g4coro.Frame, %_Z1g4coro.Frame* %0, i64 0,
i32 3
%3 = load i2, i2* %2, align 1
%4 = icmp eq i2 %3, 0
br i1 %4, label %14, label %5
5: ; preds = %1
tail call void @_Z2g2v()
store i2 -2, i2* %2, align 1
%6 = getelementptr inbounds %_Z1g4coro.Frame, %_Z1g4coro.Frame* %0, i64 0,
i32 4
%7 = load %"struct.coro::promise_type"*, %"struct.coro::promise_type"** %6,
align 8
%8 = getelementptr inbounds %"struct.coro::promise_type",
%"struct.coro::promise_type"* %7, i64 0, i32 0, i32 0, i32 0
%9 = load i8*, i8** %8, align 8
%10 = bitcast i8* %9 to { i8*, i8* }*
%11 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %10, i32 0, i32 0
%12 = load i8*, i8** %11
%13 = bitcast i8* %12 to void (i8*)*
musttail call fastcc void %13(i8* %9) #4
ret void
14: ; preds = %1
tail call void @_Z2g1v()
store i2 1, i2* %2, align 1
%15 = getelementptr inbounds %_Z1g4coro.Frame, %_Z1g4coro.Frame* %0, i64 0,
i32 4
%16 = load %"struct.coro::promise_type"*, %"struct.coro::promise_type"** %15,
align 8
%17 = getelementptr inbounds %"struct.coro::promise_type",
%"struct.coro::promise_type"* %16, i64 0, i32 0, i32 0, i32 0
%18 = load i8*, i8** %17, align 8
%19 = bitcast i8* %18 to { i8*, i8* }*
%20 = getelementptr inbounds { i8*, i8* }, { i8*, i8* }* %19, i32 0, i32 0
%21 = load i8*, i8** %20
%22 = bitcast i8* %21 to void (i8*)*
tail call fastcc void %22(i8* %18) #4
ret void
}
Note that we store -2 and 1 to the state (%2), but we only branch on *%2 != 0,
and the code for the *%2 == -2 case (that calls g3) is gone.
If I add an extra couple of unreachable await points to 'g', the problem goes
away. Perhaps LLVM miscompiles 'switch i2'?</pre>
</div>
</p>
<hr>
<span>You are receiving this mail because:</span>
<ul>
<li>You are on the CC list for the bug.</li>
</ul>
</body>
</html>