return x>>b == 0
}
+func noLimitForBitsize(bitsize uint) limit {
+ return limit{min: -(1 << (bitsize - 1)), max: 1<<(bitsize-1) - 1, umin: 0, umax: 1<<bitsize - 1}
+}
+
+func convertIntWithBitsize[Target uint64 | int64, Source uint64 | int64](x Source, bitsize uint) Target {
+ switch bitsize {
+ case 64:
+ return Target(x)
+ case 32:
+ return Target(int32(x))
+ case 16:
+ return Target(int16(x))
+ case 8:
+ return Target(int8(x))
+ default:
+ panic("unreachable")
+ }
+}
+
// add returns the limit obtained by adding a value with limit l
// to a value with limit l2. The result must fit in b bits.
func (l limit) add(l2 limit, b uint) limit {
+ var isLConst, isL2Const bool
+ var lConst, l2Const uint64
+ if l.min == l.max {
+ isLConst = true
+ lConst = convertIntWithBitsize[uint64](l.min, b)
+ } else if l.umin == l.umax {
+ isLConst = true
+ lConst = l.umin
+ }
+ if l2.min == l2.max {
+ isL2Const = true
+ l2Const = convertIntWithBitsize[uint64](l2.min, b)
+ } else if l2.umin == l2.umax {
+ isL2Const = true
+ l2Const = l2.umin
+ }
+ if isLConst && isL2Const {
+ r := lConst + l2Const
+ r &= (uint64(1) << b) - 1
+ int64r := convertIntWithBitsize[int64](r, b)
+ return limit{min: int64r, max: int64r, umin: r, umax: r}
+ }
+
r := noLimit
min, minOk := safeAdd(l.min, l2.min, b)
max, maxOk := safeAdd(l.max, l2.max, b)
}
}
+// Similar to add, but computes the negation of the limit for bitsize b.
+func (l limit) neg(b uint) limit {
+ return l.com(b).add(limit{min: 1, max: 1, umin: 1, umax: 1}, b)
+}
+
var noLimit = limit{math.MinInt64, math.MaxInt64, 0, math.MaxUint64}
// a limitFact is a limit known for a particular value.
}
// Default limits based on type.
- bitsize := v.Type.Size() * 8
- lim := limit{min: -(1 << (bitsize - 1)), max: 1<<(bitsize-1) - 1, umin: 0, umax: 1<<bitsize - 1}
+ lim := noLimitForBitsize(uint(v.Type.Size()) * 8)
// Tighter limits on some opcodes.
switch v.Op {
case OpNeg64, OpNeg32, OpNeg16, OpNeg8:
a := ft.limits[v.Args[0].ID]
bitsize := uint(v.Type.Size()) * 8
- ft.newLimit(v, a.com(bitsize).add(limit{min: 1, max: 1, umin: 1, umax: 1}, bitsize))
+ ft.newLimit(v, a.neg(bitsize))
case OpMul64, OpMul32, OpMul16, OpMul8:
a := ft.limits[v.Args[0].ID]
b := ft.limits[v.Args[1].ID]
--- /dev/null
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ssa
+
+import (
+ "math"
+ "testing"
+)
+
+func testLimitUnaryOpSigned8(t *testing.T, opName string, op func(l limit, bitsize uint) limit, opImpl func(int8) int8) {
+ sizeLimit := noLimitForBitsize(8)
+ for min := math.MinInt8; min <= math.MaxInt8; min++ {
+ for max := min; max <= math.MaxInt8; max++ {
+ realSmallest, realBiggest := int8(math.MaxInt8), int8(math.MinInt8)
+ for i := min; i <= max; i++ {
+ result := opImpl(int8(i))
+ if result < realSmallest {
+ realSmallest = result
+ }
+ if result > realBiggest {
+ realBiggest = result
+ }
+ }
+
+ l := limit{int64(min), int64(max), 0, math.MaxUint64}
+ l = op(l, 8)
+ l = l.intersect(sizeLimit) // We assume this is gonna be used by newLimit which is seeded by the op size already.
+
+ if l.min != int64(realSmallest) || l.max != int64(realBiggest) {
+ t.Errorf("%s(%d..%d) = %d..%d; want %d..%d", opName, min, max, l.min, l.max, realSmallest, realBiggest)
+ }
+ }
+ }
+}
+
+func testLimitUnaryOpUnsigned8(t *testing.T, opName string, op func(l limit, bitsize uint) limit, opImpl func(uint8) uint8) {
+ sizeLimit := noLimitForBitsize(8)
+ for min := 0; min <= math.MaxUint8; min++ {
+ for max := min; max <= math.MaxUint8; max++ {
+ realSmallest, realBiggest := uint8(math.MaxUint8), uint8(0)
+ for i := min; i <= max; i++ {
+ result := opImpl(uint8(i))
+ if result < realSmallest {
+ realSmallest = result
+ }
+ if result > realBiggest {
+ realBiggest = result
+ }
+ }
+
+ l := limit{math.MinInt64, math.MaxInt64, uint64(min), uint64(max)}
+ l = op(l, 8)
+ l = l.intersect(sizeLimit) // We assume this is gonna be used by newLimit which is seeded by the op size already.
+
+ if l.umin != uint64(realSmallest) || l.umax != uint64(realBiggest) {
+ t.Errorf("%s(%d..%d) = %d..%d; want %d..%d", opName, min, max, l.umin, l.umax, realSmallest, realBiggest)
+ }
+ }
+ }
+}
+
+func TestLimitNegSigned(t *testing.T) {
+ testLimitUnaryOpSigned8(t, "neg", limit.neg, func(x int8) int8 { return -x })
+}
+func TestLimitNegUnsigned(t *testing.T) {
+ testLimitUnaryOpUnsigned8(t, "neg", limit.neg, func(x uint8) uint8 { return -x })
+}