math/big: replace local versions of bitLen, nlz with math/bits versions

Verified that BenchmarkBitLen time went down from 2.25 ns/op to 0.65 ns/op
an a 2.3 GHz Intel Core i7, before removing that benchmark (now covered by
math/bits benchmarks).

Change-Id: I3890bb7d1889e95b9a94bd68f0bdf06f1885adeb
Reviewed-on: https://go-review.googlesource.com/38464
Run-TryBot: Robert Griesemer <gri@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/math/big/arith.go b/src/math/big/arith.go
index 8cc0fb6..ad35240 100644
--- a/src/math/big/arith.go
+++ b/src/math/big/arith.go
@@ -76,42 +76,10 @@
 	return
 }
 
-// Length of x in bits.
-func bitLen_g(x Word) int {
-	return bits.Len(uint(x))
-}
-
-// log2 computes the integer binary logarithm of x.
-// The result is the integer n for which 2^n <= x < 2^(n+1).
-// If x == 0, the result is -1.
-func log2(x Word) int {
-	// TODO(gri) Replace with call to bits.Len once we have a fast
-	// implementation for the same platforms currently supporting math/big.
-	return bitLen(x) - 1
-}
-
 // nlz returns the number of leading zeros in x.
+// Wraps bits.LeadingZeros call for convenience.
 func nlz(x Word) uint {
-	// TODO(gri) Replace with call to bits.LeadingZeros once we have a fast
-	// implementation for the same platforms currently supporting math/big.
-	return uint(_W - bitLen(x))
-}
-
-// nlz64 returns the number of leading zeros in x.
-func nlz64(x uint64) uint {
-	// TODO(gri) Replace with call to bits.LeadingZeros64 once we have a fast
-	// implementation for the same platforms currently supporting math/big.
-	switch _W {
-	case 32:
-		w := x >> 32
-		if w == 0 {
-			return 32 + nlz(Word(x))
-		}
-		return nlz(Word(w))
-	case 64:
-		return nlz(Word(x))
-	}
-	panic("unreachable")
+	return uint(bits.LeadingZeros(uint(x)))
 }
 
 // q = (u1<<_W + u0 - r)/y
diff --git a/src/math/big/arith_386.s b/src/math/big/arith_386.s
index 7c8ab8f..6c080f0 100644
--- a/src/math/big/arith_386.s
+++ b/src/math/big/arith_386.s
@@ -269,14 +269,3 @@
 
 	MOVL DX, r+32(FP)
 	RET
-
-// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),NOSPLIT,$0
-	BSRL x+0(FP), AX
-	JZ Z1
-	INCL AX
-	MOVL AX, n+4(FP)
-	RET
-
-Z1:	MOVL $0, n+4(FP)
-	RET
diff --git a/src/math/big/arith_amd64.s b/src/math/big/arith_amd64.s
index a7eba67..7e50224 100644
--- a/src/math/big/arith_amd64.s
+++ b/src/math/big/arith_amd64.s
@@ -450,14 +450,3 @@
 
 	MOVQ DX, r+64(FP)
 	RET
-
-// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),NOSPLIT,$0
-	BSRQ x+0(FP), AX
-	JZ Z1
-	ADDQ $1, AX
-	MOVQ AX, n+8(FP)
-	RET
-
-Z1:	MOVQ $0, n+8(FP)
-	RET
diff --git a/src/math/big/arith_amd64p32.s b/src/math/big/arith_amd64p32.s
index 6006646..0a67238 100644
--- a/src/math/big/arith_amd64p32.s
+++ b/src/math/big/arith_amd64p32.s
@@ -38,6 +38,3 @@
 
 TEXT ·divWVW(SB),NOSPLIT,$0
 	JMP ·divWVW_g(SB)
-
-TEXT ·bitLen(SB),NOSPLIT,$0
-	JMP ·bitLen_g(SB)
diff --git a/src/math/big/arith_arm.s b/src/math/big/arith_arm.s
index 69590ff..ba65fd2 100644
--- a/src/math/big/arith_arm.s
+++ b/src/math/big/arith_arm.s
@@ -292,11 +292,3 @@
 	MOVW	R4, z1+8(FP)
 	MOVW	R3, z0+12(FP)
 	RET
