github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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) 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) {
    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  // nlz returns the number of leading zeros.
   153  func nlz(x int64) int64 {
   154  	// log2(0) == 1, so nlz(0) == 64
   155  	return 63 - log2(x)
   156  }
   157  
   158  // ntz returns the number of trailing zeros.
   159  func ntz(x int64) int64 {
   160  	return 64 - nlz(^x&(x-1))
   161  }
   162  
   163  // nlo returns the number of leading ones.
   164  func nlo(x int64) int64 {
   165  	return nlz(^x)
   166  }
   167  
   168  // nto returns the number of trailing ones.
   169  func nto(x int64) int64 {
   170  	return ntz(^x)
   171  }
   172  
   173  // log2 returns logarithm in base of uint64(n), with log2(0) = -1.
   174  func log2(n int64) (l int64) {
   175  	l = -1
   176  	x := uint64(n)
   177  	for ; x >= 0x8000; x >>= 16 {
   178  		l += 16
   179  	}
   180  	if x >= 0x80 {
   181  		x >>= 8
   182  		l += 8
   183  	}
   184  	if x >= 0x8 {
   185  		x >>= 4
   186  		l += 4
   187  	}
   188  	if x >= 0x2 {
   189  		x >>= 2
   190  		l += 2
   191  	}
   192  	if x >= 0x1 {
   193  		l++
   194  	}
   195  	return
   196  }
   197  
   198  // isPowerOfTwo reports whether n is a power of 2.
   199  func isPowerOfTwo(n int64) bool {
   200  	return n > 0 && n&(n-1) == 0
   201  }
   202  
   203  // is32Bit reports whether n can be represented as a signed 32 bit integer.
   204  func is32Bit(n int64) bool {
   205  	return n == int64(int32(n))
   206  }
   207  
   208  // b2i translates a boolean value to 0 or 1 for assigning to auxInt.
   209  func b2i(b bool) int64 {
   210  	if b {
   211  		return 1
   212  	}
   213  	return 0
   214  }
   215  
   216  // i2f is used in rules for converting from an AuxInt to a float.
   217  func i2f(i int64) float64 {
   218  	return math.Float64frombits(uint64(i))
   219  }
   220  
   221  // i2f32 is used in rules for converting from an AuxInt to a float32.
   222  func i2f32(i int64) float32 {
   223  	return float32(math.Float64frombits(uint64(i)))
   224  }
   225  
   226  // f2i is used in the rules for storing a float in AuxInt.
   227  func f2i(f float64) int64 {
   228  	return int64(math.Float64bits(f))
   229  }
   230  
   231  // uaddOvf returns true if unsigned a+b would overflow.
   232  func uaddOvf(a, b int64) bool {
   233  	return uint64(a)+uint64(b) < uint64(a)
   234  }
   235  
   236  // isSamePtr reports whether p1 and p2 point to the same address.
   237  func isSamePtr(p1, p2 *Value) bool {
   238  	if p1 == p2 {
   239  		return true
   240  	}
   241  	if p1.Op != p2.Op {
   242  		return false
   243  	}
   244  	switch p1.Op {
   245  	case OpOffPtr:
   246  		return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0])
   247  	case OpAddr:
   248  		// OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op.
   249  		// Checking for value equality only works after [z]cse has run.
   250  		return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op
   251  	case OpAddPtr:
   252  		return p1.Args[1] == p2.Args[1] && isSamePtr(p1.Args[0], p2.Args[0])
   253  	}
   254  	return false
   255  }
   256  
   257  // DUFFZERO consists of repeated blocks of 4 MOVUPSs + ADD,
   258  // See runtime/mkduff.go.
   259  const (
   260  	dzBlocks    = 16 // number of MOV/ADD blocks
   261  	dzBlockLen  = 4  // number of clears per block
   262  	dzBlockSize = 19 // size of instructions in a single block
   263  	dzMovSize   = 4  // size of single MOV instruction w/ offset
   264  	dzAddSize   = 4  // size of single ADD instruction
   265  	dzClearStep = 16 // number of bytes cleared by each MOV instruction
   266  
   267  	dzTailLen  = 4 // number of final STOSQ instructions
   268  	dzTailSize = 2 // size of single STOSQ instruction
   269  
   270  	dzClearLen = dzClearStep * dzBlockLen // bytes cleared by one block
   271  	dzSize     = dzBlocks * dzBlockSize
   272  )
   273  
   274  func duffStart(size int64) int64 {
   275  	x, _ := duff(size)
   276  	return x
   277  }
   278  func duffAdj(size int64) int64 {
   279  	_, x := duff(size)
   280  	return x
   281  }
   282  
   283  // duff returns the offset (from duffzero, in bytes) and pointer adjust (in bytes)
   284  // required to use the duffzero mechanism for a block of the given size.
   285  func duff(size int64) (int64, int64) {
   286  	if size < 32 || size > 1024 || size%dzClearStep != 0 {
   287  		panic("bad duffzero size")
   288  	}
   289  	// TODO: arch-dependent
   290  	steps := size / dzClearStep
   291  	blocks := steps / dzBlockLen
   292  	steps %= dzBlockLen
   293  	off := dzBlockSize * (dzBlocks - blocks)
   294  	var adj int64
   295  	if steps != 0 {
   296  		off -= dzAddSize
   297  		off -= dzMovSize * steps
   298  		adj -= dzClearStep * (dzBlockLen - steps)
   299  	}
   300  	return off, adj
   301  }
   302  
   303  // mergePoint finds a block among a's blocks which dominates b and is itself
   304  // dominated by all of a's blocks. Returns nil if it can't find one.
   305  // Might return nil even if one does exist.
   306  func mergePoint(b *Block, a ...*Value) *Block {
   307  	// Walk backward from b looking for one of the a's blocks.
   308  
   309  	// Max distance
   310  	d := 100
   311  
   312  	for d > 0 {
   313  		for _, x := range a {
   314  			if b == x.Block {
   315  				goto found
   316  			}
   317  		}
   318  		if len(b.Preds) > 1 {
   319  			// Don't know which way to go back. Abort.
   320  			return nil
   321  		}
   322  		b = b.Preds[0].b
   323  		d--
   324  	}
   325  	return nil // too far away
   326  found:
   327  	// At this point, r is the first value in a that we find by walking backwards.
   328  	// if we return anything, r will be it.
   329  	r := b
   330  
   331  	// Keep going, counting the other a's that we find. They must all dominate r.
   332  	na := 0
   333  	for d > 0 {
   334  		for _, x := range a {
   335  			if b == x.Block {
   336  				na++
   337  			}
   338  		}
   339  		if na == len(a) {
   340  			// Found all of a in a backwards walk. We can return r.
   341  			return r
   342  		}
   343  		if len(b.Preds) > 1 {
   344  			return nil
   345  		}
   346  		b = b.Preds[0].b
   347  		d--
   348  
   349  	}
   350  	return nil // too far away
   351  }
   352  
   353  // clobber invalidates v.  Returns true.
   354  // clobber is used by rewrite rules to:
   355  //   A) make sure v is really dead and never used again.
   356  //   B) decrement use counts of v's args.
   357  func clobber(v *Value) bool {
   358  	v.reset(OpInvalid)
   359  	// Note: leave v.Block intact.  The Block field is used after clobber.
   360  	return true
   361  }
   362  
   363  // logRule logs the use of the rule s. This will only be enabled if
   364  // rewrite rules were generated with the -log option, see gen/rulegen.go.
   365  func logRule(s string) {
   366  	if ruleFile == nil {
   367  		// Open a log file to write log to. We open in append
   368  		// mode because all.bash runs the compiler lots of times,
   369  		// and we want the concatenation of all of those logs.
   370  		// This means, of course, that users need to rm the old log
   371  		// to get fresh data.
   372  		// TODO: all.bash runs compilers in parallel. Need to synchronize logging somehow?
   373  		w, err := os.OpenFile(filepath.Join(os.Getenv("GOROOT"), "src", "rulelog"),
   374  			os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
   375  		if err != nil {
   376  			panic(err)
   377  		}
   378  		ruleFile = w
   379  	}
   380  	_, err := fmt.Fprintf(ruleFile, "rewrite %s\n", s)
   381  	if err != nil {
   382  		panic(err)
   383  	}
   384  }
   385  
   386  var ruleFile *os.File