From: Cuong Manh Le Date: Wed, 23 Jul 2025 11:48:18 +0000 (+0700) Subject: cmd/compile: add unsigned power-of-two detector X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f3582fc80e19050d915ac9621bbeecef78c0548e;p=gostls13.git cmd/compile: add unsigned power-of-two detector Fixes #74485 Change-Id: Ia22a58ac43bdc36c8414d555672a3a3eafc749ca Reviewed-on: https://go-review.googlesource.com/c/go/+/689815 Reviewed-by: Keith Randall LUCI-TryBot-Result: Go LUCI Reviewed-by: David Chase Reviewed-by: Keith Randall Auto-Submit: Cuong Manh Le --- diff --git a/src/cmd/compile/internal/ssa/_gen/generic.rules b/src/cmd/compile/internal/ssa/_gen/generic.rules index 40966063d7..533dd96d8b 100644 --- a/src/cmd/compile/internal/ssa/_gen/generic.rules +++ b/src/cmd/compile/internal/ssa/_gen/generic.rules @@ -1014,11 +1014,10 @@ // 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 [log8(c)])) -(Div16u n (Const16 [c])) && isPowerOfTwo(c) => (Rsh16Ux64 n (Const64 [log16(c)])) -(Div32u n (Const32 [c])) && isPowerOfTwo(c) => (Rsh32Ux64 n (Const64 [log32(c)])) -(Div64u n (Const64 [c])) && isPowerOfTwo(c) => (Rsh64Ux64 n (Const64 [log64(c)])) -(Div64u n (Const64 [-1<<63])) => (Rsh64Ux64 n (Const64 [63])) +(Div8u n (Const8 [c])) && isUnsignedPowerOfTwo(uint8(c)) => (Rsh8Ux64 n (Const64 [log8u(uint8(c))])) +(Div16u n (Const16 [c])) && isUnsignedPowerOfTwo(uint16(c)) => (Rsh16Ux64 n (Const64 [log16u(uint16(c))])) +(Div32u n (Const32 [c])) && isUnsignedPowerOfTwo(uint32(c)) => (Rsh32Ux64 n (Const64 [log32u(uint32(c))])) +(Div64u n (Const64 [c])) && isUnsignedPowerOfTwo(uint64(c)) => (Rsh64Ux64 n (Const64 [log64u(uint64(c))])) // Signed non-negative divide by power of 2. (Div8 n (Const8 [c])) && isNonNegative(n) && isPowerOfTwo(c) => (Rsh8Ux64 n (Const64 [log8(c)])) diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 9185d46499..0a1a680bab 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -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) diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index f36e0b270f..9303b96822 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -6990,20 +6990,20 @@ func rewriteValuegeneric_OpDiv16u(v *Value) bool { return true } // match: (Div16u n (Const16 [c])) - // cond: isPowerOfTwo(c) - // result: (Rsh16Ux64 n (Const64 [log16(c)])) + // cond: isUnsignedPowerOfTwo(uint16(c)) + // result: (Rsh16Ux64 n (Const64 [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 [log32(c)])) + // cond: isUnsignedPowerOfTwo(uint32(c)) + // result: (Rsh32Ux64 n (Const64 [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 [log64(c)])) + // cond: isUnsignedPowerOfTwo(uint64(c)) + // result: (Rsh64Ux64 n (Const64 [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 [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 [log8(c)])) + // cond: isUnsignedPowerOfTwo(uint8(c)) + // result: (Rsh8Ux64 n (Const64 [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 index 0000000000..570707509b --- /dev/null +++ b/test/codegen/issue74485.go @@ -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 +}