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