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