https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126583

            Bug ID: 126583
           Summary: Wrong combine for signed zeros
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Keywords: wrong-code
          Severity: normal
          Priority: P3
         Component: rtl-optimization
          Assignee: unassigned at gcc dot gnu.org
          Reporter: ktkachov at gcc dot gnu.org
  Target Milestone: ---

/* combine.cc:simplify_if_then_else, lines 6609-6619.

   "Convert a == b ? b : a to a" and its NE twin are guarded by !HONOR_NANS
   only.  The identical transform in simplify-rtx.cc:7656-7676 is guarded by
   !HONOR_NANS && !HONOR_SIGNED_ZEROS.  -ffinite-math-only clears HONOR_NANS
   but does NOT imply -fno-signed-zeros, so combine drops the sign of a zero.

   aarch64-linux, trunk 2f5ed1700.
   Flags: -O1/-O2/-O3/-Os -ffinite-math-only.
   Clean with -fdisable-rtl-combine.  The responsible combination is visible in
   -fdump-rtl-combine-details as

     Trying 31 -> 32:
       31: cc:CCFP=cmp(r102:DF,r103:DF)
       32: r102:DF={(cc:CCFP!=0)?r102:DF:r103:DF}
     Successfully matched this instruction:
     (set (reg/v:DF 102 [ a ]) (reg/v:DF 102 [ a ]))  */

__attribute__((noipa)) double f_eq (double a, double b) { return a == b ? b :
a; }
__attribute__((noipa)) double f_ne (double a, double b) { return a != b ? a :
b; }
__attribute__((noipa)) float  g_eq (float a, float b)   { return a == b ? b :
a; }
__attribute__((noipa)) float  g_ne (float a, float b)   { return a != b ? a :
b; }

/* The same rules reached after the arms have been simplified.  */
__attribute__((noipa)) double f_nest (double a, double b)
{ return a == b ? (a < b ? a : b) : a; }
__attribute__((noipa)) double f_nes2 (double a, double b)
{ return a != b ? a : (a < b ? a : b); }
__attribute__((noipa)) double f_negs (double a, double b)
{ return a != b ? -a : -b; }
__attribute__((noipa)) double f_zero (double a)
{ return a != 0.0 ? a : 0.0; }
__attribute__((noipa)) float  g_zero (float a)
{ return a != 0.0f ? a : 0.0f; }

static int sgn (double x) { return __builtin_signbit (x); }
static int sgnf (float x) { return __builtin_signbitf (x); }

int main (void)
{
  volatile double pz = 0.0, nz = -0.0;
  volatile float  pzf = 0.0f, nzf = -0.0f;
  int bad = 0;

  /* a=+0, b=-0: a==b is true, so the value is b = -0.0.  */
  if (!sgn (f_eq (pz, nz))) bad = 1;
  /* a=-0, b=+0: a==b is true, so the value is b = +0.0.  */
  if ( sgn (f_eq (nz, pz))) bad = 1;
  /* a=+0, b=-0: a!=b is false, so the value is b = -0.0.  */
  if (!sgn (f_ne (pz, nz))) bad = 1;
  if ( sgn (f_ne (nz, pz))) bad = 1;

  if (!sgnf (g_eq (pzf, nzf))) bad = 1;
  if ( sgnf (g_eq (nzf, pzf))) bad = 1;
  if (!sgnf (g_ne (pzf, nzf))) bad = 1;
  if ( sgnf (g_ne (nzf, pzf))) bad = 1;

  /* a==b, so a<b is false and the true arm is b = -0.0.  */
  if (!sgn (f_nest (pz, nz))) bad = 1;
  if ( sgn (f_nest (nz, pz))) bad = 1;
  /* a==b, so the false arm is (a<b ? a : b) = b = -0.0.  */
  if (!sgn (f_nes2 (pz, nz))) bad = 1;
  if ( sgn (f_nes2 (nz, pz))) bad = 1;
  /* a==b, so the value is -b = +0.0.  */
  if ( sgn (f_negs (pz, nz))) bad = 1;
  if (!sgn (f_negs (nz, pz))) bad = 1;

  /* -0.0 != 0.0 is false, so the value is the literal +0.0.  */
  if (sgn (f_zero (nz))) bad = 1;
  if (sgnf (g_zero (nzf))) bad = 1;

  if (bad) __builtin_abort ();
  return 0;
}

Aborts on aarch64 at -O2 -ffinite-math-only and passes at -O0

Reply via email to