]> Cypherpunks repositories - gostls13.git/commitdiff
math/big: make NewInt inlineable and zero allocation
authorKeith Randall <khr@golang.org>
Wed, 8 Jun 2022 20:30:02 +0000 (13:30 -0700)
committerKeith Randall <khr@google.com>
Mon, 8 Aug 2022 17:39:06 +0000 (17:39 +0000)
Mark the assembly routines as not escaping their arguments.

Add a special case to NewInt that, when inlined, can do all
of its allocations (a big.Int and a [1]Word) on the stack.

Update #29951

Change-Id: I9bd38c262eb97df98c0ed9874da7daac381243ea
Reviewed-on: https://go-review.googlesource.com/c/go/+/411254
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Robert Griesemer <gri@google.com>
src/math/big/arith_decl.go
src/math/big/int.go
src/math/big/int_test.go

index 301aa55f1ab6fef8b8111accf41142a4d5512899..9b254f22130fcafd769ed197bec5d1cd2678ba38 100644 (file)
@@ -8,11 +8,27 @@
 package big
 
 // implemented in arith_$GOARCH.s
+
+//go:noescape
 func addVV(z, x, y []Word) (c Word)
+
+//go:noescape
 func subVV(z, x, y []Word) (c Word)
+
+//go:noescape
 func addVW(z, x []Word, y Word) (c Word)
+
+//go:noescape
 func subVW(z, x []Word, y Word) (c Word)
+
+//go:noescape
 func shlVU(z, x []Word, s uint) (c Word)
+
+//go:noescape
 func shrVU(z, x []Word, s uint) (c Word)
+
+//go:noescape
 func mulAddVWW(z, x []Word, y, r Word) (c Word)
+
+//go:noescape
 func addMulVVW(z, x []Word, y Word) (c Word)
index ec168f8ffe57189b736c19b39c642f0aeb1b3f72..ca4c3561e629fcd1aa6ed37c5203d646875131cc 100644 (file)
@@ -65,7 +65,20 @@ func (z *Int) SetUint64(x uint64) *Int {
 
 // NewInt allocates and returns a new Int set to x.
 func NewInt(x int64) *Int {
-       return new(Int).SetInt64(x)
+       // This code is arranged to be inlineable and produce
+       // zero allocations when inlined. See issue 29951.
+       u := uint64(x)
+       if x < 0 {
+               u = -u
+       }
+       var abs []Word
+       if x == 0 {
+       } else if _W == 32 && u>>32 != 0 {
+               abs = []Word{Word(u), Word(u >> 32)}
+       } else {
+               abs = []Word{Word(u)}
+       }
+       return &Int{neg: x < 0, abs: abs}
 }
 
 // Set sets z to x and returns z.
index 3c8557323a03245424a837b56d4480a2531e9193..4ebb09d2c2affd925abe36d1f751849822ad48e5 100644 (file)
@@ -8,6 +8,7 @@ import (
        "bytes"
        "encoding/hex"
        "fmt"
+       "math"
        "math/rand"
        "strconv"
        "strings"
@@ -92,7 +93,7 @@ func testFunZZ(t *testing.T, msg string, f funZZ, a argZZ) {
                t.Errorf("%s%v is not normalized", msg, z)
        }
        if (&z).Cmp(a.z) != 0 {
-               t.Errorf("%s%+v\n\tgot z = %v; want %v", msg, a, &z, a.z)
+               t.Errorf("%v %s %v\n\tgot z = %v; want %v", a.x, msg, a.y, &z, a.z)
        }
 }
 
@@ -1894,3 +1895,26 @@ func TestFillBytes(t *testing.T) {
                })
        }
 }
+
+func TestNewIntMinInt64(t *testing.T) {
+       // Test for uint64 cast in NewInt.
+       want := int64(math.MinInt64)
+       if got := NewInt(want).Int64(); got != want {
+               t.Fatalf("wanted %d, got %d", want, got)
+       }
+}
+
+func TestNewIntAllocs(t *testing.T) {
+       for _, n := range []int64{0, 7, -7, 1 << 30, -1 << 30, 1 << 50, -1 << 50} {
+               x := NewInt(3)
+               got := testing.AllocsPerRun(100, func() {
+                       // NewInt should inline, and all its allocations
+                       // can happen on the stack. Passing the result of NewInt
+                       // to Add should not cause any of those allocations to escape.
+                       x.Add(x, NewInt(n))
+               })
+               if got != 0 {
+                       t.Errorf("x.Add(x, NewInt(%d)), wanted 0 allocations, got %f", n, got)
+               }
+       }
+}