blob: 0e8ef196af758065781d342008c956d0989dc728 [file] [log] [blame]
Russ Cox8c195bd2015-02-13 14:40:36 -05001// 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//
5// The inlining facility makes 2 passes: first caninl determines which
6// functions are suitable for inlining, and for those that are it
7// saves a copy of the body. Then inlcalls walks each function body to
8// expand calls to inlinable functions.
9//
Shawn Smith58ec5832016-02-06 20:35:29 +090010// The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1,
Matthew Dempsky7092a312017-10-20 15:20:56 -070011// making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and
12// are not supported.
Russ Cox8c195bd2015-02-13 14:40:36 -050013// 0: disabled
Damien Lespiau619e37b2017-04-16 00:10:19 +010014// 1: 80-nodes leaf functions, oneliners, lazy typechecking (default)
Matthew Dempsky7092a312017-10-20 15:20:56 -070015// 2: (unassigned)
Russ Cox8c195bd2015-02-13 14:40:36 -050016// 3: allow variadic functions
Matthew Dempsky7092a312017-10-20 15:20:56 -070017// 4: allow non-leaf functions
Russ Cox8c195bd2015-02-13 14:40:36 -050018//
Matthew Dempsky7092a312017-10-20 15:20:56 -070019// At some point this may get another default and become switch-offable with -N.
Russ Cox8c195bd2015-02-13 14:40:36 -050020//
Matthew Dempsky7092a312017-10-20 15:20:56 -070021// The -d typcheckinl flag enables early typechecking of all imported bodies,
22// which is useful to flush out bugs.
23//
24// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
25// which calls get inlined or not, more is for debugging, and may go away at any point.
Russ Cox8c195bd2015-02-13 14:40:36 -050026//
27// TODO:
28// - inline functions with ... args
Russ Cox8c195bd2015-02-13 14:40:36 -050029
30package gc
31
Robert Griesemer24597c02016-12-06 17:08:06 -080032import (
Robert Griesemerf68f2922017-04-04 17:54:02 -070033 "cmd/compile/internal/types"
Than McIntosh4435fcf2017-10-06 11:32:28 -040034 "cmd/internal/obj"
Robert Griesemer24597c02016-12-06 17:08:06 -080035 "cmd/internal/src"
36 "fmt"
Than McIntosh4435fcf2017-10-06 11:32:28 -040037 "sort"
38 "strings"
Robert Griesemer24597c02016-12-06 17:08:06 -080039)
Russ Cox8c195bd2015-02-13 14:40:36 -050040
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +000041// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
Russ Cox8c195bd2015-02-13 14:40:36 -050042// the ->sym can be re-used in the local package, so peel it off the receiver's type.
Robert Griesemerf68f2922017-04-04 17:54:02 -070043func fnpkg(fn *Node) *types.Pkg {
Josh Bleecher Snyder2e4dc862016-09-11 14:43:37 -070044 if fn.IsMethod() {
Russ Cox8c195bd2015-02-13 14:40:36 -050045 // method
Matthew Dempskyf91b8322016-03-09 20:54:59 -080046 rcvr := fn.Type.Recv().Type
Russ Cox8c195bd2015-02-13 14:40:36 -050047
Matthew Dempskye76fc1b2016-03-30 15:09:25 -070048 if rcvr.IsPtr() {
Josh Bleecher Snyder8640b512016-03-30 10:57:47 -070049 rcvr = rcvr.Elem()
Russ Cox8c195bd2015-02-13 14:40:36 -050050 }
Russ Coxdc7b54b2015-02-17 22:13:49 -050051 if rcvr.Sym == nil {
Robert Griesemer6537e182016-09-09 21:08:46 -070052 Fatalf("receiver with no sym: [%v] %L (%v)", fn.Sym, fn, rcvr)
Russ Cox8c195bd2015-02-13 14:40:36 -050053 }
54 return rcvr.Sym.Pkg
55 }
56
57 // non-method
58 return fn.Sym.Pkg
59}
60
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +000061// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
Russ Cox8c195bd2015-02-13 14:40:36 -050062// because they're a copy of an already checked body.
63func typecheckinl(fn *Node) {
Robert Griesemer3a880ba2016-03-02 12:49:37 -080064 lno := setlineno(fn)
Russ Cox8c195bd2015-02-13 14:40:36 -050065
66 // typecheckinl is only for imported functions;
67 // their bodies may refer to unsafe as long as the package
68 // was marked safe during import (which was checked then).
69 // the ->inl of a local function has been typechecked before caninl copied it.
Russ Cox382b44e2015-02-23 16:07:24 -050070 pkg := fnpkg(fn)
Russ Cox8c195bd2015-02-13 14:40:36 -050071
72 if pkg == localpkg || pkg == nil {
73 return // typecheckinl on local function
74 }
75
Robert Griesemera9ea36a2016-03-18 17:21:32 -070076 if Debug['m'] > 2 || Debug_export != 0 {
Robert Griesemer6537e182016-09-09 21:08:46 -070077 fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl)
Russ Cox8c195bd2015-02-13 14:40:36 -050078 }
79
Russ Cox382b44e2015-02-23 16:07:24 -050080 save_safemode := safemode
Matthew Dempsky980ab122016-04-13 18:37:18 -070081 safemode = false
Russ Cox8c195bd2015-02-13 14:40:36 -050082
Russ Cox382b44e2015-02-23 16:07:24 -050083 savefn := Curfn
Russ Cox8c195bd2015-02-13 14:40:36 -050084 Curfn = fn
Josh Bleecher Snyderec7c4942016-03-19 17:02:01 -070085 typecheckslice(fn.Func.Inl.Slice(), Etop)
Russ Cox8c195bd2015-02-13 14:40:36 -050086 Curfn = savefn
87
88 safemode = save_safemode
89
Robert Griesemer3a880ba2016-03-02 12:49:37 -080090 lineno = lno
Russ Cox8c195bd2015-02-13 14:40:36 -050091}
92
93// Caninl determines whether fn is inlineable.
94// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
95// fn and ->nbody will already have been typechecked.
96func caninl(fn *Node) {
Russ Cox8c195bd2015-02-13 14:40:36 -050097 if fn.Op != ODCLFUNC {
Håvard Haugen3c9fa382015-08-30 23:10:03 +020098 Fatalf("caninl %v", fn)
Russ Cox8c195bd2015-02-13 14:40:36 -050099 }
Russ Coxbd4fff62015-05-27 10:42:55 -0400100 if fn.Func.Nname == nil {
Robert Griesemerff046d22016-08-31 15:22:36 -0700101 Fatalf("caninl no nname %+v", fn)
Russ Cox8c195bd2015-02-13 14:40:36 -0500102 }
103
Josh Bleecher Snydere4cae4322016-05-03 17:21:32 -0700104 var reason string // reason, if any, that the function was not inlined
105 if Debug['m'] > 1 {
106 defer func() {
107 if reason != "" {
108 fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
109 }
110 }()
111 }
112
Todd Neala92543e2015-08-24 19:45:59 -0500113 // If marked "go:noinline", don't inline
Robert Griesemer3c0fae52016-02-26 13:32:28 -0800114 if fn.Func.Pragma&Noinline != 0 {
Josh Bleecher Snydere4cae4322016-05-03 17:21:32 -0700115 reason = "marked go:noinline"
Todd Neala92543e2015-08-24 19:45:59 -0500116 return
117 }
118
Austin Clements6b451ce2017-04-19 16:58:07 -0400119 // If marked "go:cgo_unsafe_args", don't inline, since the
120 // function makes assumptions about its argument frame layout.
David Lazar9fbfe7c2016-11-30 17:09:07 -0500121 if fn.Func.Pragma&CgoUnsafeArgs != 0 {
122 reason = "marked go:cgo_unsafe_args"
123 return
124 }
125
Matthew Dempsky4e64ee42017-10-20 14:20:48 -0700126 // The nowritebarrierrec checker currently works at function
127 // granularity, so inlining yeswritebarrierrec functions can
128 // confuse it (#22342). As a workaround, disallow inlining
129 // them for now.
130 if fn.Func.Pragma&Yeswritebarrierrec != 0 {
131 reason = "marked go:yeswritebarrierrec"
132 return
133 }
134
Russ Cox8c195bd2015-02-13 14:40:36 -0500135 // If fn has no body (is defined outside of Go), cannot inline it.
Josh Bleecher Snyder1da62af2016-04-24 13:50:26 -0700136 if fn.Nbody.Len() == 0 {
Josh Bleecher Snydere4cae4322016-05-03 17:21:32 -0700137 reason = "no function body"
Russ Cox8c195bd2015-02-13 14:40:36 -0500138 return
139 }
140
Josh Bleecher Snyder502a03f2017-04-25 18:02:43 -0700141 if fn.Typecheck() == 0 {
Håvard Haugen3c9fa382015-08-30 23:10:03 +0200142 Fatalf("caninl on non-typechecked function %v", fn)
Russ Cox8c195bd2015-02-13 14:40:36 -0500143 }
144
145 // can't handle ... args yet
146 if Debug['l'] < 3 {
Josh Bleecher Snyder758431f2016-04-24 14:09:03 -0700147 f := fn.Type.Params().Fields()
148 if len := f.Len(); len > 0 {
Aliaksandr Valialkined70f372017-02-27 19:56:38 +0200149 if t := f.Index(len - 1); t.Isddd() {
Josh Bleecher Snydere4cae4322016-05-03 17:21:32 -0700150 reason = "has ... args"
Russ Cox8c195bd2015-02-13 14:40:36 -0500151 return
152 }
153 }
154 }
155
Ian Lance Taylor9e902f02015-10-20 10:00:07 -0700156 // Runtime package must not be instrumented.
157 // Instrument skips runtime package. However, some runtime code can be
Dmitry Vyukov08c43482015-04-09 10:08:29 +0300158 // inlined into other packages and instrumented there. To avoid this,
Ian Lance Taylor9e902f02015-10-20 10:00:07 -0700159 // we disable inlining of runtime functions when instrumenting.
Dmitry Vyukov08c43482015-04-09 10:08:29 +0300160 // The example that we observed is inlining of LockOSThread,
161 // which lead to false race reports on m contents.
Ian Lance Taylor9e902f02015-10-20 10:00:07 -0700162 if instrumenting && myimportpath == "runtime" {
Josh Bleecher Snydere4cae4322016-05-03 17:21:32 -0700163 reason = "instrumenting and is runtime function"
Dmitry Vyukov08c43482015-04-09 10:08:29 +0300164 return
165 }
166
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700167 n := fn.Func.Nname
168 if n.Func.InlinabilityChecked() {
169 return
170 }
171 defer n.Func.SetInlinabilityChecked(true)
172
Russ Cox77ccb162015-02-24 12:19:01 -0500173 const maxBudget = 80
Austin Clementse52d3172017-04-19 16:32:41 -0400174 visitor := hairyVisitor{budget: maxBudget}
175 if visitor.visitList(fn.Nbody) {
176 reason = visitor.reason
Josh Bleecher Snydere4cae4322016-05-03 17:21:32 -0700177 return
178 }
Austin Clementse52d3172017-04-19 16:32:41 -0400179 if visitor.budget < 0 {
Ilya Tocar475df0e2017-09-12 13:53:55 -0500180 reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", maxBudget-visitor.budget, maxBudget)
Russ Cox8c195bd2015-02-13 14:40:36 -0500181 return
182 }
183
Russ Cox382b44e2015-02-23 16:07:24 -0500184 savefn := Curfn
Russ Cox8c195bd2015-02-13 14:40:36 -0500185 Curfn = fn
186
Josh Bleecher Snyder758431f2016-04-24 14:09:03 -0700187 n.Func.Inl.Set(fn.Nbody.Slice())
188 fn.Nbody.Set(inlcopylist(n.Func.Inl.Slice()))
189 inldcl := inlcopylist(n.Name.Defn.Func.Dcl)
190 n.Func.Inldcl.Set(inldcl)
Austin Clementse52d3172017-04-19 16:32:41 -0400191 n.Func.InlCost = maxBudget - visitor.budget
Russ Cox8c195bd2015-02-13 14:40:36 -0500192
193 // hack, TODO, check for better way to link method nodes back to the thing with the ->inl
194 // this is so export can find the body of a method
Robert Griesemerf68f2922017-04-04 17:54:02 -0700195 fn.Type.FuncType().Nname = asTypesNode(n)
Russ Cox8c195bd2015-02-13 14:40:36 -0500196
197 if Debug['m'] > 1 {
Robert Griesemer266c6222016-08-31 16:19:50 -0700198 fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl)
Russ Cox8c195bd2015-02-13 14:40:36 -0500199 } else if Debug['m'] != 0 {
Josh Bleecher Snyder758431f2016-04-24 14:09:03 -0700200 fmt.Printf("%v: can inline %v\n", fn.Line(), n)
Russ Cox8c195bd2015-02-13 14:40:36 -0500201 }
202
203 Curfn = savefn
204}
205
Matthew Dempsky86845342017-10-27 15:36:59 -0700206// inlFlood marks n's inline body for export and recursively ensures
207// all called functions are marked too.
208func inlFlood(n *Node) {
209 if n == nil {
210 return
211 }
212 if n.Op != ONAME || n.Class() != PFUNC {
213 Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op, n.Class())
214 }
215 if n.Func == nil {
216 // TODO(mdempsky): Should init have a Func too?
217 if n.Sym.Name == "init" {
218 return
219 }
220 Fatalf("inlFlood: missing Func on %v", n)
221 }
222 if n.Func.Inl.Len() == 0 {
223 return
224 }
225
226 if n.Func.ExportInline() {
227 return
228 }
229 n.Func.SetExportInline(true)
230
231 typecheckinl(n)
232
233 // Recursively flood any functions called by this one.
234 inspectList(n.Func.Inl, func(n *Node) bool {
235 switch n.Op {
236 case OCALLFUNC, OCALLMETH:
237 inlFlood(asNode(n.Left.Type.Nname()))
238 }
239 return true
240 })
241}
242
Austin Clementse52d3172017-04-19 16:32:41 -0400243// hairyVisitor visits a function body to determine its inlining
244// hairiness and whether or not it can be inlined.
245type hairyVisitor struct {
246 budget int32
247 reason string
248}
249
Russ Cox8c195bd2015-02-13 14:40:36 -0500250// Look for anything we want to punt on.
Austin Clementse52d3172017-04-19 16:32:41 -0400251func (v *hairyVisitor) visitList(ll Nodes) bool {
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800252 for _, n := range ll.Slice() {
Austin Clementse52d3172017-04-19 16:32:41 -0400253 if v.visit(n) {
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -0800254 return true
255 }
256 }
257 return false
258}
259
Austin Clementse52d3172017-04-19 16:32:41 -0400260func (v *hairyVisitor) visit(n *Node) bool {
Russ Coxdc7b54b2015-02-17 22:13:49 -0500261 if n == nil {
262 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500263 }
264
Russ Cox8c195bd2015-02-13 14:40:36 -0500265 switch n.Op {
Russ Cox77ccb162015-02-24 12:19:01 -0500266 // Call is okay if inlinable and we have the budget for the body.
267 case OCALLFUNC:
Keith Randall8a9dc052016-12-09 16:59:38 -0800268 if isIntrinsicCall(n) {
Austin Clementse52d3172017-04-19 16:32:41 -0400269 v.budget--
Keith Randall8a9dc052016-12-09 16:59:38 -0800270 break
271 }
David Lazar2397cd02017-04-19 12:57:52 -0400272 // Functions that call runtime.getcaller{pc,sp} can not be inlined
273 // because getcaller{pc,sp} expect a pointer to the caller's first argument.
Josh Bleecher Snyder386765a2017-04-25 18:14:12 -0700274 if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) {
David Lazar2397cd02017-04-19 12:57:52 -0400275 fn := n.Left.Sym.Name
276 if fn == "getcallerpc" || fn == "getcallersp" {
Austin Clementse52d3172017-04-19 16:32:41 -0400277 v.reason = "call to " + fn
David Lazar2397cd02017-04-19 12:57:52 -0400278 return true
279 }
280 }
281
Keith Randall495b1672017-03-16 14:08:31 -0700282 if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 {
Austin Clementse52d3172017-04-19 16:32:41 -0400283 v.budget -= fn.InlCost
Keith Randall495b1672017-03-16 14:08:31 -0700284 break
285 }
Hugues Bruantc4b65fa2017-10-21 15:58:37 -0700286 if n.Left.Op == OCLOSURE {
287 if fn := inlinableClosure(n.Left); fn != nil {
288 v.budget -= fn.Func.InlCost
289 break
290 }
291 } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
292 // NB: this case currently cannot trigger since closure definition
293 // prevents inlining
294 // NB: ideally we would also handle captured variables defined as
295 // closures in the outer scope this brings us back to the idea of
296 // function value propagation, which if available would both avoid
297 // the "reassigned" check and neatly handle multiple use cases in a
298 // single code path
299 if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
300 if fn := inlinableClosure(d.Right); fn != nil {
301 v.budget -= fn.Func.InlCost
302 break
303 }
304 }
305 }
Russ Coxb6dc3e62016-05-25 01:33:24 -0400306
Matthew Dempskyfcd32882017-10-24 14:45:41 -0700307 if n.Left.isMethodExpression() {
Robert Griesemerf68f2922017-04-04 17:54:02 -0700308 if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 {
Austin Clementse52d3172017-04-19 16:32:41 -0400309 v.budget -= d.Func.InlCost
Russ Cox77ccb162015-02-24 12:19:01 -0500310 break
311 }
312 }
313 if Debug['l'] < 4 {
Austin Clementse52d3172017-04-19 16:32:41 -0400314 v.reason = "non-leaf function"
Russ Cox77ccb162015-02-24 12:19:01 -0500315 return true
316 }
317
318 // Call is okay if inlinable and we have the budget for the body.
319 case OCALLMETH:
Josh Bleecher Snyder758431f2016-04-24 14:09:03 -0700320 t := n.Left.Type
321 if t == nil {
Robert Griesemerff046d22016-08-31 15:22:36 -0700322 Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
Russ Cox77ccb162015-02-24 12:19:01 -0500323 }
Josh Bleecher Snyder758431f2016-04-24 14:09:03 -0700324 if t.Nname() == nil {
Robert Griesemer8d0bbe22016-08-31 10:32:40 -0700325 Fatalf("no function definition for [%p] %+v\n", t, t)
Russ Cox77ccb162015-02-24 12:19:01 -0500326 }
Robert Griesemerf68f2922017-04-04 17:54:02 -0700327 if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl.Len() != 0 {
Austin Clementse52d3172017-04-19 16:32:41 -0400328 v.budget -= inlfn.InlCost
Russ Cox77ccb162015-02-24 12:19:01 -0500329 break
330 }
331 if Debug['l'] < 4 {
Austin Clementse52d3172017-04-19 16:32:41 -0400332 v.reason = "non-leaf method"
Russ Cox77ccb162015-02-24 12:19:01 -0500333 return true
334 }
335
336 // Things that are too hairy, irrespective of the budget
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700337 case OCALL, OCALLINTER, OPANIC, ORECOVER:
Russ Cox8c195bd2015-02-13 14:40:36 -0500338 if Debug['l'] < 4 {
Austin Clementse52d3172017-04-19 16:32:41 -0400339 v.reason = "non-leaf op " + n.Op.String()
Russ Coxdc7b54b2015-02-17 22:13:49 -0500340 return true
Russ Cox8c195bd2015-02-13 14:40:36 -0500341 }
342
343 case OCLOSURE,
344 OCALLPART,
345 ORANGE,
346 OFOR,
David Chased71f36b2017-02-02 11:53:41 -0500347 OFORUNTIL,
Russ Cox8c195bd2015-02-13 14:40:36 -0500348 OSELECT,
Todd Neale41f5272016-03-16 21:29:17 -0500349 OTYPESW,
Russ Cox8c195bd2015-02-13 14:40:36 -0500350 OPROC,
351 ODEFER,
Todd Nealfc6bcde2016-03-16 18:44:17 -0500352 ODCLTYPE, // can't print yet
Todd Neale41f5272016-03-16 21:29:17 -0500353 OBREAK,
Russ Cox8c195bd2015-02-13 14:40:36 -0500354 ORETJMP:
Austin Clementse52d3172017-04-19 16:32:41 -0400355 v.reason = "unhandled op " + n.Op.String()
Russ Coxdc7b54b2015-02-17 22:13:49 -0500356 return true
Mark Pulford812b34e2017-09-03 23:53:38 +1000357
358 case ODCLCONST, OEMPTY, OFALL, OLABEL:
359 // These nodes don't produce code; omit from inlining budget.
360 return false
Russ Cox8c195bd2015-02-13 14:40:36 -0500361 }
362
Austin Clementse52d3172017-04-19 16:32:41 -0400363 v.budget--
Josh Bleecher Snyderd6dbf3a2016-04-21 19:35:26 -0700364 // TODO(mdempsky/josharian): Hacks to appease toolstash; remove.
365 // See issue 17566 and CL 31674 for discussion.
366 switch n.Op {
367 case OSTRUCTKEY:
Austin Clementse52d3172017-04-19 16:32:41 -0400368 v.budget--
Josh Bleecher Snyderd6dbf3a2016-04-21 19:35:26 -0700369 case OSLICE, OSLICEARR, OSLICESTR:
Austin Clementse52d3172017-04-19 16:32:41 -0400370 v.budget--
Josh Bleecher Snyderd6dbf3a2016-04-21 19:35:26 -0700371 case OSLICE3, OSLICE3ARR:
Austin Clementse52d3172017-04-19 16:32:41 -0400372 v.budget -= 2
Joe Tsaif0e347b2016-10-21 19:43:33 +0000373 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500374
Ilya Tocar475df0e2017-09-12 13:53:55 -0500375 // When debugging, don't stop early, to get full cost of inlining this function
376 if v.budget < 0 && Debug['m'] < 2 {
Josh Bleecher Snydera246f612016-11-08 22:18:38 -0800377 return true
378 }
379
Austin Clementse52d3172017-04-19 16:32:41 -0400380 return v.visit(n.Left) || v.visit(n.Right) ||
381 v.visitList(n.List) || v.visitList(n.Rlist) ||
382 v.visitList(n.Ninit) || v.visitList(n.Nbody)
Russ Cox8c195bd2015-02-13 14:40:36 -0500383}
384
385// Inlcopy and inlcopylist recursively copy the body of a function.
386// Any name-like node of non-local class is marked for re-export by adding it to
387// the exportlist.
Ian Lance Taylorc4012b62016-03-08 10:26:20 -0800388func inlcopylist(ll []*Node) []*Node {
Ian Lance Taylorf444b8a2016-03-09 20:29:21 -0800389 s := make([]*Node, 0, len(ll))
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800390 for _, n := range ll {
391 s = append(s, inlcopy(n))
Russ Cox8c195bd2015-02-13 14:40:36 -0500392 }
Ian Lance Taylor99b6b772016-03-04 09:37:58 -0800393 return s
Russ Cox8c195bd2015-02-13 14:40:36 -0500394}
395
396func inlcopy(n *Node) *Node {
Russ Cox8c195bd2015-02-13 14:40:36 -0500397 if n == nil {
398 return nil
399 }
400
401 switch n.Op {
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700402 case ONAME, OTYPE, OLITERAL:
Russ Cox8c195bd2015-02-13 14:40:36 -0500403 return n
404 }
405
Dave Cheneyedca4cd2016-03-23 16:01:15 +1100406 m := *n
Josh Bleecher Snyder57279ba2015-03-10 21:37:13 -0700407 if m.Func != nil {
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -0800408 m.Func.Inl.Set(nil)
Josh Bleecher Snyder57279ba2015-03-10 21:37:13 -0700409 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500410 m.Left = inlcopy(n.Left)
411 m.Right = inlcopy(n.Right)
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800412 m.List.Set(inlcopylist(n.List.Slice()))
413 m.Rlist.Set(inlcopylist(n.Rlist.Slice()))
414 m.Ninit.Set(inlcopylist(n.Ninit.Slice()))
Ian Lance Taylor99b6b772016-03-04 09:37:58 -0800415 m.Nbody.Set(inlcopylist(n.Nbody.Slice()))
Russ Cox8c195bd2015-02-13 14:40:36 -0500416
Dave Cheneyedca4cd2016-03-23 16:01:15 +1100417 return &m
Russ Cox8c195bd2015-02-13 14:40:36 -0500418}
419
420// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +0000421// calls made to inlineable functions. This is the external entry point.
Russ Cox8c195bd2015-02-13 14:40:36 -0500422func inlcalls(fn *Node) {
Russ Cox382b44e2015-02-23 16:07:24 -0500423 savefn := Curfn
Russ Cox8c195bd2015-02-13 14:40:36 -0500424 Curfn = fn
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700425 fn = inlnode(fn)
Russ Cox8c195bd2015-02-13 14:40:36 -0500426 if fn != Curfn {
Håvard Haugen3c9fa382015-08-30 23:10:03 +0200427 Fatalf("inlnode replaced curfn")
Russ Cox8c195bd2015-02-13 14:40:36 -0500428 }
429 Curfn = savefn
430}
431
432// Turn an OINLCALL into a statement.
433func inlconv2stmt(n *Node) {
434 n.Op = OBLOCK
435
436 // n->ninit stays
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800437 n.List.Set(n.Nbody.Slice())
Russ Cox8c195bd2015-02-13 14:40:36 -0500438
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -0800439 n.Nbody.Set(nil)
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800440 n.Rlist.Set(nil)
Russ Cox8c195bd2015-02-13 14:40:36 -0500441}
442
443// Turn an OINLCALL into a single valued expression.
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700444// The result of inlconv2expr MUST be assigned back to n, e.g.
445// n.Left = inlconv2expr(n.Left)
446func inlconv2expr(n *Node) *Node {
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800447 r := n.Rlist.First()
Josh Bleecher Snyder7e8e9ab2016-03-23 08:51:38 -0700448 return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...))
Russ Cox8c195bd2015-02-13 14:40:36 -0500449}
450
451// Turn the rlist (with the return values) of the OINLCALL in
452// n into an expression list lumping the ninit and body
453// containing the inlined statements on the first list element so
454// order will be preserved Used in return, oas2func and call
455// statements.
Ian Lance Taylor99b6b772016-03-04 09:37:58 -0800456func inlconv2list(n *Node) []*Node {
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800457 if n.Op != OINLCALL || n.Rlist.Len() == 0 {
Robert Griesemerff046d22016-08-31 15:22:36 -0700458 Fatalf("inlconv2list %+v\n", n)
Russ Cox8c195bd2015-02-13 14:40:36 -0500459 }
460
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800461 s := n.Rlist.Slice()
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700462 s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...))
Ian Lance Taylor99b6b772016-03-04 09:37:58 -0800463 return s
Russ Cox8c195bd2015-02-13 14:40:36 -0500464}
465
Ian Lance Taylorc4012b62016-03-08 10:26:20 -0800466func inlnodelist(l Nodes) {
Ian Lance Taylorcd6619d2016-03-09 12:39:36 -0800467 s := l.Slice()
468 for i := range s {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700469 s[i] = inlnode(s[i])
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -0800470 }
471}
472
Russ Cox8c195bd2015-02-13 14:40:36 -0500473// inlnode recurses over the tree to find inlineable calls, which will
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +0000474// be turned into OINLCALLs by mkinlcall. When the recursion comes
Russ Cox8c195bd2015-02-13 14:40:36 -0500475// back up will examine left, right, list, rlist, ninit, ntest, nincr,
476// nbody and nelse and use one of the 4 inlconv/glue functions above
477// to turn the OINLCALL into an expression, a statement, or patch it
478// in to this nodes list or rlist as appropriate.
479// NOTE it makes no sense to pass the glue functions down the
480// recursion to the level where the OINLCALL gets created because they
481// have to edit /this/ n, so you'd have to push that one down as well,
482// but then you may as well do it here. so this is cleaner and
483// shorter and less complicated.
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700484// The result of inlnode MUST be assigned back to n, e.g.
485// n.Left = inlnode(n.Left)
486func inlnode(n *Node) *Node {
487 if n == nil {
488 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500489 }
490
Russ Cox8c195bd2015-02-13 14:40:36 -0500491 switch n.Op {
492 // inhibit inlining of their argument
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700493 case ODEFER, OPROC:
Russ Cox8c195bd2015-02-13 14:40:36 -0500494 switch n.Left.Op {
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700495 case OCALLFUNC, OCALLMETH:
Aliaksandr Valialkined70f372017-02-27 19:56:38 +0200496 n.Left.SetNoInline(true)
Russ Cox8c195bd2015-02-13 14:40:36 -0500497 }
Josh Bleecher Snyder0df81e82017-02-27 10:45:26 -0800498 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500499
Alberto Donizetti590f3f02016-09-24 21:38:58 +0200500 // TODO do them here (or earlier),
Russ Cox8c195bd2015-02-13 14:40:36 -0500501 // so escape analysis can avoid more heapmoves.
502 case OCLOSURE:
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700503 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500504 }
505
Robert Griesemer3a880ba2016-03-02 12:49:37 -0800506 lno := setlineno(n)
Russ Cox8c195bd2015-02-13 14:40:36 -0500507
508 inlnodelist(n.Ninit)
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800509 for _, n1 := range n.Ninit.Slice() {
510 if n1.Op == OINLCALL {
511 inlconv2stmt(n1)
Russ Cox8c195bd2015-02-13 14:40:36 -0500512 }
513 }
514
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700515 n.Left = inlnode(n.Left)
Russ Cox8c195bd2015-02-13 14:40:36 -0500516 if n.Left != nil && n.Left.Op == OINLCALL {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700517 n.Left = inlconv2expr(n.Left)
Russ Cox8c195bd2015-02-13 14:40:36 -0500518 }
519
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700520 n.Right = inlnode(n.Right)
Russ Cox8c195bd2015-02-13 14:40:36 -0500521 if n.Right != nil && n.Right.Op == OINLCALL {
David Chased71f36b2017-02-02 11:53:41 -0500522 if n.Op == OFOR || n.Op == OFORUNTIL {
Russ Coxffef1802015-05-22 01:16:52 -0400523 inlconv2stmt(n.Right)
524 } else {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700525 n.Right = inlconv2expr(n.Right)
Russ Coxffef1802015-05-22 01:16:52 -0400526 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500527 }
528
529 inlnodelist(n.List)
530 switch n.Op {
531 case OBLOCK:
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800532 for _, n2 := range n.List.Slice() {
533 if n2.Op == OINLCALL {
534 inlconv2stmt(n2)
Russ Cox8c195bd2015-02-13 14:40:36 -0500535 }
536 }
537
Josh Bleecher Snyder0df81e82017-02-27 10:45:26 -0800538 case ORETURN, OCALLFUNC, OCALLMETH, OCALLINTER, OAPPEND, OCOMPLEX:
539 // if we just replaced arg in f(arg()) or return arg with an inlined call
540 // and arg returns multiple values, glue as list
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800541 if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 {
542 n.List.Set(inlconv2list(n.List.First()))
Russ Cox8c195bd2015-02-13 14:40:36 -0500543 break
544 }
545 fallthrough
546
Russ Cox8c195bd2015-02-13 14:40:36 -0500547 default:
Ian Lance Taylorcd6619d2016-03-09 12:39:36 -0800548 s := n.List.Slice()
549 for i1, n1 := range s {
Josh Bleecher Snyderd6dbf3a2016-04-21 19:35:26 -0700550 if n1 != nil && n1.Op == OINLCALL {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700551 s[i1] = inlconv2expr(s[i1])
Russ Cox8c195bd2015-02-13 14:40:36 -0500552 }
553 }
554 }
555
556 inlnodelist(n.Rlist)
Josh Bleecher Snyder0df81e82017-02-27 10:45:26 -0800557 if n.Op == OAS2FUNC && n.Rlist.First().Op == OINLCALL {
558 n.Rlist.Set(inlconv2list(n.Rlist.First()))
559 n.Op = OAS2
Josh Bleecher Snyder502a03f2017-04-25 18:02:43 -0700560 n.SetTypecheck(0)
Josh Bleecher Snyder0df81e82017-02-27 10:45:26 -0800561 n = typecheck(n, Etop)
562 } else {
Ian Lance Taylorcd6619d2016-03-09 12:39:36 -0800563 s := n.Rlist.Slice()
564 for i1, n1 := range s {
565 if n1.Op == OINLCALL {
Russ Coxffef1802015-05-22 01:16:52 -0400566 if n.Op == OIF {
Ian Lance Taylorcd6619d2016-03-09 12:39:36 -0800567 inlconv2stmt(n1)
Russ Coxffef1802015-05-22 01:16:52 -0400568 } else {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700569 s[i1] = inlconv2expr(s[i1])
Russ Coxffef1802015-05-22 01:16:52 -0400570 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500571 }
572 }
573 }
574
Ian Lance Taylor99b6b772016-03-04 09:37:58 -0800575 inlnodelist(n.Nbody)
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -0800576 for _, n := range n.Nbody.Slice() {
577 if n.Op == OINLCALL {
578 inlconv2stmt(n)
Russ Cox8c195bd2015-02-13 14:40:36 -0500579 }
580 }
581
Russ Cox8c195bd2015-02-13 14:40:36 -0500582 // with all the branches out of the way, it is now time to
583 // transmogrify this node itself unless inhibited by the
584 // switch at the top of this function.
585 switch n.Op {
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -0700586 case OCALLFUNC, OCALLMETH:
Aliaksandr Valialkined70f372017-02-27 19:56:38 +0200587 if n.NoInline() {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700588 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500589 }
590 }
591
592 switch n.Op {
593 case OCALLFUNC:
594 if Debug['m'] > 3 {
Robert Griesemerff046d22016-08-31 15:22:36 -0700595 fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
Russ Cox8c195bd2015-02-13 14:40:36 -0500596 }
Keith Randall320ddcf2016-08-23 16:49:28 -0700597 if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case
Aliaksandr Valialkined70f372017-02-27 19:56:38 +0200598 n = mkinlcall(n, n.Left, n.Isddd())
Matthew Dempskyfcd32882017-10-24 14:45:41 -0700599 } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil {
Robert Griesemerf68f2922017-04-04 17:54:02 -0700600 n = mkinlcall(n, asNode(n.Left.Sym.Def), n.Isddd())
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700601 } else if n.Left.Op == OCLOSURE {
602 if f := inlinableClosure(n.Left); f != nil {
603 n = mkinlcall(n, f, n.Isddd())
604 }
605 } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
606 if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
607 if f := inlinableClosure(d.Right); f != nil {
608 // NB: this check is necessary to prevent indirect re-assignment of the variable
609 // having the address taken after the invocation or only used for reads is actually fine
610 // but we have no easy way to distinguish the safe cases
611 if d.Left.Addrtaken() {
612 if Debug['m'] > 1 {
613 fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left)
614 }
615 break
616 }
617
618 // ensure the variable is never re-assigned
619 if unsafe, a := reassigned(n.Left); unsafe {
620 if Debug['m'] > 1 {
621 if a != nil {
622 fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a)
623 } else {
624 fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left)
625 }
626 }
627 break
628 }
629 n = mkinlcall(n, f, n.Isddd())
630 }
631 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500632 }
633
634 case OCALLMETH:
635 if Debug['m'] > 3 {
Robert Griesemer6537e182016-09-09 21:08:46 -0700636 fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
Russ Cox8c195bd2015-02-13 14:40:36 -0500637 }
638
639 // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
640 if n.Left.Type == nil {
Robert Griesemerff046d22016-08-31 15:22:36 -0700641 Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
Russ Cox8c195bd2015-02-13 14:40:36 -0500642 }
643
Josh Bleecher Snyderb83618f2016-03-30 16:59:53 -0700644 if n.Left.Type.Nname() == nil {
Robert Griesemer8d0bbe22016-08-31 10:32:40 -0700645 Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
Russ Cox8c195bd2015-02-13 14:40:36 -0500646 }
647
Robert Griesemerf68f2922017-04-04 17:54:02 -0700648 n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), n.Isddd())
Russ Cox8c195bd2015-02-13 14:40:36 -0500649 }
650
Robert Griesemer3a880ba2016-03-02 12:49:37 -0800651 lineno = lno
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700652 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500653}
654
Hugues Bruantc4b65fa2017-10-21 15:58:37 -0700655// inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with
656// the inlinable body. Returns nil if the function is not inlinable.
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700657func inlinableClosure(n *Node) *Node {
658 c := n.Func.Closure
659 caninl(c)
660 f := c.Func.Nname
Hugues Bruantc4b65fa2017-10-21 15:58:37 -0700661 if f == nil || f.Func.Inl.Len() == 0 {
662 return nil
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700663 }
Hugues Bruantc4b65fa2017-10-21 15:58:37 -0700664 return f
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700665}
666
667// reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
668// indicating whether the name has any assignments other than its declaration.
669// The second return value is the first such assignment encountered in the walk, if any. It is mostly
670// useful for -m output documenting the reason for inhibited optimizations.
671// NB: global variables are always considered to be re-assigned.
672// TODO: handle initial declaration not including an assignment and followed by a single assignment?
673func reassigned(n *Node) (bool, *Node) {
674 if n.Op != ONAME {
675 Fatalf("reassigned %v", n)
676 }
677 // no way to reliably check for no-reassignment of globals, assume it can be
678 if n.Name.Curfn == nil {
679 return true, nil
680 }
Hugues Bruant483e2982017-11-02 19:54:46 -0700681 f := n.Name.Curfn
682 // There just might be a good reason for this although this can be pretty surprising:
683 // local variables inside a closure have Curfn pointing to the OCLOSURE node instead
684 // of the corresponding ODCLFUNC.
685 // We need to walk the function body to check for reassignments so we follow the
686 // linkage to the ODCLFUNC node as that is where body is held.
687 if f.Op == OCLOSURE {
688 f = f.Func.Closure
689 }
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700690 v := reassignVisitor{name: n}
Hugues Bruant483e2982017-11-02 19:54:46 -0700691 a := v.visitList(f.Nbody)
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700692 return a != nil, a
693}
694
695type reassignVisitor struct {
696 name *Node
697}
698
699func (v *reassignVisitor) visit(n *Node) *Node {
700 if n == nil {
701 return nil
702 }
703 switch n.Op {
704 case OAS:
705 if n.Left == v.name && n != v.name.Name.Defn {
706 return n
707 }
708 return nil
Hugues Bruant483e2982017-11-02 19:54:46 -0700709 case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE:
Hugues Bruant4f70a2a2017-09-18 14:54:10 -0700710 for _, p := range n.List.Slice() {
711 if p == v.name && n != v.name.Name.Defn {
712 return n
713 }
714 }
715 return nil
716 }
717 if a := v.visit(n.Left); a != nil {
718 return a
719 }
720 if a := v.visit(n.Right); a != nil {
721 return a
722 }
723 if a := v.visitList(n.List); a != nil {
724 return a
725 }
726 if a := v.visitList(n.Rlist); a != nil {
727 return a
728 }
729 if a := v.visitList(n.Ninit); a != nil {
730 return a
731 }
732 if a := v.visitList(n.Nbody); a != nil {
733 return a
734 }
735 return nil
736}
737
738func (v *reassignVisitor) visitList(l Nodes) *Node {
739 for _, n := range l.Slice() {
740 if a := v.visit(n); a != nil {
741 return a
742 }
743 }
744 return nil
745}
746
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700747// The result of mkinlcall MUST be assigned back to n, e.g.
748// n.Left = mkinlcall(n.Left, fn, isddd)
749func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
Russ Cox382b44e2015-02-23 16:07:24 -0500750 save_safemode := safemode
Russ Cox8c195bd2015-02-13 14:40:36 -0500751
752 // imported functions may refer to unsafe as long as the
753 // package was marked safe during import (already checked).
Russ Cox382b44e2015-02-23 16:07:24 -0500754 pkg := fnpkg(fn)
Russ Cox8c195bd2015-02-13 14:40:36 -0500755
756 if pkg != localpkg && pkg != nil {
Matthew Dempsky980ab122016-04-13 18:37:18 -0700757 safemode = false
Russ Cox8c195bd2015-02-13 14:40:36 -0500758 }
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700759 n = mkinlcall1(n, fn, isddd)
Russ Cox8c195bd2015-02-13 14:40:36 -0500760 safemode = save_safemode
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700761 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500762}
763
Robert Griesemerf68f2922017-04-04 17:54:02 -0700764func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
765 if asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
766 inlvar := inlvars[asNode(t.Nname)]
Josh Bleecher Snyderdc5f9312016-10-26 22:58:50 -0700767 if inlvar == nil {
Robert Griesemerf68f2922017-04-04 17:54:02 -0700768 Fatalf("missing inlvar for %v\n", asNode(t.Nname))
Russ Cox8c195bd2015-02-13 14:40:36 -0500769 }
Josh Bleecher Snyderdc5f9312016-10-26 22:58:50 -0700770 return inlvar
Russ Cox8c195bd2015-02-13 14:40:36 -0500771 }
772
Josh Bleecher Snyder7e8e9ab2016-03-23 08:51:38 -0700773 return typecheck(nblank, Erv|Easgn)
Russ Cox8c195bd2015-02-13 14:40:36 -0500774}
775
776var inlgen int
777
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700778// If n is a call, and fn is a function with an inlinable body,
779// return an OINLCALL.
Russ Cox8c195bd2015-02-13 14:40:36 -0500780// On return ninit has the parameter assignments, the nbody is the
781// inlined function body and list, rlist contain the input, output
782// parameters.
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700783// The result of mkinlcall1 MUST be assigned back to n, e.g.
784// n.Left = mkinlcall1(n.Left, fn, isddd)
Matthew Dempskyb80029c2017-10-09 11:06:52 -0700785func mkinlcall1(n, fn *Node, isddd bool) *Node {
Josh Bleecher Snyder1da62af2016-04-24 13:50:26 -0700786 if fn.Func.Inl.Len() == 0 {
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700787 // No inlinable body.
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700788 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500789 }
790
Russ Cox4fdd5362015-05-26 22:19:27 -0400791 if fn == Curfn || fn.Name.Defn == Curfn {
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700792 // Can't recursively inline a function into itself.
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700793 return n
Russ Cox8c195bd2015-02-13 14:40:36 -0500794 }
795
Matthew Dempsky7092a312017-10-20 15:20:56 -0700796 if Debug_typecheckinl == 0 {
Russ Cox8c195bd2015-02-13 14:40:36 -0500797 typecheckinl(fn)
798 }
799
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700800 // We have a function node, and it has an inlineable body.
Russ Cox8c195bd2015-02-13 14:40:36 -0500801 if Debug['m'] > 1 {
Robert Griesemer266c6222016-08-31 16:19:50 -0700802 fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl)
Russ Cox8c195bd2015-02-13 14:40:36 -0500803 } else if Debug['m'] != 0 {
Russ Cox17228f42015-04-17 12:03:22 -0400804 fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
Russ Cox8c195bd2015-02-13 14:40:36 -0500805 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500806 if Debug['m'] > 2 {
Robert Griesemerff046d22016-08-31 15:22:36 -0700807 fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
Russ Cox8c195bd2015-02-13 14:40:36 -0500808 }
809
Russ Cox382b44e2015-02-23 16:07:24 -0500810 ninit := n.Ninit
Russ Cox8c195bd2015-02-13 14:40:36 -0500811
Hugues Bruantc4b65fa2017-10-21 15:58:37 -0700812 // Make temp names to use instead of the originals.
813 inlvars := make(map[*Node]*Node)
814
Than McIntosh4435fcf2017-10-06 11:32:28 -0400815 // record formals/locals for later post-processing
816 var inlfvars []*Node
817
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700818 // Find declarations corresponding to inlineable body.
Ian Lance Taylorb66a8922016-02-25 10:35:19 -0800819 var dcl []*Node
Ian Lance Taylor6abc8c92016-02-26 13:48:24 -0800820 if fn.Name.Defn != nil {
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700821 dcl = fn.Func.Inldcl.Slice() // local function
Hugues Bruantc4b65fa2017-10-21 15:58:37 -0700822
823 // handle captured variables when inlining closures
824 if c := fn.Name.Defn.Func.Closure; c != nil {
825 for _, v := range c.Func.Cvars.Slice() {
826 if v.Op == OXXX {
827 continue
828 }
829
830 o := v.Name.Param.Outer
831 // make sure the outer param matches the inlining location
832 // NB: if we enabled inlining of functions containing OCLOSURE or refined
833 // the reassigned check via some sort of copy propagation this would most
834 // likely need to be changed to a loop to walk up to the correct Param
835 if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) {
836 Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v)
837 }
838
839 if v.Name.Byval() {
840 iv := typecheck(inlvar(v), Erv)
841 ninit.Append(nod(ODCL, iv, nil))
842 ninit.Append(typecheck(nod(OAS, iv, o), Etop))
843 inlvars[v] = iv
844 } else {
845 addr := newname(lookup("&" + v.Sym.Name))
846 addr.Type = types.NewPtr(v.Type)
847 ia := typecheck(inlvar(addr), Erv)
848 ninit.Append(nod(ODCL, ia, nil))
849 ninit.Append(typecheck(nod(OAS, ia, nod(OADDR, o, nil)), Etop))
850 inlvars[addr] = ia
851
852 // When capturing by reference, all occurrence of the captured var
853 // must be substituted with dereference of the temporary address
854 inlvars[v] = typecheck(nod(OIND, ia, nil), Erv)
855 }
856 }
857 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500858 } else {
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700859 dcl = fn.Func.Dcl // imported function
Russ Cox8c195bd2015-02-13 14:40:36 -0500860 }
861
Ian Lance Taylorb66a8922016-02-25 10:35:19 -0800862 for _, ln := range dcl {
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700863 if ln.Op != ONAME {
864 continue
865 }
Josh Bleecher Snyder386765a2017-04-25 18:14:12 -0700866 if ln.Class() == PPARAMOUT { // return values handled below.
Russ Cox8c195bd2015-02-13 14:40:36 -0500867 continue
868 }
Russ Coxb6dc3e62016-05-25 01:33:24 -0400869 if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
870 continue
871 }
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700872 inlvars[ln] = typecheck(inlvar(ln), Erv)
Josh Bleecher Snyder386765a2017-04-25 18:14:12 -0700873 if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM {
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700874 ninit.Append(nod(ODCL, inlvars[ln], nil))
Russ Cox8c195bd2015-02-13 14:40:36 -0500875 }
Than McIntosh4435fcf2017-10-06 11:32:28 -0400876 if genDwarfInline > 0 {
877 inlf := inlvars[ln]
878 if ln.Class() == PPARAM {
879 inlf.SetInlFormal(true)
880 } else {
881 inlf.SetInlLocal(true)
882 }
883 inlf.Pos = ln.Pos
884 inlfvars = append(inlfvars, inlf)
885 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500886 }
887
888 // temporaries for return values.
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700889 var retvars []*Node
890 for i, t := range fn.Type.Results().Fields().Slice() {
891 var m *Node
Than McIntosh4435fcf2017-10-06 11:32:28 -0400892 var mpos src.XPos
Robert Griesemerf68f2922017-04-04 17:54:02 -0700893 if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
Than McIntosh4435fcf2017-10-06 11:32:28 -0400894 mpos = asNode(t.Nname).Pos
Robert Griesemerf68f2922017-04-04 17:54:02 -0700895 m = inlvar(asNode(t.Nname))
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700896 m = typecheck(m, Erv)
Robert Griesemerf68f2922017-04-04 17:54:02 -0700897 inlvars[asNode(t.Nname)] = m
Russ Cox8c195bd2015-02-13 14:40:36 -0500898 } else {
899 // anonymous return values, synthesize names for use in assignment that replaces return
900 m = retvar(t, i)
Russ Cox8c195bd2015-02-13 14:40:36 -0500901 }
902
Than McIntosh4435fcf2017-10-06 11:32:28 -0400903 if genDwarfInline > 0 {
904 // Don't update the src.Pos on a return variable if it
905 // was manufactured by the inliner (e.g. "~r2"); such vars
906 // were not part of the original callee.
907 if !strings.HasPrefix(m.Sym.Name, "~r") {
908 m.SetInlFormal(true)
909 m.Pos = mpos
910 inlfvars = append(inlfvars, m)
911 }
912 }
913
Dave Cheney073d2482016-09-16 11:00:54 +1000914 ninit.Append(nod(ODCL, m, nil))
Ian Lance Taylore6ea0162016-03-10 11:49:20 -0800915 retvars = append(retvars, m)
Russ Cox8c195bd2015-02-13 14:40:36 -0500916 }
917
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700918 // Assign arguments to the parameters' temp names.
Dave Cheney073d2482016-09-16 11:00:54 +1000919 as := nod(OAS2, nil, nil)
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800920 as.Rlist.Set(n.List.Slice())
Russ Cox8c195bd2015-02-13 14:40:36 -0500921
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700922 // For non-dotted calls to variadic functions, we assign the
923 // variadic parameter's temp name separately.
924 var vas *Node
Russ Cox8c195bd2015-02-13 14:40:36 -0500925
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700926 if fn.IsMethod() {
927 rcv := fn.Type.Recv()
Russ Cox8c195bd2015-02-13 14:40:36 -0500928
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700929 if n.Left.Op == ODOTMETH {
930 // For x.M(...), assign x directly to the
931 // receiver parameter.
932 if n.Left.Left == nil {
933 Fatalf("method call without receiver: %+v", n)
934 }
935 ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left)
936 ras = typecheck(ras, Etop)
937 ninit.Append(ras)
938 } else {
939 // For T.M(...), add the receiver parameter to
940 // as.List, so it's assigned by the normal
941 // arguments.
942 if as.Rlist.Len() == 0 {
943 Fatalf("non-method call to method without first arg: %+v", n)
944 }
945 as.List.Append(tinlvar(rcv, inlvars))
Russ Cox8c195bd2015-02-13 14:40:36 -0500946 }
Russ Cox8c195bd2015-02-13 14:40:36 -0500947 }
948
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700949 for _, param := range fn.Type.Params().Fields().Slice() {
950 // For ordinary parameters or variadic parameters in
951 // dotted calls, just add the variable to the
952 // assignment list, and we're done.
953 if !param.Isddd() || isddd {
954 as.List.Append(tinlvar(param, inlvars))
955 continue
Russ Cox8c195bd2015-02-13 14:40:36 -0500956 }
957
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700958 // Otherwise, we need to collect the remaining values
959 // to pass as a slice.
Russ Cox8c195bd2015-02-13 14:40:36 -0500960
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700961 numvals := n.List.Len()
962 if numvals == 1 && n.List.First().Type.IsFuncArgStruct() {
963 numvals = n.List.First().Type.NumFields()
Russ Cox8c195bd2015-02-13 14:40:36 -0500964 }
965
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700966 x := as.List.Len()
967 for as.List.Len() < numvals {
968 as.List.Append(argvar(param.Type, as.List.Len()))
969 }
970 varargs := as.List.Slice()[x:]
971
972 vas = nod(OAS, tinlvar(param, inlvars), nil)
973 if len(varargs) == 0 {
974 vas.Right = nodnil()
975 vas.Right.Type = param.Type
976 } else {
977 vas.Right = nod(OCOMPLIT, nil, typenod(param.Type))
978 vas.Right.List.Set(varargs)
Russ Cox8c195bd2015-02-13 14:40:36 -0500979 }
980 }
981
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800982 if as.Rlist.Len() != 0 {
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -0700983 as = typecheck(as, Etop)
Ian Lance Taylor38921b32016-03-08 15:10:26 -0800984 ninit.Append(as)
Russ Cox8c195bd2015-02-13 14:40:36 -0500985 }
986
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700987 if vas != nil {
988 vas = typecheck(vas, Etop)
989 ninit.Append(vas)
Russ Cox8c195bd2015-02-13 14:40:36 -0500990 }
991
Matthew Dempskyce9bef22017-04-07 13:47:10 -0700992 // Zero the return parameters.
Ian Lance Taylore6ea0162016-03-10 11:49:20 -0800993 for _, n := range retvars {
David Lazarb928e2f2017-05-27 17:43:37 -0400994 ras := nod(OAS, n, nil)
995 ras = typecheck(ras, Etop)
996 ninit.Append(ras)
Russ Cox8c195bd2015-02-13 14:40:36 -0500997 }
998
Josh Bleecher Snyderfe272912016-08-15 21:09:39 -0700999 retlabel := autolabel(".i")
Josh Bleecher Snydera9ed4772016-06-01 10:15:02 -07001000 retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
1001
Russ Cox8c195bd2015-02-13 14:40:36 -05001002 inlgen++
Russ Cox8c195bd2015-02-13 14:40:36 -05001003
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001004 parent := -1
1005 if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil {
1006 parent = b.InliningIndex()
1007 }
Than McIntosh4435fcf2017-10-06 11:32:28 -04001008 sort.Sort(byNodeName(dcl))
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001009 newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
1010
Than McIntosh4435fcf2017-10-06 11:32:28 -04001011 if genDwarfInline > 0 {
1012 if !fn.Sym.Linksym().WasInlined() {
1013 Ctxt.DwFixups.SetPrecursorFunc(fn.Sym.Linksym(), fn)
1014 fn.Sym.Linksym().Set(obj.AttrWasInlined, true)
1015 }
1016 }
1017
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001018 subst := inlsubst{
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001019 retlabel: retlabel,
1020 retvars: retvars,
1021 inlvars: inlvars,
1022 bases: make(map[*src.PosBase]*src.PosBase),
1023 newInlIndex: newIndex,
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001024 }
1025
1026 body := subst.list(fn.Func.Inl)
1027
Dave Cheney073d2482016-09-16 11:00:54 +10001028 lab := nod(OLABEL, retlabel, nil)
Josh Bleecher Snyderd1faf382016-05-27 15:33:11 -07001029 body = append(body, lab)
Russ Cox8c195bd2015-02-13 14:40:36 -05001030
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -08001031 typecheckslice(body, Etop)
Russ Cox8c195bd2015-02-13 14:40:36 -05001032
Than McIntosh4435fcf2017-10-06 11:32:28 -04001033 if genDwarfInline > 0 {
1034 for _, v := range inlfvars {
1035 v.Pos = subst.updatedPos(v.Pos)
1036 }
1037 }
1038
Russ Cox8c195bd2015-02-13 14:40:36 -05001039 //dumplist("ninit post", ninit);
1040
Dave Cheney073d2482016-09-16 11:00:54 +10001041 call := nod(OINLCALL, nil, nil)
Ian Lance Taylor38921b32016-03-08 15:10:26 -08001042 call.Ninit.Set(ninit.Slice())
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -08001043 call.Nbody.Set(body)
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001044 call.Rlist.Set(retvars)
Russ Cox8c195bd2015-02-13 14:40:36 -05001045 call.Type = n.Type
Josh Bleecher Snyder502a03f2017-04-25 18:02:43 -07001046 call.SetTypecheck(1)
Russ Cox8c195bd2015-02-13 14:40:36 -05001047
Russ Cox8c195bd2015-02-13 14:40:36 -05001048 // transitive inlining
Russ Cox77ccb162015-02-24 12:19:01 -05001049 // might be nice to do this before exporting the body,
1050 // but can't emit the body with inlining expanded.
1051 // instead we emit the things that the body needs
1052 // and each use must redo the inlining.
1053 // luckily these are small.
Ian Lance Taylor99b6b772016-03-04 09:37:58 -08001054 inlnodelist(call.Nbody)
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -08001055 for _, n := range call.Nbody.Slice() {
1056 if n.Op == OINLCALL {
1057 inlconv2stmt(n)
Russ Cox8c195bd2015-02-13 14:40:36 -05001058 }
Russ Cox8c195bd2015-02-13 14:40:36 -05001059 }
1060
1061 if Debug['m'] > 2 {
Matthew Dempskyb80029c2017-10-09 11:06:52 -07001062 fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call)
Russ Cox8c195bd2015-02-13 14:40:36 -05001063 }
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -07001064
Matthew Dempskyb80029c2017-10-09 11:06:52 -07001065 return call
Russ Cox8c195bd2015-02-13 14:40:36 -05001066}
1067
1068// Every time we expand a function we generate a new set of tmpnames,
1069// PAUTO's in the calling functions, and link them off of the
1070// PPARAM's, PAUTOS and PPARAMOUTs of the called function.
1071func inlvar(var_ *Node) *Node {
Russ Cox8c195bd2015-02-13 14:40:36 -05001072 if Debug['m'] > 3 {
Robert Griesemerff046d22016-08-31 15:22:36 -07001073 fmt.Printf("inlvar %+v\n", var_)
Russ Cox8c195bd2015-02-13 14:40:36 -05001074 }
1075
Russ Cox382b44e2015-02-23 16:07:24 -05001076 n := newname(var_.Sym)
Russ Cox8c195bd2015-02-13 14:40:36 -05001077 n.Type = var_.Type
Josh Bleecher Snyder386765a2017-04-25 18:14:12 -07001078 n.SetClass(PAUTO)
Josh Bleecher Snyderfc08a192017-04-27 15:17:57 -07001079 n.Name.SetUsed(true)
Russ Coxfd2154f2015-05-27 07:31:56 -04001080 n.Name.Curfn = Curfn // the calling function, not the called one
Aliaksandr Valialkined70f372017-02-27 19:56:38 +02001081 n.SetAddrtaken(var_.Addrtaken())
Russ Cox8c195bd2015-02-13 14:40:36 -05001082
Ian Lance Taylorb66a8922016-02-25 10:35:19 -08001083 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
Russ Cox8c195bd2015-02-13 14:40:36 -05001084 return n
1085}
1086
1087// Synthesize a variable to store the inlined function's results in.
Robert Griesemerf68f2922017-04-04 17:54:02 -07001088func retvar(t *types.Field, i int) *Node {
Dave Cheneyd7012ca2016-09-15 15:45:10 +10001089 n := newname(lookupN("~r", i))
Russ Cox8c195bd2015-02-13 14:40:36 -05001090 n.Type = t.Type
Josh Bleecher Snyder386765a2017-04-25 18:14:12 -07001091 n.SetClass(PAUTO)
Josh Bleecher Snyderfc08a192017-04-27 15:17:57 -07001092 n.Name.SetUsed(true)
Russ Coxfd2154f2015-05-27 07:31:56 -04001093 n.Name.Curfn = Curfn // the calling function, not the called one
Ian Lance Taylorb66a8922016-02-25 10:35:19 -08001094 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
Russ Cox8c195bd2015-02-13 14:40:36 -05001095 return n
1096}
1097
1098// Synthesize a variable to store the inlined function's arguments
1099// when they come from a multiple return call.
Robert Griesemerf68f2922017-04-04 17:54:02 -07001100func argvar(t *types.Type, i int) *Node {
Dave Cheneyd7012ca2016-09-15 15:45:10 +10001101 n := newname(lookupN("~arg", i))
Josh Bleecher Snyder8640b512016-03-30 10:57:47 -07001102 n.Type = t.Elem()
Josh Bleecher Snyder386765a2017-04-25 18:14:12 -07001103 n.SetClass(PAUTO)
Josh Bleecher Snyderfc08a192017-04-27 15:17:57 -07001104 n.Name.SetUsed(true)
Russ Coxfd2154f2015-05-27 07:31:56 -04001105 n.Name.Curfn = Curfn // the calling function, not the called one
Ian Lance Taylorb66a8922016-02-25 10:35:19 -08001106 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
Russ Cox8c195bd2015-02-13 14:40:36 -05001107 return n
1108}
1109
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001110// The inlsubst type implements the actual inlining of a single
1111// function call.
1112type inlsubst struct {
1113 // Target of the goto substituted in place of a return.
1114 retlabel *Node
1115
1116 // Temporary result variables.
1117 retvars []*Node
Josh Bleecher Snyderdc5f9312016-10-26 22:58:50 -07001118
1119 inlvars map[*Node]*Node
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001120
1121 // bases maps from original PosBase to PosBase with an extra
1122 // inlined call frame.
1123 bases map[*src.PosBase]*src.PosBase
1124
1125 // newInlIndex is the index of the inlined call frame to
1126 // insert for inlined nodes.
1127 newInlIndex int
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001128}
1129
1130// list inlines a list of nodes.
1131func (subst *inlsubst) list(ll Nodes) []*Node {
Ian Lance Taylor38921b32016-03-08 15:10:26 -08001132 s := make([]*Node, 0, ll.Len())
1133 for _, n := range ll.Slice() {
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001134 s = append(s, subst.node(n))
Russ Cox8c195bd2015-02-13 14:40:36 -05001135 }
Ian Lance Taylor99b6b772016-03-04 09:37:58 -08001136 return s
Ian Lance Taylor1d5001a2016-02-27 14:31:33 -08001137}
1138
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001139// node recursively copies a node from the saved pristine body of the
1140// inlined function, substituting references to input/output
1141// parameters with ones to the tmpnames, and substituting returns with
1142// assignments to the output.
1143func (subst *inlsubst) node(n *Node) *Node {
Russ Cox8c195bd2015-02-13 14:40:36 -05001144 if n == nil {
1145 return nil
1146 }
1147
1148 switch n.Op {
1149 case ONAME:
Josh Bleecher Snyderdc5f9312016-10-26 22:58:50 -07001150 if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
Russ Cox8c195bd2015-02-13 14:40:36 -05001151 if Debug['m'] > 2 {
Josh Bleecher Snyderdc5f9312016-10-26 22:58:50 -07001152 fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
Russ Cox8c195bd2015-02-13 14:40:36 -05001153 }
Josh Bleecher Snyderdc5f9312016-10-26 22:58:50 -07001154 return inlvar
Russ Cox8c195bd2015-02-13 14:40:36 -05001155 }
1156
1157 if Debug['m'] > 2 {
Robert Griesemerff046d22016-08-31 15:22:36 -07001158 fmt.Printf("not substituting name %+v\n", n)
Russ Cox8c195bd2015-02-13 14:40:36 -05001159 }
1160 return n
1161
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -07001162 case OLITERAL, OTYPE:
David Lazar1c6ef9a2017-02-17 16:07:47 -05001163 // If n is a named constant or type, we can continue
1164 // using it in the inline copy. Otherwise, make a copy
1165 // so we can update the line number.
1166 if n.Sym != nil {
1167 return n
1168 }
Russ Cox8c195bd2015-02-13 14:40:36 -05001169
1170 // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
1171
1172 // dump("Return before substitution", n);
1173 case ORETURN:
Dave Cheney073d2482016-09-16 11:00:54 +10001174 m := nod(OGOTO, subst.retlabel, nil)
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001175 m.Ninit.Set(subst.list(n.Ninit))
Russ Cox8c195bd2015-02-13 14:40:36 -05001176
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001177 if len(subst.retvars) != 0 && n.List.Len() != 0 {
Dave Cheney073d2482016-09-16 11:00:54 +10001178 as := nod(OAS2, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -05001179
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001180 // Make a shallow copy of retvars.
1181 // Otherwise OINLCALL.Rlist will be the same list,
1182 // and later walk and typecheck may clobber it.
1183 for _, n := range subst.retvars {
Ian Lance Taylorf444b8a2016-03-09 20:29:21 -08001184 as.List.Append(n)
Russ Cox8c195bd2015-02-13 14:40:36 -05001185 }
Ian Lance Taylore6ea0162016-03-10 11:49:20 -08001186 as.Rlist.Set(subst.list(n.List))
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -07001187 as = typecheck(as, Etop)
Ian Lance Taylor38921b32016-03-08 15:10:26 -08001188 m.Ninit.Append(as)
Russ Cox8c195bd2015-02-13 14:40:36 -05001189 }
1190
Josh Bleecher Snyderec7c4942016-03-19 17:02:01 -07001191 typecheckslice(m.Ninit.Slice(), Etop)
Josh Bleecher Snyder34699bc2016-03-20 08:03:31 -07001192 m = typecheck(m, Etop)
Russ Cox8c195bd2015-02-13 14:40:36 -05001193
1194 // dump("Return after substitution", m);
1195 return m
1196
Josh Bleecher Snyderb09925b2015-04-01 09:38:44 -07001197 case OGOTO, OLABEL:
Dave Cheney073d2482016-09-16 11:00:54 +10001198 m := nod(OXXX, nil, nil)
Russ Cox8c195bd2015-02-13 14:40:36 -05001199 *m = *n
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001200 m.Pos = subst.updatedPos(m.Pos)
Ian Lance Taylor38921b32016-03-08 15:10:26 -08001201 m.Ninit.Set(nil)
Russ Cox382b44e2015-02-23 16:07:24 -05001202 p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen)
Dave Cheneyd7012ca2016-09-15 15:45:10 +10001203 m.Left = newname(lookup(p))
Russ Cox8c195bd2015-02-13 14:40:36 -05001204
1205 return m
1206 }
David Lazar1c6ef9a2017-02-17 16:07:47 -05001207
1208 m := nod(OXXX, nil, nil)
1209 *m = *n
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001210 m.Pos = subst.updatedPos(m.Pos)
David Lazar1c6ef9a2017-02-17 16:07:47 -05001211 m.Ninit.Set(nil)
1212
1213 if n.Op == OCLOSURE {
1214 Fatalf("cannot inline function containing closure: %+v", n)
1215 }
1216
1217 m.Left = subst.node(n.Left)
1218 m.Right = subst.node(n.Right)
1219 m.List.Set(subst.list(n.List))
1220 m.Rlist.Set(subst.list(n.Rlist))
1221 m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...))
1222 m.Nbody.Set(subst.list(n.Nbody))
1223
1224 return m
Russ Cox8c195bd2015-02-13 14:40:36 -05001225}
1226
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001227func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
1228 pos := Ctxt.PosTable.Pos(xpos)
David Lazar699175a2017-02-17 12:28:05 -05001229 oldbase := pos.Base() // can be nil
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001230 newbase := subst.bases[oldbase]
David Lazar699175a2017-02-17 12:28:05 -05001231 if newbase == nil {
Matthew Dempsky7c8a9612017-09-18 23:02:02 -07001232 newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
1233 subst.bases[oldbase] = newbase
David Lazar699175a2017-02-17 12:28:05 -05001234 }
1235 pos.SetBase(newbase)
1236 return Ctxt.PosTable.XPos(pos)
Russ Cox8c195bd2015-02-13 14:40:36 -05001237}
Than McIntosh4435fcf2017-10-06 11:32:28 -04001238
1239func cmpNodeName(a, b *Node) bool {
1240 // named before artificial
1241 aart := 0
1242 if strings.HasPrefix(a.Sym.Name, "~r") {
1243 aart = 1
1244 }
1245 bart := 0
1246 if strings.HasPrefix(b.Sym.Name, "~r") {
1247 bart = 1
1248 }
1249 if aart != bart {
1250 return aart < bart
1251 }
1252
1253 // otherwise sort by name
1254 return a.Sym.Name < b.Sym.Name
1255}
1256
1257// byNodeName implements sort.Interface for []*Node using cmpNodeName.
1258type byNodeName []*Node
1259
1260func (s byNodeName) Len() int { return len(s) }
1261func (s byNodeName) Less(i, j int) bool { return cmpNodeName(s[i], s[j]) }
1262func (s byNodeName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }