src/cmd/compile/internal/ssacompile/prove.go GO 2,718 lines View on github.com → Search inside
File is large — showing lines 1–2,000 of 2,718.
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 ssacompile67import (8	"fmt"9	"math"10	"math/bits"11	"strings"1213	"cmd/compile/internal/ssa"14	"cmd/compile/internal/ssa/block"15	"cmd/compile/internal/ssa/ssaop"16	"cmd/compile/internal/types"17	"cmd/internal/src"18)1920type branch int2122const (23	unknown branch = iota24	positive25	negative26	// The outedges from a jump table are jumpTable0,27	// jumpTable0+1, jumpTable0+2, etc. There could be an28	// arbitrary number so we can't list them all here.29	jumpTable030)3132func (b branch) String() string {33	switch b {34	case unknown:35		return "unk"36	case positive:37		return "pos"38	case negative:39		return "neg"40	default:41		return fmt.Sprintf("jmp%d", b-jumpTable0)42	}43}4445// relation represents the set of possible relations between46// pairs of variables (v, w). Without a priori knowledge the47// mask is lt | eq | gt meaning v can be less than, equal to or48// greater than w. When the execution path branches on the condition49// `v op w` the set of relations is updated to exclude any50// relation not possible due to `v op w` being true (or false).51//52// E.g.53//54//	r := relation(...)55//56//	if v < w {57//	  newR := r & lt58//	}59//	if v >= w {60//	  newR := r & (eq|gt)61//	}62//	if v != w {63//	  newR := r & (lt|gt)64//	}65type relation uint6667const (68	lt relation = 1 << iota69	eq70	gt71)7273var relationStrings = [...]string{74	0: "none", lt: "<", eq: "==", lt | eq: "<=",75	gt: ">", gt | lt: "!=", gt | eq: ">=", gt | eq | lt: "any",76}7778func (r relation) String() string {79	if r < relation(len(relationStrings)) {80		return relationStrings[r]81	}82	return fmt.Sprintf("relation(%d)", uint(r))83}8485// domain represents the domain of a variable pair in which a set86// of relations is known. For example, relations learned for unsigned87// pairs cannot be transferred to signed pairs because the same bit88// representation can mean something else.89type domain uint9091const (92	signed domain = 1 << iota93	unsigned94	pointer95	boolean96)9798var domainStrings = [...]string{99	"signed", "unsigned", "pointer", "boolean",100}101102func (d domain) String() string {103	s := ""104	for i, ds := range domainStrings {105		if d&(1<<uint(i)) != 0 {106			if len(s) != 0 {107				s += "|"108			}109			s += ds110			d &^= 1 << uint(i)111		}112	}113	if d != 0 {114		if len(s) != 0 {115			s += "|"116		}117		s += fmt.Sprintf("0x%x", uint(d))118	}119	return s120}121122// a limitFact is a limit known for a particular value.123type limitFact struct {124	vid   ssa.ID125	limit ssa.Limit126}127128// An ordering encodes facts like v < w.129type ordering struct {130	next *ordering // linked list of all known orderings for v.131	// Note: v is implicit here, determined by which linked list it is in.132	w *ssa.Value133	d domain134	r relation // one of ==,!=,<,<=,>,>=135	// if d is boolean or pointer, r can only be ==, !=136}137138// factsTable keeps track of relations between pairs of values.139//140// The fact table logic is sound, but incomplete. Outside of a few141// special cases, it performs no deduction or arithmetic. While there142// are known decision procedures for this, the ad hoc approach taken143// by the facts table is effective for real code while remaining very144// efficient.145type factsTable struct {146	// unsat is true if facts contains a contradiction.147	//148	// Note that the factsTable logic is incomplete, so if unsat149	// is false, the assertions in factsTable could be satisfiable150	// *or* unsatisfiable.151	unsat      bool // true if facts contains a contradiction152	unsatDepth int  // number of unsat checkpoints153154	// order* is a couple of partial order sets that record information155	// about relations between SSA values in the signed and unsigned156	// domain.157	orderS *ssa.Poset158	orderU *ssa.Poset159160	// orderings contains a list of known orderings between values.161	// These lists are indexed by v.ID.162	// We do not record transitive orderings. Only explicitly learned163	// orderings are recorded. Transitive orderings can be obtained164	// by walking along the individual orderings.165	orderings map[ssa.ID]*ordering166	// stack of IDs which have had an entry added in orderings.167	// In addition, ID==0 are checkpoint markers.168	orderingsStack []ssa.ID169	orderingCache  *ordering // unused ordering records170171	// known lower and upper constant bounds on individual values.172	limits       []ssa.Limit // indexed by value ID173	limitStack   []limitFact // previous entries174	recurseCheck []bool      // recursion detector for limit propagation175176	// For each slice s, a map from s to a len(s)/cap(s) value (if any)177	// TODO: check if there are cases that matter where we have178	// more than one len(s) for a slice. We could keep a list if necessary.179	lens map[ssa.ID]*ssa.Value180	caps map[ssa.ID]*ssa.Value181182	// reusedTopoSortIDsToBlockIndexes recycle allocations for topo-sort183	reusedTopoSortIDsToBlockIndexes []uint184}185186// checkpointBound is an invalid value used for checkpointing187// and restoring factsTable.188var checkpointBound = limitFact{}189190func newFactsTable(f *ssa.Func) *factsTable {191	ft := &factsTable{}192	ft.orderS = f.NewPoset()193	ft.orderU = f.NewPoset()194	ft.orderings = make(map[ssa.ID]*ordering)195	ft.limits = f.Cache.AllocLimitSlice(f.NumValues())196	for _, b := range f.Blocks {197		for _, v := range b.Values {198			ft.limits[v.ID] = ssa.InitLimit(v)199		}200	}201	ft.limitStack = make([]limitFact, 4)202	ft.recurseCheck = f.Cache.AllocBoolSlice(f.NumValues())203	return ft204}205206// initLimitForNewValue initializes the limits for newly created values,207// possibly needing to expand the limits slice. Currently used by208// simplifyBlock when certain provably constant results are folded.209func (ft *factsTable) initLimitForNewValue(v *ssa.Value) {210	if int(v.ID) >= len(ft.limits) {211		f := v.Block.Func212		n := f.NumValues()213		if cap(ft.limits) >= n {214			ft.limits = ft.limits[:n]215		} else {216			old := ft.limits217			ft.limits = f.Cache.AllocLimitSlice(n)218			copy(ft.limits, old)219			f.Cache.FreeLimitSlice(old)220		}221	}222	ft.limits[v.ID] = ssa.InitLimit(v)223}224225// signedMin records the fact that we know v is at least226// min in the signed domain.227func (ft *factsTable) signedMin(v *ssa.Value, min int64) {228	ft.newLimit(v, ssa.Limit{Min: min, Max: math.MaxInt64, Umin: 0, Umax: math.MaxUint64})229}230231// signedMax records the fact that we know v is at most232// max in the signed domain.233func (ft *factsTable) signedMax(v *ssa.Value, max int64) {234	ft.newLimit(v, ssa.Limit{Min: math.MinInt64, Max: max, Umin: 0, Umax: math.MaxUint64})235}236func (ft *factsTable) signedMinMax(v *ssa.Value, min, max int64) {237	ft.newLimit(v, ssa.Limit{Min: min, Max: max, Umin: 0, Umax: math.MaxUint64})238}239240// setNonNegative records the fact that v is known to be non-negative.241func (ft *factsTable) setNonNegative(v *ssa.Value) {242	ft.signedMin(v, 0)243}244245// unsignedMin records the fact that we know v is at least246// min in the unsigned domain.247func (ft *factsTable) unsignedMin(v *ssa.Value, min uint64) {248	ft.newLimit(v, ssa.Limit{Min: math.MinInt64, Max: math.MaxInt64, Umin: min, Umax: math.MaxUint64})249}250251// unsignedMax records the fact that we know v is at most252// max in the unsigned domain.253func (ft *factsTable) unsignedMax(v *ssa.Value, max uint64) {254	ft.newLimit(v, ssa.Limit{Min: math.MinInt64, Max: math.MaxInt64, Umin: 0, Umax: max})255}256func (ft *factsTable) unsignedMinMax(v *ssa.Value, min, max uint64) {257	ft.newLimit(v, ssa.Limit{Min: math.MinInt64, Max: math.MaxInt64, Umin: min, Umax: max})258}259260func (ft *factsTable) booleanFalse(v *ssa.Value) {261	ft.newLimit(v, ssa.Limit{Min: 0, Max: 0, Umin: 0, Umax: 0})262}263func (ft *factsTable) booleanTrue(v *ssa.Value) {264	ft.newLimit(v, ssa.Limit{Min: 1, Max: 1, Umin: 1, Umax: 1})265}266func (ft *factsTable) pointerNil(v *ssa.Value) {267	ft.newLimit(v, ssa.Limit{Min: 0, Max: 0, Umin: 0, Umax: 0})268}269func (ft *factsTable) pointerNonNil(v *ssa.Value) {270	l := ssa.NoLimit()271	l.Umin = 1272	ft.newLimit(v, l)273}274275// newLimit adds new limiting information for v.276func (ft *factsTable) newLimit(v *ssa.Value, newLim ssa.Limit) {277	oldLim := ft.limits[v.ID]278279	// Merge old and new information.280	lim := oldLim.Intersect(newLim)281282	// signed <-> unsigned propagation283	if lim.Min >= 0 {284		lim = lim.UnsignedMinMax(uint64(lim.Min), uint64(lim.Max))285	}286	if ssa.FitsInBitsU(lim.Umax, uint(8*v.Type.Size()-1)) {287		lim = lim.SignedMinMax(int64(lim.Umin), int64(lim.Umax))288	}289290	if lim == oldLim {291		return // nothing new to record292	}293294	if lim.Unsat() {295		ft.unsat = true296		return297	}298299	// Check for recursion. This normally happens because in unsatisfiable300	// cases we have a < b < a, and every update to a's limits returns301	// here again with the limit increased by 2.302	// Normally this is caught early by the orderS/orderU posets, but in303	// cases where the comparisons jump between signed and unsigned domains,304	// the posets will not notice.305	if ft.recurseCheck[v.ID] {306		// This should only happen for unsatisfiable cases. TODO: check307		return308	}309	ft.recurseCheck[v.ID] = true310	defer func() {311		ft.recurseCheck[v.ID] = false312	}()313314	// Record undo information.315	ft.limitStack = append(ft.limitStack, limitFact{v.ID, oldLim})316	// Record new information.317	ft.limits[v.ID] = lim318	if v.Block.Func.Pass.Debug > 2 {319		// TODO: pos is probably wrong. This is the position where v is defined,320		// not the position where we learned the fact about it (which was321		// probably some subsequent compare+branch).322		v.Block.Func.Warnl(v.Pos, "new limit %s %s unsat=%v", v, lim.String(), ft.unsat)323	}324325	// Propagate this new constant range to other values326	// that we know are ordered with respect to this one.327	// Note overflow/underflow in the arithmetic below is ok,328	// it will just lead to imprecision (undetected unsatisfiability).329	for o := ft.orderings[v.ID]; o != nil; o = o.next {330		switch o.d {331		case signed:332			switch o.r {333			case eq: // v == w334				ft.signedMinMax(o.w, lim.Min, lim.Max)335			case lt | eq: // v <= w336				ft.signedMin(o.w, lim.Min)337			case lt: // v < w338				ft.signedMin(o.w, lim.Min+1)339			case gt | eq: // v >= w340				ft.signedMax(o.w, lim.Max)341			case gt: // v > w342				ft.signedMax(o.w, lim.Max-1)343			case lt | gt: // v != w344				if lim.Min == lim.Max { // v is a constant345					c := lim.Min346					if ft.limits[o.w.ID].Min == c {347						ft.signedMin(o.w, c+1)348					}349					if ft.limits[o.w.ID].Max == c {350						ft.signedMax(o.w, c-1)351					}352				}353			}354		case unsigned:355			switch o.r {356			case eq: // v == w357				ft.unsignedMinMax(o.w, lim.Umin, lim.Umax)358			case lt | eq: // v <= w359				ft.unsignedMin(o.w, lim.Umin)360			case lt: // v < w361				ft.unsignedMin(o.w, lim.Umin+1)362			case gt | eq: // v >= w363				ft.unsignedMax(o.w, lim.Umax)364			case gt: // v > w365				ft.unsignedMax(o.w, lim.Umax-1)366			case lt | gt: // v != w367				if lim.Umin == lim.Umax { // v is a constant368					c := lim.Umin369					if ft.limits[o.w.ID].Umin == c {370						ft.unsignedMin(o.w, c+1)371					}372					if ft.limits[o.w.ID].Umax == c {373						ft.unsignedMax(o.w, c-1)374					}375				}376			}377		case boolean:378			switch o.r {379			case eq:380				if lim.Min == 0 && lim.Max == 0 { // constant false381					ft.booleanFalse(o.w)382				}383				if lim.Min == 1 && lim.Max == 1 { // constant true384					ft.booleanTrue(o.w)385				}386			case lt | gt:387				if lim.Min == 0 && lim.Max == 0 { // constant false388					ft.booleanTrue(o.w)389				}390				if lim.Min == 1 && lim.Max == 1 { // constant true391					ft.booleanFalse(o.w)392				}393			}394		case pointer:395			switch o.r {396			case eq:397				if lim.Umax == 0 { // nil398					ft.pointerNil(o.w)399				}400				if lim.Umin > 0 { // non-nil401					ft.pointerNonNil(o.w)402				}403			case lt | gt:404				if lim.Umax == 0 { // nil405					ft.pointerNonNil(o.w)406				}407				// note: not equal to non-nil doesn't tell us anything.408			}409		}410	}411412	// If this is new known constant for a boolean value,413	// extract relation between its args. For example, if414	// We learn v is false, and v is defined as a<b, then we learn a>=b.415	if v.Type.IsBoolean() {416		// If we reach here, it is because we have a more restrictive417		// value for v than the default. The only two such values418		// are constant true or constant false.419		if lim.Min != lim.Max {420			v.Block.Func.Fatalf("boolean not constant %v", v)421		}422		isTrue := lim.Min == 1423		if dr, ok := domainRelationTable[v.Op]; ok && v.Op != ssaop.OpIsInBounds && v.Op != ssaop.OpIsSliceInBounds {424			d := dr.d425			r := dr.r426			if d == signed && ft.isNonNegative(v.Args[0]) && ft.isNonNegative(v.Args[1]) {427				d |= unsigned428			}429			if !isTrue {430				r ^= lt | gt | eq431			}432			// TODO: v.Block is wrong?433			addRestrictions(v.Block, ft, d, v.Args[0], v.Args[1], r)434		}435		switch v.Op {436		case ssaop.OpIsNonNil:437			if isTrue {438				ft.pointerNonNil(v.Args[0])439			} else {440				ft.pointerNil(v.Args[0])441			}442		case ssaop.OpIsInBounds, ssaop.OpIsSliceInBounds:443			// 0 <= a0 < a1 (or 0 <= a0 <= a1)444			r := lt445			if v.Op == ssaop.OpIsSliceInBounds {446				r |= eq447			}448			if isTrue {449				// On the positive branch, we learn:450				//   signed: 0 <= a0 < a1 (or 0 <= a0 <= a1)451				//   unsigned:    a0 < a1 (or a0 <= a1)452				ft.setNonNegative(v.Args[0])453				ft.update(v.Block, v.Args[0], v.Args[1], signed, r)454				ft.update(v.Block, v.Args[0], v.Args[1], unsigned, r)455			} else {456				// On the negative branch, we learn (0 > a0 ||457				// a0 >= a1). In the unsigned domain, this is458				// simply a0 >= a1 (which is the reverse of the459				// positive branch, so nothing surprising).460				// But in the signed domain, we can't express the ||461				// condition, so check if a0 is non-negative instead,462				// to be able to learn something.463				r ^= lt | gt | eq // >= (index) or > (slice)464				if ft.isNonNegative(v.Args[0]) {465					ft.update(v.Block, v.Args[0], v.Args[1], signed, r)466				}467				ft.update(v.Block, v.Args[0], v.Args[1], unsigned, r)468				// TODO: v.Block is wrong here469			}470		}471	}472}473474func (ft *factsTable) addOrdering(v, w *ssa.Value, d domain, r relation) {475	o := ft.orderingCache476	if o == nil {477		o = &ordering{}478	} else {479		ft.orderingCache = o.next480	}481	o.w = w482	o.d = d483	o.r = r484	o.next = ft.orderings[v.ID]485	ft.orderings[v.ID] = o486	ft.orderingsStack = append(ft.orderingsStack, v.ID)487}488489// update updates the set of relations between v and w in domain d490// restricting it to r.491func (ft *factsTable) update(parent *ssa.Block, v, w *ssa.Value, d domain, r relation) {492	if parent.Func.Pass.Debug > 2 {493		parent.Func.Warnl(parent.Pos, "parent=%s, update %s %s %s", parent, v, w, r)494	}495	// No need to do anything else if we already found unsat.496	if ft.unsat {497		return498	}499500	// Self-fact. It's wasteful to register it into the facts501	// table, so just note whether it's satisfiable502	if v == w {503		if r&eq == 0 {504			ft.unsat = true505		}506		return507	}508509	if d == signed || d == unsigned {510		var ok bool511		order := ft.orderS512		if d == unsigned {513			order = ft.orderU514		}515		switch r {516		case lt:517			ok = order.SetOrder(v, w)518		case gt:519			ok = order.SetOrder(w, v)520		case lt | eq:521			ok = order.SetOrderOrEqual(v, w)522		case gt | eq:523			ok = order.SetOrderOrEqual(w, v)524		case eq:525			ok = order.SetEqual(v, w)526		case lt | gt:527			ok = order.SetNonEqual(v, w)528		default:529			panic("unknown relation")530		}531		ft.addOrdering(v, w, d, r)532		ft.addOrdering(w, v, d, reverseBits[r])533534		if !ok {535			if parent.Func.Pass.Debug > 2 {536				parent.Func.Warnl(parent.Pos, "unsat %s %s %s", v, w, r)537			}538			ft.unsat = true539			return540		}541	}542	if d == boolean || d == pointer {543		for o := ft.orderings[v.ID]; o != nil; o = o.next {544			if o.d == d && o.w == w {545				// We already know a relationship between v and w.546				// Either it is a duplicate, or it is a contradiction,547				// as we only allow eq and lt|gt for these domains,548				if o.r != r {549					ft.unsat = true550				}551				return552			}553		}554		// TODO: this does not do transitive equality.555		// We could use a poset like above, but somewhat degenerate (==,!= only).556		ft.addOrdering(v, w, d, r)557		ft.addOrdering(w, v, d, r) // note: reverseBits unnecessary for eq and lt|gt.558	}559560	// Extract new constant limits based on the comparison.561	vLimit := ft.limits[v.ID]562	wLimit := ft.limits[w.ID]563	// Note: all the +1/-1 below could overflow/underflow. Either will564	// still generate correct results, it will just lead to imprecision.565	// In fact if there is overflow/underflow, the corresponding566	// code is unreachable because the known range is outside the range567	// of the value's type.568	switch d {569	case signed:570		switch r {571		case eq: // v == w572			ft.signedMinMax(v, wLimit.Min, wLimit.Max)573			ft.signedMinMax(w, vLimit.Min, vLimit.Max)574		case lt: // v < w575			ft.signedMax(v, wLimit.Max-1)576			ft.signedMin(w, vLimit.Min+1)577		case lt | eq: // v <= w578			ft.signedMax(v, wLimit.Max)579			ft.signedMin(w, vLimit.Min)580		case gt: // v > w581			ft.signedMin(v, wLimit.Min+1)582			ft.signedMax(w, vLimit.Max-1)583		case gt | eq: // v >= w584			ft.signedMin(v, wLimit.Min)585			ft.signedMax(w, vLimit.Max)586		case lt | gt: // v != w587			if vLimit.Min == vLimit.Max { // v is a constant588				c := vLimit.Min589				if wLimit.Min == c {590					ft.signedMin(w, c+1)591				}592				if wLimit.Max == c {593					ft.signedMax(w, c-1)594				}595			}596			if wLimit.Min == wLimit.Max { // w is a constant597				c := wLimit.Min598				if vLimit.Min == c {599					ft.signedMin(v, c+1)600				}601				if vLimit.Max == c {602					ft.signedMax(v, c-1)603				}604			}605		}606	case unsigned:607		switch r {608		case eq: // v == w609			ft.unsignedMinMax(v, wLimit.Umin, wLimit.Umax)610			ft.unsignedMinMax(w, vLimit.Umin, vLimit.Umax)611		case lt: // v < w612			ft.unsignedMax(v, wLimit.Umax-1)613			ft.unsignedMin(w, vLimit.Umin+1)614		case lt | eq: // v <= w615			ft.unsignedMax(v, wLimit.Umax)616			ft.unsignedMin(w, vLimit.Umin)617		case gt: // v > w618			ft.unsignedMin(v, wLimit.Umin+1)619			ft.unsignedMax(w, vLimit.Umax-1)620		case gt | eq: // v >= w621			ft.unsignedMin(v, wLimit.Umin)622			ft.unsignedMax(w, vLimit.Umax)623		case lt | gt: // v != w624			if vLimit.Umin == vLimit.Umax { // v is a constant625				c := vLimit.Umin626				if wLimit.Umin == c {627					ft.unsignedMin(w, c+1)628				}629				if wLimit.Umax == c {630					ft.unsignedMax(w, c-1)631				}632			}633			if wLimit.Umin == wLimit.Umax { // w is a constant634				c := wLimit.Umin635				if vLimit.Umin == c {636					ft.unsignedMin(v, c+1)637				}638				if vLimit.Umax == c {639					ft.unsignedMax(v, c-1)640				}641			}642		}643	case boolean:644		switch r {645		case eq: // v == w646			if vLimit.Min == 1 { // v is true647				ft.booleanTrue(w)648			}649			if vLimit.Max == 0 { // v is false650				ft.booleanFalse(w)651			}652			if wLimit.Min == 1 { // w is true653				ft.booleanTrue(v)654			}655			if wLimit.Max == 0 { // w is false656				ft.booleanFalse(v)657			}658		case lt | gt: // v != w659			if vLimit.Min == 1 { // v is true660				ft.booleanFalse(w)661			}662			if vLimit.Max == 0 { // v is false663				ft.booleanTrue(w)664			}665			if wLimit.Min == 1 { // w is true666				ft.booleanFalse(v)667			}668			if wLimit.Max == 0 { // w is false669				ft.booleanTrue(v)670			}671		}672	case pointer:673		switch r {674		case eq: // v == w675			if vLimit.Umax == 0 { // v is nil676				ft.pointerNil(w)677			}678			if vLimit.Umin > 0 { // v is non-nil679				ft.pointerNonNil(w)680			}681			if wLimit.Umax == 0 { // w is nil682				ft.pointerNil(v)683			}684			if wLimit.Umin > 0 { // w is non-nil685				ft.pointerNonNil(v)686			}687		case lt | gt: // v != w688			if vLimit.Umax == 0 { // v is nil689				ft.pointerNonNil(w)690			}691			if wLimit.Umax == 0 { // w is nil692				ft.pointerNonNil(v)693			}694			// Note: the other direction doesn't work.695			// Being not equal to a non-nil pointer doesn't696			// make you (necessarily) a nil pointer.697		}698	}699700	// Derived facts below here are only about numbers.701	if d != signed && d != unsigned {702		return703	}704705	// Additional facts we know given the relationship between len and cap.706	//707	// TODO: Since prove now derives transitive relations, it708	// should be sufficient to learn that len(w) <= cap(w) at the709	// beginning of prove where we look for all len/cap ops.710	if v.Op == ssaop.OpSliceLen && r&lt == 0 && ft.caps[v.Args[0].ID] != nil {711		// len(s) > w implies cap(s) > w712		// len(s) >= w implies cap(s) >= w713		// len(s) == w implies cap(s) >= w714		ft.update(parent, ft.caps[v.Args[0].ID], w, d, r|gt)715	}716	if w.Op == ssaop.OpSliceLen && r&gt == 0 && ft.caps[w.Args[0].ID] != nil {717		// same, length on the RHS.718		ft.update(parent, v, ft.caps[w.Args[0].ID], d, r|lt)719	}720	if v.Op == ssaop.OpSliceCap && r&gt == 0 && ft.lens[v.Args[0].ID] != nil {721		// cap(s) < w implies len(s) < w722		// cap(s) <= w implies len(s) <= w723		// cap(s) == w implies len(s) <= w724		ft.update(parent, ft.lens[v.Args[0].ID], w, d, r|lt)725	}726	if w.Op == ssaop.OpSliceCap && r&lt == 0 && ft.lens[w.Args[0].ID] != nil {727		// same, capacity on the RHS.728		ft.update(parent, v, ft.lens[w.Args[0].ID], d, r|gt)729	}730731	// Process fence-post implications.732	//733	// First, make the condition > or >=.734	if r == lt || r == lt|eq {735		v, w = w, v736		r = reverseBits[r]737	}738	switch r {739	case gt:740		if x, delta := isConstDelta(v); x != nil && delta == 1 {741			// x+1 > w  ⇒  x >= w742			//743			// This is useful for eliminating the744			// growslice branch of append.745			ft.update(parent, x, w, d, gt|eq)746		} else if x, delta := isConstDelta(w); x != nil && delta == -1 {747			// v > x-1  ⇒  v >= x748			ft.update(parent, v, x, d, gt|eq)749		}750	case gt | eq:751		if x, delta := isConstDelta(v); x != nil && delta == -1 {752			// x-1 >= w && x > min  ⇒  x > w753			//754			// Useful for i > 0; s[i-1].755			lim := ft.limits[x.ID]756			if (d == signed && lim.Min > opMin[v.Op]) || (d == unsigned && lim.Umin > 0) {757				ft.update(parent, x, w, d, gt)758			}759		} else if x, delta := isConstDelta(w); x != nil && delta == 1 {760			// v >= x+1 && x < max  ⇒  v > x761			lim := ft.limits[x.ID]762			if (d == signed && lim.Max < opMax[w.Op]) || (d == unsigned && lim.Umax < opUMax[w.Op]) {763				ft.update(parent, v, x, d, gt)764			}765		}766	}767768	// Process: x+delta > w (with delta constant)769	// Only signed domain for now (useful for accesses to slices in loops).770	if r == gt || r == gt|eq {771		if x, delta := isConstDelta(v); x != nil && d == signed {772			if parent.Func.Pass.Debug > 1 {773				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)774			}775			underflow := true776			if delta < 0 {777				l := ft.limits[x.ID]778				if (x.Type.Size() == 8 && l.Min >= math.MinInt64-delta) ||779					(x.Type.Size() == 4 && l.Min >= math.MinInt32-delta) {780					underflow = false781				}782			}783			if delta < 0 && !underflow {784				// If delta < 0 and x+delta cannot underflow then x > x+delta (that is, x > v)785				ft.update(parent, x, v, signed, gt)786			}787			if !w.IsGenericIntConst() {788				// If we know that x+delta > w but w is not constant, we can derive:789				//    if delta < 0 and x+delta cannot underflow, then x > w790				// This is useful for loops with bounds "len(slice)-K" (delta = -K)791				if delta < 0 && !underflow {792					ft.update(parent, x, w, signed, r)793				}794			} else {795				// With w,delta constants, we want to derive: x+delta > w  ⇒  x > w-delta796				//797				// We compute (using integers of the correct size):798				//    min = w - delta799				//    max = MaxInt - delta800				//801				// And we prove that:802				//    if min<max: min < x AND x <= max803				//    if min>max: min < x OR  x <= max804				//805				// This is always correct, even in case of overflow.806				//807				// If the initial fact is x+delta >= w instead, the derived conditions are:808				//    if min<max: min <= x AND x <= max809				//    if min>max: min <= x OR  x <= max810				//811				// Notice the conditions for max are still <=, as they handle overflows.812				var min, max int64813				switch x.Type.Size() {814				case 8:815					min = w.AuxInt - delta816					max = int64(^uint64(0)>>1) - delta817				case 4:818					min = int64(int32(w.AuxInt) - int32(delta))819					max = int64(int32(^uint32(0)>>1) - int32(delta))820				case 2:821					min = int64(int16(w.AuxInt) - int16(delta))822					max = int64(int16(^uint16(0)>>1) - int16(delta))823				case 1:824					min = int64(int8(w.AuxInt) - int8(delta))825					max = int64(int8(^uint8(0)>>1) - int8(delta))826				default:827					panic("unimplemented")828				}829830				if min < max {831					// Record that x > min and max >= x832					if r == gt {833						min++834					}835					ft.signedMinMax(x, min, max)836				} else {837					// We know that either x>min OR x<=max. factsTable cannot record OR conditions,838					// so let's see if we can already prove that one of them is false, in which case839					// the other must be true840					l := ft.limits[x.ID]841					if l.Max <= min {842						if r&eq == 0 || l.Max < min {843							// x>min (x>=min) is impossible, so it must be x<=max844							ft.signedMax(x, max)845						}846					} else if l.Min > max {847						// x<=max is impossible, so it must be x>min848						if r == gt {849							min++850						}851						ft.signedMin(x, min)852					}853				}854			}855		}856	}857858	// Look through value-preserving extensions.859	// If the domain is appropriate for the pre-extension Type,860	// repeat the update with the pre-extension Value.861	if isCleanExt(v) {862		switch {863		case d == signed && v.Args[0].Type.IsSigned():864			fallthrough865		case d == unsigned && !v.Args[0].Type.IsSigned():866			ft.update(parent, v.Args[0], w, d, r)867		}868	}869	if isCleanExt(w) {870		switch {871		case d == signed && w.Args[0].Type.IsSigned():872			fallthrough873		case d == unsigned && !w.Args[0].Type.IsSigned():874			ft.update(parent, v, w.Args[0], d, r)875		}876	}877}878879var opMin = map[ssaop.Op]int64{880	ssaop.OpAdd64: math.MinInt64, ssaop.OpSub64: math.MinInt64,881	ssaop.OpAdd32: math.MinInt32, ssaop.OpSub32: math.MinInt32,882}883884var opMax = map[ssaop.Op]int64{885	ssaop.OpAdd64: math.MaxInt64, ssaop.OpSub64: math.MaxInt64,886	ssaop.OpAdd32: math.MaxInt32, ssaop.OpSub32: math.MaxInt32,887}888889var opUMax = map[ssaop.Op]uint64{890	ssaop.OpAdd64: math.MaxUint64, ssaop.OpSub64: math.MaxUint64,891	ssaop.OpAdd32: math.MaxUint32, ssaop.OpSub32: math.MaxUint32,892}893894// isNonNegative reports whether v is known to be non-negative.895func (ft *factsTable) isNonNegative(v *ssa.Value) bool {896	return ft.limits[v.ID].Min >= 0897}898899// checkpoint saves the current state of known relations.900// Called when descending on a branch.901func (ft *factsTable) checkpoint() {902	if ft.unsat {903		ft.unsatDepth++904	}905	ft.limitStack = append(ft.limitStack, checkpointBound)906	ft.orderS.Checkpoint()907	ft.orderU.Checkpoint()908	ft.orderingsStack = append(ft.orderingsStack, 0)909}910911// restore restores known relation to the state just912// before the previous checkpoint.913// Called when backing up on a branch.914func (ft *factsTable) restore() {915	if ft.unsatDepth > 0 {916		ft.unsatDepth--917	} else {918		ft.unsat = false919	}920	for {921		old := ft.limitStack[len(ft.limitStack)-1]922		ft.limitStack = ft.limitStack[:len(ft.limitStack)-1]923		if old.vid == 0 { // checkpointBound924			break925		}926		ft.limits[old.vid] = old.limit927	}928	ft.orderS.Undo()929	ft.orderU.Undo()930	for {931		id := ft.orderingsStack[len(ft.orderingsStack)-1]932		ft.orderingsStack = ft.orderingsStack[:len(ft.orderingsStack)-1]933		if id == 0 { // checkpoint marker934			break935		}936		o := ft.orderings[id]937		ft.orderings[id] = o.next938		o.next = ft.orderingCache939		ft.orderingCache = o940	}941}942943var (944	reverseBits = [...]relation{0, 4, 2, 6, 1, 5, 3, 7}945946	// maps what we learn when the positive branch is taken.947	// For example:948	//      OpLess8:   {signed, lt},949	//	v1 = (OpLess8 v2 v3).950	// If we learn that v1 is true, then we can deduce that v2<v3951	// in the signed domain.952	domainRelationTable = map[ssaop.Op]struct {953		d domain954		r relation955	}{956		ssaop.OpEq8:   {signed | unsigned, eq},957		ssaop.OpEq16:  {signed | unsigned, eq},958		ssaop.OpEq32:  {signed | unsigned, eq},959		ssaop.OpEq64:  {signed | unsigned, eq},960		ssaop.OpEqPtr: {pointer, eq},961		ssaop.OpEqB:   {boolean, eq},962963		ssaop.OpNeq8:   {signed | unsigned, lt | gt},964		ssaop.OpNeq16:  {signed | unsigned, lt | gt},965		ssaop.OpNeq32:  {signed | unsigned, lt | gt},966		ssaop.OpNeq64:  {signed | unsigned, lt | gt},967		ssaop.OpNeqPtr: {pointer, lt | gt},968		ssaop.OpNeqB:   {boolean, lt | gt},969970		ssaop.OpLess8:   {signed, lt},971		ssaop.OpLess8U:  {unsigned, lt},972		ssaop.OpLess16:  {signed, lt},973		ssaop.OpLess16U: {unsigned, lt},974		ssaop.OpLess32:  {signed, lt},975		ssaop.OpLess32U: {unsigned, lt},976		ssaop.OpLess64:  {signed, lt},977		ssaop.OpLess64U: {unsigned, lt},978979		ssaop.OpLeq8:   {signed, lt | eq},980		ssaop.OpLeq8U:  {unsigned, lt | eq},981		ssaop.OpLeq16:  {signed, lt | eq},982		ssaop.OpLeq16U: {unsigned, lt | eq},983		ssaop.OpLeq32:  {signed, lt | eq},984		ssaop.OpLeq32U: {unsigned, lt | eq},985		ssaop.OpLeq64:  {signed, lt | eq},986		ssaop.OpLeq64U: {unsigned, lt | eq},987	}988)989990// cleanup returns the posets to the free list991func (ft *factsTable) cleanup(f *ssa.Func) {992	for _, po := range []*ssa.Poset{ft.orderS, ft.orderU} {993		// Make sure it's empty as it should be. A non-empty poset994		// might cause errors and miscompilations if reused.995		if checkEnabled {996			if err := po.CheckEmpty(); err != nil {997				f.Fatalf("poset not empty after function %s: %v", f.Name, err)998			}999		}1000		f.RetPoset(po)1001	}1002	f.Cache.FreeLimitSlice(ft.limits)1003	f.Cache.FreeBoolSlice(ft.recurseCheck)1004	if cap(ft.reusedTopoSortIDsToBlockIndexes) > 0 {1005		f.Cache.FreeUintSlice(ft.reusedTopoSortIDsToBlockIndexes)1006	}1007}10081009// addSlicesOfSameLen finds the slices that are in the same block and whose Op1010// is OpPhi and always have the same length, then add the equality relationship1011// between them to ft. If two slices start out with the same length and decrease1012// in length by the same amount on each round of the loop (or in the if block),1013// then we think their lengths are always equal.1014//1015// See https://go.dev/issues/751441016//1017// In fact, we are just propagating the equality1018//1019//	if len(a) == len(b) { // from here1020//		for len(a) > 4 {1021//			a = a[4:]1022//			b = b[4:]1023//		}1024//		if len(a) == len(b) { // to here1025//			return true1026//		}1027//	}1028//1029// or change the for to if:1030//1031//	if len(a) == len(b) { // from here1032//		if len(a) > 4 {1033//			a = a[4:]1034//			b = b[4:]1035//		}1036//		if len(a) == len(b) { // to here1037//			return true1038//		}1039//	}1040func addSlicesOfSameLen(ft *factsTable, b *ssa.Block) {1041	// Let w points to the first value we're interested in, and then we1042	// only process those values ​​that appear to be the same length as w,1043	// looping only once. This should be enough in most cases. And u is1044	// similar to w, see comment for predIndex.1045	var u, w *ssa.Value1046	var i, j, k sliceInfo1047	isInterested := func(v *ssa.Value) bool {1048		j = getSliceInfo(v)1049		return j.sliceWhere != sliceUnknown1050	}1051	for _, v := range b.Values {1052		if v.Uses == 0 {1053			continue1054		}1055		if v.Op == ssaop.OpPhi && len(v.Args) == 2 && ft.lens[v.ID] != nil && isInterested(v) {1056			if j.predIndex == 1 && ft.lens[v.Args[0].ID] != nil {1057				// found v = (Phi x (SliceMake _ (Add64 (Const64 [n]) (SliceLen x)) _))) or1058				// v = (Phi x (SliceMake _ (Add64 (Const64 [n]) (SliceLen v)) _)))1059				if w == nil {1060					k = j1061					w = v1062					continue1063				}1064				// propagate the equality1065				if j == k && ft.orderS.Equal(ft.lens[v.Args[0].ID], ft.lens[w.Args[0].ID]) {1066					ft.update(b, ft.lens[v.ID], ft.lens[w.ID], signed, eq)1067				}1068			} else if j.predIndex == 0 && ft.lens[v.Args[1].ID] != nil {1069				// found v = (Phi (SliceMake _ (Add64 (Const64 [n]) (SliceLen x)) _)) x) or1070				// v = (Phi (SliceMake _ (Add64 (Const64 [n]) (SliceLen v)) _)) x)1071				if u == nil {1072					i = j1073					u = v1074					continue1075				}1076				// propagate the equality1077				if j == i && ft.orderS.Equal(ft.lens[v.Args[1].ID], ft.lens[u.Args[1].ID]) {1078					ft.update(b, ft.lens[v.ID], ft.lens[u.ID], signed, eq)1079				}1080			}1081		}1082	}1083}10841085type sliceWhere int10861087const (1088	sliceUnknown sliceWhere = iota1089	sliceInFor1090	sliceInIf1091)10921093// predIndex is used to indicate the branch represented by the predecessor1094// block in which the slicing operation occurs.1095type predIndex int10961097type sliceInfo struct {1098	lengthDiff int641099	sliceWhere1100	predIndex1101}11021103// getSliceInfo returns the negative increment of the slice length in a slice1104// operation by examine the Phi node at the merge block. So, we only interest1105// in the slice operation if it is inside a for block or an if block.1106// Otherwise it returns sliceInfo{0, sliceUnknown, 0}.1107//1108// For the following for block:1109//1110//	for len(a) > 4 {1111//	    a = a[4:]1112//	}1113//1114// vp = (Phi v3 v9)1115// v5 = (SliceLen vp)1116// v7 = (Add64 (Const64 [-4]) v5)1117// v9 = (SliceMake _ v7 _)1118//1119// returns sliceInfo{-4, sliceInFor, 1}1120//1121// For a subsequent merge block after an if block:1122//1123//	if len(a) > 4 {1124//	    a = a[4:]1125//	}1126//	a // here1127//1128// vp = (Phi v3 v9)1129// v5 = (SliceLen v3)1130// v7 = (Add64 (Const64 [-4]) v5)1131// v9 = (SliceMake _ v7 _)1132//1133// returns sliceInfo{-4, sliceInIf, 1}1134//1135// Returns sliceInfo{0, sliceUnknown, 0} if it is not the slice1136// operation we are interested in.1137func getSliceInfo(vp *ssa.Value) (inf sliceInfo) {1138	if vp.Op != ssaop.OpPhi || len(vp.Args) != 2 {1139		return1140	}1141	var i predIndex1142	var l *ssa.Value // length for OpSliceMake1143	if vp.Args[0].Op != ssaop.OpSliceMake && vp.Args[1].Op == ssaop.OpSliceMake {1144		l = vp.Args[1].Args[1]1145		i = 11146	} else if vp.Args[0].Op == ssaop.OpSliceMake && vp.Args[1].Op != ssaop.OpSliceMake {1147		l = vp.Args[0].Args[1]1148		i = 01149	} else {1150		return1151	}1152	var op ssaop.Op1153	switch l.Op {1154	case ssaop.OpAdd64:1155		op = ssaop.OpConst641156	case ssaop.OpAdd32:1157		op = ssaop.OpConst321158	default:1159		return1160	}1161	if l.Args[0].Op == op && l.Args[1].Op == ssaop.OpSliceLen && l.Args[1].Args[0] == vp {1162		return sliceInfo{l.Args[0].AuxInt, sliceInFor, i}1163	}1164	if l.Args[1].Op == op && l.Args[0].Op == ssaop.OpSliceLen && l.Args[0].Args[0] == vp {1165		return sliceInfo{l.Args[1].AuxInt, sliceInFor, i}1166	}1167	if l.Args[0].Op == op && l.Args[1].Op == ssaop.OpSliceLen && l.Args[1].Args[0] == vp.Args[1-i] {1168		return sliceInfo{l.Args[0].AuxInt, sliceInIf, i}1169	}1170	if l.Args[1].Op == op && l.Args[0].Op == ssaop.OpSliceLen && l.Args[0].Args[0] == vp.Args[1-i] {1171		return sliceInfo{l.Args[1].AuxInt, sliceInIf, i}1172	}1173	return1174}11751176// prove removes redundant BlockIf branches that can be inferred1177// from previous dominating comparisons.1178//1179// By far, the most common redundant pair are generated by bounds checking.1180// For example for the code:1181//1182//	a[i] = 41183//	foo(a[i])1184//1185// The compiler will generate the following code:1186//1187//	if i >= len(a) {1188//	    panic("not in bounds")1189//	}1190//	a[i] = 41191//	if i >= len(a) {1192//	    panic("not in bounds")1193//	}1194//	foo(a[i])1195//1196// The second comparison i >= len(a) is clearly redundant because if the1197// else branch of the first comparison is executed, we already know that i < len(a).1198// The code for the second panic can be removed.1199//1200// prove works by finding contradictions and trimming branches whose1201// conditions are unsatisfiable given the branches leading up to them.1202// It tracks a "fact table" of branch conditions. For each branching1203// block, it asserts the branch conditions that uniquely dominate that1204// block, and then separately asserts the block's branch condition and1205// its negation. If either leads to a contradiction, it can trim that1206// successor.1207func prove(f *ssa.Func) {1208	// Find induction variables.1209	var indVars map[*ssa.Block][]indVar1210	for _, v := range findIndVar(f) {1211		ind := v.ind1212		if len(ind.Args) != 2 {1213			// the rewrite code assumes there is only ever two parents to loops1214			panic("unexpected induction with too many parents")1215		}12161217		nxt := v.nxt1218		if !(ind.Uses == 2 && // 2 used by comparison and next1219			nxt.Uses == 1) { // 1 used by induction1220			// ind or nxt is used inside the loop, add it for the facts table1221			if indVars == nil {1222				indVars = make(map[*ssa.Block][]indVar)1223			}1224			indVars[v.entry] = append(indVars[v.entry], v)1225			continue1226		} else {1227			// Since this induction variable is not used for anything but counting the iterations,1228			// no point in putting it into the facts table.1229		}12301231		maybeRewriteLoopToDownwardCountingLoop(f, v)1232	}12331234	ft := newFactsTable(f)1235	ft.checkpoint()12361237	// Find length and capacity ops.1238	for _, b := range f.Blocks {1239		for _, v := range b.Values {1240			if v.Uses == 0 {1241				// We don't care about dead values.1242				// (There can be some that are CSEd but not removed yet.)1243				continue1244			}1245			switch v.Op {1246			case ssaop.OpSliceLen:1247				if ft.lens == nil {1248					ft.lens = map[ssa.ID]*ssa.Value{}1249				}1250				// Set all len Values for the same slice as equal in the poset.1251				// The poset handles transitive relations, so Values related to1252				// any OpSliceLen for this slice will be correctly related to others.1253				if l, ok := ft.lens[v.Args[0].ID]; ok {1254					ft.update(b, v, l, signed, eq)1255				} else {1256					ft.lens[v.Args[0].ID] = v1257				}1258			case ssaop.OpSliceCap:1259				if ft.caps == nil {1260					ft.caps = map[ssa.ID]*ssa.Value{}1261				}1262				// Same as case OpSliceLen above, but for slice cap.1263				if c, ok := ft.caps[v.Args[0].ID]; ok {1264					ft.update(b, v, c, signed, eq)1265				} else {1266					ft.caps[v.Args[0].ID] = v1267				}1268			}1269		}1270	}12711272	// current node state1273	type walkState int1274	const (1275		descend walkState = iota1276		restore1277	)1278	// work maintains the DFS stack.1279	type bp struct {1280		block *ssa.Block // current handled block1281		state walkState  // what's to do1282	}1283	work := make([]bp, 0, 256)1284	work = append(work, bp{1285		block: f.Entry,1286		state: descend,1287	})12881289	idom := f.Idom()1290	sdom := f.Sdom()12911292	// DFS on the dominator tree.1293	//1294	// For efficiency, we consider only the dominator tree rather1295	// than the entire flow graph. On the way down, we consider1296	// incoming branches and accumulate conditions that uniquely1297	// dominate the current block. If we discover a contradiction,1298	// we can eliminate the entire block and all of its children.1299	// On the way back up, we consider outgoing branches that1300	// haven't already been considered. This way we consider each1301	// branch condition only once.1302	for len(work) > 0 {1303		node := work[len(work)-1]1304		work = work[:len(work)-1]1305		parent := idom[node.block.ID]1306		branch := getBranch(sdom, parent, node.block)13071308		switch node.state {1309		case descend:1310			ft.checkpoint()13111312			// Entering the block, add facts about the induction variable1313			// that is bound to this block.1314			for _, iv := range indVars[node.block] {1315				addIndVarRestrictions(ft, parent, iv)1316			}13171318			// Add results of reaching this block via a branch from1319			// its immediate dominator (if any).1320			if branch != unknown {1321				addBranchRestrictions(ft, parent, branch)1322			}13231324			if ft.unsat {1325				// node.block is unreachable.1326				// Remove it and don't visit1327				// its children.1328				removeBranch(parent, branch)1329				ft.restore()1330				break1331			}1332			// Otherwise, we can now commit to1333			// taking this branch. We'll restore1334			// ft when we unwind.13351336			ft.topoSortValuesInBlock(node.block)13371338			// Add slices of the same length start from current block.1339			addSlicesOfSameLen(ft, node.block)13401341			for _, v := range node.block.Values {1342				ft.flowLimit(v)1343				// constant fold arguments before addValueFact to avoid v's v.Args learned facts time traveling into v's arguments.1344				// in other words if v teaches us something about it's arguments,1345				// we can't use that to optimize v's arguments since v hasn't ran yet.1346				ft.constantFoldArguments(v)1347				ft.addValueFact(node.block, v)1348				ft.simplifyValue(node.block, v)1349			}13501351			ft.simplifyBlock(sdom, node.block)13521353			work = append(work, bp{1354				block: node.block,1355				state: restore,1356			})1357			for s := sdom.Child(node.block); s != nil; s = sdom.Sibling(s) {1358				work = append(work, bp{1359					block: s,1360					state: descend,1361				})1362			}13631364		case restore:1365			ft.restore()1366		}1367	}13681369	ft.restore()13701371	ft.cleanup(f)1372}13731374// flowLimit updates the known limits of v in ft.1375// flowLimit can use the ranges of input arguments.1376//1377// Note: this calculation only happens at the point the value is defined. We do not reevaluate1378// it later. So for example:1379//1380//	v := x + y1381//	if 0 <= x && x < 5 && 0 <= y && y < 5 { ... use v ... }1382//1383// we don't discover that the range of v is bounded in the conditioned1384// block. We could recompute the range of v once we enter the block so1385// we know that it is 0 <= v <= 8, but we don't have a mechanism to do1386// that right now.1387func (ft *factsTable) flowLimit(v *ssa.Value) {1388	if !v.Type.IsInteger() {1389		// TODO: boolean?1390		return1391	}13921393	// Additional limits based on opcode and argument.1394	// No need to repeat things here already done in initLimit.1395	switch v.Op {13961397	// extensions1398	case ssaop.OpZeroExt8to64, ssaop.OpZeroExt8to32, ssaop.OpZeroExt8to16, ssaop.OpZeroExt16to64, ssaop.OpZeroExt16to32, ssaop.OpZeroExt32to64:1399		a := ft.limits[v.Args[0].ID]1400		ft.unsignedMinMax(v, a.Umin, a.Umax)1401	case ssaop.OpSignExt8to64, ssaop.OpSignExt8to32, ssaop.OpSignExt8to16, ssaop.OpSignExt16to64, ssaop.OpSignExt16to32, ssaop.OpSignExt32to64:1402		a := ft.limits[v.Args[0].ID]1403		ft.signedMinMax(v, a.Min, a.Max)1404	case ssaop.OpTrunc64to8, ssaop.OpTrunc64to16, ssaop.OpTrunc64to32, ssaop.OpTrunc32to8, ssaop.OpTrunc32to16, ssaop.OpTrunc16to8:1405		a := ft.limits[v.Args[0].ID]1406		if a.Umax <= 1<<(uint64(v.Type.Size())*8)-1 {1407			ft.unsignedMinMax(v, a.Umin, a.Umax)1408		}14091410	// math/bits1411	case ssaop.OpCtz64, ssaop.OpCtz32, ssaop.OpCtz16, ssaop.OpCtz8:1412		a := v.Args[0]1413		al := ft.limits[a.ID]1414		ft.newLimit(v, al.Ctz(uint(a.Type.Size())*8))14151416	case ssaop.OpPopCount64, ssaop.OpPopCount32, ssaop.OpPopCount16, ssaop.OpPopCount8:1417		a := v.Args[0]1418		al := ft.limits[a.ID]1419		ft.newLimit(v, al.Popcount(uint(a.Type.Size())*8))14201421	case ssaop.OpBitLen64, ssaop.OpBitLen32, ssaop.OpBitLen16, ssaop.OpBitLen8:1422		a := v.Args[0]1423		al := ft.limits[a.ID]1424		ft.newLimit(v, al.Bitlen(uint(a.Type.Size())*8))14251426	// Masks.14271428	// TODO: if y.umax and y.umin share a leading bit pattern, y also has that leading bit pattern.1429	// we could compare the patterns of always set bits in a and b and learn more about minimum and maximum.1430	// But I doubt this help any real world code.1431	case ssaop.OpOr64, ssaop.OpOr32, ssaop.OpOr16, ssaop.OpOr8:1432		// OR can only make the value bigger and can't flip bits proved to be zero in both inputs.1433		a := ft.limits[v.Args[0].ID]1434		b := ft.limits[v.Args[1].ID]1435		ft.unsignedMinMax(v,1436			max(a.Umin, b.Umin),1437			1<<bits.Len64(a.Umax|b.Umax)-1)1438	case ssaop.OpXor64, ssaop.OpXor32, ssaop.OpXor16, ssaop.OpXor8:1439		// XOR can't flip bits that are proved to be zero in both inputs.1440		a := ft.limits[v.Args[0].ID]1441		b := ft.limits[v.Args[1].ID]1442		ft.unsignedMax(v, 1<<bits.Len64(a.Umax|b.Umax)-1)1443	case ssaop.OpCom64, ssaop.OpCom32, ssaop.OpCom16, ssaop.OpCom8:1444		a := ft.limits[v.Args[0].ID]1445		ft.newLimit(v, a.Com(uint(v.Type.Size())*8))14461447	// Arithmetic.1448	case ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8:1449		a := ft.limits[v.Args[0].ID]1450		b := ft.limits[v.Args[1].ID]1451		ft.newLimit(v, a.Add(b, uint(v.Type.Size())*8))1452	case ssaop.OpSub64, ssaop.OpSub32, ssaop.OpSub16, ssaop.OpSub8:1453		a := ft.limits[v.Args[0].ID]1454		b := ft.limits[v.Args[1].ID]1455		ft.newLimit(v, a.Sub(b, uint(v.Type.Size())*8))1456		ft.detectMod(v)1457		ft.detectSliceLenRelation(v)1458		ft.detectSubRelations(v)1459	case ssaop.OpNeg64, ssaop.OpNeg32, ssaop.OpNeg16, ssaop.OpNeg8:1460		a := ft.limits[v.Args[0].ID]1461		bitsize := uint(v.Type.Size()) * 81462		ft.newLimit(v, a.Neg(bitsize))1463	case ssaop.OpMul64, ssaop.OpMul32, ssaop.OpMul16, ssaop.OpMul8:1464		a := ft.limits[v.Args[0].ID]1465		b := ft.limits[v.Args[1].ID]1466		ft.newLimit(v, a.Mul(b, uint(v.Type.Size())*8))1467	case ssaop.OpLsh64x64, ssaop.OpLsh64x32, ssaop.OpLsh64x16, ssaop.OpLsh64x8,1468		ssaop.OpLsh32x64, ssaop.OpLsh32x32, ssaop.OpLsh32x16, ssaop.OpLsh32x8,1469		ssaop.OpLsh16x64, ssaop.OpLsh16x32, ssaop.OpLsh16x16, ssaop.OpLsh16x8,1470		ssaop.OpLsh8x64, ssaop.OpLsh8x32, ssaop.OpLsh8x16, ssaop.OpLsh8x8:1471		a := ft.limits[v.Args[0].ID]1472		b := ft.limits[v.Args[1].ID]1473		bitsize := uint(v.Type.Size()) * 81474		ft.newLimit(v, a.Mul(b.Exp2(bitsize), bitsize))1475	case ssaop.OpRsh64x64, ssaop.OpRsh64x32, ssaop.OpRsh64x16, ssaop.OpRsh64x8,1476		ssaop.OpRsh32x64, ssaop.OpRsh32x32, ssaop.OpRsh32x16, ssaop.OpRsh32x8,1477		ssaop.OpRsh16x64, ssaop.OpRsh16x32, ssaop.OpRsh16x16, ssaop.OpRsh16x8,1478		ssaop.OpRsh8x64, ssaop.OpRsh8x32, ssaop.OpRsh8x16, ssaop.OpRsh8x8:1479		a := ft.limits[v.Args[0].ID]1480		b := ft.limits[v.Args[1].ID]1481		if b.Min >= 0 {1482			// Shift of negative makes a value closer to 0 (greater),1483			// so if a.min is negative, v.min is a.min>>b.min instead of a.min>>b.max,1484			// and similarly if a.max is negative, v.max is a.max>>b.max.1485			// Easier to compute min and max of both than to write sign logic.1486			vmin := min(a.Min>>b.Min, a.Min>>b.Max)1487			vmax := max(a.Max>>b.Min, a.Max>>b.Max)1488			ft.signedMinMax(v, vmin, vmax)1489		}1490	case ssaop.OpRsh64Ux64, ssaop.OpRsh64Ux32, ssaop.OpRsh64Ux16, ssaop.OpRsh64Ux8,1491		ssaop.OpRsh32Ux64, ssaop.OpRsh32Ux32, ssaop.OpRsh32Ux16, ssaop.OpRsh32Ux8,1492		ssaop.OpRsh16Ux64, ssaop.OpRsh16Ux32, ssaop.OpRsh16Ux16, ssaop.OpRsh16Ux8,1493		ssaop.OpRsh8Ux64, ssaop.OpRsh8Ux32, ssaop.OpRsh8Ux16, ssaop.OpRsh8Ux8:1494		a := ft.limits[v.Args[0].ID]1495		b := ft.limits[v.Args[1].ID]1496		if b.Min >= 0 {1497			ft.unsignedMinMax(v, a.Umin>>b.Max, a.Umax>>b.Min)1498		}1499	case ssaop.OpDiv64, ssaop.OpDiv32, ssaop.OpDiv16, ssaop.OpDiv8:1500		a := ft.limits[v.Args[0].ID]1501		b := ft.limits[v.Args[1].ID]1502		if !(a.Nonnegative() && b.Nonnegative()) {1503			// TODO: we could handle signed limits but I didn't bother.1504			break1505		}1506		fallthrough1507	case ssaop.OpDiv64u, ssaop.OpDiv32u, ssaop.OpDiv16u, ssaop.OpDiv8u:1508		a := ft.limits[v.Args[0].ID]1509		b := ft.limits[v.Args[1].ID]1510		lim := ssa.NoLimit()1511		if b.Umax > 0 {1512			lim = lim.UnsignedMin(a.Umin / b.Umax)1513		}1514		if b.Umin > 0 {1515			lim = lim.UnsignedMax(a.Umax / b.Umin)1516		}1517		ft.newLimit(v, lim)1518	case ssaop.OpMod64, ssaop.OpMod32, ssaop.OpMod16, ssaop.OpMod8:1519		ft.modLimit(true, v, v.Args[0], v.Args[1])1520	case ssaop.OpMod64u, ssaop.OpMod32u, ssaop.OpMod16u, ssaop.OpMod8u:1521		ft.modLimit(false, v, v.Args[0], v.Args[1])15221523	case ssaop.OpPhi:1524		// Compute the union of all the input phis.1525		// Often this will convey no information, because the block1526		// is not dominated by its predecessors and hence the1527		// phi arguments might not have been processed yet. But if1528		// the values are declared earlier, it may help. e.g., for1529		//    v = phi(c3, c5)1530		// where c3 = OpConst [3] and c5 = OpConst [5] are1531		// defined in the entry block, we can derive [3,5]1532		// as the limit for v.1533		l := ft.limits[v.Args[0].ID]1534		for _, a := range v.Args[1:] {1535			l2 := ft.limits[a.ID]1536			l.Min = min(l.Min, l2.Min)1537			l.Max = max(l.Max, l2.Max)1538			l.Umin = min(l.Umin, l2.Umin)1539			l.Umax = max(l.Umax, l2.Umax)1540		}1541		ft.newLimit(v, l)1542	}1543}15441545// detectSliceLenRelation matches the pattern where1546//  1. v := slicelen - index, OR v := slicecap - index1547//     AND1548//  2. index <= slicelen - K1549//     THEN1550//1551// slicecap - index >= slicelen - index >= K1552//1553// Note that "index" is not used for indexing in this pattern, but1554// in the motivating example (chunked slice iteration) it is.1555func (ft *factsTable) detectSliceLenRelation(v *ssa.Value) {1556	if v.Op != ssaop.OpSub64 {1557		return1558	}15591560	if !(v.Args[0].Op == ssaop.OpSliceLen || v.Args[0].Op == ssaop.OpStringLen || v.Args[0].Op == ssaop.OpSliceCap) {1561		return1562	}15631564	index := v.Args[1]1565	if !ft.isNonNegative(index) {1566		return1567	}1568	slice := v.Args[0].Args[0]15691570	for o := ft.orderings[index.ID]; o != nil; o = o.next {1571		if o.d != signed {1572			continue1573		}1574		or := o.r1575		if or != lt && or != lt|eq {1576			continue1577		}1578		ow := o.w1579		if ow.Op != ssaop.OpAdd64 && ow.Op != ssaop.OpSub64 {1580			continue1581		}1582		var lenOffset *ssa.Value1583		if bound := ow.Args[0]; (bound.Op == ssaop.OpSliceLen || bound.Op == ssaop.OpStringLen) && bound.Args[0] == slice {1584			lenOffset = ow.Args[1]1585		} else if bound := ow.Args[1]; (bound.Op == ssaop.OpSliceLen || bound.Op == ssaop.OpStringLen) && bound.Args[0] == slice {1586			// Do not infer K - slicelen, see issue #76709.1587			if ow.Op == ssaop.OpAdd64 {1588				lenOffset = ow.Args[0]1589			}1590		}1591		if lenOffset == nil || lenOffset.Op != ssaop.OpConst64 {1592			continue1593		}1594		K := lenOffset.AuxInt1595		if ow.Op == ssaop.OpAdd64 {1596			K = -K1597		}1598		if K < 0 {1599			continue1600		}1601		if or == lt {1602			K++1603		}1604		if K < 0 { // We hate thinking about overflow1605			continue1606		}1607		ft.signedMin(v, K)1608	}1609}16101611// v must be Sub{64,32,16,8}.1612func (ft *factsTable) detectSubRelations(v *ssa.Value) {1613	// v = x-y1614	x := v.Args[0]1615	y := v.Args[1]1616	if x == y {1617		ft.signedMinMax(v, 0, 0)1618		return1619	}1620	xLim := ft.limits[x.ID]1621	yLim := ft.limits[y.ID]16221623	// Check if we might wrap around. If so, give up.1624	width := uint(v.Type.Size()) * 816251626	// v >= 1 in the signed domain?1627	var vSignedMinOne bool16281629	// Signed optimizations1630	if _, ok := ssa.SafeSub(xLim.Min, yLim.Max, width); ok {1631		// Large abs negative y can also overflow1632		if _, ok := ssa.SafeSub(xLim.Max, yLim.Min, width); ok {1633			// x-y won't overflow16341635			// Subtracting a positive non-zero number only makes1636			// things smaller. If it's positive or zero, it might1637			// also do nothing (x-0 == v).1638			if yLim.Min > 0 {1639				ft.update(v.Block, v, x, signed, lt)1640			} else if yLim.Min == 0 {1641				ft.update(v.Block, v, x, signed, lt|eq)1642			}16431644			// Subtracting a number from a bigger one1645			// can't go below 1. If the numbers might be1646			// equal, then it can't go below 0.1647			//1648			// This requires the overflow checks because1649			// large negative y can cause an overflow.1650			if ft.orderS.Ordered(y, x) {1651				ft.signedMin(v, 1)1652				vSignedMinOne = true1653			} else if ft.orderS.OrderedOrEqual(y, x) {1654				ft.setNonNegative(v)1655			}1656		}1657	}16581659	// Unsigned optimizations1660	if _, ok := ssa.SafeSubU(xLim.Umin, yLim.Umax, width); ok {1661		if yLim.Umin > 0 {1662			ft.update(v.Block, v, x, unsigned, lt)1663		} else {1664			ft.update(v.Block, v, x, unsigned, lt|eq)1665		}1666	}16671668	// Proving v >= 1 in the signed domain automatically1669	// proves it in the unsigned domain, so we can skip it.1670	//1671	// We don't need overflow checks here, since if y < x,1672	// then x-y can never overflow for uint.1673	if !vSignedMinOne && ft.orderU.Ordered(y, x) {1674		ft.unsignedMin(v, 1)1675	}1676}16771678// x%d has been rewritten to x - (x/d)*d.1679func (ft *factsTable) detectMod(v *ssa.Value) {1680	var opDiv, opDivU, opMul, opConst ssaop.Op1681	switch v.Op {1682	case ssaop.OpSub64:1683		opDiv = ssaop.OpDiv641684		opDivU = ssaop.OpDiv64u1685		opMul = ssaop.OpMul641686		opConst = ssaop.OpConst641687	case ssaop.OpSub32:1688		opDiv = ssaop.OpDiv321689		opDivU = ssaop.OpDiv32u1690		opMul = ssaop.OpMul321691		opConst = ssaop.OpConst321692	case ssaop.OpSub16:1693		opDiv = ssaop.OpDiv161694		opDivU = ssaop.OpDiv16u1695		opMul = ssaop.OpMul161696		opConst = ssaop.OpConst161697	case ssaop.OpSub8:1698		opDiv = ssaop.OpDiv81699		opDivU = ssaop.OpDiv8u1700		opMul = ssaop.OpMul81701		opConst = ssaop.OpConst81702	}17031704	mul := v.Args[1]1705	if mul.Op != opMul {1706		return1707	}1708	div, con := mul.Args[0], mul.Args[1]1709	if div.Op == opConst {1710		div, con = con, div1711	}1712	if con.Op != opConst || (div.Op != opDiv && div.Op != opDivU) || div.Args[0] != v.Args[0] || div.Args[1].Op != opConst || div.Args[1].AuxInt != con.AuxInt {1713		return1714	}1715	ft.modLimit(div.Op == opDiv, v, v.Args[0], con)1716}17171718// modLimit sets v with facts derived from v = p % q.1719func (ft *factsTable) modLimit(signed bool, v, p, q *ssa.Value) {1720	a := ft.limits[p.ID]1721	b := ft.limits[q.ID]1722	if signed {1723		if a.Min < 0 && b.Min > 0 {1724			ft.signedMinMax(v, -(b.Max - 1), b.Max-1)1725			return1726		}1727		if !(a.Nonnegative() && b.Nonnegative()) {1728			// TODO: we could handle signed limits but I didn't bother.1729			return1730		}1731		if a.Min >= 0 && b.Min > 0 {1732			ft.setNonNegative(v)1733		}1734	}1735	// Underflow in the arithmetic below is ok, it gives to MaxUint64 which does nothing to the limit.1736	ft.unsignedMax(v, min(a.Umax, b.Umax-1))1737}17381739// getBranch returns the range restrictions added by p1740// when reaching b. p is the immediate dominator of b.1741func getBranch(sdom ssa.SparseTree, p *ssa.Block, b *ssa.Block) branch {1742	if p == nil {1743		return unknown1744	}1745	switch p.Kind {1746	case block.BlockIf:1747		// If p and p.Succs[0] are dominators it means that every path1748		// from entry to b passes through p and p.Succs[0]. We care that1749		// no path from entry to b passes through p.Succs[1]. If p.Succs[0]1750		// has one predecessor then (apart from the degenerate case),1751		// there is no path from entry that can reach b through p.Succs[1].1752		// TODO: how about p->yes->b->yes, i.e. a loop in yes.1753		if sdom.IsAncestorEq(p.Succs[0].B, b) && len(p.Succs[0].B.Preds) == 1 {1754			return positive1755		}1756		if sdom.IsAncestorEq(p.Succs[1].B, b) && len(p.Succs[1].B.Preds) == 1 {1757			return negative1758		}1759	case block.BlockJumpTable:1760		// TODO: this loop can lead to quadratic behavior, as1761		// getBranch can be called len(p.Succs) times.1762		for i, e := range p.Succs {1763			if sdom.IsAncestorEq(e.B, b) && len(e.B.Preds) == 1 {1764				return jumpTable0 + branch(i)1765			}1766		}1767	}1768	return unknown1769}17701771// addIndVarRestrictions updates the factsTables ft with the facts1772// learned from the induction variable indVar which drives the loop1773// starting in Block b.1774func addIndVarRestrictions(ft *factsTable, b *ssa.Block, iv indVar) {1775	d := signed1776	if ft.isNonNegative(iv.min) && ft.isNonNegative(iv.max) {1777		d |= unsigned1778	}17791780	if iv.flags&indVarMinExc == 0 {1781		addRestrictions(b, ft, d, iv.min, iv.ind, lt|eq)1782	} else {1783		addRestrictions(b, ft, d, iv.min, iv.ind, lt)1784	}17851786	if iv.flags&indVarMaxInc == 0 {1787		addRestrictions(b, ft, d, iv.ind, iv.max, lt)1788	} else {1789		addRestrictions(b, ft, d, iv.ind, iv.max, lt|eq)1790	}1791}17921793// addBranchRestrictions updates the factsTables ft with the facts learned when1794// branching from Block b in direction br.1795func addBranchRestrictions(ft *factsTable, b *ssa.Block, br branch) {1796	c := b.Controls[0]1797	switch {1798	case br == negative:1799		ft.booleanFalse(c)1800	case br == positive:1801		ft.booleanTrue(c)1802	case br >= jumpTable0:1803		idx := br - jumpTable01804		val := int64(idx)1805		if v, off := isConstDelta(c); v != nil {1806			// Establish the bound on the underlying value we're switching on,1807			// not on the offset-ed value used as the jump table index.1808			c = v1809			val -= off1810		}1811		ft.newLimit(c, ssa.Limit{Min: val, Max: val, Umin: uint64(val), Umax: uint64(val)})1812	default:1813		panic("unknown branch")1814	}1815}18161817// addRestrictions updates restrictions from the immediate1818// dominating block (p) using r.1819func addRestrictions(parent *ssa.Block, ft *factsTable, t domain, v, w *ssa.Value, r relation) {1820	if t == 0 {1821		// Trivial case: nothing to do.1822		// Should not happen, but just in case.1823		return1824	}1825	for i := domain(1); i <= t; i <<= 1 {1826		if t&i == 0 {1827			continue1828		}1829		ft.update(parent, v, w, i, r)1830	}1831}18321833func unsignedAddOverflows(a, b uint64, t *types.Type) bool {1834	switch t.Size() {1835	case 8:1836		return a+b < a1837	case 4:1838		return a+b > math.MaxUint321839	case 2:1840		return a+b > math.MaxUint161841	case 1:1842		return a+b > math.MaxUint81843	default:1844		panic("unreachable")1845	}1846}18471848func signedAddOverflowsOrUnderflows(a, b int64, t *types.Type) bool {1849	r := a + b1850	switch t.Size() {1851	case 8:1852		return (a >= 0 && b >= 0 && r < 0) || (a < 0 && b < 0 && r >= 0)1853	case 4:1854		return r < math.MinInt32 || math.MaxInt32 < r1855	case 2:1856		return r < math.MinInt16 || math.MaxInt16 < r1857	case 1:1858		return r < math.MinInt8 || math.MaxInt8 < r1859	default:1860		panic("unreachable")1861	}1862}18631864func unsignedSubUnderflows(a, b uint64) bool {1865	return a < b1866}18671868// checkForChunkedIndexBounds looks for index expressions of the form1869// A[i+delta] where delta < K and i <= len(A)-K.  That is, this is a chunked1870// iteration where the index is not directly compared to the length.1871// if isReslice, then delta can be equal to K.1872func checkForChunkedIndexBounds(ft *factsTable, b *ssa.Block, index, bound *ssa.Value, isReslice bool) bool {1873	if bound.Op != ssaop.OpSliceLen && bound.Op != ssaop.OpStringLen && bound.Op != ssaop.OpSliceCap {1874		return false1875	}18761877	// this is a slice bounds check against len or capacity,1878	// and refers back to a prior check against length, which1879	// will also work for the cap since that is not smaller1880	// than the length.18811882	slice := bound.Args[0]1883	lim := ft.limits[index.ID]1884	if lim.Min < 0 {1885		return false1886	}1887	i, delta := isConstDelta(index)1888	if i == nil {1889		return false1890	}1891	if delta < 0 {1892		return false1893	}1894	// special case for blocked iteration over a slice.1895	// slicelen > i + delta && <==== if clauses above1896	// && index >= 0           <==== if clause above1897	// delta >= 0 &&           <==== if clause above1898	// slicelen-K >/>= x       <==== checked below1899	// && K >=/> delta         <==== checked below1900	// then v > w1901	// example: i <=/< len - 4/3 means i+{0,1,2,3} are legal indices1902	for o := ft.orderings[i.ID]; o != nil; o = o.next {1903		if o.d != signed {1904			continue1905		}1906		if ow := o.w; ow.Op == ssaop.OpAdd64 {1907			var lenOffset *ssa.Value1908			if bound := ow.Args[0]; (bound.Op == ssaop.OpSliceLen || bound.Op == ssaop.OpStringLen) && bound.Args[0] == slice {1909				lenOffset = ow.Args[1]1910			} else if bound := ow.Args[1]; (bound.Op == ssaop.OpSliceLen || bound.Op == ssaop.OpStringLen) && bound.Args[0] == slice {1911				lenOffset = ow.Args[0]1912			}1913			if lenOffset == nil || lenOffset.Op != ssaop.OpConst64 {1914				continue1915			}1916			if K := -lenOffset.AuxInt; K >= 0 {1917				or := o.r1918				if isReslice {1919					K++1920				}1921				if or == lt {1922					or = lt | eq1923					K++1924				}1925				if K < 0 { // We hate thinking about overflow1926					continue1927				}19281929				if delta < K && or == lt|eq {1930					return true1931				}1932			}1933		}1934	}1935	return false1936}19371938func (ft *factsTable) addValueFact(b *ssa.Block, v *ssa.Value) {1939	switch v.Op {1940	case ssaop.OpAdd64, ssaop.OpAdd32, ssaop.OpAdd16, ssaop.OpAdd8:1941		x := ft.limits[v.Args[0].ID]1942		y := ft.limits[v.Args[1].ID]1943		if !unsignedAddOverflows(x.Umax, y.Umax, v.Type) {1944			r := gt1945			if x.MaybeZero() {1946				r |= eq1947			}1948			ft.update(b, v, v.Args[1], unsigned, r)1949			r = gt1950			if y.MaybeZero() {1951				r |= eq1952			}1953			ft.update(b, v, v.Args[0], unsigned, r)1954		}1955		if x.Min >= 0 && !signedAddOverflowsOrUnderflows(x.Max, y.Max, v.Type) {1956			r := gt1957			if x.MaybeZero() {1958				r |= eq1959			}1960			ft.update(b, v, v.Args[1], signed, r)1961		}1962		if y.Min >= 0 && !signedAddOverflowsOrUnderflows(x.Max, y.Max, v.Type) {1963			r := gt1964			if y.MaybeZero() {1965				r |= eq1966			}1967			ft.update(b, v, v.Args[0], signed, r)1968		}1969		if x.Max <= 0 && !signedAddOverflowsOrUnderflows(x.Min, y.Min, v.Type) {1970			r := lt1971			if x.MaybeZero() {1972				r |= eq1973			}1974			ft.update(b, v, v.Args[1], signed, r)1975		}1976		if y.Max <= 0 && !signedAddOverflowsOrUnderflows(x.Min, y.Min, v.Type) {1977			r := lt1978			if y.MaybeZero() {1979				r |= eq1980			}1981			ft.update(b, v, v.Args[0], signed, r)1982		}1983	case ssaop.OpSub64, ssaop.OpSub32, ssaop.OpSub16, ssaop.OpSub8:1984		x := ft.limits[v.Args[0].ID]1985		y := ft.limits[v.Args[1].ID]1986		if !unsignedSubUnderflows(x.Umin, y.Umax) {1987			r := lt1988			if y.MaybeZero() {1989				r |= eq1990			}1991			ft.update(b, v, v.Args[0], unsigned, r)1992		}1993		// FIXME: we could also do signed facts but the overflow checks are much trickier and I don't need it yet.1994	case ssaop.OpAnd64, ssaop.OpAnd32, ssaop.OpAnd16, ssaop.OpAnd8:1995		ft.update(b, v, v.Args[0], unsigned, lt|eq)1996		ft.update(b, v, v.Args[1], unsigned, lt|eq)1997		if ft.isNonNegative(v.Args[0]) {1998			ft.update(b, v, v.Args[0], signed, lt|eq)1999		}2000		if ft.isNonNegative(v.Args[1]) {

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.