Skip to content

Commit

Permalink
math/big: make NewInt inlineable and zero allocation
Browse files Browse the repository at this point in the history
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 golang#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>
  • Loading branch information
randall77 authored and jproberts committed Aug 10, 2022
1 parent f29d7d2 commit 83c9b83
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 2 deletions.
16 changes: 16 additions & 0 deletions src/math/big/arith_decl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
15 changes: 14 additions & 1 deletion src/math/big/int.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
26 changes: 25 additions & 1 deletion src/math/big/int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"bytes"
"encoding/hex"
"fmt"
"math"
"math/rand"
"strconv"
"strings"
Expand Down Expand Up @@ -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)
}
}

Expand Down Expand Up @@ -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)
}
}
}

0 comments on commit 83c9b83

Please sign in to comment.