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:
grischka 2026-05-02 21:30:39 +02:00
parent 7e01b20362
commit 923fba83f1
4 changed files with 93 additions and 108 deletions

View File

@ -124,6 +124,10 @@ enum {
#define LDOUBLE_ALIGN 4 #define LDOUBLE_ALIGN 4
#endif #endif
#if LDOUBLE_SIZE == 8
# define TCC_USING_DOUBLE_FOR_LDOUBLE 1
#endif
/* maximum alignment (for aligned attribute support) */ /* maximum alignment (for aligned attribute support) */
#define MAX_ALIGN 8 #define MAX_ALIGN 8
@ -635,12 +639,9 @@ void load(int r, SValue *sv)
op=0xED100100; op=0xED100100;
if(!sign) if(!sign)
op|=0x800000; op|=0x800000;
#if LDOUBLE_SIZE == 8
if ((ft & VT_BTYPE) != VT_FLOAT)
op|=0x8000;
#else
if ((ft & VT_BTYPE) == VT_DOUBLE) if ((ft & VT_BTYPE) == VT_DOUBLE)
op|=0x8000; op|=0x8000;
#if LDOUBLE_SIZE != 8
else if ((ft & VT_BTYPE) == VT_LDOUBLE) else if ((ft & VT_BTYPE) == VT_LDOUBLE)
op|=0x400000; op|=0x400000;
#endif #endif
@ -760,13 +761,10 @@ void store(int r, SValue *sv)
op=0xED000100; op=0xED000100;
if(!sign) if(!sign)
op|=0x800000; op|=0x800000;
#if LDOUBLE_SIZE == 8
if ((ft & VT_BTYPE) != VT_FLOAT)
op|=0x8000;
#else
if ((ft & VT_BTYPE) == VT_DOUBLE) if ((ft & VT_BTYPE) == VT_DOUBLE)
op|=0x8000; op|=0x8000;
if ((ft & VT_BTYPE) == VT_LDOUBLE) #if LDOUBLE_SIZE != 8
else if ((ft & VT_BTYPE) == VT_LDOUBLE)
op|=0x400000; op|=0x400000;
#endif #endif
o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); o(op|(fpr(r)<<12)|(fc>>2)|(base<<16));
@ -904,15 +902,6 @@ static void gen_bounds_epilog(void)
} }
#endif #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. /* 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 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. 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; ref = type->ref->next;
if (ref) { if (ref) {
btype = unalias_ldbl(ref->type.t & VT_BTYPE); btype = ref->type.t & VT_BTYPE;
if (btype == VT_FLOAT || btype == VT_DOUBLE) { 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; return !ref && nb_fields <= 4;
} }
} }
@ -1254,7 +1244,6 @@ again:
size = 8; size = 8;
else else
size = LDOUBLE_SIZE; size = LDOUBLE_SIZE;
if (size == 12) if (size == 12)
r |= 0x400000; r |= 0x400000;
else if(size == 8) else if(size == 8)
@ -1946,15 +1935,13 @@ void gen_opf(int op)
vswap(); vswap();
c2 = is_fconst(); c2 = is_fconst();
x=0xEE000100; 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) if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
x|=0x80; x|=0x80;
#if LDOUBLE_SIZE != 8
else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)
x|=0x80000; x|=0x80000;
#endif #endif
switch(op) switch(op)
{ {
case '+': case '+':
@ -2190,6 +2177,12 @@ ST_FUNC void gen_cvt_itof(int t)
func=TOK___floatundisf; func=TOK___floatundisf;
else else
func=TOK___floatdisf; 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 #if LDOUBLE_SIZE != 8
} else if((t & VT_BTYPE) == VT_LDOUBLE) { } else if((t & VT_BTYPE) == VT_LDOUBLE) {
func_type = &func_ldouble_type; func_type = &func_ldouble_type;
@ -2197,15 +2190,7 @@ ST_FUNC void gen_cvt_itof(int t)
func=TOK___floatundixf; func=TOK___floatundixf;
else else
func=TOK___floatdixf; func=TOK___floatdixf;
} else if((t & VT_BTYPE) == VT_DOUBLE) {
#else
} else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) {
#endif #endif
func_type = &func_double_type;
if(vtop->type.t & VT_UNSIGNED)
func=TOK___floatundidf;
else
func=TOK___floatdidf;
} }
if(func_type) { if(func_type) {
vpushsym(func_type, external_helper_sym(func)); vpushsym(func_type, external_helper_sym(func));
@ -2239,14 +2224,12 @@ void gen_cvt_ftoi(int t)
if(u) { if(u) {
if(r2 == VT_FLOAT) if(r2 == VT_FLOAT)
func=TOK___fixunssfsi; func=TOK___fixunssfsi;
else if(r2 == VT_DOUBLE)
func=TOK___fixunsdfsi;
#if LDOUBLE_SIZE != 8 #if LDOUBLE_SIZE != 8
else if(r2 == VT_LDOUBLE) else if(r2 == VT_LDOUBLE)
func=TOK___fixunsxfsi; func=TOK___fixunsxfsi;
else if(r2 == VT_DOUBLE)
#else
else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
#endif #endif
func=TOK___fixunsdfsi;
} else { } else {
r=fpr(gv(RC_FLOAT)); r=fpr(gv(RC_FLOAT));
r2=intr(vtop->r=get_reg(RC_INT)); 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 } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1
if(r2 == VT_FLOAT) if(r2 == VT_FLOAT)
func=TOK___fixsfdi; func=TOK___fixsfdi;
else if(r2 == VT_DOUBLE)
func=TOK___fixdfdi;
#if LDOUBLE_SIZE != 8 #if LDOUBLE_SIZE != 8
else if(r2 == VT_LDOUBLE) else if(r2 == VT_LDOUBLE)
func=TOK___fixxfdi; func=TOK___fixxfdi;
else if(r2 == VT_DOUBLE)
#else
else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
#endif #endif
func=TOK___fixdfdi;
} }
if(func) { if(func) {
vpush_helper_func(func); vpush_helper_func(func);

109
tccgen.c
View File

@ -323,12 +323,6 @@ ST_FUNC int ieee_finite(double d)
return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; 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) ST_FUNC void test_lvalue(void)
{ {
if (!(vtop->r & VT_LVAL)) if (!(vtop->r & VT_LVAL))
@ -2524,9 +2518,7 @@ void gen_negf(int op)
operation. We implement this with bit manipulation and have operation. We implement this with bit manipulation and have
to do some type reinterpretation for this, which TCC can do to do some type reinterpretation for this, which TCC can do
only via memory. */ only via memory. */
int align, size, bt; int align, size, bt;
size = type_size(&vtop->type, &align); size = type_size(&vtop->type, &align);
bt = vtop->type.t & VT_BTYPE; bt = vtop->type.t & VT_BTYPE;
#if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386 #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; 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) { if (c) {
/* constant case: we can do it now */ /* constant case: we can do it now */
/* XXX: in ISOC, cannot do it if error in convert */ /* XXX: in ISOC, cannot do it if error in convert */
@ -5646,6 +5632,7 @@ ST_FUNC void unary(void)
case TOK_CLDOUBLE: case TOK_CLDOUBLE:
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE #ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
t = VT_DOUBLE | VT_LONG; t = VT_DOUBLE | VT_LONG;
tokc.d = tokc.ld;
#else #else
t = VT_LDOUBLE; t = VT_LDOUBLE;
#endif #endif
@ -7781,6 +7768,71 @@ static int decl_designator(init_params *p, CType *type, unsigned long c,
return al; 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 */ /* 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) 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); write64le(ptr, val);
break; break;
case VT_LDOUBLE: case VT_LDOUBLE:
#if defined TCC_IS_NATIVE_387 write_ldouble(ptr, &vtop->c.ld);
/* 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
break; break;
#if PTR_SIZE == 8 #if PTR_SIZE == 8

24
tccpp.c
View File

@ -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 */ /* we use 128 bit (64/112 needed) numbers */
#define BN_SIZE 4 #define BN_SIZE 4
#endif
/* bn = (bn << shift) | or_val */ /* bn = (bn << shift) | or_val */
static int bn_lshift(unsigned int *bn, int shift, int 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; int b, t, shift, frac_bits, s, exp_val, ch;
char *q; char *q;
unsigned int bn[BN_SIZE]; unsigned int bn[BN_SIZE];
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
double d;
#else
long double d; long double d;
#endif
/* number */ /* number */
q = token_buf; q = token_buf;
@ -2366,19 +2357,14 @@ static void parse_number(const char *p)
ch = *p++; ch = *p++;
} }
exp_val = exp_val * s; exp_val = exp_val * s;
/* now we can generate the number */ /* now we can generate the number */
/* XXX: should patch directly float 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 + d = (long double)bn[3] * 79228162514264337593543950336.0L +
(long double)bn[2] * 18446744073709551616.0L + (long double)bn[2] * 18446744073709551616.0L +
(long double)bn[1] * 4294967296.0L + (long double)bn[1] * 4294967296.0L +
(long double)bn[0]; (long double)bn[0];
d = ldexpl(d, exp_val - frac_bits); d = ldexpl(d, exp_val - frac_bits);
#endif
t = toup(ch); t = toup(ch);
if (t == 'F') { if (t == 'F') {
ch = *p++; ch = *p++;
@ -2388,11 +2374,7 @@ static void parse_number(const char *p)
} else if (t == 'L') { } else if (t == 'L') {
ch = *p++; ch = *p++;
tok = TOK_CLDOUBLE; tok = TOK_CLDOUBLE;
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
tokc.d = d;
#else
tokc.ld = d; tokc.ld = d;
#endif
} else { } else {
tok = TOK_CDOUBLE; tok = TOK_CDOUBLE;
tokc.d = (double)d; tokc.d = (double)d;
@ -2442,11 +2424,7 @@ static void parse_number(const char *p)
} else if (t == 'L') { } else if (t == 'L') {
ch = *p++; ch = *p++;
tok = TOK_CLDOUBLE; tok = TOK_CLDOUBLE;
#ifdef TCC_USING_DOUBLE_FOR_LDOUBLE
tokc.d = strtod(token_buf, NULL);
#else
tokc.ld = strtold(token_buf, NULL); tokc.ld = strtold(token_buf, NULL);
#endif
} else { } else {
tok = TOK_CDOUBLE; tok = TOK_CDOUBLE;
tokc.d = strtod(token_buf, NULL); tokc.d = strtod(token_buf, NULL);

View File

@ -32,8 +32,9 @@
#define XLONG_LONG_FORMAT "%Lx" #define XLONG_LONG_FORMAT "%Lx"
#endif #endif
// MinGW has 80-bit rather than 64-bit long double which isn't compatible with TCC or MSVC /* MinGW has 80-bit rather than 64-bit long double which isn't
#if defined(_WIN32) && defined(__GNUC__) compatible with printf in msvcrt */
#if defined(_WIN32)
#define LONG_DOUBLE double #define LONG_DOUBLE double
#define LONG_DOUBLE_LITERAL(x) x #define LONG_DOUBLE_LITERAL(x) x
#else #else