| @@ -1,7 +1,7 @@ |
| 1 | #ifdef TARGET_DEFS_ONLY | 1 | #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-fa7 | 4 | #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..7 | 6 | #define TREG_R(x) (x) // x = 0..7 |
| 7 | #define TREG_F(x) (x + 8) // x = 0..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 | 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 | |
| 58 | static int ireg(int r) | 61 | static 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 relax | 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 | 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.c | 418 | 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 | } else | 432 | } 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, -adj | 461 | 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 into | 524 | /* 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, RR2 | 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 | 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, adj | 537 | EI(0x13, 0, 2, 2, stack_adj + tempspace); // addi sp, sp, adj |
| 494 | } | 538 | } |
| 495 | | 539 | |
| 496 | static int func_sub_sp_offset; | 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 | aireg = afreg = 0; | 559 | aireg = afreg = 0; |
| 516 | addr = 0; // XXX not correct | 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 | 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' bytes | 587 | 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) // XXX | 601 | 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) sizes | 614 | /* generic code can only deal with structs of pow(2) sizes |
| 561 | (it always deals with whole registers), so go through our own | 615 | (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 | |
| 566 | ST_FUNC void gfunc_return(CType *func_type) | 635 | ST_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_STRUCT | 638 | 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 | else | 654 | 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 | else | 956 | vpush_global_sym(&func_old_type, func); |
| 880 | EI(0x53, 7, freg(rd), freg(rs), (0x20 << 5) | 1); // fcvt.s.d RD, RS | 957 | 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 | |
| 884 | ST_FUNC void ggoto(void) | 978 | ST_FUNC void ggoto(void) |