#include "gc.h" #define COL1 32 Reg* rega(void) { Reg *r; r = freer; if(r == R) { ALLOC(r, Reg); } else freer = r->link; *r = zreg; return r; } int rcmp(void *a1, void *a2) { Rgn *p1, *p2; int c1, c2; p1 = a1; p2 = a2; c1 = p2->costr; if(p2->costa > c1) c1 = p2->costa; c2 = p1->costr; if(p1->costa > c2) c2 = p1->costa; if(c1 -= c2) return c1; return p2->varno - p1->varno; } void regopt(Prog *p) { Reg *r, *r1, *r2; Prog *p1; int i, z; long val, initpc; ulong vreg; Bits bit; Var *v; struct { long m; long c; Reg* p; } log5[6], *lp; firstr = R; lastr = R; nvar = 0; for(z=0; zm = val; lp->c = 0; lp->p = R; val /= 5L; lp++; } val = 0; for(; p != P; p = p->link) { switch(p->as) { case ADATA: case AGLOBL: case ANAME: continue; } r = rega(); if(firstr == R) { firstr = r; lastr = r; } else { lastr->link = r; r->p1 = lastr; lastr->s1 = r; lastr = r; } r->prog = p; r->pc = val; val++; lp = log5; for(i=0; i<5; i++) { lp->c--; if(lp->c <= 0) { lp->c = lp->m; if(lp->p != R) lp->p->u0.log5 = r; lp->p = r; (lp+1)->c = 0; break; } lp++; } r1 = r->p1; if(r1 != R) switch(r1->prog->as) { case ABRA: case ARTS: case ARTE: r->p1 = R; r1->s1 = R; } bit = mkvar(&p->from, AGOK); if(bany(&bit)) switch(p->as) { case ALEA: if(!(mvbits & B_INDIR)) for(z=0; zuse1.b[z] |= bit.b[z]; } bit = mkvar(&p->to, p->as); if(bany(&bit)) switch(p->as) { case ABSR: /* funny */ for(z=0; zuse2.b[z] |= bit.b[z]; break; default: diag(Z, "reg: unknown asop: %A", p->as); case AADDB: case AADDW: case AADDL: case ASUBB: case ASUBW: case ASUBL: case AANDB: case AANDW: case AANDL: case AORB: case AORW: case AORL: case AEORB: case AEORW: case AEORL: case ABFINS: for(z=0; zuse2.b[z] |= bit.b[z]; case ANOP: case AMOVB: case AMOVW: case AMOVL: case AFMOVEB: case AFMOVEW: case AFMOVEL: case ACLRB: case ACLRW: case ACLRL: case AFMOVEF: case AFMOVED: if(mvbits & B_INDIR) for(z=0; zuse2.b[z] |= bit.b[z]; else for(z=0; zset.b[z] |= bit.b[z]; break; } } if(firstr == R) return; initpc = pc - val; /* * pass 2 * turn branch references to pointers * build back pointers */ for(r = firstr; r != R; r = r->link) { p = r->prog; if(p->to.type == D_BRANCH) { val = p->to.u0.s0.offset - initpc; r1 = firstr; while(r1 != R) { r2 = r1->u0.log5; if(r2 != R && val >= r2->pc) { r1 = r2; continue; } if(r1->pc == val) break; r1 = r1->link; } if(r1 == R) { diag(Z, "ref not found\n%L:%P", p->lineno, p); continue; } if(r1 == r) { diag(Z, "ref to self"); continue; } r->s2 = r1; r->p2link = r1->p2; r1->p2 = r; } } if(debug['R']) print("\n%L %D\n", firstr->prog->lineno, &firstr->prog->from); /* * pass 2.5 * find looping structure */ for(r = firstr; r != R; r = r->link) r->u0.active = 0; changer = 0; loopit(firstr); if(debug['R'] && debug['v']) { print("\nlooping structure:\n"); for(r = firstr; r != R; r = r->link) { print("%d:%P", r->loop, r->prog); for(z=0; zuse1.b[z] | r->use2.b[z] | r->set.b[z]; if(bany(&bit)) { print("%|", COL1); if(bany(&r->use1)) print(" u1=%B", r->use1); if(bany(&r->use2)) print(" u2=%B", r->use2); if(bany(&r->set)) print(" st=%B", r->set); } print("\n"); } } /* * pass 3 * iterate propagating usage * back until flow graph is complete */ loop1: changer = 0; for(r = firstr; r != R; r = r->link) r->u0.active = 0; for(r = firstr; r != R; r = r->link) if(r->prog->as == ARTS) prop(r, zbits, zbits); loop11: /* pick up unreachable code */ i = 0; for(r = firstr; r != R; r = r1) { r1 = r->link; if(r1 && r1->u0.active && !r->u0.active) { prop(r, zbits, zbits); i = 1; } } if(i) goto loop11; if(changer) goto loop1; /* * pass 4 * iterate propagating register/variable synchrony * forward until graph is complete */ loop2: changer = 0; for(r = firstr; r != R; r = r->link) r->u0.active = 0; synch(firstr, zbits); if(changer) goto loop2; /* * pass 5 * isolate regions * calculate costs (paint1) */ r = firstr; if(r) { for(z=0; zrefahead.b[z] | r->calahead.b[z]) & ~(externs.b[z] | params.b[z] | addrs.b[z]); if(bany(&bit)) { nearln = r->prog->lineno; warn(Z, "used and not set: %B", bit); if(debug['R'] && !debug['w']) print("used and not set: %B\n", bit); /* * 68040 'feature': * load of a denormalized fp will trap */ while(bany(&bit)) { i = bnum(bit); bit.b[i/32] &= ~(1L << (i%32)); v = var + i; if(v->type == D_AUTO) { r->set.b[i/32] |= (1L << (i%32)); if(typefd[v->etype]) addmove(r, i, NREG+NREG, 1); } } } } if(debug['R'] && debug['v']) print("\nprop structure:\n"); for(r = firstr; r != R; r = r->link) { if(debug['R'] && debug['v']) print("%P\n set = %B; rah = %B; cal = %B\n", r->prog, r->set, r->refahead, r->calahead); r->act = zbits; } rgp = region; nregion = 0; for(r = firstr; r != R; r = r->link) { for(z=0; zset.b[z] & ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); if(bany(&bit)) { nearln = r->prog->lineno; warn(Z, "set and not used: %B", bit); if(debug['R']) print("set an not used: %B\n", bit); excise(r); } for(z=0; zact.b[z] | addrs.b[z]); while(bany(&bit)) { i = bnum(bit); rgp->enter = r; rgp->varno = i; changer = 0; changea = 0; if(debug['R'] && debug['v']) print("\n"); paint1(r, i); bit.b[i/32] &= ~(1L<<(i%32)); if(changer <= 0 && changea <= 0) { if(debug['R']) print("%L$%d.%d: %B\n", r->prog->lineno, changer, changea, blsh(i)); continue; } rgp->costr = changer; rgp->costa = changea; nregion++; if(nregion >= NRGN) { warn(Z, "too many regions"); goto brk; } rgp++; } } brk: qsort(region, nregion, sizeof(region[0]), rcmp); /* * pass 6 * determine used registers (paint2) * replace code (paint3) */ rgp = region; for(i=0; ivarno); vreg = paint2(rgp->enter, rgp->varno); vreg = allreg(vreg, rgp); if(debug['R']) print("%L$%d.%d %R: %B\n", rgp->enter->prog->lineno, rgp->costr, rgp->costa, rgp->regno, bit); if(rgp->regno != D_NONE) paint3(rgp->enter, rgp->varno, vreg, rgp->regno); rgp++; } /* * pass 7 * peep-hole on basic block */ if(!debug['R'] || debug['P']) peep(); /* * pass 8 * recalculate pc */ val = initpc; for(r = firstr; r != R; r = r1) { r->pc = val; p = r->prog; p1 = P; r1 = r->link; if(r1 != R) p1 = r1->prog; for(; p != p1; p = p->link) { switch(p->as) { default: val++; break; case ANOP: case ADATA: case AGLOBL: case ANAME: break; } } } pc = val; /* * fix up branches */ if(debug['R']) if(bany(&addrs)) print("addrs: %B\n", addrs); r1 = 0; /* set */ for(r = firstr; r != R; r = r->link) { p = r->prog; if(p->to.type == D_BRANCH) p->to.u0.s0.offset = r->s2->pc; r1 = r; } /* * last pass * eliminate nops * free aux structures */ for(p = firstr->prog; p != P; p = p->link){ while(p->link && p->link->as == ANOP) p->link = p->link->link; } if(r1 != R) { r1->link = freer; freer = firstr; } } /* * add mov b,rn * just after r */ void addmove(Reg *r, int bn, int rn, int f) { Prog *p, *p1; Var *v; int badccr; badccr = 0; p = r->prog; p1 = p->link; if(p1) switch(p1->as) { case AMOVW: if(p1->from.type == D_CCR) p = p1; break; case ABEQ: case ABNE: case ABLE: case ABLS: case ABLT: case ABMI: case ABGE: case ABPL: case ABGT: case ABHI: case ABCC: case ABCS: p1 = prg(); p1->link = p->link; p->link = p1; p1->lineno = p->lineno; p1->from.type = D_CCR; p1->to.type = D_TOS; p1->as = AMOVW; p = p1; badccr = 1; } p1 = prg(); p1->link = p->link; p->link = p1; p1->lineno = p->lineno; v = var + bn; p1->from.sym = v->sym; p1->from.type = v->type; p1->from.u0.s0.offset = v->offset; p1->from.etype = v->etype; p1->to.type = rn; if(f) { p1->to = p1->from; p1->from = zprog.from; p1->from.type = rn; } p1->as = opxt[OAS][v->etype]; if(badccr) { p = p1; p1 = prg(); p1->link = p->link; p->link = p1; p1->lineno = p->lineno; p1->from.type = D_TOS; p1->to.type = D_CCR; p1->as = AMOVW; } if(debug['R']) print("%P%|.a%P\n", p, COL1, p1); } Bits mkvar(Adr *a, int as) { Var *v; int i, t, z; long o; Bits bit; Sym *s; mvbits = 0; t = a->type & D_MASK; switch(t) { default: if(t >= D_R0 && t < D_R0+NREG) { regbits |= RtoB(t-D_R0); if(as == ADIVUL || as == ADIVSL) regbits |= RtoB(t-D_R0+1); } if(t >= D_A0 && t < D_A0+NREG) regbits |= AtoB(t-D_A0); if(t >= D_F0 && t < D_F0+NREG) regbits |= FtoB(t-D_F0); goto none; case D_EXTERN: case D_STATIC: case D_AUTO: case D_PARAM: break; } s = a->sym; if(s == S) goto none; if((a->type & I_MASK) == I_ADDR) mvbits |= B_ADDR; switch(a->index & I_MASK) { case I_INDEX1: mvbits |= B_ADDR; break; case I_INDEX2: case I_INDEX3: mvbits |= B_INDIR; break; } o = a->u0.s0.offset; v = var; for(i=0; isym) if(t == v->type) if(o == v->offset) goto out; v++; } if(s) if(s->name[0] == '.') goto none; if(nvar >= NVAR) { if(s) warn(Z, "variable not optimized: %s", s->name); goto none; } i = nvar; nvar++; v = &var[i]; v->sym = s; v->offset = o; v->etype = a->etype; v->type = t; if(debug['R']) print("bit=%2d et=%2d %s (%d,%d,%d)\n", i, a->etype, s->name, v->sym, v->type, v->offset); out: bit = blsh(i); if(t == D_EXTERN || t == D_STATIC) for(z=0; zetype != v->etype || !typechlpfd[a->etype]) for(z=0; zp1) { for(z=0; zrefahead.b[z]; if(ref.b[z] != r1->refahead.b[z]) { r1->refahead.b[z] = ref.b[z]; changer++; } cal.b[z] |= r1->calahead.b[z]; if(cal.b[z] != r1->calahead.b[z]) { r1->calahead.b[z] = cal.b[z]; changer++; } } switch(r1->prog->as) { case ABSR: for(z=0; zset.b[z]) | r1->use1.b[z] | r1->use2.b[z]; cal.b[z] &= ~(r1->set.b[z] | r1->use1.b[z] | r1->use2.b[z]); r1->refbehind.b[z] = ref.b[z]; r1->calbehind.b[z] = cal.b[z]; } if(r1->u0.active) break; r1->u0.active = 1; } for(; r != r1; r = r->p1) for(r2 = r->p2; r2 != R; r2 = r2->p2link) prop(r2, r->refbehind, r->calbehind); } int loopit(Reg *r) { Reg *r1; int l, m; l = 0; r->u0.active = 1; r->loop = 0; if(r1 = r->s1) switch(r1->u0.active) { case 0: l += loopit(r1); break; case 1: l += LOOP; r1->loop += LOOP; } if(r1 = r->s2) switch(r1->u0.active) { case 0: l += loopit(r1); break; case 1: l += LOOP; r1->loop += LOOP; } r->u0.active = 2; m = r->loop; r->loop = l + 1; return l - m; } void synch(Reg *r, Bits dif) { Reg *r1; int z; for(r1 = r; r1 != R; r1 = r1->s1) { for(z=0; zrefbehind.b[z] & r1->refahead.b[z])) | r1->set.b[z] | r1->regdiff.b[z]; if(dif.b[z] != r1->regdiff.b[z]) { r1->regdiff.b[z] = dif.b[z]; changer++; } } if(r1->u0.active) break; r1->u0.active = 1; for(z=0; zcalbehind.b[z] & r1->calahead.b[z]); if(r1->s2 != R) synch(r1->s2, dif); } } ulong allreg(ulong b, Rgn *r) { Var *v; int i, j; v = var + r->varno; r->regno = D_NONE; switch(v->etype) { default: diag(Z, "unknown etype"); break; case TCHAR: case TUCHAR: case TSHORT: case TUSHORT: case TLONG: case TULONG: case TIND: i = BtoR(~b); j = BtoA(~b); if(r->costa == r->costr) if(i > j) i = NREG; if(j < NREG && r->costa > 0) if(r->costa > r->costr || i >= NREG) { r->regno = D_A0 + j; return AtoB(j); } if(i < NREG && r->costr > 0) { r->regno = D_R0 + i; return RtoB(i); } break; case TDOUBLE: case TFLOAT: i = BtoF(~b); if(i < NREG) { r->regno = D_F0 + i; return FtoB(i); } break; } return 0; } void paint1(Reg *r, int bn) { Reg *r1; Prog *p; int z; ulong bb; int x; z = bn/32; bb = 1L<<(bn%32); if(r->act.b[z] & bb) return; for(;;) { if(!(r->refbehind.b[z] & bb)) break; r1 = r->p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) break; if(r1->act.b[z] & bb) break; r = r1; } if(LOAD(r) & ~(r->set.b[z]&~(r->use1.b[z]|r->use2.b[z])) & bb) { changer -= CLOAD * r->loop; changea -= CLOAD * r->loop; if(debug['R'] && debug['v']) print("%d%P%|ld %B $%d.%d\n", r->loop, r->prog, COL1, blsh(bn), changer, changea); } for(;;) { r->act.b[z] |= bb; p = r->prog; if(r->use1.b[z] & bb) { changer += CREF * r->loop; changea += CREF * r->loop; x = p->from.index; if(x == D_NONE) { switch(p->as) { default: changea = -CINF; case AADDL: case ASUBL: case AMOVL: case ACMPL: break; } } else { changer += (CXREF-CREF) * r->loop; if(x != (I_INDEX3|D_NONE)) changer = -CINF; if((x&I_MASK) == I_INDEX1) changea = -CINF; } if(p->as == AMOVL) { x = p->to.type; if(x >= D_R0 && x < D_R0+NREG) changer += r->loop; if(x >= D_A0 && x < D_A0+NREG) changea += r->loop; } if(debug['R'] && debug['v']) print("%d%P%|u1 %B $%d.%d\n", r->loop, p, COL1, blsh(bn), changer, changea); } if((r->use2.b[z]|r->set.b[z]) & bb) { changer += CREF * r->loop; changea += CREF * r->loop; x = p->to.index; if(x == D_NONE) switch(p->as) { default: changea = -CINF; break; case AMOVL: case AADDL: case ACMPL: case ASUBL: case ACLRL: /* can be faked */ case ATSTL: /* can be faked */ break; } else { changer += (CXREF-CREF) * r->loop; if(x != (I_INDEX3|D_NONE)) changer = -CINF; if((x&I_MASK) == I_INDEX1) changea = -CINF; } if(p->as == AMOVL) { x = p->from.type; if(x >= D_R0 && x < D_R0+NREG) changer += r->loop; if(x >= D_A0 && x < D_A0+NREG) changea += r->loop; } if(debug['R'] && debug['v']) print("%d%P%|u2 %B $%d.%d\n", r->loop, p, COL1, blsh(bn), changer, changea); } if(STORE(r) & r->regdiff.b[z] & bb) { changer -= CLOAD * r->loop; changea -= CLOAD * r->loop; if(debug['R'] && debug['v']) print("%d%P%|st %B $%d.%d\n", r->loop, p, COL1, blsh(bn), changer, changea); } if(r->refbehind.b[z] & bb) for(r1 = r->p2; r1 != R; r1 = r1->p2link) if(r1->refahead.b[z] & bb) paint1(r1, bn); if(!(r->refahead.b[z] & bb)) break; r1 = r->s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint1(r1, bn); r = r->s1; if(r == R) break; if(r->act.b[z] & bb) break; if(!(r->refbehind.b[z] & bb)) break; } } ulong paint2(Reg *r, int bn) { Reg *r1; int z; ulong bb, vreg; z = bn/32; bb = 1L << (bn%32); vreg = regbits; if(!(r->act.b[z] & bb)) return vreg; for(;;) { if(!(r->refbehind.b[z] & bb)) break; r1 = r->p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) break; if(!(r1->act.b[z] & bb)) break; r = r1; } for(;;) { r->act.b[z] &= ~bb; vreg |= r->regu; if(r->refbehind.b[z] & bb) for(r1 = r->p2; r1 != R; r1 = r1->p2link) if(r1->refahead.b[z] & bb) vreg |= paint2(r1, bn); if(!(r->refahead.b[z] & bb)) break; r1 = r->s2; if(r1 != R) if(r1->refbehind.b[z] & bb) vreg |= paint2(r1, bn); r = r->s1; if(r == R) break; if(!(r->act.b[z] & bb)) break; if(!(r->refbehind.b[z] & bb)) break; } return vreg; } void paint3(Reg *r, int bn, ulong rb, int rn) { Reg *r1; Prog *p; int z; ulong bb; z = bn/32; bb = 1L << (bn%32); if(r->act.b[z] & bb) return; for(;;) { if(!(r->refbehind.b[z] & bb)) break; r1 = r->p1; if(r1 == R) break; if(!(r1->refahead.b[z] & bb)) break; if(r1->act.b[z] & bb) break; r = r1; } if(LOAD(r) & ~(r->set.b[z] & ~(r->use1.b[z]|r->use2.b[z])) & bb) addmove(r, bn, rn, 0); for(;;) { r->act.b[z] |= bb; p = r->prog; if(r->use1.b[z] & bb) { if(debug['R']) print("%P", p); addreg(&p->from, rn); if(debug['R']) print("%|.c%P\n", COL1, p); } if((r->use2.b[z]|r->set.b[z]) & bb) { if(debug['R']) print("%P", p); addreg(&p->to, rn); if(debug['R']) print("%|.c%P\n", COL1, p); } if(STORE(r) & r->regdiff.b[z] & bb) addmove(r, bn, rn, 1); r->regu |= rb; if(r->refbehind.b[z] & bb) for(r1 = r->p2; r1 != R; r1 = r1->p2link) if(r1->refahead.b[z] & bb) paint3(r1, bn, rb, rn); if(!(r->refahead.b[z] & bb)) break; r1 = r->s2; if(r1 != R) if(r1->refbehind.b[z] & bb) paint3(r1, bn, rb, rn); r = r->s1; if(r == R) break; if(r->act.b[z] & bb) break; if(!(r->refbehind.b[z] & bb)) break; } } void addreg(Adr *a, int rn) { int x; a->sym = 0; x = a->index; if(rn >= D_R0 && rn < D_R0+NREG) goto addr; if(x == (I_INDEX3|D_NONE)) { a->type = rn | I_INDIR; a->index = D_NONE; a->u0.s0.offset = a->u0.s0.displace; a->u0.s0.displace = 0; return; } if(x != D_NONE) { a->type = rn | I_INDIR; a->index += I_INDEX1 - I_INDEX2; a->u0.s0.offset = a->u0.s0.displace; a->u0.s0.displace = 0; return; } a->type = rn | (a->type & I_INDIR); return; addr: if(x == (I_INDEX3|D_NONE)) { a->type = D_NONE|I_INDIR; a->index += I_INDEX1 + rn - D_NONE - I_INDEX3; a->scale = 4; /* .L*1 */ a->u0.s0.offset = a->u0.s0.displace; a->u0.s0.displace = 0; return; } a->type = rn | (a->type & I_INDIR); } /* * bit reg * 0-7 R0-R7 * 8-15 A0-A7 * 16-23 F0-F7 */ ulong RtoB(int r) { if(r < 0 || r >= NREG) return 0; return 1L << (r + 0); } int BtoR(ulong b) { b &= 0x0000ffL; if(b == 0) return NREG; return bitno(b) - 0; } ulong AtoB(int a) { if(a < 0 || a >= NREG) return 0; return 1L << (a + NREG); } int BtoA(ulong b) { b &= 0x00ff00L; if(b == 0) return NREG; return bitno(b) - NREG; } ulong FtoB(int f) { if(f < 0 || f >= NREG) return 0; return 1L << (f + NREG+NREG); } int BtoF(ulong b) { b &= 0xff0000L; if(b == 0) return NREG; return bitno(b) - NREG-NREG; }