]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: add unsigned power-of-two detector
authorCuong Manh Le <cuong.manhle.vn@gmail.com>
Wed, 23 Jul 2025 11:48:18 +0000 (18:48 +0700)
committerGopher Robot <gobot@golang.org>
Tue, 29 Jul 2025 23:22:37 +0000 (16:22 -0700)
Fixes #74485

Change-Id: Ia22a58ac43bdc36c8414d555672a3a3eafc749ca
Reviewed-on: https://go-review.googlesource.com/c/go/+/689815
Reviewed-by: Keith Randall <khr@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Keith Randall <khr@google.com>
Auto-Submit: Cuong Manh Le <cuong.manhle.vn@gmail.com>

src/cmd/compile/internal/ssa/_gen/generic.rules
src/cmd/compile/internal/ssa/rewrite.go
src/cmd/compile/internal/ssa/rewritegeneric.go
test/codegen/issue74485.go [new file with mode: 0644]

index 40966063d7dcd8052533d1cc3491c8d09ee88488..533dd96d8b0d363109f05ae2f9f6e1b37fd976a3 100644 (file)
 // See ../magic.go for a detailed description of these algorithms.
 
 // Unsigned divide by power of 2.  Strength reduce to a shift.
-(Div8u  n (Const8  [c])) && isPowerOfTwo(c) => (Rsh8Ux64  n (Const64 <typ.UInt64> [log8(c)]))
-(Div16u n (Const16 [c])) && isPowerOfTwo(c) => (Rsh16Ux64 n (Const64 <typ.UInt64> [log16(c)]))
-(Div32u n (Const32 [c])) && isPowerOfTwo(c) => (Rsh32Ux64 n (Const64 <typ.UInt64> [log32(c)]))
-(Div64u n (Const64 [c])) && isPowerOfTwo(c) => (Rsh64Ux64 n (Const64 <typ.UInt64> [log64(c)]))
-(Div64u n (Const64 [-1<<63]))                 => (Rsh64Ux64 n (Const64 <typ.UInt64> [63]))
+(Div8u  n (Const8  [c])) && isUnsignedPowerOfTwo(uint8(c)) => (Rsh8Ux64  n (Const64 <typ.UInt64> [log8u(uint8(c))]))
+(Div16u n (Const16 [c])) && isUnsignedPowerOfTwo(uint16(c)) => (Rsh16Ux64 n (Const64 <typ.UInt64> [log16u(uint16(c))]))
+(Div32u n (Const32 [c])) && isUnsignedPowerOfTwo(uint32(c)) => (Rsh32Ux64 n (Const64 <typ.UInt64> [log32u(uint32(c))]))
+(Div64u n (Const64 [c])) && isUnsignedPowerOfTwo(uint64(c)) => (Rsh64Ux64 n (Const64 <typ.UInt64> [log64u(uint64(c))]))
 
 // Signed non-negative divide by power of 2.
 (Div8  n (Const8  [c])) && isNonNegative(n) && isPowerOfTwo(c) => (Rsh8Ux64  n (Const64 <typ.UInt64> [log8(c)]))
index 9185d46499d80959a00115c576d41e25ab9e8967..0a1a680bab7d09adc8e1f286878f2ddbb0732618 100644 (file)
@@ -493,6 +493,13 @@ func log64(n int64) int64 {
        return int64(bits.Len64(uint64(n))) - 1
 }
 
