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 @@
11 #ifdef TARGET_DEFS_ONLY
22
33 // Number of registers available to allocator:
4−#define NB_REGS 16 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa7
4+#define NB_REGS 19 // x10-x17 aka a0-a7, f10-f17 aka fa0-fa7, xxx, ra, sp
55
66 #define TREG_R(x) (x) // x = 0..7
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
5252 RC_FLOAT | RC_F(4),
5353 RC_FLOAT | RC_F(5),
5454 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
5659 };
5760
5861 static int ireg(int r)
@@ -201,7 +204,7 @@ ST_FUNC void load(int r, SValue *sv) · 142 unmodified lines
201204 }
202205 } else if (v == VT_CONST) {
203206 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);
205208 if (fr & VT_SYM) {
206209 static Sym label;
207210 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
228231 rb = rr;
229232 do32bit = 0;
230233 }
231− if (is_float(sv->type.t))
234+ if (is_float(sv->type.t) && bt != VT_LDOUBLE)
232235 tcc_error("unimp: load(float)");
233236 if (fc != sv->c.i) {
234237 int64_t si = sv->c.i;
@@ -317,7 +320,11 @@ ST_FUNC void store(int r, SValue *sv) · 82 unmodified lines
317320 int ft = sv->type.t;
318321 int bt = ft & VT_BTYPE;
319322 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;
321328 if (bt == VT_STRUCT)
322329 tcc_error("unimp: store(struct)");
323330 if (size > 8)
@@ -399,28 +406,33 @@ ST_FUNC void gfunc_call(int nb_args) · 75 unmodified lines
399406 {
400407 int i, align, size, aireg, afreg;
401408 int info[nb_args ? nb_args : 1];
402− int stack_adj = 0, ofs;
409+ int stack_adj = 0, tempspace = 0, ofs, splitofs = 0;
403410 int force_stack = 0;
404411 SValue *sv;
405412 Sym *sa;
406413 aireg = afreg = 0;
407414 sa = vtop[-nb_args].type.ref->next;
408415 for (i = 0; i < nb_args; i++) {
409− int *pareg, nregs, infreg = 0;
416+ int *pareg, nregs, infreg = 0, byref = 0, tempofs;
410417 sv = &vtop[1 + i - nb_args];
411418 sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c
412419 size = type_size(&sv->type, &align);
413− if ((size > 8 && ((sv->type.t & VT_BTYPE) != VT_LDOUBLE))
414− || ((sv->type.t & VT_BTYPE) == VT_STRUCT))
415− tcc_error("unimp: call arg %d wrong type", nb_args - i);
416− nregs = 1;
420+ if (size > 16) {
421+ tempspace = (tempspace + align - 1) & -align;
422+ tempofs = tempspace;
423+ tempspace += size;
424+ size = align = 8;
425+ byref = 1;
426+ } else if (size > 8)
427+ nregs = 2;
428+ else
429+ nregs = 1;
417430 if ((sv->type.t & VT_BTYPE) == VT_LDOUBLE) {
418− infreg = 0, nregs = 2;
419− if (!sa) {
420− aireg = (aireg + 1) & ~1;
421− }
431+ infreg = 0;
422432 } else
423433 infreg = sa && is_float(sv->type.t);
434+ if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN)
435+ aireg = (aireg + 1) & ~1;
424436 pareg = infreg ? &afreg : &aireg;
425437 if ((*pareg < 8) && !force_stack) {
426438 info[i] = *pareg + (infreg ? 8 : 0);
@@ -432,7 +444,6 @@ ST_FUNC void gfunc_call(int nb_args) · 5 unmodified lines
432444 else {
433445 info[i] |= 16;
434446 stack_adj += 8;
435− tcc_error("unimp: param passing half in reg, half on stack");
436447 }
437448 } else {
438449 info[i] = 32;
@@ -440,43 +451,76 @@ ST_FUNC void gfunc_call(int nb_args) · 1 unmodified line
440451 if (!sa)
441452 force_stack = 1;
442453 }
454+ if (byref)
455+ info[i] |= 64 | (tempofs << 7);
443456 if (sa)
444457 sa = sa->next;
445458 }
446459 stack_adj = (stack_adj + 15) & -16;
447− if (stack_adj) {
448− EI(0x13, 0, 2, 2, -stack_adj); // addi sp, sp, -adj
460+ tempspace = (tempspace + 15) & -16;
461+ if (stack_adj + tempspace) {
462+ EI(0x13, 0, 2, 2, -(stack_adj + tempspace)); // addi sp, sp, -adj
449463 for (i = ofs = 0; i < nb_args; i++) {
450− if (1 && info[i] >= 32) {
464+ if (info[i] >= 32) {
451465 vrotb(nb_args - i);
452466 size = type_size(&vtop->type, &align);
453− /* Once we support offseted regs we can do this:
454− vset(&vtop->type, TREG_SP | VT_LVAL, ofs);
455− to construct the lvalue for the outgoing stack slot,
456− until then we have to jump through hoops. */
457− vset(&char_pointer_type, TREG_SP, 0);
458− vpushi(ofs);
459− gen_op('+');
460− indir();
461− vtop->type = vtop[-1].type;
462− vswap();
463− vstore();
467+ if (info[i] & 64) {
468+ vset(&char_pointer_type, TREG_SP, 0);
469+ vpushi(stack_adj + (info[i] >> 7));
470+ gen_op('+');
471+ vpushv(vtop); // this replaces the old argument
472+ vrott(3);
473+ indir();
474+ vtop->type = vtop[-1].type;
475+ vswap();
476+ 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+ }
464496 vrott(nb_args - i);
465− ofs += (size + align - 1) & -align;
466− ofs = (ofs + 7) & -8;
497+ } else if (info[i] & 16) {
498+ assert(!splitofs);
499+ splitofs = ofs;
500+ ofs += 8;
467501 }
468502 }
469503 }
470504 for (i = 0; i < nb_args; i++) {
471505 int r = info[nb_args - 1 - i];
472− if (r < 32) {
506+ if (!(r & 32)) {
507+ CType origtype;
473508 r &= 15;
474509 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
475514 gv(r < 8 ? RC_R(r) : RC_F(r - 8));
476− if (vtop->r2 < VT_CONST) {
477− assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE);
478− assert(vtop->r < 7);
479− if (vtop->r2 != 1 + vtop->r) {
515+ vtop->type = origtype;
516+ if (size > 8) {
517+ assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE
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);
480524 /* XXX we'd like to have 'gv' move directly into
481525 the right class instead of us fixing it up. */
482526 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
489533 vrotb(nb_args + 1);
490534 gcall();
491535 vtop -= nb_args + 1;
492− if (stack_adj)
493− EI(0x13, 0, 2, 2, stack_adj); // addi sp, sp, adj
536+ if (stack_adj + tempspace)
537+ EI(0x13, 0, 2, 2, stack_adj + tempspace); // addi sp, sp, adj
494538 }
495539
496540 static int func_sub_sp_offset;
@@ -514,12 +558,14 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 17 unmodified lines
514558
515559 aireg = afreg = 0;
516560 addr = 0; // XXX not correct
517− /* if the function returns a structure, then add an
561+ /* if the function returns by reference, then add an
518562 implicit pointer parameter */
519563 size = type_size(&func_vt, &align);
520564 if (size > 2 * XLEN) {
521− tcc_error("unimp: struct return");
565+ loc -= 8;
522566 func_vc = loc;
567+ ES(0x23, 3, 8, 10 + aireg, loc); // sd a0, loc(s0)
568+ aireg++;
523569 }
524570 /* define parameters */
525571 while ((sym = sym->next) != NULL) {
@@ -531,22 +577,30 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 5 unmodified lines
531577 param_addr = addr;
532578 addr += size;
533579 } else {
534− int regcount = 1;
535− if (size > XLEN)
536− regcount++, tcc_error("unimp: scalars > 64bit");
537− if (regcount + (is_float(type->t) ? afreg : aireg) >= 8)
580+ int regcount = 1, *pareg = &aireg;
581+ if (is_float(type->t) && (type->t & VT_BTYPE) != VT_LDOUBLE)
582+ pareg = &afreg;
583+ if (regcount + *pareg > 8)
538584 goto from_stack;
585+ if (size > XLEN)
586+ regcount++;
539587 loc -= regcount * 8; // XXX could reserve only 'size' bytes
540588 param_addr = loc;
541589 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) {
543598 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)
545− afreg++;
599+ ES(0x27, size == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*8); // fs[wd] FAi, loc(s0)
546600 } else {
547− ES(0x23, 3, 8, 10 + aireg, loc + i*8); // sd aX, loc(s0) // XXX
548− aireg++;
601+ ES(0x23, 3, 8, 10 + *pareg, loc + i*8); // sd aX, loc(s0) // XXX
549602 }
603+ (*pareg)++;
550604 }
551605 }
552606 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
560614 /* generic code can only deal with structs of pow(2) sizes
561615 (it always deals with whole registers), so go through our own
562616 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;
564633 }
565634
566635 ST_FUNC void gfunc_return(CType *func_type)
567636 {
568− int align, size = type_size(func_type, &align);
569− if ((func_type->t & VT_BTYPE) == VT_STRUCT
570− || size > 2 * XLEN) {
571− tcc_error("unimp: struct or large return");
637+ int align, size = type_size(func_type, &align), nregs;
638+ CType type = *func_type;
639+ if (size > 2 * XLEN) {
640+ mk_pointer(&type);
641+ vset(&type, VT_LOCAL | VT_LVAL, func_vc);
642+ indir();
643+ vswap();
644+ vstore();
645+ vpop();
646+ return;
572647 }
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)
574653 gv(RC_FRET);
575654 else
576655 gv(RC_IRET);
@@ -868,17 +947,32 @@ ST_FUNC void gen_cvt_ftof(int dt) · 291 unmodified lines
868947 {
869948 int st = vtop->type.t & VT_BTYPE, rs, rd;
870949 dt &= VT_BTYPE;
871− assert (dt == VT_FLOAT || dt == VT_DOUBLE);
872− assert (st == VT_FLOAT || st == VT_DOUBLE);
873950 if (st == dt)
874951 return;
875− rs = gv(RC_FLOAT);
876− rd = get_reg(RC_FLOAT);
877− if (dt == VT_DOUBLE)
878− EI(0x53, 7, freg(rd), freg(rs), 0x21 << 5); // fcvt.d.s RD, RS (dyn rm)
879− else
880− EI(0x53, 7, freg(rd), freg(rs), (0x20 << 5) | 1); // fcvt.s.d RD, RS
881− vtop->r = rd;
952+ if (dt == VT_LDOUBLE || st == VT_LDOUBLE) {
953+ int func = (dt == VT_LDOUBLE) ?
954+ (st == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) :
955+ (st == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2);
956+ vpush_global_sym(&func_old_type, func);
957+ vrott(2);
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+ }
882976 }
883977
884978 ST_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
11721172 saved = 0;
11731173 l = 0;
11741174 for(p = vstack, p1 = vtop - n; p <= p1; p++) {
1175− if ((p->r & VT_VALMASK) == r ||
1176− ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
1175+ if ((p->r & VT_VALMASK) == r || (p->r2 & VT_VALMASK) == r) {
11771176 /* must save value on stack if not already done */
11781177 if (!saved) {
11791178 /* 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
11991198 o(0xd8dd); /* fstp %st(0) */
12001199 }
12011200 #endif
1202−#if PTR_SIZE == 4
12031201 /* special long long case */
1204− if ((type->t & VT_BTYPE) == VT_LLONG) {
1205− sv.c.i += 4;
1202+ if ((p->r2 & VT_VALMASK) < VT_CONST) {
1203+ sv.c.i += PTR_SIZE;
12061204 store(p->r2, &sv);
12071205 }
1208−#endif
12091206 saved = 1;
12101207 }
12111208 /* mark that stack entry as being saved on the stack */
@@ -2702,7 +2699,7 @@ static void gen_cast(CType *type) · 1490 unmodified lines
27022699 c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
27032700 p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
27042701 #if !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387
2705− c &= dbt != VT_LDOUBLE;
2702+ c &= (dbt != VT_LDOUBLE) | !!nocode_wanted;
27062703 #endif
27072704 if (c) {
27082705 /* constant case: we can do it now */
tcctok.h+1−1
@@ -261,7 +261,7 @@ · 260 unmodified lines
261261 #if defined TCC_TARGET_PE
262262 DEF(TOK___chkstk, "__chkstk")
263263 #endif
264−#ifdef TCC_TARGET_ARM64
264+#if defined TCC_TARGET_ARM64 || defined TCC_TARGET_RISCV64
265265 DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")
266266 DEF(TOK___addtf3, "__addtf3")
267267 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