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