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