riscv: Start fixing float struct passing/returnig

this fixes ret_2float_test, ret_2double_test and
ret_8plus2double_test of abitest.c.  The common gfunc_return
actually works for these cases, so let's use that for now.

The ret_mixed_test (as well as mixed2 and mixed3) are left
broken, and tccgen.c:gfunc_return can't be used for that as is,
so I'll leave the gfunc_return implementation in riscv64-gen.c for
now, I'll have to think about this some more.
This commit is contained in:
Michael Matz 2019-07-22 23:09:05 +02:00
parent 98dc4c123d
commit 98f1b83ffe
3 changed files with 88 additions and 18 deletions

View File

@ -147,8 +147,8 @@ ST_FUNC void load(int r, SValue *sv)
if (fr & VT_LVAL) { if (fr & VT_LVAL) {
int func3, opcode = 0x03, doload = 0; int func3, opcode = 0x03, doload = 0;
if (is_freg(r)) { if (is_freg(r)) {
assert(bt == VT_DOUBLE || bt == VT_FLOAT);
opcode = 0x07; opcode = 0x07;
assert(bt == VT_DOUBLE || bt == VT_FLOAT);
func3 = bt == VT_DOUBLE ? 3 : 2; func3 = bt == VT_DOUBLE ? 3 : 2;
} else { } else {
assert(is_ireg(r)); assert(is_ireg(r));
@ -433,6 +433,33 @@ static void gcall_or_jmp(int docall)
} }
} }
static int pass_in_freg(CType *type, int regs)
{
int tr;
int toplevel = !regs;
if ((type->t & VT_BTYPE) == VT_STRUCT) {
Sym *f;
if (type->ref->type.t == VT_UNION)
return toplevel ? 0 : -1;
for (f = type->ref->next; f; f = f->next) {
tr = pass_in_freg(&f->type, regs);
if (tr < 0 || (regs + tr) > 2)
return toplevel ? 0 : -1;
regs += tr;
}
return regs;
} else if (type->t & VT_ARRAY) {
if (type->ref->c < 0 || type->ref->c > 2)
return toplevel ? 0 : -1;
tr = pass_in_freg(&type->ref->type, regs);
tr *= type->ref->c;
if (tr < 0 || (regs + tr) > 2)
return toplevel ? 0 : -1;
return regs + tr;
}
return is_float(type->t) && (type->t & VT_BTYPE) != VT_LDOUBLE;
}
ST_FUNC void gfunc_call(int nb_args) ST_FUNC void gfunc_call(int nb_args)
{ {
int i, align, size, aireg, afreg; int i, align, size, aireg, afreg;
@ -461,14 +488,15 @@ ST_FUNC void gfunc_call(int nb_args)
nregs = 2; nregs = 2;
else else
nregs = 1; nregs = 1;
if ((sv->type.t & VT_BTYPE) == VT_LDOUBLE) { if (!sa) {
infreg = 0; infreg = 0;
} else } else {
infreg = sa && is_float(sv->type.t); infreg = pass_in_freg(&sv->type, 0);
}
if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN) if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN)
aireg = (aireg + 1) & ~1; aireg = (aireg + 1) & ~1;
pareg = infreg ? &afreg : &aireg; pareg = infreg ? &afreg : &aireg;
if ((*pareg < 8) && !force_stack) { if ((*pareg < 8 && (!infreg || (*pareg + nregs) <= 8)) && !force_stack) {
info[i] = *pareg + (infreg ? 8 : 0); info[i] = *pareg + (infreg ? 8 : 0);
(*pareg)++; (*pareg)++;
if (nregs == 1) if (nregs == 1)
@ -547,7 +575,38 @@ ST_FUNC void gfunc_call(int nb_args)
vrotb(i+1); vrotb(i+1);
origtype = vtop->type; origtype = vtop->type;
size = type_size(&vtop->type, &align); size = type_size(&vtop->type, &align);
if (size > 8 && (vtop->type.t & VT_BTYPE) == VT_STRUCT) if (r >= 8 && (vtop->r & VT_LVAL)) {
int infreg = pass_in_freg(&vtop->type, 0);
int loadt = vtop->type.t & VT_BTYPE;
if (loadt == VT_STRUCT) {
if (infreg == 1)
loadt = size == 4 ? VT_FLOAT : VT_DOUBLE;
else
loadt = size == 8 ? VT_FLOAT : VT_DOUBLE;
}
vtop->type.t = loadt;
assert (infreg && infreg <= 2);
save_reg_upstack(r, 1);
if (infreg > 1) {
save_reg_upstack(r + 1, 1);
test_lvalue();
}
load(r, vtop);
vset(&origtype, r, 0);
vswap();
if (infreg > 1) {
assert(r + 1 < 16);
gaddrof();
mk_pointer(&vtop->type);
vpushi(1);
gen_op('+');
indir();
load(r + 1, vtop);
vtop[-1].r2 = r + 1;
}
vtop--;
} else {
if (r < 8 && size > 8 && (vtop->type.t & VT_BTYPE) == VT_STRUCT)
vtop->type.t = VT_LDOUBLE; // force loading a pair of regs vtop->type.t = VT_LDOUBLE; // force loading a pair of regs
gv(r < 8 ? RC_R(r) : RC_F(r - 8)); gv(r < 8 ? RC_R(r) : RC_F(r - 8));
vtop->type = origtype; vtop->type = origtype;
@ -565,6 +624,7 @@ ST_FUNC void gfunc_call(int nb_args)
vtop->r2 = 1 + vtop->r; vtop->r2 = 1 + vtop->r;
} }
} }
}
vrott(i+1); vrott(i+1);
} }
} }
@ -621,11 +681,14 @@ ST_FUNC void gfunc_prolog(CType *func_type)
addr += size; addr += size;
} else { } else {
int regcount = 1, *pareg = &aireg; int regcount = 1, *pareg = &aireg;
if (is_float(type->t) && (type->t & VT_BTYPE) != VT_LDOUBLE) if ((regcount = pass_in_freg(type, 0)))
pareg = &afreg; pareg = &afreg;
else {
regcount = 1;
}
if (regcount + *pareg > 8) if (regcount + *pareg > 8)
goto from_stack; goto from_stack;
if (size > XLEN) if (size > XLEN && pareg == &aireg)
regcount++; regcount++;
loc -= regcount * 8; // XXX could reserve only 'size' bytes loc -= regcount * 8; // XXX could reserve only 'size' bytes
param_addr = loc; param_addr = loc;
@ -638,8 +701,8 @@ ST_FUNC void gfunc_prolog(CType *func_type)
continue; continue;
} }
if (pareg == &afreg) { if (pareg == &afreg) {
assert(type->t == VT_FLOAT || type->t == VT_DOUBLE); //assert(type->t == VT_FLOAT || type->t == VT_DOUBLE);
ES(0x27, size == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*8); // fs[wd] FAi, loc(s0) ES(0x27, (size / regcount) == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*(size / regcount)); // fs[wd] FAi, loc(s0)
} else { } else {
ES(0x23, 3, 8, 10 + *pareg, loc + i*8); // sd aX, loc(s0) // XXX ES(0x23, 3, 8, 10 + *pareg, loc + i*8); // sd aX, loc(s0) // XXX
} }
@ -666,11 +729,17 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret,
/* generic code can only deal with structs of pow(2) sizes /* generic code can only deal with structs of pow(2) sizes
(it always deals with whole registers), so go through our own (it always deals with whole registers), so go through our own
code. */ code. */
int align, size = type_size(vt, &align); int align, size = type_size(vt, &align), infreg;
*ret_align = 1; *ret_align = 1;
*regsize = 8; *regsize = 8;
if (size > 16) if (size > 16)
return 0; return 0;
infreg = pass_in_freg(vt, 0);
if (infreg) {
*regsize = size / infreg;
ret->t = (size / infreg) == 4 ? VT_FLOAT : VT_DOUBLE;
return infreg;
}
if (size > 8) if (size > 8)
ret->t = VT_LLONG; ret->t = VT_LLONG;
else if (size > 4) else if (size > 4)
@ -684,7 +753,7 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret,
return (size + 7) / 8; return (size + 7) / 8;
} }
ST_FUNC void gfunc_return(CType *func_type) ST_FUNC void gfunc_return2(CType *func_type)
{ {
int align, size = type_size(func_type, &align), nregs; int align, size = type_size(func_type, &align), nregs;
CType type = *func_type; CType type = *func_type;
@ -697,9 +766,12 @@ ST_FUNC void gfunc_return(CType *func_type)
vpop(); vpop();
return; return;
} }
nregs = (size + 7) / 8; nregs = pass_in_freg(func_type, 0);
if (nregs == 2) if (!nregs) {
vtop->type.t = VT_LDOUBLE; nregs = (size + 7) / 8;
if (nregs == 2)
vtop->type.t = VT_LDOUBLE;
}
if (is_float(func_type->t) && (vtop->type.t & VT_BTYPE) != VT_LDOUBLE) if (is_float(func_type->t) && (vtop->type.t & VT_BTYPE) != VT_LDOUBLE)
gv(RC_FRET); gv(RC_FRET);

