<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 - [coroutines] Compiler is incorrectly calculating the address of a parameter reference passed to promise_type::operator new()"
href="https://bugs.llvm.org/show_bug.cgi?id=38313">38313</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>[coroutines] Compiler is incorrectly calculating the address of a parameter reference passed to promise_type::operator new()
</td>
</tr>
<tr>
<th>Product</th>
<td>clang
</td>
</tr>
<tr>
<th>Version</th>
<td>trunk
</td>
</tr>
<tr>
<th>Hardware</th>
<td>PC
</td>
</tr>
<tr>
<th>OS</th>
<td>All
</td>
</tr>
<tr>
<th>Status</th>
<td>NEW
</td>
</tr>
<tr>
<th>Severity</th>
<td>enhancement
</td>
</tr>
<tr>
<th>Priority</th>
<td>P
</td>
</tr>
<tr>
<th>Component</th>
<td>LLVM Codegen
</td>
</tr>
<tr>
<th>Assignee</th>
<td>unassignedclangbugs@nondot.org
</td>
</tr>
<tr>
<th>Reporter</th>
<td>lewissbaker@gmail.com
</td>
</tr>
<tr>
<th>CC</th>
<td>llvm-bugs@lists.llvm.org
</td>
</tr></table>
<p>
<div>
<pre>Using build of clang from trunk (rev 336842).
- libc++ rev 336382 with diff from D46140 applied
- llvm rev 336841
A test-case added in <a href="https://reviews.llvm.org/D46140">https://reviews.llvm.org/D46140</a> that checks that I can
pass a `std::experimental::pmr::polymorphic_allocator` into a coroutine and
have it use that allocator to allocate the coroutine frame is failing with an
access-violation under debug builds.
Run: `lit -v --param=compile_flags='-O0'
libcxx/test/std/experimental/task/task.basic/task_custom_allocator.pass.cpp`
Test passes if I change `-O0` to `-O2`.
The (simplified) client code from `task_custom_allocator.pass.cpp` looks
something like this:
```c++
template<typename Allocator>
coro::task<void> f(std::allocator_arg_t, [[maybe_unused]] Allocator alloc)
{
co_return;
}
void test_mixed_custom_allocator_type_erasure()
{
std::vector<coro::task<>> tasks;
tasks.push_back(f(
std::allocator_arg,
std::experimental::pmr::polymorphic_allocator<char>{
std::experimental::pmr::new_delete_resource() }));
for (auto& t : tasks)
{
coro::sync_wait(t);
}
}
```
The implementation of the `task<T>::promise_type` has an overload of `operator
new` that looks like this:
```c++
template<typename _Alloc, typename... _Args>
static operator new(std::allocator_arg_t, _Alloc& __allocator, _Args&...)
{
using _CharAlloc =
typename allocator_traits<_Alloc>::template rebind_alloc<char>;
_CharAlloc __charAllocator{__allocator};
void* __pointer = __charAllocator.allocate(
__get_padded_frame_size_with_allocator<_CharAlloc>(__size));
...
}
```
The compiler is required to generate code that passes lvalue references to
parameters passed to the coroutine function into the `operator new()` call.
What I'm seeing is that the code generated under debug builds (`-fcoroutine-ts
-O0`) is incorrectly calculating the address of the `alloc` parameter of `f()`
when passing it into the call to `operator new()`.
The assembly for the start of the coroutine function, `f()`, looks like:
```
task_custom_allocator.pass.cpp.exe`f<std::experimental::fundamentals_v1::pmr::polymorphic_allocator<char>
<span class="quote">>:</span >
-> 0x100004170 <+0>: pushq %rbp
0x100004171 <+1>: movq %rsp, %rbp
0x100004174 <+4>: subq $0x110, %rsp ; imm = 0x110
0x10000417b <+11>: movq %rdi, %rax
0x10000417e <+14>: xorl %ecx, %ecx
0x100004180 <+16>: movl %ecx, %edx
0x100004182 <+18>: movb $0x1, %r8b
0x100004185 <+21>: testb $0x1, %r8b
0x100004189 <+25>: movq %rax, -0x70(%rbp)
0x10000418d <+29>: movq %rsi, -0x78(%rbp)
0x100004191 <+33>: movq %rdi, -0x80(%rbp)
0x100004195 <+37>: movq %rdx, -0x88(%rbp)
0x10000419c <+44>: jne 0x1000041b5 ; <+69> at
task_custom_allocator.pass.cpp
0x1000041a2 <+50>: movq -0x88(%rbp), %rax
0x1000041a9 <+57>: movq %rax, -0x90(%rbp)
0x1000041b0 <+64>: jmp 0x1000041da ; <+106> at
task_custom_allocator.pass.cpp:99
0x1000041b5 <+69>: movl $0xf0, %eax
0x1000041ba <+74>: movl %eax, %edi
0x1000041bc <+76>: leaq -0x40(%rbp), %rsi
0x1000041c0 <+80>: callq 0x10001012a ; symbol stub for:
void* std::experimental::coroutines_v1::__task_promise_base::operator
new<std::experimental::fundamentals_v1::pmr::polymorphic_allocator<char>
<span class="quote">>(unsigned long, std::__1::allocator_arg_t,</span >
std::experimental::fundamentals_v1::pmr::polymorphic_allocator<char>&)
0x1000041c5 <+85>: movq %rax, -0x98(%rbp)
<snip>
```
Here, the calling convention has the polymorphic allocator being passed in
register `rsi` which then is stored at the location `-0x78(%rbp)`.
Then, when it generates the call to `operator new()` it passes in the address
of the `alloc` parameter as `-0x40(%ebp)`. The contents at this address is the
null pointer and so when the `operator new` implementation subsequently
attempts to call `.allocate()` it hits an access violation dereferencing the
pointer.</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>