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