github.com/bgentry/go@v0.0.0-20150121062915-6cf5a733d54d/src/cmd/gc/inl.c (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 agressiveness. 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 // 5: transitive inlining 19 // 20 // At some point this may get another default and become switch-offable with -N. 21 // 22 // The debug['m'] flag enables diagnostic output. a single -m is useful for verifying 23 // which calls get inlined or not, more is for debugging, and may go away at any point. 24 // 25 // TODO: 26 // - inline functions with ... args 27 // - handle T.meth(f()) with func f() (t T, arg, arg, ) 28 29 #include <u.h> 30 #include <libc.h> 31 #include "go.h" 32 33 // Used by caninl. 34 static Node* inlcopy(Node *n); 35 static NodeList* inlcopylist(NodeList *ll); 36 static int ishairy(Node *n, int *budget); 37 static int ishairylist(NodeList *ll, int *budget); 38 39 // Used by inlcalls 40 static void inlnodelist(NodeList *l); 41 static void inlnode(Node **np); 42 static void mkinlcall(Node **np, Node *fn, int isddd); 43 static Node* inlvar(Node *n); 44 static Node* retvar(Type *n, int i); 45 static Node* argvar(Type *n, int i); 46 static Node* newlabel(void); 47 static Node* inlsubst(Node *n); 48 static NodeList* inlsubstlist(NodeList *l); 49 50 static void setlno(Node*, int); 51 52 // Used during inlsubst[list] 53 static Node *inlfn; // function currently being inlined 54 static Node *inlretlabel; // target of the goto substituted in place of a return 55 static NodeList *inlretvars; // temp out variables 56 57 // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods 58 // the ->sym can be re-used in the local package, so peel it off the receiver's type. 59 static Pkg* 60 fnpkg(Node *fn) 61 { 62 Type *rcvr; 63 64 if(fn->type->thistuple) { 65 // method 66 rcvr = getthisx(fn->type)->type->type; 67 if(isptr[rcvr->etype]) 68 rcvr = rcvr->type; 69 if(!rcvr->sym) 70 fatal("receiver with no sym: [%S] %lN (%T)", fn->sym, fn, rcvr); 71 return rcvr->sym->pkg; 72 } 73 // non-method 74 return fn->sym->pkg; 75 } 76 77 // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck 78 // because they're a copy of an already checked body. 79 void 80 typecheckinl(Node *fn) 81 { 82 Node *savefn; 83 Pkg *pkg; 84 int save_safemode, lno; 85 86 lno = setlineno(fn); 87 88 // typecheckinl is only for imported functions; 89 // their bodies may refer to unsafe as long as the package 90 // was marked safe during import (which was checked then). 91 // the ->inl of a local function has been typechecked before caninl copied it. 92 pkg = fnpkg(fn); 93 if (pkg == localpkg || pkg == nil) 94 return; // typecheckinl on local function 95 96 if (debug['m']>2) 97 print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); 98 99 save_safemode = safemode; 100 safemode = 0; 101 102 savefn = curfn; 103 curfn = fn; 104 typechecklist(fn->inl, Etop); 105 curfn = savefn; 106 107 safemode = save_safemode; 108 109 lineno = lno; 110 } 111 112 // Caninl determines whether fn is inlineable. 113 // If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. 114 // fn and ->nbody will already have been typechecked. 115 void 116 caninl(Node *fn) 117 { 118 Node *savefn; 119 Type *t; 120 int budget; 121 122 if(fn->op != ODCLFUNC) 123 fatal("caninl %N", fn); 124 if(!fn->nname) 125 fatal("caninl no nname %+N", fn); 126 127 // If fn has no body (is defined outside of Go), cannot inline it. 128 if(fn->nbody == nil) 129 return; 130 131 if(fn->typecheck == 0) 132 fatal("caninl on non-typechecked function %N", fn); 133 134 // can't handle ... args yet 135 if(debug['l'] < 3) 136 for(t=fn->type->type->down->down->type; t; t=t->down) 137 if(t->isddd) 138 return; 139 140 budget = 40; // allowed hairyness 141 if(ishairylist(fn->nbody, &budget)) 142 return; 143 144 savefn = curfn; 145 curfn = fn; 146 147 fn->nname->inl = fn->nbody; 148 fn->nbody = inlcopylist(fn->nname->inl); 149 fn->nname->inldcl = inlcopylist(fn->nname->defn->dcl); 150 151 // hack, TODO, check for better way to link method nodes back to the thing with the ->inl 152 // this is so export can find the body of a method 153 fn->type->nname = fn->nname; 154 155 if(debug['m'] > 1) 156 print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl); 157 else if(debug['m']) 158 print("%L: can inline %N\n", fn->lineno, fn->nname); 159 160 curfn = savefn; 161 } 162 163 // Look for anything we want to punt on. 164 static int 165 ishairylist(NodeList *ll, int* budget) 166 { 167 for(;ll;ll=ll->next) 168 if(ishairy(ll->n, budget)) 169 return 1; 170 return 0; 171 } 172 173 static int 174 ishairy(Node *n, int *budget) 175 { 176 if(!n) 177 return 0; 178 179 // Things that are too hairy, irrespective of the budget 180 switch(n->op) { 181 case OCALL: 182 case OCALLFUNC: 183 case OCALLINTER: 184 case OCALLMETH: 185 case OPANIC: 186 case ORECOVER: 187 if(debug['l'] < 4) 188 return 1; 189 break; 190 191 case OCLOSURE: 192 case OCALLPART: 193 case ORANGE: 194 case OFOR: 195 case OSELECT: 196 case OSWITCH: 197 case OPROC: 198 case ODEFER: 199 case ODCLTYPE: // can't print yet 200 case ODCLCONST: // can't print yet 201 case ORETJMP: 202 return 1; 203 204 break; 205 } 206 207 (*budget)--; 208 209 return *budget < 0 || 210 ishairy(n->left, budget) || 211 ishairy(n->right, budget) || 212 ishairylist(n->list, budget) || 213 ishairylist(n->rlist, budget) || 214 ishairylist(n->ninit, budget) || 215 ishairy(n->ntest, budget) || 216 ishairy(n->nincr, budget) || 217 ishairylist(n->nbody, budget) || 218 ishairylist(n->nelse, budget); 219 } 220 221 // Inlcopy and inlcopylist recursively copy the body of a function. 222 // Any name-like node of non-local class is marked for re-export by adding it to 223 // the exportlist. 224 static NodeList* 225 inlcopylist(NodeList *ll) 226 { 227 NodeList *l; 228 229 l = nil; 230 for(; ll; ll=ll->next) 231 l = list(l, inlcopy(ll->n)); 232 return l; 233 } 234 235 static Node* 236 inlcopy(Node *n) 237 { 238 Node *m; 239 240 if(n == N) 241 return N; 242 243 switch(n->op) { 244 case ONAME: 245 case OTYPE: 246 case OLITERAL: 247 return n; 248 } 249 250 m = nod(OXXX, N, N); 251 *m = *n; 252 m->inl = nil; 253 m->left = inlcopy(n->left); 254 m->right = inlcopy(n->right); 255 m->list = inlcopylist(n->list); 256 m->rlist = inlcopylist(n->rlist); 257 m->ninit = inlcopylist(n->ninit); 258 m->ntest = inlcopy(n->ntest); 259 m->nincr = inlcopy(n->nincr); 260 m->nbody = inlcopylist(n->nbody); 261 m->nelse = inlcopylist(n->nelse); 262 263 return m; 264 } 265 266 267 // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any 268 // calls made to inlineable functions. This is the external entry point. 269 void 270 inlcalls(Node *fn) 271 { 272 Node *savefn; 273 274 savefn = curfn; 275 curfn = fn; 276 inlnode(&fn); 277 if(fn != curfn) 278 fatal("inlnode replaced curfn"); 279 curfn = savefn; 280 } 281 282 // Turn an OINLCALL into a statement. 283 static void 284 inlconv2stmt(Node *n) 285 { 286 n->op = OBLOCK; 287 // n->ninit stays 288 n->list = n->nbody; 289 n->nbody = nil; 290 n->rlist = nil; 291 } 292 293 // Turn an OINLCALL into a single valued expression. 294 static void 295 inlconv2expr(Node **np) 296 { 297 Node *n, *r; 298 n = *np; 299 r = n->rlist->n; 300 addinit(&r, concat(n->ninit, n->nbody)); 301 *np = r; 302 } 303 304 // Turn the rlist (with the return values) of the OINLCALL in 305 // n into an expression list lumping the ninit and body 306 // containing the inlined statements on the first list element so 307 // order will be preserved Used in return, oas2func and call 308 // statements. 309 static NodeList* 310 inlconv2list(Node *n) 311 { 312 NodeList *l; 313 314 if(n->op != OINLCALL || n->rlist == nil) 315 fatal("inlconv2list %+N\n", n); 316 317 l = n->rlist; 318 addinit(&l->n, concat(n->ninit, n->nbody)); 319 return l; 320 } 321 322 static void 323 inlnodelist(NodeList *l) 324 { 325 for(; l; l=l->next) 326 inlnode(&l->n); 327 } 328 329 // inlnode recurses over the tree to find inlineable calls, which will 330 // be turned into OINLCALLs by mkinlcall. When the recursion comes 331 // back up will examine left, right, list, rlist, ninit, ntest, nincr, 332 // nbody and nelse and use one of the 4 inlconv/glue functions above 333 // to turn the OINLCALL into an expression, a statement, or patch it 334 // in to this nodes list or rlist as appropriate. 335 // NOTE it makes no sense to pass the glue functions down the 336 // recursion to the level where the OINLCALL gets created because they 337 // have to edit /this/ n, so you'd have to push that one down as well, 338 // but then you may as well do it here. so this is cleaner and 339 // shorter and less complicated. 340 static void 341 inlnode(Node **np) 342 { 343 Node *n; 344 NodeList *l; 345 int lno; 346 347 if(*np == nil) 348 return; 349 350 n = *np; 351 352 switch(n->op) { 353 case ODEFER: 354 case OPROC: 355 // inhibit inlining of their argument 356 switch(n->left->op) { 357 case OCALLFUNC: 358 case OCALLMETH: 359 n->left->etype = n->op; 360 } 361 362 case OCLOSURE: 363 // TODO do them here (or earlier), 364 // so escape analysis can avoid more heapmoves. 365 return; 366 } 367 368 lno = setlineno(n); 369 370 inlnodelist(n->ninit); 371 for(l=n->ninit; l; l=l->next) 372 if(l->n->op == OINLCALL) 373 inlconv2stmt(l->n); 374 375 inlnode(&n->left); 376 if(n->left && n->left->op == OINLCALL) 377 inlconv2expr(&n->left); 378 379 inlnode(&n->right); 380 if(n->right && n->right->op == OINLCALL) 381 inlconv2expr(&n->right); 382 383 inlnodelist(n->list); 384 switch(n->op) { 385 case OBLOCK: 386 for(l=n->list; l; l=l->next) 387 if(l->n->op == OINLCALL) 388 inlconv2stmt(l->n); 389 break; 390 391 case ORETURN: 392 case OCALLFUNC: 393 case OCALLMETH: 394 case OCALLINTER: 395 case OAPPEND: 396 case OCOMPLEX: 397 // if we just replaced arg in f(arg()) or return arg with an inlined call 398 // and arg returns multiple values, glue as list 399 if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) { 400 n->list = inlconv2list(n->list->n); 401 break; 402 } 403 404 // fallthrough 405 default: 406 for(l=n->list; l; l=l->next) 407 if(l->n->op == OINLCALL) 408 inlconv2expr(&l->n); 409 } 410 411 inlnodelist(n->rlist); 412 switch(n->op) { 413 case OAS2FUNC: 414 if(n->rlist->n->op == OINLCALL) { 415 n->rlist = inlconv2list(n->rlist->n); 416 n->op = OAS2; 417 n->typecheck = 0; 418 typecheck(np, Etop); 419 break; 420 } 421 422 // fallthrough 423 default: 424 for(l=n->rlist; l; l=l->next) 425 if(l->n->op == OINLCALL) 426 inlconv2expr(&l->n); 427 428 } 429 430 inlnode(&n->ntest); 431 if(n->ntest && n->ntest->op == OINLCALL) 432 inlconv2expr(&n->ntest); 433 434 inlnode(&n->nincr); 435 if(n->nincr && n->nincr->op == OINLCALL) 436 inlconv2stmt(n->nincr); 437 438 inlnodelist(n->nbody); 439 for(l=n->nbody; l; l=l->next) 440 if(l->n->op == OINLCALL) 441 inlconv2stmt(l->n); 442 443 inlnodelist(n->nelse); 444 for(l=n->nelse; l; l=l->next) 445 if(l->n->op == OINLCALL) 446 inlconv2stmt(l->n); 447 448 // with all the branches out of the way, it is now time to 449 // transmogrify this node itself unless inhibited by the 450 // switch at the top of this function. 451 switch(n->op) { 452 case OCALLFUNC: 453 case OCALLMETH: 454 if (n->etype == OPROC || n->etype == ODEFER) 455 return; 456 } 457 458 switch(n->op) { 459 case OCALLFUNC: 460 if(debug['m']>3) 461 print("%L:call to func %+N\n", n->lineno, n->left); 462 if(n->left->inl) // normal case 463 mkinlcall(np, n->left, n->isddd); 464 else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right && n->left->right->op == ONAME) // methods called as functions 465 if(n->left->sym->def) 466 mkinlcall(np, n->left->sym->def, n->isddd); 467 break; 468 469 case OCALLMETH: 470 if(debug['m']>3) 471 print("%L:call to meth %lN\n", n->lineno, n->left->right); 472 // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. 473 if(n->left->type == T) 474 fatal("no function type for [%p] %+N\n", n->left, n->left); 475 476 if(n->left->type->nname == N) 477 fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type); 478 479 mkinlcall(np, n->left->type->nname, n->isddd); 480 481 break; 482 } 483 484 lineno = lno; 485 } 486 487 static void mkinlcall1(Node **np, Node *fn, int isddd); 488 489 static void 490 mkinlcall(Node **np, Node *fn, int isddd) 491 { 492 int save_safemode; 493 Pkg *pkg; 494 495 save_safemode = safemode; 496 497 // imported functions may refer to unsafe as long as the 498 // package was marked safe during import (already checked). 499 pkg = fnpkg(fn); 500 if(pkg != localpkg && pkg != nil) 501 safemode = 0; 502 mkinlcall1(np, fn, isddd); 503 safemode = save_safemode; 504 } 505 506 static Node* 507 tinlvar(Type *t) 508 { 509 if(t->nname && !isblank(t->nname)) { 510 if(!t->nname->inlvar) 511 fatal("missing inlvar for %N\n", t->nname); 512 return t->nname->inlvar; 513 } 514 typecheck(&nblank, Erv | Easgn); 515 return nblank; 516 } 517 518 static int inlgen; 519 520 // if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. 521 // On return ninit has the parameter assignments, the nbody is the 522 // inlined function body and list, rlist contain the input, output 523 // parameters. 524 static void 525 mkinlcall1(Node **np, Node *fn, int isddd) 526 { 527 int i; 528 int chkargcount; 529 Node *n, *call, *saveinlfn, *as, *m; 530 NodeList *dcl, *ll, *ninit, *body; 531 Type *t; 532 // For variadic fn. 533 int variadic, varargcount, multiret; 534 Node *vararg; 535 NodeList *varargs; 536 Type *varargtype, *vararrtype; 537 538 if (fn->inl == nil) 539 return; 540 541 if (fn == curfn || fn->defn == curfn) 542 return; 543 544 if(debug['l']<2) 545 typecheckinl(fn); 546 547 n = *np; 548 549 // Bingo, we have a function node, and it has an inlineable body 550 if(debug['m']>1) 551 print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl); 552 else if(debug['m']) 553 print("%L: inlining call to %N\n", n->lineno, fn); 554 555 if(debug['m']>2) 556 print("%L: Before inlining: %+N\n", n->lineno, n); 557 558 saveinlfn = inlfn; 559 inlfn = fn; 560 561 ninit = n->ninit; 562 563 //dumplist("ninit pre", ninit); 564 565 if(fn->defn) // local function 566 dcl = fn->inldcl; 567 else // imported function 568 dcl = fn->dcl; 569 570 inlretvars = nil; 571 i = 0; 572 // Make temp names to use instead of the originals 573 for(ll = dcl; ll; ll=ll->next) { 574 if(ll->n->class == PPARAMOUT) // return values handled below. 575 continue; 576 if(ll->n->op == ONAME) { 577 ll->n->inlvar = inlvar(ll->n); 578 // Typecheck because inlvar is not necessarily a function parameter. 579 typecheck(&ll->n->inlvar, Erv); 580 if ((ll->n->class&~PHEAP) != PAUTO) 581 ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs 582 } 583 } 584 585 // temporaries for return values. 586 for(t = getoutargx(fn->type)->type; t; t = t->down) { 587 if(t != T && t->nname != N && !isblank(t->nname)) { 588 m = inlvar(t->nname); 589 typecheck(&m, Erv); 590 t->nname->inlvar = m; 591 } else { 592 // anonymous return values, synthesize names for use in assignment that replaces return 593 m = retvar(t, i++); 594 } 595 ninit = list(ninit, nod(ODCL, m, N)); 596 inlretvars = list(inlretvars, m); 597 } 598 599 // assign receiver. 600 if(fn->type->thistuple && n->left->op == ODOTMETH) { 601 // method call with a receiver. 602 t = getthisx(fn->type)->type; 603 if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) 604 fatal("missing inlvar for %N\n", t->nname); 605 if(!n->left->left) 606 fatal("method call without receiver: %+N", n); 607 if(t == T) 608 fatal("method call unknown receiver type: %+N", n); 609 as = nod(OAS, tinlvar(t), n->left->left); 610 if(as != N) { 611 typecheck(&as, Etop); 612 ninit = list(ninit, as); 613 } 614 } 615 616 // check if inlined function is variadic. 617 variadic = 0; 618 varargtype = T; 619 varargcount = 0; 620 for(t=fn->type->type->down->down->type; t; t=t->down) { 621 if(t->isddd) { 622 variadic = 1; 623 varargtype = t->type; 624 } 625 } 626 // but if argument is dotted too forget about variadicity. 627 if(variadic && isddd) 628 variadic = 0; 629 630 // check if argument is actually a returned tuple from call. 631 multiret = 0; 632 if(n->list && !n->list->next) { 633 switch(n->list->n->op) { 634 case OCALL: 635 case OCALLFUNC: 636 case OCALLINTER: 637 case OCALLMETH: 638 if(n->list->n->left->type->outtuple > 1) 639 multiret = n->list->n->left->type->outtuple-1; 640 } 641 } 642 643 if(variadic) { 644 varargcount = count(n->list) + multiret; 645 if(n->left->op != ODOTMETH) 646 varargcount -= fn->type->thistuple; 647 varargcount -= fn->type->intuple - 1; 648 } 649 650 // assign arguments to the parameters' temp names 651 as = nod(OAS2, N, N); 652 as->rlist = n->list; 653 ll = n->list; 654 655 // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? 656 if(fn->type->thistuple && n->left->op != ODOTMETH) { 657 // non-method call to method 658 if(!n->list) 659 fatal("non-method call to method without first arg: %+N", n); 660 // append receiver inlvar to LHS. 661 t = getthisx(fn->type)->type; 662 if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) 663 fatal("missing inlvar for %N\n", t->nname); 664 if(t == T) 665 fatal("method call unknown receiver type: %+N", n); 666 as->list = list(as->list, tinlvar(t)); 667 ll = ll->next; // track argument count. 668 } 669 670 // append ordinary arguments to LHS. 671 chkargcount = n->list && n->list->next; 672 vararg = N; // the slice argument to a variadic call 673 varargs = nil; // the list of LHS names to put in vararg. 674 if(!chkargcount) { 675 // 0 or 1 expression on RHS. 676 for(t = getinargx(fn->type)->type; t; t=t->down) { 677 if(variadic && t->isddd) { 678 vararg = tinlvar(t); 679 for(i=0; i<varargcount && ll; i++) { 680 m = argvar(varargtype, i); 681 varargs = list(varargs, m); 682 as->list = list(as->list, m); 683 } 684 break; 685 } 686 as->list = list(as->list, tinlvar(t)); 687 } 688 } else { 689 // match arguments except final variadic (unless the call is dotted itself) 690 for(t = getinargx(fn->type)->type; t;) { 691 if(!ll) 692 break; 693 if(variadic && t->isddd) 694 break; 695 as->list = list(as->list, tinlvar(t)); 696 t=t->down; 697 ll=ll->next; 698 } 699 // match varargcount arguments with variadic parameters. 700 if(variadic && t && t->isddd) { 701 vararg = tinlvar(t); 702 for(i=0; i<varargcount && ll; i++) { 703 m = argvar(varargtype, i); 704 varargs = list(varargs, m); 705 as->list = list(as->list, m); 706 ll=ll->next; 707 } 708 if(i==varargcount) 709 t=t->down; 710 } 711 if(ll || t) 712 fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list); 713 } 714 715 if (as->rlist) { 716 typecheck(&as, Etop); 717 ninit = list(ninit, as); 718 } 719 720 // turn the variadic args into a slice. 721 if(variadic) { 722 as = nod(OAS, vararg, N); 723 if(!varargcount) { 724 as->right = nodnil(); 725 as->right->type = varargtype; 726 } else { 727 vararrtype = typ(TARRAY); 728 vararrtype->type = varargtype->type; 729 vararrtype->bound = varargcount; 730 731 as->right = nod(OCOMPLIT, N, typenod(varargtype)); 732 as->right->list = varargs; 733 as->right = nod(OSLICE, as->right, nod(OKEY, N, N)); 734 } 735 typecheck(&as, Etop); 736 ninit = list(ninit, as); 737 } 738 739 // zero the outparams 740 for(ll = inlretvars; ll; ll=ll->next) { 741 as = nod(OAS, ll->n, N); 742 typecheck(&as, Etop); 743 ninit = list(ninit, as); 744 } 745 746 inlretlabel = newlabel(); 747 inlgen++; 748 body = inlsubstlist(fn->inl); 749 750 body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return 751 body = list(body, nod(OLABEL, inlretlabel, N)); 752 753 typechecklist(body, Etop); 754 //dumplist("ninit post", ninit); 755 756 call = nod(OINLCALL, N, N); 757 call->ninit = ninit; 758 call->nbody = body; 759 call->rlist = inlretvars; 760 call->type = n->type; 761 call->typecheck = 1; 762 763 setlno(call, n->lineno); 764 //dumplist("call body", body); 765 766 *np = call; 767 768 inlfn = saveinlfn; 769 770 // transitive inlining 771 // TODO do this pre-expansion on fn->inl directly. requires 772 // either supporting exporting statemetns with complex ninits 773 // or saving inl and making inlinl 774 if(debug['l'] >= 5) { 775 body = fn->inl; 776 fn->inl = nil; // prevent infinite recursion 777 inlnodelist(call->nbody); 778 for(ll=call->nbody; ll; ll=ll->next) 779 if(ll->n->op == OINLCALL) 780 inlconv2stmt(ll->n); 781 fn->inl = body; 782 } 783 784 if(debug['m']>2) 785 print("%L: After inlining %+N\n\n", n->lineno, *np); 786 787 } 788 789 // Every time we expand a function we generate a new set of tmpnames, 790 // PAUTO's in the calling functions, and link them off of the 791 // PPARAM's, PAUTOS and PPARAMOUTs of the called function. 792 static Node* 793 inlvar(Node *var) 794 { 795 Node *n; 796 797 if(debug['m']>3) 798 print("inlvar %+N\n", var); 799 800 n = newname(var->sym); 801 n->type = var->type; 802 n->class = PAUTO; 803 n->used = 1; 804 n->curfn = curfn; // the calling function, not the called one 805 n->addrtaken = var->addrtaken; 806 807 // esc pass wont run if we're inlining into a iface wrapper 808 // luckily, we can steal the results from the target func 809 if(var->esc == EscHeap) 810 addrescapes(n); 811 812 curfn->dcl = list(curfn->dcl, n); 813 return n; 814 } 815 816 // Synthesize a variable to store the inlined function's results in. 817 static Node* 818 retvar(Type *t, int i) 819 { 820 Node *n; 821 822 snprint(namebuf, sizeof(namebuf), "~r%d", i); 823 n = newname(lookup(namebuf)); 824 n->type = t->type; 825 n->class = PAUTO; 826 n->used = 1; 827 n->curfn = curfn; // the calling function, not the called one 828 curfn->dcl = list(curfn->dcl, n); 829 return n; 830 } 831 832 // Synthesize a variable to store the inlined function's arguments 833 // when they come from a multiple return call. 834 static Node* 835 argvar(Type *t, int i) 836 { 837 Node *n; 838 839 snprint(namebuf, sizeof(namebuf), "~arg%d", i); 840 n = newname(lookup(namebuf)); 841 n->type = t->type; 842 n->class = PAUTO; 843 n->used = 1; 844 n->curfn = curfn; // the calling function, not the called one 845 curfn->dcl = list(curfn->dcl, n); 846 return n; 847 } 848 849 static Node* 850 newlabel(void) 851 { 852 Node *n; 853 static int label; 854 855 label++; 856 snprint(namebuf, sizeof(namebuf), ".inlret%.6d", label); 857 n = newname(lookup(namebuf)); 858 n->etype = 1; // flag 'safe' for escape analysis (no backjumps) 859 return n; 860 } 861 862 // inlsubst and inlsubstlist recursively copy the body of the saved 863 // pristine ->inl body of the function while substituting references 864 // to input/output parameters with ones to the tmpnames, and 865 // substituting returns with assignments to the output. 866 static NodeList* 867 inlsubstlist(NodeList *ll) 868 { 869 NodeList *l; 870 871 l = nil; 872 for(; ll; ll=ll->next) 873 l = list(l, inlsubst(ll->n)); 874 return l; 875 } 876 877 static Node* 878 inlsubst(Node *n) 879 { 880 char *p; 881 Node *m, *as; 882 NodeList *ll; 883 884 if(n == N) 885 return N; 886 887 switch(n->op) { 888 case ONAME: 889 if(n->inlvar) { // These will be set during inlnode 890 if (debug['m']>2) 891 print ("substituting name %+N -> %+N\n", n, n->inlvar); 892 return n->inlvar; 893 } 894 if (debug['m']>2) 895 print ("not substituting name %+N\n", n); 896 return n; 897 898 case OLITERAL: 899 case OTYPE: 900 return n; 901 902 case ORETURN: 903 // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. 904 905 // dump("Return before substitution", n); 906 m = nod(OGOTO, inlretlabel, N); 907 m->ninit = inlsubstlist(n->ninit); 908 909 if(inlretvars && n->list) { 910 as = nod(OAS2, N, N); 911 // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that. 912 for(ll=inlretvars; ll; ll=ll->next) 913 as->list = list(as->list, ll->n); 914 as->rlist = inlsubstlist(n->list); 915 typecheck(&as, Etop); 916 m->ninit = list(m->ninit, as); 917 } 918 919 typechecklist(m->ninit, Etop); 920 typecheck(&m, Etop); 921 // dump("Return after substitution", m); 922 return m; 923 924 case OGOTO: 925 case OLABEL: 926 m = nod(OXXX, N, N); 927 *m = *n; 928 m->ninit = nil; 929 p = smprint("%s·%d", n->left->sym->name, inlgen); 930 m->left = newname(lookup(p)); 931 free(p); 932 return m; 933 } 934 935 936 m = nod(OXXX, N, N); 937 *m = *n; 938 m->ninit = nil; 939 940 if(n->op == OCLOSURE) 941 fatal("cannot inline function containing closure: %+N", n); 942 943 m->left = inlsubst(n->left); 944 m->right = inlsubst(n->right); 945 m->list = inlsubstlist(n->list); 946 m->rlist = inlsubstlist(n->rlist); 947 m->ninit = concat(m->ninit, inlsubstlist(n->ninit)); 948 m->ntest = inlsubst(n->ntest); 949 m->nincr = inlsubst(n->nincr); 950 m->nbody = inlsubstlist(n->nbody); 951 m->nelse = inlsubstlist(n->nelse); 952 953 return m; 954 } 955 956 // Plaster over linenumbers 957 static void 958 setlnolist(NodeList *ll, int lno) 959 { 960 for(;ll;ll=ll->next) 961 setlno(ll->n, lno); 962 } 963 964 static void 965 setlno(Node *n, int lno) 966 { 967 if(!n) 968 return; 969 970 // don't clobber names, unless they're freshly synthesized 971 if(n->op != ONAME || n->lineno == 0) 972 n->lineno = lno; 973 974 setlno(n->left, lno); 975 setlno(n->right, lno); 976 setlnolist(n->list, lno); 977 setlnolist(n->rlist, lno); 978 setlnolist(n->ninit, lno); 979 setlno(n->ntest, lno); 980 setlno(n->nincr, lno); 981 setlnolist(n->nbody, lno); 982 setlnolist(n->nelse, lno); 983 }