github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/cmd/compile/internal/gc/inl.go (about) 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 // 10 // The debug['l'] flag controls the aggressiveness. Note that main() swaps level 0 and 1, 11 // making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and 12 // are not supported. 13 // 0: disabled 14 // 1: 80-nodes leaf functions, oneliners, lazy typechecking (default) 15 // 2: (unassigned) 16 // 3: allow variadic functions 17 // 4: allow non-leaf functions 18 // 19 // At some point this may get another default and become switch-offable with -N. 20 // 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. 26 // 27 // TODO: 28 // - inline functions with ... args 29 30 package gc 31 32 import ( 33 "cmd/compile/internal/types" 34 "cmd/internal/src" 35 "fmt" 36 ) 37 38 // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods 39 // the ->sym can be re-used in the local package, so peel it off the receiver's type. 40 func fnpkg(fn *Node) *types.Pkg { 41 if fn.IsMethod() { 42 // method 43 rcvr := fn.Type.Recv().Type 44 45 if rcvr.IsPtr() { 46 rcvr = rcvr.Elem() 47 } 48 if rcvr.Sym == nil { 49 Fatalf("receiver with no sym: [%v] %L (%v)", fn.Sym, fn, rcvr) 50 } 51 return rcvr.Sym.Pkg 52 } 53 54 // non-method 55 return fn.Sym.Pkg 56 } 57 58 // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck 59 // because they're a copy of an already checked body. 60 func typecheckinl(fn *Node) { 61 lno := setlineno(fn) 62 63 // typecheckinl is only for imported functions; 64 // their bodies may refer to unsafe as long as the package 65 // was marked safe during import (which was checked then). 66 // the ->inl of a local function has been typechecked before caninl copied it. 67 pkg := fnpkg(fn) 68 69 if pkg == localpkg || pkg == nil { 70 return // typecheckinl on local function 71 } 72 73 if Debug['m'] > 2 || Debug_export != 0 { 74 fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl) 75 } 76 77 save_safemode := safemode 78 safemode = false 79 80 savefn := Curfn 81 Curfn = fn 82 typecheckslice(fn.Func.Inl.Slice(), Etop) 83 Curfn = savefn 84 85 safemode = save_safemode 86 87 lineno = lno 88 } 89 90 // Caninl determines whether fn is inlineable. 91 // If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. 92 // fn and ->nbody will already have been typechecked. 93 func caninl(fn *Node) { 94 if fn.Op != ODCLFUNC { 95 Fatalf("caninl %v", fn) 96 } 97 if fn.Func.Nname == nil { 98 Fatalf("caninl no nname %+v", fn) 99 } 100 101 var reason string // reason, if any, that the function was not inlined 102 if Debug['m'] > 1 { 103 defer func() { 104 if reason != "" { 105 fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason) 106 } 107 }() 108 } 109 110 // If marked "go:noinline", don't inline 111 if fn.Func.Pragma&Noinline != 0 { 112 reason = "marked go:noinline" 113 return 114 } 115 116 // If marked "go:cgo_unsafe_args", don't inline, since the 117 // function makes assumptions about its argument frame layout. 118 if fn.Func.Pragma&CgoUnsafeArgs != 0 { 119 reason = "marked go:cgo_unsafe_args" 120 return 121 } 122 123 // The nowritebarrierrec checker currently works at function 124 // granularity, so inlining yeswritebarrierrec functions can 125 // confuse it (#22342). As a workaround, disallow inlining 126 // them for now. 127 if fn.Func.Pragma&Yeswritebarrierrec != 0 { 128 reason = "marked go:yeswritebarrierrec" 129 return 130 } 131 132 // If fn has no body (is defined outside of Go), cannot inline it. 133 if fn.Nbody.Len() == 0 { 134 reason = "no function body" 135 return 136 } 137 138 if fn.Typecheck() == 0 { 139 Fatalf("caninl on non-typechecked function %v", fn) 140 } 141 142 // can't handle ... args yet 143 if Debug['l'] < 3 { 144 f := fn.Type.Params().Fields() 145 if len := f.Len(); len > 0 { 146 if t := f.Index(len - 1); t.Isddd() { 147 reason = "has ... args" 148 return 149 } 150 } 151 } 152 153 // Runtime package must not be instrumented. 154 // Instrument skips runtime package. However, some runtime code can be 155 // inlined into other packages and instrumented there. To avoid this, 156 // we disable inlining of runtime functions when instrumenting. 157 // The example that we observed is inlining of LockOSThread, 158 // which lead to false race reports on m contents. 159 if instrumenting && myimportpath == "runtime" { 160 reason = "instrumenting and is runtime function" 161 return 162 } 163 164 n := fn.Func.Nname 165 if n.Func.InlinabilityChecked() { 166 return 167 } 168 defer n.Func.SetInlinabilityChecked(true) 169 170 const maxBudget = 80 171 visitor := hairyVisitor{budget: maxBudget} 172 if visitor.visitList(fn.Nbody) { 173 reason = visitor.reason 174 return 175 } 176 if visitor.budget < 0 { 177 reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", maxBudget-visitor.budget, maxBudget) 178 return 179 } 180 181 savefn := Curfn 182 Curfn = fn 183 184 n.Func.Inl.Set(fn.Nbody.Slice()) 185 fn.Nbody.Set(inlcopylist(n.Func.Inl.Slice())) 186 inldcl := inlcopylist(n.Name.Defn.Func.Dcl) 187 n.Func.Inldcl.Set(inldcl) 188 n.Func.InlCost = maxBudget - visitor.budget 189 190 // hack, TODO, check for better way to link method nodes back to the thing with the ->inl 191 // this is so export can find the body of a method 192 fn.Type.FuncType().Nname = asTypesNode(n) 193 194 if Debug['m'] > 1 { 195 fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl) 196 } else if Debug['m'] != 0 { 197 fmt.Printf("%v: can inline %v\n", fn.Line(), n) 198 } 199 200 Curfn = savefn 201 } 202 203 // inlFlood marks n's inline body for export and recursively ensures 204 // all called functions are marked too. 205 func inlFlood(n *Node) { 206 if n == nil { 207 return 208 } 209 if n.Op != ONAME || n.Class() != PFUNC { 210 Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op, n.Class()) 211 } 212 if n.Func == nil { 213 // TODO(mdempsky): Should init have a Func too? 214 if n.Sym.Name == "init" { 215 return 216 } 217 Fatalf("inlFlood: missing Func on %v", n) 218 } 219 if n.Func.Inl.Len() == 0 { 220 return 221 } 222 223 if n.Func.ExportInline() { 224 return 225 } 226 n.Func.SetExportInline(true) 227 228 typecheckinl(n) 229 230 // Recursively flood any functions called by this one. 231 inspectList(n.Func.Inl, func(n *Node) bool { 232 switch n.Op { 233 case OCALLFUNC, OCALLMETH: 234 inlFlood(asNode(n.Left.Type.Nname())) 235 } 236 return true 237 }) 238 } 239 240 // hairyVisitor visits a function body to determine its inlining 241 // hairiness and whether or not it can be inlined. 242 type hairyVisitor struct { 243 budget int32 244 reason string 245 } 246 247 // Look for anything we want to punt on. 248 func (v *hairyVisitor) visitList(ll Nodes) bool { 249 for _, n := range ll.Slice() { 250 if v.visit(n) { 251 return true 252 } 253 } 254 return false 255 } 256 257 func (v *hairyVisitor) visit(n *Node) bool { 258 if n == nil { 259 return false 260 } 261 262 switch n.Op { 263 // Call is okay if inlinable and we have the budget for the body. 264 case OCALLFUNC: 265 if isIntrinsicCall(n) { 266 v.budget-- 267 break 268 } 269 // Functions that call runtime.getcaller{pc,sp} can not be inlined 270 // because getcaller{pc,sp} expect a pointer to the caller's first argument. 271 if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) { 272 fn := n.Left.Sym.Name 273 if fn == "getcallerpc" || fn == "getcallersp" { 274 v.reason = "call to " + fn 275 return true 276 } 277 } 278 279 if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 { 280 v.budget -= fn.InlCost 281 break 282 } 283 if n.Left.Op == OCLOSURE { 284 if fn := inlinableClosure(n.Left); fn != nil { 285 v.budget -= fn.Func.InlCost 286 break 287 } 288 } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil { 289 // NB: this case currently cannot trigger since closure definition 290 // prevents inlining 291 // NB: ideally we would also handle captured variables defined as 292 // closures in the outer scope this brings us back to the idea of 293 // function value propagation, which if available would both avoid 294 // the "reassigned" check and neatly handle multiple use cases in a 295 // single code path 296 if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE { 297 if fn := inlinableClosure(d.Right); fn != nil { 298 v.budget -= fn.Func.InlCost 299 break 300 } 301 } 302 } 303 304 if n.Left.isMethodExpression() { 305 if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 { 306 v.budget -= d.Func.InlCost 307 break 308 } 309 } 310 if Debug['l'] < 4 { 311 v.reason = "non-leaf function" 312 return true 313 } 314 315 // Call is okay if inlinable and we have the budget for the body. 316 case OCALLMETH: 317 t := n.Left.Type 318 if t == nil { 319 Fatalf("no function type for [%p] %+v\n", n.Left, n.Left) 320 } 321 if t.Nname() == nil { 322 Fatalf("no function definition for [%p] %+v\n", t, t) 323 } 324 if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl.Len() != 0 { 325 v.budget -= inlfn.InlCost 326 break 327 } 328 if Debug['l'] < 4 { 329 v.reason = "non-leaf method" 330 return true 331 } 332 333 // Things that are too hairy, irrespective of the budget 334 case OCALL, OCALLINTER, OPANIC, ORECOVER: 335 if Debug['l'] < 4 { 336 v.reason = "non-leaf op " + n.Op.String() 337 return true 338 } 339 340 case OCLOSURE, 341 OCALLPART, 342 ORANGE, 343 OFOR, 344 OFORUNTIL, 345 OSELECT, 346 OTYPESW, 347 OPROC, 348 ODEFER, 349 ODCLTYPE, // can't print yet 350 OBREAK, 351 ORETJMP: 352 v.reason = "unhandled op " + n.Op.String() 353 return true 354 355 case ODCLCONST, OEMPTY, OFALL, OLABEL: 356 // These nodes don't produce code; omit from inlining budget. 357 return false 358 } 359 360 v.budget-- 361 // TODO(mdempsky/josharian): Hacks to appease toolstash; remove. 362 // See issue 17566 and CL 31674 for discussion. 363 switch n.Op { 364 case OSTRUCTKEY: 365 v.budget-- 366 case OSLICE, OSLICEARR, OSLICESTR: 367 v.budget-- 368 case OSLICE3, OSLICE3ARR: 369 v.budget -= 2 370 } 371 372 // When debugging, don't stop early, to get full cost of inlining this function 373 if v.budget < 0 && Debug['m'] < 2 { 374 return true 375 } 376 377 return v.visit(n.Left) || v.visit(n.Right) || 378 v.visitList(n.List) || v.visitList(n.Rlist) || 379 v.visitList(n.Ninit) || v.visitList(n.Nbody) 380 } 381 382 // Inlcopy and inlcopylist recursively copy the body of a function. 383 // Any name-like node of non-local class is marked for re-export by adding it to 384 // the exportlist. 385 func inlcopylist(ll []*Node) []*Node { 386 s := make([]*Node, 0, len(ll)) 387 for _, n := range ll { 388 s = append(s, inlcopy(n)) 389 } 390 return s 391 } 392 393 func inlcopy(n *Node) *Node { 394 if n == nil { 395 return nil 396 } 397 398 switch n.Op { 399 case ONAME, OTYPE, OLITERAL: 400 return n 401 } 402 403 m := *n 404 if m.Func != nil { 405 m.Func.Inl.Set(nil) 406 } 407 m.Left = inlcopy(n.Left) 408 m.Right = inlcopy(n.Right) 409 m.List.Set(inlcopylist(n.List.Slice())) 410 m.Rlist.Set(inlcopylist(n.Rlist.Slice())) 411 m.Ninit.Set(inlcopylist(n.Ninit.Slice())) 412 m.Nbody.Set(inlcopylist(n.Nbody.Slice())) 413 414 return &m 415 } 416 417 // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any 418 // calls made to inlineable functions. This is the external entry point. 419 func inlcalls(fn *Node) { 420 savefn := Curfn 421 Curfn = fn 422 fn = inlnode(fn) 423 if fn != Curfn { 424 Fatalf("inlnode replaced curfn") 425 } 426 Curfn = savefn 427 } 428 429 // Turn an OINLCALL into a statement. 430 func inlconv2stmt(n *Node) { 431 n.Op = OBLOCK 432 433 // n->ninit stays 434 n.List.Set(n.Nbody.Slice()) 435 436 n.Nbody.Set(nil) 437 n.Rlist.Set(nil) 438 } 439 440 // Turn an OINLCALL into a single valued expression. 441 // The result of inlconv2expr MUST be assigned back to n, e.g. 442 // n.Left = inlconv2expr(n.Left) 443 func inlconv2expr(n *Node) *Node { 444 r := n.Rlist.First() 445 return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...)) 446 } 447 448 // Turn the rlist (with the return values) of the OINLCALL in 449 // n into an expression list lumping the ninit and body 450 // containing the inlined statements on the first list element so 451 // order will be preserved Used in return, oas2func and call 452 // statements. 453 func inlconv2list(n *Node) []*Node { 454 if n.Op != OINLCALL || n.Rlist.Len() == 0 { 455 Fatalf("inlconv2list %+v\n", n) 456 } 457 458 s := n.Rlist.Slice() 459 s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...)) 460 return s 461 } 462 463 func inlnodelist(l Nodes) { 464 s := l.Slice() 465 for i := range s { 466 s[i] = inlnode(s[i]) 467 } 468 } 469 470 // inlnode recurses over the tree to find inlineable calls, which will 471 // be turned into OINLCALLs by mkinlcall. When the recursion comes 472 // back up will examine left, right, list, rlist, ninit, ntest, nincr, 473 // nbody and nelse and use one of the 4 inlconv/glue functions above 474 // to turn the OINLCALL into an expression, a statement, or patch it 475 // in to this nodes list or rlist as appropriate. 476 // NOTE it makes no sense to pass the glue functions down the 477 // recursion to the level where the OINLCALL gets created because they 478 // have to edit /this/ n, so you'd have to push that one down as well, 479 // but then you may as well do it here. so this is cleaner and 480 // shorter and less complicated. 481 // The result of inlnode MUST be assigned back to n, e.g. 482 // n.Left = inlnode(n.Left) 483 func inlnode(n *Node) *Node { 484 if n == nil { 485 return n 486 } 487 488 switch n.Op { 489 // inhibit inlining of their argument 490 case ODEFER, OPROC: 491 switch n.Left.Op { 492 case OCALLFUNC, OCALLMETH: 493 n.Left.SetNoInline(true) 494 } 495 return n 496 497 // TODO do them here (or earlier), 498 // so escape analysis can avoid more heapmoves. 499 case OCLOSURE: 500 return n 501 } 502 503 lno := setlineno(n) 504 505 inlnodelist(n.Ninit) 506 for _, n1 := range n.Ninit.Slice() { 507 if n1.Op == OINLCALL { 508 inlconv2stmt(n1) 509 } 510 } 511 512 n.Left = inlnode(n.Left) 513 if n.Left != nil && n.Left.Op == OINLCALL { 514 n.Left = inlconv2expr(n.Left) 515 } 516 517 n.Right = inlnode(n.Right) 518 if n.Right != nil && n.Right.Op == OINLCALL { 519 if n.Op == OFOR || n.Op == OFORUNTIL { 520 inlconv2stmt(n.Right) 521 } else { 522 n.Right = inlconv2expr(n.Right) 523 } 524 } 525 526 inlnodelist(n.List) 527 switch n.Op { 528 case OBLOCK: 529 for _, n2 := range n.List.Slice() { 530 if n2.Op == OINLCALL { 531 inlconv2stmt(n2) 532 } 533 } 534 535 case ORETURN, OCALLFUNC, OCALLMETH, OCALLINTER, OAPPEND, OCOMPLEX: 536 // if we just replaced arg in f(arg()) or return arg with an inlined call 537 // and arg returns multiple values, glue as list 538 if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 { 539 n.List.Set(inlconv2list(n.List.First())) 540 break 541 } 542 fallthrough 543 544 default: 545 s := n.List.Slice() 546 for i1, n1 := range s { 547 if n1 != nil && n1.Op == OINLCALL { 548 s[i1] = inlconv2expr(s[i1]) 549 } 550 } 551 } 552 553 inlnodelist(n.Rlist) 554 if n.Op == OAS2FUNC && n.Rlist.First().Op == OINLCALL { 555 n.Rlist.Set(inlconv2list(n.Rlist.First())) 556 n.Op = OAS2 557 n.SetTypecheck(0) 558 n = typecheck(n, Etop) 559 } else { 560 s := n.Rlist.Slice() 561 for i1, n1 := range s { 562 if n1.Op == OINLCALL { 563 if n.Op == OIF { 564 inlconv2stmt(n1) 565 } else { 566 s[i1] = inlconv2expr(s[i1]) 567 } 568 } 569 } 570 } 571 572 inlnodelist(n.Nbody) 573 for _, n := range n.Nbody.Slice() { 574 if n.Op == OINLCALL { 575 inlconv2stmt(n) 576 } 577 } 578 579 // with all the branches out of the way, it is now time to 580 // transmogrify this node itself unless inhibited by the 581 // switch at the top of this function. 582 switch n.Op { 583 case OCALLFUNC, OCALLMETH: 584 if n.NoInline() { 585 return n 586 } 587 } 588 589 switch n.Op { 590 case OCALLFUNC: 591 if Debug['m'] > 3 { 592 fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) 593 } 594 if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case 595 n = mkinlcall(n, n.Left, n.Isddd()) 596 } else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil { 597 n = mkinlcall(n, asNode(n.Left.Sym.Def), n.Isddd()) 598 } else if n.Left.Op == OCLOSURE { 599 if f := inlinableClosure(n.Left); f != nil { 600 n = mkinlcall(n, f, n.Isddd()) 601 } 602 } else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil { 603 if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE { 604 if f := inlinableClosure(d.Right); f != nil { 605 // NB: this check is necessary to prevent indirect re-assignment of the variable 606 // having the address taken after the invocation or only used for reads is actually fine 607 // but we have no easy way to distinguish the safe cases 608 if d.Left.Addrtaken() { 609 if Debug['m'] > 1 { 610 fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left) 611 } 612 break 613 } 614 615 // ensure the variable is never re-assigned 616 if unsafe, a := reassigned(n.Left); unsafe { 617 if Debug['m'] > 1 { 618 if a != nil { 619 fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a) 620 } else { 621 fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left) 622 } 623 } 624 break 625 } 626 n = mkinlcall(n, f, n.Isddd()) 627 } 628 } 629 } 630 631 case OCALLMETH: 632 if Debug['m'] > 3 { 633 fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right) 634 } 635 636 // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. 637 if n.Left.Type == nil { 638 Fatalf("no function type for [%p] %+v\n", n.Left, n.Left) 639 } 640 641 if n.Left.Type.Nname() == nil { 642 Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type) 643 } 644 645 n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), n.Isddd()) 646 } 647 648 lineno = lno 649 return n 650 } 651 652 // inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with 653 // the inlinable body. Returns nil if the function is not inlinable. 654 func inlinableClosure(n *Node) *Node { 655 c := n.Func.Closure 656 caninl(c) 657 f := c.Func.Nname 658 if f == nil || f.Func.Inl.Len() == 0 { 659 return nil 660 } 661 return f 662 } 663 664 // reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean 665 // indicating whether the name has any assignments other than its declaration. 666 // The second return value is the first such assignment encountered in the walk, if any. It is mostly 667 // useful for -m output documenting the reason for inhibited optimizations. 668 // NB: global variables are always considered to be re-assigned. 669 // TODO: handle initial declaration not including an assignment and followed by a single assignment? 670 func reassigned(n *Node) (bool, *Node) { 671 if n.Op != ONAME { 672 Fatalf("reassigned %v", n) 673 } 674 // no way to reliably check for no-reassignment of globals, assume it can be 675 if n.Name.Curfn == nil { 676 return true, nil 677 } 678 f := n.Name.Curfn 679 // There just might be a good reason for this although this can be pretty surprising: 680 // local variables inside a closure have Curfn pointing to the OCLOSURE node instead 681 // of the corresponding ODCLFUNC. 682 // We need to walk the function body to check for reassignments so we follow the 683 // linkage to the ODCLFUNC node as that is where body is held. 684 if f.Op == OCLOSURE { 685 f = f.Func.Closure 686 } 687 v := reassignVisitor{name: n} 688 a := v.visitList(f.Nbody) 689 return a != nil, a 690 } 691 692 type reassignVisitor struct { 693 name *Node 694 } 695 696 func (v *reassignVisitor) visit(n *Node) *Node { 697 if n == nil { 698 return nil 699 } 700 switch n.Op { 701 case OAS: 702 if n.Left == v.name && n != v.name.Name.Defn { 703 return n 704 } 705 return nil 706 case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE: 707 for _, p := range n.List.Slice() { 708 if p == v.name && n != v.name.Name.Defn { 709 return n 710 } 711 } 712 return nil 713 } 714 if a := v.visit(n.Left); a != nil { 715 return a 716 } 717 if a := v.visit(n.Right); a != nil { 718 return a 719 } 720 if a := v.visitList(n.List); a != nil { 721 return a 722 } 723 if a := v.visitList(n.Rlist); a != nil { 724 return a 725 } 726 if a := v.visitList(n.Ninit); a != nil { 727 return a 728 } 729 if a := v.visitList(n.Nbody); a != nil { 730 return a 731 } 732 return nil 733 } 734 735 func (v *reassignVisitor) visitList(l Nodes) *Node { 736 for _, n := range l.Slice() { 737 if a := v.visit(n); a != nil { 738 return a 739 } 740 } 741 return nil 742 } 743 744 // The result of mkinlcall MUST be assigned back to n, e.g. 745 // n.Left = mkinlcall(n.Left, fn, isddd) 746 func mkinlcall(n *Node, fn *Node, isddd bool) *Node { 747 save_safemode := safemode 748 749 // imported functions may refer to unsafe as long as the 750 // package was marked safe during import (already checked). 751 pkg := fnpkg(fn) 752 753 if pkg != localpkg && pkg != nil { 754 safemode = false 755 } 756 n = mkinlcall1(n, fn, isddd) 757 safemode = save_safemode 758 return n 759 } 760 761 func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node { 762 if asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) { 763 inlvar := inlvars[asNode(t.Nname)] 764 if inlvar == nil { 765 Fatalf("missing inlvar for %v\n", asNode(t.Nname)) 766 } 767 return inlvar 768 } 769 770 return typecheck(nblank, Erv|Easgn) 771 } 772 773 var inlgen int 774 775 // If n is a call, and fn is a function with an inlinable body, 776 // return an OINLCALL. 777 // On return ninit has the parameter assignments, the nbody is the 778 // inlined function body and list, rlist contain the input, output 779 // parameters. 780 // The result of mkinlcall1 MUST be assigned back to n, e.g. 781 // n.Left = mkinlcall1(n.Left, fn, isddd) 782 func mkinlcall1(n, fn *Node, isddd bool) *Node { 783 if fn.Func.Inl.Len() == 0 { 784 // No inlinable body. 785 return n 786 } 787 788 if fn == Curfn || fn.Name.Defn == Curfn { 789 // Can't recursively inline a function into itself. 790 return n 791 } 792 793 if Debug_typecheckinl == 0 { 794 typecheckinl(fn) 795 } 796 797 // We have a function node, and it has an inlineable body. 798 if Debug['m'] > 1 { 799 fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl) 800 } else if Debug['m'] != 0 { 801 fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) 802 } 803 if Debug['m'] > 2 { 804 fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) 805 } 806 807 ninit := n.Ninit 808 809 // Make temp names to use instead of the originals. 810 inlvars := make(map[*Node]*Node) 811 812 // Find declarations corresponding to inlineable body. 813 var dcl []*Node 814 if fn.Name.Defn != nil { 815 dcl = fn.Func.Inldcl.Slice() // local function 816 817 // handle captured variables when inlining closures 818 if c := fn.Name.Defn.Func.Closure; c != nil { 819 for _, v := range c.Func.Cvars.Slice() { 820 if v.Op == OXXX { 821 continue 822 } 823 824 o := v.Name.Param.Outer 825 // make sure the outer param matches the inlining location 826 // NB: if we enabled inlining of functions containing OCLOSURE or refined 827 // the reassigned check via some sort of copy propagation this would most 828 // likely need to be changed to a loop to walk up to the correct Param 829 if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) { 830 Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v) 831 } 832 833 if v.Name.Byval() { 834 iv := typecheck(inlvar(v), Erv) 835 ninit.Append(nod(ODCL, iv, nil)) 836 ninit.Append(typecheck(nod(OAS, iv, o), Etop)) 837 inlvars[v] = iv 838 } else { 839 addr := newname(lookup("&" + v.Sym.Name)) 840 addr.Type = types.NewPtr(v.Type) 841 ia := typecheck(inlvar(addr), Erv) 842 ninit.Append(nod(ODCL, ia, nil)) 843 ninit.Append(typecheck(nod(OAS, ia, nod(OADDR, o, nil)), Etop)) 844 inlvars[addr] = ia 845 846 // When capturing by reference, all occurrence of the captured var 847 // must be substituted with dereference of the temporary address 848 inlvars[v] = typecheck(nod(OIND, ia, nil), Erv) 849 } 850 } 851 } 852 } else { 853 dcl = fn.Func.Dcl // imported function 854 } 855 856 for _, ln := range dcl { 857 if ln.Op != ONAME { 858 continue 859 } 860 if ln.Class() == PPARAMOUT { // return values handled below. 861 continue 862 } 863 if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap 864 continue 865 } 866 inlvars[ln] = typecheck(inlvar(ln), Erv) 867 if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM { 868 ninit.Append(nod(ODCL, inlvars[ln], nil)) 869 } 870 } 871 872 // temporaries for return values. 873 var retvars []*Node 874 for i, t := range fn.Type.Results().Fields().Slice() { 875 var m *Node 876 if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) { 877 m = inlvar(asNode(t.Nname)) 878 m = typecheck(m, Erv) 879 inlvars[asNode(t.Nname)] = m 880 } else { 881 // anonymous return values, synthesize names for use in assignment that replaces return 882 m = retvar(t, i) 883 } 884 885 ninit.Append(nod(ODCL, m, nil)) 886 retvars = append(retvars, m) 887 } 888 889 // Assign arguments to the parameters' temp names. 890 as := nod(OAS2, nil, nil) 891 as.Rlist.Set(n.List.Slice()) 892 893 // For non-dotted calls to variadic functions, we assign the 894 // variadic parameter's temp name separately. 895 var vas *Node 896 897 if fn.IsMethod() { 898 rcv := fn.Type.Recv() 899 900 if n.Left.Op == ODOTMETH { 901 // For x.M(...), assign x directly to the 902 // receiver parameter. 903 if n.Left.Left == nil { 904 Fatalf("method call without receiver: %+v", n) 905 } 906 ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left) 907 ras = typecheck(ras, Etop) 908 ninit.Append(ras) 909 } else { 910 // For T.M(...), add the receiver parameter to 911 // as.List, so it's assigned by the normal 912 // arguments. 913 if as.Rlist.Len() == 0 { 914 Fatalf("non-method call to method without first arg: %+v", n) 915 } 916 as.List.Append(tinlvar(rcv, inlvars)) 917 } 918 } 919 920 for _, param := range fn.Type.Params().Fields().Slice() { 921 // For ordinary parameters or variadic parameters in 922 // dotted calls, just add the variable to the 923 // assignment list, and we're done. 924 if !param.Isddd() || isddd { 925 as.List.Append(tinlvar(param, inlvars)) 926 continue 927 } 928 929 // Otherwise, we need to collect the remaining values 930 // to pass as a slice. 931 932 numvals := n.List.Len() 933 if numvals == 1 && n.List.First().Type.IsFuncArgStruct() { 934 numvals = n.List.First().Type.NumFields() 935 } 936 937 x := as.List.Len() 938 for as.List.Len() < numvals { 939 as.List.Append(argvar(param.Type, as.List.Len())) 940 } 941 varargs := as.List.Slice()[x:] 942 943 vas = nod(OAS, tinlvar(param, inlvars), nil) 944 if len(varargs) == 0 { 945 vas.Right = nodnil() 946 vas.Right.Type = param.Type 947 } else { 948 vas.Right = nod(OCOMPLIT, nil, typenod(param.Type)) 949 vas.Right.List.Set(varargs) 950 } 951 } 952 953 if as.Rlist.Len() != 0 { 954 as = typecheck(as, Etop) 955 ninit.Append(as) 956 } 957 958 if vas != nil { 959 vas = typecheck(vas, Etop) 960 ninit.Append(vas) 961 } 962 963 // Zero the return parameters. 964 for _, n := range retvars { 965 ras := nod(OAS, n, nil) 966 ras = typecheck(ras, Etop) 967 ninit.Append(ras) 968 } 969 970 retlabel := autolabel(".i") 971 retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps) 972 973 inlgen++ 974 975 parent := -1 976 if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil { 977 parent = b.InliningIndex() 978 } 979 newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym()) 980 981 subst := inlsubst{ 982 retlabel: retlabel, 983 retvars: retvars, 984 inlvars: inlvars, 985 bases: make(map[*src.PosBase]*src.PosBase), 986 newInlIndex: newIndex, 987 } 988 989 body := subst.list(fn.Func.Inl) 990 991 lab := nod(OLABEL, retlabel, nil) 992 body = append(body, lab) 993 994 typecheckslice(body, Etop) 995 996 //dumplist("ninit post", ninit); 997 998 call := nod(OINLCALL, nil, nil) 999 call.Ninit.Set(ninit.Slice()) 1000 call.Nbody.Set(body) 1001 call.Rlist.Set(retvars) 1002 call.Type = n.Type 1003 call.SetTypecheck(1) 1004 1005 // transitive inlining 1006 // might be nice to do this before exporting the body, 1007 // but can't emit the body with inlining expanded. 1008 // instead we emit the things that the body needs 1009 // and each use must redo the inlining. 1010 // luckily these are small. 1011 inlnodelist(call.Nbody) 1012 for _, n := range call.Nbody.Slice() { 1013 if n.Op == OINLCALL { 1014 inlconv2stmt(n) 1015 } 1016 } 1017 1018 if Debug['m'] > 2 { 1019 fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call) 1020 } 1021 1022 return call 1023 } 1024 1025 // Every time we expand a function we generate a new set of tmpnames, 1026 // PAUTO's in the calling functions, and link them off of the 1027 // PPARAM's, PAUTOS and PPARAMOUTs of the called function. 1028 func inlvar(var_ *Node) *Node { 1029 if Debug['m'] > 3 { 1030 fmt.Printf("inlvar %+v\n", var_) 1031 } 1032 1033 n := newname(var_.Sym) 1034 n.Type = var_.Type 1035 n.SetClass(PAUTO) 1036 n.Name.SetUsed(true) 1037 n.Name.Curfn = Curfn // the calling function, not the called one 1038 n.SetAddrtaken(var_.Addrtaken()) 1039 1040 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 1041 return n 1042 } 1043 1044 // Synthesize a variable to store the inlined function's results in. 1045 func retvar(t *types.Field, i int) *Node { 1046 n := newname(lookupN("~r", i)) 1047 n.Type = t.Type 1048 n.SetClass(PAUTO) 1049 n.Name.SetUsed(true) 1050 n.Name.Curfn = Curfn // the calling function, not the called one 1051 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 1052 return n 1053 } 1054 1055 // Synthesize a variable to store the inlined function's arguments 1056 // when they come from a multiple return call. 1057 func argvar(t *types.Type, i int) *Node { 1058 n := newname(lookupN("~arg", i)) 1059 n.Type = t.Elem() 1060 n.SetClass(PAUTO) 1061 n.Name.SetUsed(true) 1062 n.Name.Curfn = Curfn // the calling function, not the called one 1063 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 1064 return n 1065 } 1066 1067 // The inlsubst type implements the actual inlining of a single 1068 // function call. 1069 type inlsubst struct { 1070 // Target of the goto substituted in place of a return. 1071 retlabel *Node 1072 1073 // Temporary result variables. 1074 retvars []*Node 1075 1076 inlvars map[*Node]*Node 1077 1078 // bases maps from original PosBase to PosBase with an extra 1079 // inlined call frame. 1080 bases map[*src.PosBase]*src.PosBase 1081 1082 // newInlIndex is the index of the inlined call frame to 1083 // insert for inlined nodes. 1084 newInlIndex int 1085 } 1086 1087 // list inlines a list of nodes. 1088 func (subst *inlsubst) list(ll Nodes) []*Node { 1089 s := make([]*Node, 0, ll.Len()) 1090 for _, n := range ll.Slice() { 1091 s = append(s, subst.node(n)) 1092 } 1093 return s 1094 } 1095 1096 // node recursively copies a node from the saved pristine body of the 1097 // inlined function, substituting references to input/output 1098 // parameters with ones to the tmpnames, and substituting returns with 1099 // assignments to the output. 1100 func (subst *inlsubst) node(n *Node) *Node { 1101 if n == nil { 1102 return nil 1103 } 1104 1105 switch n.Op { 1106 case ONAME: 1107 if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode 1108 if Debug['m'] > 2 { 1109 fmt.Printf("substituting name %+v -> %+v\n", n, inlvar) 1110 } 1111 return inlvar 1112 } 1113 1114 if Debug['m'] > 2 { 1115 fmt.Printf("not substituting name %+v\n", n) 1116 } 1117 return n 1118 1119 case OLITERAL, OTYPE: 1120 // If n is a named constant or type, we can continue 1121 // using it in the inline copy. Otherwise, make a copy 1122 // so we can update the line number. 1123 if n.Sym != nil { 1124 return n 1125 } 1126 1127 // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. 1128 1129 // dump("Return before substitution", n); 1130 case ORETURN: 1131 m := nod(OGOTO, subst.retlabel, nil) 1132 m.Ninit.Set(subst.list(n.Ninit)) 1133 1134 if len(subst.retvars) != 0 && n.List.Len() != 0 { 1135 as := nod(OAS2, nil, nil) 1136 1137 // Make a shallow copy of retvars. 1138 // Otherwise OINLCALL.Rlist will be the same list, 1139 // and later walk and typecheck may clobber it. 1140 for _, n := range subst.retvars { 1141 as.List.Append(n) 1142 } 1143 as.Rlist.Set(subst.list(n.List)) 1144 as = typecheck(as, Etop) 1145 m.Ninit.Append(as) 1146 } 1147 1148 typecheckslice(m.Ninit.Slice(), Etop) 1149 m = typecheck(m, Etop) 1150 1151 // dump("Return after substitution", m); 1152 return m 1153 1154 case OGOTO, OLABEL: 1155 m := nod(OXXX, nil, nil) 1156 *m = *n 1157 m.Pos = subst.updatedPos(m.Pos) 1158 m.Ninit.Set(nil) 1159 p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen) 1160 m.Left = newname(lookup(p)) 1161 1162 return m 1163 } 1164 1165 m := nod(OXXX, nil, nil) 1166 *m = *n 1167 m.Pos = subst.updatedPos(m.Pos) 1168 m.Ninit.Set(nil) 1169 1170 if n.Op == OCLOSURE { 1171 Fatalf("cannot inline function containing closure: %+v", n) 1172 } 1173 1174 m.Left = subst.node(n.Left) 1175 m.Right = subst.node(n.Right) 1176 m.List.Set(subst.list(n.List)) 1177 m.Rlist.Set(subst.list(n.Rlist)) 1178 m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...)) 1179 m.Nbody.Set(subst.list(n.Nbody)) 1180 1181 return m 1182 } 1183 1184 func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos { 1185 pos := Ctxt.PosTable.Pos(xpos) 1186 oldbase := pos.Base() // can be nil 1187 newbase := subst.bases[oldbase] 1188 if newbase == nil { 1189 newbase = src.NewInliningBase(oldbase, subst.newInlIndex) 1190 subst.bases[oldbase] = newbase 1191 } 1192 pos.SetBase(newbase) 1193 return Ctxt.PosTable.XPos(pos) 1194 }