From 73b83a228e61045b76b758f6948df80f7e7e32cd Mon Sep 17 00:00:00 2001 From: =?utf8?q?R=C3=A9my=20Oudompheng?= Date: Thu, 31 Jan 2013 08:40:59 +0100 Subject: [PATCH] cmd/gc: inlining of variadic functions. R=rsc, lvd, golang-dev, kardianos CC=golang-dev https://golang.org/cl/7093050 --- src/cmd/gc/inl.c | 146 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 18 deletions(-) diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c index 1a6ec3efe0..2a61b9fecc 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; ilist = 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; ilist = 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) { -- 2.48.1