diff options
Diffstat (limited to 'ir/builder.c')
| -rw-r--r-- | ir/builder.c | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/ir/builder.c b/ir/builder.c new file mode 100644 index 0000000..213a491 --- /dev/null +++ b/ir/builder.c @@ -0,0 +1,217 @@ +#include "ir.h" + +/* binary arithmetic builder with peephole optimizations */ +union ref +irbinop(struct function *fn, enum op op, enum irclass k, union ref l, union ref r) +{ + static const union ref ONE = {.t=RICON, .i=1}; + vlong iv; + union ref c; + + if (foldbinop(&c, op, k, l, r)) + return c; + + switch (op) { + case Oadd: + if (l.bits == ZEROREF.bits) return r; /* 0 + x ==> x */ + /* fallthru */ + case Osub: + if (r.bits == ZEROREF.bits) return l; /* x +/- 0 ==> x */ + break; + case Omul: case Oumul: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x * 0 ==> 0 */ + return ZEROREF; + if (r.bits == ONE.bits) /* x * 1 ==> x */ + return l; + if (isintcon(r) && ispo2(iv = intconval(r))) { + /* x * 2^y ==> x << y */ + op = Oshl; + r = mkintcon(k, ilog2(iv)); + } + break; + case Odiv: + break; + case Oudiv: + if (isintcon(r) && ispo2(iv = intconval(r))) { + /* x / 2^y ==> x >> y */ + op = Oslr; + r = mkintcon(k, ilog2(iv)); + } + break; + case Orem: + break; + case Ourem: + if (isintcon(r) && ispo2(iv = intconval(r))) { + /* x % 2^y ==> x & 2^y-1 */ + op = Oand; + r = mkintcon(k, iv - 1); + } + break; + case Oand: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x & 0 ==> 0 */ + return ZEROREF; + break; + case Oior: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x | 0 ==> x */ + return l; + case Oxor: + if (iscon(l)) rswap(l, r); /* put const in rhs */ + if (r.bits == ZEROREF.bits) /* x ^ 0 ==> x */ + return l; + case Oshl: case Osar: case Oslr: + if (r.bits == ZEROREF.bits) /* x shift 0 ==> x */ + return l; + break; + case Oequ: + if (r.bits == l.bits && kisint(k)) + return ONE; + break; + case Oneq: + if (r.bits == l.bits && kisint(k)) + return ZEROREF; + break; + case Olth: + break; + case Ogth: + break; + case Olte: + break; + case Ogte: + break; + case Oulth: + break; + case Ougth: + break; + case Oulte: + break; + case Ougte: + break; + default: + assert(!"binop?"); + } + return addinstr(fn, mkinstr(op, k, l, r)); +} + +union ref +irunop(struct function *fn, enum op op, enum irclass k, union ref a) +{ + union ref c; + struct instr *ins = NULL; + if (foldunop(&c, op, k, a)) + return c; + if (a.t == RTMP) ins = &instrtab[a.i]; + switch (op) { + case Oneg: + if (ins && ins->op == Oneg) /* -(-x) ==> x */ + return ins->l; + break; + case Onot: + if (ins && ins->op == Onot) /* ~(~x) ==> x */ + return ins->l; + break; + case Ocvtf4s: case Ocvtf4u: case Ocvtf4f8: case Ocvtf8s: + case Ocvtf8u: case Ocvtf8f4: case Ocvts4f: case Ocvtu4f: + case Ocvts8f: case Ocvtu8f: + case Oexts1: case Oextu1: case Oexts2: case Oextu2: + case Oexts4: case Oextu4: + case Ocopy: + break; + default: assert(!"unop?"); + } + return addinstr(fn, mkinstr(op, k, a)); +} + +int newinstr(void); + +union ref +addinstr(struct function *fn, struct instr ins) +{ + int new = newinstr(); + assert(fn->curblk != NULL); + instrtab[new] = ins; + adduse(fn->curblk, new, ins.l); + adduse(fn->curblk, new, ins.r); + vpush(&fn->curblk->ins, new); + return mkref(RTMP, new); +} + +void +useblk(struct function *fn, struct block *blk) +{ + extern int nerror; + if (fn->curblk && nerror == 0) assert(fn->curblk->jmp.t && "never finished block"); + if (blk) assert(!blk->jmp.t && "reusing built block"); + if (!blk->lprev) { /* initialize */ + blk->lnext = fn->entry; + blk->lprev = fn->entry->lprev; + blk->lprev->lnext = blk; + blk->id = blk->lprev->id + 1; + ++fn->nblk; + fn->entry->lprev = blk; + } + fn->curblk = blk; +} + +union ref +addphi(struct function *fn, enum irclass cls, union ref *r) +{ + int new; + struct instr ins = { Ophi, cls }; + union ref *refs = NULL; + + xbgrow(&refs, fn->curblk->npred); + memcpy(refs, r, fn->curblk->npred * sizeof *r); + vpush(&phitab, refs); + ins.l = mkref(RXXX, phitab.n-1); + + assert(fn->curblk != NULL); + assert(fn->curblk->ins.n == 0); + new = newinstr(); + instrtab[new] = ins; + for (int i = 0; i < fn->curblk->npred; ++i) { + adduse(fn->curblk, new, r[i]); + } + vpush(&fn->curblk->phi, new); + return mkref(RTMP, new); +} + +#define putjump(fn, j, arg0, arg1, T, F) \ + fn->curblk->jmp.t = j; \ + fn->curblk->jmp.arg[0] = arg0; \ + fn->curblk->jmp.arg[1] = arg1; \ + fn->curblk->s1 = T; \ + fn->curblk->s2 = F; \ + fn->curblk = NULL; + +void +putbranch(struct function *fn, struct block *blk) +{ + assert(fn->curblk && blk); + addpred(blk, fn->curblk); + putjump(fn, Jb, NOREF, NOREF, blk, NULL); +} + +void +putcondbranch(struct function *fn, union ref arg, struct block *t, struct block *f) +{ + assert(fn->curblk && t && f); + adduse(fn->curblk, USERJUMP, arg); + addpred(t, fn->curblk); + addpred(f, fn->curblk); + putjump(fn, Jb, arg, NOREF, t, f); +} + +void +putreturn(struct function *fn, union ref r0, union ref r1) +{ + adduse(fn->curblk, USERJUMP, r0); + adduse(fn->curblk, USERJUMP, r1); + putjump(fn, Jret, r0, r1, NULL, NULL); +} + +#undef putjump + +/* vim:set ts=3 sw=3 expandtab: */ |