github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/7g/peep.go (about)

     1  // Derived from Inferno utils/6c/peep.c
     2  // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c
     3  //
     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.
    12  //
    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:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    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
    29  // THE SOFTWARE.
    30  
    31  package main
    32  
    33  import (
    34  	"cmd/internal/gc"
    35  	"cmd/internal/obj"
    36  	"cmd/internal/obj/arm64"
    37  	"fmt"
    38  )
    39  
    40  var gactive uint32
    41  
    42  func peep(firstp *obj.Prog) {
    43  	g := (*gc.Graph)(gc.Flowstart(firstp, nil))
    44  	if g == nil {
    45  		return
    46  	}
    47  	gactive = 0
    48  
    49  	var p *obj.Prog
    50  	var r *gc.Flow
    51  	var t int
    52  loop1:
    53  	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
    54  		gc.Dumpit("loop1", g.Start, 0)
    55  	}
    56  
    57  	t = 0
    58  	for r = g.Start; r != nil; r = r.Link {
    59  		p = r.Prog
    60  
    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 {
    68  			if regtyp(&p.To) {
    69  				// Try to eliminate reg->reg moves
    70  				if regtyp(&p.From) {
    71  					if p.From.Type == p.To.Type {
    72  						if copyprop(r) {
    73  							excise(r)
    74  							t++
    75  						} else if subprop(r) && copyprop(r) {
    76  							excise(r)
    77  							t++
    78  						}
    79  					}
    80  				}
    81  			}
    82  		}
    83  	}
    84  
    85  	if t != 0 {
    86  		goto loop1
    87  	}
    88  
    89  	/*
    90  	 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
    91  	 */
    92  	var p1 *obj.Prog
    93  	var r1 *gc.Flow
    94  	for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
    95  		p = r.Prog
    96  		switch p.As {
    97  		default:
    98  			continue
    99  
   100  		case arm64.AMOVH,
   101  			arm64.AMOVHU,
   102  			arm64.AMOVB,
   103  			arm64.AMOVBU,
   104  			arm64.AMOVW,
   105  			arm64.AMOVWU:
   106  			if p.To.Type != obj.TYPE_REG {
   107  				continue
   108  			}
   109  		}
   110  
   111  		r1 = r.Link
   112  		if r1 == nil {
   113  			continue
   114  		}
   115  		p1 = r1.Prog
   116  		if p1.As != p.As {
   117  			continue
   118  		}
   119  		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
   120  			continue
   121  		}
   122  		if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
   123  			continue
   124  		}
   125  		excise(r1)
   126  	}
   127  
   128  	if gc.Debug['D'] > 1 {
   129  		goto ret /* allow following code improvement to be suppressed */
   130  	}
   131  
   132  	// MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R
   133  	for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
   134  		p = r.Prog
   135  		switch p.As {
   136  		default:
   137  			continue
   138  
   139  		case arm64.AMOVD:
   140  			if p.To.Type != obj.TYPE_REG {
   141  				continue
   142  			}
   143  			if p.From.Type != obj.TYPE_CONST {
   144  				continue
   145  			}
   146  			if p.From.Offset < 0 || 4096 <= p.From.Offset {
   147  				continue
   148  			}
   149  		}
   150  		r1 = r.Link
   151  		if r1 == nil {
   152  			continue
   153  		}
   154  		p1 = r1.Prog
   155  		if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm.
   156  			continue
   157  		}
   158  		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
   159  			continue
   160  		}
   161  		if p1.To.Type != obj.TYPE_REG {
   162  			continue
   163  		}
   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(int(p1.As)), p, p1)
   166  		}
   167  		p1.From.Type = obj.TYPE_CONST
   168  		p1.From = p.From
   169  		excise(r)
   170  	}
   171  
   172  	/* TODO(minux):
   173  	 * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R
   174  	 * when OP can set condition codes correctly
   175  	 */
   176  
   177  ret:
   178  	gc.Flowend(g)
   179  }
   180  
   181  func excise(r *gc.Flow) {
   182  	p := (*obj.Prog)(r.Prog)
   183  	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
   184  		fmt.Printf("%v ===delete===\n", p)
   185  	}
   186  	obj.Nopout(p)
   187  	gc.Ostats.Ndelmov++
   188  }
   189  
   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
   193  }
   194  
   195  /*
   196   * the idea is to substitute
   197   * one register for another
   198   * from one MOV to another
   199   *	MOV	a, R1
   200   *	ADD	b, R1	/ no use of R2
   201   *	MOV	R1, R2
   202   * would be converted to
   203   *	MOV	a, R2
   204   *	ADD	b, R2
   205   *	MOV	R2, R1
   206   * hopefully, then the former or latter MOV
   207   * will be eliminated by copy propagation.
   208   *
   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.
   211   */
   212  func subprop(r0 *gc.Flow) bool {
   213  	p := (*obj.Prog)(r0.Prog)
   214  	v1 := (*obj.Addr)(&p.From)
   215  	if !regtyp(v1) {
   216  		return false
   217  	}
   218  	v2 := (*obj.Addr)(&p.To)
   219  	if !regtyp(v2) {
   220  		return false
   221  	}
   222  	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
   223  		if gc.Uniqs(r) == nil {
   224  			break
   225  		}
   226  		p = r.Prog
   227  		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
   228  			continue
   229  		}
   230  		if p.Info.Flags&gc.Call != 0 {
   231  			return false
   232  		}
   233  
   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, 1)
   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")
   242  						}
   243  						fmt.Printf("\n")
   244  					}
   245  
   246  					for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
   247  						p = r.Prog
   248  						copysub(&p.From, v1, v2, 1)
   249  						copysub1(p, v1, v2, 1)
   250  						copysub(&p.To, v1, v2, 1)
   251  						if gc.Debug['P'] != 0 {
   252  							fmt.Printf("%v\n", r.Prog)
   253  						}
   254  					}
   255  
   256  					t := int(int(v1.Reg))
   257  					v1.Reg = v2.Reg
   258  					v2.Reg = int16(t)
   259  					if gc.Debug['P'] != 0 {
   260  						fmt.Printf("%v last\n", r.Prog)
   261  					}
   262  					return true
   263  				}
   264  			}
   265  		}
   266  
   267  		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
   268  			break
   269  		}
   270  		if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
   271  			break
   272  		}
   273  	}
   274  
   275  	return false
   276  }
   277  
   278  /*
   279   * The idea is to remove redundant copies.
   280   *	v1->v2	F=0
   281   *	(use v2	s/v2/v1/)*
   282   *	set v1	F=1
   283   *	use v2	return fail (v1->v2 move must remain)
   284   *	-----------------
   285   *	v1->v2	F=0
   286   *	(use v2	s/v2/v1/)*
   287   *	set v1	F=1
   288   *	set v2	return success (caller can remove v1->v2 move)
   289   */
   290  func copyprop(r0 *gc.Flow) bool {
   291  	p := (*obj.Prog)(r0.Prog)
   292  	v1 := (*obj.Addr)(&p.From)
   293  	v2 := (*obj.Addr)(&p.To)
   294  	if copyas(v1, v2) {
   295  		if gc.Debug['P'] != 0 {
   296  			fmt.Printf("eliminating self-move\n", r0.Prog)
   297  		}
   298  		return true
   299  	}
   300  
   301  	gactive++
   302  	if gc.Debug['P'] != 0 {
   303  		fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
   304  	}
   305  	return copy1(v1, v2, r0.S1, 0)
   306  }
   307  
   308  // copy1 replaces uses of v2 with v1 starting at r and returns 1 if
   309  // all uses were rewritten.
   310  func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
   311  	if uint32(r.Active) == gactive {
   312  		if gc.Debug['P'] != 0 {
   313  			fmt.Printf("act set; return 1\n")
   314  		}
   315  		return true
   316  	}
   317  
   318  	r.Active = int32(gactive)
   319  	if gc.Debug['P'] != 0 {
   320  		fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
   321  	}
   322  	var t int
   323  	var p *obj.Prog
   324  	for ; r != nil; r = r.S1 {
   325  		p = r.Prog
   326  		if gc.Debug['P'] != 0 {
   327  			fmt.Printf("%v", p)
   328  		}
   329  		if f == 0 && gc.Uniqp(r) == nil {
   330  			// Multiple predecessors; conservatively
   331  			// assume v1 was set on other path
   332  			f = 1
   333  
   334  			if gc.Debug['P'] != 0 {
   335  				fmt.Printf("; merge; f=%d", f)
   336  			}
   337  		}
   338  
   339  		t = copyu(p, v2, nil)
   340  		switch t {
   341  		case 2: /* rar, can't split */
   342  			if gc.Debug['P'] != 0 {
   343  				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
   344  			}
   345  			return false
   346  
   347  		case 3: /* set */
   348  			if gc.Debug['P'] != 0 {
   349  				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
   350  			}
   351  			return true
   352  
   353  		case 1, /* used, substitute */
   354  			4: /* use and set */
   355  			if f != 0 {
   356  				if gc.Debug['P'] == 0 {
   357  					return false
   358  				}
   359  				if t == 4 {
   360  					fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
   361  				} else {
   362  					fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
   363  				}
   364  				return false
   365  			}
   366  
   367  			if copyu(p, v2, v1) != 0 {
   368  				if gc.Debug['P'] != 0 {
   369  					fmt.Printf("; sub fail; return 0\n")
   370  				}
   371  				return false
   372  			}
   373  
   374  			if gc.Debug['P'] != 0 {
   375  				fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
   376  			}
   377  			if t == 4 {
   378  				if gc.Debug['P'] != 0 {
   379  					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
   380  				}
   381  				return true
   382  			}
   383  		}
   384  
   385  		if f == 0 {
   386  			t = copyu(p, v1, nil)
   387  			if f == 0 && (t == 2 || t == 3 || t == 4) {
   388  				f = 1
   389  				if gc.Debug['P'] != 0 {
   390  					fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f)
   391  				}
   392  			}
   393  		}
   394  
   395  		if gc.Debug['P'] != 0 {
   396  			fmt.Printf("\n")
   397  		}
   398  		if r.S2 != nil {
   399  			if !copy1(v1, v2, r.S2, f) {
   400  				return false
   401  			}
   402  		}
   403  	}
   404  
   405  	return true
   406  }
   407  
   408  // If s==nil, copyu returns the set/use of v in p; otherwise, it
   409  // modifies p to replace reads of v with reads of s and returns 0 for
   410  // success or non-zero for failure.
   411  //
   412  // If s==nil, copy returns one of the following values:
   413  //	1 if v only used
   414  //	2 if v is set and used in one address (read-alter-rewrite;
   415  //	  can't substitute)
   416  //	3 if v is only set
   417  //	4 if v is set in one address and used in another (so addresses
   418  //	  can be rewritten independently)
   419  //	0 otherwise (not touched)
   420  func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
   421  	if p.From3.Type != obj.TYPE_NONE {
   422  		// 7g never generates a from3
   423  		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3))
   424  	}
   425  	if p.To2.Type != obj.TYPE_NONE {
   426  		// 7g never generates a to2
   427  		fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2))
   428  	}
   429  
   430  	switch p.As {
   431  	default:
   432  		fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As)))
   433  		return 2
   434  
   435  	case obj.ANOP, /* read p->from, write p->to */
   436  		arm64.ANEG,
   437  		arm64.AFNEGD,
   438  		arm64.AFNEGS,
   439  		arm64.AFSQRTD,
   440  		arm64.AFCVTZSD,
   441  		arm64.AFCVTZSS,
   442  		arm64.AFCVTZSDW,
   443  		arm64.AFCVTZSSW,
   444  		arm64.AFCVTZUD,
   445  		arm64.AFCVTZUS,
   446  		arm64.AFCVTZUDW,
   447  		arm64.AFCVTZUSW,
   448  		arm64.AFCVTSD,
   449  		arm64.AFCVTDS,
   450  		arm64.ASCVTFD,
   451  		arm64.ASCVTFS,
   452  		arm64.ASCVTFWD,
   453  		arm64.ASCVTFWS,
   454  		arm64.AUCVTFD,
   455  		arm64.AUCVTFS,
   456  		arm64.AUCVTFWD,
   457  		arm64.AUCVTFWS,
   458  		arm64.AMOVB,
   459  		arm64.AMOVBU,
   460  		arm64.AMOVH,
   461  		arm64.AMOVHU,
   462  		arm64.AMOVW,
   463  		arm64.AMOVWU,
   464  		arm64.AMOVD,
   465  		arm64.AFMOVS,
   466  		arm64.AFMOVD:
   467  		if p.Scond == 0 {
   468  			if s != nil {
   469  				if copysub(&p.From, v, s, 1) != 0 {
   470  					return 1
   471  				}
   472  
   473  				// Update only indirect uses of v in p->to
   474  				if !copyas(&p.To, v) {
   475  					if copysub(&p.To, v, s, 1) != 0 {
   476  						return 1
   477  					}
   478  				}
   479  				return 0
   480  			}
   481  
   482  			if copyas(&p.To, v) {
   483  				// Fix up implicit from
   484  				if p.From.Type == obj.TYPE_NONE {
   485  					p.From = p.To
   486  				}
   487  				if copyau(&p.From, v) {
   488  					return 4
   489  				}
   490  				return 3
   491  			}
   492  
   493  			if copyau(&p.From, v) {
   494  				return 1
   495  			}
   496  			if copyau(&p.To, v) {
   497  				// p->to only indirectly uses v
   498  				return 1
   499  			}
   500  
   501  			return 0
   502  		}
   503  
   504  		/* rar p->from, write p->to or read p->from, rar p->to */
   505  		if p.From.Type == obj.TYPE_MEM {
   506  			if copyas(&p.From, v) {
   507  				// No s!=nil check; need to fail
   508  				// anyway in that case
   509  				return 2
   510  			}
   511  
   512  			if s != nil {
   513  				if copysub(&p.To, v, s, 1) != 0 {
   514  					return 1
   515  				}
   516  				return 0
   517  			}
   518  
   519  			if copyas(&p.To, v) {
   520  				return 3
   521  			}
   522  		} else if p.To.Type == obj.TYPE_MEM {
   523  			if copyas(&p.To, v) {
   524  				return 2
   525  			}
   526  			if s != nil {
   527  				if copysub(&p.From, v, s, 1) != 0 {
   528  					return 1
   529  				}
   530  				return 0
   531  			}
   532  
   533  			if copyau(&p.From, v) {
   534  				return 1
   535  			}
   536  		} else {
   537  			fmt.Printf("copyu: bad %v\n", p)
   538  		}
   539  
   540  		return 0
   541  
   542  	case arm64.AADD, /* read p->from, read p->reg, write p->to */
   543  		arm64.ASUB,
   544  		arm64.AAND,
   545  		arm64.AORR,
   546  		arm64.AEOR,
   547  		arm64.AMUL,
   548  		arm64.ASMULL,
   549  		arm64.AUMULL,
   550  		arm64.ASMULH,
   551  		arm64.AUMULH,
   552  		arm64.ASDIV,
   553  		arm64.AUDIV,
   554  		arm64.ALSL,
   555  		arm64.ALSR,
   556  		arm64.AASR,
   557  		arm64.AFADDD,
   558  		arm64.AFADDS,
   559  		arm64.AFSUBD,
   560  		arm64.AFSUBS,
   561  		arm64.AFMULD,
   562  		arm64.AFMULS,
   563  		arm64.AFDIVD,
   564  		arm64.AFDIVS:
   565  		if s != nil {
   566  			if copysub(&p.From, v, s, 1) != 0 {
   567  				return 1
   568  			}
   569  			if copysub1(p, v, s, 1) != 0 {
   570  				return 1
   571  			}
   572  
   573  			// Update only indirect uses of v in p->to
   574  			if !copyas(&p.To, v) {
   575  				if copysub(&p.To, v, s, 1) != 0 {
   576  					return 1
   577  				}
   578  			}
   579  			return 0
   580  		}
   581  
   582  		if copyas(&p.To, v) {
   583  			if p.Reg == 0 {
   584  				// Fix up implicit reg (e.g., ADD
   585  				// R3,R4 -> ADD R3,R4,R4) so we can
   586  				// update reg and to separately.
   587  				p.Reg = p.To.Reg
   588  			}
   589  
   590  			if copyau(&p.From, v) {
   591  				return 4
   592  			}
   593  			if copyau1(p, v) {
   594  				return 4
   595  			}
   596  			return 3
   597  		}
   598  
   599  		if copyau(&p.From, v) {
   600  			return 1
   601  		}
   602  		if copyau1(p, v) {
   603  			return 1
   604  		}
   605  		if copyau(&p.To, v) {
   606  			return 1
   607  		}
   608  		return 0
   609  
   610  	case arm64.ABEQ,
   611  		arm64.ABNE,
   612  		arm64.ABGE,
   613  		arm64.ABLT,
   614  		arm64.ABGT,
   615  		arm64.ABLE,
   616  		arm64.ABLO,
   617  		arm64.ABLS,
   618  		arm64.ABHI,
   619  		arm64.ABHS:
   620  		return 0
   621  
   622  	case obj.ACHECKNIL, /* read p->from */
   623  		arm64.ACMP, /* read p->from, read p->reg */
   624  		arm64.AFCMPD,
   625  		arm64.AFCMPS:
   626  		if s != nil {
   627  			if copysub(&p.From, v, s, 1) != 0 {
   628  				return 1
   629  			}
   630  			return copysub1(p, v, s, 1)
   631  		}
   632  
   633  		if copyau(&p.From, v) {
   634  			return 1
   635  		}
   636  		if copyau1(p, v) {
   637  			return 1
   638  		}
   639  		return 0
   640  
   641  	case arm64.AB: /* read p->to */
   642  		if s != nil {
   643  			if copysub(&p.To, v, s, 1) != 0 {
   644  				return 1
   645  			}
   646  			return 0
   647  		}
   648  
   649  		if copyau(&p.To, v) {
   650  			return 1
   651  		}
   652  		return 0
   653  
   654  	case obj.ARET: /* funny */
   655  		if s != nil {
   656  			return 0
   657  		}
   658  
   659  		// All registers die at this point, so claim
   660  		// everything is set (and not used).
   661  		return 3
   662  
   663  	case arm64.ABL: /* funny */
   664  		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
   665  			return 2
   666  		}
   667  
   668  		if s != nil {
   669  			if copysub(&p.To, v, s, 1) != 0 {
   670  				return 1
   671  			}
   672  			return 0
   673  		}
   674  
   675  		if copyau(&p.To, v) {
   676  			return 4
   677  		}
   678  		return 3
   679  
   680  	// R31 is zero, used by DUFFZERO, cannot be substituted.
   681  	// R16 is ptr to memory, used and set, cannot be substituted.
   682  	case obj.ADUFFZERO:
   683  		if v.Type == obj.TYPE_REG {
   684  			if v.Reg == 31 {
   685  				return 1
   686  			}
   687  			if v.Reg == 16 {
   688  				return 2
   689  			}
   690  		}
   691  
   692  		return 0
   693  
   694  	// R16, R17 are ptr to src, dst, used and set, cannot be substituted.
   695  	// R27 is scratch, set by DUFFCOPY, cannot be substituted.
   696  	case obj.ADUFFCOPY:
   697  		if v.Type == obj.TYPE_REG {
   698  			if v.Reg == 16 || v.Reg == 17 {
   699  				return 2
   700  			}
   701  			if v.Reg == 27 {
   702  				return 3
   703  			}
   704  		}
   705  
   706  		return 0
   707  
   708  	case arm64.AHINT,
   709  		obj.ATEXT,
   710  		obj.APCDATA,
   711  		obj.AFUNCDATA,
   712  		obj.AVARDEF,
   713  		obj.AVARKILL:
   714  		return 0
   715  	}
   716  }
   717  
   718  // copyas returns 1 if a and v address the same register.
   719  //
   720  // If a is the from operand, this means this operation reads the
   721  // register in v. If a is the to operand, this means this operation
   722  // writes the register in v.
   723  func copyas(a *obj.Addr, v *obj.Addr) bool {
   724  	if regtyp(v) {
   725  		if a.Type == v.Type {
   726  			if a.Reg == v.Reg {
   727  				return true
   728  			}
   729  		}
   730  	}
   731  	return false
   732  }
   733  
   734  // copyau returns 1 if a either directly or indirectly addresses the
   735  // same register as v.
   736  //
   737  // If a is the from operand, this means this operation reads the
   738  // register in v. If a is the to operand, this means the operation
   739  // either reads or writes the register in v (if !copyas(a, v), then
   740  // the operation reads the register in v).
   741  func copyau(a *obj.Addr, v *obj.Addr) bool {
   742  	if copyas(a, v) {
   743  		return true
   744  	}
   745  	if v.Type == obj.TYPE_REG {
   746  		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
   747  			if v.Reg == a.Reg {
   748  				return true
   749  			}
   750  		}
   751  	}
   752  	return false
   753  }
   754  
   755  // copyau1 returns 1 if p->reg references the same register as v and v
   756  // is a direct reference.
   757  func copyau1(p *obj.Prog, v *obj.Addr) bool {
   758  	if regtyp(v) && v.Reg != 0 {
   759  		if p.Reg == v.Reg {
   760  			return true
   761  		}
   762  	}
   763  	return false
   764  }
   765  
   766  // copysub replaces v with s in a if f!=0 or indicates it if could if f==0.
   767  // Returns 1 on failure to substitute (it always succeeds on arm64).
   768  func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
   769  	if f != 0 {
   770  		if copyau(a, v) {
   771  			a.Reg = s.Reg
   772  		}
   773  	}
   774  	return 0
   775  }
   776  
   777  // copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0.
   778  // Returns 1 on failure to substitute (it always succeeds on arm64).
   779  func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int {
   780  	if f != 0 {
   781  		if copyau1(p1, v) {
   782  			p1.Reg = s.Reg
   783  		}
   784  	}
   785  	return 0
   786  }
   787  
   788  func sameaddr(a *obj.Addr, v *obj.Addr) bool {
   789  	if a.Type != v.Type {
   790  		return false
   791  	}
   792  	if regtyp(v) && a.Reg == v.Reg {
   793  		return true
   794  	}
   795  	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
   796  		if v.Offset == a.Offset {
   797  			return true
   798  		}
   799  	}
   800  	return false
   801  }
   802  
   803  func smallindir(a *obj.Addr, reg *obj.Addr) bool {
   804  	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
   805  }
   806  
   807  func stackaddr(a *obj.Addr) bool {
   808  	return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP
   809  }