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