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 | unary(); | 5880 | unary(); |
| 5881 | t = vtop->type.t & VT_BTYPE; | 5881 | t = vtop->type.t & VT_BTYPE; |
| 5882 | if (is_float(t)) { | 5882 | if (is_float(t)) { |
| 5883 | /* In IEEE negate(x) isn't subtract(0,x), but rather | 5883 | if ((vtop->r & VT_VALMASK) == VT_CONST) { |
| 5884 | subtract(-0, x). */ | 5884 | /* This is what gen_opif would do if we had a NEG operation. */ |
| 5885 | vpush(&vtop->type); | 5885 | if (t == VT_FLOAT) |
| 5886 | if (t == VT_FLOAT) | 5886 | vtop->c.f = -vtop->c.f; |
| 5887 | vtop->c.f = -1.0 * 0.0; | 5887 | else if (t == VT_DOUBLE) |
| 5888 | else if (t == VT_DOUBLE) | 5888 | vtop->c.d = -vtop->c.d; |
| 5889 | vtop->c.d = -1.0 * 0.0; | 5889 | else |
| 5890 | else | 5890 | vtop->c.ld = -vtop->c.ld; |
| 5891 | vtop->c.ld = -1.0 * 0.0; | 5891 | } else { |
| 5892 | } else | 5892 | /* In IEEE negate(x) isn't subtract(0,x). Without NaNs it's |
| 5893 | vpushi(0); | 5893 | subtract(-0, x), but with them it's really a sign flip |
| 5894 | vswap(); | 5894 | operation. We implement this with bit manipulation and have |
| 5895 | gen_op('-'); | 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 | break; | 5924 | break; |
| 5897 | case TOK_LAND: | 5925 | case TOK_LAND: |
| 5898 | if (!gnu_ext) | 5926 | if (!gnu_ext) |
| @@ -2227,6 +2227,12 @@ void prefix ## signed_zeros(void) \ · 2226 unmodified lines | |||
| 2227 | else\ | 2227 | else\ |
| 2228 | printf ("x != -y; this is wrong!\n");\ | 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 | void prefix ## test(void)\ | 2236 | void prefix ## test(void)\ |
| 2231 | {\ | 2237 | {\ |
| 2232 | printf("testing '%s'\n", #typename);\ | 2238 | printf("testing '%s'\n", #typename);\ |
| @@ -2237,6 +2243,7 @@ void prefix ## test(void)\ · 4 unmodified lines | |||
| 2237 | prefix ## fcast(-2334.6);\ | 2243 | prefix ## fcast(-2334.6);\ |
| 2238 | prefix ## call();\ | 2244 | prefix ## call();\ |
| 2239 | prefix ## signed_zeros();\ | 2245 | prefix ## signed_zeros();\ |
| 2246 | prefix ## nan();\ | ||
| 2240 | } | 2247 | } |
| 2241 | 2248 | ||
| 2242 | FTEST(f, float, float, "%f") | 2249 | FTEST(f, float, float, "%f") |