github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/src/cmd/compile/internal/ssa/rewrite.go (about)

     1  // Copyright 2015 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ssa
     6  
     7  import (
     8  	"cmd/compile/internal/types"
     9  	"cmd/internal/obj"
    10  	"fmt"
    11  	"io"
    12  	"math"
    13  	"os"
    14  	"path/filepath"
    15  )
    16  
    17  func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) {
    18  	// repeat rewrites until we find no more rewrites
    19  	for {
    20  		change := false
    21  		for _, b := range f.Blocks {
    22  			if b.Control != nil && b.Control.Op == OpCopy {
    23  				for b.Control.Op == OpCopy {
    24  					b.SetControl(b.Control.Args[0])
    25  				}
    26  			}
    27  			if rb(b) {
    28  				change = true
    29  			}
    30  			for _, v := range b.Values {
    31  				change = phielimValue(v) || change
    32  
    33  				// Eliminate copy inputs.
    34  				// If any copy input becomes unused, mark it
    35  				// as invalid and discard its argument. Repeat
    36  				// recursively on the discarded argument.
    37  				// This phase helps remove phantom "dead copy" uses
    38  				// of a value so that a x.Uses==1 rule condition
    39  				// fires reliably.
    40  				for i, a := range v.Args {
    41  					if a.Op != OpCopy {
    42  						continue
    43  					}
    44  					v.SetArg(i, copySource(a))
    45  					change = true
    46  					for a.Uses == 0 {
    47  						b := a.Args[0]
    48  						a.reset(OpInvalid)
    49  						a = b
    50  					}
    51  				}
    52  
    53  				// apply rewrite function
    54  				if rv(v) {
    55  					change = true
    56  				}
    57  			}
    58  		}
    59  		if !change {
    60  			break
    61  		}
    62  	}
    63  	// remove clobbered values
    64  	for _, b := range f.Blocks {
    65  		j := 0
    66  		for i, v := range b.Values {
    67  			if v.Op == OpInvalid {
    68  				f.freeValue(v)
    69  				continue
    70  			}
    71  			if i != j {
    72  				b.Values[j] = v
    73  			}
    74  			j++
    75  		}
    76  		if j != len(b.Values) {
    77  			tail := b.Values[j:]
    78  			for j := range tail {
    79  				tail[j] = nil
    80  			}
    81  			b.Values = b.Values[:j]
    82  		}
    83  	}
    84  }
    85  
    86  // Common functions called from rewriting rules
    87  
    88  func is64BitFloat(t *types.Type) bool {
    89  	return t.Size() == 8 && t.IsFloat()
    90  }
    91  
    92  func is32BitFloat(t *types.Type) bool {
    93  	return t.Size() == 4 && t.IsFloat()
    94  }
    95  
    96  func is64BitInt(t *types.Type) bool {
    97  	return t.Size() == 8 && t.IsInteger()
    98  }
    99  
   100  func is32BitInt(t *types.Type) bool {
   101  	return t.Size() == 4 && t.IsInteger()
   102  }
   103  
   104  func is16BitInt(t *types.Type) bool {
   105  	return t.Size() == 2 && t.IsInteger()
   106  }
   107  
   108  func is8BitInt(t *types.Type) bool {
   109  	return t.Size() == 1 && t.IsInteger()
   110  }
   111  
   112  func isPtr(t *types.Type) bool {
   113  	return t.IsPtrShaped()
   114  }
   115  
   116  func isSigned(t *types.Type) bool {
   117  	return t.IsSigned()
   118  }
   119  
   120  func typeSize(t *types.Type) int64 {
   121  	return t.Size()
   122  }
   123  
   124  // mergeSym merges two symbolic offsets. There is no real merging of
   125  // offsets, we just pick the non-nil one.
   126  func mergeSym(x, y interface{}) interface{} {
   127  	if x == nil {
   128  		return y
   129  	}
   130  	if y == nil {
   131  		return x
   132  	}
   133  	panic(fmt.Sprintf("mergeSym with two non-nil syms %s %s", x, y))
   134  }
   135  func canMergeSym(x, y interface{}) bool {
   136  	return x == nil || y == nil
   137  }
   138  
   139  // canMergeLoad reports whether the load can be merged into target without
   140  // invalidating the schedule.
   141  // It also checks that the other non-load argument x is something we
   142  // are ok with clobbering (all our current load+op instructions clobber
   143  // their input register).
   144  func canMergeLoad(target, load, x *Value) bool {
   145  	if target.Block.ID != load.Block.ID {
   146  		// If the load is in a different block do not merge it.
   147  		return false
   148  	}
   149  
   150  	// We can't merge the load into the target if the load
   151  	// has more than one use.
   152  	if load.Uses != 1 {
   153  		return false
   154  	}
   155  
   156  	// The register containing x is going to get clobbered.
   157  	// Don't merge if we still need the value of x.
   158  	// We don't have liveness information here, but we can
   159  	// approximate x dying with:
   160  	//  1) target is x's only use.
   161  	//  2) target is not in a deeper loop than x.
   162  	if x.Uses != 1 {
   163  		return false
   164  	}
   165  	loopnest := x.Block.Func.loopnest()
   166  	loopnest.calculateDepths()
   167  	if loopnest.depth(target.Block.ID) > loopnest.depth(x.Block.ID) {
   168  		return false
   169  	}
   170  
   171  	mem := load.MemoryArg()
   172  
   173  	// We need the load's memory arg to still be alive at target. That
   174  	// can't be the case if one of target's args depends on a memory
   175  	// state that is a successor of load's memory arg.
   176  	//
   177  	// For example, it would be invalid to merge load into target in
   178  	// the following situation because newmem has killed oldmem
   179  	// before target is reached:
   180  	//     load = read ... oldmem
   181  	//   newmem = write ... oldmem
   182  	//     arg0 = read ... newmem
   183  	//   target = add arg0 load
   184  	//
   185  	// If the argument comes from a different block then we can exclude
   186  	// it immediately because it must dominate load (which is in the
   187  	// same block as target).
   188  	var args []*Value
   189  	for _, a := range target.Args {
   190  		if a != load && a.Block.ID == target.Block.ID {
   191  			args = append(args, a)
   192  		}
   193  	}
   194  
   195  	// memPreds contains memory states known to be predecessors of load's
   196  	// memory state. It is lazily initialized.
   197  	var memPreds map[*Value]bool
   198  search:
   199  	for i := 0; len(args) > 0; i++ {
   200  		const limit = 100
   201  		if i >= limit {
   202  			// Give up if we have done a lot of iterations.
   203  			return false
   204  		}
   205  		v := args[len(args)-1]
   206  		args = args[:len(args)-1]
   207  		if target.Block.ID != v.Block.ID {
   208  			// Since target and load are in the same block
   209  			// we can stop searching when we leave the block.
   210  			continue search
   211  		}
   212  		if v.Op == OpPhi {
   213  			// A Phi implies we have reached the top of the block.
   214  			// The memory phi, if it exists, is always
   215  			// the first logical store in the block.
   216  			continue search
   217  		}
   218  		if v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() {
   219  			// We could handle this situation however it is likely
   220  			// to be very rare.
   221  			return false
   222  		}
   223  		if v.Type.IsMemory() {
   224  			if memPreds == nil {
   225  				// Initialise a map containing memory states
   226  				// known to be predecessors of load's memory
   227  				// state.
   228  				memPreds = make(map[*Value]bool)
   229  				m := mem
   230  				const limit = 50
   231  				for i := 0; i < limit; i++ {
   232  					if m.Op == OpPhi {
   233  						// The memory phi, if it exists, is always
   234  						// the first logical store in the block.
   235  						break
   236  					}
   237  					if m.Block.ID != target.Block.ID {
   238  						break
   239  					}
   240  					if !m.Type.IsMemory() {
   241  						break
   242  					}
   243  					memPreds[m] = true
   244  					if len(m.Args) == 0 {
   245  						break
   246  					}
   247  					m = m.MemoryArg()
   248  				}
   249  			}
   250  
   251  			// We can merge if v is a predecessor of mem.
   252  			//
   253  			// For example, we can merge load into target in the
   254  			// following scenario:
   255  			//      x = read ... v
   256  			//    mem = write ... v
   257  			//   load = read ... mem
   258  			// target = add x load
   259  			if memPreds[v] {
   260  				continue search
   261  			}
   262  			return false
   263  		}
   264  		if len(v.Args) > 0 && v.Args[len(v.Args)-1] == mem {
   265  			// If v takes mem as an input then we know mem
   266  			// is valid at this point.
   267  			continue search
   268  		}
   269  		for _, a := range v.Args {
   270  			if target.Block.ID == a.Block.ID {
   271  				args = append(args, a)
   272  			}
   273  		}
   274  	}
   275  
   276  	return true
   277  }
   278  
   279  // isArg returns whether s is an arg symbol
   280  func isArg(s interface{}) bool {
   281  	_, ok := s.(*ArgSymbol)
   282  	return ok
   283  }
   284  
   285  // isAuto returns whether s is an auto symbol
   286  func isAuto(s interface{}) bool {
   287  	_, ok := s.(*AutoSymbol)
   288  	return ok
   289  }
   290  
   291  // isSameSym returns whether sym is the same as the given named symbol
   292  func isSameSym(sym interface{}, name string) bool {
   293  	s, ok := sym.(fmt.Stringer)
   294  	return ok && s.String() == name
   295  }
   296  
   297  // nlz returns the number of leading zeros.
   298  func nlz(x int64) int64 {
   299  	// log2(0) == 1, so nlz(0) == 64
   300  	return 63 - log2(x)
   301  }
   302  
   303  // ntz returns the number of trailing zeros.
   304  func ntz(x int64) int64 {
   305  	return 64 - nlz(^x&(x-1))
   306  }
   307  
   308  // nlo returns the number of leading ones.
   309  func nlo(x int64) int64 {
   310  	return nlz(^x)
   311  }
   312  
   313  // nto returns the number of trailing ones.
   314  func nto(x int64) int64 {
   315  	return ntz(^x)
   316  }
   317  
   318  // log2 returns logarithm in base 2 of uint64(n), with log2(0) = -1.
   319  // Rounds down.
   320  func log2(n int64) (l int64) {
   321  	l = -1
   322  	x := uint64(n)
   323  	for ; x >= 0x8000; x >>= 16 {
   324  		l += 16
   325  	}
   326  	if x >= 0x80 {
   327  		x >>= 8
   328  		l += 8
   329  	}
   330  	if x >= 0x8 {
   331  		x >>= 4
   332  		l += 4
   333  	}
   334  	if x >= 0x2 {
   335  		x >>= 2
   336  		l += 2
   337  	}
   338  	if x >= 0x1 {
   339  		l++
   340  	}
   341  	return
   342  }
   343  
   344  // isPowerOfTwo reports whether n is a power of 2.
   345  func isPowerOfTwo(n int64) bool {
   346  	return n > 0 && n&(n-1) == 0
   347  }
   348  
   349  // is32Bit reports whether n can be represented as a signed 32 bit integer.
   350  func is32Bit(n int64) bool {
   351  	return n == int64(int32(n))
   352  }
   353  
   354  // is16Bit reports whether n can be represented as a signed 16 bit integer.
   355  func is16Bit(n int64) bool {
   356  	return n == int64(int16(n))
   357  }
   358  
   359  // isU12Bit reports whether n can be represented as an unsigned 12 bit integer.
   360  func isU12Bit(n int64) bool {
   361  	return 0 <= n && n < (1<<12)
   362  }
   363  
   364  // isU16Bit reports whether n can be represented as an unsigned 16 bit integer.
   365  func isU16Bit(n int64) bool {
   366  	return n == int64(uint16(n))
   367  }
   368  
   369  // isU32Bit reports whether n can be represented as an unsigned 32 bit integer.
   370  func isU32Bit(n int64) bool {
   371  	return n == int64(uint32(n))
   372  }
   373  
   374  // is20Bit reports whether n can be represented as a signed 20 bit integer.
   375  func is20Bit(n int64) bool {
   376  	return -(1<<19) <= n && n < (1<<19)
   377  }
   378  
   379  // b2i translates a boolean value to 0 or 1 for assigning to auxInt.
   380  func b2i(b bool) int64 {
   381  	if b {
   382  		return 1
   383  	}
   384  	return 0
   385  }
   386  
   387  // i2f is used in rules for converting from an AuxInt to a float.
   388  func i2f(i int64) float64 {
   389  	return math.Float64frombits(uint64(i))
   390  }
   391  
   392  // i2f32 is used in rules for converting from an AuxInt to a float32.
   393  func i2f32(i int64) float32 {
   394  	return float32(math.Float64frombits(uint64(i)))
   395  }
   396  
   397  // f2i is used in the rules for storing a float in AuxInt.
   398  func f2i(f float64) int64 {
   399  	return int64(math.Float64bits(f))
   400  }
   401  
   402  // uaddOvf returns true if unsigned a+b would overflow.
   403  func uaddOvf(a, b int64) bool {
   404  	return uint64(a)+uint64(b) < uint64(a)
   405  }
   406  
   407  // de-virtualize an InterCall
   408  // 'sym' is the symbol for the itab
   409  func devirt(v *Value, sym interface{}, offset int64) *obj.LSym {
   410  	f := v.Block.Func
   411  	ext, ok := sym.(*ExternSymbol)
   412  	if !ok {
   413  		return nil
   414  	}
   415  	lsym := f.fe.DerefItab(ext.Sym, offset)
   416  	if f.pass.debug > 0 {
   417  		if lsym != nil {
   418  			f.Warnl(v.Pos, "de-virtualizing call")
   419  		} else {
   420  			f.Warnl(v.Pos, "couldn't de-virtualize call")
   421  		}
   422  	}
   423  	return lsym
   424  }
   425  
   426  // isSamePtr reports whether p1 and p2 point to the same address.
   427  func isSamePtr(p1, p2 *Value) bool {
   428  	if p1 == p2 {
   429  		return true
   430  	}
   431  	if p1.Op != p2.Op {
   432  		return false
   433  	}
   434  	switch p1.Op {
   435  	case OpOffPtr:
   436  		return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0])
   437  	case OpAddr:
   438  		// OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op.
   439  		// Checking for value equality only works after [z]cse has run.
   440  		return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op
   441  	case OpAddPtr:
   442  		return p1.Args[1] == p2.Args[1] && isSamePtr(p1.Args[0], p2.Args[0])
   443  	}
   444  	return false
   445  }
   446  
   447  // moveSize returns the number of bytes an aligned MOV instruction moves
   448  func moveSize(align int64, c *Config) int64 {
   449  	switch {
   450  	case align%8 == 0 && c.PtrSize == 8:
   451  		return 8
   452  	case align%4 == 0:
   453  		return 4
   454  	case align%2 == 0:
   455  		return 2
   456  	}
   457  	return 1
   458  }
   459  
   460  // mergePoint finds a block among a's blocks which dominates b and is itself
   461  // dominated by all of a's blocks. Returns nil if it can't find one.
   462  // Might return nil even if one does exist.
   463  func mergePoint(b *Block, a ...*Value) *Block {
   464  	// Walk backward from b looking for one of the a's blocks.
   465  
   466  	// Max distance
   467  	d := 100
   468  
   469  	for d > 0 {
   470  		for _, x := range a {
   471  			if b == x.Block {
   472  				goto found
   473  			}
   474  		}
   475  		if len(b.Preds) > 1 {
   476  			// Don't know which way to go back. Abort.
   477  			return nil
   478  		}
   479  		b = b.Preds[0].b
   480  		d--
   481  	}
   482  	return nil // too far away
   483  found:
   484  	// At this point, r is the first value in a that we find by walking backwards.
   485  	// if we return anything, r will be it.
   486  	r := b
   487  
   488  	// Keep going, counting the other a's that we find. They must all dominate r.
   489  	na := 0
   490  	for d > 0 {
   491  		for _, x := range a {
   492  			if b == x.Block {
   493  				na++
   494  			}
   495  		}
   496  		if na == len(a) {
   497  			// Found all of a in a backwards walk. We can return r.
   498  			return r
   499  		}
   500  		if len(b.Preds) > 1 {
   501  			return nil
   502  		}
   503  		b = b.Preds[0].b
   504  		d--
   505  
   506  	}
   507  	return nil // too far away
   508  }
   509  
   510  // clobber invalidates v.  Returns true.
   511  // clobber is used by rewrite rules to:
   512  //   A) make sure v is really dead and never used again.
   513  //   B) decrement use counts of v's args.
   514  func clobber(v *Value) bool {
   515  	v.reset(OpInvalid)
   516  	// Note: leave v.Block intact.  The Block field is used after clobber.
   517  	return true
   518  }
   519  
   520  // noteRule is an easy way to track if a rule is matched when writing
   521  // new ones.  Make the rule of interest also conditional on
   522  //     noteRule("note to self: rule of interest matched")
   523  // and that message will print when the rule matches.
   524  func noteRule(s string) bool {
   525  	fmt.Println(s)
   526  	return true
   527  }
   528  
   529  // warnRule generates a compiler debug output with string s when
   530  // cond is true and the rule is fired.
   531  func warnRule(cond bool, v *Value, s string) bool {
   532  	if cond {
   533  		v.Block.Func.Warnl(v.Pos, s)
   534  	}
   535  	return true
   536  }
   537  
   538  // logRule logs the use of the rule s. This will only be enabled if
   539  // rewrite rules were generated with the -log option, see gen/rulegen.go.
   540  func logRule(s string) {
   541  	if ruleFile == nil {
   542  		// Open a log file to write log to. We open in append
   543  		// mode because all.bash runs the compiler lots of times,
   544  		// and we want the concatenation of all of those logs.
   545  		// This means, of course, that users need to rm the old log
   546  		// to get fresh data.
   547  		// TODO: all.bash runs compilers in parallel. Need to synchronize logging somehow?
   548  		w, err := os.OpenFile(filepath.Join(os.Getenv("GOROOT"), "src", "rulelog"),
   549  			os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
   550  		if err != nil {
   551  			panic(err)
   552  		}
   553  		ruleFile = w
   554  	}
   555  	_, err := fmt.Fprintf(ruleFile, "rewrite %s\n", s)
   556  	if err != nil {
   557  		panic(err)
   558  	}
   559  }
   560  
   561  var ruleFile io.Writer
   562  
   563  func min(x, y int64) int64 {
   564  	if x < y {
   565  		return x
   566  	}
   567  	return y
   568  }
   569  
   570  func isConstZero(v *Value) bool {
   571  	switch v.Op {
   572  	case OpConstNil:
   573  		return true
   574  	case OpConst64, OpConst32, OpConst16, OpConst8, OpConstBool, OpConst32F, OpConst64F:
   575  		return v.AuxInt == 0
   576  	}
   577  	return false
   578  }
   579  
   580  // reciprocalExact64 reports whether 1/c is exactly representable.
   581  func reciprocalExact64(c float64) bool {
   582  	b := math.Float64bits(c)
   583  	man := b & (1<<52 - 1)
   584  	if man != 0 {
   585  		return false // not a power of 2, denormal, or NaN
   586  	}
   587  	exp := b >> 52 & (1<<11 - 1)
   588  	// exponent bias is 0x3ff.  So taking the reciprocal of a number
   589  	// changes the exponent to 0x7fe-exp.
   590  	switch exp {
   591  	case 0:
   592  		return false // ±0
   593  	case 0x7ff:
   594  		return false // ±inf
   595  	case 0x7fe:
   596  		return false // exponent is not representable
   597  	default:
   598  		return true
   599  	}
   600  }
   601  
   602  // reciprocalExact32 reports whether 1/c is exactly representable.
   603  func reciprocalExact32(c float32) bool {
   604  	b := math.Float32bits(c)
   605  	man := b & (1<<23 - 1)
   606  	if man != 0 {
   607  		return false // not a power of 2, denormal, or NaN
   608  	}
   609  	exp := b >> 23 & (1<<8 - 1)
   610  	// exponent bias is 0x7f.  So taking the reciprocal of a number
   611  	// changes the exponent to 0xfe-exp.
   612  	switch exp {
   613  	case 0:
   614  		return false // ±0
   615  	case 0xff:
   616  		return false // ±inf
   617  	case 0xfe:
   618  		return false // exponent is not representable
   619  	default:
   620  		return true
   621  	}
   622  }
   623  
   624  // check if an immediate can be directly encoded into an ARM's instruction
   625  func isARMImmRot(v uint32) bool {
   626  	for i := 0; i < 16; i++ {
   627  		if v&^0xff == 0 {
   628  			return true
   629  		}
   630  		v = v<<2 | v>>30
   631  	}
   632  
   633  	return false
   634  }
   635  
   636  // overlap reports whether the ranges given by the given offset and
   637  // size pairs overlap.
   638  func overlap(offset1, size1, offset2, size2 int64) bool {
   639  	if offset1 >= offset2 && offset2+size2 > offset1 {
   640  		return true
   641  	}
   642  	if offset2 >= offset1 && offset1+size1 > offset2 {
   643  		return true
   644  	}
   645  	return false
   646  }