]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/7g: enable peephole optimizer
authorShenghou Ma <minux@golang.org>
Sat, 4 Apr 2015 05:24:36 +0000 (01:24 -0400)
committerMinux Ma <minux@golang.org>
Wed, 8 Apr 2015 08:16:54 +0000 (08:16 +0000)
Based on cmd/9g/peep.go.

Go 1 benchmark comparison:
benchmark                          old ns/op       new ns/op       delta
BenchmarkBinaryTree17              24328574000     18351639000     -24.57%
BenchmarkFannkuch11                17029365000     10817758000     -36.48%
BenchmarkFmtFprintfEmpty           291             223             -23.37%
BenchmarkFmtFprintfString          1073            799             -25.54%
BenchmarkFmtFprintfInt             1024            778             -24.02%
BenchmarkFmtFprintfIntInt          1654            1277            -22.79%
BenchmarkFmtFprintfPrefixedInt     1360            1083            -20.37%
BenchmarkFmtFprintfFloat           2272            1415            -37.72%
BenchmarkFmtManyArgs               5933            4742            -20.07%
BenchmarkGobDecode                 53166003        38584736        -27.43%
BenchmarkGobEncode                 37930156        30074874        -20.71%
BenchmarkGzip                      1880638900      1286832100      -31.57%
BenchmarkGunzip                    386343633       292194480       -24.37%
BenchmarkHTTPClientServer          237077          179776          -24.17%
BenchmarkJSONEncode                101731690       73116925        -28.13%
BenchmarkJSONDecode                344655360       241277600       -29.99%
BenchmarkMandelbrot200             28329778        12950809        -54.29%
BenchmarkGoParse                   21670755        16554244        -23.61%
BenchmarkRegexpMatchEasy0_32       557             484             -13.11%
BenchmarkRegexpMatchEasy0_1K       4687            4832            +3.09%
BenchmarkRegexpMatchEasy1_32       539             483             -10.39%
BenchmarkRegexpMatchEasy1_1K       5100            5080            -0.39%
BenchmarkRegexpMatchMedium_32      796             651             -18.22%
BenchmarkRegexpMatchMedium_1K      233099          182047          -21.90%
BenchmarkRegexpMatchHard_32        13202           9897            -25.03%
BenchmarkRegexpMatchHard_1K        401027          303602          -24.29%
BenchmarkRevcomp                   3837679666      2816546600      -26.61%
BenchmarkTemplate                  440608300       324831040       -26.28%
BenchmarkTimeParse                 1460            1019            -30.21%
BenchmarkTimeFormat                1609            1174            -27.04%

benchmark                         old MB/s     new MB/s     speedup
BenchmarkGobDecode                14.44        19.89        1.38x
BenchmarkGobEncode                20.24        25.52        1.26x
BenchmarkGzip                     10.32        15.08        1.46x
BenchmarkGunzip                   50.23        66.41        1.32x
BenchmarkJSONEncode               19.07        26.54        1.39x
BenchmarkJSONDecode               5.63         8.04         1.43x
BenchmarkGoParse                  2.67         3.50         1.31x
BenchmarkRegexpMatchEasy0_32      57.38        66.05        1.15x
BenchmarkRegexpMatchEasy0_1K      218.47       211.91       0.97x
BenchmarkRegexpMatchEasy1_32      59.29        66.21        1.12x
BenchmarkRegexpMatchEasy1_1K      200.76       201.54       1.00x
BenchmarkRegexpMatchMedium_32     1.26         1.53         1.21x
BenchmarkRegexpMatchMedium_1K     4.39         5.62         1.28x
BenchmarkRegexpMatchHard_32       2.42         3.23         1.33x
BenchmarkRegexpMatchHard_1K       2.55         3.37         1.32x
BenchmarkRevcomp                  66.23        90.24        1.36x
BenchmarkTemplate                 4.40         5.97         1.36x

Fixes #10105.

