1 // Derived from Inferno utils/6c/peep.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6 // Portions Copyright © 1997-1999 Vita Nuova Limited
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8 // Portions Copyright © 2004,2006 Bruce Ellis
9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11 // Portions Copyright © 2009 The Go Authors. All rights reserved.
13 // Permission is hereby granted, free of charge, to any person obtaining a copy
14 // of this software and associated documentation files (the "Software"), to deal
15 // in the Software without restriction, including without limitation the rights
16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 // copies of the Software, and to permit persons to whom the Software is
18 // furnished to do so, subject to the following conditions:
20 // The above copyright notice and this permission notice shall be included in
21 // all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
34 "cmd/compile/internal/gc"
36 "cmd/internal/obj/arm64"
42 func peep(firstp *obj.Prog) {
43 g := gc.Flowstart(firstp, nil)
53 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
54 gc.Dumpit("loop1", g.Start, 0)
58 for r = g.Start; r != nil; r = r.Link {
61 // TODO(minux) Handle smaller moves. arm and amd64
62 // distinguish between moves that *must* sign/zero
63 // extend and moves that don't care so they
64 // can eliminate moves that don't care without
65 // breaking moves that do care. This might let us
66 // simplify or remove the next peep loop, too.
67 if p.As == arm64.AMOVD || p.As == arm64.AFMOVD {
69 // Try to eliminate reg->reg moves
71 if p.From.Type == p.To.Type {
75 } else if subprop(r) && copyprop(r) {
90 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
94 for r := g.Start; r != nil; r = r.Link {
106 if p.To.Type != obj.TYPE_REG {
119 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
122 if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
128 if gc.Debug['D'] > 1 {
129 goto ret /* allow following code improvement to be suppressed */
132 // MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R
133 for r := g.Start; r != nil; r = r.Link {
140 if p.To.Type != obj.TYPE_REG {
143 if p.From.Type != obj.TYPE_CONST {
146 if p.From.Offset < 0 || 4096 <= p.From.Offset {
155 if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm.
158 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
161 if p1.To.Type != obj.TYPE_REG {
164 if gc.Debug['P'] != 0 {
165 fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(p1.As), p, p1)
167 p1.From.Type = obj.TYPE_CONST
173 * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R
174 * when OP can set condition codes correctly
181 func excise(r *gc.Flow) {
183 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
184 fmt.Printf("%v ===delete===\n", p)
190 func regtyp(a *obj.Addr) bool {
191 // TODO(rsc): Floating point register exclusions?
192 return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO
196 * the idea is to substitute
197 * one register for another
198 * from one MOV to another
200 * ADD b, R1 / no use of R2
202 * would be converted to
206 * hopefully, then the former or latter MOV
207 * will be eliminated by copy propagation.
209 * r0 (the argument, not the register) is the MOV at the end of the
210 * above sequences. This returns 1 if it modified any instructions.
212 func subprop(r0 *gc.Flow) bool {
222 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
223 if gc.Uniqs(r) == nil {
227 if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
230 if p.Info.Flags&gc.Call != 0 {
234 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
235 if p.To.Type == v1.Type {
236 if p.To.Reg == v1.Reg {
237 copysub(&p.To, v1, v2, true)
238 if gc.Debug['P'] != 0 {
239 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
240 if p.From.Type == v2.Type {
241 fmt.Printf(" excise")
246 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
248 copysub(&p.From, v1, v2, true)
249 copysub1(p, v1, v2, true)
250 copysub(&p.To, v1, v2, true)
251 if gc.Debug['P'] != 0 {
252 fmt.Printf("%v\n", r.Prog)
256 v1.Reg, v2.Reg = v2.Reg, v1.Reg
257 if gc.Debug['P'] != 0 {
258 fmt.Printf("%v last\n", r.Prog)
265 if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
268 if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) {
277 * The idea is to remove redundant copies.
281 * use v2 return fail (v1->v2 move must remain)
286 * set v2 return success (caller can remove v1->v2 move)
288 func copyprop(r0 *gc.Flow) bool {
293 if gc.Debug['P'] != 0 {
294 fmt.Printf("eliminating self-move: %v\n", r0.Prog)
300 if gc.Debug['P'] != 0 {
301 fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
303 return copy1(v1, v2, r0.S1, false)
306 // copy1 replaces uses of v2 with v1 starting at r and returns 1 if
307 // all uses were rewritten.
308 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
309 if uint32(r.Active) == gactive {
310 if gc.Debug['P'] != 0 {
311 fmt.Printf("act set; return 1\n")
316 r.Active = int32(gactive)
317 if gc.Debug['P'] != 0 {
318 fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
320 for ; r != nil; r = r.S1 {
322 if gc.Debug['P'] != 0 {
325 if !f && gc.Uniqp(r) == nil {
326 // Multiple predecessors; conservatively
327 // assume v1 was set on other path
330 if gc.Debug['P'] != 0 {
331 fmt.Printf("; merge; f=%v", f)
335 switch t := copyu(p, v2, nil); t {
336 case 2: /* rar, can't split */
337 if gc.Debug['P'] != 0 {
338 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
343 if gc.Debug['P'] != 0 {
344 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
348 case 1, /* used, substitute */
351 if gc.Debug['P'] == 0 {
355 fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
357 fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
362 if copyu(p, v2, v1) != 0 {
363 if gc.Debug['P'] != 0 {
364 fmt.Printf("; sub fail; return 0\n")
369 if gc.Debug['P'] != 0 {
370 fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
373 if gc.Debug['P'] != 0 {
374 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
381 t := copyu(p, v1, nil)
382 if t == 2 || t == 3 || t == 4 {
384 if gc.Debug['P'] != 0 {
385 fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
390 if gc.Debug['P'] != 0 {
394 if !copy1(v1, v2, r.S2, f) {
402 // If s==nil, copyu returns the set/use of v in p; otherwise, it
403 // modifies p to replace reads of v with reads of s and returns 0 for
404 // success or non-zero for failure.
406 // If s==nil, copy returns one of the following values:
408 // 2 if v is set and used in one address (read-alter-rewrite;
410 // 3 if v is only set
411 // 4 if v is set in one address and used in another (so addresses
412 // can be rewritten independently)
413 // 0 otherwise (not touched)
414 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
415 if p.From3Type() != obj.TYPE_NONE {
416 // 7g never generates a from3
417 fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
419 if p.RegTo2 != obj.REG_NONE {
420 // 7g never generates a to2
421 fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2)))
426 fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As))
429 case obj.ANOP, /* read p->from, write p->to */
463 if copysub(&p.From, v, s, true) {
467 // Update only indirect uses of v in p->to
468 if !copyas(&p.To, v) {
469 if copysub(&p.To, v, s, true) {
476 if copyas(&p.To, v) {
477 // Fix up implicit from
478 if p.From.Type == obj.TYPE_NONE {
481 if copyau(&p.From, v) {
487 if copyau(&p.From, v) {
490 if copyau(&p.To, v) {
491 // p->to only indirectly uses v
498 /* rar p->from, write p->to or read p->from, rar p->to */
499 if p.From.Type == obj.TYPE_MEM {
500 if copyas(&p.From, v) {
501 // No s!=nil check; need to fail
502 // anyway in that case
507 if copysub(&p.To, v, s, true) {
513 if copyas(&p.To, v) {
516 } else if p.To.Type == obj.TYPE_MEM {
517 if copyas(&p.To, v) {
521 if copysub(&p.From, v, s, true) {
527 if copyau(&p.From, v) {
531 fmt.Printf("copyu: bad %v\n", p)
536 case arm64.AADD, /* read p->from, read p->reg, write p->to */
563 if copysub(&p.From, v, s, true) {
566 if copysub1(p, v, s, true) {
570 // Update only indirect uses of v in p->to
571 if !copyas(&p.To, v) {
572 if copysub(&p.To, v, s, true) {
579 if copyas(&p.To, v) {
581 // Fix up implicit reg (e.g., ADD
582 // R3,R4 -> ADD R3,R4,R4) so we can
583 // update reg and to separately.
587 if copyau(&p.From, v) {
596 if copyau(&p.From, v) {
602 if copyau(&p.To, v) {
619 case obj.ACHECKNIL, /* read p->from */
620 arm64.ACMP, /* read p->from, read p->reg */
624 if copysub(&p.From, v, s, true) {
627 if copysub1(p, v, s, true) {
633 if copyau(&p.From, v) {
641 case arm64.AB: /* read p->to */
643 if copysub(&p.To, v, s, true) {
649 if copyau(&p.To, v) {
654 case obj.ARET: /* funny */
659 // All registers die at this point, so claim
660 // everything is set (and not used).
663 case arm64.ABL: /* funny */
664 if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
669 if copysub(&p.To, v, s, true) {
675 if copyau(&p.To, v) {
680 // R31 is zero, used by DUFFZERO, cannot be substituted.
681 // R16 is ptr to memory, used and set, cannot be substituted.
683 if v.Type == obj.TYPE_REG {
694 // R16, R17 are ptr to src, dst, used and set, cannot be substituted.
695 // R27 is scratch, set by DUFFCOPY, cannot be substituted.
697 if v.Type == obj.TYPE_REG {
698 if v.Reg == 16 || v.Reg == 17 {
720 // copyas returns true if a and v address the same register.
722 // If a is the from operand, this means this operation reads the
723 // register in v. If a is the to operand, this means this operation
724 // writes the register in v.
725 func copyas(a *obj.Addr, v *obj.Addr) bool {
726 return regtyp(v) && a.Type == v.Type && a.Reg == v.Reg
729 // copyau returns true if a either directly or indirectly addresses the
730 // same register as v.
732 // If a is the from operand, this means this operation reads the
733 // register in v. If a is the to operand, this means the operation
734 // either reads or writes the register in v (if !copyas(a, v), then
735 // the operation reads the register in v).
736 func copyau(a *obj.Addr, v *obj.Addr) bool {
740 if v.Type == obj.TYPE_REG {
741 if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
750 // copyau1 returns true if p->reg references the same register as v and v
751 // is a direct reference.
752 func copyau1(p *obj.Prog, v *obj.Addr) bool {
753 return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg
756 // copysub replaces v with s in a if f==true or indicates it if could if f==false.
757 // Returns true on failure to substitute (it always succeeds on arm64).
758 // TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
759 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
760 if f && copyau(a, v) {
766 // copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false.
767 // Returns true on failure to substitute (it always succeeds on arm64).
768 // TODO(dfc) remove unused return value, remove calls with f=false as they do nothing.
769 func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool {
770 if f && copyau1(p1, v) {
776 func sameaddr(a *obj.Addr, v *obj.Addr) bool {
777 if a.Type != v.Type {
780 if regtyp(v) && a.Reg == v.Reg {
783 if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
784 if v.Offset == a.Offset {
791 func smallindir(a *obj.Addr, reg *obj.Addr) bool {
792 return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
795 func stackaddr(a *obj.Addr) bool {
796 return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP