aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2021-09-28 12:13:33 -0700
committerMichael Forney <mforney@mforney.org>2021-09-28 12:17:29 -0700
commit7e8386697aa60c1813019eda3cd2073e6be5b021 (patch)
tree14659072efa826982a43253af8cc3eb9b4c85381
parentab3946c6801be3520beeb79f5500ed1a944d4667 (diff)
Skip unnecessary conversion to bool for logical and conditional expressions
As in ede6a5c9, if an expression is used only to control a jnz, we don't need to convert it to a 0 or 1 value. QBE ignores the upper 32-bits of the argument to jnz, so the conversion is still needed for pointer, long, and floating point types (including float since -0 has non-zero bit representation).
-rw-r--r--cc.h2
-rw-r--r--expr.c4
-rw-r--r--qbe.c45
-rw-r--r--stmt.c49
-rw-r--r--test/conditional-compound-literal.qbe19
-rw-r--r--test/conditional.c11
-rw-r--r--test/conditional.qbe44
-rw-r--r--test/logical-and.c11
-rw-r--r--test/logical-and.qbe38
-rw-r--r--test/logical-or.c11
-rw-r--r--test/logical-or.qbe38
11 files changed, 213 insertions, 59 deletions
diff --git a/cc.h b/cc.h
index 41f7d90..49a3fd0 100644
--- a/cc.h
+++ b/cc.h
@@ -527,7 +527,7 @@ struct type *functype(struct func *);
void funclabel(struct func *, struct block *);
struct value *funcexpr(struct func *, struct expr *);
void funcjmp(struct func *, struct block *);
-void funcjnz(struct func *, struct value *, struct block *, struct block *);
+void funcjnz(struct func *, struct value *, struct type *, struct block *, struct block *);
void funcret(struct func *, struct value *);
struct gotolabel *funcgoto(struct func *, char *);
void funcswitch(struct func *, struct value *, struct switchcases *, struct block *);
diff --git a/expr.c b/expr.c
index 114aa5a..e2fbe0b 100644
--- a/expr.c
+++ b/expr.c
@@ -204,8 +204,6 @@ mkbinaryexpr(struct location *loc, enum tokenkind op, struct expr *l, struct exp
error(loc, "left operand of '%s' operator must be scalar", tokstr[op]);
if (!(rp & PROPSCALAR))
error(loc, "right operand of '%s' operator must be scalar", tokstr[op]);
- l = exprconvert(l, &typebool);
- r = exprconvert(r, &typebool);
t = &typeint;
break;
case TEQL:
@@ -1023,7 +1021,7 @@ condexpr(struct scope *s)
if (!consume(TQUESTION))
return r;
e = mkexpr(EXPRCOND, NULL);
- e->base = exprconvert(r, &typebool);
+ e->base = r;
e->cond.t = expr(s);
expect(TCOLON, "in conditional expression");
e->cond.f = condexpr(s);
diff --git a/qbe.c b/qbe.c
index df6a74d..f6ac39b 100644
--- a/qbe.c
+++ b/qbe.c
@@ -405,7 +405,7 @@ utof(struct func *f, int dst, int src, struct value *v)
join->phi.blk[1] = mkblock("utof_big");
big = funcinst(f, ICSLTL, 'w', v, mkintconst(0));
- funcjnz(f, big, join->phi.blk[1], join->phi.blk[0]);
+ funcjnz(f, big, NULL, join->phi.blk[1], join->phi.blk[0]);
funclabel(f, join->phi.blk[0]);
join->phi.val[0] = funcinst(f, ISLTOF, dst, v, NULL);
@@ -442,7 +442,7 @@ ftou(struct func *f, int dst, int src, struct value *v)
maxint = mkintconst(1ull<<63);
big = funcinst(f, src == 's' ? ICGES : ICGED, 'w', v, maxflt);
- funcjnz(f, big, join->phi.blk[1], join->phi.blk[0]);
+ funcjnz(f, big, NULL, join->phi.blk[1], join->phi.blk[0]);
funclabel(f, join->phi.blk[0]);
join->phi.val[0] = funcinst(f, op, dst, v, NULL);
@@ -616,16 +616,26 @@ funcjmp(struct func *f, struct block *l)
}
void
-funcjnz(struct func *f, struct value *v, struct block *l1, struct block *l2)
+funcjnz(struct func *f, struct value *v, struct type *t, struct block *l1, struct block *l2)
{
struct block *b = f->end;
- if (!b->jump.kind) {
- b->jump.kind = JUMP_JNZ;
- b->jump.arg = v;
- b->jump.blk[0] = l1;
- b->jump.blk[1] = l2;
+ if (b->jump.kind)
+ return;
+ if (t) {
+ assert(t->prop & PROPSCALAR);
+ /*
+ Ideally we would just do this conversion unconditionally,
+ but QBE is not currently able to optimize the conversion
+ away for int.
+ */
+ if (t->prop & PROPFLOAT || t->size > 4)
+ v = convert(f, &typebool, t, v);
}
+ b->jump.kind = JUMP_JNZ;
+ b->jump.arg = v;
+ b->jump.blk[0] = l1;
+ b->jump.blk[1] = l2;
}
void
@@ -786,11 +796,14 @@ funcexpr(struct func *f, struct expr *e)
if (e->op == TLOR || e->op == TLAND) {
b[0] = mkblock("logic_right");
b[1] = mkblock("logic_join");
- if (e->op == TLOR)
- funcjnz(f, l, b[1], b[0]);
- else
- funcjnz(f, l, b[0], b[1]);
- b[1]->phi.val[0] = l;
+ t = e->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->binary.r);
@@ -882,7 +895,7 @@ funcexpr(struct func *f, struct expr *e)
b[2] = mkblock("cond_join");
v = funcexpr(f, e->base);
- funcjnz(f, v, b[0], b[1]);
+ funcjnz(f, v, e->base->type, b[0], b[1]);
funclabel(f, b[0]);
b[2]->phi.val[0] = funcexpr(f, e->cond.t);
@@ -1030,10 +1043,10 @@ casesearch(struct func *f, int class, struct value *v, struct switchcase *c, str
// XXX: linear search if c->node.height < 4
key = mkintconst(c->node.key);
res = funcinst(f, class == 'w' ? ICEQW : ICEQL, 'w', v, key);
- funcjnz(f, res, c->body, label[0]);
+ funcjnz(f, res, NULL, c->body, label[0]);
funclabel(f, label[0]);
res = funcinst(f, class == 'w' ? ICULTW : ICULTL, 'w', v, key);
- funcjnz(f, res, label[1], label[2]);
+ funcjnz(f, res, NULL, label[1], label[2]);
funclabel(f, label[1]);
casesearch(f, class, v, c->node.child[0], defaultlabel);
funclabel(f, label[2]);
diff --git a/stmt.c b/stmt.c
index 0a5c118..a997290 100644
--- a/stmt.c
+++ b/stmt.c
@@ -24,27 +24,6 @@ gotolabel(struct func *f)
return true;
}
-/* controlling expression of loops and if-statements */
-static struct expr *
-ctrlexpr(struct scope *s)
-{
- struct expr *e;
- struct type *t;
-
- e = expr(s);
- t = e->type;
- if (!(t->prop & PROPSCALAR))
- error(&tok.loc, "controlling expression must have scalar type");
- /*
- Ideally we would just do this conversion unconditionally,
- but QBE is not currently able to optimize the conversion
- away for int.
- */
- if (t->prop & PROPFLOAT || t->size > 4)
- e = exprconvert(e, &typebool);
- return e;
-}
-
/* 6.8 Statements and blocks */
void
stmt(struct func *f, struct scope *s)
@@ -112,14 +91,17 @@ stmt(struct func *f, struct scope *s)
next();
s = mkscope(s);
expect(TLPAREN, "after 'if'");
- e = ctrlexpr(s);
+ e = expr(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");
label[0] = mkblock("if_true");
label[1] = mkblock("if_false");
- funcjnz(f, v, label[0], label[1]);
+ funcjnz(f, v, t, label[0], label[1]);
funclabel(f, label[0]);
s = mkscope(s);
@@ -179,7 +161,10 @@ stmt(struct func *f, struct scope *s)
next();
s = mkscope(s);
expect(TLPAREN, "after 'while'");
- e = ctrlexpr(s);
+ e = expr(s);
+ t = e->type;
+ if (!(t->prop & PROPSCALAR))
+ error(&tok.loc, "controlling expression of loop must have scalar type");
expect(TRPAREN, "after expression");
label[0] = mkblock("while_cond");
@@ -188,7 +173,7 @@ stmt(struct func *f, struct scope *s)
funclabel(f, label[0]);
v = funcexpr(f, e);
- funcjnz(f, v, label[1], label[2]);
+ funcjnz(f, v, t, label[1], label[2]);
funclabel(f, label[1]);
s = mkscope(s);
s->continuelabel = label[0];
@@ -217,11 +202,14 @@ stmt(struct func *f, struct scope *s)
expect(TWHILE, "after 'do' statement");
expect(TLPAREN, "after 'while'");
funclabel(f, label[1]);
- e = ctrlexpr(s);
+ e = expr(s);
+ t = e->type;
+ if (!(t->prop & PROPSCALAR))
+ error(&tok.loc, "controlling expression of loop must have scalar type");
expect(TRPAREN, "after expression");
v = funcexpr(f, e);
- funcjnz(f, v, label[0], label[2]); // XXX: compare to 0
+ funcjnz(f, v, t, label[0], label[2]); // XXX: compare to 0
funclabel(f, label[2]);
s = delscope(s);
expect(TSEMICOLON, "after 'do' statement");
@@ -246,9 +234,12 @@ stmt(struct func *f, struct scope *s)
funclabel(f, label[0]);
if (tok.kind != TSEMICOLON) {
- e = ctrlexpr(s);
+ e = expr(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, label[1], label[3]);
+ funcjnz(f, v, t, label[1], label[3]);
delexpr(e);
}
expect(TSEMICOLON, NULL);
diff --git a/test/conditional-compound-literal.qbe b/test/conditional-compound-literal.qbe
index 57358e6..739eea4 100644
--- a/test/conditional-compound-literal.qbe
+++ b/test/conditional-compound-literal.qbe
@@ -3,20 +3,19 @@ function w $main() {
@start.1
%.1 =l alloc4 4
%.2 =l alloc8 8
- %.4 =l alloc4 4
+ %.3 =l alloc4 4
@body.2
storew 0, %.1
- %.3 =w cnew 0, 0
- jnz %.3, @cond_true.3, @cond_false.4
+ jnz 0, @cond_true.3, @cond_false.4
@cond_true.3
jmp @cond_join.5
@cond_false.4
- %.5 =w loadw %.1
- storew %.5, %.4
+ %.4 =w loadw %.1
+ storew %.4, %.3
@cond_join.5
- %.6 =l phi @cond_true.3 0, @cond_false.4 %.4
- storel %.6, %.2
- %.7 =l loadl %.2
- %.8 =w loadw %.7
- ret %.8
+ %.5 =l phi @cond_true.3 0, @cond_false.4 %.3
+ storel %.5, %.2
+ %.6 =l loadl %.2
+ %.7 =w loadw %.6
+ ret %.7
}
diff --git a/test/conditional.c b/test/conditional.c
new file mode 100644
index 0000000..1c03ee5
--- /dev/null
+++ b/test/conditional.c
@@ -0,0 +1,11 @@
+int i;
+float f;
+void *p;
+int main(void) {
+ if (i ? 1 : 0)
+ return 1;
+ if (f ? 1 : 0)
+ return 1;
+ if (p ? 1 : 0)
+ return 1;
+}
diff --git a/test/conditional.qbe b/test/conditional.qbe
new file mode 100644
index 0000000..f4d6f28
--- /dev/null
+++ b/test/conditional.qbe
@@ -0,0 +1,44 @@
+export
+function w $main() {
+@start.1
+@body.2
+ %.1 =w loadw $i
+ jnz %.1, @cond_true.3, @cond_false.4
+@cond_true.3
+ jmp @cond_join.5
+@cond_false.4
+@cond_join.5
+ %.2 =w phi @cond_true.3 1, @cond_false.4 0
+ jnz %.2, @if_true.6, @if_false.7
+@if_true.6
+ ret 1
+@if_false.7
+ %.3 =s loads $f
+ %.4 =w cnes %.3, s_0
+ jnz %.4, @cond_true.8, @cond_false.9
+@cond_true.8
+ jmp @cond_join.10
+@cond_false.9
+@cond_join.10
+ %.5 =w phi @cond_true.8 1, @cond_false.9 0
+ jnz %.5, @if_true.11, @if_false.12
+@if_true.11
+ ret 1
+@if_false.12
+ %.6 =l loadl $p
+ %.7 =w cnel %.6, 0
+ jnz %.7, @cond_true.13, @cond_false.14
+@cond_true.13
+ jmp @cond_join.15
+@cond_false.14
+@cond_join.15
+ %.8 =w phi @cond_true.13 1, @cond_false.14 0
+ jnz %.8, @if_true.16, @if_false.17
+@if_true.16
+ ret 1
+@if_false.17
+ ret 0
+}
+export data $i = align 4 { z 4 }
+export data $f = align 4 { z 4 }
+export data $p = align 8 { z 8 }
diff --git a/test/logical-and.c b/test/logical-and.c
new file mode 100644
index 0000000..34b4867
--- /dev/null
+++ b/test/logical-and.c
@@ -0,0 +1,11 @@
+int i;
+float f;
+void *p;
+int main(void) {
+ if (i && 1)
+ return 1;
+ if (f && 1)
+ return 1;
+ if (p && 1)
+ return 1;
+}
diff --git a/test/logical-and.qbe b/test/logical-and.qbe
new file mode 100644
index 0000000..21fd79a
--- /dev/null
+++ b/test/logical-and.qbe
@@ -0,0 +1,38 @@
+export
+function w $main() {
+@start.1
+@body.2
+ %.1 =w loadw $i
+ jnz %.1, @logic_right.3, @logic_join.4
+@logic_right.3
+@logic_join.4
+ %.2 =w phi @body.2 0, @logic_right.3 1
+ jnz %.2, @if_true.5, @if_false.6
+@if_true.5
+ ret 1
+@if_false.6
+ %.3 =s loads $f
+ %.4 =w cnes %.3, s_0
+ jnz %.4, @logic_right.7, @logic_join.8
+@logic_right.7
+@logic_join.8
+ %.5 =w phi @if_false.6 0, @logic_right.7 1
+ jnz %.5, @if_true.9, @if_false.10
+@if_true.9
+ ret 1
+@if_false.10
+ %.6 =l loadl $p
+ %.7 =w cnel %.6, 0
+ jnz %.7, @logic_right.11, @logic_join.12
+@logic_right.11
+@logic_join.12
+ %.8 =w phi @if_false.10 0, @logic_right.11 1
+ jnz %.8, @if_true.13, @if_false.14
+@if_true.13
+ ret 1
+@if_false.14
+ ret 0
+}
+export data $i = align 4 { z 4 }
+export data $f = align 4 { z 4 }
+export data $p = align 8 { z 8 }
diff --git a/test/logical-or.c b/test/logical-or.c
new file mode 100644
index 0000000..2a0485c
--- /dev/null
+++ b/test/logical-or.c
@@ -0,0 +1,11 @@
+int i;
+float f;
+void *p;
+int main(void) {
+ if (i || 0)
+ return 1;
+ if (f || 0)
+ return 1;
+ if (p || 0)
+ return 1;
+}
diff --git a/test/logical-or.qbe b/test/logical-or.qbe
new file mode 100644
index 0000000..e1025c9
--- /dev/null
+++ b/test/logical-or.qbe
@@ -0,0 +1,38 @@
+export
+function w $main() {
+@start.1
+@body.2
+ %.1 =w loadw $i
+ jnz %.1, @logic_join.4, @logic_right.3
+@logic_right.3
+@logic_join.4
+ %.2 =w phi @body.2 1, @logic_right.3 0
+ jnz %.2, @if_true.5, @if_false.6
+@if_true.5
+ ret 1
+@if_false.6
+ %.3 =s loads $f
+ %.4 =w cnes %.3, s_0
+ jnz %.4, @logic_join.8, @logic_right.7
+@logic_right.7
+@logic_join.8
+ %.5 =w phi @if_false.6 1, @logic_right.7 0
+ jnz %.5, @if_true.9, @if_false.10
+@if_true.9
+ ret 1
+@if_false.10
+ %.6 =l loadl $p
+ %.7 =w cnel %.6, 0
+ jnz %.7, @logic_join.12, @logic_right.11
+@logic_right.11
+@logic_join.12
+ %.8 =w phi @if_false.10 1, @logic_right.11 0
+ jnz %.8, @if_true.13, @if_false.14
+@if_true.13
+ ret 1
+@if_false.14
+ ret 0
+}
+export data $i = align 4 { z 4 }
+export data $f = align 4 { z 4 }
+export data $p = align 8 { z 8 }