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