}
}
+ // Big stuff escapes unconditionally
+ // "Big" conditions that were scattered around in walk have been gathered here
+ if n.Esc != EscHeap && n.Type != nil && (n.Type.Width > MaxStackVarSize ||
+ n.Op == ONEW && n.Type.Type.Width >= 1<<16 ||
+ n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
+ if Debug['m'] > 1 {
+ Warnl(int(n.Lineno), "%v is too large for stack", n)
+ }
+ n.Esc = EscHeap
+ addrescapes(n)
+ escassign(e, &e.theSink, n)
+ }
+
esc(e, n.Left, n)
esc(e, n.Right, n)
esc(e, n.Ntest, n)
// which stores the addresses of stack variables into the closure.
// If the closure does not escape, it needs to be on the stack
// or else the stack copier will not update it.
+ // Large values are also moved off stack in escape analysis;
+ // because large values may contain pointers, it must happen early.
escapes(xtop)
- // Escape analysis moved escaped values off stack.
- // Move large values off stack too.
- movelarge(xtop)
-
// Phase 7: Transform closure bodies to properly reference captured variables.
// This needs to happen before walk, because closures must be transformed
// before walk reaches a call of a closure.
}
}
-func movelarge(l *NodeList) {
- for ; l != nil; l = l.Next {
- if l.N.Op == ODCLFUNC {
- movelargefn(l.N)
- }
- }
-}
-
-func movelargefn(fn *Node) {
- var n *Node
-
- for l := fn.Func.Dcl; l != nil; l = l.Next {
- n = l.N
- if n.Class == PAUTO && n.Type != nil && n.Type.Width > MaxStackVarSize {
- addrescapes(n)
- }
- }
-}
-
func Cgen_checknil(n *Node) {
if Disable_checknil != 0 {
return
*np = n
}
+func isSmallMakeSlice(n *Node) bool {
+ if n.Op != OMAKESLICE {
+ return false
+ }
+ l := n.Left
+ r := n.Right
+ if r == nil {
+ r = l
+ }
+ t := n.Type
+
+ return Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.(*Mpint)) < (1<<16)/t.Type.Width)
+}
+
/*
* walk the whole tree of the body of an
* expression or simple statement.
goto ret
case ONEW:
- if n.Esc == EscNone && n.Type.Type.Width < 1<<16 {
+ if n.Esc == EscNone {
+ if n.Type.Type.Width >= 1<<16 {
+ Fatal("Large ONEW with EscNone, %v", n)
+ }
r := temp(n.Type.Type)
r = Nod(OAS, r, nil) // zero temp
typecheck(&r, Etop)
l = r
}
t := n.Type
- if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.(*Mpint)) < (1<<16)/t.Type.Width) {
+ if n.Esc == EscNone {
+ if !isSmallMakeSlice(n) {
+ Fatal("Non-small OMAKESLICE with EscNone, %v", n)
+ }
// var arr [r]T
// n = arr[:l]
t = aindex(r, t.Type) // [r]T
return p, q
}
-
var global interface{}
type T1 struct {
func f9() {
var j T1 // ERROR "moved to heap: j"
- f8(&j) // ERROR "&j escapes to heap"
+ f8(&j) // ERROR "&j escapes to heap"
}
func f10() {
// These don't escape but are too big for the stack
- var x [1<<30]byte // ERROR "moved to heap: x"
- var y = make([]byte, 1<<30) // ERROR "does not escape"
+ var x [1 << 30]byte // ERROR "moved to heap: x"
+ var y = make([]byte, 1<<30) // ERROR "make\(\[\]byte, 1 << 30\) escapes to heap"
_ = x[0] + y[0]
}
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Test escape analysis for function parameters.
-
-// In this test almost everything is BAD except the simplest cases
-// where input directly flows to output.
+// Test escape analysis for arrays and some large things
package foo
return x[1]
}
-// BAD: would be nice to record that *y (content) is what leaks, not y itself
func fum(x *U, y **string) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$"
x[0] = *y
return x[1]
}
-// BAD: would be nice to record that y[0] (content) is what leaks, not y itself
func fuo(x *U, y *U) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$"
x[0] = y[0]
return x[1]
}
+
+// These two tests verify that:
+// small array literals are stack allocated;
+// pointers stored in small array literals do not escape;
+// large array literals are heap allocated;
+// pointers stored in large array literals escape.
+func hugeLeaks1(x **string, y **string) { // ERROR "leaking param content: x" "hugeLeaks1 y does not escape" "mark escaped content: x"
+ a := [10]*string{*y}
+ _ = a
+ // 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
+ b := [4000000]*string{*x} // ERROR "moved to heap: b"
+ _ = b
+}
+
+func hugeLeaks2(x *string, y *string) { // ERROR "leaking param: x" "hugeLeaks2 y does not escape"
+ a := [10]*string{y}
+ _ = a
+ // 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
+ b := [4000000]*string{x} // ERROR "moved to heap: b"
+ _ = b
+}
+
+// BAD: x need not leak.
+func doesNew1(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+ a := new([10]*string) // ERROR "new\(\[10\]\*string\) does not escape"
+ a[0] = x
+ b := new([65537]*string) // ERROR "new\(\[65537\]\*string\) escapes to heap"
+ b[0] = y
+}
+
+type a10 struct {
+ s *string
+ i [10]int32
+}
+
+type a65537 struct {
+ s *string
+ i [65537]int32
+}
+
+// BAD: x need not leak.
+func doesNew2(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+ a := new(a10) // ERROR "new\(a10\) does not escape"
+ a.s = x
+ b := new(a65537) // ERROR "new\(a65537\) escapes to heap"
+ b.s = y
+}
+
+// BAD: x need not leak.
+func doesMakeSlice(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+ a := make([]*string, 10) // ERROR "make\(\[\]\*string, 10\) does not escape"
+ a[0] = x
+ b := make([]*string, 65537) // ERROR "make\(\[\]\*string, 65537\) escapes to heap"
+ b[0] = y
+}