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