emit.c (12109B)
1 #include "all.h" 2 3 enum { 4 Ki = -1, /* matches Kw and Kl */ 5 Ka = -2, /* matches all classes */ 6 }; 7 8 static struct { 9 short op; 10 short cls; 11 char *asm; 12 } omap[] = { 13 { Oadd, Ki, "add%k %=, %0, %1" }, 14 { Oadd, Ka, "fadd.%k %=, %0, %1" }, 15 { Osub, Ki, "sub%k %=, %0, %1" }, 16 { Osub, Ka, "fsub.%k %=, %0, %1" }, 17 { Oneg, Ki, "neg%k %=, %0" }, 18 { Oneg, Ka, "fneg.%k %=, %0" }, 19 { Odiv, Ki, "div%k %=, %0, %1" }, 20 { Odiv, Ka, "fdiv.%k %=, %0, %1" }, 21 { Orem, Ki, "rem%k %=, %0, %1" }, 22 { Orem, Kl, "rem %=, %0, %1" }, 23 { Oudiv, Ki, "divu%k %=, %0, %1" }, 24 { Ourem, Ki, "remu%k %=, %0, %1" }, 25 { Omul, Ki, "mul%k %=, %0, %1" }, 26 { Omul, Ka, "fmul.%k %=, %0, %1" }, 27 { Oand, Ki, "and %=, %0, %1" }, 28 { Oor, Ki, "or %=, %0, %1" }, 29 { Oxor, Ki, "xor %=, %0, %1" }, 30 { Osar, Ki, "sra%k %=, %0, %1" }, 31 { Oshr, Ki, "srl%k %=, %0, %1" }, 32 { Oshl, Ki, "sll%k %=, %0, %1" }, 33 { Ocsltl, Ki, "slt %=, %0, %1" }, 34 { Ocultl, Ki, "sltu %=, %0, %1" }, 35 { Oceqs, Ki, "feq.s %=, %0, %1" }, 36 { Ocges, Ki, "fge.s %=, %0, %1" }, 37 { Ocgts, Ki, "fgt.s %=, %0, %1" }, 38 { Ocles, Ki, "fle.s %=, %0, %1" }, 39 { Oclts, Ki, "flt.s %=, %0, %1" }, 40 { Oceqd, Ki, "feq.d %=, %0, %1" }, 41 { Ocged, Ki, "fge.d %=, %0, %1" }, 42 { Ocgtd, Ki, "fgt.d %=, %0, %1" }, 43 { Ocled, Ki, "fle.d %=, %0, %1" }, 44 { Ocltd, Ki, "flt.d %=, %0, %1" }, 45 { Ostoreb, Kw, "sb %0, %M1" }, 46 { Ostoreh, Kw, "sh %0, %M1" }, 47 { Ostorew, Kw, "sw %0, %M1" }, 48 { Ostorel, Ki, "sd %0, %M1" }, 49 { Ostores, Kw, "fsw %0, %M1" }, 50 { Ostored, Kw, "fsd %0, %M1" }, 51 { Oloadsb, Ki, "lb %=, %M0" }, 52 { Oloadub, Ki, "lbu %=, %M0" }, 53 { Oloadsh, Ki, "lh %=, %M0" }, 54 { Oloaduh, Ki, "lhu %=, %M0" }, 55 { Oloadsw, Ki, "lw %=, %M0" }, 56 /* riscv64 always sign-extends 32-bit 57 * values stored in 64-bit registers 58 */ 59 { Oloaduw, Kw, "lw %=, %M0" }, 60 { Oloaduw, Kl, "lwu %=, %M0" }, 61 { Oload, Kw, "lw %=, %M0" }, 62 { Oload, Kl, "ld %=, %M0" }, 63 { Oload, Ks, "flw %=, %M0" }, 64 { Oload, Kd, "fld %=, %M0" }, 65 { Oextsb, Ki, "sext.b %=, %0" }, 66 { Oextub, Ki, "zext.b %=, %0" }, 67 { Oextsh, Ki, "sext.h %=, %0" }, 68 { Oextuh, Ki, "zext.h %=, %0" }, 69 { Oextsw, Kl, "sext.w %=, %0" }, 70 { Oextuw, Kl, "zext.w %=, %0" }, 71 { Otruncd, Ks, "fcvt.s.d %=, %0" }, 72 { Oexts, Kd, "fcvt.d.s %=, %0" }, 73 { Ostosi, Kw, "fcvt.w.s %=, %0, rtz" }, 74 { Ostosi, Kl, "fcvt.l.s %=, %0, rtz" }, 75 { Ostoui, Kw, "fcvt.wu.s %=, %0, rtz" }, 76 { Ostoui, Kl, "fcvt.lu.s %=, %0, rtz" }, 77 { Odtosi, Kw, "fcvt.w.d %=, %0, rtz" }, 78 { Odtosi, Kl, "fcvt.l.d %=, %0, rtz" }, 79 { Odtoui, Kw, "fcvt.wu.d %=, %0, rtz" }, 80 { Odtoui, Kl, "fcvt.lu.d %=, %0, rtz" }, 81 { Oswtof, Ka, "fcvt.%k.w %=, %0" }, 82 { Ouwtof, Ka, "fcvt.%k.wu %=, %0" }, 83 { Osltof, Ka, "fcvt.%k.l %=, %0" }, 84 { Oultof, Ka, "fcvt.%k.lu %=, %0" }, 85 { Ocast, Kw, "fmv.x.w %=, %0" }, 86 { Ocast, Kl, "fmv.x.d %=, %0" }, 87 { Ocast, Ks, "fmv.w.x %=, %0" }, 88 { Ocast, Kd, "fmv.d.x %=, %0" }, 89 { Ocopy, Ki, "mv %=, %0" }, 90 { Ocopy, Ka, "fmv.%k %=, %0" }, 91 { Oswap, Ki, "mv %?, %0\n\tmv %0, %1\n\tmv %1, %?" }, 92 { Oswap, Ka, "fmv.%k %?, %0\n\tfmv.%k %0, %1\n\tfmv.%k %1, %?" }, 93 { Oreqz, Ki, "seqz %=, %0" }, 94 { Ornez, Ki, "snez %=, %0" }, 95 { Ocall, Kw, "jalr %0" }, 96 { NOp, 0, 0 } 97 }; 98 99 static char *rname[] = { 100 [FP] = "fp", 101 [SP] = "sp", 102 [GP] = "gp", 103 [TP] = "tp", 104 [RA] = "ra", 105 [T0] = "t0", "t1", "t2", "t3", "t4", "t5", 106 [A0] = "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", 107 [S1] = "s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", 108 "s9", "s10", "s11", 109 [FT0] = "ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", 110 "ft8", "ft9", "ft10", 111 [FA0] = "fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7", 112 [FS0] = "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", 113 "fs8", "fs9", "fs10", "fs11", 114 [T6] = "t6", 115 [FT11] = "ft11", 116 }; 117 118 static int64_t 119 slot(Ref r, Fn *fn) 120 { 121 int s; 122 123 s = rsval(r); 124 assert(s <= fn->slot); 125 if (s < 0) 126 return 8 * -s; 127 else 128 return -4 * (fn->slot - s); 129 } 130 131 static void 132 emitaddr(Con *c, FILE *f) 133 { 134 assert(c->sym.type == SGlo); 135 fputs(str(c->sym.id), f); 136 if (c->bits.i) 137 fprintf(f, "+%"PRIi64, c->bits.i); 138 } 139 140 static void 141 emitf(char *s, Ins *i, Fn *fn, FILE *f) 142 { 143 static char clschr[] = {'w', 'l', 's', 'd'}; 144 Ref r; 145 int k, c; 146 Con *pc; 147 int64_t offset; 148 149 fputc('\t', f); 150 for (;;) { 151 k = i->cls; 152 while ((c = *s++) != '%') 153 if (!c) { 154 fputc('\n', f); 155 return; 156 } else 157 fputc(c, f); 158 switch ((c = *s++)) { 159 default: 160 die("invalid escape"); 161 case '?': 162 if (KBASE(k) == 0) 163 fputs("t6", f); 164 else 165 fputs("ft11", f); 166 break; 167 case 'k': 168 if (i->cls != Kl) 169 fputc(clschr[i->cls], f); 170 break; 171 case '=': 172 case '0': 173 r = c == '=' ? i->to : i->arg[0]; 174 assert(isreg(r)); 175 fputs(rname[r.val], f); 176 break; 177 case '1': 178 r = i->arg[1]; 179 switch (rtype(r)) { 180 default: 181 die("invalid second argument"); 182 case RTmp: 183 assert(isreg(r)); 184 fputs(rname[r.val], f); 185 break; 186 case RCon: 187 pc = &fn->con[r.val]; 188 assert(pc->type == CBits); 189 assert(pc->bits.i >= -2048 && pc->bits.i < 2048); 190 fprintf(f, "%d", (int)pc->bits.i); 191 break; 192 } 193 break; 194 case 'M': 195 c = *s++; 196 assert(c == '0' || c == '1'); 197 r = i->arg[c - '0']; 198 switch (rtype(r)) { 199 default: 200 die("invalid address argument"); 201 case RTmp: 202 fprintf(f, "0(%s)", rname[r.val]); 203 break; 204 case RCon: 205 pc = &fn->con[r.val]; 206 assert(pc->type == CAddr); 207 emitaddr(pc, f); 208 if (isstore(i->op) 209 || (isload(i->op) && KBASE(i->cls) == 1)) { 210 /* store (and float load) 211 * pseudo-instructions need a 212 * temporary register in which to 213 * load the address 214 */ 215 fprintf(f, ", t6"); 216 } 217 break; 218 case RSlot: 219 offset = slot(r, fn); 220 assert(offset >= -2048 && offset <= 2047); 221 fprintf(f, "%d(fp)", (int)offset); 222 break; 223 } 224 break; 225 } 226 } 227 } 228 229 static void 230 loadaddr(Con *c, char *rn, FILE *f) 231 { 232 char off[32]; 233 234 if (c->sym.type == SThr) { 235 if (c->bits.i) 236 sprintf(off, "+%"PRIi64, c->bits.i); 237 else 238 off[0] = 0; 239 fprintf(f, "\tlui %s, %%tprel_hi(%s)%s\n", 240 rn, str(c->sym.id), off); 241 fprintf(f, "\tadd %s, %s, tp, %%tprel_add(%s)%s\n", 242 rn, rn, str(c->sym.id), off); 243 fprintf(f, "\taddi %s, %s, %%tprel_lo(%s)%s\n", 244 rn, rn, str(c->sym.id), off); 245 } else { 246 fprintf(f, "\tla %s, ", rn); 247 emitaddr(c, f); 248 fputc('\n', f); 249 } 250 } 251 252 static void 253 loadcon(Con *c, int r, int k, FILE *f) 254 { 255 char *rn; 256 int64_t n; 257 258 rn = rname[r]; 259 switch (c->type) { 260 case CAddr: 261 loadaddr(c, rn, f); 262 break; 263 case CBits: 264 n = c->bits.i; 265 if (!KWIDE(k)) 266 n = (int32_t)n; 267 fprintf(f, "\tli %s, %"PRIi64"\n", rn, n); 268 break; 269 default: 270 die("invalid constant"); 271 } 272 } 273 274 static void 275 fixmem(Ref *pr, Fn *fn, FILE *f) 276 { 277 Ref r; 278 int64_t s; 279 Con *c; 280 281 r = *pr; 282 if (rtype(r) == RCon) { 283 c = &fn->con[r.val]; 284 if (c->type == CAddr) 285 if (c->sym.type == SThr) { 286 loadcon(c, T6, Kl, f); 287 *pr = TMP(T6); 288 } 289 } 290 if (rtype(r) == RSlot) { 291 s = slot(r, fn); 292 if (s < -2048 || s > 2047) { 293 fprintf(f, "\tli t6, %"PRId64"\n", s); 294 fprintf(f, "\tadd t6, fp, t6\n"); 295 *pr = TMP(T6); 296 } 297 } 298 } 299 300 static void 301 emitins(Ins *i, Fn *fn, FILE *f) 302 { 303 int o; 304 char *rn; 305 int64_t s; 306 Con *con; 307 308 switch (i->op) { 309 default: 310 if (isload(i->op)) 311 fixmem(&i->arg[0], fn, f); 312 else if (isstore(i->op)) 313 fixmem(&i->arg[1], fn, f); 314 Table: 315 /* most instructions are just pulled out of 316 * the table omap[], some special cases are 317 * detailed below */ 318 for (o=0;; o++) { 319 /* this linear search should really be a binary 320 * search */ 321 if (omap[o].op == NOp) 322 die("no match for %s(%c)", 323 optab[i->op].name, "wlsd"[i->cls]); 324 if (omap[o].op == i->op) 325 if (omap[o].cls == i->cls || omap[o].cls == Ka 326 || (omap[o].cls == Ki && KBASE(i->cls) == 0)) 327 break; 328 } 329 emitf(omap[o].asm, i, fn, f); 330 break; 331 case Ocopy: 332 if (req(i->to, i->arg[0])) 333 break; 334 if (rtype(i->to) == RSlot) { 335 switch (rtype(i->arg[0])) { 336 case RSlot: 337 case RCon: 338 die("unimplemented"); 339 break; 340 default: 341 assert(isreg(i->arg[0])); 342 i->arg[1] = i->to; 343 i->to = R; 344 switch (i->cls) { 345 case Kw: i->op = Ostorew; break; 346 case Kl: i->op = Ostorel; break; 347 case Ks: i->op = Ostores; break; 348 case Kd: i->op = Ostored; break; 349 } 350 fixmem(&i->arg[1], fn, f); 351 goto Table; 352 } 353 break; 354 } 355 assert(isreg(i->to)); 356 switch (rtype(i->arg[0])) { 357 case RCon: 358 loadcon(&fn->con[i->arg[0].val], i->to.val, i->cls, f); 359 break; 360 case RSlot: 361 i->op = Oload; 362 fixmem(&i->arg[0], fn, f); 363 goto Table; 364 default: 365 assert(isreg(i->arg[0])); 366 goto Table; 367 } 368 break; 369 case Onop: 370 break; 371 case Oaddr: 372 assert(rtype(i->arg[0]) == RSlot); 373 rn = rname[i->to.val]; 374 s = slot(i->arg[0], fn); 375 if (-s < 2048) { 376 fprintf(f, "\tadd %s, fp, %"PRId64"\n", rn, s); 377 } else { 378 fprintf(f, 379 "\tli %s, %"PRId64"\n" 380 "\tadd %s, fp, %s\n", 381 rn, s, rn, rn 382 ); 383 } 384 break; 385 case Ocall: 386 switch (rtype(i->arg[0])) { 387 case RCon: 388 con = &fn->con[i->arg[0].val]; 389 if (con->type != CAddr 390 || con->sym.type != SGlo 391 || con->bits.i) 392 goto Invalid; 393 fprintf(f, "\tcall %s\n", str(con->sym.id)); 394 break; 395 case RTmp: 396 emitf("jalr %0", i, fn, f); 397 break; 398 default: 399 Invalid: 400 die("invalid call argument"); 401 } 402 break; 403 case Osalloc: 404 emitf("sub sp, sp, %0", i, fn, f); 405 if (!req(i->to, R)) 406 emitf("mv %=, sp", i, fn, f); 407 break; 408 case Odbgloc: 409 emitdbgloc(i->arg[0].val, f); 410 break; 411 } 412 } 413 414 /* 415 416 Stack-frame layout: 417 418 +=============+ 419 | varargs | 420 | save area | 421 +-------------+ 422 | saved ra | 423 | saved fp | 424 +-------------+ <- fp 425 | ... | 426 | spill slots | 427 | ... | 428 +-------------+ 429 | ... | 430 | locals | 431 | ... | 432 +-------------+ 433 | padding | 434 +-------------+ 435 | callee-save | 436 | registers | 437 +=============+ 438 439 */ 440 441 void 442 rv64_emitfn(Fn *fn, FILE *f) 443 { 444 static int id0; 445 int lbl, neg, off, frame, *pr, r; 446 Blk *b, *s; 447 Ins *i; 448 449 emitfnlnk(fn->name, &fn->lnk, f); 450 451 if (fn->vararg) { 452 /* TODO: only need space for registers 453 * unused by named arguments 454 */ 455 fprintf(f, "\tadd sp, sp, -64\n"); 456 for (r=A0; r<=A7; r++) 457 fprintf(f, 458 "\tsd %s, %d(sp)\n", 459 rname[r], 8 * (r - A0) 460 ); 461 } 462 fprintf(f, "\tsd fp, -16(sp)\n"); 463 fprintf(f, "\tsd ra, -8(sp)\n"); 464 fprintf(f, "\tadd fp, sp, -16\n"); 465 466 frame = (16 + 4 * fn->slot + 15) & ~15; 467 for (pr=rv64_rclob; *pr>=0; pr++) { 468 if (fn->reg & BIT(*pr)) 469 frame += 8; 470 } 471 frame = (frame + 15) & ~15; 472 473 if (frame <= 2048) 474 fprintf(f, 475 "\tadd sp, sp, -%d\n", 476 frame 477 ); 478 else 479 fprintf(f, 480 "\tli t6, %d\n" 481 "\tsub sp, sp, t6\n", 482 frame 483 ); 484 for (pr=rv64_rclob, off=0; *pr>=0; pr++) { 485 if (fn->reg & BIT(*pr)) { 486 fprintf(f, 487 "\t%s %s, %d(sp)\n", 488 *pr < FT0 ? "sd" : "fsd", 489 rname[*pr], off 490 ); 491 off += 8; 492 } 493 } 494 495 for (lbl=0, b=fn->start; b; b=b->link) { 496 if (lbl || b->npred > 1) 497 fprintf(f, ".L%d:\n", id0+b->id); 498 for (i=b->ins; i!=&b->ins[b->nins]; i++) 499 emitins(i, fn, f); 500 lbl = 1; 501 switch (b->jmp.type) { 502 case Jhlt: 503 fprintf(f, "\tebreak\n"); 504 break; 505 case Jret0: 506 if (fn->dynalloc) { 507 if (frame - 16 <= 2048) 508 fprintf(f, 509 "\tadd sp, fp, -%d\n", 510 frame - 16 511 ); 512 else 513 fprintf(f, 514 "\tli t6, %d\n" 515 "\tsub sp, fp, t6\n", 516 frame - 16 517 ); 518 } 519 for (pr=rv64_rclob, off=0; *pr>=0; pr++) { 520 if (fn->reg & BIT(*pr)) { 521 fprintf(f, 522 "\t%s %s, %d(sp)\n", 523 *pr < FT0 ? "ld" : "fld", 524 rname[*pr], off 525 ); 526 off += 8; 527 } 528 } 529 fprintf(f, 530 "\tadd sp, fp, %d\n" 531 "\tld ra, 8(fp)\n" 532 "\tld fp, 0(fp)\n" 533 "\tret\n", 534 16 + fn->vararg * 64 535 ); 536 break; 537 case Jjmp: 538 Jmp: 539 if (b->s1 != b->link) 540 fprintf(f, "\tj .L%d\n", id0+b->s1->id); 541 else 542 lbl = 0; 543 break; 544 case Jjnz: 545 neg = 0; 546 if (b->link == b->s2) { 547 s = b->s1; 548 b->s1 = b->s2; 549 b->s2 = s; 550 neg = 1; 551 } 552 assert(isreg(b->jmp.arg)); 553 fprintf(f, 554 "\tb%sz %s, .L%d\n", 555 neg ? "ne" : "eq", 556 rname[b->jmp.arg.val], 557 id0+b->s2->id 558 ); 559 goto Jmp; 560 } 561 } 562 id0 += fn->nblk; 563 elf_emitfnfin(fn->name, f); 564 }