github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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  )
    11  
    12  func applyRewrite(f *Func, rb func(*Block) bool, rv func(*Value, *Config) bool) {
    13  	// repeat rewrites until we find no more rewrites
    14  	var curb *Block
    15  	var curv *Value
    16  	defer func() {
    17  		if curb != nil {
    18  			curb.Fatalf("panic during rewrite of block %s\n", curb.LongString())
    19  		}
    20  		if curv != nil {
    21  			curv.Fatalf("panic during rewrite of value %s\n", curv.LongString())
    22  			// TODO(khr): print source location also
    23  		}
    24  	}()
    25  	config := f.Config
    26  	for {
    27  		change := false
    28  		for _, b := range f.Blocks {
    29  			if b.Kind == BlockDead {
    30  				continue
    31  			}
    32  			if b.Control != nil && b.Control.Op == OpCopy {
    33  				for b.Control.Op == OpCopy {
    34  					b.SetControl(b.Control.Args[0])
    35  				}
    36  			}
    37  			curb = b
    38  			if rb(b) {
    39  				change = true
    40  			}
    41  			curb = nil
    42  			for _, v := range b.Values {
    43  				change = copyelimValue(v) || change
    44  				change = phielimValue(v) || change
    45  
    46  				// apply rewrite function
    47  				curv = v
    48  				if rv(v, config) {
    49  					change = true
    50  				}
    51  				curv = nil
    52  			}
    53  		}
    54  		if !change {
    55  			return
    56  		}
    57  	}
    58  }
    59  
    60  // Common functions called from rewriting rules
    61  
    62  func is64BitFloat(t Type) bool {
    63  	return t.Size() == 8 && t.IsFloat()
    64  }
    65  
    66  func is32BitFloat(t Type) bool {
    67  	return t.Size() == 4 && t.IsFloat()
    68  }
    69  
    70  func is64BitInt(t Type) bool {
    71  	return t.Size() == 8 && t.IsInteger()
    72  }
    73  
    74  func is32BitInt(t Type) bool {
    75  	return t.Size() == 4 && t.IsInteger()
    76  }
    77  
    78  func is16BitInt(t Type) bool {
    79  	return t.Size() == 2 && t.IsInteger()
    80  }
    81  
    82  func is8BitInt(t Type) bool {
    83  	return t.Size() == 1 && t.IsInteger()
    84  }
    85  
    86  func isPtr(t Type) bool {
    87  	return t.IsPtrShaped()
    88  }
    89  
    90  func isSigned(t Type) bool {
    91  	return t.IsSigned()
    92  }
    93  
    94  func typeSize(t Type) int64 {
    95  	return t.Size()
    96  }
    97  
    98  // mergeSym merges two symbolic offsets. There is no real merging of
    99  // offsets, we just pick the non-nil one.
   100  func mergeSym(x, y interface{}) interface{} {
   101  	if x == nil {
   102  		return y
   103  	}
   104  	if y == nil {
   105  		return x
   106  	}
   107  	panic(fmt.Sprintf("mergeSym with two non-nil syms %s %s", x, y))
   108  	return nil
   109  }
   110  func canMergeSym(x, y interface{}) bool {
   111  	return x == nil || y == nil
   112  }
   113  
   114  // nlz returns the number of leading zeros.
   115  func nlz(x int64) int64 {
   116  	// log2(0) == 1, so nlz(0) == 64
   117  	return 63 - log2(x)
   118  }
   119  
   120  // ntz returns the number of trailing zeros.
   121  func ntz(x int64) int64 {
   122  	return 64 - nlz(^x&(x-1))
   123  }
   124  
   125  // nlo returns the number of leading ones.
   126  func nlo(x int64) int64 {
   127  	return nlz(^x)
   128  }
   129  
   130  // nto returns the number of trailing ones.
   131  func nto(x int64) int64 {
   132  	return ntz(^x)
   133  }
   134  
   135  // log2 returns logarithm in base of uint64(n), with log2(0) = -1.
   136  func log2(n int64) (l int64) {
   137  	l = -1
   138  	x := uint64(n)
   139  	for ; x >= 0x8000; x >>= 16 {
   140  		l += 16
   141  	}
   142  	if x >= 0x80 {
   143  		x >>= 8
   144  		l += 8
   145  	}
   146  	if x >= 0x8 {
   147  		x >>= 4
   148  		l += 4
   149  	}
   150  	if x >= 0x2 {
   151  		x >>= 2
   152  		l += 2
   153  	}
   154  	if x >= 0x1 {
   155  		l++
   156  	}
   157  	return
   158  }
   159  
   160  // isPowerOfTwo reports whether n is a power of 2.
   161  func isPowerOfTwo(n int64) bool {
   162  	return n > 0 && n&(n-1) == 0
   163  }
   164  
   165  // is32Bit reports whether n can be represented as a signed 32 bit integer.
   166  func is32Bit(n int64) bool {
   167  	return n == int64(int32(n))
   168  }
   169  
   170  // b2i translates a boolean value to 0 or 1 for assigning to auxInt.
   171  func b2i(b bool) int64 {
   172  	if b {
   173  		return 1
   174  	}
   175  	return 0
   176  }
   177  
   178  // i2f is used in rules for converting from an AuxInt to a float.
   179  func i2f(i int64) float64 {
   180  	return math.Float64frombits(uint64(i))
   181  }
   182  
   183  // i2f32 is used in rules for converting from an AuxInt to a float32.
   184  func i2f32(i int64) float32 {
   185  	return float32(math.Float64frombits(uint64(i)))
   186  }
   187  
   188  // f2i is used in the rules for storing a float in AuxInt.
   189  func f2i(f float64) int64 {
   190  	return int64(math.Float64bits(f))
   191  }
   192  
   193  // uaddOvf returns true if unsigned a+b would overflow.
   194  func uaddOvf(a, b int64) bool {
   195  	return uint64(a)+uint64(b) < uint64(a)
   196  }
   197  
   198  // isSamePtr reports whether p1 and p2 point to the same address.
   199  func isSamePtr(p1, p2 *Value) bool {
   200  	if p1 == p2 {
   201  		return true
   202  	}
   203  	if p1.Op != p2.Op {
   204  		return false
   205  	}
   206  	switch p1.Op {
   207  	case OpOffPtr:
   208  		return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0])
   209  	case OpAddr:
   210  		// OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op.
   211  		// Checking for value equality only works after [z]cse has run.
   212  		return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op
   213  	case OpAddPtr:
   214  		return p1.Args[1] == p2.Args[1] && isSamePtr(p1.Args[0], p2.Args[0])
   215  	}
   216  	return false
   217  }
   218  
   219  // DUFFZERO consists of repeated blocks of 4 MOVUPSs + ADD,
   220  // See runtime/mkduff.go.
   221  const (
   222  	dzBlocks    = 16 // number of MOV/ADD blocks
   223  	dzBlockLen  = 4  // number of clears per block
   224  	dzBlockSize = 19 // size of instructions in a single block
   225  	dzMovSize   = 4  // size of single MOV instruction w/ offset
   226  	dzAddSize   = 4  // size of single ADD instruction
   227  	dzClearStep = 16 // number of bytes cleared by each MOV instruction
   228  
   229  	dzTailLen  = 4 // number of final STOSQ instructions
   230  	dzTailSize = 2 // size of single STOSQ instruction
   231  
   232  	dzClearLen = dzClearStep * dzBlockLen // bytes cleared by one block
   233  	dzSize     = dzBlocks * dzBlockSize
   234  )
   235  
   236  func duffStart(size int64) int64 {
   237  	x, _ := duff(size)
   238  	return x
   239  }
   240  func duffAdj(size int64) int64 {
   241  	_, x := duff(size)
   242  	return x
   243  }
   244  
   245  // duff returns the offset (from duffzero, in bytes) and pointer adjust (in bytes)
   246  // required to use the duffzero mechanism for a block of the given size.
   247  func duff(size int64) (int64, int64) {
   248  	if size < 32 || size > 1024 || size%dzClearStep != 0 {
   249  		panic("bad duffzero size")
   250  	}
   251  	// TODO: arch-dependent
   252  	steps := size / dzClearStep
   253  	blocks := steps / dzBlockLen
   254  	steps %= dzBlockLen
   255  	off := dzBlockSize * (dzBlocks - blocks)
   256  	var adj int64
   257  	if steps != 0 {
   258  		off -= dzAddSize
   259  		off -= dzMovSize * steps
   260  		adj -= dzClearStep * (dzBlockLen - steps)
   261  	}
   262  	return off, adj
   263  }
   264  
   265  // mergePoint finds a block among a's blocks which dominates b and is itself
   266  // dominated by all of a's blocks. Returns nil if it can't find one.
   267  // Might return nil even if one does exist.
   268  func mergePoint(b *Block, a ...*Value) *Block {
   269  	// Walk backward from b looking for one of the a's blocks.
   270  
   271  	// Max distance
   272  	d := 100
   273  
   274  	for d > 0 {
   275  		for _, x := range a {
   276  			if b == x.Block {
   277  				goto found
   278  			}
   279  		}
   280  		if len(b.Preds) > 1 {
   281  			// Don't know which way to go back. Abort.
   282  			return nil
   283  		}
   284  		b = b.Preds[0]
   285  		d--
   286  	}
   287  	return nil // too far away
   288  found:
   289  	// At this point, r is the first value in a that we find by walking backwards.
   290  	// if we return anything, r will be it.
   291  	r := b
   292  
   293  	// Keep going, counting the other a's that we find. They must all dominate r.
   294  	na := 0
   295  	for d > 0 {
   296  		for _, x := range a {
   297  			if b == x.Block {
   298  				na++
   299  			}
   300  		}
   301  		if na == len(a) {
   302  			// Found all of a in a backwards walk. We can return r.
   303  			return r
   304  		}
   305  		if len(b.Preds) > 1 {
   306  			return nil
   307  		}
   308  		b = b.Preds[0]
   309  		d--
   310  
   311  	}
   312  	return nil // too far away
   313  }