+// logXu returns the logarithm of n base 2.
+// n must be a power of 2 (isUnsignedPowerOfTwo returns true)
+func log8u(n uint8) int64   { return int64(bits.Len8(n)) - 1 }
+func log16u(n uint16) int64 { return int64(bits.Len16(n)) - 1 }
+func log32u(n uint32) int64 { return int64(bits.Len32(n)) - 1 }
+func log64u(n uint64) int64 { return int64(bits.Len64(n)) - 1 }
+
 // log2uint32 returns logarithm in base 2 of uint32(n), with log2(0) = -1.
 // Rounds down.
 func log2uint32(n int64) int64 {
@@ -504,6 +511,11 @@ func isPowerOfTwo[T int8 | int16 | int32 | int64](n T) bool {
        return n > 0 && n&(n-1) == 0
 }
 
+// isUnsignedPowerOfTwo reports whether n is an unsigned power of 2.
+func isUnsignedPowerOfTwo[T uint8 | uint16 | uint32 | uint64](n T) bool {
+       return n != 0 && n&(n-1) == 0
+}
+
 // isUint64PowerOfTwo reports whether uint64(n) is a power of 2.
 func isUint64PowerOfTwo(in int64) bool {
        n := uint64(in)
index f36e0b270fd5976973a3aba58c4c532759dd8e40..9303b96822014060caa0c2abe231e30d14d45e4a 100644 (file)
@@ -6990,20 +6990,20 @@ func rewriteValuegeneric_OpDiv16u(v *Value) bool {
                return true
        }
        // match: (Div16u n (Const16 [c]))
-       // cond: isPowerOfTwo(c)
-       // result: (Rsh16Ux64 n (Const64 <typ.UInt64> [log16(c)]))
+       // cond: isUnsignedPowerOfTwo(uint16(c))
+       // result: (Rsh16Ux64 n (Const64 <typ.UInt64> [log16u(uint16(c))]))
        for {
                n := v_0
                if v_1.Op != OpConst16 {
                        break
                }
                c := auxIntToInt16(v_1.AuxInt)
-               if !(isPowerOfTwo(c)) {
+               if !(isUnsignedPowerOfTwo(uint16(c))) {
                        break
                }
                v.reset(OpRsh16Ux64)
                v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64)
-               v0.AuxInt = int64ToAuxInt(log16(c))
+               v0.AuxInt = int64ToAuxInt(log16u(uint16(c)))
                v.AddArg2(n, v0)
                return true
        }
@@ -7400,20 +7400,20 @@ func rewriteValuegeneric_OpDiv32u(v *Value) bool {
                return true
        }
        // match: (Div32u n (Const32 [c]))
-       // cond: isPowerOfTwo(c)
-       // result: (Rsh32Ux64 n (Const64 <typ.UInt64> [log32(c)]))
+       // cond: isUnsignedPowerOfTwo(uint32(c))
+       // result: (Rsh32Ux64 n (Const64 <typ.UInt64> [log32u(uint32(c))]))
        for {
                n := v_0
                if v_1.Op != OpConst32 {
                        break
                }
                c := auxIntToInt32(v_1.AuxInt)
-               if !(isPowerOfTwo(c)) {
+               if !(isUnsignedPowerOfTwo(uint32(c))) {
                        break
                }
                v.reset(OpRsh32Ux64)
                v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64)
-               v0.AuxInt = int64ToAuxInt(log32(c))
+               v0.AuxInt = int64ToAuxInt(log32u(uint32(c)))
                v.AddArg2(n, v0)
                return true
        }
@@ -7839,33 +7839,20 @@ func rewriteValuegeneric_OpDiv64u(v *Value) bool {
                return true
        }
        // match: (Div64u n (Const64 [c]))
-       // cond: isPowerOfTwo(c)
-       // result: (Rsh64Ux64 n (Const64 <typ.UInt64> [log64(c)]))
+       // cond: isUnsignedPowerOfTwo(uint64(c))
+       // result: (Rsh64Ux64 n (Const64 <typ.UInt64> [log64u(uint64(c))]))
        for {
                n := v_0
                if v_1.Op != OpConst64 {
                        break
                }
                c := auxIntToInt64(v_1.AuxInt)
-               if !(isPowerOfTwo(c)) {
+               if !(isUnsignedPowerOfTwo(uint64(c))) {
                        break
                }
                v.reset(OpRsh64Ux64)
                v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64)
-               v0.AuxInt = int64ToAuxInt(log64(c))
-               v.AddArg2(n, v0)
-               return true
-       }
-       // match: (Div64u n (Const64 [-1<<63]))
-       // result: (Rsh64Ux64 n (Const64 <typ.UInt64> [63]))
-       for {
-               n := v_0
-               if v_1.Op != OpConst64 || auxIntToInt64(v_1.AuxInt) != -1<<63 {
-                       break
-               }
-               v.reset(OpRsh64Ux64)
-               v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64)
-               v0.AuxInt = int64ToAuxInt(63)
+               v0.AuxInt = int64ToAuxInt(log64u(uint64(c)))
                v.AddArg2(n, v0)
                return true
        }
@@ -8175,20 +8162,20 @@ func rewriteValuegeneric_OpDiv8u(v *Value) bool {
                return true
        }
        // match: (Div8u n (Const8 [c]))
-       // cond: isPowerOfTwo(c)
-       // result: (Rsh8Ux64 n (Const64 <typ.UInt64> [log8(c)]))
+       // cond: isUnsignedPowerOfTwo(uint8(c))
+       // result: (Rsh8Ux64 n (Const64 <typ.UInt64> [log8u(uint8(c))]))
        for {
                n := v_0
                if v_1.Op != OpConst8 {
                        break
                }
                c := auxIntToInt8(v_1.AuxInt)
-               if !(isPowerOfTwo(c)) {
+               if !(isUnsignedPowerOfTwo(uint8(c))) {
                        break
                }
                v.reset(OpRsh8Ux64)
                v0 := b.NewValue0(v.Pos, OpConst64, typ.UInt64)
-               v0.AuxInt = int64ToAuxInt(log8(c))
+               v0.AuxInt = int64ToAuxInt(log8u(uint8(c)))
                v.AddArg2(n, v0)
                return true
        }
diff --git a/test/codegen/issue74485.go b/test/codegen/issue74485.go
new file mode 100644 (file)
index 0000000..5707075
--- /dev/null
@@ -0,0 +1,27 @@
+// asmcheck
+
+// 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 codegen
+
+func divUint64(b uint64) uint64 {
+       // amd64:"SHRQ  [$]63, AX"
+       return b / 9223372036854775808
+}
+
+func divUint32(b uint32) uint32 {
+       // amd64:"SHRL  [$]31, AX"
+       return b / 2147483648
+}
+
+func divUint16(b uint16) uint16 {
+       // amd64:"SHRW  [$]15, AX"
+       return b / 32768
+}
+
+func divUint8(b uint8) uint8 {
+       // amd64:"SHRB  [$]7, AL"
+       return b / 128
+}