aboutsummaryrefslogtreecommitdiffhomepage
path: root/ir/inliner.c
diff options
context:
space:
mode:
Diffstat (limited to 'ir/inliner.c')
-rw-r--r--ir/inliner.c309
1 files changed, 0 insertions, 309 deletions
diff --git a/ir/inliner.c b/ir/inliner.c
deleted file mode 100644
index b99d3dc..0000000
--- a/ir/inliner.c
+++ /dev/null
@@ -1,309 +0,0 @@
-#include "ir.h"
-
-struct savedfunc {
- uint ninstr;
- struct instr *instrtab;
- struct xcon *contab;
- struct call *calltab;
- union ref **phitab;
- struct block *entry;
- union type fnty, retty;
- struct abiarg *abiarg, abiret[2];
- ushort nabiarg, nabiret;
- ushort nretpoints;
-};
-
-enum { MAX_INLINED_FN_NINS = 50,
- MAX_INLINED_FN_NBLK = 16, };
-
-static pmap_of(struct savedfunc *) savedfns;
-
-bool
-maybeinlinee(struct function *fn)
-{
- static struct arena *savearena;
- extern int ninstr;
-
- // TODO better heuristics
- if (ccopt.o < OPT1) return 0;
- if (!fn->inlin && ccopt.o < OPT2) return 0;
- if (ninstr > MAX_INLINED_FN_NINS) return 0;
- if (fn->nblk > MAX_INLINED_FN_NBLK) return 0;
- for (int i = 0; i < fn->nabiarg; ++i) {
- /* TODO inlining functions with stack args */
- if (fn->abiarg[i].isstk) return 0;
- }
- if (fn->nabiret > 1) return 0; /* TODO 2reg scalar return */
-
- if (!savearena) {
- enum { N = 1<<12 };
- static union { char m[sizeof(struct arena) + N]; struct arena *_align; } amem;
- savearena = (void *)amem.m;
- savearena->cap = N;
- }
-
- if (ccopt.dbg.y) {
- bfmt(ccopt.dbgout, "> stashing '%s' for inlining\n", fn->name);
- }
- struct savedfunc *sv = allocz(&savearena, sizeof *sv, 0);
- sv->fnty = fn->fnty, sv->retty = fn->retty;
- if (fn->abiarg)
- sv->abiarg = alloccopy(&savearena, fn->abiarg, sizeof *sv->abiarg * fn->nabiarg, 0);
- sv->nabiarg = fn->nabiarg;
- if ((sv->nabiret = fn->nabiret) > 0)
- memcpy(sv->abiret, fn->abiret, sizeof sv->abiret);
- struct block *bmap[MAX_INLINED_FN_NBLK];
- struct block *b = fn->entry;
- int id = 0;
- do {
- b->id = id++;
- struct block *q = alloccopy(&savearena, b, sizeof *b, 0);
- if (q->phi.n)
- q->phi.p = alloccopy(&savearena, q->phi.p, sizeof *q->phi.p * q->phi.n, 0);
- if (q->ins.n)
- q->ins.p = alloccopy(&savearena, q->ins.p, sizeof *q->ins.p * q->ins.n, 0);
- if (q->npred > 1)
- q->_pred = alloccopy(&savearena, q->_pred, sizeof *q->_pred * q->npred, 0);
- q->lprev = NULL;
- q->idom = NULL;
- bmap[b->id] = q;
- sv->nretpoints += b->jmp.t == Jret;
- } while ((b = b->lnext) != fn->entry);
- b = sv->entry = bmap[0];
- do {
- if (b->s1) b->s1 = bmap[b->s1->id];
- if (b->s2) b->s2 = bmap[b->s2->id];
- if (b->npred == 1)
- b->_pred0 = bmap[b->_pred0->id];
- else for (int i = 0; i < b->npred; ++i)
- b->_pred[i] = bmap[b->_pred[i]->id];
- b->lnext = b->lnext == fn->entry ? NULL : bmap[b->lnext->id];
- } while ((b = b->lnext));
-
- sv->instrtab = alloccopy(&savearena, instrtab, sizeof *instrtab * (sv->ninstr = ninstr), 0);
- sv->contab = alloccopy(&savearena, contab.p, sizeof *contab.p * contab.n, 0);
- if (calltab.n) {
- sv->calltab = alloccopy(&savearena, calltab.p, sizeof *calltab.p * calltab.n, 0);
- for (int i = 0; i < calltab.n; ++i) {
- if (sv->calltab[i].abiarg)
- sv->calltab[i].abiarg = alloccopy(&savearena, sv->calltab[i].abiarg,
- sv->calltab[i].narg * sizeof *sv->calltab[i].abiarg, 0);
- }
- }
- if (phitab.n) {
- sv->phitab = alloc(&savearena, sizeof *phitab.p * phitab.n, 0);
- for (int i = 0; i < phitab.n; ++i) {
- sv->phitab[i] = alloccopy(&savearena, phitab.p[i], sizeof *phitab.p[i] * xbcap(phitab.p[i]), 0);
- }
- }
- pmap_set(&savedfns, fn->name, sv);
- return 1;
-}
-
-static union ref
-mapref(short *instrmap, struct savedfunc *sv, union ref r)
-{
- assert(r.bits);
- if (r.t == RTMP) return r.i = instrmap[r.i], r;
- if (r.t == RXCON) return newxcon(&sv->contab[r.i]);
- assert(r.t != RADDR);
- assert(r.t != RSTACK);
- return r;
-}
-
-static struct block *
-inlcall(struct function *fn, struct block *blk, int curi, struct savedfunc *sv)
-{
- int res = blk->ins.p[curi], res2;
- struct instr *ins = &instrtab[res];
- struct call *call = &calltab.p[ins->r.i];
- union ref retvals[64];
- union ref args[64];
- assert(sv->nabiret < 2 && sv->nretpoints < countof(retvals));
- for (int n = call->narg, i = curi-1; n > 0; --i) {
- assert(i >= 0);
- struct instr *ins = &instrtab[blk->ins.p[i]];
- if (ins->op == Oarg) {
- args[--n] = ins->r;
- *ins = mkinstr(Onop,0,);
- }
- }
- if (call->abiret[1].ty.bits) {
- assert(curi+1 < blk->ins.n);
- res2 = blk->ins.p[curi+1];
- assert(instrtab[res2].op == Ocall2r);
- }
- struct block *exit = blksplitafter(fn, blk, curi-1);
- if (!ins->cls) {
- *ins = mkinstr(Onop,0,);
- } else {
- if (sv->nretpoints == 1) {
- *ins = mkinstr(Ocopy, ins->cls, );
- } else {
- /* turn into a phi */
- *ins = mkinstr(Ophi, ins->cls, );
- exit->ins.p[0] = newinstr(blk, mkinstr(Onop,0,));
- vpush(&exit->phi, res);
- }
- }
-
- struct block *bmap[MAX_INLINED_FN_NBLK];
- short instrmap[MAX_INLINED_FN_NINS];
- for (struct block *b = sv->entry; b; b = b->lnext) {
- bmap[b->id] = newblk(fn);
- }
- for (int i = 0; i < sv->ninstr; ++i) {
- /* TODO don't wastefully allocate for tombstone instructions
- * - mark instructions some way when they are freed (put in instrfreelist)
- * */
- int allocinstr(void);
- instrmap[i] = allocinstr();
- }
-
- exit->npred = 0;
- exit->_pred0 = NULL;
- blk->s1 = bmap[0];
- int iret = 0;
- for (struct block *b = sv->entry, *prev = blk, *new; b; prev = new, b = b->lnext) {
- new = bmap[b->id];
- new->id = fn->nblk++;
- new->lprev = prev;
- prev->lnext = new;
- new->lnext = exit;
- exit->lprev = new;
- if (b->npred == 1) new->_pred0 = bmap[b->_pred0->id];
- else if (b->npred > 0) {
- xbgrow(&new->_pred, b->npred);
- for (int i = 0; i < b->npred; ++i)
- new->_pred[i] = bmap[b->_pred[i]->id];
- }
- new->npred = b->npred;
- vresize(&new->phi, b->phi.n);
- for (int i = 0; i < b->phi.n; ++i) {
- int t = b->phi.p[i];
- union ref *refs = NULL,
- *src = sv->phitab[sv->instrtab[t].l.i];
- xbgrow(&refs, b->npred);
- for (int i = 0; i < b->npred; ++i)
- refs[i] = mapref(instrmap, sv, src[i]);
- vpush(&phitab, refs);
- instrtab[instrmap[t]] = mkinstr(Ophi, sv->instrtab[t].cls, .l.i = phitab.n-1);
- new->phi.p[i] = instrmap[t];
- }
- vresize(&new->ins, b->ins.n);
- for (int i = 0; i < b->ins.n; ++i) {
- int t = b->ins.p[i];
- struct instr *ins = &instrtab[instrmap[t]];
- *ins = sv->instrtab[t];
- if (ins->op == Oparam) {
- ins->op = Ocopy;
- assert(ins->l.t == RICON && (uint) ins->l.i < call->narg);
- ins->l = args[ins->l.i];
- } else if (ins->op == Ocall || ins->op == Ointrin) {
- ins->l = mapref(instrmap, sv, ins->l);
- for (struct instr *ins2;
- ins->l.t == RTMP && (ins2 = &instrtab[ins->l.i])->op == Ocopy
- && (isaddrcon(ins2->l,0) || (ins2->l.t == RTMP && instrtab[ins->l.i].cls == KPTR));) {
- /* for an indirect function call, eagerly copy-propagate the callee. this allows
- * subsequent inlining of function pointers statically known after the inlining */
- ins->l = ins2->l;
- }
- vpush(&calltab, sv->calltab[ins->r.i]);
- ins->r.i = calltab.n-1;
- } else {
- if (ins->l.t) ins->l = mapref(instrmap, sv, ins->l);
- if (ins->r.t) ins->r = mapref(instrmap, sv, ins->r);
- }
- new->ins.p[i] = instrmap[t];
- }
- if (b->jmp.t == Jret) {
- new->jmp.t = Jb;
- new->s1 = exit;
- retvals[iret++] = b->jmp.arg[0].bits ? mapref(instrmap, sv, b->jmp.arg[0]) : UNDREF;
- addpred(exit, new);
- } else {
- new->jmp.t = b->jmp.t;
- for (int i = 0; i < 2; ++i) {
- if (!b->jmp.arg[i].bits) break;
- new->jmp.arg[i] = mapref(instrmap, sv, b->jmp.arg[i]);
- }
- }
- if (b->s1) {
- new->s1 = bmap[b->s1->id];
- if (b->s2) new->s2 = bmap[b->s2->id];
- }
- }
- exit->id = fn->nblk;
- fn->prop &= ~FNUSE;
- if (ins->cls && sv->nretpoints > 0) {
- assert(sv->nretpoints == exit->npred);
- if (sv->nretpoints == 1) {
- /* fill copy */
- ins->l = retvals[0];
- } else {
- /* fill phi */
- union ref *refs = NULL;
- xbgrow(&refs, sv->nretpoints);
- memcpy(refs, retvals, sizeof *refs * sv->nretpoints);
- vpush(&phitab, refs);
- ins->l = mkref(RXXX, phitab.n-1);
- }
- }
- bmap[0]->_pred0 = blk;
- bmap[0]->npred = 1;
- return exit;
-}
-
-enum { MAX_REC_INLINE = 16 };
-void
-doinline(struct function *fn)
-{
- if (calltab.n == 0) return;
- struct block *b = fn->entry;
- struct stack { /* stack of callees being inline expanded */
- struct block *b; /* block after the end of expansion */
- struct savedfunc *sv;
- } stkbuf[MAX_REC_INLINE], *stk = stkbuf + MAX_REC_INLINE, *stkend = stk;
- bool dumpbefore = 0;
- do {
- if (stk != stkend && b == stk->b)
- ++stk; /* pop */
- else if (stk == stkbuf) /* stack full? */
- continue;
- for (int i = 0; i < b->ins.n; ++i) {
- struct instr *ins = &instrtab[b->ins.p[i]];
- if (ins->op != Ocall) continue;
- if (!isaddrcon(ins->l,0)) continue;
- struct call *call = &calltab.p[ins->r.i];
- internstr fname = xcon2sym(ins->l.i);
- struct savedfunc **pcallee, *sv;
- if ((pcallee = pmap_get(&savedfns, fname))
- && (sv = *pcallee)->nabiarg == call->narg && call->vararg == -1
- && call->narg == sv->nabiarg
- && (!call->narg || !memcmp(sv->abiarg, call->abiarg, sizeof *sv->abiarg * sv->nabiarg))
- && !memcmp(sv->abiret, call->abiret, sizeof sv->abiret)) {
- for (struct stack *s = stk; s != stkend; ++s) {
- if (s->sv == sv) goto Skip; /* recursion encountered */
- }
- if (ccopt.dbg.y) {
- if (!dumpbefore) {
- bfmt(ccopt.dbgout, "<< Before inlining >>\n");
- irdump(fn);
- dumpbefore = 1;
- }
- }
-
- (--stk)->b = inlcall(fn, b, i, *pcallee);
- stk->sv = sv;
- if (ccopt.dbg.y) {
- bfmt(ccopt.dbgout, "<< After inlining '%s' (@%d-@%d) >>\n", fname, b->lnext->id, stk->b->id);
- irdump(fn);
- }
- break;
- }
- Skip:;
- }
- } while ((b = b->lnext) != fn->entry);
-}
-
-/* vim:set ts=3 sw=3 expandtab: */