https://llvm.org/bugs/show_bug.cgi?id=27498
Bug ID: 27498 Summary: clang emits memcpy for std::swap, which can introduce undefined behavior Product: clang Version: 3.8 Hardware: PC OS: Linux Status: NEW Severity: normal Priority: P Component: LLVM Codegen Assignee: unassignedclangb...@nondot.org Reporter: whitequ...@whitequark.org CC: llvm-bugs@lists.llvm.org Classification: Unclassified 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 } -- You are receiving this mail because: You are on the CC list for the bug.
_______________________________________________ llvm-bugs mailing list llvm-bugs@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs