diff --git a/tccgen.c b/tccgen.c index 1c9eb834..dabc897f 100644 --- a/tccgen.c +++ b/tccgen.c @@ -5880,19 +5880,47 @@ ST_FUNC void unary(void) unary(); t = vtop->type.t & VT_BTYPE; if (is_float(t)) { - /* In IEEE negate(x) isn't subtract(0,x), but rather - subtract(-0, x). */ - vpush(&vtop->type); - if (t == VT_FLOAT) - vtop->c.f = -1.0 * 0.0; - else if (t == VT_DOUBLE) - vtop->c.d = -1.0 * 0.0; - else - vtop->c.ld = -1.0 * 0.0; - } else - vpushi(0); - vswap(); - gen_op('-'); + if ((vtop->r & VT_VALMASK) == VT_CONST) { + /* This is what gen_opif would do if we had a NEG operation. */ + if (t == VT_FLOAT) + vtop->c.f = -vtop->c.f; + else if (t == VT_DOUBLE) + vtop->c.d = -vtop->c.d; + else + vtop->c.ld = -vtop->c.ld; + } else { + /* In IEEE negate(x) isn't subtract(0,x). Without NaNs it's + subtract(-0, x), but with them it's really a sign flip + operation. We implement this with bit manipulation and have + to do some type reinterpretation for this, which TCC can do + only via memory. */ + int align, size = type_size(&vtop->type, &align); + save_reg(gv(RC_TYPE(t))); + vdup(); + gaddrof(); + vtop->type = char_pointer_type; + /* Byte of sign bit. For big endian, this would have to + add zero always. */ +#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_I386) + /* sizeof long double is 12 or 16 here, but it's + really the 80bit extended float format. */ + if (t == VT_LDOUBLE) + size = 10; +#endif + vpushi(size - 1); + gen_op('+'); + indir(); + vdup(); + vpushi(0x80); /* flip sign */ + gen_op('^'); + vstore(); + vpop(); + } + } else { + vpushi(0); + vswap(); + gen_op('-'); + } break; case TOK_LAND: if (!gnu_ext) diff --git a/tests/tcctest.c b/tests/tcctest.c index d0ebe3c5..6c6753f7 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2227,6 +2227,12 @@ void prefix ## signed_zeros(void) \ else\ printf ("x != -y; this is wrong!\n");\ }\ +void prefix ## nan(void)\ +{\ + type nan = 0.0/0.0;\ + type nnan = -nan; \ + printf("nantest: " fmt " " fmt "\n", nan, nnan);\ +}\ void prefix ## test(void)\ {\ printf("testing '%s'\n", #typename);\ @@ -2237,6 +2243,7 @@ void prefix ## test(void)\ prefix ## fcast(-2334.6);\ prefix ## call();\ prefix ## signed_zeros();\ + prefix ## nan();\ } FTEST(f, float, float, "%f")