#include "gc.h" #include "bound.h" static BB* bbfree; static BBset* bbsfree; static int bballoc; static int bbsalloc; static BB bbz; static BBset bbsz; static BB* firstbb; static BB* lastbb; static BB* wounded; static BB* bbaux; static BBset* recalc; static BBset* bbhash[BBHASH]; static BB** ordered; static int bcount; static BBset** heap; static int heapn; static int bsize; static char bbbuff[BBBSIZE]; static int bchange; #define bdebug (debug['v']) #define dbg 0 #define bcheck 0 static long Rn(Reg *r) { if(r == R) return -1; return r->rpo; } static BB* bba(void) { BB *b; bballoc++; b = bbfree; if(b == nil) { b = alloc(sizeof(*b)); } else bbfree = b->link; *b = bbz; return b; } static void bfree(BB *b) { bballoc--; b->link = bbfree; bbfree = b; } static BBset* bbsa(void) { BBset *b; bballoc++; b = bbsfree; if(b == nil) { b = alloc(sizeof(*b)); } else bbsfree = b->link; *b = bbsz; return b; } static void bsfree(BBset *b) { bballoc--; b->link = bbsfree; bbsfree = b; } static void dumpheap(void) { int i; for(i = 1; i <= heapn; i++) print(" %d", heap[i]->damage); } static void checkheap(void) { int N, N2, n, c; N = heapn; N2 = N >> 1; for(n = 1; n <= N2; n++) { c = n << 1; if((heap[c]->damage > heap[n]->damage) || ((c < N) && (heap[c + 1]->damage > heap[n]->damage))) { print("bad heap (%d:%d) %d [", n, heap[n]->damage, heapn); dumpheap(); print(" ]\n"); abort(); } } } static void downheap(int n) { int N, N2, d, c; BBset *s, *t, *u; s = heap[n]; d = s->damage; //print("down %d %d", n, d); N = heapn; N2 = N >> 1; while(n <= N2) { c = n << 1; t = heap[c]; if(c < N) { u = heap[c + 1]; if(t->damage < u->damage) { t = u; c++; } } //print(" [%d %d]", c, t->damage); if(t->damage < d) break; heap[n] = t; t->index = n; n = c; } heap[n] = s; s->index = n; //print("\n"); //checkheap(); } static void upheap(int n) { int f, d; BBset *s, *t; s = heap[n]; d = s->damage; //print("up %d %d", n, d); while(n > 1) { f = n >> 1; t = heap[f]; //print(" [%d %d]", f, t->damage); if(t->damage >= d) break; heap[n] = t; t->index = n; n = f; } heap[n] = s; s->index = n; //print("\n"); //checkheap(); } static void heapremove(BBset *s) { int x; BBset *t; x = s->index; s->index = 0; if(x == 0) return; if(x == heapn) { heapn--; return; } t = heap[heapn--]; heap[x] = t; t->index = x; if(s->damage < t->damage) upheap(x); else downheap(x); } static void heapadd(BBset *s) { int n; n = heapn + 1; heap[n] = s; s->index = n; heapn = n; upheap(n); } static void bbsrecalc(BBset *s) { if(s->recalc) return; s->recalc = 1; s->link = recalc; recalc = s; heapremove(s); } static void bbadd(BB *b, Hval h) { int k; BBset *s; k = h[0] & BBMASK; for(s = bbhash[k]; s != nil; s = s->next) { if(BBEQ(s->hash, h)) { b->set = s; b->link = s->ents; s->ents = b; bbsrecalc(s); return; } } s = bbsa(); s->next = bbhash[k]; bbhash[k] = s; b->set = s; b->link = nil; s->ents = b; BBCP(s->hash, h); bbsrecalc(s); } static int hashbb(BB *b, Hval h) { Reg *r; Prog *p; char *s; int c, f, i, n; r = b->first; s = bbbuff; i = 0; n = BBBSIZE; for(;;) { p = r->prog; if(p->as != ANOP) { if(p->to.type == D_BRANCH) p->to.offset = r->s2->rpo; c = snprint(s, n, "%P", p); s += c; n -= c; i++; } if(r == b->last) break; r = r->link; } if(n == 0) return Bbig; b->len = i; BBMKHASH(bbbuff, BBBSIZE - n, h); f = b->flags; if(i == 1 && r->prog->as == AJMP && b->first->p1 == R) f = Bjo; else if(b->first->p1 != R) f |= Bpre; if(bdebug) print("A %x %s %ux %ux\n", f, bbbuff, h[0], h[1]); return f; } static void enterbb(BB *b) { Hval h; b->flags = hashbb(b, h); if(b->flags != Bbig) bbadd(b, h); } static void preproc(BB *b, int x) { BB *n; Reg *r; ordered[x] = b; if(b->last->rpo - b->first->rpo > BBBIG) { b->flags = Bbig; return; } if(b->first->p2 == nil) { b->flags = Bdel; return; } switch(b->last->prog->as) { case ARET: case AJMP: case AIRETL: break; default: b->flags = Bdel; n = bba(); n->first = b->first; for(r = b->last->link; r != R; r = r->link) { switch(r->prog->as) { case ARET: case AJMP: case AIRETL: n->last = r; n->flags = Bpin; enterbb(n); if(n->flags & Bpin) { n->aux = bbaux; bbaux = n; } else bfree(n); return; } } bfree(n); return; } enterbb(b); } static int p2len(Reg *r) { int c; c = 0; for(r = r->p2; r != nil; r = r->p2link) c++; return c; } static void calcdamage(BBset *s) { BB *f; int d, t; s->recalc = 0; f = s->ents; if(f == nil) return; if(f->flags & Bjo) { if(bdebug) print("add %ld jo\n", f->first->rpo); s->damage = COSTJO; heapadd(s); return; } if(f->link == nil) { if(bdebug) print("solo %x %x\n", s->hash[0], s->hash[1]); return; } d = 0; t = 0; while(f != nil) { if((f->flags & (Bpre|Bpin)) == 0 && f->last->link != R) { t = 1; d += (f->last->rpo - f->first->rpo) >> 1; } d += p2len(f->first); f = f->link; } if(t == 0) { if(bdebug) print("all pre %ld\n", s->ents->first->rpo); return; } if(bdebug) print("add %ld %d\n", s->ents->first->rpo, d); if(d > COSTHI) d = COSTHI; s->damage = d; heapadd(s); } static Reg* findjump(BB *b) { Reg *r, *l; r = b->first; l = b->last; for(;;) { if(r->prog->as == AJMP) break; if(r == l) { diag(Z, "findjump botch"); break; } r = r->link; } return r; } static BB* findset(int r) { BB *s, **p; int n, n2; if(r < ordered[0]->first->rpo) return nil; n = bcount; p = ordered; while(n > 0) { n2 = n >> 1; s = p[n2]; if(r < s->first->rpo) { n = n2; continue; } if(r > s->last->rpo) { n2++; p += n2; n -= n2; continue; } return s; } diag(Z, "findset botch"); return nil; } static void wound(Reg *r) { BB *b, *p, **n; BBset *s; b = findset(r->rpo); if(b == nil) return; s = b->set; if(s == nil) return; for(n = &s->ents; (p = *n) != nil; n = &(*n)->link) { if(p == b) { *n = b->link; b->link = wounded; wounded = b; bbsrecalc(s); return; } } } static void printbl(Reg *l) { if(l == nil) { print("Z"); return; } print("%ld", l->rpo); while((l = l->p2link) != nil) print(" %ld", l->rpo); } static void appset(Reg *e, Reg *s) { for(;;) { if(s->p2link == R) { s->p2link = e; return; } s = s->p2link; } } static Reg* delset(Reg *e, Reg *s) { Reg *c, *l; c = s; l = nil; for(;;) { if(e == c) { if(l == nil) return s->p2link; l->p2link = c->p2link; return s; } l = c; c = c->p2link; if(c == nil) return s; } } static void redest(Reg *s, Reg *d) { while(s != R) { s->s2 = d; s = s->p2link; } } static void changedest(Reg *s, Reg *d, int x) { Reg *l; if(bdebug) { print("change %ld [", s->rpo); printbl(s->p2); print("] -> %ld [", d->rpo); printbl(d->p2); print("]\n"); } if(s->p2 == nil) { // print("deadjmp\n"); return; } l = s->p2; for(;;) { if(bdebug) print("s2 %ld = %ld\n", l->rpo, d->rpo); l->s2 = d; wound(l); if(l->p2link == nil) break; l = l->p2link; } if(x) { l->p2link = delset(s, d->p2); d->p2 = s->p2; } else { l->p2link = d->p2; d->p2 = s->p2; s->p2 = nil; } if(bdebug) { print("result ["); printbl(d->p2); print("]\n"); } bchange = 1; } static void bexcise(BB *b) { Reg *r, *l; l = b->last; r = b->first; if(bdebug) print("excise %ld to %ld\n", r->rpo, l->rpo); for(;;) { r->prog->as = ANOP; r->prog->to.type = D_NONE; r->p2 = R; if(r->s2 != R) { r->s2->p2 = delset(r, r->s2->p2); r->s2 = R; } if(r == l) break; r = r->link; } } static int backtrack(Reg *s, Reg *d) { int i; char l[BINST], r[BINST]; //print("backtrack %ld %ld\n", Rn(s), Rn(d)); i = 0; while(s != nil && d != nil) { if(snprint(l, BINST, "%P", s->prog) == BINST) break; if(snprint(r, BINST, "%P", d->prog) == BINST) break; //print("%s\t%s\n", l, r); if(strcmp(l, r) != 0) break; i++; s = s->p2link; d = d->p2link; } return i; } static void checktails(void) { int c; Reg *r; c = 0; for(r = firstr; r->link != R; r = r->link) { if(r->prog->as == AJMP && r->s2 != nil) c += backtrack(r->p1, r->s2->p1); } if(c > 0) print("tails %s %d\n", firstr->prog->from.sym->name, c); } static void process(BBset *s) { Reg *h; BB *f, *o, *p, *t; if(bdebug) print("process %d %x %x\n", s->damage, s->hash[0], s->hash[1]); f = s->ents; if(f->flags & Bjo) { s->ents = nil; h = findjump(f)->s2; o = nil; while(f != nil) { t = f->link; if((f->flags & Bjo) != 0 && f->first->s2 != f->first) { changedest(f->first, h, 1); bexcise(f); } else { f->link = o; o = f; } f = t; } s->ents = o; } else { o = nil; p = nil; while(f != nil) { t = f->link; if((f->flags & (Bpre|Bpin)) != 0 || (f->last->link == R)) { f->link = p; p = f; } else { f->link = o; o = f; } f = t; } if(o == nil) { diag(Z, "all Bpre"); return; } if(p == nil) { p = o; o = p->link; p->link = nil; s->ents = p; } else s->ents = p; h = p->first; // oblit o list repl with jmp to h while(o != nil) { changedest(o->first, h, 1); bexcise(o); o = o->link; } bbsrecalc(s); } } static void iterate(void) { BBset *s; BB *b, *t; heapn = 0; for(;;) { for(b = wounded; b != nil; b = t) { t = b->link; enterbb(b); } wounded = nil; for(s = recalc; s != nil; s = s->link) calcdamage(s); recalc = nil; if(heapn == 0) return; s = heap[1]; heapremove(s); process(s); } } static void cleanup(void) { int i; BB *l, *n; BBset *b, *t; for(i = 0; i < BBHASH; i++) { b = bbhash[i]; bbhash[i] = nil; while(b != nil) { t = b->next; bsfree(b); b = t; } } for(i = 0; i < bcount; i++) bfree(ordered[i]); for(l = bbaux; l != nil; l = n) { n = l->aux; bfree(l); } bbaux = nil; } static void prreg(Reg *r) { Prog *p; p = r->prog; if(p->to.type == D_BRANCH) p->to.offset = r->s2->rpo; print("%ld:%P\tr %lX ", r->rpo, r->prog, r->regu); print("p1 %ld p2 %ld p2l %ld s1 %ld s2 %ld link %ld", Rn(r->p1), Rn(r->p2), Rn(r->p2link), Rn(r->s1), Rn(r->s2), Rn(r->link)); if(!r->active) print(" d"); // print(" %p %p\n", r->prog, r->prog->link); print("\n"); } static void prfunc(char*); static void checkr(int d) { Prog *p; Reg *r, *t; for(r = firstr; r->link != R; r = r->link) { for(p = r->prog->link; p != P && p != r->link->prog; p = p->link) ; if(p == P) { print("%ld: bad prog link\n", r->rpo); if(d) prfunc(nil); abort(); } if(r->s1 != R && (r->s1 != r->link || r->link->p1 != r)) { print("%ld: bad s1 p1\n", r->rpo); if(d) prfunc(nil); abort(); } if(r->s2 != R && r->s2->p2 == nil) { print("%ld: no p2 for s2\n", r->rpo); if(d) prfunc(nil); abort(); } if(r->p2 != R) { t = r->p2->s2; while(t != r) { t = t->p2link; if(t == R) { print("%ld: bad s2 for p2\n", r->rpo); if(d) prfunc(nil); abort(); } } } } } static void prfunc(char *s) { Reg *r; if(s != nil) print("%s structure %s\n", s, firstr->prog->from.sym->name); for(r = firstr; r != R; r = r->link) prreg(r); if(s != nil) { print("end\n"); checkr(0); } } /* find p in r's list and replace with l */ static void adjprog(Reg *r, Prog *p, Prog *l) { Prog *t, **n; for(n = &r->prog->link; (t = *n) != nil; n = &t->link) { if(t == p) { *n = l; return; } } print("adjprog botch\n"); abort(); } static void jumptojump(void) { Reg *r; for(r = firstr; r != R; r = r->link) { if(r->prog->as == AJMP && r->p2 != R && r->s2 != r) { if(bdebug) print("jump as dest %ld -> %ld\n", r->rpo, r->s2->rpo); changedest(r, r->s2, 0); bchange++; } } } /* drag a tail to replace a jump. seems to be a bad idea. */ static void rearrange(void) { int i; Reg *j, *t; BB *b, *p, *s; for(i = 0; i < bcount; i++) { b = ordered[i]; if(b->flags & Bdel) continue; j = b->last; if(j->prog->as == AJMP && j->s2->p1 == R) { t = j->s2; if(t == b->first) continue; s = findset(t->rpo); if(s == nil) { diag(Z, "no self"); continue; } if(s == ordered[0]) continue; if(s->flags & Bdel) continue; if(s->last->link == R) continue; if(bdebug) print("drag %ld to %ld\n", t->rpo, j->rpo); p = findset(t->rpo - 1); if(p == nil) { diag(Z, "no predec"); continue; } if(p->last->link != t) { diag(Z, "bad predec %ld %ld", p->last->rpo, t->rpo); continue; } /* poison everything in sight */ b->flags |= Bdel; s->flags |= Bdel; findset(j->link->rpo)->flags |= Bdel; findset(s->last->link->rpo)->flags |= Bdel; /* remove */ adjprog(p->last, t->prog, s->last->link->prog); p->last->link = s->last->link; /* fix tail */ adjprog(s->last, s->last->link->prog, j->link->prog); s->last->link = j->link; /* fix head */ adjprog(j, j->link->prog, t->prog); j->link = t; /* nop the jump */ j->prog->as = ANOP; j->prog->to.type = D_NONE; j->s2 = nil; j->link->p2 = delset(j, j->link->p2); j->s1 = t; t->p1 = j; if(bcheck) checkr(1); bchange++; } } } void jumptodot(void) { Reg *r; for(r = firstr; r != R; r = r->link) { if(r->prog->as == AJMP && r->s2 == r->link) { if(debug['v']) print("jump to next %ld\n", r->rpo); r->prog->as = ANOP; r->prog->to.type = D_NONE; r->s2 = nil; r->link->p2 = delset(r, r->link->p2); findset(r->rpo)->flags |= Bdel; findset(r->link->rpo)->flags |= Bdel; bchange++; } } } void comtarg(void) { int n; BB *b, *c; Reg *r, *l, *p, *t; loop: bchange = 0; /* excise NOPS because they just get in the way */ /* some have p2 because they are excised labelled moves */ if(debug['v']) { n = 0; for(r = firstr; r != R; r = r->link) r->rpo = n++; prfunc("prenop"); } r = firstr; l = r->link; while(l != R) { if(l->prog->as == ANOP) { t = l->p1; p = l->p2; if(dbg) print("nop %ld [", l->rpo); if(dbg) printbl(p); for(;;) { adjprog(r, l->prog, l->prog->link); r->link = l->link; l->link = freer; freer = l; l = r->link; if(l->prog->as != ANOP) break; if(dbg) print("] %ld [", l->rpo); if(dbg) printbl(l->p2); if(p == R) p = l->p2; else if(l->p2 != nil) appset(l->p2, p); } if(dbg) print("] %ld [", l->rpo); if(dbg) printbl(l->p2); if(p != R) { redest(p, l); if(l->p2 != R) appset(p, l->p2); else l->p2 = p; } if(dbg) print("] -> ["); if(dbg) printbl(l->p2); if(dbg) print("]\n"); if(r->s1 != R) r->s1 = l; l->p1 = t; } r = l; l = r->link; } n = 0; for(r = firstr; r != R; r = r->link) r->rpo = n++; if(debug['v']) prfunc("input"); firstbb = nil; lastbb = nil; if(debug['v']) print("bbstart\n"); n = 0; r = firstr; do { b = bba(); b->first = r; for(;;) { l = r; r = r->link; switch(l->prog->as) { case ARET: case AJMP: case AIRETL: goto out; } if(r->p2 != R) break; } out: b->last = l; if(lastbb == nil) firstbb = b; else lastbb->link = b; lastbb = b; if(bdebug) print("BB %ld %ld\n", b->first->rpo, b->last->rpo); n++; } while(r != R); if(debug['v']) print("end\n"); if(n > bsize) { bsize = n * 3 / 2; if(bsize < BBINIT) bsize = BBINIT; ordered = alloc(bsize * sizeof(*ordered)); heap = alloc((bsize + 1) * sizeof(*ordered)); } if(debug['v']) print("preprocstart\n"); n = 0; for(b = firstbb; b != nil; b = c) { c = b->link; preproc(b, n++); } if(debug['v']) print("end\n"); bcount = n; jumptojump(); if(debug['v']) print("iteratestart\n"); iterate(); //checktails(); if(debug['v']) print("end\n"); if(debug['v']) print("extrastart\n"); jumptodot(); // rearrange(); if(debug['v']) print("end\n"); cleanup(); if(bballoc || bbsalloc) diag(Z, "bballoc %d %d", bballoc, bbsalloc); if(debug['v']) prfunc("output"); if(1 && bchange) goto loop; }