github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/compile/internal/x86/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 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  	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)
   253  }
   254  
   255  // movb elimination.
   256  // movb is simulated by the linker
   257  // when a register other than ax, bx, cx, dx
   258  // is used, so rewrite to other instructions
   259  // when possible.  a movb into a register
   260  // can smash the entire 64-bit register without
   261  // causing any trouble.
   262  func elimshortmov(g *gc.Graph) {
   263  	var p *obj.Prog
   264  
   265  	for r := g.Start; r != nil; r = r.Link {
   266  		p = r.Prog
   267  		if regtyp(&p.To) {
   268  			switch p.As {
   269  			case x86.AINCB,
   270  				x86.AINCW:
   271  				p.As = x86.AINCL
   272  
   273  			case x86.ADECB,
   274  				x86.ADECW:
   275  				p.As = x86.ADECL
   276  
   277  			case x86.ANEGB,
   278  				x86.ANEGW:
   279  				p.As = x86.ANEGL
   280  
   281  			case x86.ANOTB,
   282  				x86.ANOTW:
   283  				p.As = x86.ANOTL
   284  			}
   285  
   286  			if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST {
   287  				// move or artihmetic into partial register.
   288  				// from another register or constant can be movl.
   289  				// we don't switch to 32-bit arithmetic if it can
   290  				// change how the carry bit is set (and the carry bit is needed).
   291  				switch p.As {
   292  				case x86.AMOVB,
   293  					x86.AMOVW:
   294  					p.As = x86.AMOVL
   295  
   296  				case x86.AADDB,
   297  					x86.AADDW:
   298  					if !needc(p.Link) {
   299  						p.As = x86.AADDL
   300  					}
   301  
   302  				case x86.ASUBB,
   303  					x86.ASUBW:
   304  					if !needc(p.Link) {
   305  						p.As = x86.ASUBL
   306  					}
   307  
   308  				case x86.AMULB,
   309  					x86.AMULW:
   310  					p.As = x86.AMULL
   311  
   312  				case x86.AIMULB,
   313  					x86.AIMULW:
   314  					p.As = x86.AIMULL
   315  
   316  				case x86.AANDB,
   317  					x86.AANDW:
   318  					p.As = x86.AANDL
   319  
   320  				case x86.AORB,
   321  					x86.AORW:
   322  					p.As = x86.AORL
   323  
   324  				case x86.AXORB,
   325  					x86.AXORW:
   326  					p.As = x86.AXORL
   327  
   328  				case x86.ASHLB,
   329  					x86.ASHLW:
   330  					p.As = x86.ASHLL
   331  				}
   332  			} else {
   333  				// explicit zero extension
   334  				switch p.As {
   335  				case x86.AMOVB:
   336  					p.As = x86.AMOVBLZX
   337  
   338  				case x86.AMOVW:
   339  					p.As = x86.AMOVWLZX
   340  				}
   341  			}
   342  		}
   343  	}
   344  }
   345  
   346  /*
   347   * the idea is to substitute
   348   * one register for another
   349   * from one MOV to another
   350   *	MOV	a, R0
   351   *	ADD	b, R0	/ no use of R1
   352   *	MOV	R0, R1
   353   * would be converted to
   354   *	MOV	a, R1
   355   *	ADD	b, R1
   356   *	MOV	R1, R0
   357   * hopefully, then the former or latter MOV
   358   * will be eliminated by copy propagation.
   359   */
   360  func subprop(r0 *gc.Flow) bool {
   361  	p := r0.Prog
   362  	v1 := &p.From
   363  	if !regtyp(v1) {
   364  		return false
   365  	}
   366  	v2 := &p.To
   367  	if !regtyp(v2) {
   368  		return false
   369  	}
   370  	for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) {
   371  		if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 {
   372  			fmt.Printf("\t? %v\n", r.Prog)
   373  		}
   374  		if gc.Uniqs(r) == nil {
   375  			break
   376  		}
   377  		p = r.Prog
   378  		if p.As == obj.AVARDEF || p.As == obj.AVARKILL {
   379  			continue
   380  		}
   381  		if p.Info.Flags&gc.Call != 0 {
   382  			return false
   383  		}
   384  
   385  		if p.Info.Reguse|p.Info.Regset != 0 {
   386  			return false
   387  		}
   388  
   389  		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 {
   390  			copysub(&p.To, v1, v2, 1)
   391  			if gc.Debug['P'] != 0 {
   392  				fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog)
   393  				if p.From.Type == v2.Type && p.From.Reg == v2.Reg {
   394  					fmt.Printf(" excise")
   395  				}
   396  				fmt.Printf("\n")
   397  			}
   398  
   399  			for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) {
   400  				p = r.Prog
   401  				copysub(&p.From, v1, v2, 1)
   402  				copysub(&p.To, v1, v2, 1)
   403  				if gc.Debug['P'] != 0 {
   404  					fmt.Printf("%v\n", r.Prog)
   405  				}
   406  			}
   407  
   408  			t := int(v1.Reg)
   409  			v1.Reg = v2.Reg
   410  			v2.Reg = int16(t)
   411  			if gc.Debug['P'] != 0 {
   412  				fmt.Printf("%v last\n", r.Prog)
   413  			}
   414  			return true
   415  		}
   416  
   417  		if copyau(&p.From, v2) || copyau(&p.To, v2) {
   418  			break
   419  		}
   420  		if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 {
   421  			break
   422  		}
   423  	}
   424  
   425  	return false
   426  }
   427  
   428  /*
   429   * The idea is to remove redundant copies.
   430   *	v1->v2	F=0
   431   *	(use v2	s/v2/v1/)*
   432   *	set v1	F=1
   433   *	use v2	return fail
   434   *	-----------------
   435   *	v1->v2	F=0
   436   *	(use v2	s/v2/v1/)*
   437   *	set v1	F=1
   438   *	set v2	return success
   439   */
   440  func copyprop(g *gc.Graph, r0 *gc.Flow) bool {
   441  	p := r0.Prog
   442  	v1 := &p.From
   443  	v2 := &p.To
   444  	if copyas(v1, v2) {
   445  		return true
   446  	}
   447  	gactive++
   448  	return copy1(v1, v2, r0.S1, 0)
   449  }
   450  
   451  func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool {
   452  	if uint32(r.Active) == gactive {
   453  		if gc.Debug['P'] != 0 {
   454  			fmt.Printf("act set; return 1\n")
   455  		}
   456  		return true
   457  	}
   458  
   459  	r.Active = int32(gactive)
   460  	if gc.Debug['P'] != 0 {
   461  		fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f)
   462  	}
   463  	var t int
   464  	var p *obj.Prog
   465  	for ; r != nil; r = r.S1 {
   466  		p = r.Prog
   467  		if gc.Debug['P'] != 0 {
   468  			fmt.Printf("%v", p)
   469  		}
   470  		if f == 0 && gc.Uniqp(r) == nil {
   471  			f = 1
   472  			if gc.Debug['P'] != 0 {
   473  				fmt.Printf("; merge; f=%d", f)
   474  			}
   475  		}
   476  
   477  		t = copyu(p, v2, nil)
   478  		switch t {
   479  		case 2: /* rar, can't split */
   480  			if gc.Debug['P'] != 0 {
   481  				fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2))
   482  			}
   483  			return false
   484  
   485  		case 3: /* set */
   486  			if gc.Debug['P'] != 0 {
   487  				fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2))
   488  			}
   489  			return true
   490  
   491  		case 1, /* used, substitute */
   492  			4: /* use and set */
   493  			if f != 0 {
   494  				if gc.Debug['P'] == 0 {
   495  					return false
   496  				}
   497  				if t == 4 {
   498  					fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
   499  				} else {
   500  					fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f)
   501  				}
   502  				return false
   503  			}
   504  
   505  			if copyu(p, v2, v1) != 0 {
   506  				if gc.Debug['P'] != 0 {
   507  					fmt.Printf("; sub fail; return 0\n")
   508  				}
   509  				return false
   510  			}
   511  
   512  			if gc.Debug['P'] != 0 {
   513  				fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1))
   514  			}
   515  			if t == 4 {
   516  				if gc.Debug['P'] != 0 {
   517  					fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2))
   518  				}
   519  				return true
   520  			}
   521  		}
   522  
   523  		if f == 0 {
   524  			t = copyu(p, v1, nil)
   525  			if f == 0 && (t == 2 || t == 3 || t == 4) {
   526  				f = 1
   527  				if gc.Debug['P'] != 0 {
   528  					fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f)
   529  				}
   530  			}
   531  		}
   532  
   533  		if gc.Debug['P'] != 0 {
   534  			fmt.Printf("\n")
   535  		}
   536  		if r.S2 != nil {
   537  			if !copy1(v1, v2, r.S2, f) {
   538  				return false
   539  			}
   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, 1) != 0 {
   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, 1) != 0 {
   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  				return copysub(&p.From, v, s, 1)
   629  			}
   630  			if copyau(&p.From, v) {
   631  				return 4
   632  			}
   633  			return 3
   634  		}
   635  	}
   636  
   637  	if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 {
   638  		if s != nil {
   639  			if copysub(&p.From, v, s, 1) != 0 {
   640  				return 1
   641  			}
   642  			return copysub(&p.To, v, s, 1)
   643  		}
   644  
   645  		if copyau(&p.From, v) {
   646  			return 1
   647  		}
   648  		if copyau(&p.To, v) {
   649  			return 1
   650  		}
   651  	}
   652  
   653  	return 0
   654  }
   655  
   656  /*
   657   * direct reference,
   658   * could be set/use depending on
   659   * semantics
   660   */
   661  func copyas(a *obj.Addr, v *obj.Addr) bool {
   662  	if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL {
   663  		gc.Fatalf("use of byte register")
   664  	}
   665  	if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL {
   666  		gc.Fatalf("use of byte register")
   667  	}
   668  
   669  	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
   670  		return false
   671  	}
   672  	if regtyp(v) {
   673  		return true
   674  	}
   675  	if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
   676  		if v.Offset == a.Offset {
   677  			return true
   678  		}
   679  	}
   680  	return false
   681  }
   682  
   683  func sameaddr(a *obj.Addr, v *obj.Addr) bool {
   684  	if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg {
   685  		return false
   686  	}
   687  	if regtyp(v) {
   688  		return true
   689  	}
   690  	if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) {
   691  		if v.Offset == a.Offset {
   692  			return true
   693  		}
   694  	}
   695  	return false
   696  }
   697  
   698  /*
   699   * either direct or indirect
   700   */
   701  func copyau(a *obj.Addr, v *obj.Addr) bool {
   702  	if copyas(a, v) {
   703  		return true
   704  	}
   705  	if regtyp(v) {
   706  		if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg {
   707  			return true
   708  		}
   709  		if a.Index == v.Reg {
   710  			return true
   711  		}
   712  	}
   713  
   714  	return false
   715  }
   716  
   717  /*
   718   * substitute s for v in a
   719   * return failure to substitute
   720   */
   721  func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int {
   722  	if copyas(a, v) {
   723  		reg := int(s.Reg)
   724  		if reg >= x86.REG_AX && reg <= x86.REG_DI || reg >= x86.REG_X0 && reg <= x86.REG_X7 {
   725  			if f != 0 {
   726  				a.Reg = int16(reg)
   727  			}
   728  		}
   729  
   730  		return 0
   731  	}
   732  
   733  	if regtyp(v) {
   734  		reg := int(v.Reg)
   735  		if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && int(a.Reg) == reg {
   736  			if (s.Reg == x86.REG_BP) && a.Index != obj.TYPE_NONE {
   737  				return 1 /* can't use BP-base with index */
   738  			}
   739  			if f != 0 {
   740  				a.Reg = s.Reg
   741  			}
   742  		}
   743  
   744  		//			return 0;
   745  		if int(a.Index) == reg {
   746  			if f != 0 {
   747  				a.Index = s.Reg
   748  			}
   749  			return 0
   750  		}
   751  
   752  		return 0
   753  	}
   754  
   755  	return 0
   756  }
   757  
   758  func conprop(r0 *gc.Flow) {
   759  	var p *obj.Prog
   760  	var t int
   761  
   762  	p0 := r0.Prog
   763  	v0 := &p0.To
   764  	r := r0
   765  
   766  loop:
   767  	r = gc.Uniqs(r)
   768  	if r == nil || r == r0 {
   769  		return
   770  	}
   771  	if gc.Uniqp(r) == nil {
   772  		return
   773  	}
   774  
   775  	p = r.Prog
   776  	t = copyu(p, v0, nil)
   777  	switch t {
   778  	case 0, // miss
   779  		1: // use
   780  		goto loop
   781  
   782  	case 2, // rar
   783  		4: // use and set
   784  		break
   785  
   786  	case 3: // set
   787  		if p.As == p0.As {
   788  			if p.From.Type == p0.From.Type {
   789  				if p.From.Reg == p0.From.Reg {
   790  					if p.From.Node == p0.From.Node {
   791  						if p.From.Offset == p0.From.Offset {
   792  							if p.From.Scale == p0.From.Scale {
   793  								if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) {
   794  									if p.From.Index == p0.From.Index {
   795  										excise(r)
   796  										goto loop
   797  									}
   798  								}
   799  							}
   800  						}
   801  					}
   802  				}
   803  			}
   804  		}
   805  	}
   806  }
   807  
   808  func smallindir(a *obj.Addr, reg *obj.Addr) bool {
   809  	return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096
   810  }
   811  
   812  func stackaddr(a *obj.Addr) bool {
   813  	return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP
   814  }