github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/cmd/compile/internal/ssa/prove.go (about) 1 // Copyright 2016 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 type branch int 13 14 const ( 15 unknown = iota 16 positive 17 negative 18 ) 19 20 // relation represents the set of possible relations between 21 // pairs of variables (v, w). Without a priori knowledge the 22 // mask is lt | eq | gt meaning v can be less than, equal to or 23 // greater than w. When the execution path branches on the condition 24 // `v op w` the set of relations is updated to exclude any 25 // relation not possible due to `v op w` being true (or false). 26 // 27 // E.g. 28 // 29 // r := relation(...) 30 // 31 // if v < w { 32 // newR := r & lt 33 // } 34 // if v >= w { 35 // newR := r & (eq|gt) 36 // } 37 // if v != w { 38 // newR := r & (lt|gt) 39 // } 40 type relation uint 41 42 const ( 43 lt relation = 1 << iota 44 eq 45 gt 46 ) 47 48 // domain represents the domain of a variable pair in which a set 49 // of relations is known. For example, relations learned for unsigned 50 // pairs cannot be transferred to signed pairs because the same bit 51 // representation can mean something else. 52 type domain uint 53 54 const ( 55 signed domain = 1 << iota 56 unsigned 57 pointer 58 boolean 59 ) 60 61 type pair struct { 62 v, w *Value // a pair of values, ordered by ID. 63 // v can be nil, to mean the zero value. 64 // for booleans the zero value (v == nil) is false. 65 d domain 66 } 67 68 // fact is a pair plus a relation for that pair. 69 type fact struct { 70 p pair 71 r relation 72 } 73 74 // a limit records known upper and lower bounds for a value. 75 type limit struct { 76 min, max int64 // min <= value <= max, signed 77 umin, umax uint64 // umin <= value <= umax, unsigned 78 } 79 80 func (l limit) String() string { 81 return fmt.Sprintf("sm,SM,um,UM=%d,%d,%d,%d", l.min, l.max, l.umin, l.umax) 82 } 83 84 var noLimit = limit{math.MinInt64, math.MaxInt64, 0, math.MaxUint64} 85 86 // a limitFact is a limit known for a particular value. 87 type limitFact struct { 88 vid ID 89 limit limit 90 } 91 92 // factsTable keeps track of relations between pairs of values. 93 type factsTable struct { 94 facts map[pair]relation // current known set of relation 95 stack []fact // previous sets of relations 96 97 // known lower and upper bounds on individual values. 98 limits map[ID]limit 99 limitStack []limitFact // previous entries 100 101 // For each slice s, a map from s to a len(s)/cap(s) value (if any) 102 // TODO: check if there are cases that matter where we have 103 // more than one len(s) for a slice. We could keep a list if necessary. 104 lens map[ID]*Value 105 caps map[ID]*Value 106 } 107 108 // checkpointFact is an invalid value used for checkpointing 109 // and restoring factsTable. 110 var checkpointFact = fact{} 111 var checkpointBound = limitFact{} 112 113 func newFactsTable() *factsTable { 114 ft := &factsTable{} 115 ft.facts = make(map[pair]relation) 116 ft.stack = make([]fact, 4) 117 ft.limits = make(map[ID]limit) 118 ft.limitStack = make([]limitFact, 4) 119 return ft 120 } 121 122 // get returns the known possible relations between v and w. 123 // If v and w are not in the map it returns lt|eq|gt, i.e. any order. 124 func (ft *factsTable) get(v, w *Value, d domain) relation { 125 if v.isGenericIntConst() || w.isGenericIntConst() { 126 reversed := false 127 if v.isGenericIntConst() { 128 v, w = w, v 129 reversed = true 130 } 131 r := lt | eq | gt 132 lim, ok := ft.limits[v.ID] 133 if !ok { 134 return r 135 } 136 c := w.AuxInt 137 switch d { 138 case signed: 139 switch { 140 case c < lim.min: 141 r = gt 142 case c > lim.max: 143 r = lt 144 case c == lim.min && c == lim.max: 145 r = eq 146 case c == lim.min: 147 r = gt | eq 148 case c == lim.max: 149 r = lt | eq 150 } 151 case unsigned: 152 // TODO: also use signed data if lim.min >= 0? 153 var uc uint64 154 switch w.Op { 155 case OpConst64: 156 uc = uint64(c) 157 case OpConst32: 158 uc = uint64(uint32(c)) 159 case OpConst16: 160 uc = uint64(uint16(c)) 161 case OpConst8: 162 uc = uint64(uint8(c)) 163 } 164 switch { 165 case uc < lim.umin: 166 r = gt 167 case uc > lim.umax: 168 r = lt 169 case uc == lim.umin && uc == lim.umax: 170 r = eq 171 case uc == lim.umin: 172 r = gt | eq 173 case uc == lim.umax: 174 r = lt | eq 175 } 176 } 177 if reversed { 178 return reverseBits[r] 179 } 180 return r 181 } 182 183 reversed := false 184 if lessByID(w, v) { 185 v, w = w, v 186 reversed = !reversed 187 } 188 189 p := pair{v, w, d} 190 r, ok := ft.facts[p] 191 if !ok { 192 if p.v == p.w { 193 r = eq 194 } else { 195 r = lt | eq | gt 196 } 197 } 198 199 if reversed { 200 return reverseBits[r] 201 } 202 return r 203 } 204 205 // update updates the set of relations between v and w in domain d 206 // restricting it to r. 207 func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) { 208 if lessByID(w, v) { 209 v, w = w, v 210 r = reverseBits[r] 211 } 212 213 p := pair{v, w, d} 214 oldR := ft.get(v, w, d) 215 ft.stack = append(ft.stack, fact{p, oldR}) 216 ft.facts[p] = oldR & r 217 218 // Extract bounds when comparing against constants 219 if v.isGenericIntConst() { 220 v, w = w, v 221 r = reverseBits[r] 222 } 223 if v != nil && w.isGenericIntConst() { 224 c := w.AuxInt 225 // Note: all the +1/-1 below could overflow/underflow. Either will 226 // still generate correct results, it will just lead to imprecision. 227 // In fact if there is overflow/underflow, the corresponding 228 // code is unreachable because the known range is outside the range 229 // of the value's type. 230 old, ok := ft.limits[v.ID] 231 if !ok { 232 old = noLimit 233 } 234 lim := old 235 // Update lim with the new information we know. 236 switch d { 237 case signed: 238 switch r { 239 case lt: 240 if c-1 < lim.max { 241 lim.max = c - 1 242 } 243 case lt | eq: 244 if c < lim.max { 245 lim.max = c 246 } 247 case gt | eq: 248 if c > lim.min { 249 lim.min = c 250 } 251 case gt: 252 if c+1 > lim.min { 253 lim.min = c + 1 254 } 255 case lt | gt: 256 if c == lim.min { 257 lim.min++ 258 } 259 if c == lim.max { 260 lim.max-- 261 } 262 case eq: 263 lim.min = c 264 lim.max = c 265 } 266 case unsigned: 267 var uc uint64 268 switch w.Op { 269 case OpConst64: 270 uc = uint64(c) 271 case OpConst32: 272 uc = uint64(uint32(c)) 273 case OpConst16: 274 uc = uint64(uint16(c)) 275 case OpConst8: 276 uc = uint64(uint8(c)) 277 } 278 switch r { 279 case lt: 280 if uc-1 < lim.umax { 281 lim.umax = uc - 1 282 } 283 case lt | eq: 284 if uc < lim.umax { 285 lim.umax = uc 286 } 287 case gt | eq: 288 if uc > lim.umin { 289 lim.umin = uc 290 } 291 case gt: 292 if uc+1 > lim.umin { 293 lim.umin = uc + 1 294 } 295 case lt | gt: 296 if uc == lim.umin { 297 lim.umin++ 298 } 299 if uc == lim.umax { 300 lim.umax-- 301 } 302 case eq: 303 lim.umin = uc 304 lim.umax = uc 305 } 306 } 307 ft.limitStack = append(ft.limitStack, limitFact{v.ID, old}) 308 ft.limits[v.ID] = lim 309 if v.Block.Func.pass.debug > 2 { 310 v.Block.Func.Warnl(parent.Pos, "parent=%s, new limits %s %s %s", parent, v, w, lim.String()) 311 } 312 } 313 } 314 315 // isNonNegative returns true if v is known to be non-negative. 316 func (ft *factsTable) isNonNegative(v *Value) bool { 317 if isNonNegative(v) { 318 return true 319 } 320 l, has := ft.limits[v.ID] 321 return has && (l.min >= 0 || l.umax <= math.MaxInt64) 322 } 323 324 // checkpoint saves the current state of known relations. 325 // Called when descending on a branch. 326 func (ft *factsTable) checkpoint() { 327 ft.stack = append(ft.stack, checkpointFact) 328 ft.limitStack = append(ft.limitStack, checkpointBound) 329 } 330 331 // restore restores known relation to the state just 332 // before the previous checkpoint. 333 // Called when backing up on a branch. 334 func (ft *factsTable) restore() { 335 for { 336 old := ft.stack[len(ft.stack)-1] 337 ft.stack = ft.stack[:len(ft.stack)-1] 338 if old == checkpointFact { 339 break 340 } 341 if old.r == lt|eq|gt { 342 delete(ft.facts, old.p) 343 } else { 344 ft.facts[old.p] = old.r 345 } 346 } 347 for { 348 old := ft.limitStack[len(ft.limitStack)-1] 349 ft.limitStack = ft.limitStack[:len(ft.limitStack)-1] 350 if old.vid == 0 { // checkpointBound 351 break 352 } 353 if old.limit == noLimit { 354 delete(ft.limits, old.vid) 355 } else { 356 ft.limits[old.vid] = old.limit 357 } 358 } 359 } 360 361 func lessByID(v, w *Value) bool { 362 if v == nil && w == nil { 363 // Should not happen, but just in case. 364 return false 365 } 366 if v == nil { 367 return true 368 } 369 return w != nil && v.ID < w.ID 370 } 371 372 var ( 373 reverseBits = [...]relation{0, 4, 2, 6, 1, 5, 3, 7} 374 375 // maps what we learn when the positive branch is taken. 376 // For example: 377 // OpLess8: {signed, lt}, 378 // v1 = (OpLess8 v2 v3). 379 // If v1 branch is taken than we learn that the rangeMaks 380 // can be at most lt. 381 domainRelationTable = map[Op]struct { 382 d domain 383 r relation 384 }{ 385 OpEq8: {signed | unsigned, eq}, 386 OpEq16: {signed | unsigned, eq}, 387 OpEq32: {signed | unsigned, eq}, 388 OpEq64: {signed | unsigned, eq}, 389 OpEqPtr: {pointer, eq}, 390 391 OpNeq8: {signed | unsigned, lt | gt}, 392 OpNeq16: {signed | unsigned, lt | gt}, 393 OpNeq32: {signed | unsigned, lt | gt}, 394 OpNeq64: {signed | unsigned, lt | gt}, 395 OpNeqPtr: {pointer, lt | gt}, 396 397 OpLess8: {signed, lt}, 398 OpLess8U: {unsigned, lt}, 399 OpLess16: {signed, lt}, 400 OpLess16U: {unsigned, lt}, 401 OpLess32: {signed, lt}, 402 OpLess32U: {unsigned, lt}, 403 OpLess64: {signed, lt}, 404 OpLess64U: {unsigned, lt}, 405 406 OpLeq8: {signed, lt | eq}, 407 OpLeq8U: {unsigned, lt | eq}, 408 OpLeq16: {signed, lt | eq}, 409 OpLeq16U: {unsigned, lt | eq}, 410 OpLeq32: {signed, lt | eq}, 411 OpLeq32U: {unsigned, lt | eq}, 412 OpLeq64: {signed, lt | eq}, 413 OpLeq64U: {unsigned, lt | eq}, 414 415 OpGeq8: {signed, eq | gt}, 416 OpGeq8U: {unsigned, eq | gt}, 417 OpGeq16: {signed, eq | gt}, 418 OpGeq16U: {unsigned, eq | gt}, 419 OpGeq32: {signed, eq | gt}, 420 OpGeq32U: {unsigned, eq | gt}, 421 OpGeq64: {signed, eq | gt}, 422 OpGeq64U: {unsigned, eq | gt}, 423 424 OpGreater8: {signed, gt}, 425 OpGreater8U: {unsigned, gt}, 426 OpGreater16: {signed, gt}, 427 OpGreater16U: {unsigned, gt}, 428 OpGreater32: {signed, gt}, 429 OpGreater32U: {unsigned, gt}, 430 OpGreater64: {signed, gt}, 431 OpGreater64U: {unsigned, gt}, 432 433 // TODO: OpIsInBounds actually test 0 <= a < b. This means 434 // that the positive branch learns signed/LT and unsigned/LT 435 // but the negative branch only learns unsigned/GE. 436 OpIsInBounds: {unsigned, lt}, 437 OpIsSliceInBounds: {unsigned, lt | eq}, 438 } 439 ) 440 441 // prove removes redundant BlockIf branches that can be inferred 442 // from previous dominating comparisons. 443 // 444 // By far, the most common redundant pair are generated by bounds checking. 445 // For example for the code: 446 // 447 // a[i] = 4 448 // foo(a[i]) 449 // 450 // The compiler will generate the following code: 451 // 452 // if i >= len(a) { 453 // panic("not in bounds") 454 // } 455 // a[i] = 4 456 // if i >= len(a) { 457 // panic("not in bounds") 458 // } 459 // foo(a[i]) 460 // 461 // The second comparison i >= len(a) is clearly redundant because if the 462 // else branch of the first comparison is executed, we already know that i < len(a). 463 // The code for the second panic can be removed. 464 func prove(f *Func) { 465 ft := newFactsTable() 466 467 // Find length and capacity ops. 468 for _, b := range f.Blocks { 469 for _, v := range b.Values { 470 if v.Uses == 0 { 471 // We don't care about dead values. 472 // (There can be some that are CSEd but not removed yet.) 473 continue 474 } 475 switch v.Op { 476 case OpSliceLen: 477 if ft.lens == nil { 478 ft.lens = map[ID]*Value{} 479 } 480 ft.lens[v.Args[0].ID] = v 481 case OpSliceCap: 482 if ft.caps == nil { 483 ft.caps = map[ID]*Value{} 484 } 485 ft.caps[v.Args[0].ID] = v 486 } 487 } 488 } 489 490 // current node state 491 type walkState int 492 const ( 493 descend walkState = iota 494 simplify 495 ) 496 // work maintains the DFS stack. 497 type bp struct { 498 block *Block // current handled block 499 state walkState // what's to do 500 } 501 work := make([]bp, 0, 256) 502 work = append(work, bp{ 503 block: f.Entry, 504 state: descend, 505 }) 506 507 idom := f.Idom() 508 sdom := f.sdom() 509 510 // DFS on the dominator tree. 511 for len(work) > 0 { 512 node := work[len(work)-1] 513 work = work[:len(work)-1] 514 parent := idom[node.block.ID] 515 branch := getBranch(sdom, parent, node.block) 516 517 switch node.state { 518 case descend: 519 if branch != unknown { 520 ft.checkpoint() 521 c := parent.Control 522 updateRestrictions(parent, ft, boolean, nil, c, lt|gt, branch) 523 if tr, has := domainRelationTable[parent.Control.Op]; has { 524 // When we branched from parent we learned a new set of 525 // restrictions. Update the factsTable accordingly. 526 updateRestrictions(parent, ft, tr.d, c.Args[0], c.Args[1], tr.r, branch) 527 } 528 } 529 530 work = append(work, bp{ 531 block: node.block, 532 state: simplify, 533 }) 534 for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) { 535 work = append(work, bp{ 536 block: s, 537 state: descend, 538 }) 539 } 540 541 case simplify: 542 succ := simplifyBlock(ft, node.block) 543 if succ != unknown { 544 b := node.block 545 b.Kind = BlockFirst 546 b.SetControl(nil) 547 if succ == negative { 548 b.swapSuccessors() 549 } 550 } 551 552 if branch != unknown { 553 ft.restore() 554 } 555 } 556 } 557 } 558 559 // getBranch returns the range restrictions added by p 560 // when reaching b. p is the immediate dominator of b. 561 func getBranch(sdom SparseTree, p *Block, b *Block) branch { 562 if p == nil || p.Kind != BlockIf { 563 return unknown 564 } 565 // If p and p.Succs[0] are dominators it means that every path 566 // from entry to b passes through p and p.Succs[0]. We care that 567 // no path from entry to b passes through p.Succs[1]. If p.Succs[0] 568 // has one predecessor then (apart from the degenerate case), 569 // there is no path from entry that can reach b through p.Succs[1]. 570 // TODO: how about p->yes->b->yes, i.e. a loop in yes. 571 if sdom.isAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 { 572 return positive 573 } 574 if sdom.isAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 { 575 return negative 576 } 577 return unknown 578 } 579 580 // updateRestrictions updates restrictions from the immediate 581 // dominating block (p) using r. r is adjusted according to the branch taken. 582 func updateRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r relation, branch branch) { 583 if t == 0 || branch == unknown { 584 // Trivial case: nothing to do, or branch unknown. 585 // Shoult not happen, but just in case. 586 return 587 } 588 if branch == negative { 589 // Negative branch taken, complement the relations. 590 r = (lt | eq | gt) ^ r 591 } 592 for i := domain(1); i <= t; i <<= 1 { 593 if t&i == 0 { 594 continue 595 } 596 ft.update(parent, v, w, i, r) 597 598 // Additional facts we know given the relationship between len and cap. 599 if i != signed && i != unsigned { 600 continue 601 } 602 if v.Op == OpSliceLen && r< == 0 && ft.caps[v.Args[0].ID] != nil { 603 // len(s) > w implies cap(s) > w 604 // len(s) >= w implies cap(s) >= w 605 // len(s) == w implies cap(s) >= w 606 ft.update(parent, ft.caps[v.Args[0].ID], w, i, r|gt) 607 } 608 if w.Op == OpSliceLen && r> == 0 && ft.caps[w.Args[0].ID] != nil { 609 // same, length on the RHS. 610 ft.update(parent, v, ft.caps[w.Args[0].ID], i, r|lt) 611 } 612 if v.Op == OpSliceCap && r> == 0 && ft.lens[v.Args[0].ID] != nil { 613 // cap(s) < w implies len(s) < w 614 // cap(s) <= w implies len(s) <= w 615 // cap(s) == w implies len(s) <= w 616 ft.update(parent, ft.lens[v.Args[0].ID], w, i, r|lt) 617 } 618 if w.Op == OpSliceCap && r< == 0 && ft.lens[w.Args[0].ID] != nil { 619 // same, capacity on the RHS. 620 ft.update(parent, v, ft.lens[w.Args[0].ID], i, r|gt) 621 } 622 } 623 } 624 625 // simplifyBlock simplifies block known the restrictions in ft. 626 // Returns which branch must always be taken. 627 func simplifyBlock(ft *factsTable, b *Block) branch { 628 for _, v := range b.Values { 629 if v.Op != OpSlicemask { 630 continue 631 } 632 add := v.Args[0] 633 if add.Op != OpAdd64 && add.Op != OpAdd32 { 634 continue 635 } 636 // Note that the arg of slicemask was originally a sub, but 637 // was rewritten to an add by generic.rules (if the thing 638 // being subtracted was a constant). 639 x := add.Args[0] 640 y := add.Args[1] 641 if x.Op == OpConst64 || x.Op == OpConst32 { 642 x, y = y, x 643 } 644 if y.Op != OpConst64 && y.Op != OpConst32 { 645 continue 646 } 647 // slicemask(x + y) 648 // if x is larger than -y (y is negative), then slicemask is -1. 649 lim, ok := ft.limits[x.ID] 650 if !ok { 651 continue 652 } 653 if lim.umin > uint64(-y.AuxInt) { 654 if v.Args[0].Op == OpAdd64 { 655 v.reset(OpConst64) 656 } else { 657 v.reset(OpConst32) 658 } 659 if b.Func.pass.debug > 0 { 660 b.Func.Warnl(v.Pos, "Proved slicemask not needed") 661 } 662 v.AuxInt = -1 663 } 664 } 665 666 if b.Kind != BlockIf { 667 return unknown 668 } 669 670 // First, checks if the condition itself is redundant. 671 m := ft.get(nil, b.Control, boolean) 672 if m == lt|gt { 673 if b.Func.pass.debug > 0 { 674 if b.Func.pass.debug > 1 { 675 b.Func.Warnl(b.Pos, "Proved boolean %s (%s)", b.Control.Op, b.Control) 676 } else { 677 b.Func.Warnl(b.Pos, "Proved boolean %s", b.Control.Op) 678 } 679 } 680 return positive 681 } 682 if m == eq { 683 if b.Func.pass.debug > 0 { 684 if b.Func.pass.debug > 1 { 685 b.Func.Warnl(b.Pos, "Disproved boolean %s (%s)", b.Control.Op, b.Control) 686 } else { 687 b.Func.Warnl(b.Pos, "Disproved boolean %s", b.Control.Op) 688 } 689 } 690 return negative 691 } 692 693 // Next look check equalities. 694 c := b.Control 695 tr, has := domainRelationTable[c.Op] 696 if !has { 697 return unknown 698 } 699 700 a0, a1 := c.Args[0], c.Args[1] 701 for d := domain(1); d <= tr.d; d <<= 1 { 702 if d&tr.d == 0 { 703 continue 704 } 705 706 // tr.r represents in which case the positive branch is taken. 707 // m represents which cases are possible because of previous relations. 708 // If the set of possible relations m is included in the set of relations 709 // need to take the positive branch (or negative) then that branch will 710 // always be taken. 711 // For shortcut, if m == 0 then this block is dead code. 712 m := ft.get(a0, a1, d) 713 if m != 0 && tr.r&m == m { 714 if b.Func.pass.debug > 0 { 715 if b.Func.pass.debug > 1 { 716 b.Func.Warnl(b.Pos, "Proved %s (%s)", c.Op, c) 717 } else { 718 b.Func.Warnl(b.Pos, "Proved %s", c.Op) 719 } 720 } 721 return positive 722 } 723 if m != 0 && ((lt|eq|gt)^tr.r)&m == m { 724 if b.Func.pass.debug > 0 { 725 if b.Func.pass.debug > 1 { 726 b.Func.Warnl(b.Pos, "Disproved %s (%s)", c.Op, c) 727 } else { 728 b.Func.Warnl(b.Pos, "Disproved %s", c.Op) 729 } 730 } 731 return negative 732 } 733 } 734 735 // HACK: If the first argument of IsInBounds or IsSliceInBounds 736 // is a constant and we already know that constant is smaller (or equal) 737 // to the upper bound than this is proven. Most useful in cases such as: 738 // if len(a) <= 1 { return } 739 // do something with a[1] 740 if (c.Op == OpIsInBounds || c.Op == OpIsSliceInBounds) && ft.isNonNegative(c.Args[0]) { 741 m := ft.get(a0, a1, signed) 742 if m != 0 && tr.r&m == m { 743 if b.Func.pass.debug > 0 { 744 if b.Func.pass.debug > 1 { 745 b.Func.Warnl(b.Pos, "Proved non-negative bounds %s (%s)", c.Op, c) 746 } else { 747 b.Func.Warnl(b.Pos, "Proved non-negative bounds %s", c.Op) 748 } 749 } 750 return positive 751 } 752 } 753 754 return unknown 755 } 756 757 // isNonNegative returns true is v is known to be greater or equal to zero. 758 func isNonNegative(v *Value) bool { 759 switch v.Op { 760 case OpConst64: 761 return v.AuxInt >= 0 762 763 case OpConst32: 764 return int32(v.AuxInt) >= 0 765 766 case OpStringLen, OpSliceLen, OpSliceCap, 767 OpZeroExt8to64, OpZeroExt16to64, OpZeroExt32to64: 768 return true 769 770 case OpRsh64x64: 771 return isNonNegative(v.Args[0]) 772 } 773 return false 774 }