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 }