| // Copyright 2016 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package ssa | |
| import ( | |
| "cmd/internal/src" | |
| "fmt" | |
| "math" | |
| ) | |
| type branch int | |
| const ( | |
| unknown branch = iota | |
| positive | |
| negative | |
| ) | |
| // relation represents the set of possible relations between | |
| // pairs of variables (v, w). Without a priori knowledge the | |
| // mask is lt | eq | gt meaning v can be less than, equal to or | |
| // greater than w. When the execution path branches on the condition | |
| // `v op w` the set of relations is updated to exclude any | |
| // relation not possible due to `v op w` being true (or false). | |
| // | |
| // E.g. | |
| // | |
| // r := relation(...) | |
| // | |
| // if v < w { | |
| // newR := r & lt | |
| // } | |
| // if v >= w { | |
| // newR := r & (eq|gt) | |
| // } | |
| // if v != w { | |
| // newR := r & (lt|gt) | |
| // } | |
| type relation uint | |
| const ( | |
| lt relation = 1 << iota | |
| eq | |
| gt | |
| ) | |
| var relationStrings = [...]string{ | |
| 0: "none", lt: "<", eq: "==", lt | eq: "<=", | |
| gt: ">", gt | lt: "!=", gt | eq: ">=", gt | eq | lt: "any", | |
| } | |
| func (r relation) String() string { | |
| if r < relation(len(relationStrings)) { | |
| return relationStrings[r] | |
| } | |
| return fmt.Sprintf("relation(%d)", uint(r)) | |
| } | |
| // domain represents the domain of a variable pair in which a set | |
| // of relations is known. For example, relations learned for unsigned | |
| // pairs cannot be transferred to signed pairs because the same bit | |
| // representation can mean something else. | |
| type domain uint | |
| const ( | |
| signed domain = 1 << iota | |
| unsigned | |
| pointer | |
| boolean | |
| ) | |
| var domainStrings = [...]string{ | |
| "signed", "unsigned", "pointer", "boolean", | |
| } | |
| func (d domain) String() string { | |
| s := "" | |
| for i, ds := range domainStrings { | |
| if d&(1<<uint(i)) != 0 { | |
| if len(s) != 0 { | |
| s += "|" | |
| } | |
| s += ds | |
| d &^= 1 << uint(i) | |
| } | |
| } | |
| if d != 0 { | |
| if len(s) != 0 { | |
| s += "|" | |
| } | |
| s += fmt.Sprintf("0x%x", uint(d)) | |
| } | |
| return s | |
| } | |
| type pair struct { | |
| v, w *Value // a pair of values, ordered by ID. | |
| // v can be nil, to mean the zero value. | |
| // for booleans the zero value (v == nil) is false. | |
| d domain | |
| } | |
| // fact is a pair plus a relation for that pair. | |
| type fact struct { | |
| p pair | |
| r relation | |
| } | |
| // a limit records known upper and lower bounds for a value. | |
| type limit struct { | |
| min, max int64 // min <= value <= max, signed | |
| umin, umax uint64 // umin <= value <= umax, unsigned | |
| } | |
| func (l limit) String() string { | |
| return fmt.Sprintf("sm,SM,um,UM=%d,%d,%d,%d", l.min, l.max, l.umin, l.umax) | |
| } | |
| func (l limit) intersect(l2 limit) limit { | |
| if l.min < l2.min { | |
| l.min = l2.min | |
| } | |
| if l.umin < l2.umin { | |
| l.umin = l2.umin | |
| } | |
| if l.max > l2.max { | |
| l.max = l2.max | |
| } | |
| if l.umax > l2.umax { | |
| l.umax = l2.umax | |
| } | |
| return l | |
| } | |
| var noLimit = limit{math.MinInt64, math.MaxInt64, 0, math.MaxUint64} | |
| // a limitFact is a limit known for a particular value. | |
| type limitFact struct { | |
| vid ID | |
| limit limit | |
| } | |
| // factsTable keeps track of relations between pairs of values. | |
| // | |
| // The fact table logic is sound, but incomplete. Outside of a few | |
| // special cases, it performs no deduction or arithmetic. While there | |
| // are known decision procedures for this, the ad hoc approach taken | |
| // by the facts table is effective for real code while remaining very | |
| // efficient. | |
| type factsTable struct { | |
| // unsat is true if facts contains a contradiction. | |
| // | |
| // Note that the factsTable logic is incomplete, so if unsat | |
| // is false, the assertions in factsTable could be satisfiable | |
| // *or* unsatisfiable. | |
| unsat bool // true if facts contains a contradiction | |
| unsatDepth int // number of unsat checkpoints | |
| facts map[pair]relation // current known set of relation | |
| stack []fact // previous sets of relations | |
| // order is a couple of partial order sets that record information | |
| // about relations between SSA values in the signed and unsigned | |
| // domain. | |
| orderS *poset | |
| orderU *poset | |
| // known lower and upper bounds on individual values. | |
| limits map[ID]limit | |
| limitStack []limitFact // previous entries | |
| // For each slice s, a map from s to a len(s)/cap(s) value (if any) | |
| // TODO: check if there are cases that matter where we have | |
| // more than one len(s) for a slice. We could keep a list if necessary. | |
| lens map[ID]*Value | |
| caps map[ID]*Value | |
| // zero is a zero-valued constant | |
| zero *Value | |
| } | |
| // checkpointFact is an invalid value used for checkpointing | |
| // and restoring factsTable. | |
| var checkpointFact = fact{} | |
| var checkpointBound = limitFact{} | |
| func newFactsTable(f *Func) *factsTable { | |
| ft := &factsTable{} | |
| ft.orderS = f.newPoset() | |
| ft.orderU = f.newPoset() | |
| ft.orderS.SetUnsigned(false) | |
| ft.orderU.SetUnsigned(true) | |
| ft.facts = make(map[pair]relation) | |
| ft.stack = make([]fact, 4) | |
| ft.limits = make(map[ID]limit) | |
| ft.limitStack = make([]limitFact, 4) | |
| ft.zero = f.ConstInt64(f.Config.Types.Int64, 0) | |
| return ft | |
| } | |
| // update updates the set of relations between v and w in domain d | |
| // restricting it to r. | |
| func (ft *factsTable) update(parent *Block, v, w *Value, d domain, r relation) { | |
| if parent.Func.pass.debug > 2 { | |
| parent.Func.Warnl(parent.Pos, "parent=%s, update %s %s %s", parent, v, w, r) | |
| } | |
| // No need to do anything else if we already found unsat. | |
| if ft.unsat { | |
| return | |
| } | |
| // Self-fact. It's wasteful to register it into the facts | |
| // table, so just note whether it's satisfiable | |
| if v == w { | |
| if r&eq == 0 { | |
| ft.unsat = true | |
| } | |
| return | |
| } | |
| if d == signed || d == unsigned { | |
| var ok bool | |
| order := ft.orderS | |
| if d == unsigned { | |
| order = ft.orderU | |
| } | |
| switch r { | |
| case lt: | |
| ok = order.SetOrder(v, w) | |
| case gt: | |
| ok = order.SetOrder(w, v) | |
| case lt | eq: | |
| ok = order.SetOrderOrEqual(v, w) | |
| case gt | eq: | |
| ok = order.SetOrderOrEqual(w, v) | |
| case eq: | |
| ok = order.SetEqual(v, w) | |
| case lt | gt: | |
| ok = order.SetNonEqual(v, w) | |
| default: | |
| panic("unknown relation") | |
| } | |
| if !ok { | |
| if parent.Func.pass.debug > 2 { | |
| parent.Func.Warnl(parent.Pos, "unsat %s %s %s", v, w, r) | |
| } | |
| ft.unsat = true | |
| return | |
| } | |
| } else { | |
| if lessByID(w, v) { | |
| v, w = w, v | |
| r = reverseBits[r] | |
| } | |
| p := pair{v, w, d} | |
| oldR, ok := ft.facts[p] | |
| if !ok { | |
| if v == w { | |
| oldR = eq | |
| } else { | |
| oldR = lt | eq | gt | |
| } | |
| } | |
| // No changes compared to information already in facts table. | |
| if oldR == r { | |
| return | |
| } | |
| ft.stack = append(ft.stack, fact{p, oldR}) | |
| ft.facts[p] = oldR & r | |
| // If this relation is not satisfiable, mark it and exit right away | |
| if oldR&r == 0 { | |
| if parent.Func.pass.debug > 2 { | |
| parent.Func.Warnl(parent.Pos, "unsat %s %s %s", v, w, r) | |
| } | |
| ft.unsat = true | |
| return | |
| } | |
| } | |
| // Extract bounds when comparing against constants | |
| if v.isGenericIntConst() { | |
| v, w = w, v | |
| r = reverseBits[r] | |
| } | |
| if v != nil && w.isGenericIntConst() { | |
| // Note: all the +1/-1 below could overflow/underflow. Either will | |
| // still generate correct results, it will just lead to imprecision. | |
| // In fact if there is overflow/underflow, the corresponding | |
| // code is unreachable because the known range is outside the range | |
| // of the value's type. | |
| old, ok := ft.limits[v.ID] | |
| if !ok { | |
| old = noLimit | |
| if v.isGenericIntConst() { | |
| switch d { | |
| case signed: | |
| old.min, old.max = v.AuxInt, v.AuxInt | |
| if v.AuxInt >= 0 { | |
| old.umin, old.umax = uint64(v.AuxInt), uint64(v.AuxInt) | |
| } | |
| case unsigned: | |
| old.umin = v.AuxUnsigned() | |
| old.umax = old.umin | |
| if int64(old.umin) >= 0 { | |
| old.min, old.max = int64(old.umin), int64(old.umin) | |
| } | |
| } | |
| } | |
| } | |
| lim := noLimit | |
| switch d { | |
| case signed: | |
| c := w.AuxInt | |
| switch r { | |
| case lt: | |
| lim.max = c - 1 | |
| case lt | eq: | |
| lim.max = c | |
| case gt | eq: | |
| lim.min = c | |
| case gt: | |
| lim.min = c + 1 | |
| case lt | gt: | |
| lim = old | |
| if c == lim.min { | |
| lim.min++ | |
| } | |
| if c == lim.max { | |
| lim.max-- | |
| } | |
| case eq: | |
| lim.min = c | |
| lim.max = c | |
| } | |
| if lim.min >= 0 { | |
| // int(x) >= 0 && int(x) >= N ⇒ uint(x) >= N | |
| lim.umin = uint64(lim.min) | |
| } | |
| if lim.max != noLimit.max && old.min >= 0 && lim.max >= 0 { | |
| // 0 <= int(x) <= N ⇒ 0 <= uint(x) <= N | |
| // This is for a max update, so the lower bound | |
| // comes from what we already know (old). | |
| lim.umax = uint64(lim.max) | |
| } | |
| case unsigned: | |
| uc := w.AuxUnsigned() | |
| switch r { | |
| case lt: | |
| lim.umax = uc - 1 | |
| case lt | eq: | |
| lim.umax = uc | |
| case gt | eq: | |
| lim.umin = uc | |
| case gt: | |
| lim.umin = uc + 1 | |
| case lt | gt: | |
| lim = old | |
| if uc == lim.umin { | |
| lim.umin++ | |
| } | |
| if uc == lim.umax { | |
| lim.umax-- | |
| } | |
| case eq: | |
| lim.umin = uc | |
| lim.umax = uc | |
| } | |
| // We could use the contrapositives of the | |
| // signed implications to derive signed facts, | |
| // but it turns out not to matter. | |
| } | |
| ft.limitStack = append(ft.limitStack, limitFact{v.ID, old}) | |
| lim = old.intersect(lim) | |
| ft.limits[v.ID] = lim | |
| if v.Block.Func.pass.debug > 2 { | |
| v.Block.Func.Warnl(parent.Pos, "parent=%s, new limits %s %s %s %s", parent, v, w, r, lim.String()) | |
| } | |
| if lim.min > lim.max || lim.umin > lim.umax { | |
| ft.unsat = true | |
| return | |
| } | |
| } | |
| // Derived facts below here are only about numbers. | |
| if d != signed && d != unsigned { | |
| return | |
| } | |
| // Additional facts we know given the relationship between len and cap. | |
| // | |
| // TODO: Since prove now derives transitive relations, it | |
| // should be sufficient to learn that len(w) <= cap(w) at the | |
| // beginning of prove where we look for all len/cap ops. | |
| if v.Op == OpSliceLen && r< == 0 && ft.caps[v.Args[0].ID] != nil { | |
| // len(s) > w implies cap(s) > w | |
| // len(s) >= w implies cap(s) >= w | |
| // len(s) == w implies cap(s) >= w | |
| ft.update(parent, ft.caps[v.Args[0].ID], w, d, r|gt) | |
| } | |
| if w.Op == OpSliceLen && r> == 0 && ft.caps[w.Args[0].ID] != nil { | |
| // same, length on the RHS. | |
| ft.update(parent, v, ft.caps[w.Args[0].ID], d, r|lt) | |
| } | |
| if v.Op == OpSliceCap && r> == 0 && ft.lens[v.Args[0].ID] != nil { | |
| // cap(s) < w implies len(s) < w | |
| // cap(s) <= w implies len(s) <= w | |
| // cap(s) == w implies len(s) <= w | |
| ft.update(parent, ft.lens[v.Args[0].ID], w, d, r|lt) | |
| } | |
| if w.Op == OpSliceCap && r< == 0 && ft.lens[w.Args[0].ID] != nil { | |
| // same, capacity on the RHS. | |
| ft.update(parent, v, ft.lens[w.Args[0].ID], d, r|gt) | |
| } | |
| // Process fence-post implications. | |
| // | |
| // First, make the condition > or >=. | |
| if r == lt || r == lt|eq { | |
| v, w = w, v | |
| r = reverseBits[r] | |
| } | |
| switch r { | |
| case gt: | |
| if x, delta := isConstDelta(v); x != nil && delta == 1 { | |
| // x+1 > w ⇒ x >= w | |
| // | |
| // This is useful for eliminating the | |
| // growslice branch of append. | |
| ft.update(parent, x, w, d, gt|eq) | |
| } else if x, delta := isConstDelta(w); x != nil && delta == -1 { | |
| // v > x-1 ⇒ v >= x | |
| ft.update(parent, v, x, d, gt|eq) | |
| } | |
| case gt | eq: | |
| if x, delta := isConstDelta(v); x != nil && delta == -1 { | |
| // x-1 >= w && x > min ⇒ x > w | |
| // | |
| // Useful for i > 0; s[i-1]. | |
| lim, ok := ft.limits[x.ID] | |
| if ok && ((d == signed && lim.min > opMin[v.Op]) || (d == unsigned && lim.umin > 0)) { | |
| ft.update(parent, x, w, d, gt) | |
| } | |
| } else if x, delta := isConstDelta(w); x != nil && delta == 1 { | |
| // v >= x+1 && x < max ⇒ v > x | |
| lim, ok := ft.limits[x.ID] | |
| if ok && ((d == signed && lim.max < opMax[w.Op]) || (d == unsigned && lim.umax < opUMax[w.Op])) { | |
| ft.update(parent, v, x, d, gt) | |
| } | |
| } | |
| } | |
| // Process: x+delta > w (with delta constant) | |
| // Only signed domain for now (useful for accesses to slices in loops). | |
| if r == gt || r == gt|eq { | |
| if x, delta := isConstDelta(v); x != nil && d == signed { | |
| if parent.Func.pass.debug > 1 { | |
| 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) | |
| } | |
| if !w.isGenericIntConst() { | |
| // If we know that x+delta > w but w is not constant, we can derive: | |
| // if delta < 0 and x > MinInt - delta, then x > w (because x+delta cannot underflow) | |
| // This is useful for loops with bounds "len(slice)-K" (delta = -K) | |
| if l, has := ft.limits[x.ID]; has && delta < 0 { | |
| if (x.Type.Size() == 8 && l.min >= math.MinInt64-delta) || | |
| (x.Type.Size() == 4 && l.min >= math.MinInt32-delta) { | |
| ft.update(parent, x, w, signed, r) | |
| } | |
| } | |
| } else { | |
| // With w,delta constants, we want to derive: x+delta > w ⇒ x > w-delta | |
| // | |
| // We compute (using integers of the correct size): | |
| // min = w - delta | |
| // max = MaxInt - delta | |
| // | |
| // And we prove that: | |
| // if min<max: min < x AND x <= max | |
| // if min>max: min < x OR x <= max | |
| // | |
| // This is always correct, even in case of overflow. | |
| // | |
| // If the initial fact is x+delta >= w instead, the derived conditions are: | |
| // if min<max: min <= x AND x <= max | |
| // if min>max: min <= x OR x <= max | |
| // | |
| // Notice the conditions for max are still <=, as they handle overflows. | |
| var min, max int64 | |
| var vmin, vmax *Value | |
| switch x.Type.Size() { | |
| case 8: | |
| min = w.AuxInt - delta | |
| max = int64(^uint64(0)>>1) - delta | |
| vmin = parent.NewValue0I(parent.Pos, OpConst64, parent.Func.Config.Types.Int64, min) | |
| vmax = parent.NewValue0I(parent.Pos, OpConst64, parent.Func.Config.Types.Int64, max) | |
| case 4: | |
| min = int64(int32(w.AuxInt) - int32(delta)) | |
| max = int64(int32(^uint32(0)>>1) - int32(delta)) | |
| vmin = parent.NewValue0I(parent.Pos, OpConst32, parent.Func.Config.Types.Int32, min) | |
| vmax = parent.NewValue0I(parent.Pos, OpConst32, parent.Func.Config.Types.Int32, max) | |
| default: | |
| panic("unimplemented") | |
| } | |
| if min < max { | |
| // Record that x > min and max >= x | |
| ft.update(parent, x, vmin, d, r) | |
| ft.update(parent, vmax, x, d, r|eq) | |
| } else { | |
| // We know that either x>min OR x<=max. factsTable cannot record OR conditions, | |
| // so let's see if we can already prove that one of them is false, in which case | |
| // the other must be true | |
| if l, has := ft.limits[x.ID]; has { | |
| if l.max <= min { | |
| if r&eq == 0 || l.max < min { | |
| // x>min (x>=min) is impossible, so it must be x<=max | |
| ft.update(parent, vmax, x, d, r|eq) | |
| } | |
| } else if l.min > max { | |
| // x<=max is impossible, so it must be x>min | |
| ft.update(parent, x, vmin, d, r) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // Look through value-preserving extensions. | |
| // If the domain is appropriate for the pre-extension Type, | |
| // repeat the update with the pre-extension Value. | |
| if isCleanExt(v) { | |
| switch { | |
| case d == signed && v.Args[0].Type.IsSigned(): | |
| fallthrough | |
| case d == unsigned && !v.Args[0].Type.IsSigned(): | |
| ft.update(parent, v.Args[0], w, d, r) | |
| } | |
| } | |
| if isCleanExt(w) { | |
| switch { | |
| case d == signed && w.Args[0].Type.IsSigned(): | |
| fallthrough | |
| case d == unsigned && !w.Args[0].Type.IsSigned(): | |
| ft.update(parent, v, w.Args[0], d, r) | |
| } | |
| } | |
| } | |
| var opMin = map[Op]int64{ | |
| OpAdd64: math.MinInt64, OpSub64: math.MinInt64, | |
| OpAdd32: math.MinInt32, OpSub32: math.MinInt32, | |
| } | |
| var opMax = map[Op]int64{ | |
| OpAdd64: math.MaxInt64, OpSub64: math.MaxInt64, | |
| OpAdd32: math.MaxInt32, OpSub32: math.MaxInt32, | |
| } | |
| var opUMax = map[Op]uint64{ | |
| OpAdd64: math.MaxUint64, OpSub64: math.MaxUint64, | |
| OpAdd32: math.MaxUint32, OpSub32: math.MaxUint32, | |
| } | |
| // isNonNegative reports whether v is known to be non-negative. | |
| func (ft *factsTable) isNonNegative(v *Value) bool { | |
| if isNonNegative(v) { | |
| return true | |
| } | |
| var max int64 | |
| switch v.Type.Size() { | |
| case 1: | |
| max = math.MaxInt8 | |
| case 2: | |
| max = math.MaxInt16 | |
| case 4: | |
| max = math.MaxInt32 | |
| case 8: | |
| max = math.MaxInt64 | |
| default: | |
| panic("unexpected integer size") | |
| } | |
| // Check if the recorded limits can prove that the value is positive | |
| if l, has := ft.limits[v.ID]; has && (l.min >= 0 || l.umax <= uint64(max)) { | |
| return true | |
| } | |
| // Check if v = x+delta, and we can use x's limits to prove that it's positive | |
| if x, delta := isConstDelta(v); x != nil { | |
| if l, has := ft.limits[x.ID]; has { | |
| if delta > 0 && l.min >= -delta && l.max <= max-delta { | |
| return true | |
| } | |
| if delta < 0 && l.min >= -delta { | |
| return true | |
| } | |
| } | |
| } | |
| // Check if v is a value-preserving extension of a non-negative value. | |
| if isCleanExt(v) && ft.isNonNegative(v.Args[0]) { | |
| return true | |
| } | |
| // Check if the signed poset can prove that the value is >= 0 | |
| return ft.orderS.OrderedOrEqual(ft.zero, v) | |
| } | |
| // checkpoint saves the current state of known relations. | |
| // Called when descending on a branch. | |
| func (ft *factsTable) checkpoint() { | |
| if ft.unsat { | |
| ft.unsatDepth++ | |
| } | |
| ft.stack = append(ft.stack, checkpointFact) | |
| ft.limitStack = append(ft.limitStack, checkpointBound) | |
| ft.orderS.Checkpoint() | |
| ft.orderU.Checkpoint() | |
| } | |
| // restore restores known relation to the state just | |
| // before the previous checkpoint. | |
| // Called when backing up on a branch. | |
| func (ft *factsTable) restore() { | |
| if ft.unsatDepth > 0 { | |
| ft.unsatDepth-- | |
| } else { | |
| ft.unsat = false | |
| } | |
| for { | |
| old := ft.stack[len(ft.stack)-1] | |
| ft.stack = ft.stack[:len(ft.stack)-1] | |
| if old == checkpointFact { | |
| break | |
| } | |
| if old.r == lt|eq|gt { | |
| delete(ft.facts, old.p) | |
| } else { | |
| ft.facts[old.p] = old.r | |
| } | |
| } | |
| for { | |
| old := ft.limitStack[len(ft.limitStack)-1] | |
| ft.limitStack = ft.limitStack[:len(ft.limitStack)-1] | |
| if old.vid == 0 { // checkpointBound | |
| break | |
| } | |
| if old.limit == noLimit { | |
| delete(ft.limits, old.vid) | |
| } else { | |
| ft.limits[old.vid] = old.limit | |
| } | |
| } | |
| ft.orderS.Undo() | |
| ft.orderU.Undo() | |
| } | |
| func lessByID(v, w *Value) bool { | |
| if v == nil && w == nil { | |
| // Should not happen, but just in case. | |
| return false | |
| } | |
| if v == nil { | |
| return true | |
| } | |
| return w != nil && v.ID < w.ID | |
| } | |
| var ( | |
| reverseBits = [...]relation{0, 4, 2, 6, 1, 5, 3, 7} | |
| // maps what we learn when the positive branch is taken. | |
| // For example: | |
| // OpLess8: {signed, lt}, | |
| // v1 = (OpLess8 v2 v3). | |
| // If v1 branch is taken then we learn that the rangeMask | |
| // can be at most lt. | |
| domainRelationTable = map[Op]struct { | |
| d domain | |
| r relation | |
| }{ | |
| OpEq8: {signed | unsigned, eq}, | |
| OpEq16: {signed | unsigned, eq}, | |
| OpEq32: {signed | unsigned, eq}, | |
| OpEq64: {signed | unsigned, eq}, | |
| OpEqPtr: {pointer, eq}, | |
| OpNeq8: {signed | unsigned, lt | gt}, | |
| OpNeq16: {signed | unsigned, lt | gt}, | |
| OpNeq32: {signed | unsigned, lt | gt}, | |
| OpNeq64: {signed | unsigned, lt | gt}, | |
| OpNeqPtr: {pointer, lt | gt}, | |
| OpLess8: {signed, lt}, | |
| OpLess8U: {unsigned, lt}, | |
| OpLess16: {signed, lt}, | |
| OpLess16U: {unsigned, lt}, | |
| OpLess32: {signed, lt}, | |
| OpLess32U: {unsigned, lt}, | |
| OpLess64: {signed, lt}, | |
| OpLess64U: {unsigned, lt}, | |
| OpLeq8: {signed, lt | eq}, | |
| OpLeq8U: {unsigned, lt | eq}, | |
| OpLeq16: {signed, lt | eq}, | |
| OpLeq16U: {unsigned, lt | eq}, | |
| OpLeq32: {signed, lt | eq}, | |
| OpLeq32U: {unsigned, lt | eq}, | |
| OpLeq64: {signed, lt | eq}, | |
| OpLeq64U: {unsigned, lt | eq}, | |
| // For these ops, the negative branch is different: we can only | |
| // prove signed/GE (signed/GT) if we can prove that arg0 is non-negative. | |
| // See the special case in addBranchRestrictions. | |
| OpIsInBounds: {signed | unsigned, lt}, // 0 <= arg0 < arg1 | |
| OpIsSliceInBounds: {signed | unsigned, lt | eq}, // 0 <= arg0 <= arg1 | |
| } | |
| ) | |
| // prove removes redundant BlockIf branches that can be inferred | |
| // from previous dominating comparisons. | |
| // | |
| // By far, the most common redundant pair are generated by bounds checking. | |
| // For example for the code: | |
| // | |
| // a[i] = 4 | |
| // foo(a[i]) | |
| // | |
| // The compiler will generate the following code: | |
| // | |
| // if i >= len(a) { | |
| // panic("not in bounds") | |
| // } | |
| // a[i] = 4 | |
| // if i >= len(a) { | |
| // panic("not in bounds") | |
| // } | |
| // foo(a[i]) | |
| // | |
| // The second comparison i >= len(a) is clearly redundant because if the | |
| // else branch of the first comparison is executed, we already know that i < len(a). | |
| // The code for the second panic can be removed. | |
| // | |
| // prove works by finding contradictions and trimming branches whose | |
| // conditions are unsatisfiable given the branches leading up to them. | |
| // It tracks a "fact table" of branch conditions. For each branching | |
| // block, it asserts the branch conditions that uniquely dominate that | |
| // block, and then separately asserts the block's branch condition and | |
| // its negation. If either leads to a contradiction, it can trim that | |
| // successor. | |
| func prove(f *Func) { | |
| ft := newFactsTable(f) | |
| ft.checkpoint() | |
| var lensVars map[*Block][]*Value | |
| // Find length and capacity ops. | |
| for _, b := range f.Blocks { | |
| for _, v := range b.Values { | |
| if v.Uses == 0 { | |
| // We don't care about dead values. | |
| // (There can be some that are CSEd but not removed yet.) | |
| continue | |
| } | |
| switch v.Op { | |
| case OpStringLen: | |
| ft.update(b, v, ft.zero, signed, gt|eq) | |
| case OpSliceLen: | |
| if ft.lens == nil { | |
| ft.lens = map[ID]*Value{} | |
| } | |
| ft.lens[v.Args[0].ID] = v | |
| ft.update(b, v, ft.zero, signed, gt|eq) | |
| if v.Args[0].Op == OpSliceMake { | |
| if lensVars == nil { | |
| lensVars = make(map[*Block][]*Value) | |
| } | |
| lensVars[b] = append(lensVars[b], v) | |
| } | |
| case OpSliceCap: | |
| if ft.caps == nil { | |
| ft.caps = map[ID]*Value{} | |
| } | |
| ft.caps[v.Args[0].ID] = v | |
| ft.update(b, v, ft.zero, signed, gt|eq) | |
| if v.Args[0].Op == OpSliceMake { | |
| if lensVars == nil { | |
| lensVars = make(map[*Block][]*Value) | |
| } | |
| lensVars[b] = append(lensVars[b], v) | |
| } | |
| } | |
| } | |
| } | |
| // Find induction variables. Currently, findIndVars | |
| // is limited to one induction variable per block. | |
| var indVars map[*Block]indVar | |
| for _, v := range findIndVar(f) { | |
| if indVars == nil { | |
| indVars = make(map[*Block]indVar) | |
| } | |
| indVars[v.entry] = v | |
| } | |
| // current node state | |
| type walkState int | |
| const ( | |
| descend walkState = iota | |
| simplify | |
| ) | |
| // work maintains the DFS stack. | |
| type bp struct { | |
| block *Block // current handled block | |
| state walkState // what's to do | |
| } | |
| work := make([]bp, 0, 256) | |
| work = append(work, bp{ | |
| block: f.Entry, | |
| state: descend, | |
| }) | |
| idom := f.Idom() | |
| sdom := f.Sdom() | |
| // DFS on the dominator tree. | |
| // | |
| // For efficiency, we consider only the dominator tree rather | |
| // than the entire flow graph. On the way down, we consider | |
| // incoming branches and accumulate conditions that uniquely | |
| // dominate the current block. If we discover a contradiction, | |
| // we can eliminate the entire block and all of its children. | |
| // On the way back up, we consider outgoing branches that | |
| // haven't already been considered. This way we consider each | |
| // branch condition only once. | |
| for len(work) > 0 { | |
| node := work[len(work)-1] | |
| work = work[:len(work)-1] | |
| parent := idom[node.block.ID] | |
| branch := getBranch(sdom, parent, node.block) | |
| switch node.state { | |
| case descend: | |
| ft.checkpoint() | |
| // Entering the block, add the block-depending facts that we collected | |
| // at the beginning: induction variables and lens/caps of slices. | |
| if iv, ok := indVars[node.block]; ok { | |
| addIndVarRestrictions(ft, parent, iv) | |
| } | |
| if lens, ok := lensVars[node.block]; ok { | |
| for _, v := range lens { | |
| switch v.Op { | |
| case OpSliceLen: | |
| ft.update(node.block, v, v.Args[0].Args[1], signed, eq) | |
| case OpSliceCap: | |
| ft.update(node.block, v, v.Args[0].Args[2], signed, eq) | |
| } | |
| } | |
| } | |
| if branch != unknown { | |
| addBranchRestrictions(ft, parent, branch) | |
| if ft.unsat { | |
| // node.block is unreachable. | |
| // Remove it and don't visit | |
| // its children. | |
| removeBranch(parent, branch) | |
| ft.restore() | |
| break | |
| } | |
| // Otherwise, we can now commit to | |
| // taking this branch. We'll restore | |
| // ft when we unwind. | |
| } | |
| // Add inductive facts for phis in this block. | |
| addLocalInductiveFacts(ft, node.block) | |
| work = append(work, bp{ | |
| block: node.block, | |
| state: simplify, | |
| }) | |
| for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) { | |
| work = append(work, bp{ | |
| block: s, | |
| state: descend, | |
| }) | |
| } | |
| case simplify: | |
| simplifyBlock(sdom, ft, node.block) | |
| ft.restore() | |
| } | |
| } | |
| ft.restore() | |
| // Return the posets to the free list | |
| for _, po := range []*poset{ft.orderS, ft.orderU} { | |
| // Make sure it's empty as it should be. A non-empty poset | |
| // might cause errors and miscompilations if reused. | |
| if checkEnabled { | |
| if err := po.CheckEmpty(); err != nil { | |
| f.Fatalf("prove poset not empty after function %s: %v", f.Name, err) | |
| } | |
| } | |
| f.retPoset(po) | |
| } | |
| } | |
| // getBranch returns the range restrictions added by p | |
| // when reaching b. p is the immediate dominator of b. | |
| func getBranch(sdom SparseTree, p *Block, b *Block) branch { | |
| if p == nil || p.Kind != BlockIf { | |
| return unknown | |
| } | |
| // If p and p.Succs[0] are dominators it means that every path | |
| // from entry to b passes through p and p.Succs[0]. We care that | |
| // no path from entry to b passes through p.Succs[1]. If p.Succs[0] | |
| // has one predecessor then (apart from the degenerate case), | |
| // there is no path from entry that can reach b through p.Succs[1]. | |
| // TODO: how about p->yes->b->yes, i.e. a loop in yes. | |
| if sdom.IsAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 { | |
| return positive | |
| } | |
| if sdom.IsAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 { | |
| return negative | |
| } | |
| return unknown | |
| } | |
| // addIndVarRestrictions updates the factsTables ft with the facts | |
| // learned from the induction variable indVar which drives the loop | |
| // starting in Block b. | |
| func addIndVarRestrictions(ft *factsTable, b *Block, iv indVar) { | |
| d := signed | |
| if ft.isNonNegative(iv.min) && ft.isNonNegative(iv.max) { | |
| d |= unsigned | |
| } | |
| if iv.flags&indVarMinExc == 0 { | |
| addRestrictions(b, ft, d, iv.min, iv.ind, lt|eq) | |
| } else { | |
| addRestrictions(b, ft, d, iv.min, iv.ind, lt) | |
| } | |
| if iv.flags&indVarMaxInc == 0 { | |
| addRestrictions(b, ft, d, iv.ind, iv.max, lt) | |
| } else { | |
| addRestrictions(b, ft, d, iv.ind, iv.max, lt|eq) | |
| } | |
| } | |
| // addBranchRestrictions updates the factsTables ft with the facts learned when | |
| // branching from Block b in direction br. | |
| func addBranchRestrictions(ft *factsTable, b *Block, br branch) { | |
| c := b.Controls[0] | |
| switch br { | |
| case negative: | |
| addRestrictions(b, ft, boolean, nil, c, eq) | |
| case positive: | |
| addRestrictions(b, ft, boolean, nil, c, lt|gt) | |
| default: | |
| panic("unknown branch") | |
| } | |
| if tr, has := domainRelationTable[c.Op]; has { | |
| // When we branched from parent we learned a new set of | |
| // restrictions. Update the factsTable accordingly. | |
| d := tr.d | |
| if d == signed && ft.isNonNegative(c.Args[0]) && ft.isNonNegative(c.Args[1]) { | |
| d |= unsigned | |
| } | |
| switch c.Op { | |
| case OpIsInBounds, OpIsSliceInBounds: | |
| // 0 <= a0 < a1 (or 0 <= a0 <= a1) | |
| // | |
| // On the positive branch, we learn: | |
| // signed: 0 <= a0 < a1 (or 0 <= a0 <= a1) | |
| // unsigned: a0 < a1 (or a0 <= a1) | |
| // | |
| // On the negative branch, we learn (0 > a0 || | |
| // a0 >= a1). In the unsigned domain, this is | |
| // simply a0 >= a1 (which is the reverse of the | |
| // positive branch, so nothing surprising). | |
| // But in the signed domain, we can't express the || | |
| // condition, so check if a0 is non-negative instead, | |
| // to be able to learn something. | |
| switch br { | |
| case negative: | |
| d = unsigned | |
| if ft.isNonNegative(c.Args[0]) { | |
| d |= signed | |
| } | |
| addRestrictions(b, ft, d, c.Args[0], c.Args[1], tr.r^(lt|gt|eq)) | |
| case positive: | |
| addRestrictions(b, ft, signed, ft.zero, c.Args[0], lt|eq) | |
| addRestrictions(b, ft, d, c.Args[0], c.Args[1], tr.r) | |
| } | |
| default: | |
| switch br { | |
| case negative: | |
| addRestrictions(b, ft, d, c.Args[0], c.Args[1], tr.r^(lt|gt|eq)) | |
| case positive: | |
| addRestrictions(b, ft, d, c.Args[0], c.Args[1], tr.r) | |
| } | |
| } | |
| } | |
| } | |
| // addRestrictions updates restrictions from the immediate | |
| // dominating block (p) using r. | |
| func addRestrictions(parent *Block, ft *factsTable, t domain, v, w *Value, r relation) { | |
| if t == 0 { | |
| // Trivial case: nothing to do. | |
| // Shoult not happen, but just in case. | |
| return | |
| } | |
| for i := domain(1); i <= t; i <<= 1 { | |
| if t&i == 0 { | |
| continue | |
| } | |
| ft.update(parent, v, w, i, r) | |
| } | |
| } | |
| // addLocalInductiveFacts adds inductive facts when visiting b, where | |
| // b is a join point in a loop. In contrast with findIndVar, this | |
| // depends on facts established for b, which is why it happens when | |
| // visiting b. addLocalInductiveFacts specifically targets the pattern | |
| // created by OFORUNTIL, which isn't detected by findIndVar. | |
| // | |
| // TODO: It would be nice to combine this with findIndVar. | |
| func addLocalInductiveFacts(ft *factsTable, b *Block) { | |
| // This looks for a specific pattern of induction: | |
| // | |
| // 1. i1 = OpPhi(min, i2) in b | |
| // 2. i2 = i1 + 1 | |
| // 3. i2 < max at exit from b.Preds[1] | |
| // 4. min < max | |
| // | |
| // If all of these conditions are true, then i1 < max and i1 >= min. | |
| // To ensure this is a loop header node. | |
| if len(b.Preds) != 2 { | |
| return | |
| } | |
| for _, i1 := range b.Values { | |
| if i1.Op != OpPhi { | |
| continue | |
| } | |
| // Check for conditions 1 and 2. This is easy to do | |
| // and will throw out most phis. | |
| min, i2 := i1.Args[0], i1.Args[1] | |
| if i1q, delta := isConstDelta(i2); i1q != i1 || delta != 1 { | |
| continue | |
| } | |
| // Try to prove condition 3. We can't just query the | |
| // fact table for this because we don't know what the | |
| // facts of b.Preds[1] are (in general, b.Preds[1] is | |
| // a loop-back edge, so we haven't even been there | |
| // yet). As a conservative approximation, we look for | |
| // this condition in the predecessor chain until we | |
| // hit a join point. | |
| uniquePred := func(b *Block) *Block { | |
| if len(b.Preds) == 1 { | |
| return b.Preds[0].b | |
| } | |
| return nil | |
| } | |
| pred, child := b.Preds[1].b, b | |
| for ; pred != nil; pred = uniquePred(pred) { | |
| if pred.Kind != BlockIf { | |
| continue | |
| } | |
| control := pred.Controls[0] | |
| br := unknown | |
| if pred.Succs[0].b == child { | |
| br = positive | |
| } | |
| if pred.Succs[1].b == child { | |
| if br != unknown { | |
| continue | |
| } | |
| br = negative | |
| } | |
| if br == unknown { | |
| continue | |
| } | |
| tr, has := domainRelationTable[control.Op] | |
| if !has { | |
| continue | |
| } | |
| r := tr.r | |
| if br == negative { | |
| // Negative branch taken to reach b. | |
| // Complement the relations. | |
| r = (lt | eq | gt) ^ r | |
| } | |
| // Check for i2 < max or max > i2. | |
| var max *Value | |
| if r == lt && control.Args[0] == i2 { | |
| max = control.Args[1] | |
| } else if r == gt && control.Args[1] == i2 { | |
| max = control.Args[0] | |
| } else { | |
| continue | |
| } | |
| // Check condition 4 now that we have a | |
| // candidate max. For this we can query the | |
| // fact table. We "prove" min < max by showing | |
| // that min >= max is unsat. (This may simply | |
| // compare two constants; that's fine.) | |
| ft.checkpoint() | |
| ft.update(b, min, max, tr.d, gt|eq) | |
| proved := ft.unsat | |
| ft.restore() | |
| if proved { | |
| // We know that min <= i1 < max. | |
| if b.Func.pass.debug > 0 { | |
| printIndVar(b, i1, min, max, 1, 0) | |
| } | |
| ft.update(b, min, i1, tr.d, lt|eq) | |
| ft.update(b, i1, max, tr.d, lt) | |
| } | |
| } | |
| } | |
| } | |
| var ctzNonZeroOp = map[Op]Op{OpCtz8: OpCtz8NonZero, OpCtz16: OpCtz16NonZero, OpCtz32: OpCtz32NonZero, OpCtz64: OpCtz64NonZero} | |
| var mostNegativeDividend = map[Op]int64{ | |
| OpDiv16: -1 << 15, | |
| OpMod16: -1 << 15, | |
| OpDiv32: -1 << 31, | |
| OpMod32: -1 << 31, | |
| OpDiv64: -1 << 63, | |
| OpMod64: -1 << 63} | |
| // simplifyBlock simplifies some constant values in b and evaluates | |
| // branches to non-uniquely dominated successors of b. | |
| func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) { | |
| for _, v := range b.Values { | |
| switch v.Op { | |
| case OpSlicemask: | |
| // Replace OpSlicemask operations in b with constants where possible. | |
| x, delta := isConstDelta(v.Args[0]) | |
| if x == nil { | |
| continue | |
| } | |
| // slicemask(x + y) | |
| // if x is larger than -y (y is negative), then slicemask is -1. | |
| lim, ok := ft.limits[x.ID] | |
| if !ok { | |
| continue | |
| } | |
| if lim.umin > uint64(-delta) { | |
| if v.Args[0].Op == OpAdd64 { | |
| v.reset(OpConst64) | |
| } else { | |
| v.reset(OpConst32) | |
| } | |
| if b.Func.pass.debug > 0 { | |
| b.Func.Warnl(v.Pos, "Proved slicemask not needed") | |
| } | |
| v.AuxInt = -1 | |
| } | |
| case OpCtz8, OpCtz16, OpCtz32, OpCtz64: | |
| // On some architectures, notably amd64, we can generate much better | |
| // code for CtzNN if we know that the argument is non-zero. | |
| // Capture that information here for use in arch-specific optimizations. | |
| x := v.Args[0] | |
| lim, ok := ft.limits[x.ID] | |
| if !ok { | |
| continue | |
| } | |
| if lim.umin > 0 || lim.min > 0 || lim.max < 0 { | |
| if b.Func.pass.debug > 0 { | |
| b.Func.Warnl(v.Pos, "Proved %v non-zero", v.Op) | |
| } | |
| v.Op = ctzNonZeroOp[v.Op] | |
| } | |
| case OpRsh8x8, OpRsh8x16, OpRsh8x32, OpRsh8x64, | |
| OpRsh16x8, OpRsh16x16, OpRsh16x32, OpRsh16x64, | |
| OpRsh32x8, OpRsh32x16, OpRsh32x32, OpRsh32x64, | |
| OpRsh64x8, OpRsh64x16, OpRsh64x32, OpRsh64x64: | |
| // Check whether, for a >> b, we know that a is non-negative | |
| // and b is all of a's bits except the MSB. If so, a is shifted to zero. | |
| bits := 8 * v.Type.Size() | |
| if v.Args[1].isGenericIntConst() && v.Args[1].AuxInt >= bits-1 && ft.isNonNegative(v.Args[0]) { | |
| if b.Func.pass.debug > 0 { | |
| b.Func.Warnl(v.Pos, "Proved %v shifts to zero", v.Op) | |
| } | |
| switch bits { | |
| case 64: | |
| v.reset(OpConst64) | |
| case 32: | |
| v.reset(OpConst32) | |
| case 16: | |
| v.reset(OpConst16) | |
| case 8: | |
| v.reset(OpConst8) | |
| default: | |
| panic("unexpected integer size") | |
| } | |
| v.AuxInt = 0 | |
| continue // Be sure not to fallthrough - this is no longer OpRsh. | |
| } | |
| // If the Rsh hasn't been replaced with 0, still check if it is bounded. | |
| fallthrough | |
| case OpLsh8x8, OpLsh8x16, OpLsh8x32, OpLsh8x64, | |
| OpLsh16x8, OpLsh16x16, OpLsh16x32, OpLsh16x64, | |
| OpLsh32x8, OpLsh32x16, OpLsh32x32, OpLsh32x64, | |
| OpLsh64x8, OpLsh64x16, OpLsh64x32, OpLsh64x64, | |
| OpRsh8Ux8, OpRsh8Ux16, OpRsh8Ux32, OpRsh8Ux64, | |
| OpRsh16Ux8, OpRsh16Ux16, OpRsh16Ux32, OpRsh16Ux64, | |
| OpRsh32Ux8, OpRsh32Ux16, OpRsh32Ux32, OpRsh32Ux64, | |
| OpRsh64Ux8, OpRsh64Ux16, OpRsh64Ux32, OpRsh64Ux64: | |
| // Check whether, for a << b, we know that b | |
| // is strictly less than the number of bits in a. | |
| by := v.Args[1] | |
| lim, ok := ft.limits[by.ID] | |
| if !ok { | |
| continue | |
| } | |
| bits := 8 * v.Args[0].Type.Size() | |
| if lim.umax < uint64(bits) || (lim.max < bits && ft.isNonNegative(by)) { | |
| v.AuxInt = 1 // see shiftIsBounded | |
| if b.Func.pass.debug > 0 { | |
| b.Func.Warnl(v.Pos, "Proved %v bounded", v.Op) | |
| } | |
| } | |
| case OpDiv16, OpDiv32, OpDiv64, OpMod16, OpMod32, OpMod64: | |
| // On amd64 and 386 fix-up code can be avoided if we know | |
| // the divisor is not -1 or the dividend > MinIntNN. | |
| // Don't modify AuxInt on other architectures, | |
| // as that can interfere with CSE. | |
| // TODO: add other architectures? | |
| if b.Func.Config.arch != "386" && b.Func.Config.arch != "amd64" { | |
| break | |
| } | |
| divr := v.Args[1] | |
| divrLim, divrLimok := ft.limits[divr.ID] | |
| divd := v.Args[0] | |
| divdLim, divdLimok := ft.limits[divd.ID] | |
| if (divrLimok && (divrLim.max < -1 || divrLim.min > -1)) || | |
| (divdLimok && divdLim.min > mostNegativeDividend[v.Op]) { | |
| // See DivisionNeedsFixUp in rewrite.go. | |
| // v.AuxInt = 1 means we have proved both that the divisor is not -1 | |
| // and that the dividend is not the most negative integer, | |
| // so we do not need to add fix-up code. | |
| v.AuxInt = 1 | |
| if b.Func.pass.debug > 0 { | |
| b.Func.Warnl(v.Pos, "Proved %v does not need fix-up", v.Op) | |
| } | |
| } | |
| } | |
| } | |
| if b.Kind != BlockIf { | |
| return | |
| } | |
| // Consider outgoing edges from this block. | |
| parent := b | |
| for i, branch := range [...]branch{positive, negative} { | |
| child := parent.Succs[i].b | |
| if getBranch(sdom, parent, child) != unknown { | |
| // For edges to uniquely dominated blocks, we | |
| // already did this when we visited the child. | |
| continue | |
| } | |
| // For edges to other blocks, this can trim a branch | |
| // even if we couldn't get rid of the child itself. | |
| ft.checkpoint() | |
| addBranchRestrictions(ft, parent, branch) | |
| unsat := ft.unsat | |
| ft.restore() | |
| if unsat { | |
| // This branch is impossible, so remove it | |
| // from the block. | |
| removeBranch(parent, branch) | |
| // No point in considering the other branch. | |
| // (It *is* possible for both to be | |
| // unsatisfiable since the fact table is | |
| // incomplete. We could turn this into a | |
| // BlockExit, but it doesn't seem worth it.) | |
| break | |
| } | |
| } | |
| } | |
| func removeBranch(b *Block, branch branch) { | |
| c := b.Controls[0] | |
| if b.Func.pass.debug > 0 { | |
| verb := "Proved" | |
| if branch == positive { | |
| verb = "Disproved" | |
| } | |
| if b.Func.pass.debug > 1 { | |
| b.Func.Warnl(b.Pos, "%s %s (%s)", verb, c.Op, c) | |
| } else { | |
| b.Func.Warnl(b.Pos, "%s %s", verb, c.Op) | |
| } | |
| } | |
| if c != nil && c.Pos.IsStmt() == src.PosIsStmt && c.Pos.SameFileAndLine(b.Pos) { | |
| // attempt to preserve statement marker. | |
| b.Pos = b.Pos.WithIsStmt() | |
| } | |
| b.Kind = BlockFirst | |
| b.ResetControls() | |
| if branch == positive { | |
| b.swapSuccessors() | |
| } | |
| } | |
| // isNonNegative reports whether v is known to be greater or equal to zero. | |
| func isNonNegative(v *Value) bool { | |
| if !v.Type.IsInteger() { | |
| v.Fatalf("isNonNegative bad type: %v", v.Type) | |
| } | |
| // TODO: return true if !v.Type.IsSigned() | |
| // SSA isn't type-safe enough to do that now (issue 37753). | |
| // The checks below depend only on the pattern of bits. | |
| switch v.Op { | |
| case OpConst64: | |
| return v.AuxInt >= 0 | |
| case OpConst32: | |
| return int32(v.AuxInt) >= 0 | |
| case OpConst16: | |
| return int16(v.AuxInt) >= 0 | |
| case OpConst8: | |
| return int8(v.AuxInt) >= 0 | |
| case OpStringLen, OpSliceLen, OpSliceCap, | |
| OpZeroExt8to64, OpZeroExt16to64, OpZeroExt32to64, | |
| OpZeroExt8to32, OpZeroExt16to32, OpZeroExt8to16, | |
| OpCtz64, OpCtz32, OpCtz16, OpCtz8: | |
| return true | |
| case OpRsh64Ux64, OpRsh32Ux64: | |
| by := v.Args[1] | |
| return by.Op == OpConst64 && by.AuxInt > 0 | |
| case OpRsh64x64, OpRsh32x64, OpRsh8x64, OpRsh16x64, OpRsh32x32, OpRsh64x32, | |
| OpSignExt32to64, OpSignExt16to64, OpSignExt8to64, OpSignExt16to32, OpSignExt8to32: | |
| return isNonNegative(v.Args[0]) | |
| case OpAnd64, OpAnd32, OpAnd16, OpAnd8: | |
| return isNonNegative(v.Args[0]) || isNonNegative(v.Args[1]) | |
| case OpMod64, OpMod32, OpMod16, OpMod8, | |
| OpDiv64, OpDiv32, OpDiv16, OpDiv8, | |
| OpOr64, OpOr32, OpOr16, OpOr8, | |
| OpXor64, OpXor32, OpXor16, OpXor8: | |
| return isNonNegative(v.Args[0]) && isNonNegative(v.Args[1]) | |
| // We could handle OpPhi here, but the improvements from doing | |
| // so are very minor, and it is neither simple nor cheap. | |
| } | |
| return false | |
| } | |
| // isConstDelta returns non-nil if v is equivalent to w+delta (signed). | |
| func isConstDelta(v *Value) (w *Value, delta int64) { | |
| cop := OpConst64 | |
| switch v.Op { | |
| case OpAdd32, OpSub32: | |
| cop = OpConst32 | |
| } | |
| switch v.Op { | |
| case OpAdd64, OpAdd32: | |
| if v.Args[0].Op == cop { | |
| return v.Args[1], v.Args[0].AuxInt | |
| } | |
| if v.Args[1].Op == cop { | |
| return v.Args[0], v.Args[1].AuxInt | |
| } | |
| case OpSub64, OpSub32: | |
| if v.Args[1].Op == cop { | |
| aux := v.Args[1].AuxInt | |
| if aux != -aux { // Overflow; too bad | |
| return v.Args[0], -aux | |
| } | |
| } | |
| } | |
| return nil, 0 | |
| } | |
| // isCleanExt reports whether v is the result of a value-preserving | |
| // sign or zero extension | |
| func isCleanExt(v *Value) bool { | |
| switch v.Op { | |
| case OpSignExt8to16, OpSignExt8to32, OpSignExt8to64, | |
| OpSignExt16to32, OpSignExt16to64, OpSignExt32to64: | |
| // signed -> signed is the only value-preserving sign extension | |
| return v.Args[0].Type.IsSigned() && v.Type.IsSigned() | |
| case OpZeroExt8to16, OpZeroExt8to32, OpZeroExt8to64, | |
| OpZeroExt16to32, OpZeroExt16to64, OpZeroExt32to64: | |
| // unsigned -> signed/unsigned are value-preserving zero extensions | |
| return !v.Args[0].Type.IsSigned() | |
| } | |
| return false | |
| } |