diff options
author | Michael Forney <mforney@mforney.org> | 2021-09-28 12:13:33 -0700 |
---|---|---|
committer | Michael Forney <mforney@mforney.org> | 2021-09-28 12:17:29 -0700 |
commit | 7e8386697aa60c1813019eda3cd2073e6be5b021 (patch) | |
tree | 14659072efa826982a43253af8cc3eb9b4c85381 | |
parent | ab3946c6801be3520beeb79f5500ed1a944d4667 (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.h | 2 | ||||
-rw-r--r-- | expr.c | 4 | ||||
-rw-r--r-- | qbe.c | 45 | ||||
-rw-r--r-- | stmt.c | 49 | ||||
-rw-r--r-- | test/conditional-compound-literal.qbe | 19 | ||||
-rw-r--r-- | test/conditional.c | 11 | ||||
-rw-r--r-- | test/conditional.qbe | 44 | ||||
-rw-r--r-- | test/logical-and.c | 11 | ||||
-rw-r--r-- | test/logical-and.qbe | 38 | ||||
-rw-r--r-- | test/logical-or.c | 11 | ||||
-rw-r--r-- | test/logical-or.qbe | 38 |
11 files changed, 213 insertions, 59 deletions
@@ -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 *); @@ -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); @@ -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]); @@ -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 } |