package escape
import (
+ "cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
+ "go/constant"
+ "go/token"
)
func isSliceSelfAssign(dst, src ir.Node) bool {
if n.Op() == ir.OMAKESLICE {
n := n.(*ir.MakeExpr)
- r := n.Cap
- if r == nil {
- r = n.Len
+
+ r := &n.Cap
+ if n.Cap == nil {
+ r = &n.Len
+ }
+
+ // Try to determine static values of make() calls, to avoid allocating them on the heap.
+ // We are doing this in escape analysis, so that it happens after inlining and devirtualization.
+ if s := ir.StaticValue(*r); s.Op() == ir.OLITERAL {
+ lit, ok := s.(*ir.BasicLit)
+ if !ok || lit.Val().Kind() != constant.Int {
+ base.Fatalf("unexpected BasicLit Kind")
+ }
+ if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
+ *r = lit
+ }
}
- if !ir.IsSmallIntConst(r) {
+
+ if !ir.IsSmallIntConst(*r) {
return "non-constant size"
}
- if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Size() {
+ if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(*r) > ir.MaxImplicitStackVarSize/t.Elem().Size() {
return "too large for stack"
}
}
--- /dev/null
+// errorcheck -0 -m
+
+// Copyright 2025 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.
+
+package escape
+
+const globalConstSize = 128
+
+var globalVarSize = 128
+
+//go:noinline
+func testSlices() {
+ {
+ size := 128
+ _ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
+ }
+
+ {
+ s := 128
+ size := s
+ _ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
+ }
+
+ {
+ size := 128
+ _ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
+ }
+
+ {
+ s := 128
+ size := s
+ _ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
+ }
+
+ {
+ s1 := 128
+ s2 := 256
+ _ = make([]byte, s2, s1) // ERROR "make\(\[\]byte, s2, 128\) does not escape"
+ }
+
+ allocLen(256) // ERROR "make\(\[\]byte, 256\) does not escape" "inlining call"
+ allocCap(256) // ERROR "make\(\[\]byte, 0, 256\) does not escape" "inlining call"
+ _ = newT(256) // ERROR "make\(\[\]byte, 256\) does not escape" "inlining call"
+
+ {
+ size := globalConstSize
+ _ = make([]byte, size) // ERROR "make\(\[\]byte, 128\) does not escape"
+ }
+
+ allocLen(globalConstSize) // ERROR "make\(\[\]byte, 128\) does not escape" "inlining call"
+ allocCap(globalConstSize) // ERROR "make\(\[\]byte, 0, 128\) does not escape" "inlining call"
+ _ = newT(globalConstSize) // ERROR "make\(\[\]byte, 128\) does not escape" "inlining call"
+
+ {
+ c := 128
+ s := 256
+ _ = make([]byte, s, c) // ERROR "make\(\[\]byte, s, 128\) does not escape"
+ }
+
+ {
+ s := 256
+ _ = make([]byte, s, globalConstSize) // ERROR "make\(\[\]byte, s, 128\) does not escape"
+ }
+
+ {
+ _ = make([]byte, globalVarSize) // ERROR "make\(\[\]byte, globalVarSize\) escapes to heap"
+ _ = make([]byte, globalVarSize, globalConstSize) // ERROR "make\(\[\]byte, globalVarSize, 128\) does not escape"
+ }
+}
+
+func allocLen(l int) []byte { // ERROR "can inline"
+ return make([]byte, l) // ERROR "escapes to heap"
+}
+
+func allocCap(l int) []byte { // ERROR "can inline"
+ return make([]byte, 0, l) // ERROR "escapes to heap"
+}
+
+type t struct {
+ s []byte
+}
+
+func newT(l int) t { // ERROR "can inline"
+ return t{make([]byte, l)} // ERROR "make.*escapes to heap"
+}
+
+//go:noinline
+func testMaps() {
+ size := 128
+ _ = make(map[string]int, size) // ERROR "does not escape"
+
+ _ = allocMapLen(128) // ERROR "does not escape" "inlining call"
+ _ = newM(128) // ERROR "does not escape" "inlining call"
+}
+
+func allocMapLen(l int) map[string]int { // ERROR "can inline"
+ return make(map[string]int, l) // ERROR "escapes to heap"
+}
+
+type m struct {
+ m map[string]int
+}
+
+func newM(l int) m { // ERROR "can inline"
+ return m{make(map[string]int, l)} // ERROR "make.*escapes to heap"
+}