Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
var opMin = map[Op]int64{
1// Copyright 2016 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package ssa67import (8 "cmd/compile/internal/types"9 "cmd/internal/src"10 "cmp"11 "fmt"12 "math"13 "math/bits"14 "slices"15 "strings"16)1718type branch int1920const (21 unknown branch = iota22 positive23 negative24 // The outedges from a jump table are jumpTable0,25 // jumpTable0+1, jumpTable0+2, etc. There could be an26 // arbitrary number so we can't list them all here.27 jumpTable028)2930func (b branch) String() string {31 switch b {32 case unknown:33 return "unk"34 case positive:35 return "pos"36 case negative:37 return "neg"38 default:39 return fmt.Sprintf("jmp%d", b-jumpTable0)40 }41}4243// relation represents the set of possible relations between44// pairs of variables (v, w). Without a priori knowledge the45// mask is lt | eq | gt meaning v can be less than, equal to or46// greater than w. When the execution path branches on the condition47// `v op w` the set of relations is updated to exclude any48// relation not possible due to `v op w` being true (or false).49//50// E.g.51//52// r := relation(...)53//54// if v < w {55// newR := r & lt56// }57// if v >= w {58// newR := r & (eq|gt)59// }60// if v != w {61// newR := r & (lt|gt)62// }63type relation uint6465const (66 lt relation = 1 << iota67 eq68 gt69)7071var relationStrings = [...]string{72 0: "none", lt: "<", eq: "==", lt | eq: "<=",73 gt: ">", gt | lt: "!=", gt | eq: ">=", gt | eq | lt: "any",74}7576func (r relation) String() string {77 if r < relation(len(relationStrings)) {78 return relationStrings[r]79 }80 return fmt.Sprintf("relation(%d)", uint(r))81}8283// domain represents the domain of a variable pair in which a set84// of relations is known. For example, relations learned for unsigned85// pairs cannot be transferred to signed pairs because the same bit86// representation can mean something else.87type domain uint8889const (90 signed domain = 1 << iota91 unsigned92 pointer93 boolean94)9596var domainStrings = [...]string{97 "signed", "unsigned", "pointer", "boolean",98}99100func (d domain) String() string {101 s := ""102 for i, ds := range domainStrings {103 if d&(1<<uint(i)) != 0 {104 if len(s) != 0 {105 s += "|"106 }107 s += ds108 d &^= 1 << uint(i)109 }110 }111 if d != 0 {112 if len(s) != 0 {113 s += "|"114 }115 s += fmt.Sprintf("0x%x", uint(d))116 }117 return s118}119120// a limit records known upper and lower bounds for a value.121//122// If we have min>max or umin>umax, then this limit is123// called "unsatisfiable". When we encounter such a limit, we124// know that any code for which that limit applies is unreachable.125// We don't particularly care how unsatisfiable limits propagate,126// including becoming satisfiable, because any optimization127// decisions based on those limits only apply to unreachable code.128type limit struct {129 min, max int64 // min <= value <= max, signed130 umin, umax uint64 // umin <= value <= umax, unsigned131 // For booleans, we use 0==false, 1==true for both ranges132 // For pointers, we use 0,0,0,0 for nil and minInt64,maxInt64,1,maxUint64 for nonnil133}134135func (l limit) String() string {136 return fmt.Sprintf("sm,SM=%d,%d um,UM=%d,%d", l.min, l.max, l.umin, l.umax)137}138139func (l limit) intersect(l2 limit) limit {140 l.min = max(l.min, l2.min)141 l.umin = max(l.umin, l2.umin)142 l.max = min(l.max, l2.max)143 l.umax = min(l.umax, l2.umax)144 return l145}146147func (l limit) signedMin(m int64) limit {148 l.min = max(l.min, m)149 return l150}151152func (l limit) signedMinMax(minimum, maximum int64) limit {153 l.min = max(l.min, minimum)154 l.max = min(l.max, maximum)155 return l156}157158func (l limit) unsignedMin(m uint64) limit {159 l.umin = max(l.umin, m)160 return l161}162func (l limit) unsignedMax(m uint64) limit {163 l.umax = min(l.umax, m)164 return l165}166func (l limit) unsignedMinMax(minimum, maximum uint64) limit {167 l.umin = max(l.umin, minimum)168 l.umax = min(l.umax, maximum)169 return l170}171172func (l limit) nonzero() bool {173 return l.min > 0 || l.umin > 0 || l.max < 0174}175func (l limit) maybeZero() bool {176 return !l.nonzero()177}178func (l limit) nonnegative() bool {179 return l.min >= 0180}181func (l limit) unsat() bool {182 return l.min > l.max || l.umin > l.umax183}184185// If x and y can add without overflow or underflow186// (using b bits), safeAdd returns x+y, true.187// Otherwise, returns 0, false.188func safeAdd(x, y int64, b uint) (int64, bool) {189 s := x + y190 if x >= 0 && y >= 0 && s < 0 {191 return 0, false // 64-bit overflow192 }193 if x < 0 && y < 0 && s >= 0 {194 return 0, false // 64-bit underflow195 }196 if !fitsInBits(s, b) {197 return 0, false198 }199 return s, true200}201202// same as safeAdd for unsigned arithmetic.203func safeAddU(x, y uint64, b uint) (uint64, bool) {204 s := x + y205 if s < x || s < y {206 return 0, false // 64-bit overflow207 }208 if !fitsInBitsU(s, b) {209 return 0, false210 }211 return s, true212}213214// same as safeAdd but for subtraction.215func safeSub(x, y int64, b uint) (int64, bool) {216 if y == math.MinInt64 {217 if x == math.MaxInt64 {218 return 0, false // 64-bit overflow219 }220 x++221 y++222 }223 return safeAdd(x, -y, b)224}225226// same as safeAddU but for subtraction.227func safeSubU(x, y uint64, b uint) (uint64, bool) {228 if x < y {229 return 0, false // 64-bit underflow230 }231 s := x - y232 if !fitsInBitsU(s, b) {233 return 0, false234 }235 return s, true236}237238// fitsInBits reports whether x fits in b bits (signed).239func fitsInBits(x int64, b uint) bool {240 if b == 64 {241 return true242 }243 m := int64(-1) << (b - 1)244 M := -m - 1245 return x >= m && x <= M246}247248// fitsInBitsU reports whether x fits in b bits (unsigned).249func fitsInBitsU(x uint64, b uint) bool {250 return x>>b == 0251}252253func noLimit() limit {254 return noLimitForBitsize(64)255}256257func noLimitForBitsize(bitsize uint) limit {258 return limit{min: -(1 << (bitsize - 1)), max: 1<<(bitsize-1) - 1, umin: 0, umax: 1<<bitsize - 1}259}260261func convertIntWithBitsize[Target uint64 | int64, Source uint64 | int64](x Source, bitsize uint) Target {262 switch bitsize {263 case 64:264 return Target(x)265 case 32:266 return Target(int32(x))267 case 16:268 return Target(int16(x))269 case 8:270 return Target(int8(x))271 default:272 panic("unreachable")273 }274}275276// unsignedFixedLeadingBits extracts the all the most significant fixed bits from the limit.277// fixed and count are an other way to represent a limit, you can convert them to a limit as follows:278//279// umin = fixed280// umax = fixed | (1<<(64-count) - 1)281//282// In order to be useful for bitmanip analysis fixed and count are a coarser tool than a limit:283// 1. the varying section (umax-umin) is always one less than a power of two284// 2. that section is naturally aligned inside the 64-bit space285func (l limit) unsignedFixedLeadingBits() (fixed uint64, count uint) {286 varying := uint(bits.Len64(l.umin ^ l.umax))287 count = uint(bits.LeadingZeros64(l.umin ^ l.umax))288 fixed = l.umin &^ (1<<varying - 1)289 return290}291292// add returns the limit obtained by adding a value with limit l293// to a value with limit l2. The result must fit in b bits.294func (l limit) add(l2 limit, b uint) limit {295 var isLConst, isL2Const bool296 var lConst, l2Const uint64297 if l.min == l.max {298 isLConst = true299 lConst = convertIntWithBitsize[uint64](l.min, b)300 } else if l.umin == l.umax {301 isLConst = true302 lConst = l.umin303 }304 if l2.min == l2.max {305 isL2Const = true306 l2Const = convertIntWithBitsize[uint64](l2.min, b)307 } else if l2.umin == l2.umax {308 isL2Const = true309 l2Const = l2.umin310 }311 if isLConst && isL2Const {312 r := lConst + l2Const313 r &= (uint64(1) << b) - 1314 int64r := convertIntWithBitsize[int64](r, b)315 return limit{min: int64r, max: int64r, umin: r, umax: r}316 }317318 r := noLimit()319 min, minOk := safeAdd(l.min, l2.min, b)320 max, maxOk := safeAdd(l.max, l2.max, b)321 if minOk && maxOk {322 r.min = min323 r.max = max324 }325 umin, uminOk := safeAddU(l.umin, l2.umin, b)326 umax, umaxOk := safeAddU(l.umax, l2.umax, b)327 if uminOk && umaxOk {328 r.umin = umin329 r.umax = umax330 }331 return r332}333334// same as add but for subtraction.335func (l limit) sub(l2 limit, b uint) limit {336 r := noLimit()337 min, minOk := safeSub(l.min, l2.max, b)338 max, maxOk := safeSub(l.max, l2.min, b)339 if minOk && maxOk {340 r.min = min341 r.max = max342 }343 umin, uminOk := safeSubU(l.umin, l2.umax, b)344 umax, umaxOk := safeSubU(l.umax, l2.umin, b)345 if uminOk && umaxOk {346 r.umin = umin347 r.umax = umax348 }349 return r350}351352// same as add but for multiplication.353func (l limit) mul(l2 limit, b uint) limit {354 r := noLimit()355 umaxhi, umaxlo := bits.Mul64(l.umax, l2.umax)356 if umaxhi == 0 && fitsInBitsU(umaxlo, b) {357 r.umax = umaxlo358 r.umin = l.umin * l2.umin359 // Note: if the code containing this multiply is360 // unreachable, then we may have umin>umax, and this361 // multiply may overflow. But that's ok for362 // unreachable code. If this code is reachable, we363 // know umin<=umax, so this multiply will not overflow364 // because the max multiply didn't.365 }366 // Signed is harder, so don't bother. The only useful367 // case is when we know both multiplicands are nonnegative,368 // but that case is handled above because we would have then369 // previously propagated signed info to the unsigned domain,370 // and will propagate it back after the multiply.371 return r372}373374// Similar to add, but compute 1 << l if it fits without overflow in b bits.375func (l limit) exp2(b uint) limit {376 r := noLimit()377 if l.umax < uint64(b) {378 r.umin = 1 << l.umin379 r.umax = 1 << l.umax380 // Same as above in mul, signed<->unsigned propagation381 // will handle the signed case for us.382 }383 return r384}385386// Similar to add, but computes the complement of the limit for bitsize b.387func (l limit) com(b uint) limit {388 switch b {389 case 64:390 return limit{391 min: ^l.max,392 max: ^l.min,393 umin: ^l.umax,394 umax: ^l.umin,395 }396 case 32:397 return limit{398 min: int64(^int32(l.max)),399 max: int64(^int32(l.min)),400 umin: uint64(^uint32(l.umax)),401 umax: uint64(^uint32(l.umin)),402 }403 case 16:404 return limit{405 min: int64(^int16(l.max)),406 max: int64(^int16(l.min)),407 umin: uint64(^uint16(l.umax)),408 umax: uint64(^uint16(l.umin)),409 }410 case 8:411 return limit{412 min: int64(^int8(l.max)),413 max: int64(^int8(l.min)),414 umin: uint64(^uint8(l.umax)),415 umax: uint64(^uint8(l.umin)),416 }417 default:418 panic("unreachable")419 }420}421422// Similar to add, but computes the negation of the limit for bitsize b.423func (l limit) neg(b uint) limit {424 return l.com(b).add(limit{min: 1, max: 1, umin: 1, umax: 1}, b)425}426427// Similar to add, but computes the TrailingZeros of the limit for bitsize b.428func (l limit) ctz(b uint) limit {429 fixed, fixedCount := l.unsignedFixedLeadingBits()430 if fixedCount == 64 {431 constResult := min(uint(bits.TrailingZeros64(fixed)), b)432 return limit{min: int64(constResult), max: int64(constResult), umin: uint64(constResult), umax: uint64(constResult)}433 }434435 varying := 64 - fixedCount436 if l.umin&((1<<varying)-1) != 0 {437 // there will always be at least one non-zero bit in the varying part438 varying--439 return noLimit().unsignedMax(uint64(varying))440 }441 return noLimit().unsignedMax(uint64(min(uint(bits.TrailingZeros64(fixed)), b)))442}443444// Similar to add, but computes the Len of the limit for bitsize b.445func (l limit) bitlen(b uint) limit {446 return noLimit().unsignedMinMax(447 uint64(bits.Len64(l.umin)),448 uint64(bits.Len64(l.umax)),449 )450}451452// Similar to add, but computes the PopCount of the limit for bitsize b.453func (l limit) popcount(b uint) limit {454 fixed, fixedCount := l.unsignedFixedLeadingBits()455 varying := 64 - fixedCount456 fixedContribution := uint64(bits.OnesCount64(fixed))457458 min := fixedContribution459 max := fixedContribution + uint64(varying)460461 varyingMask := uint64(1)<<varying - 1462463 if varyingPartOfUmax := l.umax & varyingMask; uint(bits.OnesCount64(varyingPartOfUmax)) != varying {464 // there is at least one zero bit in the varying part465 max--466 }467 if varyingPartOfUmin := l.umin & varyingMask; varyingPartOfUmin != 0 {468 // there is at least one non-zero bit in the varying part469 min++470 }471472 return noLimit().unsignedMinMax(min, max)473}474475func (l limit) constValue() (_ int64, ok bool) {476 switch {477 case l.min == l.max:478 return l.min, true479 case l.umin == l.umax:480 return int64(l.umin), true481 default:482 return 0, false483 }484}485486// a limitFact is a limit known for a particular value.487type limitFact struct {488 vid ID489 limit limit490}491492// An ordering encodes facts like v < w.493type ordering struct {494 next *ordering // linked list of all known orderings for v.495 // Note: v is implicit here, determined by which linked list it is in.496 w *Value497 d domain498 r relation // one of ==,!=,<,<=,>,>=499 // if d is boolean or pointer, r can only be ==, !=500}501502// factsTable keeps track of relations between pairs of values.503//504// The fact table logic is sound, but incomplete. Outside of a few505// special cases, it performs no deduction or arithmetic. While there506// are known decision procedures for this, the ad hoc approach taken507// by the facts table is effective for real code while remaining very508// efficient.509type factsTable struct {510 // unsat is true if facts contains a contradiction.511 //512 // Note that the factsTable logic is incomplete, so if unsat513 // is false, the assertions in factsTable could be satisfiable514 // *or* unsatisfiable.515 unsat bool // true if facts contains a contradiction516 unsatDepth int // number of unsat checkpoints517518 // order* is a couple of partial order sets that record information519 // about relations between SSA values in the signed and unsigned520 // domain.521 orderS *poset522 orderU *poset523524 // orderings contains a list of known orderings between values.525 // These lists are indexed by v.ID.526 // We do not record transitive orderings. Only explicitly learned527 // orderings are recorded. Transitive orderings can be obtained528 // by walking along the individual orderings.529 orderings map[ID]*ordering530 // stack of IDs which have had an entry added in orderings.531 // In addition, ID==0 are checkpoint markers.532 orderingsStack []ID533 orderingCache *ordering // unused ordering records534535 // known lower and upper constant bounds on individual values.536 limits []limit // indexed by value ID537 limitStack []limitFact // previous entries538 recurseCheck []bool // recursion detector for limit propagation539540 // For each slice s, a map from s to a len(s)/cap(s) value (if any)541 // TODO: check if there are cases that matter where we have542 // more than one len(s) for a slice. We could keep a list if necessary.543 lens map[ID]*Value544 caps map[ID]*Value545546 // reusedTopoSortScoresTable recycle allocations for topo-sort547 reusedTopoSortScoresTable []uint548}549550// checkpointBound is an invalid value used for checkpointing551// and restoring factsTable.552var checkpointBound = limitFact{}553554func newFactsTable(f *Func) *factsTable {555 ft := &factsTable{}556 ft.orderS = f.newPoset()557 ft.orderU = f.newPoset()558 ft.orderS.SetUnsigned(false)559 ft.orderU.SetUnsigned(true)560 ft.orderings = make(map[ID]*ordering)561 ft.limits = f.Cache.allocLimitSlice(f.NumValues())562 for _, b := range f.Blocks {563 for _, v := range b.Values {564 ft.limits[v.ID] = initLimit(v)565 }566 }567 ft.limitStack = make([]limitFact, 4)568 ft.recurseCheck = f.Cache.allocBoolSlice(f.NumValues())569 return ft570}571572// initLimitForNewValue initializes the limits for newly created values,573// possibly needing to expand the limits slice. Currently used by574// simplifyBlock when certain provably constant results are folded.575func (ft *factsTable) initLimitForNewValue(v *Value) {576 if int(v.ID) >= len(ft.limits) {577 f := v.Block.Func578 n := f.NumValues()579 if cap(ft.limits) >= n {580 ft.limits = ft.limits[:n]581 } else {582 old := ft.limits583 ft.limits = f.Cache.allocLimitSlice(n)584 copy(ft.limits, old)585 f.Cache.freeLimitSlice(old)586 }587 }588 ft.limits[v.ID] = initLimit(v)589}590591// signedMin records the fact that we know v is at least592// min in the signed domain.593func (ft *factsTable) signedMin(v *Value, min int64) {594 ft.newLimit(v, limit{min: min, max: math.MaxInt64, umin: 0, umax: math.MaxUint64})595}596597// signedMax records the fact that we know v is at most598// max in the signed domain.599func (ft *factsTable) signedMax(v *Value, max int64) {600 ft.newLimit(v, limit{min: math.MinInt64, max: max, umin: 0, umax: math.MaxUint64})601}602func (ft *factsTable) signedMinMax(v *Value, min, max int64) {603 ft.newLimit(v, limit{min: min, max: max, umin: 0, umax: math.MaxUint64})604}605606// setNonNegative records the fact that v is known to be non-negative.607func (ft *factsTable) setNonNegative(v *Value) {608 ft.signedMin(v, 0)609}610611// unsignedMin records the fact that we know v is at least612// min in the unsigned domain.613func (ft *factsTable) unsignedMin(v *Value, min uint64) {614 ft.newLimit(v, limit{min: math.MinInt64, max: math.MaxInt64, umin: min, umax: math.MaxUint64})615}616617// unsignedMax records the fact that we know v is at most618// max in the unsigned domain.619func (ft *factsTable) unsignedMax(v *Value, max uint64) {620 ft.newLimit(v, limit{min: math.MinInt64, max: math.MaxInt64, umin: 0, umax: max})621}622func (ft *factsTable) unsignedMinMax(v *Value, min, max uint64) {623 ft.newLimit(v, limit{min: math.MinInt64, max: math.MaxInt64, umin: min, umax: max})624}625626func (ft *factsTable) booleanFalse(v *Value) {627 ft.newLimit(v, limit{min: 0, max: 0, umin: 0, umax: 0})628}629func (ft *factsTable) booleanTrue(v *Value) {630 ft.newLimit(v, limit{min: 1, max: 1, umin: 1, umax: 1})631}632func (ft *factsTable) pointerNil(v *Value) {633 ft.newLimit(v, limit{min: 0, max: 0, umin: 0, umax: 0})634}635func (ft *factsTable) pointerNonNil(v *Value) {636 l := noLimit()637 l.umin = 1638 ft.newLimit(v, l)639}640641// newLimit adds new limiting information for v.642func (ft *factsTable) newLimit(v *Value, newLim limit) {643 oldLim := ft.limits[v.ID]644645 // Merge old and new information.646 lim := oldLim.intersect(newLim)647648 // signed <-> unsigned propagation649 if lim.min >= 0 {650 lim = lim.unsignedMinMax(uint64(lim.min), uint64(lim.max))651 }652 if fitsInBitsU(lim.umax, uint(8*v.Type.Size()-1)) {653 lim = lim.signedMinMax(int64(lim.umin), int64(lim.umax))654 }655656 if lim == oldLim {657 return // nothing new to record658 }659660 if lim.unsat() {661 ft.unsat = true662 return663 }664665 // Check for recursion. This normally happens because in unsatisfiable666 // cases we have a < b < a, and every update to a's limits returns667 // here again with the limit increased by 2.668 // Normally this is caught early by the orderS/orderU posets, but in669 // cases where the comparisons jump between signed and unsigned domains,670 // the posets will not notice.671 if ft.recurseCheck[v.ID] {672 // This should only happen for unsatisfiable cases. TODO: check673 return674 }675 ft.recurseCheck[v.ID] = true676 defer func() {677 ft.recurseCheck[v.ID] = false678 }()679680 // Record undo information.681 ft.limitStack = append(ft.limitStack, limitFact{v.ID, oldLim})682 // Record new information.683 ft.limits[v.ID] = lim684 if v.Block.Func.pass.debug > 2 {685 // TODO: pos is probably wrong. This is the position where v is defined,686 // not the position where we learned the fact about it (which was687 // probably some subsequent compare+branch).688 v.Block.Func.Warnl(v.Pos, "new limit %s %s unsat=%v", v, lim.String(), ft.unsat)689 }690691 // Propagate this new constant range to other values692 // that we know are ordered with respect to this one.693 // Note overflow/underflow in the arithmetic below is ok,694 // it will just lead to imprecision (undetected unsatisfiability).695 for o := ft.orderings[v.ID]; o != nil; o = o.next {696 switch o.d {697 case signed:698 switch o.r {699 case eq: // v == w700 ft.signedMinMax(o.w, lim.min, lim.max)701 case lt | eq: // v <= w702 ft.signedMin(o.w, lim.min)703 case lt: // v < w704 ft.signedMin(o.w, lim.min+1)705 case gt | eq: // v >= w706 ft.signedMax(o.w, lim.max)707 case gt: // v > w708 ft.signedMax(o.w, lim.max-1)709 case lt | gt: // v != w710 if lim.min == lim.max { // v is a constant711 c := lim.min712 if ft.limits[o.w.ID].min == c {713 ft.signedMin(o.w, c+1)714 }715 if ft.limits[o.w.ID].max == c {716 ft.signedMax(o.w, c-1)717 }718 }719 }720 case unsigned:721 switch o.r {722 case eq: // v == w723 ft.unsignedMinMax(o.w, lim.umin, lim.umax)724 case lt | eq: // v <= w725 ft.unsignedMin(o.w, lim.umin)726 case lt: // v < w727 ft.unsignedMin(o.w, lim.umin+1)728 case gt | eq: // v >= w729 ft.unsignedMax(o.w, lim.umax)730 case gt: // v > w731 ft.unsignedMax(o.w, lim.umax-1)732 case lt | gt: // v != w733 if lim.umin == lim.umax { // v is a constant734 c := lim.umin735 if ft.limits[o.w.ID].umin == c {736 ft.unsignedMin(o.w, c+1)737 }738 if ft.limits[o.w.ID].umax == c {739 ft.unsignedMax(o.w, c-1)740 }741 }742 }743 case boolean:744 switch o.r {745 case eq:746 if lim.min == 0 && lim.max == 0 { // constant false747 ft.booleanFalse(o.w)748 }749 if lim.min == 1 && lim.max == 1 { // constant true750 ft.booleanTrue(o.w)751 }752 case lt | gt:753 if lim.min == 0 && lim.max == 0 { // constant false754 ft.booleanTrue(o.w)755 }756 if lim.min == 1 && lim.max == 1 { // constant true757 ft.booleanFalse(o.w)758 }759 }760 case pointer:761 switch o.r {762 case eq:763 if lim.umax == 0 { // nil764 ft.pointerNil(o.w)765 }766 if lim.umin > 0 { // non-nil767 ft.pointerNonNil(o.w)768 }769 case lt | gt:770 if lim.umax == 0 { // nil771 ft.pointerNonNil(o.w)772 }773 // note: not equal to non-nil doesn't tell us anything.774 }775 }776 }777778 // If this is new known constant for a boolean value,779 // extract relation between its args. For example, if780 // We learn v is false, and v is defined as a<b, then we learn a>=b.781 if v.Type.IsBoolean() {782 // If we reach here, it is because we have a more restrictive783 // value for v than the default. The only two such values784 // are constant true or constant false.785 if lim.min != lim.max {786 v.Block.Func.Fatalf("boolean not constant %v", v)787 }788 isTrue := lim.min == 1789 if dr, ok := domainRelationTable[v.Op]; ok && v.Op != OpIsInBounds && v.Op != OpIsSliceInBounds {790 d := dr.d791 r := dr.r792 if d == signed && ft.isNonNegative(v.Args[0]) && ft.isNonNegative(v.Args[1]) {793 d |= unsigned794 }795 if !isTrue {796 r ^= lt | gt | eq797 }798 // TODO: v.Block is wrong?799 addRestrictions(v.Block, ft, d, v.Args[0], v.Args[1], r)800 }801 switch v.Op {802 case OpIsNonNil:803 if isTrue {804 ft.pointerNonNil(v.Args[0])805 } else {806 ft.pointerNil(v.Args[0])807 }808 case OpIsInBounds, OpIsSliceInBounds:809 // 0 <= a0 < a1 (or 0 <= a0 <= a1)810 r := lt811 if v.Op == OpIsSliceInBounds {812 r |= eq813 }814 if isTrue {815 // On the positive branch, we learn:816 // signed: 0 <= a0 < a1 (or 0 <= a0 <= a1)817 // unsigned: a0 < a1 (or a0 <= a1)818 ft.setNonNegative(v.Args[0])819 ft.update(v.Block, v.Args[0], v.Args[1], signed, r)820 ft.update(v.Block, v.Args[0], v.Args[1], unsigned, r)821 } else {822 // On the negative branch, we learn (0 > a0 ||823 // a0 >= a1). In the unsigned domain, this is824 // simply a0 >= a1 (which is the reverse of the825 // positive branch, so nothing surprising).826 // But in the signed domain, we can't express the ||827 // condition, so check if a0 is non-negative instead,828 // to be able to learn something.829 r ^= lt | gt | eq // >= (index) or > (slice)830 if ft.isNonNegative(v.Args[0]) {831 ft.update(v.Block, v.Args[0], v.Args[1], signed, r)832 }833 ft.update(v.Block, v.Args[0], v.Args[1], unsigned, r)834 // TODO: v.Block is wrong here835 }836 }837 }838}839840func (ft *factsTable) addOrdering(v, w *Value, d domain, r relation) {841 o := ft.orderingCache842 if o == nil {843 o = &ordering{}844 } else {845 ft.orderingCache = o.next846 }847 o.w = w848 o.d = d849 o.r = r850 o.next = ft.orderings[v.ID]851 ft.orderings[v.ID] = o852 ft.orderingsStack = append(ft.orderingsStack, v.ID)853}854855// update updates the set of relations between v and w in domain d856// restricting it to r.857func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) {858 if parent.Func.pass.debug > 2 {859 parent.Func.Warnl(parent.Pos, "parent=%s, update %s %s %s", parent, v, w, r)860 }861 // No need to do anything else if we already found unsat.862 if ft.unsat {863 return864 }865866 // Self-fact. It's wasteful to register it into the facts867 // table, so just note whether it's satisfiable868 if v == w {869 if r&eq == 0 {870 ft.unsat = true871 }872 return873 }874875 if d == signed || d == unsigned {876 var ok bool877 order := ft.orderS878 if d == unsigned {879 order = ft.orderU880 }881 switch r {882 case lt:883 ok = order.SetOrder(v, w)884 case gt:885 ok = order.SetOrder(w, v)886 case lt | eq:887 ok = order.SetOrderOrEqual(v, w)888 case gt | eq:889 ok = order.SetOrderOrEqual(w, v)890 case eq:891 ok = order.SetEqual(v, w)892 case lt | gt:893 ok = order.SetNonEqual(v, w)894 default:895 panic("unknown relation")896 }897 ft.addOrdering(v, w, d, r)898 ft.addOrdering(w, v, d, reverseBits[r])899900 if !ok {901 if parent.Func.pass.debug > 2 {902 parent.Func.Warnl(parent.Pos, "unsat %s %s %s", v, w, r)903 }904 ft.unsat = true905 return906 }907 }908 if d == boolean || d == pointer {909 for o := ft.orderings[v.ID]; o != nil; o = o.next {910 if o.d == d && o.w == w {911 // We already know a relationship between v and w.912 // Either it is a duplicate, or it is a contradiction,913 // as we only allow eq and lt|gt for these domains,914 if o.r != r {915 ft.unsat = true916 }917 return918 }919 }920 // TODO: this does not do transitive equality.921 // We could use a poset like above, but somewhat degenerate (==,!= only).922 ft.addOrdering(v, w, d, r)923 ft.addOrdering(w, v, d, r) // note: reverseBits unnecessary for eq and lt|gt.924 }925926 // Extract new constant limits based on the comparison.927 vLimit := ft.limits[v.ID]928 wLimit := ft.limits[w.ID]929 // Note: all the +1/-1 below could overflow/underflow. Either will930 // still generate correct results, it will just lead to imprecision.931 // In fact if there is overflow/underflow, the corresponding932 // code is unreachable because the known range is outside the range933 // of the value's type.934 switch d {935 case signed:936 switch r {937 case eq: // v == w938 ft.signedMinMax(v, wLimit.min, wLimit.max)939 ft.signedMinMax(w, vLimit.min, vLimit.max)940 case lt: // v < w941 ft.signedMax(v, wLimit.max-1)942 ft.signedMin(w, vLimit.min+1)943 case lt | eq: // v <= w944 ft.signedMax(v, wLimit.max)945 ft.signedMin(w, vLimit.min)946 case gt: // v > w947 ft.signedMin(v, wLimit.min+1)948 ft.signedMax(w, vLimit.max-1)949 case gt | eq: // v >= w950 ft.signedMin(v, wLimit.min)951 ft.signedMax(w, vLimit.max)952 case lt | gt: // v != w953 if vLimit.min == vLimit.max { // v is a constant954 c := vLimit.min955 if wLimit.min == c {956 ft.signedMin(w, c+1)957 }958 if wLimit.max == c {959 ft.signedMax(w, c-1)960 }961 }962 if wLimit.min == wLimit.max { // w is a constant963 c := wLimit.min964 if vLimit.min == c {965 ft.signedMin(v, c+1)966 }967 if vLimit.max == c {968 ft.signedMax(v, c-1)969 }970 }971 }972 case unsigned:973 switch r {974 case eq: // v == w975 ft.unsignedMinMax(v, wLimit.umin, wLimit.umax)976 ft.unsignedMinMax(w, vLimit.umin, vLimit.umax)977 case lt: // v < w978 ft.unsignedMax(v, wLimit.umax-1)979 ft.unsignedMin(w, vLimit.umin+1)980 case lt | eq: // v <= w981 ft.unsignedMax(v, wLimit.umax)982 ft.unsignedMin(w, vLimit.umin)983 case gt: // v > w984 ft.unsignedMin(v, wLimit.umin+1)985 ft.unsignedMax(w, vLimit.umax-1)986 case gt | eq: // v >= w987 ft.unsignedMin(v, wLimit.umin)988 ft.unsignedMax(w, vLimit.umax)989 case lt | gt: // v != w990 if vLimit.umin == vLimit.umax { // v is a constant991 c := vLimit.umin992 if wLimit.umin == c {993 ft.unsignedMin(w, c+1)994 }995 if wLimit.umax == c {996 ft.unsignedMax(w, c-1)997 }998 }999 if wLimit.umin == wLimit.umax { // w is a constant1000 c := wLimit.umin1001 if vLimit.umin == c {1002 ft.unsignedMin(v, c+1)1003 }1004 if vLimit.umax == c {1005 ft.unsignedMax(v, c-1)1006 }1007 }1008 }1009 case boolean:1010 switch r {1011 case eq: // v == w1012 if vLimit.min == 1 { // v is true1013 ft.booleanTrue(w)1014 }1015 if vLimit.max == 0 { // v is false1016 ft.booleanFalse(w)1017 }1018 if wLimit.min == 1 { // w is true1019 ft.booleanTrue(v)1020 }1021 if wLimit.max == 0 { // w is false1022 ft.booleanFalse(v)1023 }1024 case lt | gt: // v != w1025 if vLimit.min == 1 { // v is true1026 ft.booleanFalse(w)1027 }1028 if vLimit.max == 0 { // v is false1029 ft.booleanTrue(w)1030 }1031 if wLimit.min == 1 { // w is true1032 ft.booleanFalse(v)1033 }1034 if wLimit.max == 0 { // w is false1035 ft.booleanTrue(v)1036 }1037 }1038 case pointer:1039 switch r {1040 case eq: // v == w1041 if vLimit.umax == 0 { // v is nil1042 ft.pointerNil(w)1043 }1044 if vLimit.umin > 0 { // v is non-nil1045 ft.pointerNonNil(w)1046 }1047 if wLimit.umax == 0 { // w is nil1048 ft.pointerNil(v)1049 }1050 if wLimit.umin > 0 { // w is non-nil1051 ft.pointerNonNil(v)1052 }1053 case lt | gt: // v != w1054 if vLimit.umax == 0 { // v is nil1055 ft.pointerNonNil(w)1056 }1057 if wLimit.umax == 0 { // w is nil1058 ft.pointerNonNil(v)1059 }1060 // Note: the other direction doesn't work.1061 // Being not equal to a non-nil pointer doesn't1062 // make you (necessarily) a nil pointer.1063 }1064 }10651066 // Derived facts below here are only about numbers.1067 if d != signed && d != unsigned {1068 return1069 }10701071 // Additional facts we know given the relationship between len and cap.1072 //1073 // TODO: Since prove now derives transitive relations, it1074 // should be sufficient to learn that len(w) <= cap(w) at the1075 // beginning of prove where we look for all len/cap ops.1076 if v.Op == OpSliceLen && r< == 0 && ft.caps[v.Args[0].ID] != nil {1077 // len(s) > w implies cap(s) > w1078 // len(s) >= w implies cap(s) >= w1079 // len(s) == w implies cap(s) >= w1080 ft.update(parent, ft.caps[v.Args[0].ID], w, d, r|gt)1081 }1082 if w.Op == OpSliceLen && r> == 0 && ft.caps[w.Args[0].ID] != nil {1083 // same, length on the RHS.1084 ft.update(parent, v, ft.caps[w.Args[0].ID], d, r|lt)1085 }1086 if v.Op == OpSliceCap && r> == 0 && ft.lens[v.Args[0].ID] != nil {1087 // cap(s) < w implies len(s) < w1088 // cap(s) <= w implies len(s) <= w1089 // cap(s) == w implies len(s) <= w1090 ft.update(parent, ft.lens[v.Args[0].ID], w, d, r|lt)1091 }1092 if w.Op == OpSliceCap && r< == 0 && ft.lens[w.Args[0].ID] != nil {1093 // same, capacity on the RHS.1094 ft.update(parent, v, ft.lens[w.Args[0].ID], d, r|gt)1095 }10961097 // Process fence-post implications.1098 //1099 // First, make the condition > or >=.1100 if r == lt || r == lt|eq {1101 v, w = w, v1102 r = reverseBits[r]1103 }1104 switch r {1105 case gt:1106 if x, delta := isConstDelta(v); x != nil && delta == 1 {1107 // x+1 > w ⇒ x >= w1108 //1109 // This is useful for eliminating the1110 // growslice branch of append.1111 ft.update(parent, x, w, d, gt|eq)1112 } else if x, delta := isConstDelta(w); x != nil && delta == -1 {1113 // v > x-1 ⇒ v >= x1114 ft.update(parent, v, x, d, gt|eq)1115 }1116 case gt | eq:1117 if x, delta := isConstDelta(v); x != nil && delta == -1 {1118 // x-1 >= w && x > min ⇒ x > w1119 //1120 // Useful for i > 0; s[i-1].1121 lim := ft.limits[x.ID]1122 if (d == signed && lim.min > opMin[v.Op]) || (d == unsigned && lim.umin > 0) {1123 ft.update(parent, x, w, d, gt)1124 }1125 } else if x, delta := isConstDelta(w); x != nil && delta == 1 {1126 // v >= x+1 && x < max ⇒ v > x1127 lim := ft.limits[x.ID]1128 if (d == signed && lim.max < opMax[w.Op]) || (d == unsigned && lim.umax < opUMax[w.Op]) {1129 ft.update(parent, v, x, d, gt)1130 }1131 }1132 }11331134 // Process: x+delta > w (with delta constant)1135 // Only signed domain for now (useful for accesses to slices in loops).1136 if r == gt || r == gt|eq {1137 if x, delta := isConstDelta(v); x != nil && d == signed {1138 if parent.Func.pass.debug > 1 {1139 parent.Func.Warnl(parent.Pos, "x+d %s w; x:%v %v delta:%v w:%v d:%v", r, x, parent.String(), delta, w.AuxInt, d)1140 }1141 underflow := true1142 if delta < 0 {1143 l := ft.limits[x.ID]1144 if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) ||1145 (x.Type.Size() == 4 && l.min >= math.MinInt32-delta) {1146 underflow = false1147 }1148 }1149 if delta < 0 && !underflow {1150 // If delta < 0 and x+delta cannot underflow then x > x+delta (that is, x > v)1151 ft.update(parent, x, v, signed, gt)1152 }1153 if !w.isGenericIntConst() {1154 // If we know that x+delta > w but w is not constant, we can derive:1155 // if delta < 0 and x+delta cannot underflow, then x > w1156 // This is useful for loops with bounds "len(slice)-K" (delta = -K)1157 if delta < 0 && !underflow {1158 ft.update(parent, x, w, signed, r)1159 }1160 } else {1161 // With w,delta constants, we want to derive: x+delta > w ⇒ x > w-delta1162 //1163 // We compute (using integers of the correct size):1164 // min = w - delta1165 // max = MaxInt - delta1166 //1167 // And we prove that:1168 // if min<max: min < x AND x <= max1169 // if min>max: min < x OR x <= max1170 //1171 // This is always correct, even in case of overflow.1172 //1173 // If the initial fact is x+delta >= w instead, the derived conditions are:1174 // if min<max: min <= x AND x <= max1175 // if min>max: min <= x OR x <= max1176 //1177 // Notice the conditions for max are still <=, as they handle overflows.1178 var min, max int641179 switch x.Type.Size() {1180 case 8:1181 min = w.AuxInt - delta1182 max = int64(^uint64(0)>>1) - delta1183 case 4:1184 min = int64(int32(w.AuxInt) - int32(delta))1185 max = int64(int32(^uint32(0)>>1) - int32(delta))1186 case 2:1187 min = int64(int16(w.AuxInt) - int16(delta))1188 max = int64(int16(^uint16(0)>>1) - int16(delta))1189 case 1:1190 min = int64(int8(w.AuxInt) - int8(delta))1191 max = int64(int8(^uint8(0)>>1) - int8(delta))1192 default:1193 panic("unimplemented")1194 }11951196 if min < max {1197 // Record that x > min and max >= x1198 if r == gt {1199 min++1200 }1201 ft.signedMinMax(x, min, max)1202 } else {1203 // We know that either x>min OR x<=max. factsTable cannot record OR conditions,1204 // so let's see if we can already prove that one of them is false, in which case1205 // the other must be true1206 l := ft.limits[x.ID]1207 if l.max <= min {1208 if r&eq == 0 || l.max < min {1209 // x>min (x>=min) is impossible, so it must be x<=max1210 ft.signedMax(x, max)1211 }1212 } else if l.min > max {1213 // x<=max is impossible, so it must be x>min1214 if r == gt {1215 min++1216 }1217 ft.signedMin(x, min)1218 }1219 }1220 }1221 }1222 }12231224 // Look through value-preserving extensions.1225 // If the domain is appropriate for the pre-extension Type,1226 // repeat the update with the pre-extension Value.1227 if isCleanExt(v) {1228 switch {1229 case d == signed && v.Args[0].Type.IsSigned():1230 fallthrough1231 case d == unsigned && !v.Args[0].Type.IsSigned():1232 ft.update(parent, v.Args[0], w, d, r)1233 }1234 }1235 if isCleanExt(w) {1236 switch {1237 case d == signed && w.Args[0].Type.IsSigned():1238 fallthrough1239 case d == unsigned && !w.Args[0].Type.IsSigned():1240 ft.update(parent, v, w.Args[0], d, r)1241 }1242 }1243}12441245var opMin = map[Op]int64{1246 OpAdd64: math.MinInt64, OpSub64: math.MinInt64,1247 OpAdd32: math.MinInt32, OpSub32: math.MinInt32,1248}12491250var opMax = map[Op]int64{1251 OpAdd64: math.MaxInt64, OpSub64: math.MaxInt64,1252 OpAdd32: math.MaxInt32, OpSub32: math.MaxInt32,1253}12541255var opUMax = map[Op]uint64{1256 OpAdd64: math.MaxUint64, OpSub64: math.MaxUint64,1257 OpAdd32: math.MaxUint32, OpSub32: math.MaxUint32,1258}12591260// isNonNegative reports whether v is known to be non-negative.1261func (ft *factsTable) isNonNegative(v *Value) bool {1262 return ft.limits[v.ID].min >= 01263}12641265// checkpoint saves the current state of known relations.1266// Called when descending on a branch.1267func (ft *factsTable) checkpoint() {1268 if ft.unsat {1269 ft.unsatDepth++1270 }1271 ft.limitStack = append(ft.limitStack, checkpointBound)1272 ft.orderS.Checkpoint()1273 ft.orderU.Checkpoint()1274 ft.orderingsStack = append(ft.orderingsStack, 0)1275}12761277// restore restores known relation to the state just1278// before the previous checkpoint.1279// Called when backing up on a branch.1280func (ft *factsTable) restore() {1281 if ft.unsatDepth > 0 {1282 ft.unsatDepth--1283 } else {1284 ft.unsat = false1285 }1286 for {1287 old := ft.limitStack[len(ft.limitStack)-1]1288 ft.limitStack = ft.limitStack[:len(ft.limitStack)-1]1289 if old.vid == 0 { // checkpointBound1290 break1291 }1292 ft.limits[old.vid] = old.limit1293 }1294 ft.orderS.Undo()1295 ft.orderU.Undo()1296 for {1297 id := ft.orderingsStack[len(ft.orderingsStack)-1]1298 ft.orderingsStack = ft.orderingsStack[:len(ft.orderingsStack)-1]1299 if id == 0 { // checkpoint marker1300 break1301 }1302 o := ft.orderings[id]1303 ft.orderings[id] = o.next1304 o.next = ft.orderingCache1305 ft.orderingCache = o1306 }1307}13081309var (1310 reverseBits = [...]relation{0, 4, 2, 6, 1, 5, 3, 7}13111312 // maps what we learn when the positive branch is taken.1313 // For example:1314 // OpLess8: {signed, lt},1315 // v1 = (OpLess8 v2 v3).1316 // If we learn that v1 is true, then we can deduce that v2<v31317 // in the signed domain.1318 domainRelationTable = map[Op]struct {1319 d domain1320 r relation1321 }{1322 OpEq8: {signed | unsigned, eq},1323 OpEq16: {signed | unsigned, eq},1324 OpEq32: {signed | unsigned, eq},1325 OpEq64: {signed | unsigned, eq},1326 OpEqPtr: {pointer, eq},1327 OpEqB: {boolean, eq},13281329 OpNeq8: {signed | unsigned, lt | gt},1330 OpNeq16: {signed | unsigned, lt | gt},1331 OpNeq32: {signed | unsigned, lt | gt},1332 OpNeq64: {signed | unsigned, lt | gt},1333 OpNeqPtr: {pointer, lt | gt},1334 OpNeqB: {boolean, lt | gt},13351336 OpLess8: {signed, lt},1337 OpLess8U: {unsigned, lt},1338 OpLess16: {signed, lt},1339 OpLess16U: {unsigned, lt},1340 OpLess32: {signed, lt},1341 OpLess32U: {unsigned, lt},1342 OpLess64: {signed, lt},1343 OpLess64U: {unsigned, lt},13441345 OpLeq8: {signed, lt | eq},1346 OpLeq8U: {unsigned, lt | eq},1347 OpLeq16: {signed, lt | eq},1348 OpLeq16U: {unsigned, lt | eq},1349 OpLeq32: {signed, lt | eq},1350 OpLeq32U: {unsigned, lt | eq},1351 OpLeq64: {signed, lt | eq},1352 OpLeq64U: {unsigned, lt | eq},1353 }1354)13551356// cleanup returns the posets to the free list1357func (ft *factsTable) cleanup(f *Func) {1358 for _, po := range []*poset{ft.orderS, ft.orderU} {1359 // Make sure it's empty as it should be. A non-empty poset1360 // might cause errors and miscompilations if reused.1361 if checkEnabled {1362 if err := po.CheckEmpty(); err != nil {1363 f.Fatalf("poset not empty after function %s: %v", f.Name, err)1364 }1365 }1366 f.retPoset(po)1367 }1368 f.Cache.freeLimitSlice(ft.limits)1369 f.Cache.freeBoolSlice(ft.recurseCheck)1370 if cap(ft.reusedTopoSortScoresTable) > 0 {1371 f.Cache.freeUintSlice(ft.reusedTopoSortScoresTable)1372 }1373}13741375// addSlicesOfSameLen finds the slices that are in the same block and whose Op1376// is OpPhi and always have the same length, then add the equality relationship1377// between them to ft. If two slices start out with the same length and decrease1378// in length by the same amount on each round of the loop (or in the if block),1379// then we think their lengths are always equal.1380//1381// See https://go.dev/issues/751441382//1383// In fact, we are just propagating the equality1384//1385// if len(a) == len(b) { // from here1386// for len(a) > 4 {1387// a = a[4:]1388// b = b[4:]1389// }1390// if len(a) == len(b) { // to here1391// return true1392// }1393// }1394//1395// or change the for to if:1396//1397// if len(a) == len(b) { // from here1398// if len(a) > 4 {1399// a = a[4:]1400// b = b[4:]1401// }1402// if len(a) == len(b) { // to here1403// return true1404// }1405// }1406func addSlicesOfSameLen(ft *factsTable, b *Block) {1407 // Let w points to the first value we're interested in, and then we1408 // only process those values that appear to be the same length as w,1409 // looping only once. This should be enough in most cases. And u is1410 // similar to w, see comment for predIndex.1411 var u, w *Value1412 var i, j, k sliceInfo1413 isInterested := func(v *Value) bool {1414 j = getSliceInfo(v)1415 return j.sliceWhere != sliceUnknown1416 }1417 for _, v := range b.Values {1418 if v.Uses == 0 {1419 continue1420 }1421 if v.Op == OpPhi && len(v.Args) == 2 && ft.lens[v.ID] != nil && isInterested(v) {1422 if j.predIndex == 1 && ft.lens[v.Args[0].ID] != nil {1423 // found v = (Phi x (SliceMake _ (Add64 (Const64 [n]) (SliceLen x)) _))) or1424 // v = (Phi x (SliceMake _ (Add64 (Const64 [n]) (SliceLen v)) _)))1425 if w == nil {1426 k = j1427 w = v1428 continue1429 }1430 // propagate the equality1431 if j == k && ft.orderS.Equal(ft.lens[v.Args[0].ID], ft.lens[w.Args[0].ID]) {1432 ft.update(b, ft.lens[v.ID], ft.lens[w.ID], signed, eq)1433 }1434 } else if j.predIndex == 0 && ft.lens[v.Args[1].ID] != nil {1435 // found v = (Phi (SliceMake _ (Add64 (Const64 [n]) (SliceLen x)) _)) x) or1436 // v = (Phi (SliceMake _ (Add64 (Const64 [n]) (SliceLen v)) _)) x)1437 if u == nil {1438 i = j1439 u = v1440 continue1441 }1442 // propagate the equality1443 if j == i && ft.orderS.Equal(ft.lens[v.Args[1].ID], ft.lens[u.Args[1].ID]) {1444 ft.update(b, ft.lens[v.ID], ft.lens[u.ID], signed, eq)1445 }1446 }1447 }1448 }1449}14501451type sliceWhere int14521453const (1454 sliceUnknown sliceWhere = iota1455 sliceInFor1456 sliceInIf1457)14581459// predIndex is used to indicate the branch represented by the predecessor1460// block in which the slicing operation occurs.1461type predIndex int14621463type sliceInfo struct {1464 lengthDiff int641465 sliceWhere1466 predIndex1467}14681469// getSliceInfo returns the negative increment of the slice length in a slice1470// operation by examine the Phi node at the merge block. So, we only interest1471// in the slice operation if it is inside a for block or an if block.1472// Otherwise it returns sliceInfo{0, sliceUnknown, 0}.1473//1474// For the following for block:1475//1476// for len(a) > 4 {1477// a = a[4:]1478// }1479//1480// vp = (Phi v3 v9)1481// v5 = (SliceLen vp)1482// v7 = (Add64 (Const64 [-4]) v5)1483// v9 = (SliceMake _ v7 _)1484//1485// returns sliceInfo{-4, sliceInFor, 1}1486//1487// For a subsequent merge block after an if block:1488//1489// if len(a) > 4 {1490// a = a[4:]1491// }1492// a // here1493//1494// vp = (Phi v3 v9)1495// v5 = (SliceLen v3)1496// v7 = (Add64 (Const64 [-4]) v5)1497// v9 = (SliceMake _ v7 _)1498//1499// returns sliceInfo{-4, sliceInIf, 1}1500//1501// Returns sliceInfo{0, sliceUnknown, 0} if it is not the slice1502// operation we are interested in.1503func getSliceInfo(vp *Value) (inf sliceInfo) {1504 if vp.Op != OpPhi || len(vp.Args) != 2 {1505 return1506 }1507 var i predIndex1508 var l *Value // length for OpSliceMake1509 if vp.Args[0].Op != OpSliceMake && vp.Args[1].Op == OpSliceMake {1510 l = vp.Args[1].Args[1]1511 i = 11512 } else if vp.Args[0].Op == OpSliceMake && vp.Args[1].Op != OpSliceMake {1513 l = vp.Args[0].Args[1]1514 i = 01515 } else {1516 return1517 }1518 var op Op1519 switch l.Op {1520 case OpAdd64:1521 op = OpConst641522 case OpAdd32:1523 op = OpConst321524 default:1525 return1526 }1527 if l.Args[0].Op == op && l.Args[1].Op == OpSliceLen && l.Args[1].Args[0] == vp {1528 return sliceInfo{l.Args[0].AuxInt, sliceInFor, i}1529 }1530 if l.Args[1].Op == op && l.Args[0].Op == OpSliceLen && l.Args[0].Args[0] == vp {1531 return sliceInfo{l.Args[1].AuxInt, sliceInFor, i}1532 }1533 if l.Args[0].Op == op && l.Args[1].Op == OpSliceLen && l.Args[1].Args[0] == vp.Args[1-i] {1534 return sliceInfo{l.Args[0].AuxInt, sliceInIf, i}1535 }1536 if l.Args[1].Op == op && l.Args[0].Op == OpSliceLen && l.Args[0].Args[0] == vp.Args[1-i] {1537 return sliceInfo{l.Args[1].AuxInt, sliceInIf, i}1538 }1539 return1540}15411542// prove removes redundant BlockIf branches that can be inferred1543// from previous dominating comparisons.1544//1545// By far, the most common redundant pair are generated by bounds checking.1546// For example for the code:1547//1548// a[i] = 41549// foo(a[i])1550//1551// The compiler will generate the following code:1552//1553// if i >= len(a) {1554// panic("not in bounds")1555// }1556// a[i] = 41557// if i >= len(a) {1558// panic("not in bounds")1559// }1560// foo(a[i])1561//1562// The second comparison i >= len(a) is clearly redundant because if the1563// else branch of the first comparison is executed, we already know that i < len(a).1564// The code for the second panic can be removed.1565//1566// prove works by finding contradictions and trimming branches whose1567// conditions are unsatisfiable given the branches leading up to them.1568// It tracks a "fact table" of branch conditions. For each branching1569// block, it asserts the branch conditions that uniquely dominate that1570// block, and then separately asserts the block's branch condition and1571// its negation. If either leads to a contradiction, it can trim that1572// successor.1573func prove(f *Func) {1574 // Find induction variables.1575 var indVars map[*Block][]indVar1576 for _, v := range findIndVar(f) {1577 ind := v.ind1578 if len(ind.Args) != 2 {1579 // the rewrite code assumes there is only ever two parents to loops1580 panic("unexpected induction with too many parents")1581 }15821583 nxt := v.nxt1584 if !(ind.Uses == 2 && // 2 used by comparison and next1585 nxt.Uses == 1) { // 1 used by induction1586 // ind or nxt is used inside the loop, add it for the facts table1587 if indVars == nil {1588 indVars = make(map[*Block][]indVar)1589 }1590 indVars[v.entry] = append(indVars[v.entry], v)1591 continue1592 } else {1593 // Since this induction variable is not used for anything but counting the iterations,1594 // no point in putting it into the facts table.1595 }15961597 maybeRewriteLoopToDownwardCountingLoop(f, v)1598 }15991600 ft := newFactsTable(f)1601 ft.checkpoint()16021603 // Find length and capacity ops.1604 for _, b := range f.Blocks {1605 for _, v := range b.Values {1606 if v.Uses == 0 {1607 // We don't care about dead values.1608 // (There can be some that are CSEd but not removed yet.)1609 continue1610 }1611 switch v.Op {1612 case OpSliceLen:1613 if ft.lens == nil {1614 ft.lens = map[ID]*Value{}1615 }1616 // Set all len Values for the same slice as equal in the poset.1617 // The poset handles transitive relations, so Values related to1618 // any OpSliceLen for this slice will be correctly related to others.1619 if l, ok := ft.lens[v.Args[0].ID]; ok {1620 ft.update(b, v, l, signed, eq)1621 } else {1622 ft.lens[v.Args[0].ID] = v1623 }1624 case OpSliceCap:1625 if ft.caps == nil {1626 ft.caps = map[ID]*Value{}1627 }1628 // Same as case OpSliceLen above, but for slice cap.1629 if c, ok := ft.caps[v.Args[0].ID]; ok {1630 ft.update(b, v, c, signed, eq)1631 } else {1632 ft.caps[v.Args[0].ID] = v1633 }1634 }1635 }1636 }16371638 // current node state1639 type walkState int1640 const (1641 descend walkState = iota1642 simplify1643 )1644 // work maintains the DFS stack.1645 type bp struct {1646 block *Block // current handled block1647 state walkState // what's to do1648 }1649 work := make([]bp, 0, 256)1650 work = append(work, bp{1651 block: f.Entry,1652 state: descend,1653 })16541655 idom := f.Idom()1656 sdom := f.Sdom()16571658 // DFS on the dominator tree.1659 //1660 // For efficiency, we consider only the dominator tree rather1661 // than the entire flow graph. On the way down, we consider1662 // incoming branches and accumulate conditions that uniquely1663 // dominate the current block. If we discover a contradiction,1664 // we can eliminate the entire block and all of its children.1665 // On the way back up, we consider outgoing branches that1666 // haven't already been considered. This way we consider each1667 // branch condition only once.1668 for len(work) > 0 {1669 node := work[len(work)-1]1670 work = work[:len(work)-1]1671 parent := idom[node.block.ID]1672 branch := getBranch(sdom, parent, node.block)16731674 switch node.state {1675 case descend:1676 ft.checkpoint()16771678 // Entering the block, add facts about the induction variable1679 // that is bound to this block.1680 for _, iv := range indVars[node.block] {1681 addIndVarRestrictions(ft, parent, iv)1682 }16831684 // Add results of reaching this block via a branch from1685 // its immediate dominator (if any).1686 if branch != unknown {1687 addBranchRestrictions(ft, parent, branch)1688 }16891690 // Add slices of the same length start from current block.1691 addSlicesOfSameLen(ft, node.block)16921693 if ft.unsat {1694 // node.block is unreachable.1695 // Remove it and don't visit1696 // its children.1697 removeBranch(parent, branch)1698 ft.restore()1699 break1700 }1701 // Otherwise, we can now commit to1702 // taking this branch. We'll restore1703 // ft when we unwind.17041705 // Add facts about the values in the current block.1706 addLocalFacts(ft, node.block)17071708 work = append(work, bp{1709 block: node.block,1710 state: simplify,1711 })1712 for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {1713 work = append(work, bp{1714 block: s,1715 state: descend,1716 })1717 }17181719 case simplify:1720 simplifyBlock(sdom, ft, node.block)1721 ft.restore()1722 }1723 }17241725 ft.restore()17261727 ft.cleanup(f)1728}17291730// initLimit sets initial constant limit for v. This limit is based1731// only on the operation itself, not any of its input arguments. This1732// method is only used in two places, once when the prove pass startup1733// and the other when a new ssa value is created, both for init. (unlike1734// flowLimit, below, which computes additional constraints based on1735// ranges of opcode arguments).1736func initLimit(v *Value) limit {1737 if v.Type.IsBoolean() {1738 switch v.Op {1739 case OpConstBool:1740 b := v.AuxInt1741 return limit{min: b, max: b, umin: uint64(b), umax: uint64(b)}1742 default:1743 return limit{min: 0, max: 1, umin: 0, umax: 1}1744 }1745 }1746 if v.Type.IsPtrShaped() { // These are the types that EqPtr/NeqPtr operate on, except uintptr.1747 switch v.Op {1748 case OpConstNil:1749 return limit{min: 0, max: 0, umin: 0, umax: 0}1750 case OpAddr, OpLocalAddr: // TODO: others?1751 l := noLimit()1752 l.umin = 11753 return l1754 default:1755 return noLimit()1756 }1757 }1758 if !v.Type.IsInteger() {1759 return noLimit()1760 }17611762 // Default limits based on type.1763 lim := noLimitForBitsize(uint(v.Type.Size()) * 8)17641765 // Tighter limits on some opcodes.1766 switch v.Op {1767 // constants1768 case OpConst64:1769 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(v.AuxInt), umax: uint64(v.AuxInt)}1770 case OpConst32:1771 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(uint32(v.AuxInt)), umax: uint64(uint32(v.AuxInt))}1772 case OpConst16:1773 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(uint16(v.AuxInt)), umax: uint64(uint16(v.AuxInt))}1774 case OpConst8:1775 lim = limit{min: v.AuxInt, max: v.AuxInt, umin: uint64(uint8(v.AuxInt)), umax: uint64(uint8(v.AuxInt))}17761777 // extensions1778 case OpZeroExt8to64, OpZeroExt8to32, OpZeroExt8to16:1779 lim = lim.signedMinMax(0, 1<<8-1)1780 lim = lim.unsignedMax(1<<8 - 1)1781 case OpZeroExt16to64, OpZeroExt16to32:1782 lim = lim.signedMinMax(0, 1<<16-1)1783 lim = lim.unsignedMax(1<<16 - 1)1784 case OpZeroExt32to64:1785 lim = lim.signedMinMax(0, 1<<32-1)1786 lim = lim.unsignedMax(1<<32 - 1)1787 case OpSignExt8to64, OpSignExt8to32, OpSignExt8to16:1788 lim = lim.signedMinMax(math.MinInt8, math.MaxInt8)1789 case OpSignExt16to64, OpSignExt16to32:1790 lim = lim.signedMinMax(math.MinInt16, math.MaxInt16)1791 case OpSignExt32to64:1792 lim = lim.signedMinMax(math.MinInt32, math.MaxInt32)17931794 // math/bits intrinsics1795 case OpCtz64, OpBitLen64, OpPopCount64,1796 OpCtz32, OpBitLen32, OpPopCount32,1797 OpCtz16, OpBitLen16, OpPopCount16,1798 OpCtz8, OpBitLen8, OpPopCount8:1799 lim = lim.unsignedMax(uint64(v.Args[0].Type.Size() * 8))18001801 // bool to uint8 conversion1802 case OpCvtBoolToUint8:1803 lim = lim.unsignedMax(1)18041805 // length operations1806 case OpSliceLen, OpSliceCap:1807 f := v.Block.Func1808 elemSize := uint64(v.Args[0].Type.Elem().Size())1809 if elemSize > 0 {1810 heapSize := uint64(1)<<(uint64(f.Config.PtrSize)*8) - 11811 maximumElementsFittingInHeap := heapSize / elemSize1812 lim = lim.unsignedMax(maximumElementsFittingInHeap)1813 }1814 fallthrough1815 case OpStringLen:1816 lim = lim.signedMin(0)1817 }18181819 // signed <-> unsigned propagation1820 if lim.min >= 0 {1821 lim = lim.unsignedMinMax(uint64(lim.min), uint64(lim.max))1822 }1823 if fitsInBitsU(lim.umax, uint(8*v.Type.Size()-1)) {1824 lim = lim.signedMinMax(int64(lim.umin), int64(lim.umax))1825 }18261827 return lim1828}18291830// flowLimit updates the known limits of v in ft.1831// flowLimit can use the ranges of input arguments.1832//1833// Note: this calculation only happens at the point the value is defined. We do not reevaluate1834// it later. So for example:1835//1836// v := x + y1837// if 0 <= x && x < 5 && 0 <= y && y < 5 { ... use v ... }1838//1839// we don't discover that the range of v is bounded in the conditioned1840// block. We could recompute the range of v once we enter the block so1841// we know that it is 0 <= v <= 8, but we don't have a mechanism to do1842// that right now.1843func (ft *factsTable) flowLimit(v *Value) {1844 if !v.Type.IsInteger() {1845 // TODO: boolean?1846 return1847 }18481849 // Additional limits based on opcode and argument.1850 // No need to repeat things here already done in initLimit.1851 switch v.Op {18521853 // extensions1854 case OpZeroExt8to64, OpZeroExt8to32, OpZeroExt8to16, OpZeroExt16to64, OpZeroExt16to32, OpZeroExt32to64:1855 a := ft.limits[v.Args[0].ID]1856 ft.unsignedMinMax(v, a.umin, a.umax)1857 case OpSignExt8to64, OpSignExt8to32, OpSignExt8to16, OpSignExt16to64, OpSignExt16to32, OpSignExt32to64:1858 a := ft.limits[v.Args[0].ID]1859 ft.signedMinMax(v, a.min, a.max)1860 case OpTrunc64to8, OpTrunc64to16, OpTrunc64to32, OpTrunc32to8, OpTrunc32to16, OpTrunc16to8:1861 a := ft.limits[v.Args[0].ID]1862 if a.umax <= 1<<(uint64(v.Type.Size())*8)-1 {1863 ft.unsignedMinMax(v, a.umin, a.umax)1864 }18651866 // math/bits1867 case OpCtz64, OpCtz32, OpCtz16, OpCtz8:1868 a := v.Args[0]1869 al := ft.limits[a.ID]1870 ft.newLimit(v, al.ctz(uint(a.Type.Size())*8))18711872 case OpPopCount64, OpPopCount32, OpPopCount16, OpPopCount8:1873 a := v.Args[0]1874 al := ft.limits[a.ID]1875 ft.newLimit(v, al.popcount(uint(a.Type.Size())*8))18761877 case OpBitLen64, OpBitLen32, OpBitLen16, OpBitLen8:1878 a := v.Args[0]1879 al := ft.limits[a.ID]1880 ft.newLimit(v, al.bitlen(uint(a.Type.Size())*8))18811882 // Masks.18831884 // TODO: if y.umax and y.umin share a leading bit pattern, y also has that leading bit pattern.1885 // we could compare the patterns of always set bits in a and b and learn more about minimum and maximum.1886 // But I doubt this help any real world code.1887 case OpOr64, OpOr32, OpOr16, OpOr8:1888 // OR can only make the value bigger and can't flip bits proved to be zero in both inputs.1889 a := ft.limits[v.Args[0].ID]1890 b := ft.limits[v.Args[1].ID]1891 ft.unsignedMinMax(v,1892 max(a.umin, b.umin),1893 1<<bits.Len64(a.umax|b.umax)-1)1894 case OpXor64, OpXor32, OpXor16, OpXor8:1895 // XOR can't flip bits that are proved to be zero in both inputs.1896 a := ft.limits[v.Args[0].ID]1897 b := ft.limits[v.Args[1].ID]1898 ft.unsignedMax(v, 1<<bits.Len64(a.umax|b.umax)-1)1899 case OpCom64, OpCom32, OpCom16, OpCom8:1900 a := ft.limits[v.Args[0].ID]1901 ft.newLimit(v, a.com(uint(v.Type.Size())*8))19021903 // Arithmetic.1904 case OpAdd64, OpAdd32, OpAdd16, OpAdd8:1905 a := ft.limits[v.Args[0].ID]1906 b := ft.limits[v.Args[1].ID]1907 ft.newLimit(v, a.add(b, uint(v.Type.Size())*8))1908 case OpSub64, OpSub32, OpSub16, OpSub8:1909 a := ft.limits[v.Args[0].ID]1910 b := ft.limits[v.Args[1].ID]1911 ft.newLimit(v, a.sub(b, uint(v.Type.Size())*8))1912 ft.detectMod(v)1913 ft.detectSliceLenRelation(v)1914 ft.detectSubRelations(v)1915 case OpNeg64, OpNeg32, OpNeg16, OpNeg8:1916 a := ft.limits[v.Args[0].ID]1917 bitsize := uint(v.Type.Size()) * 81918 ft.newLimit(v, a.neg(bitsize))1919 case OpMul64, OpMul32, OpMul16, OpMul8:1920 a := ft.limits[v.Args[0].ID]1921 b := ft.limits[v.Args[1].ID]1922 ft.newLimit(v, a.mul(b, uint(v.Type.Size())*8))1923 case OpLsh64x64, OpLsh64x32, OpLsh64x16, OpLsh64x8,1924 OpLsh32x64, OpLsh32x32, OpLsh32x16, OpLsh32x8,1925 OpLsh16x64, OpLsh16x32, OpLsh16x16, OpLsh16x8,1926 OpLsh8x64, OpLsh8x32, OpLsh8x16, OpLsh8x8:1927 a := ft.limits[v.Args[0].ID]1928 b := ft.limits[v.Args[1].ID]1929 bitsize := uint(v.Type.Size()) * 81930 ft.newLimit(v, a.mul(b.exp2(bitsize), bitsize))1931 case OpRsh64x64, OpRsh64x32, OpRsh64x16, OpRsh64x8,1932 OpRsh32x64, OpRsh32x32, OpRsh32x16, OpRsh32x8,1933 OpRsh16x64, OpRsh16x32, OpRsh16x16, OpRsh16x8,1934 OpRsh8x64, OpRsh8x32, OpRsh8x16, OpRsh8x8:1935 a := ft.limits[v.Args[0].ID]1936 b := ft.limits[v.Args[1].ID]1937 if b.min >= 0 {1938 // Shift of negative makes a value closer to 0 (greater),1939 // so if a.min is negative, v.min is a.min>>b.min instead of a.min>>b.max,1940 // and similarly if a.max is negative, v.max is a.max>>b.max.1941 // Easier to compute min and max of both than to write sign logic.1942 vmin := min(a.min>>b.min, a.min>>b.max)1943 vmax := max(a.max>>b.min, a.max>>b.max)1944 ft.signedMinMax(v, vmin, vmax)1945 }1946 case OpRsh64Ux64, OpRsh64Ux32, OpRsh64Ux16, OpRsh64Ux8,1947 OpRsh32Ux64, OpRsh32Ux32, OpRsh32Ux16, OpRsh32Ux8,1948 OpRsh16Ux64, OpRsh16Ux32, OpRsh16Ux16, OpRsh16Ux8,1949 OpRsh8Ux64, OpRsh8Ux32, OpRsh8Ux16, OpRsh8Ux8:1950 a := ft.limits[v.Args[0].ID]1951 b := ft.limits[v.Args[1].ID]1952 if b.min >= 0 {1953 ft.unsignedMinMax(v, a.umin>>b.max, a.umax>>b.min)1954 }1955 case OpDiv64, OpDiv32, OpDiv16, OpDiv8:1956 a := ft.limits[v.Args[0].ID]1957 b := ft.limits[v.Args[1].ID]1958 if !(a.nonnegative() && b.nonnegative()) {1959 // TODO: we could handle signed limits but I didn't bother.1960 break1961 }1962 fallthrough1963 case OpDiv64u, OpDiv32u, OpDiv16u, OpDiv8u:1964 a := ft.limits[v.Args[0].ID]1965 b := ft.limits[v.Args[1].ID]1966 lim := noLimit()1967 if b.umax > 0 {1968 lim = lim.unsignedMin(a.umin / b.umax)1969 }1970 if b.umin > 0 {1971 lim = lim.unsignedMax(a.umax / b.umin)1972 }1973 ft.newLimit(v, lim)1974 case OpMod64, OpMod32, OpMod16, OpMod8:1975 ft.modLimit(true, v, v.Args[0], v.Args[1])1976 case OpMod64u, OpMod32u, OpMod16u, OpMod8u:1977 ft.modLimit(false, v, v.Args[0], v.Args[1])19781979 case OpPhi:1980 // Compute the union of all the input phis.1981 // Often this will convey no information, because the block1982 // is not dominated by its predecessors and hence the1983 // phi arguments might not have been processed yet. But if1984 // the values are declared earlier, it may help. e.g., for1985 // v = phi(c3, c5)1986 // where c3 = OpConst [3] and c5 = OpConst [5] are1987 // defined in the entry block, we can derive [3,5]1988 // as the limit for v.1989 l := ft.limits[v.Args[0].ID]1990 for _, a := range v.Args[1:] {1991 l2 := ft.limits[a.ID]1992 l.min = min(l.min, l2.min)1993 l.max = max(l.max, l2.max)1994 l.umin = min(l.umin, l2.umin)1995 l.umax = max(l.umax, l2.umax)1996 }1997 ft.newLimit(v, l)1998 }1999}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.