github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/cmd/compile/internal/mips64/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 mips64
    32  
    33  import (
    34  	"cmd/compile/internal/gc"
    35  	"cmd/internal/obj"
    36  	"cmd/internal/obj/mips"
    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(austin) Handle smaller moves.  arm and amd64
    62  		// distinguish between moves that moves that *must*
    63  		// sign/zero 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 == mips.AMOVV || p.As == mips.AMOVF || p.As == mips.AMOVD {
    68  			if regtyp(&p.To) {
    69  				// Try to eliminate reg->reg moves
    70  				if regtyp(&p.From) {
    71  					if isfreg(&p.From) == isfreg(&p.To) {
    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  				// Convert uses to $0 to uses of R0 and
    83  				// propagate R0
    84  				if regzer(&p.From) != 0 {
    85  					if p.To.Type == obj.TYPE_REG && !isfreg(&p.To) {
    86  						p.From.Type = obj.TYPE_REG
    87  						p.From.Reg = mips.REGZERO
    88  						if copyprop(r) {
    89  							excise(r)
    90  							t++
    91  						} else if subprop(r) && copyprop(r) {
    92  							excise(r)
    93  							t++
    94  						}
    95  					}
    96  				}
    97  			}
    98  		}
    99  	}
   100  
   101  	if t != 0 {
   102  		goto loop1
   103  	}
   104  
   105  	/*
   106  	 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
   107  	 */
   108  	var p1 *obj.Prog
   109  	var r1 *gc.Flow
   110  	for r := (*gc.Flow)(g.Start); r != nil; r = r.Link {
   111  		p = r.Prog
   112  		switch p.As {
   113  		default:
   114  			continue
   115  
   116  		case mips.AMOVH,
   117  			mips.AMOVHU,
   118  			mips.AMOVB,
   119  			mips.AMOVBU,
   120  			mips.AMOVW,
   121  			mips.AMOVWU:
   122  			if p.To.Type != obj.TYPE_REG {
   123  				continue
   124  			}
   125  		}
   126  
   127  		r1 = r.Link
   128  		if r1 == nil {
   129  			continue
   130  		}
   131  		p1 = r1.Prog
   132  		if p1.As != p.As {
   133  			continue
   134  		}
   135  		if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg {
   136  			continue
   137  		}
   138  		if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg {
   139  			continue
   140  		}
   141  		excise(r1)
   142  	}
   143  
   144  	gc.Flowend(g)
   145  }
   146  
   147  func excise(r *gc.Flow) {
   148  	p := (*obj.Prog)(r.Prog)
   149  	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
   150  		fmt.Printf("%v ===delete===\n", p)
   151  	}
   152  	obj.Nopout(p)
   153  	gc.Ostats.Ndelmov++
   154  }
   155  
   156  /*
   157   * regzer returns 1 if a's value is 0 (a is R0 or $0)
   158   */
   159  func regzer(a *obj.Addr) int {
   160  	if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR {
   161  		if a.Sym == nil && a.Reg == 0 {
   162  			if a.Offset == 0 {
   163  				return 1
   164  			}
   165  		}
   166  	}
   167  	if a.Type == obj.TYPE_REG {
   168  		if a.Reg == mips.REGZERO {
   169  			return 1
   170  		}
   171  	}
   172  	return 0
   173  }
   174  
   175  func regtyp(a *obj.Addr) bool {
   176  	// TODO(rsc): Floating point register exclusions?
   177  	return a.Type == obj.TYPE_REG && mips.REG_R0 <= a.Reg && a.Reg <= mips.REG_F31 && a.Reg != mips.REGZERO
   178  }
   179  
   180  func isfreg(a *obj.Addr) bool {
   181  	return mips.REG_F0 <= a.Reg && a.Reg <= mips.REG_F31
   182  }
   183  
   184  /*
   185   * the idea is to substitute
   186   * one register for another
   187   * from one MOV to another
   188   *	MOV	a, R1
   189   *	ADD	b, R1	/ no use of R2
   190   *	MOV	R1, R2
   191   * would be converted to
   192   *	MOV	a, R2
   193   *	ADD	b, R2
   194   *	MOV	R2, R1
   195   * hopefully, then the former or latter MOV
   196   * will be eliminated by copy propagation.
   197   *
   198   * r0 (the argument, not the register) is the MOV at the end of the
   199   * above sequences.  This returns 1 if it modified any instructions.
   200   */
   201  func subprop(r0 *gc.Flow) bool {
   202  	p := (*obj.Prog)(r0.Prog)
   203  	v1 := (*obj.Addr)(&p.From)
   204  	if !regtyp(v1) {
   205  		return false
   206  	}
   207  	v2 := (*obj.Addr)(&p.To)
   208  	if !regtyp(v2) {
   209  		return false
   210  	}
   211  	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
   212  		if gc.Uniqs(r) == nil {
   213  			break
   214  		}
   215  		p = r.Prog
   216  		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
   217  			continue
   218  		}
   219  		if p.Info.Flags&gc.Call != 0 {
   220  			return false
   221  		}
   222  
   223  		if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite {
   224  			if p.To.Type == v1.Type {
   225  				if p.To.Reg == v1.Reg {
   226  					copysub(&p.To, v1, v2, 1)
   227  					if gc.Debug['P'] != 0 {
   228  						fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
   229  						if p.From.Type == v2.Type {
   230  							fmt.Printf(" excise")
   231  						}
   232  						fmt.Printf("\n")
   233  					}
   234  
   235  					for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
   236  						p = r.Prog
   237  						copysub(&p.From, v1, v2, 1)
   238  						copysub1(p, v1, v2, 1)
   239  						copysub(&p.To, v1, v2, 1)
   240  						if gc.Debug['P'] != 0 {
   241  							fmt.Printf("%v\n", r.Prog)
   242  						}
   243  					}
   244  
   245  					t := int(int(v1.Reg))
   246  					v1.Reg = v2.Reg
   247  					v2.Reg = int16(t)
   248  					if gc.Debug['P'] != 0 {
   249  						fmt.Printf("%v last\n", r.Prog)
   250  					}
   251  					return true
   252  				}
   253  			}
   254  		}
   255  
   256  		if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) {
   257  			break
   258  		}
   259  		if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
   260  			break
   261  		}
   262  	}
   263  
   264  	return false
   265  }
   266  
   267  /*
   268   * The idea is to remove redundant copies.
   269   *	v1->v2	F=0
   270   *	(use v2	s/v2/v1/)*
   271   *	set v1	F=1
   272   *	use v2	return fail (v1->v2 move must remain)
   273   *	-----------------
   274   *	v1->v2	F=0
   275   *	(use v2	s/v2/v1/)*
   276   *	set v1	F=1
   277   *	set v2	return success (caller can remove v1->v2 move)
   278   */
   279  func copyprop(r0 *gc.Flow) bool {
   280  	p := (*obj.Prog)(r0.Prog)
   281  	v1 := (*obj.Addr)(&p.From)
   282  	v2 := (*obj.Addr)(&p.To)
   283  	if copyas(v1, v2) {
   284  		if gc.Debug['P'] != 0 {
   285  			fmt.Printf("eliminating self-move: %v\n", r0.Prog)
   286  		}
   287  		return true
   288  	}
   289  
   290  	gactive++
   291  	if gc.Debug['P'] != 0 {
   292  		fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog)
   293  	}
   294  	return copy1(v1, v2, r0.S1, 0)
   295  }
   296  
   297  // copy1 replaces uses of v2 with v1 starting at r and returns 1 if
   298  // all uses were rewritten.
   299  func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
   300  	if uint32(r.Active) == gactive {
   301  		if gc.Debug['P'] != 0 {
   302  			fmt.Printf("act set; return 1\n")
   303  		}
   304  		return true
   305  	}
   306  
   307  	r.Active = int32(gactive)
   308  	if gc.Debug['P'] != 0 {
   309  		fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f)
   310  	}
   311  	var t int
   312  	var p *obj.Prog
   313  	for ; r != nil; r = r.S1 {
   314  		p = r.Prog
   315  		if gc.Debug['P'] != 0 {
   316  			fmt.Printf("%v", p)
   317  		}
   318  		if f == 0 && gc.Uniqp(r) == nil {
   319  			// Multiple predecessors; conservatively
   320  			// assume v1 was set on other path
   321  			f = 1
   322  
   323  			if gc.Debug['P'] != 0 {
   324  				fmt.Printf("; merge; f=%d", f)
   325  			}
   326  		}
   327  
   328  		t = copyu(p, v2, nil)
   329  		switch t {
   330  		case 2: /* rar, can't split */
   331  			if gc.Debug['P'] != 0 {
   332  				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
   333  			}
   334  			return false
   335  
   336  		case 3: /* set */
   337  			if gc.Debug['P'] != 0 {
   338  				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
   339  			}
   340  			return true
   341  
   342  		case 1, /* used, substitute */
   343  			4: /* use and set */
   344  			if f != 0 {
   345  				if gc.Debug['P'] == 0 {
   346  					return false
   347  				}
   348  				if t == 4 {
   349  					fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
   350  				} else {
   351  					fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
   352  				}
   353  				return false
   354  			}
   355  
   356  			if copyu(p, v2, v1) != 0 {
   357  				if gc.Debug['P'] != 0 {
   358  					fmt.Printf("; sub fail; return 0\n")
   359  				}
   360  				return false
   361  			}
   362  
   363  			if gc.Debug['P'] != 0 {
   364  				fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p)
   365  			}
   366  			if t == 4 {
   367  				if gc.Debug['P'] != 0 {
   368  					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
   369  				}
   370  				return true
   371  			}
   372  		}
   373  
   374  		if f == 0 {
   375  			t = copyu(p, v1, nil)
   376  			if f == 0 && (t == 2 || t == 3 || t == 4) {
   377  				f = 1
   378  				if gc.Debug['P'] != 0 {
   379  					fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f)
   380  				}
   381  			}
   382  		}
   383  
   384  		if gc.Debug['P'] != 0 {
   385  			fmt.Printf("\n")
   386  		}
   387  		if r.S2 != nil {
   388  			if !copy1(v1, v2, r.S2, f) {
   389  				return false
   390  			}
   391  		}
   392  	}
   393  
   394  	return true
   395  }
   396  
   397  // If s==nil, copyu returns the set/use of v in p; otherwise, it
   398  // modifies p to replace reads of v with reads of s and returns 0 for
   399  // success or non-zero for failure.
   400  //
   401  // If s==nil, copy returns one of the following values:
   402  // 	1 if v only used
   403  //	2 if v is set and used in one address (read-alter-rewrite;
   404  // 	  can't substitute)
   405  //	3 if v is only set
   406  //	4 if v is set in one address and used in another (so addresses
   407  // 	  can be rewritten independently)
   408  //	0 otherwise (not touched)
   409  func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
   410  	if p.From3Type() != obj.TYPE_NONE {
   411  		// never generates a from3
   412  		fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3))
   413  	}
   414  
   415  	switch p.As {
   416  	default:
   417  		fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As)))
   418  		return 2
   419  
   420  	case obj.ANOP, /* read p->from, write p->to */
   421  		mips.AMOVV,
   422  		mips.AMOVF,
   423  		mips.AMOVD,
   424  		mips.AMOVH,
   425  		mips.AMOVHU,
   426  		mips.AMOVB,
   427  		mips.AMOVBU,
   428  		mips.AMOVW,
   429  		mips.AMOVWU,
   430  		mips.AMOVFD,
   431  		mips.AMOVDF,
   432  		mips.AMOVDW,
   433  		mips.AMOVWD,
   434  		mips.AMOVFW,
   435  		mips.AMOVWF,
   436  		mips.AMOVDV,
   437  		mips.AMOVVD,
   438  		mips.AMOVFV,
   439  		mips.AMOVVF,
   440  		mips.ATRUNCFV,
   441  		mips.ATRUNCDV,
   442  		mips.ATRUNCFW,
   443  		mips.ATRUNCDW:
   444  		if s != nil {
   445  			if copysub(&p.From, v, s, 1) != 0 {
   446  				return 1
   447  			}
   448  
   449  			// Update only indirect uses of v in p->to
   450  			if !copyas(&p.To, v) {
   451  				if copysub(&p.To, v, s, 1) != 0 {
   452  					return 1
   453  				}
   454  			}
   455  			return 0
   456  		}
   457  
   458  		if copyas(&p.To, v) {
   459  			// Fix up implicit from
   460  			if p.From.Type == obj.TYPE_NONE {
   461  				p.From = p.To
   462  			}
   463  			if copyau(&p.From, v) {
   464  				return 4
   465  			}
   466  			return 3
   467  		}
   468  
   469  		if copyau(&p.From, v) {
   470  			return 1
   471  		}
   472  		if copyau(&p.To, v) {
   473  			// p->to only indirectly uses v
   474  			return 1
   475  		}
   476  
   477  		return 0
   478  
   479  	case mips.ASGT, /* read p->from, read p->reg, write p->to */
   480  		mips.ASGTU,
   481  
   482  		mips.AADD,
   483  		mips.AADDU,
   484  		mips.ASUB,
   485  		mips.ASUBU,
   486  		mips.ASLL,
   487  		mips.ASRL,
   488  		mips.ASRA,
   489  		mips.AOR,
   490  		mips.ANOR,
   491  		mips.AAND,
   492  		mips.AXOR,
   493  
   494  		mips.AADDV,
   495  		mips.AADDVU,
   496  		mips.ASUBV,
   497  		mips.ASUBVU,
   498  		mips.ASLLV,
   499  		mips.ASRLV,
   500  		mips.ASRAV,
   501  
   502  		mips.AADDF,
   503  		mips.AADDD,
   504  		mips.ASUBF,
   505  		mips.ASUBD,
   506  		mips.AMULF,
   507  		mips.AMULD,
   508  		mips.ADIVF,
   509  		mips.ADIVD:
   510  		if s != nil {
   511  			if copysub(&p.From, v, s, 1) != 0 {
   512  				return 1
   513  			}
   514  			if copysub1(p, v, s, 1) != 0 {
   515  				return 1
   516  			}
   517  
   518  			// Update only indirect uses of v in p->to
   519  			if !copyas(&p.To, v) {
   520  				if copysub(&p.To, v, s, 1) != 0 {
   521  					return 1
   522  				}
   523  			}
   524  			return 0
   525  		}
   526  
   527  		if copyas(&p.To, v) {
   528  			if p.Reg == 0 {
   529  				// Fix up implicit reg (e.g., ADD
   530  				// R3,R4 -> ADD R3,R4,R4) so we can
   531  				// update reg and to separately.
   532  				p.Reg = p.To.Reg
   533  			}
   534  
   535  			if copyau(&p.From, v) {
   536  				return 4
   537  			}
   538  			if copyau1(p, v) {
   539  				return 4
   540  			}
   541  			return 3
   542  		}
   543  
   544  		if copyau(&p.From, v) {
   545  			return 1
   546  		}
   547  		if copyau1(p, v) {
   548  			return 1
   549  		}
   550  		if copyau(&p.To, v) {
   551  			return 1
   552  		}
   553  		return 0
   554  
   555  	case obj.ACHECKNIL, /* read p->from */
   556  		mips.ABEQ, /* read p->from, read p->reg */
   557  		mips.ABNE,
   558  		mips.ABGTZ,
   559  		mips.ABGEZ,
   560  		mips.ABLTZ,
   561  		mips.ABLEZ,
   562  
   563  		mips.ACMPEQD,
   564  		mips.ACMPEQF,
   565  		mips.ACMPGED,
   566  		mips.ACMPGEF,
   567  		mips.ACMPGTD,
   568  		mips.ACMPGTF,
   569  		mips.ABFPF,
   570  		mips.ABFPT,
   571  
   572  		mips.AMUL,
   573  		mips.AMULU,
   574  		mips.ADIV,
   575  		mips.ADIVU,
   576  		mips.AMULV,
   577  		mips.AMULVU,
   578  		mips.ADIVV,
   579  		mips.ADIVVU:
   580  		if s != nil {
   581  			if copysub(&p.From, v, s, 1) != 0 {
   582  				return 1
   583  			}
   584  			return copysub1(p, v, s, 1)
   585  		}
   586  
   587  		if copyau(&p.From, v) {
   588  			return 1
   589  		}
   590  		if copyau1(p, v) {
   591  			return 1
   592  		}
   593  		return 0
   594  
   595  	case mips.AJMP: /* read p->to */
   596  		if s != nil {
   597  			if copysub(&p.To, v, s, 1) != 0 {
   598  				return 1
   599  			}
   600  			return 0
   601  		}
   602  
   603  		if copyau(&p.To, v) {
   604  			return 1
   605  		}
   606  		return 0
   607  
   608  	case mips.ARET: /* funny */
   609  		if s != nil {
   610  			return 0
   611  		}
   612  
   613  		// All registers die at this point, so claim
   614  		// everything is set (and not used).
   615  		return 3
   616  
   617  	case mips.AJAL: /* funny */
   618  		if v.Type == obj.TYPE_REG {
   619  			// TODO(rsc): REG_R0 and REG_F0 used to be
   620  			// (when register numbers started at 0) exregoffset and exfregoffset,
   621  			// which are unset entirely.
   622  			// It's strange that this handles R0 and F0 differently from the other
   623  			// registers. Possible failure to optimize?
   624  			if mips.REG_R0 < v.Reg && v.Reg <= mips.REG_R31 {
   625  				return 2
   626  			}
   627  			if v.Reg == mips.REGARG {
   628  				return 2
   629  			}
   630  			if mips.REG_F0 < v.Reg && v.Reg <= mips.REG_F31 {
   631  				return 2
   632  			}
   633  		}
   634  
   635  		if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg {
   636  			return 2
   637  		}
   638  
   639  		if s != nil {
   640  			if copysub(&p.To, v, s, 1) != 0 {
   641  				return 1
   642  			}
   643  			return 0
   644  		}
   645  
   646  		if copyau(&p.To, v) {
   647  			return 4
   648  		}
   649  		return 3
   650  
   651  	// R0 is zero, used by DUFFZERO, cannot be substituted.
   652  	// R1 is ptr to memory, used and set, cannot be substituted.
   653  	case obj.ADUFFZERO:
   654  		if v.Type == obj.TYPE_REG {
   655  			if v.Reg == 0 {
   656  				return 1
   657  			}
   658  			if v.Reg == 1 {
   659  				return 2
   660  			}
   661  		}
   662  
   663  		return 0
   664  
   665  	// R1, R2 are ptr to src, dst, used and set, cannot be substituted.
   666  	// R3 is scratch, set by DUFFCOPY, cannot be substituted.
   667  	case obj.ADUFFCOPY:
   668  		if v.Type == obj.TYPE_REG {
   669  			if v.Reg == 1 || v.Reg == 2 {
   670  				return 2
   671  			}
   672  			if v.Reg == 3 {
   673  				return 3
   674  			}
   675  		}
   676  
   677  		return 0
   678  
   679  	case obj.ATEXT: /* funny */
   680  		if v.Type == obj.TYPE_REG {
   681  			if v.Reg == mips.REGARG {
   682  				return 3
   683  			}
   684  		}
   685  		return 0
   686  
   687  	case obj.APCDATA,
   688  		obj.AFUNCDATA,
   689  		obj.AVARDEF,
   690  		obj.AVARKILL,
   691  		obj.AUSEFIELD:
   692  		return 0
   693  	}
   694  }
   695  
   696  // copyas returns 1 if a and v address the same register.
   697  //
   698  // If a is the from operand, this means this operation reads the
   699  // register in v.  If a is the to operand, this means this operation
   700  // writes the register in v.
   701  func copyas(a *obj.Addr, v *obj.Addr) bool {
   702  	if regtyp(v) {
   703  		if a.Type == v.Type {
   704  			if a.Reg == v.Reg {
   705  				return true
   706  			}
   707  		}
   708  	}
   709  	return false
   710  }
   711  
   712  // copyau returns 1 if a either directly or indirectly addresses the
   713  // same register as v.
   714  //
   715  // If a is the from operand, this means this operation reads the
   716  // register in v.  If a is the to operand, this means the operation
   717  // either reads or writes the register in v (if !copyas(a, v), then
   718  // the operation reads the register in v).
   719  func copyau(a *obj.Addr, v *obj.Addr) bool {
   720  	if copyas(a, v) {
   721  		return true
   722  	}
   723  	if v.Type == obj.TYPE_REG {
   724  		if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) {
   725  			if v.Reg == a.Reg {
   726  				return true
   727  			}
   728  		}
   729  	}
   730  	return false
   731  }
   732  
   733  // copyau1 returns 1 if p->reg references the same register as v and v
   734  // is a direct reference.
   735  func copyau1(p *obj.Prog, v *obj.Addr) bool {
   736  	if regtyp(v) && v.Reg != 0 {
   737  		if p.Reg == v.Reg {
   738  			return true
   739  		}
   740  	}
   741  	return false
   742  }
   743  
   744  // copysub replaces v with s in a if f!=0 or indicates it if could if f==0.
   745  // Returns 1 on failure to substitute (it always succeeds on mips).
   746  func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
   747  	if f != 0 {
   748  		if copyau(a, v) {
   749  			a.Reg = s.Reg
   750  		}
   751  	}
   752  	return 0
   753  }
   754  
   755  // copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0.
   756  // Returns 1 on failure to substitute (it always succeeds on mips).
   757  func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int {
   758  	if f != 0 {
   759  		if copyau1(p1, v) {
   760  			p1.Reg = s.Reg
   761  		}
   762  	}
   763  	return 0
   764  }
   765  
   766  func sameaddr(a *obj.Addr, v *obj.Addr) bool {
   767  	if a.Type != v.Type {
   768  		return false
   769  	}
   770  	if regtyp(v) && a.Reg == v.Reg {
   771  		return true
   772  	}
   773  	if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM {
   774  		if v.Offset == a.Offset {
   775  			return true
   776  		}
   777  	}
   778  	return false
   779  }
   780  
   781  func smallindir(a *obj.Addr, reg *obj.Addr) bool {
   782  	return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096
   783  }
   784  
   785  func stackaddr(a *obj.Addr) bool {
   786  	return a.Type == obj.TYPE_REG && a.Reg == mips.REGSP
   787  }