https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121789

            Bug ID: 121789
           Summary: std:uninitialized_move_n() and friends don't optimize
                    to memcpy
           Product: gcc
           Version: 15.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: avi at scylladb dot com
  Target Milestone: ---

Consider

```
#include <memory>

struct a { int x; };

void f(const a* a1, a* a2, size_t n) {
    std::uninitialized_copy_n(a1, n, a2);
}

void g(const a* a1, a* a2, size_t n) {
    std::uninitialized_move_n(a1, n, a2);
}
```

f() and g() are practically identical, since a is trivially-everything.

Yet f() compiles to memcpy(), and g() does not:

```
f(a const*, a*, unsigned long):
        salq    $2, %rdx
        movq    %rdi, %rax
        movq    %rsi, %rdi
        testq   %rdx, %rdx
        jle     .L1
        movq    %rax, %rsi
        jmp     memcpy
.L1:
        ret
g(a const*, a*, unsigned long):
        salq    $2, %rdx
        je      .L4
        xorl    %eax, %eax
.L6:
        movl    (%rdi,%rax), %ecx
        movl    %ecx, (%rsi,%rax)
        addq    $4, %rax
        cmpq    %rax, %rdx
        jne     .L6
.L4:
        ret
```

Likely the memcpyable detection web gets confused by the move_iterator attached
by std::uninitialized_move_n().

godbolt: https://godbolt.org/z/sYfEK3cz3

(btw: the testq/jle sequence in f() can be replaced with jne, since the flags
aren't touched and having a negative memory size is undefined behavior; I'm
also not sure we aren't better off delegating the check to memcpy itself)

Reply via email to