~mcf/cproc

specialized branch compilation v1 PROPOSED

Quentin Carbonneaux: 1
 specialized branch compilation

 4 files changed, 79 insertions(+), 32 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~mcf/cproc/patches/53742/mbox | git am -3
Learn more about email & git

[PATCH] specialized branch compilation Export this patch

Hello,

This patch is a minor refactor of code generation for
branch conditions. Until now branch conditions were
treated just like any other expression. With the
present patch, and its associated modest increase in
complexity, branch conditions are compiled in a
context where the two targets (taken / not taken) are
available. This leads to slightly simpler qbe IL.

The change I was most concerned about is the different
compilation of TLNOT. Rather than turning it into a
comparison with zero early, we keep it in the IL and
defer to funcjnz later on. I tested it with pointers
and double arguments and the compilation seems to
behave alright. The correctness argument I found is
that the code path I introduce will have convert()
called as part of funcjnz(), this same function was
called in complex cases as a result of the EXPRCAST
being generated when turning TLNOT into `== 0` (TEQL)
in expr.c.

cproc bootstraps fine with the patch. Coremark sees
a 12% improvement when using qbe 1.2 as backend.


---
 cc.h   |  1 +
 expr.c |  6 +++-
 qbe.c  | 87 ++++++++++++++++++++++++++++++++++++++++++++--------------
 stmt.c | 17 ++++--------
 4 files changed, 79 insertions(+), 32 deletions(-)

diff --git a/cc.h b/cc.h
index cb53b0d..3a56ea9 100644
--- a/cc.h
+++ b/cc.h
@@ -559,6 +559,7 @@ struct func *mkfunc(struct decl *, char *, struct type *, struct scope *);
void delfunc(struct func *);
struct type *functype(struct func *);
void funclabel(struct func *, struct block *);
void funcbranch(struct func *, struct expr *, struct block *, struct block *);
struct value *funcexpr(struct func *, struct expr *);
void funcjmp(struct func *, struct block *);
void funcjnz(struct func *, struct value *, struct type *, struct block *, struct block *);
diff --git a/expr.c b/expr.c
index aef6fe9..0bd2158 100644
--- a/expr.c
+++ b/expr.c
@@ -156,6 +156,10 @@ mkunaryexpr(enum tokenkind op, struct expr *base)
			expr->op = op;
		}
		return decay(expr);
	case TLNOT:
		expr = mkexpr(EXPRUNARY, &typeint, base);
		expr->op = TLNOT;
		return expr;
	}
	/* other unary operators get compiled as equivalent binary ones */
	fatal("internal error: unknown unary operator %d", op);
@@ -1069,7 +1073,7 @@ unaryexpr(struct scope *s)
		e = castexpr(s);
		if (!(e->type->prop & PROPSCALAR))
			error(&tok.loc, "operator '!' must have scalar operand");
		e = mkbinaryexpr(&tok.loc, TEQL, e, mkconstexpr(&typeint, 0));
		e = mkunaryexpr(TLNOT, e);
		break;
	case TSIZEOF:
	case TALIGNOF:
diff --git a/qbe.c b/qbe.c
index 33d6b71..7951409 100644
--- a/qbe.c
+++ b/qbe.c
@@ -694,6 +694,69 @@ funclval(struct func *f, struct expr *e)
	return lval;
}

void
funcbranch(struct func *f, struct expr *e, struct block *bt, struct block *bf)
{
	struct value *v;
	struct block *b;

	/* Maybe we we could do something for EXPRCOND as well. */
	switch (e->kind) {
	case EXPRUNARY:
		if (e->op == TLNOT) {
			funcbranch(f, e->base, bf, bt);
			return;
		}
		break;
	case EXPRBINARY:
		if (e->op == TLOR || e->op == TLAND) {
			if (e->op == TLOR) {
				b = mkblock("logic_or");
				funcbranch(f, e->u.binary.l, bt, b);
			} else {
				b = mkblock("logic_and");
				funcbranch(f, e->u.binary.l, b, bf);
			}
			funclabel(f, b);
			funcbranch(f, e->u.binary.r, bt, bf);
			return;
		}
		break;
	case EXPRCOMMA:
		for (e = e->base; e->next; e = e->next)
			funcexpr(f, e);
		funcbranch(f, e, bt, bf);
		return;
	}
	v = funcexpr(f, e);
	funcjnz(f, v, e->type, bt, bf);
}

