go/src/math/bits/bits_test.go
erifan01 5714c91b53 cmd/compile: intrinsify math/bits.Add64 for arm64
This CL instrinsifies Add64 with arm64 instruction sequence ADDS, ADCS
and ADC, and optimzes the case of carry chains.The CL also changes the
test code so that the intrinsic implementation can be tested.

Benchmarks:
name               old time/op       new time/op       delta
Add-224            2.500000ns +- 0%  2.090000ns +- 4%  -16.40%  (p=0.000 n=9+10)
Add32-224          2.500000ns +- 0%  2.500000ns +- 0%     ~     (all equal)
Add64-224          2.500000ns +- 0%  1.577778ns +- 2%  -36.89%  (p=0.000 n=10+9)
Add64multiple-224  6.000000ns +- 0%  2.000000ns +- 0%  -66.67%  (p=0.000 n=10+10)

Change-Id: I6ee91c9a85c16cc72ade5fd94868c579f16c7615
Reviewed-on: https://go-review.googlesource.com/c/go/+/159017
Run-TryBot: Ben Shi <powerman1st@163.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
2019-03-20 05:39:49 +00:00

1153 lines
28 KiB
Go

// 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 bits_test
import (
. "math/bits"
"runtime"
"testing"
"unsafe"
)
func TestUintSize(t *testing.T) {
var x uint
if want := unsafe.Sizeof(x) * 8; UintSize != want {
t.Fatalf("UintSize = %d; want %d", UintSize, want)
}
}
func TestLeadingZeros(t *testing.T) {
for i := 0; i < 256; i++ {
nlz := tab[i].nlz
for k := 0; k < 64-8; k++ {
x := uint64(i) << uint(k)
if x <= 1<<8-1 {
got := LeadingZeros8(uint8(x))
want := nlz - k + (8 - 8)
if x == 0 {
want = 8
}
if got != want {
t.Fatalf("LeadingZeros8(%#02x) == %d; want %d", x, got, want)
}
}
if x <= 1<<16-1 {
got := LeadingZeros16(uint16(x))
want := nlz - k + (16 - 8)
if x == 0 {
want = 16
}
if got != want {
t.Fatalf("LeadingZeros16(%#04x) == %d; want %d", x, got, want)
}
}
if x <= 1<<32-1 {
got := LeadingZeros32(uint32(x))
want := nlz - k + (32 - 8)
if x == 0 {
want = 32
}
if got != want {
t.Fatalf("LeadingZeros32(%#08x) == %d; want %d", x, got, want)
}
if UintSize == 32 {
got = LeadingZeros(uint(x))
if got != want {
t.Fatalf("LeadingZeros(%#08x) == %d; want %d", x, got, want)
}
}
}
if x <= 1<<64-1 {
got := LeadingZeros64(uint64(x))
want := nlz - k + (64 - 8)
if x == 0 {
want = 64
}
if got != want {
t.Fatalf("LeadingZeros64(%#016x) == %d; want %d", x, got, want)
}
if UintSize == 64 {
got = LeadingZeros(uint(x))
if got != want {
t.Fatalf("LeadingZeros(%#016x) == %d; want %d", x, got, want)
}
}
}
}
}
}
// Exported (global) variable serving as input for some
// of the benchmarks to ensure side-effect free calls
// are not optimized away.
var Input uint64 = DeBruijn64
// Exported (global) variable to store function results
// during benchmarking to ensure side-effect free calls
// are not optimized away.
var Output int
func BenchmarkLeadingZeros(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += LeadingZeros(uint(Input) >> (uint(i) % UintSize))
}
Output = s
}
func BenchmarkLeadingZeros8(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += LeadingZeros8(uint8(Input) >> (uint(i) % 8))
}
Output = s
}
func BenchmarkLeadingZeros16(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += LeadingZeros16(uint16(Input) >> (uint(i) % 16))
}
Output = s
}
func BenchmarkLeadingZeros32(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += LeadingZeros32(uint32(Input) >> (uint(i) % 32))
}
Output = s
}
func BenchmarkLeadingZeros64(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += LeadingZeros64(uint64(Input) >> (uint(i) % 64))
}
Output = s
}
func TestTrailingZeros(t *testing.T) {
for i := 0; i < 256; i++ {
ntz := tab[i].ntz
for k := 0; k < 64-8; k++ {
x := uint64(i) << uint(k)
want := ntz + k
if x <= 1<<8-1 {
got := TrailingZeros8(uint8(x))
if x == 0 {
want = 8
}
if got != want {
t.Fatalf("TrailingZeros8(%#02x) == %d; want %d", x, got, want)
}
}
if x <= 1<<16-1 {
got := TrailingZeros16(uint16(x))
if x == 0 {
want = 16
}
if got != want {
t.Fatalf("TrailingZeros16(%#04x) == %d; want %d", x, got, want)
}
}
if x <= 1<<32-1 {
got := TrailingZeros32(uint32(x))
if x == 0 {
want = 32
}
if got != want {
t.Fatalf("TrailingZeros32(%#08x) == %d; want %d", x, got, want)
}
if UintSize == 32 {
got = TrailingZeros(uint(x))
if got != want {
t.Fatalf("TrailingZeros(%#08x) == %d; want %d", x, got, want)
}
}
}
if x <= 1<<64-1 {
got := TrailingZeros64(uint64(x))
if x == 0 {
want = 64
}
if got != want {
t.Fatalf("TrailingZeros64(%#016x) == %d; want %d", x, got, want)
}
if UintSize == 64 {
got = TrailingZeros(uint(x))
if got != want {
t.Fatalf("TrailingZeros(%#016x) == %d; want %d", x, got, want)
}
}
}
}
}
}
func BenchmarkTrailingZeros(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += TrailingZeros(uint(Input) << (uint(i) % UintSize))
}
Output = s
}
func BenchmarkTrailingZeros8(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += TrailingZeros8(uint8(Input) << (uint(i) % 8))
}
Output = s
}
func BenchmarkTrailingZeros16(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += TrailingZeros16(uint16(Input) << (uint(i) % 16))
}
Output = s
}
func BenchmarkTrailingZeros32(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += TrailingZeros32(uint32(Input) << (uint(i) % 32))
}
Output = s
}
func BenchmarkTrailingZeros64(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += TrailingZeros64(uint64(Input) << (uint(i) % 64))
}
Output = s
}
func TestOnesCount(t *testing.T) {
var x uint64
for i := 0; i <= 64; i++ {
testOnesCount(t, x, i)
x = x<<1 | 1
}
for i := 64; i >= 0; i-- {
testOnesCount(t, x, i)
x = x << 1
}
for i := 0; i < 256; i++ {
for k := 0; k < 64-8; k++ {
testOnesCount(t, uint64(i)<<uint(k), tab[i].pop)
}
}
}
func testOnesCount(t *testing.T, x uint64, want int) {
if x <= 1<<8-1 {
got := OnesCount8(uint8(x))
if got != want {
t.Fatalf("OnesCount8(%#02x) == %d; want %d", uint8(x), got, want)
}
}
if x <= 1<<16-1 {
got := OnesCount16(uint16(x))
if got != want {
t.Fatalf("OnesCount16(%#04x) == %d; want %d", uint16(x), got, want)
}
}
if x <= 1<<32-1 {
got := OnesCount32(uint32(x))
if got != want {
t.Fatalf("OnesCount32(%#08x) == %d; want %d", uint32(x), got, want)
}
if UintSize == 32 {
got = OnesCount(uint(x))
if got != want {
t.Fatalf("OnesCount(%#08x) == %d; want %d", uint32(x), got, want)
}
}
}
if x <= 1<<64-1 {
got := OnesCount64(uint64(x))
if got != want {
t.Fatalf("OnesCount64(%#016x) == %d; want %d", x, got, want)
}
if UintSize == 64 {
got = OnesCount(uint(x))
if got != want {
t.Fatalf("OnesCount(%#016x) == %d; want %d", x, got, want)
}
}
}
}
func BenchmarkOnesCount(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += OnesCount(uint(Input))
}
Output = s
}
func BenchmarkOnesCount8(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += OnesCount8(uint8(Input))
}
Output = s
}
func BenchmarkOnesCount16(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += OnesCount16(uint16(Input))
}
Output = s
}
func BenchmarkOnesCount32(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += OnesCount32(uint32(Input))
}
Output = s
}
func BenchmarkOnesCount64(b *testing.B) {
var s int
for i := 0; i < b.N; i++ {
s += OnesCount64(uint64(Input))
}
Output = s
}
func TestRotateLeft(t *testing.T) {
var m uint64 = DeBruijn64
for k := uint(0); k < 128; k++ {
x8 := uint8(m)
got8 := RotateLeft8(x8, int(k))
want8 := x8<<(k&0x7) | x8>>(8-k&0x7)
if got8 != want8 {
t.Fatalf("RotateLeft8(%#02x, %d) == %#02x; want %#02x", x8, k, got8, want8)
}
got8 = RotateLeft8(want8, -int(k))
if got8 != x8 {
t.Fatalf("RotateLeft8(%#02x, -%d) == %#02x; want %#02x", want8, k, got8, x8)
}
x16 := uint16(m)
got16 := RotateLeft16(x16, int(k))
want16 := x16<<(k&0xf) | x16>>(16-k&0xf)
if got16 != want16 {
t.Fatalf("RotateLeft16(%#04x, %d) == %#04x; want %#04x", x16, k, got16, want16)
}
got16 = RotateLeft16(want16, -int(k))
if got16 != x16 {
t.Fatalf("RotateLeft16(%#04x, -%d) == %#04x; want %#04x", want16, k, got16, x16)
}
x32 := uint32(m)
got32 := RotateLeft32(x32, int(k))
want32 := x32<<(k&0x1f) | x32>>(32-k&0x1f)
if got32 != want32 {
t.Fatalf("RotateLeft32(%#08x, %d) == %#08x; want %#08x", x32, k, got32, want32)
}
got32 = RotateLeft32(want32, -int(k))
if got32 != x32 {
t.Fatalf("RotateLeft32(%#08x, -%d) == %#08x; want %#08x", want32, k, got32, x32)
}
if UintSize == 32 {
x := uint(m)
got := RotateLeft(x, int(k))
want := x<<(k&0x1f) | x>>(32-k&0x1f)
if got != want {
t.Fatalf("RotateLeft(%#08x, %d) == %#08x; want %#08x", x, k, got, want)
}
got = RotateLeft(want, -int(k))
if got != x {
t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x)
}
}
x64 := uint64(m)
got64 := RotateLeft64(x64, int(k))
want64 := x64<<(k&0x3f) | x64>>(64-k&0x3f)
if got64 != want64 {
t.Fatalf("RotateLeft64(%#016x, %d) == %#016x; want %#016x", x64, k, got64, want64)
}
got64 = RotateLeft64(want64, -int(k))
if got64 != x64 {
t.Fatalf("RotateLeft64(%#016x, -%d) == %#016x; want %#016x", want64, k, got64, x64)
}
if UintSize == 64 {
x := uint(m)
got := RotateLeft(x, int(k))
want := x<<(k&0x3f) | x>>(64-k&0x3f)
if got != want {
t.Fatalf("RotateLeft(%#016x, %d) == %#016x; want %#016x", x, k, got, want)
}
got = RotateLeft(want, -int(k))
if got != x {
t.Fatalf("RotateLeft(%#08x, -%d) == %#08x; want %#08x", want, k, got, x)
}
}
}
}
func BenchmarkRotateLeft(b *testing.B) {
var s uint
for i := 0; i < b.N; i++ {
s += RotateLeft(uint(Input), i)
}
Output = int(s)
}
func BenchmarkRotateLeft8(b *testing.B) {
var s uint8
for i := 0; i < b.N; i++ {
s += RotateLeft8(uint8(Input), i)
}
Output = int(s)
}
func BenchmarkRotateLeft16(b *testing.B) {
var s uint16
for i := 0; i < b.N; i++ {
s += RotateLeft16(uint16(Input), i)
}
Output = int(s)
}
func BenchmarkRotateLeft32(b *testing.B) {
var s uint32
for i := 0; i < b.N; i++ {
s += RotateLeft32(uint32(Input), i)
}
Output = int(s)
}
func BenchmarkRotateLeft64(b *testing.B) {
var s uint64
for i := 0; i < b.N; i++ {
s += RotateLeft64(uint64(Input), i)
}
Output = int(s)
}
func TestReverse(t *testing.T) {
// test each bit
for i := uint(0); i < 64; i++ {
testReverse(t, uint64(1)<<i, uint64(1)<<(63-i))
}
// test a few patterns
for _, test := range []struct {
x, r uint64
}{
{0, 0},
{0x1, 0x8 << 60},
{0x2, 0x4 << 60},
{0x3, 0xc << 60},
{0x4, 0x2 << 60},
{0x5, 0xa << 60},
{0x6, 0x6 << 60},
{0x7, 0xe << 60},
{0x8, 0x1 << 60},
{0x9, 0x9 << 60},
{0xa, 0x5 << 60},
{0xb, 0xd << 60},
{0xc, 0x3 << 60},
{0xd, 0xb << 60},
{0xe, 0x7 << 60},
{0xf, 0xf << 60},
{0x5686487, 0xe12616a000000000},
{0x0123456789abcdef, 0xf7b3d591e6a2c480},
} {
testReverse(t, test.x, test.r)
testReverse(t, test.r, test.x)
}
}
func testReverse(t *testing.T, x64, want64 uint64) {
x8 := uint8(x64)
got8 := Reverse8(x8)
want8 := uint8(want64 >> (64 - 8))
if got8 != want8 {
t.Fatalf("Reverse8(%#02x) == %#02x; want %#02x", x8, got8, want8)
}
x16 := uint16(x64)
got16 := Reverse16(x16)
want16 := uint16(want64 >> (64 - 16))
if got16 != want16 {
t.Fatalf("Reverse16(%#04x) == %#04x; want %#04x", x16, got16, want16)
}
x32 := uint32(x64)
got32 := Reverse32(x32)
want32 := uint32(want64 >> (64 - 32))
if got32 != want32 {
t.Fatalf("Reverse32(%#08x) == %#08x; want %#08x", x32, got32, want32)
}
if UintSize == 32 {
x := uint(x32)
got := Reverse(x)
want := uint(want32)
if got != want {
t.Fatalf("Reverse(%#08x) == %#08x; want %#08x", x, got, want)
}
}
got64 := Reverse64(x64)
if got64 != want64 {
t.Fatalf("Reverse64(%#016x) == %#016x; want %#016x", x64, got64, want64)
}
if UintSize == 64 {
x := uint(x64)
got := Reverse(x)
want := uint(want64)
if got != want {
t.Fatalf("Reverse(%#08x) == %#016x; want %#016x", x, got, want)
}
}
}
func BenchmarkReverse(b *testing.B) {
var s uint
for i := 0; i < b.N; i++ {
s += Reverse(uint(i))
}
Output = int(s)
}
func BenchmarkReverse8(b *testing.B) {
var s uint8
for i := 0; i < b.N; i++ {
s += Reverse8(uint8(i))
}
Output = int(s)
}
func BenchmarkReverse16(b *testing.B) {
var s uint16
for i := 0; i < b.N; i++ {
s += Reverse16(uint16(i))
}
Output = int(s)
}
func BenchmarkReverse32(b *testing.B) {
var s uint32
for i := 0; i < b.N; i++ {
s += Reverse32(uint32(i))
}
Output = int(s)
}
func BenchmarkReverse64(b *testing.B) {
var s uint64
for i := 0; i < b.N; i++ {
s += Reverse64(uint64(i))
}
Output = int(s)
}
func TestReverseBytes(t *testing.T) {
for _, test := range []struct {
x, r uint64
}{
{0, 0},
{0x01, 0x01 << 56},
{0x0123, 0x2301 << 48},
{0x012345, 0x452301 << 40},
{0x01234567, 0x67452301 << 32},
{0x0123456789, 0x8967452301 << 24},
{0x0123456789ab, 0xab8967452301 << 16},
{0x0123456789abcd, 0xcdab8967452301 << 8},
{0x0123456789abcdef, 0xefcdab8967452301 << 0},
} {
testReverseBytes(t, test.x, test.r)
testReverseBytes(t, test.r, test.x)
}
}
func testReverseBytes(t *testing.T, x64, want64 uint64) {
x16 := uint16(x64)
got16 := ReverseBytes16(x16)
want16 := uint16(want64 >> (64 - 16))
if got16 != want16 {
t.Fatalf("ReverseBytes16(%#04x) == %#04x; want %#04x", x16, got16, want16)
}
x32 := uint32(x64)
got32 := ReverseBytes32(x32)
want32 := uint32(want64 >> (64 - 32))
if got32 != want32 {
t.Fatalf("ReverseBytes32(%#08x) == %#08x; want %#08x", x32, got32, want32)
}
if UintSize == 32 {
x := uint(x32)
got := ReverseBytes(x)
want := uint(want32)
if got != want {
t.Fatalf("ReverseBytes(%#08x) == %#08x; want %#08x", x, got, want)
}
}
got64 := ReverseBytes64(x64)
if got64 != want64 {
t.Fatalf("ReverseBytes64(%#016x) == %#016x; want %#016x", x64, got64, want64)
}
if UintSize == 64 {
x := uint(x64)
got := ReverseBytes(x)
want := uint(want64)
if got != want {
t.Fatalf("ReverseBytes(%#016x) == %#016x; want %#016x", x, got, want)
}
}
}
func BenchmarkReverseBytes(b *testing.B) {
var s uint
for i := 0; i < b.N; i++ {
s += ReverseBytes(uint(i))
}
Output = int(s)
}
func BenchmarkReverseBytes16(b *testing.B) {
var s uint16
for i := 0; i < b.N; i++ {
s += ReverseBytes16(uint16(i))
}
Output = int(s)
}
func BenchmarkReverseBytes32(b *testing.B) {
var s uint32
for i := 0; i < b.N; i++ {
s += ReverseBytes32(uint32(i))
}
Output = int(s)
}
func BenchmarkReverseBytes64(b *testing.B) {
var s uint64
for i := 0; i < b.N; i++ {
s += ReverseBytes64(uint64(i))
}
Output = int(s)
}
func TestLen(t *testing.T) {
for i := 0; i < 256; i++ {
len := 8 - tab[i].nlz
for k := 0; k < 64-8; k++ {
x := uint64(i) << uint(k)
want := 0
if x != 0 {
want = len + k
}
if x <= 1<<8-1 {
got := Len8(uint8(x))
if got != want {
t.Fatalf("Len8(%#02x) == %d; want %d", x, got, want)
}
}
if x <= 1<<16-1 {
got := Len16(uint16(x))
if got != want {
t.Fatalf("Len16(%#04x) == %d; want %d", x, got, want)
}
}
if x <= 1<<32-1 {
got := Len32(uint32(x))
if got != want {
t.Fatalf("Len32(%#08x) == %d; want %d", x, got, want)
}
if UintSize == 32 {
got := Len(uint(x))
if got != want {
t.Fatalf("Len(%#08x) == %d; want %d", x, got, want)
}
}
}
if x <= 1<<64-1 {
got := Len64(uint64(x))
if got != want {
t.Fatalf("Len64(%#016x) == %d; want %d", x, got, want)
}
if UintSize == 64 {
got := Len(uint(x))
if got != want {
t.Fatalf("Len(%#016x) == %d; want %d", x, got, want)
}
}
}
}
}
}
const (
_M = 1<<UintSize - 1
_M32 = 1<<32 - 1
_M64 = 1<<64 - 1
)
func TestAddSubUint(t *testing.T) {
test := func(msg string, f func(x, y, c uint) (z, cout uint), x, y, c, z, cout uint) {
z1, cout1 := f(x, y, c)
if z1 != z || cout1 != cout {
t.Errorf("%s: got z:cout = %#x:%#x; want %#x:%#x", msg, z1, cout1, z, cout)
}
}
for _, a := range []struct{ x, y, c, z, cout uint }{
{0, 0, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 1, 1, 0},
{0, 1, 1, 2, 0},
{12345, 67890, 0, 80235, 0},
{12345, 67890, 1, 80236, 0},
{_M, 1, 0, 0, 1},
{_M, 0, 1, 0, 1},
{_M, 1, 1, 1, 1},
{_M, _M, 0, _M - 1, 1},
{_M, _M, 1, _M, 1},
} {
test("Add", Add, a.x, a.y, a.c, a.z, a.cout)
test("Add symmetric", Add, a.y, a.x, a.c, a.z, a.cout)
test("Sub", Sub, a.z, a.x, a.c, a.y, a.cout)
test("Sub symmetric", Sub, a.z, a.y, a.c, a.x, a.cout)
// The above code can't test intrinsic implementation, because the passed function is not called directly.
// The following code uses a closure to test the intrinsic version in case the function is intrinsified.
test("Add intrinsic", func(x, y, c uint) (uint, uint) { return Add(x, y, c) }, a.x, a.y, a.c, a.z, a.cout)
test("Add intrinsic symmetric", func(x, y, c uint) (uint, uint) { return Add(x, y, c) }, a.y, a.x, a.c, a.z, a.cout)
test("Sub intrinsic", func(x, y, c uint) (uint, uint) { return Sub(x, y, c) }, a.z, a.x, a.c, a.y, a.cout)
test("Add intrinsic symmetric", func(x, y, c uint) (uint, uint) { return Sub(x, y, c) }, a.z, a.y, a.c, a.x, a.cout)
}
}
func TestAddSubUint32(t *testing.T) {
test := func(msg string, f func(x, y, c uint32) (z, cout uint32), x, y, c, z, cout uint32) {
z1, cout1 := f(x, y, c)
if z1 != z || cout1 != cout {
t.Errorf("%s: got z:cout = %#x:%#x; want %#x:%#x", msg, z1, cout1, z, cout)
}
}
for _, a := range []struct{ x, y, c, z, cout uint32 }{
{0, 0, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 1, 1, 0},
{0, 1, 1, 2, 0},
{12345, 67890, 0, 80235, 0},
{12345, 67890, 1, 80236, 0},
{_M32, 1, 0, 0, 1},
{_M32, 0, 1, 0, 1},
{_M32, 1, 1, 1, 1},
{_M32, _M32, 0, _M32 - 1, 1},
{_M32, _M32, 1, _M32, 1},
} {
test("Add32", Add32, a.x, a.y, a.c, a.z, a.cout)
test("Add32 symmetric", Add32, a.y, a.x, a.c, a.z, a.cout)
test("Sub32", Sub32, a.z, a.x, a.c, a.y, a.cout)
test("Sub32 symmetric", Sub32, a.z, a.y, a.c, a.x, a.cout)
}
}
func TestAddSubUint64(t *testing.T) {
test := func(msg string, f func(x, y, c uint64) (z, cout uint64), x, y, c, z, cout uint64) {
z1, cout1 := f(x, y, c)
if z1 != z || cout1 != cout {
t.Errorf("%s: got z:cout = %#x:%#x; want %#x:%#x", msg, z1, cout1, z, cout)
}
}
for _, a := range []struct{ x, y, c, z, cout uint64 }{
{0, 0, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 1, 1, 0},
{0, 1, 1, 2, 0},
{12345, 67890, 0, 80235, 0},
{12345, 67890, 1, 80236, 0},
{_M64, 1, 0, 0, 1},
{_M64, 0, 1, 0, 1},
{_M64, 1, 1, 1, 1},
{_M64, _M64, 0, _M64 - 1, 1},
{_M64, _M64, 1, _M64, 1},
} {
test("Add64", Add64, a.x, a.y, a.c, a.z, a.cout)
test("Add64 symmetric", Add64, a.y, a.x, a.c, a.z, a.cout)
test("Sub64", Sub64, a.z, a.x, a.c, a.y, a.cout)
test("Sub64 symmetric", Sub64, a.z, a.y, a.c, a.x, a.cout)
// The above code can't test intrinsic implementation, because the passed function is not called directly.
// The following code uses a closure to test the intrinsic version in case the function is intrinsified.
test("Add64 intrinsic", func(x, y, c uint64) (uint64, uint64) { return Add64(x, y, c) }, a.x, a.y, a.c, a.z, a.cout)
test("Add64 intrinsic symmetric", func(x, y, c uint64) (uint64, uint64) { return Add64(x, y, c) }, a.y, a.x, a.c, a.z, a.cout)
test("Sub64 intrinsic", func(x, y, c uint64) (uint64, uint64) { return Sub64(x, y, c) }, a.z, a.x, a.c, a.y, a.cout)
test("Add64 intrinsic symmetric", func(x, y, c uint64) (uint64, uint64) { return Sub64(x, y, c) }, a.z, a.y, a.c, a.x, a.cout)
}
}
func TestMulDiv(t *testing.T) {
testMul := func(msg string, f func(x, y uint) (hi, lo uint), x, y, hi, lo uint) {
hi1, lo1 := f(x, y)
if hi1 != hi || lo1 != lo {
t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo)
}
}
testDiv := func(msg string, f func(hi, lo, y uint) (q, r uint), hi, lo, y, q, r uint) {
q1, r1 := f(hi, lo, y)
if q1 != q || r1 != r {
t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r)
}
}
for _, a := range []struct {
x, y uint
hi, lo, r uint
}{
{1 << (UintSize - 1), 2, 1, 0, 1},
{_M, _M, _M - 1, 1, 42},
} {
testMul("Mul", Mul, a.x, a.y, a.hi, a.lo)
testMul("Mul symmetric", Mul, a.y, a.x, a.hi, a.lo)
testDiv("Div", Div, a.hi, a.lo+a.r, a.y, a.x, a.r)
testDiv("Div symmetric", Div, a.hi, a.lo+a.r, a.x, a.y, a.r)
// The above code can't test intrinsic implementation, because the passed function is not called directly.
// The following code uses a closure to test the intrinsic version in case the function is intrinsified.
testMul("Mul intrinsic", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.x, a.y, a.hi, a.lo)
testMul("Mul intrinsic symmetric", func(x, y uint) (uint, uint) { return Mul(x, y) }, a.y, a.x, a.hi, a.lo)
testDiv("Div intrinsic", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r)
testDiv("Div intrinsic symmetric", func(hi, lo, y uint) (uint, uint) { return Div(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r)
}
}
func TestMulDiv32(t *testing.T) {
testMul := func(msg string, f func(x, y uint32) (hi, lo uint32), x, y, hi, lo uint32) {
hi1, lo1 := f(x, y)
if hi1 != hi || lo1 != lo {
t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo)
}
}
testDiv := func(msg string, f func(hi, lo, y uint32) (q, r uint32), hi, lo, y, q, r uint32) {
q1, r1 := f(hi, lo, y)
if q1 != q || r1 != r {
t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r)
}
}
for _, a := range []struct {
x, y uint32
hi, lo, r uint32
}{
{1 << 31, 2, 1, 0, 1},
{0xc47dfa8c, 50911, 0x98a4, 0x998587f4, 13},
{_M32, _M32, _M32 - 1, 1, 42},
} {
testMul("Mul32", Mul32, a.x, a.y, a.hi, a.lo)
testMul("Mul32 symmetric", Mul32, a.y, a.x, a.hi, a.lo)
testDiv("Div32", Div32, a.hi, a.lo+a.r, a.y, a.x, a.r)
testDiv("Div32 symmetric", Div32, a.hi, a.lo+a.r, a.x, a.y, a.r)
}
}
func TestMulDiv64(t *testing.T) {
testMul := func(msg string, f func(x, y uint64) (hi, lo uint64), x, y, hi, lo uint64) {
hi1, lo1 := f(x, y)
if hi1 != hi || lo1 != lo {
t.Errorf("%s: got hi:lo = %#x:%#x; want %#x:%#x", msg, hi1, lo1, hi, lo)
}
}
testDiv := func(msg string, f func(hi, lo, y uint64) (q, r uint64), hi, lo, y, q, r uint64) {
q1, r1 := f(hi, lo, y)
if q1 != q || r1 != r {
t.Errorf("%s: got q:r = %#x:%#x; want %#x:%#x", msg, q1, r1, q, r)
}
}
for _, a := range []struct {
x, y uint64
hi, lo, r uint64
}{
{1 << 63, 2, 1, 0, 1},
{0x3626229738a3b9, 0xd8988a9f1cc4a61, 0x2dd0712657fe8, 0x9dd6a3364c358319, 13},
{_M64, _M64, _M64 - 1, 1, 42},
} {
testMul("Mul64", Mul64, a.x, a.y, a.hi, a.lo)
testMul("Mul64 symmetric", Mul64, a.y, a.x, a.hi, a.lo)
testDiv("Div64", Div64, a.hi, a.lo+a.r, a.y, a.x, a.r)
testDiv("Div64 symmetric", Div64, a.hi, a.lo+a.r, a.x, a.y, a.r)
// The above code can't test intrinsic implementation, because the passed function is not called directly.
// The following code uses a closure to test the intrinsic version in case the function is intrinsified.
testMul("Mul64 intrinsic", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.x, a.y, a.hi, a.lo)
testMul("Mul64 intrinsic symmetric", func(x, y uint64) (uint64, uint64) { return Mul64(x, y) }, a.y, a.x, a.hi, a.lo)
testDiv("Div64 intrinsic", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.y, a.x, a.r)
testDiv("Div64 intrinsic symmetric", func(hi, lo, y uint64) (uint64, uint64) { return Div64(hi, lo, y) }, a.hi, a.lo+a.r, a.x, a.y, a.r)
}
}
const (
divZeroError = "runtime error: integer divide by zero"
overflowError = "runtime error: integer overflow"
)
func TestDivPanicOverflow(t *testing.T) {
// Expect a panic
defer func() {
if err := recover(); err == nil {
t.Error("Div should have panicked when y<=hi")
} else if e, ok := err.(runtime.Error); !ok || e.Error() != overflowError {
t.Errorf("Div expected panic: %q, got: %q ", overflowError, e.Error())
}
}()
q, r := Div(1, 0, 1)
t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r)
}
func TestDiv32PanicOverflow(t *testing.T) {
// Expect a panic
defer func() {
if err := recover(); err == nil {
t.Error("Div32 should have panicked when y<=hi")
} else if e, ok := err.(runtime.Error); !ok || e.Error() != overflowError {
t.Errorf("Div32 expected panic: %q, got: %q ", overflowError, e.Error())
}
}()
q, r := Div32(1, 0, 1)
t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r)
}
func TestDiv64PanicOverflow(t *testing.T) {
// Expect a panic
defer func() {
if err := recover(); err == nil {
t.Error("Div64 should have panicked when y<=hi")
} else if e, ok := err.(runtime.Error); !ok || e.Error() != overflowError {
t.Errorf("Div64 expected panic: %q, got: %q ", overflowError, e.Error())
}
}()
q, r := Div64(1, 0, 1)
t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r)
}
func TestDivPanicZero(t *testing.T) {
// Expect a panic
defer func() {
if err := recover(); err == nil {
t.Error("Div should have panicked when y==0")
} else if e, ok := err.(runtime.Error); !ok || e.Error() != divZeroError {
t.Errorf("Div expected panic: %q, got: %q ", divZeroError, e.Error())
}
}()
q, r := Div(1, 1, 0)
t.Errorf("undefined q, r = %v, %v calculated when Div should have panicked", q, r)
}
func TestDiv32PanicZero(t *testing.T) {
// Expect a panic
defer func() {
if err := recover(); err == nil {
t.Error("Div32 should have panicked when y==0")
} else if e, ok := err.(runtime.Error); !ok || e.Error() != divZeroError {
t.Errorf("Div32 expected panic: %q, got: %q ", divZeroError, e.Error())
}
}()
q, r := Div32(1, 1, 0)
t.Errorf("undefined q, r = %v, %v calculated when Div32 should have panicked", q, r)
}
func TestDiv64PanicZero(t *testing.T) {
// Expect a panic
defer func() {
if err := recover(); err == nil {
t.Error("Div64 should have panicked when y==0")
} else if e, ok := err.(runtime.Error); !ok || e.Error() != divZeroError {
t.Errorf("Div64 expected panic: %q, got: %q ", divZeroError, e.Error())
}
}()
q, r := Div64(1, 1, 0)
t.Errorf("undefined q, r = %v, %v calculated when Div64 should have panicked", q, r)
}
func BenchmarkAdd(b *testing.B) {
var z, c uint
for i := 0; i < b.N; i++ {
z, c = Add(uint(Input), uint(i), c)
}
Output = int(z + c)
}
func BenchmarkAdd32(b *testing.B) {
var z, c uint32
for i := 0; i < b.N; i++ {
z, c = Add32(uint32(Input), uint32(i), c)
}
Output = int(z + c)
}
func BenchmarkAdd64(b *testing.B) {
var z, c uint64
for i := 0; i < b.N; i++ {
z, c = Add64(uint64(Input), uint64(i), c)
}
Output = int(z + c)
}
func BenchmarkAdd64multiple(b *testing.B) {
var z0 = uint64(Input)
var z1 = uint64(Input)
var z2 = uint64(Input)
var z3 = uint64(Input)
for i := 0; i < b.N; i++ {
var c uint64
z0, c = Add64(z0, uint64(i), c)
z1, c = Add64(z1, uint64(i), c)
z2, c = Add64(z2, uint64(i), c)
z3, _ = Add64(z3, uint64(i), c)
}
Output = int(z0 + z1 + z2 + z3)
}
func BenchmarkSub(b *testing.B) {
var z, c uint
for i := 0; i < b.N; i++ {
z, c = Sub(uint(Input), uint(i), c)
}
Output = int(z + c)
}
func BenchmarkSub32(b *testing.B) {
var z, c uint32
for i := 0; i < b.N; i++ {
z, c = Sub32(uint32(Input), uint32(i), c)
}
Output = int(z + c)
}
func BenchmarkSub64(b *testing.B) {
var z, c uint64
for i := 0; i < b.N; i++ {
z, c = Sub64(uint64(Input), uint64(i), c)
}
Output = int(z + c)
}
func BenchmarkSub64multiple(b *testing.B) {
var z0 = uint64(Input)
var z1 = uint64(Input)
var z2 = uint64(Input)
var z3 = uint64(Input)
for i := 0; i < b.N; i++ {
var c uint64
z0, c = Sub64(z0, uint64(i), c)
z1, c = Sub64(z1, uint64(i), c)
z2, c = Sub64(z2, uint64(i), c)
z3, _ = Sub64(z3, uint64(i), c)
}
Output = int(z0 + z1 + z2 + z3)
}
func BenchmarkMul(b *testing.B) {
var hi, lo uint
for i := 0; i < b.N; i++ {
hi, lo = Mul(uint(Input), uint(i))
}
Output = int(hi + lo)
}
func BenchmarkMul32(b *testing.B) {
var hi, lo uint32
for i := 0; i < b.N; i++ {
hi, lo = Mul32(uint32(Input), uint32(i))
}
Output = int(hi + lo)
}
func BenchmarkMul64(b *testing.B) {
var hi, lo uint64
for i := 0; i < b.N; i++ {
hi, lo = Mul64(uint64(Input), uint64(i))
}
Output = int(hi + lo)
}
func BenchmarkDiv(b *testing.B) {
var q, r uint
for i := 0; i < b.N; i++ {
q, r = Div(1, uint(i), uint(Input))
}
Output = int(q + r)
}
func BenchmarkDiv32(b *testing.B) {
var q, r uint32
for i := 0; i < b.N; i++ {
q, r = Div32(1, uint32(i), uint32(Input))
}
Output = int(q + r)
}
func BenchmarkDiv64(b *testing.B) {
var q, r uint64
for i := 0; i < b.N; i++ {
q, r = Div64(1, uint64(i), uint64(Input))
}
Output = int(q + r)
}
// ----------------------------------------------------------------------------
// Testing support
type entry = struct {
nlz, ntz, pop int
}
// tab contains results for all uint8 values
var tab [256]entry
func init() {
tab[0] = entry{8, 8, 0}
for i := 1; i < len(tab); i++ {
// nlz
x := i // x != 0
n := 0
for x&0x80 == 0 {
n++
x <<= 1
}
tab[i].nlz = n
// ntz
x = i // x != 0
n = 0
for x&1 == 0 {
n++
x >>= 1
}
tab[i].ntz = n
// pop
x = i // x != 0
n = 0
for x != 0 {
n += int(x & 1)
x >>= 1
}
tab[i].pop = n
}
}