Skip to content

Commit e37cc29

Browse files
committed
cmd/compile: optimize integer-in-range checks
This CL incorporates code from CL 201206 by Josh Bleecher Snyder (thanks Josh). This CL restores the integer-in-range optimizations in the SSA backend. The fuse pass is enhanced to detect inequalities that could be merged and fuse their associated blocks while the generic rules optimize them into a single unsigned comparison. For example, the inequality `x >= 0 && x < 10` will now be optimized to `unsigned(x) < 10`. Overall has a fairly positive impact on binary sizes. name old time/op new time/op delta Template 192ms ± 1% 192ms ± 1% ~ (p=0.757 n=17+18) Unicode 76.6ms ± 2% 76.5ms ± 2% ~ (p=0.603 n=19+19) GoTypes 694ms ± 1% 693ms ± 1% ~ (p=0.569 n=19+20) Compiler 3.26s ± 0% 3.27s ± 0% +0.25% (p=0.000 n=20+20) SSA 7.41s ± 0% 7.49s ± 0% +1.10% (p=0.000 n=17+19) Flate 120ms ± 1% 120ms ± 1% +0.38% (p=0.003 n=19+19) GoParser 152ms ± 1% 152ms ± 1% ~ (p=0.061 n=17+19) Reflect 422ms ± 1% 425ms ± 2% +0.76% (p=0.001 n=18+20) Tar 167ms ± 1% 167ms ± 0% ~ (p=0.730 n=18+19) XML 233ms ± 4% 231ms ± 1% ~ (p=0.752 n=20+17) LinkCompiler 927ms ± 8% 928ms ± 8% ~ (p=0.857 n=19+20) ExternalLinkCompiler 1.81s ± 2% 1.81s ± 2% ~ (p=0.513 n=19+20) LinkWithoutDebugCompiler 556ms ±10% 583ms ±13% +4.95% (p=0.007 n=20+20) [Geo mean] 478ms 481ms +0.52% name old user-time/op new user-time/op delta Template 270ms ± 5% 269ms ± 7% ~ (p=0.925 n=20+20) Unicode 134ms ± 7% 131ms ±14% ~ (p=0.593 n=18+20) GoTypes 981ms ± 3% 987ms ± 2% +0.63% (p=0.049 n=19+18) Compiler 4.50s ± 2% 4.50s ± 1% ~ (p=0.588 n=19+20) SSA 10.6s ± 2% 10.6s ± 1% ~ (p=0.141 n=20+19) Flate 164ms ± 8% 165ms ±10% ~ (p=0.738 n=20+20) GoParser 202ms ± 5% 203ms ± 6% ~ (p=0.820 n=20+20) Reflect 587ms ± 6% 597ms ± 3% ~ (p=0.087 n=20+18) Tar 230ms ± 6% 228ms ± 8% ~ (p=0.569 n=19+20) XML 311ms ± 6% 314ms ± 5% ~ (p=0.369 n=20+20) LinkCompiler 878ms ± 8% 887ms ± 7% ~ (p=0.289 n=20+20) ExternalLinkCompiler 1.60s ± 7% 1.60s ± 7% ~ (p=0.820 n=20+20) LinkWithoutDebugCompiler 498ms ±12% 489ms ±11% ~ (p=0.398 n=20+20) [Geo mean] 611ms 611ms +0.05% name old alloc/op new alloc/op delta Template 36.1MB ± 0% 36.0MB ± 0% -0.32% (p=0.000 n=20+20) Unicode 28.3MB ± 0% 28.3MB ± 0% -0.03% (p=0.000 n=19+20) GoTypes 121MB ± 0% 121MB ± 0% ~ (p=0.226 n=16+20) Compiler 563MB ± 0% 563MB ± 0% ~ (p=0.166 n=20+19) SSA 1.32GB ± 0% 1.33GB ± 0% +0.88% (p=0.000 n=20+19) Flate 22.7MB ± 0% 22.7MB ± 0% -0.02% (p=0.033 n=19+20) GoParser 27.9MB ± 0% 27.9MB ± 0% -0.02% (p=0.001 n=20+20) Reflect 78.3MB ± 0% 78.2MB ± 0% -0.01% (p=0.019 n=20+20) Tar 34.0MB ± 0% 34.0MB ± 0% -0.04% (p=0.000 n=20+20) XML 43.9MB ± 0% 43.9MB ± 0% -0.07% (p=0.000 n=20+19) LinkCompiler 205MB ± 0% 205MB ± 0% +0.44% (p=0.000 n=20+18) ExternalLinkCompiler 223MB ± 0% 223MB ± 0% +0.03% (p=0.000 n=20+20) LinkWithoutDebugCompiler 139MB ± 0% 142MB ± 0% +1.75% (p=0.000 n=20+20) [Geo mean] 93.7MB 93.9MB +0.20% name old allocs/op new allocs/op delta Template 363k ± 0% 361k ± 0% -0.58% (p=0.000 n=20+19) Unicode 329k ± 0% 329k ± 0% -0.06% (p=0.000 n=19+20) GoTypes 1.28M ± 0% 1.28M ± 0% -0.01% (p=0.000 n=20+20) Compiler 5.40M ± 0% 5.40M ± 0% -0.01% (p=0.000 n=20+20) SSA 12.7M ± 0% 12.8M ± 0% +0.80% (p=0.000 n=20+20) Flate 228k ± 0% 228k ± 0% ~ (p=0.194 n=20+20) GoParser 295k ± 0% 295k ± 0% -0.04% (p=0.000 n=20+20) Reflect 949k ± 0% 949k ± 0% -0.01% (p=0.000 n=20+20) Tar 337k ± 0% 337k ± 0% -0.06% (p=0.000 n=20+20) XML 418k ± 0% 417k ± 0% -0.17% (p=0.000 n=20+20) LinkCompiler 553k ± 0% 554k ± 0% +0.22% (p=0.000 n=20+19) ExternalLinkCompiler 1.52M ± 0% 1.52M ± 0% +0.27% (p=0.000 n=20+20) LinkWithoutDebugCompiler 186k ± 0% 186k ± 0% +0.06% (p=0.000 n=20+20) [Geo mean] 723k 723k +0.03% name old text-bytes new text-bytes delta HelloSize 828kB ± 0% 828kB ± 0% -0.01% (p=0.000 n=20+20) name old data-bytes new data-bytes delta HelloSize 13.4kB ± 0% 13.4kB ± 0% ~ (all equal) name old bss-bytes new bss-bytes delta HelloSize 180kB ± 0% 180kB ± 0% ~ (all equal) name old exe-bytes new exe-bytes delta HelloSize 1.23MB ± 0% 1.23MB ± 0% -0.33% (p=0.000 n=20+20) file before after Δ % addr2line 4320075 4311883 -8192 -0.190% asm 5191932 5187836 -4096 -0.079% buildid 2835338 2831242 -4096 -0.144% compile 20531717 20569099 +37382 +0.182% cover 5322511 5318415 -4096 -0.077% dist 3723749 3719653 -4096 -0.110% doc 4743515 4739419 -4096 -0.086% fix 3413960 3409864 -4096 -0.120% link 6690119 6686023 -4096 -0.061% nm 4269616 4265520 -4096 -0.096% pprof 14942189 14929901 -12288 -0.082% trace 11807164 11790780 -16384 -0.139% vet 8384104 8388200 +4096 +0.049% go 15339076 15334980 -4096 -0.027% total 132258257 132226007 -32250 -0.024% Fixes #30645. Change-Id: If551ac5996097f3685870d083151b5843170aab0 Reviewed-on: https://go-review.googlesource.com/c/go/+/165998 Run-TryBot: Michael Munday <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]>
1 parent c9ece81 commit e37cc29

File tree

9 files changed

+4087
-1034
lines changed

9 files changed

+4087
-1034
lines changed

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

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func elimIf(f *Func, loadAddr *sparseSet, dom *Block) bool {
148148
// the number of useless instructions executed.
149149
const maxfuseinsts = 2
150150

151-
if len(simple.Values) > maxfuseinsts || !allTrivial(simple) {
151+
if len(simple.Values) > maxfuseinsts || !canSpeculativelyExecute(simple) {
152152
return false
153153
}
154154

@@ -305,10 +305,10 @@ func elimIfElse(f *Func, loadAddr *sparseSet, b *Block) bool {
305305
return false
306306
}
307307
yes, no := b.Succs[0].Block(), b.Succs[1].Block()
308-
if !isLeafPlain(yes) || len(yes.Values) > 1 || !allTrivial(yes) {
308+
if !isLeafPlain(yes) || len(yes.Values) > 1 || !canSpeculativelyExecute(yes) {
309309
return false
310310
}
311-
if !isLeafPlain(no) || len(no.Values) > 1 || !allTrivial(no) {
311+
if !isLeafPlain(no) || len(no.Values) > 1 || !canSpeculativelyExecute(no) {
312312
return false
313313
}
314314
if b.Succs[0].Block().Succs[0].Block() != b.Succs[1].Block().Succs[0].Block() {
@@ -415,7 +415,15 @@ func shouldElimIfElse(no, yes, post *Block, arch string) bool {
415415
}
416416
}
417417

418-
func allTrivial(b *Block) bool {
418+
// canSpeculativelyExecute reports whether every value in the block can
419+
// be evaluated without causing any observable side effects (memory
420+
// accesses, panics and so on) except for execution time changes. It
421+
// also ensures that the block does not contain any phis which we can't
422+
// speculatively execute.
423+
// Warning: this function cannot currently detect values that represent
424+
// instructions the execution of which need to be guarded with CPU
425+
// hardware feature checks. See issue #34950.
426+
func canSpeculativelyExecute(b *Block) bool {
419427
// don't fuse memory ops, Phi ops, divides (can panic),
420428
// or anything else with side-effects
421429
for _, v := range b.Values {

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,15 +428,15 @@ var passes = [...]pass{
428428
{name: "gcse deadcode", fn: deadcode, required: true}, // clean out after cse and phiopt
429429
{name: "nilcheckelim", fn: nilcheckelim},
430430
{name: "prove", fn: prove},
431-
{name: "fuse plain", fn: fusePlain},
431+
{name: "early fuse", fn: fuseEarly},
432432
{name: "decompose builtin", fn: decomposeBuiltIn, required: true},
433433
{name: "softfloat", fn: softfloat, required: true},
434434
{name: "late opt", fn: opt, required: true}, // TODO: split required rules and optimizing rules
435435
{name: "dead auto elim", fn: elimDeadAutosGeneric},
436436
{name: "generic deadcode", fn: deadcode, required: true}, // remove dead stores, which otherwise mess up store chain
437437
{name: "check bce", fn: checkbce},
438438
{name: "branchelim", fn: branchelim},
439-
{name: "fuse", fn: fuseAll},
439+
{name: "late fuse", fn: fuseLate},
440440
{name: "dse", fn: dse},
441441
{name: "writebarrier", fn: writebarrier, required: true}, // expand write barrier ops
442442
{name: "insert resched checks", fn: insertLoopReschedChecks,
@@ -491,7 +491,7 @@ var passOrder = [...]constraint{
491491
// allow deadcode to clean up after nilcheckelim
492492
{"nilcheckelim", "generic deadcode"},
493493
// nilcheckelim generates sequences of plain basic blocks
494-
{"nilcheckelim", "fuse"},
494+
{"nilcheckelim", "late fuse"},
495495
// nilcheckelim relies on opt to rewrite user nil checks
496496
{"opt", "nilcheckelim"},
497497
// tighten will be most effective when as many values have been removed as possible

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ import (
88
"cmd/internal/src"
99
)
1010

11-
// fusePlain runs fuse(f, fuseTypePlain).
12-
func fusePlain(f *Func) { fuse(f, fuseTypePlain) }
11+
// fuseEarly runs fuse(f, fuseTypePlain|fuseTypeIntInRange).
12+
func fuseEarly(f *Func) { fuse(f, fuseTypePlain|fuseTypeIntInRange) }
1313

14-
// fuseAll runs fuse(f, fuseTypeAll).
15-
func fuseAll(f *Func) { fuse(f, fuseTypeAll) }
14+
// fuseLate runs fuse(f, fuseTypePlain|fuseTypeIf).
15+
func fuseLate(f *Func) { fuse(f, fuseTypePlain|fuseTypeIf) }
1616

1717
type fuseType uint8
1818

1919
const (
2020
fuseTypePlain fuseType = 1 << iota
2121
fuseTypeIf
22-
fuseTypeAll = fuseTypePlain | fuseTypeIf
22+
fuseTypeIntInRange
2323
)
2424

2525
// fuse simplifies control flow by joining basic blocks.
@@ -32,6 +32,9 @@ func fuse(f *Func, typ fuseType) {
3232
if typ&fuseTypeIf != 0 {
3333
changed = fuseBlockIf(b) || changed
3434
}
35+
if typ&fuseTypeIntInRange != 0 {
36+
changed = fuseIntegerComparisons(b) || changed
37+
}
3538
if typ&fuseTypePlain != 0 {
3639
changed = fuseBlockPlain(b) || changed
3740
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package ssa
6+
7+
// fuseIntegerComparisons optimizes inequalities such as '1 <= x && x < 5',
8+
// which can be optimized to 'unsigned(x-1) < 4'.
9+
//
10+
// Look for branch structure like:
11+
//
12+
// p
13+
// |\
14+
// | b
15+
// |/ \
16+
// s0 s1
17+
//
18+
// In our example, p has control '1 <= x', b has control 'x < 5',
19+
// and s0 and s1 are the if and else results of the comparison.
20+
//
21+
// This will be optimized into:
22+
//
23+
// p
24+
// \
25+
// b
26+
// / \
27+
// s0 s1
28+
//
29+
// where b has the combined control value 'unsigned(x-1) < 4'.
30+
// Later passes will then fuse p and b.
31+
func fuseIntegerComparisons(b *Block) bool {
32+
if len(b.Preds) != 1 {
33+
return false
34+
}
35+
p := b.Preds[0].Block()
36+
if b.Kind != BlockIf || p.Kind != BlockIf {
37+
return false
38+
}
39+
40+
// Don't merge control values if b is likely to be bypassed anyway.
41+
if p.Likely == BranchLikely && p.Succs[0].Block() != b {
42+
return false
43+
}
44+
if p.Likely == BranchUnlikely && p.Succs[1].Block() != b {
45+
return false
46+
}
47+
48+
// Check if the control values combine to make an integer inequality that
49+
// can be further optimized later.
50+
bc := b.Controls[0]
51+
pc := p.Controls[0]
52+
if !areMergeableInequalities(bc, pc) {
53+
return false
54+
}
55+
56+
// If the first (true) successors match then we have a disjunction (||).
57+
// If the second (false) successors match then we have a conjunction (&&).
58+
for i, op := range [2]Op{OpOrB, OpAndB} {
59+
if p.Succs[i].Block() != b.Succs[i].Block() {
60+
continue
61+
}
62+
63+
// TODO(mundaym): should we also check the cost of executing b?
64+
// Currently we might speculatively execute b even if b contains
65+
// a lot of instructions. We could just check that len(b.Values)
66+
// is lower than a fixed amount. Bear in mind however that the
67+
// other optimization passes might yet reduce the cost of b
68+
// significantly so we shouldn't be overly conservative.
69+
if !canSpeculativelyExecute(b) {
70+
return false
71+
}
72+
73+
// Logically combine the control values for p and b.
74+
v := b.NewValue0(bc.Pos, op, bc.Type)
75+
v.AddArg(pc)
76+
v.AddArg(bc)
77+
78+
// Set the combined control value as the control value for b.
79+
b.SetControl(v)
80+
81+
// Modify p so that it jumps directly to b.
82+
p.removeEdge(i)
83+
p.Kind = BlockPlain
84+
p.Likely = BranchUnknown
85+
p.ResetControls()
86+
87+
return true
88+
}
89+
90+
// TODO: could negate condition(s) to merge controls.
91+
return false
92+
}
93+
94+
// getConstIntArgIndex returns the index of the first argument that is a
95+
// constant integer or -1 if no such argument exists.
96+
func getConstIntArgIndex(v *Value) int {
97+
for i, a := range v.Args {
98+
switch a.Op {
99+
case OpConst8, OpConst16, OpConst32, OpConst64:
100+
return i
101+
}
102+
}
103+
return -1
104+
}
105+
106+
// isSignedInequality reports whether op represents the inequality < or ≤
107+
// in the signed domain.
108+
func isSignedInequality(v *Value) bool {
109+
switch v.Op {
110+
case OpLess64, OpLess32, OpLess16, OpLess8,
111+
OpLeq64, OpLeq32, OpLeq16, OpLeq8:
112+
return true
113+
}
114+
return false
115+
}
116+
117+
// isUnsignedInequality reports whether op represents the inequality < or ≤
118+
// in the unsigned domain.
119+
func isUnsignedInequality(v *Value) bool {
120+
switch v.Op {
121+
case OpLess64U, OpLess32U, OpLess16U, OpLess8U,
122+
OpLeq64U, OpLeq32U, OpLeq16U, OpLeq8U:
123+
return true
124+
}
125+
return false
126+
}
127+
128+
func areMergeableInequalities(x, y *Value) bool {
129+
// We need both inequalities to be either in the signed or unsigned domain.
130+
// TODO(mundaym): it would also be good to merge when we have an Eq op that
131+
// could be transformed into a Less/Leq. For example in the unsigned
132+
// domain 'x == 0 || 3 < x' is equivalent to 'x <= 0 || 3 < x'
133+
inequalityChecks := [...]func(*Value) bool{
134+
isSignedInequality,
135+
isUnsignedInequality,
136+
}
137+
for _, f := range inequalityChecks {
138+
if !f(x) || !f(y) {
139+
continue
140+
}
141+
142+
// Check that both inequalities are comparisons with constants.
143+
xi := getConstIntArgIndex(x)
144+
if xi < 0 {
145+
return false
146+
}
147+
yi := getConstIntArgIndex(y)
148+
if yi < 0 {
149+
return false
150+
}
151+
152+
// Check that the non-constant arguments to the inequalities
153+
// are the same.
154+
return x.Args[xi^1] == y.Args[yi^1]
155+
}
156+
return false
157+
}

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestFuseEliminatesOneBranch(t *testing.T) {
2626
Exit("mem")))
2727

2828
CheckFunc(fun.f)
29-
fuseAll(fun.f)
29+
fuseLate(fun.f)
3030

3131
for _, b := range fun.f.Blocks {
3232
if b == fun.blocks["then"] && b.Kind != BlockInvalid {
@@ -56,7 +56,7 @@ func TestFuseEliminatesBothBranches(t *testing.T) {
5656
Exit("mem")))
5757

5858
CheckFunc(fun.f)
59-
fuseAll(fun.f)
59+
fuseLate(fun.f)
6060

6161
for _, b := range fun.f.Blocks {
6262
if b == fun.blocks["then"] && b.Kind != BlockInvalid {
@@ -90,7 +90,7 @@ func TestFuseHandlesPhis(t *testing.T) {
9090
Exit("mem")))
9191

9292
CheckFunc(fun.f)
93-
fuseAll(fun.f)
93+
fuseLate(fun.f)
9494

9595
for _, b := range fun.f.Blocks {
9696
if b == fun.blocks["then"] && b.Kind != BlockInvalid {
@@ -122,7 +122,7 @@ func TestFuseEliminatesEmptyBlocks(t *testing.T) {
122122
))
123123

124124
CheckFunc(fun.f)
125-
fuseAll(fun.f)
125+
fuseLate(fun.f)
126126

127127
for k, b := range fun.blocks {
128128
if k[:1] == "z" && b.Kind != BlockInvalid {
@@ -153,7 +153,7 @@ func TestFuseSideEffects(t *testing.T) {
153153
Goto("loop")))
154154

155155
CheckFunc(fun.f)
156-
fuseAll(fun.f)
156+
fuseLate(fun.f)
157157

158158
for _, b := range fun.f.Blocks {
159159
if b == fun.blocks["then"] && b.Kind == BlockInvalid {
@@ -196,7 +196,7 @@ func BenchmarkFuse(b *testing.B) {
196196
b.ResetTimer()
197197
for i := 0; i < b.N; i++ {
198198
fun := c.Fun("entry", blocks...)
199-
fuseAll(fun.f)
199+
fuseLate(fun.f)
200200
}
201201
})
202202
}

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,54 @@
254254
(Neq16 (Const16 <t> [c]) (Add16 (Const16 <t> [d]) x)) -> (Neq16 (Const16 <t> [int64(int16(c-d))]) x)
255255
(Neq8 (Const8 <t> [c]) (Add8 (Const8 <t> [d]) x)) -> (Neq8 (Const8 <t> [int64(int8(c-d))]) x)
256256

257+
// signed integer range: ( c <= x && x (<|<=) d ) -> ( unsigned(x-c) (<|<=) unsigned(d-c) )
258+
(AndB (Leq64 (Const64 [c]) x) ((Less|Leq)64 x (Const64 [d]))) && d >= c -> ((Less|Leq)64U (Sub64 <x.Type> x (Const64 <x.Type> [c])) (Const64 <x.Type> [d-c]))
259+
(AndB (Leq32 (Const32 [c]) x) ((Less|Leq)32 x (Const32 [d]))) && d >= c -> ((Less|Leq)32U (Sub32 <x.Type> x (Const32 <x.Type> [c])) (Const32 <x.Type> [d-c]))
260+
(AndB (Leq16 (Const16 [c]) x) ((Less|Leq)16 x (Const16 [d]))) && d >= c -> ((Less|Leq)16U (Sub16 <x.Type> x (Const16 <x.Type> [c])) (Const16 <x.Type> [d-c]))
261+
(AndB (Leq8 (Const8 [c]) x) ((Less|Leq)8 x (Const8 [d]))) && d >= c -> ((Less|Leq)8U (Sub8 <x.Type> x (Const8 <x.Type> [c])) (Const8 <x.Type> [d-c]))
262+
263+
// signed integer range: ( c < x && x (<|<=) d ) -> ( unsigned(x-(c+1)) (<|<=) unsigned(d-(c+1)) )
264+
(AndB (Less64 (Const64 [c]) x) ((Less|Leq)64 x (Const64 [d]))) && d >= c+1 && int64(c+1) > int64(c) -> ((Less|Leq)64U (Sub64 <x.Type> x (Const64 <x.Type> [c+1])) (Const64 <x.Type> [d-c-1]))
265+
(AndB (Less32 (Const32 [c]) x) ((Less|Leq)32 x (Const32 [d]))) && d >= c+1 && int32(c+1) > int32(c) -> ((Less|Leq)32U (Sub32 <x.Type> x (Const32 <x.Type> [c+1])) (Const32 <x.Type> [d-c-1]))
266+
(AndB (Less16 (Const16 [c]) x) ((Less|Leq)16 x (Const16 [d]))) && d >= c+1 && int16(c+1) > int16(c) -> ((Less|Leq)16U (Sub16 <x.Type> x (Const16 <x.Type> [c+1])) (Const16 <x.Type> [d-c-1]))
267+
(AndB (Less8 (Const8 [c]) x) ((Less|Leq)8 x (Const8 [d]))) && d >= c+1 && int8(c+1) > int8(c) -> ((Less|Leq)8U (Sub8 <x.Type> x (Const8 <x.Type> [c+1])) (Const8 <x.Type> [d-c-1]))
268+
269+
// unsigned integer range: ( c <= x && x (<|<=) d ) -> ( x-c (<|<=) d-c )
270+
(AndB (Leq64U (Const64 [c]) x) ((Less|Leq)64U x (Const64 [d]))) && uint64(d) >= uint64(c) -> ((Less|Leq)64U (Sub64 <x.Type> x (Const64 <x.Type> [c])) (Const64 <x.Type> [d-c]))
271+
(AndB (Leq32U (Const32 [c]) x) ((Less|Leq)32U x (Const32 [d]))) && uint32(d) >= uint32(c) -> ((Less|Leq)32U (Sub32 <x.Type> x (Const32 <x.Type> [c])) (Const32 <x.Type> [int64(int32(d-c))]))
272+
(AndB (Leq16U (Const16 [c]) x) ((Less|Leq)16U x (Const16 [d]))) && uint16(d) >= uint16(c) -> ((Less|Leq)16U (Sub16 <x.Type> x (Const16 <x.Type> [c])) (Const16 <x.Type> [int64(int16(d-c))]))
273+
(AndB (Leq8U (Const8 [c]) x) ((Less|Leq)8U x (Const8 [d]))) && uint8(d) >= uint8(c) -> ((Less|Leq)8U (Sub8 <x.Type> x (Const8 <x.Type> [c])) (Const8 <x.Type> [int64(int8(d-c))]))
274+
275+
// unsigned integer range: ( c < x && x (<|<=) d ) -> ( x-(c+1) (<|<=) d-(c+1) )
276+
(AndB (Less64U (Const64 [c]) x) ((Less|Leq)64U x (Const64 [d]))) && uint64(d) >= uint64(c+1) && uint64(c+1) > uint64(c) -> ((Less|Leq)64U (Sub64 <x.Type> x (Const64 <x.Type> [c+1])) (Const64 <x.Type> [d-c-1]))
277+
(AndB (Less32U (Const32 [c]) x) ((Less|Leq)32U x (Const32 [d]))) && uint32(d) >= uint32(c+1) && uint32(c+1) > uint32(c) -> ((Less|Leq)32U (Sub32 <x.Type> x (Const32 <x.Type> [int64(int32(c+1))])) (Const32 <x.Type> [int64(int32(d-c-1))]))
278+
(AndB (Less16U (Const16 [c]) x) ((Less|Leq)16U x (Const16 [d]))) && uint16(d) >= uint16(c+1) && uint16(c+1) > uint16(c) -> ((Less|Leq)16U (Sub16 <x.Type> x (Const16 <x.Type> [int64(int16(c+1))])) (Const16 <x.Type> [int64(int16(d-c-1))]))
279+
(AndB (Less8U (Const8 [c]) x) ((Less|Leq)8U x (Const8 [d]))) && uint8(d) >= uint8(c+1) && uint8(c+1) > uint8(c) -> ((Less|Leq)8U (Sub8 <x.Type> x (Const8 <x.Type> [int64(int8(c+1))])) (Const8 <x.Type> [int64(int8(d-c-1))]))
280+
281+
// signed integer range: ( c (<|<=) x || x < d ) -> ( unsigned(c-d) (<|<=) unsigned(x-d) )
282+
(OrB ((Less|Leq)64 (Const64 [c]) x) (Less64 x (Const64 [d]))) && c >= d -> ((Less|Leq)64U (Const64 <x.Type> [c-d]) (Sub64 <x.Type> x (Const64 <x.Type> [d])))
283+
(OrB ((Less|Leq)32 (Const32 [c]) x) (Less32 x (Const32 [d]))) && c >= d -> ((Less|Leq)32U (Const32 <x.Type> [c-d]) (Sub32 <x.Type> x (Const32 <x.Type> [d])))
284+
(OrB ((Less|Leq)16 (Const16 [c]) x) (Less16 x (Const16 [d]))) && c >= d -> ((Less|Leq)16U (Const16 <x.Type> [c-d]) (Sub16 <x.Type> x (Const16 <x.Type> [d])))
285+
(OrB ((Less|Leq)8 (Const8 [c]) x) (Less8 x (Const8 [d]))) && c >= d -> ((Less|Leq)8U (Const8 <x.Type> [c-d]) (Sub8 <x.Type> x (Const8 <x.Type> [d])))
286+
287+
// signed integer range: ( c (<|<=) x || x <= d ) -> ( unsigned(c-(d+1)) (<|<=) unsigned(x-(d+1)) )
288+
(OrB ((Less|Leq)64 (Const64 [c]) x) (Leq64 x (Const64 [d]))) && c >= d+1 && int64(d+1) > int64(d) -> ((Less|Leq)64U (Const64 <x.Type> [c-d-1]) (Sub64 <x.Type> x (Const64 <x.Type> [d+1])))
289+
(OrB ((Less|Leq)32 (Const32 [c]) x) (Leq32 x (Const32 [d]))) && c >= d+1 && int32(d+1) > int32(d) -> ((Less|Leq)32U (Const32 <x.Type> [c-d-1]) (Sub32 <x.Type> x (Const32 <x.Type> [d+1])))
290+
(OrB ((Less|Leq)16 (Const16 [c]) x) (Leq16 x (Const16 [d]))) && c >= d+1 && int16(d+1) > int16(d) -> ((Less|Leq)16U (Const16 <x.Type> [c-d-1]) (Sub16 <x.Type> x (Const16 <x.Type> [d+1])))
291+
(OrB ((Less|Leq)8 (Const8 [c]) x) (Leq8 x (Const8 [d]))) && c >= d+1 && int8(d+1) > int8(d) -> ((Less|Leq)8U (Const8 <x.Type> [c-d-1]) (Sub8 <x.Type> x (Const8 <x.Type> [d+1])))
292+
293+
// unsigned integer range: ( c (<|<=) x || x < d ) -> ( c-d (<|<=) x-d )
294+
(OrB ((Less|Leq)64U (Const64 [c]) x) (Less64U x (Const64 [d]))) && uint64(c) >= uint64(d) -> ((Less|Leq)64U (Const64 <x.Type> [c-d]) (Sub64 <x.Type> x (Const64 <x.Type> [d])))
295+
(OrB ((Less|Leq)32U (Const32 [c]) x) (Less32U x (Const32 [d]))) && uint32(c) >= uint32(d) -> ((Less|Leq)32U (Const32 <x.Type> [int64(int32(c-d))]) (Sub32 <x.Type> x (Const32 <x.Type> [d])))
296+
(OrB ((Less|Leq)16U (Const16 [c]) x) (Less16U x (Const16 [d]))) && uint16(c) >= uint16(d) -> ((Less|Leq)16U (Const16 <x.Type> [int64(int16(c-d))]) (Sub16 <x.Type> x (Const16 <x.Type> [d])))
297+
(OrB ((Less|Leq)8U (Const8 [c]) x) (Less8U x (Const8 [d]))) && uint8(c) >= uint8(d) -> ((Less|Leq)8U (Const8 <x.Type> [int64( int8(c-d))]) (Sub8 <x.Type> x (Const8 <x.Type> [d])))
298+
299+
// unsigned integer range: ( c (<|<=) x || x <= d ) -> ( c-(d+1) (<|<=) x-(d+1) )
300+
(OrB ((Less|Leq)64U (Const64 [c]) x) (Leq64U x (Const64 [d]))) && uint64(c) >= uint64(d+1) && uint64(d+1) > uint64(d) -> ((Less|Leq)64U (Const64 <x.Type> [c-d-1]) (Sub64 <x.Type> x (Const64 <x.Type> [d+1])))
301+
(OrB ((Less|Leq)32U (Const32 [c]) x) (Leq32U x (Const32 [d]))) && uint32(c) >= uint32(d+1) && uint32(d+1) > uint32(d) -> ((Less|Leq)32U (Const32 <x.Type> [int64(int32(c-d-1))]) (Sub32 <x.Type> x (Const32 <x.Type> [int64(int32(d+1))])))
302+
(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> [int64(int16(c-d-1))]) (Sub16 <x.Type> x (Const16 <x.Type> [int64(int16(d+1))])))
303+
(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> [int64( int8(c-d-1))]) (Sub8 <x.Type> x (Const8 <x.Type> [int64( int8(d+1))])))
304+
257305
// Canonicalize x-const to x+(-const)
258306
(Sub64 x (Const64 <t> [c])) && x.Op != OpConst64 -> (Add64 (Const64 <t> [-c]) x)
259307
(Sub32 x (Const32 <t> [c])) && x.Op != OpConst32 -> (Add32 (Const32 <t> [int64(int32(-c))]) x)

0 commit comments

Comments
 (0)