static struct value *
funclogical(struct func *f, struct expr *e)
{
	struct block *b[3];

	b[0] = mkblock("logic_true");
	b[1] = mkblock("logic_false");
	b[2] = mkblock("logic_join");

	funcbranch(f, e, b[0], b[1]);
	funclabel(f, b[0]);
	funcjmp(f, b[2]);
	funclabel(f, b[1]);

	b[2]->phi.blk[0] = b[0];
	b[2]->phi.val[0] = mkintconst(1);
	b[2]->phi.blk[1] = b[1];
	b[2]->phi.val[1] = mkintconst(0);
	functemp(f, &b[2]->phi.res);
	b[2]->phi.class = 'w';
	funclabel(f, b[2]);

	return &b[2]->phi.res;
}

struct value *
funcexpr(struct func *f, struct expr *e)
{
@@ -769,6 +832,8 @@ funcexpr(struct func *f, struct expr *e)
		return v;
	case EXPRUNARY:
		switch (e->op) {
		case TLNOT:
			return funclogical(f, e);
		case TBAND:
			lval = funclval(f, e->base);
			return lval.addr;
@@ -787,28 +852,10 @@ funcexpr(struct func *f, struct expr *e)
		l = funcexpr(f, e->base);
		return convert(f, e->type, e->base->type, l);
	case EXPRBINARY:
		l = funcexpr(f, e->u.binary.l);
		if (e->op == TLOR || e->op == TLAND) {
			b[0] = mkblock("logic_right");
			b[1] = mkblock("logic_join");
			t = e->u.binary.l->type;
			if (e->op == TLOR) {
				funcjnz(f, l, t, b[1], b[0]);
				b[1]->phi.val[0] = mkintconst(1);
			} else {
				funcjnz(f, l, t, b[0], b[1]);
				b[1]->phi.val[0] = mkintconst(0);
			}
			b[1]->phi.blk[0] = f->end;
			funclabel(f, b[0]);
			r = funcexpr(f, e->u.binary.r);
			b[1]->phi.val[1] = convert(f, &typebool, e->u.binary.r->type, r);
			b[1]->phi.blk[1] = f->end;
			funclabel(f, b[1]);
			functemp(f, &b[1]->phi.res);
			b[1]->phi.class = 'w';
			return &b[1]->phi.res;
			return funclogical(f, e);
		}
		l = funcexpr(f, e->u.binary.l);
		r = funcexpr(f, e->u.binary.r);
		t = e->u.binary.l->type;
		if (t->kind == TYPEPOINTER)
diff --git a/stmt.c b/stmt.c
index ff163bf..9b96f0d 100644
--- a/stmt.c
+++ b/stmt.c
@@ -104,13 +104,11 @@ stmt(struct func *f, struct scope *s)
		t = e->type;
		if (!(t->prop & PROPSCALAR))
			error(&tok.loc, "controlling expression of if statement must have scalar type");
		v = funcexpr(f, e);
		delexpr(e);
		expect(TRPAREN, "after expression");

		b[0] = mkblock("if_true");
		b[1] = mkblock("if_false");
		funcjnz(f, v, t, b[0], b[1]);
		funcbranch(f, e, b[0], b[1]);
		delexpr(e);
		expect(TRPAREN, "after expression");

		funclabel(f, b[0]);
		s = mkscope(s);
@@ -181,8 +179,7 @@ stmt(struct func *f, struct scope *s)
		b[2] = mkblock("while_join");

		funclabel(f, b[0]);
		v = funcexpr(f, e);
		funcjnz(f, v, t, b[1], b[2]);
		funcbranch(f, e, b[1], b[2]);
		funclabel(f, b[1]);
		s = mkscope(s);
		s->continuelabel = b[0];
@@ -217,8 +214,7 @@ stmt(struct func *f, struct scope *s)
			error(&tok.loc, "controlling expression of loop must have scalar type");
		expect(TRPAREN, "after expression");

		v = funcexpr(f, e);
		funcjnz(f, v, t, b[0], b[2]);
		funcbranch(f, e, b[0], b[2]);
		funclabel(f, b[2]);
		s = delscope(s);
		expect(TSEMICOLON, "after 'do' statement");
@@ -247,8 +243,7 @@ stmt(struct func *f, struct scope *s)
			t = e->type;
			if (!(t->prop & PROPSCALAR))
				error(&tok.loc, "controlling expression of loop must have scalar type");
			v = funcexpr(f, e);
			funcjnz(f, v, t, b[1], b[3]);
			funcbranch(f, e, b[1], b[3]);
			delexpr(e);
		}
		expect(TSEMICOLON, NULL);
-- 
2.44.0