Source file src/cmd/compile/internal/escape/utils.go

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package escape
     6  
     7  import (
     8  	"cmd/compile/internal/ir"
     9  	"cmd/compile/internal/typecheck"
    10  )
    11  
    12  func isSliceSelfAssign(dst, src ir.Node) bool {
    13  	// Detect the following special case.
    14  	//
    15  	//	func (b *Buffer) Foo() {
    16  	//		n, m := ...
    17  	//		b.buf = b.buf[n:m]
    18  	//	}
    19  	//
    20  	// This assignment is a no-op for escape analysis,
    21  	// it does not store any new pointers into b that were not already there.
    22  	// However, without this special case b will escape, because we assign to OIND/ODOTPTR.
    23  	// Here we assume that the statement will not contain calls,
    24  	// that is, that order will move any calls to init.
    25  	// Otherwise base ONAME value could change between the moments
    26  	// when we evaluate it for dst and for src.
    27  
    28  	// dst is ONAME dereference.
    29  	var dstX ir.Node
    30  	switch dst.Op() {
    31  	default:
    32  		return false
    33  	case ir.ODEREF:
    34  		dst := dst.(*ir.StarExpr)
    35  		dstX = dst.X
    36  	case ir.ODOTPTR:
    37  		dst := dst.(*ir.SelectorExpr)
    38  		dstX = dst.X
    39  	}
    40  	if dstX.Op() != ir.ONAME {
    41  		return false
    42  	}
    43  	// src is a slice operation.
    44  	switch src.Op() {
    45  	case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
    46  		// OK.
    47  	case ir.OSLICEARR, ir.OSLICE3ARR:
    48  		// Since arrays are embedded into containing object,
    49  		// slice of non-pointer array will introduce a new pointer into b that was not already there
    50  		// (pointer to b itself). After such assignment, if b contents escape,
    51  		// b escapes as well. If we ignore such OSLICEARR, we will conclude
    52  		// that b does not escape when b contents do.
    53  		//
    54  		// Pointer to an array is OK since it's not stored inside b directly.
    55  		// For slicing an array (not pointer to array), there is an implicit OADDR.
    56  		// We check that to determine non-pointer array slicing.
    57  		src := src.(*ir.SliceExpr)
    58  		if src.X.Op() == ir.OADDR {
    59  			return false
    60  		}
    61  	default:
    62  		return false
    63  	}
    64  	// slice is applied to ONAME dereference.
    65  	var baseX ir.Node
    66  	switch base := src.(*ir.SliceExpr).X; base.Op() {
    67  	default:
    68  		return false
    69  	case ir.ODEREF:
    70  		base := base.(*ir.StarExpr)
    71  		baseX = base.X
    72  	case ir.ODOTPTR:
    73  		base := base.(*ir.SelectorExpr)
    74  		baseX = base.X
    75  	}
    76  	if baseX.Op() != ir.ONAME {
    77  		return false
    78  	}
    79  	// dst and src reference the same base ONAME.
    80  	return dstX.(*ir.Name) == baseX.(*ir.Name)
    81  }
    82  
    83  // isSelfAssign reports whether assignment from src to dst can
    84  // be ignored by the escape analysis as it's effectively a self-assignment.
    85  func isSelfAssign(dst, src ir.Node) bool {
    86  	if isSliceSelfAssign(dst, src) {
    87  		return true
    88  	}
    89  
    90  	// Detect trivial assignments that assign back to the same object.
    91  	//
    92  	// It covers these cases:
    93  	//	val.x = val.y
    94  	//	val.x[i] = val.y[j]
    95  	//	val.x1.x2 = val.x1.y2
    96  	//	... etc
    97  	//
    98  	// These assignments do not change assigned object lifetime.
    99  
   100  	if dst == nil || src == nil || dst.Op() != src.Op() {
   101  		return false
   102  	}
   103  
   104  	// The expression prefix must be both "safe" and identical.
   105  	switch dst.Op() {
   106  	case ir.ODOT, ir.ODOTPTR:
   107  		// Safe trailing accessors that are permitted to differ.
   108  		dst := dst.(*ir.SelectorExpr)
   109  		src := src.(*ir.SelectorExpr)
   110  		return ir.SameSafeExpr(dst.X, src.X)
   111  	case ir.OINDEX:
   112  		dst := dst.(*ir.IndexExpr)
   113  		src := src.(*ir.IndexExpr)
   114  		if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
   115  			return false
   116  		}
   117  		return ir.SameSafeExpr(dst.X, src.X)
   118  	default:
   119  		return false
   120  	}
   121  }
   122  
   123  // mayAffectMemory reports whether evaluation of n may affect the program's
   124  // memory state. If the expression can't affect memory state, then it can be
   125  // safely ignored by the escape analysis.
   126  func mayAffectMemory(n ir.Node) bool {
   127  	// We may want to use a list of "memory safe" ops instead of generally
   128  	// "side-effect free", which would include all calls and other ops that can
   129  	// allocate or change global state. For now, it's safer to start with the latter.
   130  	//
   131  	// We're ignoring things like division by zero, index out of range,
   132  	// and nil pointer dereference here.
   133  
   134  	// TODO(rsc): It seems like it should be possible to replace this with
   135  	// an ir.Any looking for any op that's not the ones in the case statement.
   136  	// But that produces changes in the compiled output detected by buildall.
   137  	switch n.Op() {
   138  	case ir.ONAME, ir.OLITERAL, ir.ONIL:
   139  		return false
   140  
   141  	case ir.OADD, ir.OSUB, ir.OOR, ir.OXOR, ir.OMUL, ir.OLSH, ir.ORSH, ir.OAND, ir.OANDNOT, ir.ODIV, ir.OMOD:
   142  		n := n.(*ir.BinaryExpr)
   143  		return mayAffectMemory(n.X) || mayAffectMemory(n.Y)
   144  
   145  	case ir.OINDEX:
   146  		n := n.(*ir.IndexExpr)
   147  		return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
   148  
   149  	case ir.OCONVNOP, ir.OCONV:
   150  		n := n.(*ir.ConvExpr)
   151  		return mayAffectMemory(n.X)
   152  
   153  	case ir.OLEN, ir.OCAP, ir.ONOT, ir.OBITNOT, ir.OPLUS, ir.ONEG, ir.OALIGNOF, ir.OOFFSETOF, ir.OSIZEOF:
   154  		n := n.(*ir.UnaryExpr)
   155  		return mayAffectMemory(n.X)
   156  
   157  	case ir.ODOT, ir.ODOTPTR:
   158  		n := n.(*ir.SelectorExpr)
   159  		return mayAffectMemory(n.X)
   160  
   161  	case ir.ODEREF:
   162  		n := n.(*ir.StarExpr)
   163  		return mayAffectMemory(n.X)
   164  
   165  	default:
   166  		return true
   167  	}
   168  }
   169  
   170  // HeapAllocReason returns the reason the given Node must be heap
   171  // allocated, or the empty string if it doesn't.
   172  func HeapAllocReason(n ir.Node) string {
   173  	if n == nil || n.Type() == nil {
   174  		return ""
   175  	}
   176  
   177  	// Parameters are always passed via the stack.
   178  	if n.Op() == ir.ONAME {
   179  		n := n.(*ir.Name)
   180  		if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
   181  			return ""
   182  		}
   183  	}
   184  
   185  	if n.Type().Size() > ir.MaxStackVarSize {
   186  		return "too large for stack"
   187  	}
   188  
   189  	if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Size() > ir.MaxImplicitStackVarSize {
   190  		return "too large for stack"
   191  	}
   192  
   193  	if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() > ir.MaxImplicitStackVarSize {
   194  		return "too large for stack"
   195  	}
   196  	if n.Op() == ir.OMETHVALUE && typecheck.MethodValueType(n.(*ir.SelectorExpr)).Size() > ir.MaxImplicitStackVarSize {
   197  		return "too large for stack"
   198  	}
   199  
   200  	if n.Op() == ir.OMAKESLICE {
   201  		n := n.(*ir.MakeExpr)
   202  		r := n.Cap
   203  		if r == nil {
   204  			r = n.Len
   205  		}
   206  		if !ir.IsSmallIntConst(r) {
   207  			return "non-constant size"
   208  		}
   209  		if t := n.Type(); t.Elem().Size() != 0 && ir.Int64Val(r) > ir.MaxImplicitStackVarSize/t.Elem().Size() {
   210  			return "too large for stack"
   211  		}
   212  	}
   213  
   214  	return ""
   215  }
   216  

View as plain text