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