[llvm-bugs] [Bug 38313] New: [coroutines] Compiler is incorrectly calculating the address of a parameter reference passed to promise_type::operator new()

via llvm-bugs llvm-bugs at lists.llvm.org
Wed Jul 25 13:28:03 PDT 2018


https://bugs.llvm.org/show_bug.cgi?id=38313

            Bug ID: 38313
           Summary: [coroutines] Compiler is incorrectly calculating the
                    address of a parameter reference passed to
                    promise_type::operator new()
           Product: clang
           Version: trunk
          Hardware: PC
                OS: All
            Status: NEW
          Severity: enhancement
          Priority: P
         Component: LLVM Codegen
          Assignee: unassignedclangbugs at nondot.org
          Reporter: lewissbaker at gmail.com
                CC: llvm-bugs at lists.llvm.org

Using build of clang from trunk (rev 336842).
- libc++ rev 336382 with diff from D46140 applied
- llvm rev 336841

A test-case added in https://reviews.llvm.org/D46140 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>
>:
->  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>
>(unsigned long, std::__1::allocator_arg_t,
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.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20180725/89adb2cb/attachment.html>


More information about the llvm-bugs mailing list