// 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(var->esc == EscHeap)
                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);
}