// 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) func copyprop(r *gc.Flow) bool { p := r.Prog canSub := false switch p.As { case s390x.AFMOVS, s390x.AFMOVD, s390x.AMOVD: canSub = true default: for rr := gc.Uniqp(r); rr != nil; rr = gc.Uniqp(rr) { if gc.Uniqs(rr) == nil { break } switch copyu(rr.Prog, &p.From, nil) { case _Read, _None: continue } // write if rr.Prog.As == p.As { canSub = true } break } } if !canSub { return false } if copyas(&p.From, &p.To) { return true } gactive++ return copy1(&p.From, &p.To, r.S1, 0) }
// 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. subprop returns true if it modified any instructions. func subprop(r0 *gc.Flow) bool { p := r0.Prog v1 := &p.From if !isReg(v1) { return false } v2 := &p.To if !isReg(v2) { return false } cast := false switch p.As { case s390x.AMOVW, s390x.AMOVWZ, s390x.AMOVH, s390x.AMOVHZ, s390x.AMOVB, s390x.AMOVBZ: cast = true } for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { if gc.Uniqs(r) == nil { break } p = r.Prog switch copyu(p, v1, nil) { case _Write, _ReadWriteDiff: if p.As == obj.ACALL { return false } if (!cast || p.As == r0.Prog.As) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { copysub(&p.To, v1, v2) for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { p = r.Prog copysub(&p.From, v1, v2) copysub1(p, v1, v2) copysub(&p.To, v1, v2) } v1.Reg, v2.Reg = v2.Reg, v1.Reg return true } if cast { return false } case _ReadWriteSame: if cast { return false } } if copyu(p, v2, nil) != _None { return false } } return false }
// is reg guaranteed to be truncated by a previous L instruction? func prevl(r0 *gc.Flow, reg int16) bool { for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { p := r.Prog if p.To.Type == obj.TYPE_REG && p.To.Reg == reg { flags := progflags(p) if flags&gc.RightWrite != 0 { if flags&gc.SizeL != 0 { return true } return false } } } return false }
/* * findinc finds ADD instructions with a constant * argument which falls within the immed_12 range. */ func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow { var r1 *gc.Flow var p *obj.Prog for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) { if gc.Uniqp(r1) != r { return nil } switch copyu(r1.Prog, v, nil) { case 0: /* not touched */ continue case 4: /* set and used */ p = r1.Prog if p.As == arm.AADD { if isdconst(&p.From) { if p.From.Offset > -4096 && p.From.Offset < 4096 { return r1 } } } fallthrough default: return nil } } return nil }
// removeLoadHitStores trys to remove loads that take place // immediately after a store to the same location. Returns // true if load-hit-stores were removed. // // For example: // MOVD R1, 0(R15) // MOVD 0(R15), R2 // Would become: // MOVD R1, 0(R15) // MOVD R1, R2 func removeLoadHitStores(r *gc.Flow) int { n := 0 for ; r != nil; r = r.Link { p := r.Prog if !isStore(p) { continue } for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { pp := rr.Prog if gc.Uniqp(rr) == nil { break } if pp.As == obj.ANOP { continue } if isLoad(pp) && sameStackMem(&p.To, &pp.From) { if size(p.As) >= size(pp.As) && isGPR(&p.From) == isGPR(&pp.To) { pp.From = p.From } } if !isMove(pp) || isStore(pp) { break } if copyau(&p.From, &pp.To) { break } } } return n }
/* * 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. */ func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow { var r1 *gc.Flow for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) { if gc.Uniqs(r1) != r { return nil } switch copyu(r1.Prog, v, nil) { case 1, /* used */ 2: /* read-alter-rewrite */ return nil case 3, /* set */ 4: /* set and used */ return r1 } } return nil }
func conprop(r0 *gc.Flow) { var p *obj.Prog p0 := r0.Prog v0 := &p0.To r := r0 loop: r = gc.Uniqs(r) if r == nil || r == r0 { return } if gc.Uniqp(r) == nil { return } p = r.Prog switch copyu(p, v0, nil) { case 0, // miss 1: // use goto loop case 2, // rar 4: // use and set break case 3: // set if p.As == p0.As { if p.From.Type == p0.From.Type { if p.From.Reg == p0.From.Reg { 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 == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { if p.From.Index == p0.From.Index { excise(r) goto loop } } } } } } } } } }
// copy1 replaces uses of v2 with v1 starting at r and returns true if // all uses were rewritten. func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { if uint32(r.Active) == gactive { return true } r.Active = int32(gactive) for ; r != nil; r = r.S1 { p := r.Prog if f == 0 && gc.Uniqp(r) == nil { // Multiple predecessors; conservatively // assume v1 was set on other path f = 1 } t := copyu(p, v2, nil) switch t { case _ReadWriteSame: return false case _Write: return true case _Read, _ReadWriteDiff: if f != 0 { return false } if copyu(p, v2, v1) != 0 { return false } if t == _ReadWriteDiff { return true } } if f == 0 { switch copyu(p, v1, nil) { case _ReadWriteSame, _ReadWriteDiff, _Write: f = 1 } } if r.S2 != nil { if !copy1(v1, v2, r.S2, f) { return false } } } return true }
/* * 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. */ func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) { if gc.Debug['P'] != 0 { fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1)) } var p *obj.Prog for ; r != nil; r = r.S1 { p = r.Prog if gc.Debug['P'] != 0 { fmt.Printf("%v", p) } if gc.Uniqp(r) == nil { if gc.Debug['P'] != 0 { fmt.Printf("; merge; return\n") } return } if p.As == arm.AMOVW && copyas(&p.From, c1) { if gc.Debug['P'] != 0 { fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1)) } p.From = *v1 } else if copyu(p, v1, nil) > 1 { if gc.Debug['P'] != 0 { fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1)) } return } if gc.Debug['P'] != 0 { fmt.Printf("\n") } if r.S2 != nil { constprop(c1, v1, r.S2) } } }
/* * 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. */ func subprop(r0 *gc.Flow) bool { p := r0.Prog v1 := &p.From if !regtyp(v1) { return false } v2 := &p.To if !regtyp(v2) { return false } for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { if gc.Uniqs(r) == nil { break } p = r.Prog if p.As == obj.AVARDEF || p.As == obj.AVARKILL { continue } if p.Info.Flags&gc.Call != 0 { return false } if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { if p.To.Type == v1.Type { if p.To.Reg == v1.Reg { copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) if p.From.Type == v2.Type { fmt.Printf(" excise") } fmt.Printf("\n") } for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { p = r.Prog copysub(&p.From, v1, v2, true) copysub1(p, v1, v2, true) copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("%v\n", r.Prog) } } v1.Reg, v2.Reg = v2.Reg, v1.Reg if gc.Debug['P'] != 0 { fmt.Printf("%v last\n", r.Prog) } return true } } } if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { break } if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { break } } return false }
func pushback(r0 *gc.Flow) { var r *gc.Flow var b *gc.Flow p0 := r0.Prog for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { p := r.Prog if p.As != obj.ANOP { if !(isReg(&p.From) || isConst(&p.From)) || !isReg(&p.To) { break } if copyu(p, &p0.To, nil) != _None || copyu(p0, &p.To, nil) != _None { break } } if p.As == obj.ACALL { break } b = r } if b == nil { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("no pushback: %v\n", r0.Prog) if r != nil { fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) } } return } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("pushback\n") for r := b; ; r = r.Link { fmt.Printf("\t%v\n", r.Prog) if r == r0 { break } } } t := *r0.Prog for r = gc.Uniqp(r0); ; r = gc.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 p0.From3 = p.From3 p0.Reg = p.Reg p0.RegTo2 = p.RegTo2 if r == b { break } } p0 = r.Prog p0.As = t.As p0.Lineno = t.Lineno p0.From = t.From p0.To = t.To p0.From3 = t.From3 p0.Reg = t.Reg p0.RegTo2 = t.RegTo2 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tafter\n") for r := b; ; r = r.Link { fmt.Printf("\t%v\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. */ func subprop(r0 *gc.Flow) bool { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("subprop %v\n", r0.Prog) } p := r0.Prog v1 := &p.From if !regtyp(v1) { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1)) } return false } v2 := &p.To if !regtyp(v2) { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2)) } return false } for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\t? %v\n", r.Prog) } if gc.Uniqs(r) == nil { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tno unique successor\n") } break } p = r.Prog if p.As == obj.AVARDEF || p.As == obj.AVARKILL { continue } if p.Info.Flags&gc.Call != 0 { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tfound %v; return 0\n", p) } return false } if p.Info.Reguse|p.Info.Regset != 0 { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tfound %v; return 0\n", p) } return false } if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) if p.From.Type == v2.Type && p.From.Reg == v2.Reg { fmt.Printf(" excise") } fmt.Printf("\n") } for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { p = r.Prog copysub(&p.From, v1, v2, true) copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("%v\n", r.Prog) } } v1.Reg, v2.Reg = v2.Reg, v1.Reg if gc.Debug['P'] != 0 { fmt.Printf("%v last\n", r.Prog) } return true } if copyau(&p.From, v2) || copyau(&p.To, v2) { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2)) } break } if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tcopysub failed\n") } break } } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tran off end; return 0\n") } return false }
func pushback(r0 *gc.Flow) { var r *gc.Flow var p *obj.Prog var b *gc.Flow p0 := r0.Prog for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { p = r.Prog if p.As != obj.ANOP { if !regconsttyp(&p.From) || !regtyp(&p.To) { break } if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 { break } } if p.As == obj.ACALL { break } b = r } if b == nil { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("no pushback: %v\n", r0.Prog) if r != nil { fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) } } return } if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("pushback\n") for r := b; ; r = r.Link { fmt.Printf("\t%v\n", r.Prog) if r == r0 { break } } } t := *r0.Prog for r = gc.Uniqp(r0); ; r = gc.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 gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\tafter\n") for r := b; ; r = r.Link { fmt.Printf("\t%v\n", r.Prog) if r == r0 { break } } } }
/* * 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<<y),a,b * .. */ func shiftprop(r *gc.Flow) bool { p := r.Prog if p.To.Type != obj.TYPE_REG { if gc.Debug['P'] != 0 { fmt.Printf("\tBOTCH: result not reg; FAILURE\n") } return false } n := p.To.Reg var a obj.Addr if p.Reg != 0 && p.Reg != p.To.Reg { a.Type = obj.TYPE_REG a.Reg = p.Reg } if gc.Debug['P'] != 0 { fmt.Printf("shiftprop\n%v", p) } r1 := r var p1 *obj.Prog for { /* find first use of shift result; abort if shift operands or result are changed */ r1 = gc.Uniqs(r1) if r1 == nil { if gc.Debug['P'] != 0 { fmt.Printf("\tbranch; FAILURE\n") } return false } if gc.Uniqp(r1) == nil { if gc.Debug['P'] != 0 { fmt.Printf("\tmerge; FAILURE\n") } return false } p1 = r1.Prog if gc.Debug['P'] != 0 { fmt.Printf("\n%v", p1) } switch copyu(p1, &p.To, nil) { case 0: /* not used or set */ if (p.From.Type == obj.TYPE_REG && copyu(p1, &p.From, nil) > 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) { if gc.Debug['P'] != 0 { fmt.Printf("\targs modified; FAILURE\n") } return false } continue case 3: /* set, not used */ { if gc.Debug['P'] != 0 { fmt.Printf("\tBOTCH: noref; FAILURE\n") } return false } } break } /* check whether substitution can be done */ switch p1.As { default: if gc.Debug['P'] != 0 { fmt.Printf("\tnon-dpi; FAILURE\n") } return false case arm.AAND, arm.AEOR, arm.AADD, arm.AADC, arm.AORR, arm.ASUB, arm.ASBC, arm.ARSB, arm.ARSC: if p1.Reg == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && p1.To.Reg == n) { if p1.From.Type != obj.TYPE_REG { if gc.Debug['P'] != 0 { fmt.Printf("\tcan't swap; FAILURE\n") } return false } p1.Reg = p1.From.Reg p1.From.Reg = n switch p1.As { case arm.ASUB: p1.As = arm.ARSB case arm.ARSB: p1.As = arm.ASUB case arm.ASBC: p1.As = arm.ARSC case arm.ARSC: p1.As = arm.ASBC } if gc.Debug['P'] != 0 { fmt.Printf("\t=>%v", p1) } } fallthrough case arm.ABIC, arm.ATST, arm.ACMP, arm.ACMN: if p1.Reg == n { if gc.Debug['P'] != 0 { fmt.Printf("\tcan't swap; FAILURE\n") } return false } if p1.Reg == 0 && p1.To.Reg == n { if gc.Debug['P'] != 0 { fmt.Printf("\tshift result used twice; FAILURE\n") } return false } // case AMVN: if p1.From.Type == obj.TYPE_SHIFT { if gc.Debug['P'] != 0 { fmt.Printf("\tshift result used in shift; FAILURE\n") } return false } if p1.From.Type != obj.TYPE_REG || p1.From.Reg != n { if gc.Debug['P'] != 0 { fmt.Printf("\tBOTCH: where is it used?; FAILURE\n") } return false } } /* check whether shift result is used subsequently */ p2 := p1 if p1.To.Reg != n { var p1 *obj.Prog for { r1 = gc.Uniqs(r1) if r1 == nil { if gc.Debug['P'] != 0 { fmt.Printf("\tinconclusive; FAILURE\n") } return false } p1 = r1.Prog if gc.Debug['P'] != 0 { fmt.Printf("\n%v", p1) } switch copyu(p1, &p.To, nil) { case 0: /* not used or set */ continue case 3: /* set, not used */ break default: /* used */ if gc.Debug['P'] != 0 { fmt.Printf("\treused; FAILURE\n") } return false } break } } /* make the substitution */ p2.From.Reg = 0 o := p.Reg if o == 0 { o = p.To.Reg } o &= 15 switch p.From.Type { case obj.TYPE_CONST: o |= int16(p.From.Offset&0x1f) << 7 case obj.TYPE_REG: o |= 1<<4 | (p.From.Reg&15)<<8 } switch p.As { case arm.ASLL: o |= 0 << 5 case arm.ASRL: o |= 1 << 5 case arm.ASRA: o |= 2 << 5 } p2.From = obj.Addr{} p2.From.Type = obj.TYPE_SHIFT p2.From.Offset = int64(o) if gc.Debug['P'] != 0 { fmt.Printf("\t=>%v\tSUCCEED\n", p2) } return true }
// fuseClear merges memory clear operations. // // Looks for this pattern (sequence of clears): // MOVD R0, n(R15) // MOVD R0, n+8(R15) // MOVD R0, n+16(R15) // Replaces with: // CLEAR $24, n(R15) func fuseClear(r *gc.Flow) int { n := 0 var align int64 var clear *obj.Prog for ; r != nil; r = r.Link { // If there is a branch into the instruction stream then // we can't fuse into previous instructions. if gc.Uniqp(r) == nil { clear = nil } p := r.Prog if p.As == obj.ANOP { continue } if p.As == s390x.AXC { if p.From.Reg == p.To.Reg && p.From.Offset == p.To.Offset { // TODO(mundaym): merge clears? p.As = s390x.ACLEAR p.From.Offset = p.From3.Offset p.From3 = nil p.From.Type = obj.TYPE_CONST p.From.Reg = 0 clear = p } else { clear = nil } continue } // Is our source a constant zero? if !isZero(&p.From) { clear = nil continue } // Are we moving to memory? if p.To.Type != obj.TYPE_MEM || p.To.Index != 0 || p.To.Offset >= 4096 || !(p.To.Name == obj.NAME_NONE || p.To.Name == obj.NAME_AUTO || p.To.Name == obj.NAME_PARAM) { clear = nil continue } size := int64(0) switch p.As { default: clear = nil continue case s390x.AMOVB, s390x.AMOVBZ: size = 1 case s390x.AMOVH, s390x.AMOVHZ: size = 2 case s390x.AMOVW, s390x.AMOVWZ: size = 4 case s390x.AMOVD: size = 8 } // doubleword aligned clears should be kept doubleword // aligned if (size == 8 && align != 8) || (size != 8 && align == 8) { clear = nil } if clear != nil && clear.To.Reg == p.To.Reg && clear.To.Name == p.To.Name && clear.To.Node == p.To.Node && clear.To.Sym == p.To.Sym { min := clear.To.Offset max := clear.To.Offset + clear.From.Offset // previous clear is already clearing this region if min <= p.To.Offset && max >= p.To.Offset+size { excise(r) n++ continue } // merge forwards if max == p.To.Offset { clear.From.Offset += size excise(r) n++ continue } // merge backwards if min-size == p.To.Offset { clear.From.Offset += size clear.To.Offset -= size excise(r) n++ continue } } // transform into clear p.From.Type = obj.TYPE_CONST p.From.Offset = size p.From.Reg = 0 p.As = s390x.ACLEAR clear = p align = size } return n }
// castPropagation tries to eliminate unecessary casts. // // For example: // MOVHZ R1, R2 // uint16 // MOVB R2, 0(R15) // int8 // Can be simplified to: // MOVB R1, 0(R15) func castPropagation(r *gc.Flow) int { n := 0 for ; r != nil; r = r.Link { p := r.Prog if !isMove(p) || !isGPR(&p.To) { continue } // r is a move with a destination register var move *gc.Flow for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { if gc.Uniqp(rr) == nil { // branch target: leave alone break } pp := rr.Prog if isMove(pp) && copyas(&pp.From, &p.To) { if pp.To.Type == obj.TYPE_MEM { if p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_ADDR { break } if p.From.Type == obj.TYPE_CONST && int64(int16(p.From.Offset)) != p.From.Offset { break } } move = rr break } if pp.As == obj.ANOP { continue } break } if move == nil { continue } // we have a move that reads from our destination reg, check if any future // instructions also read from the reg mp := move.Prog if !copyas(&mp.From, &mp.To) { safe := false for rr := gc.Uniqs(move); rr != nil; rr = gc.Uniqs(rr) { if gc.Uniqp(rr) == nil { break } switch copyu(rr.Prog, &p.To, nil) { case _None: continue case _Write: safe = true } break } if !safe { continue } } // at this point we have something like: // MOV* const/mem/reg, reg // MOV* reg, reg/mem // now check if this is a cast that cannot be forward propagated execute := false if p.As == mp.As || isZero(&p.From) || size(p.As) == size(mp.As) { execute = true } else if isGPR(&p.From) && size(p.As) >= size(mp.As) { execute = true } if execute { mp.From = p.From excise(r) n++ } } return n }
// fuseMultiple merges memory loads and stores into load multiple and // store multiple operations. // // Looks for this pattern (sequence of loads or stores): // MOVD R1, 0(R15) // MOVD R2, 8(R15) // MOVD R3, 16(R15) // Replaces with: // STMG R1, R3, 0(R15) func fuseMultiple(r *gc.Flow) int { n := 0 var fused *obj.Prog for ; r != nil; r = r.Link { // If there is a branch into the instruction stream then // we can't fuse into previous instructions. if gc.Uniqp(r) == nil { fused = nil } p := r.Prog isStore := isGPR(&p.From) && isBDMem(&p.To) isLoad := isGPR(&p.To) && isBDMem(&p.From) // are we a candidate? size := int64(0) switch p.As { default: fused = nil continue case obj.ANOP: // skip over nops continue case s390x.AMOVW, s390x.AMOVWZ: size = 4 // TODO(mundaym): 32-bit load multiple is currently not supported // as it requires sign/zero extension. if !isStore { fused = nil continue } case s390x.AMOVD: size = 8 if !isLoad && !isStore { fused = nil continue } } // If we merge two loads/stores with different source/destination Nodes // then we will lose a reference the second Node which means that the // compiler might mark the Node as unused and free its slot on the stack. // TODO(mundaym): allow this by adding a dummy reference to the Node. if fused == nil || fused.From.Node != p.From.Node || fused.From.Type != p.From.Type || fused.To.Node != p.To.Node || fused.To.Type != p.To.Type { fused = p continue } // check two addresses ca := func(a, b *obj.Addr, offset int64) bool { return a.Reg == b.Reg && a.Offset+offset == b.Offset && a.Sym == b.Sym && a.Name == b.Name } switch fused.As { default: fused = p case s390x.AMOVW, s390x.AMOVWZ: if size == 4 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 4) { fused.As = s390x.ASTMY fused.Reg = p.From.Reg excise(r) n++ } else { fused = p } case s390x.AMOVD: if size == 8 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 8) { fused.As = s390x.ASTMG fused.Reg = p.From.Reg excise(r) n++ } else if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, 8) { fused.As = s390x.ALMG fused.Reg = fused.To.Reg fused.To.Reg = p.To.Reg excise(r) n++ } else { fused = p } case s390x.ASTMG, s390x.ASTMY: if (fused.As == s390x.ASTMY && size != 4) || (fused.As == s390x.ASTMG && size != 8) { fused = p continue } offset := size * int64(fused.Reg-fused.From.Reg+1) if fused.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, offset) { fused.Reg = p.From.Reg excise(r) n++ } else { fused = p } case s390x.ALMG: offset := 8 * int64(fused.To.Reg-fused.Reg+1) if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, offset) { fused.To.Reg = p.To.Reg excise(r) n++ } else { fused = p } } } return n }
/* * 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. */ func subprop(r0 *gc.Flow) bool { p := r0.Prog v1 := &p.From if !regtyp(v1) { return false } v2 := &p.To if !regtyp(v2) { return false } for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { fmt.Printf("\t? %v\n", r.Prog) } if gc.Uniqs(r) == nil { break } p = r.Prog if p.As == obj.AVARDEF || p.As == obj.AVARKILL { continue } if p.Info.Flags&gc.Call != 0 { return false } if p.Info.Reguse|p.Info.Regset != 0 { return false } if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) if p.From.Type == v2.Type && p.From.Reg == v2.Reg { fmt.Printf(" excise") } fmt.Printf("\n") } for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { p = r.Prog copysub(&p.From, v1, v2, true) copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("%v\n", r.Prog) } } t := int(v1.Reg) v1.Reg = v2.Reg v2.Reg = int16(t) if gc.Debug['P'] != 0 { fmt.Printf("%v last\n", r.Prog) } return true } if copyau(&p.From, v2) || copyau(&p.To, v2) { break } if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { break } } return false }
// fuseOpMoves looks for moves following 2-operand operations and trys to merge them into // a 3-operand operation. // // For example: // ADD R1, R2 // MOVD R2, R3 // might become // ADD R1, R2, R3 func fuseOpMoves(r *gc.Flow) int { n := 0 for ; r != nil; r = r.Link { p := r.Prog switch p.As { case s390x.AADD: case s390x.ASUB: if isConst(&p.From) && int64(int16(p.From.Offset)) != p.From.Offset { continue } case s390x.ASLW, s390x.ASRW, s390x.ASRAW, s390x.ASLD, s390x.ASRD, s390x.ASRAD, s390x.ARLL, s390x.ARLLG: // ok - p.From will be a reg or a constant case s390x.AOR, s390x.AORN, s390x.AAND, s390x.AANDN, s390x.ANAND, s390x.ANOR, s390x.AXOR, s390x.AMULLW, s390x.AMULLD: if isConst(&p.From) { // these instructions can either use 3 register form // or have an immediate but not both continue } default: continue } if p.Reg != 0 && p.Reg != p.To.Reg { continue } var move *gc.Flow rr := gc.Uniqs(r) for { if rr == nil || gc.Uniqp(rr) == nil || rr == r { break } pp := rr.Prog switch copyu(pp, &p.To, nil) { case _None: rr = gc.Uniqs(rr) continue case _Read: if move == nil && pp.As == s390x.AMOVD && isGPR(&pp.From) && isGPR(&pp.To) { move = rr rr = gc.Uniqs(rr) continue } case _Write: if move == nil { // dead code excise(r) n++ } else { for prev := gc.Uniqp(move); prev != r; prev = gc.Uniqp(prev) { if copyu(prev.Prog, &move.Prog.To, nil) != 0 { move = nil break } } if move == nil { break } p.Reg, p.To.Reg = p.To.Reg, move.Prog.To.Reg excise(move) n++ // clean up if p.From.Reg == p.To.Reg && isCommutative(p.As) { p.From.Reg, p.Reg = p.Reg, 0 } if p.To.Reg == p.Reg { p.Reg = 0 } // we could try again if p has become a 2-operand op // but in testing nothing extra was extracted } } break } } return n }
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) }
// constantPropagation removes redundant constant copies. func constantPropagation(r *gc.Flow) int { n := 0 // find MOV $con,R followed by // another MOV $con,R without // setting R in the interim for ; r != nil; r = r.Link { p := r.Prog if isMove(p) { if !isReg(&p.To) { continue } if !isConst(&p.From) { continue } } else { continue } rr := r for { rr = gc.Uniqs(rr) if rr == nil || rr == r { break } if gc.Uniqp(rr) == nil { break } pp := rr.Prog t := copyu(pp, &p.To, nil) switch t { case _None: continue case _Read: if !isGPR(&pp.From) || !isMove(pp) { continue } if p.From.Type == obj.TYPE_CONST { v := applyCast(p.As, p.From.Offset) if isGPR(&pp.To) { if int64(int32(v)) == v || ((v>>32)<<32) == v { pp.From.Reg = 0 pp.From.Offset = v pp.From.Type = obj.TYPE_CONST n++ } } else if int64(int16(v)) == v { pp.From.Reg = 0 pp.From.Offset = v pp.From.Type = obj.TYPE_CONST n++ } } continue case _Write: if p.As != pp.As || p.From.Type != pp.From.Type { break } if p.From.Type == obj.TYPE_CONST && p.From.Offset == pp.From.Offset { excise(rr) n++ continue } else if p.From.Type == obj.TYPE_FCONST { if p.From.Val.(float64) == pp.From.Val.(float64) { excise(rr) n++ continue } } } break } } return n }
// copy1 replaces uses of v2 with v1 starting at r and returns true if // all uses were rewritten. func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { if uint32(r.Active) == gactive { if gc.Debug['P'] != 0 { fmt.Printf("act set; return 1\n") } return true } r.Active = int32(gactive) if gc.Debug['P'] != 0 { fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) } for ; r != nil; r = r.S1 { p := r.Prog if gc.Debug['P'] != 0 { fmt.Printf("%v", p) } if !f && gc.Uniqp(r) == nil { // Multiple predecessors; conservatively // assume v1 was set on other path f = true if gc.Debug['P'] != 0 { fmt.Printf("; merge; f=%v", f) } } switch t := copyu(p, v2, nil); t { case 2: /* rar, can't split */ if gc.Debug['P'] != 0 { fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) } return false case 3: /* set */ if gc.Debug['P'] != 0 { fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) } return true case 1, /* used, substitute */ 4: /* use and set */ if f { if gc.Debug['P'] == 0 { return false } if t == 4 { fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) } else { fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) } return false } if copyu(p, v2, v1) != 0 { if gc.Debug['P'] != 0 { fmt.Printf("; sub fail; return 0\n") } return false } if gc.Debug['P'] != 0 { fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) } if t == 4 { if gc.Debug['P'] != 0 { fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) } return true } } if !f { t := copyu(p, v1, nil) if t == 2 || t == 3 || t == 4 { f = true if gc.Debug['P'] != 0 { fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) } } } if gc.Debug['P'] != 0 { fmt.Printf("\n") } if r.S2 != nil { if !copy1(v1, v2, r.S2, f) { return false } } } return true }
/* * 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. */ func subprop(r0 *gc.Flow) bool { p := r0.Prog v1 := &p.From if !regtyp(v1) { return false } v2 := &p.To if !regtyp(v2) { return false } for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { if gc.Uniqs(r) == nil { break } p = r.Prog if p.As == obj.AVARDEF || p.As == obj.AVARKILL { continue } if p.Info.Flags&gc.Call != 0 { return false } // TODO(rsc): Whatever invalidated the info should have done this call. proginfo(p) if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG { p.Info.Flags |= gc.RegRead p.Info.Flags &^= (gc.CanRegRead | gc.RightRead) p.Reg = p.To.Reg } switch p.As { case arm.AMULLU, arm.AMULA, arm.AMVN: return false } if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { if p.To.Type == v1.Type { if p.To.Reg == v1.Reg { if p.Scond == arm.C_SCOND_NONE { copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) if p.From.Type == v2.Type { fmt.Printf(" excise") } fmt.Printf("\n") } for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { p = r.Prog copysub(&p.From, v1, v2, true) copysub1(p, v1, v2, true) copysub(&p.To, v1, v2, true) if gc.Debug['P'] != 0 { fmt.Printf("%v\n", r.Prog) } } v1.Reg, v2.Reg = v2.Reg, v1.Reg if gc.Debug['P'] != 0 { fmt.Printf("%v last\n", r.Prog) } return true } } } } if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { break } if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { break } } return false }