mirror of https://github.com/golang/go.git
216 lines
6.0 KiB
Go
216 lines
6.0 KiB
Go
// Copyright 2018 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
|
|
|
|
import (
|
|
"cmd/compile/internal/ir"
|
|
"cmd/compile/internal/typecheck"
|
|
)
|
|
|
|
func isSliceSelfAssign(dst, src ir.Node) bool {
|
|
// Detect the following special case.
|
|
//
|
|
// func (b *Buffer) Foo() {
|
|
// n, m := ...
|
|
// b.buf = b.buf[n:m]
|
|
// }
|
|
//
|
|
// This assignment is a no-op for escape analysis,
|
|
// it does not store any new pointers into b that were not already there.
|
|
// However, without this special case b will escape, because we assign to OIND/ODOTPTR.
|
|
// Here we assume that the statement will not contain calls,
|
|
// that is, that order will move any calls to init.
|
|
// Otherwise base ONAME value could change between the moments
|
|
// when we evaluate it for dst and for src.
|
|
|
|
// dst is ONAME dereference.
|
|
var dstX ir.Node
|
|
switch dst.Op() {
|
|
default:
|
|
return false
|
|
case ir.ODEREF:
|
|
dst := dst.(*ir.StarExpr)
|
|
dstX = dst.X
|
|
case ir.ODOTPTR:
|
|
dst := dst.(*ir.SelectorExpr)
|
|
dstX = dst.X
|
|
}
|
|
if dstX.Op() != ir.ONAME {
|
|
return false
|
|
}
|
|
// src is a slice operation.
|
|
switch src.Op() {
|
|
case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
|
|
// OK.
|
|
case ir.OSLICEARR, ir.OSLICE3ARR:
|
|
// Since arrays are embedded into containing object,
|
|
// slice of non-pointer array will introduce a new pointer into b that was not already there
|
|
// (pointer to b itself). After such assignment, if b contents escape,
|
|
// b escapes as well. If we ignore such OSLICEARR, we will conclude
|
|
// that b does not escape when b contents do.
|
|
//
|
|
// Pointer to an array is OK since it's not stored inside b directly.
|
|
// For slicing an array (not pointer to array), there is an implicit OADDR.
|
|
// We check that to determine non-pointer array slicing.
|
|
src := src.(*ir.SliceExpr)
|
|
if src.X.Op() == ir.OADDR {
|
|
return false
|
|
}
|
|
default:
|
|
return false
|
|
}
|
|
// slice is applied to ONAME dereference.
|
|
var baseX ir.Node
|
|
switch base := src.(*ir.SliceExpr).X; base.Op() {
|
|
default:
|
|
return false
|
|
case ir.ODEREF:
|
|
base := base.(*ir.StarExpr)
|
|
baseX = base.X
|
|
case ir.ODOTPTR:
|
|
base := base.(*ir.SelectorExpr)
|
|
baseX = base.X
|
|
}
|
|
if baseX.Op() != ir.ONAME {
|
|
return false
|
|
}
|
|
// dst and src reference the same base ONAME.
|
|
return dstX.(*ir.Name) == baseX.(*ir.Name)
|
|
}
|
|
|
|
// isSelfAssign reports whether assignment from src to dst can
|
|
// be ignored by the escape analysis as it's effectively a self-assignment.
|
|
func isSelfAssign(dst, src ir.Node) bool {
|
|
if isSliceSelfAssign(dst, src) {
|
|
return true
|
|
}
|
|
|
|
// Detect trivial assignments that assign back to the same object.
|
|
//
|
|
// It covers these cases:
|
|
// val.x = val.y
|
|
// val.x[i] = val.y[j]
|
|
// val.x1.x2 = val.x1.y2
|
|
// ... etc
|
|
//
|
|
// These assignments do not change assigned object lifetime.
|
|
|
|
if dst == nil || src == nil || dst.Op() != src.Op() {
|
|
return false
|
|
}
|
|
|
|
// The expression prefix must be both "safe" and identical.
|
|
switch dst.Op() {
|
|
case ir.ODOT, ir.ODOTPTR:
|
|
// Safe trailing accessors that are permitted to differ.
|
|
dst := dst.(*ir.SelectorExpr)
|
|
src := src.(*ir.SelectorExpr)
|
|
return ir.SameSafeExpr(dst.X, src.X)
|
|
case ir.OINDEX:
|
|
dst := dst.(*ir.IndexExpr)
|
|
src := src.(*ir.IndexExpr)
|
|
if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
|
|
return false
|
|
}
|
|
return ir.SameSafeExpr(dst.X, src.X)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// mayAffectMemory reports whether evaluation of n may affect the program's
|
|
// memory state. If the expression can't affect memory state, then it can be
|
|
// safely ignored by the escape analysis.
|
|
func mayAffectMemory(n ir.Node) bool {
|
|
// We may want to use a list of "memory safe" ops instead of generally
|
|
// "side-effect free", which would include all calls and other ops that can
|
|
// allocate or change global state. For now, it's safer to start with the latter.
|
|
//
|
|
// We're ignoring things like division by zero, index out of range,
|
|
// and nil pointer dereference here.
|
|
|
|
// TODO(rsc): It seems like it should be possible to replace this with
|
|
// an ir.Any looking for any op that's not the ones in the case statement.
|
|
// But that produces changes in the compiled output detected by buildall.
|
|
switch n.Op() {
|
|
case ir.ONAME, ir.OLITERAL, ir.ONIL:
|
|
return false
|
|
|
|
case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
|
|
n := n.(*ir.BinaryExpr)
|
|
return mayAffectMemory(n.X) || mayAffectMemory(n.Y)
|
|
|
|
case ir.OINDEX:
|
|
n := n.(*ir.IndexExpr)
|
|
return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
|
|
|
|
case ir.OCONVNOP, ir.OCONV:
|
|
n := n.(*ir.ConvExpr)
|
|
return mayAffectMemory(n.X)
|
|
|
|
case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
|
|
n := n.(*ir.UnaryExpr)
|
|
return mayAffectMemory(n.X)
|
|
|
|
case ir.ODOT, ir.ODOTPTR:
|
|
n := n.(*ir.SelectorExpr)
|
|
return mayAffectMemory(n.X)
|
|
|
|
case ir.ODEREF:
|
|
n := n.(*ir.StarExpr)
|
|
return mayAffectMemory(n.X)
|
|
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
// HeapAllocReason returns the reason the given Node must be heap
|
|
// allocated, or the empty string if it doesn't.
|
|
func HeapAllocReason(n ir.Node) string {
|
|
if n == nil || n.Type() == nil {
|
|
return ""
|
|
}
|
|
|
|
// Parameters are always passed via the stack.
|
|
if n.Op() == ir.ONAME {
|
|
n := n.(*ir.Name)
|
|
if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
|
|
return ""
|
|
}
|
|
}
|
|
|
|
if n.Type().Width > ir.MaxStackVarSize {
|
|
return "too large for stack"
|
|
}
|
|
|
|
if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width > ir.MaxImplicitStackVarSize {
|
|
return "too large for stack"
|
|
}
|
|
|
|
if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
|
|
return "too large for stack"
|
|
}
|
|
if n.Op() == ir.OMETHVALUE && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
|
|
return "too large for stack"
|
|
}
|
|
|
|
if n.Op() == ir.OMAKESLICE {
|
|
n := n.(*ir.MakeExpr)
|
|
r := n.Cap
|
|
if r == nil {
|
|
r = n.Len
|
|
}
|
|
if !ir.IsSmallIntConst(r) {
|
|
return "non-constant size"
|
|
}
|
|
if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Width {
|
|
return "too large for stack"
|
|
}
|
|
}
|
|
|
|
return ""
|
|
}
|