github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/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