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