aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuuk van Dijk <lvd@golang.org>2012-01-26 17:20:48 +0100
committerLuuk van Dijk <lvd@golang.org>2012-01-26 17:20:48 +0100
commit93e547a0c2aec056027558bca5dcfa706d9f6eda (patch)
tree8abf41c8fd6c1be520cfa0c87ae1fb71390553a4
parenteaa8b30d5a73a1406b7be12346dd67f013ac8221 (diff)
downloadgo-93e547a0c2aec056027558bca5dcfa706d9f6eda.tar.gz
go-93e547a0c2aec056027558bca5dcfa706d9f6eda.zip
gc: softer criteria for inlinability.
R=rsc CC=golang-dev https://golang.org/cl/5555072
-rw-r--r--src/cmd/gc/inl.c168
1 files changed, 95 insertions, 73 deletions
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
index b8ebcbcbda..ed7a7eb959 100644
--- a/src/cmd/gc/inl.c
+++ b/src/cmd/gc/inl.c
@@ -7,11 +7,24 @@
// saves a copy of the body. Then inlcalls walks each function body to
// 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:
// - inline functions with ... args
// - 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 <libc.h>
@@ -20,8 +33,8 @@
// Used by caninl.
static Node* inlcopy(Node *n);
static NodeList* inlcopylist(NodeList *ll);
-static int ishairy(Node *n);
-static int ishairylist(NodeList *ll);
+static int ishairy(Node *n, int *budget);
+static int ishairylist(NodeList *ll, int *budget);
// Used by inlcalls
static void inlnodelist(NodeList *l);
@@ -31,7 +44,7 @@ static Node* inlvar(Node *n);
static Node* retvar(Type *n, int i);
static Node* newlabel(void);
static Node* inlsubst(Node *n);
-static NodeList* inlsubstlist(NodeList *ll);
+static NodeList* inlsubstlist(NodeList *l);
static void setlno(Node*, int);
@@ -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 NodeList *inlretvars; // temp out variables
-
+// Lazy typechecking of imported bodies.
+// TODO avoid redoing local functions (imporpkg would be wrong)
void
typecheckinl(Node *fn)
{
@@ -66,34 +80,24 @@ caninl(Node *fn)
{
Node *savefn;
Type *t;
+ int budget;
if(fn->op != ODCLFUNC)
fatal("caninl %N", fn);
if(!fn->nname)
fatal("caninl no nname %+N", fn);
- // exactly 1 statement
- if(fn->nbody == nil || fn->nbody->next != nil)
+ // If fn has no body (is defined outside of Go), cannot inline it.
+ if(fn->nbody == nil)
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
for(t=fn->type->type->down->down->type; t; t=t->down)
if(t->isddd)
return;
- // TODO Anything non-trivial
- if(ishairy(fn))
+ budget = 40; // allowed hairyness
+ if(ishairylist(fn->nbody, &budget))
return;
savefn = curfn;
@@ -116,47 +120,64 @@ caninl(Node *fn)
// Look for anything we want to punt on.
static int
-ishairylist(NodeList *ll)
+ishairylist(NodeList *ll, int* budget)
{
for(;ll;ll=ll->next)
- if(ishairy(ll->n))
+ if(ishairy(ll->n, budget))
return 1;
return 0;
}
static int
-ishairy(Node *n)
+ishairy(Node *n, int *budget)
{
if(!n)
return 0;
- // Some of these are implied by the single-assign-or-return condition in caninl,
- // but they may stay even if that one is relaxed.
+ // Things that are too hairy, irrespective of the budget
switch(n->op) {
case OCALL:
case OCALLFUNC:
case OCALLINTER:
case OCALLMETH:
- case OCLOSURE: // TODO too hard to inlvar the PARAMREFs
- case OIF:
+ if(debug['l'] < 4)
+ return 1;
+ break;
+
+ case OCLOSURE:
case ORANGE:
case OFOR:
case OSELECT:
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;
}
- return ishairy(n->left) ||
- ishairy(n->right) ||
- ishairylist(n->list) ||
- ishairylist(n->rlist) ||
- ishairylist(n->ninit) ||
- ishairy(n->ntest) ||
- ishairy(n->nincr) ||
- ishairylist(n->nbody) ||
- ishairylist(n->nelse);
+ (*budget)--;
+
+ return *budget < 0 ||
+ ishairy(n->left, budget) ||
+ ishairy(n->right, budget) ||
+ ishairylist(n->list, budget) ||
+ ishairylist(n->rlist, budget) ||
+ ishairylist(n->ninit, budget) ||
+ 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.
@@ -236,39 +257,30 @@ static void
inlconv2expr(Node **np)
{
Node *n, *r;
-
n = *np;
r = n->rlist->n;
addinit(&r, concat(n->ninit, n->nbody));
*np = r;
}
-// Turn the OINLCALL in n->list into an expression list on n.
-// Used in return and call statements.
-static void
-inlgluelist(Node *n)
-{
- Node *c;
-
- 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)
+// Turn the rlist (with the return values) of the OINLCALL in
+// n into an expression list lumping the ninit and body
+// containing the inlined statements on the first list element so
+// order will be preserved Used in return, oas2func and call
+// statements.
+static NodeList*
+inlconv2list(Node *n)
{
- Node *c;
+ NodeList *l;
- c = n->rlist->n; // this is the OINLCALL
- n->ninit = concat(n->ninit, c->ninit);
- n->ninit = concat(n->ninit, c->nbody);
- n->rlist = c->rlist;
+ if(n->op != OINLCALL || n->rlist == nil)
+ fatal("inlconv2list %+N\n", n);
+
+ l = n->rlist;
+ addinit(&l->n, concat(n->ninit, n->nbody));
+ return l;
}
-
+
static void
inlnodelist(NodeList *l)
{
@@ -339,26 +351,18 @@ inlnode(Node **np)
break;
case ORETURN:
- if(count(n->list) == 1 && curfn->type->outtuple > 1 && n->list->n->op == OINLCALL) {
- inlgluelist(n);
- break;
- }
-
- goto list_dflt;
-
+ case OCALLFUNC:
case OCALLMETH:
case OCALLINTER:
- case OCALLFUNC:
- // if we just replaced arg in f(arg()) with an inlined call
+ // if we just replaced arg in f(arg()) or return arg with an inlined call
// and arg returns multiple values, glue as list
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;
}
// fallthrough
default:
- list_dflt:
for(l=n->list; l; l=l->next)
if(l->n->op == OINLCALL)
inlconv2expr(&l->n);
@@ -368,7 +372,7 @@ inlnode(Node **np)
switch(n->op) {
case OAS2FUNC:
if(n->rlist->n->op == OINLCALL) {
- inlgluerlist(n);
+ n->rlist = inlconv2list(n->rlist->n);
n->op = OAS2;
n->typecheck = 0;
typecheck(np, Etop);
@@ -455,6 +459,9 @@ mkinlcall(Node **np, Node *fn)
if (fn->inl == nil)
return;
+ if (fn == curfn || fn->defn == curfn)
+ return;
+
if(debug['l']<2)
typecheckinl(fn);
@@ -591,6 +598,21 @@ mkinlcall(Node **np, Node *fn)
*np = call;
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)
print("%L: After inlining %+N\n\n", n->lineno, *np);