func peep(firstp *obj.Prog) { g := gc.Flowstart(firstp, nil) if g == nil { return } gactive = 0 run := func(name string, pass func(r *gc.Flow) int) int { n := pass(g.Start) if gc.Debug['P'] != 0 { fmt.Println(name, ":", n) } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit(name, g.Start, 0) } return n } for { n := 0 n += run("constant propagation", constantPropagation) n += run("copy propagation", copyPropagation) n += run("cast propagation", castPropagation) n += run("remove load-hit-stores", removeLoadHitStores) n += run("dead code elimination", deadCodeElimination) if n == 0 { break } } run("fuse op moves", fuseOpMoves) run("fuse clears", fuseClear) run("load pipelining", loadPipelining) run("fuse compare branch", fuseCompareBranch) run("simplify ops", simplifyOps) run("dead code elimination", deadCodeElimination) // TODO(mundaym): load/store multiple aren't currently handled by copyu // so this pass must be last. run("fuse multiple", fuseMultiple) gc.Flowend(g) }
func peep(firstp *obj.Prog) { g := gc.Flowstart(firstp, nil) if g == nil { return } gactive = 0 var p *obj.Prog var r *gc.Flow var t obj.As loop1: if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.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 == ppc64.AMOVD || p.As == ppc64.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 == obj.TYPE_REG { p.From.Type = obj.TYPE_REG p.From.Reg = ppc64.REGZERO if copyprop(r) { excise(r) t++ } else if subprop(r) && copyprop(r) { excise(r) t++ } } } } } } if t != 0 { goto loop1 } /* * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) */ var p1 *obj.Prog var r1 *gc.Flow for r := g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { default: continue case ppc64.AMOVH, ppc64.AMOVHZ, ppc64.AMOVB, ppc64.AMOVBZ, ppc64.AMOVW, ppc64.AMOVWZ: if p.To.Type != obj.TYPE_REG { continue } } r1 = r.Link if r1 == nil { continue } p1 = r1.Prog if p1.As != p.As { continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { continue } excise(r1) } if gc.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 ppc64.ACMP, ppc64.ACMPW: /* always safe? */ if !regzer(&p.To) { continue } r1 = r.S1 if r1 == nil { continue } switch r1.Prog.As { default: continue /* the conditions can be complex and these are currently little used */ case ppc64.ABCL, ppc64.ABC: continue case ppc64.ABEQ, ppc64.ABGE, ppc64.ABGT, ppc64.ABLE, ppc64.ABLT, ppc64.ABNE, ppc64.ABVC, ppc64.ABVS: break } r1 = r for { r1 = gc.Uniqp(r1) if r1 == nil || r1.Prog.As != obj.ANOP { break } } if r1 == nil { continue } p1 = r1.Prog if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.From.Reg { continue } switch p1.As { /* irregular instructions */ case ppc64.ASUB, ppc64.AADD, ppc64.AXOR, ppc64.AOR: if p1.From.Type == obj.TYPE_CONST || p1.From.Type == obj.TYPE_ADDR { continue } } switch p1.As { default: continue case ppc64.AMOVW, ppc64.AMOVD: if p1.From.Type != obj.TYPE_REG { continue } continue case ppc64.AANDCC, ppc64.AANDNCC, ppc64.AORCC, ppc64.AORNCC, ppc64.AXORCC, ppc64.ASUBCC, ppc64.ASUBECC, ppc64.ASUBMECC, ppc64.ASUBZECC, ppc64.AADDCC, ppc64.AADDCCC, ppc64.AADDECC, ppc64.AADDMECC, ppc64.AADDZECC, ppc64.ARLWMICC, ppc64.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: */ ppc64.AADD, ppc64.AADDV, ppc64.AADDC, ppc64.AADDCV, ppc64.AADDME, ppc64.AADDMEV, ppc64.AADDE, ppc64.AADDEV, ppc64.AADDZE, ppc64.AADDZEV, ppc64.AAND, ppc64.AANDN, ppc64.ADIVW, ppc64.ADIVWV, ppc64.ADIVWU, ppc64.ADIVWUV, ppc64.ADIVD, ppc64.ADIVDV, ppc64.ADIVDU, ppc64.ADIVDUV, ppc64.AEQV, ppc64.AEXTSB, ppc64.AEXTSH, ppc64.AEXTSW, ppc64.AMULHW, ppc64.AMULHWU, ppc64.AMULLW, ppc64.AMULLWV, ppc64.AMULHD, ppc64.AMULHDU, ppc64.AMULLD, ppc64.AMULLDV, ppc64.ANAND, ppc64.ANEG, ppc64.ANEGV, ppc64.ANOR, ppc64.AOR, ppc64.AORN, ppc64.AREM, ppc64.AREMV, ppc64.AREMU, ppc64.AREMUV, ppc64.AREMD, ppc64.AREMDV, ppc64.AREMDU, ppc64.AREMDUV, ppc64.ARLWMI, ppc64.ARLWNM, ppc64.ASLW, ppc64.ASRAW, ppc64.ASRW, ppc64.ASLD, ppc64.ASRAD, ppc64.ASRD, ppc64.ASUB, ppc64.ASUBV, ppc64.ASUBC, ppc64.ASUBCV, ppc64.ASUBME, ppc64.ASUBMEV, ppc64.ASUBE, ppc64.ASUBEV, ppc64.ASUBZE, ppc64.ASUBZEV, ppc64.AXOR: t = variant2as(p1.As, as2variant(p1.As)|V_CC) } if gc.Debug['D'] != 0 { fmt.Printf("cmp %v; %v -> ", p1, p) } p1.As = t if gc.Debug['D'] != 0 { fmt.Printf("%v\n", p1) } excise(r) continue } } ret: gc.Flowend(g) }
// UNUSED func peep(firstp *obj.Prog) { g := (*gc.Graph)(gc.Flowstart(firstp, nil)) if g == nil { return } gactive = 0 var r *gc.Flow var p *obj.Prog var t int loop1: if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("loop1", g.Start, 0) } t = 0 for r = g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { /* * elide shift into TYPE_SHIFT operand of subsequent instruction */ // if(shiftprop(r)) { // excise(r); // t++; // break; // } case arm.ASLL, arm.ASRL, arm.ASRA: break case arm.AMOVB, arm.AMOVH, arm.AMOVW, arm.AMOVF, arm.AMOVD: if regtyp(&p.From) { if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) { if p.Scond == arm.C_SCOND_NONE { if copyprop(g, r) { excise(r) t++ break } if subprop(r) && copyprop(g, r) { excise(r) t++ break } } } } case arm.AMOVHS, arm.AMOVHU, arm.AMOVBS, arm.AMOVBU: if p.From.Type == obj.TYPE_REG { if shortprop(r) { t++ } } } } /* if(p->scond == C_SCOND_NONE) if(regtyp(&p->to)) if(isdconst(&p->from)) { constprop(&p->from, &p->to, r->s1); } break; */ if t != 0 { goto loop1 } for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { /* * EOR -1,x,y => MVN x,y */ case arm.AEOR: if isdconst(&p.From) && p.From.Offset == -1 { p.As = arm.AMVN p.From.Type = obj.TYPE_REG if p.Reg != 0 { p.From.Reg = p.Reg } else { p.From.Reg = p.To.Reg } p.Reg = 0 } } } for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case arm.AMOVW, arm.AMOVB, arm.AMOVBS, arm.AMOVBU: if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 { xtramodes(g, r, &p.From) } else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 { xtramodes(g, r, &p.To) } else { continue } } } // case ACMP: // /* // * elide CMP $0,x if calculation of x can set condition codes // */ // if(isdconst(&p->from) || p->from.offset != 0) // continue; // r2 = r->s1; // if(r2 == nil) // 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 != nil && r1->prog->as == ANOP); // if(r1 == nil) // continue; // p1 = r1->prog; // if(p1->to.type != TYPE_REG) // continue; // if(p1->to.reg != p->reg) // if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg)) // continue; // // switch(p1->as) { // default: // continue; // case AMOVW: // if(p1->from.type != TYPE_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(g); gc.Flowend(g) }
func peep(firstp *obj.Prog) { g := (*gc.Graph)(gc.Flowstart(firstp, nil)) if g == nil { return } gactive = 0 // byte, word arithmetic elimination. elimshortmov(g) // constant propagation // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim var p *obj.Prog for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case x86.ALEAL, x86.ALEAQ: if regtyp(&p.To) { if p.From.Sym != nil { if p.From.Index == x86.REG_NONE { conprop(r) } } } case x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVQ, x86.AMOVSS, x86.AMOVSD: if regtyp(&p.To) { if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { conprop(r) } } } } var r *gc.Flow var r1 *gc.Flow var p1 *obj.Prog var t int loop1: if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("loop1", g.Start, 0) } t = 0 for r = g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { case x86.AMOVL, x86.AMOVQ, x86.AMOVSS, x86.AMOVSD: if regtyp(&p.To) { if regtyp(&p.From) { if copyprop(g, r) { excise(r) t++ } else if subprop(r) && copyprop(g, r) { excise(r) t++ } } } case x86.AMOVBLZX, x86.AMOVWLZX, x86.AMOVBLSX, x86.AMOVWLSX: if regtyp(&p.To) { r1 = rnops(gc.Uniqs(r)) if r1 != nil { p1 = r1.Prog if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { p1.As = x86.AMOVL t++ } } } case x86.AMOVBQSX, x86.AMOVBQZX, x86.AMOVWQSX, x86.AMOVWQZX, x86.AMOVLQSX, x86.AMOVLQZX, x86.AMOVQL: if regtyp(&p.To) { r1 = rnops(gc.Uniqs(r)) if r1 != nil { p1 = r1.Prog if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { p1.As = x86.AMOVQ t++ } } } case x86.AADDL, x86.AADDQ, x86.AADDW: if p.From.Type != obj.TYPE_CONST || needc(p.Link) { break } if p.From.Offset == -1 { if p.As == x86.AADDQ { p.As = x86.ADECQ } else if p.As == x86.AADDL { p.As = x86.ADECL } else { p.As = x86.ADECW } p.From = obj.Addr{} break } if p.From.Offset == 1 { if p.As == x86.AADDQ { p.As = x86.AINCQ } else if p.As == x86.AADDL { p.As = x86.AINCL } else { p.As = x86.AINCW } p.From = obj.Addr{} break } case x86.ASUBL, x86.ASUBQ, x86.ASUBW: if p.From.Type != obj.TYPE_CONST || needc(p.Link) { break } if p.From.Offset == -1 { if p.As == x86.ASUBQ { p.As = x86.AINCQ } else if p.As == x86.ASUBL { p.As = x86.AINCL } else { p.As = x86.AINCW } p.From = obj.Addr{} break } if p.From.Offset == 1 { if p.As == x86.ASUBQ { p.As = x86.ADECQ } else if p.As == x86.ASUBL { p.As = x86.ADECL } else { p.As = x86.ADECW } p.From = obj.Addr{} break } } } if t != 0 { goto loop1 } // MOVLQZX removal. // The MOVLQZX exists to avoid being confused for a // MOVL that is just copying 32-bit data around during // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 // if it is dominated by an earlier ADDL/MOVL/etc into R1 that // will have already cleared the high bits. // // MOVSD removal. // We never use packed registers, so a MOVSD between registers // can be replaced by MOVAPD, which moves the pair of float64s // instead of just the lower one. We only use the lower one, but // the processor can do better if we do moves using both. for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog if p.As == x86.AMOVLQZX { if regtyp(&p.From) { if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg { if prevl(r, int(p.From.Reg)) { excise(r) } } } } if p.As == x86.AMOVSD { if regtyp(&p.From) { if regtyp(&p.To) { p.As = x86.AMOVAPD } } } } // load pipelining // push any load from memory as early as possible // to give it time to complete before use. for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVQ, x86.AMOVLQZX: if regtyp(&p.To) && !regconsttyp(&p.From) { pushback(r) } } } gc.Flowend(g) }
func peep(firstp *obj.Prog) { g := (*gc.Graph)(gc.Flowstart(firstp, nil)) if g == nil { return } gactive = 0 var p *obj.Prog var r *gc.Flow var t int loop1: if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.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 == mips.AMOVV || p.As == mips.AMOVF || p.As == mips.AMOVD { if regtyp(&p.To) { // Try to eliminate reg->reg moves if regtyp(&p.From) { if isfreg(&p.From) == isfreg(&p.To) { 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) != 0 { if p.To.Type == obj.TYPE_REG && !isfreg(&p.To) { p.From.Type = obj.TYPE_REG p.From.Reg = mips.REGZERO if copyprop(r) { excise(r) t++ } else if subprop(r) && copyprop(r) { excise(r) t++ } } } } } } if t != 0 { goto loop1 } /* * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) */ var p1 *obj.Prog var r1 *gc.Flow for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { default: continue case mips.AMOVH, mips.AMOVHU, mips.AMOVB, mips.AMOVBU, mips.AMOVW, mips.AMOVWU: if p.To.Type != obj.TYPE_REG { continue } } r1 = r.Link if r1 == nil { continue } p1 = r1.Prog if p1.As != p.As { continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { continue } excise(r1) } gc.Flowend(g) }
func peep(firstp *obj.Prog) { g := gc.Flowstart(firstp, nil) if g == nil { return } gactive = 0 // byte, word arithmetic elimination. elimshortmov(g) // constant propagation // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim var p *obj.Prog for r := g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { case x86.ALEAL: if regtyp(&p.To) { if p.From.Sym != nil { if p.From.Index == x86.REG_NONE { conprop(r) } } } case x86.AMOVB, x86.AMOVW, x86.AMOVL, x86.AMOVSS, x86.AMOVSD: if regtyp(&p.To) { if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { conprop(r) } } } } var r1 *gc.Flow var p1 *obj.Prog var r *gc.Flow var t int loop1: if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("loop1", g.Start, 0) } t = 0 for r = g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { case x86.AMOVL, x86.AMOVSS, x86.AMOVSD: if regtyp(&p.To) { if regtyp(&p.From) { if copyprop(g, r) { excise(r) t++ } else if subprop(r) && copyprop(g, r) { excise(r) t++ } } } case x86.AMOVBLZX, x86.AMOVWLZX, x86.AMOVBLSX, x86.AMOVWLSX: if regtyp(&p.To) { r1 = rnops(gc.Uniqs(r)) if r1 != nil { p1 = r1.Prog if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { p1.As = x86.AMOVL t++ } } } case x86.AADDL, x86.AADDW: if p.From.Type != obj.TYPE_CONST || needc(p.Link) { break } if p.From.Offset == -1 { if p.As == x86.AADDL { p.As = x86.ADECL } else { p.As = x86.ADECW } p.From = obj.Addr{} break } if p.From.Offset == 1 { if p.As == x86.AADDL { p.As = x86.AINCL } else { p.As = x86.AINCW } p.From = obj.Addr{} break } case x86.ASUBL, x86.ASUBW: if p.From.Type != obj.TYPE_CONST || needc(p.Link) { break } if p.From.Offset == -1 { if p.As == x86.ASUBL { p.As = x86.AINCL } else { p.As = x86.AINCW } p.From = obj.Addr{} break } if p.From.Offset == 1 { if p.As == x86.ASUBL { p.As = x86.ADECL } else { p.As = x86.ADECW } p.From = obj.Addr{} break } } } if t != 0 { goto loop1 } // MOVSD removal. // We never use packed registers, so a MOVSD between registers // can be replaced by MOVAPD, which moves the pair of float64s // instead of just the lower one. We only use the lower one, but // the processor can do better if we do moves using both. for r := g.Start; r != nil; r = r.Link { p = r.Prog if p.As == x86.AMOVSD { if regtyp(&p.From) { if regtyp(&p.To) { p.As = x86.AMOVAPD } } } } gc.Flowend(g) }
func peep(firstp *obj.Prog) { g := gc.Flowstart(firstp, nil) if g == nil { return } gactive = 0 var p *obj.Prog var r *gc.Flow var t int loop1: if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("loop1", g.Start, 0) } t = 0 for r = g.Start; r != nil; r = r.Link { p = r.Prog // TODO(minux) Handle smaller moves. arm and amd64 // distinguish between 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 == arm64.AMOVD || p.As == arm64.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++ } } } } } } if t != 0 { goto loop1 } /* * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) */ var p1 *obj.Prog var r1 *gc.Flow for r := g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { default: continue case arm64.AMOVH, arm64.AMOVHU, arm64.AMOVB, arm64.AMOVBU, arm64.AMOVW, arm64.AMOVWU: if p.To.Type != obj.TYPE_REG { continue } } r1 = r.Link if r1 == nil { continue } p1 = r1.Prog if p1.As != p.As { continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { continue } excise(r1) } if gc.Debug['D'] > 1 { goto ret /* allow following code improvement to be suppressed */ } // MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R for r := g.Start; r != nil; r = r.Link { p = r.Prog switch p.As { default: continue case arm64.AMOVD: if p.To.Type != obj.TYPE_REG { continue } if p.From.Type != obj.TYPE_CONST { continue } if p.From.Offset < 0 || 4096 <= p.From.Offset { continue } } r1 = r.Link if r1 == nil { continue } p1 = r1.Prog if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm. continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if p1.To.Type != obj.TYPE_REG { continue } if gc.Debug['P'] != 0 { fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(p1.As), p, p1) } p1.From.Type = obj.TYPE_CONST p1.From = p.From excise(r) } /* TODO(minux): * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R * when OP can set condition codes correctly */ ret: gc.Flowend(g) }
func peep(firstp *obj.Prog) { g := (*gc.Graph)(gc.Flowstart(firstp, nil)) if g == nil { return } gactive = 0 var p *obj.Prog if false { // constant propagation // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case s390x.AMOVB, s390x.AMOVW, s390x.AMOVD: if regtyp(&p.To) { if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { conprop(r) } } } } } var r *gc.Flow var t int loop1: // if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { // gc.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 == s390x.AMOVD || p.As == s390x.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) != 0 { if p.To.Type == obj.TYPE_REG { p.From.Type = obj.TYPE_REG p.From.Reg = s390x.REGZERO if copyprop(r) { excise(r) t++ } else if subprop(r) && copyprop(r) { excise(r) t++ } } } } } } if t != 0 { goto loop1 } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("pass7 copyprop", g.Start, 0) } /* * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) */ var p1 *obj.Prog var r1 *gc.Flow for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { default: continue case s390x.AMOVH, s390x.AMOVHZ, s390x.AMOVB, s390x.AMOVBZ, s390x.AMOVW, s390x.AMOVWZ: if p.To.Type != obj.TYPE_REG { continue } } r1 = r.Link if r1 == nil { continue } p1 = r1.Prog if p1.As != p.As { continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { continue } excise(r1) } if gc.Debug['P'] > 1 { goto ret /* allow following code improvement to be suppressed */ } if gc.Debug['p'] == 0 { // load pipelining // push any load from memory as early as possible // to give it time to complete before use. for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { p = r.Prog switch p.As { case s390x.AMOVB, s390x.AMOVW, s390x.AMOVD: if regtyp(&p.To) && !regconsttyp(&p.From) { pushback(r) } } } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { gc.Dumpit("pass8 push load as early as possible", g.Start, 0) } } /* * look for OP a, b, c; MOV c, d; -> OP a, b, d; */ for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { if (gc.Debugmergeopmv != -1) && (mergeopmv_cnt >= gc.Debugmergeopmv) { break } p = r.Prog switch p.As { case s390x.AADD, s390x.AADDC, s390x.AADDME, s390x.AADDE, s390x.AADDZE, s390x.AAND, s390x.AANDN, s390x.ADIVW, s390x.ADIVWU, s390x.ADIVD, s390x.ADIVDU, s390x.AMULLW, s390x.AMULHD, s390x.AMULHDU, s390x.AMULLD, s390x.ANAND, s390x.ANOR, s390x.AOR, s390x.AORN, s390x.AREM, s390x.AREMU, s390x.AREMD, s390x.AREMDU, s390x.ARLWMI, s390x.ARLWNM, s390x.ASLW, s390x.ASRAW, s390x.ASRW, s390x.ASLD, s390x.ASRAD, s390x.ASRD, s390x.ASUB, s390x.ASUBC, s390x.ASUBME, s390x.ASUBE, s390x.ASUBZE, s390x.AXOR: if p.To.Type != obj.TYPE_REG { continue } if p.Reg == 0 { // Only for 3 ops instruction continue } default: continue } r1 := r.Link for ; r1 != nil; r1 = r1.Link { if r1.Prog.As != obj.ANOP { break } } if r1 == nil { continue } p1 := r1.Prog switch p1.As { case s390x.AMOVD, s390x.AMOVW, s390x.AMOVWZ, s390x.AMOVH, s390x.AMOVHZ, s390x.AMOVB, s390x.AMOVBZ: if p1.To.Type != obj.TYPE_REG { continue } default: continue } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { continue } if trymergeopmv(r1) { p.To = p1.To excise(r1) mergeopmv_cnt += 1 } } if gc.Debug['v'] != 0 { gc.Dumpit("Merge operation and move", g.Start, 0) } /* * look for CMP x, y; Branch -> Compare and branch */ if gc.Debugcnb == 0 { goto ret } for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { if (gc.Debugcnb != -1) && (cnb_cnt >= gc.Debugcnb) { break } p = r.Prog r1 = gc.Uniqs(r) if r1 == nil { continue } p1 = r1.Prog switch p.As { case s390x.ACMP: switch p1.As { case s390x.ABCL, s390x.ABC: continue case s390x.ABEQ: t = s390x.ACMPBEQ case s390x.ABGE: t = s390x.ACMPBGE case s390x.ABGT: t = s390x.ACMPBGT case s390x.ABLE: t = s390x.ACMPBLE case s390x.ABLT: t = s390x.ACMPBLT case s390x.ABNE: t = s390x.ACMPBNE default: continue } case s390x.ACMPU: switch p1.As { case s390x.ABCL, s390x.ABC: continue case s390x.ABEQ: t = s390x.ACMPUBEQ case s390x.ABGE: t = s390x.ACMPUBGE case s390x.ABGT: t = s390x.ACMPUBGT case s390x.ABLE: t = s390x.ACMPUBLE case s390x.ABLT: t = s390x.ACMPUBLT case s390x.ABNE: t = s390x.ACMPUBNE default: continue } case s390x.ACMPW, s390x.ACMPWU: continue default: continue } if gc.Debug['D'] != 0 { fmt.Printf("cnb %v; %v -> ", p, p1) } if p1.To.Sym != nil { continue } if p.To.Type == obj.TYPE_REG { p1.As = int16(t) p1.From = p.From p1.Reg = p.To.Reg p1.From3 = nil } else if p.To.Type == obj.TYPE_CONST { switch p.As { case s390x.ACMP, s390x.ACMPW: if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) { continue } case s390x.ACMPU, s390x.ACMPWU: if p.To.Offset >= (1 << 8) { continue } default: } p1.As = int16(t) p1.From = p.From p1.Reg = 0 p1.From3 = new(obj.Addr) *(p1.From3) = p.To } else { continue } if gc.Debug['D'] != 0 { fmt.Printf("%v\n", p1) } cnb_cnt += 1 excise(r) } if gc.Debug['v'] != 0 { gc.Dumpit("compare and branch", g.Start, 0) } ret: gc.Flowend(g) }