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