Change-Id: I353cc9fdf922e431821508c9dbbe4d9a85d64bd4
Signed-off-by: Shenghou Ma <minux@golang.org>
Reviewed-on: https://go-review.googlesource.com/8471
Reviewed-by: Dave Cheney <dave@cheney.net>
src/cmd/7g/peep.go
test/nilptr3.go

index 7faef1a74798ea5790c99ddef7420287a5627085..37fa37615781998fe0980f9472a9db0b1e769782 100644 (file)
@@ -40,7 +40,102 @@ import (
 var gactive uint32
 
 func peep(firstp *obj.Prog) {
-       // TODO(aram)
+       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(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 := (*gc.Flow)(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 */
+       }
+
+       /* 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 excise(r *gc.Flow) {
@@ -57,6 +152,599 @@ func regtyp(a *obj.Addr) bool {
        return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO
 }
 
+/*
+ * 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 := (*obj.Prog)(r0.Prog)
+       v1 := (*obj.Addr)(&p.From)
+       if !regtyp(v1) {
+               return false
+       }
+       v2 := (*obj.Addr)(&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, 1)
+                                       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, 1)
+                                               copysub1(p, v1, v2, 1)
+                                               copysub(&p.To, v1, v2, 1)
+                                               if gc.Debug['P'] != 0 {
+                                                       fmt.Printf("%v\n", r.Prog)
+                                               }
+                                       }
+
+                                       t := int(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) || copyau1(p, v2) || copyau(&p.To, v2) {
+                       break
+               }
+               if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
+                       break
+               }
+       }
+
+       return false
+}
+
+/*
+ * 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(r0 *gc.Flow) bool {
+       p := (*obj.Prog)(r0.Prog)
+       v1 := (*obj.Addr)(&p.From)
+       v2 := (*obj.Addr)(&p.To)
+       if copyas(v1, v2) {
+               if gc.Debug['P'] != 0 {
+                       fmt.Printf("eliminating self-move\n", r0.Prog)
+               }
+               return true
+       }
+
+       gactive++
+       if gc.Debug['P'] != 0 {
+               fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(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.
+func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) 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=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
+       }
+       var t int
+       var p *obj.Prog
+       for ; r != nil; r = r.S1 {
+               p = r.Prog
+               if gc.Debug['P'] != 0 {
+                       fmt.Printf("%v", p)
+               }
+               if f == 0 && gc.Uniqp(r) == nil {
+                       // Multiple predecessors; conservatively
+                       // assume v1 was set on other path
+                       f = 1
+
+                       if gc.Debug['P'] != 0 {
+                               fmt.Printf("; merge; f=%d", f)
+                       }
+               }
+
+               t = copyu(p, v2, nil)
+               switch 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 != 0 {
+                               if gc.Debug['P'] == 0 {
+                                       return false
+                               }
+                               if t == 4 {
+                                       fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
+                               } else {
+                                       fmt.Printf("; %v used and f=%d; 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 == 0 {
+                       t = copyu(p, v1, nil)
+                       if f == 0 && (t == 2 || t == 3 || t == 4) {
+                               f = 1
+                               if gc.Debug['P'] != 0 {
+                                       fmt.Printf("; %v set and !f; f=%d", 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
+}
+
+// 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)
+func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
+       if p.From3.Type != obj.TYPE_NONE {
+               // 7g never generates a from3
+               fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3))
+       }
+       if p.To2.Type != obj.TYPE_NONE {
+               // 7g never generates a to2
+               fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2))
+       }
+
+       switch p.As {
+       default:
+               fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As)))
+               return 2
+
+       case obj.ANOP, /* read p->from, write p->to */
+               arm64.ANEG,
+               arm64.AFNEGD,
+               arm64.AFNEGS,
+               arm64.AFSQRTD,
+               arm64.AFCVTZSD,
+               arm64.AFCVTZSS,
+               arm64.AFCVTZSDW,
+               arm64.AFCVTZSSW,
+               arm64.AFCVTZUD,
+               arm64.AFCVTZUS,
+               arm64.AFCVTZUDW,
+               arm64.AFCVTZUSW,
+               arm64.AFCVTSD,
+               arm64.AFCVTDS,
+               arm64.ASCVTFD,
+               arm64.ASCVTFS,
+               arm64.ASCVTFWD,
+               arm64.ASCVTFWS,
+               arm64.AUCVTFD,
+               arm64.AUCVTFS,
+               arm64.AUCVTFWD,
+               arm64.AUCVTFWS,
+               arm64.AMOVB,
+               arm64.AMOVBU,
+               arm64.AMOVH,
+               arm64.AMOVHU,
+               arm64.AMOVW,
+               arm64.AMOVWU,
+               arm64.AMOVD,
+               arm64.AFMOVS,
+               arm64.AFMOVD:
+               if p.Scond == 0 {
+                       if s != nil {
+                               if copysub(&p.From, v, s, 1) != 0 {
+                                       return 1
+                               }
+
+                               // Update only indirect uses of v in p->to
+                               if !copyas(&p.To, v) {
+                                       if copysub(&p.To, v, s, 1) != 0 {
+                                               return 1
+                                       }
+                               }
+                               return 0
+                       }
+
+                       if copyas(&p.To, v) {
+                               // Fix up implicit from
+                               if p.From.Type == obj.TYPE_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
+               }
+
+               /* rar p->from, write p->to or read p->from, rar p->to */
+               if p.From.Type == obj.TYPE_MEM {
+                       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) != 0 {
+                                       return 1
+                               }
+                               return 0
+                       }
+
+                       if copyas(&p.To, v) {
+                               return 3
+                       }
+               } else if p.To.Type == obj.TYPE_MEM {
+                       if copyas(&p.To, v) {
+                               return 2
+                       }
+                       if s != nil {
+                               if copysub(&p.From, v, s, 1) != 0 {
+                                       return 1
+                               }
+                               return 0
+                       }
+
+                       if copyau(&p.From, v) {
+                               return 1
+                       }
+               } else {
+                       fmt.Printf("copyu: bad %v\n", p)
+               }
+
+               return 0
+
+       case arm64.AADD, /* read p->from, read p->reg, write p->to */
+               arm64.ASUB,
+               arm64.AAND,
+               arm64.AORR,
+               arm64.AEOR,
+               arm64.AMUL,
+               arm64.ASMULL,
+               arm64.AUMULL,
+               arm64.ASMULH,
+               arm64.AUMULH,
+               arm64.ASDIV,
+               arm64.AUDIV,
+               arm64.ALSL,
+               arm64.ALSR,
+               arm64.AASR,
+               arm64.AFADDD,
+               arm64.AFADDS,
+               arm64.AFSUBD,
+               arm64.AFSUBS,
+               arm64.AFMULD,
+               arm64.AFMULS,
+               arm64.AFDIVD,
+               arm64.AFDIVS:
+               if s != nil {
+                       if copysub(&p.From, v, s, 1) != 0 {
+                               return 1
+                       }
+                       if copysub1(p, v, s, 1) != 0 {
+                               return 1
+                       }
+
+                       // Update only indirect uses of v in p->to
+                       if !copyas(&p.To, v) {
+                               if copysub(&p.To, v, s, 1) != 0 {
+                                       return 1
+                               }
+                       }
+                       return 0
+               }
+
+               if copyas(&p.To, v) {
+                       if p.Reg == 0 {
+                               // 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 arm64.ABEQ,
+               arm64.ABNE,
+               arm64.ABGE,
+               arm64.ABLT,
+               arm64.ABGT,
+               arm64.ABLE,
+               arm64.ABLO,
+               arm64.ABLS,
+               arm64.ABHI,
+               arm64.ABHS:
+               return 0
+
+       case obj.ACHECKNIL, /* read p->from */
+               arm64.ACMP, /* read p->from, read p->reg */
+               arm64.AFCMPD,
+               arm64.AFCMPS:
+               if s != nil {
+                       if copysub(&p.From, v, s, 1) != 0 {
+                               return 1
+                       }
+                       return copysub1(p, v, s, 1)
+               }
+
+               if copyau(&p.From, v) {
+                       return 1
+               }
+               if copyau1(p, v) {
+                       return 1
+               }
+               return 0
+
+       case arm64.AB: /* read p->to */
+               if s != nil {
+                       if copysub(&p.To, v, s, 1) != 0 {
+                               return 1
+                       }
+                       return 0
+               }
+
+               if copyau(&p.To, v) {
+                       return 1
+               }
+               return 0
+
+       case obj.ARET: /* funny */
+               if s != nil {
+                       return 0
+               }
+
+               // All registers die at this point, so claim
+               // everything is set (and not used).
+               return 3
+
+       case arm64.ABL: /* funny */
+               if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
+                       return 2
+               }
+
+               if s != nil {
+                       if copysub(&p.To, v, s, 1) != 0 {
+                               return 1
+                       }
+                       return 0
+               }
+
+               if copyau(&p.To, v) {
+                       return 4
+               }
+               return 3
+
+       // R31 is zero, used by DUFFZERO, cannot be substituted.
+       // R16 is ptr to memory, used and set, cannot be substituted.
+       case obj.ADUFFZERO:
+               if v.Type == obj.TYPE_REG {
+                       if v.Reg == 31 {
+                               return 1
+                       }
+                       if v.Reg == 16 {
+                               return 2
+                       }
+               }
+
+               return 0
+
+       // R16, R17 are ptr to src, dst, used and set, cannot be substituted.
+       // R27 is scratch, set by DUFFCOPY, cannot be substituted.
+       case obj.ADUFFCOPY:
+               if v.Type == obj.TYPE_REG {
+                       if v.Reg == 16 || v.Reg == 17 {
+                               return 2
+                       }
+                       if v.Reg == 27 {
+                               return 3
+                       }
+               }
+
+               return 0
+
+       case arm64.AHINT,
+               obj.ATEXT,
+               obj.APCDATA,
+               obj.AFUNCDATA,
+               obj.AVARDEF,
+               obj.AVARKILL:
+               return 0
+       }
+}
+
+// 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.
+func copyas(a *obj.Addr, v *obj.Addr) bool {
+       if regtyp(v) {
+               if a.Type == v.Type {
+                       if a.Reg == v.Reg {
+                               return true
+                       }
+               }
+       }
+       return false
+}
+
+// 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).
+func copyau(a *obj.Addr, v *obj.Addr) bool {
+       if copyas(a, v) {
+               return true
+       }
+       if v.Type == obj.TYPE_REG {
+               if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
+                       if v.Reg == a.Reg {
+                               return true
+                       }
+               }
+       }
+       return false
+}
+
+// copyau1 returns 1 if p->reg references the same register as v and v
+// is a direct reference.
+func copyau1(p *obj.Prog, v *obj.Addr) bool {
+       if regtyp(v) && v.Reg != 0 {
+               if p.Reg == v.Reg {
+                       return true
+               }
+       }
+       return false
+}
+
+// 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 arm64).
+func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
+       if f != 0 {
+               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 arm64).
+func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int {
+       if f != 0 {
+               if copyau1(p1, v) {
+                       p1.Reg = s.Reg
+               }
+       }
+       return 0
+}
+
 func sameaddr(a *obj.Addr, v *obj.Addr) bool {
        if a.Type != v.Type {
                return false
index a62b26240709d4bab16bd800b15e0033796fa5e8..607c6fb9846c0f20475b7a0bb5e405bcf07dccf4 100644 (file)
@@ -1,7 +1,7 @@
 // errorcheck -0 -d=nil
-// Fails on ppc64x and arm64 because of incomplete optimization.
-// See issues 9058 and 10105.
-// +build !ppc64,!ppc64le,!arm64
+// Fails on ppc64x because of incomplete optimization.
+// See issues 9058.
+// +build !ppc64,!ppc64le
 
 // Copyright 2013 The Go Authors.  All rights reserved.
 // Use of this source code is governed by a BSD-style