This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
[Bug c/78228] New: fstrict-overflow breaks code without overflow?
- From: "adl at gnu dot org" <gcc-bugzilla at gcc dot gnu dot org>
- To: gcc-bugs at gcc dot gnu dot org
- Date: Sun, 06 Nov 2016 20:47:48 +0000
- Subject: [Bug c/78228] New: fstrict-overflow breaks code without overflow?
- Auto-submitted: auto-generated
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78228
Bug ID: 78228
Summary: fstrict-overflow breaks code without overflow?
Product: gcc
Version: 6.2.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c
Assignee: unassigned at gcc dot gnu.org
Reporter: adl at gnu dot org
Target Milestone: ---
I'm compiling for x86_64.
These two functions used to work fine in gcc-5.4.1, but behave incorrectly with
gcc-6.2 when optimizations are turned on.
The job is to compute the number of characters needed to display a signed
integer when represented in base 10. Assuming int is 32bits, this is most
efficiently done using a series of comparisons (int_width_7) that can even be
vectorized (int_width_8). Positive input values are negated before the
comparisons start; working with negative values instead of positive values is a
way to *avoid* signed overflow when processing INT_MIN.
Because i = -i is the only operation done on int, and it is only applied to
positive values, I don't think there is any undefined behavior in the following
code, isn't it?
#include <stdio.h>
#include <limits.h>
__attribute__((noinline))
unsigned int_width_7(int i)
{
unsigned width = i < 0;
if (i > 0)
i = -i;
if (i <= -1000000000)
return width + 10;
if (i <= -100000000)
return width + 9;
if (i <= -10000000)
return width + 8;
if (i <= -1000000)
return width + 7;
if (i <= -100000)
return width + 6;
if (i <= -10000)
return width + 5;
if (i <= -1000)
return width + 4;
if (i <= -100)
return width + 3;
if (i <= -10)
return width + 2;
return width + 1;
}
__attribute__((noinline))
unsigned int_width_8(int i)
{
unsigned width = i < 0;
if (i > 0)
i = -i;
static const int v[] = { -1000000000, -100000000, -10000000, -1000000,
-100000, -10000, -1000, -100, -10, 0 };
for (int vi = 0; vi < sizeof(v)/sizeof(*v); ++vi)
width += (i <= v[vi]);
return width;
}
int main()
{
printf("%d %d\n", int_width_7(INT_MIN), int_width_8(INT_MIN));
}
% gcc-6 -Wall -O foo.c && ./a.out
11 11
% gcc-6 -Wall -O2 foo.c && ./a.out
2 11
% gcc-6 -Wall -O3 foo.c && ./a.out
2 10
The changes between -O and -O2 seems to be due to -fstrict-overflow:
% gcc -Wall -O -fstrict-overflow foo.c && ./a.out
2 11
Looking into the assembly generated code for int_width_7, we can see that the
following -O output
int_width_7:
.LFB11:
.cfi_startproc
movl %edi, %eax
shrl $31, %eax
movl %edi, %edx
sarl $31, %edx
xorl %edx, %edi
subl %edi, %edx
cmpl $-999999999, %edx
jl .L12
cmpl $-99999999, %edx
jl .L13
cmpl $-9999999, %edx
jl .L14
cmpl $-999999, %edx
jl .L15
cmpl $-99999, %edx
jl .L16
cmpl $-9999, %edx
jl .L17
cmpl $-999, %edx
jl .L18
cmpl $-99, %edx
jl .L19
leal 1(%rax), %ecx
addl $2, %eax
cmpl $-10, %edx
cmovg %ecx, %eax
ret
becomes as follows once compiled with -O -fstrict-overflow:
int_width_7:
.LFB11:
.cfi_startproc
movl %edi, %eax
shrl $31, %eax
movl %edi, %edx
sarl $31, %edx
xorl %edx, %edi
subl %edx, %edi
cmpl $999999999, %edi
jg .L12
cmpl $99999999, %edi
jg .L13
cmpl $9999999, %edi
jg .L14
cmpl $999999, %edi
jg .L15
cmpl $99999, %edi
jg .L16
cmpl $9999, %edi
jg .L17
cmpl $999, %edi
jg .L18
cmpl $99, %edi
jg .L19
leal 2(%rax), %edx
addl $1, %eax
cmpl $10, %edi
cmovge %edx, %eax
ret
Somehow, gcc decided to work with positive integers instead, even though that
cannot work MIN_INT?
I have not looked at the problem with int_width_8 in details: I can see that
the constants are still negative, yet the result is clearly incorrect.