https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80617
Bug ID: 80617 Summary: [missed optimization] Storing constant in two possibly-aliased locations Product: gcc Version: 8.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: tree-optimization Assignee: unassigned at gcc dot gnu.org Reporter: herring at lanl dot gov Target Milestone: --- Swapping (by move-construction/assignment) two instances of struct A { // vaguely unique_ptr-like void *p; A(A &&a) : p(a.release()) {} ~A() {if(p) std::free(p);} // not that you can't free(nullptr) void* release() {return std::exchange(p,nullptr);} void swap(A &a) {std::swap(p,a.p);} // Make temporary to guarantee destroying replaced p before returning: A& operator=(A &&a) {A(std::move(a)).swap(*this); return *this;} }; can never result in a call to free (not even free(nullptr)), since all the temporaries being destroyed have been moved-from. However, due presumably to a fear of aliasing (i.e., "A a={0}; std::swap(a,a);"), the optimizer does not remove the conditionals: movq (%rdi), %rbp movq $0, (%rdi) ; store #1 movq (%rsi), %rdx movq $0, (%rsi) ; this store could clobber #1 movq (%rdi), %rax ; but this reads 0 regardless movq %rdx, (%rdi) testq %rax, %rax ; testq 0, 0 je .L2 ; will always be taken ; call free, check the other object, call free again... Thus std::swap<A> is several times as large and slow as the member swap: movq (%rdi), %rax movq (%rsi), %rdx movq %rdx, (%rdi) movq %rax, (%rsi) ret Full example (including a hack to show the assembly from HEAD) at https://wandbox.org/permlink/PZ7d7MwSVAlvrUA2