From 9309585dbe64e89614fcf87f3a879bb18c25c4ca Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sun, 14 Jul 2019 05:48:15 +0200 Subject: [PATCH] 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. --- riscv64-gen.c | 48 ++++++++++++++++++++++++++++++++++++++++-------- tccgen.c | 38 +++++++++++++++++++++++++++++++++++--- 2 files changed, 75 insertions(+), 11 deletions(-) diff --git a/riscv64-gen.c b/riscv64-gen.c index 6a5a80eb..bc2f983a 100644 --- a/riscv64-gen.c +++ b/riscv64-gen.c @@ -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); } } diff --git a/tccgen.c b/tccgen.c index 745df990..5ea91f6e 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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