<html>
    <head>
      <base href="https://llvm.org/bugs/" />
    </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 --- - clang emits memcpy for std::swap, which can introduce undefined behavior"
   href="https://llvm.org/bugs/show_bug.cgi?id=27498">27498</a>
          </td>
        </tr>

        <tr>
          <th>Summary</th>
          <td>clang emits memcpy for std::swap, which can introduce undefined behavior
          </td>
        </tr>

        <tr>
          <th>Product</th>
          <td>clang
          </td>
        </tr>

        <tr>
          <th>Version</th>
          <td>3.8
          </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>LLVM Codegen
          </td>
        </tr>

        <tr>
          <th>Assignee</th>
          <td>unassignedclangbugs@nondot.org
          </td>
        </tr>

        <tr>
          <th>Reporter</th>
          <td>whitequark@whitequark.org
          </td>
        </tr>

        <tr>
          <th>CC</th>
          <td>llvm-bugs@lists.llvm.org
          </td>
        </tr>

        <tr>
          <th>Classification</th>
          <td>Unclassified
          </td>
        </tr></table>
      <p>
        <div>
        <pre>When clang is generating code for std::swap<T> where T is a POD aggregate, it
can lower the moves to llvm.memcpy intrinsic invocations. The code generated
for std::swap amounts to tmp=a;a=b;b=tmp, which in case of a=b would become
tmp=a;a=a;a=tmp. If a=a is lowered to memcpy, then the source and destination
overlap, which is undefined behavior.

According to n4567 §17.6.3.2, "An rvalue or lvalue t is swappable if and only
if t is swappable with any rvalue or lvalue, respectively, of type T", and in
§20.2.2, we also see that swap "Requires: Type T shall be MoveConstructible
(Table 20) and MoveAssignable (Table 22)." (thanks Ralith!) So values ought to
be swappable with themselves.

Testcase:

#include <algorithm>

int main() {
  struct s { char c[40]; } t = {};
  std::swap(t, t);
  return 0;
}

Build and run as: clang++-3.8 -std=c++11 test.cpp -o test && valgrind ./test

This results in the following error:

Source and destination overlap in memcpy(0xffefffa10, 0xffefffa10, 40)
   at 0x4C2E593: memcpy@@GLIBC_2.14 (in
/usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
   by 0x4006ED: void std::swap<main::s>(main::s&, main::s&) (in
/home/whitequark/t/test)
   by 0x400684: main (in /home/whitequark/t/test)

The generated IR contains the following (presented at -O1 to make it cleaner,
but the idea is the same at -O0):

define internal fastcc void @_ZSt4swapIZ4mainE1sEvRT_S2_(%struct.s*
dereferenceable(40) %__a, %struct.s* dereferenceable(40) %__b) unnamed_addr #0
{
  %__tmp = alloca %struct.s, align 1
  %1 = getelementptr inbounds %struct.s, %struct.s* %__tmp, i64 0, i32 0, i64 0
  call void @llvm.lifetime.start(i64 40, i8* %1) #3
  %2 = tail call fastcc dereferenceable(40) %struct.s*
@_ZSt4moveIRZ4mainE1sEONSt16remove_referenceIT_E4typeEOS3_(%struct.s* nonnull
dereferenceable(40) %__a) #3
  %3 = getelementptr inbounds %struct.s, %struct.s* %2, i64 0, i32 0, i64 0
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %1, i8* %3, i64 40, i32 1, i1
false), !tbaa.struct !1
  %4 = tail call fastcc dereferenceable(40) %struct.s*
@_ZSt4moveIRZ4mainE1sEONSt16remove_referenceIT_E4typeEOS3_(%struct.s* nonnull
dereferenceable(40) %__b) #3
  %5 = getelementptr inbounds %struct.s, %struct.s* %__a, i64 0, i32 0, i64 0
  %6 = getelementptr inbounds %struct.s, %struct.s* %4, i64 0, i32 0, i64 0
  tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* %5, i8* %6, i64 40, i32 1, i1
false), !tbaa.struct !1
  %7 = call fastcc dereferenceable(40) %struct.s*
@_ZSt4moveIRZ4mainE1sEONSt16remove_referenceIT_E4typeEOS3_(%struct.s* nonnull
dereferenceable(40) %__tmp) #3
  %8 = getelementptr inbounds %struct.s, %struct.s* %__b, i64 0, i32 0, i64 0
  %9 = getelementptr inbounds %struct.s, %struct.s* %7, i64 0, i32 0, i64 0
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %8, i8* %9, i64 40, i32 1, i1
false), !tbaa.struct !1
  call void @llvm.lifetime.end(i64 40, i8* %1) #3
  ret void
}</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>