| @@ -1,7 +1,7 @@ |
| 1 | 1 | #ifdef TARGET_DEFS_ONLY |
| 2 | 2 | |
| 3 | 3 | // 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 |
| 5 | 5 | |
| 6 | 6 | #define TREG_R(x) (x) // x = 0..7 |
| 7 | 7 | #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 | 52 | RC_FLOAT | RC_F(4), |
| 53 | 53 | RC_FLOAT | RC_F(5), |
| 54 | 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 | |
| 58 | 61 | static int ireg(int r) |
| @@ -201,7 +204,7 @@ ST_FUNC void load(int r, SValue *sv) · 142 unmodified lines |
| 201 | 204 | } |
| 202 | 205 | } else if (v == VT_CONST) { |
| 203 | 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 | 208 | if (fr & VT_SYM) { |
| 206 | 209 | static Sym label; |
| 207 | 210 | 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 | 231 | rb = rr; |
| 229 | 232 | do32bit = 0; |
| 230 | 233 | } |
| 231 | | − if (is_float(sv->type.t)) |
| 234 | + if (is_float(sv->type.t) && bt != VT_LDOUBLE) |
| 232 | 235 | tcc_error("unimp: load(float)"); |
| 233 | 236 | if (fc != sv->c.i) { |
| 234 | 237 | int64_t si = sv->c.i; |
| @@ -317,7 +320,11 @@ ST_FUNC void store(int r, SValue *sv) · 82 unmodified lines |
| 317 | 320 | int ft = sv->type.t; |
| 318 | 321 | int bt = ft & VT_BTYPE; |
| 319 | 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 | 328 | if (bt == VT_STRUCT) |
| 322 | 329 | tcc_error("unimp: store(struct)"); |
| 323 | 330 | if (size > 8) |
| @@ -399,28 +406,33 @@ ST_FUNC void gfunc_call(int nb_args) · 75 unmodified lines |
| 399 | 406 | { |
| 400 | 407 | int i, align, size, aireg, afreg; |
| 401 | 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 | 410 | int force_stack = 0; |
| 404 | 411 | SValue *sv; |
| 405 | 412 | Sym *sa; |
| 406 | 413 | aireg = afreg = 0; |
| 407 | 414 | sa = vtop[-nb_args].type.ref->next; |
| 408 | 415 | for (i = 0; i < nb_args; i++) { |
| 409 | | − int *pareg, nregs, infreg = 0; |
| 416 | + int *pareg, nregs, infreg = 0, byref = 0, tempofs; |
| 410 | 417 | sv = &vtop[1 + i - nb_args]; |
| 411 | 418 | sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c |
| 412 | 419 | 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; |
| 417 | 430 | 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; |
| 422 | 432 | } else |
| 423 | 433 | infreg = sa && is_float(sv->type.t); |
| 434 | + if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN) |
| 435 | + aireg = (aireg + 1) & ~1; |
| 424 | 436 | pareg = infreg ? &afreg : &aireg; |
| 425 | 437 | if ((*pareg < 8) && !force_stack) { |
| 426 | 438 | info[i] = *pareg + (infreg ? 8 : 0); |
| @@ -432,7 +444,6 @@ ST_FUNC void gfunc_call(int nb_args) · 5 unmodified lines |
| 432 | 444 | else { |
| 433 | 445 | info[i] |= 16; |
| 434 | 446 | stack_adj += 8; |
| 435 | | − tcc_error("unimp: param passing half in reg, half on stack"); |
| 436 | 447 | } |
| 437 | 448 | } else { |
| 438 | 449 | info[i] = 32; |
| @@ -440,43 +451,76 @@ ST_FUNC void gfunc_call(int nb_args) · 1 unmodified line |
| 440 | 451 | if (!sa) |
| 441 | 452 | force_stack = 1; |
| 442 | 453 | } |
| 454 | + if (byref) |
| 455 | + info[i] |= 64 | (tempofs << 7); |
| 443 | 456 | if (sa) |
| 444 | 457 | sa = sa->next; |
| 445 | 458 | } |
| 446 | 459 | 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 |
| 449 | 463 | for (i = ofs = 0; i < nb_args; i++) { |
| 450 | | − if (1 && info[i] >= 32) { |
| 464 | + if (info[i] >= 32) { |
| 451 | 465 | vrotb(nb_args - i); |
| 452 | 466 | 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 | + } |
| 464 | 496 | 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; |
| 467 | 501 | } |
| 468 | 502 | } |
| 469 | 503 | } |
| 470 | 504 | for (i = 0; i < nb_args; i++) { |
| 471 | 505 | int r = info[nb_args - 1 - i]; |
| 472 | | − if (r < 32) { |
| 506 | + if (!(r & 32)) { |
| 507 | + CType origtype; |
| 473 | 508 | r &= 15; |
| 474 | 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 | 514 | 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); |
| 480 | 524 | /* XXX we'd like to have 'gv' move directly into |
| 481 | 525 | the right class instead of us fixing it up. */ |
| 482 | 526 | 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 | 533 | vrotb(nb_args + 1); |
| 490 | 534 | gcall(); |
| 491 | 535 | 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 |
| 494 | 538 | } |
| 495 | 539 | |
| 496 | 540 | static int func_sub_sp_offset; |
| @@ -514,12 +558,14 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 17 unmodified lines |
| 514 | 558 | |
| 515 | 559 | aireg = afreg = 0; |
| 516 | 560 | 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 |
| 518 | 562 | implicit pointer parameter */ |
| 519 | 563 | size = type_size(&func_vt, &align); |
| 520 | 564 | if (size > 2 * XLEN) { |
| 521 | | − tcc_error("unimp: struct return"); |
| 565 | + loc -= 8; |
| 522 | 566 | func_vc = loc; |
| 567 | + ES(0x23, 3, 8, 10 + aireg, loc); // sd a0, loc(s0) |
| 568 | + aireg++; |
| 523 | 569 | } |
| 524 | 570 | /* define parameters */ |
| 525 | 571 | while ((sym = sym->next) != NULL) { |
| @@ -531,22 +577,30 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 5 unmodified lines |
| 531 | 577 | param_addr = addr; |
| 532 | 578 | addr += size; |
| 533 | 579 | } 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) |
| 538 | 584 | goto from_stack; |
| 585 | + if (size > XLEN) |
| 586 | + regcount++; |
| 539 | 587 | loc -= regcount * 8; // XXX could reserve only 'size' bytes |
| 540 | 588 | param_addr = loc; |
| 541 | 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 | 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) |
| 545 | | − afreg++; |
| 599 | + ES(0x27, size == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*8); // fs[wd] FAi, loc(s0) |
| 546 | 600 | } 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 |
| 549 | 602 | } |
| 603 | + (*pareg)++; |
| 550 | 604 | } |
| 551 | 605 | } |
| 552 | 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 | 614 | /* generic code can only deal with structs of pow(2) sizes |
| 561 | 615 | (it always deals with whole registers), so go through our own |
| 562 | 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 | |
| 566 | 635 | ST_FUNC void gfunc_return(CType *func_type) |
| 567 | 636 | { |
| 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; |
| 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 | 653 | gv(RC_FRET); |
| 575 | 654 | else |
| 576 | 655 | gv(RC_IRET); |
| @@ -868,17 +947,32 @@ ST_FUNC void gen_cvt_ftof(int dt) · 291 unmodified lines |
| 868 | 947 | { |
| 869 | 948 | int st = vtop->type.t & VT_BTYPE, rs, rd; |
| 870 | 949 | dt &= VT_BTYPE; |
| 871 | | − assert (dt == VT_FLOAT || dt == VT_DOUBLE); |
| 872 | | − assert (st == VT_FLOAT || st == VT_DOUBLE); |
| 873 | 950 | if (st == dt) |
| 874 | 951 | 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 | + } |
| 882 | 976 | } |
| 883 | 977 | |
| 884 | 978 | ST_FUNC void ggoto(void) |