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