Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 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 | // |
| 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 Smith | 58ec583 | 2016-02-06 20:35:29 +0900 | [diff] [blame] | 10 | // The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1, |
Matthew Dempsky | 7092a31 | 2017-10-20 15:20:56 -0700 | [diff] [blame] | 11 | // making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and |
| 12 | // are not supported. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 13 | // 0: disabled |
Damien Lespiau | 619e37b | 2017-04-16 00:10:19 +0100 | [diff] [blame] | 14 | // 1: 80-nodes leaf functions, oneliners, lazy typechecking (default) |
Matthew Dempsky | 7092a31 | 2017-10-20 15:20:56 -0700 | [diff] [blame] | 15 | // 2: (unassigned) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 16 | // 3: allow variadic functions |
Matthew Dempsky | 7092a31 | 2017-10-20 15:20:56 -0700 | [diff] [blame] | 17 | // 4: allow non-leaf functions |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 18 | // |
Matthew Dempsky | 7092a31 | 2017-10-20 15:20:56 -0700 | [diff] [blame] | 19 | // At some point this may get another default and become switch-offable with -N. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 20 | // |
Matthew Dempsky | 7092a31 | 2017-10-20 15:20:56 -0700 | [diff] [blame] | 21 | // 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 26 | // |
| 27 | // TODO: |
| 28 | // - inline functions with ... args |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 29 | |
| 30 | package gc |
| 31 | |
Robert Griesemer | 24597c0 | 2016-12-06 17:08:06 -0800 | [diff] [blame] | 32 | import ( |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 33 | "cmd/compile/internal/types" |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 34 | "cmd/internal/obj" |
Robert Griesemer | 24597c0 | 2016-12-06 17:08:06 -0800 | [diff] [blame] | 35 | "cmd/internal/src" |
| 36 | "fmt" |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 37 | "sort" |
| 38 | "strings" |
Robert Griesemer | 24597c0 | 2016-12-06 17:08:06 -0800 | [diff] [blame] | 39 | ) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 40 | |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 41 | // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 42 | // the ->sym can be re-used in the local package, so peel it off the receiver's type. |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 43 | func fnpkg(fn *Node) *types.Pkg { |
Josh Bleecher Snyder | 2e4dc86 | 2016-09-11 14:43:37 -0700 | [diff] [blame] | 44 | if fn.IsMethod() { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 45 | // method |
Matthew Dempsky | f91b832 | 2016-03-09 20:54:59 -0800 | [diff] [blame] | 46 | rcvr := fn.Type.Recv().Type |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 47 | |
Matthew Dempsky | e76fc1b | 2016-03-30 15:09:25 -0700 | [diff] [blame] | 48 | if rcvr.IsPtr() { |
Josh Bleecher Snyder | 8640b51 | 2016-03-30 10:57:47 -0700 | [diff] [blame] | 49 | rcvr = rcvr.Elem() |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 50 | } |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 51 | if rcvr.Sym == nil { |
Robert Griesemer | 6537e18 | 2016-09-09 21:08:46 -0700 | [diff] [blame] | 52 | Fatalf("receiver with no sym: [%v] %L (%v)", fn.Sym, fn, rcvr) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 53 | } |
| 54 | return rcvr.Sym.Pkg |
| 55 | } |
| 56 | |
| 57 | // non-method |
| 58 | return fn.Sym.Pkg |
| 59 | } |
| 60 | |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 61 | // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 62 | // because they're a copy of an already checked body. |
| 63 | func typecheckinl(fn *Node) { |
Robert Griesemer | 3a880ba | 2016-03-02 12:49:37 -0800 | [diff] [blame] | 64 | lno := setlineno(fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 65 | |
| 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 Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 70 | pkg := fnpkg(fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 71 | |
| 72 | if pkg == localpkg || pkg == nil { |
| 73 | return // typecheckinl on local function |
| 74 | } |
| 75 | |
Robert Griesemer | a9ea36a | 2016-03-18 17:21:32 -0700 | [diff] [blame] | 76 | if Debug['m'] > 2 || Debug_export != 0 { |
Robert Griesemer | 6537e18 | 2016-09-09 21:08:46 -0700 | [diff] [blame] | 77 | fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 78 | } |
| 79 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 80 | save_safemode := safemode |
Matthew Dempsky | 980ab12 | 2016-04-13 18:37:18 -0700 | [diff] [blame] | 81 | safemode = false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 82 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 83 | savefn := Curfn |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 84 | Curfn = fn |
Josh Bleecher Snyder | ec7c494 | 2016-03-19 17:02:01 -0700 | [diff] [blame] | 85 | typecheckslice(fn.Func.Inl.Slice(), Etop) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 86 | Curfn = savefn |
| 87 | |
| 88 | safemode = save_safemode |
| 89 | |
Robert Griesemer | 3a880ba | 2016-03-02 12:49:37 -0800 | [diff] [blame] | 90 | lineno = lno |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 91 | } |
| 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. |
| 96 | func caninl(fn *Node) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 97 | if fn.Op != ODCLFUNC { |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame] | 98 | Fatalf("caninl %v", fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 99 | } |
Russ Cox | bd4fff6 | 2015-05-27 10:42:55 -0400 | [diff] [blame] | 100 | if fn.Func.Nname == nil { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 101 | Fatalf("caninl no nname %+v", fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 102 | } |
| 103 | |
Josh Bleecher Snyder | e4cae432 | 2016-05-03 17:21:32 -0700 | [diff] [blame] | 104 | 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 Neal | a92543e | 2015-08-24 19:45:59 -0500 | [diff] [blame] | 113 | // If marked "go:noinline", don't inline |
Robert Griesemer | 3c0fae5 | 2016-02-26 13:32:28 -0800 | [diff] [blame] | 114 | if fn.Func.Pragma&Noinline != 0 { |
Josh Bleecher Snyder | e4cae432 | 2016-05-03 17:21:32 -0700 | [diff] [blame] | 115 | reason = "marked go:noinline" |
Todd Neal | a92543e | 2015-08-24 19:45:59 -0500 | [diff] [blame] | 116 | return |
| 117 | } |
| 118 | |
Austin Clements | 6b451ce | 2017-04-19 16:58:07 -0400 | [diff] [blame] | 119 | // If marked "go:cgo_unsafe_args", don't inline, since the |
| 120 | // function makes assumptions about its argument frame layout. |
David Lazar | 9fbfe7c | 2016-11-30 17:09:07 -0500 | [diff] [blame] | 121 | if fn.Func.Pragma&CgoUnsafeArgs != 0 { |
| 122 | reason = "marked go:cgo_unsafe_args" |
| 123 | return |
| 124 | } |
| 125 | |
Matthew Dempsky | 4e64ee4 | 2017-10-20 14:20:48 -0700 | [diff] [blame] | 126 | // 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 135 | // If fn has no body (is defined outside of Go), cannot inline it. |
Josh Bleecher Snyder | 1da62af | 2016-04-24 13:50:26 -0700 | [diff] [blame] | 136 | if fn.Nbody.Len() == 0 { |
Josh Bleecher Snyder | e4cae432 | 2016-05-03 17:21:32 -0700 | [diff] [blame] | 137 | reason = "no function body" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 138 | return |
| 139 | } |
| 140 | |
Josh Bleecher Snyder | 502a03f | 2017-04-25 18:02:43 -0700 | [diff] [blame] | 141 | if fn.Typecheck() == 0 { |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame] | 142 | Fatalf("caninl on non-typechecked function %v", fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 143 | } |
| 144 | |
| 145 | // can't handle ... args yet |
| 146 | if Debug['l'] < 3 { |
Josh Bleecher Snyder | 758431f | 2016-04-24 14:09:03 -0700 | [diff] [blame] | 147 | f := fn.Type.Params().Fields() |
| 148 | if len := f.Len(); len > 0 { |
Aliaksandr Valialkin | ed70f37 | 2017-02-27 19:56:38 +0200 | [diff] [blame] | 149 | if t := f.Index(len - 1); t.Isddd() { |
Josh Bleecher Snyder | e4cae432 | 2016-05-03 17:21:32 -0700 | [diff] [blame] | 150 | reason = "has ... args" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 151 | return |
| 152 | } |
| 153 | } |
| 154 | } |
| 155 | |
Ian Lance Taylor | 9e902f0 | 2015-10-20 10:00:07 -0700 | [diff] [blame] | 156 | // Runtime package must not be instrumented. |
| 157 | // Instrument skips runtime package. However, some runtime code can be |
Dmitry Vyukov | 08c4348 | 2015-04-09 10:08:29 +0300 | [diff] [blame] | 158 | // inlined into other packages and instrumented there. To avoid this, |
Ian Lance Taylor | 9e902f0 | 2015-10-20 10:00:07 -0700 | [diff] [blame] | 159 | // we disable inlining of runtime functions when instrumenting. |
Dmitry Vyukov | 08c4348 | 2015-04-09 10:08:29 +0300 | [diff] [blame] | 160 | // The example that we observed is inlining of LockOSThread, |
| 161 | // which lead to false race reports on m contents. |
Ian Lance Taylor | 9e902f0 | 2015-10-20 10:00:07 -0700 | [diff] [blame] | 162 | if instrumenting && myimportpath == "runtime" { |
Josh Bleecher Snyder | e4cae432 | 2016-05-03 17:21:32 -0700 | [diff] [blame] | 163 | reason = "instrumenting and is runtime function" |
Dmitry Vyukov | 08c4348 | 2015-04-09 10:08:29 +0300 | [diff] [blame] | 164 | return |
| 165 | } |
| 166 | |
Hugues Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 167 | n := fn.Func.Nname |
| 168 | if n.Func.InlinabilityChecked() { |
| 169 | return |
| 170 | } |
| 171 | defer n.Func.SetInlinabilityChecked(true) |
| 172 | |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 173 | const maxBudget = 80 |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 174 | visitor := hairyVisitor{budget: maxBudget} |
| 175 | if visitor.visitList(fn.Nbody) { |
| 176 | reason = visitor.reason |
Josh Bleecher Snyder | e4cae432 | 2016-05-03 17:21:32 -0700 | [diff] [blame] | 177 | return |
| 178 | } |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 179 | if visitor.budget < 0 { |
Ilya Tocar | 475df0e | 2017-09-12 13:53:55 -0500 | [diff] [blame] | 180 | reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", maxBudget-visitor.budget, maxBudget) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 181 | return |
| 182 | } |
| 183 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 184 | savefn := Curfn |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 185 | Curfn = fn |
| 186 | |
Josh Bleecher Snyder | 758431f | 2016-04-24 14:09:03 -0700 | [diff] [blame] | 187 | 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 Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 191 | n.Func.InlCost = maxBudget - visitor.budget |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 192 | |
| 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 Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 195 | fn.Type.FuncType().Nname = asTypesNode(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 196 | |
| 197 | if Debug['m'] > 1 { |
Robert Griesemer | 266c622 | 2016-08-31 16:19:50 -0700 | [diff] [blame] | 198 | fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 199 | } else if Debug['m'] != 0 { |
Josh Bleecher Snyder | 758431f | 2016-04-24 14:09:03 -0700 | [diff] [blame] | 200 | fmt.Printf("%v: can inline %v\n", fn.Line(), n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 201 | } |
| 202 | |
| 203 | Curfn = savefn |
| 204 | } |
| 205 | |
Matthew Dempsky | 8684534 | 2017-10-27 15:36:59 -0700 | [diff] [blame] | 206 | // inlFlood marks n's inline body for export and recursively ensures |
| 207 | // all called functions are marked too. |
| 208 | func 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 Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 243 | // hairyVisitor visits a function body to determine its inlining |
| 244 | // hairiness and whether or not it can be inlined. |
| 245 | type hairyVisitor struct { |
| 246 | budget int32 |
| 247 | reason string |
| 248 | } |
| 249 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 250 | // Look for anything we want to punt on. |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 251 | func (v *hairyVisitor) visitList(ll Nodes) bool { |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 252 | for _, n := range ll.Slice() { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 253 | if v.visit(n) { |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 254 | return true |
| 255 | } |
| 256 | } |
| 257 | return false |
| 258 | } |
| 259 | |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 260 | func (v *hairyVisitor) visit(n *Node) bool { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 261 | if n == nil { |
| 262 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 263 | } |
| 264 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 265 | switch n.Op { |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 266 | // Call is okay if inlinable and we have the budget for the body. |
| 267 | case OCALLFUNC: |
Keith Randall | 8a9dc05 | 2016-12-09 16:59:38 -0800 | [diff] [blame] | 268 | if isIntrinsicCall(n) { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 269 | v.budget-- |
Keith Randall | 8a9dc05 | 2016-12-09 16:59:38 -0800 | [diff] [blame] | 270 | break |
| 271 | } |
David Lazar | 2397cd0 | 2017-04-19 12:57:52 -0400 | [diff] [blame] | 272 | // 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 Snyder | 386765a | 2017-04-25 18:14:12 -0700 | [diff] [blame] | 274 | if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) { |
David Lazar | 2397cd0 | 2017-04-19 12:57:52 -0400 | [diff] [blame] | 275 | fn := n.Left.Sym.Name |
| 276 | if fn == "getcallerpc" || fn == "getcallersp" { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 277 | v.reason = "call to " + fn |
David Lazar | 2397cd0 | 2017-04-19 12:57:52 -0400 | [diff] [blame] | 278 | return true |
| 279 | } |
| 280 | } |
| 281 | |
Keith Randall | 495b167 | 2017-03-16 14:08:31 -0700 | [diff] [blame] | 282 | if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 283 | v.budget -= fn.InlCost |
Keith Randall | 495b167 | 2017-03-16 14:08:31 -0700 | [diff] [blame] | 284 | break |
| 285 | } |
Hugues Bruant | c4b65fa | 2017-10-21 15:58:37 -0700 | [diff] [blame] | 286 | 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 Cox | b6dc3e6 | 2016-05-25 01:33:24 -0400 | [diff] [blame] | 306 | |
Matthew Dempsky | fcd3288 | 2017-10-24 14:45:41 -0700 | [diff] [blame] | 307 | if n.Left.isMethodExpression() { |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 308 | if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 309 | v.budget -= d.Func.InlCost |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 310 | break |
| 311 | } |
| 312 | } |
| 313 | if Debug['l'] < 4 { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 314 | v.reason = "non-leaf function" |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 315 | return true |
| 316 | } |
| 317 | |
| 318 | // Call is okay if inlinable and we have the budget for the body. |
| 319 | case OCALLMETH: |
Josh Bleecher Snyder | 758431f | 2016-04-24 14:09:03 -0700 | [diff] [blame] | 320 | t := n.Left.Type |
| 321 | if t == nil { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 322 | Fatalf("no function type for [%p] %+v\n", n.Left, n.Left) |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 323 | } |
Josh Bleecher Snyder | 758431f | 2016-04-24 14:09:03 -0700 | [diff] [blame] | 324 | if t.Nname() == nil { |
Robert Griesemer | 8d0bbe2 | 2016-08-31 10:32:40 -0700 | [diff] [blame] | 325 | Fatalf("no function definition for [%p] %+v\n", t, t) |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 326 | } |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 327 | if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl.Len() != 0 { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 328 | v.budget -= inlfn.InlCost |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 329 | break |
| 330 | } |
| 331 | if Debug['l'] < 4 { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 332 | v.reason = "non-leaf method" |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 333 | return true |
| 334 | } |
| 335 | |
| 336 | // Things that are too hairy, irrespective of the budget |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 337 | case OCALL, OCALLINTER, OPANIC, ORECOVER: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 338 | if Debug['l'] < 4 { |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 339 | v.reason = "non-leaf op " + n.Op.String() |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 340 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 341 | } |
| 342 | |
| 343 | case OCLOSURE, |
| 344 | OCALLPART, |
| 345 | ORANGE, |
| 346 | OFOR, |
David Chase | d71f36b | 2017-02-02 11:53:41 -0500 | [diff] [blame] | 347 | OFORUNTIL, |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 348 | OSELECT, |
Todd Neal | e41f527 | 2016-03-16 21:29:17 -0500 | [diff] [blame] | 349 | OTYPESW, |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 350 | OPROC, |
| 351 | ODEFER, |
Todd Neal | fc6bcde | 2016-03-16 18:44:17 -0500 | [diff] [blame] | 352 | ODCLTYPE, // can't print yet |
Todd Neal | e41f527 | 2016-03-16 21:29:17 -0500 | [diff] [blame] | 353 | OBREAK, |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 354 | ORETJMP: |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 355 | v.reason = "unhandled op " + n.Op.String() |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 356 | return true |
Mark Pulford | 812b34e | 2017-09-03 23:53:38 +1000 | [diff] [blame] | 357 | |
| 358 | case ODCLCONST, OEMPTY, OFALL, OLABEL: |
| 359 | // These nodes don't produce code; omit from inlining budget. |
| 360 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 361 | } |
| 362 | |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 363 | v.budget-- |
Josh Bleecher Snyder | d6dbf3a | 2016-04-21 19:35:26 -0700 | [diff] [blame] | 364 | // 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 Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 368 | v.budget-- |
Josh Bleecher Snyder | d6dbf3a | 2016-04-21 19:35:26 -0700 | [diff] [blame] | 369 | case OSLICE, OSLICEARR, OSLICESTR: |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 370 | v.budget-- |
Josh Bleecher Snyder | d6dbf3a | 2016-04-21 19:35:26 -0700 | [diff] [blame] | 371 | case OSLICE3, OSLICE3ARR: |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 372 | v.budget -= 2 |
Joe Tsai | f0e347b | 2016-10-21 19:43:33 +0000 | [diff] [blame] | 373 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 374 | |
Ilya Tocar | 475df0e | 2017-09-12 13:53:55 -0500 | [diff] [blame] | 375 | // When debugging, don't stop early, to get full cost of inlining this function |
| 376 | if v.budget < 0 && Debug['m'] < 2 { |
Josh Bleecher Snyder | a246f61 | 2016-11-08 22:18:38 -0800 | [diff] [blame] | 377 | return true |
| 378 | } |
| 379 | |
Austin Clements | e52d317 | 2017-04-19 16:32:41 -0400 | [diff] [blame] | 380 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 383 | } |
| 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 Taylor | c4012b6 | 2016-03-08 10:26:20 -0800 | [diff] [blame] | 388 | func inlcopylist(ll []*Node) []*Node { |
Ian Lance Taylor | f444b8a | 2016-03-09 20:29:21 -0800 | [diff] [blame] | 389 | s := make([]*Node, 0, len(ll)) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 390 | for _, n := range ll { |
| 391 | s = append(s, inlcopy(n)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 392 | } |
Ian Lance Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 393 | return s |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 394 | } |
| 395 | |
| 396 | func inlcopy(n *Node) *Node { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 397 | if n == nil { |
| 398 | return nil |
| 399 | } |
| 400 | |
| 401 | switch n.Op { |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 402 | case ONAME, OTYPE, OLITERAL: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 403 | return n |
| 404 | } |
| 405 | |
Dave Cheney | edca4cd | 2016-03-23 16:01:15 +1100 | [diff] [blame] | 406 | m := *n |
Josh Bleecher Snyder | 57279ba | 2015-03-10 21:37:13 -0700 | [diff] [blame] | 407 | if m.Func != nil { |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 408 | m.Func.Inl.Set(nil) |
Josh Bleecher Snyder | 57279ba | 2015-03-10 21:37:13 -0700 | [diff] [blame] | 409 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 410 | m.Left = inlcopy(n.Left) |
| 411 | m.Right = inlcopy(n.Right) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 412 | 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 Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 415 | m.Nbody.Set(inlcopylist(n.Nbody.Slice())) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 416 | |
Dave Cheney | edca4cd | 2016-03-23 16:01:15 +1100 | [diff] [blame] | 417 | return &m |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 418 | } |
| 419 | |
| 420 | // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 421 | // calls made to inlineable functions. This is the external entry point. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 422 | func inlcalls(fn *Node) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 423 | savefn := Curfn |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 424 | Curfn = fn |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 425 | fn = inlnode(fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 426 | if fn != Curfn { |
Håvard Haugen | 3c9fa38 | 2015-08-30 23:10:03 +0200 | [diff] [blame] | 427 | Fatalf("inlnode replaced curfn") |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 428 | } |
| 429 | Curfn = savefn |
| 430 | } |
| 431 | |
| 432 | // Turn an OINLCALL into a statement. |
| 433 | func inlconv2stmt(n *Node) { |
| 434 | n.Op = OBLOCK |
| 435 | |
| 436 | // n->ninit stays |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 437 | n.List.Set(n.Nbody.Slice()) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 438 | |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 439 | n.Nbody.Set(nil) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 440 | n.Rlist.Set(nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 441 | } |
| 442 | |
| 443 | // Turn an OINLCALL into a single valued expression. |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 444 | // The result of inlconv2expr MUST be assigned back to n, e.g. |
| 445 | // n.Left = inlconv2expr(n.Left) |
| 446 | func inlconv2expr(n *Node) *Node { |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 447 | r := n.Rlist.First() |
Josh Bleecher Snyder | 7e8e9ab | 2016-03-23 08:51:38 -0700 | [diff] [blame] | 448 | return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 449 | } |
| 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 Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 456 | func inlconv2list(n *Node) []*Node { |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 457 | if n.Op != OINLCALL || n.Rlist.Len() == 0 { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 458 | Fatalf("inlconv2list %+v\n", n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 459 | } |
| 460 | |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 461 | s := n.Rlist.Slice() |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 462 | s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...)) |
Ian Lance Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 463 | return s |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 464 | } |
| 465 | |
Ian Lance Taylor | c4012b6 | 2016-03-08 10:26:20 -0800 | [diff] [blame] | 466 | func inlnodelist(l Nodes) { |
Ian Lance Taylor | cd6619d | 2016-03-09 12:39:36 -0800 | [diff] [blame] | 467 | s := l.Slice() |
| 468 | for i := range s { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 469 | s[i] = inlnode(s[i]) |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 470 | } |
| 471 | } |
| 472 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 473 | // inlnode recurses over the tree to find inlineable calls, which will |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 474 | // be turned into OINLCALLs by mkinlcall. When the recursion comes |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 475 | // 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 Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 484 | // The result of inlnode MUST be assigned back to n, e.g. |
| 485 | // n.Left = inlnode(n.Left) |
| 486 | func inlnode(n *Node) *Node { |
| 487 | if n == nil { |
| 488 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 489 | } |
| 490 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 491 | switch n.Op { |
| 492 | // inhibit inlining of their argument |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 493 | case ODEFER, OPROC: |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 494 | switch n.Left.Op { |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 495 | case OCALLFUNC, OCALLMETH: |
Aliaksandr Valialkin | ed70f37 | 2017-02-27 19:56:38 +0200 | [diff] [blame] | 496 | n.Left.SetNoInline(true) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 497 | } |
Josh Bleecher Snyder | 0df81e8 | 2017-02-27 10:45:26 -0800 | [diff] [blame] | 498 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 499 | |
Alberto Donizetti | 590f3f0 | 2016-09-24 21:38:58 +0200 | [diff] [blame] | 500 | // TODO do them here (or earlier), |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 501 | // so escape analysis can avoid more heapmoves. |
| 502 | case OCLOSURE: |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 503 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 504 | } |
| 505 | |
Robert Griesemer | 3a880ba | 2016-03-02 12:49:37 -0800 | [diff] [blame] | 506 | lno := setlineno(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 507 | |
| 508 | inlnodelist(n.Ninit) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 509 | for _, n1 := range n.Ninit.Slice() { |
| 510 | if n1.Op == OINLCALL { |
| 511 | inlconv2stmt(n1) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 512 | } |
| 513 | } |
| 514 | |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 515 | n.Left = inlnode(n.Left) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 516 | if n.Left != nil && n.Left.Op == OINLCALL { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 517 | n.Left = inlconv2expr(n.Left) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 518 | } |
| 519 | |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 520 | n.Right = inlnode(n.Right) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 521 | if n.Right != nil && n.Right.Op == OINLCALL { |
David Chase | d71f36b | 2017-02-02 11:53:41 -0500 | [diff] [blame] | 522 | if n.Op == OFOR || n.Op == OFORUNTIL { |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 523 | inlconv2stmt(n.Right) |
| 524 | } else { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 525 | n.Right = inlconv2expr(n.Right) |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 526 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 527 | } |
| 528 | |
| 529 | inlnodelist(n.List) |
| 530 | switch n.Op { |
| 531 | case OBLOCK: |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 532 | for _, n2 := range n.List.Slice() { |
| 533 | if n2.Op == OINLCALL { |
| 534 | inlconv2stmt(n2) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 535 | } |
| 536 | } |
| 537 | |
Josh Bleecher Snyder | 0df81e8 | 2017-02-27 10:45:26 -0800 | [diff] [blame] | 538 | 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 Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 541 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 543 | break |
| 544 | } |
| 545 | fallthrough |
| 546 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 547 | default: |
Ian Lance Taylor | cd6619d | 2016-03-09 12:39:36 -0800 | [diff] [blame] | 548 | s := n.List.Slice() |
| 549 | for i1, n1 := range s { |
Josh Bleecher Snyder | d6dbf3a | 2016-04-21 19:35:26 -0700 | [diff] [blame] | 550 | if n1 != nil && n1.Op == OINLCALL { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 551 | s[i1] = inlconv2expr(s[i1]) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 552 | } |
| 553 | } |
| 554 | } |
| 555 | |
| 556 | inlnodelist(n.Rlist) |
Josh Bleecher Snyder | 0df81e8 | 2017-02-27 10:45:26 -0800 | [diff] [blame] | 557 | if n.Op == OAS2FUNC && n.Rlist.First().Op == OINLCALL { |
| 558 | n.Rlist.Set(inlconv2list(n.Rlist.First())) |
| 559 | n.Op = OAS2 |
Josh Bleecher Snyder | 502a03f | 2017-04-25 18:02:43 -0700 | [diff] [blame] | 560 | n.SetTypecheck(0) |
Josh Bleecher Snyder | 0df81e8 | 2017-02-27 10:45:26 -0800 | [diff] [blame] | 561 | n = typecheck(n, Etop) |
| 562 | } else { |
Ian Lance Taylor | cd6619d | 2016-03-09 12:39:36 -0800 | [diff] [blame] | 563 | s := n.Rlist.Slice() |
| 564 | for i1, n1 := range s { |
| 565 | if n1.Op == OINLCALL { |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 566 | if n.Op == OIF { |
Ian Lance Taylor | cd6619d | 2016-03-09 12:39:36 -0800 | [diff] [blame] | 567 | inlconv2stmt(n1) |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 568 | } else { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 569 | s[i1] = inlconv2expr(s[i1]) |
Russ Cox | ffef180 | 2015-05-22 01:16:52 -0400 | [diff] [blame] | 570 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 571 | } |
| 572 | } |
| 573 | } |
| 574 | |
Ian Lance Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 575 | inlnodelist(n.Nbody) |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 576 | for _, n := range n.Nbody.Slice() { |
| 577 | if n.Op == OINLCALL { |
| 578 | inlconv2stmt(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 579 | } |
| 580 | } |
| 581 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 582 | // 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 Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 586 | case OCALLFUNC, OCALLMETH: |
Aliaksandr Valialkin | ed70f37 | 2017-02-27 19:56:38 +0200 | [diff] [blame] | 587 | if n.NoInline() { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 588 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 589 | } |
| 590 | } |
| 591 | |
| 592 | switch n.Op { |
| 593 | case OCALLFUNC: |
| 594 | if Debug['m'] > 3 { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 595 | fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 596 | } |
Keith Randall | 320ddcf | 2016-08-23 16:49:28 -0700 | [diff] [blame] | 597 | if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case |
Aliaksandr Valialkin | ed70f37 | 2017-02-27 19:56:38 +0200 | [diff] [blame] | 598 | n = mkinlcall(n, n.Left, n.Isddd()) |
Matthew Dempsky | fcd3288 | 2017-10-24 14:45:41 -0700 | [diff] [blame] | 599 | } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil { |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 600 | n = mkinlcall(n, asNode(n.Left.Sym.Def), n.Isddd()) |
Hugues Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 601 | } 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 632 | } |
| 633 | |
| 634 | case OCALLMETH: |
| 635 | if Debug['m'] > 3 { |
Robert Griesemer | 6537e18 | 2016-09-09 21:08:46 -0700 | [diff] [blame] | 636 | fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 637 | } |
| 638 | |
| 639 | // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. |
| 640 | if n.Left.Type == nil { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 641 | Fatalf("no function type for [%p] %+v\n", n.Left, n.Left) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 642 | } |
| 643 | |
Josh Bleecher Snyder | b83618f | 2016-03-30 16:59:53 -0700 | [diff] [blame] | 644 | if n.Left.Type.Nname() == nil { |
Robert Griesemer | 8d0bbe2 | 2016-08-31 10:32:40 -0700 | [diff] [blame] | 645 | Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 646 | } |
| 647 | |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 648 | n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), n.Isddd()) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 649 | } |
| 650 | |
Robert Griesemer | 3a880ba | 2016-03-02 12:49:37 -0800 | [diff] [blame] | 651 | lineno = lno |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 652 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 653 | } |
| 654 | |
Hugues Bruant | c4b65fa | 2017-10-21 15:58:37 -0700 | [diff] [blame] | 655 | // 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 Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 657 | func inlinableClosure(n *Node) *Node { |
| 658 | c := n.Func.Closure |
| 659 | caninl(c) |
| 660 | f := c.Func.Nname |
Hugues Bruant | c4b65fa | 2017-10-21 15:58:37 -0700 | [diff] [blame] | 661 | if f == nil || f.Func.Inl.Len() == 0 { |
| 662 | return nil |
Hugues Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 663 | } |
Hugues Bruant | c4b65fa | 2017-10-21 15:58:37 -0700 | [diff] [blame] | 664 | return f |
Hugues Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 665 | } |
| 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? |
| 673 | func 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 Bruant | 483e298 | 2017-11-02 19:54:46 -0700 | [diff] [blame] | 681 | 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 Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 690 | v := reassignVisitor{name: n} |
Hugues Bruant | 483e298 | 2017-11-02 19:54:46 -0700 | [diff] [blame] | 691 | a := v.visitList(f.Nbody) |
Hugues Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 692 | return a != nil, a |
| 693 | } |
| 694 | |
| 695 | type reassignVisitor struct { |
| 696 | name *Node |
| 697 | } |
| 698 | |
| 699 | func (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 Bruant | 483e298 | 2017-11-02 19:54:46 -0700 | [diff] [blame] | 709 | case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE: |
Hugues Bruant | 4f70a2a | 2017-09-18 14:54:10 -0700 | [diff] [blame] | 710 | 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 | |
| 738 | func (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 Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 747 | // The result of mkinlcall MUST be assigned back to n, e.g. |
| 748 | // n.Left = mkinlcall(n.Left, fn, isddd) |
| 749 | func mkinlcall(n *Node, fn *Node, isddd bool) *Node { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 750 | save_safemode := safemode |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 751 | |
| 752 | // imported functions may refer to unsafe as long as the |
| 753 | // package was marked safe during import (already checked). |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 754 | pkg := fnpkg(fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 755 | |
| 756 | if pkg != localpkg && pkg != nil { |
Matthew Dempsky | 980ab12 | 2016-04-13 18:37:18 -0700 | [diff] [blame] | 757 | safemode = false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 758 | } |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 759 | n = mkinlcall1(n, fn, isddd) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 760 | safemode = save_safemode |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 761 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 762 | } |
| 763 | |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 764 | func 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 Snyder | dc5f931 | 2016-10-26 22:58:50 -0700 | [diff] [blame] | 767 | if inlvar == nil { |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 768 | Fatalf("missing inlvar for %v\n", asNode(t.Nname)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 769 | } |
Josh Bleecher Snyder | dc5f931 | 2016-10-26 22:58:50 -0700 | [diff] [blame] | 770 | return inlvar |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 771 | } |
| 772 | |
Josh Bleecher Snyder | 7e8e9ab | 2016-03-23 08:51:38 -0700 | [diff] [blame] | 773 | return typecheck(nblank, Erv|Easgn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 774 | } |
| 775 | |
| 776 | var inlgen int |
| 777 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 778 | // If n is a call, and fn is a function with an inlinable body, |
| 779 | // return an OINLCALL. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 780 | // 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 Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 783 | // The result of mkinlcall1 MUST be assigned back to n, e.g. |
| 784 | // n.Left = mkinlcall1(n.Left, fn, isddd) |
Matthew Dempsky | b80029c | 2017-10-09 11:06:52 -0700 | [diff] [blame] | 785 | func mkinlcall1(n, fn *Node, isddd bool) *Node { |
Josh Bleecher Snyder | 1da62af | 2016-04-24 13:50:26 -0700 | [diff] [blame] | 786 | if fn.Func.Inl.Len() == 0 { |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 787 | // No inlinable body. |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 788 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 789 | } |
| 790 | |
Russ Cox | 4fdd536 | 2015-05-26 22:19:27 -0400 | [diff] [blame] | 791 | if fn == Curfn || fn.Name.Defn == Curfn { |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 792 | // Can't recursively inline a function into itself. |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 793 | return n |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 794 | } |
| 795 | |
Matthew Dempsky | 7092a31 | 2017-10-20 15:20:56 -0700 | [diff] [blame] | 796 | if Debug_typecheckinl == 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 797 | typecheckinl(fn) |
| 798 | } |
| 799 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 800 | // We have a function node, and it has an inlineable body. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 801 | if Debug['m'] > 1 { |
Robert Griesemer | 266c622 | 2016-08-31 16:19:50 -0700 | [diff] [blame] | 802 | fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 803 | } else if Debug['m'] != 0 { |
Russ Cox | 17228f4 | 2015-04-17 12:03:22 -0400 | [diff] [blame] | 804 | fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 805 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 806 | if Debug['m'] > 2 { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 807 | fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 808 | } |
| 809 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 810 | ninit := n.Ninit |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 811 | |
Hugues Bruant | c4b65fa | 2017-10-21 15:58:37 -0700 | [diff] [blame] | 812 | // Make temp names to use instead of the originals. |
| 813 | inlvars := make(map[*Node]*Node) |
| 814 | |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 815 | // record formals/locals for later post-processing |
| 816 | var inlfvars []*Node |
| 817 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 818 | // Find declarations corresponding to inlineable body. |
Ian Lance Taylor | b66a892 | 2016-02-25 10:35:19 -0800 | [diff] [blame] | 819 | var dcl []*Node |
Ian Lance Taylor | 6abc8c9 | 2016-02-26 13:48:24 -0800 | [diff] [blame] | 820 | if fn.Name.Defn != nil { |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 821 | dcl = fn.Func.Inldcl.Slice() // local function |
Hugues Bruant | c4b65fa | 2017-10-21 15:58:37 -0700 | [diff] [blame] | 822 | |
| 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 858 | } else { |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 859 | dcl = fn.Func.Dcl // imported function |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 860 | } |
| 861 | |
Ian Lance Taylor | b66a892 | 2016-02-25 10:35:19 -0800 | [diff] [blame] | 862 | for _, ln := range dcl { |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 863 | if ln.Op != ONAME { |
| 864 | continue |
| 865 | } |
Josh Bleecher Snyder | 386765a | 2017-04-25 18:14:12 -0700 | [diff] [blame] | 866 | if ln.Class() == PPARAMOUT { // return values handled below. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 867 | continue |
| 868 | } |
Russ Cox | b6dc3e6 | 2016-05-25 01:33:24 -0400 | [diff] [blame] | 869 | if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap |
| 870 | continue |
| 871 | } |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 872 | inlvars[ln] = typecheck(inlvar(ln), Erv) |
Josh Bleecher Snyder | 386765a | 2017-04-25 18:14:12 -0700 | [diff] [blame] | 873 | if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM { |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 874 | ninit.Append(nod(ODCL, inlvars[ln], nil)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 875 | } |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 876 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 886 | } |
| 887 | |
| 888 | // temporaries for return values. |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 889 | var retvars []*Node |
| 890 | for i, t := range fn.Type.Results().Fields().Slice() { |
| 891 | var m *Node |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 892 | var mpos src.XPos |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 893 | if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) { |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 894 | mpos = asNode(t.Nname).Pos |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 895 | m = inlvar(asNode(t.Nname)) |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 896 | m = typecheck(m, Erv) |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 897 | inlvars[asNode(t.Nname)] = m |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 898 | } else { |
| 899 | // anonymous return values, synthesize names for use in assignment that replaces return |
| 900 | m = retvar(t, i) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 901 | } |
| 902 | |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 903 | 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 Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 914 | ninit.Append(nod(ODCL, m, nil)) |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 915 | retvars = append(retvars, m) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 916 | } |
| 917 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 918 | // Assign arguments to the parameters' temp names. |
Dave Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 919 | as := nod(OAS2, nil, nil) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 920 | as.Rlist.Set(n.List.Slice()) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 921 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 922 | // For non-dotted calls to variadic functions, we assign the |
| 923 | // variadic parameter's temp name separately. |
| 924 | var vas *Node |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 925 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 926 | if fn.IsMethod() { |
| 927 | rcv := fn.Type.Recv() |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 928 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 929 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 946 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 947 | } |
| 948 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 949 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 956 | } |
| 957 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 958 | // Otherwise, we need to collect the remaining values |
| 959 | // to pass as a slice. |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 960 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 961 | numvals := n.List.Len() |
| 962 | if numvals == 1 && n.List.First().Type.IsFuncArgStruct() { |
| 963 | numvals = n.List.First().Type.NumFields() |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 964 | } |
| 965 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 966 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 979 | } |
| 980 | } |
| 981 | |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 982 | if as.Rlist.Len() != 0 { |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 983 | as = typecheck(as, Etop) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 984 | ninit.Append(as) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 985 | } |
| 986 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 987 | if vas != nil { |
| 988 | vas = typecheck(vas, Etop) |
| 989 | ninit.Append(vas) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 990 | } |
| 991 | |
Matthew Dempsky | ce9bef2 | 2017-04-07 13:47:10 -0700 | [diff] [blame] | 992 | // Zero the return parameters. |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 993 | for _, n := range retvars { |
David Lazar | b928e2f | 2017-05-27 17:43:37 -0400 | [diff] [blame] | 994 | ras := nod(OAS, n, nil) |
| 995 | ras = typecheck(ras, Etop) |
| 996 | ninit.Append(ras) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 997 | } |
| 998 | |
Josh Bleecher Snyder | fe27291 | 2016-08-15 21:09:39 -0700 | [diff] [blame] | 999 | retlabel := autolabel(".i") |
Josh Bleecher Snyder | a9ed477 | 2016-06-01 10:15:02 -0700 | [diff] [blame] | 1000 | retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps) |
| 1001 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1002 | inlgen++ |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1003 | |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1004 | parent := -1 |
| 1005 | if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil { |
| 1006 | parent = b.InliningIndex() |
| 1007 | } |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 1008 | sort.Sort(byNodeName(dcl)) |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1009 | newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym()) |
| 1010 | |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 1011 | 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 Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1018 | subst := inlsubst{ |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1019 | retlabel: retlabel, |
| 1020 | retvars: retvars, |
| 1021 | inlvars: inlvars, |
| 1022 | bases: make(map[*src.PosBase]*src.PosBase), |
| 1023 | newInlIndex: newIndex, |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1024 | } |
| 1025 | |
| 1026 | body := subst.list(fn.Func.Inl) |
| 1027 | |
Dave Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 1028 | lab := nod(OLABEL, retlabel, nil) |
Josh Bleecher Snyder | d1faf38 | 2016-05-27 15:33:11 -0700 | [diff] [blame] | 1029 | body = append(body, lab) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1030 | |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 1031 | typecheckslice(body, Etop) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1032 | |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 1033 | if genDwarfInline > 0 { |
| 1034 | for _, v := range inlfvars { |
| 1035 | v.Pos = subst.updatedPos(v.Pos) |
| 1036 | } |
| 1037 | } |
| 1038 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1039 | //dumplist("ninit post", ninit); |
| 1040 | |
Dave Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 1041 | call := nod(OINLCALL, nil, nil) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 1042 | call.Ninit.Set(ninit.Slice()) |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 1043 | call.Nbody.Set(body) |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1044 | call.Rlist.Set(retvars) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1045 | call.Type = n.Type |
Josh Bleecher Snyder | 502a03f | 2017-04-25 18:02:43 -0700 | [diff] [blame] | 1046 | call.SetTypecheck(1) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1047 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1048 | // transitive inlining |
Russ Cox | 77ccb16 | 2015-02-24 12:19:01 -0500 | [diff] [blame] | 1049 | // 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 Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 1054 | inlnodelist(call.Nbody) |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 1055 | for _, n := range call.Nbody.Slice() { |
| 1056 | if n.Op == OINLCALL { |
| 1057 | inlconv2stmt(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1058 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1059 | } |
| 1060 | |
| 1061 | if Debug['m'] > 2 { |
Matthew Dempsky | b80029c | 2017-10-09 11:06:52 -0700 | [diff] [blame] | 1062 | fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1063 | } |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 1064 | |
Matthew Dempsky | b80029c | 2017-10-09 11:06:52 -0700 | [diff] [blame] | 1065 | return call |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1066 | } |
| 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. |
| 1071 | func inlvar(var_ *Node) *Node { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1072 | if Debug['m'] > 3 { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 1073 | fmt.Printf("inlvar %+v\n", var_) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1074 | } |
| 1075 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 1076 | n := newname(var_.Sym) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1077 | n.Type = var_.Type |
Josh Bleecher Snyder | 386765a | 2017-04-25 18:14:12 -0700 | [diff] [blame] | 1078 | n.SetClass(PAUTO) |
Josh Bleecher Snyder | fc08a19 | 2017-04-27 15:17:57 -0700 | [diff] [blame] | 1079 | n.Name.SetUsed(true) |
Russ Cox | fd2154f | 2015-05-27 07:31:56 -0400 | [diff] [blame] | 1080 | n.Name.Curfn = Curfn // the calling function, not the called one |
Aliaksandr Valialkin | ed70f37 | 2017-02-27 19:56:38 +0200 | [diff] [blame] | 1081 | n.SetAddrtaken(var_.Addrtaken()) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1082 | |
Ian Lance Taylor | b66a892 | 2016-02-25 10:35:19 -0800 | [diff] [blame] | 1083 | Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1084 | return n |
| 1085 | } |
| 1086 | |
| 1087 | // Synthesize a variable to store the inlined function's results in. |
Robert Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 1088 | func retvar(t *types.Field, i int) *Node { |
Dave Cheney | d7012ca | 2016-09-15 15:45:10 +1000 | [diff] [blame] | 1089 | n := newname(lookupN("~r", i)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1090 | n.Type = t.Type |
Josh Bleecher Snyder | 386765a | 2017-04-25 18:14:12 -0700 | [diff] [blame] | 1091 | n.SetClass(PAUTO) |
Josh Bleecher Snyder | fc08a19 | 2017-04-27 15:17:57 -0700 | [diff] [blame] | 1092 | n.Name.SetUsed(true) |
Russ Cox | fd2154f | 2015-05-27 07:31:56 -0400 | [diff] [blame] | 1093 | n.Name.Curfn = Curfn // the calling function, not the called one |
Ian Lance Taylor | b66a892 | 2016-02-25 10:35:19 -0800 | [diff] [blame] | 1094 | Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1095 | 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 Griesemer | f68f292 | 2017-04-04 17:54:02 -0700 | [diff] [blame] | 1100 | func argvar(t *types.Type, i int) *Node { |
Dave Cheney | d7012ca | 2016-09-15 15:45:10 +1000 | [diff] [blame] | 1101 | n := newname(lookupN("~arg", i)) |
Josh Bleecher Snyder | 8640b51 | 2016-03-30 10:57:47 -0700 | [diff] [blame] | 1102 | n.Type = t.Elem() |
Josh Bleecher Snyder | 386765a | 2017-04-25 18:14:12 -0700 | [diff] [blame] | 1103 | n.SetClass(PAUTO) |
Josh Bleecher Snyder | fc08a19 | 2017-04-27 15:17:57 -0700 | [diff] [blame] | 1104 | n.Name.SetUsed(true) |
Russ Cox | fd2154f | 2015-05-27 07:31:56 -0400 | [diff] [blame] | 1105 | n.Name.Curfn = Curfn // the calling function, not the called one |
Ian Lance Taylor | b66a892 | 2016-02-25 10:35:19 -0800 | [diff] [blame] | 1106 | Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1107 | return n |
| 1108 | } |
| 1109 | |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1110 | // The inlsubst type implements the actual inlining of a single |
| 1111 | // function call. |
| 1112 | type 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 Snyder | dc5f931 | 2016-10-26 22:58:50 -0700 | [diff] [blame] | 1118 | |
| 1119 | inlvars map[*Node]*Node |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1120 | |
| 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 Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1128 | } |
| 1129 | |
| 1130 | // list inlines a list of nodes. |
| 1131 | func (subst *inlsubst) list(ll Nodes) []*Node { |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 1132 | s := make([]*Node, 0, ll.Len()) |
| 1133 | for _, n := range ll.Slice() { |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1134 | s = append(s, subst.node(n)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1135 | } |
Ian Lance Taylor | 99b6b77 | 2016-03-04 09:37:58 -0800 | [diff] [blame] | 1136 | return s |
Ian Lance Taylor | 1d5001a | 2016-02-27 14:31:33 -0800 | [diff] [blame] | 1137 | } |
| 1138 | |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1139 | // 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. |
| 1143 | func (subst *inlsubst) node(n *Node) *Node { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1144 | if n == nil { |
| 1145 | return nil |
| 1146 | } |
| 1147 | |
| 1148 | switch n.Op { |
| 1149 | case ONAME: |
Josh Bleecher Snyder | dc5f931 | 2016-10-26 22:58:50 -0700 | [diff] [blame] | 1150 | if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1151 | if Debug['m'] > 2 { |
Josh Bleecher Snyder | dc5f931 | 2016-10-26 22:58:50 -0700 | [diff] [blame] | 1152 | fmt.Printf("substituting name %+v -> %+v\n", n, inlvar) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1153 | } |
Josh Bleecher Snyder | dc5f931 | 2016-10-26 22:58:50 -0700 | [diff] [blame] | 1154 | return inlvar |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1155 | } |
| 1156 | |
| 1157 | if Debug['m'] > 2 { |
Robert Griesemer | ff046d2 | 2016-08-31 15:22:36 -0700 | [diff] [blame] | 1158 | fmt.Printf("not substituting name %+v\n", n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1159 | } |
| 1160 | return n |
| 1161 | |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 1162 | case OLITERAL, OTYPE: |
David Lazar | 1c6ef9a | 2017-02-17 16:07:47 -0500 | [diff] [blame] | 1163 | // 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1169 | |
| 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 Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 1174 | m := nod(OGOTO, subst.retlabel, nil) |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1175 | m.Ninit.Set(subst.list(n.Ninit)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1176 | |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1177 | if len(subst.retvars) != 0 && n.List.Len() != 0 { |
Dave Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 1178 | as := nod(OAS2, nil, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1179 | |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1180 | // 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 Taylor | f444b8a | 2016-03-09 20:29:21 -0800 | [diff] [blame] | 1184 | as.List.Append(n) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1185 | } |
Ian Lance Taylor | e6ea016 | 2016-03-10 11:49:20 -0800 | [diff] [blame] | 1186 | as.Rlist.Set(subst.list(n.List)) |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 1187 | as = typecheck(as, Etop) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 1188 | m.Ninit.Append(as) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1189 | } |
| 1190 | |
Josh Bleecher Snyder | ec7c494 | 2016-03-19 17:02:01 -0700 | [diff] [blame] | 1191 | typecheckslice(m.Ninit.Slice(), Etop) |
Josh Bleecher Snyder | 34699bc | 2016-03-20 08:03:31 -0700 | [diff] [blame] | 1192 | m = typecheck(m, Etop) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1193 | |
| 1194 | // dump("Return after substitution", m); |
| 1195 | return m |
| 1196 | |
Josh Bleecher Snyder | b09925b | 2015-04-01 09:38:44 -0700 | [diff] [blame] | 1197 | case OGOTO, OLABEL: |
Dave Cheney | 073d248 | 2016-09-16 11:00:54 +1000 | [diff] [blame] | 1198 | m := nod(OXXX, nil, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1199 | *m = *n |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1200 | m.Pos = subst.updatedPos(m.Pos) |
Ian Lance Taylor | 38921b3 | 2016-03-08 15:10:26 -0800 | [diff] [blame] | 1201 | m.Ninit.Set(nil) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 1202 | p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen) |
Dave Cheney | d7012ca | 2016-09-15 15:45:10 +1000 | [diff] [blame] | 1203 | m.Left = newname(lookup(p)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1204 | |
| 1205 | return m |
| 1206 | } |
David Lazar | 1c6ef9a | 2017-02-17 16:07:47 -0500 | [diff] [blame] | 1207 | |
| 1208 | m := nod(OXXX, nil, nil) |
| 1209 | *m = *n |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1210 | m.Pos = subst.updatedPos(m.Pos) |
David Lazar | 1c6ef9a | 2017-02-17 16:07:47 -0500 | [diff] [blame] | 1211 | 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 Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1225 | } |
| 1226 | |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1227 | func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos { |
| 1228 | pos := Ctxt.PosTable.Pos(xpos) |
David Lazar | 699175a | 2017-02-17 12:28:05 -0500 | [diff] [blame] | 1229 | oldbase := pos.Base() // can be nil |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1230 | newbase := subst.bases[oldbase] |
David Lazar | 699175a | 2017-02-17 12:28:05 -0500 | [diff] [blame] | 1231 | if newbase == nil { |
Matthew Dempsky | 7c8a961 | 2017-09-18 23:02:02 -0700 | [diff] [blame] | 1232 | newbase = src.NewInliningBase(oldbase, subst.newInlIndex) |
| 1233 | subst.bases[oldbase] = newbase |
David Lazar | 699175a | 2017-02-17 12:28:05 -0500 | [diff] [blame] | 1234 | } |
| 1235 | pos.SetBase(newbase) |
| 1236 | return Ctxt.PosTable.XPos(pos) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1237 | } |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame^] | 1238 | |
| 1239 | func 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. |
| 1258 | type byNodeName []*Node |
| 1259 | |
| 1260 | func (s byNodeName) Len() int { return len(s) } |
| 1261 | func (s byNodeName) Less(i, j int) bool { return cmpNodeName(s[i], s[j]) } |
| 1262 | func (s byNodeName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |