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

411 lines
9 KiB
Go
Raw Normal View History

// Copyright 2017 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 ssa
import (
"math/big"
"testing"
)
func TestMagicExhaustive8(t *testing.T) {
testMagicExhaustive(t, 8)
}
func TestMagicExhaustive8U(t *testing.T) {
testMagicExhaustiveU(t, 8)
}
func TestMagicExhaustive16(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
testMagicExhaustive(t, 16)
}
func TestMagicExhaustive16U(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
testMagicExhaustiveU(t, 16)
}
// exhaustive test of magic for n bits
func testMagicExhaustive(t *testing.T, n uint) {
min := -int64(1) << (n - 1)
max := int64(1) << (n - 1)
for c := int64(1); c < max; c++ {
if !smagicOK(n, c) {
continue
}
m := int64(smagic(n, c).m)
s := smagic(n, c).s
for i := min; i < max; i++ {
want := i / c
got := (i * m) >> (n + uint(s))
if i < 0 {
got++
}
if want != got {
t.Errorf("signed magic wrong for %d / %d: got %d, want %d (m=%d,s=%d)\n", i, c, got, want, m, s)
}
}
}
}
func testMagicExhaustiveU(t *testing.T, n uint) {
max := uint64(1) << n
for c := uint64(1); c < max; c++ {
if !umagicOK(n, int64(c)) {
continue
}
m := umagic(n, int64(c)).m
s := umagic(n, int64(c)).s
for i := uint64(0); i < max; i++ {
want := i / c
got := (i * (max + m)) >> (n + uint(s))
if want != got {
t.Errorf("unsigned magic wrong for %d / %d: got %d, want %d (m=%d,s=%d)\n", i, c, got, want, m, s)
}
}
}
}
func TestMagicUnsigned(t *testing.T) {
One := new(big.Int).SetUint64(1)
for _, n := range [...]uint{8, 16, 32, 64} {
TwoN := new(big.Int).Lsh(One, n)
Max := new(big.Int).Sub(TwoN, One)
for _, c := range [...]uint64{
3,
5,
6,
7,
9,
10,
11,
12,
13,
14,
15,
17,
1<<8 - 1,
1<<8 + 1,
1<<16 - 1,
1<<16 + 1,
1<<32 - 1,
1<<32 + 1,
1<<64 - 1,
} {
if c>>n != 0 {
continue // not appropriate for the given n.
}
if !umagicOK(n, int64(c)) {
t.Errorf("expected n=%d c=%d to pass\n", n, c)
}
m := umagic(n, int64(c)).m
s := umagic(n, int64(c)).s
C := new(big.Int).SetUint64(c)
M := new(big.Int).SetUint64(m)
M.Add(M, TwoN)
// Find largest multiple of c.
Mul := new(big.Int).Div(Max, C)
Mul.Mul(Mul, C)
mul := Mul.Uint64()
// Try some input values, mostly around multiples of c.
for _, x := range [...]uint64{0, 1,
c - 1, c, c + 1,
2*c - 1, 2 * c, 2*c + 1,
mul - 1, mul, mul + 1,
uint64(1)<<n - 1,
} {
X := new(big.Int).SetUint64(x)
if X.Cmp(Max) > 0 {
continue
}
Want := new(big.Int).Quo(X, C)
Got := new(big.Int).Mul(X, M)
Got.Rsh(Got, n+uint(s))
if Want.Cmp(Got) != 0 {
t.Errorf("umagic for %d/%d n=%d doesn't work, got=%s, want %s\n", x, c, n, Got, Want)
}
}
}
}
}
func TestMagicSigned(t *testing.T) {
One := new(big.Int).SetInt64(1)
for _, n := range [...]uint{8, 16, 32, 64} {
TwoNMinusOne := new(big.Int).Lsh(One, n-1)
Max := new(big.Int).Sub(TwoNMinusOne, One)
Min := new(big.Int).Neg(TwoNMinusOne)
for _, c := range [...]int64{
3,
5,
6,
7,
9,
10,
11,
12,
13,
14,
15,
17,
1<<7 - 1,
1<<7 + 1,
1<<15 - 1,
1<<15 + 1,
1<<31 - 1,
1<<31 + 1,
1<<63 - 1,
} {
if c>>(n-1) != 0 {
continue // not appropriate for the given n.
}
if !smagicOK(n, c) {
t.Errorf("expected n=%d c=%d to pass\n", n, c)
}
m := smagic(n, c).m
s := smagic(n, c).s
C := new(big.Int).SetInt64(c)
M := new(big.Int).SetUint64(m)
// Find largest multiple of c.
Mul := new(big.Int).Div(Max, C)
Mul.Mul(Mul, C)
mul := Mul.Int64()
// Try some input values, mostly around multiples of c.
for _, x := range [...]int64{
-1, 1,
-c - 1, -c, -c + 1, c - 1, c, c + 1,
-2*c - 1, -2 * c, -2*c + 1, 2*c - 1, 2 * c, 2*c + 1,
-mul - 1, -mul, -mul + 1, mul - 1, mul, mul + 1,
cmd/compile: add signed divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication, and add and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also needed. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. On amd64, the divisibility check is 30-45% faster. name old time/op new time/op delta` DivisiblePow2constI64-4 0.83ns ± 1% 0.82ns ± 0% ~ (p=0.079 n=5+4) DivisibleconstI64-4 2.68ns ± 1% 1.87ns ± 0% -30.33% (p=0.000 n=5+4) DivisibleWDivconstI64-4 2.69ns ± 1% 2.71ns ± 3% ~ (p=1.000 n=5+5) DivisiblePow2constI32-4 1.15ns ± 1% 1.15ns ± 0% ~ (p=0.238 n=5+4) DivisibleconstI32-4 2.24ns ± 1% 1.20ns ± 0% -46.48% (p=0.016 n=5+4) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.683 n=5+5) DivisiblePow2constI16-4 0.81ns ± 1% 0.82ns ± 1% ~ (p=0.135 n=5+5) DivisibleconstI16-4 2.11ns ± 2% 1.20ns ± 1% -42.99% (p=0.008 n=5+5) DivisibleWDivconstI16-4 2.23ns ± 0% 2.27ns ± 2% +1.79% (p=0.029 n=4+4) DivisiblePow2constI8-4 0.81ns ± 1% 0.81ns ± 1% ~ (p=0.286 n=5+5) DivisibleconstI8-4 2.13ns ± 3% 1.19ns ± 1% -43.84% (p=0.008 n=5+5) DivisibleWDivconstI8-4 2.23ns ± 1% 2.25ns ± 1% ~ (p=0.183 n=5+5) Fixes #30282 Fixes #15806 Change-Id: Id20d78263a4fdfe0509229ae4dfa2fede83fc1d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173998 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-05 14:05:07 -06:00
int64(1)<<(n-1) - 1, -int64(1) << (n - 1),
} {
X := new(big.Int).SetInt64(x)
if X.Cmp(Min) < 0 || X.Cmp(Max) > 0 {
continue
}
Want := new(big.Int).Quo(X, C)
Got := new(big.Int).Mul(X, M)
Got.Rsh(Got, n+uint(s))
if x < 0 {
Got.Add(Got, One)
}
if Want.Cmp(Got) != 0 {
t.Errorf("smagic for %d/%d n=%d doesn't work, got=%s, want %s\n", x, c, n, Got, Want)
}
}
}
}
}
cmd/compile: add unsigned divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also available. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. Additional rules to lower the generic RotateLeft* ops were also applied. On amd64, the divisibility check is 25-50% faster. name old time/op new time/op delta DivconstI64-4 2.08ns ± 0% 2.08ns ± 1% ~ (p=0.881 n=5+5) DivisibleconstI64-4 2.67ns ± 0% 2.67ns ± 1% ~ (p=1.000 n=5+5) DivisibleWDivconstI64-4 2.67ns ± 0% 2.67ns ± 0% ~ (p=0.683 n=5+5) DivconstU64-4 2.08ns ± 1% 2.08ns ± 1% ~ (p=1.000 n=5+5) DivisibleconstU64-4 2.77ns ± 1% 1.55ns ± 2% -43.90% (p=0.008 n=5+5) DivisibleWDivconstU64-4 2.99ns ± 1% 2.99ns ± 1% ~ (p=1.000 n=5+5) DivconstI32-4 1.53ns ± 2% 1.53ns ± 0% ~ (p=1.000 n=5+5) DivisibleconstI32-4 2.23ns ± 0% 2.25ns ± 3% ~ (p=0.167 n=5+5) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.429 n=5+5) DivconstU32-4 1.78ns ± 0% 1.78ns ± 1% ~ (p=1.000 n=4+5) DivisibleconstU32-4 2.52ns ± 2% 1.26ns ± 0% -49.96% (p=0.000 n=5+4) DivisibleWDivconstU32-4 2.63ns ± 0% 2.85ns ±10% +8.29% (p=0.016 n=4+5) DivconstI16-4 1.54ns ± 0% 1.54ns ± 0% ~ (p=0.333 n=4+5) DivisibleconstI16-4 2.10ns ± 0% 2.10ns ± 1% ~ (p=0.571 n=4+5) DivisibleWDivconstI16-4 2.22ns ± 0% 2.23ns ± 1% ~ (p=0.556 n=4+5) DivconstU16-4 1.09ns ± 0% 1.01ns ± 1% -7.74% (p=0.000 n=4+5) DivisibleconstU16-4 1.83ns ± 0% 1.26ns ± 0% -31.52% (p=0.008 n=5+5) DivisibleWDivconstU16-4 1.88ns ± 0% 1.89ns ± 1% ~ (p=0.365 n=5+5) DivconstI8-4 1.54ns ± 1% 1.54ns ± 1% ~ (p=1.000 n=5+5) DivisibleconstI8-4 2.10ns ± 0% 2.11ns ± 0% ~ (p=0.238 n=5+4) DivisibleWDivconstI8-4 2.22ns ± 0% 2.23ns ± 2% ~ (p=0.762 n=5+5) DivconstU8-4 0.92ns ± 1% 0.94ns ± 1% +2.65% (p=0.008 n=5+5) DivisibleconstU8-4 1.66ns ± 0% 1.26ns ± 1% -24.28% (p=0.008 n=5+5) DivisibleWDivconstU8-4 1.79ns ± 0% 1.80ns ± 1% ~ (p=0.079 n=4+5) A follow-up change will address the signed division case. Updates #30282 Change-Id: I7e995f167179aa5c76bb10fbcbeb49c520943403 Reviewed-on: https://go-review.googlesource.com/c/go/+/168037 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-03-09 21:58:16 -07:00
func testDivisibleExhaustiveU(t *testing.T, n uint) {
maxU := uint64(1) << n
for c := uint64(1); c < maxU; c++ {
if !udivisibleOK(n, int64(c)) {
continue
}
k := udivisible(n, int64(c)).k
m := udivisible(n, int64(c)).m
max := udivisible(n, int64(c)).max
mask := ^uint64(0) >> (64 - n)
for i := uint64(0); i < maxU; i++ {
want := i%c == 0
mul := (i * m) & mask
rot := (mul>>uint(k) | mul<<(n-uint(k))) & mask
got := rot <= max
if want != got {
t.Errorf("unsigned divisible wrong for %d %% %d == 0: got %v, want %v (k=%d,m=%d,max=%d)\n", i, c, got, want, k, m, max)
}
}
}
}
func TestDivisibleExhaustive8U(t *testing.T) {
testDivisibleExhaustiveU(t, 8)
}
func TestDivisibleExhaustive16U(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
testDivisibleExhaustiveU(t, 16)
}
func TestDivisibleUnsigned(t *testing.T) {
One := new(big.Int).SetUint64(1)
for _, n := range [...]uint{8, 16, 32, 64} {
TwoN := new(big.Int).Lsh(One, n)
Max := new(big.Int).Sub(TwoN, One)
for _, c := range [...]uint64{
3,
5,
6,
7,
9,
10,
11,
12,
13,
14,
15,
17,
1<<8 - 1,
1<<8 + 1,
1<<16 - 1,
1<<16 + 1,
1<<32 - 1,
1<<32 + 1,
1<<64 - 1,
} {
if c>>n != 0 {
continue // c too large for the given n.
}
if !udivisibleOK(n, int64(c)) {
t.Errorf("expected n=%d c=%d to pass\n", n, c)
}
k := udivisible(n, int64(c)).k
m := udivisible(n, int64(c)).m
max := udivisible(n, int64(c)).max
mask := ^uint64(0) >> (64 - n)
C := new(big.Int).SetUint64(c)
// Find largest multiple of c.
Mul := new(big.Int).Div(Max, C)
Mul.Mul(Mul, C)
mul := Mul.Uint64()
// Try some input values, mostly around multiples of c.
for _, x := range [...]uint64{0, 1,
c - 1, c, c + 1,
2*c - 1, 2 * c, 2*c + 1,
mul - 1, mul, mul + 1,
uint64(1)<<n - 1,
} {
X := new(big.Int).SetUint64(x)
if X.Cmp(Max) > 0 {
continue
}
want := x%c == 0
mul := (x * m) & mask
rot := (mul>>uint(k) | mul<<(n-uint(k))) & mask
got := rot <= max
if want != got {
t.Errorf("unsigned divisible wrong for %d %% %d == 0: got %v, want %v (k=%d,m=%d,max=%d)\n", x, c, got, want, k, m, max)
}
}
}
}
}
cmd/compile: add signed divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication, and add and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also needed. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. On amd64, the divisibility check is 30-45% faster. name old time/op new time/op delta` DivisiblePow2constI64-4 0.83ns ± 1% 0.82ns ± 0% ~ (p=0.079 n=5+4) DivisibleconstI64-4 2.68ns ± 1% 1.87ns ± 0% -30.33% (p=0.000 n=5+4) DivisibleWDivconstI64-4 2.69ns ± 1% 2.71ns ± 3% ~ (p=1.000 n=5+5) DivisiblePow2constI32-4 1.15ns ± 1% 1.15ns ± 0% ~ (p=0.238 n=5+4) DivisibleconstI32-4 2.24ns ± 1% 1.20ns ± 0% -46.48% (p=0.016 n=5+4) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.683 n=5+5) DivisiblePow2constI16-4 0.81ns ± 1% 0.82ns ± 1% ~ (p=0.135 n=5+5) DivisibleconstI16-4 2.11ns ± 2% 1.20ns ± 1% -42.99% (p=0.008 n=5+5) DivisibleWDivconstI16-4 2.23ns ± 0% 2.27ns ± 2% +1.79% (p=0.029 n=4+4) DivisiblePow2constI8-4 0.81ns ± 1% 0.81ns ± 1% ~ (p=0.286 n=5+5) DivisibleconstI8-4 2.13ns ± 3% 1.19ns ± 1% -43.84% (p=0.008 n=5+5) DivisibleWDivconstI8-4 2.23ns ± 1% 2.25ns ± 1% ~ (p=0.183 n=5+5) Fixes #30282 Fixes #15806 Change-Id: Id20d78263a4fdfe0509229ae4dfa2fede83fc1d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173998 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-05 14:05:07 -06:00
func testDivisibleExhaustive(t *testing.T, n uint) {
minI := -int64(1) << (n - 1)
maxI := int64(1) << (n - 1)
for c := int64(1); c < maxI; c++ {
if !sdivisibleOK(n, c) {
cmd/compile: add signed divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication, and add and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also needed. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. On amd64, the divisibility check is 30-45% faster. name old time/op new time/op delta` DivisiblePow2constI64-4 0.83ns ± 1% 0.82ns ± 0% ~ (p=0.079 n=5+4) DivisibleconstI64-4 2.68ns ± 1% 1.87ns ± 0% -30.33% (p=0.000 n=5+4) DivisibleWDivconstI64-4 2.69ns ± 1% 2.71ns ± 3% ~ (p=1.000 n=5+5) DivisiblePow2constI32-4 1.15ns ± 1% 1.15ns ± 0% ~ (p=0.238 n=5+4) DivisibleconstI32-4 2.24ns ± 1% 1.20ns ± 0% -46.48% (p=0.016 n=5+4) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.683 n=5+5) DivisiblePow2constI16-4 0.81ns ± 1% 0.82ns ± 1% ~ (p=0.135 n=5+5) DivisibleconstI16-4 2.11ns ± 2% 1.20ns ± 1% -42.99% (p=0.008 n=5+5) DivisibleWDivconstI16-4 2.23ns ± 0% 2.27ns ± 2% +1.79% (p=0.029 n=4+4) DivisiblePow2constI8-4 0.81ns ± 1% 0.81ns ± 1% ~ (p=0.286 n=5+5) DivisibleconstI8-4 2.13ns ± 3% 1.19ns ± 1% -43.84% (p=0.008 n=5+5) DivisibleWDivconstI8-4 2.23ns ± 1% 2.25ns ± 1% ~ (p=0.183 n=5+5) Fixes #30282 Fixes #15806 Change-Id: Id20d78263a4fdfe0509229ae4dfa2fede83fc1d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173998 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-05 14:05:07 -06:00
continue
}
k := sdivisible(n, c).k
m := sdivisible(n, c).m
a := sdivisible(n, c).a
max := sdivisible(n, c).max
cmd/compile: add signed divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication, and add and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also needed. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. On amd64, the divisibility check is 30-45% faster. name old time/op new time/op delta` DivisiblePow2constI64-4 0.83ns ± 1% 0.82ns ± 0% ~ (p=0.079 n=5+4) DivisibleconstI64-4 2.68ns ± 1% 1.87ns ± 0% -30.33% (p=0.000 n=5+4) DivisibleWDivconstI64-4 2.69ns ± 1% 2.71ns ± 3% ~ (p=1.000 n=5+5) DivisiblePow2constI32-4 1.15ns ± 1% 1.15ns ± 0% ~ (p=0.238 n=5+4) DivisibleconstI32-4 2.24ns ± 1% 1.20ns ± 0% -46.48% (p=0.016 n=5+4) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.683 n=5+5) DivisiblePow2constI16-4 0.81ns ± 1% 0.82ns ± 1% ~ (p=0.135 n=5+5) DivisibleconstI16-4 2.11ns ± 2% 1.20ns ± 1% -42.99% (p=0.008 n=5+5) DivisibleWDivconstI16-4 2.23ns ± 0% 2.27ns ± 2% +1.79% (p=0.029 n=4+4) DivisiblePow2constI8-4 0.81ns ± 1% 0.81ns ± 1% ~ (p=0.286 n=5+5) DivisibleconstI8-4 2.13ns ± 3% 1.19ns ± 1% -43.84% (p=0.008 n=5+5) DivisibleWDivconstI8-4 2.23ns ± 1% 2.25ns ± 1% ~ (p=0.183 n=5+5) Fixes #30282 Fixes #15806 Change-Id: Id20d78263a4fdfe0509229ae4dfa2fede83fc1d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173998 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-05 14:05:07 -06:00
mask := ^uint64(0) >> (64 - n)
for i := minI; i < maxI; i++ {
want := i%c == 0
mul := (uint64(i)*m + a) & mask
rot := (mul>>uint(k) | mul<<(n-uint(k))) & mask
got := rot <= max
if want != got {
t.Errorf("signed divisible wrong for %d %% %d == 0: got %v, want %v (k=%d,m=%d,a=%d,max=%d)\n", i, c, got, want, k, m, a, max)
}
}
}
}
func TestDivisibleExhaustive8(t *testing.T) {
testDivisibleExhaustive(t, 8)
}
func TestDivisibleExhaustive16(t *testing.T) {
if testing.Short() {
t.Skip("slow test; skipping")
}
testDivisibleExhaustive(t, 16)
}
func TestDivisibleSigned(t *testing.T) {
One := new(big.Int).SetInt64(1)
for _, n := range [...]uint{8, 16, 32, 64} {
TwoNMinusOne := new(big.Int).Lsh(One, n-1)
Max := new(big.Int).Sub(TwoNMinusOne, One)
Min := new(big.Int).Neg(TwoNMinusOne)
for _, c := range [...]int64{
3,
5,
6,
7,
9,
10,
11,
12,
13,
14,
15,
17,
1<<7 - 1,
1<<7 + 1,
1<<15 - 1,
1<<15 + 1,
1<<31 - 1,
1<<31 + 1,
1<<63 - 1,
} {
if c>>(n-1) != 0 {
continue // not appropriate for the given n.
}
if !sdivisibleOK(n, c) {
cmd/compile: add signed divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication, and add and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also needed. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. On amd64, the divisibility check is 30-45% faster. name old time/op new time/op delta` DivisiblePow2constI64-4 0.83ns ± 1% 0.82ns ± 0% ~ (p=0.079 n=5+4) DivisibleconstI64-4 2.68ns ± 1% 1.87ns ± 0% -30.33% (p=0.000 n=5+4) DivisibleWDivconstI64-4 2.69ns ± 1% 2.71ns ± 3% ~ (p=1.000 n=5+5) DivisiblePow2constI32-4 1.15ns ± 1% 1.15ns ± 0% ~ (p=0.238 n=5+4) DivisibleconstI32-4 2.24ns ± 1% 1.20ns ± 0% -46.48% (p=0.016 n=5+4) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.683 n=5+5) DivisiblePow2constI16-4 0.81ns ± 1% 0.82ns ± 1% ~ (p=0.135 n=5+5) DivisibleconstI16-4 2.11ns ± 2% 1.20ns ± 1% -42.99% (p=0.008 n=5+5) DivisibleWDivconstI16-4 2.23ns ± 0% 2.27ns ± 2% +1.79% (p=0.029 n=4+4) DivisiblePow2constI8-4 0.81ns ± 1% 0.81ns ± 1% ~ (p=0.286 n=5+5) DivisibleconstI8-4 2.13ns ± 3% 1.19ns ± 1% -43.84% (p=0.008 n=5+5) DivisibleWDivconstI8-4 2.23ns ± 1% 2.25ns ± 1% ~ (p=0.183 n=5+5) Fixes #30282 Fixes #15806 Change-Id: Id20d78263a4fdfe0509229ae4dfa2fede83fc1d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173998 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-05 14:05:07 -06:00
t.Errorf("expected n=%d c=%d to pass\n", n, c)
}
k := sdivisible(n, c).k
m := sdivisible(n, c).m
a := sdivisible(n, c).a
max := sdivisible(n, c).max
cmd/compile: add signed divisibility rules "Division by invariant integers using multiplication" paper by Granlund and Montgomery contains a method for directly computing divisibility (x%c == 0 for c constant) by means of the modular inverse. The method is further elaborated in "Hacker's Delight" by Warren Section 10-17 This general rule can compute divisibilty by one multiplication, and add and a compare for odd divisors and an additional rotate for even divisors. To apply the divisibility rule, we must take into account the rules to rewrite x%c = x-((x/c)*c) and (x/c) for c constant on the first optimization pass "opt". This complicates the matching as we want to match only in the cases where the result of (x/c) is not also needed. So, we must match on the expanded form of (x/c) in the expression x == c*(x/c) in the "late opt" pass after common subexpresion elimination. Note, that if there is an intermediate opt pass introduced in the future we could simplify these rules by delaying the magic division rewrite to "late opt" and matching directly on (x/c) in the intermediate opt pass. On amd64, the divisibility check is 30-45% faster. name old time/op new time/op delta` DivisiblePow2constI64-4 0.83ns ± 1% 0.82ns ± 0% ~ (p=0.079 n=5+4) DivisibleconstI64-4 2.68ns ± 1% 1.87ns ± 0% -30.33% (p=0.000 n=5+4) DivisibleWDivconstI64-4 2.69ns ± 1% 2.71ns ± 3% ~ (p=1.000 n=5+5) DivisiblePow2constI32-4 1.15ns ± 1% 1.15ns ± 0% ~ (p=0.238 n=5+4) DivisibleconstI32-4 2.24ns ± 1% 1.20ns ± 0% -46.48% (p=0.016 n=5+4) DivisibleWDivconstI32-4 2.27ns ± 1% 2.27ns ± 1% ~ (p=0.683 n=5+5) DivisiblePow2constI16-4 0.81ns ± 1% 0.82ns ± 1% ~ (p=0.135 n=5+5) DivisibleconstI16-4 2.11ns ± 2% 1.20ns ± 1% -42.99% (p=0.008 n=5+5) DivisibleWDivconstI16-4 2.23ns ± 0% 2.27ns ± 2% +1.79% (p=0.029 n=4+4) DivisiblePow2constI8-4 0.81ns ± 1% 0.81ns ± 1% ~ (p=0.286 n=5+5) DivisibleconstI8-4 2.13ns ± 3% 1.19ns ± 1% -43.84% (p=0.008 n=5+5) DivisibleWDivconstI8-4 2.23ns ± 1% 2.25ns ± 1% ~ (p=0.183 n=5+5) Fixes #30282 Fixes #15806 Change-Id: Id20d78263a4fdfe0509229ae4dfa2fede83fc1d0 Reviewed-on: https://go-review.googlesource.com/c/go/+/173998 Run-TryBot: Brian Kessler <brian.m.kessler@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
2019-04-05 14:05:07 -06:00
mask := ^uint64(0) >> (64 - n)
C := new(big.Int).SetInt64(c)
// Find largest multiple of c.
Mul := new(big.Int).Div(Max, C)
Mul.Mul(Mul, C)
mul := Mul.Int64()
// Try some input values, mostly around multiples of c.
for _, x := range [...]int64{
-1, 1,
-c - 1, -c, -c + 1, c - 1, c, c + 1,
-2*c - 1, -2 * c, -2*c + 1, 2*c - 1, 2 * c, 2*c + 1,
-mul - 1, -mul, -mul + 1, mul - 1, mul, mul + 1,
int64(1)<<(n-1) - 1, -int64(1) << (n - 1),
} {
X := new(big.Int).SetInt64(x)
if X.Cmp(Min) < 0 || X.Cmp(Max) > 0 {
continue
}
want := x%c == 0
mul := (uint64(x)*m + a) & mask
rot := (mul>>uint(k) | mul<<(n-uint(k))) & mask
got := rot <= max
if want != got {
t.Errorf("signed divisible wrong for %d %% %d == 0: got %v, want %v (k=%d,m=%d,a=%d,max=%d)\n", x, c, got, want, k, m, a, max)
}
}
}
}
}