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