https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127746
Bug ID: 127746
Summary: [17 regression] wrong code for wrong code with x !=
INT_MIN ? x + INT_MIN : 0
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: gabravier at gmail dot com
Target Milestone: ---
#define INT_MAX __INT_MAX__
#define INT_MIN (-(INT_MAX) - 1)
__attribute__((noipa)) int
f (int x)
{
return (x != INT_MAX ? x + 1 : INT_MIN) > x;
}
__attribute__((noipa)) int
g (int x)
{
return (x != INT_MIN ? x - 1 : INT_MAX) < x;
}
__attribute__((noipa)) int
h (int x)
{
return (x != INT_MIN ? x + INT_MIN : 0) < 0;
}
int
main ()
{
if (f (INT_MAX) != 0)
__builtin_abort ();
if (g (INT_MIN) != 0)
__builtin_abort ();
if (h (INT_MIN) != 0)
__builtin_abort ();
return 0;
}
The above program should return 0. It aborts, and all three functions are
compiled to `return 1;`. This occurs on both -O0 and -O2, when compiling with
gcc
(Compiler-Explorer-Build-gcc-748199b3435594e1521480258897ea0b39cde910-binutils-2.44)
17.0.0 20261006 (experimental). It correctly returns 0 when compiled with GCC
16.2
The cause appears to be 5e26bbfbdfa875ef464896bb8768669dd5a9ceb9 (for the first
two) and 2b0b9d3c696e500cbbdd5864566c726f3d7e903a (for the third one - although
the pattern is the same one that the first commit later modifies, so there is
only one to fix), from what the transforms seem to do:
/* x != CST1 ? x + CST2 : CST3 -> x + CST2 when CST1 + CST2 == CST3.
This handles both scalars and uniform vectors. */
(for cnd (cond vec_cond)
(simplify
(cnd (ne @0 uniform_integer_cst_p@1)
(plus@2 @0 uniform_integer_cst_p@3)
uniform_integer_cst_p@4)
(with {
tree cst1 = uniform_integer_cst_p (@1);
tree cst2 = uniform_integer_cst_p (@3);
tree cst3 = uniform_integer_cst_p (@4);
}
(if (wi::to_wide (cst1) + wi::to_wide (cst2) == wi::to_wide (cst3))
@2))))
...I believe that this transformation is correct if:
- cst1 + cst2 does not overflow
- the type wraps on overflow
- this is a vector condition (which are documented to always evaluate all
operands, such that the overflow would already have happened such as to make
this transformation correct)
- this is in GIMPLE (for which operands of a COND_EXPR would by construction
already have been evaluated, with the same consequences as for a vector
condition)
so I think wi::add|TYPE_OVERFLOW_WRAPS|compare with VEC_COND_EXPR|GIMPLE fixes
it.
...and in the alternative where it can't do the current transform I assume it
could also presumably emit `(T)((unsigned T) x + (unsigned T) CST2)` which
would be safe, although that might be more work than just the condition bugfix,
I figure :p
(note: I have written no actual such code and thus have not tested this
whatsoever - it is merely what seems to me like it would be correct).