<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>