cmd/compile: make isSmallMakeSlice checks slice cap only

If slice cap is not set, it will be equal to slice len. So
isSmallMakeSlice only needs to check whether slice cap is constant.

While at it, also add test to make sure panicmakeslicecap is called
when make slice contains invalid non-constant len.

For this benchmark:

func BenchmarkMakeSliceNonConstantLen(b *testing.B) {
	len := 1
	for i := 0; i < b.N; i++ {
		s := make([]int, len, 2)
		_ = s

	}
}

Result compare with parent:

name                        old time/op  new time/op  delta
MakeSliceNonConstantLen-12  18.4ns ± 1%   0.2ns ± 2%  -98.66%  (p=0.008 n=5+5)

Fixes #37975

Change-Id: I4bc926361bc2ffeab4cfaa888ef0a30cbc3b80e8
Reviewed-on: https://go-review.googlesource.com/c/go/+/226278
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Cuong Manh Le 2020-03-29 01:19:50 +07:00 committed by Matthew Dempsky
parent 8114242359
commit 7b30a2d268
5 changed files with 86 additions and 10 deletions

View file

@ -13,6 +13,7 @@ var runtimeDecls = [...]struct {
{"panicdivide", funcTag, 5}, {"panicdivide", funcTag, 5},
{"panicshift", funcTag, 5}, {"panicshift", funcTag, 5},
{"panicmakeslicelen", funcTag, 5}, {"panicmakeslicelen", funcTag, 5},
{"panicmakeslicecap", funcTag, 5},
{"throwinit", funcTag, 5}, {"throwinit", funcTag, 5},
{"panicwrap", funcTag, 5}, {"panicwrap", funcTag, 5},
{"gopanic", funcTag, 7}, {"gopanic", funcTag, 7},

View file

@ -18,6 +18,7 @@ func newobject(typ *byte) *any
func panicdivide() func panicdivide()
func panicshift() func panicshift()
func panicmakeslicelen() func panicmakeslicelen()
func panicmakeslicecap()
func throwinit() func throwinit()
func panicwrap() func panicwrap()

View file

@ -354,14 +354,13 @@ func isSmallMakeSlice(n *Node) bool {
if n.Op != OMAKESLICE { if n.Op != OMAKESLICE {
return false return false
} }
l := n.Left
r := n.Right r := n.Right
if r == nil { if r == nil {
r = l r = n.Left
} }
t := n.Type t := n.Type
return smallintconst(l) && smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < maxImplicitStackVarSize/t.Elem().Width) return smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < maxImplicitStackVarSize/t.Elem().Width)
} }
// walk the whole tree of the body of an // walk the whole tree of the body of an
@ -1338,6 +1337,19 @@ opswitch:
if i < 0 { if i < 0 {
Fatalf("walkexpr: invalid index %v", r) Fatalf("walkexpr: invalid index %v", r)
} }
// if len < 0 { panicmakeslicelen }
nif := nod(OIF, nod(OLT, l, nodintconst(0)), nil)
nif.Nbody.Set1(mkcall("panicmakeslicelen", nil, init))
nif = typecheck(nif, ctxStmt)
init.Append(nif)
// if len > cap { panicmakeslicecap }
nif = nod(OIF, nod(OGT, conv(l, types.Types[TUINT64]), nodintconst(i)), nil)
nif.Nbody.Set1(mkcall("panicmakeslicecap", nil, init))
nif = typecheck(nif, ctxStmt)
init.Append(nif)
t = types.NewArray(t.Elem(), i) // [r]T t = types.NewArray(t.Elem(), i) // [r]T
var_ := temp(t) var_ := temp(t)
a := nod(OAS, var_, nil) // zero temp a := nod(OAS, var_, nil) // zero temp

View file

@ -18,28 +18,28 @@ var sink interface{}
func slice0() { func slice0() {
var s []*int var s []*int
// BAD: i should not escape // BAD: i should not escape
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s = append(s, &i) s = append(s, &i)
_ = s _ = s
} }
func slice1() *int { func slice1() *int {
var s []*int var s []*int
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s = append(s, &i) s = append(s, &i)
return s[0] return s[0]
} }
func slice2() []*int { func slice2() []*int {
var s []*int var s []*int
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s = append(s, &i) s = append(s, &i)
return s return s
} }
func slice3() *int { func slice3() *int {
var s []*int var s []*int
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s = append(s, &i) s = append(s, &i)
for _, p := range s { for _, p := range s {
return p return p
@ -48,7 +48,7 @@ func slice3() *int {
} }
func slice4(s []*int) { // ERROR "s does not escape" func slice4(s []*int) { // ERROR "s does not escape"
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s[0] = &i s[0] = &i
} }
@ -56,14 +56,14 @@ func slice5(s []*int) { // ERROR "s does not escape"
if s != nil { if s != nil {
s = make([]*int, 10) // ERROR "make\(\[\]\*int, 10\) does not escape" s = make([]*int, 10) // ERROR "make\(\[\]\*int, 10\) does not escape"
} }
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s[0] = &i s[0] = &i
} }
func slice6() { func slice6() {
s := make([]*int, 10) // ERROR "make\(\[\]\*int, 10\) does not escape" s := make([]*int, 10) // ERROR "make\(\[\]\*int, 10\) does not escape"
// BAD: i should not escape // BAD: i should not escape
i := 0 // ERROR "moved to heap: i" i := 0 // ERROR "moved to heap: i"
s[0] = &i s[0] = &i
_ = s _ = s
} }
@ -93,6 +93,14 @@ func slice10() []*int {
return s return s
} }
func slice11() {
i := 2
s := make([]int, 2, 3) // ERROR "make\(\[\]int, 2, 3\) does not escape"
s = make([]int, i, 3) // ERROR "make\(\[\]int, i, 3\) does not escape"
s = make([]int, i, 1) // ERROR "make\(\[\]int, i, 1\) does not escape"
_ = s
}
func envForDir(dir string) []string { // ERROR "dir does not escape" func envForDir(dir string) []string { // ERROR "dir does not escape"
env := os.Environ() env := os.Environ()
return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape" return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape"

View file

@ -0,0 +1,54 @@
// run
// Copyright 2020 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.
// Make sure runtime.panicmakeslice* are called.
package main
import "strings"
func main() {
// Test typechecking passes if len is valid
// but cap is out of range for len's type.
var x byte
_ = make([]int, x, 300)
capOutOfRange := func() {
i := 2
s := make([]int, i, 1)
s[0] = 1
}
lenOutOfRange := func() {
i := -1
s := make([]int, i, 3)
s[0] = 1
}
tests := []struct {
f func()
panicStr string
}{
{capOutOfRange, "cap out of range"},
{lenOutOfRange, "len out of range"},
}
for _, tc := range tests {
shouldPanic(tc.panicStr, tc.f)
}
}
func shouldPanic(str string, f func()) {
defer func() {
err := recover()
runtimeErr := err.(error).Error()
if !strings.Contains(runtimeErr, str) {
panic("got panic " + runtimeErr + ", want " + str)
}
}()
f()
}