mirror of
https://github.com/golang/go
synced 2024-11-25 01:17:56 -07:00
gc: inlining fixes
flag -l means: inlining on, -ll inline with early typecheck -l lazily typechecks imports on use and re-export, nicer for debugging -lm produces output suitable for errchk tests, repeated -mm... increases inl.c's verbosity export processed constants, instead of originals outparams get ->inlvar too, and initialized to zero fix shared rlist bug, that lead to typecheck messing up the patched tree properly handle non-method calls to methods T.meth(t, a...) removed embryonic code to handle closures in inlined bodies also inline calls inside closures (todo: move from phase 6b to 4) Fixes #2579. R=rsc CC=golang-dev https://golang.org/cl/5489106
This commit is contained in:
parent
5032a7dc0c
commit
97fd7d5f34
@ -104,18 +104,34 @@ reexportdep(Node *n)
|
|||||||
if(!n)
|
if(!n)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// print("reexportdep %+hN\n", n);
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
case ONAME:
|
case ONAME:
|
||||||
switch(n->class&~PHEAP) {
|
switch(n->class&~PHEAP) {
|
||||||
case PFUNC:
|
case PFUNC:
|
||||||
|
// methods will be printed along with their type
|
||||||
|
if(!n->type || n->type->thistuple > 0)
|
||||||
|
break;
|
||||||
|
// fallthrough
|
||||||
case PEXTERN:
|
case PEXTERN:
|
||||||
if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
|
if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
|
||||||
exportlist = list(exportlist, n);
|
exportlist = list(exportlist, n);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OTYPE:
|
|
||||||
case OLITERAL:
|
case OLITERAL:
|
||||||
|
t = n->type;
|
||||||
|
if(t != types[n->type->etype] && t != idealbool && t != idealstring) {
|
||||||
|
if(isptr[t->etype])
|
||||||
|
t = t->type;
|
||||||
|
if (t && t->sym && t->sym->def && t->sym->pkg != localpkg && t->sym->pkg != builtinpkg) {
|
||||||
|
// print("reexport literal type %+hN\n", t->sym->def);
|
||||||
|
exportlist = list(exportlist, t->sym->def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// fallthrough
|
||||||
|
case OTYPE:
|
||||||
if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
|
if (n->sym && n->sym->pkg != localpkg && n->sym->pkg != builtinpkg)
|
||||||
exportlist = list(exportlist, n);
|
exportlist = list(exportlist, n);
|
||||||
break;
|
break;
|
||||||
@ -176,7 +192,7 @@ dumpexportvar(Sym *s)
|
|||||||
Type *t;
|
Type *t;
|
||||||
|
|
||||||
n = s->def;
|
n = s->def;
|
||||||
typecheck(&n, Erv);
|
typecheck(&n, Erv|Ecall);
|
||||||
if(n == N || n->type == T) {
|
if(n == N || n->type == T) {
|
||||||
yyerror("variable exported but not defined: %S", s);
|
yyerror("variable exported but not defined: %S", s);
|
||||||
return;
|
return;
|
||||||
@ -187,6 +203,10 @@ dumpexportvar(Sym *s)
|
|||||||
|
|
||||||
if(t->etype == TFUNC && n->class == PFUNC) {
|
if(t->etype == TFUNC && n->class == PFUNC) {
|
||||||
if (n->inl) {
|
if (n->inl) {
|
||||||
|
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
|
||||||
|
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
|
||||||
|
if(debug['l'] < 2)
|
||||||
|
typecheckinl(n);
|
||||||
Bprint(bout, "\tfunc %#S%#hT { %#H }\n", s, t, n->inl);
|
Bprint(bout, "\tfunc %#S%#hT { %#H }\n", s, t, n->inl);
|
||||||
reexportdeplist(n->inl);
|
reexportdeplist(n->inl);
|
||||||
} else
|
} else
|
||||||
@ -243,6 +263,10 @@ dumpexporttype(Type *t)
|
|||||||
for(i=0; i<n; i++) {
|
for(i=0; i<n; i++) {
|
||||||
f = m[i];
|
f = m[i];
|
||||||
if (f->type->nname && f->type->nname->inl) { // nname was set by caninl
|
if (f->type->nname && f->type->nname->inl) { // nname was set by caninl
|
||||||
|
// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
|
||||||
|
// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
|
||||||
|
if(debug['l'] < 2)
|
||||||
|
typecheckinl(f->type->nname);
|
||||||
Bprint(bout, "\tfunc (%#T) %#hhS%#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl);
|
Bprint(bout, "\tfunc (%#T) %#hhS%#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl);
|
||||||
reexportdeplist(f->type->nname->inl);
|
reexportdeplist(f->type->nname->inl);
|
||||||
} else
|
} else
|
||||||
@ -261,7 +285,7 @@ dumpsym(Sym *s)
|
|||||||
yyerror("unknown export symbol: %S", s);
|
yyerror("unknown export symbol: %S", s);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// print("dumpsym %O %+S\n", s->def->op, s);
|
||||||
dumppkg(s->pkg);
|
dumppkg(s->pkg);
|
||||||
|
|
||||||
switch(s->def->op) {
|
switch(s->def->op) {
|
||||||
|
@ -933,6 +933,7 @@ stmtfmt(Fmt *f, Node *n)
|
|||||||
static int opprec[] = {
|
static int opprec[] = {
|
||||||
[OAPPEND] = 8,
|
[OAPPEND] = 8,
|
||||||
[OARRAYBYTESTR] = 8,
|
[OARRAYBYTESTR] = 8,
|
||||||
|
[OARRAYLIT] = 8,
|
||||||
[OCALLFUNC] = 8,
|
[OCALLFUNC] = 8,
|
||||||
[OCALLINTER] = 8,
|
[OCALLINTER] = 8,
|
||||||
[OCALLMETH] = 8,
|
[OCALLMETH] = 8,
|
||||||
@ -947,6 +948,7 @@ static int opprec[] = {
|
|||||||
[OLITERAL] = 8,
|
[OLITERAL] = 8,
|
||||||
[OMAKESLICE] = 8,
|
[OMAKESLICE] = 8,
|
||||||
[OMAKE] = 8,
|
[OMAKE] = 8,
|
||||||
|
[OMAPLIT] = 8,
|
||||||
[ONAME] = 8,
|
[ONAME] = 8,
|
||||||
[ONEW] = 8,
|
[ONEW] = 8,
|
||||||
[ONONAME] = 8,
|
[ONONAME] = 8,
|
||||||
@ -957,10 +959,14 @@ static int opprec[] = {
|
|||||||
[OPRINT] = 8,
|
[OPRINT] = 8,
|
||||||
[ORECV] = 8,
|
[ORECV] = 8,
|
||||||
[ORUNESTR] = 8,
|
[ORUNESTR] = 8,
|
||||||
[OTPAREN] = 8,
|
|
||||||
[OSTRUCTLIT] = 8,
|
[OSTRUCTLIT] = 8,
|
||||||
[OMAPLIT] = 8,
|
[OTARRAY] = 8,
|
||||||
[OARRAYLIT] = 8,
|
[OTCHAN] = 8,
|
||||||
|
[OTFUNC] = 8,
|
||||||
|
[OTINTER] = 8,
|
||||||
|
[OTMAP] = 8,
|
||||||
|
[OTPAREN] = 8,
|
||||||
|
[OTSTRUCT] = 8,
|
||||||
|
|
||||||
[OINDEXMAP] = 8,
|
[OINDEXMAP] = 8,
|
||||||
[OINDEX] = 8,
|
[OINDEX] = 8,
|
||||||
@ -1291,7 +1297,13 @@ nodefmt(Fmt *f, Node *n)
|
|||||||
Type *t;
|
Type *t;
|
||||||
|
|
||||||
t = n->type;
|
t = n->type;
|
||||||
if(n->orig != N)
|
if(n->orig == N)
|
||||||
|
fatal("node with no orig %N", n);
|
||||||
|
|
||||||
|
// we almost always want the original, except in export mode for literals
|
||||||
|
// this saves the importer some work, and avoids us having to redo some
|
||||||
|
// special casing for package unsafe
|
||||||
|
if(fmtmode != FExp || n->op != OLITERAL)
|
||||||
n = n->orig;
|
n = n->orig;
|
||||||
|
|
||||||
if(f->flags&FmtLong && t != T) {
|
if(f->flags&FmtLong && t != T) {
|
||||||
|
@ -996,6 +996,7 @@ Sym* renameinit(void);
|
|||||||
*/
|
*/
|
||||||
void caninl(Node *fn);
|
void caninl(Node *fn);
|
||||||
void inlcalls(Node *fn);
|
void inlcalls(Node *fn);
|
||||||
|
void typecheckinl(Node *fn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* lex.c
|
* lex.c
|
||||||
|
219
src/cmd/gc/inl.c
219
src/cmd/gc/inl.c
@ -7,6 +7,11 @@
|
|||||||
// 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.
|
||||||
//
|
//
|
||||||
|
// 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 <u.h>
|
||||||
#include <libc.h>
|
#include <libc.h>
|
||||||
@ -36,6 +41,22 @@ 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
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
typecheckinl(Node *fn)
|
||||||
|
{
|
||||||
|
Node *savefn;
|
||||||
|
|
||||||
|
if (debug['m']>2)
|
||||||
|
print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl);
|
||||||
|
|
||||||
|
savefn = curfn;
|
||||||
|
curfn = fn;
|
||||||
|
importpkg = fn->sym->pkg;
|
||||||
|
typechecklist(fn->inl, Etop);
|
||||||
|
importpkg = nil;
|
||||||
|
curfn = savefn;
|
||||||
|
}
|
||||||
|
|
||||||
// Caninl determines whether fn is inlineable. Currently that means:
|
// Caninl determines whether fn is inlineable. Currently that means:
|
||||||
// fn is exactly 1 statement, either a return or an assignment, and
|
// fn is exactly 1 statement, either a return or an assignment, and
|
||||||
// some temporary constraints marked TODO. If fn is inlineable, saves
|
// some temporary constraints marked TODO. If fn is inlineable, saves
|
||||||
@ -55,7 +76,7 @@ caninl(Node *fn)
|
|||||||
if(fn->nbody == nil || fn->nbody->next != nil)
|
if(fn->nbody == nil || fn->nbody->next != nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// the single statement should be a return or an assignment.
|
// the single statement should be a return, an assignment or empty.
|
||||||
switch(fn->nbody->n->op) {
|
switch(fn->nbody->n->op) {
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
@ -85,8 +106,10 @@ caninl(Node *fn)
|
|||||||
// this is so export can find the body of a method
|
// this is so export can find the body of a method
|
||||||
fn->type->nname = fn->nname;
|
fn->type->nname = fn->nname;
|
||||||
|
|
||||||
if(debug['l']>1)
|
if(debug['m'] > 1)
|
||||||
print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl);
|
print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl);
|
||||||
|
else if(debug['m'])
|
||||||
|
print("%L: can inline %N\n", fn->lineno, fn->nname);
|
||||||
|
|
||||||
curfn = savefn;
|
curfn = savefn;
|
||||||
}
|
}
|
||||||
@ -107,14 +130,21 @@ ishairy(Node *n)
|
|||||||
if(!n)
|
if(!n)
|
||||||
return 0;
|
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.
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
case OPROC:
|
|
||||||
case ODEFER:
|
|
||||||
case OCALL:
|
case OCALL:
|
||||||
case OCALLFUNC:
|
case OCALLFUNC:
|
||||||
case OCALLINTER:
|
case OCALLINTER:
|
||||||
case OCALLMETH:
|
case OCALLMETH:
|
||||||
case OCLOSURE:
|
case OCLOSURE: // TODO too hard to inlvar the PARAMREFs
|
||||||
|
case OIF:
|
||||||
|
case ORANGE:
|
||||||
|
case OFOR:
|
||||||
|
case OSELECT:
|
||||||
|
case OSWITCH:
|
||||||
|
case OPROC:
|
||||||
|
case ODEFER:
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -250,8 +280,11 @@ inlnodelist(NodeList *l)
|
|||||||
// nbody and nelse and use one of the 4 inlconv/glue functions above
|
// nbody and nelse and use one of the 4 inlconv/glue functions above
|
||||||
// to turn the OINLCALL into an expression, a statement, or patch it
|
// to turn the OINLCALL into an expression, a statement, or patch it
|
||||||
// in to this nodes list or rlist as appropriate.
|
// in to this nodes list or rlist as appropriate.
|
||||||
// NOTE it makes no sense to pass the glue functions down the recursion to the level where the OINLCALL gets created because they have to edit /this/ n,
|
// NOTE it makes no sense to pass the glue functions down the
|
||||||
// so you'd have to push that one down as well, but then you may as well do it here. so this is cleaner and shorter and less complicated.
|
// recursion to the level where the OINLCALL gets created because they
|
||||||
|
// have to edit /this/ n, so you'd have to push that one down as well,
|
||||||
|
// but then you may as well do it here. so this is cleaner and
|
||||||
|
// shorter and less complicated.
|
||||||
static void
|
static void
|
||||||
inlnode(Node **np)
|
inlnode(Node **np)
|
||||||
{
|
{
|
||||||
@ -274,7 +307,8 @@ inlnode(Node **np)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case OCLOSURE:
|
case OCLOSURE:
|
||||||
// TODO. do them here rather than in lex.c phase 6b
|
// TODO do them here instead of in lex.c phase 6b, so escape analysis
|
||||||
|
// can avoid more heapmoves.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,19 +408,27 @@ inlnode(Node **np)
|
|||||||
|
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
case OCALLFUNC:
|
case OCALLFUNC:
|
||||||
if(debug['l']>3)
|
if(debug['m']>3)
|
||||||
print("%L:call to func %lN\n", n->lineno, n->left);
|
print("%L:call to func %+N\n", n->lineno, n->left);
|
||||||
mkinlcall(np, n->left);
|
if(n->left->inl) // normal case
|
||||||
|
mkinlcall(np, n->left);
|
||||||
|
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);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCALLMETH:
|
case OCALLMETH:
|
||||||
if(debug['l']>3)
|
if(debug['m']>3)
|
||||||
print("%L:call to meth %lN\n", n->lineno, n->left->right);
|
print("%L:call to meth %lN\n", n->lineno, n->left->right);
|
||||||
// typecheck resolved ODOTMETH->type, whose nname points to the actual function.
|
// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
|
||||||
if(n->left->type->nname)
|
if(n->left->type == T)
|
||||||
mkinlcall(np, n->left->type->nname);
|
fatal("no function type for [%p] %+N\n", n->left, n->left);
|
||||||
else
|
|
||||||
|
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);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -399,20 +441,25 @@ static void
|
|||||||
mkinlcall(Node **np, Node *fn)
|
mkinlcall(Node **np, Node *fn)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
Node *n, *call, *saveinlfn, *as;
|
Node *n, *call, *saveinlfn, *as, *m;
|
||||||
NodeList *dcl, *ll, *ninit, *body;
|
NodeList *dcl, *ll, *ninit, *body;
|
||||||
Type *t;
|
Type *t;
|
||||||
|
|
||||||
if (fn->inl == nil)
|
if (fn->inl == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if(debug['l']<2)
|
||||||
|
typecheckinl(fn);
|
||||||
|
|
||||||
n = *np;
|
n = *np;
|
||||||
|
|
||||||
// Bingo, we have a function node, and it has an inlineable body
|
// Bingo, we have a function node, and it has an inlineable body
|
||||||
if(debug['l']>1)
|
if(debug['m']>1)
|
||||||
print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl);
|
print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl);
|
||||||
|
else if(debug['m'])
|
||||||
|
print("%L: inlining call to %N\n", n->lineno, fn);
|
||||||
|
|
||||||
if(debug['l']>2)
|
if(debug['m']>2)
|
||||||
print("%L: Before inlining: %+N\n", n->lineno, n);
|
print("%L: Before inlining: %+N\n", n->lineno, n);
|
||||||
|
|
||||||
saveinlfn = inlfn;
|
saveinlfn = inlfn;
|
||||||
@ -425,30 +472,56 @@ mkinlcall(Node **np, Node *fn)
|
|||||||
else // imported function
|
else // imported function
|
||||||
dcl = fn->dcl;
|
dcl = fn->dcl;
|
||||||
|
|
||||||
// Make temp names to use instead of the originals for anything but the outparams
|
inlretvars = nil;
|
||||||
|
i = 0;
|
||||||
|
// Make temp names to use instead of the originals
|
||||||
for(ll = dcl; ll; ll=ll->next)
|
for(ll = dcl; ll; ll=ll->next)
|
||||||
if(ll->n->op == ONAME && ll->n->class != PPARAMOUT) {
|
if(ll->n->op == ONAME) {
|
||||||
ll->n->inlvar = inlvar(ll->n);
|
ll->n->inlvar = inlvar(ll->n);
|
||||||
ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs
|
ninit = list(ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs
|
||||||
|
if (ll->n->class == PPARAMOUT) // we rely on the order being correct here
|
||||||
|
inlretvars = list(inlretvars, ll->n->inlvar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// anonymous return values, synthesize names for use in assignment that replaces return
|
||||||
|
if(inlretvars == nil && fn->type->outtuple > 0)
|
||||||
|
for(t = getoutargx(fn->type)->type; t; t = t->down) {
|
||||||
|
m = retvar(t, i++);
|
||||||
|
ninit = list(ninit, nod(ODCL, m, N));
|
||||||
|
inlretvars = list(inlretvars, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign arguments to the parameters' temp names
|
// assign arguments to the parameters' temp names
|
||||||
|
as = N;
|
||||||
if(fn->type->thistuple) {
|
if(fn->type->thistuple) {
|
||||||
if (!n->left->op == ODOTMETH || !n->left->left)
|
|
||||||
fatal("method call without receiver: %+N", n);
|
|
||||||
t = getthisx(fn->type)->type;
|
t = getthisx(fn->type)->type;
|
||||||
if(t != T && t->nname) {
|
|
||||||
if(!t->nname->inlvar)
|
if(t != T && t->nname != N && !t->nname->inlvar)
|
||||||
fatal("missing inlvar for %N\n", t->nname);
|
fatal("missing inlvar for %N\n", t->nname);
|
||||||
as = nod(OAS, t->nname->inlvar, n->left->left);
|
|
||||||
|
if(n->left->op == ODOTMETH) {
|
||||||
|
if (!n->left->left)
|
||||||
|
fatal("method call without receiver: %+N", n);
|
||||||
|
if(t != T && t->nname)
|
||||||
|
as = nod(OAS, t->nname->inlvar, n->left->left);
|
||||||
|
// else if !ONAME add to init anyway?
|
||||||
|
} else { // non-method call to method
|
||||||
|
if (!n->list)
|
||||||
|
fatal("non-method call to method without first arg: %+N", n);
|
||||||
|
if(t != T && t->nname)
|
||||||
|
as = nod(OAS, t->nname->inlvar, n->list->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(as != N) {
|
||||||
typecheck(&as, Etop);
|
typecheck(&as, Etop);
|
||||||
ninit = list(ninit, as);
|
ninit = list(ninit, as);
|
||||||
} // else if !ONAME add to init anyway?
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
as = nod(OAS2, N, N);
|
as = nod(OAS2, N, N);
|
||||||
if(fn->type->intuple > 1 && n->list && !n->list->next) {
|
if(fn->type->intuple > 1 && n->list && !n->list->next) {
|
||||||
// TODO check that n->list->n is a call?
|
// TODO check that n->list->n is a call?
|
||||||
|
// TODO: non-method call to T.meth(f()) where f returns t, args...
|
||||||
as->rlist = n->list;
|
as->rlist = n->list;
|
||||||
for(t = getinargx(fn->type)->type; t; t=t->down) {
|
for(t = getinargx(fn->type)->type; t; t=t->down) {
|
||||||
if(t->nname && !isblank(t->nname)) {
|
if(t->nname && !isblank(t->nname)) {
|
||||||
@ -461,6 +534,9 @@ mkinlcall(Node **np, Node *fn)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ll = n->list;
|
ll = n->list;
|
||||||
|
if(fn->type->thistuple && n->left->op != ODOTMETH) // non method call to method
|
||||||
|
ll=ll->next; // was handled above in if(thistuple)
|
||||||
|
|
||||||
for(t = getinargx(fn->type)->type; t && ll; t=t->down) {
|
for(t = getinargx(fn->type)->type; t && ll; t=t->down) {
|
||||||
if(t->nname && !isblank(t->nname)) {
|
if(t->nname && !isblank(t->nname)) {
|
||||||
if(!t->nname->inlvar)
|
if(!t->nname->inlvar)
|
||||||
@ -479,12 +555,13 @@ mkinlcall(Node **np, Node *fn)
|
|||||||
ninit = list(ninit, as);
|
ninit = list(ninit, as);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make the outparams. No need to declare because currently they'll only be used in the assignment that replaces returns.
|
// zero the outparams
|
||||||
inlretvars = nil;
|
for(ll = inlretvars; ll; ll=ll->next) {
|
||||||
i = 0;
|
as = nod(OAS, ll->n, N);
|
||||||
for(t = getoutargx(fn->type)->type; t; t = t->down)
|
typecheck(&as, Etop);
|
||||||
inlretvars = list(inlretvars, retvar(t, i++));
|
ninit = list(ninit, as);
|
||||||
|
}
|
||||||
|
|
||||||
inlretlabel = newlabel();
|
inlretlabel = newlabel();
|
||||||
body = inlsubstlist(fn->inl);
|
body = inlsubstlist(fn->inl);
|
||||||
|
|
||||||
@ -505,7 +582,7 @@ mkinlcall(Node **np, Node *fn)
|
|||||||
*np = call;
|
*np = call;
|
||||||
|
|
||||||
inlfn = saveinlfn;
|
inlfn = saveinlfn;
|
||||||
if(debug['l']>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);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -518,7 +595,7 @@ inlvar(Node *var)
|
|||||||
{
|
{
|
||||||
Node *n;
|
Node *n;
|
||||||
|
|
||||||
if(debug['l']>3)
|
if(debug['m']>3)
|
||||||
print("inlvar %+N\n", var);
|
print("inlvar %+N\n", var);
|
||||||
|
|
||||||
n = newname(var->sym);
|
n = newname(var->sym);
|
||||||
@ -530,29 +607,6 @@ inlvar(Node *var)
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a new pparamref
|
|
||||||
static Node*
|
|
||||||
inlref(Node *var)
|
|
||||||
{
|
|
||||||
Node *n;
|
|
||||||
|
|
||||||
if (!var->closure)
|
|
||||||
fatal("No ->closure: %N", var);
|
|
||||||
|
|
||||||
if (!var->closure->inlvar)
|
|
||||||
fatal("No ->closure->inlref: %N", var);
|
|
||||||
|
|
||||||
n = nod(OXXX, N, N);
|
|
||||||
*n = *var;
|
|
||||||
|
|
||||||
// if(debug['l']>1)
|
|
||||||
// print("inlref: %N -> %N\n", var, var->closure->inlvar);
|
|
||||||
|
|
||||||
var = var->closure->inlvar;
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Synthesize a variable to store the inlined function's results in.
|
// Synthesize a variable to store the inlined function's results in.
|
||||||
static Node*
|
static Node*
|
||||||
retvar(Type *t, int i)
|
retvar(Type *t, int i)
|
||||||
@ -597,8 +651,6 @@ inlsubstlist(NodeList *ll)
|
|||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int closuredepth;
|
|
||||||
|
|
||||||
static Node*
|
static Node*
|
||||||
inlsubst(Node *n)
|
inlsubst(Node *n)
|
||||||
{
|
{
|
||||||
@ -611,12 +663,12 @@ inlsubst(Node *n)
|
|||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
case ONAME:
|
case ONAME:
|
||||||
if(n->inlvar) { // These will be set during inlnode
|
if(n->inlvar) { // These will be set during inlnode
|
||||||
if (debug['l']>2)
|
if (debug['m']>2)
|
||||||
print ("substituting name %N -> %N\n", n, n->inlvar);
|
print ("substituting name %+N -> %+N\n", n, n->inlvar);
|
||||||
return n->inlvar;
|
return n->inlvar;
|
||||||
}
|
}
|
||||||
if (debug['l']>2)
|
if (debug['m']>2)
|
||||||
print ("not substituting name %N\n", n);
|
print ("not substituting name %+N\n", n);
|
||||||
return n;
|
return n;
|
||||||
|
|
||||||
case OLITERAL:
|
case OLITERAL:
|
||||||
@ -624,26 +676,17 @@ inlsubst(Node *n)
|
|||||||
return n;
|
return n;
|
||||||
|
|
||||||
case ORETURN:
|
case ORETURN:
|
||||||
// only rewrite returns belonging to this function, not nested ones.
|
// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
|
||||||
if (closuredepth > 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// dump("Return before substitution", n);
|
// dump("Return before substitution", n);
|
||||||
m = nod(OGOTO, inlretlabel, N);
|
m = nod(OGOTO, inlretlabel, N);
|
||||||
m->ninit = inlsubstlist(n->ninit);
|
m->ninit = inlsubstlist(n->ninit);
|
||||||
|
|
||||||
// rewrite naked return for function with return values to return PPARAMOUTs
|
|
||||||
if(count(n->list) == 0 && inlfn->type->outtuple > 0) {
|
|
||||||
for(ll = inlfn->dcl; ll; ll=ll->next)
|
|
||||||
if(ll->n->op == ONAME && ll->n->class == PPARAMOUT)
|
|
||||||
n->list = list(n->list, ll->n);
|
|
||||||
|
|
||||||
// dump("Return naked -> dressed ", n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(inlretvars && n->list) {
|
if(inlretvars && n->list) {
|
||||||
as = nod(OAS2, N, N);
|
as = nod(OAS2, N, N);
|
||||||
as->list = inlretvars;
|
// shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that.
|
||||||
|
for(ll=inlretvars; ll; ll=ll->next)
|
||||||
|
as->list = list(as->list, ll->n);
|
||||||
as->rlist = inlsubstlist(n->list);
|
as->rlist = inlsubstlist(n->list);
|
||||||
typecheck(&as, Etop);
|
typecheck(&as, Etop);
|
||||||
m->ninit = list(m->ninit, as);
|
m->ninit = list(m->ninit, as);
|
||||||
@ -660,20 +703,9 @@ inlsubst(Node *n)
|
|||||||
*m = *n;
|
*m = *n;
|
||||||
m->ninit = nil;
|
m->ninit = nil;
|
||||||
|
|
||||||
if(n->op == OCLOSURE) {
|
if(n->op == OCLOSURE)
|
||||||
closuredepth++;
|
fatal("cannot inline function containing closure: %+N", n);
|
||||||
|
|
||||||
for(ll = m->dcl; ll; ll=ll->next)
|
|
||||||
if(ll->n->op == ONAME) {
|
|
||||||
ll->n->inlvar = inlvar(ll->n);
|
|
||||||
m->ninit = list(m->ninit, nod(ODCL, ll->n->inlvar, N)); // otherwise gen won't emit the allocations for heapallocs
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ll=m->cvars; ll; ll=ll->next)
|
|
||||||
if (ll->n->op == ONAME)
|
|
||||||
ll->n->cvars = list(ll->n->cvars, inlref(ll->n));
|
|
||||||
}
|
|
||||||
|
|
||||||
m->left = inlsubst(n->left);
|
m->left = inlsubst(n->left);
|
||||||
m->right = inlsubst(n->right);
|
m->right = inlsubst(n->right);
|
||||||
m->list = inlsubstlist(n->list);
|
m->list = inlsubstlist(n->list);
|
||||||
@ -684,9 +716,6 @@ inlsubst(Node *n)
|
|||||||
m->nbody = inlsubstlist(n->nbody);
|
m->nbody = inlsubstlist(n->nbody);
|
||||||
m->nelse = inlsubstlist(n->nelse);
|
m->nelse = inlsubstlist(n->nelse);
|
||||||
|
|
||||||
if(n->op == OCLOSURE)
|
|
||||||
closuredepth--;
|
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,20 +337,14 @@ main(int argc, char *argv[])
|
|||||||
errorexit();
|
errorexit();
|
||||||
|
|
||||||
// Phase 4: Inlining
|
// Phase 4: Inlining
|
||||||
if (debug['l']) { // TODO only if debug['l'] > 1, otherwise lazily when used.
|
if (debug['l'] > 1) {
|
||||||
// Typecheck imported function bodies
|
// Typecheck imported function bodies if debug['l'] > 1,
|
||||||
for(l=importlist; l; l=l->next) {
|
// otherwise lazily when used or re-exported.
|
||||||
if (l->n->inl == nil)
|
for(l=importlist; l; l=l->next)
|
||||||
continue;
|
if (l->n->inl) {
|
||||||
curfn = l->n;
|
saveerrors();
|
||||||
saveerrors();
|
typecheckinl(l->n);
|
||||||
importpkg = l->n->sym->pkg;
|
}
|
||||||
if (debug['l']>2)
|
|
||||||
print("typecheck import [%S] %lN { %#H }\n", l->n->sym, l->n, l->n->inl);
|
|
||||||
typechecklist(l->n->inl, Etop);
|
|
||||||
importpkg = nil;
|
|
||||||
}
|
|
||||||
curfn = nil;
|
|
||||||
|
|
||||||
if(nsavederrors+nerrors)
|
if(nsavederrors+nerrors)
|
||||||
errorexit();
|
errorexit();
|
||||||
@ -384,8 +378,11 @@ main(int argc, char *argv[])
|
|||||||
while(closures) {
|
while(closures) {
|
||||||
l = closures;
|
l = closures;
|
||||||
closures = nil;
|
closures = nil;
|
||||||
for(; l; l=l->next)
|
for(; l; l=l->next) {
|
||||||
|
if (debug['l'])
|
||||||
|
inlcalls(l->n);
|
||||||
funccompile(l->n, 1);
|
funccompile(l->n, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 7: check external declarations.
|
// Phase 7: check external declarations.
|
||||||
|
Loading…
Reference in New Issue
Block a user