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

            Bug ID: 77779
           Summary: unnecessary trap checks for pointer subtraction with
                    -ftrapv
           Product: gcc
           Version: 5.4.1
            Status: UNCONFIRMED
          Severity: minor
          Priority: P3
         Component: middle-end
          Assignee: unassigned at gcc dot gnu.org
          Reporter: jfc at mit dot edu
  Target Milestone: ---

Consider this code:

long diff(long *a, long *b) { return (a - b) + (a - b); }

Compiled with gcc -O2 -ftrapv on x86-64 the resulting code is

diff:
.LFB0:
        .cfi_startproc
        subq    $8, %rsp
        .cfi_def_cfa_offset 16
        call    __subvdi3
        sarq    $3, %rax
        movq    %rax, %rdi
        movq    %rax, %rsi
        call    __addvdi3
        addq    $8, %rsp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE0:
        .size   diff, .-diff


There are two problems here -- not bugs, but suboptimal code.

1. Pointer subtraction of sizes larger than 2 bytes should not generate a
trapping subtract.  In the usual case where pointers and ptrdiff_t are the same
size, the result will fit in a ptrdiff_t.

2. The addition can not overflow.  In general, x/A + y/B with A and B both
greater than 2 will not overflow.  (This might not be worth fixing, but I think
the previous problem is.)

I contrived this C example after seeing the first problem in C++.  The real
code is a call to

   std::vector<T>::size()

with type T 4 bytes or larger.  size() can't overflow, but g++ inserts a call
to __subvdi3 anyway.  On Linux+ELF this results in a dynamic linker operation.

This is in 5.2 and 5.4.1 20160926.  I have not checked gcc 6+.

Reply via email to