mirror of
git://c9x.me/qbe.git
synced 2026-04-05 09:59:47 +00:00
gvn/gcm review
- Many stylistic nits. - Removed blkmerge(). - Some minor bug fixes. - GCM reassoc is now "sink"; a pass that moves trivial ops in their target block with the same goal of reducing register pressure, but starting from instructions that benefit from having their inputs close.
This commit is contained in:
parent
1cb255cb04
commit
f3ca257737
34
all.h
34
all.h
@ -223,7 +223,7 @@ struct Op {
|
|||||||
uint cmpeqwl:1; /* Kl/Kw cmp eq/ne? */
|
uint cmpeqwl:1; /* Kl/Kw cmp eq/ne? */
|
||||||
uint cmplgtewl:1; /* Kl/Kw cmp lt/gt/le/ge? */
|
uint cmplgtewl:1; /* Kl/Kw cmp lt/gt/le/ge? */
|
||||||
uint eqval:1; /* 1 for eq; 0 for ne */
|
uint eqval:1; /* 1 for eq; 0 for ne */
|
||||||
uint ispinned:1; /* GCM pinned op? */
|
uint pinned:1; /* GCM pinned op? */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Ins {
|
struct Ins {
|
||||||
@ -260,9 +260,9 @@ struct Blk {
|
|||||||
|
|
||||||
Blk *idom;
|
Blk *idom;
|
||||||
Blk *dom, *dlink;
|
Blk *dom, *dlink;
|
||||||
int domdpth;
|
|
||||||
Blk **fron;
|
Blk **fron;
|
||||||
uint nfron;
|
uint nfron;
|
||||||
|
int depth;
|
||||||
|
|
||||||
Blk **pred;
|
Blk **pred;
|
||||||
uint npred;
|
uint npred;
|
||||||
@ -489,13 +489,14 @@ char *str(uint32_t);
|
|||||||
int argcls(Ins *, int);
|
int argcls(Ins *, int);
|
||||||
int isreg(Ref);
|
int isreg(Ref);
|
||||||
int iscmp(int, int *, int *);
|
int iscmp(int, int *, int *);
|
||||||
int invcmpwl(int);
|
void igroup(Blk *, Ins *, Ins **, Ins **);
|
||||||
void emit(int, int, Ref, Ref, Ref);
|
void emit(int, int, Ref, Ref, Ref);
|
||||||
void emiti(Ins);
|
void emiti(Ins);
|
||||||
void idup(Blk *, Ins *, ulong);
|
void idup(Blk *, Ins *, ulong);
|
||||||
Ins *icpy(Ins *, Ins *, ulong);
|
Ins *icpy(Ins *, Ins *, ulong);
|
||||||
int cmpop(int);
|
int cmpop(int);
|
||||||
int cmpneg(int);
|
int cmpneg(int);
|
||||||
|
int cmpwlneg(int);
|
||||||
int clsmerge(short *, short);
|
int clsmerge(short *, short);
|
||||||
int phicls(int, Tmp *);
|
int phicls(int, Tmp *);
|
||||||
uint phiargn(Phi *, Blk *);
|
uint phiargn(Phi *, Blk *);
|
||||||
@ -507,7 +508,6 @@ Ref newcon(Con *, Fn *);
|
|||||||
Ref getcon(int64_t, Fn *);
|
Ref getcon(int64_t, Fn *);
|
||||||
int addcon(Con *, Con *, int);
|
int addcon(Con *, Con *, int);
|
||||||
int isconbits(Fn *fn, Ref r, int64_t *v);
|
int isconbits(Fn *fn, Ref r, int64_t *v);
|
||||||
int istmpconbits(Fn *fn, Ins *i, int64_t *v);
|
|
||||||
void salloc(Ref, Ref, Fn *);
|
void salloc(Ref, Ref, Fn *);
|
||||||
void dumpts(BSet *, Tmp *, FILE *);
|
void dumpts(BSet *, Tmp *, FILE *);
|
||||||
void runmatch(uchar *, Num *, Ref, Ref *);
|
void runmatch(uchar *, Num *, Ref, Ref *);
|
||||||
@ -549,19 +549,11 @@ int sdom(Blk *, Blk *);
|
|||||||
int dom(Blk *, Blk *);
|
int dom(Blk *, Blk *);
|
||||||
void fillfron(Fn *);
|
void fillfron(Fn *);
|
||||||
void loopiter(Fn *, void (*)(Blk *, Blk *));
|
void loopiter(Fn *, void (*)(Blk *, Blk *));
|
||||||
void filldomdpth(Fn *);
|
void filldepth(Fn *);
|
||||||
Blk *lca(Blk *, Blk *);
|
Blk *lca(Blk *, Blk *);
|
||||||
void fillloop(Fn *);
|
void fillloop(Fn *);
|
||||||
void simpljmp(Fn *);
|
void simpljmp(Fn *);
|
||||||
void replacepreds(Blk *, Blk *, Blk *);
|
int reaches(Fn *, Blk *, Blk *);
|
||||||
void killblks(Fn *);
|
|
||||||
void blkmerge(Fn *);
|
|
||||||
int ifgraph(Blk *, Blk **, Blk **, Blk **);
|
|
||||||
int ifjoin(Blk *, Blk **, Blk **, Blk **);
|
|
||||||
int emptyblk(Blk *);
|
|
||||||
void ifelim(Fn *);
|
|
||||||
void clrbvisit(Fn *);
|
|
||||||
int reaches(Fn *,Blk *, Blk *);
|
|
||||||
int reachesnotvia(Fn *, Blk *, Blk *, Blk *);
|
int reachesnotvia(Fn *, Blk *, Blk *, Blk *);
|
||||||
|
|
||||||
/* mem.c */
|
/* mem.c */
|
||||||
@ -586,25 +578,23 @@ void ssa(Fn *);
|
|||||||
void ssacheck(Fn *);
|
void ssacheck(Fn *);
|
||||||
|
|
||||||
/* copy.c */
|
/* copy.c */
|
||||||
int iswu1(Fn *, Ref);
|
void narrowpars(Fn *fn);
|
||||||
void narrowpars(Fn *);
|
|
||||||
Ref copyref(Fn *, Blk *, Ins *);
|
Ref copyref(Fn *, Blk *, Ins *);
|
||||||
|
Ref phicopyref(Fn *, Blk *, Phi *);
|
||||||
|
|
||||||
/* fold.c */
|
/* fold.c */
|
||||||
|
int foldint(Con *, int, int, Con *, Con *);
|
||||||
Ref foldref(Fn *, Ins *);
|
Ref foldref(Fn *, Ins *);
|
||||||
|
|
||||||
/* gvn.c */
|
/* gvn.c */
|
||||||
extern Ref con01[2];
|
extern Ref con01[2]; /* 0 and 1 */
|
||||||
int is0non0(Fn *, Blk *, Ref, int, int *);
|
int zeroval(Fn *, Blk *, Ref, int, int *);
|
||||||
void gvn(Fn *);
|
void gvn(Fn *);
|
||||||
|
|
||||||
/* gcm.c */
|
/* gcm.c */
|
||||||
int isfixed(Fn *, Ins *);
|
int pinned(Ins *);
|
||||||
void gcm(Fn *);
|
void gcm(Fn *);
|
||||||
|
|
||||||
/* reassoc.c */
|
|
||||||
void reassoc(Fn *);
|
|
||||||
|
|
||||||
/* simpl.c */
|
/* simpl.c */
|
||||||
void simpl(Fn *);
|
void simpl(Fn *);
|
||||||
|
|
||||||
|
|||||||
@ -612,6 +612,7 @@ amd64_emitfn(Fn *fn, FILE *f)
|
|||||||
Blk *b, *s;
|
Blk *b, *s;
|
||||||
Ins *i, itmp;
|
Ins *i, itmp;
|
||||||
int *r, c, o, n, lbl;
|
int *r, c, o, n, lbl;
|
||||||
|
uint p;
|
||||||
E *e;
|
E *e;
|
||||||
|
|
||||||
e = &(E){.f = f, .fn = fn};
|
e = &(E){.f = f, .fn = fn};
|
||||||
@ -640,8 +641,14 @@ amd64_emitfn(Fn *fn, FILE *f)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (lbl=0, b=fn->start; b; b=b->link) {
|
for (lbl=0, b=fn->start; b; b=b->link) {
|
||||||
if (lbl || b->npred > 1)
|
if (lbl || b->npred > 1) {
|
||||||
|
for (p=0; p<b->npred; p++)
|
||||||
|
if (b->pred[p]->id >= b->id)
|
||||||
|
break;
|
||||||
|
if (p != b->npred)
|
||||||
|
fprintf(f, ".p2align 4\n");
|
||||||
fprintf(f, "%sbb%d:\n", T.asloc, id0+b->id);
|
fprintf(f, "%sbb%d:\n", T.asloc, id0+b->id);
|
||||||
|
}
|
||||||
for (i=b->ins; i!=&b->ins[b->nins]; i++)
|
for (i=b->ins; i!=&b->ins[b->nins]; i++)
|
||||||
emitins(*i, e);
|
emitins(*i, e);
|
||||||
lbl = 1;
|
lbl = 1;
|
||||||
|
|||||||
350
cfg.c
350
cfg.c
@ -8,49 +8,40 @@ newblk()
|
|||||||
|
|
||||||
b = alloc(sizeof *b);
|
b = alloc(sizeof *b);
|
||||||
*b = z;
|
*b = z;
|
||||||
|
b->ins = vnew(0, sizeof b->ins[0], PFn);
|
||||||
|
b->pred = vnew(0, sizeof b->pred[0], PFn);
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO - this never seems to do anything??? */
|
|
||||||
static void
|
static void
|
||||||
prunephis(Fn *f)
|
fixphis(Fn *f)
|
||||||
{
|
{
|
||||||
Blk *b;
|
Blk *b;
|
||||||
Phi *p, **pp;
|
Phi *p;
|
||||||
uint na, na0;
|
uint n, n0;
|
||||||
|
|
||||||
for (b = f->start; b; b = b->link) {
|
for (b=f->start; b; b=b->link) {
|
||||||
assert(b->id < f->nblk);
|
assert(b->id < f->nblk);
|
||||||
for (pp = &b->phi; (*pp);) {
|
for (p=b->phi; p; p=p->link) {
|
||||||
p = *pp;
|
for (n=n0=0; n<p->narg; n++)
|
||||||
for (na = na0 = 0; na < p->narg; na++)
|
if (p->blk[n]->id != -1u) {
|
||||||
if (p->blk[na]->id != -1u) {
|
p->blk[n0] = p->blk[n];
|
||||||
p->blk[na0] = p->blk[na];
|
p->arg[n0] = p->arg[n];
|
||||||
p->arg[na0] = p->arg[na];
|
n0++;
|
||||||
na0++;
|
|
||||||
}
|
|
||||||
if (na0 == 0) {
|
|
||||||
*pp = p->link;
|
|
||||||
} else {
|
|
||||||
p->narg = na0;
|
|
||||||
pp = &p->link;
|
|
||||||
}
|
}
|
||||||
|
assert(n0 > 0);
|
||||||
|
p->narg = n0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
addpred(Blk *bp, Blk *bc)
|
addpred(Blk *bp, Blk *b)
|
||||||
{
|
{
|
||||||
++bc->npred;
|
vgrow(&b->pred, ++b->npred);
|
||||||
if (bc->pred)
|
b->pred[b->npred-1] = bp;
|
||||||
vgrow(&bc->pred, bc->npred);
|
|
||||||
else
|
|
||||||
bc->pred = vnew(bc->npred, sizeof bc->pred[0], PFn);
|
|
||||||
bc->pred[bc->npred-1] = bp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill predecessors information in blocks; prune dead phi refs */
|
|
||||||
void
|
void
|
||||||
fillpreds(Fn *f)
|
fillpreds(Fn *f)
|
||||||
{
|
{
|
||||||
@ -64,8 +55,6 @@ fillpreds(Fn *f)
|
|||||||
if (b->s2 && b->s2 != b->s1)
|
if (b->s2 && b->s2 != b->s1)
|
||||||
addpred(b, b->s2);
|
addpred(b, b->s2);
|
||||||
}
|
}
|
||||||
/* TODO - this never seems to do anything??? */
|
|
||||||
prunephis(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -87,7 +76,6 @@ porec(Blk *b, uint *npo)
|
|||||||
b->id = (*npo)++;
|
b->id = (*npo)++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill the rpo information; prune dead blks */
|
|
||||||
static void
|
static void
|
||||||
fillrpo(Fn *f)
|
fillrpo(Fn *f)
|
||||||
{
|
{
|
||||||
@ -97,27 +85,25 @@ fillrpo(Fn *f)
|
|||||||
b->id = -1u;
|
b->id = -1u;
|
||||||
f->nblk = 0;
|
f->nblk = 0;
|
||||||
porec(f->start, &f->nblk);
|
porec(f->start, &f->nblk);
|
||||||
if (f->rpo)
|
|
||||||
vgrow(&f->rpo, f->nblk);
|
vgrow(&f->rpo, f->nblk);
|
||||||
else
|
|
||||||
f->rpo = vnew(f->nblk, sizeof f->rpo[0], PFn);
|
|
||||||
for (p=&f->start; (b=*p);) {
|
for (p=&f->start; (b=*p);) {
|
||||||
if (b->id == -1u) {
|
if (b->id == -1u) {
|
||||||
*p = b->link;
|
*p = b->link;
|
||||||
} else {
|
} else {
|
||||||
b->id = f->nblk-b->id-1; /* po -> rpo */
|
b->id = f->nblk-b->id-1;
|
||||||
f->rpo[b->id] = b;
|
f->rpo[b->id] = b;
|
||||||
p = &b->link;
|
p = &b->link;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill rpo, preds; prune dead blks, prune dead blk refs from phis */
|
/* fill rpo, preds; prune dead blks */
|
||||||
void
|
void
|
||||||
fillcfg(Fn *f)
|
fillcfg(Fn *f)
|
||||||
{
|
{
|
||||||
fillrpo(f);
|
fillrpo(f);
|
||||||
fillpreds(f);
|
fillpreds(f);
|
||||||
|
fixphis(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* for dominators computation, read
|
/* for dominators computation, read
|
||||||
@ -260,34 +246,28 @@ loopiter(Fn *fn, void f(Blk *, Blk *))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* dominator tree depth */
|
||||||
void
|
void
|
||||||
multloop(Blk *hd, Blk *b)
|
filldepth(Fn *fn)
|
||||||
{
|
{
|
||||||
(void)hd;
|
Blk *b, *d;
|
||||||
b->loop *= 10;
|
int depth;
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
filldomdpth(Fn *fn)
|
|
||||||
{
|
|
||||||
Blk *b, *dom;
|
|
||||||
uint dpth;
|
|
||||||
|
|
||||||
for (b=fn->start; b; b=b->link)
|
for (b=fn->start; b; b=b->link)
|
||||||
b->domdpth = -1;
|
b->depth = -1;
|
||||||
|
|
||||||
fn->start->domdpth = 0;
|
fn->start->depth = 0;
|
||||||
|
|
||||||
for (b=fn->start; b; b=b->link) {
|
for (b=fn->start; b; b=b->link) {
|
||||||
if (b->domdpth != -1)
|
if (b->depth != -1)
|
||||||
continue;
|
continue;
|
||||||
dpth = 1;
|
depth = 1;
|
||||||
for (dom = b->idom; dom->domdpth == -1; dom = dom->idom)
|
for (d=b->idom; d->depth==-1; d=d->idom)
|
||||||
dpth++;
|
depth++;
|
||||||
dpth += dom->domdpth;
|
depth += d->depth;
|
||||||
b->domdpth = dpth;
|
b->depth = depth;
|
||||||
for (dom = b->idom; dom->domdpth == -1; dom = dom->idom)
|
for (d=b->idom; d->depth==-1; d=d->idom)
|
||||||
dom->domdpth = --dpth;
|
d->depth = --depth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,9 +279,9 @@ lca(Blk *b1, Blk *b2)
|
|||||||
return b2;
|
return b2;
|
||||||
if (!b2)
|
if (!b2)
|
||||||
return b1;
|
return b1;
|
||||||
while (b1->domdpth > b2->domdpth)
|
while (b1->depth > b2->depth)
|
||||||
b1 = b1->idom;
|
b1 = b1->idom;
|
||||||
while (b2->domdpth > b1->domdpth)
|
while (b2->depth > b1->depth)
|
||||||
b2 = b2->idom;
|
b2 = b2->idom;
|
||||||
while (b1 != b2) {
|
while (b1 != b2) {
|
||||||
b1 = b1->idom;
|
b1 = b1->idom;
|
||||||
@ -310,6 +290,13 @@ lca(Blk *b1, Blk *b2)
|
|||||||
return b1;
|
return b1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
multloop(Blk *hd, Blk *b)
|
||||||
|
{
|
||||||
|
(void)hd;
|
||||||
|
b->loop *= 10;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
fillloop(Fn *fn)
|
fillloop(Fn *fn)
|
||||||
{
|
{
|
||||||
@ -371,244 +358,41 @@ simpljmp(Fn *fn)
|
|||||||
free(uf);
|
free(uf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
replacepred(Blk **blks, uint nblk, Blk *to, Blk *from)
|
|
||||||
{
|
|
||||||
uint n;
|
|
||||||
for(n=0; n<nblk; n++)
|
|
||||||
if (blks[n] == from) {
|
|
||||||
blks[n] = to;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
assert(n != nblk);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* replace b->pred[] and p->blk[] entries */
|
|
||||||
void
|
|
||||||
replacepreds(Blk *s, Blk *to, Blk *from)
|
|
||||||
{
|
|
||||||
Phi *p;
|
|
||||||
|
|
||||||
if (!s)
|
|
||||||
return;
|
|
||||||
assert(s->npred);
|
|
||||||
replacepred(s->pred, s->npred, to, from);
|
|
||||||
for (p = s->phi; p; p = p->link) {
|
|
||||||
assert(p->narg == s->npred);
|
|
||||||
replacepred(p->blk, p->narg, to, from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove marked-dead blks - marked as fn->rpo[id] == 0 */
|
|
||||||
void
|
|
||||||
killblks(Fn *fn)
|
|
||||||
{
|
|
||||||
Blk **pb;
|
|
||||||
|
|
||||||
for (pb = &fn->start; *pb;)
|
|
||||||
if (fn->rpo[(*pb)->id])
|
|
||||||
pb = &(*pb)->link;
|
|
||||||
else
|
|
||||||
*pb = (*pb)->link;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* merge linear jmp chains */
|
|
||||||
/* requires rpo pred, breaks cfg use */
|
|
||||||
void
|
|
||||||
blkmerge(Fn *fn)
|
|
||||||
{
|
|
||||||
uint bid, nins;
|
|
||||||
Blk *curb, *b;
|
|
||||||
Ins *vins;
|
|
||||||
|
|
||||||
if (debug['B'])
|
|
||||||
fputs("\n> Block merge:\n", stderr);
|
|
||||||
|
|
||||||
vins = vnew(0, sizeof vins[0], PFn);
|
|
||||||
curb = 0;
|
|
||||||
/* linear jmp chains will be consecutive in rpo */
|
|
||||||
for (bid=0; bid<fn->nblk; bid++) {
|
|
||||||
b = fn->rpo[bid];
|
|
||||||
if (curb == 0) {
|
|
||||||
curb = b;
|
|
||||||
nins = 0;
|
|
||||||
} else
|
|
||||||
fn->rpo[bid] = 0;
|
|
||||||
addbins(b, &vins, &nins);
|
|
||||||
/* note - there are cases where GVN does not eliminate singleton phis */
|
|
||||||
if (b->jmp.type != Jjmp || b->s1->npred != 1 || b->s1->phi) {
|
|
||||||
idup(curb, vins, nins);
|
|
||||||
curb->nins = nins;
|
|
||||||
curb->jmp = b->jmp;
|
|
||||||
replacepreds(b->s1, curb, b);
|
|
||||||
if (b->s1 != b->s2)
|
|
||||||
replacepreds(b->s2, curb, b);
|
|
||||||
curb->s1 = b->s1;
|
|
||||||
curb->s2 = b->s2;
|
|
||||||
curb = 0;
|
|
||||||
} else {
|
|
||||||
assert(b->s1->id == bid+1);
|
|
||||||
if (debug['B'])
|
|
||||||
fprintf(stderr, " merging blocks @%s -> @%s\n", b->name, b->s1->name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(curb == 0);
|
|
||||||
killblks(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
lonesucc(Blk *b, Blk *s)
|
|
||||||
{
|
|
||||||
assert(s);
|
|
||||||
if (s != b)
|
|
||||||
if (s->npred == 1)
|
|
||||||
if (s->pred[0] == b)
|
|
||||||
if (s->phi == 0)
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
lonejmpsucc(Blk *b, Blk *s)
|
|
||||||
{
|
|
||||||
return s->jmp.type == Jjmp && lonesucc(b, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* (otherwise) isolated if-then[-else] graphlet */
|
|
||||||
int
|
|
||||||
ifgraph(Blk *ifb, Blk **pthenb, Blk **pelseb, Blk **pjoinb)
|
|
||||||
{
|
|
||||||
if (ifb->jmp.type != Jjnz)
|
|
||||||
return 0;
|
|
||||||
assert(ifb->s1 && ifb->s2);
|
|
||||||
assert(ifb->s1 != ifb->s2); /* dubious */
|
|
||||||
*pthenb = ifb->s1;
|
|
||||||
*pelseb = ifb->s2;
|
|
||||||
*pjoinb = ifb->s1->s1;
|
|
||||||
if (ifb->s1 == ifb->s2->s1) {
|
|
||||||
/* empty then */
|
|
||||||
*pthenb = ifb;
|
|
||||||
*pjoinb = ifb->s1;
|
|
||||||
}
|
|
||||||
if (ifb->s1->s1 == ifb->s2)
|
|
||||||
/* empty else */
|
|
||||||
*pelseb = ifb;
|
|
||||||
|
|
||||||
if (*pthenb == ifb ||
|
|
||||||
((*pthenb)->s1 == *pjoinb && lonejmpsucc(ifb, *pthenb)))
|
|
||||||
if (*pelseb == ifb ||
|
|
||||||
((*pelseb)->s1 == *pjoinb && lonejmpsucc(ifb, *pelseb)))
|
|
||||||
/* there are cases where npred == 2 is not strictly needed - ifconvert for example */
|
|
||||||
if ((*pjoinb)->npred == 2)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* join blk of if-then[-else] graphlet */
|
|
||||||
int
|
|
||||||
ifjoin(Blk *joinb, Blk **pifb, Blk **pthenb, Blk **pelseb)
|
|
||||||
{
|
|
||||||
Blk *joinb1;
|
|
||||||
|
|
||||||
if (joinb->npred)
|
|
||||||
if (ifgraph(joinb->pred[0], pthenb, pelseb, &joinb1))
|
|
||||||
if (joinb == joinb1) {
|
|
||||||
*pifb = joinb->pred[0];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (joinb->npred && joinb->pred[0]->npred)
|
|
||||||
if (ifgraph(joinb->pred[0]->pred[0], pthenb, pelseb, &joinb1))
|
|
||||||
if (joinb == joinb1) {
|
|
||||||
*pifb = joinb->pred[0]->pred[0];
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
emptyblk(Blk *b)
|
|
||||||
{
|
|
||||||
Ins *i;
|
|
||||||
|
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
||||||
if (i->op != Onop)
|
|
||||||
if (i->op != Odbgloc)
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* remove empty jnz graphlets */
|
|
||||||
/* needs rpo; breaks cfg */
|
|
||||||
void
|
|
||||||
ifelim(Fn *fn)
|
|
||||||
{
|
|
||||||
uint bid;
|
|
||||||
Blk *ifb, *thenb, *elseb, *joinb;
|
|
||||||
|
|
||||||
for (bid = 0; bid < fn->nblk; bid++) {
|
|
||||||
ifb = fn->rpo[bid];
|
|
||||||
if (ifb == 0)
|
|
||||||
continue;
|
|
||||||
if (ifgraph(ifb, &thenb, &elseb, &joinb))
|
|
||||||
if (joinb->phi == 0)
|
|
||||||
if (thenb == ifb || emptyblk(thenb))
|
|
||||||
if (elseb == ifb || emptyblk(elseb)) {
|
|
||||||
if (debug['B'])
|
|
||||||
fprintf(stderr, " eliminating empty if @%s -> @%s, @%s -> @%s\n",
|
|
||||||
ifb->name, thenb->name, elseb->name, joinb->name);
|
|
||||||
if (thenb != ifb)
|
|
||||||
fn->rpo[thenb->id] = 0;
|
|
||||||
if (elseb != ifb)
|
|
||||||
fn->rpo[elseb->id] = 0;
|
|
||||||
ifb->jmp.type = Jjmp;
|
|
||||||
ifb->jmp.arg = R;
|
|
||||||
ifb->s1 = joinb;
|
|
||||||
ifb->s2 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
killblks(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
clrbvisit(Fn *fn)
|
|
||||||
{
|
|
||||||
Blk *b;
|
|
||||||
for (b = fn->start; b; b = b->link)
|
|
||||||
b->visit = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
reaches1(Blk *b1, Blk *b2)
|
reachrec(Blk *b, Blk *to)
|
||||||
{
|
{
|
||||||
assert(b2);
|
if (b == to)
|
||||||
if (b1 == b2)
|
|
||||||
return 1;
|
return 1;
|
||||||
if (b1 == 0 || b1->visit)
|
if (!b || b->visit)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
b->visit = 1;
|
||||||
|
if (reachrec(b->s1, to))
|
||||||
|
return 1;
|
||||||
|
if (reachrec(b->s2, to))
|
||||||
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
b1->visit = 1;
|
|
||||||
return reaches1(b1->s1, b2) || reaches1(b1->s2, b2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* path from b1 to b2 not thru bnotvia? */
|
/* Blk.visit needs to be clear at entry */
|
||||||
/* uses b->visit */
|
|
||||||
int
|
int
|
||||||
reachesnotvia(Fn *fn, Blk *b1, Blk *b2, Blk *bnotvia)
|
reaches(Fn *fn, Blk *b, Blk *to)
|
||||||
{
|
{
|
||||||
int rc;
|
int r;
|
||||||
|
|
||||||
if (bnotvia)
|
assert(to);
|
||||||
bnotvia->visit = 1;
|
r = reachrec(b, to);
|
||||||
rc = reaches1(b1, b2);
|
for (b=fn->start; b; b=b->link)
|
||||||
clrbvisit(fn);
|
b->visit = 0;
|
||||||
return rc;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* path from b1 to b2? */
|
/* can b reach 'to' not through excl
|
||||||
/* uses b->visit */
|
* Blk.visit needs to be clear at entry */
|
||||||
int
|
int
|
||||||
reaches(Fn *fn, Blk *b1, Blk *b2)
|
reachesnotvia(Fn *fn, Blk *b, Blk *to, Blk *excl)
|
||||||
{
|
{
|
||||||
return reachesnotvia(fn, b1, b2, 0);
|
excl->visit = 1;
|
||||||
|
return reaches(fn, b, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
354
copy.c
354
copy.c
@ -1,9 +1,35 @@
|
|||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
static uint
|
typedef struct Ext Ext;
|
||||||
u64_wbits(uint64_t v)
|
|
||||||
|
struct Ext {
|
||||||
|
char zext;
|
||||||
|
char nopw; /* is a no-op if arg width is <= nopw */
|
||||||
|
char usew; /* uses only the low usew bits of arg */
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
ext(Ins *i, Ext *e)
|
||||||
{
|
{
|
||||||
uint n;
|
static Ext tbl[] = {
|
||||||
|
/*extsb*/ {0, 7, 8},
|
||||||
|
/*extub*/ {1, 8, 8},
|
||||||
|
/*extsh*/ {0, 15, 16},
|
||||||
|
/*extuh*/ {1, 16, 16},
|
||||||
|
/*extsw*/ {0, 31, 32},
|
||||||
|
/*extuw*/ {1, 32, 32},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isext(i->op))
|
||||||
|
return 0;
|
||||||
|
*e = tbl[i->op - Oextsb];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
bitwidth(uint64_t v)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
n = 0;
|
n = 0;
|
||||||
if (v >> 32) { n += 32; v >>= 32; }
|
if (v >> 32) { n += 32; v >>= 32; }
|
||||||
@ -15,44 +41,29 @@ u64_wbits(uint64_t v)
|
|||||||
return n+v;
|
return n+v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* no more than w bits are used */
|
||||||
static int
|
static int
|
||||||
EXTSIGNED[] = { /*extsb*/1, /*extub*/0, /*extsh*/1, /*extuh*/0, /*extsw*/1, /*extuw*/0 };
|
usewidthle(Fn *fn, Ref r, int w)
|
||||||
|
|
||||||
static uint
|
|
||||||
EXTMAXW[] = { /*extsb*/7, /*extub*/8, /*extsh*/15, /*extuh*/16, /*extsw*/31, /*extuw*/32 };
|
|
||||||
|
|
||||||
static uint
|
|
||||||
EXTW[] = { /*extsb*/8, /*extub*/8, /*extsh*/16, /*extuh*/16, /*extsw*/32, /*extuw*/32 };
|
|
||||||
|
|
||||||
static uint
|
|
||||||
STW[] = { /*storeb*/8, /*storeh*/16, /*storew*/32, /*storel*/64, /*stores*/32, /*stored*/64 };
|
|
||||||
|
|
||||||
/* is the ref used only as a narrow value? */
|
|
||||||
static int
|
|
||||||
usewidthle(Fn *fn, Ref r, uint wbits)
|
|
||||||
{
|
{
|
||||||
|
Ext e;
|
||||||
Tmp *t;
|
Tmp *t;
|
||||||
Use *u;
|
Use *u;
|
||||||
Phi *p;
|
Phi *p;
|
||||||
int b;
|
|
||||||
Ins *i;
|
Ins *i;
|
||||||
Ref rc;
|
Ref rc;
|
||||||
int64_t v;
|
int64_t v;
|
||||||
|
int b;
|
||||||
|
|
||||||
if (isconbits(fn, r, &v))
|
assert(rtype(r) == RTmp);
|
||||||
if (u64_wbits(v) <= wbits)
|
|
||||||
return 1;
|
|
||||||
if (rtype(r) != RTmp)
|
|
||||||
return 0;
|
|
||||||
t = &fn->tmp[r.val];
|
t = &fn->tmp[r.val];
|
||||||
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
for (u=t->use; u<&t->use[t->nuse]; u++) {
|
||||||
switch (u->type) {
|
switch (u->type) {
|
||||||
case UPhi:
|
case UPhi:
|
||||||
p = u->u.phi;
|
p = u->u.phi;
|
||||||
if (p->visit)
|
if (p->visit)
|
||||||
continue;
|
continue;
|
||||||
p->visit = 1;
|
p->visit = 1;
|
||||||
b = usewidthle(fn, p->to, wbits);
|
b = usewidthle(fn, p->to, w);
|
||||||
p->visit = 0;
|
p->visit = 0;
|
||||||
if (b)
|
if (b)
|
||||||
continue;
|
continue;
|
||||||
@ -61,14 +72,13 @@ usewidthle(Fn *fn, Ref r, uint wbits)
|
|||||||
i = u->u.ins;
|
i = u->u.ins;
|
||||||
assert(i != 0);
|
assert(i != 0);
|
||||||
if (i->op == Ocopy)
|
if (i->op == Ocopy)
|
||||||
if (usewidthle(fn, i->to, wbits))
|
if (usewidthle(fn, i->to, w))
|
||||||
continue;
|
continue;
|
||||||
if (isext(i->op)) {
|
if (ext(i, &e)) {
|
||||||
if (EXTW[i->op - Oextsb] <= wbits)
|
if (e.usew <= w)
|
||||||
|
continue;
|
||||||
|
if (usewidthle(fn, i->to, w))
|
||||||
continue;
|
continue;
|
||||||
else
|
|
||||||
if (usewidthle(fn, i->to, wbits))
|
|
||||||
continue;;
|
|
||||||
}
|
}
|
||||||
if (i->op == Oand) {
|
if (i->op == Oand) {
|
||||||
if (req(r, i->arg[0]))
|
if (req(r, i->arg[0]))
|
||||||
@ -77,15 +87,11 @@ usewidthle(Fn *fn, Ref r, uint wbits)
|
|||||||
assert(req(r, i->arg[1]));
|
assert(req(r, i->arg[1]));
|
||||||
rc = i->arg[0];
|
rc = i->arg[0];
|
||||||
}
|
}
|
||||||
if (isconbits(fn, rc, &v))
|
if (isconbits(fn, rc, &v)
|
||||||
if (u64_wbits(v) <= wbits)
|
&& bitwidth(v) <= w)
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (isstore(i->op))
|
|
||||||
if (req(r, i->arg[1]))
|
|
||||||
if (STW[i->op - Ostoreb] > wbits)
|
|
||||||
continue;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -95,27 +101,17 @@ usewidthle(Fn *fn, Ref r, uint wbits)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Phi*
|
static int
|
||||||
findphi(Fn *fn, uint bid, Ref to)
|
min(int v1, int v2)
|
||||||
{
|
|
||||||
Phi *p;
|
|
||||||
for (p = fn->rpo[bid]->phi; p; p = p->link)
|
|
||||||
if (req(p->to, to))
|
|
||||||
break;
|
|
||||||
assert(p);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint
|
|
||||||
uint_min(uint v1, uint v2)
|
|
||||||
{
|
{
|
||||||
return v1 < v2 ? v1 : v2;
|
return v1 < v2 ? v1 : v2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* is the ref def a narrow value? */
|
/* is the ref narrower than w bits */
|
||||||
static int
|
static int
|
||||||
defwidthle(Fn *fn, Ref r, uint wbits)
|
defwidthle(Fn *fn, Ref r, int w)
|
||||||
{
|
{
|
||||||
|
Ext e;
|
||||||
Tmp *t;
|
Tmp *t;
|
||||||
Phi *p;
|
Phi *p;
|
||||||
Ins *i;
|
Ins *i;
|
||||||
@ -123,93 +119,99 @@ defwidthle(Fn *fn, Ref r, uint wbits)
|
|||||||
int64_t v;
|
int64_t v;
|
||||||
int x;
|
int x;
|
||||||
|
|
||||||
if (isconbits(fn, r, &v))
|
if (isconbits(fn, r, &v)
|
||||||
if (u64_wbits(v) <= wbits)
|
&& bitwidth(v) <= w)
|
||||||
return 1;
|
return 1;
|
||||||
if (rtype(r) != RTmp)
|
if (rtype(r) != RTmp)
|
||||||
return 0;
|
return 0;
|
||||||
t = &fn->tmp[r.val];
|
t = &fn->tmp[r.val];
|
||||||
if (t->cls != Kw)
|
if (t->cls != Kw)
|
||||||
return 0;
|
return 0;
|
||||||
i = t->def;
|
|
||||||
if (i == 0) {
|
if (!t->def) {
|
||||||
/* phi def */
|
/* phi def */
|
||||||
p = findphi(fn, t->bid, r);
|
for (p=fn->rpo[t->bid]->phi; p; p=p->link)
|
||||||
|
if (req(p->to, r))
|
||||||
|
break;
|
||||||
|
assert(p);
|
||||||
if (p->visit)
|
if (p->visit)
|
||||||
return 1;
|
return 1;
|
||||||
p->visit = 1;
|
p->visit = 1;
|
||||||
for (n = 0; n < p->narg; n++)
|
for (n=0; n<p->narg; n++)
|
||||||
if (!defwidthle(fn, p->arg[n], wbits)) {
|
if (!defwidthle(fn, p->arg[n], w)) {
|
||||||
p->visit = 0;
|
p->visit = 0;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
p->visit = 0;
|
p->visit = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
/* ins def */
|
|
||||||
|
i = t->def;
|
||||||
if (i->op == Ocopy)
|
if (i->op == Ocopy)
|
||||||
return defwidthle(fn, i->arg[0], wbits);
|
return defwidthle(fn, i->arg[0], w);
|
||||||
if (i->op == Oshr || i->op == Osar) {
|
if (i->op == Oshr || i->op == Osar) {
|
||||||
if (isconbits(fn, i->arg[1], &v))
|
if (isconbits(fn, i->arg[1], &v))
|
||||||
if (0 < v && v <= 32) {
|
if (0 < v && v <= 32) {
|
||||||
if (i->op == Oshr && 32-v <= wbits)
|
if (i->op == Oshr && w+v >= 32)
|
||||||
return 1;
|
return 1;
|
||||||
if (0 <= v && v < 32 && wbits < 32)
|
if (w < 32) {
|
||||||
return defwidthle(fn, i->arg[0], uint_min((i->op == Osar ? 31 : 32), wbits+v));
|
if (i->op == Osar)
|
||||||
|
w = min(31, w+v);
|
||||||
|
else
|
||||||
|
w = min(32, w+v);
|
||||||
}
|
}
|
||||||
return defwidthle(fn, i->arg[0], wbits);
|
}
|
||||||
|
return defwidthle(fn, i->arg[0], w);
|
||||||
}
|
}
|
||||||
if (iscmp(i->op, &x, &x))
|
if (iscmp(i->op, &x, &x))
|
||||||
return wbits >= 1;
|
return w >= 1;
|
||||||
if (i->op == Oand)
|
if (i->op == Oand) {
|
||||||
return defwidthle(fn, i->arg[0], wbits) || defwidthle(fn, i->arg[1], wbits);
|
if (defwidthle(fn, i->arg[0], w)
|
||||||
if (i->op == Oor || i->op == Oxor)
|
|| defwidthle(fn, i->arg[1], w))
|
||||||
return defwidthle(fn, i->arg[0], wbits) && defwidthle(fn, i->arg[1], wbits);
|
|
||||||
if (isext(i->op)) {
|
|
||||||
if (EXTSIGNED[i->op - Oextsb])
|
|
||||||
return defwidthle(fn, i->arg[0], uint_min(wbits, EXTMAXW[i->op - Oextsb]));
|
|
||||||
if (EXTW[i->op - Oextsb] <= wbits)
|
|
||||||
return 1;
|
return 1;
|
||||||
return defwidthle(fn, i->arg[0], wbits);
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (i->op == Oor || i->op == Oxor) {
|
||||||
|
if (defwidthle(fn, i->arg[0], w)
|
||||||
|
&& defwidthle(fn, i->arg[1], w))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (ext(i, &e)) {
|
||||||
|
if (e.zext && e.usew <= w)
|
||||||
|
return 1;
|
||||||
|
w = min(w, e.nopw);
|
||||||
|
return defwidthle(fn, i->arg[0], w);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* is the ref a boolean - 0, 1 - value? */
|
static int
|
||||||
int
|
isw1(Fn *fn, Ref r)
|
||||||
iswu1(Fn *fn, Ref r)
|
|
||||||
{
|
{
|
||||||
return defwidthle(fn, r, 1);
|
return defwidthle(fn, r, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
/* insert early extub/extuh instructions
|
||||||
isnarrowpar(Fn *fn, Ref r)
|
* for pars used only narrowly; this
|
||||||
{
|
* helps factoring extensions out of
|
||||||
Tmp *t;
|
* loops
|
||||||
|
*
|
||||||
if (rtype(r) != RTmp)
|
* needs use; breaks use
|
||||||
return 0;
|
*/
|
||||||
t = &fn->tmp[r.val];
|
|
||||||
if (t->bid != fn->start->id || t->def == 0)
|
|
||||||
return 0;
|
|
||||||
return ispar(t->def->op);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Insert extub/extuh instructions in start for pars used only narrowly */
|
|
||||||
/* needs use; breaks use */
|
|
||||||
void
|
void
|
||||||
narrowpars(Fn *fn)
|
narrowpars(Fn *fn)
|
||||||
{
|
{
|
||||||
Blk *b;
|
Blk *b;
|
||||||
int loop;
|
int loop;
|
||||||
Ins *i, *ins;
|
Ins ext, *i, *ins;
|
||||||
uint npar, nins;
|
uint npar, nins;
|
||||||
enum O extop;
|
|
||||||
Ref r;
|
Ref r;
|
||||||
|
|
||||||
/* only useful for functions with loops */
|
/* only useful for functions with loops */
|
||||||
loop = 0;
|
loop = 0;
|
||||||
for (b = fn->start; b; b = b->link)
|
for (b=fn->start; b; b=b->link)
|
||||||
if (b->loop > 1) {
|
if (b->loop > 1) {
|
||||||
loop = 1;
|
loop = 1;
|
||||||
break;
|
break;
|
||||||
@ -218,49 +220,47 @@ narrowpars(Fn *fn)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
b = fn->start;
|
b = fn->start;
|
||||||
npar = 0;
|
|
||||||
|
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++) {
|
npar = 0;
|
||||||
|
for (i=b->ins; i<&b->ins[b->nins]; i++) {
|
||||||
if (!ispar(i->op))
|
if (!ispar(i->op))
|
||||||
break;
|
break;
|
||||||
npar++;
|
npar++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (npar == 0)
|
if (npar == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nins = b->nins + npar;
|
nins = b->nins + npar;
|
||||||
ins = vnew(nins, sizeof ins[0], PFn); //alloc(nins * sizeof ins[0]);
|
ins = vnew(nins, sizeof ins[0], PFn);
|
||||||
memcpy(ins, b->ins, npar * sizeof ins[0]);
|
icpy(ins, b->ins, npar);
|
||||||
memcpy(ins + 2*npar, b->ins + npar, (b->nins - npar) * sizeof ins[0]);
|
icpy(ins + 2*npar, b->ins+npar, b->nins-npar);
|
||||||
b->ins = ins;
|
b->ins = ins;
|
||||||
b->nins = nins;
|
b->nins = nins;
|
||||||
|
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++) {
|
for (i=b->ins; i<&b->ins[b->nins]; i++) {
|
||||||
if (!ispar(i->op))
|
if (!ispar(i->op))
|
||||||
break;
|
break;
|
||||||
extop = Onop;
|
ext = (Ins){.op = Onop};
|
||||||
if (i->cls == Kw)
|
if (i->cls == Kw)
|
||||||
if (usewidthle(fn, i->to, 16)) {
|
if (usewidthle(fn, i->to, 16)) {
|
||||||
|
ext.op = Oextuh;
|
||||||
if (usewidthle(fn, i->to, 8))
|
if (usewidthle(fn, i->to, 8))
|
||||||
extop = Oextub;
|
ext.op = Oextub;
|
||||||
else
|
|
||||||
extop = Oextuh;
|
|
||||||
}
|
|
||||||
if (extop == Onop) {
|
|
||||||
*(i+npar) = (Ins) {.op = Onop};
|
|
||||||
} else {
|
|
||||||
r = newtmp("vw", i->cls, fn);
|
r = newtmp("vw", i->cls, fn);
|
||||||
*(i+npar) = (Ins) {.op = extop, .cls = i->cls, .to = i->to, .arg = {r}};
|
ext.cls = i->cls;
|
||||||
|
ext.to = i->to;
|
||||||
|
ext.arg[0] = r;
|
||||||
i->to = r;
|
i->to = r;
|
||||||
}
|
}
|
||||||
|
*(i+npar) = ext;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* used by GVN */
|
|
||||||
Ref
|
Ref
|
||||||
copyref(Fn *fn, Blk *b, Ins *i)
|
copyref(Fn *fn, Blk *b, Ins *i)
|
||||||
{
|
{
|
||||||
|
/* which extensions are copies for a given
|
||||||
|
* argument width */
|
||||||
static bits extcpy[] = {
|
static bits extcpy[] = {
|
||||||
[WFull] = 0,
|
[WFull] = 0,
|
||||||
[Wsb] = BIT(Wsb) | BIT(Wsh) | BIT(Wsw),
|
[Wsb] = BIT(Wsb) | BIT(Wsh) | BIT(Wsw),
|
||||||
@ -270,63 +270,133 @@ copyref(Fn *fn, Blk *b, Ins *i)
|
|||||||
[Wsw] = BIT(Wsw),
|
[Wsw] = BIT(Wsw),
|
||||||
[Wuw] = BIT(Wuw),
|
[Wuw] = BIT(Wuw),
|
||||||
};
|
};
|
||||||
bits bext;
|
Ext e;
|
||||||
Tmp *t;
|
Tmp *t;
|
||||||
int64_t v;
|
int64_t v;
|
||||||
int is0;
|
int w, z;
|
||||||
|
|
||||||
if (i->op == Ocopy)
|
if (i->op == Ocopy)
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
|
||||||
/* op identity value */
|
/* op identity value */
|
||||||
if (optab[i->op].hasid)
|
if (optab[i->op].hasid
|
||||||
if (KBASE(i->cls) == 0) /* integer only - fp NaN! */
|
&& KBASE(i->cls) == 0 /* integer only - fp NaN! */
|
||||||
if (req(i->arg[1], con01[optab[i->op].idval]))
|
&& req(i->arg[1], con01[optab[i->op].idval])
|
||||||
if (!optab[i->op].cmpeqwl || iswu1(fn, i->arg[0]))
|
&& (!optab[i->op].cmpeqwl || isw1(fn, i->arg[0])))
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
|
||||||
/* idempotent op with identical args */
|
/* idempotent op with identical args */
|
||||||
if (optab[i->op].idemp)
|
if (optab[i->op].idemp
|
||||||
if (req(i->arg[0], i->arg[1]))
|
&& req(i->arg[0], i->arg[1]))
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
|
||||||
/* integer cmp with identical args */
|
/* integer cmp with identical args */
|
||||||
if (optab[i->op].cmpeqwl || optab[i->op].cmplgtewl)
|
if ((optab[i->op].cmpeqwl || optab[i->op].cmplgtewl)
|
||||||
if (req(i->arg[0], i->arg[1]))
|
&& req(i->arg[0], i->arg[1]))
|
||||||
return con01[optab[i->op].eqval];
|
return con01[optab[i->op].eqval];
|
||||||
|
|
||||||
/* cmpeq/ne 0 with 0/non-0 inference from dominating jnz */
|
/* cmpeq/ne 0 with 0/non-0 inference */
|
||||||
if (optab[i->op].cmpeqwl)
|
if (optab[i->op].cmpeqwl
|
||||||
if (req(i->arg[1], con01[0]))
|
&& req(i->arg[1], CON_Z)
|
||||||
if (is0non0(fn, b, i->arg[0], argcls(i,0), &is0))
|
&& zeroval(fn, b, i->arg[0], argcls(i, 0), &z))
|
||||||
return con01[optab[i->op].eqval^is0^1];
|
return con01[optab[i->op].eqval^z^1];
|
||||||
|
|
||||||
/* redundant and mask */
|
/* redundant and mask */
|
||||||
if (i->op == Oand)
|
if (i->op == Oand
|
||||||
if (isconbits(fn, i->arg[1], &v))
|
&& isconbits(fn, i->arg[1], &v)
|
||||||
if (((v+1) & v) == 0) /* v == 2^N-1 */
|
&& (v > 0 && ((v+1) & v) == 0)
|
||||||
if (defwidthle(fn, i->arg[0], u64_wbits(v)))
|
&& defwidthle(fn, i->arg[0], bitwidth(v)))
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
|
||||||
if (!isext(i->op) || rtype(i->arg[0]) != RTmp)
|
if (i->cls == Kw
|
||||||
return R;
|
&& (i->op == Oextsw || i->op == Oextuw))
|
||||||
if (i->op == Oextsw || i->op == Oextuw)
|
|
||||||
if (i->cls == Kw)
|
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
|
||||||
|
if (ext(i, &e) && rtype(i->arg[0]) == RTmp) {
|
||||||
t = &fn->tmp[i->arg[0].val];
|
t = &fn->tmp[i->arg[0].val];
|
||||||
assert(KBASE(t->cls) == 0);
|
assert(KBASE(t->cls) == 0);
|
||||||
if (i->cls == Kl && t->cls == Kw)
|
|
||||||
|
/* do not break typing by returning
|
||||||
|
* a narrower temp */
|
||||||
|
if (KWIDE(i->cls) > KWIDE(t->cls))
|
||||||
return R;
|
return R;
|
||||||
bext = extcpy[t->width];
|
|
||||||
if ((BIT(Wsb + (i->op-Oextsb)) & bext) != 0)
|
w = Wsb + (i->op - Oextsb);
|
||||||
|
if (BIT(w) & extcpy[t->width])
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
|
||||||
if (!isnarrowpar(fn, i->arg[0]))
|
/* avoid eliding extensions of params
|
||||||
if (usewidthle(fn, i->to, EXTW[i->op - Oextsb]))
|
* inserted in the start block; their
|
||||||
|
* point is to make further extensions
|
||||||
|
* redundant */
|
||||||
|
if ((!t->def || !ispar(t->def->op))
|
||||||
|
&& usewidthle(fn, i->to, e.usew))
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
if (defwidthle(fn, i->arg[0], EXTMAXW[i->op - Oextsb]))
|
|
||||||
|
if (defwidthle(fn, i->arg[0], e.nopw))
|
||||||
return i->arg[0];
|
return i->arg[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
phieq(Phi *pa, Phi *pb)
|
||||||
|
{
|
||||||
|
Ref r;
|
||||||
|
uint n;
|
||||||
|
|
||||||
|
assert(pa->narg == pb->narg);
|
||||||
|
for (n=0; n<pa->narg; n++) {
|
||||||
|
r = phiarg(pb, pa->blk[n]);
|
||||||
|
if (!req(pa->arg[n], r))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref
|
||||||
|
phicopyref(Fn *fn, Blk *b, Phi *p)
|
||||||
|
{
|
||||||
|
Blk *d, **s;
|
||||||
|
Phi *p1;
|
||||||
|
uint n, c;
|
||||||
|
|
||||||
|
/* identical args */
|
||||||
|
for (n=0; n<p->narg-1; n++)
|
||||||
|
if (!req(p->arg[n], p->arg[n+1]))
|
||||||
|
break;
|
||||||
|
if (n == p->narg-1)
|
||||||
|
return p->arg[n];
|
||||||
|
|
||||||
|
/* same as a previous phi */
|
||||||
|
for (p1=b->phi; p1!=p; p1=p1->link) {
|
||||||
|
assert(p1);
|
||||||
|
if (phieq(p1, p))
|
||||||
|
return p1->to;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* can be replaced by a
|
||||||
|
* dominating jnz arg */
|
||||||
|
d = b->idom;
|
||||||
|
if (p->narg != 2
|
||||||
|
|| d->jmp.type != Jjnz
|
||||||
|
|| !isw1(fn, d->jmp.arg))
|
||||||
|
return R;
|
||||||
|
|
||||||
|
s = (Blk*[]){0, 0};
|
||||||
|
for (n=0; n<2; n++)
|
||||||
|
for (c=0; c<2; c++)
|
||||||
|
if (req(p->arg[n], con01[c]))
|
||||||
|
s[c] = p->blk[n];
|
||||||
|
|
||||||
|
/* if s1 ends with a jnz on either b
|
||||||
|
* or s2; the inference below is wrong
|
||||||
|
* without the jump type checks */
|
||||||
|
if (d->s1 == s[1] && d->s2 == s[0]
|
||||||
|
&& d->s1->jmp.type == Jjmp
|
||||||
|
&& d->s2->jmp.type == Jjmp)
|
||||||
|
return d->jmp.arg;
|
||||||
|
|
||||||
return R;
|
return R;
|
||||||
}
|
}
|
||||||
|
|||||||
2
fold.c
2
fold.c
@ -13,7 +13,7 @@ iscon(Con *c, int w, uint64_t k)
|
|||||||
return (uint32_t)c->bits.i == (uint32_t)k;
|
return (uint32_t)c->bits.i == (uint32_t)k;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
foldint(Con *res, int op, int w, Con *cl, Con *cr)
|
foldint(Con *res, int op, int w, Con *cl, Con *cr)
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
|
|||||||
365
gcm.c
365
gcm.c
@ -2,36 +2,35 @@
|
|||||||
|
|
||||||
#define NOBID (-1u)
|
#define NOBID (-1u)
|
||||||
|
|
||||||
/* ins can trap at runtime */
|
|
||||||
static int
|
static int
|
||||||
istrapping(Fn *fn, Ins *i)
|
isdivwl(Ins *i)
|
||||||
{
|
{
|
||||||
int64_t v;
|
switch (i->op) {
|
||||||
|
case Odiv:
|
||||||
if (KBASE(i->cls) == 0)
|
case Orem:
|
||||||
if (INRANGE(i->op, Odiv, Ourem))
|
case Oudiv:
|
||||||
if (!isconbits(fn, i->arg[1], &v) || v == 0)
|
case Ourem:
|
||||||
return 1;
|
return KBASE(i->cls) == 0;
|
||||||
|
default:
|
||||||
return 0;
|
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
|
int
|
||||||
isfixed(Fn *fn, Ins *i)
|
pinned(Ins *i)
|
||||||
{
|
{
|
||||||
return optab[i->op].ispinned || istrapping(fn, i);
|
return optab[i->op].pinned || isdivwl(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pinned ins that can be eliminated if unused */
|
||||||
|
static int
|
||||||
|
canelim(Ins *i)
|
||||||
|
{
|
||||||
|
return isload(i->op) || isalloc(i->op) || isdivwl(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint earlyins(Fn *, Blk *, Ins *);
|
static uint earlyins(Fn *, Blk *, Ins *);
|
||||||
|
|
||||||
/* return earlybid of ref def ins */
|
|
||||||
static uint
|
static uint
|
||||||
schedearly(Fn *fn, Ref r)
|
schedearly(Fn *fn, Ref r)
|
||||||
{
|
{
|
||||||
@ -39,43 +38,39 @@ schedearly(Fn *fn, Ref r)
|
|||||||
Blk *b;
|
Blk *b;
|
||||||
|
|
||||||
if (rtype(r) != RTmp)
|
if (rtype(r) != RTmp)
|
||||||
return 0 /* root/start */;
|
return 0;
|
||||||
|
|
||||||
t = &fn->tmp[r.val];
|
t = &fn->tmp[r.val];
|
||||||
if (t->gcmbid != NOBID)
|
if (t->gcmbid != NOBID)
|
||||||
return t->gcmbid; /* already visited/visiting */
|
return t->gcmbid;
|
||||||
|
|
||||||
b = fn->rpo[t->bid];
|
b = fn->rpo[t->bid];
|
||||||
if (t->def) {
|
if (t->def) {
|
||||||
/* def is an ins */
|
|
||||||
assert(b->ins <= t->def && t->def < &b->ins[b->nins]);
|
assert(b->ins <= t->def && t->def < &b->ins[b->nins]);
|
||||||
t->gcmbid = 0; /* mark visiting root/start blk */
|
t->gcmbid = 0; /* mark as visiting */
|
||||||
t->gcmbid = earlyins(fn, b, t->def); /* schedule ins input defs */
|
t->gcmbid = earlyins(fn, b, t->def);
|
||||||
} else {
|
} else {
|
||||||
/* def is a phi - it stays in its def blk */
|
/* phis do not move */
|
||||||
t->gcmbid = t->bid;
|
t->gcmbid = t->bid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return t->gcmbid;
|
return t->gcmbid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return deepest domdpth bid of arg defs */
|
|
||||||
static uint
|
static uint
|
||||||
earlyins(Fn *fn, Blk *b, Ins* i)
|
earlyins(Fn *fn, Blk *b, Ins *i)
|
||||||
{
|
{
|
||||||
uint earlybid, arg1earlybid;
|
uint b0, b1;
|
||||||
|
|
||||||
earlybid = schedearly(fn, i->arg[0]);
|
b0 = schedearly(fn, i->arg[0]);
|
||||||
assert(earlybid != NOBID);
|
assert(b0 != NOBID);
|
||||||
arg1earlybid = schedearly(fn, i->arg[1]);
|
b1 = schedearly(fn, i->arg[1]);
|
||||||
assert(arg1earlybid != NOBID);
|
assert(b1 != NOBID);
|
||||||
if (fn->rpo[earlybid]->domdpth < fn->rpo[arg1earlybid]->domdpth) {
|
if (fn->rpo[b0]->depth < fn->rpo[b1]->depth) {
|
||||||
assert(dom(fn->rpo[earlybid], fn->rpo[arg1earlybid]));
|
assert(dom(fn->rpo[b0], fn->rpo[b1]));
|
||||||
earlybid = arg1earlybid;
|
b0 = b1;
|
||||||
}
|
}
|
||||||
|
return pinned(i) ? b->id : b0;
|
||||||
/* fixed ins remain in their defining blk */
|
|
||||||
return isfixed(fn, i) ? b->id : earlybid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -87,12 +82,14 @@ earlyblk(Fn *fn, uint bid)
|
|||||||
uint n;
|
uint n;
|
||||||
|
|
||||||
b = fn->rpo[bid];
|
b = fn->rpo[bid];
|
||||||
for (p = b->phi; p; p = p->link)
|
for (p=b->phi; p; p=p->link)
|
||||||
for (n = 0; n < p->narg; n++)
|
for (n=0; n<p->narg; n++)
|
||||||
schedearly(fn, p->arg[n]);
|
schedearly(fn, p->arg[n]);
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
||||||
if (isfixed(fn, i))
|
if (pinned(i)) {
|
||||||
earlyins(fn, b, i);
|
schedearly(fn, i->arg[0]);
|
||||||
|
schedearly(fn, i->arg[1]);
|
||||||
|
}
|
||||||
schedearly(fn, b->jmp.arg);
|
schedearly(fn, b->jmp.arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,16 +151,17 @@ schedlate(Fn *fn, Ref r)
|
|||||||
|
|
||||||
t = &fn->tmp[r.val];
|
t = &fn->tmp[r.val];
|
||||||
if (t->visit)
|
if (t->visit)
|
||||||
return t->gcmbid; /* already visited/visiting */
|
return t->gcmbid;
|
||||||
|
|
||||||
t->visit = 1; /* mark visiting/visited */
|
t->visit = 1;
|
||||||
earlybid = t->gcmbid;
|
earlybid = t->gcmbid;
|
||||||
if (earlybid == NOBID)
|
if (earlybid == NOBID)
|
||||||
return NOBID; /* not used */
|
return NOBID; /* not used */
|
||||||
t->gcmbid = t->bid; /* t->gcmbid is now latebid */
|
|
||||||
|
|
||||||
|
/* reuse gcmbid for late bid */
|
||||||
|
t->gcmbid = t->bid;
|
||||||
latebid = NOBID;
|
latebid = NOBID;
|
||||||
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
for (u=t->use; u<&t->use[t->nuse]; u++) {
|
||||||
assert(u->bid < fn->nblk);
|
assert(u->bid < fn->nblk);
|
||||||
b = fn->rpo[u->bid];
|
b = fn->rpo[u->bid];
|
||||||
switch (u->type) {
|
switch (u->type) {
|
||||||
@ -182,39 +180,37 @@ schedlate(Fn *fn, Ref r)
|
|||||||
}
|
}
|
||||||
latebid = lcabid(fn, latebid, uselatebid);
|
latebid = lcabid(fn, latebid, uselatebid);
|
||||||
}
|
}
|
||||||
|
/* latebid may be NOBID if the temp is used
|
||||||
|
* in fixed instructions that may be eliminated
|
||||||
|
* and are themselves unused transitively */
|
||||||
|
|
||||||
/* phis stay in their def blk */
|
if (t->def && !pinned(t->def))
|
||||||
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);
|
t->gcmbid = bestbid(fn, earlybid, latebid);
|
||||||
}
|
/* else, keep the early one */
|
||||||
|
|
||||||
|
/* now, gcmbid is the best bid */
|
||||||
return t->gcmbid;
|
return t->gcmbid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return lca bid of uses, or NOBID if no active uses */
|
/* returns lca bid of uses or NOBID if
|
||||||
|
* the definition can be eliminated */
|
||||||
static uint
|
static uint
|
||||||
lateins(Fn *fn, Blk *b, Ins *i, Ref r)
|
lateins(Fn *fn, Blk *b, Ins *i, Ref r)
|
||||||
{
|
{
|
||||||
uint latebid;
|
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]);
|
assert(b->ins <= i && i < &b->ins[b->nins]);
|
||||||
|
assert(req(i->arg[0], r) || req(i->arg[1], r));
|
||||||
|
|
||||||
latebid = schedlate(fn, i->to);
|
latebid = schedlate(fn, i->to);
|
||||||
/* allow elimination of unused load/alloc/trapping ins */
|
if (pinned(i)) {
|
||||||
if (latebid == NOBID)
|
if (latebid == NOBID)
|
||||||
if (canelim(fn, i))
|
if (canelim(i))
|
||||||
return NOBID;
|
return NOBID;
|
||||||
/* ... otherwise fixed ins stay in defining blk */
|
return b->id;
|
||||||
return isfixed(fn, i) ? b->id : latebid;
|
}
|
||||||
|
|
||||||
|
return latebid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint
|
static uint
|
||||||
@ -254,17 +250,16 @@ lateblk(Fn *fn, uint bid)
|
|||||||
Ins *i;
|
Ins *i;
|
||||||
|
|
||||||
b = fn->rpo[bid];
|
b = fn->rpo[bid];
|
||||||
for (pp = &b->phi; *(pp);)
|
for (pp=&b->phi; *(pp);)
|
||||||
if (schedlate(fn, (*pp)->to) == NOBID) {
|
if (schedlate(fn, (*pp)->to) == NOBID) {
|
||||||
/* unused */
|
|
||||||
(*pp)->narg = 0; /* mark unused */
|
(*pp)->narg = 0; /* mark unused */
|
||||||
*pp = (*pp)->link; /* remove phi */
|
*pp = (*pp)->link; /* remove phi */
|
||||||
} else
|
} else
|
||||||
pp = &(*pp)->link;
|
pp = &(*pp)->link;
|
||||||
|
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
||||||
if (isfixed(fn, i))
|
if (pinned(i))
|
||||||
lateins(fn, b, i, i->arg[0]);
|
schedlate(fn, i->to);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -274,7 +269,7 @@ addgcmins(Fn *fn, Ins *vins, uint nins)
|
|||||||
Tmp *t;
|
Tmp *t;
|
||||||
Blk *b;
|
Blk *b;
|
||||||
|
|
||||||
for (i = vins; i < &vins[nins]; i++) {
|
for (i=vins; i<&vins[nins]; i++) {
|
||||||
assert(rtype(i->to) == RTmp);
|
assert(rtype(i->to) == RTmp);
|
||||||
t = &fn->tmp[i->to.val];
|
t = &fn->tmp[i->to.val];
|
||||||
b = fn->rpo[t->gcmbid];
|
b = fn->rpo[t->gcmbid];
|
||||||
@ -282,9 +277,10 @@ addgcmins(Fn *fn, Ins *vins, uint nins)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove unused instructions */
|
/* move live instructions to the
|
||||||
/* move instructions to (end of) target block */
|
* end of their target block; use-
|
||||||
/* use-before-def is fixed up afterwards */
|
* before-def errors are fixed by
|
||||||
|
* schedblk */
|
||||||
static void
|
static void
|
||||||
gcmmove(Fn *fn)
|
gcmmove(Fn *fn)
|
||||||
{
|
{
|
||||||
@ -295,13 +291,13 @@ gcmmove(Fn *fn)
|
|||||||
nins = 0;
|
nins = 0;
|
||||||
vins = vnew(nins, sizeof vins[0], PFn);
|
vins = vnew(nins, sizeof vins[0], PFn);
|
||||||
|
|
||||||
for (t=&fn->tmp[Tmp0]; t < &fn->tmp[fn->ntmp]; t++) {
|
for (t=fn->tmp; t<&fn->tmp[fn->ntmp]; t++) {
|
||||||
if (t->def == 0)
|
if (t->def == 0)
|
||||||
continue;
|
continue;
|
||||||
if (t->bid == t->gcmbid)
|
if (t->bid == t->gcmbid)
|
||||||
continue;
|
continue;
|
||||||
i = t->def;
|
i = t->def;
|
||||||
if (isfixed(fn, i) && !canelim(fn, i))
|
if (pinned(i) && !canelim(i))
|
||||||
continue;
|
continue;
|
||||||
assert(rtype(i->to) == RTmp);
|
assert(rtype(i->to) == RTmp);
|
||||||
assert(t == &fn->tmp[i->to.val]);
|
assert(t == &fn->tmp[i->to.val]);
|
||||||
@ -309,176 +305,153 @@ gcmmove(Fn *fn)
|
|||||||
addins(&vins, &nins, i);
|
addins(&vins, &nins, i);
|
||||||
*i = (Ins){.op = Onop};
|
*i = (Ins){.op = Onop};
|
||||||
}
|
}
|
||||||
|
|
||||||
addgcmins(fn, vins, nins);
|
addgcmins(fn, vins, nins);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
/* dfs ordering */
|
||||||
schedins(Fn *fn, Blk *b, Ins *i0, Ins **pvins, uint *pnins)
|
static Ins *
|
||||||
|
schedins(Fn *fn, Blk *b, Ins *i, Ins **pvins, uint *pnins)
|
||||||
{
|
{
|
||||||
Ins *i, *i1;
|
Ins *i0, *i1;
|
||||||
Tmp *t;
|
Tmp *t;
|
||||||
uint na;
|
uint n;
|
||||||
|
|
||||||
if (i0->op == Onop)
|
igroup(b, i, &i0, &i1);
|
||||||
return;
|
for (i=i0; i<i1; i++)
|
||||||
/* arg...call have to stay together */
|
for (n=0; n<2; n++) {
|
||||||
/* TODO - sel0...sel1 too */
|
if (rtype(i->arg[n]) != RTmp)
|
||||||
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;
|
continue;
|
||||||
t = &fn->tmp[i->arg[na].val];
|
t = &fn->tmp[i->arg[n].val];
|
||||||
/* def in different blk, or phi in this blk */
|
if (t->bid != b->id || !t->def)
|
||||||
if (t->bid != b->id || t->def == 0)
|
|
||||||
continue;
|
continue;
|
||||||
/* already scheduled */
|
|
||||||
if (t->def->op == Onop) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
schedins(fn, b, t->def, pvins, pnins);
|
schedins(fn, b, t->def, pvins, pnins);
|
||||||
}
|
}
|
||||||
for (i = i0; i <= i1; i++) {
|
for (i=i0; i<i1; i++) {
|
||||||
addins(pvins, pnins, i);
|
addins(pvins, pnins, i);
|
||||||
*i = (Ins){.op = Onop};
|
*i = (Ins){.op = Onop};
|
||||||
}
|
}
|
||||||
|
return i1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* order ins within a block */
|
||||||
static void
|
static void
|
||||||
fixbub4d(Fn *fn, Blk *b, Ins **pvins)
|
schedblk(Fn *fn)
|
||||||
{
|
|
||||||
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;
|
Blk *b;
|
||||||
Ins *vins; // TODO insb
|
Ins *i, *vins;
|
||||||
|
uint nins;
|
||||||
|
|
||||||
vins = vnew(0, sizeof vins[0], PFn);
|
vins = vnew(0, sizeof vins[0], PHeap);
|
||||||
for (b = fn->start; b; b = b->link)
|
for (b=fn->start; b; b=b->link) {
|
||||||
fixbub4d(fn, b, &vins);
|
nins = 0;
|
||||||
|
for (i=b->ins; i<&b->ins[b->nins];)
|
||||||
|
i = schedins(fn, b, i, &vins, &nins);
|
||||||
|
idup(b, vins, nins);
|
||||||
|
}
|
||||||
|
vfree(vins);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
iskladdcon(Fn *fn, Ins *i, int64_t *v)
|
cheap(Ins *i)
|
||||||
{
|
{
|
||||||
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;
|
int x;
|
||||||
|
|
||||||
assert(b->ins <= i && i < &b->ins[b->nins]);
|
if (KBASE(i->cls) != 0)
|
||||||
if (!iscmp(i->op, &x, &x))
|
return 0;
|
||||||
if (!iskladdcon(fn, i, &v))
|
switch (i->op) {
|
||||||
return;
|
case Oneg:
|
||||||
|
case Oadd:
|
||||||
assert(rtype(i->to) == RTmp);
|
case Osub:
|
||||||
t = &fn->tmp[i->to.val];
|
case Omul:
|
||||||
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
case Oand:
|
||||||
if (u->type == UPhi)
|
case Oor:
|
||||||
continue;
|
case Oxor:
|
||||||
b2 = fn->rpo[u->bid];
|
case Osar:
|
||||||
addins(pvins, pnins, i);
|
case Oshr:
|
||||||
/* careful, can move fn->tmp */
|
case Oshl:
|
||||||
r2 = newtmp("rea", t->cls, fn);
|
return 1;
|
||||||
t = &fn->tmp[i->to.val];
|
default:
|
||||||
t2 = &fn->tmp[r2.val];
|
return iscmp(i->op, &x, &x);
|
||||||
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
|
static void
|
||||||
cleartmps(Fn *fn)
|
sinkref(Fn *fn, Blk *b, Ref *pr)
|
||||||
{
|
{
|
||||||
|
Ins i;
|
||||||
Tmp *t;
|
Tmp *t;
|
||||||
|
Ref r;
|
||||||
|
|
||||||
for (t=&fn->tmp[Tmp0]; t < &fn->tmp[fn->ntmp]; t++) {
|
if (rtype(*pr) != RTmp)
|
||||||
t->visit = 0;
|
return;
|
||||||
t->gcmbid = NOBID;
|
t = &fn->tmp[pr->val];
|
||||||
}
|
if (!t->def
|
||||||
|
|| t->bid == b->id
|
||||||
|
|| pinned(t->def)
|
||||||
|
|| !cheap(t->def))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* sink t->def to b */
|
||||||
|
i = *t->def;
|
||||||
|
r = newtmp("snk", t->cls, fn);
|
||||||
|
t = 0; /* invalidated */
|
||||||
|
*pr = r;
|
||||||
|
i.to = r;
|
||||||
|
fn->tmp[r.val].gcmbid = b->id;
|
||||||
|
emiti(i);
|
||||||
|
sinkref(fn, b, &i.arg[0]);
|
||||||
|
sinkref(fn, b, &i.arg[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* https://courses.cs.washington.edu/courses/cse501/06wi/reading/click-pldi95.pdf */
|
/* redistribute trivial ops to point of
|
||||||
/* require use dom */
|
* use to reduce register pressure
|
||||||
/* maintains rpo pred dom */
|
* requires rpo, use; breaks use
|
||||||
/* breaks use */
|
*/
|
||||||
|
static void
|
||||||
|
sink(Fn *fn)
|
||||||
|
{
|
||||||
|
Blk *b;
|
||||||
|
Ins *i;
|
||||||
|
|
||||||
|
for (b=fn->start; b; b=b->link) {
|
||||||
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
||||||
|
if (isload(i->op))
|
||||||
|
sinkref(fn, b, &i->arg[0]);
|
||||||
|
else if (isstore(i->op))
|
||||||
|
sinkref(fn, b, &i->arg[1]);
|
||||||
|
sinkref(fn, b, &b->jmp.arg);
|
||||||
|
}
|
||||||
|
addgcmins(fn, curi, &insb[NIns] - curi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* requires use dom
|
||||||
|
* maintains rpo pred dom
|
||||||
|
* breaks use
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
gcm(Fn *fn)
|
gcm(Fn *fn)
|
||||||
{
|
{
|
||||||
|
Tmp *t;
|
||||||
uint bid;
|
uint bid;
|
||||||
|
|
||||||
filldomdpth(fn);
|
filldepth(fn);
|
||||||
fillloop(fn);
|
fillloop(fn);
|
||||||
|
|
||||||
cleartmps(fn);
|
for (t=fn->tmp; t<&fn->tmp[fn->ntmp]; t++) {
|
||||||
|
t->visit = 0;
|
||||||
|
t->gcmbid = NOBID;
|
||||||
|
}
|
||||||
for (bid=0; bid<fn->nblk; bid++)
|
for (bid=0; bid<fn->nblk; bid++)
|
||||||
earlyblk(fn, bid);
|
earlyblk(fn, bid);
|
||||||
for (bid=0; bid<fn->nblk; bid++)
|
for (bid=0; bid<fn->nblk; bid++)
|
||||||
lateblk(fn, bid);
|
lateblk(fn, bid);
|
||||||
|
|
||||||
gcmmove(fn);
|
gcmmove(fn);
|
||||||
cleartmps(fn); /* filluse() uses visit */
|
|
||||||
filluse(fn);
|
filluse(fn);
|
||||||
reassoc(fn);
|
curi = &insb[NIns];
|
||||||
|
sink(fn);
|
||||||
filluse(fn);
|
filluse(fn);
|
||||||
fixub4d(fn);
|
schedblk(fn);
|
||||||
|
|
||||||
if (debug['G']) {
|
if (debug['G']) {
|
||||||
fprintf(stderr, "\n> After GCM:\n");
|
fprintf(stderr, "\n> After GCM:\n");
|
||||||
|
|||||||
664
gvn.c
664
gvn.c
@ -1,6 +1,5 @@
|
|||||||
#include "all.h"
|
#include "all.h"
|
||||||
|
|
||||||
/* literal constants 0, 1 */
|
|
||||||
Ref con01[2];
|
Ref con01[2];
|
||||||
|
|
||||||
static inline uint
|
static inline uint
|
||||||
@ -18,121 +17,91 @@ rhash(Ref r)
|
|||||||
static uint
|
static uint
|
||||||
ihash(Ins *i)
|
ihash(Ins *i)
|
||||||
{
|
{
|
||||||
uint a0h, a1h, ah, h;
|
uint h;
|
||||||
|
|
||||||
a0h = rhash(i->arg[0]);
|
h = mix(i->op, i->cls);
|
||||||
a1h = rhash(i->arg[1]);
|
h = mix(h, rhash(i->arg[0]));
|
||||||
ah = mix(a0h, a1h);
|
h = mix(h, rhash(i->arg[1]));
|
||||||
h = mix(i->cls, i->op);
|
|
||||||
h = mix(h, ah);
|
|
||||||
|
|
||||||
return h;
|
return h;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ieq(Ins *ia, Ins *ib)
|
ieq(Ins *ia, Ins *ib)
|
||||||
{
|
{
|
||||||
if (ia->cls == ib->cls)
|
|
||||||
if (ia->op == ib->op)
|
if (ia->op == ib->op)
|
||||||
|
if (ia->cls == ib->cls)
|
||||||
if (req(ia->arg[0], ib->arg[0]))
|
if (req(ia->arg[0], ib->arg[0]))
|
||||||
if (req(ia->arg[1], ib->arg[1]))
|
if (req(ia->arg[1], ib->arg[1]))
|
||||||
return 1;
|
return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Ins** gvntbl;
|
static Ins **gvntbl;
|
||||||
static uint gvntbln;
|
static uint gvntbln;
|
||||||
static uint lookupn;
|
|
||||||
static uint proben;
|
|
||||||
static uint maxproben;
|
|
||||||
|
|
||||||
static Ins *
|
static Ins *
|
||||||
gvndup(Ins *i, int insert) {
|
gvndup(Ins *i, int insert)
|
||||||
|
{
|
||||||
uint idx, n;
|
uint idx, n;
|
||||||
Ins *i2;
|
Ins *ii;
|
||||||
|
|
||||||
lookupn++;
|
|
||||||
|
|
||||||
idx = ihash(i) % gvntbln;
|
idx = ihash(i) % gvntbln;
|
||||||
for (n = 1;; n++) {
|
for (n=1;; n++) {
|
||||||
proben++;
|
ii = gvntbl[idx];
|
||||||
if (n > maxproben)
|
if (!ii)
|
||||||
maxproben = n;
|
|
||||||
i2 = gvntbl[idx];
|
|
||||||
if (!i2)
|
|
||||||
break;
|
break;
|
||||||
if (ieq(i, i2))
|
if (ieq(i, ii))
|
||||||
return i2;
|
return ii;
|
||||||
|
|
||||||
idx++;
|
idx++;
|
||||||
if (gvntbln <= idx)
|
if (gvntbln <= idx)
|
||||||
idx = 0;
|
idx = 0;
|
||||||
}
|
}
|
||||||
/* not found */
|
if (insert)
|
||||||
if (insert) {
|
|
||||||
gvntbl[idx] = i;
|
gvntbl[idx] = i;
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
replaceref(Ref *r, Ref r1, Ref r2)
|
replaceuse(Fn *fn, Use *u, Ref r1, Ref r2)
|
||||||
{
|
{
|
||||||
if (req(*r, r1))
|
|
||||||
*r = r2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
replacepuse(Phi *p, Ref r1, Ref r2)
|
|
||||||
{
|
|
||||||
Ref *a;
|
|
||||||
|
|
||||||
for (a = p->arg; a < &p->arg[p->narg]; a++)
|
|
||||||
replaceref(a, r1, r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
replaceiuse(Ins *i, Ref r1, Ref r2)
|
|
||||||
{
|
|
||||||
replaceref(&i->arg[0], r1, r2);
|
|
||||||
replaceref(&i->arg[1], r1, r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
replacejuse(Blk* b, Ref r1, Ref r2)
|
|
||||||
{
|
|
||||||
replaceref(&b->jmp.arg, r1, r2);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
replaceuse(Fn *fn, Use* u, Ref r1, Ref r2)
|
|
||||||
{
|
|
||||||
Tmp *t2;
|
|
||||||
Blk *b;
|
Blk *b;
|
||||||
|
Ins *i;
|
||||||
|
Phi *p;
|
||||||
|
Ref *pr;
|
||||||
|
Tmp *t2;
|
||||||
|
int n;
|
||||||
|
|
||||||
t2 = rtype(r2) == RTmp ? &fn->tmp[r2.val] : 0;
|
t2 = 0;
|
||||||
|
if (rtype(r2) == RTmp)
|
||||||
|
t2 = &fn->tmp[r2.val];
|
||||||
b = fn->rpo[u->bid];
|
b = fn->rpo[u->bid];
|
||||||
|
|
||||||
switch (u->type) {
|
switch (u->type) {
|
||||||
case UXXX:
|
|
||||||
die("unreachable");
|
|
||||||
break;
|
|
||||||
case UPhi:
|
case UPhi:
|
||||||
replacepuse(u->u.phi, r1, r2);
|
p = u->u.phi;
|
||||||
|
for (pr=p->arg; pr<&p->arg[p->narg]; pr++)
|
||||||
|
if (req(*pr, r1))
|
||||||
|
*pr = r2;
|
||||||
if (t2)
|
if (t2)
|
||||||
adduse(t2, UPhi, b, u->u.phi);
|
adduse(t2, UPhi, b, p);
|
||||||
break;
|
break;
|
||||||
case UIns:
|
case UIns:
|
||||||
replaceiuse(u->u.ins, r1, r2);
|
i = u->u.ins;
|
||||||
|
for (n=0; n<2; n++)
|
||||||
|
if (req(i->arg[n], r1))
|
||||||
|
i->arg[n] = r2;
|
||||||
if (t2)
|
if (t2)
|
||||||
adduse(t2, UIns, b, u->u.ins);
|
adduse(t2, UIns, b, i);
|
||||||
break;
|
break;
|
||||||
case UJmp:
|
case UJmp:
|
||||||
replacejuse(fn->rpo[u->bid], r1, r2);
|
if (req(b->jmp.arg, r1))
|
||||||
|
b->jmp.arg = r2;
|
||||||
if (t2)
|
if (t2)
|
||||||
adduse(t2, UJmp, b);
|
adduse(t2, UJmp, b);
|
||||||
break;
|
break;
|
||||||
|
case UXXX:
|
||||||
|
die("unreachable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,216 +113,33 @@ replaceuses(Fn *fn, Ref r1, Ref r2)
|
|||||||
|
|
||||||
assert(rtype(r1) == RTmp);
|
assert(rtype(r1) == RTmp);
|
||||||
t1 = &fn->tmp[r1.val];
|
t1 = &fn->tmp[r1.val];
|
||||||
|
for (u=t1->use; u<&t1->use[t1->nuse]; u++)
|
||||||
for (u = t1->use; u < &t1->use[t1->nuse]; u++)
|
|
||||||
replaceuse(fn, u, r1, r2);
|
replaceuse(fn, u, r1, r2);
|
||||||
|
|
||||||
t1->nuse = 0;
|
t1->nuse = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rmuse(Fn *fn, Blk *b, uint ty, Ins *i, Phi *p, Ref r, int strict)
|
dedupphi(Fn *fn, Blk *b)
|
||||||
{
|
{
|
||||||
Tmp *t;
|
Phi *p, **pp;
|
||||||
Use *u;
|
Ref r;
|
||||||
int found, rm;
|
|
||||||
|
|
||||||
if (rtype(r) != RTmp)
|
for (pp=&b->phi; (p=*pp);) {
|
||||||
return;
|
r = phicopyref(fn, b, p);
|
||||||
found = 0;
|
if (!req(r, R)) {
|
||||||
t = &fn->tmp[r.val];
|
|
||||||
for (u = t->use; u < &t->use[t->nuse];) {
|
|
||||||
rm = 0;
|
|
||||||
if (u->type == ty) {
|
|
||||||
switch (ty) {
|
|
||||||
case UXXX:
|
|
||||||
die("unreachable");
|
|
||||||
break;
|
|
||||||
case UIns:
|
|
||||||
assert(p == 0);
|
|
||||||
assert(i);
|
|
||||||
rm = u->u.ins == i;
|
|
||||||
break;
|
|
||||||
case UPhi:
|
|
||||||
assert(i == 0);
|
|
||||||
assert(p);
|
|
||||||
rm = u->u.phi == p;
|
|
||||||
break;
|
|
||||||
case UJmp:
|
|
||||||
assert(i == 0);
|
|
||||||
assert(p == 0);
|
|
||||||
rm = u->bid == b->id;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
die("unreachable");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (rm) {
|
|
||||||
found++;
|
|
||||||
assert(u < &t->use[t->nuse]);
|
|
||||||
assert(u->bid == b->id);
|
|
||||||
/* careful - deleting below iterator */
|
|
||||||
memcpy(u, u+1, ((t->nuse) - ((u+1)-t->use)) * sizeof u[0]);
|
|
||||||
t->nuse--;
|
|
||||||
} else
|
|
||||||
u++;
|
|
||||||
}
|
|
||||||
if (strict)
|
|
||||||
assert(found);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
phieq(Phi *pa, Phi *pb)
|
|
||||||
{
|
|
||||||
uint n;
|
|
||||||
|
|
||||||
assert(pa->narg == pb->narg);
|
|
||||||
for (n=0; n<pa->narg; n++) {
|
|
||||||
if (!req(pa->arg[n], phiarg(pb, pa->blk[n])))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
killphi(Fn *fn, Blk *b, Phi **pp, Ref r)
|
|
||||||
{
|
|
||||||
Phi *p;
|
|
||||||
uint n;
|
|
||||||
|
|
||||||
p = *pp;
|
|
||||||
replaceuses(fn, p->to, r);
|
replaceuses(fn, p->to, r);
|
||||||
assert(b->npred == p->narg);
|
p->to = R;
|
||||||
for (n = 0; n < p->narg; n++)
|
|
||||||
rmuse(fn, b, UPhi, 0, p, p->arg[n], 0/*!strict TODO dups*/);
|
|
||||||
p->narg = 0; /* mark as unused - TODO should not be necessary with rmuse*/
|
|
||||||
*pp = p->link;
|
*pp = p->link;
|
||||||
}
|
} else
|
||||||
|
pp = &p->link;
|
||||||
static void
|
|
||||||
ifphiopt(Fn *fn, Blk *b)
|
|
||||||
{
|
|
||||||
Blk *ifb, *thenb, *elseb, *tmp;
|
|
||||||
Phi *p, **pp;
|
|
||||||
Tmp *t;
|
|
||||||
Ref argt, argf, r1;
|
|
||||||
int k, x, caninv;
|
|
||||||
|
|
||||||
/* is b the join blk of an if[-then][-else] graphlet? */
|
|
||||||
if (!ifjoin(b, &ifb, &thenb, &elseb))
|
|
||||||
return;
|
|
||||||
|
|
||||||
assert(ifb->jmp.type == Jjnz);
|
|
||||||
assert(ifb->s1 == b || (ifb->s1 == thenb && thenb->s1 == b));
|
|
||||||
assert(ifb->s2 == b || (ifb->s2 == elseb && elseb->s1 == b));
|
|
||||||
if (!iswu1(fn, ifb->jmp.arg))
|
|
||||||
return; /* no opportunity */
|
|
||||||
|
|
||||||
caninv = 1;
|
|
||||||
for (p = b->phi; p; p = p->link)
|
|
||||||
if (req(phiarg(p, elseb), con01[0])) {
|
|
||||||
caninv = 0;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* phi bool 1/0 "copy" */
|
|
||||||
for (pp = &b->phi; *pp;) {
|
|
||||||
p = *pp;
|
|
||||||
assert(p->narg == 2);
|
|
||||||
argt = phiarg(p, thenb);
|
|
||||||
argf = phiarg(p, elseb);
|
|
||||||
/* jnz jmp.arg in phi is 1/0 */
|
|
||||||
if (req(argt, ifb->jmp.arg)) {
|
|
||||||
if (!req(argf, ifb->jmp.arg))
|
|
||||||
rmuse(fn, b, UPhi, 0, p, argt, 1/*strict*/);
|
|
||||||
argt = p->arg[phiargn(p, thenb)] = con01[1];
|
|
||||||
}
|
|
||||||
if (req(argf, ifb->jmp.arg)) {
|
|
||||||
rmuse(fn, b, UPhi, 0, p, argf, 1/*strict*/);
|
|
||||||
argf = p->arg[phiargn(p, elseb)] = con01[0];
|
|
||||||
}
|
|
||||||
/* prefer 0 as argf and/or 1 as argt, so try to invert the cmp */
|
|
||||||
if (caninv &&
|
|
||||||
((req(argt, con01[0]) && !req(argf, con01[0])) ||
|
|
||||||
(req(argf, con01[1]) && !req(argt, con01[1])))) {
|
|
||||||
assert(rtype(ifb->jmp.arg) == RTmp);
|
|
||||||
t = &fn->tmp[ifb->jmp.arg.val];
|
|
||||||
if (t->nuse == 1)
|
|
||||||
if (t->def && iscmp(t->def->op, &k, &x) && KBASE(k) == 0) {
|
|
||||||
assert(t->use[0].type == UJmp);
|
|
||||||
assert(t->bid == ifb->id);
|
|
||||||
t->def->op = invcmpwl(t->def->op);
|
|
||||||
tmp = ifb->s1;
|
|
||||||
ifb->s1 = ifb->s2;
|
|
||||||
ifb->s2 = tmp;
|
|
||||||
r1 = argt; argt = argf; argf = r1;
|
|
||||||
caninv = 0; /* only once */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (req(argt, con01[1]) && req(argf, con01[0])) {
|
|
||||||
killphi(fn, b, pp, ifb->jmp.arg);
|
|
||||||
caninv = 0; /* used already */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
pp = &(*pp)->link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* phi "copy" - singleton or all args identical */
|
|
||||||
static void
|
|
||||||
copyphielim(Fn *fn, Blk *b) {
|
|
||||||
Phi *p, **pp;
|
|
||||||
uint n;
|
|
||||||
|
|
||||||
for (pp = &b->phi; *pp;) {
|
|
||||||
p = *pp;
|
|
||||||
assert(p->narg == b->npred);
|
|
||||||
for (n = 0; n < p->narg-1; n++) {
|
|
||||||
if (!req(p->arg[n], p->arg[n+1]))
|
|
||||||
goto Skip;
|
|
||||||
}
|
|
||||||
killphi(fn, b, pp, p->arg[0]);
|
|
||||||
continue;
|
|
||||||
Skip:;
|
|
||||||
pp = &(*pp)->link;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* redundant phis */
|
|
||||||
static void
|
|
||||||
dupphielim(Fn *fn, Blk *b) {
|
|
||||||
Phi *p, *p2, **pp;
|
|
||||||
|
|
||||||
/* O(N^2.M) but meh */
|
|
||||||
for (pp = &b->phi; *pp;) {
|
|
||||||
p = *pp;
|
|
||||||
assert(p->narg == b->npred);
|
|
||||||
for (p2 = p->link; p2; p2 = p2->link) {
|
|
||||||
assert(p2->narg == b->npred);
|
|
||||||
if (phieq(p, p2)) {
|
|
||||||
killphi(fn, b, pp, p2->to);
|
|
||||||
goto Skip;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pp = &(*pp)->link;
|
|
||||||
Skip:;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dedupphis(Fn *fn, Blk *b) {
|
|
||||||
ifphiopt(fn, b);
|
|
||||||
copyphielim(fn,b);
|
|
||||||
dupphielim(fn, b);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
rcmp(Ref a, Ref b)
|
rcmp(Ref a, Ref b)
|
||||||
{
|
{
|
||||||
if (rtype(a) != rtype(b))
|
if (rtype(a) != rtype(b))
|
||||||
return rtype(a)-rtype(b);
|
return rtype(a) - rtype(b);
|
||||||
return a.val - b.val;
|
return a.val - b.val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,14 +150,17 @@ normins(Fn *fn, Ins *i)
|
|||||||
int64_t v;
|
int64_t v;
|
||||||
Ref r;
|
Ref r;
|
||||||
|
|
||||||
/* truncate constant bits to 32 bits for "s"/w" uses */
|
/* truncate constant bits to
|
||||||
for (n = 0; n < 2; n++) {
|
* 32 bits for s/w uses */
|
||||||
|
for (n=0; n<2; n++) {
|
||||||
if (!KWIDE(argcls(i, n)))
|
if (!KWIDE(argcls(i, n)))
|
||||||
if (isconbits(fn, i->arg[n], &v))
|
if (isconbits(fn, i->arg[n], &v))
|
||||||
if ((v & 0xffffffff) != v)
|
if ((v & 0xffffffff) != v)
|
||||||
i->arg[n] = getcon(v & 0xffffffff, fn);
|
i->arg[n] = getcon(v & 0xffffffff, fn);
|
||||||
}
|
}
|
||||||
/* order arg[0] <= arg[1] for commutative ops, preferring RTmp in arg[0] */
|
/* order arg[0] <= arg[1] for
|
||||||
|
* commutative ops, preferring
|
||||||
|
* RTmp in arg[0] */
|
||||||
if (optab[i->op].commutes)
|
if (optab[i->op].commutes)
|
||||||
if (rcmp(i->arg[0], i->arg[1]) > 0) {
|
if (rcmp(i->arg[0], i->arg[1]) > 0) {
|
||||||
r = i->arg[1];
|
r = i->arg[1];
|
||||||
@ -380,140 +169,106 @@ normins(Fn *fn, Ins *i)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Ref
|
static int
|
||||||
negcon(Fn *fn, int cls, Ref r)
|
negcon(int cls, Con *c)
|
||||||
{
|
{
|
||||||
int64_t v, v1;
|
static Con z = {.type = CBits, .bits.i = 0};
|
||||||
|
|
||||||
assert(isconbits(fn, r, &v));
|
return foldint(c, Osub, cls, &z, c);
|
||||||
assert(KBASE(cls) == 0);
|
|
||||||
v1 = -v;
|
|
||||||
if (cls == Kw)
|
|
||||||
v1 = ((int64_t)(-(int32_t)v)) & 0xffffffff;
|
|
||||||
return getcon(v1, fn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
assoccon(Fn *fn, Blk *b, Ins *i1)
|
assoccon(Fn *fn, Blk *b, Ins *i1)
|
||||||
{
|
{
|
||||||
Tmp *t2;
|
Tmp *t2;
|
||||||
Ins *i2, i;
|
Ins *i2;
|
||||||
Ref r0, r1, rc;
|
int op, fail;
|
||||||
int op1, op2;
|
Con c, c1, c2;
|
||||||
int64_t v1, v2, vc;
|
|
||||||
|
|
||||||
op1 = i1->op;
|
op = i1->op;
|
||||||
if (op1 == Osub)
|
if (op == Osub)
|
||||||
op1 = Oadd;
|
op = Oadd;
|
||||||
if (!optab[op1].assoc || KBASE(i1->cls) != 0 || rtype(i1->arg[0]) != RTmp
|
|
||||||
|| !isconbits(fn, i1->arg[1], &v1) || (optab[op1].hasid && v1 == optab[op1].idval))
|
if (!optab[op].assoc
|
||||||
|
|| KBASE(i1->cls) != 0
|
||||||
|
|| rtype(i1->arg[0]) != RTmp
|
||||||
|
|| rtype(i1->arg[1]) != RCon)
|
||||||
return;
|
return;
|
||||||
|
c1 = fn->con[i1->arg[1].val];
|
||||||
|
|
||||||
t2 = &fn->tmp[i1->arg[0].val];
|
t2 = &fn->tmp[i1->arg[0].val];
|
||||||
if (t2->def == 0)
|
if (t2->def == 0)
|
||||||
return;
|
return;
|
||||||
i2 = t2->def;
|
i2 = t2->def;
|
||||||
op2 = i2->op;
|
|
||||||
if (op2 == Osub)
|
if (op != (i2->op == Osub ? Oadd : i2->op)
|
||||||
op2 = Oadd;
|
|| rtype(i2->arg[1]) != RCon)
|
||||||
if (op1 != op2 || rtype(i2->arg[0]) != RTmp || !isconbits(fn, i2->arg[1], &v2))
|
|
||||||
return;
|
return;
|
||||||
|
c2 = fn->con[i2->arg[1].val];
|
||||||
|
|
||||||
assert(KBASE(i2->cls) == 0);
|
assert(KBASE(i2->cls) == 0);
|
||||||
assert(i1->cls == Kl || i2->cls == Kw);
|
assert(KWIDE(i2->cls) >= KWIDE(i1->cls));
|
||||||
r0 = i1->arg[1];
|
|
||||||
if (i1->op == Osub)
|
if (i1->op == Osub && negcon(i1->cls, &c1))
|
||||||
r0 = negcon(fn, i1->cls, r0);
|
return;
|
||||||
r1 = i2->arg[1];
|
if (i2->op == Osub && negcon(i2->cls, &c2))
|
||||||
if (i2->op == Osub)
|
return;
|
||||||
r1 = negcon(fn, i2->cls, r1);
|
if (foldint(&c, op, i1->cls, &c1, &c2))
|
||||||
i = (Ins){.to = i2->to, .op = op2, .cls = i2->cls, .arg = {r0, r1}};
|
return;
|
||||||
rc = foldref(fn, &i);
|
|
||||||
assert(isconbits(fn, rc, &vc));
|
if (op == Oadd && c.type == CBits)
|
||||||
if (op1 == Oadd) {
|
if ((i1->cls == Kl && c.bits.i < 0)
|
||||||
if (i2->cls == Kw) {
|
|| (i1->cls == Kw && (int32_t)c.bits.i < 0)) {
|
||||||
if ((int32_t)vc < 0) {
|
fail = negcon(i1->cls, &c);
|
||||||
op1 = Osub;
|
assert(fail == 0);
|
||||||
rc = negcon(fn, Kw, rc);
|
op = Osub;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
assert(i2->cls == Kl);
|
i1->op = op;
|
||||||
if (vc < 0) {
|
|
||||||
op1 = Osub;
|
|
||||||
rc = negcon(fn, Kl, rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
i1->op = op1;
|
|
||||||
rmuse(fn, b, UIns, i1, 0, i1->arg[0], 1/*strict*/);
|
|
||||||
i1->arg[0] = i2->arg[0];
|
i1->arg[0] = i2->arg[0];
|
||||||
|
i1->arg[1] = newcon(&c, fn);
|
||||||
adduse(&fn->tmp[i1->arg[0].val], UIns, b, i1);
|
adduse(&fn->tmp[i1->arg[0].val], UIns, b, i1);
|
||||||
i1->arg[1] = rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
killins(Fn *fn, Blk *b, Ins *i, Ref r1, Ref r2)
|
killins(Fn *fn, Ins *i, Ref r)
|
||||||
{
|
{
|
||||||
replaceuses(fn, r1, r2);
|
replaceuses(fn, i->to, r);
|
||||||
rmuse(fn, b, UIns, i, 0, i->arg[0], 1/*strict*/);
|
|
||||||
if (!req(i->arg[0], i->arg[1]))
|
|
||||||
rmuse(fn, b, UIns, i, 0, i->arg[1], 1/*strict*/);
|
|
||||||
*i = (Ins){.op = Onop};
|
*i = (Ins){.op = Onop};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dedupi(Fn *fn, Blk *b, Ins *i)
|
dedupins(Fn *fn, Blk *b, Ins *i)
|
||||||
{
|
{
|
||||||
Ref r2;
|
Ref r;
|
||||||
Ins *i2;
|
Ins *i1;
|
||||||
|
|
||||||
if (i->op == Onop || i->op == Odbgloc)
|
|
||||||
return;
|
|
||||||
|
|
||||||
normins(fn, i);
|
normins(fn, i);
|
||||||
|
if (i->op == Onop || pinned(i))
|
||||||
if (optab[i->op].ispinned)
|
|
||||||
return;
|
return;
|
||||||
assert(rtype(i->to) == RTmp);
|
|
||||||
|
|
||||||
/* merge associative ops with constant arg[1] */
|
assert(!req(i->to, R));
|
||||||
assoccon(fn, b, i);
|
assoccon(fn, b, i);
|
||||||
|
|
||||||
/* effective copy? */
|
r = copyref(fn, b, i);
|
||||||
r2 = copyref(fn, b, i);
|
if (!req(r, R)) {
|
||||||
if (!req(r2, R)) {
|
killins(fn, i, r);
|
||||||
killins(fn, b, i, i->to, r2);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
r = foldref(fn, i);
|
||||||
/* effective constant? */
|
if (!req(r, R)) {
|
||||||
r2 = foldref(fn, i);
|
killins(fn, i, r);
|
||||||
if (!req(r2, R)) {
|
|
||||||
killins(fn, b, i, i->to, r2);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
i1 = gvndup(i, 1);
|
||||||
/* do not dedup (trapping) ins that GCM will not move */
|
if (i1) {
|
||||||
if (isfixed(fn, i))
|
killins(fn, i, i1->to);
|
||||||
return;
|
|
||||||
|
|
||||||
/* duplicate? */
|
|
||||||
i2 = gvndup(i, 1);
|
|
||||||
if (i2) {
|
|
||||||
killins(fn, b, i, i->to, i2->to);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
dedupins(Fn *fn, Blk *b)
|
|
||||||
{
|
|
||||||
Ins *i;
|
|
||||||
|
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
|
||||||
dedupi(fn, b, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
cmpeq0def(Fn *fn, Ref r, Ref *arg0, int *cls, int *eqval)
|
cmpeqz(Fn *fn, Ref r, Ref *arg, int *cls, int *eqval)
|
||||||
{
|
{
|
||||||
Ins *i;
|
Ins *i;
|
||||||
|
|
||||||
@ -522,8 +277,8 @@ cmpeq0def(Fn *fn, Ref r, Ref *arg0, int *cls, int *eqval)
|
|||||||
i = fn->tmp[r.val].def;
|
i = fn->tmp[r.val].def;
|
||||||
if (i)
|
if (i)
|
||||||
if (optab[i->op].cmpeqwl)
|
if (optab[i->op].cmpeqwl)
|
||||||
if (req(i->arg[1], con01[0])) {
|
if (req(i->arg[1], CON_Z)) {
|
||||||
*arg0 = i->arg[0];
|
*arg = i->arg[0];
|
||||||
*cls = argcls(i, 0);
|
*cls = argcls(i, 0);
|
||||||
*eqval = optab[i->op].eqval;
|
*eqval = optab[i->op].eqval;
|
||||||
return 1;
|
return 1;
|
||||||
@ -534,23 +289,25 @@ cmpeq0def(Fn *fn, Ref r, Ref *arg0, int *cls, int *eqval)
|
|||||||
static int
|
static int
|
||||||
branchdom(Fn *fn, Blk *bif, Blk *bbr1, Blk *bbr2, Blk *b)
|
branchdom(Fn *fn, Blk *bif, Blk *bbr1, Blk *bbr2, Blk *b)
|
||||||
{
|
{
|
||||||
if (bif->jmp.type == Jjnz)
|
assert(bif->jmp.type == Jjnz);
|
||||||
if (b != bif)
|
|
||||||
if (dom(bbr1, b))
|
if (b != bif
|
||||||
if (!reachesnotvia(fn, bbr2, b, bif))
|
&& dom(bbr1, b)
|
||||||
|
&& !reachesnotvia(fn, bbr2, b, bif))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dom0non0(Fn *fn, Blk *bdom, Blk *b, int *is0)
|
domzero(Fn *fn, Blk *d, Blk *b, int *z)
|
||||||
{
|
{
|
||||||
if (branchdom(fn, bdom, bdom->s1, bdom->s2, b)) {
|
if (branchdom(fn, d, d->s1, d->s2, b)) {
|
||||||
*is0 = 0;
|
*z = 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
if (branchdom(fn, bdom, bdom->s2, bdom->s1, b)) {
|
if (branchdom(fn, d, d->s2, d->s1, b)) {
|
||||||
*is0 = 1;
|
*z = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@ -558,24 +315,25 @@ dom0non0(Fn *fn, Blk *bdom, Blk *b, int *is0)
|
|||||||
|
|
||||||
/* infer 0/non-0 value from dominating jnz */
|
/* infer 0/non-0 value from dominating jnz */
|
||||||
int
|
int
|
||||||
is0non0(Fn *fn, Blk *b, Ref r, int cls, int *is0)
|
zeroval(Fn *fn, Blk *b, Ref r, int cls, int *z)
|
||||||
{
|
{
|
||||||
Blk *bdom;
|
Blk *d;
|
||||||
Ref arg0;
|
Ref arg;
|
||||||
int cls1, eqval;
|
int cls1, eqval;
|
||||||
|
|
||||||
for (bdom = b->idom; bdom; bdom = bdom->idom) {
|
for (d=b->idom; d; d=d->idom) {
|
||||||
if (bdom->jmp.type != Jjnz)
|
if (d->jmp.type != Jjnz)
|
||||||
continue;
|
continue;
|
||||||
if (cls == Kw)
|
if (req(r, d->jmp.arg)
|
||||||
if (req(r, bdom->jmp.arg))
|
&& cls == Kw
|
||||||
if (dom0non0(fn, bdom, b, is0))
|
&& domzero(fn, d, b, z)) {
|
||||||
return 1;
|
return 1;
|
||||||
if (cmpeq0def(fn, bdom->jmp.arg, &arg0, &cls1, &eqval))
|
}
|
||||||
if (cls == cls1)
|
if (cmpeqz(fn, d->jmp.arg, &arg, &cls1, &eqval)
|
||||||
if (req(r, arg0))
|
&& req(r, arg)
|
||||||
if (dom0non0(fn, bdom, b, is0)) {
|
&& cls == cls1
|
||||||
*is0 = *is0 ^ eqval;
|
&& domzero(fn, d, b, z)) {
|
||||||
|
*z ^= eqval;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -583,19 +341,27 @@ is0non0(Fn *fn, Blk *b, Ref r, int cls, int *is0)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
usecls(Use *u, Ref r)
|
usecls(Use *u, Ref r, int cls)
|
||||||
{
|
{
|
||||||
|
int k;
|
||||||
|
|
||||||
switch (u->type) {
|
switch (u->type) {
|
||||||
case UXXX: break;
|
|
||||||
case UIns:
|
case UIns:
|
||||||
/* safe to take arg[0] cls if both args == r, even for store */
|
k = Kx; /* widest use */
|
||||||
if (req(u->u.ins->arg[0], r))
|
if (req(u->u.ins->arg[0], r))
|
||||||
return argcls(u->u.ins, 0);
|
k = argcls(u->u.ins, 0);
|
||||||
if (req(u->u.ins->arg[1], r))
|
if (req(u->u.ins->arg[1], r))
|
||||||
return argcls(u->u.ins, 1);
|
if (k == Kx || !KWIDE(k))
|
||||||
|
k = argcls(u->u.ins, 1);
|
||||||
|
return k == Kx ? cls : k;
|
||||||
|
case UPhi:
|
||||||
|
if (req(u->u.phi->to, R))
|
||||||
|
return cls; /* eliminated */
|
||||||
|
return u->u.phi->cls;
|
||||||
|
case UJmp:
|
||||||
|
return Kw;
|
||||||
|
default:
|
||||||
break;
|
break;
|
||||||
case UPhi: return u->u.phi->cls;
|
|
||||||
case UJmp: return Kw;
|
|
||||||
}
|
}
|
||||||
die("unreachable");
|
die("unreachable");
|
||||||
}
|
}
|
||||||
@ -610,136 +376,126 @@ propjnz0(Fn *fn, Blk *bif, Blk *s0, Blk *snon0, Ref r, int cls)
|
|||||||
if (s0->npred != 1 || rtype(r) != RTmp)
|
if (s0->npred != 1 || rtype(r) != RTmp)
|
||||||
return;
|
return;
|
||||||
t = &fn->tmp[r.val];
|
t = &fn->tmp[r.val];
|
||||||
for (u = t->use; u < &t->use[t->nuse]; u++) {
|
for (u=t->use; u<&t->use[t->nuse]; u++) {
|
||||||
b = fn->rpo[u->bid];
|
b = fn->rpo[u->bid];
|
||||||
if (usecls(u, r) == cls)
|
/* we may compare an l temp with a w
|
||||||
|
* comparison; so check that the use
|
||||||
|
* does not involve high bits */
|
||||||
|
if (usecls(u, r, cls) == cls)
|
||||||
if (branchdom(fn, bif, s0, snon0, b))
|
if (branchdom(fn, bif, s0, snon0, b))
|
||||||
replaceuse(fn, u, r, con01[0]);
|
replaceuse(fn, u, r, CON_Z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dedupjmp(Fn *fn, Blk *b) {
|
dedupjmp(Fn *fn, Blk *b)
|
||||||
Blk *s1s2[2];
|
{
|
||||||
|
Blk **ps;
|
||||||
int64_t v;
|
int64_t v;
|
||||||
Ref arg0;
|
Ref arg;
|
||||||
int cls, eqval, is0;
|
int cls, eqval, z;
|
||||||
|
|
||||||
if (b->jmp.type != Jjnz)
|
if (b->jmp.type != Jjnz)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* propagate jmp arg as 0 thru s2 */
|
/* propagate jmp arg as 0 through s2 */
|
||||||
propjnz0(fn, b, b->s2, b->s1, b->jmp.arg, Kw);
|
propjnz0(fn, b, b->s2, b->s1, b->jmp.arg, Kw);
|
||||||
/* propagate cmp eq/ne 0 def of jmp arg as 0 */
|
/* propagate cmp eq/ne 0 def of jmp arg as 0 */
|
||||||
s1s2[0] = b->s1; s1s2[1] = b->s2;
|
if (cmpeqz(fn, b->jmp.arg, &arg, &cls, &eqval)) {
|
||||||
if (cmpeq0def(fn, b->jmp.arg, &arg0, &cls, &eqval))
|
ps = (Blk*[]){b->s1, b->s2};
|
||||||
propjnz0(fn, b, s1s2[eqval^1], s1s2[eqval], arg0, cls);
|
propjnz0(fn, b, ps[eqval^1], ps[eqval], arg, cls);
|
||||||
|
}
|
||||||
|
|
||||||
/* collapse trivial/constant jnz to jmp */
|
/* collapse trivial/constant jnz to jmp */
|
||||||
v = 1;
|
v = 1;
|
||||||
is0 = 0;
|
z = 0;
|
||||||
if (b->s1 == b->s2
|
if (b->s1 == b->s2
|
||||||
|| isconbits(fn, b->jmp.arg, &v)
|
|| isconbits(fn, b->jmp.arg, &v)
|
||||||
|| is0non0(fn, b, b->jmp.arg, Kw, &is0)) {
|
|| zeroval(fn, b, b->jmp.arg, Kw, &z)) {
|
||||||
if (v == 0 || is0)
|
if (v == 0 || z)
|
||||||
b->s1 = b->s2;
|
b->s1 = b->s2;
|
||||||
/* we later move active ins out of dead blks */
|
/* we later move active ins out of dead blks */
|
||||||
b->s2 = 0;
|
b->s2 = 0;
|
||||||
b->jmp.type = Jjmp;
|
b->jmp.type = Jjmp;
|
||||||
rmuse(fn, b, UJmp, 0, 0, b->jmp.arg, 1/*strict*/);
|
|
||||||
b->jmp.arg = R;
|
b->jmp.arg = R;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* rebuild rpo pred use */
|
|
||||||
/* careful not to lose active ins in dead blks */
|
|
||||||
static void
|
static void
|
||||||
rebuildcfg(Fn *fn) {
|
rebuildcfg(Fn *fn)
|
||||||
uint n, prevnblk;
|
{
|
||||||
Blk **prevrpo;
|
uint n, nblk;
|
||||||
Blk *b;
|
Blk *b, *s, **rpo;
|
||||||
Ins *i;
|
Ins *i;
|
||||||
|
|
||||||
prevnblk = fn->nblk;
|
nblk = fn->nblk;
|
||||||
prevrpo = emalloc(prevnblk * sizeof prevrpo[0]);
|
rpo = emalloc(nblk * sizeof rpo[0]);
|
||||||
memcpy(prevrpo, fn->rpo, prevnblk * sizeof prevrpo[0]);
|
memcpy(rpo, fn->rpo, nblk * sizeof rpo[0]);
|
||||||
|
|
||||||
fillcfg(fn); // TODO - OK?
|
fillcfg(fn);
|
||||||
|
|
||||||
for (n=0; n<prevnblk; n++) {
|
/* move instructions that were in
|
||||||
b = prevrpo[n];
|
* killed blocks and may be active
|
||||||
if (b->id == -1u) {
|
* in the computation in the start
|
||||||
assert(b != fn->start);
|
* block */
|
||||||
|
s = fn->start;
|
||||||
|
for (n=0; n<nblk; n++) {
|
||||||
|
b = rpo[n];
|
||||||
|
if (b->id != -1u)
|
||||||
|
continue;
|
||||||
/* blk unreachable after GVN */
|
/* blk unreachable after GVN */
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
assert(b != s);
|
||||||
if (i->op != Onop)
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
||||||
if (!optab[i->op].ispinned)
|
if (!optab[i->op].pinned)
|
||||||
if (gvndup(i, 0) == i)
|
if (gvndup(i, 0) == i)
|
||||||
/* (possibly) active ins - add to start blk */
|
addins(&s->ins, &s->nins, i);
|
||||||
addins(&fn->start->ins, &fn->start->nins, i);
|
|
||||||
}
|
}
|
||||||
}
|
free(rpo);
|
||||||
free(prevrpo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* https://courses.cs.washington.edu/courses/cse501/06wi/reading/click-pldi95.pdf */
|
/* requires rpo pred ssa use
|
||||||
/* require rpo pred ssa use */
|
* recreates rpo preds
|
||||||
/* recreates rpo */
|
* breaks pred use dom ssa (GCM fixes ssa)
|
||||||
/* breaks pred use dom ssa (GCM fixes ssa) */
|
*/
|
||||||
void
|
void
|
||||||
gvn(Fn *fn)
|
gvn(Fn *fn)
|
||||||
{
|
{
|
||||||
uint n, nins;
|
|
||||||
Blk *b;
|
Blk *b;
|
||||||
Phi *p;
|
Phi *p;
|
||||||
|
Ins *i;
|
||||||
/* facilitate jnz simplification */
|
uint n, nins;
|
||||||
blkmerge(fn);
|
|
||||||
fillcfg(fn);
|
|
||||||
filluse(fn);
|
|
||||||
filldom(fn);
|
|
||||||
|
|
||||||
con01[0] = getcon(0, fn);
|
con01[0] = getcon(0, fn);
|
||||||
con01[1] = getcon(1, fn);
|
con01[1] = getcon(1, fn);
|
||||||
|
|
||||||
nins = 0;
|
/* copy.c uses the visit bit */
|
||||||
for (b=fn->start; b; b=b->link)
|
for (b=fn->start; b; b=b->link)
|
||||||
for (p = b->phi; p; p = p->link)
|
for (p=b->phi; p; p=p->link)
|
||||||
p->visit = 0;
|
p->visit = 0;
|
||||||
|
|
||||||
/* facilitate ext elimination */
|
|
||||||
fillloop(fn);
|
fillloop(fn);
|
||||||
narrowpars(fn);
|
narrowpars(fn);
|
||||||
filluse(fn);
|
filluse(fn);
|
||||||
ssacheck(fn);
|
ssacheck(fn);
|
||||||
|
|
||||||
for (b=fn->start; b; b=b->link)
|
nins = 0;
|
||||||
|
for (b=fn->start; b; b=b->link) {
|
||||||
|
b->visit = 0;
|
||||||
nins += b->nins;
|
nins += b->nins;
|
||||||
|
}
|
||||||
|
|
||||||
gvntbln = nins + nins/2;
|
gvntbln = nins + nins/2;
|
||||||
gvntbl = emalloc(gvntbln * sizeof gvntbl[0]);
|
gvntbl = emalloc(gvntbln * sizeof gvntbl[0]);
|
||||||
lookupn = 0;
|
|
||||||
proben = 0;
|
|
||||||
maxproben = 0;
|
|
||||||
|
|
||||||
/* GVN */
|
|
||||||
clrbvisit(fn);
|
|
||||||
for (n=0; n<fn->nblk; n++) {
|
for (n=0; n<fn->nblk; n++) {
|
||||||
b = fn->rpo[n];
|
b = fn->rpo[n];
|
||||||
dedupphis(fn, b);
|
dedupphi(fn, b);
|
||||||
dedupins(fn, b);
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
||||||
|
dedupins(fn, b, i);
|
||||||
dedupjmp(fn, b);
|
dedupjmp(fn, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
rebuildcfg(fn);
|
rebuildcfg(fn);
|
||||||
|
|
||||||
free(gvntbl);
|
free(gvntbl);
|
||||||
gvntbl = 0;
|
gvntbl = 0;
|
||||||
gvntbln = 0;
|
|
||||||
lookupn = 0;
|
|
||||||
proben = 0;
|
|
||||||
maxproben = 0;
|
|
||||||
|
|
||||||
if (debug['G']) {
|
if (debug['G']) {
|
||||||
fprintf(stderr, "\n> After GVN:\n");
|
fprintf(stderr, "\n> After GVN:\n");
|
||||||
|
|||||||
1
load.c
1
load.c
@ -380,6 +380,7 @@ def(Slice sl, bits msk, Blk *b, Ins *i, Loc *il)
|
|||||||
goto Load;
|
goto Load;
|
||||||
p->arg[np] = r1;
|
p->arg[np] = r1;
|
||||||
p->blk[np] = bp;
|
p->blk[np] = bp;
|
||||||
|
/* XXX - multiplicity in predecessors!!! */
|
||||||
}
|
}
|
||||||
if (msk != msks)
|
if (msk != msks)
|
||||||
mask(cls, &r, msk, il);
|
mask(cls, &r, msk, il);
|
||||||
|
|||||||
3
main.c
3
main.c
@ -77,9 +77,6 @@ func(Fn *fn)
|
|||||||
gvn(fn);
|
gvn(fn);
|
||||||
fillcfg(fn);
|
fillcfg(fn);
|
||||||
filluse(fn);
|
filluse(fn);
|
||||||
ifelim(fn);
|
|
||||||
fillcfg(fn);
|
|
||||||
filluse(fn);
|
|
||||||
filldom(fn);
|
filldom(fn);
|
||||||
gcm(fn);
|
gcm(fn);
|
||||||
filluse(fn);
|
filluse(fn);
|
||||||
|
|||||||
14
ops.h
14
ops.h
@ -7,7 +7,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef F
|
#ifndef F
|
||||||
#define F(CanFold, HasId, IdVal, Commutes, Associates, Idemp, IsCmpEq, IsCmpLgte, CmpEqVal, IsPinned)
|
#define F(a,b,c,d,e,f,g,h,i,j)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define T(a,b,c,d,e,f,g,h) { \
|
#define T(a,b,c,d,e,f,g,h) { \
|
||||||
@ -19,7 +19,17 @@
|
|||||||
/* PUBLIC OPERATIONS */
|
/* PUBLIC OPERATIONS */
|
||||||
/*********************/
|
/*********************/
|
||||||
|
|
||||||
/* Arithmetic and Bits */
|
/* can fold */
|
||||||
|
/* | has identity */
|
||||||
|
/* | | identity value for arg[1] */
|
||||||
|
/* | | | commutative */
|
||||||
|
/* | | | | associative */
|
||||||
|
/* | | | | | idempotent */
|
||||||
|
/* | | | | | | c{eq,ne}[wl] */
|
||||||
|
/* | | | | | | | c[us][gl][et][wl] */
|
||||||
|
/* | | | | | | | | value if = args */
|
||||||
|
/* | | | | | | | | | pinned */
|
||||||
|
/* Arithmetic and Bits v v v v v v v v v v */
|
||||||
O(add, T(w,l,s,d, w,l,s,d), F(1,1,0,1,1,0,0,0,0,0)) X(2,1,0) V(1)
|
O(add, T(w,l,s,d, w,l,s,d), F(1,1,0,1,1,0,0,0,0,0)) X(2,1,0) V(1)
|
||||||
O(sub, T(w,l,s,d, w,l,s,d), F(1,1,0,0,0,0,0,0,0,0)) X(2,1,0) V(0)
|
O(sub, T(w,l,s,d, w,l,s,d), F(1,1,0,0,0,0,0,0,0,0)) X(2,1,0) V(0)
|
||||||
O(neg, T(w,l,s,d, x,x,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(1,1,0) V(0)
|
O(neg, T(w,l,s,d, x,x,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(1,1,0) V(0)
|
||||||
|
|||||||
4
parse.c
4
parse.c
@ -22,7 +22,7 @@ Op optab[NOp] = {
|
|||||||
.commutes = co, .assoc = as, \
|
.commutes = co, .assoc = as, \
|
||||||
.idemp = im, \
|
.idemp = im, \
|
||||||
.cmpeqwl = ic, .cmplgtewl = lg, .eqval = cv, \
|
.cmpeqwl = ic, .cmplgtewl = lg, .eqval = cv, \
|
||||||
.ispinned = pn
|
.pinned = pn
|
||||||
#define O(op, k, flags) [O##op]={.name = #op, .argcls = k, flags},
|
#define O(op, k, flags) [O##op]={.name = #op, .argcls = k, flags},
|
||||||
#include "ops.h"
|
#include "ops.h"
|
||||||
#undef F
|
#undef F
|
||||||
@ -940,7 +940,7 @@ parsefn(Lnk *lnk)
|
|||||||
curf->mem = vnew(0, sizeof curf->mem[0], PFn);
|
curf->mem = vnew(0, sizeof curf->mem[0], PFn);
|
||||||
curf->nmem = 0;
|
curf->nmem = 0;
|
||||||
curf->nblk = nblk;
|
curf->nblk = nblk;
|
||||||
curf->rpo = 0;
|
curf->rpo = vnew(nblk, sizeof curf->rpo[0], PFn);
|
||||||
for (b=curf->start; b; b=b->link)
|
for (b=curf->start; b; b=b->link)
|
||||||
b->dlink = 0; /* was trashed by findblk() */
|
b->dlink = 0; /* was trashed by findblk() */
|
||||||
for (i=0; i<BMask+1; ++i)
|
for (i=0; i<BMask+1; ++i)
|
||||||
|
|||||||
3
ssa.c
3
ssa.c
@ -44,7 +44,6 @@ filluse(Fn *fn)
|
|||||||
uint a;
|
uint a;
|
||||||
Tmp *tmp;
|
Tmp *tmp;
|
||||||
|
|
||||||
/* todo, is this the correct file? */
|
|
||||||
tmp = fn->tmp;
|
tmp = fn->tmp;
|
||||||
for (t=Tmp0; t<fn->ntmp; t++) {
|
for (t=Tmp0; t<fn->ntmp; t++) {
|
||||||
tmp[t].def = 0;
|
tmp[t].def = 0;
|
||||||
@ -145,7 +144,7 @@ phiins(Fn *fn)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bszero(u);
|
bszero(u);
|
||||||
k = -1;
|
k = Kx;
|
||||||
bp = be;
|
bp = be;
|
||||||
for (b=fn->start; b; b=b->link) {
|
for (b=fn->start; b; b=b->link) {
|
||||||
b->visit = 0;
|
b->visit = 0;
|
||||||
|
|||||||
15
test/copy.ssa
Normal file
15
test/copy.ssa
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export function w $f() {
|
||||||
|
@start
|
||||||
|
%x0 =w loadsb $a
|
||||||
|
# the extension must not be eliminated
|
||||||
|
# even though the load already extended
|
||||||
|
%x1 =l extsb %x0
|
||||||
|
%c =w ceql %x1, -1
|
||||||
|
ret %c
|
||||||
|
}
|
||||||
|
|
||||||
|
# >>> driver
|
||||||
|
# char a = -1;
|
||||||
|
# extern int f();
|
||||||
|
# int main() { return !(f() == 1); }
|
||||||
|
# <<<
|
||||||
92
util.c
92
util.c
@ -168,7 +168,7 @@ addbins(Blk *b, Ins **pvins, uint *pnins)
|
|||||||
{
|
{
|
||||||
Ins *i;
|
Ins *i;
|
||||||
|
|
||||||
for (i = b->ins; i < &b->ins[b->nins]; i++)
|
for (i=b->ins; i<&b->ins[b->nins]; i++)
|
||||||
addins(pvins, pnins, i);
|
addins(pvins, pnins, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,24 +247,51 @@ iscmp(int op, int *pk, int *pc)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int INVCMPWL[] = {
|
void
|
||||||
/*Oceqw*/Ocnew, /*Ocnew*/Oceqw,
|
igroup(Blk *b, Ins *i, Ins **i0, Ins **i1)
|
||||||
/*Ocsgew*/Ocsltw, /*Ocsgtw*/Ocslew, /*Ocslew*/Ocsgtw, /*Ocsltw*/Ocsgew,
|
|
||||||
/*Ocugew*/Ocultw, /*Ocugtw*/Oculew, /*Oculew*/Ocugtw, /*Ocultw*/Ocugew,
|
|
||||||
/*Oceql*/Ocnel, /*Ocnel*/Oceql,
|
|
||||||
/*Ocsgel*/Ocsltl, /*Ocsgtl*/Ocslel, /*Ocslel*/Ocsgtl, /*Ocsltl*/Ocsgel,
|
|
||||||
/*Ocugel*/Ocultl, /*Ocugtl*/Oculel, /*Oculel*/Ocugtl, /*Ocultl*/Ocugel,
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
invcmpwl(int cmp)
|
|
||||||
{
|
{
|
||||||
assert(Oceqw <= cmp && cmp <= Ocultl);
|
Ins *ib, *ie;
|
||||||
return INVCMPWL[cmp - Oceqw];
|
|
||||||
|
ib = b->ins;
|
||||||
|
ie = ib + b->nins;
|
||||||
|
switch (i->op) {
|
||||||
|
case Oblit0:
|
||||||
|
*i0 = i;
|
||||||
|
*i1 = i + 2;
|
||||||
|
return;
|
||||||
|
case Oblit1:
|
||||||
|
*i0 = i - 1;
|
||||||
|
*i1 = i + 1;
|
||||||
|
return;
|
||||||
|
case_Opar:
|
||||||
|
for (; i>ib && ispar((i-1)->op); i--)
|
||||||
|
;
|
||||||
|
*i0 = i;
|
||||||
|
for (; i<ie && ispar(i->op); i++)
|
||||||
|
;
|
||||||
|
*i1 = i;
|
||||||
|
return;
|
||||||
|
case Ocall:
|
||||||
|
case_Oarg:
|
||||||
|
for (; i>ib && isarg((i-1)->op); i--)
|
||||||
|
;
|
||||||
|
*i0 = i;
|
||||||
|
for (; i<ie && i->op != Ocall; i++)
|
||||||
|
;
|
||||||
|
assert(i < ie);
|
||||||
|
*i1 = i + 1;
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
if (ispar(i->op))
|
||||||
|
goto case_Opar;
|
||||||
|
if (isarg(i->op))
|
||||||
|
goto case_Oarg;
|
||||||
|
*i0 = i;
|
||||||
|
*i1 = i + 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
argcls(Ins *i, int n)
|
argcls(Ins *i, int n)
|
||||||
{
|
{
|
||||||
@ -291,12 +318,8 @@ emiti(Ins i)
|
|||||||
void
|
void
|
||||||
idup(Blk *b, Ins *s, ulong n)
|
idup(Blk *b, Ins *s, ulong n)
|
||||||
{
|
{
|
||||||
if (b->ins)
|
|
||||||
vgrow(&b->ins, n);
|
vgrow(&b->ins, n);
|
||||||
else
|
icpy(b->ins, s, n);
|
||||||
b->ins = vnew(n, sizeof(Ins), PFn);
|
|
||||||
if (n)
|
|
||||||
memcpy(b->ins, s, n * sizeof(Ins));
|
|
||||||
b->nins = n;
|
b->nins = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,6 +367,16 @@ cmpop(int c)
|
|||||||
return cmptab[c][1];
|
return cmptab[c][1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cmpwlneg(int op)
|
||||||
|
{
|
||||||
|
if (INRANGE(op, Ocmpw, Ocmpw1))
|
||||||
|
return cmpneg(op - Ocmpw) + Ocmpw;
|
||||||
|
if (INRANGE(op, Ocmpl, Ocmpl1))
|
||||||
|
return cmpneg(op - Ocmpl) + Ocmpl;
|
||||||
|
die("not a wl comparison");
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
clsmerge(short *pk, short k)
|
clsmerge(short *pk, short k)
|
||||||
{
|
{
|
||||||
@ -379,16 +412,21 @@ phiargn(Phi *p, Blk *b)
|
|||||||
{
|
{
|
||||||
uint n;
|
uint n;
|
||||||
|
|
||||||
for (n = 0; n < p->narg; n++)
|
if (p)
|
||||||
|
for (n=0; n<p->narg; n++)
|
||||||
if (p->blk[n] == b)
|
if (p->blk[n] == b)
|
||||||
return n;
|
return n;
|
||||||
die("unreachable");
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref
|
Ref
|
||||||
phiarg(Phi *p, Blk *b)
|
phiarg(Phi *p, Blk *b)
|
||||||
{
|
{
|
||||||
return p->arg[phiargn(p, b)];
|
uint n;
|
||||||
|
|
||||||
|
n = phiargn(p, b);
|
||||||
|
assert(n != -1u && "block not found");
|
||||||
|
return p->arg[n];
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref
|
Ref
|
||||||
@ -489,12 +527,6 @@ isconbits(Fn *fn, Ref r, int64_t *v)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
istmpconbits(Fn *fn, Ins *i, int64_t *v)
|
|
||||||
{
|
|
||||||
return rtype(i->arg[0]) == RTmp && isconbits(fn, i->arg[1], v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
salloc(Ref rt, Ref rs, Fn *fn)
|
salloc(Ref rt, Ref rs, Fn *fn)
|
||||||
{
|
{
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user