From 50b11318febd74c26cc466cc4557347bcb53181a Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 20 Apr 2020 14:43:30 -0700 Subject: [PATCH] cmd/compile: use oneBit instead of isPowerOfTwo in bit optimization This optimization works on any integer with exactly one bit set. This is identical to being a power of two, except in the most negative number. Use oneBit instead. The rule now triggers in a few more places in std+cmd, in packages encoding/asn1, crypto/elliptic, and vendor/golang.org/x/crypto/cryptobyte. This change obviates the need for CL 222479 by doing this optimization consistently in the compiler. Change-Id: I983c6235290fdc634fda5e11b10f1f8ce041272f Reviewed-on: https://go-review.googlesource.com/c/go/+/229124 Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick --- .../compile/internal/ssa/gen/generic.rules | 16 +++++----- src/cmd/compile/internal/ssa/rewrite.go | 8 +++-- .../compile/internal/ssa/rewritegeneric.go | 32 +++++++++---------- test/codegen/bits.go | 5 +++ 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules index e81e7d733e6..9a66312c901 100644 --- a/src/cmd/compile/internal/ssa/gen/generic.rules +++ b/src/cmd/compile/internal/ssa/gen/generic.rules @@ -1775,21 +1775,21 @@ (Neq(8|16|32|64) s:(Sub(8|16|32|64) x y) (Const(8|16|32|64) [0])) && s.Uses == 1 -> (Neq(8|16|32|64) x y) // Optimize bitsets -(Eq8 (And8 x (Const8 [y])) (Const8 [y])) && isPowerOfTwo8(y) +(Eq8 (And8 x (Const8 [y])) (Const8 [y])) && oneBit8(y) => (Neq8 (And8 x (Const8 [y])) (Const8 [0])) -(Eq16 (And16 x (Const16 [y])) (Const16 [y])) && isPowerOfTwo16(y) +(Eq16 (And16 x (Const16 [y])) (Const16 [y])) && oneBit16(y) => (Neq16 (And16 x (Const16 [y])) (Const16 [0])) -(Eq32 (And32 x (Const32 [y])) (Const32 [y])) && isPowerOfTwo32(y) +(Eq32 (And32 x (Const32 [y])) (Const32 [y])) && oneBit32(y) => (Neq32 (And32 x (Const32 [y])) (Const32 [0])) -(Eq64 (And64 x (Const64 [y])) (Const64 [y])) && isPowerOfTwo64(y) +(Eq64 (And64 x (Const64 [y])) (Const64 [y])) && oneBit64(y) => (Neq64 (And64 x (Const64 [y])) (Const64 [0])) -(Neq8 (And8 x (Const8 [y])) (Const8 [y])) && isPowerOfTwo8(y) +(Neq8 (And8 x (Const8 [y])) (Const8 [y])) && oneBit8(y) => (Eq8 (And8 x (Const8 [y])) (Const8 [0])) -(Neq16 (And16 x (Const16 [y])) (Const16 [y])) && isPowerOfTwo16(y) +(Neq16 (And16 x (Const16 [y])) (Const16 [y])) && oneBit16(y) => (Eq16 (And16 x (Const16 [y])) (Const16 [0])) -(Neq32 (And32 x (Const32 [y])) (Const32 [y])) && isPowerOfTwo32(y) +(Neq32 (And32 x (Const32 [y])) (Const32 [y])) && oneBit32(y) => (Eq32 (And32 x (Const32 [y])) (Const32 [0])) -(Neq64 (And64 x (Const64 [y])) (Const64 [y])) && isPowerOfTwo64(y) +(Neq64 (And64 x (Const64 [y])) (Const64 [y])) && oneBit64(y) => (Eq64 (And64 x (Const64 [y])) (Const64 [0])) // Reassociate expressions involving diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go index 3681daaebfb..ca327c9f0f2 100644 --- a/src/cmd/compile/internal/ssa/rewrite.go +++ b/src/cmd/compile/internal/ssa/rewrite.go @@ -395,9 +395,11 @@ func ntz32(x int32) int { return bits.TrailingZeros32(uint32(x)) } func ntz16(x int16) int { return bits.TrailingZeros16(uint16(x)) } func ntz8(x int8) int { return bits.TrailingZeros8(uint8(x)) } -func oneBit(x int64) bool { - return bits.OnesCount64(uint64(x)) == 1 -} +func oneBit(x int64) bool { return bits.OnesCount64(uint64(x)) == 1 } +func oneBit8(x int8) bool { return bits.OnesCount8(uint8(x)) == 1 } +func oneBit16(x int16) bool { return bits.OnesCount16(uint16(x)) == 1 } +func oneBit32(x int32) bool { return bits.OnesCount32(uint32(x)) == 1 } +func oneBit64(x int64) bool { return bits.OnesCount64(uint64(x)) == 1 } // nlo returns the number of leading ones. func nlo(x int64) int64 { diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go index 5ef9ac5d30c..1fabe738d4c 100644 --- a/src/cmd/compile/internal/ssa/rewritegeneric.go +++ b/src/cmd/compile/internal/ssa/rewritegeneric.go @@ -6313,7 +6313,7 @@ func rewriteValuegeneric_OpEq16(v *Value) bool { break } // match: (Eq16 (And16 x (Const16 [y])) (Const16 [y])) - // cond: isPowerOfTwo16(y) + // cond: oneBit16(y) // result: (Neq16 (And16 x (Const16 [y])) (Const16 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -6330,7 +6330,7 @@ func rewriteValuegeneric_OpEq16(v *Value) bool { continue } y := auxIntToInt16(v_0_1.AuxInt) - if v_1.Op != OpConst16 || v_1.Type != t || auxIntToInt16(v_1.AuxInt) != y || !(isPowerOfTwo16(y)) { + if v_1.Op != OpConst16 || v_1.Type != t || auxIntToInt16(v_1.AuxInt) != y || !(oneBit16(y)) { continue } v.reset(OpNeq16) @@ -7177,7 +7177,7 @@ func rewriteValuegeneric_OpEq32(v *Value) bool { break } // match: (Eq32 (And32 x (Const32 [y])) (Const32 [y])) - // cond: isPowerOfTwo32(y) + // cond: oneBit32(y) // result: (Neq32 (And32 x (Const32 [y])) (Const32 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -7194,7 +7194,7 @@ func rewriteValuegeneric_OpEq32(v *Value) bool { continue } y := auxIntToInt32(v_0_1.AuxInt) - if v_1.Op != OpConst32 || v_1.Type != t || auxIntToInt32(v_1.AuxInt) != y || !(isPowerOfTwo32(y)) { + if v_1.Op != OpConst32 || v_1.Type != t || auxIntToInt32(v_1.AuxInt) != y || !(oneBit32(y)) { continue } v.reset(OpNeq32) @@ -7758,7 +7758,7 @@ func rewriteValuegeneric_OpEq64(v *Value) bool { break } // match: (Eq64 (And64 x (Const64 [y])) (Const64 [y])) - // cond: isPowerOfTwo64(y) + // cond: oneBit64(y) // result: (Neq64 (And64 x (Const64 [y])) (Const64 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -7775,7 +7775,7 @@ func rewriteValuegeneric_OpEq64(v *Value) bool { continue } y := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 || v_1.Type != t || auxIntToInt64(v_1.AuxInt) != y || !(isPowerOfTwo64(y)) { + if v_1.Op != OpConst64 || v_1.Type != t || auxIntToInt64(v_1.AuxInt) != y || !(oneBit64(y)) { continue } v.reset(OpNeq64) @@ -8180,7 +8180,7 @@ func rewriteValuegeneric_OpEq8(v *Value) bool { break } // match: (Eq8 (And8 x (Const8 [y])) (Const8 [y])) - // cond: isPowerOfTwo8(y) + // cond: oneBit8(y) // result: (Neq8 (And8 x (Const8 [y])) (Const8 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -8197,7 +8197,7 @@ func rewriteValuegeneric_OpEq8(v *Value) bool { continue } y := auxIntToInt8(v_0_1.AuxInt) - if v_1.Op != OpConst8 || v_1.Type != t || auxIntToInt8(v_1.AuxInt) != y || !(isPowerOfTwo8(y)) { + if v_1.Op != OpConst8 || v_1.Type != t || auxIntToInt8(v_1.AuxInt) != y || !(oneBit8(y)) { continue } v.reset(OpNeq8) @@ -14907,7 +14907,7 @@ func rewriteValuegeneric_OpNeq16(v *Value) bool { break } // match: (Neq16 (And16 x (Const16 [y])) (Const16 [y])) - // cond: isPowerOfTwo16(y) + // cond: oneBit16(y) // result: (Eq16 (And16 x (Const16 [y])) (Const16 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -14924,7 +14924,7 @@ func rewriteValuegeneric_OpNeq16(v *Value) bool { continue } y := auxIntToInt16(v_0_1.AuxInt) - if v_1.Op != OpConst16 || v_1.Type != t || auxIntToInt16(v_1.AuxInt) != y || !(isPowerOfTwo16(y)) { + if v_1.Op != OpConst16 || v_1.Type != t || auxIntToInt16(v_1.AuxInt) != y || !(oneBit16(y)) { continue } v.reset(OpEq16) @@ -15094,7 +15094,7 @@ func rewriteValuegeneric_OpNeq32(v *Value) bool { break } // match: (Neq32 (And32 x (Const32 [y])) (Const32 [y])) - // cond: isPowerOfTwo32(y) + // cond: oneBit32(y) // result: (Eq32 (And32 x (Const32 [y])) (Const32 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -15111,7 +15111,7 @@ func rewriteValuegeneric_OpNeq32(v *Value) bool { continue } y := auxIntToInt32(v_0_1.AuxInt) - if v_1.Op != OpConst32 || v_1.Type != t || auxIntToInt32(v_1.AuxInt) != y || !(isPowerOfTwo32(y)) { + if v_1.Op != OpConst32 || v_1.Type != t || auxIntToInt32(v_1.AuxInt) != y || !(oneBit32(y)) { continue } v.reset(OpEq32) @@ -15304,7 +15304,7 @@ func rewriteValuegeneric_OpNeq64(v *Value) bool { break } // match: (Neq64 (And64 x (Const64 [y])) (Const64 [y])) - // cond: isPowerOfTwo64(y) + // cond: oneBit64(y) // result: (Eq64 (And64 x (Const64 [y])) (Const64 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -15321,7 +15321,7 @@ func rewriteValuegeneric_OpNeq64(v *Value) bool { continue } y := auxIntToInt64(v_0_1.AuxInt) - if v_1.Op != OpConst64 || v_1.Type != t || auxIntToInt64(v_1.AuxInt) != y || !(isPowerOfTwo64(y)) { + if v_1.Op != OpConst64 || v_1.Type != t || auxIntToInt64(v_1.AuxInt) != y || !(oneBit64(y)) { continue } v.reset(OpEq64) @@ -15514,7 +15514,7 @@ func rewriteValuegeneric_OpNeq8(v *Value) bool { break } // match: (Neq8 (And8 x (Const8 [y])) (Const8 [y])) - // cond: isPowerOfTwo8(y) + // cond: oneBit8(y) // result: (Eq8 (And8 x (Const8 [y])) (Const8 [0])) for { for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 { @@ -15531,7 +15531,7 @@ func rewriteValuegeneric_OpNeq8(v *Value) bool { continue } y := auxIntToInt8(v_0_1.AuxInt) - if v_1.Op != OpConst8 || v_1.Type != t || auxIntToInt8(v_1.AuxInt) != y || !(isPowerOfTwo8(y)) { + if v_1.Op != OpConst8 || v_1.Type != t || auxIntToInt8(v_1.AuxInt) != y || !(oneBit8(y)) { continue } v.reset(OpEq8) diff --git a/test/codegen/bits.go b/test/codegen/bits.go index 18f9daf7cf3..0a5428b55a3 100644 --- a/test/codegen/bits.go +++ b/test/codegen/bits.go @@ -278,6 +278,11 @@ func bitOpOnMem(a []uint32) { a[5] ^= 0x2000 } +func bitcheckMostNegative(b uint8) bool { + // amd64:"TESTB" + return b&0x80 == 0x80 +} + // Check AND masking on arm64 (Issue #19857) func and_mask_1(a uint64) uint64 {