summaryrefslogtreecommitdiff
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.c259
1 files changed, 191 insertions, 68 deletions
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
index b2b1faff7..1cc13a304 100644
--- a/src/cmd/gc/inl.c
+++ b/src/cmd/gc/inl.c
@@ -13,7 +13,7 @@
// 0: disabled
// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
// 2: early typechecking of all imported bodies
-// 3:
+// 3: allow variadic functions
// 4: allow non-leaf functions , (breaks runtime.Caller)
// 5: transitive inlining
//
@@ -39,9 +39,10 @@ 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);
+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);
@@ -82,20 +83,18 @@ typecheckinl(Node *fn)
Pkg *pkg;
int save_safemode, lno;
- if(fn->typecheck)
- return;
-
lno = setlineno(fn);
- if (debug['m']>2)
- print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl);
-
- // typecheckinl is only used for imported functions;
+ // 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)
- fatal("typecheckinl on local function %lN", fn);
+ 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;
@@ -103,7 +102,6 @@ typecheckinl(Node *fn)
savefn = curfn;
curfn = fn;
typechecklist(fn->inl, Etop);
- fn->typecheck = 1;
curfn = savefn;
safemode = save_safemode;
@@ -111,10 +109,9 @@ typecheckinl(Node *fn)
lineno = lno;
}
-// Caninl determines whether fn is inlineable. Currently that means:
-// fn is exactly 1 statement, either a return or an assignment, and
-// some temporary constraints marked TODO. If fn is inlineable, saves
-// fn->nbody in fn->inl and substitutes it with a copy.
+// 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)
{
@@ -131,10 +128,14 @@ caninl(Node *fn)
if(fn->nbody == nil)
return;
+ if(fn->typecheck == 0)
+ fatal("caninl on non-typechecked function %N", fn);
+
// can't handle ... args yet
- for(t=fn->type->type->down->down->type; t; t=t->down)
- if(t->isddd)
- return;
+ 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))
@@ -145,8 +146,6 @@ caninl(Node *fn)
fn->nname->inl = fn->nbody;
fn->nbody = inlcopylist(fn->nname->inl);
- // nbody will have been typechecked, so we can set this:
- fn->typecheck = 1;
// 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
@@ -195,19 +194,11 @@ ishairy(Node *n, int *budget)
case OSWITCH:
case OPROC:
case ODEFER:
- case ODCL: // declares locals as globals b/c of @"". qualification
case ODCLTYPE: // can't print yet
case ODCLCONST: // can't print yet
return 1;
break;
- case OAS:
- // x = <N> zero initializing assignments aren't representible in export yet.
- // alternatively we may just skip them in printing and hope their DCL printed
- // as a var will regenerate it
- if(n->right == N)
- return 1;
- break;
}
(*budget)--;
@@ -366,8 +357,8 @@ inlnode(Node **np)
}
case OCLOSURE:
- // TODO do them here instead of in lex.c phase 6b, so escape analysis
- // can avoid more heapmoves.
+ // TODO do them here (or earlier) instead of in walkcallclosure,
+ // so escape analysis can avoid more heapmoves.
return;
}
@@ -464,10 +455,10 @@ inlnode(Node **np)
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);
+ 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);
+ mkinlcall(np, n->left->sym->def, n->isddd);
break;
case OCALLMETH:
@@ -480,7 +471,7 @@ inlnode(Node **np)
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);
+ mkinlcall(np, n->left->type->nname, n->isddd);
break;
}
@@ -488,10 +479,10 @@ inlnode(Node **np)
lineno = lno;
}
-static void mkinlcall1(Node **np, Node *fn);
+static void mkinlcall1(Node **np, Node *fn, int isddd);
static void
-mkinlcall(Node **np, Node *fn)
+mkinlcall(Node **np, Node *fn, int isddd)
{
int save_safemode;
Pkg *pkg;
@@ -503,7 +494,7 @@ mkinlcall(Node **np, Node *fn)
pkg = fnpkg(fn);
if(pkg != localpkg && pkg != nil)
safemode = 0;
- mkinlcall1(np, fn);
+ mkinlcall1(np, fn, isddd);
safemode = save_safemode;
}
@@ -519,17 +510,25 @@ tinlvar(Type *t)
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)
+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;
@@ -556,6 +555,8 @@ mkinlcall1(Node **np, Node *fn)
ninit = n->ninit;
+//dumplist("ninit pre", ninit);
+
if (fn->defn) // local function
dcl = fn->defn->dcl;
else // imported function
@@ -567,7 +568,10 @@ mkinlcall1(Node **np, Node *fn)
for(ll = dcl; ll; ll=ll->next)
if(ll->n->op == ONAME) {
ll->n->inlvar = inlvar(ll->n);
- ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs
+ // 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
if (ll->n->class == PPARAMOUT) // we rely on the order being correct here
inlretvars = list(inlretvars, ll->n->inlvar);
}
@@ -580,49 +584,118 @@ mkinlcall1(Node **np, Node *fn)
inlretvars = list(inlretvars, m);
}
- // assign arguments to the parameters' temp names
- as = N;
- if(fn->type->thistuple) {
+ // 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->op == ODOTMETH) {
- 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);
- } else { // non-method call to method
- if(!n->list)
- fatal("non-method call to method without first arg: %+N", n);
- if(t != T)
- as = nod(OAS, tinlvar(t), n->list->n);
- }
-
+ 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);
- if(fn->type->intuple > 1 && n->list && !n->list->next) {
- // TODO check that n->list->n is a call?
- // TODO: non-method call to T.meth(f()) where f returns t, args...
- as->rlist = n->list;
- for(t = getinargx(fn->type)->type; t; t=t->down)
- as->list = list(as->list, tinlvar(t));
- } else {
- ll = n->list;
- if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method
- ll=ll->next; // was handled above in if(thistuple)
+ 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.
+ }
- for(t = getinargx(fn->type)->type; t && ll; t=t->down) {
+ // 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));
- as->rlist = list(as->rlist, ll->n);
+ 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);
}
@@ -632,6 +705,25 @@ mkinlcall1(Node **np, Node *fn)
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);
@@ -640,12 +732,14 @@ mkinlcall1(Node **np, Node *fn)
}
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;
@@ -655,6 +749,7 @@ mkinlcall1(Node **np, Node *fn)
call->typecheck = 1;
setlno(call, n->lineno);
+//dumplist("call body", body);
*np = call;
@@ -705,7 +800,24 @@ retvar(Type *t, int i)
{
Node *n;
- snprint(namebuf, sizeof(namebuf), ".r%d", i);
+ 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;
@@ -746,6 +858,7 @@ inlsubstlist(NodeList *ll)
static Node*
inlsubst(Node *n)
{
+ char *p;
Node *m, *as;
NodeList *ll;
@@ -788,6 +901,16 @@ inlsubst(Node *n)
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;
}