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