Reg* findinc(Reg *r, Reg *r2, Adr *v) { Reg *r1; Prog *p; for(r1=uniqs(r); r1!=R && r1!=r2; r=r1,r1=uniqs(r)) { if(uniqp(r1) != r) return R; switch(copyu(r1->prog, v, A)) { case 0: /* not touched */ continue; case 4: /* set and used */ p = r1->prog; if(p->as == AADD) if(p->from.type == D_CONST) if(p->from.offset > -4096 && p->from.offset < 4096) return r1; default: return R; } } return R; }
/* * findinc finds ADD instructions with a constant * argument which falls within the immed_12 range. */ static Flow* findinc(Flow *r, Flow *r2, Adr *v) { Flow *r1; Prog *p; for(r1=uniqs(r); r1!=nil && r1!=r2; r=r1,r1=uniqs(r)) { if(uniqp(r1) != r) return nil; switch(copyu(r1->prog, v, A)) { case 0: /* not touched */ continue; case 4: /* set and used */ p = r1->prog; if(p->as == AADD) if(isdconst(&p->from)) if(p->from.offset > -4096 && p->from.offset < 4096) return r1; default: return nil; } } return nil; }
/* * The idea is to remove redundant constants. * $c1->v1 * ($c1->v2 s/$c1/v1)* * set v1 return * The v1->v2 should be eliminated by copy propagation. */ void constprop(Adr *c1, Adr *v1, Reg *r) { Prog *p; if(debug['C']) print("constprop %D->%D\n", c1, v1); for(; r != R; r = r->s1) { p = r->prog; if(debug['C']) print("%P", p); if(uniqp(r) == R) { if(debug['C']) print("; merge; return\n"); return; } if(p->as == AMOVW && copyas(&p->from, c1)) { if(debug['C']) print("; sub%D/%D", &p->from, v1); p->from = *v1; } else if(copyu(p, v1, A) > 1) { if(debug['C']) print("; %Dset; return\n", v1); return; } if(debug['C']) print("\n"); if(r->s2) constprop(c1, v1, r->s2); } }
Reg* findpre(Reg *r, Adr *v) { Reg *r1; for(r1=uniqp(r); r1!=R; r=r1,r1=uniqp(r)) { if(uniqs(r1) != r) return R; switch(copyu(r1->prog, v, A)) { case 1: /* used */ case 2: /* read-alter-rewrite */ return R; case 3: /* set */ case 4: /* set and used */ return r1; } } return R; }
/* * findpre returns the last instruction mentioning v * before r. It must be a set, and there must be * a unique path from that instruction to r. */ static Flow* findpre(Flow *r, Adr *v) { Flow *r1; for(r1=uniqp(r); r1!=nil; r=r1,r1=uniqp(r)) { if(uniqs(r1) != r) return nil; switch(copyu(r1->prog, v, A)) { case 1: /* used */ case 2: /* read-alter-rewrite */ return nil; case 3: /* set */ case 4: /* set and used */ return r1; } } return nil; }
// is reg guaranteed to be truncated by a previous L instruction? static int prevl(Flow *r0, int reg) { Prog *p; Flow *r; ProgInfo info; for(r=uniqp(r0); r!=nil; r=uniqp(r)) { p = r->prog; if(p->to.type == reg) { proginfo(&info, p); if(info.flags & RightWrite) { if(info.flags & SizeL) return 1; return 0; } } } return 0; }
// is reg guaranteed to be truncated by a previous L instruction? static int prevl(Reg *r0, int reg) { Prog *p; Reg *r; for(r=uniqp(r0); r!=R; r=uniqp(r)) { p = r->prog; if(p->to.type == reg) { switch(p->as) { case AADDL: case AANDL: case ADECL: case ADIVL: case AIDIVL: case AIMULL: case AINCL: case AMOVL: case AMULL: case AORL: case ARCLL: case ARCRL: case AROLL: case ARORL: case ASALL: case ASARL: case ASHLL: case ASHRL: case ASUBL: case AXORL: return 1; } return 0; } } return 0; }
static void conprop(Reg *r0) { Reg *r; Prog *p, *p0; int t; Adr *v0; p0 = r0->prog; v0 = &p0->to; r = r0; loop: r = uniqs(r); if(r == R || r == r0) return; if(uniqp(r) == R) return; p = r->prog; t = copyu(p, v0, A); switch(t) { case 0: // miss case 1: // use goto loop; case 2: // rar case 4: // use and set break; case 3: // set if(p->as == p0->as) if(p->from.type == p0->from.type) if(p->from.sym == p0->from.sym) if(p->from.offset == p0->from.offset) if(p->from.scale == p0->from.scale) if(p->from.dval == p0->from.dval) if(p->from.index == p0->from.index) { excise(r); t++; goto loop; } break; } }
static void conprop(Flow *r0) { Flow *r; Prog *p, *p0; int t; Adr *v0; p0 = r0->prog; v0 = &p0->to; r = r0; loop: r = uniqs(r); if(r == nil || r == r0) return; if(uniqp(r) == nil) return; p = r->prog; t = copyu(p, v0, nil); switch(t) { case 0: // miss case 1: // use goto loop; case 2: // rar case 4: // use and set break; case 3: // set if(p->as == p0->as) if(p->from.type == p0->from.type) if(p->from.node == p0->from.node) if(p->from.offset == p0->from.offset) if(p->from.scale == p0->from.scale) if(p->from.type == D_FCONST && p->from.u.dval == p0->from.u.dval) if(p->from.index == p0->from.index) { excise(r); goto loop; } break; } }
static void pushback(Reg *r0) { Reg *r, *b; Prog *p0, *p, t; b = R; p0 = r0->prog; for(r=uniqp(r0); r!=R && uniqs(r)!=R; r=uniqp(r)) { p = r->prog; if(p->as != ANOP) { if(!regconsttyp(&p->from) || !regtyp(&p->to)) break; if(copyu(p, &p0->to, A) || copyu(p0, &p->to, A)) break; } if(p->as == ACALL) break; b = r; } if(b == R) { if(debug['v']) { print("no pushback: %P\n", r0->prog); if(r) print("\t%P [%d]\n", r->prog, uniqs(r)!=R); } return; } if(debug['v']) { print("pushback\n"); for(r=b;; r=r->link) { print("\t%P\n", r->prog); if(r == r0) break; } } t = *r0->prog; for(r=uniqp(r0);; r=uniqp(r)) { p0 = r->link->prog; p = r->prog; p0->as = p->as; p0->lineno = p->lineno; p0->from = p->from; p0->to = p->to; if(r == b) break; } p0 = r->prog; p0->as = t.as; p0->lineno = t.lineno; p0->from = t.from; p0->to = t.to; if(debug['v']) { print("\tafter\n"); for(r=b;; r=r->link) { print("\t%P\n", r->prog); if(r == r0) break; } } }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ static int subprop(Flow *r0) { Prog *p; Adr *v1, *v2; Flow *r; int t; ProgInfo info; p = r0->prog; v1 = &p->from; if(!regtyp(v1)) return 0; v2 = &p->to; if(!regtyp(v2)) return 0; for(r=uniqp(r0); r!=nil; r=uniqp(r)) { if(uniqs(r) == nil) break; p = r->prog; proginfo(&info, p); if(info.flags & Call) return 0; if((info.flags & CanRegRead) && p->to.type == D_REG) { info.flags |= RegRead; info.flags &= ~(CanRegRead | RightRead); p->reg = p->to.reg; } switch(p->as) { case AMULLU: case AMULA: case AMVN: return 0; } if((info.flags & (RightRead|RightWrite)) == RightWrite) { if(p->to.type == v1->type) if(p->to.reg == v1->reg) if(p->scond == C_SCOND_NONE) goto gotit; } if(copyau(&p->from, v2) || copyau1(p, v2) || copyau(&p->to, v2)) break; if(copysub(&p->from, v1, v2, 0) || copysub1(p, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) break; } return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub1(p, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->reg; v1->reg = v2->reg; v2->reg = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ static int subprop(Flow *r0) { Prog *p; ProgInfo info; Adr *v1, *v2; Flow *r; int t; if(debug['P'] && debug['v']) print("subprop %P\n", r0->prog); p = r0->prog; v1 = &p->from; if(!regtyp(v1)) { if(debug['P'] && debug['v']) print("\tnot regtype %D; return 0\n", v1); return 0; } v2 = &p->to; if(!regtyp(v2)) { if(debug['P'] && debug['v']) print("\tnot regtype %D; return 0\n", v2); return 0; } for(r=uniqp(r0); r!=nil; r=uniqp(r)) { if(debug['P'] && debug['v']) print("\t? %P\n", r->prog); if(uniqs(r) == nil) { if(debug['P'] && debug['v']) print("\tno unique successor\n"); break; } p = r->prog; proginfo(&info, p); if(info.flags & Call) { if(debug['P'] && debug['v']) print("\tfound %P; return 0\n", p); return 0; } if(info.reguse | info.regset) { if(debug['P'] && debug['v']) print("\tfound %P; return 0\n", p); return 0; } if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type) goto gotit; if(copyau(&p->from, v2) || copyau(&p->to, v2)) { if(debug['P'] && debug['v']) print("\tcopyau %D failed\n", v2); break; } if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) { if(debug['P'] && debug['v']) print("\tcopysub failed\n"); break; } } if(debug['P'] && debug['v']) print("\tran off end; return 0\n"); return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->type; v1->type = v2->type; v2->type = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
void peep(void) { Reg *r, *r1, *r2; Prog *p, *p1; int t; /* * complete R structure */ t = 0; for(r=firstr; r!=R; r=r1) { r1 = r->link; if(r1 == R) break; p = r->prog->link; while(p != r1->prog) switch(p->as) { default: r2 = rega(); r->link = r2; r2->link = r1; r2->prog = p; r2->p1 = r; r->s1 = r2; r2->s1 = r1; r1->p1 = r2; r = r2; t++; case ADATA: case AGLOBL: case ANAME: case ASIGNAME: p = p->link; } } loop1: t = 0; for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->as == AMOVW || p->as == AFMOVS || p->as == AFMOVD) if(regtyp(&p->to)) { if(regtyp(&p->from)) if(p->from.type == p->to.type) { if(copyprop(r)) { excise(r); t++; } else if(subprop(r) && copyprop(r)) { excise(r); t++; } } if(regzer(&p->from)) if(p->to.type == D_REG) { p->from.type = D_REG; p->from.reg = REGZERO; if(copyprop(r)) { excise(r); t++; } else if(subprop(r) && copyprop(r)) { excise(r); t++; } } } } if(t) goto loop1; /* * look for MOVB x,R; MOVB R,R */ for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { default: continue; case AMOVH: case AMOVHZ: case AMOVB: case AMOVBZ: if(p->to.type != D_REG) continue; break; } r1 = r->link; if(r1 == R) continue; p1 = r1->prog; if(p1->as != p->as) continue; if(p1->from.type != D_REG || p1->from.reg != p->to.reg) continue; if(p1->to.type != D_REG || p1->to.reg != p->to.reg) continue; excise(r1); } if(debug['Q'] > 1) return; /* allow following code improvement to be suppressed */ /* * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R * when OP can set condition codes correctly */ for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { case ACMP: if(!regzer(&p->to)) continue; r1 = r->s1; if(r1 == R) continue; switch(r1->prog->as) { default: continue; case ABCL: case ABC: /* the conditions can be complex and these are currently little used */ continue; case ABEQ: case ABGE: case ABGT: case ABLE: case ABLT: case ABNE: case ABVC: case ABVS: break; } r1 = r; do r1 = uniqp(r1); while (r1 != R && r1->prog->as == ANOP); if(r1 == R) continue; p1 = r1->prog; if(p1->to.type != D_REG || p1->to.reg != p->from.reg) continue; switch(p1->as) { case ASUB: case AADD: case AXOR: case AOR: /* irregular instructions */ if(p1->from.type == D_CONST) continue; break; } switch(p1->as) { default: continue; case AMOVW: if(p1->from.type != D_REG) continue; continue; case AANDCC: case AANDNCC: case AORCC: case AORNCC: case AXORCC: case ASUBCC: case AADDCC: t = p1->as; break; /* don't deal with floating point instructions for now */ /* case AFABS: t = AFABSCC; break; case AFADD: t = AFADDCC; break; case AFADDS: t = AFADDSCC; break; case AFCTIW: t = AFCTIWCC; break; case AFCTIWZ: t = AFCTIWZCC; break; case AFDIV: t = AFDIVCC; break; case AFDIVS: t = AFDIVSCC; break; case AFMADD: t = AFMADDCC; break; case AFMADDS: t = AFMADDSCC; break; case AFMOVD: t = AFMOVDCC; break; case AFMSUB: t = AFMSUBCC; break; case AFMSUBS: t = AFMSUBSCC; break; case AFMUL: t = AFMULCC; break; case AFMULS: t = AFMULSCC; break; case AFNABS: t = AFNABSCC; break; case AFNEG: t = AFNEGCC; break; case AFNMADD: t = AFNMADDCC; break; case AFNMADDS: t = AFNMADDSCC; break; case AFNMSUB: t = AFNMSUBCC; break; case AFNMSUBS: t = AFNMSUBSCC; break; case AFRSP: t = AFRSPCC; break; case AFSUB: t = AFSUBCC; break; case AFSUBS: t = AFSUBSCC; break; case ACNTLZW: t = ACNTLZWCC; break; case AMTFSB0: t = AMTFSB0CC; break; case AMTFSB1: t = AMTFSB1CC; break; */ case AADD: t = AADDCC; break; case AADDV: t = AADDVCC; break; case AADDC: t = AADDCCC; break; case AADDCV: t = AADDCVCC; break; case AADDME: t = AADDMECC; break; case AADDMEV: t = AADDMEVCC; break; case AADDE: t = AADDECC; break; case AADDEV: t = AADDEVCC; break; case AADDZE: t = AADDZECC; break; case AADDZEV: t = AADDZEVCC; break; case AAND: t = AANDCC; break; case AANDN: t = AANDNCC; break; case ADIVW: t = ADIVWCC; break; case ADIVWV: t = ADIVWVCC; break; case ADIVWU: t = ADIVWUCC; break; case ADIVWUV: t = ADIVWUVCC; break; case AEQV: t = AEQVCC; break; case AEXTSB: t = AEXTSBCC; break; case AEXTSH: t = AEXTSHCC; break; case AMULHW: t = AMULHWCC; break; case AMULHWU: t = AMULHWUCC; break; case AMULLW: t = AMULLWCC; break; case AMULLWV: t = AMULLWVCC; break; case ANAND: t = ANANDCC; break; case ANEG: t = ANEGCC; break; case ANEGV: t = ANEGVCC; break; case ANOR: t = ANORCC; break; case AOR: t = AORCC; break; case AORN: t = AORNCC; break; case AREM: t = AREMCC; break; case AREMV: t = AREMVCC; break; case AREMU: t = AREMUCC; break; case AREMUV: t = AREMUVCC; break; case ARLWMI: t = ARLWMICC; break; case ARLWNM: t = ARLWNMCC; break; case ASLW: t = ASLWCC; break; case ASRAW: t = ASRAWCC; break; case ASRW: t = ASRWCC; break; case ASUB: t = ASUBCC; break; case ASUBV: t = ASUBVCC; break; case ASUBC: t = ASUBCCC; break; case ASUBCV: t = ASUBCVCC; break; case ASUBME: t = ASUBMECC; break; case ASUBMEV: t = ASUBMEVCC; break; case ASUBE: t = ASUBECC; break; case ASUBEV: t = ASUBEVCC; break; case ASUBZE: t = ASUBZECC; break; case ASUBZEV: t = ASUBZEVCC; break; case AXOR: t = AXORCC; break; break; } if(debug['Q']) print("cmp %P; %P -> ", p1, p); p1->as = t; if(debug['Q']) print("%P\n", p1); excise(r); continue; } } }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ int subprop(Reg *r0) { Prog *p; Adr *v1, *v2; Reg *r; int t; p = r0->prog; v1 = &p->from; if(!regtyp(v1)) return 0; v2 = &p->to; if(!regtyp(v2)) return 0; for(r=uniqp(r0); r!=R; r=uniqp(r)) { if(uniqs(r) == R) break; p = r->prog; switch(p->as) { case AJMPL: return 0; case AADD: case ASUB: case ASLL: case ASRL: case ASRA: case AOR: case AAND: case AXOR: case AMUL: case ADIV: case ADIVL: case AMOD: case AMODL: case AFADDD: case AFADDF: case AFSUBD: case AFSUBF: case AFMULD: case AFMULF: case AFDIVD: case AFDIVF: if(p->to.type == v1->type) if(p->to.reg == v1->reg) { if(p->reg == NREG) p->reg = p->to.reg; goto gotit; } break; case AFMOVF: case AFMOVD: case AMOVW: if(p->to.type == v1->type) if(p->to.reg == v1->reg) goto gotit; break; } if(copyau(&p->from, v2) || copyau1(p, v2) || copyau(&p->to, v2)) break; if(copysub(&p->from, v1, v2, 0) || copysub1(p, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) break; } return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub1(p, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->reg; v1->reg = v2->reg; v2->reg = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
int shiftprop(Reg *r) { Reg *r1; Prog *p, *p1, *p2; int n, o; Adr a; p = r->prog; if(p->to.type != D_REG) FAIL("BOTCH: result not reg"); n = p->to.reg; a = zprog.from; if(p->reg != NREG && p->reg != p->to.reg) { a.type = D_REG; a.reg = p->reg; } if(debug['H']) print("shiftprop\n%P", p); r1 = r; for(;;) { /* find first use of shift result; abort if shift operands or result are changed */ r1 = uniqs(r1); if(r1 == R) FAIL("branch"); if(uniqp(r1) == R) FAIL("merge"); p1 = r1->prog; if(debug['H']) print("\n%P", p1); switch(copyu(p1, &p->to, A)) { case 0: /* not used or set */ if((p->from.type == D_REG && copyu(p1, &p->from, A) > 1) || (a.type == D_REG && copyu(p1, &a, A) > 1)) FAIL("args modified"); continue; case 3: /* set, not used */ FAIL("BOTCH: noref"); } break; } /* check whether substitution can be done */ switch(p1->as) { default: FAIL("non-dpi"); case AAND: case AEOR: case AADD: case AADC: case AORR: case ASUB: case ARSB: case ASBC: case ARSC: if(p1->reg == n || (p1->reg == NREG && p1->to.type == D_REG && p1->to.reg == n)) { if(p1->from.type != D_REG) FAIL("can't swap"); p1->reg = p1->from.reg; p1->from.reg = n; switch(p1->as) { case ASUB: p1->as = ARSB; break; case ARSB: p1->as = ASUB; break; case ASBC: p1->as = ARSC; break; case ARSC: p1->as = ASBC; break; } if(debug['H']) print("\t=>%P", p1); } case ABIC: case ACMP: case ACMN: if(p1->reg == n) FAIL("can't swap"); if(p1->reg == NREG && p1->to.reg == n) FAIL("shift result used twice"); case AMVN: if(p1->from.type == D_SHIFT) FAIL("shift result used in shift"); if(p1->from.type != D_REG || p1->from.reg != n) FAIL("BOTCH: where is it used?"); break; } /* check whether shift result is used subsequently */ p2 = p1; if(p1->to.reg != n) for (;;) { r1 = uniqs(r1); if(r1 == R) FAIL("inconclusive"); p1 = r1->prog; if(debug['H']) print("\n%P", p1); switch(copyu(p1, &p->to, A)) { case 0: /* not used or set */ continue; case 3: /* set, not used */ break; default:/* used */ FAIL("reused"); } break; } /* make the substitution */ p2->from.type = D_SHIFT; p2->from.reg = NREG; o = p->reg; if(o == NREG) o = p->to.reg; switch(p->from.type){ case D_CONST: o |= (p->from.offset&0x1f)<<7; break; case D_REG: o |= (1<<4) | (p->from.reg<<8); break; } switch(p->as){ case ASLL: o |= 0<<5; break; case ASRL: o |= 1<<5; break; case ASRA: o |= 2<<5; break; } p2->from.offset = o; if(debug['H']) print("\t=>%P\tSUCCEED\n", p2); return 1; }
void mergetemp(Prog *firstp) { int i, j, nvar, ninuse, nfree, nkill; TempVar *var, *v, *v1, **bystart, **inuse; TempFlow *r; NodeList *l, **lp; Node *n; Prog *p, *p1; Type *t; ProgInfo info, info1; int32 gen; Graph *g; enum { Debug = 0 }; g = flowstart(firstp, sizeof(TempFlow)); if(g == nil) return; // Build list of all mergeable variables. nvar = 0; for(l = curfn->dcl; l != nil; l = l->next) if(canmerge(l->n)) nvar++; var = calloc(nvar*sizeof var[0], 1); nvar = 0; for(l = curfn->dcl; l != nil; l = l->next) { n = l->n; if(canmerge(n)) { v = &var[nvar++]; n->opt = v; v->node = n; } } // Build list of uses. // We assume that the earliest reference to a temporary is its definition. // This is not true of variables in general but our temporaries are all // single-use (that's why we have so many!). for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { p = r->f.prog; proginfo(&info, p); if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt) fatal("double node %P", p); if((n = p->from.node) != N && (v = n->opt) != nil || (n = p->to.node) != N && (v = n->opt) != nil) { if(v->def == nil) v->def = r; r->uselink = v->use; v->use = r; if(n == p->from.node && (info.flags & LeftAddr)) v->addr = 1; } } if(Debug > 1) dumpit("before", g->start, 0); nkill = 0; // Special case. for(v = var; v < var+nvar; v++) { if(v->addr) continue; // Used in only one instruction, which had better be a write. if((r = v->use) != nil && r->uselink == nil) { p = r->f.prog; proginfo(&info, p); if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) { p->as = ANOP; p->to = zprog.to; v->removed = 1; if(Debug) print("drop write-only %S\n", v->node->sym); } else fatal("temp used and not set: %P", p); nkill++; continue; } // Written in one instruction, read in the next, otherwise unused, // no jumps to the next instruction. Happens mainly in 386 compiler. if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) { p = r->f.prog; proginfo(&info, p); p1 = r->f.link->prog; proginfo(&info1, p1); enum { SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD, }; if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) && !((info.flags|info1.flags) & (LeftAddr|RightAddr)) && (info.flags & SizeAny) == (info1.flags & SizeAny)) { p1->from = p->from; excise(&r->f); v->removed = 1; if(Debug) print("drop immediate-use %S\n", v->node->sym); } nkill++; continue; } } // Traverse live range of each variable to set start, end. // Each flood uses a new value of gen so that we don't have // to clear all the r->f.active words after each variable. gen = 0; for(v = var; v < var+nvar; v++) { gen++; for(r = v->use; r != nil; r = r->uselink) mergewalk(v, r, gen); } // Sort variables by start. bystart = malloc(nvar*sizeof bystart[0]); for(i=0; i<nvar; i++) bystart[i] = &var[i]; qsort(bystart, nvar, sizeof bystart[0], startcmp); // List of in-use variables, sorted by end, so that the ones that // will last the longest are the earliest ones in the array. // The tail inuse[nfree:] holds no-longer-used variables. // In theory we should use a sorted tree so that insertions are // guaranteed O(log n) and then the loop is guaranteed O(n log n). // In practice, it doesn't really matter. inuse = malloc(nvar*sizeof inuse[0]); ninuse = 0; nfree = nvar; for(i=0; i<nvar; i++) { v = bystart[i]; if(v->addr || v->removed) continue; // Expire no longer in use. while(ninuse > 0 && inuse[ninuse-1]->end < v->start) { v1 = inuse[--ninuse]; inuse[--nfree] = v1; } // Find old temp to reuse if possible. t = v->node->type; for(j=nfree; j<nvar; j++) { v1 = inuse[j]; if(eqtype(t, v1->node->type)) { inuse[j] = inuse[nfree++]; if(v1->merge) v->merge = v1->merge; else v->merge = v1; nkill++; break; } } // Sort v into inuse. j = ninuse++; while(j > 0 && inuse[j-1]->end < v->end) { inuse[j] = inuse[j-1]; j--; } inuse[j] = v; } if(Debug) { print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill); for(v=var; v<var+nvar; v++) { print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end); if(v->addr) print(" addr=1"); if(v->removed) print(" dead=1"); if(v->merge) print(" merge %#N", v->merge->node); if(v->start == v->end) print(" %P", v->def->f.prog); print("\n"); } if(Debug > 1) dumpit("after", g->start, 0); } // Update node references to use merged temporaries. for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) { p = r->f.prog; if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil) p->from.node = v->merge->node; if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil) p->to.node = v->merge->node; } // Delete merged nodes from declaration list. for(lp = &curfn->dcl; (l = *lp); ) { curfn->dcl->end = l; n = l->n; v = n->opt; if(v && (v->merge || v->removed)) { *lp = l->next; continue; } lp = &l->next; } // Clear aux structures. for(v=var; v<var+nvar; v++) v->node->opt = nil; free(var); free(bystart); free(inuse); flowend(g); }
static void nilwalkback(NilFlow *rcheck) { Prog *p; ProgInfo info; NilFlow *r; for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) { p = r->f.prog; proginfo(&info, p); if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) { // Found initialization of value we're checking for nil. // without first finding the check, so this one is unchecked. return; } if(r != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) { rcheck->kill = 1; return; } } // Here is a more complex version that scans backward across branches. // It assumes rcheck->kill = 1 has been set on entry, and its job is to find a reason // to keep the check (setting rcheck->kill = 0). // It doesn't handle copying of aggregates as well as I would like, // nor variables with their address taken, // and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. /* for(r1 = r0; r1 != nil; r1 = (NilFlow*)r1->f.p1) { if(r1->f.active == gen) break; r1->f.active = gen; p = r1->f.prog; // If same check, stop this loop but still check // alternate predecessors up to this point. if(r1 != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) break; proginfo(&info, p); if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) { // Found initialization of value we're checking for nil. // without first finding the check, so this one is unchecked. rcheck->kill = 0; return; } if(r1->f.p1 == nil && r1->f.p2 == nil) { print("lost pred for %P\n", rcheck->f.prog); for(r1=r0; r1!=nil; r1=(NilFlow*)r1->f.p1) { proginfo(&info, r1->f.prog); print("\t%P %d %d %D %D\n", r1->f.prog, info.flags&RightWrite, sameaddr(&r1->f.prog->to, &rcheck->f.prog->from), &r1->f.prog->to, &rcheck->f.prog->from); } fatal("lost pred trail"); } } for(r = r0; r != r1; r = (NilFlow*)r->f.p1) for(r2 = (NilFlow*)r->f.p2; r2 != nil; r2 = (NilFlow*)r2->f.p2link) nilwalkback(rcheck, r2, gen); */ }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ int subprop(Reg *r0) { Prog *p; Adr *v1, *v2; Reg *r; int t; p = r0->prog; v1 = &p->from; if(!regtyp(v1)) return 0; v2 = &p->to; if(!regtyp(v2)) return 0; for(r=uniqp(r0); r!=R; r=uniqp(r)) { if(uniqs(r) == R) break; p = r->prog; switch(p->as) { case ABL: return 0; case AMULLU: case AMULA: case AMVN: return 0; case ACMN: case AADD: case ASUB: case ASBC: case ARSB: case ASLL: case ASRL: case ASRA: case AORR: case AAND: case AEOR: case AMUL: case AMULU: case ADIV: case ADIVU: case AMOD: case AMODU: case AADDD: case AADDF: case ASUBD: case ASUBF: case AMULD: case AMULF: case ADIVD: case ADIVF: if(p->to.type == v1->type) if(p->to.reg == v1->reg) if(p->scond == C_SCOND_NONE) { if(p->reg == NREG) p->reg = p->to.reg; goto gotit; } break; case AMOVF: case AMOVD: case AMOVW: if(p->to.type == v1->type) if(p->to.reg == v1->reg) if(p->scond == C_SCOND_NONE) goto gotit; break; case AMOVM: t = 1<<v2->reg; if((p->from.type == D_CONST && (p->from.offset&t)) || (p->to.type == D_CONST && (p->to.offset&t))) return 0; break; } if(copyau(&p->from, v2) || copyau1(p, v2) || copyau(&p->to, v2)) break; if(copysub(&p->from, v1, v2, 0) || copysub1(p, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) break; } return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub1(p, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->reg; v1->reg = v2->reg; v2->reg = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ int subprop(Reg *r0) { Prog *p; Adr *v1, *v2; Reg *r; int t; if(debug['P'] && debug['v']) print("subprop %P\n", r0->prog); p = r0->prog; v1 = &p->from; if(!regtyp(v1)) { if(debug['P'] && debug['v']) print("\tnot regtype %D; return 0\n", v1); return 0; } v2 = &p->to; if(!regtyp(v2)) { if(debug['P'] && debug['v']) print("\tnot regtype %D; return 0\n", v2); return 0; } for(r=uniqp(r0); r!=R; r=uniqp(r)) { if(debug['P'] && debug['v']) print("\t? %P\n", r->prog); if(uniqs(r) == R) { if(debug['P'] && debug['v']) print("\tno unique successor\n"); break; } p = r->prog; switch(p->as) { case ACALL: if(debug['P'] && debug['v']) print("\tfound %P; return 0\n", p); return 0; case AIMULL: case AIMULQ: case AIMULW: if(p->to.type != D_NONE) break; goto giveup; case ARCLB: case ARCLL: case ARCLQ: case ARCLW: case ARCRB: case ARCRL: case ARCRQ: case ARCRW: case AROLB: case AROLL: case AROLQ: case AROLW: case ARORB: case ARORL: case ARORQ: case ARORW: case ASALB: case ASALL: case ASALQ: case ASALW: case ASARB: case ASARL: case ASARQ: case ASARW: case ASHLB: case ASHLL: case ASHLQ: case ASHLW: case ASHRB: case ASHRL: case ASHRQ: case ASHRW: if(p->from.type == D_CONST) break; goto giveup; case ADIVB: case ADIVL: case ADIVQ: case ADIVW: case AIDIVB: case AIDIVL: case AIDIVQ: case AIDIVW: case AIMULB: case AMULB: case AMULL: case AMULQ: case AMULW: case AREP: case AREPN: case ACWD: case ACDQ: case ACQO: case ASTOSB: case ASTOSL: case ASTOSQ: case AMOVSB: case AMOVSL: case AMOVSQ: giveup: if(debug['P'] && debug['v']) print("\tfound %P; return 0\n", p); return 0; case AMOVL: case AMOVQ: case AMOVSS: case AMOVSD: if(p->to.type == v1->type) goto gotit; break; } if(copyau(&p->from, v2) || copyau(&p->to, v2)) { if(debug['P'] && debug['v']) print("\tcopyau %D failed\n", v2); break; } if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) { if(debug['P'] && debug['v']) print("\tcopysub failed\n"); break; } } if(debug['P'] && debug['v']) print("\tran off end; return 0\n"); return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->type; v1->type = v2->type; v2->type = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ int subprop(Reg *r0) { Prog *p; Adr *v1, *v2; Reg *r; int t; p = r0->prog; v1 = &p->from; if(!regtyp(v1)) return 0; v2 = &p->to; if(!regtyp(v2)) return 0; for(r=uniqp(r0); r!=R; r=uniqp(r)) { if(uniqs(r) == R) break; p = r->prog; switch(p->as) { case ACALL: return 0; case AIMULL: case AIMULQ: case AIMULW: if(p->to.type != D_NONE) break; case ADIVB: case ADIVL: case ADIVQ: case ADIVW: case AIDIVB: case AIDIVL: case AIDIVQ: case AIDIVW: case AIMULB: case AMULB: case AMULL: case AMULQ: case AMULW: case ARCLB: case ARCLL: case ARCLQ: case ARCLW: case ARCRB: case ARCRL: case ARCRQ: case ARCRW: case AROLB: case AROLL: case AROLQ: case AROLW: case ARORB: case ARORL: case ARORQ: case ARORW: case ASALB: case ASALL: case ASALQ: case ASALW: case ASARB: case ASARL: case ASARQ: case ASARW: case ASHLB: case ASHLL: case ASHLQ: case ASHLW: case ASHRB: case ASHRL: case ASHRQ: case ASHRW: case AREP: case AREPN: case ACWD: case ACDQ: case ACQO: case ASTOSB: case ASTOSL: case ASTOSQ: case AMOVSB: case AMOVSL: case AMOVSQ: return 0; case AMOVL: case AMOVQ: if(p->to.type == v1->type) goto gotit; break; } if(copyau(&p->from, v2) || copyau(&p->to, v2)) break; if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) break; } return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->type; v1->type = v2->type; v2->type = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
/* * the idea is to substitute * one register for another * from one MOV to another * MOV a, R0 * ADD b, R0 / no use of R1 * MOV R0, R1 * would be converted to * MOV a, R1 * ADD b, R1 * MOV R1, R0 * hopefully, then the former or latter MOV * will be eliminated by copy propagation. */ static int subprop(Flow *r0) { Prog *p; Adr *v1, *v2; Flow *r; int t; ProgInfo info; p = r0->prog; v1 = &p->from; if(!regtyp(v1)) return 0; v2 = &p->to; if(!regtyp(v2)) return 0; for(r=uniqp(r0); r!=nil; r=uniqp(r)) { if(debug['P'] && debug['v']) print("\t? %P\n", r->prog); if(uniqs(r) == nil) break; p = r->prog; proginfo(&info, p); if(info.flags & Call) return 0; if(info.reguse | info.regset) return 0; if((info.flags & Move) && (info.flags & (SizeL|SizeQ|SizeF|SizeD)) && p->to.type == v1->type) goto gotit; if(copyau(&p->from, v2) || copyau(&p->to, v2)) break; if(copysub(&p->from, v1, v2, 0) || copysub(&p->to, v1, v2, 0)) break; } return 0; gotit: copysub(&p->to, v1, v2, 1); if(debug['P']) { print("gotit: %D->%D\n%P", v1, v2, r->prog); if(p->from.type == v2->type) print(" excise"); print("\n"); } for(r=uniqs(r); r!=r0; r=uniqs(r)) { p = r->prog; copysub(&p->from, v1, v2, 1); copysub(&p->to, v1, v2, 1); if(debug['P']) print("%P\n", r->prog); } t = v1->type; v1->type = v2->type; v2->type = t; if(debug['P']) print("%P last\n", r->prog); return 1; }
int copy1(Adr *v1, Adr *v2, Reg *r, int f) { int t; Prog *p; if(r->active) { if(debug['P']) print("act set; return 1\n"); return 1; } r->active = 1; if(debug['P']) print("copy %D->%D f=%d\n", v1, v2, f); for(; r != R; r = r->s1) { p = r->prog; if(debug['P']) print("%P", p); if(!f && uniqp(r) == R) { f = 1; if(debug['P']) print("; merge; f=%d", f); } t = copyu(p, v2, A); switch(t) { case 2: /* rar, cant split */ if(debug['P']) print("; %D rar; return 0\n", v2); return 0; case 3: /* set */ if(debug['P']) print("; %D set; return 1\n", v2); return 1; case 1: /* used, substitute */ case 4: /* use and set */ if(f) { if(!debug['P']) return 0; if(t == 4) print("; %D used+set and f=%d; return 0\n", v2, f); else print("; %D used and f=%d; return 0\n", v2, f); return 0; } if(copyu(p, v2, v1)) { if(debug['P']) print("; sub fail; return 0\n"); return 0; } if(debug['P']) print("; sub %D/%D", v2, v1); if(t == 4) { if(debug['P']) print("; %D used+set; return 1\n", v2); return 1; } break; } if(!f) { t = copyu(p, v1, A); if(!f && (t == 2 || t == 3 || t == 4)) { f = 1; if(debug['P']) print("; %D set and !f; f=%d", v1, f); } } if(debug['P']) print("\n"); if(r->s2) if(!copy1(v1, v2, r->s2, f)) return 0; } return 1; }
void peep(void) { Reg *r, *r1, *r2; Prog *p, *p1; int t; /* * complete R structure */ t = 0; for(r=firstr; r!=R; r=r1) { r1 = r->link; if(r1 == R) break; p = r->prog->link; while(p != r1->prog) switch(p->as) { default: r2 = rega(); r->link = r2; r2->link = r1; r2->prog = p; r2->p1 = r; r->s1 = r2; r2->s1 = r1; r1->p1 = r2; r = r2; t++; case ADATA: case AGLOBL: case ANAME: case ASIGNAME: p = p->link; } } loop1: t = 0; for(r=firstr; r!=R; r=r->link) { p = r->prog; if(p->as == ASLL || p->as == ASRL || p->as == ASRA) { /* * elide shift into D_SHIFT operand of subsequent instruction */ if(shiftprop(r)) { excise(r); t++; } } if(p->as == AMOVW || p->as == AMOVF || p->as == AMOVD) if(regtyp(&p->to)) { if(p->from.type == D_CONST) constprop(&p->from, &p->to, r->s1); else if(regtyp(&p->from)) if(p->from.type == p->to.type) { if(copyprop(r)) { excise(r); t++; } else if(subprop(r) && copyprop(r)) { excise(r); t++; } } } } if(t) goto loop1; /* * look for MOVB x,R; MOVB R,R */ for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { default: continue; case AEOR: /* * EOR -1,x,y => MVN x,y */ if(p->from.type == D_CONST && p->from.offset == -1) { p->as = AMVN; p->from.type = D_REG; if(p->reg != NREG) p->from.reg = p->reg; else p->from.reg = p->to.reg; p->reg = NREG; } continue; case AMOVH: case AMOVHU: case AMOVB: case AMOVBU: if(p->to.type != D_REG) continue; break; } r1 = r->link; if(r1 == R) continue; p1 = r1->prog; if(p1->as != p->as) continue; if(p1->from.type != D_REG || p1->from.reg != p->to.reg) continue; if(p1->to.type != D_REG || p1->to.reg != p->to.reg) continue; excise(r1); } for(r=firstr; r!=R; r=r->link) { p = r->prog; switch(p->as) { case AMOVW: case AMOVB: case AMOVBU: if(p->from.type == D_OREG && p->from.offset == 0) xtramodes(r, &p->from); else if(p->to.type == D_OREG && p->to.offset == 0) xtramodes(r, &p->to); else continue; break; case ACMP: /* * elide CMP $0,x if calculation of x can set condition codes */ if(p->from.type != D_CONST || p->from.offset != 0) continue; r2 = r->s1; if(r2 == R) continue; t = r2->prog->as; switch(t) { default: continue; case ABEQ: case ABNE: case ABMI: case ABPL: break; case ABGE: t = ABPL; break; case ABLT: t = ABMI; break; case ABHI: t = ABNE; break; case ABLS: t = ABEQ; break; } r1 = r; do r1 = uniqp(r1); while (r1 != R && r1->prog->as == ANOP); if(r1 == R) continue; p1 = r1->prog; if(p1->to.type != D_REG) continue; if(p1->to.reg != p->reg) if(!(p1->as == AMOVW && p1->from.type == D_REG && p1->from.reg == p->reg)) continue; switch(p1->as) { default: continue; case AMOVW: if(p1->from.type != D_REG) continue; case AAND: case AEOR: case AORR: case ABIC: case AMVN: case ASUB: case ARSB: case AADD: case AADC: case ASBC: case ARSC: break; } p1->scond |= C_SBIT; r2->prog->as = t; excise(r); continue; } } predicate(); }