-
-// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),NOSPLIT,$0
-	MOVW	x+0(FP), R0
-	CLZ 	R0, R0
-	RSB	$32, R0
-	MOVW	R0, n+4(FP)
-	RET
diff --git a/src/math/big/arith_arm64.s b/src/math/big/arith_arm64.s
index 24a717c..397b463 100644
--- a/src/math/big/arith_arm64.s
+++ b/src/math/big/arith_arm64.s
@@ -165,13 +165,3 @@
 // func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
 TEXT ·divWVW(SB),NOSPLIT,$0
 	B ·divWVW_g(SB)
-
-
-// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),NOSPLIT,$0
-	MOVD	x+0(FP), R0
-	CLZ	R0, R0
-	MOVD	$64, R1
-	SUB	R0, R1, R0
-	MOVD	R0, n+8(FP)
-	RET
diff --git a/src/math/big/arith_decl.go b/src/math/big/arith_decl.go
index 5433b6d..41e5923 100644
--- a/src/math/big/arith_decl.go
+++ b/src/math/big/arith_decl.go
@@ -18,4 +18,3 @@
 func mulAddVWW(z, x []Word, y, r Word) (c Word)
 func addMulVVW(z, x []Word, y Word) (c Word)
 func divWVW(z []Word, xn Word, x []Word, y Word) (r Word)
-func bitLen(x Word) (n int)
diff --git a/src/math/big/arith_decl_pure.go b/src/math/big/arith_decl_pure.go
index 21775dd..4ae49c1 100644
--- a/src/math/big/arith_decl_pure.go
+++ b/src/math/big/arith_decl_pure.go
@@ -49,7 +49,3 @@
 func divWVW(z []Word, xn Word, x []Word, y Word) (r Word) {
 	return divWVW_g(z, xn, x, y)
 }
-
-func bitLen(x Word) (n int) {
-	return bitLen_g(x)
-}
diff --git a/src/math/big/arith_mips64x.s b/src/math/big/arith_mips64x.s
index f9288fc..983510e 100644
--- a/src/math/big/arith_mips64x.s
+++ b/src/math/big/arith_mips64x.s
@@ -41,6 +41,3 @@
 
 TEXT ·divWVW(SB),NOSPLIT,$0
 	JMP ·divWVW_g(SB)
-
-TEXT ·bitLen(SB),NOSPLIT,$0
-	JMP ·bitLen_g(SB)
diff --git a/src/math/big/arith_mipsx.s b/src/math/big/arith_mipsx.s
index ac23114..54cafbd 100644
--- a/src/math/big/arith_mipsx.s
+++ b/src/math/big/arith_mipsx.s
@@ -41,6 +41,3 @@
 
 TEXT ·divWVW(SB),NOSPLIT,$0
 	JMP	·divWVW_g(SB)
-
-TEXT ·bitLen(SB),NOSPLIT,$0
-	JMP	·bitLen_g(SB)
diff --git a/src/math/big/arith_ppc64x.s b/src/math/big/arith_ppc64x.s
index 89d1cbf..3606dae0 100644
--- a/src/math/big/arith_ppc64x.s
+++ b/src/math/big/arith_ppc64x.s
@@ -175,12 +175,3 @@
 
 TEXT ·divWVW(SB), NOSPLIT, $0
 	BR ·divWVW_g(SB)
-
-// func bitLen(x Word) int
-TEXT ·bitLen(SB), NOSPLIT, $0
-	MOVD   x+0(FP), R4
-	CNTLZD R4, R4
-	MOVD   $64, R5
-	SUB    R4, R5
-	MOVD   R5, n+8(FP)
-	RET
diff --git a/src/math/big/arith_s390x.s b/src/math/big/arith_s390x.s
index bddfd9e..4520d16 100644
--- a/src/math/big/arith_s390x.s
+++ b/src/math/big/arith_s390x.s
@@ -1237,13 +1237,3 @@
 
 	MOVD	R10, r+64(FP)
 	RET
