mirror of
git://c9x.me/qbe.git
synced 2026-04-05 09:59:47 +00:00
488 lines
9.3 KiB
C
488 lines
9.3 KiB
C
#include "all.h"
|
|
|
|
#define NOBID (-1u)
|
|
|
|
/* ins can trap at runtime */
|
|
static int
|
|
istrapping(Fn *fn, Ins *i)
|
|
{
|
|
int64_t v;
|
|
|
|
if (KBASE(i->cls) == 0)
|
|
if (INRANGE(i->op, Odiv, Ourem))
|
|
if (!isconbits(fn, i->arg[1], &v) || v == 0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* fixed ins that can be eliminated if unused */
|
|
static int
|
|
canelim(Fn *fn, Ins *i)
|
|
{
|
|
return isload(i->op) || isalloc(i->op) || istrapping(fn, i);
|
|
}
|
|
|
|
/* ins must stay in def blk */
|
|
int
|
|
isfixed(Fn *fn, Ins *i)
|
|
{
|
|
return optab[i->op].ispinned || istrapping(fn, i);
|
|
}
|
|
|
|
static uint earlyins(Fn *, Blk *, Ins *);
|
|
|
|
/* return earlybid of ref def ins */
|
|
static uint
|
|
schedearly(Fn *fn, Ref r)
|
|
{
|
|
Tmp *t;
|
|
Blk *b;
|
|
|
|
if (rtype(r) != RTmp)
|
|
return 0 /* root/start */;
|
|
|
|
t = &fn->tmp[r.val];
|
|
if (t->gcmbid != NOBID)
|
|
return t->gcmbid; /* already visited/visiting */
|
|
|
|
b = fn->rpo[t->bid];
|
|
if (t->def) {
|
|
/* def is an ins */
|
|
assert(b->ins <= t->def && t->def < &b->ins[b->nins]);
|
|
t->gcmbid = 0; /* mark visiting root/start blk */
|
|
t->gcmbid = earlyins(fn, b, t->def); /* schedule ins input defs */
|
|
} else {
|
|
/* def is a phi - it stays in its def blk */
|
|
t->gcmbid = t->bid;
|
|
}
|
|
|
|
return t->gcmbid;
|
|
}
|
|
|
|
/* return deepest domdpth bid of arg defs */
|
|
static uint
|
|
earlyins(Fn *fn, Blk *b, Ins* i)
|
|
{
|
|
uint earlybid, arg1earlybid;
|
|
|
|
earlybid = schedearly(fn, i->arg[0]);
|
|
assert(earlybid != NOBID);
|
|
arg1earlybid = schedearly(fn, i->arg[1]);
|
|
assert(arg1earlybid != NOBID);
|
|
if (fn->rpo[earlybid]->domdpth < fn->rpo[arg1earlybid]->domdpth) {
|
|
assert(dom(fn->rpo[earlybid], fn->rpo[arg1earlybid]));
|
|
earlybid = arg1earlybid;
|
|
}
|
|
|
|
/* fixed ins remain in their defining blk */
|
|
return isfixed(fn, i) ? b->id : earlybid;
|
|
}
|
|
|
|
static void
|
|
earlyblk(Fn *fn, uint bid)
|
|
{
|
|
Blk *b;
|
|
Phi *p;
|
|
Ins *i;
|
|
uint n;
|
|
|
|
b = fn->rpo[bid];
|
|
for (p = b->phi; p; p = p->link)
|
|
for (n = 0; n < p->narg; n++)
|
|
schedearly(fn, p->arg[n]);
|
|
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
if (isfixed(fn, i))
|
|
earlyins(fn, b, i);
|
|
schedearly(fn, b->jmp.arg);
|
|
}
|
|
|
|
/* least common ancestor in dom tree */
|
|
static uint
|
|
lcabid(Fn *fn, uint bid1, uint bid2)
|
|
{
|
|
Blk *b;
|
|
|
|
if (bid1 == NOBID)
|
|
return bid2;
|
|
if (bid2 == NOBID)
|
|
return bid1;
|
|
|
|
b = lca(fn->rpo[bid1], fn->rpo[bid2]);
|
|
assert(b);
|
|
return b->id;
|
|
}
|
|
|
|
static uint
|
|
bestbid(Fn *fn, uint earlybid, uint latebid)
|
|
{
|
|
Blk *curb, *earlyb, *bestb;
|
|
|
|
if (latebid == NOBID)
|
|
return NOBID; /* unused */
|
|
|
|
assert(earlybid != NOBID);
|
|
|
|
earlyb = fn->rpo[earlybid];
|
|
bestb = curb = fn->rpo[latebid];
|
|
assert(dom(earlyb, curb));
|
|
|
|
while (curb != earlyb) {
|
|
curb = curb->idom;
|
|
if (curb->loop < bestb->loop)
|
|
bestb = curb;
|
|
}
|
|
return bestb->id;
|
|
}
|
|
|
|
static uint lateins(Fn *, Blk *, Ins *, Ref r);
|
|
static uint latephi(Fn *, Phi *, Ref r);
|
|
static uint latejmp(Blk *, Ref r);
|
|
|
|
/* return lca bid of ref uses */
|
|
static uint
|
|
schedlate(Fn *fn, Ref r)
|
|
{
|
|
Tmp *t;
|
|
Blk *b;
|
|
Use *u;
|
|
uint earlybid;
|
|
uint latebid;
|
|
uint uselatebid;
|
|
|
|
if (rtype(r) != RTmp)
|
|
return NOBID;
|
|
|
|
t = &fn->tmp[r.val];
|
|
if (t->visit)
|
|
return t->gcmbid; /* already visited/visiting */
|
|
|
|
t->visit = 1; /* mark visiting/visited */
|
|
earlybid = t->gcmbid;
|
|
if (earlybid == NOBID)
|
|
return NOBID; /* not used */
|
|
t->gcmbid = t->bid; /* t->gcmbid is now latebid */
|
|
|
|
latebid = NOBID;
|
|
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
|
assert(u->bid < fn->nblk);
|
|
b = fn->rpo[u->bid];
|
|
switch (u->type) {
|
|
case UXXX:
|
|
die("unreachable");
|
|
break;
|
|
case UPhi:
|
|
uselatebid = latephi(fn, u->u.phi, r);
|
|
break;
|
|
case UIns:
|
|
uselatebid = lateins(fn, b, u->u.ins, r);
|
|
break;
|
|
case UJmp:
|
|
uselatebid = latejmp(b, r);
|
|
break;
|
|
}
|
|
latebid = lcabid(fn, latebid, uselatebid);
|
|
}
|
|
|
|
/* phis stay in their def blk */
|
|
if (t->def) {
|
|
/* allow elimination of unused load/alloc/trapping ins */
|
|
if (latebid == NOBID && canelim(fn, t->def))
|
|
t->gcmbid = NOBID;
|
|
/* ... otherwise fixed ins stay in defining blk */
|
|
else if(!isfixed(fn, t->def))
|
|
t->gcmbid = bestbid(fn, earlybid, latebid);
|
|
}
|
|
|
|
return t->gcmbid;
|
|
}
|
|
|
|
/* return lca bid of uses, or NOBID if no active uses */
|
|
static uint
|
|
lateins(Fn *fn, Blk *b, Ins *i, Ref r)
|
|
{
|
|
uint latebid;
|
|
|
|
assert(i->op == Onop || req(i->arg[0], r) || req(i->arg[1], r));
|
|
if (i->op == Onop)
|
|
return NOBID; /* already eliminated */
|
|
|
|
assert(b->ins <= i && i < &b->ins[b->nins]);
|
|
|
|
latebid = schedlate(fn, i->to);
|
|
/* allow elimination of unused load/alloc/trapping ins */
|
|
if (latebid == NOBID)
|
|
if (canelim(fn, i))
|
|
return NOBID;
|
|
/* ... otherwise fixed ins stay in defining blk */
|
|
return isfixed(fn, i) ? b->id : latebid;
|
|
}
|
|
|
|
static uint
|
|
latephi(Fn *fn, Phi *p, Ref r)
|
|
{
|
|
uint n;
|
|
uint latebid;
|
|
|
|
if (!p->narg)
|
|
return NOBID; /* marked as unused */
|
|
|
|
latebid = NOBID;
|
|
for (n = 0; n < p->narg; n++)
|
|
if (req(p->arg[n], r))
|
|
latebid = lcabid(fn, latebid, p->blk[n]->id);
|
|
|
|
assert(latebid != NOBID);
|
|
return latebid;
|
|
}
|
|
|
|
static uint
|
|
latejmp(Blk *b, Ref r)
|
|
{
|
|
if (req(b->jmp.arg, R))
|
|
return NOBID;
|
|
else {
|
|
assert(req(b->jmp.arg, r));
|
|
return b->id;
|
|
}
|
|
}
|
|
|
|
static void
|
|
lateblk(Fn *fn, uint bid)
|
|
{
|
|
Blk *b;
|
|
Phi **pp;
|
|
Ins *i;
|
|
|
|
b = fn->rpo[bid];
|
|
for (pp = &b->phi; *(pp);)
|
|
if (schedlate(fn, (*pp)->to) == NOBID) {
|
|
/* unused */
|
|
(*pp)->narg = 0; /* mark unused */
|
|
*pp = (*pp)->link; /* remove phi */
|
|
} else
|
|
pp = &(*pp)->link;
|
|
|
|
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
if (isfixed(fn, i))
|
|
lateins(fn, b, i, i->arg[0]);
|
|
}
|
|
|
|
static void
|
|
addgcmins(Fn *fn, Ins *vins, uint nins)
|
|
{
|
|
Ins *i;
|
|
Tmp *t;
|
|
Blk *b;
|
|
|
|
for (i = vins; i < &vins[nins]; i++) {
|
|
assert(rtype(i->to) == RTmp);
|
|
t = &fn->tmp[i->to.val];
|
|
b = fn->rpo[t->gcmbid];
|
|
addins(&b->ins, &b->nins, i);
|
|
}
|
|
}
|
|
|
|
/* remove unused instructions */
|
|
/* move instructions to (end of) target block */
|
|
/* use-before-def is fixed up afterwards */
|
|
static void
|
|
gcmmove(Fn *fn)
|
|
{
|
|
Tmp *t;
|
|
Ins *vins, *i;
|
|
uint nins;
|
|
|
|
nins = 0;
|
|
vins = vnew(nins, sizeof vins[0], PFn);
|
|
|
|
for (t=&fn->tmp[Tmp0]; t < &fn->tmp[fn->ntmp]; t++) {
|
|
if (t->def == 0)
|
|
continue;
|
|
if (t->bid == t->gcmbid)
|
|
continue;
|
|
i = t->def;
|
|
if (isfixed(fn, i) && !canelim(fn, i))
|
|
continue;
|
|
assert(rtype(i->to) == RTmp);
|
|
assert(t == &fn->tmp[i->to.val]);
|
|
if (t->gcmbid != NOBID)
|
|
addins(&vins, &nins, i);
|
|
*i = (Ins){.op = Onop};
|
|
}
|
|
|
|
addgcmins(fn, vins, nins);
|
|
}
|
|
|
|
static void
|
|
schedins(Fn *fn, Blk *b, Ins *i0, Ins **pvins, uint *pnins)
|
|
{
|
|
Ins *i, *i1;
|
|
Tmp *t;
|
|
uint na;
|
|
|
|
if (i0->op == Onop)
|
|
return;
|
|
/* arg...call have to stay together */
|
|
/* TODO - sel0...sel1 too */
|
|
for (i1 = i0; isarg(i1->op); i1++) {}
|
|
for (i = i0; i <= i1; i++)
|
|
for (na = 0; na < 2; na++) {
|
|
if (rtype(i->arg[na]) != RTmp)
|
|
continue;
|
|
t = &fn->tmp[i->arg[na].val];
|
|
/* def in different blk, or phi in this blk */
|
|
if (t->bid != b->id || t->def == 0)
|
|
continue;
|
|
/* already scheduled */
|
|
if (t->def->op == Onop) {
|
|
continue;
|
|
}
|
|
schedins(fn, b, t->def, pvins, pnins);
|
|
}
|
|
for (i = i0; i <= i1; i++) {
|
|
addins(pvins, pnins, i);
|
|
*i = (Ins){.op = Onop};
|
|
}
|
|
}
|
|
|
|
static void
|
|
fixbub4d(Fn *fn, Blk *b, Ins **pvins)
|
|
{
|
|
Ins *i;
|
|
uint nins;
|
|
|
|
nins = 0;
|
|
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
schedins(fn, b, i, pvins, &nins);
|
|
idup(b, *pvins, nins);
|
|
}
|
|
|
|
static void
|
|
fixub4d(Fn *fn)
|
|
{
|
|
Blk *b;
|
|
Ins *vins; // TODO insb
|
|
|
|
vins = vnew(0, sizeof vins[0], PFn);
|
|
for (b = fn->start; b; b = b->link)
|
|
fixbub4d(fn, b, &vins);
|
|
}
|
|
|
|
static int
|
|
iskladdcon(Fn *fn, Ins *i, int64_t *v)
|
|
{
|
|
if (i->op == Oadd)
|
|
if (i->cls == Kl)
|
|
if (rtype(i->arg[0]) == RTmp)
|
|
if (isconbits(fn, i->arg[1], v))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
ireassoc(Fn *fn, Blk *b, Ins *i, Ins **pvins, uint *pnins)
|
|
{
|
|
Blk *b2;
|
|
Tmp *t, *t2;
|
|
Use *u;
|
|
Ref r2;
|
|
int64_t v;
|
|
int x;
|
|
|
|
assert(b->ins <= i && i < &b->ins[b->nins]);
|
|
if (!iscmp(i->op, &x, &x))
|
|
if (!iskladdcon(fn, i, &v))
|
|
return;
|
|
|
|
assert(rtype(i->to) == RTmp);
|
|
t = &fn->tmp[i->to.val];
|
|
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
|
if (u->type == UPhi)
|
|
continue;
|
|
b2 = fn->rpo[u->bid];
|
|
addins(pvins, pnins, i);
|
|
/* careful, can move fn->tmp */
|
|
r2 = newtmp("rea", t->cls, fn);
|
|
t = &fn->tmp[i->to.val];
|
|
t2 = &fn->tmp[r2.val];
|
|
t2->gcmbid = u->bid;
|
|
(*pvins)[(*pnins)-1].to = r2;
|
|
if (u->type == UIns) {
|
|
assert(req(u->u.ins->arg[0], i->to)
|
|
|| req(u->u.ins->arg[1], i->to));
|
|
if (req(u->u.ins->arg[0], i->to))
|
|
u->u.ins->arg[0] = r2;
|
|
if (req(u->u.ins->arg[1], i->to))
|
|
u->u.ins->arg[1] = r2;
|
|
} else {
|
|
assert(u->type == UJmp);
|
|
assert(req(b2->jmp.arg, i->to));
|
|
b2->jmp.arg = r2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Redistribute trivial ops to point of use. */
|
|
/* Reduces register pressure. */
|
|
/* needs rpo, use; breaks use */
|
|
void
|
|
reassoc(Fn *fn)
|
|
{
|
|
Blk *b;
|
|
Ins *vins, *i;
|
|
uint nins;
|
|
|
|
nins = 0;
|
|
vins = vnew(nins, sizeof vins[0], PFn);
|
|
|
|
/* identify trivial ins */
|
|
for (b = fn->start; b; b = b->link) {
|
|
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
ireassoc(fn, b, i, &vins, &nins);
|
|
}
|
|
|
|
addgcmins(fn, vins, nins);
|
|
}
|
|
|
|
static void
|
|
cleartmps(Fn *fn)
|
|
{
|
|
Tmp *t;
|
|
|
|
for (t=&fn->tmp[Tmp0]; t < &fn->tmp[fn->ntmp]; t++) {
|
|
t->visit = 0;
|
|
t->gcmbid = NOBID;
|
|
}
|
|
}
|
|
|
|
/* https://courses.cs.washington.edu/courses/cse501/06wi/reading/click-pldi95.pdf */
|
|
/* require use dom */
|
|
/* maintains rpo pred dom */
|
|
/* breaks use */
|
|
void
|
|
gcm(Fn *fn)
|
|
{
|
|
uint bid;
|
|
|
|
filldomdpth(fn);
|
|
fillloop(fn);
|
|
|
|
cleartmps(fn);
|
|
for (bid=0; bid<fn->nblk; bid++)
|
|
earlyblk(fn, bid);
|
|
for (bid=0; bid<fn->nblk; bid++)
|
|
lateblk(fn, bid);
|
|
|
|
gcmmove(fn);
|
|
cleartmps(fn); /* filluse() uses visit */
|
|
filluse(fn);
|
|
reassoc(fn);
|
|
filluse(fn);
|
|
fixub4d(fn);
|
|
|
|
if (debug['G']) {
|
|
fprintf(stderr, "\n> After GCM:\n");
|
|
printfn(fn, stderr);
|
|
}
|
|
}
|