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

Reply via email to