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