uint32 allreg(uint32 b, Rgn *r) { Var *v; int i; v = var + r->varno; r->regno = 0; switch(v->etype) { default: fatal("unknown etype %d/%E", bitno(b), v->etype); break; case TINT8: case TUINT8: case TINT16: case TUINT16: case TINT32: case TUINT32: case TINT: case TUINT: case TUINTPTR: case TBOOL: case TPTR32: i = BtoR(~b); if(i && r->cost >= 0) { r->regno = i; return RtoB(i); } break; case TFLOAT32: case TFLOAT64: i = BtoF(~b); if(i && r->cost >= 0) { r->regno = i+NREG; return FtoB(i); } break; case TINT64: case TUINT64: case TPTR64: case TINTER: case TSTRUCT: case TARRAY: break; } return 0; }
uint32 doregbits(int r) { uint32 b; b = 0; if(r >= D_INDIR) r -= D_INDIR; if(r >= D_AX && r <= D_R15) b |= RtoB(r); else if(r >= D_AL && r <= D_R15B) b |= RtoB(r-D_AL+D_AX); else if(r >= D_AH && r <= D_BH) b |= RtoB(r-D_AH+D_AX); else if(r >= D_X0 && r <= D_X0+15) b |= FtoB(r); return b; }
uint32 allreg(uint32 b, Rgn *r) { Var *v; int i; v = var + r->varno; r->regno = 0; switch(v->etype) { default: diag(Z, "unknown etype %d/%d", bitno(b), v->etype); break; case TCHAR: case TUCHAR: case TSHORT: case TUSHORT: case TINT: case TUINT: case TLONG: case TULONG: case TVLONG: case TUVLONG: case TIND: case TARRAY: i = BtoR(~b); if(i && r->cost > 0) { r->regno = i; return RtoB(i); } break; case TDOUBLE: case TFLOAT: i = BtoF(~b); if(i && r->cost > 0) { r->regno = i; return FtoB(i); } break; } return 0; }
void regopt(Prog *firstp) { Reg *r, *r1; Prog *p; int i, z, nr; uint32 vreg; Bits bit; if(first == 0) { fmtinstall('Q', Qconv); } fixjmp(firstp); first++; if(debug['K']) { if(first != 13) return; // debug['R'] = 2; // debug['P'] = 2; print("optimizing %S\n", curfn->nname->sym); } // count instructions nr = 0; for(p=firstp; p!=P; p=p->link) nr++; // if too big dont bother if(nr >= 10000) { // print("********** %S is too big (%d)\n", curfn->nname->sym, nr); return; } r1 = R; firstr = R; lastr = R; /* * control flow is more complicated in generated go code * than in generated c code. define pseudo-variables for * registers, so we have complete register usage information. */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); for(i=0; i<NREGVAR; i++) var[i].node = newname(lookup(regname[i])); regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); for(z=0; z<BITS; z++) { externs.b[z] = 0; params.b[z] = 0; consts.b[z] = 0; addrs.b[z] = 0; ovar.b[z] = 0; } // build list of return variables setoutvar(); /* * pass 1 * build aux data structure * allocate pcs * find use and set of variables */ nr = 0; for(p=firstp; p != P; p = p->link) { switch(p->as) { case ADATA: case AGLOBL: case ANAME: case ASIGNAME: continue; } r = rega(); nr++; if(firstr == R) { firstr = r; lastr = r; } else { lastr->link = r; r->p1 = lastr; lastr->s1 = r; lastr = r; } r->prog = p; p->regp = r; r1 = r->p1; if(r1 != R) { switch(r1->prog->as) { case ARET: case AB: case ARFE: r->p1 = R; r1->s1 = R; } } /* * left side always read */ bit = mkvar(r, &p->from); for(z=0; z<BITS; z++) r->use1.b[z] |= bit.b[z]; /* * middle always read when present */ if(p->reg != NREG) { if(p->from.type != D_FREG) r->use1.b[0] |= RtoB(p->reg); else r->use1.b[0] |= FtoB(p->reg); } /* * right side depends on opcode */ bit = mkvar(r, &p->to); if(bany(&bit)) switch(p->as) { default: yyerror("reg: unknown op: %A", p->as); break; /* * right side read */ case ATST: case ATEQ: case ACMP: case ACMN: case ACMPD: case ACMPF: rightread: for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; break; /* * right side read or read+write, depending on middle * ADD x, z => z += x * ADD x, y, z => z = x + y */ case AADD: case AAND: case AEOR: case ASUB: case ARSB: case AADC: case ASBC: case ARSC: case AORR: case ABIC: case ASLL: case ASRL: case ASRA: case AMUL: case AMULU: case ADIV: case AMOD: case AMODU: case ADIVU: if(p->reg != NREG) goto rightread; // fall through /* * right side read+write */ case AADDF: case AADDD: case ASUBF: case ASUBD: case AMULF: case AMULD: case ADIVF: case ADIVD: case AMULA: case AMULAL: case AMULALU: for(z=0; z<BITS; z++) { r->use2.b[z] |= bit.b[z]; r->set.b[z] |= bit.b[z]; } break; /* * right side write */ case ANOP: case AMOVB: case AMOVBU: case AMOVD: case AMOVDF: case AMOVDW: case AMOVF: case AMOVFW: case AMOVH: case AMOVHU: case AMOVW: case AMOVWD: case AMOVWF: case AMVN: case AMULL: case AMULLU: if((p->scond & C_SCOND) != C_SCOND_NONE) for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; break; /* * funny */ case ABL: setaddrs(bit); break; } if(p->as == AMOVM) { z = p->to.offset; if(p->from.type == D_CONST) z = p->from.offset; for(i=0; z; i++) { if(z&1) regbits |= RtoB(i); z >>= 1; } } }
Bits mkvar(Adr *a, int docon) { Var *v; int i, t, n, et, z; long o; Bits bit; Sym *s; t = a->type; if(t == D_REG && a->reg != NREG) regbits |= RtoB(a->reg); if(t == D_FREG && a->reg != NREG) regbits |= FtoB(a->reg); s = a->sym; o = a->offset; et = a->etype; if(s == S) { if(t != D_CONST || !docon || a->reg != NREG) goto none; et = TLONG; } if(t == D_CONST) { if(s == S && sval(o)) goto none; } n = a->name; v = var; for(i=0; i<nvar; i++) { if(s == v->sym) if(n == v->name) if(o == v->offset) goto out; v++; } if(s) if(s->name[0] == '.') goto none; if(nvar >= NVAR) { if(debug['w'] > 1 && 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 = et; v->name = n; if(debug['R']) print("bit=%2d et=%2d %D\n", i, et, a); out: bit = blsh(i); if(n == D_EXTERN || n == D_STATIC) for(z=0; z<BITS; z++) externs.b[z] |= bit.b[z]; if(n == D_PARAM) for(z=0; z<BITS; z++) params.b[z] |= bit.b[z]; if(v->etype != et || !(typechlpfd[et] || typev[et])) /* funny punning */ for(z=0; z<BITS; z++) addrs.b[z] |= bit.b[z]; if(t == D_CONST) { if(s == S) { for(z=0; z<BITS; z++) consts.b[z] |= bit.b[z]; return bit; } if(et != TARRAY) for(z=0; z<BITS; z++) addrs.b[z] |= bit.b[z]; for(z=0; z<BITS; z++) params.b[z] |= bit.b[z]; return bit; } if(t == D_OREG) return bit; none: return zbits; }
Bits mkvar(Reg *r, Adr *a) { Var *v; int i, t, n, et, z, w, flag; int32 o; Bits bit; Node *node; // mark registers used t = a->type; flag = 0; switch(t) { default: print("type %d %d %D\n", t, a->name, a); goto none; case D_NONE: case D_FCONST: case D_BRANCH: break; case D_REGREG: case D_REGREG2: bit = zbits; if(a->offset != NREG) bit.b[0] |= RtoB(a->offset); if(a->reg != NREG) bit.b[0] |= RtoB(a->reg); return bit; case D_CONST: case D_REG: case D_SHIFT: if(a->reg != NREG) { bit = zbits; bit.b[0] = RtoB(a->reg); return bit; } break; case D_OREG: if(a->reg != NREG) { if(a == &r->f.prog->from) r->use1.b[0] |= RtoB(a->reg); else r->use2.b[0] |= RtoB(a->reg); if(r->f.prog->scond & (C_PBIT|C_WBIT)) r->set.b[0] |= RtoB(a->reg); } break; case D_FREG: if(a->reg != NREG) { bit = zbits; bit.b[0] = FtoB(a->reg); return bit; } break; } switch(a->name) { default: goto none; case D_EXTERN: case D_STATIC: case D_AUTO: case D_PARAM: n = a->name; break; } node = a->node; if(node == N || node->op != ONAME || node->orig == N) goto none; node = node->orig; if(node->orig != node) fatal("%D: bad node", a); if(node->sym == S || node->sym->name[0] == '.') goto none; et = a->etype; o = a->offset; w = a->width; if(w < 0) fatal("bad width %d for %D", w, a); for(i=0; i<nvar; i++) { v = var+i; if(v->node == node && v->name == n) { if(v->offset == o) if(v->etype == et) if(v->width == w) if(!flag) return blsh(i); // if they overlap, disable both if(overlap(v->offset, v->width, o, w)) { v->addr = 1; flag = 1; } } } switch(et) { case 0: case TFUNC: goto none; } if(nvar >= NVAR) { if(debug['w'] > 1 && node) fatal("variable not optimized: %D", a); goto none; } i = nvar; nvar++; //print("var %d %E %D %S\n", i, et, a, s); v = var+i; v->offset = o; v->name = n; v->etype = et; v->width = w; v->addr = flag; // funny punning v->node = node; if(debug['R']) print("bit=%2d et=%2E w=%d+%d %#N %D flag=%d\n", i, et, o, w, node, a, v->addr); bit = blsh(i); if(n == D_EXTERN || n == D_STATIC) for(z=0; z<BITS; z++) externs.b[z] |= bit.b[z]; if(n == D_PARAM) for(z=0; z<BITS; z++) params.b[z] |= bit.b[z]; return bit; none: return zbits; }
void regopt(Prog *firstp) { Reg *r, *r1; Prog *p; Graph *g; int i, z; uint32 vreg; Bits bit; ProgInfo info; if(first) { fmtinstall('Q', Qconv); first = 0; } fixjmp(firstp); mergetemp(firstp); /* * control flow is more complicated in generated go code * than in generated c code. define pseudo-variables for * registers, so we have complete register usage information. */ nvar = NREGVAR; memset(var, 0, NREGVAR*sizeof var[0]); for(i=0; i<NREGVAR; i++) { if(regnodes[i] == N) regnodes[i] = newname(lookup(regname[i])); var[i].node = regnodes[i]; } regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); for(z=0; z<BITS; z++) { externs.b[z] = 0; params.b[z] = 0; consts.b[z] = 0; addrs.b[z] = 0; ovar.b[z] = 0; } // build list of return variables setoutvar(); /* * pass 1 * build aux data structure * allocate pcs * find use and set of variables */ g = flowstart(firstp, sizeof(Reg)); if(g == nil) return; firstr = (Reg*)g->start; for(r = firstr; r != R; r = (Reg*)r->f.link) { p = r->f.prog; proginfo(&info, p); // Avoid making variables for direct-called functions. if(p->as == ABL && p->to.type == D_EXTERN) continue; bit = mkvar(r, &p->from); if(info.flags & LeftRead) for(z=0; z<BITS; z++) r->use1.b[z] |= bit.b[z]; if(info.flags & LeftAddr) setaddrs(bit); if(info.flags & RegRead) { if(p->from.type != D_FREG) r->use1.b[0] |= RtoB(p->reg); else r->use1.b[0] |= FtoB(p->reg); } if(info.flags & (RightAddr | RightRead | RightWrite)) { bit = mkvar(r, &p->to); if(info.flags & RightAddr) setaddrs(bit); if(info.flags & RightRead) for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; if(info.flags & RightWrite) for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; } } if(firstr == R) return; for(i=0; i<nvar; i++) { Var *v = var+i; if(v->addr) { bit = blsh(i); for(z=0; z<BITS; z++) addrs.b[z] |= bit.b[z]; } if(debug['R'] && debug['v']) print("bit=%2d addr=%d et=%-6E w=%-2d s=%N + %lld\n", i, v->addr, v->etype, v->width, v->node, v->offset); } if(debug['R'] && debug['v']) dumpit("pass1", &firstr->f, 1); /* * pass 2 * find looping structure */ flowrpo(g); if(debug['R'] && debug['v']) dumpit("pass2", &firstr->f, 1); /* * pass 3 * iterate propagating usage * back until flow graph is complete */ loop1: change = 0; for(r = firstr; r != R; r = (Reg*)r->f.link) r->f.active = 0; for(r = firstr; r != R; r = (Reg*)r->f.link) if(r->f.prog->as == ARET) prop(r, zbits, zbits); loop11: /* pick up unreachable code */ i = 0; for(r = firstr; r != R; r = r1) { r1 = (Reg*)r->f.link; if(r1 && r1->f.active && !r->f.active) { prop(r, zbits, zbits); i = 1; } } if(i) goto loop11; if(change) goto loop1; if(debug['R'] && debug['v']) dumpit("pass3", &firstr->f, 1); /* * pass 4 * iterate propagating register/variable synchrony * forward until graph is complete */ loop2: change = 0; for(r = firstr; r != R; r = (Reg*)r->f.link) r->f.active = 0; synch(firstr, zbits); if(change) goto loop2; addsplits(); if(debug['R'] && debug['v']) dumpit("pass4", &firstr->f, 1); if(debug['R'] > 1) { print("\nprop structure:\n"); for(r = firstr; r != R; r = (Reg*)r->f.link) { print("%d:%P", r->f.loop, r->f.prog); for(z=0; z<BITS; z++) { bit.b[z] = r->set.b[z] | r->refahead.b[z] | r->calahead.b[z] | r->refbehind.b[z] | r->calbehind.b[z] | r->use1.b[z] | r->use2.b[z]; bit.b[z] &= ~addrs.b[z]; } if(bany(&bit)) { print("\t"); if(bany(&r->use1)) print(" u1=%Q", r->use1); if(bany(&r->use2)) print(" u2=%Q", r->use2); if(bany(&r->set)) print(" st=%Q", r->set); if(bany(&r->refahead)) print(" ra=%Q", r->refahead); if(bany(&r->calahead)) print(" ca=%Q", r->calahead); if(bany(&r->refbehind)) print(" rb=%Q", r->refbehind); if(bany(&r->calbehind)) print(" cb=%Q", r->calbehind); } print("\n"); } } /* * pass 4.5 * move register pseudo-variables into regu. */ for(r = firstr; r != R; r = (Reg*)r->f.link) { r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; r->set.b[0] &= ~REGBITS; r->use1.b[0] &= ~REGBITS; r->use2.b[0] &= ~REGBITS; r->refbehind.b[0] &= ~REGBITS; r->refahead.b[0] &= ~REGBITS; r->calbehind.b[0] &= ~REGBITS; r->calahead.b[0] &= ~REGBITS; r->regdiff.b[0] &= ~REGBITS; r->act.b[0] &= ~REGBITS; } if(debug['R'] && debug['v']) dumpit("pass4.5", &firstr->f, 1); /* * pass 5 * isolate regions * calculate costs (paint1) */ r = firstr; if(r) { for(z=0; z<BITS; z++) bit.b[z] = (r->refahead.b[z] | r->calahead.b[z]) & ~(externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]); if(bany(&bit) & !r->f.refset) { // should never happen - all variables are preset if(debug['w']) print("%L: used and not set: %Q\n", r->f.prog->lineno, bit); r->f.refset = 1; } } for(r = firstr; r != R; r = (Reg*)r->f.link) r->act = zbits; rgp = region; nregion = 0; for(r = firstr; r != R; r = (Reg*)r->f.link) { for(z=0; z<BITS; z++) bit.b[z] = r->set.b[z] & ~(r->refahead.b[z] | r->calahead.b[z] | addrs.b[z]); if(bany(&bit) && !r->f.refset) { if(debug['w']) print("%L: set and not used: %Q\n", r->f.prog->lineno, bit); r->f.refset = 1; excise(&r->f); } for(z=0; z<BITS; z++) bit.b[z] = LOAD(r) & ~(r->act.b[z] | addrs.b[z]); while(bany(&bit)) { i = bnum(bit); rgp->enter = r; rgp->varno = i; change = 0; if(debug['R'] > 1) print("\n"); paint1(r, i); bit.b[i/32] &= ~(1L<<(i%32)); if(change <= 0) { if(debug['R']) print("%L $%d: %Q\n", r->f.prog->lineno, change, blsh(i)); continue; } rgp->cost = change; nregion++; if(nregion >= NRGN) { if(debug['R'] > 1) print("too many regions\n"); goto brk; } rgp++; } } brk: qsort(region, nregion, sizeof(region[0]), rcmp); if(debug['R'] && debug['v']) dumpit("pass5", &firstr->f, 1); /* * pass 6 * determine used registers (paint2) * replace code (paint3) */ rgp = region; for(i=0; i<nregion; i++) { bit = blsh(rgp->varno); vreg = paint2(rgp->enter, rgp->varno); vreg = allreg(vreg, rgp); if(debug['R']) { if(rgp->regno >= NREG) print("%L $%d F%d: %Q\n", rgp->enter->f.prog->lineno, rgp->cost, rgp->regno-NREG, bit); else print("%L $%d R%d: %Q\n", rgp->enter->f.prog->lineno, rgp->cost, rgp->regno, bit); } if(rgp->regno != 0) paint3(rgp->enter, rgp->varno, vreg, rgp->regno); rgp++; } if(debug['R'] && debug['v']) dumpit("pass6", &firstr->f, 1); /* * pass 7 * peep-hole on basic block */ if(!debug['R'] || debug['P']) { peep(firstp); } if(debug['R'] && debug['v']) dumpit("pass7", &firstr->f, 1); /* * last pass * eliminate nops * free aux structures * adjust the stack pointer * MOVW.W R1,-12(R13) <<- start * MOVW R0,R1 * MOVW R1,8(R13) * MOVW $0,R1 * MOVW R1,4(R13) * BL ,runtime.newproc+0(SB) * MOVW &ft+-32(SP),R7 <<- adjust * MOVW &j+-40(SP),R6 <<- adjust * MOVW autotmp_0003+-24(SP),R5 <<- adjust * MOVW $12(R13),R13 <<- finish */ vreg = 0; for(p = firstp; p != P; p = p->link) { while(p->link != P && p->link->as == ANOP) p->link = p->link->link; if(p->to.type == D_BRANCH) while(p->to.u.branch != P && p->to.u.branch->as == ANOP) p->to.u.branch = p->to.u.branch->link; if(p->as == AMOVW && p->to.reg == 13) { if(p->scond & C_WBIT) { vreg = -p->to.offset; // in adjust region // print("%P adjusting %d\n", p, vreg); continue; } if(p->from.type == D_CONST && p->to.type == D_REG) { if(p->from.offset != vreg) print("in and out different\n"); // print("%P finish %d\n", p, vreg); vreg = 0; // done adjust region continue; } // print("%P %d %d from type\n", p, p->from.type, D_CONST); // print("%P %d %d to type\n\n", p, p->to.type, D_REG); } if(p->as == AMOVW && vreg != 0) { if(p->from.sym != S) if(p->from.name == D_AUTO || p->from.name == D_PARAM) { p->from.offset += vreg; // print("%P adjusting from %d %d\n", p, vreg, p->from.type); } if(p->to.sym != S) if(p->to.name == D_AUTO || p->to.name == D_PARAM) { p->to.offset += vreg; // print("%P adjusting to %d %d\n", p, vreg, p->from.type); } } } flowend(g); }