2
tcc.h
View File

@ -1604,7 +1604,7 @@ ST_FUNC void gen_clear_cache(void);
#ifdef TCC_TARGET_RISCV64 #ifdef TCC_TARGET_RISCV64
ST_FUNC void gen_cvt_sxtw(void); ST_FUNC void gen_cvt_sxtw(void);
ST_FUNC void gen_opl(int op); ST_FUNC void gen_opl(int op);
ST_FUNC void gfunc_return(CType *func_type); //ST_FUNC void gfunc_return(CType *func_type);
ST_FUNC void gen_va_start(void); ST_FUNC void gen_va_start(void);
ST_FUNC void gen_va_arg(CType *t); ST_FUNC void gen_va_arg(CType *t);
#endif #endif

View File

@ -6083,7 +6083,6 @@ ST_FUNC int expr_const(void)
/* return from function */ /* return from function */
#ifndef TCC_TARGET_ARM64 #ifndef TCC_TARGET_ARM64
#ifndef TCC_TARGET_RISCV64
static void gfunc_return(CType *func_type) static void gfunc_return(CType *func_type)
{ {
if ((func_type->t & VT_BTYPE) == VT_STRUCT) { if ((func_type->t & VT_BTYPE) == VT_STRUCT) {
@ -6148,7 +6147,6 @@ static void gfunc_return(CType *func_type)
vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
} }
#endif #endif
#endif
static void check_func_return(void) static void check_func_return(void)
{ {