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/base"
9	"cmd/compile/internal/ir"
10	"cmd/compile/internal/typecheck"
11	"cmd/compile/internal/types"
12	"cmd/internal/src"
13)
14
15// call evaluates a call expressions, including builtin calls. ks
16// should contain the holes representing where the function callee's
17// results flows.
18func (e *escape) call(ks []hole, call ir.Node) {
19	argument := func(k hole, arg ir.Node) {
20		// TODO(mdempsky): Should be "call argument".
21		e.expr(k.note(call, "call parameter"), arg)
22	}
23
24	switch call.Op() {
25	default:
26		ir.Dump("esc", call)
27		base.Fatalf("unexpected call op: %v", call.Op())
28
29	case ir.OCALLFUNC, ir.OCALLINTER:
30		call := call.(*ir.CallExpr)
31		typecheck.AssertFixedCall(call)
32
33		// Pick out the function callee, if statically known.
34		//
35		// TODO(mdempsky): Change fn from *ir.Name to *ir.Func, but some
36		// functions (e.g., runtime builtins, method wrappers, generated
37		// eq/hash functions) don't have it set. Investigate whether
38		// that's a concern.
39		var fn *ir.Name
40		switch call.Op() {
41		case ir.OCALLFUNC:
42			v := ir.StaticValue(call.Fun)
43			fn = ir.StaticCalleeName(v)
44		}
45
46		fntype := call.Fun.Type()
47		if fn != nil {
48			fntype = fn.Type()
49		}
50
51		if ks != nil && fn != nil && e.inMutualBatch(fn) {
52			for i, result := range fn.Type().Results() {
53				e.expr(ks[i], result.Nname.(*ir.Name))
54			}
55		}
56
57		var recvArg ir.Node
58		if call.Op() == ir.OCALLFUNC {
59			// Evaluate callee function expression.
60			calleeK := e.discardHole()
61			if fn == nil { // unknown callee
62				for _, k := range ks {
63					if k.dst != &e.blankLoc {
64						// The results flow somewhere, but we don't statically
65						// know the callee function. If a closure flows here, we
66						// need to conservatively assume its results might flow to
67						// the heap.
68						calleeK = e.calleeHole().note(call, "callee operand")
69						break
70					}
71				}
72			}
73			e.expr(calleeK, call.Fun)
74		} else {
75			recvArg = call.Fun.(*ir.SelectorExpr).X
76		}
77
78		// argumentParam handles escape analysis of assigning a call
79		// argument to its corresponding parameter.
80		argumentParam := func(param *types.Field, arg ir.Node) {
81			e.rewriteArgument(arg, call, fn)
82			argument(e.tagHole(ks, fn, param), arg)
83		}
84
85		args := call.Args
86		if recvParam := fntype.Recv(); recvParam != nil {
87			if recvArg == nil {
88				// Function call using method expression. Receiver argument is
89				// at the front of the regular arguments list.
90				recvArg, args = args[0], args[1:]
91			}
92
93			argumentParam(recvParam, recvArg)
94		}
95
96		for i, param := range fntype.Params() {
97			argumentParam(param, args[i])
98		}
99
100	case ir.OINLCALL:
101		call := call.(*ir.InlinedCallExpr)
102		e.stmts(call.Body)
103		for i, result := range call.ReturnVars {
104			k := e.discardHole()
105			if ks != nil {
106				k = ks[i]
107			}
108			e.expr(k, result)
109		}
110
111	case ir.OAPPEND:
112		call := call.(*ir.CallExpr)
113		args := call.Args
114
115		// Appendee slice may flow directly to the result, if
116		// it has enough capacity. Alternatively, a new heap
117		// slice might be allocated, and all slice elements
118		// might flow to heap.
119		appendeeK := e.teeHole(ks[0], e.mutatorHole())
120		if args[0].Type().Elem().HasPointers() {
121			appendeeK = e.teeHole(appendeeK, e.heapHole().deref(call, "appendee slice"))
122		}
123		argument(appendeeK, args[0])
124
125		if call.IsDDD {
126			appendedK := e.discardHole()
127			if args[1].Type().IsSlice() && args[1].Type().Elem().HasPointers() {
128				appendedK = e.heapHole().deref(call, "appended slice...")
129			}
130			argument(appendedK, args[1])
131		} else {
132			for i := 1; i < len(args); i++ {
133				argument(e.heapHole(), args[i])
134			}
135		}
136		e.discard(call.RType)
137
138	case ir.OCOPY:
139		call := call.(*ir.BinaryExpr)
140		argument(e.mutatorHole(), call.X)
141
142		copiedK := e.discardHole()
143		if call.Y.Type().IsSlice() && call.Y.Type().Elem().HasPointers() {
144			copiedK = e.heapHole().deref(call, "copied slice")
145		}
146		argument(copiedK, call.Y)
147		e.discard(call.RType)
148
149	case ir.OPANIC:
150		call := call.(*ir.UnaryExpr)
151		argument(e.heapHole(), call.X)
152
153	case ir.OCOMPLEX:
154		call := call.(*ir.BinaryExpr)
155		e.discard(call.X)
156		e.discard(call.Y)
157
158	case ir.ODELETE, ir.OPRINT, ir.OPRINTLN, ir.ORECOVERFP:
159		call := call.(*ir.CallExpr)
160		for _, arg := range call.Args {
161			e.discard(arg)
162		}
163		e.discard(call.RType)
164
165	case ir.OMIN, ir.OMAX:
166		call := call.(*ir.CallExpr)
167		for _, arg := range call.Args {
168			argument(ks[0], arg)
169		}
170		e.discard(call.RType)
171
172	case ir.OLEN, ir.OCAP, ir.OREAL, ir.OIMAG, ir.OCLOSE:
173		call := call.(*ir.UnaryExpr)
174		e.discard(call.X)
175
176	case ir.OCLEAR:
177		call := call.(*ir.UnaryExpr)
178		argument(e.mutatorHole(), call.X)
179
180	case ir.OUNSAFESTRINGDATA, ir.OUNSAFESLICEDATA:
181		call := call.(*ir.UnaryExpr)
182		argument(ks[0], call.X)
183
184	case ir.OUNSAFEADD, ir.OUNSAFESLICE, ir.OUNSAFESTRING:
185		call := call.(*ir.BinaryExpr)
186		argument(ks[0], call.X)
187		e.discard(call.Y)
188		e.discard(call.RType)
189	}
190}
191
192// goDeferStmt analyzes a "go" or "defer" statement.
193func (e *escape) goDeferStmt(n *ir.GoDeferStmt) {
194	k := e.heapHole()
195	if n.Op() == ir.ODEFER && e.loopDepth == 1 && n.DeferAt == nil {
196		// Top-level defer arguments don't escape to the heap,
197		// but they do need to last until they're invoked.
198		k = e.later(e.discardHole())
199
200		// force stack allocation of defer record, unless
201		// open-coded defers are used (see ssa.go)
202		n.SetEsc(ir.EscNever)
203	}
204
205	// If the function is already a zero argument/result function call,
206	// just escape analyze it normally.
207	//
208	// Note that the runtime is aware of this optimization for
209	// "go" statements that start in reflect.makeFuncStub or
210	// reflect.methodValueCall.
211
212	call, ok := n.Call.(*ir.CallExpr)
213	if !ok || call.Op() != ir.OCALLFUNC {
214		base.FatalfAt(n.Pos(), "expected function call: %v", n.Call)
215	}
216	if sig := call.Fun.Type(); sig.NumParams()+sig.NumResults() != 0 {
217		base.FatalfAt(n.Pos(), "expected signature without parameters or results: %v", sig)
218	}
219
220	if clo, ok := call.Fun.(*ir.ClosureExpr); ok && n.Op() == ir.OGO {
221		clo.IsGoWrap = true
222	}
223
224	e.expr(k, call.Fun)
225}
226
227// rewriteArgument rewrites the argument arg of the given call expression.
228// fn is the static callee function, if known.
229func (e *escape) rewriteArgument(arg ir.Node, call *ir.CallExpr, fn *ir.Name) {
230	if fn == nil || fn.Func == nil {
231		return
232	}
233	pragma := fn.Func.Pragma
234	if pragma&(ir.UintptrKeepAlive|ir.UintptrEscapes) == 0 {
235		return
236	}
237
238	// unsafeUintptr rewrites "uintptr(ptr)" arguments to syscall-like
239	// functions, so that ptr is kept alive and/or escaped as
240	// appropriate. unsafeUintptr also reports whether it modified arg0.
241	unsafeUintptr := func(arg ir.Node) {
242		// If the argument is really a pointer being converted to uintptr,
243		// arrange for the pointer to be kept alive until the call
244		// returns, by copying it into a temp and marking that temp still
245		// alive when we pop the temp stack.
246		conv, ok := arg.(*ir.ConvExpr)
247		if !ok || conv.Op() != ir.OCONVNOP {
248			return // not a conversion
249		}
250		if !conv.X.Type().IsUnsafePtr() || !conv.Type().IsUintptr() {
251			return // not an unsafe.Pointer->uintptr conversion
252		}
253
254		// Create and declare a new pointer-typed temp variable.
255		//
256		// TODO(mdempsky): This potentially violates the Go spec's order
257		// of evaluations, by evaluating arg.X before any other
258		// operands.
259		tmp := e.copyExpr(conv.Pos(), conv.X, call.PtrInit())
260		conv.X = tmp
261
262		k := e.mutatorHole()
263		if pragma&ir.UintptrEscapes != 0 {
264			k = e.heapHole().note(conv, "//go:uintptrescapes")
265		}
266		e.flow(k, e.oldLoc(tmp))
267
268		if pragma&ir.UintptrKeepAlive != 0 {
269			tmp.SetAddrtaken(true) // ensure SSA keeps the tmp variable
270			call.KeepAlive = append(call.KeepAlive, tmp)
271		}
272	}
273
274	// For variadic functions, the compiler has already rewritten:
275	//
276	//     f(a, b, c)
277	//
278	// to:
279	//
280	//     f([]T{a, b, c}...)
281	//
282	// So we need to look into slice elements to handle uintptr(ptr)
283	// arguments to variadic syscall-like functions correctly.
284	if arg.Op() == ir.OSLICELIT {
285		list := arg.(*ir.CompLitExpr).List
286		for _, el := range list {
287			if el.Op() == ir.OKEY {
288				el = el.(*ir.KeyExpr).Value
289			}
290			unsafeUintptr(el)
291		}
292	} else {
293		unsafeUintptr(arg)
294	}
295}
296
297// copyExpr creates and returns a new temporary variable within fn;
298// appends statements to init to declare and initialize it to expr;
299// and escape analyzes the data flow.
300func (e *escape) copyExpr(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
301	if ir.HasUniquePos(expr) {
302		pos = expr.Pos()
303	}
304
305	tmp := typecheck.TempAt(pos, e.curfn, expr.Type())
306
307	stmts := []ir.Node{
308		ir.NewDecl(pos, ir.ODCL, tmp),
309		ir.NewAssignStmt(pos, tmp, expr),
310	}
311	typecheck.Stmts(stmts)
312	init.Append(stmts...)
313
314	e.newLoc(tmp, true)
315	e.stmts(stmts)
316
317	return tmp
318}
319
320// tagHole returns a hole for evaluating an argument passed to param.
321// ks should contain the holes representing where the function
322// callee's results flows. fn is the statically-known callee function,
323// if any.
324func (e *escape) tagHole(ks []hole, fn *ir.Name, param *types.Field) hole {
325	// If this is a dynamic call, we can't rely on param.Note.
326	if fn == nil {
327		return e.heapHole()
328	}
329
330	if e.inMutualBatch(fn) {
331		if param.Nname == nil {
332			return e.discardHole()
333		}
334		return e.addr(param.Nname.(*ir.Name))
335	}
336
337	// Call to previously tagged function.
338
339	var tagKs []hole
340	esc := parseLeaks(param.Note)
341
342	if x := esc.Heap(); x >= 0 {
343		tagKs = append(tagKs, e.heapHole().shift(x))
344	}
345	if x := esc.Mutator(); x >= 0 {
346		tagKs = append(tagKs, e.mutatorHole().shift(x))
347	}
348	if x := esc.Callee(); x >= 0 {
349		tagKs = append(tagKs, e.calleeHole().shift(x))
350	}
351
352	if ks != nil {
353		for i := 0; i < numEscResults; i++ {
354			if x := esc.Result(i); x >= 0 {
355				tagKs = append(tagKs, ks[i].shift(x))
356			}
357		}
358	}
359
360	return e.teeHole(tagKs...)
361}
362