qbe

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

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 }