Skip to content

Commit 0a56952

Browse files
Michael Mundaygopherbot
authored andcommitted
cmd/compile: optimize comparisons with single bit difference
Optimize comparisons with constants that only differ by 1 bit (i.e. a power of 2). For example: x == 4 || x == 6 -> x|2 == 6 x != 1 && x != 5 -> x|4 != 5 Change-Id: Ic61719e5118446d21cf15652d9da22f7d95b2a15 Reviewed-on: https://go-review.googlesource.com/c/go/+/719420 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Junyang Shao <[email protected]> Auto-Submit: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent 1e5e666 commit 0a56952

File tree

5 files changed

+530
-1
lines changed

5 files changed

+530
-1
lines changed

src/cmd/compile/internal/ssa/_gen/generic.rules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,12 @@
337337
(OrB ((Less|Leq)16U (Const16 [c]) x) (Leq16U x (Const16 [d]))) && uint16(c) >= uint16(d+1) && uint16(d+1) > uint16(d) => ((Less|Leq)16U (Const16 <x.Type> [c-d-1]) (Sub16 <x.Type> x (Const16 <x.Type> [d+1])))
338338
(OrB ((Less|Leq)8U (Const8 [c]) x) (Leq8U x (Const8 [d]))) && uint8(c) >= uint8(d+1) && uint8(d+1) > uint8(d) => ((Less|Leq)8U (Const8 <x.Type> [c-d-1]) (Sub8 <x.Type> x (Const8 <x.Type> [d+1])))
339339

340+
// single bit difference: ( x != c && x != d ) -> ( x|(c^d) != c )
341+
(AndB (Neq(64|32|16|8) x cv:(Const(64|32|16|8) [c])) (Neq(64|32|16|8) x (Const(64|32|16|8) [d]))) && c|d == c && oneBit(c^d) => (Neq(64|32|16|8) (Or(64|32|16|8) <x.Type> x (Const(64|32|16|8) <x.Type> [c^d])) cv)
342+
343+
// single bit difference: ( x == c || x == d ) -> ( x|(c^d) == c )
344+
(OrB (Eq(64|32|16|8) x cv:(Const(64|32|16|8) [c])) (Eq(64|32|16|8) x (Const(64|32|16|8) [d]))) && c|d == c && oneBit(c^d) => (Eq(64|32|16|8) (Or(64|32|16|8) <x.Type> x (Const(64|32|16|8) <x.Type> [c^d])) cv)
345+
340346
// NaN check: ( x != x || x (>|>=|<|<=) c ) -> ( !(c (>=|>|<=|<) x) )
341347
(OrB (Neq64F x x) ((Less|Leq)64F x y:(Const64F [c]))) => (Not ((Leq|Less)64F y x))
342348
(OrB (Neq64F x x) ((Less|Leq)64F y:(Const64F [c]) x)) => (Not ((Leq|Less)64F x y))

src/cmd/compile/internal/ssa/fuse.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
)
1111

1212
// fuseEarly runs fuse(f, fuseTypePlain|fuseTypeIntInRange|fuseTypeNanCheck).
13-
func fuseEarly(f *Func) { fuse(f, fuseTypePlain|fuseTypeIntInRange|fuseTypeNanCheck) }
13+
func fuseEarly(f *Func) {
14+
fuse(f, fuseTypePlain|fuseTypeIntInRange|fuseTypeSingleBitDifference|fuseTypeNanCheck)
15+
}
1416

1517
// fuseLate runs fuse(f, fuseTypePlain|fuseTypeIf|fuseTypeBranchRedirect).
1618
func fuseLate(f *Func) { fuse(f, fuseTypePlain|fuseTypeIf|fuseTypeBranchRedirect) }
@@ -21,6 +23,7 @@ const (
2123
fuseTypePlain fuseType = 1 << iota
2224
fuseTypeIf
2325
fuseTypeIntInRange
26+
fuseTypeSingleBitDifference
2427
fuseTypeNanCheck
2528
fuseTypeBranchRedirect
2629
fuseTypeShortCircuit
@@ -41,6 +44,9 @@ func fuse(f *Func, typ fuseType) {
4144
if typ&fuseTypeIntInRange != 0 {
4245
changed = fuseIntInRange(b) || changed
4346
}
47+
if typ&fuseTypeSingleBitDifference != 0 {
48+
changed = fuseSingleBitDifference(b) || changed
49+
}
4450
if typ&fuseTypeNanCheck != 0 {
4551
changed = fuseNanCheck(b) || changed
4652
}

src/cmd/compile/internal/ssa/fuse_comparisons.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ func fuseNanCheck(b *Block) bool {
1919
return fuseComparisons(b, canOptNanCheck)
2020
}
2121

22+
// fuseSingleBitDifference replaces the short-circuit operators between equality checks with
23+
// constants that only differ by a single bit. For example, it would convert
24+
// `if x == 4 || x == 6 { ... }` into `if (x == 4) | (x == 6) { ... }`. Rewrite rules can
25+
// then optimize these using a bitwise operation, in this case generating `if x|2 == 6 { ... }`.
26+
func fuseSingleBitDifference(b *Block) bool {
27+
return fuseComparisons(b, canOptSingleBitDifference)
28+
}
29+
2230
// fuseComparisons looks for control graphs that match this pattern:
2331
//
2432
// p - predecessor
@@ -229,3 +237,40 @@ func canOptNanCheck(x, y *Value, op Op) bool {
229237
}
230238
return false
231239
}
240+
241+
// canOptSingleBitDifference returns true if x op y matches either:
242+
//
243+
// v == c || v == d
244+
// v != c && v != d
245+
//
246+
// Where c and d are constant values that differ by a single bit.
247+
func canOptSingleBitDifference(x, y *Value, op Op) bool {
248+
if x.Op != y.Op {
249+
return false
250+
}
251+
switch x.Op {
252+
case OpEq64, OpEq32, OpEq16, OpEq8:
253+
if op != OpOrB {
254+
return false
255+
}
256+
case OpNeq64, OpNeq32, OpNeq16, OpNeq8:
257+
if op != OpAndB {
258+
return false
259+
}
260+
default:
261+
return false
262+
}
263+
264+
xi := getConstIntArgIndex(x)
265+
if xi < 0 {
266+
return false
267+
}
268+
yi := getConstIntArgIndex(y)
269+
if yi < 0 {
270+
return false
271+
}
272+
if x.Args[xi^1] != y.Args[yi^1] {
273+
return false
274+
}
275+
return oneBit(x.Args[xi].AuxInt ^ y.Args[yi].AuxInt)
276+
}

0 commit comments

Comments
 (0)