#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: if (isnumcon(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 (isnumcon(l)) rswap(l, r); /* put const in rhs */ if (r.bits == ZEROREF.bits) /* x & 0 ==> 0 */ return ZEROREF; break; case Oior: if (isnumcon(l)) rswap(l, r); /* put const in rhs */ if (r.bits == ZEROREF.bits) /* x | 0 ==> x */ return l; case Oxor: if (isnumcon(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 Ocvtf32s: case Ocvtf32u: case Ocvtf32f64: case Ocvtf64s: case Ocvtf64u: case Ocvtf64f32: case Ocvts32f: case Ocvtu32f: case Ocvts64f: case Ocvtu64f: 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)); } int allocinstr(void); union ref addinstr(struct function *fn, struct instr ins) { int new = allocinstr(); 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 = allocinstr(); 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); if (iscon(arg)) { bool truthy; if (isintcon(arg)) truthy = intconval(arg) != 0; else if (isfltcon(arg)) truthy = fltconval(arg) != 0.0; else if (isaddrcon(arg,0)) truthy = 1; /* XXX ok to assume symbols have non null addresses? */ else goto Cond; putbranch(fn, truthy ? t : f); } else { Cond: 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) { assert(fn->curblk); adduse(fn->curblk, USERJUMP, r0); adduse(fn->curblk, USERJUMP, r1); putjump(fn, Jret, r0, r1, NULL, NULL); } void puttrap(struct function *fn) { assert(fn->curblk); putjump(fn, Jtrap, NOREF, NOREF, NULL, NULL); } #undef putjump /* vim:set ts=3 sw=3 expandtab: */