diff --git a/arm-gen.c b/arm-gen.c index 51a4b7d6..b6a25d2d 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -124,6 +124,10 @@ enum { #define LDOUBLE_ALIGN 4 #endif +#if LDOUBLE_SIZE == 8 +# define TCC_USING_DOUBLE_FOR_LDOUBLE 1 +#endif + /* maximum alignment (for aligned attribute support) */ #define MAX_ALIGN 8 @@ -635,12 +639,9 @@ void load(int r, SValue *sv) op=0xED100100; if(!sign) op|=0x800000; -#if LDOUBLE_SIZE == 8 - if ((ft & VT_BTYPE) != VT_FLOAT) - op|=0x8000; -#else if ((ft & VT_BTYPE) == VT_DOUBLE) op|=0x8000; +#if LDOUBLE_SIZE != 8 else if ((ft & VT_BTYPE) == VT_LDOUBLE) op|=0x400000; #endif @@ -760,13 +761,10 @@ void store(int r, SValue *sv) op=0xED000100; if(!sign) op|=0x800000; -#if LDOUBLE_SIZE == 8 - if ((ft & VT_BTYPE) != VT_FLOAT) - op|=0x8000; -#else if ((ft & VT_BTYPE) == VT_DOUBLE) op|=0x8000; - if ((ft & VT_BTYPE) == VT_LDOUBLE) +#if LDOUBLE_SIZE != 8 + else if ((ft & VT_BTYPE) == VT_LDOUBLE) op|=0x400000; #endif o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); @@ -904,15 +902,6 @@ static void gen_bounds_epilog(void) } #endif -static int unalias_ldbl(int btype) -{ -#if LDOUBLE_SIZE == 8 - if (btype == VT_LDOUBLE) - btype = VT_DOUBLE; -#endif - return btype; -} - /* Return whether a structure is an homogeneous float aggregate or not. The answer is true if all the elements of the structure are of the same primitive float type and there is less than 4 elements. @@ -926,9 +915,10 @@ static int is_hgen_float_aggr(CType *type) ref = type->ref->next; if (ref) { - btype = unalias_ldbl(ref->type.t & VT_BTYPE); + btype = ref->type.t & VT_BTYPE; if (btype == VT_FLOAT || btype == VT_DOUBLE) { - for(; ref && btype == unalias_ldbl(ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++); + for(; ref && btype == (ref->type.t & VT_BTYPE); ref = ref->next, nb_fields++) + ; return !ref && nb_fields <= 4; } } @@ -1254,7 +1244,6 @@ again: size = 8; else size = LDOUBLE_SIZE; - if (size == 12) r |= 0x400000; else if(size == 8) @@ -1946,15 +1935,13 @@ void gen_opf(int op) vswap(); c2 = is_fconst(); x=0xEE000100; -#if LDOUBLE_SIZE == 8 - if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) - x|=0x80; -#else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) x|=0x80; +#if LDOUBLE_SIZE != 8 else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) x|=0x80000; #endif + switch(op) { case '+': @@ -2190,6 +2177,12 @@ ST_FUNC void gen_cvt_itof(int t) func=TOK___floatundisf; else func=TOK___floatdisf; + } else if((t & VT_BTYPE) == VT_DOUBLE) { + func_type = &func_double_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundidf; + else + func=TOK___floatdidf; #if LDOUBLE_SIZE != 8 } else if((t & VT_BTYPE) == VT_LDOUBLE) { func_type = &func_ldouble_type; @@ -2197,15 +2190,7 @@ ST_FUNC void gen_cvt_itof(int t) func=TOK___floatundixf; else func=TOK___floatdixf; - } else if((t & VT_BTYPE) == VT_DOUBLE) { -#else - } else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) { #endif - func_type = &func_double_type; - if(vtop->type.t & VT_UNSIGNED) - func=TOK___floatundidf; - else - func=TOK___floatdidf; } if(func_type) { vpushsym(func_type, external_helper_sym(func)); @@ -2239,14 +2224,12 @@ void gen_cvt_ftoi(int t) if(u) { if(r2 == VT_FLOAT) func=TOK___fixunssfsi; + else if(r2 == VT_DOUBLE) + func=TOK___fixunsdfsi; #if LDOUBLE_SIZE != 8 else if(r2 == VT_LDOUBLE) func=TOK___fixunsxfsi; - else if(r2 == VT_DOUBLE) -#else - else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) #endif - func=TOK___fixunsdfsi; } else { r=fpr(gv(RC_FLOAT)); r2=intr(vtop->r=get_reg(RC_INT)); @@ -2257,14 +2240,12 @@ void gen_cvt_ftoi(int t) } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 if(r2 == VT_FLOAT) func=TOK___fixsfdi; + else if(r2 == VT_DOUBLE) + func=TOK___fixdfdi; #if LDOUBLE_SIZE != 8 else if(r2 == VT_LDOUBLE) func=TOK___fixxfdi; - else if(r2 == VT_DOUBLE) -#else - else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) #endif - func=TOK___fixdfdi; } if(func) { vpush_helper_func(func); diff --git a/tccgen.c b/tccgen.c index c5118dd7..656d7a5f 100644 --- a/tccgen.c +++ b/tccgen.c @@ -323,12 +323,6 @@ ST_FUNC int ieee_finite(double d) return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; } -/* compiling intel long double natively */ -#if (defined __i386__ || defined __x86_64__) \ - && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) -# define TCC_IS_NATIVE_387 -#endif - ST_FUNC void test_lvalue(void) { if (!(vtop->r & VT_LVAL)) @@ -2524,9 +2518,7 @@ void gen_negf(int op) 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, bt; - size = type_size(&vtop->type, &align); bt = vtop->type.t & VT_BTYPE; #if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386 @@ -3295,12 +3287,6 @@ error: } c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; -#if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387 - /* don't try to convert to ldouble when cross-compiling - (except when it's '0' which is needed for arm:gen_negf()) */ - if (dbt_bt == VT_LDOUBLE && !nocode_wanted && (sf || vtop->c.i != 0)) - c = 0; -#endif if (c) { /* constant case: we can do it now */ /* XXX: in ISOC, cannot do it if error in convert */ @@ -5646,6 +5632,7 @@ ST_FUNC void unary(void) case TOK_CLDOUBLE: #ifdef TCC_USING_DOUBLE_FOR_LDOUBLE t = VT_DOUBLE | VT_LONG; + tokc.d = tokc.ld; #else t = VT_LDOUBLE; #endif @@ -7781,6 +7768,71 @@ static int decl_designator(init_params *p, CType *type, unsigned long c, return al; } +static void write_ldouble(unsigned char *d, void *s) +{ + //printf("long double %Lf\n", *(long double*)s); +#ifdef TCC_CROSS_TEST + if (LDOUBLE_SIZE >= 10) { + double b = *(long double*)s; + s = &b; +#else + if (sizeof (long double) == 8 && LDOUBLE_SIZE >= 10) { +#endif + /* our 'long double' is a double really (_WIN32, __APPLE__) */ + uint64_t m = *(uint64_t*)s; + int e = m >> 48; + int f = e >> 4 & 0x7FF; + m <<= 11; + if (0 == f) { + if (0 == m) + goto set; + for (f = 1; !(m & 1ULL<<63); --f) + m <<= 1; + } + if (f == 0x7ff) + f = 0x43FF; + e = (e & 0x8000) | (f + 0x3C00); + m |= 1ULL<<63; + set: + #if (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) + /* double -> extended */ + write64le(d, m); + write16le(d+8, e); + #elif LDOUBLE_SIZE == 16 + /* double -> quad */ + write64le(d+6, m << 1); + write16le(d+14, e); + #endif + ; + } else { + #if LDOUBLE_SIZE == 8 + /* long double -> double */ + double b = *(long double*)s; + memcpy(d, &b, 8); + #elif (__i386__ || __x86_64__) && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) + /* extended -> extended */ + memcpy(d, s, 10); + #elif (__i386__ || __x86_64__) && (defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64) + /* extended -> quad */ + uint64_t m = *(uint64_t*)s; + int e = *(uint16_t*)((char*)s + 8); + write64le(d+6, m << 1); + write16le(d+14, e); + #elif (__aarch64__ || __riscv) && (defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64) + /* quad -> extended */ + uint64_t m = read64le((char*)s + 6); + int e = read16le((char*)s + 14); + (e & 0x7fff) && (m & 1) && 0 == ++m && ++e; + write64le(d, m >> 1 | ((e & 0x7fff) ? 1ULL<<63 : 0)); + write16le(d+8, e); + #else + /* unknown */ + if (sizeof (long double) == LDOUBLE_SIZE) + memcpy(d, s, LDOUBLE_SIZE); + #endif + } +} + /* store a value or an expression directly in global data or in local array */ static void init_putv(init_params *p, CType *type, unsigned long c) { @@ -7893,34 +7945,7 @@ static void init_putv(init_params *p, CType *type, unsigned long c) write64le(ptr, val); break; case VT_LDOUBLE: -#if defined TCC_IS_NATIVE_387 - /* Host and target platform may be different but both have x87. - On windows, tcc does not use VT_LDOUBLE, except when it is a - cross compiler. In this case a mingw gcc as host compiler - comes here with 10-byte long doubles, while msvc or tcc won't. - tcc itself can still translate by asm. - In any case we avoid possibly random bytes 11 and 12. - */ - if (sizeof (long double) >= 10) - memcpy(ptr, &vtop->c.ld, 10); -#ifdef __TINYC__ - else if (sizeof (long double) == sizeof (double)) - __asm__("fldl %1\nfstpt %0\n" : "=m" (*ptr) : "m" (vtop->c.ld)); -#endif - else -#endif - /* For other platforms it should work natively, but may not work - for cross compilers */ - if (sizeof(long double) == LDOUBLE_SIZE) - memcpy(ptr, &vtop->c.ld, LDOUBLE_SIZE); - else if (sizeof(double) == LDOUBLE_SIZE) - *(double*)ptr = (double)vtop->c.ld; - else if (0 == memcmp(ptr, &vtop->c.ld, LDOUBLE_SIZE)) - ; /* nothing to do for 0.0 */ -#ifndef TCC_CROSS_TEST - else - tcc_error("can't cross compile long double constants"); -#endif + write_ldouble(ptr, &vtop->c.ld); break; #if PTR_SIZE == 8 diff --git a/tccpp.c b/tccpp.c index 21bfc0e3..214e9cd4 100644 --- a/tccpp.c +++ b/tccpp.c @@ -2212,13 +2212,8 @@ static void parse_string(const char *s, int len) } } -#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE -/* we use 64 bit (52 needed) numbers */ -#define BN_SIZE 2 -#else /* we use 128 bit (64/112 needed) numbers */ #define BN_SIZE 4 -#endif /* bn = (bn << shift) | or_val */ static int bn_lshift(unsigned int *bn, int shift, int or_val) @@ -2250,11 +2245,7 @@ static void parse_number(const char *p) int b, t, shift, frac_bits, s, exp_val, ch; char *q; unsigned int bn[BN_SIZE]; -#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE - double d; -#else long double d; -#endif /* number */ q = token_buf; @@ -2366,19 +2357,14 @@ static void parse_number(const char *p) ch = *p++; } exp_val = exp_val * s; - + /* now we can generate the number */ /* XXX: should patch directly float number */ -#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE - d = (double)bn[1] * 4294967296.0 + (double)bn[0]; - d = ldexp(d, exp_val - frac_bits); -#else d = (long double)bn[3] * 79228162514264337593543950336.0L + (long double)bn[2] * 18446744073709551616.0L + (long double)bn[1] * 4294967296.0L + (long double)bn[0]; d = ldexpl(d, exp_val - frac_bits); -#endif t = toup(ch); if (t == 'F') { ch = *p++; @@ -2388,11 +2374,7 @@ static void parse_number(const char *p) } else if (t == 'L') { ch = *p++; tok = TOK_CLDOUBLE; -#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE - tokc.d = d; -#else tokc.ld = d; -#endif } else { tok = TOK_CDOUBLE; tokc.d = (double)d; @@ -2442,11 +2424,7 @@ static void parse_number(const char *p) } else if (t == 'L') { ch = *p++; tok = TOK_CLDOUBLE; -#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE - tokc.d = strtod(token_buf, NULL); -#else tokc.ld = strtold(token_buf, NULL); -#endif } else { tok = TOK_CDOUBLE; tokc.d = strtod(token_buf, NULL); diff --git a/tests/tcctest.c b/tests/tcctest.c index 0f55710d..20d970bc 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -32,8 +32,9 @@ #define XLONG_LONG_FORMAT "%Lx" #endif -// MinGW has 80-bit rather than 64-bit long double which isn't compatible with TCC or MSVC -#if defined(_WIN32) && defined(__GNUC__) +/* MinGW has 80-bit rather than 64-bit long double which isn't + compatible with printf in msvcrt */ +#if defined(_WIN32) #define LONG_DOUBLE double #define LONG_DOUBLE_LITERAL(x) x #else