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.
8 "cmd/compile/internal/ir"
9 "cmd/compile/internal/typecheck"
12 func isSliceSelfAssign(dst, src ir.Node) bool {
13 // Detect the following special case.
15 // func (b *Buffer) Foo() {
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.
28 // dst is ONAME dereference.
34 dst := dst.(*ir.StarExpr)
37 dst := dst.(*ir.SelectorExpr)
40 if dstX.Op() != ir.ONAME {
43 // src is a slice operation.
45 case ir.OSLICE, ir.OSLICE3, ir.OSLICESTR:
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.
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 {
64 // slice is applied to ONAME dereference.
66 switch base := src.(*ir.SliceExpr).X; base.Op() {
70 base := base.(*ir.StarExpr)
73 base := base.(*ir.SelectorExpr)
76 if baseX.Op() != ir.ONAME {
79 // dst and src reference the same base ONAME.
80 return dstX.(*ir.Name) == baseX.(*ir.Name)
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) {
90 // Detect trivial assignments that assign back to the same object.
92 // It covers these cases:
94 // val.x[i] = val.y[j]
95 // val.x1.x2 = val.x1.y2
98 // These assignments do not change assigned object lifetime.
100 if dst == nil || src == nil || dst.Op() != src.Op() {
104 // The expression prefix must be both "safe" and identical.
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)
112 dst := dst.(*ir.IndexExpr)
113 src := src.(*ir.IndexExpr)
114 if mayAffectMemory(dst.Index) || mayAffectMemory(src.Index) {
117 return ir.SameSafeExpr(dst.X, src.X)
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.
131 // We're ignoring things like division by zero, index out of range,
132 // and nil pointer dereference here.
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.
138 case ir.ONAME, ir.OLITERAL, ir.ONIL:
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)
146 n := n.(*ir.IndexExpr)
147 return mayAffectMemory(n.X) || mayAffectMemory(n.Index)
149 case ir.OCONVNOP, ir.OCONV:
150 n := n.(*ir.ConvExpr)
151 return mayAffectMemory(n.X)
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)
157 case ir.ODOT, ir.ODOTPTR:
158 n := n.(*ir.SelectorExpr)
159 return mayAffectMemory(n.X)
162 n := n.(*ir.StarExpr)
163 return mayAffectMemory(n.X)
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 {
177 // Parameters are always passed via the stack.
178 if n.Op() == ir.ONAME {
180 if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
185 if n.Type().Width > ir.MaxStackVarSize {
186 return "too large for stack"
189 if (n.Op() == ir.ONEW || n.Op() == ir.OPTRLIT) && n.Type().Elem().Width >= ir.MaxImplicitStackVarSize {
190 return "too large for stack"
193 if n.Op() == ir.OCLOSURE && typecheck.ClosureType(n.(*ir.ClosureExpr)).Size() >= ir.MaxImplicitStackVarSize {
194 return "too large for stack"
196 if n.Op() == ir.OMETHVALUE && typecheck.PartialCallType(n.(*ir.SelectorExpr)).Size() >= ir.MaxImplicitStackVarSize {
197 return "too large for stack"
200 if n.Op() == ir.OMAKESLICE {
201 n := n.(*ir.MakeExpr)
206 if !ir.IsSmallIntConst(r) {
207 return "non-constant size"
209 if t := n.Type(); t.Elem().Width != 0 && ir.Int64Val(r) >= ir.MaxImplicitStackVarSize/t.Elem().Width {
210 return "too large for stack"