mirror of
git://c9x.me/qbe.git
synced 2026-05-26 15:34:41 +00:00
Two simple loop optimizations. 1. Strength reduction of mul[tiplication] by loop induction variable. 2. Hoisting of (address) base into phi where loop induction variable is used only as a base (address) offset. Limited to loops with a single body block, which happily is always innermost loops. This restriction would not be very hard to lift - it would require detecting the set of loop blocks (and ensuring reducibility?) Limited to loop induction variables with 0 initial value and increment of 1 (for mul strength reduction). This limitation is trivial to lift; however all of the cproc/hare[c]/coremark opportunity is with 0/1 loops for mul reduction, and 0 initial value for base-offset opt.
218 lines
4.1 KiB
C
218 lines
4.1 KiB
C
#include "all.h"
|
|
|
|
typedef struct Cntr Cntr;
|
|
typedef struct Loop Loop;
|
|
|
|
static int
|
|
forloop(Blk *bh, Blk *bb)
|
|
{
|
|
if (bh->npred == 2)
|
|
if (bh->jmp.type == Jjnz)
|
|
if (bb->npred == 1)
|
|
if (bb->jmp.type == Jjmp)
|
|
if (bb->s1 == bh)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
doloop(Blk *bh)
|
|
{
|
|
if (bh->npred == 2)
|
|
if (bh->jmp.type == Jjnz)
|
|
if (bh->s1 == bh || bh->s2 == bh)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
struct Loop {
|
|
Blk *bp; /* pre-header */
|
|
Blk *bh; /* header */
|
|
Blk *bb; /* body */
|
|
};
|
|
|
|
static int
|
|
invariant(Ref r, Loop l, Fn *fn)
|
|
{
|
|
Tmp *t;
|
|
|
|
if (rtype(r) == RCon)
|
|
return 1;
|
|
assert(rtype(r) == RTmp);
|
|
t = &fn->tmp[r.val];
|
|
if (t->bid != l.bh->id)
|
|
if (t->bid != l.bb->id)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
struct Cntr {
|
|
Phi *p; /* defining phi */
|
|
Ref r0; /* initial value */
|
|
Ref rc; /* post-increment */
|
|
Ref ri; /* increment */
|
|
};
|
|
|
|
/* check if a phi defines an additive
|
|
* induction variable
|
|
*/
|
|
static int
|
|
counter(Fn *fn, Loop l, Phi *p, Cntr *c)
|
|
{
|
|
Tmp *t;
|
|
Ins *i;
|
|
|
|
assert(l.bh->npred == 2);
|
|
assert(l.bh->pred[0] == l.bb || l.bh->pred[1] == l.bb);
|
|
c->p = p;
|
|
c->r0 = phiarg(p, l.bp);
|
|
c->rc = phiarg(p, l.bb);
|
|
if (KBASE(p->cls) != 0
|
|
|| rtype(c->rc) != RTmp
|
|
|| (t = &fn->tmp[c->rc.val])->bid != l.bb->id
|
|
|| !(i = t->def)
|
|
|| i->op != Oadd
|
|
|| req(i->arg[0], p->to) == req(i->arg[1], p->to))
|
|
return 0;
|
|
c->ri = i->arg[req(i->arg[0], p->to)];
|
|
assert(p->cls == i->cls);
|
|
return invariant(c->ri, l, fn);
|
|
}
|
|
|
|
static void
|
|
mulredux(Fn *fn, Loop l, Cntr c)
|
|
{
|
|
Ins *i, *ie, ia;
|
|
Phi *p;
|
|
Ref rm;
|
|
|
|
/* dead simple case for now...
|
|
* majority case in practice */
|
|
if (!req(c.r0, con01[0]) || !req(c.ri, con01[1]))
|
|
return;
|
|
|
|
ia = (Ins){.op = Oadd};
|
|
ie = &l.bb->ins[l.bb->nins]; /* l.bb->ins may change */
|
|
for (i=l.bb->ins; i<ie; i++) {
|
|
/* TODO i->cls != p->cls is not necessary? */
|
|
if (i->op != Omul
|
|
|| i->cls != p->cls
|
|
|| req(i->arg[0], p->to) == req(i->arg[1], p->to))
|
|
continue;
|
|
assert(KBASE(i->cls) == 0);
|
|
rm = i->arg[req(i->arg[0], p->to)];
|
|
if (!invariant(rm, l, fn))
|
|
continue;
|
|
|
|
ia.to = newtmp("lop", i->cls, fn);
|
|
ia.cls = i->cls;
|
|
ia.arg[0] = i->to;
|
|
ia.arg[1] = rm;
|
|
addins(&l.bb->ins, &l.bb->nins, &ia);
|
|
p = alloc(sizeof *p);
|
|
p->cls = i->cls;
|
|
p->to = i->to;
|
|
p->narg = 2;
|
|
p->blk = vnew(2, sizeof p->blk[0], PFn);
|
|
p->blk[0] = l.bp;
|
|
p->blk[1] = l.bb;
|
|
p->arg = vnew(2, sizeof p->arg[0], PFn);
|
|
p->arg[0] = CON_Z;
|
|
p->arg[1] = ia.to;
|
|
p->link = l.bh->phi;
|
|
l.bh->phi = p;
|
|
*i = (Ins){.op = Onop};
|
|
}
|
|
}
|
|
|
|
static void
|
|
addbase(Fn *fn, Loop l, Cntr c)
|
|
{
|
|
Ins *i, *ii, *ib;
|
|
Tmp *t, *ti;
|
|
Use *u;
|
|
Ref rb;
|
|
uint n;
|
|
|
|
/* dead simple case for now...
|
|
* majority case in practice */
|
|
if (!req(c.r0, CON_Z))
|
|
return;
|
|
|
|
assert(rtype(c.p->to) == RTmp);
|
|
t = &fn->tmp[c.p->to.val];
|
|
if (t->nuse != 2)
|
|
return;
|
|
|
|
ii = ib = 0;
|
|
for (u=t->use; u<&t->use[t->nuse]; u++) {
|
|
if (u->type != UIns)
|
|
return;
|
|
i = u->u.ins;
|
|
if (i->op != Oadd
|
|
|| i->cls != c.p->cls
|
|
|| u->bid != l.bb->id)
|
|
return;
|
|
assert(req(i->arg[0], c.p->to) || req(i->arg[1], c.p->to));
|
|
if (req(i->to, c.rc)) {
|
|
assert(rtype(i->to) == RTmp);
|
|
ti = &fn->tmp[i->to.val];
|
|
if (ti->nuse != 1)
|
|
return;
|
|
assert(ti->use[0].type == UPhi);
|
|
assert(ti->use[0].bid == l.bh->id);
|
|
assert(ti->use[0].u.phi == c.p);
|
|
ii = i;
|
|
} else {
|
|
rb = i->arg[req(i->arg[0], c.p->to)];
|
|
if (!invariant(rb, l, fn))
|
|
return;
|
|
assert(!req(rb, c.p->to));
|
|
ib = i;
|
|
}
|
|
}
|
|
n = phiargn(c.p, l.bp);
|
|
assert(n != -1u && req(c.p->arg[n], CON_Z));
|
|
c.p->arg[n] = rb;
|
|
ii->arg[!req(ii->arg[0], c.p->to)] = ib->to;
|
|
c.p->to = ib->to;
|
|
*ib = (Ins){.op = Onop};
|
|
}
|
|
|
|
static void
|
|
counteropt(Fn *fn, void opt(Fn *, Loop, Cntr))
|
|
{
|
|
Loop l;
|
|
Cntr c;
|
|
Phi *p;
|
|
uint n;
|
|
|
|
for (n=0; n<fn->nblk; n++) {
|
|
l.bh = fn->rpo[n];
|
|
if (forloop(l.bh, l.bh->s1))
|
|
l.bb = l.bh->s1;
|
|
else if (forloop(l.bh, l.bh->s2))
|
|
l.bb = l.bh->s2;
|
|
else if (doloop(l.bh))
|
|
l.bb = l.bh; /* header and body */
|
|
else
|
|
continue;
|
|
l.bp = l.bh->pred[l.bh->pred[0] == l.bh];
|
|
assert(l.bh->loop > 1);
|
|
assert(l.bh->loop == l.bb->loop);
|
|
for (p=l.bh->phi; p; p=p->link)
|
|
if (counter(fn, l, p, &c)) {
|
|
assert(KBASE(p->cls) == 0);
|
|
opt(fn, l, c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
loopopt(Fn *fn)
|
|
{
|
|
counteropt(fn, mulredux);
|
|
filluse(fn);
|
|
counteropt(fn, addbase);
|
|
}
|