Commit 73b83a22 authored by Rémy Oudompheng's avatar Rémy Oudompheng

cmd/gc: inlining of variadic functions.

R=rsc, lvd, golang-dev, kardianos
CC=golang-dev
https://golang.org/cl/7093050
parent 0e74f04a
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
// 0: disabled // 0: disabled
// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default) // 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
// 2: early typechecking of all imported bodies // 2: early typechecking of all imported bodies
// 3: // 3: allow variadic functions
// 4: allow non-leaf functions , (breaks runtime.Caller) // 4: allow non-leaf functions , (breaks runtime.Caller)
// 5: transitive inlining // 5: transitive inlining
// //
...@@ -39,9 +39,10 @@ static int ishairylist(NodeList *ll, int *budget); ...@@ -39,9 +39,10 @@ static int ishairylist(NodeList *ll, int *budget);
// Used by inlcalls // Used by inlcalls
static void inlnodelist(NodeList *l); static void inlnodelist(NodeList *l);
static void inlnode(Node **np); 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* inlvar(Node *n);
static Node* retvar(Type *n, int i); static Node* retvar(Type *n, int i);
static Node* argvar(Type *n, int i);
static Node* newlabel(void); static Node* newlabel(void);
static Node* inlsubst(Node *n); static Node* inlsubst(Node *n);
static NodeList* inlsubstlist(NodeList *l); static NodeList* inlsubstlist(NodeList *l);
...@@ -131,9 +132,10 @@ caninl(Node *fn) ...@@ -131,9 +132,10 @@ caninl(Node *fn)
fatal("caninl on non-typechecked function %N", fn); fatal("caninl on non-typechecked function %N", fn);
// can't handle ... args yet // can't handle ... args yet
for(t=fn->type->type->down->down->type; t; t=t->down) if(debug['l'] < 3)
if(t->isddd) for(t=fn->type->type->down->down->type; t; t=t->down)
return; if(t->isddd)
return;
budget = 40; // allowed hairyness budget = 40; // allowed hairyness
if(ishairylist(fn->nbody, &budget)) if(ishairylist(fn->nbody, &budget))
...@@ -453,10 +455,10 @@ inlnode(Node **np) ...@@ -453,10 +455,10 @@ inlnode(Node **np)
if(debug['m']>3) if(debug['m']>3)
print("%L:call to func %+N\n", n->lineno, n->left); print("%L:call to func %+N\n", n->lineno, n->left);
if(n->left->inl) // normal case 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 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) if(n->left->sym->def)
mkinlcall(np, n->left->sym->def); mkinlcall(np, n->left->sym->def, n->isddd);
break; break;
case OCALLMETH: case OCALLMETH:
...@@ -469,7 +471,7 @@ inlnode(Node **np) ...@@ -469,7 +471,7 @@ inlnode(Node **np)
if(n->left->type->nname == N) if(n->left->type->nname == N)
fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type); 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; break;
} }
...@@ -477,10 +479,10 @@ inlnode(Node **np) ...@@ -477,10 +479,10 @@ inlnode(Node **np)
lineno = lno; lineno = lno;
} }
static void mkinlcall1(Node **np, Node *fn); static void mkinlcall1(Node **np, Node *fn, int isddd);
static void static void
mkinlcall(Node **np, Node *fn) mkinlcall(Node **np, Node *fn, int isddd)
{ {
int save_safemode; int save_safemode;
Pkg *pkg; Pkg *pkg;
...@@ -492,7 +494,7 @@ mkinlcall(Node **np, Node *fn) ...@@ -492,7 +494,7 @@ mkinlcall(Node **np, Node *fn)
pkg = fnpkg(fn); pkg = fnpkg(fn);
if(pkg != localpkg && pkg != nil) if(pkg != localpkg && pkg != nil)
safemode = 0; safemode = 0;
mkinlcall1(np, fn); mkinlcall1(np, fn, isddd);
safemode = save_safemode; safemode = save_safemode;
} }
...@@ -513,13 +515,18 @@ tinlvar(Type *t) ...@@ -513,13 +515,18 @@ tinlvar(Type *t)
// inlined function body and list, rlist contain the input, output // inlined function body and list, rlist contain the input, output
// parameters. // parameters.
static void static void
mkinlcall1(Node **np, Node *fn) mkinlcall1(Node **np, Node *fn, int isddd)
{ {
int i; int i;
int chkargcount; int chkargcount;
Node *n, *call, *saveinlfn, *as, *m; Node *n, *call, *saveinlfn, *as, *m;
NodeList *dcl, *ll, *ninit, *body; NodeList *dcl, *ll, *ninit, *body;
Type *t; Type *t;
// For variadic fn.
int variadic, varargcount, multiret;
Node *vararg;
NodeList *varargs;
Type *varargtype, *vararrtype;
if (fn->inl == nil) if (fn->inl == nil)
return; return;
...@@ -589,6 +596,40 @@ mkinlcall1(Node **np, Node *fn) ...@@ -589,6 +596,40 @@ mkinlcall1(Node **np, Node *fn)
} }
} }
// 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 // assign arguments to the parameters' temp names
as = nod(OAS2, N, N); as = nod(OAS2, N, N);
as->rlist = n->list; as->rlist = n->list;
...@@ -611,21 +652,73 @@ mkinlcall1(Node **np, Node *fn) ...@@ -611,21 +652,73 @@ mkinlcall1(Node **np, Node *fn)
// append ordinary arguments to LHS. // append ordinary arguments to LHS.
chkargcount = n->list && n->list->next; chkargcount = n->list && n->list->next;
for(t = getinargx(fn->type)->type; t && (!chkargcount || ll); t=t->down) { vararg = N; // the slice argument to a variadic call
if(chkargcount && ll) { varargs = nil; // the list of LHS names to put in vararg.
// len(n->list) > 1, count arguments. 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; ll=ll->next;
} }
as->list = list(as->list, tinlvar(t)); // 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(chkargcount && (ll || t))
fatal("arg count mismatch: %#T vs %,H\n", getinargx(fn->type), n->list);
if (as->rlist) { if (as->rlist) {
typecheck(&as, Etop); typecheck(&as, Etop);
ninit = list(ninit, as); 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 // zero the outparams
for(ll = inlretvars; ll; ll=ll->next) { for(ll = inlretvars; ll; ll=ll->next) {
as = nod(OAS, ll->n, N); as = nod(OAS, ll->n, N);
...@@ -709,6 +802,23 @@ retvar(Type *t, int i) ...@@ -709,6 +802,23 @@ retvar(Type *t, int i)
return 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* static Node*
newlabel(void) newlabel(void)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment