github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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 "cmd/internal/obj" 9 "fmt" 10 "io" 11 "math" 12 "os" 13 "path/filepath" 14 ) 15 16 func applyRewrite(f *Func, rb blockRewriter, rv valueRewriter) { 17 // repeat rewrites until we find no more rewrites 18 for { 19 change := false 20 for _, b := range f.Blocks { 21 if b.Control != nil && b.Control.Op == OpCopy { 22 for b.Control.Op == OpCopy { 23 b.SetControl(b.Control.Args[0]) 24 } 25 } 26 if rb(b) { 27 change = true 28 } 29 for _, v := range b.Values { 30 change = phielimValue(v) || change 31 32 // Eliminate copy inputs. 33 // If any copy input becomes unused, mark it 34 // as invalid and discard its argument. Repeat 35 // recursively on the discarded argument. 36 // This phase helps remove phantom "dead copy" uses 37 // of a value so that a x.Uses==1 rule condition 38 // fires reliably. 39 for i, a := range v.Args { 40 if a.Op != OpCopy { 41 continue 42 } 43 v.SetArg(i, copySource(a)) 44 change = true 45 for a.Uses == 0 { 46 b := a.Args[0] 47 a.reset(OpInvalid) 48 a = b 49 } 50 } 51 52 // apply rewrite function 53 if rv(v) { 54 change = true 55 } 56 } 57 } 58 if !change { 59 break 60 } 61 } 62 // remove clobbered values 63 for _, b := range f.Blocks { 64 j := 0 65 for i, v := range b.Values { 66 if v.Op == OpInvalid { 67 f.freeValue(v) 68 continue 69 } 70 if i != j { 71 b.Values[j] = v 72 } 73 j++ 74 } 75 if j != len(b.Values) { 76 tail := b.Values[j:] 77 for j := range tail { 78 tail[j] = nil 79 } 80 b.Values = b.Values[:j] 81 } 82 } 83 } 84 85 // Common functions called from rewriting rules 86 87 func is64BitFloat(t Type) bool { 88 return t.Size() == 8 && t.IsFloat() 89 } 90 91 func is32BitFloat(t Type) bool { 92 return t.Size() == 4 && t.IsFloat() 93 } 94 95 func is64BitInt(t Type) bool { 96 return t.Size() == 8 && t.IsInteger() 97 } 98 99 func is32BitInt(t Type) bool { 100 return t.Size() == 4 && t.IsInteger() 101 } 102 103 func is16BitInt(t Type) bool { 104 return t.Size() == 2 && t.IsInteger() 105 } 106 107 func is8BitInt(t Type) bool { 108 return t.Size() == 1 && t.IsInteger() 109 } 110 111 func isPtr(t Type) bool { 112 return t.IsPtrShaped() 113 } 114 115 func isSigned(t Type) bool { 116 return t.IsSigned() 117 } 118 119 func typeSize(t Type) int64 { 120 return t.Size() 121 } 122 123 // mergeSym merges two symbolic offsets. There is no real merging of 124 // offsets, we just pick the non-nil one. 125 func mergeSym(x, y interface{}) interface{} { 126 if x == nil { 127 return y 128 } 129 if y == nil { 130 return x 131 } 132 panic(fmt.Sprintf("mergeSym with two non-nil syms %s %s", x, y)) 133 } 134 func canMergeSym(x, y interface{}) bool { 135 return x == nil || y == nil 136 } 137 138 // canMergeLoad reports whether the load can be merged into target without 139 // invalidating the schedule. 140 // It also checks that the other non-load argument x is something we 141 // are ok with clobbering (all our current load+op instructions clobber 142 // their input register). 143 func canMergeLoad(target, load, x *Value) bool { 144 if target.Block.ID != load.Block.ID { 145 // If the load is in a different block do not merge it. 146 return false 147 } 148 149 // We can't merge the load into the target if the load 150 // has more than one use. 151 if load.Uses != 1 { 152 return false 153 } 154 155 // The register containing x is going to get clobbered. 156 // Don't merge if we still need the value of x. 157 // We don't have liveness information here, but we can 158 // approximate x dying with: 159 // 1) target is x's only use. 160 // 2) target is not in a deeper loop than x. 161 if x.Uses != 1 { 162 return false 163 } 164 loopnest := x.Block.Func.loopnest() 165 loopnest.calculateDepths() 166 if loopnest.depth(target.Block.ID) > loopnest.depth(x.Block.ID) { 167 return false 168 } 169 170 mem := load.MemoryArg() 171 172 // We need the load's memory arg to still be alive at target. That 173 // can't be the case if one of target's args depends on a memory 174 // state that is a successor of load's memory arg. 175 // 176 // For example, it would be invalid to merge load into target in 177 // the following situation because newmem has killed oldmem 178 // before target is reached: 179 // load = read ... oldmem 180 // newmem = write ... oldmem 181 // arg0 = read ... newmem 182 // target = add arg0 load 183 // 184 // If the argument comes from a different block then we can exclude 185 // it immediately because it must dominate load (which is in the 186 // same block as target). 187 var args []*Value 188 for _, a := range target.Args { 189 if a != load && a.Block.ID == target.Block.ID { 190 args = append(args, a) 191 } 192 } 193 194 // memPreds contains memory states known to be predecessors of load's 195 // memory state. It is lazily initialized. 196 var memPreds map[*Value]bool 197 search: 198 for i := 0; len(args) > 0; i++ { 199 const limit = 100 200 if i >= limit { 201 // Give up if we have done a lot of iterations. 202 return false 203 } 204 v := args[len(args)-1] 205 args = args[:len(args)-1] 206 if target.Block.ID != v.Block.ID { 207 // Since target and load are in the same block 208 // we can stop searching when we leave the block. 209 continue search 210 } 211 if v.Op == OpPhi { 212 // A Phi implies we have reached the top of the block. 213 continue search 214 } 215 if v.Type.IsTuple() && v.Type.FieldType(1).IsMemory() { 216 // We could handle this situation however it is likely 217 // to be very rare. 218 return false 219 } 220 if v.Type.IsMemory() { 221 if memPreds == nil { 222 // Initialise a map containing memory states 223 // known to be predecessors of load's memory 224 // state. 225 memPreds = make(map[*Value]bool) 226 m := mem 227 const limit = 50 228 for i := 0; i < limit; i++ { 229 if m.Op == OpPhi { 230 break 231 } 232 if m.Block.ID != target.Block.ID { 233 break 234 } 235 if !m.Type.IsMemory() { 236 break 237 } 238 memPreds[m] = true 239 if len(m.Args) == 0 { 240 break 241 } 242 m = m.MemoryArg() 243 } 244 } 245 246 // We can merge if v is a predecessor of mem. 247 // 248 // For example, we can merge load into target in the 249 // following scenario: 250 // x = read ... v 251 // mem = write ... v 252 // load = read ... mem 253 // target = add x load 254 if memPreds[v] { 255 continue search 256 } 257 return false 258 } 259 if len(v.Args) > 0 && v.Args[len(v.Args)-1] == mem { 260 // If v takes mem as an input then we know mem 261 // is valid at this point. 262 continue search 263 } 264 for _, a := range v.Args { 265 if target.Block.ID == a.Block.ID { 266 args = append(args, a) 267 } 268 } 269 } 270 271 return true 272 } 273 274 // isArg returns whether s is an arg symbol 275 func isArg(s interface{}) bool { 276 _, ok := s.(*ArgSymbol) 277 return ok 278 } 279 280 // isAuto returns whether s is an auto symbol 281 func isAuto(s interface{}) bool { 282 _, ok := s.(*AutoSymbol) 283 return ok 284 } 285 286 func fitsARM64Offset(off, align int64, sym interface{}) bool { 287 // only small offset (between -256 and 256) or offset that is a multiple of data size 288 // can be encoded in the instructions 289 // since this rewriting takes place before stack allocation, the offset to SP is unknown, 290 // so don't do it for args and locals with unaligned offset 291 if !is32Bit(off) { 292 return false 293 } 294 if align == 1 { 295 return true 296 } 297 return !isArg(sym) && (off%align == 0 || off < 256 && off > -256 && !isAuto(sym)) 298 } 299 300 // isSameSym returns whether sym is the same as the given named symbol 301 func isSameSym(sym interface{}, name string) bool { 302 s, ok := sym.(fmt.Stringer) 303 return ok && s.String() == name 304 } 305 306 // nlz returns the number of leading zeros. 307 func nlz(x int64) int64 { 308 // log2(0) == 1, so nlz(0) == 64 309 return 63 - log2(x) 310 } 311 312 // ntz returns the number of trailing zeros. 313 func ntz(x int64) int64 { 314 return 64 - nlz(^x&(x-1)) 315 } 316 317 // nlo returns the number of leading ones. 318 func nlo(x int64) int64 { 319 return nlz(^x) 320 } 321 322 // nto returns the number of trailing ones. 323 func nto(x int64) int64 { 324 return ntz(^x) 325 } 326 327 // log2 returns logarithm in base 2 of uint64(n), with log2(0) = -1. 328 // Rounds down. 329 func log2(n int64) (l int64) { 330 l = -1 331 x := uint64(n) 332 for ; x >= 0x8000; x >>= 16 { 333 l += 16 334 } 335 if x >= 0x80 { 336 x >>= 8 337 l += 8 338 } 339 if x >= 0x8 { 340 x >>= 4 341 l += 4 342 } 343 if x >= 0x2 { 344 x >>= 2 345 l += 2 346 } 347 if x >= 0x1 { 348 l++ 349 } 350 return 351 } 352 353 // isPowerOfTwo reports whether n is a power of 2. 354 func isPowerOfTwo(n int64) bool { 355 return n > 0 && n&(n-1) == 0 356 } 357 358 // is32Bit reports whether n can be represented as a signed 32 bit integer. 359 func is32Bit(n int64) bool { 360 return n == int64(int32(n)) 361 } 362 363 // is16Bit reports whether n can be represented as a signed 16 bit integer. 364 func is16Bit(n int64) bool { 365 return n == int64(int16(n)) 366 } 367 368 // isU16Bit reports whether n can be represented as an unsigned 16 bit integer. 369 func isU16Bit(n int64) bool { 370 return n == int64(uint16(n)) 371 } 372 373 // isU32Bit reports whether n can be represented as an unsigned 32 bit integer. 374 func isU32Bit(n int64) bool { 375 return n == int64(uint32(n)) 376 } 377 378 // is20Bit reports whether n can be represented as a signed 20 bit integer. 379 func is20Bit(n int64) bool { 380 return -(1<<19) <= n && n < (1<<19) 381 } 382 383 // b2i translates a boolean value to 0 or 1 for assigning to auxInt. 384 func b2i(b bool) int64 { 385 if b { 386 return 1 387 } 388 return 0 389 } 390 391 // i2f is used in rules for converting from an AuxInt to a float. 392 func i2f(i int64) float64 { 393 return math.Float64frombits(uint64(i)) 394 } 395 396 // i2f32 is used in rules for converting from an AuxInt to a float32. 397 func i2f32(i int64) float32 { 398 return float32(math.Float64frombits(uint64(i))) 399 } 400 401 // f2i is used in the rules for storing a float in AuxInt. 402 func f2i(f float64) int64 { 403 return int64(math.Float64bits(f)) 404 } 405 406 // uaddOvf returns true if unsigned a+b would overflow. 407 func uaddOvf(a, b int64) bool { 408 return uint64(a)+uint64(b) < uint64(a) 409 } 410 411 // de-virtualize an InterCall 412 // 'sym' is the symbol for the itab 413 func devirt(v *Value, sym interface{}, offset int64) *obj.LSym { 414 f := v.Block.Func 415 ext, ok := sym.(*ExternSymbol) 416 if !ok { 417 return nil 418 } 419 lsym := f.fe.DerefItab(ext.Sym, offset) 420 if f.pass.debug > 0 { 421 if lsym != nil { 422 f.Warnl(v.Pos, "de-virtualizing call") 423 } else { 424 f.Warnl(v.Pos, "couldn't de-virtualize call") 425 } 426 } 427 return lsym 428 } 429 430 // isSamePtr reports whether p1 and p2 point to the same address. 431 func isSamePtr(p1, p2 *Value) bool { 432 if p1 == p2 { 433 return true 434 } 435 if p1.Op != p2.Op { 436 return false 437 } 438 switch p1.Op { 439 case OpOffPtr: 440 return p1.AuxInt == p2.AuxInt && isSamePtr(p1.Args[0], p2.Args[0]) 441 case OpAddr: 442 // OpAddr's 0th arg is either OpSP or OpSB, which means that it is uniquely identified by its Op. 443 // Checking for value equality only works after [z]cse has run. 444 return p1.Aux == p2.Aux && p1.Args[0].Op == p2.Args[0].Op 445 case OpAddPtr: 446 return p1.Args[1] == p2.Args[1] && isSamePtr(p1.Args[0], p2.Args[0]) 447 } 448 return false 449 } 450 451 // moveSize returns the number of bytes an aligned MOV instruction moves 452 func moveSize(align int64, c *Config) int64 { 453 switch { 454 case align%8 == 0 && c.PtrSize == 8: 455 return 8 456 case align%4 == 0: 457 return 4 458 case align%2 == 0: 459 return 2 460 } 461 return 1 462 } 463 464 // mergePoint finds a block among a's blocks which dominates b and is itself 465 // dominated by all of a's blocks. Returns nil if it can't find one. 466 // Might return nil even if one does exist. 467 func mergePoint(b *Block, a ...*Value) *Block { 468 // Walk backward from b looking for one of the a's blocks. 469 470 // Max distance 471 d := 100 472 473 for d > 0 { 474 for _, x := range a { 475 if b == x.Block { 476 goto found 477 } 478 } 479 if len(b.Preds) > 1 { 480 // Don't know which way to go back. Abort. 481 return nil 482 } 483 b = b.Preds[0].b 484 d-- 485 } 486 return nil // too far away 487 found: 488 // At this point, r is the first value in a that we find by walking backwards. 489 // if we return anything, r will be it. 490 r := b 491 492 // Keep going, counting the other a's that we find. They must all dominate r. 493 na := 0 494 for d > 0 { 495 for _, x := range a { 496 if b == x.Block { 497 na++ 498 } 499 } 500 if na == len(a) { 501 // Found all of a in a backwards walk. We can return r. 502 return r 503 } 504 if len(b.Preds) > 1 { 505 return nil 506 } 507 b = b.Preds[0].b 508 d-- 509 510 } 511 return nil // too far away 512 } 513 514 // clobber invalidates v. Returns true. 515 // clobber is used by rewrite rules to: 516 // A) make sure v is really dead and never used again. 517 // B) decrement use counts of v's args. 518 func clobber(v *Value) bool { 519 v.reset(OpInvalid) 520 // Note: leave v.Block intact. The Block field is used after clobber. 521 return true 522 } 523 524 // noteRule is an easy way to track if a rule is matched when writing 525 // new ones. Make the rule of interest also conditional on 526 // noteRule("note to self: rule of interest matched") 527 // and that message will print when the rule matches. 528 func noteRule(s string) bool { 529 fmt.Println(s) 530 return true 531 } 532 533 // warnRule generates a compiler debug output with string s when 534 // cond is true and the rule is fired. 535 func warnRule(cond bool, v *Value, s string) bool { 536 if cond { 537 v.Block.Func.Warnl(v.Pos, s) 538 } 539 return true 540 } 541 542 // logRule logs the use of the rule s. This will only be enabled if 543 // rewrite rules were generated with the -log option, see gen/rulegen.go. 544 func logRule(s string) { 545 if ruleFile == nil { 546 // Open a log file to write log to. We open in append 547 // mode because all.bash runs the compiler lots of times, 548 // and we want the concatenation of all of those logs. 549 // This means, of course, that users need to rm the old log 550 // to get fresh data. 551 // TODO: all.bash runs compilers in parallel. Need to synchronize logging somehow? 552 w, err := os.OpenFile(filepath.Join(os.Getenv("GOROOT"), "src", "rulelog"), 553 os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) 554 if err != nil { 555 panic(err) 556 } 557 ruleFile = w 558 } 559 _, err := fmt.Fprintf(ruleFile, "rewrite %s\n", s) 560 if err != nil { 561 panic(err) 562 } 563 } 564 565 var ruleFile io.Writer 566 567 func min(x, y int64) int64 { 568 if x < y { 569 return x 570 } 571 return y 572 } 573 574 func isConstZero(v *Value) bool { 575 switch v.Op { 576 case OpConstNil: 577 return true 578 case OpConst64, OpConst32, OpConst16, OpConst8, OpConstBool, OpConst32F, OpConst64F: 579 return v.AuxInt == 0 580 } 581 return false 582 } 583 584 // reciprocalExact64 reports whether 1/c is exactly representable. 585 func reciprocalExact64(c float64) bool { 586 b := math.Float64bits(c) 587 man := b & (1<<52 - 1) 588 if man != 0 { 589 return false // not a power of 2, denormal, or NaN 590 } 591 exp := b >> 52 & (1<<11 - 1) 592 // exponent bias is 0x3ff. So taking the reciprocal of a number 593 // changes the exponent to 0x7fe-exp. 594 switch exp { 595 case 0: 596 return false // ±0 597 case 0x7ff: 598 return false // ±inf 599 case 0x7fe: 600 return false // exponent is not representable 601 default: 602 return true 603 } 604 } 605 606 // reciprocalExact32 reports whether 1/c is exactly representable. 607 func reciprocalExact32(c float32) bool { 608 b := math.Float32bits(c) 609 man := b & (1<<23 - 1) 610 if man != 0 { 611 return false // not a power of 2, denormal, or NaN 612 } 613 exp := b >> 23 & (1<<8 - 1) 614 // exponent bias is 0x7f. So taking the reciprocal of a number 615 // changes the exponent to 0xfe-exp. 616 switch exp { 617 case 0: 618 return false // ±0 619 case 0xff: 620 return false // ±inf 621 case 0xfe: 622 return false // exponent is not representable 623 default: 624 return true 625 } 626 }