Commit ec4e8517 authored by LE Manh Cuong's avatar LE Manh Cuong Committed by Keith Randall

cmd/compile: support more length types for slice extension optimization

golang.org/cl/109517 optimized the compiler to avoid the allocation for make in
append(x, make([]T, y)...). This was only implemented for the case that y has type int.

This change extends the optimization to trigger for all integer types where the value
is known at compile time to fit into an int.

name             old time/op    new time/op    delta
ExtendInt-12        106ns ± 4%     106ns ± 0%      ~     (p=0.351 n=10+6)
ExtendUint64-12    1.03µs ± 5%    0.10µs ± 4%   -90.01%  (p=0.000 n=9+10)

name             old alloc/op   new alloc/op   delta
ExtendInt-12        0.00B          0.00B           ~     (all equal)
ExtendUint64-12    13.6kB ± 0%     0.0kB       -100.00%  (p=0.000 n=10+10)

name             old allocs/op  new allocs/op  delta
ExtendInt-12         0.00           0.00           ~     (all equal)
ExtendUint64-12      1.00 ± 0%      0.00       -100.00%  (p=0.000 n=10+10)

Updates #29785

Change-Id: Ief7760097c285abd591712da98c5b02bc3961fcd
Reviewed-on: https://go-review.googlesource.com/c/go/+/182559
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 07ad8400
......@@ -2700,15 +2700,14 @@ func isAppendOfMake(n *Node) bool {
return false
}
// y must be either an integer constant or a variable of type int.
// typecheck checks that constant arguments to make are not negative and
// fit into an int.
// runtime.growslice uses int as type for the newcap argument.
// Constraining variables to be type int avoids the need for runtime checks
// that e.g. check if an int64 value fits into an int.
// TODO(moehrmann): support other integer types that always fit in an int
// y must be either an integer constant or the largest possible positive value
// of variable y needs to fit into an uint.
// typecheck made sure that constant arguments to make are not negative and fit into an int.
// The care of overflow of the len argument to make will be handled by an explicit check of int(len) < 0 during runtime.
y := second.Left
if !Isconst(y, CTINT) && y.Type.Etype != TINT {
if !Isconst(y, CTINT) && maxintval[y.Type.Etype].Cmp(maxintval[TUINT]) > 0 {
return false
}
......@@ -2742,7 +2741,9 @@ func isAppendOfMake(n *Node) bool {
// }
// s
func extendslice(n *Node, init *Nodes) *Node {
// isAppendOfMake made sure l2 fits in an int.
// isAppendOfMake made sure all possible positive values of l2 fit into an uint.
// The case of l2 overflow when converting from e.g. uint to int is handled by an explicit
// check of l2 < 0 at runtime which is generated below.
l2 := conv(n.List.Second().Left, types.Types[TINT])
l2 = typecheck(l2, ctxExpr)
n.List.SetSecond(l2) // walkAppendArgs expects l2 in n.List.Second().
......
......@@ -44,6 +44,27 @@ func SliceExtensionConst(s []int) []int {
return append(s, make([]int, 1<<2)...)
}
func SliceExtensionConstInt64(s []int) []int {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, int64(1<<2))...)
}
func SliceExtensionConstUint64(s []int) []int {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, uint64(1<<2))...)
}
func SliceExtensionConstUint(s []int) []int {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:-`.*runtime\.panicmakeslicelen`
return append(s, make([]int, uint(1<<2))...)
}
func SliceExtensionPointer(s []*int, l int) []*int {
// amd64:`.*runtime\.memclrHasPointers`
// amd64:-`.*runtime\.makeslice`
......@@ -56,6 +77,27 @@ func SliceExtensionVar(s []byte, l int) []byte {
return append(s, make([]byte, l)...)
}
func SliceExtensionVarInt64(s []byte, l int64) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:`.*runtime\.panicmakeslicelen`
return append(s, make([]byte, l)...)
}
func SliceExtensionVarUint64(s []byte, l uint64) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:`.*runtime\.panicmakeslicelen`
return append(s, make([]byte, l)...)
}
func SliceExtensionVarUint(s []byte, l uint) []byte {
// amd64:`.*runtime\.memclrNoHeapPointers`
// amd64:-`.*runtime\.makeslice`
// amd64:`.*runtime\.panicmakeslicelen`
return append(s, make([]byte, l)...)
}
func SliceExtensionInt64(s []int, l64 int64) []int {
// 386:`.*runtime\.makeslice`
// 386:-`.*runtime\.memclr`
......
......@@ -19,29 +19,36 @@ func main() {
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
testMakeInAppend(n)
var t *byte
if unsafe.Sizeof(t) == 8 {
// Test mem > maxAlloc
var n2 int64 = 1 << 59
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
testMakeInAppend(int(n2))
// Test elem.size*cap overflow
n2 = 1<<63 - 1
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
testMakeInAppend(int(n2))
var x uint64 = 1<<64 - 1
shouldPanic("len out of range", func() { _ = make([]byte, x) })
shouldPanic("cap out of range", func() { _ = make(T, 0, x) })
testMakeInAppend(int(x))
} else {
n = 1<<31 - 1
shouldPanic("len out of range", func() { _ = make(T, n) })
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
testMakeInAppend(n)
var x uint64 = 1<<32 - 1
shouldPanic("len out of range", func() { _ = make([]byte, x) })
shouldPanic("cap out of range", func() { _ = make(T, 0, x) })
testMakeInAppend(int(x))
}
// Test make in append panics since the gc compiler optimizes makes in appends.
shouldPanic("len out of range", func() { _ = append(T{}, make(T, n)...) })
shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, n)...) })
shouldPanic("len out of range", func() { _ = append(T{}, make(T, int64(n))...) })
shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, int64(n))...) })
}
func shouldPanic(str string, f func()) {
......@@ -58,3 +65,21 @@ func shouldPanic(str string, f func()) {
f()
}
// Test make in append panics since the gc compiler optimizes makes in appends.
func testMakeInAppend(n int) {
lengths := []int{0, 1}
for _, length := range lengths {
t := make(T, length)
shouldPanic("len out of range", func() { _ = append(t, make(T, n)...) })
shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, n)...) })
shouldPanic("len out of range", func() { _ = append(t, make(T, int64(n))...) })
shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, int64(n))...) })
shouldPanic("len out of range", func() { _ = append(t, make(T, uint64(n))...) })
shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, uint64(n))...) })
shouldPanic("len out of range", func() { _ = append(t, make(T, int(n))...) })
shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, int(n))...) })
shouldPanic("len out of range", func() { _ = append(t, make(T, uint(n))...) })
shouldPanic("cap out of range", func() { _ = append(t, make(T, 0, uint(n))...) })
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment