| // Derived from Inferno utils/6c/peep.c |
| // http://code.google.com/p/inferno-os/source/browse/utils/6c/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 <u.h> |
| #include <libc.h> |
| #include "gg.h" |
| #include "opt.h" |
| |
| static int regzer(Addr *a); |
| static int subprop(Flow*); |
| static int copyprop(Flow*); |
| static int copy1(Addr*, Addr*, Flow*, int); |
| static int copyas(Addr*, Addr*); |
| static int copyau(Addr*, Addr*); |
| static int copysub(Addr*, Addr*, Addr*, int); |
| static int copysub1(Prog*, Addr*, Addr*, int); |
| static int copyau1(Prog *p, Addr *v); |
| |
| static uint32 gactive; |
| |
| void |
| peep(Prog *firstp) |
| { |
| Graph *g; |
| Flow *r, *r1; |
| Prog *p, *p1; |
| int t; |
| |
| g = flowstart(firstp, sizeof(Flow)); |
| if(g == nil) |
| return; |
| gactive = 0; |
| |
| loop1: |
| if(debug['P'] && debug['v']) |
| dumpit("loop1", g->start, 0); |
| |
| t = 0; |
| for(r=g->start; r!=nil; r=r->link) { |
| p = r->prog; |
| // TODO(austin) Handle smaller moves. arm and amd64 |
| // distinguish between moves that moves that *must* |
| // sign/zero extend and moves that don't care so they |
| // can eliminate moves that don't care without |
| // breaking moves that do care. This might let us |
| // simplify or remove the next peep loop, too. |
| if(p->as == AMOVD || p->as == AFMOVD) |
| if(regtyp(&p->to)) { |
| // Try to eliminate reg->reg moves |
| 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++; |
| } |
| } |
| // Convert uses to $0 to uses of R0 and |
| // propagate R0 |
| 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 small MOVs not handled above) |
| */ |
| for(r=g->start; r!=nil; 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 == nil) |
| 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) |
| goto ret; /* 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=g->start; r!=nil; 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 == nil) |
| 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 != nil && r1->prog->as == ANOP); |
| if(r1 == nil) |
| 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: |
| /* don't deal with floating point instructions for now */ |
| /* |
| case AFABS: |
| case AFADD: |
| case AFADDS: |
| case AFCTIW: |
| case AFCTIWZ: |
| case AFDIV: |
| case AFDIVS: |
| case AFMADD: |
| case AFMADDS: |
| case AFMOVD: |
| case AFMSUB: |
| case AFMSUBS: |
| case AFMUL: |
| case AFMULS: |
| case AFNABS: |
| case AFNEG: |
| case AFNMADD: |
| case AFNMADDS: |
| case AFNMSUB: |
| case AFNMSUBS: |
| case AFRSP: |
| case AFSUB: |
| case AFSUBS: |
| case ACNTLZW: |
| case AMTFSB0: |
| case AMTFSB1: |
| */ |
| case AADD: |
| case AADDV: |
| case AADDC: |
| case AADDCV: |
| case AADDME: |
| case AADDMEV: |
| case AADDE: |
| case AADDEV: |
| case AADDZE: |
| case AADDZEV: |
| case AAND: |
| case AANDN: |
| case ADIVW: |
| case ADIVWV: |
| case ADIVWU: |
| case ADIVWUV: |
| case ADIVD: |
| case ADIVDV: |
| case ADIVDU: |
| case ADIVDUV: |
| case AEQV: |
| case AEXTSB: |
| case AEXTSH: |
| case AEXTSW: |
| case AMULHW: |
| case AMULHWU: |
| case AMULLW: |
| case AMULLWV: |
| case AMULHD: |
| case AMULHDU: |
| case AMULLD: |
| case AMULLDV: |
| case ANAND: |
| case ANEG: |
| case ANEGV: |
| case ANOR: |
| case AOR: |
| case AORN: |
| case AREM: |
| case AREMV: |
| case AREMU: |
| case AREMUV: |
| case AREMD: |
| case AREMDV: |
| case AREMDU: |
| case AREMDUV: |
| case ARLWMI: |
| case ARLWNM: |
| case ASLW: |
| case ASRAW: |
| case ASRW: |
| case ASLD: |
| case ASRAD: |
| case ASRD: |
| case ASUB: |
| case ASUBV: |
| case ASUBC: |
| case ASUBCV: |
| case ASUBME: |
| case ASUBMEV: |
| case ASUBE: |
| case ASUBEV: |
| case ASUBZE: |
| case ASUBZEV: |
| case AXOR: |
| t = variant2as(p1->as, as2variant(p1->as) | V_CC); |
| break; |
| } |
| if(debug['D']) |
| print("cmp %P; %P -> ", p1, p); |
| p1->as = t; |
| if(debug['D']) |
| print("%P\n", p1); |
| excise(r); |
| continue; |
| } |
| } |
| |
| ret: |
| flowend(g); |
| } |
| |
| void |
| excise(Flow *r) |
| { |
| Prog *p, *l; |
| |
| p = r->prog; |
| if(debug['P'] && debug['v']) |
| print("%P ===delete===\n", p); |
| l = p->link; |
| *p = zprog; |
| p->as = ANOP; |
| p->link = l; |
| ostats.ndelmov++; |
| } |
| |
| /* |
| * regzer returns 1 if a's value is 0 (a is R0 or $0) |
| */ |
| static int |
| regzer(Addr *a) |
| { |
| if(a->type == D_CONST) |
| if(a->sym == nil && a->reg == NREG) |
| if(a->offset == 0) |
| return 1; |
| if(a->type == D_REG) |
| if(a->reg == REGZERO) |
| return 1; |
| return 0; |
| } |
| |
| int |
| regtyp(Adr *a) |
| { |
| switch(a->type) { |
| default: |
| return 0; |
| case D_REG: |
| if(a->reg == REGZERO) |
| return 0; |
| case D_FREG: |
| return 1; |
| } |
| } |
| |
| /* |
| * the idea is to substitute |
| * one register for another |
| * from one MOV to another |
| * MOV a, R1 |
| * ADD b, R1 / no use of R2 |
| * MOV R1, R2 |
| * would be converted to |
| * MOV a, R2 |
| * ADD b, R2 |
| * MOV R2, R1 |
| * hopefully, then the former or latter MOV |
| * will be eliminated by copy propagation. |
| * |
| * r0 (the argument, not the register) is the MOV at the end of the |
| * above sequences. This returns 1 if it modified any instructions. |
| */ |
| static int |
| subprop(Flow *r0) |
| { |
| Prog *p; |
| Addr *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; |
| if(p->as == AVARDEF || p->as == AVARKILL) |
| continue; |
| proginfo(&info, p); |
| if(info.flags & Call) |
| return 0; |
| |
| if((info.flags & (RightRead|RightWrite)) == RightWrite) { |
| if(p->to.type == v1->type) |
| if(p->to.reg == v1->reg) |
| 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 remove redundant copies. |
| * v1->v2 F=0 |
| * (use v2 s/v2/v1/)* |
| * set v1 F=1 |
| * use v2 return fail (v1->v2 move must remain) |
| * ----------------- |
| * v1->v2 F=0 |
| * (use v2 s/v2/v1/)* |
| * set v1 F=1 |
| * set v2 return success (caller can remove v1->v2 move) |
| */ |
| static int |
| copyprop(Flow *r0) |
| { |
| Prog *p; |
| Addr *v1, *v2; |
| |
| p = r0->prog; |
| v1 = &p->from; |
| v2 = &p->to; |
| if(copyas(v1, v2)) { |
| if(debug['P']) |
| print("eliminating self-move\n", r0->prog); |
| return 1; |
| } |
| gactive++; |
| if(debug['P']) |
| print("trying to eliminate %D->%D move from:\n%P\n", v1, v2, r0->prog); |
| return copy1(v1, v2, r0->s1, 0); |
| } |
| |
| // copy1 replaces uses of v2 with v1 starting at r and returns 1 if |
| // all uses were rewritten. |
| static int |
| copy1(Addr *v1, Addr *v2, Flow *r, int f) |
| { |
| int t; |
| Prog *p; |
| |
| if(r->active == gactive) { |
| if(debug['P']) |
| print("act set; return 1\n"); |
| return 1; |
| } |
| r->active = gactive; |
| if(debug['P']) |
| print("copy1 replace %D with %D f=%d\n", v2, v1, f); |
| for(; r != nil; r = r->s1) { |
| p = r->prog; |
| if(debug['P']) |
| print("%P", p); |
| if(!f && uniqp(r) == nil) { |
| // Multiple predecessors; conservatively |
| // assume v1 was set on other path |
| f = 1; |
| if(debug['P']) |
| print("; merge; f=%d", f); |
| } |
| t = copyu(p, v2, nil); |
| switch(t) { |
| case 2: /* rar, can't 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\n => %P", v2, v1, p); |
| if(t == 4) { |
| if(debug['P']) |
| print("; %D used+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("; %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; |
| } |
| |
| // If s==nil, copyu returns the set/use of v in p; otherwise, it |
| // modifies p to replace reads of v with reads of s and returns 0 for |
| // success or non-zero for failure. |
| // |
| // If s==nil, copy returns one of the following values: |
| // 1 if v only used |
| // 2 if v is set and used in one address (read-alter-rewrite; |
| // can't substitute) |
| // 3 if v is only set |
| // 4 if v is set in one address and used in another (so addresses |
| // can be rewritten independently) |
| // 0 otherwise (not touched) |
| int |
| copyu(Prog *p, Addr *v, Addr *s) |
| { |
| if(p->from3.type != D_NONE) |
| // 9g never generates a from3 |
| print("copyu: from3 (%D) not implemented\n", p->from3); |
| |
| switch(p->as) { |
| |
| default: |
| print("copyu: can't find %A\n", p->as); |
| return 2; |
| |
| case ANOP: /* read p->from, write p->to */ |
| 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 AFCTID: |
| case AFCTIDZ: |
| case AFCFID: |
| case AFCFIDCC: |
| case AFMOVS: |
| case AFMOVD: |
| case AFRSP: |
| case AFNEG: |
| case AFNEGCC: |
| if(s != nil) { |
| if(copysub(&p->from, v, s, 1)) |
| return 1; |
| // Update only indirect uses of v in p->to |
| if(!copyas(&p->to, v)) |
| if(copysub(&p->to, v, s, 1)) |
| return 1; |
| return 0; |
| } |
| if(copyas(&p->to, v)) { |
| // Fix up implicit from |
| if(p->from.type == D_NONE) |
| p->from = p->to; |
| if(copyau(&p->from, v)) |
| return 4; |
| return 3; |
| } |
| if(copyau(&p->from, v)) |
| return 1; |
| if(copyau(&p->to, v)) |
| // p->to only indirectly uses v |
| return 1; |
| return 0; |
| |
| case AMOVBU: /* rar p->from, write p->to or read p->from, rar p->to */ |
| case AMOVBZU: |
| case AMOVHU: |
| case AMOVHZU: |
| case AMOVWZU: |
| case AMOVDU: |
| if(p->from.type == D_OREG) { |
| if(copyas(&p->from, v)) |
| // No s!=nil check; need to fail |
| // anyway in that case |
| return 2; |
| if(s != nil) { |
| if(copysub(&p->to, v, s, 1)) |
| return 1; |
| return 0; |
| } |
| if(copyas(&p->to, v)) |
| return 3; |
| } else if (p->to.type == D_OREG) { |
| if(copyas(&p->to, v)) |
| return 2; |
| if(s != nil) { |
| if(copysub(&p->from, v, s, 1)) |
| return 1; |
| return 0; |
| } |
| if(copyau(&p->from, v)) |
| return 1; |
| } else { |
| print("copyu: bad %P\n", p); |
| } |
| return 0; |
| |
| case ARLWMI: /* read p->from, read p->reg, rar p->to */ |
| case ARLWMICC: |
| if(copyas(&p->to, v)) |
| return 2; |
| /* fall through */ |
| |
| case AADD: /* read p->from, read p->reg, write p->to */ |
| 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; |
| // Update only indirect uses of v in p->to |
| 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) |
| // Fix up implicit reg (e.g., ADD |
| // R3,R4 -> ADD R3,R4,R4) so we can |
| // update reg and to separately. |
| 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: |
| return 0; |
| |
| case ACHECKNIL: /* read p->from */ |
| case ACMP: /* read p->from, read p->to */ |
| 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; |
| return 0; |
| |
| case ABR: /* read p->to */ |
| // 9g never generates a branch to a GPR (this isn't |
| // even a normal instruction; liblink turns it in to a |
| // mov and a branch). |
| 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(s != nil) |
| return 0; |
| // All registers die at this point, so claim |
| // everything is set (and not used). |
| return 3; |
| |
| 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(p->from.type == D_REG && v->type == D_REG && p->from.reg == v->reg) |
| 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 ADUFFZERO: |
| // R0 is zero, used by DUFFZERO, cannot be substituted. |
| // R3 is ptr to memory, used and set, cannot be substituted. |
| if(v->type == D_REG) { |
| if(v->reg == 0) |
| return 1; |
| if(v->reg == 3) |
| return 2; |
| } |
| return 0; |
| |
| case ADUFFCOPY: |
| // R3, R4 are ptr to src, dst, used and set, cannot be substituted. |
| // R5 is scratch, set by DUFFCOPY, cannot be substituted. |
| if(v->type == D_REG) { |
| if(v->reg == 3 || v->reg == 4) |
| return 2; |
| if(v->reg == 5) |
| return 3; |
| } |
| return 0; |
| |
| case ATEXT: /* funny */ |
| if(v->type == D_REG) |
| if(v->reg == REGARG) |
| return 3; |
| return 0; |
| |
| case APCDATA: |
| case AFUNCDATA: |
| case AVARDEF: |
| case AVARKILL: |
| return 0; |
| } |
| } |
| |
| int |
| a2type(Prog *p) |
| { |
| ProgInfo info; |
| proginfo(&info, p); |
| if(info.flags & (SizeB|SizeW|SizeL|SizeQ)) |
| return D_REG; |
| if(info.flags & (SizeF|SizeD)) |
| return D_FREG; |
| return D_NONE; |
| } |
| |
| // copyas returns 1 if a and v address the same register. |
| // |
| // If a is the from operand, this means this operation reads the |
| // register in v. If a is the to operand, this means this operation |
| // writes the register in v. |
| static int |
| copyas(Addr *a, Addr *v) |
| { |
| if(regtyp(v)) |
| if(a->type == v->type) |
| if(a->reg == v->reg) |
| return 1; |
| return 0; |
| } |
| |
| // copyau returns 1 if a either directly or indirectly addresses the |
| // same register as v. |
| // |
| // If a is the from operand, this means this operation reads the |
| // register in v. If a is the to operand, this means the operation |
| // either reads or writes the register in v (if !copyas(a, v), then |
| // the operation reads the register in v). |
| static int |
| copyau(Addr *a, Addr *v) |
| { |
| if(copyas(a, v)) |
| return 1; |
| if(v->type == D_REG) |
| if(a->type == D_OREG || (a->type == D_CONST && a->reg != NREG)) |
| if(v->reg == a->reg) |
| return 1; |
| return 0; |
| } |
| |
| // copyau1 returns 1 if p->reg references the same register as v and v |
| // is a direct reference. |
| static 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) { |
| // Whether p->reg is a GPR or an FPR is |
| // implied by the instruction (both are |
| // numbered from 0). But the type should |
| // match v->type. Sanity check this. |
| if(a2type(p) != v->type) |
| print("botch a2type %P\n", p); |
| return 1; |
| } |
| return 0; |
| } |
| |
| // copysub replaces v with s in a if f!=0 or indicates it if could if f==0. |
| // Returns 1 on failure to substitute (it always succeeds on ppc64). |
| static int |
| copysub(Addr *a, Addr *v, Addr *s, int f) |
| { |
| if(f) |
| if(copyau(a, v)) |
| a->reg = s->reg; |
| return 0; |
| } |
| |
| // copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. |
| // Returns 1 on failure to substitute (it always succeeds on ppc64). |
| static int |
| copysub1(Prog *p1, Addr *v, Addr *s, int f) |
| { |
| if(f) |
| if(copyau1(p1, v)) |
| p1->reg = s->reg; |
| return 0; |
| } |
| |
| int |
| sameaddr(Addr *a, Addr *v) |
| { |
| if(a->type != v->type) |
| return 0; |
| if(regtyp(v) && a->reg == v->reg) |
| return 1; |
| if(v->type == D_AUTO || v->type == D_PARAM) |
| if(v->offset == a->offset) |
| return 1; |
| return 0; |
| } |
| |
| int |
| smallindir(Addr *a, Addr *reg) |
| { |
| return reg->type == D_REG && a->type == D_OREG && |
| a->reg == reg->reg && |
| 0 <= a->offset && a->offset < 4096; |
| } |
| |
| int |
| stackaddr(Addr *a) |
| { |
| return a->type == D_REG && a->reg == REGSP; |
| } |