aboutsummaryrefslogtreecommitdiffhomepage
path: root/ir/builder.c
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2025-11-05 13:56:54 +0100
committerlemon <lsof@mailbox.org>2025-11-05 19:02:44 +0100
commit13471741b538baa45cd53a521cf7d52087f3200f (patch)
tree15624e0f3a2e8a11f2c114f8309af6e3207193c0 /ir/builder.c
parent088c3c1ce51de82ef317592bae766ad20f82208d (diff)
amd64: fix aggregate abi stuff;; ir: fold, peephole optimizing constructors
Diffstat (limited to 'ir/builder.c')
-rw-r--r--ir/builder.c217
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: */