This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c/78228] New: fstrict-overflow breaks code without overflow?


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.

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]