// cmd/9c/peep.c from Vita Nuova. // // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) // Portions Copyright © 1997-1999 Vita Nuova Limited // Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) // Portions Copyright © 2004,2006 Bruce Ellis // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) // Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others // Portions Copyright © 2009 The Go Authors. All rights reserved. // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include "gc.h" /* static Reg* rnops(Reg *r) { Prog *p; Reg *r1; if(r != R) for(;;){ p = r->prog; if(p->as != ANOP || p->from.type != D_NONE || p->to.type != D_NONE) break; r1 = uniqs(r); if(r1 == R) break; r = r1; } return r; } */ 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 == AMOVD || 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: case AMOVW: case AMOVWZ: 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['D'] > 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: case ACMPW: /* always safe? */ 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: case AMOVD: if(p1->from.type != D_REG) continue; continue; case AANDCC: case AANDNCC: case AORCC: case AORNCC: case AXORCC: case ASUBCC: case ASUBECC: case ASUBMECC: case ASUBZECC: case AADDCC: case AADDCCC: case AADDECC: case AADDMECC: case AADDZECC: case ARLWMICC: case ARLWNMCC: 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 ADIVD: t = ADIVDCC; break; case ADIVDV: t = ADIVDVCC; break; case ADIVDU: t = ADIVDUCC; break; case ADIVDUV: t = ADIVDUVCC; break; case AEQV: t = AEQVCC; break; case AEXTSB: t = AEXTSBCC; break; case AEXTSH: t = AEXTSHCC; break; case AEXTSW: t = AEXTSWCC; break; case AMULHW: t = AMULHWCC; break; case AMULHWU: t = AMULHWUCC; break; case AMULLW: t = AMULLWCC; break; case AMULLWV: t = AMULLWVCC; break; case AMULHD: t = AMULHDCC; break; case AMULHDU: t = AMULHDUCC; break; case AMULLD: t = AMULLDCC; break; case AMULLDV: t = AMULLDVCC; 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 AREMD: t = AREMDCC; break; case AREMDV: t = AREMDVCC; break; case AREMDU: t = AREMDUCC; break; case AREMDUV: t = AREMDUVCC; 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 ASLD: t = ASLDCC; break; case ASRAD: t = ASRADCC; break; case ASRD: t = ASRDCC; 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['D']) print("cmp %P; %P -> ", p1, p); p1->as = t; if(debug['D']) print("%P\n", p1); excise(r); continue; } } } void excise(Reg *r) { Prog *p; p = r->prog; p->as = ANOP; p->from = zprog.from; p->from3 = zprog.from3; p->to = zprog.to; p->reg = zprog.reg; /**/ } Reg* uniqp(Reg *r) { Reg *r1; r1 = r->p1; if(r1 == R) { r1 = r->p2; if(r1 == R || r1->p2link != R) return R; } else if(r->p2 != R) return R; return r1; } Reg* uniqs(Reg *r) { Reg *r1; r1 = r->s1; if(r1 == R) { r1 = r->s2; if(r1 == R) return R; } else if(r->s2 != R) return R; return r1; } /* * if the system forces R0 to be zero, * convert references to $0 to references to R0. */ int regzer(Addr *a) { if(R0ISZERO) { if(a->type == D_CONST) if(a->sym == nil) if(a->offset == 0) return 1; if(a->type == D_REG) if(a->reg == REGZERO) return 1; } return 0; } int regtyp(Addr *a) { if(a->type == D_REG) { if(!R0ISZERO || a->reg != REGZERO) return 1; return 0; } if(a->type == D_FREG) return 1; return 0; } /* * 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; Addr *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 AADD: case AADDC: case AADDCC: case AADDE: case AADDECC: case ASUB: case ASUBCC: case ASUBC: case ASUBCCC: case ASUBE: case ASUBECC: case ASLW: case ASRW: case ASRWCC: case ASRAW: case ASRAWCC: case ASLD: case ASRD: case ASRAD: case AOR: case AORCC: case AORN: case AORNCC: case AAND: case AANDCC: case AANDN: case AANDNCC: case ANAND: case ANANDCC: case ANOR: case ANORCC: case AXOR: case AXORCC: case AMULHW: case AMULHWU: case AMULLW: case AMULLD: case ADIVW: case ADIVWU: case ADIVD: case ADIVDU: case AREM: case AREMU: case AREMD: case AREMDU: case ARLWNM: case ARLWNMCC: case AFADD: case AFADDS: case AFSUB: case AFSUBS: case AFMUL: case AFMULS: case AFDIV: case AFDIVS: 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 AADDME: case AADDMECC: case AADDZE: case AADDZECC: case ASUBME: case ASUBMECC: case ASUBZE: case ASUBZECC: case ANEG: case ANEGCC: case AFNEG: case AFNEGCC: case AFMOVS: case AFMOVD: case AMOVW: case AMOVD: 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; } /* * The idea is to remove redundant copies. * v1->v2 F=0 * (use v2 s/v2/v1/)* * set v1 F=1 * use v2 return fail * ----------------- * v1->v2 F=0 * (use v2 s/v2/v1/)* * set v1 F=1 * set v2 return success */ int copyprop(Reg *r0) { Prog *p; Addr *v1, *v2; Reg *r; p = r0->prog; v1 = &p->from; v2 = &p->to; if(copyas(v1, v2)) return 1; for(r=firstr; r!=R; r=r->link) r->active = 0; return copy1(v1, v2, r0->s1, 0); } int copy1(Addr *v1, Addr *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, nil); switch(t) { case 2: /* rar, cant split */ if(debug['P']) print("; %Drar; return 0\n", v2); return 0; case 3: /* set */ if(debug['P']) print("; %Dset; 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("; %Dused+set and f=%d; return 0\n", v2, f); else print("; %Dused 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("; %Dused+set; return 1\n", v2); return 1; } break; } if(!f) { t = copyu(p, v1, nil); if(!f && (t == 2 || t == 3 || t == 4)) { f = 1; if(debug['P']) print("; %Dset 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; } /* * return * 1 if v only used (and substitute), * 2 if read-alter-rewrite * 3 if set * 4 if set and used * 0 otherwise (not touched) */ int copyu(Prog *p, Addr *v, Addr *s) { switch(p->as) { default: if(debug['P']) print(" (\?\?\?)"); return 2; case ANOP: /* read, write */ case AMOVH: case AMOVHZ: case AMOVB: case AMOVBZ: case AMOVW: case AMOVWZ: case AMOVD: case ANEG: case ANEGCC: case AADDME: case AADDMECC: case AADDZE: case AADDZECC: case ASUBME: case ASUBMECC: case ASUBZE: case ASUBZECC: case AFCTIW: case AFCTIWZ: case AFMOVS: case AFMOVD: case AFRSP: case AFNEG: case AFNEGCC: if(s != nil) { if(copysub(&p->from, v, s, 1)) return 1; if(!copyas(&p->to, v)) if(copysub(&p->to, v, s, 1)) return 1; return 0; } if(copyas(&p->to, v)) { if(copyau(&p->from, v)) return 4; return 3; } if(copyau(&p->from, v)) return 1; if(copyau(&p->to, v)) return 1; return 0; case ARLWMI: /* read read rar */ case ARLWMICC: if(copyas(&p->to, v)) return 2; /* fall through */ case AADD: /* read read write */ case AADDC: case AADDE: case ASUB: case ASLW: case ASRW: case ASRAW: case ASLD: case ASRD: case ASRAD: case AOR: case AORCC: case AORN: case AORNCC: case AAND: case AANDCC: case AANDN: case AANDNCC: case ANAND: case ANANDCC: case ANOR: case ANORCC: case AXOR: case AMULHW: case AMULHWU: case AMULLW: case AMULLD: case ADIVW: case ADIVD: case ADIVWU: case ADIVDU: case AREM: case AREMU: case AREMD: case AREMDU: case ARLWNM: case ARLWNMCC: case AFADDS: case AFADD: case AFSUBS: case AFSUB: case AFMULS: case AFMUL: case AFDIVS: case AFDIV: if(s != nil) { if(copysub(&p->from, v, s, 1)) return 1; if(copysub1(p, v, s, 1)) return 1; if(!copyas(&p->to, v)) if(copysub(&p->to, v, s, 1)) return 1; return 0; } if(copyas(&p->to, v)) { if(p->reg == NREG) p->reg = p->to.reg; if(copyau(&p->from, v)) return 4; if(copyau1(p, v)) return 4; return 3; } if(copyau(&p->from, v)) return 1; if(copyau1(p, v)) return 1; if(copyau(&p->to, v)) return 1; return 0; case ABEQ: case ABGT: case ABGE: case ABLT: case ABLE: case ABNE: case ABVC: case ABVS: break; case ACMP: /* read read */ case ACMPU: case ACMPW: case ACMPWU: case AFCMPO: case AFCMPU: if(s != nil) { if(copysub(&p->from, v, s, 1)) return 1; return copysub(&p->to, v, s, 1); } if(copyau(&p->from, v)) return 1; if(copyau(&p->to, v)) return 1; break; case ABR: /* funny */ if(s != nil) { if(copysub(&p->to, v, s, 1)) return 1; return 0; } if(copyau(&p->to, v)) return 1; return 0; case ARETURN: /* funny */ if(v->type == D_REG) if(v->reg == REGRET) return 2; if(v->type == D_FREG) if(v->reg == FREGRET) return 2; case ABL: /* funny */ if(v->type == D_REG) { if(v->reg <= REGEXT && v->reg > exregoffset) return 2; if(v->reg == REGARG) return 2; } if(v->type == D_FREG) { if(v->reg <= FREGEXT && v->reg > exfregoffset) return 2; } if(s != nil) { if(copysub(&p->to, v, s, 1)) return 1; return 0; } if(copyau(&p->to, v)) return 4; return 3; case ATEXT: /* funny */ if(v->type == D_REG) if(v->reg == REGARG) return 3; return 0; } return 0; } int a2type(Prog *p) { switch(p->as) { case AADD: case AADDC: case AADDCC: case AADDCCC: case AADDE: case AADDECC: case AADDME: case AADDMECC: case AADDZE: case AADDZECC: case ASUB: case ASUBC: case ASUBCC: case ASUBCCC: case ASUBE: case ASUBECC: case ASUBME: case ASUBMECC: case ASUBZE: case ASUBZECC: case ASLW: case ASLWCC: case ASRW: case ASRWCC: case ASRAW: case ASRAWCC: case ASLD: case ASLDCC: case ASRD: case ASRDCC: case ASRAD: case ASRADCC: case AOR: case AORCC: case AORN: case AORNCC: case AAND: case AANDCC: case AANDN: case AANDNCC: case AXOR: case AXORCC: case ANEG: case ANEGCC: case AMULHW: case AMULHWU: case AMULLW: case AMULLWCC: case ADIVW: case ADIVWCC: case ADIVWU: case ADIVWUCC: case AREM: case AREMCC: case AREMU: case AREMUCC: case AMULLD: case AMULLDCC: case ADIVD: case ADIVDCC: case ADIVDU: case ADIVDUCC: case AREMD: case AREMDCC: case AREMDU: case AREMDUCC: case ANAND: case ANANDCC: case ANOR: case ANORCC: case ARLWMI: case ARLWMICC: case ARLWNM: case ARLWNMCC: return D_REG; case AFADDS: case AFADDSCC: case AFADD: case AFADDCC: case AFSUBS: case AFSUBSCC: case AFSUB: case AFSUBCC: case AFMULS: case AFMULSCC: case AFMUL: case AFMULCC: case AFDIVS: case AFDIVSCC: case AFDIV: case AFDIVCC: case AFNEG: case AFNEGCC: return D_FREG; } return D_NONE; } /* * direct reference, * could be set/use depending on * semantics */ int copyas(Addr *a, Addr *v) { if(regtyp(v)) if(a->type == v->type) if(a->reg == v->reg) return 1; return 0; } /* * either direct or indirect */ int copyau(Addr *a, Addr *v) { if(copyas(a, v)) return 1; if(v->type == D_REG) if(a->type == D_OREG) if(v->reg == a->reg) return 1; return 0; } int copyau1(Prog *p, Addr *v) { if(regtyp(v)) if(p->from.type == v->type || p->to.type == v->type) if(p->reg == v->reg) { if(a2type(p) != v->type) print("botch a2type %P\n", p); return 1; } return 0; } /* * substitute s for v in a * return failure to substitute */ int copysub(Addr *a, Addr *v, Addr *s, int f) { if(f) if(copyau(a, v)) a->reg = s->reg; return 0; } int copysub1(Prog *p1, Addr *v, Addr *s, int f) { if(f) if(copyau1(p1, v)) p1->reg = s->reg; return 0; }