-
-// func bitLen(x Word) (n int)
-TEXT ·bitLen(SB),NOSPLIT,$0
-	MOVD  x+0(FP), R2
-	FLOGR R2, R2 // clobbers R3
-	MOVD  $64, R3
-	SUB   R2, R3
-	MOVD  R3, n+8(FP)
-	RET
-
diff --git a/src/math/big/arith_test.go b/src/math/big/arith_test.go
index f2b3083..13b0436 100644
--- a/src/math/big/arith_test.go
+++ b/src/math/big/arith_test.go
@@ -395,32 +395,3 @@
 		})
 	}
 }
-
-func testWordBitLen(t *testing.T, fname string, f func(Word) int) {
-	for i := 0; i <= _W; i++ {
-		x := Word(1) << uint(i-1) // i == 0 => x == 0
-		n := f(x)
-		if n != i {
-			t.Errorf("got %d; want %d for %s(%#x)", n, i, fname, x)
-		}
-	}
-}
-
-func TestWordBitLen(t *testing.T) {
-	testWordBitLen(t, "bitLen", bitLen)
-	testWordBitLen(t, "bitLen_g", bitLen_g)
-}
-
-// runs b.N iterations of bitLen called on a Word containing (1 << nbits)-1.
-func BenchmarkBitLen(b *testing.B) {
-	// Individual bitLen tests. Numbers chosen to examine both sides
-	// of powers-of-two boundaries.
-	for _, nbits := range []uint{0, 1, 2, 3, 4, 5, 8, 9, 16, 17, 31} {
-		testword := Word((uint64(1) << nbits) - 1)
-		b.Run(fmt.Sprint(nbits), func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				bitLen(testword)
-			}
-		})
-	}
-}
diff --git a/src/math/big/float.go b/src/math/big/float.go
index 6517e20..ac5464b 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -14,6 +14,7 @@
 import (
 	"fmt"
 	"math"
+	"math/bits"
 )
 
 const debugFloat = false // enable for debugging
@@ -498,8 +499,8 @@
 	}
 	// x != 0
 	z.form = finite
-	s := nlz64(x)
-	z.mant = z.mant.setUint64(x << s)
+	s := bits.LeadingZeros64(x)
+	z.mant = z.mant.setUint64(x << uint(s))
 	z.exp = int32(64 - s) // always fits
 	if z.prec < 64 {
 		z.round(0)
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index 9911280..6d0f17d 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -8,6 +8,7 @@
 	"bytes"
 	"fmt"
 	"math"
+	"math/bits"
 	"strconv"
 	"testing"
 )
@@ -328,9 +329,9 @@
 
 // actualPrec returns the number of actually used mantissa bits.
 func actualPrec(x float64) uint {
-	if bits := math.Float64bits(x); x != 0 && bits&(0x7ff<<52) == 0 {
+	if mant := math.Float64bits(x); x != 0 && mant&(0x7ff<<52) == 0 {
 		// x is denormalized
-		return 64 - nlz64(bits&(1<<52-1))
+		return 64 - uint(bits.LeadingZeros64(mant&(1<<52-1)))
 	}
 	return 53
 }
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 6717655..889eacb 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -644,7 +644,7 @@
 // Length of x in bits. x must be normalized.
 func (x nat) bitLen() int {
 	if i := len(x) - 1; i >= 0 {
-		return i*_W + bitLen(x[i])
+		return i*_W + bits.Len(uint(x[i]))
 	}
 	return 0
 }
diff --git a/src/math/big/natconv_test.go b/src/math/big/natconv_test.go
index bdb60e6..898a39fc 100644
--- a/src/math/big/natconv_test.go
+++ b/src/math/big/natconv_test.go
@@ -8,10 +8,18 @@
 	"bytes"
 	"fmt"
 	"io"
+	"math/bits"
 	"strings"
 	"testing"
 )
 
+// log2 computes the integer binary logarithm of x.
+// The result is the integer n for which 2^n <= x < 2^(n+1).
+// If x == 0, the result is -1.
+func log2(x Word) int {
+	return bits.Len(uint(x)) - 1
+}
+
 func itoa(x nat, base int) []byte {
 	// special cases
 	switch {