qbe

Internal scc patchset buffer for QBE
Log | Files | Refs | README | LICENSE

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 }