This is the mail archive of the
gcc-help@gcc.gnu.org
mailing list for the GCC project.
Re: GCC optimization bug?
- From: Mason <slash dot tmp at free dot fr>
- To: GCC help <gcc-help at gcc dot gnu dot org>
- Cc: anubhav <anubhav at cse dot iitb dot ac dot in>
- Date: Tue, 17 Jan 2017 11:49:48 +0100
- Subject: Re: GCC optimization bug?
- Authentication-results: sourceware.org; auth=none
- References: <8bd91f0702cba1bb8ea712e572a52c3b@cse.iitb.ac.in>
On 17/01/2017 11:15, anubhav wrote:
> The following code prints different results when run with different
> optimization levels (O0 vs O2).
>
> int main() {
> int i = INT_MAX - 5;
> int j = 0;
> for (; i <= INT_MAX - 2; i += 3) {
> if (i <= 1) {
> j = 1;
> }
> }
> printf("%d",j);
> return 0;
> }
>
> Commands used :
> gcc -O0 -fno-strict-overflow bug.c -o bug # Output is 1
> gcc -O2 -fno-strict-overflow bug.c -o bug # Output is 0
>
> GCC version :
> gcc version 6.2.0 20160901 (Ubuntu 6.2.0-3ubuntu11~16.04)
>
> The expected output is 1 since the value of i overflows once. Is this
> an optimization bug?
Interesting.
To be clear, in standard C, signed integer overflow has undefined behavior.
So we are actually considering the *extension* requested with -fno-strict-overflow.
https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fstrict-overflow-935
> -fstrict-overflow
>
> Allow the compiler to assume strict signed overflow rules, depending
> on the language being compiled. For C (and C++) this means that
> overflow when doing arithmetic with signed numbers is undefined,
> which means that the compiler may assume that it does not happen.
> This permits various optimizations. For example, the compiler assumes
> that an expression like i + 10 > i is always true for signed i. This
> assumption is only valid if signed overflow is undefined, as the
> expression is false if i + 10 overflows when using twos complement
> arithmetic. When this option is in effect any attempt to determine
> whether an operation on signed numbers overflows must be written
> carefully to not actually involve overflow.
>
> This option also allows the compiler to assume strict pointer
> semantics: given a pointer to an object, if adding an offset to that
> pointer does not produce a pointer to the same object, the addition
> is undefined. This permits the compiler to conclude that p + u > p is
> always true for a pointer p and unsigned integer u. This assumption
> is only valid because pointer wraparound is undefined, as the
> expression is false if p + u overflows using twos complement
> arithmetic.
>
> See also the -fwrapv option. Using -fwrapv means that integer signed
> overflow is fully defined: it wraps. When -fwrapv is used, there is
> no difference between -fstrict-overflow and -fno-strict-overflow for
> integers. With -fwrapv certain types of overflow are permitted. For
> example, if the compiler gets an overflow when doing arithmetic on
> constants, the overflowed value can still be used with -fwrapv, but
> not otherwise.
>
> The -fstrict-overflow option is enabled at levels -O2, -O3, -Os.
It seems there is a complex interaction between -fstrict-overflow
and -fwrapv. I'm not sure -fno-strict-overflow guarantees that signed
integer overflow will actually wrap around.
> -fwrapv
>
> This option instructs the compiler to assume that signed arithmetic
> overflow of addition, subtraction and multiplication wraps around
> using twos-complement representation. This flag enables some
> optimizations and disables others. The options -ftrapv and -fwrapv
> override each other, so using -ftrapv -fwrapv on the command-line
> results in -fwrapv being effective. Note that only active options
> override, so using -ftrapv -fwrapv -fno-wrapv on the command-line
> results in -ftrapv being effective.
So the question becomes:
What is the exact semantic of -fno-strict-overflow -fno-wrapv ?
#include <limits.h>
#include <stdio.h>
int main() {
int i = INT_MAX - 5;
int j = 0;
for (; i <= INT_MAX - 2; i += 3) {
if (i <= 1) {
j = 1;
}
}
printf("%d",j);
return 0;
}
translated by gcc-6.3 -O2 -fstrict-overflow -fno-wrapv
.LC0:
.string "%d"
main:
sub rsp, 8
xor esi, esi
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
translated by gcc-6.3 -O2 -fno-strict-overflow -fno-wrapv
main:
sub rsp, 8
mov eax, 2147483642
.L2:
add eax, 3
cmp eax, 2147483645
jle .L2
xor esi, esi
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
=> gcc keeps the loop, but removes the test and just calls printf("%d", 0); always.
I'm not sure what these "xor eax, eax" are doing. They seem wasted.
translated by gcc-6.3 -O2 -fno-strict-overflow -fwrapv
main:
sub rsp, 8
mov eax, 2147483642
xor esi, esi
mov edx, 1
jmp .L2
.L4:
cmp eax, 1
cmovle esi, edx
.L2:
add eax, 3
cmp eax, 2147483645
jle .L4
mov edi, OFFSET FLAT:.LC0
xor eax, eax
call printf
xor eax, eax
add rsp, 8
ret
This is the code you were expecting, I think.
Maybe you just want -fwrapv?
Regards.