abi.c (12877B)
1 #include "all.h" 2 3 /* the risc-v lp64d abi */ 4 5 typedef struct Class Class; 6 typedef struct Insl Insl; 7 typedef struct Params Params; 8 9 enum { 10 Cptr = 1, /* replaced by a pointer */ 11 Cstk1 = 2, /* pass first XLEN on the stack */ 12 Cstk2 = 4, /* pass second XLEN on the stack */ 13 Cstk = Cstk1 | Cstk2, 14 Cfpint = 8, /* float passed like integer */ 15 }; 16 17 struct Class { 18 char class; 19 Typ *type; 20 int reg[2]; 21 int cls[2]; 22 int off[2]; 23 char ngp; /* only valid after typclass() */ 24 char nfp; /* ditto */ 25 char nreg; 26 }; 27 28 struct Insl { 29 Ins i; 30 Insl *link; 31 }; 32 33 struct Params { 34 int ngp; 35 int nfp; 36 int stk; /* stack offset for varargs */ 37 }; 38 39 static int gpreg[10] = {A0, A1, A2, A3, A4, A5, A6, A7}; 40 static int fpreg[10] = {FA0, FA1, FA2, FA3, FA4, FA5, FA6, FA7}; 41 42 /* layout of call's second argument (RCall) 43 * 44 * 29 12 8 4 2 0 45 * |0.00|x|xxxx|xxxx|xx|xx| range 46 * | | | | ` gp regs returned (0..2) 47 * | | | ` fp regs returned (0..2) 48 * | | ` gp regs passed (0..8) 49 * | ` fp regs passed (0..8) 50 * ` env pointer passed in t5 (0..1) 51 */ 52 53 bits 54 rv64_retregs(Ref r, int p[2]) 55 { 56 bits b; 57 int ngp, nfp; 58 59 assert(rtype(r) == RCall); 60 ngp = r.val & 3; 61 nfp = (r.val >> 2) & 3; 62 if (p) { 63 p[0] = ngp; 64 p[1] = nfp; 65 } 66 b = 0; 67 while (ngp--) 68 b |= BIT(A0+ngp); 69 while (nfp--) 70 b |= BIT(FA0+nfp); 71 return b; 72 } 73 74 bits 75 rv64_argregs(Ref r, int p[2]) 76 { 77 bits b; 78 int ngp, nfp, t5; 79 80 assert(rtype(r) == RCall); 81 ngp = (r.val >> 4) & 15; 82 nfp = (r.val >> 8) & 15; 83 t5 = (r.val >> 12) & 1; 84 if (p) { 85 p[0] = ngp + t5; 86 p[1] = nfp; 87 } 88 b = 0; 89 while (ngp--) 90 b |= BIT(A0+ngp); 91 while (nfp--) 92 b |= BIT(FA0+nfp); 93 return b | ((bits)t5 << T5); 94 } 95 96 static int 97 fpstruct(Typ *t, int off, Class *c) 98 { 99 Field *f; 100 int n; 101 102 if (t->isunion) 103 return -1; 104 105 for (f=*t->fields; f->type != FEnd; f++) 106 if (f->type == FPad) 107 off += f->len; 108 else if (f->type == FTyp) { 109 if (fpstruct(&typ[f->len], off, c) == -1) 110 return -1; 111 } 112 else { 113 n = c->nfp + c->ngp; 114 if (n == 2) 115 return -1; 116 switch (f->type) { 117 default: die("unreachable"); 118 case Fb: 119 case Fh: 120 case Fw: c->cls[n] = Kw; c->ngp++; break; 121 case Fl: c->cls[n] = Kl; c->ngp++; break; 122 case Fs: c->cls[n] = Ks; c->nfp++; break; 123 case Fd: c->cls[n] = Kd; c->nfp++; break; 124 } 125 c->off[n] = off; 126 off += f->len; 127 } 128 129 return c->nfp; 130 } 131 132 static void 133 typclass(Class *c, Typ *t, int fpabi, int *gp, int *fp) 134 { 135 uint n; 136 int i; 137 138 c->type = t; 139 c->class = 0; 140 c->ngp = 0; 141 c->nfp = 0; 142 143 if (t->align > 4) 144 err("alignments larger than 16 are not supported"); 145 146 if (t->isdark || t->size > 16 || t->size == 0) { 147 /* large structs are replaced by a 148 * pointer to some caller-allocated 149 * memory 150 */ 151 c->class |= Cptr; 152 *c->cls = Kl; 153 *c->off = 0; 154 c->ngp = 1; 155 } 156 else if (!fpabi || fpstruct(t, 0, c) <= 0) { 157 for (n=0; 8*n<t->size; n++) { 158 c->cls[n] = Kl; 159 c->off[n] = 8*n; 160 } 161 c->nfp = 0; 162 c->ngp = n; 163 } 164 165 c->nreg = c->nfp + c->ngp; 166 for (i=0; i<c->nreg; i++) 167 if (KBASE(c->cls[i]) == 0) 168 c->reg[i] = *gp++; 169 else 170 c->reg[i] = *fp++; 171 } 172 173 static void 174 sttmps(Ref tmp[], int ntmp, Class *c, Ref mem, Fn *fn) 175 { 176 static int st[] = { 177 [Kw] = Ostorew, [Kl] = Ostorel, 178 [Ks] = Ostores, [Kd] = Ostored 179 }; 180 int i; 181 Ref r; 182 183 assert(ntmp > 0); 184 assert(ntmp <= 2); 185 for (i=0; i<ntmp; i++) { 186 tmp[i] = newtmp("abi", c->cls[i], fn); 187 r = newtmp("abi", Kl, fn); 188 emit(st[c->cls[i]], 0, R, tmp[i], r); 189 emit(Oadd, Kl, r, mem, getcon(c->off[i], fn)); 190 } 191 } 192 193 static void 194 ldregs(Class *c, Ref mem, Fn *fn) 195 { 196 int i; 197 Ref r; 198 199 for (i=0; i<c->nreg; i++) { 200 r = newtmp("abi", Kl, fn); 201 emit(Oload, c->cls[i], TMP(c->reg[i]), r, R); 202 emit(Oadd, Kl, r, mem, getcon(c->off[i], fn)); 203 } 204 } 205 206 static void 207 selret(Blk *b, Fn *fn) 208 { 209 int j, k, cty; 210 Ref r; 211 Class cr; 212 213 j = b->jmp.type; 214 215 if (!isret(j) || j == Jret0) 216 return; 217 218 r = b->jmp.arg; 219 b->jmp.type = Jret0; 220 221 if (j == Jretc) { 222 typclass(&cr, &typ[fn->retty], 1, gpreg, fpreg); 223 if (cr.class & Cptr) { 224 assert(rtype(fn->retr) == RTmp); 225 emit(Oblit1, 0, R, INT(cr.type->size), R); 226 emit(Oblit0, 0, R, r, fn->retr); 227 cty = 0; 228 } else { 229 ldregs(&cr, r, fn); 230 cty = (cr.nfp << 2) | cr.ngp; 231 } 232 } else { 233 k = j - Jretw; 234 if (KBASE(k) == 0) { 235 emit(Ocopy, k, TMP(A0), r, R); 236 cty = 1; 237 } else { 238 emit(Ocopy, k, TMP(FA0), r, R); 239 cty = 1 << 2; 240 } 241 } 242 243 b->jmp.arg = CALL(cty); 244 } 245 246 static int 247 argsclass(Ins *i0, Ins *i1, Class *carg, int retptr) 248 { 249 int ngp, nfp, *gp, *fp, vararg, envc; 250 Class *c; 251 Typ *t; 252 Ins *i; 253 254 gp = gpreg; 255 fp = fpreg; 256 ngp = 8; 257 nfp = 8; 258 vararg = 0; 259 envc = 0; 260 if (retptr) { 261 gp++; 262 ngp--; 263 } 264 for (i=i0, c=carg; i<i1; i++, c++) { 265 switch (i->op) { 266 case Opar: 267 case Oarg: 268 *c->cls = i->cls; 269 if (!vararg && KBASE(i->cls) == 1 && nfp > 0) { 270 nfp--; 271 *c->reg = *fp++; 272 } else if (ngp > 0) { 273 if (KBASE(i->cls) == 1) 274 c->class |= Cfpint; 275 ngp--; 276 *c->reg = *gp++; 277 } else 278 c->class |= Cstk1; 279 break; 280 case Oargv: 281 vararg = 1; 282 break; 283 case Oparc: 284 case Oargc: 285 t = &typ[i->arg[0].val]; 286 typclass(c, t, 1, gp, fp); 287 if (c->nfp > 0) 288 if (c->nfp >= nfp || c->ngp >= ngp) 289 typclass(c, t, 0, gp, fp); 290 assert(c->nfp <= nfp); 291 if (c->ngp <= ngp) { 292 ngp -= c->ngp; 293 nfp -= c->nfp; 294 gp += c->ngp; 295 fp += c->nfp; 296 } else if (ngp > 0) { 297 assert(c->ngp == 2); 298 assert(c->class == 0); 299 c->class |= Cstk2; 300 c->nreg = 1; 301 ngp--; 302 gp++; 303 } else { 304 c->class |= Cstk1; 305 if (c->nreg > 1) 306 c->class |= Cstk2; 307 c->nreg = 0; 308 } 309 break; 310 case Opare: 311 case Oarge: 312 *c->reg = T5; 313 *c->cls = Kl; 314 envc = 1; 315 break; 316 } 317 } 318 return envc << 12 | (gp-gpreg) << 4 | (fp-fpreg) << 8; 319 } 320 321 static void 322 stkblob(Ref r, Typ *t, Fn *fn, Insl **ilp) 323 { 324 Insl *il; 325 int al; 326 uint64_t sz; 327 328 il = alloc(sizeof *il); 329 al = t->align - 2; /* specific to NAlign == 3 */ 330 if (al < 0) 331 al = 0; 332 sz = (t->size + 7) & ~7; 333 il->i = (Ins){Oalloc+al, Kl, r, {getcon(sz, fn)}}; 334 il->link = *ilp; 335 *ilp = il; 336 } 337 338 static void 339 selcall(Fn *fn, Ins *i0, Ins *i1, Insl **ilp) 340 { 341 Ins *i; 342 Class *ca, *c, cr; 343 int j, k, cty; 344 uint64_t stk, off; 345 Ref r, r1, r2, tmp[2]; 346 347 ca = alloc((i1-i0) * sizeof ca[0]); 348 cr.class = 0; 349 350 if (!req(i1->arg[1], R)) 351 typclass(&cr, &typ[i1->arg[1].val], 1, gpreg, fpreg); 352 353 cty = argsclass(i0, i1, ca, cr.class & Cptr); 354 stk = 0; 355 for (i=i0, c=ca; i<i1; i++, c++) { 356 if (i->op == Oargv) 357 continue; 358 if (c->class & Cptr) { 359 i->arg[0] = newtmp("abi", Kl, fn); 360 stkblob(i->arg[0], c->type, fn, ilp); 361 i->op = Oarg; 362 } 363 if (c->class & Cstk1) 364 stk += 8; 365 if (c->class & Cstk2) 366 stk += 8; 367 } 368 stk += stk & 15; 369 if (stk) 370 emit(Osalloc, Kl, R, getcon(-stk, fn), R); 371 372 if (!req(i1->arg[1], R)) { 373 stkblob(i1->to, cr.type, fn, ilp); 374 cty |= (cr.nfp << 2) | cr.ngp; 375 if (cr.class & Cptr) 376 /* spill & rega expect calls to be 377 * followed by copies from regs, 378 * so we emit a dummy 379 */ 380 emit(Ocopy, Kw, R, TMP(A0), R); 381 else { 382 sttmps(tmp, cr.nreg, &cr, i1->to, fn); 383 for (j=0; j<cr.nreg; j++) { 384 r = TMP(cr.reg[j]); 385 emit(Ocopy, cr.cls[j], tmp[j], r, R); 386 } 387 } 388 } else if (KBASE(i1->cls) == 0) { 389 emit(Ocopy, i1->cls, i1->to, TMP(A0), R); 390 cty |= 1; 391 } else { 392 emit(Ocopy, i1->cls, i1->to, TMP(FA0), R); 393 cty |= 1 << 2; 394 } 395 396 emit(Ocall, 0, R, i1->arg[0], CALL(cty)); 397 398 if (cr.class & Cptr) 399 /* struct return argument */ 400 emit(Ocopy, Kl, TMP(A0), i1->to, R); 401 402 /* move arguments into registers */ 403 for (i=i0, c=ca; i<i1; i++, c++) { 404 if (i->op == Oargv || c->class & Cstk1) 405 continue; 406 if (i->op == Oargc) { 407 ldregs(c, i->arg[1], fn); 408 } else if (c->class & Cfpint) { 409 k = KWIDE(*c->cls) ? Kl : Kw; 410 r = newtmp("abi", k, fn); 411 emit(Ocopy, k, TMP(*c->reg), r, R); 412 *c->reg = r.val; 413 } else { 414 emit(Ocopy, *c->cls, TMP(*c->reg), i->arg[0], R); 415 } 416 } 417 418 for (i=i0, c=ca; i<i1; i++, c++) { 419 if (c->class & Cfpint) { 420 k = KWIDE(*c->cls) ? Kl : Kw; 421 emit(Ocast, k, TMP(*c->reg), i->arg[0], R); 422 } 423 if (c->class & Cptr) { 424 emit(Oblit1, 0, R, INT(c->type->size), R); 425 emit(Oblit0, 0, R, i->arg[1], i->arg[0]); 426 } 427 } 428 429 if (!stk) 430 return; 431 432 /* populate the stack */ 433 off = 0; 434 r = newtmp("abi", Kl, fn); 435 for (i=i0, c=ca; i<i1; i++, c++) { 436 if (i->op == Oargv || !(c->class & Cstk)) 437 continue; 438 if (i->op == Oarg) { 439 r1 = newtmp("abi", Kl, fn); 440 emit(Ostorew+i->cls, Kw, R, i->arg[0], r1); 441 if (i->cls == Kw) { 442 /* TODO: we only need this sign 443 * extension for l temps passed 444 * as w arguments 445 * (see rv64/isel.c:fixarg) 446 */ 447 curi->op = Ostorel; 448 curi->arg[0] = newtmp("abi", Kl, fn); 449 emit(Oextsw, Kl, curi->arg[0], i->arg[0], R); 450 } 451 emit(Oadd, Kl, r1, r, getcon(off, fn)); 452 off += 8; 453 } 454 if (i->op == Oargc) { 455 if (c->class & Cstk1) { 456 r1 = newtmp("abi", Kl, fn); 457 r2 = newtmp("abi", Kl, fn); 458 emit(Ostorel, 0, R, r2, r1); 459 emit(Oadd, Kl, r1, r, getcon(off, fn)); 460 emit(Oload, Kl, r2, i->arg[1], R); 461 off += 8; 462 } 463 if (c->class & Cstk2) { 464 r1 = newtmp("abi", Kl, fn); 465 r2 = newtmp("abi", Kl, fn); 466 emit(Ostorel, 0, R, r2, r1); 467 emit(Oadd, Kl, r1, r, getcon(off, fn)); 468 r1 = newtmp("abi", Kl, fn); 469 emit(Oload, Kl, r2, r1, R); 470 emit(Oadd, Kl, r1, i->arg[1], getcon(8, fn)); 471 off += 8; 472 } 473 } 474 } 475 emit(Osalloc, Kl, r, getcon(stk, fn), R); 476 } 477 478 static Params 479 selpar(Fn *fn, Ins *i0, Ins *i1) 480 { 481 Class *ca, *c, cr; 482 Insl *il; 483 Ins *i; 484 int j, k, s, cty, nt; 485 Ref r, tmp[17], *t; 486 487 ca = alloc((i1-i0) * sizeof ca[0]); 488 cr.class = 0; 489 curi = &insb[NIns]; 490 491 if (fn->retty >= 0) { 492 typclass(&cr, &typ[fn->retty], 1, gpreg, fpreg); 493 if (cr.class & Cptr) { 494 fn->retr = newtmp("abi", Kl, fn); 495 emit(Ocopy, Kl, fn->retr, TMP(A0), R); 496 } 497 } 498 499 cty = argsclass(i0, i1, ca, cr.class & Cptr); 500 fn->reg = rv64_argregs(CALL(cty), 0); 501 502 il = 0; 503 t = tmp; 504 for (i=i0, c=ca; i<i1; i++, c++) { 505 if (c->class & Cfpint) { 506 r = i->to; 507 k = *c->cls; 508 *c->cls = KWIDE(k) ? Kl : Kw; 509 i->to = newtmp("abi", k, fn); 510 emit(Ocast, k, r, i->to, R); 511 } 512 if (i->op == Oparc) 513 if (!(c->class & Cptr)) 514 if (c->nreg != 0) { 515 nt = c->nreg; 516 if (c->class & Cstk2) { 517 c->cls[1] = Kl; 518 c->off[1] = 8; 519 assert(nt == 1); 520 nt = 2; 521 } 522 sttmps(t, nt, c, i->to, fn); 523 stkblob(i->to, c->type, fn, &il); 524 t += nt; 525 } 526 } 527 for (; il; il=il->link) 528 emiti(il->i); 529 530 t = tmp; 531 s = 2 + 8*fn->vararg; 532 for (i=i0, c=ca; i<i1; i++, c++) 533 if (i->op == Oparc && !(c->class & Cptr)) { 534 if (c->nreg == 0) { 535 fn->tmp[i->to.val].slot = -s; 536 s += (c->class & Cstk2) ? 2 : 1; 537 continue; 538 } 539 for (j=0; j<c->nreg; j++) { 540 r = TMP(c->reg[j]); 541 emit(Ocopy, c->cls[j], *t++, r, R); 542 } 543 if (c->class & Cstk2) { 544 emit(Oload, Kl, *t, SLOT(-s), R); 545 t++, s++; 546 } 547 } else if (c->class & Cstk1) { 548 emit(Oload, *c->cls, i->to, SLOT(-s), R); 549 s++; 550 } else { 551 emit(Ocopy, *c->cls, i->to, TMP(*c->reg), R); 552 } 553 554 return (Params){ 555 .stk = s, 556 .ngp = (cty >> 4) & 15, 557 .nfp = (cty >> 8) & 15, 558 }; 559 } 560 561 static void 562 selvaarg(Fn *fn, Ins *i) 563 { 564 Ref loc, newloc; 565 566 loc = newtmp("abi", Kl, fn); 567 newloc = newtmp("abi", Kl, fn); 568 emit(Ostorel, Kw, R, newloc, i->arg[0]); 569 emit(Oadd, Kl, newloc, loc, getcon(8, fn)); 570 emit(Oload, i->cls, i->to, loc, R); 571 emit(Oload, Kl, loc, i->arg[0], R); 572 } 573 574 static void 575 selvastart(Fn *fn, Params p, Ref ap) 576 { 577 Ref rsave; 578 int s; 579 580 rsave = newtmp("abi", Kl, fn); 581 emit(Ostorel, Kw, R, rsave, ap); 582 s = p.stk > 2 + 8 * fn->vararg ? p.stk : 2 + p.ngp; 583 emit(Oaddr, Kl, rsave, SLOT(-s), R); 584 } 585 586 void 587 rv64_abi(Fn *fn) 588 { 589 Blk *b; 590 Ins *i, *i0, *ip; 591 Insl *il; 592 int n; 593 Params p; 594 595 for (b=fn->start; b; b=b->link) 596 b->visit = 0; 597 598 /* lower parameters */ 599 for (b=fn->start, i=b->ins; i<&b->ins[b->nins]; i++) 600 if (!ispar(i->op)) 601 break; 602 p = selpar(fn, b->ins, i); 603 n = b->nins - (i - b->ins) + (&insb[NIns] - curi); 604 i0 = alloc(n * sizeof(Ins)); 605 ip = icpy(ip = i0, curi, &insb[NIns] - curi); 606 ip = icpy(ip, i, &b->ins[b->nins] - i); 607 b->nins = n; 608 b->ins = i0; 609 610 /* lower calls, returns, and vararg instructions */ 611 il = 0; 612 b = fn->start; 613 do { 614 if (!(b = b->link)) 615 b = fn->start; /* do it last */ 616 if (b->visit) 617 continue; 618 curi = &insb[NIns]; 619 selret(b, fn); 620 for (i=&b->ins[b->nins]; i!=b->ins;) 621 switch ((--i)->op) { 622 default: 623 emiti(*i); 624 break; 625 case Ocall: 626 for (i0=i; i0>b->ins; i0--) 627 if (!isarg((i0-1)->op)) 628 break; 629 selcall(fn, i0, i, &il); 630 i = i0; 631 break; 632 case Ovastart: 633 selvastart(fn, p, i->arg[0]); 634 break; 635 case Ovaarg: 636 selvaarg(fn, i); 637 break; 638 case Oarg: 639 case Oargc: 640 die("unreachable"); 641 } 642 if (b == fn->start) 643 for (; il; il=il->link) 644 emiti(il->i); 645 b->nins = &insb[NIns] - curi; 646 idup(&b->ins, curi, b->nins); 647 } while (b != fn->start); 648 649 if (debug['A']) { 650 fprintf(stderr, "\n> After ABI lowering:\n"); 651 printfn(fn, stderr); 652 } 653 }