diff options
| author | 2026-03-17 13:22:00 +0100 | |
|---|---|---|
| committer | 2026-03-17 13:22:00 +0100 | |
| commit | a8d6f8bf30c07edb775e56889f568ca20240bedf (patch) | |
| tree | b5a452b2675b2400f15013617291fe6061180bbf /src/ir.c | |
| parent | 24f14b7ad1af08d872971d72ce089a529911f657 (diff) | |
REFACTOR: move sources to src/
Diffstat (limited to 'src/ir.c')
| -rw-r--r-- | src/ir.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/src/ir.c b/src/ir.c new file mode 100644 index 0000000..b612143 --- /dev/null +++ b/src/ir.c @@ -0,0 +1,689 @@ +#include "ir.h" +#include "../obj/obj.h" + +uchar type2cls[NTYPETAG]; +uchar cls2siz[] = { [KI32] = 4, [KI64] = 8, [KF32] = 4, [KF64] = 8 }; +uchar cls2load[] = { + [KI32] = Oloadu32, [KI64] = Oloadi64, + [KF32] = Oloadf32, [KF64] = Oloadf64, [KPTR] = -1 +}, cls2store[] = { + [KI32] = Ostorei32, [KI64] = Ostorei64, + [KF32] = Ostoref32, [KF64] = Ostoref64, [KPTR] = -1 +}; +const uchar siz2intcls[] = { [1] = KI32, [2] = KI32, [4] = KI32, [8] = KI64 }; + +const char *opnames[] = { + "?\??", +#define _(o,...) #o, +#include "op.def" +#undef _ +}; + +const uchar opnarg[] = { + 0, +#define _(o,n) n, +#include "op.def" +#undef _ +}; + +struct instr instrtab[MAXINSTR]; +struct use *instruse[MAXINSTR]; +int ninstr; +static int instrfreelist; +static struct use *usefreelist; +static struct arena **usearena; +struct calltab calltab; +struct phitab phitab; +struct dattab dattab; +struct contab contab; +struct addrtab addrtab; +static ushort *addrht; +static int naddrht; +int visitmark; + +void +irinit(struct function *fn) +{ + static struct call callsbuf[64]; + static union ref *phisbuf[64]; + static struct irdat datsbuf[64]; + static struct xcon consbuf[64]; + static struct addr addrsbuf[64]; + + assert(fn->arena && !fn->passarena); + + ninstr = 0; + instrfreelist = -1; + usefreelist = NULL; + usearena = fn->arena; + vinit(&calltab, callsbuf, countof(callsbuf)); + for (int i = 0; i < phitab.n; ++i) xbfree(phitab.p[i]); + vinit(&phitab, phisbuf, countof(phisbuf)); + vinit(&contab, consbuf, countof(consbuf)); + if (!dattab.p) vinit(&dattab, datsbuf, countof(datsbuf)); + naddrht = 128; + if (!addrht) xbgrowz(&addrht, naddrht); + else if (addrtab.n) memset(addrht, 0, xbcap(addrht) * sizeof *addrht); + vinit(&addrtab, addrsbuf, countof(addrsbuf)); + + if (!type2cls[TYINT]) { + for (int i = TYBOOL; i <= TYUVLONG; ++i) { + int siz = targ_primsizes[i]; + type2cls[i] = siz < 8 ? KI32 : KI64; + } + type2cls[TYFLOAT] = KF32; + type2cls[TYDOUBLE] = KF64; + type2cls[TYLDOUBLE] = KF64; + type2cls[TYPTR] = KPTR; + type2cls[TYARRAY] = KPTR; + cls2siz[KPTR] = targ_primsizes[TYPTR]; + cls2load[KPTR] = targ_64bit ? Oloadi64 : Oloads32; + cls2store[KPTR] = targ_64bit ? Ostorei64 : Ostorei32; + } + fn->entry = fn->curblk = allocz(fn->arena, sizeof(struct block), 0); + fn->nblk = 1; + fn->entry->lprev = fn->entry->lnext = fn->entry; + fn->prop = FNUSE; /* builder keeps this */ +} + +static int +newaddr(const struct addr *addr) +{ + if (addrtab.n >= naddrht/4*3 /*75% load factor */) { + xbgrowz(&addrht, naddrht*2); + memset(addrht, 0, naddrht * sizeof *addrht); + naddrht *= 2; + for (int i = 0; i < addrtab.n; ++i) { /* rehash */ + const struct addr *addr = &addrtab.p[i]; + for (uint h = hashb(0, addr, sizeof *addr), j = h;; ++j) { + if (!addrht[j &= naddrht - 1]) { + addrht[j] = i+1; + break; + } + } + } + } + for (uint h = hashb(0, addr, sizeof *addr), i = h;; ++i) { + i &= naddrht - 1; + if (!addrht[i]) { + vpush(&addrtab, *addr); + return (addrht[i] = addrtab.n) - 1; + } else if (!memcmp(&addrtab.p[addrht[i]-1], addr, sizeof *addr)) { + return addrht[i] - 1; + } + } +} + +union ref +newxcon(const struct xcon *con) +{ + assert((con->issym ^ con->isdat) || con->cls); + vpush(&contab, *con); + return mkref(RXCON, contab.n-1); +} + +union irtype +mkirtype(union type t) +{ + if (t.t == TYVOID || isscalar(t)) + return (union irtype) { .cls = type2cls[scalartypet(t)] }; + assert(isagg(t)); + return (union irtype) { .isagg = 1, .dat = t.dat }; +} + +union ref +mkintcon(enum irclass k, vlong i) +{ + if (i < 1l << 28 && i >= -(1l << 28)) { + return mkref(RICON, i); + } else { + struct xcon con = { .cls = k, .i = i }; + if (cls2siz[k] == 4) /* check upper half is zero or -1 */ + assert(in_range((i >> 32) + 1, 0, 1)); + return newxcon(&con); + } +} + +union ref +mkfltcon(enum irclass k, double f) +{ + struct xcon con = { .cls = k, .f = k == KF32 ? (float) f : f }; + return newxcon(&con); +} + +union ref +mksymref(internstr s, enum symflags symflags) +{ + struct xcon con = { .issym = 1, .sym = s, .flag = symflags }; + return newxcon(&con); +} + +union ref +mkdatref(internstr name, union type ctype, uint siz, uint align, + const void *bytes, uint n, bool deref, bool funclocal) +{ + struct irdat dat = { .ctype = ctype, .align = align, .siz = siz, .name = name, .section = Srodata }; + if (funclocal && objout.code && align >= 4 && align <= targ_primsizes[TYPTR] && siz <= 16) + dat.section = Stext; + + assert(n <= siz && siz && align); + if (!name) { + char buf[32]; + struct wbuf wbuf = MEMBUF(buf, sizeof buf); + + bfmt(&wbuf, ".L%c.%d", dat.section == Stext ? 'L' : 'D', dattab.n); + ioputc(&wbuf, 0); + assert(!wbuf.err); + dat.name = name = intern(buf); + } + dat.off = objnewdat(name, dat.section, 0, siz, align); + uchar *p = (dat.section == Stext ? objout.textbegin : objout.rodata.p) + dat.off; + if (n) memcpy(p, bytes, n); + if (dat.section != Stext) memset(p+n, 0, siz - n); + vpush(&dattab, dat); + return newxcon(&(struct xcon){.isdat = 1, .deref = deref, .dat = dattab.n - 1, .flag = SLOCAL}); +} + +internstr +xcon2sym(int ref) +{ + struct xcon con = contab.p[ref]; + assert(con.issym ^ con.isdat); + return con.issym ? con.sym : dattab.p[con.dat].name; +} + +struct instr +mkalloca(uint siz, uint align) +{ + struct instr ins = { .cls = KPTR }; + assert(ispo2(align) && align <= 16); + ins.op = Oalloca1 + ilog2(align); + ins.l = mkref(RICON, siz/align + (siz%align != 0)); + return ins; +} + +union ref +mkcallarg(union irtype ret, uint narg, int vararg) +{ + struct call call = { .ret=ret, .narg=narg, .vararg=vararg }; + assert(vararg == -1 || (uint)vararg <= narg); + vpush(&calltab, call); + return mkref(RXXX, calltab.n-1); +} + +union ref +mkaddr(struct addr addr) +{ + return mkref(RADDR, newaddr(&addr)); +} + +void +addpred(struct block *blk, struct block *p) +{ + if (blk->npred == 0) { + blk->_pred0 = p; + ++blk->npred; + return; + } + if (blk->npred == 1) { + struct block *p0 = blk->_pred0; + blk->_pred = NULL; + xbgrow(&blk->_pred, 4); + *blk->_pred = p0; + } + xbpush(&blk->_pred, &blk->npred, p); +} + +void +delpred(struct block *blk, struct block *p) +{ + for (int i = 0; i < blk->npred; ++i) { + if (blkpred(blk, i) == p) { + for (int j = 0; j < blk->phi.n; ++j) { + union ref *phiargs = phitab.p[instrtab[blk->phi.p[j]].l.i]; + for (int k = i; k < blk->npred - 1; ++k) { + phiargs[k] = phiargs[k + 1]; + } + } + for (int k = i; k < blk->npred - 1; ++k) { + blkpred(blk, k) = blkpred(blk, k + 1); + } + if (--blk->npred == 1) { + struct block *p0 = blk->_pred[0]; + xbfree(blk->_pred); + blk->_pred0 = p0; + } + return; + } + } + //assert(0&&"blk not in p"); +} + +struct block * +newblk(struct function *fn) +{ + struct block *blk = allocz(fn->arena, sizeof(struct block), 0); + blk->id = -1; + return blk; +} + +void +freeblk(struct function *fn, struct block *blk) +{ + if (blk->npred > 1) + xbfree(blk->_pred); + blk->npred = 0; + blk->_pred = NULL; + + for (int i = 0; i < blk->phi.n; ++i) { + int ui = blk->phi.p[i]; + union ref *r = phitab.p[instrtab[ui].l.i]; + for (int j = 0; j < blk->npred; ++j) { + deluse(blk, ui, *r); + } + } + for (int i = 0; i < blk->ins.n; ++i) { + int ui = blk->ins.p[i]; + struct instr *ins = &instrtab[ui]; + if (ins->l.t == RTMP) deluse(blk, ui, ins->l); + if (ins->r.t == RTMP) deluse(blk, ui, ins->r); + } + for (int i = 0; i < 2; ++i) { + if (blk->jmp.arg[i].t == RTMP) deluse(blk, USERJUMP, blk->jmp.arg[i]); + } + if (blk->s1) delpred(blk->s1, blk); + if (blk->s2) delpred(blk->s2, blk); + vfree(&blk->phi); + vfree(&blk->ins); + if (blk->id != -1) + --fn->nblk; + if (blk->lprev) blk->lprev->lnext = blk->lnext; + if (blk->lnext) blk->lnext->lprev = blk->lprev; + blk->id = 1u<<31; +} + +struct block * +insertblk(struct function *fn, struct block *pred, struct block *subst) +{ + struct block *new = newblk(fn); + struct block **s = pred->s1 == subst ? &pred->s1 : &pred->s2; + assert(*s == subst); + new->lnext = pred->lnext; + new->lprev = pred; + pred->lnext->lprev = new; + pred->lnext = new; + *s = new; + new->jmp.t = Jb; + new->s1 = subst; + addpred(new, pred); + for (int i = 0; i < subst->npred; ++i) { + if (blkpred(subst, i) == pred) { + blkpred(subst, i) = new; + ++fn->nblk; + return new; + } + } + assert(0); +} + +struct block * +blksplitafter(struct function *fn, struct block *blk, int idx) +{ + struct block *new = newblk(fn); + ++fn->nblk; + new->lprev = blk; + new->lnext = blk->lnext; + blk->lnext = new; + new->lnext->lprev = new; + if (idx == -1) { + new->ins = blk->ins; + memset(&blk->ins, 0, sizeof blk->ins); + } else { + if (idx < blk->ins.n-1) + vpushn(&new->ins, &blk->ins.p[idx+1], blk->ins.n-idx-1); + blk->ins.n -= new->ins.n; + } + new->jmp = blk->jmp; + blk->jmp.t = Jb; + memset(blk->jmp.arg, 0, sizeof blk->jmp.arg); + for (int i = 0; i < 2; ++i) { + struct block *s = (&blk->s1)[i]; + if (s) for (int i = 0; i < s->npred; ++i) { + if (blkpred(s, i) == blk) + blkpred(s, i) = new; + } + } + new->s1 = blk->s1, new->s2 = blk->s2; + blk->s1 = new, blk->s2 = NULL; + addpred(new, blk); + fn->prop &= ~FNUSE; + return new; +} + +int +allocinstr(void) +{ + int t; + if (instrfreelist != -1) { + t = instrfreelist; + memcpy(&instrfreelist, &instrtab[t], sizeof(int)); + if (instruse[t]) deluses(t); + } else { + assert(ninstr < countof(instrtab)); + t = ninstr++; + instruse[t] = NULL; + } + return t; +} + +void +adduse(struct block *ublk, int ui, union ref r) { + if (r.t != RTMP) return; + struct use *use; + if (usefreelist) { + use = usefreelist; + usefreelist = usefreelist->next; + } else { + use = alloc(usearena, sizeof *use, 0); + } + assert(use != instruse[r.i]); + use->next = instruse[r.i]; + use->blk = ublk, + use->u = ui; + instruse[r.i] = use; +} + +bool +deluse(struct block *ublk, int ui, union ref r) { + if (r.t != RTMP) return 0; + + for (struct use **puse = &instruse[r.i]; *puse; puse = &(*puse)->next) { + struct use *use = *puse; + if (use->blk == ublk && use->u == ui) { + *puse = use->next; + use->blk = 0; + use->u = 0; + use->next = usefreelist; + usefreelist = use; + return 1; + } + } + return 0; +} + +void +filluses(struct function *fn) +{ + struct block *blk = fn->entry; + + for (int i = 0; i < ninstr; ++i) + deluses(i); + + do { + for (int i = 0; i < blk->phi.n; ++i) { + int ins = blk->phi.p[i]; + union ref *phi = phitab.p[instrtab[ins].l.i]; + for (int i = 0; i < blk->npred; ++i) + adduse(blk, ins, phi[i]); + } + for (int i = 0; i < blk->ins.n; ++i) { + int ins = blk->ins.p[i]; + adduse(blk, ins, instrtab[ins].l); + adduse(blk, ins, instrtab[ins].r); + } + adduse(blk, USERJUMP, blk->jmp.arg[0]); + adduse(blk, USERJUMP, blk->jmp.arg[1]); + } while ((blk = blk->lnext) != fn->entry); + + fn->prop |= FNUSE; +} + +int +newinstr(struct block *at, struct instr ins) +{ + int new = allocinstr(); + instrtab[new] = ins; + if (at) { + adduse(at, new, ins.l); + adduse(at, new, ins.r); + } + return new; +} + +union ref +insertinstr(struct block *blk, int idx, struct instr ins) +{ + int new = newinstr(blk, ins); + if (idx == blk->ins.n) vpush(&blk->ins, new); + else { + assert(idx >= 0 && idx < blk->ins.n); + vpush_(&blk->ins._vb, sizeof *blk->ins.p); + vresize(&blk->ins, blk->ins.n); + for (int i = blk->ins.n++; i > idx; --i) + blk->ins.p[i] = blk->ins.p[i - 1]; + blk->ins.p[idx] = new; + } + return mkref(RTMP, new); +} + +union ref +insertphi(struct block *blk, enum irclass cls) +{ + int new = allocinstr(); + union ref *refs = NULL; + assert(blk->npred > 0); + xbgrowz(&refs, blk->npred); + vpush(&phitab, refs); + instrtab[new] = mkinstr(Ophi, cls, mkref(RXXX, phitab.n - 1)); + vpush(&blk->phi, new); + return mkref(RTMP, new); +} + +uint +numberinstrs(struct function *fn) +{ + struct block *blk = fn->entry; + int start = 0; + do { + blk->inumstart = start; + start += blk->ins.n+2; + } while ((blk = blk->lnext) != fn->entry); + return start-1; +} + +static bool +reachablerec(struct function *fn, struct block *blk) +{ + if (blk == fn->entry) return 1; + markvisited(blk); + if (blk->npred == 1 && !wasvisited(blkpred(blk, 0))) + return reachablerec(fn, blkpred(blk, 0)); + else for (int i = 0; i < blk->npred; ++i) { + struct block *p = blkpred(blk, i); + if (!wasvisited(p) && reachablerec(fn, p)) return 1; + } + return 0; +} + +bool +blkreachable(struct function *fn, struct block *blk) +{ + startbbvisit(); + return reachablerec(fn, blk); +} + +/* require use */ +void +replcuses(union ref from, union ref to) +{ + assert(from.t == RTMP); + for (struct use *use = instruse[from.i], *next; use; use = next) { + union ref *u; + int n, j; + next = use->next; + if (use->u == from.i) continue; + if (use->u == USERJUMP) { + u = &use->blk->jmp.arg[0]; + n = 2; + } else if (instrtab[use->u].op == Ophi) { + u = phitab.p[instrtab[use->u].l.i]; + n = use->blk->npred; + if (use->blk->phi.n == 0) continue; /* shouldn't happen */ + } else { + u = &instrtab[use->u].l; + n = 2; + } + + for (j = 0; j < n; ++j) { + if (u[j].bits == from.bits) { + u[j].bits = to.bits; + adduse(use->blk, use->u, to); + next = use; + break; + } + } + } +} + +void +deluses(int ins) +{ + for (struct use *use = instruse[ins], *next; use; use = next) { + next = use->next; + use->blk = 0; + use->u = 0; + use->next = usefreelist; + usefreelist = use; + } + instruse[ins] = NULL; +} + +void +delinstr(struct block *blk, int idx) +{ + int t = blk->ins.p[idx]; + assert(idx >= 0 && idx < blk->ins.n); + for (int i = 0; i < 2; ++i) { + deluse(blk, t, (&instrtab[t].l)[i]); + } + memcpy(&instrtab[t], &instrfreelist, sizeof(int)); + instrfreelist = t; + deluses(t); + for (int i = idx; i < blk->ins.n - 1; ++i) + blk->ins.p[i] = blk->ins.p[i + 1]; + --blk->ins.n; +} + +void +delphi(struct block *blk, int idx) +{ + int t = blk->phi.p[idx]; + assert(idx >= 0 && idx < blk->phi.n); + memcpy(&instrtab[t], &instrfreelist, sizeof(int)); + instrfreelist = t; + deluses(t); + for (int i = idx; i < blk->phi.n - 1; ++i) + blk->phi.p[i] = blk->phi.p[i + 1]; + --blk->phi.n; +} + +void +delnops(struct block *blk) +{ + int i, n, t; + /* delete trailing nops */ + while (blk->ins.n > 0 && instrtab[t = blk->ins.p[blk->ins.n - 1]].op == Onop) { + --blk->ins.n; + deluses(t); + memcpy(&instrtab[t], &instrfreelist, sizeof(int)); + instrfreelist = t; + } + /* delete rest of nops */ + for (i = blk->ins.n - 2, n = 0; i >= 0; --i) { + if (instrtab[t = blk->ins.p[i]].op == Onop) { + deluses(t); + memcpy(&instrtab[t], &instrfreelist, sizeof(int)); + instrfreelist = t; + ++n; + } else if (n) { + memmove(blk->ins.p+i+1, blk->ins.p+i+1+n, (blk->ins.n - n - i - 1)*sizeof *blk->ins.p); + blk->ins.n -= n; + n = 0; + } + } + if (n) + memmove(blk->ins.p, blk->ins.p + n, (blk->ins.n -= n)*sizeof *blk->ins.p); +} + +void +fillblkids(struct function *fn) +{ + int i = 0; + struct block *blk = fn->entry; + do blk->id = i++; while ((blk = blk->lnext) != fn->entry); + + fn->prop |= FNBLKID; +} + +/** Misc **/ + +static void +freefn(struct function *fn) +{ + struct block *blk = fn->entry; + do { + if (blk->npred > 1) xbfree(blk->_pred); + vfree(&blk->phi); + vfree(&blk->ins); + } while ((blk = blk->lnext) != fn->entry); +} + +void +irfini(struct function *fn) +{ + extern int nerror; + static union { char m[sizeof(struct arena) + (4<<10)]; struct arena *_align; } amem; + struct arena *passarena = (void *)&amem.m; + fn->passarena = &passarena; + if (nerror) { + freefn(fn); + return; + } + + abi0(fn); + lowerintrin(fn); + if (ccopt.o > OPT0) { + mem2reg(fn); + freearena(fn->passarena); + copyopt(fn); + } + if (ccopt.o >= OPT1) { + doinline(fn); + filldom(fn); + if (!(fn->prop & FNUSE)) filluses(fn); + cselim(fn); + freearena(fn->passarena); + simpl(fn); + freearena(fn->passarena); + } + if (maybeinlinee(fn)) { + // goto Fin; XXX do this by having inline function rematerialization when symbol is actually referenced + } + lowerstack(fn); + freearena(fn->passarena); + if (ccopt.dbg.o) { + bfmt(ccopt.dbgout, "<< Before isel >>\n"); + irdump(fn); + } + mctarg->isel(fn); + regalloc(fn); + freearena(fn->passarena); + if (objout.code) + mctarg->emit(fn); + +//Fin: + freearena(fn->passarena); + freefn(fn); +} + +/* vim:set ts=3 sw=3 expandtab: */ |