summary refs log tree commit diff
path: root/lisc/isel.c
diff options
context:
space:
mode:
Diffstat (limited to 'lisc/isel.c')
-rw-r--r--lisc/isel.c129
1 files changed, 107 insertions, 22 deletions
diff --git a/lisc/isel.c b/lisc/isel.c
index 31de291..db5cb92 100644
--- a/lisc/isel.c
+++ b/lisc/isel.c
@@ -40,12 +40,53 @@ newtmp(int type, Fn *fn)
 }
 
 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)
 {
 	Ref r0, r1, ra, rd;
@@ -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;