riscv: some long double support

long double on risc-v is 128bit, but there are no registers
for that type (without the Q ISA extension).  They are passed
like two 64bit integers values (with an exception for varargs,
where it's an aligned register pair).  This all requires some
hacks in generic code as otherwise RC_FLOAT regs are tried for
holding values of long double type, but we need a RC_INT register
pair.  This really could all use some cleanup for all archs.

This doesn't implement any conversions of operations for long
double, but it's enough to get 70_floating_point_literals working.
mob
Michael Matz 2019-07-14 05:48:15 +02:00
parent 9c1b17407f
commit 9309585dbe
2 changed files with 75 additions and 11 deletions

View File

@ -345,24 +345,45 @@ ST_FUNC void gfunc_call(int nb_args)
int i, align, size, aireg, afreg;
int info[nb_args ? nb_args : 1];
int stack_adj = 0, ofs;
int force_stack = 0;
SValue *sv;
Sym *sa;
aireg = afreg = 0;
sa = vtop[-nb_args].type.ref->next;
for (i = 0; i < nb_args; i++) {
int *pareg;
int *pareg, nregs, infreg = 0;
sv = &vtop[1 + i - nb_args];
sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c
size = type_size(&sv->type, &align);
if (size > 8 || ((sv->type.t & VT_BTYPE) == VT_STRUCT))
if ((size > 8 && ((sv->type.t & VT_BTYPE) != VT_LDOUBLE))
|| ((sv->type.t & VT_BTYPE) == VT_STRUCT))
tcc_error("unimp: call arg %d wrong type", nb_args - i);
pareg = sa && is_float(sv->type.t) ? &afreg : &aireg;
if (*pareg < 8) {
info[i] = *pareg + (sa && is_float(sv->type.t) ? 8 : 0);
nregs = 1;
if ((sv->type.t & VT_BTYPE) == VT_LDOUBLE) {
infreg = 0, nregs = 2;
if (!sa) {
aireg = (aireg + 1) & ~1;
}
} else
infreg = sa && is_float(sv->type.t);
pareg = infreg ? &afreg : &aireg;
if ((*pareg < 8) && !force_stack) {
info[i] = *pareg + (infreg ? 8 : 0);
(*pareg)++;
if (nregs == 1)
;
else if (*pareg < 8)
(*pareg)++;
else {
info[i] |= 16;
stack_adj += 8;
tcc_error("unimp: param passing half in reg, half on stack");
}
} else {
info[i] = 16;
info[i] = 32;
stack_adj += (size + align - 1) & -align;
if (!sa)
force_stack = 1;
}
if (sa)
sa = sa->next;
@ -371,7 +392,7 @@ ST_FUNC void gfunc_call(int nb_args)
if (stack_adj) {
EI(0x13, 0, 2, 2, -stack_adj); // addi sp, sp, -adj
for (i = ofs = 0; i < nb_args; i++) {
if (1 && info[i] >= 16) {
if (1 && info[i] >= 32) {
vrotb(nb_args - i);
size = type_size(&vtop->type, &align);
/* Once we support offseted regs we can do this:
@ -393,9 +414,20 @@ ST_FUNC void gfunc_call(int nb_args)
}
for (i = 0; i < nb_args; i++) {
int r = info[nb_args - 1 - i];
if (r < 16) {
if (r < 32) {
r &= 15;
vrotb(i+1);
gv(r < 8 ? RC_R(r) : RC_F(r - 8));
if (vtop->r2 < VT_CONST) {
assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE);
assert(vtop->r < 7);
if (vtop->r2 != 1 + vtop->r) {
/* XXX we'd like to have 'gv' move directly into
the right class instead of us fixing it up. */
EI(0x13, 0, ireg(vtop->r) + 1, ireg(vtop->r2), 0); // mv Ra+1, RR2
vtop->r2 = 1 + vtop->r;
}
}
vrott(i+1);
}
}

View File

@ -1548,6 +1548,11 @@ ST_FUNC int gv(int rc)
if (vtop->r & VT_MUSTBOUND)
gbound();
#endif
#ifdef TCC_TARGET_RISCV64
/* XXX mega hack */
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE && rc == RC_FLOAT)
rc = RC_INT;
#endif
r = vtop->r & VT_VALMASK;
rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
@ -1568,7 +1573,10 @@ ST_FUNC int gv(int rc)
if (r >= VT_CONST
|| (vtop->r & VT_LVAL)
|| !(reg_classes[r] & rc)
#if PTR_SIZE == 8
#ifdef TCC_TARGET_RISCV64
|| ((vtop->type.t & VT_BTYPE) == VT_QLONG && (vtop->r2 >= NB_REGS || !(reg_classes[vtop->r2] & rc2)))
|| ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE && (vtop->r2 >= NB_REGS || !(reg_classes[vtop->r2] & rc2)))
#elif PTR_SIZE == 8
|| ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
|| ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
#else
@ -1577,7 +1585,10 @@ ST_FUNC int gv(int rc)
)
{
r = get_reg(rc);
#if PTR_SIZE == 8
#ifdef TCC_TARGET_RISCV64
if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)) {
int addr_type = VT_LLONG, load_size = 8, load_type = VT_LLONG;
#elif PTR_SIZE == 8
if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
@ -1708,6 +1719,9 @@ static int rc_fret(int t)
if (t == VT_LDOUBLE) {
return RC_ST0;
}
#elif defined TCC_TARGET_RISCV64
if (t == VT_LDOUBLE)
return RC_IRET;
#endif
return RC_FRET;
}
@ -1720,6 +1734,9 @@ static int reg_fret(int t)
if (t == VT_LDOUBLE) {
return TREG_ST0;
}
#elif defined TCC_TARGET_RISCV64
if (t == VT_LDOUBLE)
return REG_IRET;
#endif
return REG_FRET;
}
@ -1797,6 +1814,9 @@ static void gv_dup(void)
if ((t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#elif defined TCC_TARGET_RISCV64
if ((t & VT_BTYPE) == VT_LDOUBLE)
rc = RC_INT;
#endif
sv.type.t = t;
}
@ -3403,6 +3423,9 @@ ST_FUNC void vstore(void)
} else if ((ft & VT_BTYPE) == VT_QFLOAT) {
rc = RC_FRET;
}
#elif defined TCC_TARGET_RISCV64
if (dbt == VT_LDOUBLE)
rc = RC_INT;
#endif
}
r = gv(rc); /* generate value */
@ -3421,7 +3444,10 @@ ST_FUNC void vstore(void)
vtop[-1].r = t | VT_LVAL;
}
/* two word case handling : store second register at word + 4 (or +8 for x86-64) */
#if PTR_SIZE == 8
#ifdef TCC_TARGET_RISCV64
if (dbt == VT_QLONG || dbt == VT_LDOUBLE) {
int addr_type = VT_LLONG, load_size = 8, load_type = VT_LLONG;
#elif PTR_SIZE == 8
if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
@ -5743,6 +5769,9 @@ static void expr_cond(void)
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#elif defined TCC_TARGET_RISCV64
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)
rc = RC_INT;
#endif
}
gv(rc);
@ -5914,6 +5943,9 @@ static void expr_cond(void)
if ((type.t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#elif defined TCC_TARGET_RISCV64
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)
rc = RC_INT;
#endif
} else if ((type.t & VT_BTYPE) == VT_LLONG) {
/* for long longs, we use fixed registers to avoid having