History

riscv: some work on large function arguments

like long double (16 bytes) and structs. Not completely correct, but 73_arm64 somewhat works now (when the stdarg part is disabled), though with some errors. What's definitely incorrect is arguments of a mixed int/float struct. I'm using VT_LDOUBLE (which conveniently has to be placed in a int-reg-pair) to load/store structure arguments of size > 8 and <= 16, and that can lead to overreads.

Michael Matz committed 7 years ago

Commit f44df9d1 parent ddb0c2d3 files · +164 −73Browse files

Unified · Split

3 files
  1. riscv64-gen.c +159 −65
  2. tccgen.c +4 −7
  3. tcctok.h +1 −1
riscv64-gen.c+159−65
@@ -1,7 +1,7 @@
1#ifdef TARGET_DEFS_ONLY1#ifdef TARGET_DEFS_ONLY
2 2
3// Number of registers available to allocator:3// Number of registers available to allocator:
4#define NB_REGS 16 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa74#define NB_REGS 19 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa7, xxx, ra, sp
5 5
6#define TREG_R(x) (x) // x = 0..76#define TREG_R(x) (x) // x = 0..7
7#define TREG_F(x) (x + 8) // x = 0..77#define TREG_F(x) (x + 8) // x = 0..7
@@ -52,7 +52,10 @@ ST_DATA const int reg_classes[NB_REGS] = { · 44 unmodified lines
52 RC_FLOAT | RC_F(4),52 RC_FLOAT | RC_F(4),
53 RC_FLOAT | RC_F(5),53 RC_FLOAT | RC_F(5),
54 RC_FLOAT | RC_F(6),54 RC_FLOAT | RC_F(6),
55 RC_FLOAT | RC_F(7)55 RC_FLOAT | RC_F(7),
56 0,
57 1 << TREG_RA,
58 1 << TREG_SP
56};59};
57 60
58static int ireg(int r)61static int ireg(int r)
@@ -201,7 +204,7 @@ ST_FUNC void load(int r, SValue *sv) · 142 unmodified lines
201 }204 }
202 } else if (v == VT_CONST) {205 } else if (v == VT_CONST) {
203 int rb = 0, do32bit = 8, doload = 0;206 int rb = 0, do32bit = 8, doload = 0;
204 assert(!is_float(sv->type.t) && is_ireg(r));207 assert(!is_float(sv->type.t) && is_ireg(r) || bt == VT_LDOUBLE);
205 if (fr & VT_SYM) {208 if (fr & VT_SYM) {
206 static Sym label;209 static Sym label;
207 if (sv->sym->type.t & VT_STATIC) { // XXX do this per linker relax210 if (sv->sym->type.t & VT_STATIC) { // XXX do this per linker relax
@@ -228,7 +231,7 @@ ST_FUNC void load(int r, SValue *sv) · 20 unmodified lines
228 rb = rr;231 rb = rr;
229 do32bit = 0;232 do32bit = 0;
230 }233 }
231 if (is_float(sv->type.t))234 if (is_float(sv->type.t) && bt != VT_LDOUBLE)
232 tcc_error("unimp: load(float)");235 tcc_error("unimp: load(float)");
233 if (fc != sv->c.i) {236 if (fc != sv->c.i) {
234 int64_t si = sv->c.i;237 int64_t si = sv->c.i;
@@ -317,7 +320,11 @@ ST_FUNC void store(int r, SValue *sv) · 82 unmodified lines
317 int ft = sv->type.t;320 int ft = sv->type.t;
318 int bt = ft & VT_BTYPE;321 int bt = ft & VT_BTYPE;
319 int align, size = type_size(&sv->type, &align);322 int align, size = type_size(&sv->type, &align);
320 assert(!is_float(bt) || is_freg(r));323 assert(!is_float(bt) || is_freg(r) || bt == VT_LDOUBLE);
324 /* long doubles are in two integer registers, but the load/store
325 primitives only deal with one, so do as if it's one reg. */
326 if (bt == VT_LDOUBLE)
327 size = align = 8;
321 if (bt == VT_STRUCT)328 if (bt == VT_STRUCT)
322 tcc_error("unimp: store(struct)");329 tcc_error("unimp: store(struct)");
323 if (size > 8)330 if (size > 8)
@@ -399,28 +406,33 @@ ST_FUNC void gfunc_call(int nb_args) · 75 unmodified lines
399{406{
400 int i, align, size, aireg, afreg;407 int i, align, size, aireg, afreg;
401 int info[nb_args ? nb_args : 1];408 int info[nb_args ? nb_args : 1];
402 int stack_adj = 0, ofs;409 int stack_adj = 0, tempspace = 0, ofs, splitofs = 0;
403 int force_stack = 0;410 int force_stack = 0;
404 SValue *sv;411 SValue *sv;
405 Sym *sa;412 Sym *sa;
406 aireg = afreg = 0;413 aireg = afreg = 0;
407 sa = vtop[-nb_args].type.ref->next;414 sa = vtop[-nb_args].type.ref->next;
408 for (i = 0; i < nb_args; i++) {415 for (i = 0; i < nb_args; i++) {
409 int *pareg, nregs, infreg = 0;416 int *pareg, nregs, infreg = 0, byref = 0, tempofs;
410 sv = &vtop[1 + i - nb_args];417 sv = &vtop[1 + i - nb_args];
411 sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c418 sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c
412 size = type_size(&sv->type, &align);419 size = type_size(&sv->type, &align);
413 if ((size > 8 && ((sv->type.t & VT_BTYPE) != VT_LDOUBLE))420 if (size > 16) {
414 || ((sv->type.t & VT_BTYPE) == VT_STRUCT))421 tempspace = (tempspace + align - 1) & -align;
415 tcc_error("unimp: call arg %d wrong type", nb_args - i);422 tempofs = tempspace;
416 nregs = 1;423 tempspace += size;
424 size = align = 8;
425 byref = 1;
426 } else if (size > 8)
427 nregs = 2;
428 else
429 nregs = 1;
417 if ((sv->type.t & VT_BTYPE) == VT_LDOUBLE) {430 if ((sv->type.t & VT_BTYPE) == VT_LDOUBLE) {
418 infreg = 0, nregs = 2;431 infreg = 0;
419 if (!sa) {
420 aireg = (aireg + 1) & ~1;
421 }
422 } else432 } else
423 infreg = sa && is_float(sv->type.t);433 infreg = sa && is_float(sv->type.t);
434 if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN)
435 aireg = (aireg + 1) & ~1;
424 pareg = infreg ? &afreg : &aireg;436 pareg = infreg ? &afreg : &aireg;
425 if ((*pareg < 8) && !force_stack) {437 if ((*pareg < 8) && !force_stack) {
426 info[i] = *pareg + (infreg ? 8 : 0);438 info[i] = *pareg + (infreg ? 8 : 0);
@@ -432,7 +444,6 @@ ST_FUNC void gfunc_call(int nb_args) · 5 unmodified lines
432 else {444 else {
433 info[i] |= 16;445 info[i] |= 16;
434 stack_adj += 8;446 stack_adj += 8;
435 tcc_error("unimp: param passing half in reg, half on stack");
436 }447 }
437 } else {448 } else {
438 info[i] = 32;449 info[i] = 32;
@@ -440,43 +451,76 @@ ST_FUNC void gfunc_call(int nb_args) · 1 unmodified line
440 if (!sa)451 if (!sa)
441 force_stack = 1;452 force_stack = 1;
442 }453 }
454 if (byref)
455 info[i] |= 64 | (tempofs << 7);
443 if (sa)456 if (sa)
444 sa = sa->next;457 sa = sa->next;
445 }458 }
446 stack_adj = (stack_adj + 15) & -16;459 stack_adj = (stack_adj + 15) & -16;
447 if (stack_adj) {460 tempspace = (tempspace + 15) & -16;
448 EI(0x13, 0, 2, 2, -stack_adj); // addi sp, sp, -adj461 if (stack_adj + tempspace) {
462 EI(0x13, 0, 2, 2, -(stack_adj + tempspace)); // addi sp, sp, -adj
449 for (i = ofs = 0; i < nb_args; i++) {463 for (i = ofs = 0; i < nb_args; i++) {
450 if (1 && info[i] >= 32) {464 if (info[i] >= 32) {
451 vrotb(nb_args - i);465 vrotb(nb_args - i);
452 size = type_size(&vtop->type, &align);466 size = type_size(&vtop->type, &align);
453 /* Once we support offseted regs we can do this:467 if (info[i] & 64) {
454 vset(&vtop->type, TREG_SP | VT_LVAL, ofs);468 vset(&char_pointer_type, TREG_SP, 0);
455 to construct the lvalue for the outgoing stack slot,469 vpushi(stack_adj + (info[i] >> 7));
456 until then we have to jump through hoops. */470 gen_op('+');
457 vset(&char_pointer_type, TREG_SP, 0);471 vpushv(vtop); // this replaces the old argument
458 vpushi(ofs);472 vrott(3);
459 gen_op('+');473 indir();
460 indir();474 vtop->type = vtop[-1].type;
461 vtop->type = vtop[-1].type;475 vswap();
462 vswap();476 vstore();
463 vstore();477 vpop();
478 size = align = 8;
479 }
480 if (info[i] & 32) {
481 /* Once we support offseted regs we can do this:
482 vset(&vtop->type, TREG_SP | VT_LVAL, ofs);
483 to construct the lvalue for the outgoing stack slot,
484 until then we have to jump through hoops. */
485 vset(&char_pointer_type, TREG_SP, 0);
486 ofs = (ofs + align - 1) & -align;
487 vpushi(ofs);
488 gen_op('+');
489 indir();
490 vtop->type = vtop[-1].type;
491 vswap();
492 vstore();
493 vtop->r = vtop->r2 = VT_CONST; // this arg is done
494 ofs += size;
495 }
464 vrott(nb_args - i);496 vrott(nb_args - i);
465 ofs += (size + align - 1) & -align;497 } else if (info[i] & 16) {
466 ofs = (ofs + 7) & -8;498 assert(!splitofs);
499 splitofs = ofs;
500 ofs += 8;
467 }501 }
468 }502 }
469 }503 }
470 for (i = 0; i < nb_args; i++) {504 for (i = 0; i < nb_args; i++) {
471 int r = info[nb_args - 1 - i];505 int r = info[nb_args - 1 - i];
472 if (r < 32) {506 if (!(r & 32)) {
507 CType origtype;
473 r &= 15;508 r &= 15;
474 vrotb(i+1);509 vrotb(i+1);
510 origtype = vtop->type;
511 size = type_size(&vtop->type, &align);
512 if (size > 8 && (vtop->type.t & VT_BTYPE) == VT_STRUCT)
513 vtop->type.t = VT_LDOUBLE; // force loading a pair of regs
475 gv(r < 8 ? RC_R(r) : RC_F(r - 8));514 gv(r < 8 ? RC_R(r) : RC_F(r - 8));
476 if (vtop->r2 < VT_CONST) {515 vtop->type = origtype;
477 assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE);516 if (size > 8) {
478 assert(vtop->r < 7);517 assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE
479 if (vtop->r2 != 1 + vtop->r) {518 || (vtop->type.t & VT_BTYPE) == VT_STRUCT);
519 assert(vtop->r2 < VT_CONST);
520 if (info[nb_args - 1 - i] & 16) {
521 ES(0x23, 3, 2, ireg(vtop->r2), splitofs); // sd t0, ofs(sp)
522 } else if (vtop->r2 != 1 + vtop->r) {
523 assert(vtop->r < 7);
480 /* XXX we'd like to have 'gv' move directly into524 /* XXX we'd like to have 'gv' move directly into
481 the right class instead of us fixing it up. */525 the right class instead of us fixing it up. */
482 EI(0x13, 0, ireg(vtop->r) + 1, ireg(vtop->r2), 0); // mv Ra+1, RR2526 EI(0x13, 0, ireg(vtop->r) + 1, ireg(vtop->r2), 0); // mv Ra+1, RR2
@@ -489,8 +533,8 @@ ST_FUNC void gfunc_call(int nb_args) · 6 unmodified lines
489 vrotb(nb_args + 1);533 vrotb(nb_args + 1);
490 gcall();534 gcall();
491 vtop -= nb_args + 1;535 vtop -= nb_args + 1;
492 if (stack_adj)536 if (stack_adj + tempspace)
493 EI(0x13, 0, 2, 2, stack_adj); // addi sp, sp, adj537 EI(0x13, 0, 2, 2, stack_adj + tempspace); // addi sp, sp, adj
494}538}
495 539
496static int func_sub_sp_offset;540static int func_sub_sp_offset;
@@ -514,12 +558,14 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 17 unmodified lines
514 558
515 aireg = afreg = 0;559 aireg = afreg = 0;
516 addr = 0; // XXX not correct560 addr = 0; // XXX not correct
517 /* if the function returns a structure, then add an561 /* if the function returns by reference, then add an
518 implicit pointer parameter */562 implicit pointer parameter */
519 size = type_size(&func_vt, &align);563 size = type_size(&func_vt, &align);
520 if (size > 2 * XLEN) {564 if (size > 2 * XLEN) {
521 tcc_error("unimp: struct return");565 loc -= 8;
522 func_vc = loc;566 func_vc = loc;
567 ES(0x23, 3, 8, 10 + aireg, loc); // sd a0, loc(s0)
568 aireg++;
523 }569 }
524 /* define parameters */570 /* define parameters */
525 while ((sym = sym->next) != NULL) {571 while ((sym = sym->next) != NULL) {
@@ -531,22 +577,30 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 5 unmodified lines
531 param_addr = addr;577 param_addr = addr;
532 addr += size;578 addr += size;
533 } else {579 } else {
534 int regcount = 1;580 int regcount = 1, *pareg = &aireg;
535 if (size > XLEN)581 if (is_float(type->t) && (type->t & VT_BTYPE) != VT_LDOUBLE)
536 regcount++, tcc_error("unimp: scalars > 64bit");582 pareg = &afreg;
537 if (regcount + (is_float(type->t) ? afreg : aireg) >= 8)583 if (regcount + *pareg > 8)
538 goto from_stack;584 goto from_stack;
585 if (size > XLEN)
586 regcount++;
539 loc -= regcount * 8; // XXX could reserve only 'size' bytes587 loc -= regcount * 8; // XXX could reserve only 'size' bytes
540 param_addr = loc;588 param_addr = loc;
541 for (i = 0; i < regcount; i++) {589 for (i = 0; i < regcount; i++) {
542 if (is_float(type->t)) {590 if (*pareg >= 8) {
591 assert(i == 1 && regcount == 2 && !(addr & 7));
592 EI(0x03, 3, 5, 8, addr); // ld t0, addr(s0)
593 addr += 8;
594 ES(0x23, 3, 8, 5, loc + i*8); // sd t0, loc(s0)
595 continue;
596 }
597 if (pareg == &afreg) {
543 assert(type->t == VT_FLOAT || type->t == VT_DOUBLE);598 assert(type->t == VT_FLOAT || type->t == VT_DOUBLE);
544 ES(0x27, size == 4 ? 2 : 3, 8, 10 + afreg, loc + i*8); // fs[wd] FAi, loc(s0)599 ES(0x27, size == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*8); // fs[wd] FAi, loc(s0)
545 afreg++;
546 } else {600 } else {
547 ES(0x23, 3, 8, 10 + aireg, loc + i*8); // sd aX, loc(s0) // XXX601 ES(0x23, 3, 8, 10 + *pareg, loc + i*8); // sd aX, loc(s0) // XXX
548 aireg++;
549 }602 }
603 (*pareg)++;
550 }604 }
551 }605 }
552 sym_push(sym->v & ~SYM_FIELD, type,606 sym_push(sym->v & ~SYM_FIELD, type,
@@ -560,17 +614,42 @@ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, · 7 unmodified lines
560 /* generic code can only deal with structs of pow(2) sizes614 /* generic code can only deal with structs of pow(2) sizes
561 (it always deals with whole registers), so go through our own615 (it always deals with whole registers), so go through our own
562 code. */616 code. */
563 return 0;617 int align, size = type_size(vt, &align);
618 *ret_align = 1;
619 *regsize = 8;
620 if (size > 16)
621 return 0;
622 if (size > 8)
623 ret->t = VT_LDOUBLE;
624 else if (size > 4)
625 ret->t = VT_LLONG;
626 else if (size > 2)
627 ret->t = VT_INT;
628 else if (size > 1)
629 ret->t = VT_SHORT;
630 else
631 ret->t = VT_BYTE;
632 return (size + 7) / 8;
564}633}
565 634
566ST_FUNC void gfunc_return(CType *func_type)635ST_FUNC void gfunc_return(CType *func_type)
567{636{
568 int align, size = type_size(func_type, &align);637 int align, size = type_size(func_type, &align), nregs;
569 if ((func_type->t & VT_BTYPE) == VT_STRUCT638 CType type = *func_type;
570 || size > 2 * XLEN) {639 if (size > 2 * XLEN) {
571 tcc_error("unimp: struct or large return");640 mk_pointer(&type);
641 vset(&type, VT_LOCAL | VT_LVAL, func_vc);
642 indir();
643 vswap();
644 vstore();
645 vpop();
646 return;
572 }647 }
573 if (is_float(func_type->t))648 nregs = (size + 7) / 8;
649 if (nregs == 2)
650 vtop->type.t = VT_LDOUBLE;
651
652 if (is_float(func_type->t) && (vtop->type.t & VT_BTYPE) != VT_LDOUBLE)
574 gv(RC_FRET);653 gv(RC_FRET);
575 else654 else
576 gv(RC_IRET);655 gv(RC_IRET);
@@ -868,17 +947,32 @@ ST_FUNC void gen_cvt_ftof(int dt) · 291 unmodified lines
868{947{
869 int st = vtop->type.t & VT_BTYPE, rs, rd;948 int st = vtop->type.t & VT_BTYPE, rs, rd;
870 dt &= VT_BTYPE;949 dt &= VT_BTYPE;
871 assert (dt == VT_FLOAT || dt == VT_DOUBLE);
872 assert (st == VT_FLOAT || st == VT_DOUBLE);
873 if (st == dt)950 if (st == dt)
874 return;951 return;
875 rs = gv(RC_FLOAT);952 if (dt == VT_LDOUBLE || st == VT_LDOUBLE) {
876 rd = get_reg(RC_FLOAT);953 int func = (dt == VT_LDOUBLE) ?
877 if (dt == VT_DOUBLE)954 (st == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) :
878 EI(0x53, 7, freg(rd), freg(rs), 0x21 << 5); // fcvt.d.s RD, RS (dyn rm)955 (st == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2);
879 else956 vpush_global_sym(&func_old_type, func);
880 EI(0x53, 7, freg(rd), freg(rs), (0x20 << 5) | 1); // fcvt.s.d RD, RS957 vrott(2);
881 vtop->r = rd;958 gfunc_call(1);
959 vpushi(0);
960 vtop->type.t = dt;
961 if (dt == VT_LDOUBLE)
962 vtop->r = REG_IRET, vtop->r2 = REG_IRET+1;
963 else
964 vtop->r = REG_FRET;
965 } else {
966 assert (dt == VT_FLOAT || dt == VT_DOUBLE);
967 assert (st == VT_FLOAT || st == VT_DOUBLE);
968 rs = gv(RC_FLOAT);
969 rd = get_reg(RC_FLOAT);
970 if (dt == VT_DOUBLE)
971 EI(0x53, 7, freg(rd), freg(rs), 0x21 << 5); // fcvt.d.s RD, RS (dyn rm)
972 else
973 EI(0x53, 7, freg(rd), freg(rs), (0x20 << 5) | 1); // fcvt.s.d RD, RS
974 vtop->r = rd;
975 }
882}976}
883 977
884ST_FUNC void ggoto(void)978ST_FUNC void ggoto(void)
tccgen.c+4−7
@@ -1172,8 +1172,7 @@ ST_FUNC void save_reg_upstack(int r, int n) · 1171 unmodified lines
1172 saved = 0;1172 saved = 0;
1173 l = 0;1173 l = 0;
1174 for(p = vstack, p1 = vtop - n; p <= p1; p++) {1174 for(p = vstack, p1 = vtop - n; p <= p1; p++) {
1175 if ((p->r & VT_VALMASK) == r ||1175 if ((p->r & VT_VALMASK) == r || (p->r2 & VT_VALMASK) == r) {
1176 ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
1177 /* must save value on stack if not already done */1176 /* must save value on stack if not already done */
1178 if (!saved) {1177 if (!saved) {
1179 /* NOTE: must reload 'r' because r might be equal to r2 */1178 /* NOTE: must reload 'r' because r might be equal to r2 */
@@ -1199,13 +1198,11 @@ ST_FUNC void save_reg_upstack(int r, int n) · 19 unmodified lines
1199 o(0xd8dd); /* fstp %st(0) */1198 o(0xd8dd); /* fstp %st(0) */
1200 }1199 }
1201#endif1200#endif
1202#if PTR_SIZE == 4
1203 /* special long long case */1201 /* special long long case */
1204 if ((type->t & VT_BTYPE) == VT_LLONG) {1202 if ((p->r2 & VT_VALMASK) < VT_CONST) {
1205 sv.c.i += 4;1203 sv.c.i += PTR_SIZE;
1206 store(p->r2, &sv);1204 store(p->r2, &sv);
1207 }1205 }
1208#endif
1209 saved = 1;1206 saved = 1;
1210 }1207 }
1211 /* mark that stack entry as being saved on the stack */1208 /* mark that stack entry as being saved on the stack */
@@ -2702,7 +2699,7 @@ static void gen_cast(CType *type) · 1490 unmodified lines
2702 c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;2699 c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
2703 p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);2700 p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
2704#if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_3872701#if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387
2705 c &= dbt != VT_LDOUBLE;2702 c &= (dbt != VT_LDOUBLE) | !!nocode_wanted;
2706#endif2703#endif
2707 if (c) {2704 if (c) {
2708 /* constant case: we can do it now */2705 /* constant case: we can do it now */
tcctok.h+1−1
@@ -261,7 +261,7 @@ · 260 unmodified lines
261#if defined TCC_TARGET_PE261#if defined TCC_TARGET_PE
262 DEF(TOK___chkstk, "__chkstk")262 DEF(TOK___chkstk, "__chkstk")
263#endif263#endif
264#ifdef TCC_TARGET_ARM64264#if defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64
265 DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")265 DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")
266 DEF(TOK___addtf3, "__addtf3")266 DEF(TOK___addtf3, "__addtf3")
267 DEF(TOK___subtf3, "__subtf3")267 DEF(TOK___subtf3, "__subtf3")

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