summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorR?my Oudompheng <oudomphe@phare.normalesup.org>2013-01-31 08:40:59 +0100
committerR?my Oudompheng <oudomphe@phare.normalesup.org>2013-01-31 08:40:59 +0100
commit347c1f1c3ce63a4b8f6b3c275d911d6a7d8c5c19 (patch)
treeb5402b2728c3b7d859e7ca17972504799952076d /src
parenteb4abd2a03b9855655bcc0e1e87a8a601b66dc90 (diff)
downloadgo-347c1f1c3ce63a4b8f6b3c275d911d6a7d8c5c19.tar.gz
cmd/gc: inlining of variadic functions.
R=rsc, lvd, golang-dev, kardianos CC=golang-dev https://codereview.appspot.com/7093050
Diffstat (limited to 'src')
-rw-r--r--src/cmd/gc/inl.c146
1 files changed, 128 insertions, 18 deletions
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
index 1a6ec3efe..2a61b9fec 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);
@@ -131,9 +132,10 @@ caninl(Node *fn)
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))
@@ -453,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:
@@ -469,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;
}
@@ -477,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;
@@ -492,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;
}
@@ -513,13 +515,18 @@ tinlvar(Type *t)
// 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;
@@ -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
as = nod(OAS2, N, N);
as->rlist = n->list;
@@ -611,21 +652,73 @@ mkinlcall1(Node **np, Node *fn)
// append ordinary arguments to LHS.
chkargcount = n->list && n->list->next;
- for(t = getinargx(fn->type)->type; t && (!chkargcount || ll); t=t->down) {
- if(chkargcount && ll) {
- // len(n->list) > 1, count arguments.
+ 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;
}
- 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) {
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);
@@ -709,6 +802,23 @@ retvar(Type *t, int i)
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)
{