diff options
| author | 2025-12-21 17:41:50 +0100 | |
|---|---|---|
| committer | 2025-12-21 17:42:31 +0100 | |
| commit | 97953ac6f077ef2ed2a59f7b1e2328573981d41e (patch) | |
| tree | 1b0cf2007f29f0b010fa3cdb77c478b3843b5ed0 | |
| parent | 3e74de26d16780e626241e0c42313fcb37b91cf2 (diff) | |
ir: simpl: optimize some constant multiplications
Reuse irbinop() and irunop() for the constant results cases.
| -rw-r--r-- | ir/builder.c | 16 | ||||
| -rw-r--r-- | ir/simpl.c | 63 |
2 files changed, 61 insertions, 18 deletions
diff --git a/ir/builder.c b/ir/builder.c index 36f955c..8fb626c 100644 --- a/ir/builder.c +++ b/ir/builder.c @@ -16,7 +16,8 @@ irbinop(struct function *fn, enum op op, enum irclass k, union ref l, union ref if (l.bits == ZEROREF.bits) return r; /* 0 + x ==> x */ /* fallthru */ case Osub: - if (r.bits == ZEROREF.bits) return l; /* x +/- 0 ==> x */ + if (r.bits == ZEROREF.bits) return l; /* x - 0 ==> x */ + if (kisint(k) && l.bits == r.bits) return ZEROREF; /* x - 0 ==> x */ break; case Omul: if (isnumcon(l)) rswap(l, r); /* put const in rhs */ @@ -28,7 +29,7 @@ irbinop(struct function *fn, enum op op, enum irclass k, union ref l, union ref if (isintcon(r) && ispo2(iv = intconval(r))) { /* x * 2^y ==> x << y */ op = Oshl; - r = mkintcon(k, ilog2(iv)); + r = mkref(RICON, ilog2(iv)); } break; case Odiv: @@ -38,7 +39,7 @@ irbinop(struct function *fn, enum op op, enum irclass k, union ref l, union ref if (isintcon(r) && ispo2(iv = intconval(r))) { /* x / 2^y ==> x >> y */ op = Oslr; - r = mkintcon(k, ilog2(iv)); + r = mkref(RICON, ilog2(iv)); } break; case Orem: @@ -98,7 +99,7 @@ irbinop(struct function *fn, enum op op, enum irclass k, union ref l, union ref default: assert(!"binop?"); } - return addinstr(fn, mkinstr(op, k, l, r)); + return fn ? addinstr(fn, mkinstr(op, k, l, r)) : NOREF; } /* implements f32/f64 -> u64 conversion */ @@ -175,7 +176,7 @@ irunop(struct function *fn, enum op op, enum irclass k, union ref a) case Ocvts64f: break; case Ocvtf32u: case Ocvtf64u: - if (k == KI64) { + if (k == KI64 && fn) { /* XXX some architectures like arm64 do have these instructions natively * this should probably be handled in a separate "arithmetic-lowering" pass, earlier than isel */ @@ -184,14 +185,15 @@ irunop(struct function *fn, enum op op, enum irclass k, union ref a) break; case Ocvtu64f: /* XXX see above */ - return cvtu64f(fn, k, a); + if (fn) + return cvtu64f(fn, k, a); case Oexts8: case Oextu8: case Oexts16: case Oextu16: case Oexts32: case Oextu32: case Ocopy: break; default: assert(!"unop?"); } - return addinstr(fn, mkinstr(op, k, a)); + return fn ? addinstr(fn, mkinstr(op, k, a)) : NOREF; } int allocinstr(void); @@ -1,22 +1,62 @@ #include "ir.h" static int -ins(struct function *fn, struct instr *ins, struct block *blk, int *curi) +mulk(struct instr *ins, struct block *blk, int *curi) { - int narg = opnarg[ins->op]; - if ((oisarith(ins->op) || oiscmp(ins->op)) && isnumcon(ins->l) && (narg == 1 || isnumcon(ins->r))) { - bool ok; - if (narg == 1) ok = foldunop(&ins->l, ins->op, ins->cls, ins->l); - else ok = foldbinop(&ins->l, ins->op, ins->cls, ins->l, ins->r); - if (!ok) return 0; /* could be div/0 */ - ins->op = Ocopy; - ins->cls = insrescls(*ins); - ins->r = NOREF; + vlong iv = intconval(ins->r); + enum irclass cls = ins->cls; + assert(iv > 1 && "trivial mul not handled by irbinop() ?"); + /* This can be generalized to any sequence of shifts and + * adds/subtracts, but whether that's worth it depends on the number of them + * and the microarchitecture.. clang seems to stop after two shifts. Should + * we compute approximate cost of instrs to determine? For now just handle + * the po2 (+/- 1) case */ + if (ispo2(iv)) { + /* x * 2^y ==> x << y */ + ins->op = Oshl; + ins->r = mkref(RICON, ilog2(iv)); + return 1; + } else if (ispo2(iv-1)) { + /* x * 5 ==> (x << 2) + x */ + ins->op = Oadd; + ins->r = ins->l; + ins->l = insertinstr(blk, (*curi)++, mkinstr(Oshl, cls, ins->l, mkref(RICON, ilog2(iv-1)))); + return 1; + } else if (ispo2(iv+1)) { + /* x * 7 ==> (x << 3) - x */ + ins->op = Osub; + ins->r = ins->l; + ins->l = insertinstr(blk, (*curi)++, mkinstr(Oshl, cls, ins->l, mkref(RICON, ilog2(iv+1)))); return 1; } return 0; } +static int +ins(struct instr *ins, struct block *blk, int *curi) +{ + int narg = opnarg[ins->op]; + if (oisarith(ins->op)) { + union ref r = narg == 1 ? irunop(NULL, ins->op, ins->cls, ins->l) + : irbinop(NULL, ins->op, ins->cls, ins->l, ins->r); + if (r.bits) { + ins->op = Ocopy; + ins->cls = insrescls(*ins); + ins->l = r; + ins->r = NOREF; + return 1; + } + } + enum irclass k = ins->cls; + switch (ins->op) { + case Omul: + if (kisflt(k)) break; + if (isnumcon(ins->l)) rswap(ins->l, ins->r); /* put const in rhs */ + if (isintcon(ins->r)) return mulk(ins, blk, curi); + } + return 0; +} + static void jmpfind(struct block **final, struct block **pblk) { @@ -73,12 +113,13 @@ simpl(struct function *fn) struct block **jmpfinal = allocz(fn->passarena, fn->nblk * sizeof *jmpfinal, 0); struct block *blk = fn->entry; + fn->curblk = NULL; do { for (int i = 0; i < blk->phi.n; ++i) { } for (int i = 0; i < blk->ins.n; ++i) { - inschange += ins(fn, &instrtab[blk->ins.p[i]], blk, &i); + inschange += ins(&instrtab[blk->ins.p[i]], blk, &i); } /* merge blocks: |