github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/x86/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 x86
    32  
    33  import (
    34  	"cmd/compile/internal/gc"
    35  	"cmd/internal/obj"
    36  	"cmd/internal/obj/x86"
    37  	"fmt"
    38  )
    39  
    40  const (
    41  	REGEXT      = 0
    42  	exregoffset = x86.REG_DI
    43  )
    44  
    45  var gactive uint32
    46  
    47  // do we need the carry bit
    48  func needc(p *obj.Prog) bool {
    49  	for p != nil {
    50  		if p.Info.Flags&gc.UseCarry != 0 {
    51  			return true
    52  		}
    53  		if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 {
    54  			return false
    55  		}
    56  		p = p.Link
    57  	}
    58  
    59  	return false
    60  }
    61  
    62  func rnops(r *gc.Flow) *gc.Flow {
    63  	if r != nil {
    64  		var p *obj.Prog
    65  		var r1 *gc.Flow
    66  		for {
    67  			p = r.Prog
    68  			if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE {
    69  				break
    70  			}
    71  			r1 = gc.Uniqs(r)
    72  			if r1 == nil {
    73  				break
    74  			}
    75  			r = r1
    76  		}
    77  	}
    78  
    79  	return r
    80  }
    81  
    82  func peep(firstp *obj.Prog) {
    83  	g := gc.Flowstart(firstp, nil)
    84  	if g == nil {
    85  		return
    86  	}
    87  	gactive = 0
    88  
    89  	// byte, word arithmetic elimination.
    90  	elimshortmov(g)
    91  
    92  	// constant propagation
    93  	// find MOV $con,R followed by
    94  	// another MOV $con,R without
    95  	// setting R in the interim
    96  	var p *obj.Prog
    97  	for r := g.Start; r != nil; r = r.Link {
    98  		p = r.Prog
    99  		switch p.As {
   100  		case x86.ALEAL:
   101  			if regtyp(&p.To) {
   102  				if p.From.Sym != nil {
   103  					if p.From.Index == x86.REG_NONE {
   104  						conprop(r)
   105  					}
   106  				}
   107  			}
   108  
   109  		case x86.AMOVB,
   110  			x86.AMOVW,
   111  			x86.AMOVL,
   112  			x86.AMOVSS,
   113  			x86.AMOVSD:
   114  			if regtyp(&p.To) {
   115  				if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST {
   116  					conprop(r)
   117  				}
   118  			}
   119  		}
   120  	}
   121  
   122  	var r1 *gc.Flow
   123  	var p1 *obj.Prog
   124  	var r *gc.Flow
   125  	var t int
   126  loop1:
   127  	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
   128  		gc.Dumpit("loop1", g.Start, 0)
   129  	}
   130  
   131  	t = 0
   132  	for r = g.Start; r != nil; r = r.Link {
   133  		p = r.Prog
   134  		switch p.As {
   135  		case x86.AMOVL,
   136  			x86.AMOVSS,
   137  			x86.AMOVSD:
   138  			if regtyp(&p.To) {
   139  				if regtyp(&p.From) {
   140  					if copyprop(g, r) {
   141  						excise(r)
   142  						t++
   143  					} else if subprop(r) && copyprop(g, r) {
   144  						excise(r)
   145  						t++
   146  					}
   147  				}
   148  			}
   149  
   150  		case x86.AMOVBLZX,
   151  			x86.AMOVWLZX,
   152  			x86.AMOVBLSX,
   153  			x86.AMOVWLSX:
   154  			if regtyp(&p.To) {
   155  				r1 = rnops(gc.Uniqs(r))
   156  				if r1 != nil {
   157  					p1 = r1.Prog
   158  					if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg {
   159  						p1.As = x86.AMOVL
   160  						t++
   161  					}
   162  				}
   163  			}
   164  
   165  		case x86.AADDL,
   166  			x86.AADDW:
   167  			if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
   168  				break
   169  			}
   170  			if p.From.Offset == -1 {
   171  				if p.As == x86.AADDL {
   172  					p.As = x86.ADECL
   173  				} else {
   174  					p.As = x86.ADECW
   175  				}
   176  				p.From = obj.Addr{}
   177  				break
   178  			}
   179  
   180  			if p.From.Offset == 1 {
   181  				if p.As == x86.AADDL {
   182  					p.As = x86.AINCL
   183  				} else {
   184  					p.As = x86.AINCW
   185  				}
   186  				p.From = obj.Addr{}
   187  				break
   188  			}
   189  
   190  		case x86.ASUBL,
   191  			x86.ASUBW:
   192  			if p.From.Type != obj.TYPE_CONST || needc(p.Link) {
   193  				break
   194  			}
   195  			if p.From.Offset == -1 {
   196  				if p.As == x86.ASUBL {
   197  					p.As = x86.AINCL
   198  				} else {
   199  					p.As = x86.AINCW
   200  				}
   201  				p.From = obj.Addr{}
   202  				break
   203  			}
   204  
   205  			if p.From.Offset == 1 {
   206  				if p.As == x86.ASUBL {
   207  					p.As = x86.ADECL
   208  				} else {
   209  					p.As = x86.ADECW
   210  				}
   211  				p.From = obj.Addr{}
   212  				break
   213  			}
   214  		}
   215  	}
   216  
   217  	if t != 0 {
   218  		goto loop1
   219  	}
   220  
   221  	// MOVSD removal.
   222  	// We never use packed registers, so a MOVSD between registers
   223  	// can be replaced by MOVAPD, which moves the pair of float64s
   224  	// instead of just the lower one. We only use the lower one, but
   225  	// the processor can do better if we do moves using both.
   226  	for r := g.Start; r != nil; r = r.Link {
   227  		p = r.Prog
   228  		if p.As == x86.AMOVSD {
   229  			if regtyp(&p.From) {
   230  				if regtyp(&p.To) {
   231  					p.As = x86.AMOVAPD
   232  				}
   233  			}
   234  		}
   235  	}
   236  
   237  	gc.Flowend(g)
   238  }
   239  
   240  func excise(r *gc.Flow) {
   241  	p := r.Prog
   242  	if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
   243  		fmt.Printf("%v ===delete===\n", p)
   244  	}
   245  
   246  	obj.Nopout(p)
   247  
   248  	gc.Ostats.Ndelmov++
   249  }
   250  
   251  func regtyp(a *obj.Addr) bool {
   252  	if gc.Ctxt.Flag_shared && a.Type == obj.TYPE_REG && a.Reg == x86.REG_CX {
   253  		// don't propagate CX, it is used implicitly by PIC global references
   254  		return false
   255  	}
   256  	return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7)
   257  }
   258  
   259  // movb elimination.
   260  // movb is simulated by the linker
   261  // when a register other than ax, bx, cx, dx
   262  // is used, so rewrite to other instructions
   263  // when possible.  a movb into a register
   264  // can smash the entire 64-bit register without
   265  // causing any trouble.
   266  func elimshortmov(g *gc.Graph) {
   267  	var p *obj.Prog
   268  
   269  	for r := g.Start; r != nil; r = r.Link {
   270  		p = r.Prog
   271  		if regtyp(&p.To) {
   272  			switch p.As {
   273  			case x86.AINCB,
   274  				x86.AINCW:
   275  				p.As = x86.AINCL
   276  
   277  			case x86.ADECB,
   278  				x86.ADECW:
   279  				p.As = x86.ADECL
   280  
   281  			case x86.ANEGB,
   282  				x86.ANEGW:
   283  				p.As = x86.ANEGL
   284  
   285  			case x86.ANOTB,
   286  				x86.ANOTW:
   287  				p.As = x86.ANOTL
   288  			}
   289  
   290  			if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
   291  				// move or arithmetic into partial register.
   292  				// from another register or constant can be movl.
   293  				// we don't switch to 32-bit arithmetic if it can
   294  				// change how the carry bit is set (and the carry bit is needed).
   295  				switch p.As {
   296  				case x86.AMOVB,
   297  					x86.AMOVW:
   298  					p.As = x86.AMOVL
   299  
   300  				case x86.AADDB,
   301  					x86.AADDW:
   302  					if !needc(p.Link) {
   303  						p.As = x86.AADDL
   304  					}
   305  
   306  				case x86.ASUBB,
   307  					x86.ASUBW:
   308  					if !needc(p.Link) {
   309  						p.As = x86.ASUBL
   310  					}
   311  
   312  				case x86.AMULB,
   313  					x86.AMULW:
   314  					p.As = x86.AMULL
   315  
   316  				case x86.AIMULB,
   317  					x86.AIMULW:
   318  					p.As = x86.AIMULL
   319  
   320  				case x86.AANDB,
   321  					x86.AANDW:
   322  					p.As = x86.AANDL
   323  
   324  				case x86.AORB,
   325  					x86.AORW:
   326  					p.As = x86.AORL
   327  
   328  				case x86.AXORB,
   329  					x86.AXORW:
   330  					p.As = x86.AXORL
   331  
   332  				case x86.ASHLB,
   333  					x86.ASHLW:
   334  					p.As = x86.ASHLL
   335  				}
   336  			} else {
   337  				// explicit zero extension
   338  				switch p.As {
   339  				case x86.AMOVB:
   340  					p.As = x86.AMOVBLZX
   341  
   342  				case x86.AMOVW:
   343  					p.As = x86.AMOVWLZX
   344  				}
   345  			}
   346  		}
   347  	}
   348  }
   349  
   350  /*
   351   * the idea is to substitute
   352   * one register for another
   353   * from one MOV to another
   354   *	MOV	a, R0
   355   *	ADD	b, R0	/ no use of R1
   356   *	MOV	R0, R1
   357   * would be converted to
   358   *	MOV	a, R1
   359   *	ADD	b, R1
   360   *	MOV	R1, R0
   361   * hopefully, then the former or latter MOV
   362   * will be eliminated by copy propagation.
   363   */
   364  func subprop(r0 *gc.Flow) bool {
   365  	p := r0.Prog
   366  	v1 := &p.From
   367  	if !regtyp(v1) {
   368  		return false
   369  	}
   370  	v2 := &p.To
   371  	if !regtyp(v2) {
   372  		return false
   373  	}
   374  	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
   375  		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
   376  			fmt.Printf("\t? %v\n", r.Prog)
   377  		}
   378  		if gc.Uniqs(r) == nil {
   379  			break
   380  		}
   381  		p = r.Prog
   382  		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
   383  			continue
   384  		}
   385  		if p.Info.Flags&gc.Call != 0 {
   386  			return false
   387  		}
   388  
   389  		if p.Info.Reguse|p.Info.Regset != 0 {
   390  			return false
   391  		}
   392  
   393  		if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg {
   394  			copysub(&p.To, v1, v2, true)
   395  			if gc.Debug['P'] != 0 {
   396  				fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
   397  				if p.From.Type == v2.Type && p.From.Reg == v2.Reg {
   398  					fmt.Printf(" excise")
   399  				}
   400  				fmt.Printf("\n")
   401  			}
   402  
   403  			for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
   404  				p = r.Prog
   405  				copysub(&p.From, v1, v2, true)
   406  				copysub(&p.To, v1, v2, true)
   407  				if gc.Debug['P'] != 0 {
   408  					fmt.Printf("%v\n", r.Prog)
   409  				}
   410  			}
   411  
   412  			t := int(v1.Reg)
   413  			v1.Reg = v2.Reg
   414  			v2.Reg = int16(t)
   415  			if gc.Debug['P'] != 0 {
   416  				fmt.Printf("%v last\n", r.Prog)
   417  			}
   418  			return true
   419  		}
   420  
   421  		if copyau(&p.From, v2) || copyau(&p.To, v2) {
   422  			break
   423  		}
   424  		if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) {
   425  			break
   426  		}
   427  	}
   428  
   429  	return false
   430  }
   431  
   432  /*
   433   * The idea is to remove redundant copies.
   434   *	v1->v2	F=0
   435   *	(use v2	s/v2/v1/)*
   436   *	set v1	F=1
   437   *	use v2	return fail
   438   *	-----------------
   439   *	v1->v2	F=0
   440   *	(use v2	s/v2/v1/)*
   441   *	set v1	F=1
   442   *	set v2	return success
   443   */
   444  func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
   445  	p := r0.Prog
   446  	v1 := &p.From
   447  	v2 := &p.To
   448  	if copyas(v1, v2) {
   449  		return true
   450  	}
   451  	gactive++
   452  	return copy1(v1, v2, r0.S1, false)
   453  }
   454  
   455  func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool {
   456  	if uint32(r.Active) == gactive {
   457  		if gc.Debug['P'] != 0 {
   458  			fmt.Printf("act set; return 1\n")
   459  		}
   460  		return true
   461  	}
   462  
   463  	r.Active = int32(gactive)
   464  	if gc.Debug['P'] != 0 {
   465  		fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
   466  	}
   467  	for ; r != nil; r = r.S1 {
   468  		p := r.Prog
   469  		if gc.Debug['P'] != 0 {
   470  			fmt.Printf("%v", p)
   471  		}
   472  		if !f && gc.Uniqp(r) == nil {
   473  			f = true
   474  			if gc.Debug['P'] != 0 {
   475  				fmt.Printf("; merge; f=%v", f)
   476  			}
   477  		}
   478  
   479  		switch t := copyu(p, v2, nil); t {
   480  		case 2: /* rar, can't split */
   481  			if gc.Debug['P'] != 0 {
   482  				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
   483  			}
   484  			return false
   485  
   486  		case 3: /* set */
   487  			if gc.Debug['P'] != 0 {
   488  				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
   489  			}
   490  			return true
   491  
   492  		case 1, /* used, substitute */
   493  			4: /* use and set */
   494  			if f {
   495  				if gc.Debug['P'] == 0 {
   496  					return false
   497  				}
   498  				if t == 4 {
   499  					fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
   500  				} else {
   501  					fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f)
   502  				}
   503  				return false
   504  			}
   505  
   506  			if copyu(p, v2, v1) != 0 {
   507  				if gc.Debug['P'] != 0 {
   508  					fmt.Printf("; sub fail; return 0\n")
   509  				}
   510  				return false
   511  			}
   512  
   513  			if gc.Debug['P'] != 0 {
   514  				fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
   515  			}
   516  			if t == 4 {
   517  				if gc.Debug['P'] != 0 {
   518  					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
   519  				}
   520  				return true
   521  			}
   522  		}
   523  
   524  		if !f {
   525  			t := copyu(p, v1, nil)
   526  			if t == 2 || t == 3 || t == 4 {
   527  				f = true
   528  				if gc.Debug['P'] != 0 {
   529  					fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f)
   530  				}
   531  			}
   532  		}
   533  
   534  		if gc.Debug['P'] != 0 {
   535  			fmt.Printf("\n")
   536  		}
   537  		if r.S2 != nil {
   538  			if !copy1(v1, v2, r.S2, f) {
   539  				return false
   540  			}
   541  		}
   542  	}
   543  	return true
   544  }
   545  
   546  /*
   547   * return
   548   * 1 if v only used (and substitute),
   549   * 2 if read-alter-rewrite
   550   * 3 if set
   551   * 4 if set and used
   552   * 0 otherwise (not touched)
   553   */
   554  func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int {
   555  	switch p.As {
   556  	case obj.AJMP:
   557  		if s != nil {
   558  			if copysub(&p.To, v, s, true) {
   559  				return 1
   560  			}
   561  			return 0
   562  		}
   563  
   564  		if copyau(&p.To, v) {
   565  			return 1
   566  		}
   567  		return 0
   568  
   569  	case obj.ARET:
   570  		if s != nil {
   571  			return 1
   572  		}
   573  		return 3
   574  
   575  	case obj.ACALL:
   576  		if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset {
   577  			return 2
   578  		}
   579  		if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
   580  			return 2
   581  		}
   582  		if v.Type == p.From.Type && v.Reg == p.From.Reg {
   583  			return 2
   584  		}
   585  
   586  		if s != nil {
   587  			if copysub(&p.To, v, s, true) {
   588  				return 1
   589  			}
   590  			return 0
   591  		}
   592  
   593  		if copyau(&p.To, v) {
   594  			return 4
   595  		}
   596  		return 3
   597  
   598  	case obj.ATEXT:
   599  		if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG {
   600  			return 3
   601  		}
   602  		return 0
   603  	}
   604  
   605  	if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
   606  		return 0
   607  	}
   608  
   609  	if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 {
   610  		return 2
   611  	}
   612  
   613  	if p.Info.Flags&gc.LeftAddr != 0 {
   614  		if copyas(&p.From, v) {
   615  			return 2
   616  		}
   617  	}
   618  
   619  	if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite {
   620  		if copyas(&p.To, v) {
   621  			return 2
   622  		}
   623  	}
   624  
   625  	if p.Info.Flags&gc.RightWrite != 0 {
   626  		if copyas(&p.To, v) {
   627  			if s != nil {
   628  				if copysub(&p.From, v, s, true) {
   629  					return 1
   630  				}
   631  				return 0
   632  			}
   633  			if copyau(&p.From, v) {
   634  				return 4
   635  			}
   636  			return 3
   637  		}
   638  	}
   639  
   640  	if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
   641  		if s != nil {
   642  			if copysub(&p.From, v, s, true) {
   643  				return 1
   644  			}
   645  			if copysub(&p.To, v, s, true) {
   646  				return 1
   647  			}
   648  			return 0
   649  		}
   650  		if copyau(&p.From, v) {
   651  			return 1
   652  		}
   653  		if copyau(&p.To, v) {
   654  			return 1
   655  		}
   656  	}
   657  	return 0
   658  }
   659  
   660  /*
   661   * direct reference,
   662   * could be set/use depending on
   663   * semantics
   664   */
   665  func copyas(a *obj.Addr, v *obj.Addr) bool {
   666  	if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL {
   667  		gc.Fatalf("use of byte register")
   668  	}
   669  	if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL {
   670  		gc.Fatalf("use of byte register")
   671  	}
   672  
   673  	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
   674  		return false
   675  	}
   676  	if regtyp(v) {
   677  		return true
   678  	}
   679  	if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
   680  		if v.Offset == a.Offset {
   681  			return true
   682  		}
   683  	}
   684  	return false
   685  }
   686  
   687  func sameaddr(a *obj.Addr, v *obj.Addr) bool {
   688  	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
   689  		return false
   690  	}
   691  	if regtyp(v) {
   692  		return true
   693  	}
   694  	if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
   695  		if v.Offset == a.Offset {
   696  			return true
   697  		}
   698  	}
   699  	return false
   700  }
   701  
   702  /*
   703   * either direct or indirect
   704   */
   705  func copyau(a *obj.Addr, v *obj.Addr) bool {
   706  	if copyas(a, v) {
   707  		return true
   708  	}
   709  	if regtyp(v) {
   710  		if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
   711  			return true
   712  		}
   713  		if a.Index == v.Reg {
   714  			return true
   715  		}
   716  	}
   717  
   718  	return false
   719  }
   720  
   721  // copysub substitute s for v in a.
   722  // copysub returns true on failure to substitute.
   723  // TODO(dfc) reverse this logic to return false on sunstitution failure.
   724  func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool {
   725  	if copyas(a, v) {
   726  		if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 {
   727  			if f {
   728  				a.Reg = s.Reg
   729  			}
   730  		}
   731  		return false
   732  	}
   733  
   734  	if regtyp(v) {
   735  		if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
   736  			if (s.Reg == x86.REG_BP) && a.Index != x86.REG_NONE {
   737  				return true /* can't use BP-base with index */
   738  			}
   739  			if f {
   740  				a.Reg = s.Reg
   741  			}
   742  		}
   743  
   744  		if a.Index == v.Reg {
   745  			if f {
   746  				a.Index = s.Reg
   747  			}
   748  		}
   749  	}
   750  	return false
   751  }
   752  
   753  func conprop(r0 *gc.Flow) {
   754  	var p *obj.Prog
   755  
   756  	p0 := r0.Prog
   757  	v0 := &p0.To
   758  	r := r0
   759  
   760  loop:
   761  	r = gc.Uniqs(r)
   762  	if r == nil || r == r0 {
   763  		return
   764  	}
   765  	if gc.Uniqp(r) == nil {
   766  		return
   767  	}
   768  
   769  	p = r.Prog
   770  	switch copyu(p, v0, nil) {
   771  	case 0, // miss
   772  		1: // use
   773  		goto loop
   774  
   775  	case 2, // rar
   776  		4: // use and set
   777  		break
   778  
   779  	case 3: // set
   780  		if p.As == p0.As {
   781  			if p.From.Type == p0.From.Type {
   782  				if p.From.Reg == p0.From.Reg {
   783  					if p.From.Node == p0.From.Node {
   784  						if p.From.Offset == p0.From.Offset {
   785  							if p.From.Scale == p0.From.Scale {
   786  								if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) {
   787  									if p.From.Index == p0.From.Index {
   788  										excise(r)
   789  										goto loop
   790  									}
   791  								}
   792  							}
   793  						}
   794  					}
   795  				}
   796  			}
   797  		}
   798  	}
   799  }
   800  
   801  func smallindir(a *obj.Addr, reg *obj.Addr) bool {
   802  	return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096
   803  }
   804  
   805  func stackaddr(a *obj.Addr) bool {
   806  	return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP
   807  }