diff options
Diffstat (limited to 'src/cmd/gc/inl.c')
-rw-r--r-- | src/cmd/gc/inl.c | 986 |
1 files changed, 0 insertions, 986 deletions
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c deleted file mode 100644 index 45e15bb9b7..0000000000 --- a/src/cmd/gc/inl.c +++ /dev/null @@ -1,986 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// The inlining facility makes 2 passes: first caninl determines which -// functions are suitable for inlining, and for those that are it -// saves a copy of the body. Then inlcalls walks each function body to -// expand calls to inlinable functions. -// -// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1, -// making 1 the default and -l disable. -ll and more is useful to flush out bugs. -// These additional levels (beyond -l) may be buggy and are not supported. -// 0: disabled -// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) -// 2: early typechecking of all imported bodies -// 3: allow variadic functions -// 4: allow non-leaf functions , (breaks runtime.Caller) -// 5: transitive inlining -// -// At some point this may get another default and become switch-offable with -N. -// -// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying -// which calls get inlined or not, more is for debugging, and may go away at any point. -// -// TODO: -// - inline functions with ... args -// - handle T.meth(f()) with func f() (t T, arg, arg, ) - -#include <u.h> -#include <libc.h> -#include "go.h" - -// Used by caninl. -static Node* inlcopy(Node *n); -static NodeList* inlcopylist(NodeList *ll); -static int ishairy(Node *n, int *budget); -static int ishairylist(NodeList *ll, int *budget); - -// Used by inlcalls -static void inlnodelist(NodeList *l); -static void inlnode(Node **np); -static void mkinlcall(Node **np, Node *fn, int isddd); -static Node* inlvar(Node *n); -static Node* retvar(Type *n, int i); -static Node* argvar(Type *n, int i); -static Node* newlabel(void); -static Node* inlsubst(Node *n); -static NodeList* inlsubstlist(NodeList *l); - -static void setlno(Node*, int); - -// Used during inlsubst[list] -static Node *inlfn; // function currently being inlined -static Node *inlretlabel; // target of the goto substituted in place of a return -static NodeList *inlretvars; // temp out variables - -// Get the function's package. For ordinary functions it's on the ->sym, but for imported methods -// the ->sym can be re-used in the local package, so peel it off the receiver's type. -static Pkg* -fnpkg(Node *fn) -{ - Type *rcvr; - - if(fn->type->thistuple) { - // method - rcvr = getthisx(fn->type)->type->type; - if(isptr[rcvr->etype]) - rcvr = rcvr->type; - if(!rcvr->sym) - fatal("receiver with no sym: [%S] %lN (%T)", fn->sym, fn, rcvr); - return rcvr->sym->pkg; - } - // non-method - return fn->sym->pkg; -} - -// Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck -// because they're a copy of an already checked body. -void -typecheckinl(Node *fn) -{ - Node *savefn; - Pkg *pkg; - int save_safemode, lno; - - lno = setlineno(fn); - - // typecheckinl is only for imported functions; - // their bodies may refer to unsafe as long as the package - // was marked safe during import (which was checked then). - // the ->inl of a local function has been typechecked before caninl copied it. - pkg = fnpkg(fn); - if (pkg == localpkg || pkg == nil) - return; // typecheckinl on local function - - if (debug['m']>2) - print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl); - - save_safemode = safemode; - safemode = 0; - - savefn = curfn; - curfn = fn; - typechecklist(fn->inl, Etop); - curfn = savefn; - - safemode = save_safemode; - - lineno = lno; -} - -// Caninl determines whether fn is inlineable. -// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy. -// fn and ->nbody will already have been typechecked. -void -caninl(Node *fn) -{ - Node *savefn; - Type *t; - int budget; - - if(fn->op != ODCLFUNC) - fatal("caninl %N", fn); - if(!fn->nname) - fatal("caninl no nname %+N", fn); - - // If fn has no body (is defined outside of Go), cannot inline it. - if(fn->nbody == nil) - return; - - if(fn->typecheck == 0) - fatal("caninl on non-typechecked function %N", fn); - - // can't handle ... args yet - if(debug['l'] < 3) - for(t=fn->type->type->down->down->type; t; t=t->down) - if(t->isddd) - return; - - budget = 40; // allowed hairyness - if(ishairylist(fn->nbody, &budget)) - return; - - savefn = curfn; - curfn = fn; - - fn->nname->inl = fn->nbody; - fn->nbody = inlcopylist(fn->nname->inl); - fn->nname->inldcl = inlcopylist(fn->nname->defn->dcl); - - // hack, TODO, check for better way to link method nodes back to the thing with the ->inl - // this is so export can find the body of a method - fn->type->nname = fn->nname; - - if(debug['m'] > 1) - print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl); - else if(debug['m']) - print("%L: can inline %N\n", fn->lineno, fn->nname); - - curfn = savefn; -} - -// Look for anything we want to punt on. -static int -ishairylist(NodeList *ll, int* budget) -{ - for(;ll;ll=ll->next) - if(ishairy(ll->n, budget)) - return 1; - return 0; -} - -static int -ishairy(Node *n, int *budget) -{ - if(!n) - return 0; - - // Things that are too hairy, irrespective of the budget - switch(n->op) { - case OCALL: - case OCALLFUNC: - case OCALLINTER: - case OCALLMETH: - case OPANIC: - case ORECOVER: - if(debug['l'] < 4) - return 1; - break; - - case OCLOSURE: - case OCALLPART: - case ORANGE: - case OFOR: - case OSELECT: - case OSWITCH: - case OPROC: - case ODEFER: - case ODCLTYPE: // can't print yet - case ODCLCONST: // can't print yet - case ORETJMP: - return 1; - - break; - } - - (*budget)--; - - return *budget < 0 || - ishairy(n->left, budget) || - ishairy(n->right, budget) || - ishairylist(n->list, budget) || - ishairylist(n->rlist, budget) || - ishairylist(n->ninit, budget) || - ishairy(n->ntest, budget) || - ishairy(n->nincr, budget) || - ishairylist(n->nbody, budget) || - ishairylist(n->nelse, budget); -} - -// Inlcopy and inlcopylist recursively copy the body of a function. -// Any name-like node of non-local class is marked for re-export by adding it to -// the exportlist. -static NodeList* -inlcopylist(NodeList *ll) -{ - NodeList *l; - - l = nil; - for(; ll; ll=ll->next) - l = list(l, inlcopy(ll->n)); - return l; -} - -static Node* -inlcopy(Node *n) -{ - Node *m; - - if(n == N) - return N; - - switch(n->op) { - case ONAME: - case OTYPE: - case OLITERAL: - return n; - } - - m = nod(OXXX, N, N); - *m = *n; - m->inl = nil; - m->left = inlcopy(n->left); - m->right = inlcopy(n->right); - m->list = inlcopylist(n->list); - m->rlist = inlcopylist(n->rlist); - m->ninit = inlcopylist(n->ninit); - m->ntest = inlcopy(n->ntest); - m->nincr = inlcopy(n->nincr); - m->nbody = inlcopylist(n->nbody); - m->nelse = inlcopylist(n->nelse); - - return m; -} - - -// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any -// calls made to inlineable functions. This is the external entry point. -void -inlcalls(Node *fn) -{ - Node *savefn; - - savefn = curfn; - curfn = fn; - inlnode(&fn); - if(fn != curfn) - fatal("inlnode replaced curfn"); - curfn = savefn; -} - -// Turn an OINLCALL into a statement. -static void -inlconv2stmt(Node *n) -{ - n->op = OBLOCK; - // n->ninit stays - n->list = n->nbody; - n->nbody = nil; - n->rlist = nil; -} - -// Turn an OINLCALL into a single valued expression. -static void -inlconv2expr(Node **np) -{ - Node *n, *r; - n = *np; - r = n->rlist->n; - addinit(&r, concat(n->ninit, n->nbody)); - *np = r; -} - -// Turn the rlist (with the return values) of the OINLCALL in -// n into an expression list lumping the ninit and body -// containing the inlined statements on the first list element so -// order will be preserved Used in return, oas2func and call -// statements. -static NodeList* -inlconv2list(Node *n) -{ - NodeList *l; - - if(n->op != OINLCALL || n->rlist == nil) - fatal("inlconv2list %+N\n", n); - - l = n->rlist; - addinit(&l->n, concat(n->ninit, n->nbody)); - return l; -} - -static void -inlnodelist(NodeList *l) -{ - for(; l; l=l->next) - inlnode(&l->n); -} - -// inlnode recurses over the tree to find inlineable calls, which will -// be turned into OINLCALLs by mkinlcall. When the recursion comes -// back up will examine left, right, list, rlist, ninit, ntest, nincr, -// nbody and nelse and use one of the 4 inlconv/glue functions above -// to turn the OINLCALL into an expression, a statement, or patch it -// in to this nodes list or rlist as appropriate. -// NOTE it makes no sense to pass the glue functions down the -// recursion to the level where the OINLCALL gets created because they -// have to edit /this/ n, so you'd have to push that one down as well, -// but then you may as well do it here. so this is cleaner and -// shorter and less complicated. -static void -inlnode(Node **np) -{ - Node *n; - NodeList *l; - int lno; - - if(*np == nil) - return; - - n = *np; - - switch(n->op) { - case ODEFER: - case OPROC: - // inhibit inlining of their argument - switch(n->left->op) { - case OCALLFUNC: - case OCALLMETH: - n->left->etype = n->op; - } - - case OCLOSURE: - // TODO do them here (or earlier), - // so escape analysis can avoid more heapmoves. - return; - } - - lno = setlineno(n); - - inlnodelist(n->ninit); - for(l=n->ninit; l; l=l->next) - if(l->n->op == OINLCALL) - inlconv2stmt(l->n); - - inlnode(&n->left); - if(n->left && n->left->op == OINLCALL) - inlconv2expr(&n->left); - - inlnode(&n->right); - if(n->right && n->right->op == OINLCALL) - inlconv2expr(&n->right); - - inlnodelist(n->list); - switch(n->op) { - case OBLOCK: - for(l=n->list; l; l=l->next) - if(l->n->op == OINLCALL) - inlconv2stmt(l->n); - break; - - case ORETURN: - case OCALLFUNC: - case OCALLMETH: - case OCALLINTER: - case OAPPEND: - case OCOMPLEX: - // if we just replaced arg in f(arg()) or return arg with an inlined call - // and arg returns multiple values, glue as list - if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) { - n->list = inlconv2list(n->list->n); - break; - } - - // fallthrough - default: - for(l=n->list; l; l=l->next) - if(l->n->op == OINLCALL) - inlconv2expr(&l->n); - } - - inlnodelist(n->rlist); - switch(n->op) { - case OAS2FUNC: - if(n->rlist->n->op == OINLCALL) { - n->rlist = inlconv2list(n->rlist->n); - n->op = OAS2; - n->typecheck = 0; - typecheck(np, Etop); - break; - } - - // fallthrough - default: - for(l=n->rlist; l; l=l->next) - if(l->n->op == OINLCALL) - inlconv2expr(&l->n); - - } - - inlnode(&n->ntest); - if(n->ntest && n->ntest->op == OINLCALL) - inlconv2expr(&n->ntest); - - inlnode(&n->nincr); - if(n->nincr && n->nincr->op == OINLCALL) - inlconv2stmt(n->nincr); - - inlnodelist(n->nbody); - for(l=n->nbody; l; l=l->next) - if(l->n->op == OINLCALL) - inlconv2stmt(l->n); - - inlnodelist(n->nelse); - for(l=n->nelse; l; l=l->next) - if(l->n->op == OINLCALL) - inlconv2stmt(l->n); - - // with all the branches out of the way, it is now time to - // transmogrify this node itself unless inhibited by the - // switch at the top of this function. - switch(n->op) { - case OCALLFUNC: - case OCALLMETH: - if (n->etype == OPROC || n->etype == ODEFER) - return; - } - - switch(n->op) { - case OCALLFUNC: - if(debug['m']>3) - print("%L:call to func %+N\n", n->lineno, n->left); - if(n->left->inl) // normal case - mkinlcall(np, n->left, n->isddd); - 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 - if(n->left->sym->def) - mkinlcall(np, n->left->sym->def, n->isddd); - break; - - case OCALLMETH: - if(debug['m']>3) - print("%L:call to meth %lN\n", n->lineno, n->left->right); - // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function. - if(n->left->type == T) - fatal("no function type for [%p] %+N\n", n->left, n->left); - - if(n->left->type->nname == N) - fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type); - - mkinlcall(np, n->left->type->nname, n->isddd); - - break; - } - - lineno = lno; -} - -static void mkinlcall1(Node **np, Node *fn, int isddd); - -static void -mkinlcall(Node **np, Node *fn, int isddd) -{ - int save_safemode; - Pkg *pkg; - - save_safemode = safemode; - - // imported functions may refer to unsafe as long as the - // package was marked safe during import (already checked). - pkg = fnpkg(fn); - if(pkg != localpkg && pkg != nil) - safemode = 0; - mkinlcall1(np, fn, isddd); - safemode = save_safemode; -} - -static Node* -tinlvar(Type *t) -{ - if(t->nname && !isblank(t->nname)) { - if(!t->nname->inlvar) - fatal("missing inlvar for %N\n", t->nname); - return t->nname->inlvar; - } - typecheck(&nblank, Erv | Easgn); - return nblank; -} - -static int inlgen; - -// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL. -// On return ninit has the parameter assignments, the nbody is the -// inlined function body and list, rlist contain the input, output -// parameters. -static void -mkinlcall1(Node **np, Node *fn, int isddd) -{ - int i; - int chkargcount; - Node *n, *call, *saveinlfn, *as, *m; - NodeList *dcl, *ll, *ninit, *body; - Type *t; - // For variadic fn. - int variadic, varargcount, multiret; - Node *vararg; - NodeList *varargs; - Type *varargtype, *vararrtype; - - if (fn->inl == nil) - return; - - if (fn == curfn || fn->defn == curfn) - return; - - if(debug['l']<2) - typecheckinl(fn); - - n = *np; - - // Bingo, we have a function node, and it has an inlineable body - if(debug['m']>1) - print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl); - else if(debug['m']) - print("%L: inlining call to %N\n", n->lineno, fn); - - if(debug['m']>2) - print("%L: Before inlining: %+N\n", n->lineno, n); - - saveinlfn = inlfn; - inlfn = fn; - - ninit = n->ninit; - -//dumplist("ninit pre", ninit); - - if(fn->defn) // local function - dcl = fn->inldcl; - else // imported function - dcl = fn->dcl; - - inlretvars = nil; - i = 0; - // Make temp names to use instead of the originals - for(ll = dcl; ll; ll=ll->next) { - if(ll->n->class == PPARAMOUT) // return values handled below. - continue; - if(ll->n->op == ONAME) { - ll->n->inlvar = inlvar(ll->n); - // Typecheck because inlvar is not necessarily a function parameter. - typecheck(&ll->n->inlvar, Erv); - if ((ll->n->class&~PHEAP) != PAUTO) - ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs - } - } - - // temporaries for return values. - for(t = getoutargx(fn->type)->type; t; t = t->down) { - if(t != T && t->nname != N && !isblank(t->nname)) { - m = inlvar(t->nname); - typecheck(&m, Erv); - t->nname->inlvar = m; - } else { - // anonymous return values, synthesize names for use in assignment that replaces return - m = retvar(t, i++); - } - ninit = list(ninit, nod(ODCL, m, N)); - inlretvars = list(inlretvars, m); - } - - // assign receiver. - if(fn->type->thistuple && n->left->op == ODOTMETH) { - // method call with a receiver. - t = getthisx(fn->type)->type; - if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) - fatal("missing inlvar for %N\n", t->nname); - if(!n->left->left) - fatal("method call without receiver: %+N", n); - if(t == T) - fatal("method call unknown receiver type: %+N", n); - as = nod(OAS, tinlvar(t), n->left->left); - if(as != N) { - typecheck(&as, Etop); - ninit = list(ninit, as); - } - } - - // check if inlined function is variadic. - variadic = 0; - varargtype = T; - varargcount = 0; - for(t=fn->type->type->down->down->type; t; t=t->down) { - if(t->isddd) { - variadic = 1; - varargtype = t->type; - } - } - // but if argument is dotted too forget about variadicity. - if(variadic && isddd) - variadic = 0; - - // check if argument is actually a returned tuple from call. - multiret = 0; - if(n->list && !n->list->next) { - switch(n->list->n->op) { - case OCALL: - case OCALLFUNC: - case OCALLINTER: - case OCALLMETH: - if(n->list->n->left->type->outtuple > 1) - multiret = n->list->n->left->type->outtuple-1; - } - } - - if(variadic) { - varargcount = count(n->list) + multiret; - if(n->left->op != ODOTMETH) - varargcount -= fn->type->thistuple; - varargcount -= fn->type->intuple - 1; - } - - // assign arguments to the parameters' temp names - as = nod(OAS2, N, N); - as->rlist = n->list; - ll = n->list; - - // TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call? - if(fn->type->thistuple && n->left->op != ODOTMETH) { - // non-method call to method - if(!n->list) - fatal("non-method call to method without first arg: %+N", n); - // append receiver inlvar to LHS. - t = getthisx(fn->type)->type; - if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar) - fatal("missing inlvar for %N\n", t->nname); - if(t == T) - fatal("method call unknown receiver type: %+N", n); - as->list = list(as->list, tinlvar(t)); - ll = ll->next; // track argument count. - } - - // append ordinary arguments to LHS. - chkargcount = n->list && n->list->next; - vararg = N; // the slice argument to a variadic call - varargs = nil; // the list of LHS names to put in vararg. - if(!chkargcount) { - // 0 or 1 expression on RHS. - for(t = getinargx(fn->type)->type; t; t=t->down) { - if(variadic && t->isddd) { - vararg = tinlvar(t); - for(i=0; i<varargcount && ll; i++) { - m = argvar(varargtype, i); - varargs = list(varargs, m); - as->list = list(as->list, m); - } - break; - } - as->list = list(as->list, tinlvar(t)); - } - } else { - // match arguments except final variadic (unless the call is dotted itself) - for(t = getinargx(fn->type)->type; t;) { - if(!ll) - break; - if(variadic && t->isddd) - break; - as->list = list(as->list, tinlvar(t)); - t=t->down; - ll=ll->next; - } - // match varargcount arguments with variadic parameters. - if(variadic && t && t->isddd) { - vararg = tinlvar(t); - for(i=0; i<varargcount && ll; i++) { - m = argvar(varargtype, i); - varargs = list(varargs, m); - as->list = list(as->list, m); - ll=ll->next; - } - if(i==varargcount) - t=t->down; - } - if(ll || t) - fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list); - } - - if (as->rlist) { - typecheck(&as, Etop); - ninit = list(ninit, as); - } - - // turn the variadic args into a slice. - if(variadic) { - as = nod(OAS, vararg, N); - if(!varargcount) { - as->right = nodnil(); - as->right->type = varargtype; - } else { - vararrtype = typ(TARRAY); - vararrtype->type = varargtype->type; - vararrtype->bound = varargcount; - - as->right = nod(OCOMPLIT, N, typenod(varargtype)); - as->right->list = varargs; - as->right = nod(OSLICE, as->right, nod(OKEY, N, N)); - } - typecheck(&as, Etop); - ninit = list(ninit, as); - } - - // zero the outparams - for(ll = inlretvars; ll; ll=ll->next) { - as = nod(OAS, ll->n, N); - typecheck(&as, Etop); - ninit = list(ninit, as); - } - - inlretlabel = newlabel(); - inlgen++; - body = inlsubstlist(fn->inl); - - body = list(body, nod(OGOTO, inlretlabel, N)); // avoid 'not used' when function doesnt have return - body = list(body, nod(OLABEL, inlretlabel, N)); - - typechecklist(body, Etop); -//dumplist("ninit post", ninit); - - call = nod(OINLCALL, N, N); - call->ninit = ninit; - call->nbody = body; - call->rlist = inlretvars; - call->type = n->type; - call->typecheck = 1; - - setlno(call, n->lineno); -//dumplist("call body", body); - - *np = call; - - inlfn = saveinlfn; - - // transitive inlining - // TODO do this pre-expansion on fn->inl directly. requires - // either supporting exporting statemetns with complex ninits - // or saving inl and making inlinl - if(debug['l'] >= 5) { - body = fn->inl; - fn->inl = nil; // prevent infinite recursion - inlnodelist(call->nbody); - for(ll=call->nbody; ll; ll=ll->next) - if(ll->n->op == OINLCALL) - inlconv2stmt(ll->n); - fn->inl = body; - } - - if(debug['m']>2) - print("%L: After inlining %+N\n\n", n->lineno, *np); - -} - -// Every time we expand a function we generate a new set of tmpnames, -// PAUTO's in the calling functions, and link them off of the -// PPARAM's, PAUTOS and PPARAMOUTs of the called function. -static Node* -inlvar(Node *var) -{ - Node *n; - - if(debug['m']>3) - print("inlvar %+N\n", var); - - n = newname(var->sym); - n->type = var->type; - n->class = PAUTO; - n->used = 1; - n->curfn = curfn; // the calling function, not the called one - n->addrtaken = var->addrtaken; - - // Esc pass wont run if we're inlining into a iface wrapper. - // Luckily, we can steal the results from the target func. - // If inlining a function defined in another package after - // escape analysis is done, treat all local vars as escaping. - // See issue 9537. - if(var->esc == EscHeap || (inl_nonlocal && var->op == ONAME)) - addrescapes(n); - - curfn->dcl = list(curfn->dcl, n); - return n; -} - -// Synthesize a variable to store the inlined function's results in. -static Node* -retvar(Type *t, int i) -{ - Node *n; - - snprint(namebuf, sizeof(namebuf), "~r%d", i); - n = newname(lookup(namebuf)); - n->type = t->type; - n->class = PAUTO; - n->used = 1; - n->curfn = curfn; // the calling function, not the called one - curfn->dcl = list(curfn->dcl, n); - return n; -} - -// Synthesize a variable to store the inlined function's arguments -// when they come from a multiple return call. -static Node* -argvar(Type *t, int i) -{ - Node *n; - - snprint(namebuf, sizeof(namebuf), "~arg%d", i); - n = newname(lookup(namebuf)); - n->type = t->type; - n->class = PAUTO; - n->used = 1; - n->curfn = curfn; // the calling function, not the called one - curfn->dcl = list(curfn->dcl, n); - return n; -} - -static Node* -newlabel(void) -{ - Node *n; - static int label; - - label++; - snprint(namebuf, sizeof(namebuf), ".inlret%.6d", label); - n = newname(lookup(namebuf)); - n->etype = 1; // flag 'safe' for escape analysis (no backjumps) - return n; -} - -// inlsubst and inlsubstlist recursively copy the body of the saved -// pristine ->inl body of the function while substituting references -// to input/output parameters with ones to the tmpnames, and -// substituting returns with assignments to the output. -static NodeList* -inlsubstlist(NodeList *ll) -{ - NodeList *l; - - l = nil; - for(; ll; ll=ll->next) - l = list(l, inlsubst(ll->n)); - return l; -} - -static Node* -inlsubst(Node *n) -{ - char *p; - Node *m, *as; - NodeList *ll; - - if(n == N) - return N; - - switch(n->op) { - case ONAME: - if(n->inlvar) { // These will be set during inlnode - if (debug['m']>2) - print ("substituting name %+N -> %+N\n", n, n->inlvar); - return n->inlvar; - } - if (debug['m']>2) - print ("not substituting name %+N\n", n); - return n; - - case OLITERAL: - case OTYPE: - return n; - - case ORETURN: - // Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function. - -// dump("Return before substitution", n); - m = nod(OGOTO, inlretlabel, N); - m->ninit = inlsubstlist(n->ninit); - - if(inlretvars && n->list) { - as = nod(OAS2, N, N); - // shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that. - for(ll=inlretvars; ll; ll=ll->next) - as->list = list(as->list, ll->n); - as->rlist = inlsubstlist(n->list); - typecheck(&as, Etop); - m->ninit = list(m->ninit, as); - } - - typechecklist(m->ninit, Etop); - typecheck(&m, Etop); -// dump("Return after substitution", m); - return m; - - case OGOTO: - case OLABEL: - m = nod(OXXX, N, N); - *m = *n; - m->ninit = nil; - p = smprint("%s·%d", n->left->sym->name, inlgen); - m->left = newname(lookup(p)); - free(p); - return m; - } - - - m = nod(OXXX, N, N); - *m = *n; - m->ninit = nil; - - if(n->op == OCLOSURE) - fatal("cannot inline function containing closure: %+N", n); - - m->left = inlsubst(n->left); - m->right = inlsubst(n->right); - m->list = inlsubstlist(n->list); - m->rlist = inlsubstlist(n->rlist); - m->ninit = concat(m->ninit, inlsubstlist(n->ninit)); - m->ntest = inlsubst(n->ntest); - m->nincr = inlsubst(n->nincr); - m->nbody = inlsubstlist(n->nbody); - m->nelse = inlsubstlist(n->nelse); - - return m; -} - -// Plaster over linenumbers -static void -setlnolist(NodeList *ll, int lno) -{ - for(;ll;ll=ll->next) - setlno(ll->n, lno); -} - -static void -setlno(Node *n, int lno) -{ - if(!n) - return; - - // don't clobber names, unless they're freshly synthesized - if(n->op != ONAME || n->lineno == 0) - n->lineno = lno; - - setlno(n->left, lno); - setlno(n->right, lno); - setlnolist(n->list, lno); - setlnolist(n->rlist, lno); - setlnolist(n->ninit, lno); - setlno(n->ntest, lno); - setlno(n->nincr, lno); - setlnolist(n->nbody, lno); - setlnolist(n->nelse, lno); -} |