mirror of
git://repo.or.cz/tinycc.git
synced 2026-06-17 15:44:18 +08:00
general: long double issues
tccgen.c: - init_putv(): improve long double cross constants now in separate function including some basic conversions arm-gen.c: - switch to using TCC_USING_DOUBLE_FOR_LDOUBLE now it actually never will see VT_LDOUBLE lib/lib-arm64.c: - simplify by using unions tccpp.c: - reduce amounts of #ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
This commit is contained in:
parent
7e01b20362
commit
923fba83f1
63
arm-gen.c
63
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);
|
||||
|
||||
109
tccgen.c
109
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
|
||||
|
||||
24
tccpp.c
24
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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user