History

riscv: rewrite parameter passing

this fixes the ret_mixed_test of abitest.c, now everything of the testsuite works. The generic code for returns is good enough for our use, except in the specific case of a mixed int/float structures returned in registers, so instead of duplicating the whole generic gfunc_return function, add another modus for gfunc_sret: returning -1 makes the actual register transfer by a new backend function.

Michael Matz committed 7 years ago

Commit c5050741 parent 9b0efa93 files · +209 −164Browse files

Unified · Split

3 files
  1. riscv64-gen.c +165 −135
  2. tcc.h +1 −0
  3. tccgen.c +43 −29
riscv64-gen.c+165−135
@@ -433,32 +433,36 @@ static void gcall_or_jmp(int docall) · 432 unmodified lines
433433 }
434434 }
435435
436−static int pass_in_freg(CType *type, int regs)
436+static void reg_pass(CType *type, int *rc, int *fieldofs, int ofs)
437437 {
438− int tr;
439− int toplevel = !regs;
440438 if ((type->t & VT_BTYPE) == VT_STRUCT) {
441439 Sym *f;
442440 if (type->ref->type.t == VT_UNION)
443− return toplevel ? 0 : -1;
444− for (f = type->ref->next; f; f = f->next) {
445− tr = pass_in_freg(&f->type, regs);
446− if (tr < 0 || (regs + tr) > 2)
447− return toplevel ? 0 : -1;
448− regs += tr;
449− }
450− return regs;
441+ rc[0] = -1;
442+ else for (f = type->ref->next; f; f = f->next)
443+ reg_pass(&f->type, rc, fieldofs, ofs + f->c);
451444 } else if (type->t & VT_ARRAY) {
452445 if (type->ref->c < 0 || type->ref->c > 2)
453− return toplevel ? 0 : -1;
454− tr = pass_in_freg(&type->ref->type, regs);
455− tr *= type->ref->c;
456− if (tr < 0 || (regs + tr) > 2)
457− return toplevel ? 0 : -1;
458− return regs + tr;
459− }
460− return is_float(type->t) && (type->t & VT_BTYPE) != VT_LDOUBLE
461− ? 1 : toplevel ? 0 : -1;
446+ rc[0] = -1;
447+ else {
448+ int a, sz = type_size(&type->ref->type, &a);
449+ reg_pass(&type->ref->type, rc, fieldofs, ofs);
450+ if (rc[0] > 2 || (rc[0] == 2 && type->ref->c > 1))
451+ rc[0] = -1;
452+ else if (type->ref->c == 2 && rc[0] && rc[1] == RC_FLOAT) {
453+ rc[++rc[0]] = RC_FLOAT;
454+ fieldofs[rc[0]] = ((ofs + sz) << 4)
455+ | (type->ref->type.t & VT_BTYPE);
456+ } else if (type->ref->c == 2)
457+ rc[0] = -1;
458+ }
459+ } else if (rc[0] == 2 || rc[0] < 0 || (type->t & VT_BTYPE) == VT_LDOUBLE)
460+ rc[0] = -1;
461+ else if (!rc[0] || rc[1] == RC_FLOAT || is_float(type->t)) {
462+ rc[++rc[0]] = is_float(type->t) ? RC_FLOAT : RC_INT;
463+ fieldofs[rc[0]] = (ofs << 4) | (type->t & VT_BTYPE);
464+ } else
465+ rc[0] = -1;
462466 }
463467
464468 ST_FUNC void gfunc_call(int nb_args)
@@ -472,7 +476,9 @@ ST_FUNC void gfunc_call(int nb_args) · 7 unmodified lines
472476 aireg = afreg = 0;
473477 sa = vtop[-nb_args].type.ref->next;
474478 for (i = 0; i < nb_args; i++) {
475− int *pareg, nregs, infreg = 0, byref = 0, tempofs;
479+ int *pareg, nregs, byref = 0, tempofs;
480+ int prc[3], fieldofs[3];
481+ prc[0] = 0;
476482 sv = &vtop[1 + i - nb_args];
477483 sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c
478484 size = type_size(&sv->type, &align);
@@ -490,31 +496,43 @@ ST_FUNC void gfunc_call(int nb_args) · 11 unmodified lines
490496 else
491497 nregs = 1;
492498 if (!sa) {
493− infreg = 0;
499+ prc[0] = nregs, prc[1] = prc[2] = RC_INT;
494500 } else {
495− infreg = pass_in_freg(&sv->type, 0);
501+ reg_pass(&sv->type, prc, fieldofs, 0);
502+ if (prc[0] < 0)
503+ prc[0] = nregs, prc[1] = prc[2] = RC_INT;
496504 }
497− if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN)
505+ if (!sa && align == 2*XLEN && size <= 2*XLEN)
498506 aireg = (aireg + 1) & ~1;
499− pareg = infreg ? &afreg : &aireg;
500− if ((*pareg < 8 && (!infreg || (*pareg + nregs) <= 8)) && !force_stack) {
501− info[i] = *pareg + (infreg ? 8 : 0);
502− (*pareg)++;
503− if (nregs == 1)
504− ;
505− else if (*pareg < 8)
506− (*pareg)++;
507− else {
508− info[i] |= 16;
509− stack_adj += 8;
510− }
511− } else {
507+ nregs = prc[0];
508+ assert(nregs);
509+ if (!nregs
510+ || (prc[1] == RC_INT && aireg >= 8)
511+ || (prc[1] == RC_FLOAT && afreg >= 8)
512+ || (nregs == 2 && prc[2] == RC_FLOAT && afreg >= 7)
513+ || (nregs == 2 && prc[1] != prc[2] && (afreg >= 8 || aireg >= 8))
514+ || force_stack) {
512515 info[i] = 32;
513516 if (align < XLEN)
514517 align = XLEN;
515518 stack_adj += (size + align - 1) & -align;
516519 if (!sa)
517520 force_stack = 1;
521+ } else {
522+ if (prc[1] == RC_FLOAT)
523+ info[i] = 8 + afreg++;
524+ else
525+ info[i] = aireg++;
526+ if (nregs == 2) {
527+ if (prc[2] == RC_FLOAT)
528+ info[i] |= (1 + 8 + afreg++) << 7;
529+ else if (aireg < 8)
530+ info[i] |= (1 + aireg++) << 7;
531+ else {
532+ info[i] |= 16;
533+ stack_adj += 8;
534+ }
535+ }
518536 }
519537 if (byref)
520538 info[i] |= 64 | (tempofs << 7);
@@ -569,62 +587,72 @@ ST_FUNC void gfunc_call(int nb_args) · 48 unmodified lines
569587 }
570588 }
571589 for (i = 0; i < nb_args; i++) {
572− int r = info[nb_args - 1 - i];
590+ int r = info[nb_args - 1 - i], r2 = r;
573591 if (!(r & 32)) {
574592 CType origtype;
593+ int prc[3], fieldofs[3], loadt;
594+ prc[0] = 0;
575595 r &= 15;
596+ r2 = r2 & 64 ? 0 : r2 >> 7;
576597 vrotb(i+1);
577598 origtype = vtop->type;
578599 size = type_size(&vtop->type, &align);
579− if (r >= 8 && (vtop->r & VT_LVAL)) {
580− int infreg = pass_in_freg(&vtop->type, 0);
581− int loadt = vtop->type.t & VT_BTYPE;
600+ reg_pass(&vtop->type, prc, fieldofs, 0);
601+ if (prc[0] <= 0 || (!r2 && prc[0] > 1)) {
602+ prc[0] = (size + 7) >> 3;
603+ prc[1] = prc[2] = RC_INT;
604+ fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG);
605+ fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG);
606+ }
607+ loadt = vtop->type.t & VT_BTYPE;
608+ if (loadt == VT_STRUCT) {
609+ loadt = fieldofs[1] & VT_BTYPE;
610+ }
611+ if (info[nb_args - 1 - i] & 16) {
612+ assert(!r2);
613+ r2 = 1 + TREG_RA;
614+ }
615+ if (loadt == VT_LDOUBLE) {
616+ assert(r2);
617+ r2--;
618+ } else if (r2) {
619+ test_lvalue();
620+ vpushv(vtop);
621+ }
622+ vtop->type.t = loadt;
623+ gv(r < 8 ? RC_R(r) : RC_F(r - 8));
624+ vtop->type = origtype;
625+
626+ if (r2 && loadt != VT_LDOUBLE) {
627+ r2--;
628+ assert(r2 < 16 || r2 == TREG_RA);
629+ vswap();
630+ gaddrof();
631+ vtop->type = char_pointer_type;
632+ vpushi(fieldofs[2] >> 4);
633+ gen_op('+');
634+ indir();
635+ vtop->type = origtype;
636+ loadt = vtop->type.t & VT_BTYPE;
582637 if (loadt == VT_STRUCT) {
583− if (infreg == 1)
584− loadt = size == 4 ? VT_FLOAT : VT_DOUBLE;
585− else
586− loadt = size == 8 ? VT_FLOAT : VT_DOUBLE;
638+ loadt = fieldofs[2] & VT_BTYPE;
587639 }
640+ save_reg_upstack(r2, 1);
588641 vtop->type.t = loadt;
589− assert (infreg && infreg <= 2);
590− save_reg_upstack(r, 1);
591− if (infreg > 1) {
592− save_reg_upstack(r + 1, 1);
593− test_lvalue();
594− }
595− load(r, vtop);
596− vset(&origtype, r, 0);
597− vswap();
598− if (infreg > 1) {
599− assert(r + 1 < 16);
600− gaddrof();
601− mk_pointer(&vtop->type);
602− vpushi(1);
603− gen_op('+');
604− indir();
605− load(r + 1, vtop);
606− vtop[-1].r2 = r + 1;
607− }
642+ load(r2, vtop);
643+ assert(r2 < VT_CONST);
608644 vtop--;
609− } else {
610− if (r < 8 && size > 8 && (vtop->type.t & VT_BTYPE) == VT_STRUCT)
611− vtop->type.t = VT_LDOUBLE; // force loading a pair of regs
612− gv(r < 8 ? RC_R(r) : RC_F(r - 8));
613− vtop->type = origtype;
614− if (size > 8) {
615− assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE
616− || (vtop->type.t & VT_BTYPE) == VT_STRUCT);
617− assert(vtop->r2 < VT_CONST);
618− if (info[nb_args - 1 - i] & 16) {
619− ES(0x23, 3, 2, ireg(vtop->r2), splitofs); // sd t0, ofs(sp)
620− } else if (vtop->r2 != 1 + vtop->r) {
621− assert(vtop->r < 7);
622− /* XXX we'd like to have 'gv' move directly into
623− the right class instead of us fixing it up. */
624− EI(0x13, 0, ireg(vtop->r) + 1, ireg(vtop->r2), 0); // mv Ra+1, RR2
625− vtop->r2 = 1 + vtop->r;
626− }
645+ vtop->r2 = r2;
627646 }
647+ if (info[nb_args - 1 - i] & 16) {
648+ ES(0x23, 3, 2, ireg(vtop->r2), splitofs); // sd t0, ofs(sp)
649+ vtop->r2 = VT_CONST;
650+ } else if (loadt == VT_LDOUBLE && vtop->r2 != r2) {
651+ assert(vtop->r2 <= 7 && r2 <= 7);
652+ /* XXX we'd like to have 'gv' move directly into
653+ the right class instead of us fixing it up. */
654+ EI(0x13, 0, ireg(r2), ireg(vtop->r2), 0); // mv Ra+1, RR2
655+ vtop->r2 = r2;
628656 }
629657 vrott(i+1);
630658 }
@@ -681,19 +709,29 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 50 unmodified lines
681709 param_addr = addr;
682710 addr += size;
683711 } else {
684− int regcount = 1, *pareg = &aireg;
685− if ((regcount = pass_in_freg(type, 0)))
686− pareg = &afreg;
687− else {
688− regcount = 1;
712+ int regcount, *pareg;
713+ int prc[3], fieldofs[3];
714+ prc[0] = 0;
715+ reg_pass(type, prc, fieldofs, 0);
716+ if (prc[0] <= 0) {
717+ prc[0] = (size + 7) >> 3;
718+ prc[1] = prc[2] = RC_INT;
719+ fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG);
720+ fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG);
689721 }
690− if (regcount + *pareg > 8)
722+ regcount = prc[0];
723+ if (!regcount
724+ || (prc[1] == RC_INT && aireg >= 8)
725+ || (prc[1] == RC_FLOAT && afreg >= 8)
726+ || (regcount == 2 && prc[2] == RC_FLOAT && afreg >= 7)
727+ || (regcount == 2 && prc[1] != prc[2] && (afreg >= 8 || aireg >= 8)))
691728 goto from_stack;
692− if (size > XLEN && pareg == &aireg)
693− regcount++;
729+ if (size > XLEN)
730+ assert(regcount == 2);
694731 loc -= regcount * 8; // XXX could reserve only 'size' bytes
695732 param_addr = loc;
696733 for (i = 0; i < regcount; i++) {
734+ pareg = prc[1+i] == RC_INT ? &aireg : &afreg;
697735 if (*pareg >= 8) {
698736 assert(i == 1 && regcount == 2 && !(addr & 7));
699737 EI(0x03, 3, 5, 8, addr); // ld t0, addr(s0)
@@ -703,11 +741,10 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 3 unmodified lines
703741 }
704742 if (pareg == &afreg) {
705743 //assert(type->t == VT_FLOAT || type->t == VT_DOUBLE);
706− ES(0x27, (size / regcount) == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*(size / regcount)); // fs[wd] FAi, loc(s0)
744+ ES(0x27, (size / regcount) == 4 ? 2 : 3, 8, 10 + afreg++, loc + (fieldofs[i+1] >> 4)); // fs[wd] FAi, loc(s0)
707745 } else {
708− ES(0x23, 3, 8, 10 + *pareg, loc + i*8); // sd aX, loc(s0) // XXX
746+ ES(0x23, 3, 8, 10 + aireg++, loc + i*8); // sd aX, loc(s0) // XXX
709747 }
710− (*pareg)++;
711748 }
712749 }
713750 sym_push(sym->v & ~SYM_FIELD, &sym->type,
@@ -727,57 +764,50 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 13 unmodified lines
727764 ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret,
728765 int *ret_align, int *regsize)
729766 {
730− /* generic code can only deal with structs of pow(2) sizes
731− (it always deals with whole registers), so go through our own
732− code. */
733− int align, size = type_size(vt, &align), infreg;
767+ int align, size = type_size(vt, &align), infreg, nregs;
768+ int prc[3], fieldofs[3];
734769 *ret_align = 1;
735770 *regsize = 8;
736771 if (size > 16)
737772 return 0;
738− infreg = pass_in_freg(vt, 0);
739− if (infreg) {
740− *regsize = size / infreg;
741− ret->t = (size / infreg) == 4 ? VT_FLOAT : VT_DOUBLE;
742− return infreg;
773+ prc[0] = 0;
774+ reg_pass(vt, prc, fieldofs, 0);
775+ if (prc[0] <= 0) {
776+ prc[0] = (size + 7) >> 3;
777+ prc[1] = prc[2] = RC_INT;
778+ fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG);
779+ fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG);
743780 }
744− if (size > 8)
745− ret->t = VT_LLONG;
746− else if (size > 4)
747− ret->t = VT_LLONG;
748− else if (size > 2)
749− ret->t = VT_INT;
750− else if (size > 1)
751− ret->t = VT_SHORT;
752− else
753− ret->t = VT_BYTE;
754− return (size + 7) / 8;
781+ nregs = prc[0];
782+ assert(nregs > 0);
783+ if (nregs == 2 && prc[1] != prc[2])
784+ return -1; /* generic code can't deal with this case */
785+ if (prc[1] == RC_FLOAT) {
786+ *regsize = size / nregs;
787+ }
788+ ret->t = fieldofs[1] & VT_BTYPE;
789+ return nregs;
755790 }
756791
757−ST_FUNC void gfunc_return2(CType *func_type)
792+ST_FUNC void arch_transfer_ret_regs(int aftercall)
758793 {
759− int align, size = type_size(func_type, &align), nregs;
760− CType type = *func_type;
761− if (size > 2 * XLEN) {
762− mk_pointer(&type);
763− vset(&type, VT_LOCAL | VT_LVAL, func_vc);
764− indir();
765− vswap();
766− vstore();
767− vpop();
768− return;
769− }
770− nregs = pass_in_freg(func_type, 0);
771− if (!nregs) {
772− nregs = (size + 7) / 8;
773− if (nregs == 2)
774− vtop->type.t = VT_LDOUBLE;
775− }
776−
777− if (is_float(func_type->t) && (vtop->type.t & VT_BTYPE) != VT_LDOUBLE)
778− gv(RC_FRET);
794+ int prc[3], fieldofs[3];
795+ prc[0] = 0;
796+ reg_pass(&vtop->type, prc, fieldofs, 0);
797+ assert(prc[0] == 2 && prc[1] != prc[2] && !(fieldofs[1] >> 4));
798+ assert(vtop->r == (VT_LOCAL | VT_LVAL));
799+ vpushv(vtop);
800+ vtop->type.t = fieldofs[1] & VT_BTYPE;
801+ if (aftercall)
802+ store(prc[1] == RC_INT ? REG_IRET : REG_FRET, vtop);
803+ else
804+ load(prc[1] == RC_INT ? REG_IRET : REG_FRET, vtop);
805+ vtop->c.i += fieldofs[2] >> 4;
806+ vtop->type.t = fieldofs[2] & VT_BTYPE;
807+ if (aftercall)
808+ store(prc[2] == RC_INT ? REG_IRET : REG_FRET, vtop);
779809 else
780− gv(RC_IRET);
810+ load(prc[2] == RC_INT ? REG_IRET : REG_FRET, vtop);
781811 vtop--;
782812 }
783813
tcc.h+1−0
@@ -1607,6 +1607,7 @@ ST_FUNC void gen_opl(int op); · 1606 unmodified lines
16071607 //ST_FUNC void gfunc_return(CType *func_type);
16081608 ST_FUNC void gen_va_start(void);
16091609 ST_FUNC void gen_va_arg(CType *t);
1610+ST_FUNC void arch_transfer_ret_regs(int);
16101611 #endif
16111612 /* ------------ c67-gen.c ------------ */
16121613 #ifdef TCC_TARGET_C67
tccgen.c+43−29
@@ -5490,7 +5490,7 @@ special_math_val: · 5489 unmodified lines
54905490 variadic = (s->f.func_type == FUNC_ELLIPSIS);
54915491 ret_nregs = gfunc_sret(&s->type, variadic, &ret.type,
54925492 &ret_align, &regsize);
5493− if (!ret_nregs) {
5493+ if (ret_nregs <= 0) {
54945494 /* get some space for the returned structure */
54955495 size = type_size(&s->type, &align);
54965496 #ifdef TCC_TARGET_ARM64
@@ -5509,14 +5509,17 @@ special_math_val: · 12 unmodified lines
55095509 problems */
55105510 vseti(VT_LOCAL, loc);
55115511 ret.c = vtop->c;
5512− nb_args++;
5512+ if (ret_nregs < 0)
5513+ vtop--;
5514+ else
5515+ nb_args++;
55135516 }
55145517 } else {
55155518 ret_nregs = 1;
55165519 ret.type = s->type;
55175520 }
55185521
5519− if (ret_nregs) {
5522+ if (ret_nregs > 0) {
55205523 /* return in register */
55215524 if (is_float(ret.type.t)) {
55225525 ret.r = reg_fret(ret.type.t);
@@ -5559,34 +5562,41 @@ special_math_val: · 36 unmodified lines
55595562 skip(')');
55605563 gfunc_call(nb_args);
55615564
5562− /* return value */
5563− for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
5564− vsetc(&ret.type, r, &ret.c);
5565− vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
5566− }
5565+ if (ret_nregs < 0) {
5566+ vsetc(&ret.type, ret.r, &ret.c);
5567+#ifdef TCC_TARGET_RISCV64
5568+ arch_transfer_ret_regs(1);
5569+#endif
5570+ } else {
5571+ /* return value */
5572+ for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
5573+ vsetc(&ret.type, r, &ret.c);
5574+ vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
5575+ }
55675576
5568− /* handle packed struct return */
5569− if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
5570− int addr, offset;
5577+ /* handle packed struct return */
5578+ if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
5579+ int addr, offset;
55715580
5572− size = type_size(&s->type, &align);
5573− /* We're writing whole regs often, make sure there's enough
5574− space. Assume register size is power of 2. */
5575− if (regsize > align)
5576− align = regsize;
5577− loc = (loc - size) & -align;
5578− addr = loc;
5579− offset = 0;
5580− for (;;) {
5581− vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
5582− vswap();
5583− vstore();
5584− vtop--;
5585− if (--ret_nregs == 0)
5586− break;
5587− offset += regsize;
5581+ size = type_size(&s->type, &align);
5582+ /* We're writing whole regs often, make sure there's enough
5583+ space. Assume register size is power of 2. */
5584+ if (regsize > align)
5585+ align = regsize;
5586+ loc = (loc - size) & -align;
5587+ addr = loc;
5588+ offset = 0;
5589+ for (;;) {
5590+ vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
5591+ vswap();
5592+ vstore();
5593+ vtop--;
5594+ if (--ret_nregs == 0)
5595+ break;
5596+ offset += regsize;
5597+ }
5598+ vset(&s->type, VT_LOCAL | VT_LVAL, addr);
55885599 }
5589− vset(&s->type, VT_LOCAL | VT_LVAL, addr);
55905600 }
55915601 if (s->f.func_noreturn)
55925602 CODE_OFF();
@@ -6090,7 +6100,11 @@ static void gfunc_return(CType *func_type) · 497 unmodified lines
60906100 int ret_align, ret_nregs, regsize;
60916101 ret_nregs = gfunc_sret(func_type, func_var, &ret_type,
60926102 &ret_align, &regsize);
6093− if (0 == ret_nregs) {
6103+ if (ret_nregs < 0) {
6104+#ifdef TCC_TARGET_RISCV64
6105+ arch_transfer_ret_regs(0);
6106+#endif
6107+ } else if (0 == ret_nregs) {
60946108 /* if returning structure, must copy it to implicit
60956109 first pointer arg location */
60966110 type = *func_type;

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t