This source file includes following definitions.
- fnpkg
- typecheckinl
- caninl
- ishairylist
- ishairy
- inlcopylist
- inlcopy
- inlcalls
- inlconv2stmt
- inlconv2expr
- inlconv2list
- inlnodelist
- inlnode
- mkinlcall
- tinlvar
- mkinlcall1
- inlvar
- retvar
- argvar
- newlabel
- inlsubstlist
- inlsubst
- setlnolist
- setlno
#include <u.h>
#include <libc.h>
#include "go.h"
static Node* inlcopy(Node *n);
static NodeList* inlcopylist(NodeList *ll);
static int ishairy(Node *n, int *budget);
static int ishairylist(NodeList *ll, int *budget);
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);
static Node *inlfn;
static Node *inlretlabel;
static NodeList *inlretvars;
static Pkg*
fnpkg(Node *fn)
{
Type *rcvr;
if(fn->type->thistuple) {
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;
}
return fn->sym->pkg;
}
void
typecheckinl(Node *fn)
{
Node *savefn;
Pkg *pkg;
int save_safemode, lno;
lno = setlineno(fn);
pkg = fnpkg(fn);
if (pkg == localpkg || pkg == nil)
return;
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;
}
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->nbody == nil)
return;
if(fn->typecheck == 0)
fatal("caninl on non-typechecked function %N", fn);
if(debug['l'] < 3)
for(t=fn->type->type->down->down->type; t; t=t->down)
if(t->isddd)
return;
budget = 40;
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);
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;
}
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;
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:
case ODCLCONST:
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);
}
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;
}
void
inlcalls(Node *fn)
{
Node *savefn;
savefn = curfn;
curfn = fn;
inlnode(&fn);
if(fn != curfn)
fatal("inlnode replaced curfn");
curfn = savefn;
}
static void
inlconv2stmt(Node *n)
{
n->op = OBLOCK;
n->list = n->nbody;
n->nbody = nil;
n->rlist = nil;
}
static void
inlconv2expr(Node **np)
{
Node *n, *r;
n = *np;
r = n->rlist->n;
addinit(&r, concat(n->ninit, n->nbody));
*np = r;
}
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);
}
static void
inlnode(Node **np)
{
Node *n;
NodeList *l;
int lno;
if(*np == nil)
return;
n = *np;
switch(n->op) {
case ODEFER:
case OPROC:
switch(n->left->op) {
case OCALLFUNC:
case OCALLMETH:
n->left->etype = n->op;
}
case OCLOSURE:
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(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) {
n->list = inlconv2list(n->list->n);
break;
}
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;
}
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);
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)
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)
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);
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;
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;
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;
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;
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;
if(fn->defn)
dcl = fn->inldcl;
else
dcl = fn->dcl;
inlretvars = nil;
i = 0;
for(ll = dcl; ll; ll=ll->next) {
if(ll->n->class == PPARAMOUT)
continue;
if(ll->n->op == ONAME) {
ll->n->inlvar = inlvar(ll->n);
typecheck(&ll->n->inlvar, Erv);
if ((ll->n->class&~PHEAP) != PAUTO)
ninit = list(ninit, nod(ODCL, ll->n->inlvar, N));
}
}
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 {
m = retvar(t, i++);
}
ninit = list(ninit, nod(ODCL, m, N));
inlretvars = list(inlretvars, m);
}
if(fn->type->thistuple && n->left->op == ODOTMETH) {
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);
}
}
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;
}
}
if(variadic && isddd)
variadic = 0;
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;
}
as = nod(OAS2, N, N);
as->rlist = n->list;
ll = n->list;
if(fn->type->thistuple && n->left->op != ODOTMETH) {
if(!n->list)
fatal("non-method call to method without first arg: %+N", n);
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;
}
chkargcount = n->list && n->list->next;
vararg = N;
varargs = nil;
if(!chkargcount) {
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 {
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;
}
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);
}
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);
}
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));
body = list(body, nod(OLABEL, inlretlabel, N));
typechecklist(body, Etop);
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);
*np = call;
inlfn = saveinlfn;
if(debug['l'] >= 5) {
body = fn->inl;
fn->inl = nil;
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);
}
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;
n->addrtaken = var->addrtaken;
if(var->esc == EscHeap)
addrescapes(n);
curfn->dcl = list(curfn->dcl, n);
return n;
}
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;
curfn->dcl = list(curfn->dcl, n);
return n;
}
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;
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;
return n;
}
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) {
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:
m = nod(OGOTO, inlretlabel, N);
m->ninit = inlsubstlist(n->ninit);
if(inlretvars && n->list) {
as = nod(OAS2, N, N);
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);
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;
}
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;
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);
}