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