Using "-0.0 - x" still isn't enough if x is a NaN, so bite the bullet and implement sign-bit flipping via memory. (It's usually not too bad, the -0.0-x method also uses memory for the floating point constant). This way is at least shorter than implementing a new top-level operation for all backends (a negate) that would be unary.
Unified · Split
| @@ -5880,19 +5880,47 @@ ST_FUNC void unary(void) · 5879 unmodified lines | ||
| 5880 | 5880 | unary(); |
| 5881 | 5881 | t = vtop->type.t & VT_BTYPE; |
| 5882 | 5882 | if (is_float(t)) { |
| 5883 | − /* In IEEE negate(x) isn't subtract(0,x), but rather | |
| 5884 | − subtract(-0, x). */ | |
| 5885 | − vpush(&vtop->type); | |
| 5886 | − if (t == VT_FLOAT) | |
| 5887 | − vtop->c.f = -1.0 * 0.0; | |
| 5888 | − else if (t == VT_DOUBLE) | |
| 5889 | − vtop->c.d = -1.0 * 0.0; | |
| 5890 | − else | |
| 5891 | − vtop->c.ld = -1.0 * 0.0; | |
| 5892 | − } else | |
| 5893 | − vpushi(0); | |
| 5894 | − vswap(); | |
| 5895 | − gen_op('-'); | |
| 5883 | + if ((vtop->r & VT_VALMASK) == VT_CONST) { | |
| 5884 | + /* This is what gen_opif would do if we had a NEG operation. */ | |
| 5885 | + if (t == VT_FLOAT) | |
| 5886 | + vtop->c.f = -vtop->c.f; | |
| 5887 | + else if (t == VT_DOUBLE) | |
| 5888 | + vtop->c.d = -vtop->c.d; | |
| 5889 | + else | |
| 5890 | + vtop->c.ld = -vtop->c.ld; | |
| 5891 | + } else { | |
| 5892 | + /* In IEEE negate(x) isn't subtract(0,x). Without NaNs it's | |
| 5893 | + subtract(-0, x), but with them it's really a sign flip | |
| 5894 | + operation. We implement this with bit manipulation and have | |
| 5895 | + to do some type reinterpretation for this, which TCC can do | |
| 5896 | + only via memory. */ | |
| 5897 | + int align, size = type_size(&vtop->type, &align); | |
| 5898 | + save_reg(gv(RC_TYPE(t))); | |
| 5899 | + vdup(); | |
| 5900 | + gaddrof(); | |
| 5901 | + vtop->type = char_pointer_type; | |
| 5902 | + /* Byte of sign bit. For big endian, this would have to | |
| 5903 | + add zero always. */ | |
| 5904 | +#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_I386) | |
| 5905 | + /* sizeof long double is 12 or 16 here, but it's | |
| 5906 | + really the 80bit extended float format. */ | |
| 5907 | + if (t == VT_LDOUBLE) | |
| 5908 | + size = 10; | |
| 5909 | +#endif | |
| 5910 | + vpushi(size - 1); | |
| 5911 | + gen_op('+'); | |
| 5912 | + indir(); | |
| 5913 | + vdup(); | |
| 5914 | + vpushi(0x80); /* flip sign */ | |
| 5915 | + gen_op('^'); | |
| 5916 | + vstore(); | |
| 5917 | + vpop(); | |
| 5918 | + } | |
| 5919 | + } else { | |
| 5920 | + vpushi(0); | |
| 5921 | + vswap(); | |
| 5922 | + gen_op('-'); | |
| 5923 | + } | |
| 5896 | 5924 | break; |
| 5897 | 5925 | case TOK_LAND: |
| 5898 | 5926 | if (!gnu_ext) |
| @@ -2227,6 +2227,12 @@ void prefix ## signed_zeros(void) \ · 2226 unmodified lines | ||
| 2227 | 2227 | else\ |
| 2228 | 2228 | printf ("x != -y; this is wrong!\n");\ |
| 2229 | 2229 | }\ |
| 2230 | +void prefix ## nan(void)\ | |
| 2231 | +{\ | |
| 2232 | + type nan = 0.0/0.0;\ | |
| 2233 | + type nnan = -nan; \ | |
| 2234 | + printf("nantest: " fmt " " fmt "\n", nan, nnan);\ | |
| 2235 | +}\ | |
| 2230 | 2236 | void prefix ## test(void)\ |
| 2231 | 2237 | {\ |
| 2232 | 2238 | printf("testing '%s'\n", #typename);\ |
| @@ -2237,6 +2243,7 @@ void prefix ## test(void)\ · 4 unmodified lines | ||
| 2237 | 2243 | prefix ## fcast(-2334.6);\ |
| 2238 | 2244 | prefix ## call();\ |
| 2239 | 2245 | prefix ## signed_zeros();\ |
| 2246 | + prefix ## nan();\ | |
| 2240 | 2247 | } |
| 2241 | 2248 | |
| 2242 | 2249 | FTEST(f, float, float, "%f") |