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