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+.