implement smarter compare+branch

This commit is contained in:
Quentin Carbonneaux 2015-08-07 14:27:20 -04:00
parent 7dc3e5dcf6
commit ad012e9d55
3 changed files with 132 additions and 30 deletions

View file

@ -39,12 +39,53 @@ newtmp(int type, Fn *fn)
return t;
}
static int
cneg(int cmp)
{
switch (cmp) {
default: diag("cneg: unhandled comparison");
case Ceq: return Cne;
case Csle: return Csgt;
case Cslt: return Csge;
case Csgt: return Csle;
case Csge: return Cslt;
case Cne: return Ceq;
}
}
static int
islong(Ref r, Fn *fn)
{
return rtype(r) == RTmp && fn->tmp[r.val].type == TLong;
}
static void
selcmp(Ref arg[2], Fn *fn)
{
Ref r;
int t;
t = -1;
if (rtype(arg[0]) == RCon) {
r = arg[1];
arg[1] = arg[0];
arg[0] = r;
if (rtype(r) == RCon) {
/* todo, use the constant
* size to dimension the
* constant */
t = newtmp(TWord, fn);
arg[0] = TMP(t);
}
}
if (islong(arg[0], fn) || islong(arg[1], fn))
emit(OXCmpl, R, arg[1], arg[0]);
else
emit(OXCmpw, R, arg[1], arg[0]);
if (t != -1)
emit(OCopy, TMP(t), r, R);
}
static void
sel(Ins i, Fn *fn)
{
@ -92,35 +133,78 @@ sel(Ins i, Fn *fn)
break;
default:
if (OCmp <= i.op && i.op <= OCmp1) {
t = -1;
r0 = i.arg[0];
c = i.op - OCmp;
if (rtype(i.arg[0]) == RCon) {
if (rtype(i.arg[1]) == RCon) {
/* todo, use the constant
* size to dimension the
* constant */
t = newtmp(TWord, fn);
r0 = TMP(t);
} else {
r0 = i.arg[1];
i.arg[1] = i.arg[0];
c = COP(c);
}
}
if (rtype(i.arg[0]) == RCon)
c = COP(c);
emit(OXSet+c, i.to, R, R);
if (islong(r0, fn) || islong(i.arg[1], fn))
emit(OXCmpl, R, i.arg[1], r0);
else
emit(OXCmpw, R, i.arg[1], r0);
if (t != -1)
emit(OCopy, r0, i.arg[0], R);
selcmp(i.arg, fn);
break;
}
diag("isel: non-exhaustive implementation");
}
}
static Ins *
flagi(Ins *i0, Ins *i)
{
while (i>i0)
switch ((--i)->op) {
default:
return i;
case OCopy:
case OStore:
case OLoad:;
}
return 0;
}
static Ins *
seljmp(Blk *b, Fn *fn)
{
Ref r;
int c;
Ins *fi;
fi = &b->ins[b->nins];
if (b->jmp.type != JJez)
return fi;
r = b->jmp.arg;
b->jmp.arg = R;
assert(!req(r, R));
if (rtype(r) == RCon) {
b->jmp.type = JJmp;
if (!req(r, CON_Z))
b->s1 = b->s2;
b->s2 = 0;
return fi;
}
fi = flagi(b->ins, fi);
if (fi && req(fi->to, r)) {
assert(1 == fn->tmp[r.val].nuse);
if (fn->tmp[r.val].nuse == 1
&& OCmp <= fi->op && fi->op <= OCmp1) {
c = fi->op - OCmp;
if (rtype(fi->arg[0]) == RCon)
c = COP(c);
b->jmp.type = JXJc + cneg(c);
selcmp(fi->arg, fn);
return fi;
}
/* what if it is a comparison
* that is used more than once?
* !!!
*/
b->jmp.type = JXJc + Ceq;
return fi+1;
}
if (islong(r, fn))
emit(OXCmpl, R, CON_Z, r);
else
emit(OXCmpw, R, CON_Z, r);
b->jmp.type = JXJc + Ceq;
return &b->ins[b->nins];
}
/* instruction selection
* requires use counts (as given by parsing)
*/
@ -133,7 +217,8 @@ isel(Fn *fn)
for (b=fn->start; b; b=b->link) {
curi = &insb[NIns];
for (i=&b->ins[b->nins]; i!=b->ins;) {
i = seljmp(b, fn);
while (i>b->ins) {
sel(*--i, fn);
}
nins = &insb[NIns] - curi;