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