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