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

Code quality findings 22

Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var opMin = map[Op]int64{
Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var opMax = map[Op]int64{
Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var opUMax = map[Op]uint64{
Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var ctzNonZeroOp = map[Op]Op{
Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var mostNegativeDividend = map[Op]int64{
Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var unsignedOp = map[Op]Op{
Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
warning correctness nil-map-write
var invertEqNeqOp = map[Op]Op{
Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
warning correctness defer-in-loop
defer func() {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, ds := range domainStrings {
Map created without size hint before being populated in a loop; provide capacity hint to reduce allocations
info performance map-without-size-hint
ft.orderings = make(map[ID]*ordering)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
ft.limitStack = append(ft.limitStack, limitFact{v.ID, oldLim})
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
ft.limitStack = append(ft.limitStack, checkpointBound)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
ft.orderingsStack = append(ft.orderingsStack, 0)
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
indVars[v.entry] = append(indVars[v.entry], v)
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
switch v.Op {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
if ft.lens == nil {
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
work = append(work, bp{
Multiple appends without pre-allocation; use make() with capacity when size is known
info performance append-without-prealloc
work = append(work, bp{
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, e := range p.Succs {
Deeply nested control structures reduce readability; consider extracting to functions or using early returns
info maintainability deep-nesting
// for debugging and especially for test/prove.go.
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, arg := range v.Args {
Range over slice copies each element by value; use index or pointer receiver for large structs to avoid copies
info performance copy-large-struct
for i, branch := range [...]branch{positive, negative} {

Get this view in your editor

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