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 }