github.com/hlts2/go@v0.0.0-20170904000733-812b34efaed8/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 case ODCLCONST, OEMPTY, OFALL, OLABEL: 284 // These nodes don't produce code; omit from inlining budget. 285 return false 286 } 287 288 v.budget-- 289 // TODO(mdempsky/josharian): Hacks to appease toolstash; remove. 290 // See issue 17566 and CL 31674 for discussion. 291 switch n.Op { 292 case OSTRUCTKEY: 293 v.budget-- 294 case OSLICE, OSLICEARR, OSLICESTR: 295 v.budget-- 296 case OSLICE3, OSLICE3ARR: 297 v.budget -= 2 298 } 299 300 if v.budget < 0 { 301 v.reason = "function too complex" 302 return true 303 } 304 305 return v.visit(n.Left) || v.visit(n.Right) || 306 v.visitList(n.List) || v.visitList(n.Rlist) || 307 v.visitList(n.Ninit) || v.visitList(n.Nbody) 308 } 309 310 // Inlcopy and inlcopylist recursively copy the body of a function. 311 // Any name-like node of non-local class is marked for re-export by adding it to 312 // the exportlist. 313 func inlcopylist(ll []*Node) []*Node { 314 s := make([]*Node, 0, len(ll)) 315 for _, n := range ll { 316 s = append(s, inlcopy(n)) 317 } 318 return s 319 } 320 321 func inlcopy(n *Node) *Node { 322 if n == nil { 323 return nil 324 } 325 326 switch n.Op { 327 case ONAME, OTYPE, OLITERAL: 328 return n 329 } 330 331 m := *n 332 if m.Func != nil { 333 m.Func.Inl.Set(nil) 334 } 335 m.Left = inlcopy(n.Left) 336 m.Right = inlcopy(n.Right) 337 m.List.Set(inlcopylist(n.List.Slice())) 338 m.Rlist.Set(inlcopylist(n.Rlist.Slice())) 339 m.Ninit.Set(inlcopylist(n.Ninit.Slice())) 340 m.Nbody.Set(inlcopylist(n.Nbody.Slice())) 341 342 return &m 343 } 344 345 // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any 346 // calls made to inlineable functions. This is the external entry point. 347 func inlcalls(fn *Node) { 348 savefn := Curfn 349 Curfn = fn 350 fn = inlnode(fn) 351 if fn != Curfn { 352 Fatalf("inlnode replaced curfn") 353 } 354 Curfn = savefn 355 } 356 357 // Turn an OINLCALL into a statement. 358 func inlconv2stmt(n *Node) { 359 n.Op = OBLOCK 360 361 // n->ninit stays 362 n.List.Set(n.Nbody.Slice()) 363 364 n.Nbody.Set(nil) 365 n.Rlist.Set(nil) 366 } 367 368 // Turn an OINLCALL into a single valued expression. 369 // The result of inlconv2expr MUST be assigned back to n, e.g. 370 // n.Left = inlconv2expr(n.Left) 371 func inlconv2expr(n *Node) *Node { 372 r := n.Rlist.First() 373 return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...)) 374 } 375 376 // Turn the rlist (with the return values) of the OINLCALL in 377 // n into an expression list lumping the ninit and body 378 // containing the inlined statements on the first list element so 379 // order will be preserved Used in return, oas2func and call 380 // statements. 381 func inlconv2list(n *Node) []*Node { 382 if n.Op != OINLCALL || n.Rlist.Len() == 0 { 383 Fatalf("inlconv2list %+v\n", n) 384 } 385 386 s := n.Rlist.Slice() 387 s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...)) 388 return s 389 } 390 391 func inlnodelist(l Nodes) { 392 s := l.Slice() 393 for i := range s { 394 s[i] = inlnode(s[i]) 395 } 396 } 397 398 // inlnode recurses over the tree to find inlineable calls, which will 399 // be turned into OINLCALLs by mkinlcall. When the recursion comes 400 // back up will examine left, right, list, rlist, ninit, ntest, nincr, 401 // nbody and nelse and use one of the 4 inlconv/glue functions above 402 // to turn the OINLCALL into an expression, a statement, or patch it 403 // in to this nodes list or rlist as appropriate. 404 // NOTE it makes no sense to pass the glue functions down the 405 // recursion to the level where the OINLCALL gets created because they 406 // have to edit /this/ n, so you'd have to push that one down as well, 407 // but then you may as well do it here. so this is cleaner and 408 // shorter and less complicated. 409 // The result of inlnode MUST be assigned back to n, e.g. 410 // n.Left = inlnode(n.Left) 411 func inlnode(n *Node) *Node { 412 if n == nil { 413 return n 414 } 415 416 switch n.Op { 417 // inhibit inlining of their argument 418 case ODEFER, OPROC: 419 switch n.Left.Op { 420 case OCALLFUNC, OCALLMETH: 421 n.Left.SetNoInline(true) 422 } 423 return n 424 425 // TODO do them here (or earlier), 426 // so escape analysis can avoid more heapmoves. 427 case OCLOSURE: 428 return n 429 } 430 431 lno := setlineno(n) 432 433 inlnodelist(n.Ninit) 434 for _, n1 := range n.Ninit.Slice() { 435 if n1.Op == OINLCALL { 436 inlconv2stmt(n1) 437 } 438 } 439 440 n.Left = inlnode(n.Left) 441 if n.Left != nil && n.Left.Op == OINLCALL { 442 n.Left = inlconv2expr(n.Left) 443 } 444 445 n.Right = inlnode(n.Right) 446 if n.Right != nil && n.Right.Op == OINLCALL { 447 if n.Op == OFOR || n.Op == OFORUNTIL { 448 inlconv2stmt(n.Right) 449 } else { 450 n.Right = inlconv2expr(n.Right) 451 } 452 } 453 454 inlnodelist(n.List) 455 switch n.Op { 456 case OBLOCK: 457 for _, n2 := range n.List.Slice() { 458 if n2.Op == OINLCALL { 459 inlconv2stmt(n2) 460 } 461 } 462 463 case ORETURN, OCALLFUNC, OCALLMETH, OCALLINTER, OAPPEND, OCOMPLEX: 464 // if we just replaced arg in f(arg()) or return arg with an inlined call 465 // and arg returns multiple values, glue as list 466 if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 { 467 n.List.Set(inlconv2list(n.List.First())) 468 break 469 } 470 fallthrough 471 472 default: 473 s := n.List.Slice() 474 for i1, n1 := range s { 475 if n1 != nil && n1.Op == OINLCALL { 476 s[i1] = inlconv2expr(s[i1]) 477 } 478 } 479 } 480 481 inlnodelist(n.Rlist) 482 if n.Op == OAS2FUNC && n.Rlist.First().Op == OINLCALL { 483 n.Rlist.Set(inlconv2list(n.Rlist.First())) 484 n.Op = OAS2 485 n.SetTypecheck(0) 486 n = typecheck(n, Etop) 487 } else { 488 s := n.Rlist.Slice() 489 for i1, n1 := range s { 490 if n1.Op == OINLCALL { 491 if n.Op == OIF { 492 inlconv2stmt(n1) 493 } else { 494 s[i1] = inlconv2expr(s[i1]) 495 } 496 } 497 } 498 } 499 500 inlnodelist(n.Nbody) 501 for _, n := range n.Nbody.Slice() { 502 if n.Op == OINLCALL { 503 inlconv2stmt(n) 504 } 505 } 506 507 // with all the branches out of the way, it is now time to 508 // transmogrify this node itself unless inhibited by the 509 // switch at the top of this function. 510 switch n.Op { 511 case OCALLFUNC, OCALLMETH: 512 if n.NoInline() { 513 return n 514 } 515 } 516 517 switch n.Op { 518 case OCALLFUNC: 519 if Debug['m'] > 3 { 520 fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left) 521 } 522 if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case 523 n = mkinlcall(n, n.Left, n.Isddd()) 524 } else if n.isMethodCalledAsFunction() && asNode(n.Left.Sym.Def) != nil { 525 n = mkinlcall(n, asNode(n.Left.Sym.Def), n.Isddd()) 526 } 527 528 case OCALLMETH: 529 if Debug['m'] > 3 { 530 fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right) 531 } 532 533 // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. 534 if n.Left.Type == nil { 535 Fatalf("no function type for [%p] %+v\n", n.Left, n.Left) 536 } 537 538 if n.Left.Type.Nname() == nil { 539 Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type) 540 } 541 542 n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), n.Isddd()) 543 } 544 545 lineno = lno 546 return n 547 } 548 549 // The result of mkinlcall MUST be assigned back to n, e.g. 550 // n.Left = mkinlcall(n.Left, fn, isddd) 551 func mkinlcall(n *Node, fn *Node, isddd bool) *Node { 552 save_safemode := safemode 553 554 // imported functions may refer to unsafe as long as the 555 // package was marked safe during import (already checked). 556 pkg := fnpkg(fn) 557 558 if pkg != localpkg && pkg != nil { 559 safemode = false 560 } 561 n = mkinlcall1(n, fn, isddd) 562 safemode = save_safemode 563 return n 564 } 565 566 func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node { 567 if asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) { 568 inlvar := inlvars[asNode(t.Nname)] 569 if inlvar == nil { 570 Fatalf("missing inlvar for %v\n", asNode(t.Nname)) 571 } 572 return inlvar 573 } 574 575 return typecheck(nblank, Erv|Easgn) 576 } 577 578 var inlgen int 579 580 // If n is a call, and fn is a function with an inlinable body, 581 // return an OINLCALL. 582 // On return ninit has the parameter assignments, the nbody is the 583 // inlined function body and list, rlist contain the input, output 584 // parameters. 585 // The result of mkinlcall1 MUST be assigned back to n, e.g. 586 // n.Left = mkinlcall1(n.Left, fn, isddd) 587 func mkinlcall1(n *Node, fn *Node, isddd bool) *Node { 588 if fn.Func.Inl.Len() == 0 { 589 // No inlinable body. 590 return n 591 } 592 593 if fn == Curfn || fn.Name.Defn == Curfn { 594 // Can't recursively inline a function into itself. 595 return n 596 } 597 598 if Debug['l'] < 2 { 599 typecheckinl(fn) 600 } 601 602 // We have a function node, and it has an inlineable body. 603 if Debug['m'] > 1 { 604 fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl) 605 } else if Debug['m'] != 0 { 606 fmt.Printf("%v: inlining call to %v\n", n.Line(), fn) 607 } 608 if Debug['m'] > 2 { 609 fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n) 610 } 611 612 ninit := n.Ninit 613 614 // Find declarations corresponding to inlineable body. 615 var dcl []*Node 616 if fn.Name.Defn != nil { 617 dcl = fn.Func.Inldcl.Slice() // local function 618 } else { 619 dcl = fn.Func.Dcl // imported function 620 } 621 622 // Make temp names to use instead of the originals. 623 inlvars := make(map[*Node]*Node) 624 for _, ln := range dcl { 625 if ln.Op != ONAME { 626 continue 627 } 628 if ln.Class() == PPARAMOUT { // return values handled below. 629 continue 630 } 631 if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap 632 continue 633 } 634 inlvars[ln] = typecheck(inlvar(ln), Erv) 635 if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM { 636 ninit.Append(nod(ODCL, inlvars[ln], nil)) 637 } 638 } 639 640 // temporaries for return values. 641 var retvars []*Node 642 for i, t := range fn.Type.Results().Fields().Slice() { 643 var m *Node 644 if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) { 645 m = inlvar(asNode(t.Nname)) 646 m = typecheck(m, Erv) 647 inlvars[asNode(t.Nname)] = m 648 } else { 649 // anonymous return values, synthesize names for use in assignment that replaces return 650 m = retvar(t, i) 651 } 652 653 ninit.Append(nod(ODCL, m, nil)) 654 retvars = append(retvars, m) 655 } 656 657 // Assign arguments to the parameters' temp names. 658 as := nod(OAS2, nil, nil) 659 as.Rlist.Set(n.List.Slice()) 660 661 // For non-dotted calls to variadic functions, we assign the 662 // variadic parameter's temp name separately. 663 var vas *Node 664 665 if fn.IsMethod() { 666 rcv := fn.Type.Recv() 667 668 if n.Left.Op == ODOTMETH { 669 // For x.M(...), assign x directly to the 670 // receiver parameter. 671 if n.Left.Left == nil { 672 Fatalf("method call without receiver: %+v", n) 673 } 674 ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left) 675 ras = typecheck(ras, Etop) 676 ninit.Append(ras) 677 } else { 678 // For T.M(...), add the receiver parameter to 679 // as.List, so it's assigned by the normal 680 // arguments. 681 if as.Rlist.Len() == 0 { 682 Fatalf("non-method call to method without first arg: %+v", n) 683 } 684 as.List.Append(tinlvar(rcv, inlvars)) 685 } 686 } 687 688 for _, param := range fn.Type.Params().Fields().Slice() { 689 // For ordinary parameters or variadic parameters in 690 // dotted calls, just add the variable to the 691 // assignment list, and we're done. 692 if !param.Isddd() || isddd { 693 as.List.Append(tinlvar(param, inlvars)) 694 continue 695 } 696 697 // Otherwise, we need to collect the remaining values 698 // to pass as a slice. 699 700 numvals := n.List.Len() 701 if numvals == 1 && n.List.First().Type.IsFuncArgStruct() { 702 numvals = n.List.First().Type.NumFields() 703 } 704 705 x := as.List.Len() 706 for as.List.Len() < numvals { 707 as.List.Append(argvar(param.Type, as.List.Len())) 708 } 709 varargs := as.List.Slice()[x:] 710 711 vas = nod(OAS, tinlvar(param, inlvars), nil) 712 if len(varargs) == 0 { 713 vas.Right = nodnil() 714 vas.Right.Type = param.Type 715 } else { 716 vas.Right = nod(OCOMPLIT, nil, typenod(param.Type)) 717 vas.Right.List.Set(varargs) 718 } 719 } 720 721 if as.Rlist.Len() != 0 { 722 as = typecheck(as, Etop) 723 ninit.Append(as) 724 } 725 726 if vas != nil { 727 vas = typecheck(vas, Etop) 728 ninit.Append(vas) 729 } 730 731 // Zero the return parameters. 732 for _, n := range retvars { 733 ras := nod(OAS, n, nil) 734 ras = typecheck(ras, Etop) 735 ninit.Append(ras) 736 } 737 738 retlabel := autolabel(".i") 739 retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps) 740 741 inlgen++ 742 743 subst := inlsubst{ 744 retlabel: retlabel, 745 retvars: retvars, 746 inlvars: inlvars, 747 } 748 749 body := subst.list(fn.Func.Inl) 750 751 lab := nod(OLABEL, retlabel, nil) 752 body = append(body, lab) 753 754 typecheckslice(body, Etop) 755 756 //dumplist("ninit post", ninit); 757 758 call := nod(OINLCALL, nil, nil) 759 call.Ninit.Set(ninit.Slice()) 760 call.Nbody.Set(body) 761 call.Rlist.Set(retvars) 762 call.Type = n.Type 763 call.SetTypecheck(1) 764 765 // Hide the args from setPos -- the parameters to the inlined 766 // call already have good line numbers that should be preserved. 767 args := as.Rlist 768 as.Rlist.Set(nil) 769 770 // Rewrite the line information for the inlined AST. 771 parent := -1 772 callBase := Ctxt.PosTable.Pos(n.Pos).Base() 773 if callBase != nil { 774 parent = callBase.InliningIndex() 775 } 776 newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym()) 777 setpos := &setPos{ 778 bases: make(map[*src.PosBase]*src.PosBase), 779 newInlIndex: newIndex, 780 } 781 setpos.node(call) 782 783 as.Rlist.Set(args.Slice()) 784 785 //dumplist("call body", body); 786 787 n = call 788 789 // transitive inlining 790 // might be nice to do this before exporting the body, 791 // but can't emit the body with inlining expanded. 792 // instead we emit the things that the body needs 793 // and each use must redo the inlining. 794 // luckily these are small. 795 body = fn.Func.Inl.Slice() 796 fn.Func.Inl.Set(nil) // prevent infinite recursion (shouldn't happen anyway) 797 inlnodelist(call.Nbody) 798 for _, n := range call.Nbody.Slice() { 799 if n.Op == OINLCALL { 800 inlconv2stmt(n) 801 } 802 } 803 fn.Func.Inl.Set(body) 804 805 if Debug['m'] > 2 { 806 fmt.Printf("%v: After inlining %+v\n\n", n.Line(), n) 807 } 808 809 return n 810 } 811 812 // Every time we expand a function we generate a new set of tmpnames, 813 // PAUTO's in the calling functions, and link them off of the 814 // PPARAM's, PAUTOS and PPARAMOUTs of the called function. 815 func inlvar(var_ *Node) *Node { 816 if Debug['m'] > 3 { 817 fmt.Printf("inlvar %+v\n", var_) 818 } 819 820 n := newname(var_.Sym) 821 n.Type = var_.Type 822 n.SetClass(PAUTO) 823 n.Name.SetUsed(true) 824 n.Name.Curfn = Curfn // the calling function, not the called one 825 n.SetAddrtaken(var_.Addrtaken()) 826 827 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 828 return n 829 } 830 831 // Synthesize a variable to store the inlined function's results in. 832 func retvar(t *types.Field, i int) *Node { 833 n := newname(lookupN("~r", i)) 834 n.Type = t.Type 835 n.SetClass(PAUTO) 836 n.Name.SetUsed(true) 837 n.Name.Curfn = Curfn // the calling function, not the called one 838 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 839 return n 840 } 841 842 // Synthesize a variable to store the inlined function's arguments 843 // when they come from a multiple return call. 844 func argvar(t *types.Type, i int) *Node { 845 n := newname(lookupN("~arg", i)) 846 n.Type = t.Elem() 847 n.SetClass(PAUTO) 848 n.Name.SetUsed(true) 849 n.Name.Curfn = Curfn // the calling function, not the called one 850 Curfn.Func.Dcl = append(Curfn.Func.Dcl, n) 851 return n 852 } 853 854 // The inlsubst type implements the actual inlining of a single 855 // function call. 856 type inlsubst struct { 857 // Target of the goto substituted in place of a return. 858 retlabel *Node 859 860 // Temporary result variables. 861 retvars []*Node 862 863 inlvars map[*Node]*Node 864 } 865 866 // list inlines a list of nodes. 867 func (subst *inlsubst) list(ll Nodes) []*Node { 868 s := make([]*Node, 0, ll.Len()) 869 for _, n := range ll.Slice() { 870 s = append(s, subst.node(n)) 871 } 872 return s 873 } 874 875 // node recursively copies a node from the saved pristine body of the 876 // inlined function, substituting references to input/output 877 // parameters with ones to the tmpnames, and substituting returns with 878 // assignments to the output. 879 func (subst *inlsubst) node(n *Node) *Node { 880 if n == nil { 881 return nil 882 } 883 884 switch n.Op { 885 case ONAME: 886 if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode 887 if Debug['m'] > 2 { 888 fmt.Printf("substituting name %+v -> %+v\n", n, inlvar) 889 } 890 return inlvar 891 } 892 893 if Debug['m'] > 2 { 894 fmt.Printf("not substituting name %+v\n", n) 895 } 896 return n 897 898 case OLITERAL, OTYPE: 899 // If n is a named constant or type, we can continue 900 // using it in the inline copy. Otherwise, make a copy 901 // so we can update the line number. 902 if n.Sym != nil { 903 return n 904 } 905 906 // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. 907 908 // dump("Return before substitution", n); 909 case ORETURN: 910 m := nod(OGOTO, subst.retlabel, nil) 911 912 m.Ninit.Set(subst.list(n.Ninit)) 913 914 if len(subst.retvars) != 0 && n.List.Len() != 0 { 915 as := nod(OAS2, nil, nil) 916 917 // Make a shallow copy of retvars. 918 // Otherwise OINLCALL.Rlist will be the same list, 919 // and later walk and typecheck may clobber it. 920 for _, n := range subst.retvars { 921 as.List.Append(n) 922 } 923 as.Rlist.Set(subst.list(n.List)) 924 as = typecheck(as, Etop) 925 m.Ninit.Append(as) 926 } 927 928 typecheckslice(m.Ninit.Slice(), Etop) 929 m = typecheck(m, Etop) 930 931 // dump("Return after substitution", m); 932 return m 933 934 case OGOTO, OLABEL: 935 m := nod(OXXX, nil, nil) 936 *m = *n 937 m.Ninit.Set(nil) 938 p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen) 939 m.Left = newname(lookup(p)) 940 941 return m 942 } 943 944 m := nod(OXXX, nil, nil) 945 *m = *n 946 m.Ninit.Set(nil) 947 948 if n.Op == OCLOSURE { 949 Fatalf("cannot inline function containing closure: %+v", n) 950 } 951 952 m.Left = subst.node(n.Left) 953 m.Right = subst.node(n.Right) 954 m.List.Set(subst.list(n.List)) 955 m.Rlist.Set(subst.list(n.Rlist)) 956 m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...)) 957 m.Nbody.Set(subst.list(n.Nbody)) 958 959 return m 960 } 961 962 // setPos is a visitor to update position info with a new inlining index. 963 type setPos struct { 964 bases map[*src.PosBase]*src.PosBase 965 newInlIndex int 966 } 967 968 func (s *setPos) nodelist(ll Nodes) { 969 for _, n := range ll.Slice() { 970 s.node(n) 971 } 972 } 973 974 func (s *setPos) node(n *Node) { 975 if n == nil { 976 return 977 } 978 if n.Op == OLITERAL || n.Op == OTYPE { 979 if n.Sym != nil { 980 // This node is not a copy, so don't clobber position. 981 return 982 } 983 } 984 985 // don't clobber names, unless they're freshly synthesized 986 if n.Op != ONAME || !n.Pos.IsKnown() { 987 n.Pos = s.updatedPos(n) 988 } 989 990 s.node(n.Left) 991 s.node(n.Right) 992 s.nodelist(n.List) 993 s.nodelist(n.Rlist) 994 s.nodelist(n.Ninit) 995 s.nodelist(n.Nbody) 996 } 997 998 func (s *setPos) updatedPos(n *Node) src.XPos { 999 pos := Ctxt.PosTable.Pos(n.Pos) 1000 oldbase := pos.Base() // can be nil 1001 newbase := s.bases[oldbase] 1002 if newbase == nil { 1003 newbase = src.NewInliningBase(oldbase, s.newInlIndex) 1004 pos.SetBase(newbase) 1005 s.bases[oldbase] = newbase 1006 } 1007 pos.SetBase(newbase) 1008 return Ctxt.PosTable.XPos(pos) 1009 } 1010 1011 func (n *Node) isMethodCalledAsFunction() bool { 1012 return n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME 1013 }