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