1// Copyright 2009 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 walk
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// directClosureCall rewrites a direct call of a function literal into
16// a normal function call with closure variables passed as arguments.
17// This avoids allocation of a closure object.
18//
19// For illustration, the following call:
20//
21//	func(a int) {
22//		println(byval)
23//		byref++
24//	}(42)
25//
26// becomes:
27//
28//	func(byval int, &byref *int, a int) {
29//		println(byval)
30//		(*&byref)++
31//	}(byval, &byref, 42)
32func directClosureCall(n *ir.CallExpr) {
33	clo := n.Fun.(*ir.ClosureExpr)
34	clofn := clo.Func
35
36	if ir.IsTrivialClosure(clo) {
37		return // leave for walkClosure to handle
38	}
39
40	// We are going to insert captured variables before input args.
41	var params []*types.Field
42	var decls []*ir.Name
43	for _, v := range clofn.ClosureVars {
44		if !v.Byval() {
45			// If v of type T is captured by reference,
46			// we introduce function param &v *T
47			// and v remains PAUTOHEAP with &v heapaddr
48			// (accesses will implicitly deref &v).
49
50			addr := ir.NewNameAt(clofn.Pos(), typecheck.Lookup("&"+v.Sym().Name), types.NewPtr(v.Type()))
51			addr.Curfn = clofn
52			v.Heapaddr = addr
53			v = addr
54		}
55
56		v.Class = ir.PPARAM
57		decls = append(decls, v)
58
59		fld := types.NewField(src.NoXPos, v.Sym(), v.Type())
60		fld.Nname = v
61		params = append(params, fld)
62	}
63
64	// f is ONAME of the actual function.
65	f := clofn.Nname
66	typ := f.Type()
67
68	// Create new function type with parameters prepended, and
69	// then update type and declarations.
70	typ = types.NewSignature(nil, append(params, typ.Params()...), typ.Results())
71	f.SetType(typ)
72	clofn.Dcl = append(decls, clofn.Dcl...)
73
74	// Rewrite call.
75	n.Fun = f
76	n.Args.Prepend(closureArgs(clo)...)
77
78	// Update the call expression's type. We need to do this
79	// because typecheck gave it the result type of the OCLOSURE
80	// node, but we only rewrote the ONAME node's type. Logically,
81	// they're the same, but the stack offsets probably changed.
82	if typ.NumResults() == 1 {
83		n.SetType(typ.Result(0).Type)
84	} else {
85		n.SetType(typ.ResultsTuple())
86	}
87
88	// Add to Closures for enqueueFunc. It's no longer a proper
89	// closure, but we may have already skipped over it in the
90	// functions list as a non-trivial closure, so this just
91	// ensures it's compiled.
92	ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
93}
94
95func walkClosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
96	clofn := clo.Func
97
98	// If no closure vars, don't bother wrapping.
99	if ir.IsTrivialClosure(clo) {
100		if base.Debug.Closure > 0 {
101			base.WarnfAt(clo.Pos(), "closure converted to global")
102		}
103		return clofn.Nname
104	}
105
106	// The closure is not trivial or directly called, so it's going to stay a closure.
107	ir.ClosureDebugRuntimeCheck(clo)
108	clofn.SetNeedctxt(true)
109
110	// The closure expression may be walked more than once if it appeared in composite
111	// literal initialization (e.g, see issue #49029).
112	//
113	// Don't add the closure function to compilation queue more than once, since when
114	// compiling a function twice would lead to an ICE.
115	if !clofn.Walked() {
116		clofn.SetWalked(true)
117		ir.CurFunc.Closures = append(ir.CurFunc.Closures, clofn)
118	}
119
120	typ := typecheck.ClosureType(clo)
121
122	clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, typ, nil)
123	clos.SetEsc(clo.Esc())
124	clos.List = append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, clofn.Nname)}, closureArgs(clo)...)
125	for i, value := range clos.List {
126		clos.List[i] = ir.NewStructKeyExpr(base.Pos, typ.Field(i), value)
127	}
128
129	addr := typecheck.NodAddr(clos)
130	addr.SetEsc(clo.Esc())
131
132	// Force type conversion from *struct to the func type.
133	cfn := typecheck.ConvNop(addr, clo.Type())
134
135	// non-escaping temp to use, if any.
136	if x := clo.Prealloc; x != nil {
137		if !types.Identical(typ, x.Type()) {
138			panic("closure type does not match order's assigned type")
139		}
140		addr.Prealloc = x
141		clo.Prealloc = nil
142	}
143
144	return walkExpr(cfn, init)
145}
146
147// closureArgs returns a slice of expressions that can be used to
148// initialize the given closure's free variables. These correspond
149// one-to-one with the variables in clo.Func.ClosureVars, and will be
150// either an ONAME node (if the variable is captured by value) or an
151// OADDR-of-ONAME node (if not).
152func closureArgs(clo *ir.ClosureExpr) []ir.Node {
153	fn := clo.Func
154
155	args := make([]ir.Node, len(fn.ClosureVars))
156	for i, v := range fn.ClosureVars {
157		var outer ir.Node
158		outer = v.Outer
159		if !v.Byval() {
160			outer = typecheck.NodAddrAt(fn.Pos(), outer)
161		}
162		args[i] = typecheck.Expr(outer)
163	}
164	return args
165}
166
167func walkMethodValue(n *ir.SelectorExpr, init *ir.Nodes) ir.Node {
168	// Create closure in the form of a composite literal.
169	// For x.M with receiver (x) type T, the generated code looks like:
170	//
171	//	clos = &struct{F uintptr; R T}{T.M·f, x}
172	//
173	// Like walkClosure above.
174
175	if n.X.Type().IsInterface() {
176		// Trigger panic for method on nil interface now.
177		// Otherwise it happens in the wrapper and is confusing.
178		n.X = cheapExpr(n.X, init)
179		n.X = walkExpr(n.X, nil)
180
181		tab := ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X)
182		check := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab)
183		init.Append(typecheck.Stmt(check))
184	}
185
186	typ := typecheck.MethodValueType(n)
187
188	clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, typ, nil)
189	clos.SetEsc(n.Esc())
190	clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, methodValueWrapper(n)), n.X}
191
192	addr := typecheck.NodAddr(clos)
193	addr.SetEsc(n.Esc())
194
195	// Force type conversion from *struct to the func type.
196	cfn := typecheck.ConvNop(addr, n.Type())
197
198	// non-escaping temp to use, if any.
199	if x := n.Prealloc; x != nil {
200		if !types.Identical(typ, x.Type()) {
201			panic("partial call type does not match order's assigned type")
202		}
203		addr.Prealloc = x
204		n.Prealloc = nil
205	}
206
207	return walkExpr(cfn, init)
208}
209
210// methodValueWrapper returns the ONAME node representing the
211// wrapper function (*-fm) needed for the given method value. If the
212// wrapper function hasn't already been created yet, it's created and
213// added to typecheck.Target.Decls.
214func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
215	if dot.Op() != ir.OMETHVALUE {
216		base.Fatalf("methodValueWrapper: unexpected %v (%v)", dot, dot.Op())
217	}
218
219	meth := dot.Sel
220	rcvrtype := dot.X.Type()
221	sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
222
223	if sym.Uniq() {
224		return sym.Def.(*ir.Name)
225	}
226	sym.SetUniq(true)
227
228	base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth)
229	panic("unreachable")
230}
231