aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/gc/inl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/gc/inl.c')
-rw-r--r--src/cmd/gc/inl.c986
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);
-}