1// Copyright 2011 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 gc
6
7import (
8	"cmd/compile/internal/types"
9	"fmt"
10)
11
12func escapes(all []*Node) {
13	visitBottomUp(all, escapeFuncs)
14}
15
16const (
17	EscFuncUnknown = 0 + iota
18	EscFuncPlanned
19	EscFuncStarted
20	EscFuncTagged
21)
22
23func min8(a, b int8) int8 {
24	if a < b {
25		return a
26	}
27	return b
28}
29
30func max8(a, b int8) int8 {
31	if a > b {
32		return a
33	}
34	return b
35}
36
37const (
38	EscUnknown = iota
39	EscNone    // Does not escape to heap, result, or parameters.
40	EscHeap    // Reachable from the heap
41	EscNever   // By construction will not escape.
42)
43
44// funcSym returns fn.Func.Nname.Sym if no nils are encountered along the way.
45func funcSym(fn *Node) *types.Sym {
46	if fn == nil || fn.Func.Nname == nil {
47		return nil
48	}
49	return fn.Func.Nname.Sym
50}
51
52// Mark labels that have no backjumps to them as not increasing e.loopdepth.
53// Walk hasn't generated (goto|label).Left.Sym.Label yet, so we'll cheat
54// and set it to one of the following two. Then in esc we'll clear it again.
55var (
56	looping    Node
57	nonlooping Node
58)
59
60func isSliceSelfAssign(dst, src *Node) bool {
61	// Detect the following special case.
62	//
63	//	func (b *Buffer) Foo() {
64	//		n, m := ...
65	//		b.buf = b.buf[n:m]
66	//	}
67	//
68	// This assignment is a no-op for escape analysis,
69	// it does not store any new pointers into b that were not already there.
70	// However, without this special case b will escape, because we assign to OIND/ODOTPTR.
71	// Here we assume that the statement will not contain calls,
72	// that is, that order will move any calls to init.
73	// Otherwise base ONAME value could change between the moments
74	// when we evaluate it for dst and for src.
75
76	// dst is ONAME dereference.
77	if dst.Op != ODEREF && dst.Op != ODOTPTR || dst.Left.Op != ONAME {
78		return false
79	}
80	// src is a slice operation.
81	switch src.Op {
82	case OSLICE, OSLICE3, OSLICESTR:
83		// OK.
84	case OSLICEARR, OSLICE3ARR:
85		// Since arrays are embedded into containing object,
86		// slice of non-pointer array will introduce a new pointer into b that was not already there
87		// (pointer to b itself). After such assignment, if b contents escape,
88		// b escapes as well. If we ignore such OSLICEARR, we will conclude
89		// that b does not escape when b contents do.
90		//
91		// Pointer to an array is OK since it's not stored inside b directly.
92		// For slicing an array (not pointer to array), there is an implicit OADDR.
93		// We check that to determine non-pointer array slicing.
94		if src.Left.Op == OADDR {
95			return false
96		}
97	default:
98		return false
99	}
100	// slice is applied to ONAME dereference.
101	if src.Left.Op != ODEREF && src.Left.Op != ODOTPTR || src.Left.Left.Op != ONAME {
102		return false
103	}
104	// dst and src reference the same base ONAME.
105	return dst.Left == src.Left.Left
106}
107
108// isSelfAssign reports whether assignment from src to dst can
109// be ignored by the escape analysis as it's effectively a self-assignment.
110func isSelfAssign(dst, src *Node) bool {
111	if isSliceSelfAssign(dst, src) {
112		return true
113	}
114
115	// Detect trivial assignments that assign back to the same object.
116	//
117	// It covers these cases:
118	//	val.x = val.y
119	//	val.x[i] = val.y[j]
120	//	val.x1.x2 = val.x1.y2
121	//	... etc
122	//
123	// These assignments do not change assigned object lifetime.
124
125	if dst == nil || src == nil || dst.Op != src.Op {
126		return false
127	}
128
129	switch dst.Op {
130	case ODOT, ODOTPTR:
131		// Safe trailing accessors that are permitted to differ.
132	case OINDEX:
133		if mayAffectMemory(dst.Right) || mayAffectMemory(src.Right) {
134			return false
135		}
136	default:
137		return false
138	}
139
140	// The expression prefix must be both "safe" and identical.
141	return samesafeexpr(dst.Left, src.Left)
142}
143
144// mayAffectMemory reports whether n evaluation may affect program memory state.
145// If expression can't affect it, then it can be safely ignored by the escape analysis.
146func mayAffectMemory(n *Node) bool {
147	// We may want to use "memory safe" black list instead of general
148	// "side-effect free", which can include all calls and other ops
149	// that can affect allocate or change global state.
150	// It's safer to start from a whitelist for now.
151	//
152	// We're ignoring things like division by zero, index out of range,
153	// and nil pointer dereference here.
154	switch n.Op {
155	case ONAME, OCLOSUREVAR, OLITERAL:
156		return false
157
158	// Left+Right group.
159	case OINDEX, OADD, OSUB, OOR, OXOR, OMUL, OLSH, ORSH, OAND, OANDNOT, ODIV, OMOD:
160		return mayAffectMemory(n.Left) || mayAffectMemory(n.Right)
161
162	// Left group.
163	case ODOT, ODOTPTR, ODEREF, OCONVNOP, OCONV, OLEN, OCAP,
164		ONOT, OBITNOT, OPLUS, ONEG, OALIGNOF, OOFFSETOF, OSIZEOF:
165		return mayAffectMemory(n.Left)
166
167	default:
168		return true
169	}
170}
171
172func mustHeapAlloc(n *Node) bool {
173	if n.Type == nil {
174		return false
175	}
176
177	// Parameters are always passed via the stack.
178	if n.Op == ONAME && (n.Class() == PPARAM || n.Class() == PPARAMOUT) {
179		return false
180	}
181
182	if n.Type.Width > maxStackVarSize {
183		return true
184	}
185
186	if (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= maxImplicitStackVarSize {
187		return true
188	}
189
190	if n.Op == OMAKESLICE && !isSmallMakeSlice(n) {
191		return true
192	}
193
194	return false
195}
196
197// addrescapes tags node n as having had its address taken
198// by "increasing" the "value" of n.Esc to EscHeap.
199// Storage is allocated as necessary to allow the address
200// to be taken.
201func addrescapes(n *Node) {
202	switch n.Op {
203	default:
204		// Unexpected Op, probably due to a previous type error. Ignore.
205
206	case ODEREF, ODOTPTR:
207		// Nothing to do.
208
209	case ONAME:
210		if n == nodfp {
211			break
212		}
213
214		// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
215		// on PPARAM it means something different.
216		if n.Class() == PAUTO && n.Esc == EscNever {
217			break
218		}
219
220		// If a closure reference escapes, mark the outer variable as escaping.
221		if n.Name.IsClosureVar() {
222			addrescapes(n.Name.Defn)
223			break
224		}
225
226		if n.Class() != PPARAM && n.Class() != PPARAMOUT && n.Class() != PAUTO {
227			break
228		}
229
230		// This is a plain parameter or local variable that needs to move to the heap,
231		// but possibly for the function outside the one we're compiling.
232		// That is, if we have:
233		//
234		//	func f(x int) {
235		//		func() {
236		//			global = &x
237		//		}
238		//	}
239		//
240		// then we're analyzing the inner closure but we need to move x to the
241		// heap in f, not in the inner closure. Flip over to f before calling moveToHeap.
242		oldfn := Curfn
243		Curfn = n.Name.Curfn
244		if Curfn.Func.Closure != nil && Curfn.Op == OCLOSURE {
245			Curfn = Curfn.Func.Closure
246		}
247		ln := lineno
248		lineno = Curfn.Pos
249		moveToHeap(n)
250		Curfn = oldfn
251		lineno = ln
252
253	// ODOTPTR has already been introduced,
254	// so these are the non-pointer ODOT and OINDEX.
255	// In &x[0], if x is a slice, then x does not
256	// escape--the pointer inside x does, but that
257	// is always a heap pointer anyway.
258	case ODOT, OINDEX, OPAREN, OCONVNOP:
259		if !n.Left.Type.IsSlice() {
260			addrescapes(n.Left)
261		}
262	}
263}
264
265// moveToHeap records the parameter or local variable n as moved to the heap.
266func moveToHeap(n *Node) {
267	if Debug['r'] != 0 {
268		Dump("MOVE", n)
269	}
270	if compiling_runtime {
271		yyerror("%v escapes to heap, not allowed in runtime", n)
272	}
273	if n.Class() == PAUTOHEAP {
274		Dump("n", n)
275		Fatalf("double move to heap")
276	}
277
278	// Allocate a local stack variable to hold the pointer to the heap copy.
279	// temp will add it to the function declaration list automatically.
280	heapaddr := temp(types.NewPtr(n.Type))
281	heapaddr.Sym = lookup("&" + n.Sym.Name)
282	heapaddr.Orig.Sym = heapaddr.Sym
283	heapaddr.Pos = n.Pos
284
285	// Unset AutoTemp to persist the &foo variable name through SSA to
286	// liveness analysis.
287	// TODO(mdempsky/drchase): Cleaner solution?
288	heapaddr.Name.SetAutoTemp(false)
289
290	// Parameters have a local stack copy used at function start/end
291	// in addition to the copy in the heap that may live longer than
292	// the function.
293	if n.Class() == PPARAM || n.Class() == PPARAMOUT {
294		if n.Xoffset == BADWIDTH {
295			Fatalf("addrescapes before param assignment")
296		}
297
298		// We rewrite n below to be a heap variable (indirection of heapaddr).
299		// Preserve a copy so we can still write code referring to the original,
300		// and substitute that copy into the function declaration list
301		// so that analyses of the local (on-stack) variables use it.
302		stackcopy := newname(n.Sym)
303		stackcopy.Type = n.Type
304		stackcopy.Xoffset = n.Xoffset
305		stackcopy.SetClass(n.Class())
306		stackcopy.Name.Param.Heapaddr = heapaddr
307		if n.Class() == PPARAMOUT {
308			// Make sure the pointer to the heap copy is kept live throughout the function.
309			// The function could panic at any point, and then a defer could recover.
310			// Thus, we need the pointer to the heap copy always available so the
311			// post-deferreturn code can copy the return value back to the stack.
312			// See issue 16095.
313			heapaddr.Name.SetIsOutputParamHeapAddr(true)
314		}
315		n.Name.Param.Stackcopy = stackcopy
316
317		// Substitute the stackcopy into the function variable list so that
318		// liveness and other analyses use the underlying stack slot
319		// and not the now-pseudo-variable n.
320		found := false
321		for i, d := range Curfn.Func.Dcl {
322			if d == n {
323				Curfn.Func.Dcl[i] = stackcopy
324				found = true
325				break
326			}
327			// Parameters are before locals, so can stop early.
328			// This limits the search even in functions with many local variables.
329			if d.Class() == PAUTO {
330				break
331			}
332		}
333		if !found {
334			Fatalf("cannot find %v in local variable list", n)
335		}
336		Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
337	}
338
339	// Modify n in place so that uses of n now mean indirection of the heapaddr.
340	n.SetClass(PAUTOHEAP)
341	n.Xoffset = 0
342	n.Name.Param.Heapaddr = heapaddr
343	n.Esc = EscHeap
344	if Debug['m'] != 0 {
345		Warnl(n.Pos, "moved to heap: %v", n)
346	}
347}
348
349// This special tag is applied to uintptr variables
350// that we believe may hold unsafe.Pointers for
351// calls into assembly functions.
352const unsafeUintptrTag = "unsafe-uintptr"
353
354// This special tag is applied to uintptr parameters of functions
355// marked go:uintptrescapes.
356const uintptrEscapesTag = "uintptr-escapes"
357
358func (e *Escape) paramTag(fn *Node, narg int, f *types.Field) string {
359	name := func() string {
360		if f.Sym != nil {
361			return f.Sym.Name
362		}
363		return fmt.Sprintf("arg#%d", narg)
364	}
365
366	if fn.Nbody.Len() == 0 {
367		// Assume that uintptr arguments must be held live across the call.
368		// This is most important for syscall.Syscall.
369		// See golang.org/issue/13372.
370		// This really doesn't have much to do with escape analysis per se,
371		// but we are reusing the ability to annotate an individual function
372		// argument and pass those annotations along to importing code.
373		if f.Type.Etype == TUINTPTR {
374			if Debug['m'] != 0 {
375				Warnl(f.Pos, "assuming %v is unsafe uintptr", name())
376			}
377			return unsafeUintptrTag
378		}
379
380		if !types.Haspointers(f.Type) { // don't bother tagging for scalars
381			return ""
382		}
383
384		var esc EscLeaks
385
386		// External functions are assumed unsafe, unless
387		// //go:noescape is given before the declaration.
388		if fn.Func.Pragma&Noescape != 0 {
389			if Debug['m'] != 0 && f.Sym != nil {
390				Warnl(f.Pos, "%v does not escape", name())
391			}
392		} else {
393			if Debug['m'] != 0 && f.Sym != nil {
394				Warnl(f.Pos, "leaking param: %v", name())
395			}
396			esc.AddHeap(0)
397		}
398
399		return esc.Encode()
400	}
401
402	if fn.Func.Pragma&UintptrEscapes != 0 {
403		if f.Type.Etype == TUINTPTR {
404			if Debug['m'] != 0 {
405				Warnl(f.Pos, "marking %v as escaping uintptr", name())
406			}
407			return uintptrEscapesTag
408		}
409		if f.IsDDD() && f.Type.Elem().Etype == TUINTPTR {
410			// final argument is ...uintptr.
411			if Debug['m'] != 0 {
412				Warnl(f.Pos, "marking %v as escaping ...uintptr", name())
413			}
414			return uintptrEscapesTag
415		}
416	}
417
418	if !types.Haspointers(f.Type) { // don't bother tagging for scalars
419		return ""
420	}
421
422	// Unnamed parameters are unused and therefore do not escape.
423	if f.Sym == nil || f.Sym.IsBlank() {
424		var esc EscLeaks
425		return esc.Encode()
426	}
427
428	n := asNode(f.Nname)
429	loc := e.oldLoc(n)
430	esc := loc.paramEsc
431	esc.Optimize()
432
433	if Debug['m'] != 0 && !loc.escapes {
434		if esc.Empty() {
435			Warnl(f.Pos, "%v does not escape", name())
436		}
437		if x := esc.Heap(); x >= 0 {
438			if x == 0 {
439				Warnl(f.Pos, "leaking param: %v", name())
440			} else {
441				// TODO(mdempsky): Mention level=x like below?
442				Warnl(f.Pos, "leaking param content: %v", name())
443			}
444		}
445		for i := 0; i < numEscResults; i++ {
446			if x := esc.Result(i); x >= 0 {
447				res := fn.Type.Results().Field(i).Sym
448				Warnl(f.Pos, "leaking param: %v to result %v level=%d", name(), res, x)
449			}
450		}
451	}
452
453	return esc.Encode()
454}
455