From: Ken Thompson Date: Sat, 8 Jan 2011 02:20:26 +0000 (-0800) Subject: add peep X-Git-Tag: weekly.2011-01-12~43 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=4ed17ceacefb79a9183faa2b8077a69374cfae3e;p=gostls13.git add peep R=rsc CC=golang-dev https://golang.org/cl/3902042 --- diff --git a/src/cmd/5g/peep.c b/src/cmd/5g/peep.c new file mode 100644 index 0000000000..3e6bf3eb98 --- /dev/null +++ b/src/cmd/5g/peep.c @@ -0,0 +1,1476 @@ +// Inferno utils/5c/peep.c +// http://code.google.com/p/inferno-os/source/browse/utils/5g/peep.c +// +// 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-2007 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-2007 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 "gg.h" +#include "opt.h" + +int xtramodes(Reg*, Adr*); +int shiftprop(Reg *r); +void constprop(Adr *c1, Adr *v1, Reg *r); +void predicate(void); +int copyau1(Prog *p, Adr *v); + +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(); +} + +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; +} + +int +regtyp(Adr *a) +{ + + if(a->type == D_REG) + return 1; + 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; + 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 ACMP: + case ACMN: + case AADD: + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + + case ACMPF: + case ACMPD: + 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->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) + goto gotit; + break; + + case AMOVM: + t = 1<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 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; + Adr *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(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("; %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, A); + 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; +} + +/* + * 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['P']) + print("constprop %D->%D\n", c1, v1); + for(; r != R; r = r->s1) { + p = r->prog; + if(debug['P']) + print("%P", p); + if(uniqp(r) == R) { + if(debug['P']) + print("; merge; return\n"); + return; + } + if(p->as == AMOVW && copyas(&p->from, c1)) { + if(debug['P']) + print("; sub%D/%D", &p->from, v1); + p->from = *v1; + } else if(copyu(p, v1, A) > 1) { + if(debug['P']) + print("; %Dset; return\n", v1); + return; + } + if(debug['P']) + print("\n"); + if(r->s2) + constprop(c1, v1, r->s2); + } +} + +/* + * ASLL x,y,w + * .. (not use w, not set x y w) + * AXXX w,a,b (a != w) + * .. (not use w) + * (set w) + * ----------- changed to + * .. + * AXXX (x<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['P']) + 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['P']) + 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['P']) + 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['P']) + 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['P']) + print("\t=>%P\tSUCCEED\n", p2); + return 1; +} + +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; +} + +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; +} + +int +nochange(Reg *r, Reg *r2, Prog *p) +{ + Adr a[3]; + int i, n; + + if(r == r2) + return 1; + n = 0; + if(p->reg != NREG && p->reg != p->to.reg) { + a[n].type = D_REG; + a[n++].reg = p->reg; + } + switch(p->from.type) { + case D_SHIFT: + a[n].type = D_REG; + a[n++].reg = p->from.offset&0xf; + case D_REG: + a[n].type = D_REG; + a[n++].reg = p->from.reg; + } + if(n == 0) + return 1; + for(; r!=R && r!=r2; r=uniqs(r)) { + p = r->prog; + for(i=0; i 1) + return 0; + } + return 1; +} + +int +findu1(Reg *r, Adr *v) +{ + for(; r != R; r = r->s1) { + if(r->active) + return 0; + r->active = 1; + switch(copyu(r->prog, v, A)) { + case 1: /* used */ + case 2: /* read-alter-rewrite */ + case 4: /* set and used */ + return 1; + case 3: /* set */ + return 0; + } + if(r->s2) + if (findu1(r->s2, v)) + return 1; + } + return 0; +} + +int +finduse(Reg *r, Adr *v) +{ + Reg *r1; + + for(r1=firstr; r1!=R; r1=r1->link) + r1->active = 0; + return findu1(r, v); +} + +int +xtramodes(Reg *r, Adr *a) +{ + Reg *r1, *r2, *r3; + Prog *p, *p1; + Adr v; + + p = r->prog; + if(debug['h'] && p->as == AMOVB && p->from.type == D_OREG) /* byte load */ + return 0; + v = *a; + v.type = D_REG; + r1 = findpre(r, &v); + if(r1 != R) { + p1 = r1->prog; + if(p1->to.type == D_REG && p1->to.reg == v.reg) + switch(p1->as) { + case AADD: + if(p1->from.type == D_REG || + (p1->from.type == D_SHIFT && (p1->from.offset&(1<<4)) == 0 && + (p->as != AMOVB || (a == &p->from && (p1->from.offset&~0xf) == 0))) || + (p1->from.type == D_CONST && + p1->from.offset > -4096 && p1->from.offset < 4096)) + if(nochange(uniqs(r1), r, p1)) { + if(a != &p->from || v.reg != p->to.reg) + if (finduse(r->s1, &v)) { + if(p1->reg == NREG || p1->reg == v.reg) + /* pre-indexing */ + p->scond |= C_WBIT; + else return 0; + } + switch (p1->from.type) { + case D_REG: + /* register offset */ + a->type = D_SHIFT; + a->offset = p1->from.reg; + break; + case D_SHIFT: + /* scaled register offset */ + a->type = D_SHIFT; + case D_CONST: + /* immediate offset */ + a->offset = p1->from.offset; + break; + } + if(p1->reg != NREG) + a->reg = p1->reg; + excise(r1); + return 1; + } + break; + case AMOVW: + if(p1->from.type == D_REG) + if((r2 = findinc(r1, r, &p1->from)) != R) { + for(r3=uniqs(r2); r3->prog->as==ANOP; r3=uniqs(r3)) + ; + if(r3 == r) { + /* post-indexing */ + p1 = r2->prog; + a->reg = p1->to.reg; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + if(!finduse(r, &r1->prog->to)) + excise(r1); + excise(r2); + return 1; + } + } + break; + } + } + if(a != &p->from || a->reg != p->to.reg) + if((r1 = findinc(r, R, &v)) != R) { + /* post-indexing */ + p1 = r1->prog; + a->offset = p1->from.offset; + p->scond |= C_PBIT; + excise(r1); + return 1; + } + return 0; +} + +/* + * 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, Adr *v, Adr *s) +{ + + switch(p->as) { + + default: + if(debug['P']) + print(" (?)"); + return 2; + + case AMOVM: + if(v->type != D_REG) + return 0; + if(p->from.type == D_CONST) { /* read reglist, read/rar */ + if(s != A) { + if(p->from.offset&(1<reg)) + return 1; + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) { + if(p->scond&C_WBIT) + return 2; + return 1; + } + if(p->from.offset&(1<reg)) + return 1; + } else { /* read/rar, write reglist */ + if(s != A) { + if(p->to.offset&(1<reg)) + return 1; + if(copysub(&p->from, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->from, v)) { + if(p->scond&C_WBIT) + return 2; + if(p->to.offset&(1<reg)) + return 4; + return 1; + } + if(p->to.offset&(1<reg)) + return 3; + } + return 0; + + case ANOP: /* read, write */ + case AMOVW: + case AMOVF: + case AMOVD: + case AMOVH: + case AMOVHU: + case AMOVB: + case AMOVBU: + case AMOVDW: + case AMOVWD: + case AMOVFD: + case AMOVDF: + if(p->scond&(C_WBIT|C_PBIT)) + if(v->type == D_REG) { + if(p->from.type == D_OREG || p->from.type == D_SHIFT) { + if(p->from.reg == v->reg) + return 2; + } else { + if(p->to.reg == v->reg) + return 2; + } + } + if(s != A) { + 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 AADD: /* read, read, write */ + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + + case ACMPF: + case ACMPD: + case ACMP: + case ACMN: + case ACASE: + if(s != A) { + 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: /* read, read */ + case ABNE: + case ABCS: + case ABHS: + case ABCC: + case ABLO: + case ABMI: + case ABPL: + case ABVS: + case ABVC: + case ABHI: + case ABLS: + case ABGE: + case ABLT: + case ABGT: + case ABLE: + if(s != A) { + if(copysub(&p->from, v, s, 1)) + return 1; + return copysub1(p, v, s, 1); + } + if(copyau(&p->from, v)) + return 1; + if(copyau1(p, v)) + return 1; + return 0; + + case AB: /* funny */ + if(s != A) { + if(copysub(&p->to, v, s, 1)) + return 1; + return 0; + } + if(copyau(&p->to, v)) + return 1; + return 0; + + case ARET: /* 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 != A) { + 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 ACMP: + case ACMN: + + case AADD: + case ASUB: + case ARSB: + case ASLL: + case ASRL: + case ASRA: + case AORR: + case AAND: + case AEOR: + case AMUL: + case ADIV: + case ADIVU: + return D_REG; + + case ACMPF: + case ACMPD: + + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + return D_FREG; + } + return D_NONE; +} + +/* + * direct reference, + * could be set/use depending on + * semantics + */ +int +copyas(Adr *a, Adr *v) +{ + + if(regtyp(v)) { + if(a->type == v->type) + if(a->reg == v->reg) + return 1; + } else + if(v->type == D_CONST) { /* for constprop */ + if(a->type == v->type) + if(a->name == v->name) + if(a->sym == v->sym) + if(a->reg == v->reg) + if(a->offset == v->offset) + return 1; + } + return 0; +} + +/* + * either direct or indirect + */ +int +copyau(Adr *a, Adr *v) +{ + + if(copyas(a, v)) + return 1; + if(v->type == D_REG) { + if(a->type == D_OREG) { + if(v->reg == a->reg) + return 1; + } else + if(a->type == D_REGREG) { + if(v->reg == a->reg) + return 1; + if(a->offset == v->reg) + return 1; + } else + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + return 1; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + return 1; + } + } + return 0; +} + +int +copyau1(Prog *p, Adr *v) +{ + + if(regtyp(v)) { + if(a2type(p) == 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(Adr *a, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau(a, v)) { + if(a->type == D_SHIFT) { + if((a->offset&0xf) == v->reg) + a->offset = (a->offset&~0xf)|s->reg; + if((a->offset&(1<<4)) && (a->offset>>8) == v->reg) + a->offset = (a->offset&~(0xf<<8))|(s->reg<<8); + } else + if(a->type == D_REGREG) { + if(a->offset == v->reg) + a->offset = s->reg; + if(a->reg == v->reg) + a->reg = s->reg; + } else + a->reg = s->reg; + } + return 0; +} + +int +copysub1(Prog *p1, Adr *v, Adr *s, int f) +{ + + if(f) + if(copyau1(p1, v)) + p1->reg = s->reg; + return 0; +} + +struct { + int opcode; + int notopcode; + int scond; + int notscond; +} predinfo[] = { + { ABEQ, ABNE, 0x0, 0x1, }, + { ABNE, ABEQ, 0x1, 0x0, }, + { ABCS, ABCC, 0x2, 0x3, }, + { ABHS, ABLO, 0x2, 0x3, }, + { ABCC, ABCS, 0x3, 0x2, }, + { ABLO, ABHS, 0x3, 0x2, }, + { ABMI, ABPL, 0x4, 0x5, }, + { ABPL, ABMI, 0x5, 0x4, }, + { ABVS, ABVC, 0x6, 0x7, }, + { ABVC, ABVS, 0x7, 0x6, }, + { ABHI, ABLS, 0x8, 0x9, }, + { ABLS, ABHI, 0x9, 0x8, }, + { ABGE, ABLT, 0xA, 0xB, }, + { ABLT, ABGE, 0xB, 0xA, }, + { ABGT, ABLE, 0xC, 0xD, }, + { ABLE, ABGT, 0xD, 0xC, }, +}; + +typedef struct { + Reg *start; + Reg *last; + Reg *end; + int len; +} Joininfo; + +enum { + Join, + Split, + End, + Branch, + Setcond, + Toolong +}; + +enum { + Falsecond, + Truecond, + Delbranch, + Keepbranch +}; + +int +isbranch(Prog *p) +{ + return (ABEQ <= p->as) && (p->as <= ABLE); +} + +int +predicable(Prog *p) +{ + if (isbranch(p) + || p->as == ANOP + || p->as == AXXX + || p->as == ADATA + || p->as == AGLOBL + || p->as == AGOK + || p->as == AHISTORY + || p->as == ANAME + || p->as == ASIGNAME + || p->as == ATEXT + || p->as == AWORD + || p->as == ABCASE + || p->as == ACASE) + return 0; + return 1; +} + +/* + * Depends on an analysis of the encodings performed by 5l. + * These seem to be all of the opcodes that lead to the "S" bit + * being set in the instruction encodings. + * + * C_SBIT may also have been set explicitly in p->scond. + */ +int +modifiescpsr(Prog *p) +{ + return (p->scond&C_SBIT) + || p->as == ATST + || p->as == ATEQ + || p->as == ACMN + || p->as == ACMP + || p->as == AMULU + || p->as == ADIVU + || p->as == AMUL + || p->as == ADIV + || p->as == AMOD + || p->as == AMODU + || p->as == ABL; +} + +/* + * Find the maximal chain of instructions starting with r which could + * be executed conditionally + */ +int +joinsplit(Reg *r, Joininfo *j) +{ + j->start = r; + j->last = r; + j->len = 0; + do { + if (r->p2 && (r->p1 || r->p2->p2link)) { + j->end = r; + return Join; + } + if (r->s1 && r->s2) { + j->end = r; + return Split; + } + j->last = r; + if (r->prog->as != ANOP) + j->len++; + if (!r->s1 && !r->s2) { + j->end = r->link; + return End; + } + if (r->s2) { + j->end = r->s2; + return Branch; + } + if (modifiescpsr(r->prog)) { + j->end = r->s1; + return Setcond; + } + r = r->s1; + } while (j->len < 4); + j->end = r; + return Toolong; +} + +Reg * +successor(Reg *r) +{ + if (r->s1) + return r->s1; + else + return r->s2; +} + +void +applypred(Reg *rstart, Joininfo *j, int cond, int branch) +{ + int pred; + Reg *r; + + if(j->len == 0) + return; + if (cond == Truecond) + pred = predinfo[rstart->prog->as - ABEQ].scond; + else + pred = predinfo[rstart->prog->as - ABEQ].notscond; + + for (r = j->start; ; r = successor(r)) { + if (r->prog->as == AB) { + if (r != j->last || branch == Delbranch) + excise(r); + else { + if (cond == Truecond) + r->prog->as = predinfo[rstart->prog->as - ABEQ].opcode; + else + r->prog->as = predinfo[rstart->prog->as - ABEQ].notopcode; + } + } + else if (predicable(r->prog)) + r->prog->scond = (r->prog->scond&~C_SCOND)|pred; + if (r->s1 != r->link) { + r->s1 = r->link; + r->link->p1 = r; + } + if (r == j->last) + break; + } +} + +void +predicate(void) +{ + Reg *r; + int t1, t2; + Joininfo j1, j2; + + for(r=firstr; r!=R; r=r->link) { + if (isbranch(r->prog)) { + t1 = joinsplit(r->s1, &j1); + t2 = joinsplit(r->s2, &j2); + if(j1.last->link != j2.start) + continue; + if(j1.end == j2.end) + if((t1 == Branch && (t2 == Join || t2 == Setcond)) || + (t2 == Join && (t1 == Join || t1 == Setcond))) { + applypred(r, &j1, Falsecond, Delbranch); + applypred(r, &j2, Truecond, Delbranch); + excise(r); + continue; + } + if(t1 == End || t1 == Branch) { + applypred(r, &j1, Falsecond, Keepbranch); + excise(r); + continue; + } + } + } +}