Commit 93e547a0 authored by Luuk van Dijk's avatar Luuk van Dijk

gc: softer criteria for inlinability.

R=rsc
CC=golang-dev
https://golang.org/cl/5555072
parent eaa8b30d
...@@ -7,11 +7,24 @@ ...@@ -7,11 +7,24 @@
// saves a copy of the body. Then inlcalls walks each function body to // saves a copy of the body. Then inlcalls walks each function body to
// expand calls to inlinable functions. // expand calls to inlinable functions.
// //
// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1,
// making 1 the default and -l disable. -ll and more is useful to flush out bugs.
// These additional levels (beyond -l) may be buggy and are not supported.
// 0: disabled
// 1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
// 2: early typechecking of all imported bodies
// 3:
// 4: allow non-leaf functions , (breaks runtime.Caller)
// 5: transitive inlining
//
// At some point this may get another default and become switch-offable with -N.
//
// The debug['m'] flag enables diagnostic output. a single -m is useful for verifying
// which calls get inlined or not, more is for debugging, and may go away at any point.
//
// TODO: // TODO:
// - inline functions with ... args // - inline functions with ... args
// - handle T.meth(f()) with func f() (t T, arg, arg, ) // - handle T.meth(f()) with func f() (t T, arg, arg, )
// - (limited) recursive inlining
// - it would be nice if func max(x, y int) { if x > y { return x }; return y } would be inlineable
#include <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
...@@ -20,8 +33,8 @@ ...@@ -20,8 +33,8 @@
// Used by caninl. // Used by caninl.
static Node* inlcopy(Node *n); static Node* inlcopy(Node *n);
static NodeList* inlcopylist(NodeList *ll); static NodeList* inlcopylist(NodeList *ll);
static int ishairy(Node *n); static int ishairy(Node *n, int *budget);
static int ishairylist(NodeList *ll); static int ishairylist(NodeList *ll, int *budget);
// Used by inlcalls // Used by inlcalls
static void inlnodelist(NodeList *l); static void inlnodelist(NodeList *l);
...@@ -31,7 +44,7 @@ static Node* inlvar(Node *n); ...@@ -31,7 +44,7 @@ static Node* inlvar(Node *n);
static Node* retvar(Type *n, int i); static Node* retvar(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 *ll); static NodeList* inlsubstlist(NodeList *l);
static void setlno(Node*, int); static void setlno(Node*, int);
...@@ -40,7 +53,8 @@ static Node *inlfn; // function currently being inlined ...@@ -40,7 +53,8 @@ static Node *inlfn; // function currently being inlined
static Node *inlretlabel; // target of the goto substituted in place of a return static Node *inlretlabel; // target of the goto substituted in place of a return
static NodeList *inlretvars; // temp out variables static NodeList *inlretvars; // temp out variables
// Lazy typechecking of imported bodies.
// TODO avoid redoing local functions (imporpkg would be wrong)
void void
typecheckinl(Node *fn) typecheckinl(Node *fn)
{ {
...@@ -66,34 +80,24 @@ caninl(Node *fn) ...@@ -66,34 +80,24 @@ caninl(Node *fn)
{ {
Node *savefn; Node *savefn;
Type *t; Type *t;
int budget;
if(fn->op != ODCLFUNC) if(fn->op != ODCLFUNC)
fatal("caninl %N", fn); fatal("caninl %N", fn);
if(!fn->nname) if(!fn->nname)
fatal("caninl no nname %+N", fn); fatal("caninl no nname %+N", fn);
// exactly 1 statement // If fn has no body (is defined outside of Go), cannot inline it.
if(fn->nbody == nil || fn->nbody->next != nil) if(fn->nbody == nil)
return; return;
// the single statement should be a return, an assignment or empty.
switch(fn->nbody->n->op) {
default:
return;
case ORETURN:
case OAS:
case OAS2:
case OEMPTY:
break;
}
// can't handle ... args yet // can't handle ... args yet
for(t=fn->type->type->down->down->type; t; t=t->down) for(t=fn->type->type->down->down->type; t; t=t->down)
if(t->isddd) if(t->isddd)
return; return;
// TODO Anything non-trivial budget = 40; // allowed hairyness
if(ishairy(fn)) if(ishairylist(fn->nbody, &budget))
return; return;
savefn = curfn; savefn = curfn;
...@@ -116,47 +120,64 @@ caninl(Node *fn) ...@@ -116,47 +120,64 @@ caninl(Node *fn)
// Look for anything we want to punt on. // Look for anything we want to punt on.
static int static int
ishairylist(NodeList *ll) ishairylist(NodeList *ll, int* budget)
{ {
for(;ll;ll=ll->next) for(;ll;ll=ll->next)
if(ishairy(ll->n)) if(ishairy(ll->n, budget))
return 1; return 1;
return 0; return 0;
} }
static int static int
ishairy(Node *n) ishairy(Node *n, int *budget)
{ {
if(!n) if(!n)
return 0; return 0;
// Some of these are implied by the single-assign-or-return condition in caninl, // Things that are too hairy, irrespective of the budget
// but they may stay even if that one is relaxed.
switch(n->op) { switch(n->op) {
case OCALL: case OCALL:
case OCALLFUNC: case OCALLFUNC:
case OCALLINTER: case OCALLINTER:
case OCALLMETH: case OCALLMETH:
case OCLOSURE: // TODO too hard to inlvar the PARAMREFs if(debug['l'] < 4)
case OIF: return 1;
break;
case OCLOSURE:
case ORANGE: case ORANGE:
case OFOR: case OFOR:
case OSELECT: case OSELECT:
case OSWITCH: case OSWITCH:
case OPROC: case OPROC:
case ODEFER: 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; 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;
} }
return ishairy(n->left) || (*budget)--;
ishairy(n->right) ||
ishairylist(n->list) || return *budget < 0 ||
ishairylist(n->rlist) || ishairy(n->left, budget) ||
ishairylist(n->ninit) || ishairy(n->right, budget) ||
ishairy(n->ntest) || ishairylist(n->list, budget) ||
ishairy(n->nincr) || ishairylist(n->rlist, budget) ||
ishairylist(n->nbody) || ishairylist(n->ninit, budget) ||
ishairylist(n->nelse); ishairy(n->ntest, budget) ||
ishairy(n->nincr, budget) ||
ishairylist(n->nbody, budget) ||
ishairylist(n->nelse, budget);
} }
// Inlcopy and inlcopylist recursively copy the body of a function. // Inlcopy and inlcopylist recursively copy the body of a function.
...@@ -236,39 +257,30 @@ static void ...@@ -236,39 +257,30 @@ static void
inlconv2expr(Node **np) inlconv2expr(Node **np)
{ {
Node *n, *r; Node *n, *r;
n = *np; n = *np;
r = n->rlist->n; r = n->rlist->n;
addinit(&r, concat(n->ninit, n->nbody)); addinit(&r, concat(n->ninit, n->nbody));
*np = r; *np = r;
} }
// Turn the OINLCALL in n->list into an expression list on n. // Turn the rlist (with the return values) of the OINLCALL in
// Used in return and call statements. // n into an expression list lumping the ninit and body
static void // containing the inlined statements on the first list element so
inlgluelist(Node *n) // order will be preserved Used in return, oas2func and call
{ // statements.
Node *c; static NodeList*
inlconv2list(Node *n)
c = n->list->n; // this is the OINLCALL
n->ninit = concat(n->ninit, c->ninit);
n->ninit = concat(n->ninit, c->nbody);
n->list = c->rlist;
}
// Turn the OINLCALL in n->rlist->n into an expression list on n.
// Used in OAS2FUNC.
static void
inlgluerlist(Node *n)
{ {
Node *c; NodeList *l;
c = n->rlist->n; // this is the OINLCALL if(n->op != OINLCALL || n->rlist == nil)
n->ninit = concat(n->ninit, c->ninit); fatal("inlconv2list %+N\n", n);
n->ninit = concat(n->ninit, c->nbody);
n->rlist = c->rlist; l = n->rlist;
addinit(&l->n, concat(n->ninit, n->nbody));
return l;
} }
static void static void
inlnodelist(NodeList *l) inlnodelist(NodeList *l)
{ {
...@@ -339,26 +351,18 @@ inlnode(Node **np) ...@@ -339,26 +351,18 @@ inlnode(Node **np)
break; break;
case ORETURN: case ORETURN:
if(count(n->list) == 1 && curfn->type->outtuple > 1 && n->list->n->op == OINLCALL) { case OCALLFUNC:
inlgluelist(n);
break;
}
goto list_dflt;
case OCALLMETH: case OCALLMETH:
case OCALLINTER: case OCALLINTER:
case OCALLFUNC: // if we just replaced arg in f(arg()) or return arg with an inlined call
// if we just replaced arg in f(arg()) with an inlined call
// and arg returns multiple values, glue as list // and arg returns multiple values, glue as list
if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) { if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) {
inlgluelist(n); n->list = inlconv2list(n->list->n);
break; break;
} }
// fallthrough // fallthrough
default: default:
list_dflt:
for(l=n->list; l; l=l->next) for(l=n->list; l; l=l->next)
if(l->n->op == OINLCALL) if(l->n->op == OINLCALL)
inlconv2expr(&l->n); inlconv2expr(&l->n);
...@@ -368,7 +372,7 @@ inlnode(Node **np) ...@@ -368,7 +372,7 @@ inlnode(Node **np)
switch(n->op) { switch(n->op) {
case OAS2FUNC: case OAS2FUNC:
if(n->rlist->n->op == OINLCALL) { if(n->rlist->n->op == OINLCALL) {
inlgluerlist(n); n->rlist = inlconv2list(n->rlist->n);
n->op = OAS2; n->op = OAS2;
n->typecheck = 0; n->typecheck = 0;
typecheck(np, Etop); typecheck(np, Etop);
...@@ -455,6 +459,9 @@ mkinlcall(Node **np, Node *fn) ...@@ -455,6 +459,9 @@ mkinlcall(Node **np, Node *fn)
if (fn->inl == nil) if (fn->inl == nil)
return; return;
if (fn == curfn || fn->defn == curfn)
return;
if(debug['l']<2) if(debug['l']<2)
typecheckinl(fn); typecheckinl(fn);
...@@ -591,6 +598,21 @@ mkinlcall(Node **np, Node *fn) ...@@ -591,6 +598,21 @@ mkinlcall(Node **np, Node *fn)
*np = call; *np = call;
inlfn = saveinlfn; inlfn = saveinlfn;
// transitive inlining
// TODO do this pre-expansion on fn->inl directly. requires
// either supporting exporting statemetns with complex ninits
// or saving inl and making inlinl
if(debug['l'] >= 5) {
body = fn->inl;
fn->inl = nil; // prevent infinite recursion
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) if(debug['m']>2)
print("%L: After inlining %+N\n\n", n->lineno, *np); print("%L: After inlining %+N\n\n", n->lineno, *np);
......
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