github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/mips64/peep.go (about)

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