Commit 847b61b5 authored by Luuk van Dijk's avatar Luuk van Dijk

gc: Escape analysis.

For now it's switch-on-and-offable with -s, and the effects can be inspected
with -m.  Defaults are the old codepaths.

R=rsc
CC=golang-dev
https://golang.org/cl/4634073
parent 52818f45
......@@ -22,6 +22,7 @@ OFILES=\
closure.$O\
const.$O\
dcl.$O\
esc.$O\
export.$O\
gen.$O\
init.$O\
......
......@@ -820,6 +820,10 @@ stotype(NodeList *l, int et, Type **t, int funarg)
f->width = BADWIDTH;
f->isddd = n->isddd;
// esc.c needs to find f given a PPARAM to add the tag.
if(funarg && n->left && n->left->class == PPARAM)
n->left->paramfld = f;
if(left != N && left->op == ONAME) {
f->nname = left;
f->embedded = n->embedded;
......
This diff is collapsed.
......@@ -55,7 +55,7 @@ allocparams(void)
}
if(n->op != ONAME || n->class != PAUTO)
continue;
if (n->xoffset != BADWIDTH)
if(n->xoffset != BADWIDTH)
continue;
if(n->type == T)
continue;
......@@ -72,6 +72,96 @@ allocparams(void)
lineno = lno;
}
/*
* the address of n has been taken and might be used after
* the current function returns. mark any local vars
* as needing to move to the heap.
*/
void
addrescapes(Node *n)
{
char buf[100];
switch(n->op) {
default:
// probably a type error already.
// dump("addrescapes", n);
break;
case ONAME:
if(n == nodfp)
break;
// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
// on PPARAM it means something different.
if(n->class == PAUTO && n->esc == EscNever)
break;
if(!debug['s'] && n->esc != EscUnknown)
fatal("without escape analysis, only PAUTO's should have esc: %N", n);
switch(n->class) {
case PPARAMREF:
addrescapes(n->defn);
break;
case PPARAM:
case PPARAMOUT:
// if func param, need separate temporary
// to hold heap pointer.
// the function type has already been checked
// (we're in the function body)
// so the param already has a valid xoffset.
// expression to refer to stack copy
n->stackparam = nod(OPARAM, n, N);
n->stackparam->type = n->type;
n->stackparam->addable = 1;
if(n->xoffset == BADWIDTH)
fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset;
// fallthrough
case PAUTO:
n->class |= PHEAP;
n->addable = 0;
n->ullman = 2;
n->xoffset = 0;
// create stack variable to hold pointer to heap
n->heapaddr = nod(ONAME, N, N);
n->heapaddr->type = ptrto(n->type);
snprint(buf, sizeof buf, "&%S", n->sym);
n->heapaddr->sym = lookup(buf);
n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
n->heapaddr->ullman = 1;
n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
if(debug['s'])
n->esc = EscHeap;
if(debug['m'])
print("%L: moved to heap: %hN\n", n->lineno, n);
break;
}
break;
case OIND:
case ODOTPTR:
break;
case ODOT:
case OINDEX:
// ODOTPTR has already been introduced,
// so these are the non-pointer ODOT and OINDEX.
// In &x[0], if x is a slice, then x does not
// escape--the pointer inside x does, but that
// is always a heap pointer anyway.
if(!isslice(n->left->type))
addrescapes(n->left);
break;
}
}
void
clearlabels(void)
{
......@@ -753,7 +843,7 @@ tempname(Node *nn, Type *t)
if(stksize < 0)
fatal("tempname not during code generation");
if (curfn == N)
if(curfn == N)
fatal("no curfn for tempname");
if(t == T) {
......@@ -772,7 +862,7 @@ tempname(Node *nn, Type *t)
n->class = PAUTO;
n->addable = 1;
n->ullman = 1;
n->noescape = 1;
n->esc = EscNever;
n->curfn = curfn;
curfn->dcl = list(curfn->dcl, n);
......
......@@ -155,7 +155,6 @@ struct Type
{
uchar etype;
uchar chan;
uchar recur; // to detect loops
uchar trecur; // to detect loops
uchar printed;
uchar embedded; // TFIELD embedded type
......@@ -203,6 +202,15 @@ struct Type
};
#define T ((Type*)0)
enum
{
EscUnknown,
EscHeap,
EscScope,
EscNone,
EscNever,
};
struct Node
{
uchar op;
......@@ -215,7 +223,7 @@ struct Node
uchar embedded; // ODCLFIELD embedded type
uchar colas; // OAS resulting from :=
uchar diag; // already printed error about this
uchar noescape; // ONAME never move to heap
uchar esc; // EscXXX
uchar funcdepth;
uchar builtin; // built-in name, like len or close
uchar walkdef;
......@@ -266,6 +274,7 @@ struct Node
Node* defn;
Node* pack; // real package for import . names
Node* curfn; // function for local variables
Type* paramfld; // TFIELD for this PPARAM
// ONAME func param with PHEAP
Node* heapaddr; // temp holding heap address of param
......@@ -279,6 +288,11 @@ struct Node
// OPACK
Pkg* pkg;
// Escape analysis.
NodeList* escflowsrc; // flow(this, src)
int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes
int escfloodgen; // increased for every flood to detect loops
Sym* sym; // various
int32 vargen; // unique name for OTYPE/ONAME
int32 lineno;
......@@ -374,7 +388,6 @@ enum
OADDR,
OANDAND,
OAPPEND,
OARRAY,
OARRAYBYTESTR, OARRAYRUNESTR,
OSTRARRAYBYTE, OSTRARRAYRUNE,
OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
......@@ -444,6 +457,7 @@ enum
// misc
ODDD,
ODDDARG,
// for back ends
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
......@@ -910,6 +924,11 @@ void typedcl2(Type *pt, Type *t);
Node* typenod(Type *t);
NodeList* variter(NodeList *vl, Node *t, NodeList *el);
/*
* esc.c
*/
void escapes(void);
/*
* export.c
*/
......@@ -927,6 +946,7 @@ Type* pkgtype(Sym *s);
/*
* gen.c
*/
void addrescapes(Node *n);
void allocparams(void);
void cgen_as(Node *nl, Node *nr);
void cgen_callmeth(Node *n, int proc);
......@@ -1050,6 +1070,7 @@ void dumptypestructs(void);
Type* methodfunc(Type *f, Type*);
Node* typename(Type *t);
Sym* typesym(Type *t);
int haspointers(Type *t);
/*
* select.c
......
......@@ -235,24 +235,24 @@ main(int argc, char *argv[])
if(debug['f'])
frame(1);
// Process top-level declarations in four phases.
// Process top-level declarations in phases.
// Phase 1: const, type, and names and types of funcs.
// This will gather all the information about types
// and methods but doesn't depend on any of it.
// Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1.
// Phase 3: Type check function bodies.
// Phase 4: Compile function bodies.
defercheckwidth();
for(l=xtop; l; l=l->next)
if(l->n->op != ODCL && l->n->op != OAS)
typecheck(&l->n, Etop);
// Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1.
for(l=xtop; l; l=l->next)
if(l->n->op == ODCL || l->n->op == OAS)
typecheck(&l->n, Etop);
resumetypecopy();
resumecheckwidth();
// Phase 3: Type check function bodies.
for(l=xtop; l; l=l->next) {
if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
curfn = l->n;
......@@ -268,6 +268,11 @@ main(int argc, char *argv[])
if(nsavederrors+nerrors)
errorexit();
// Phase 3b: escape analysis.
if(debug['s'])
escapes();
// Phase 4: Compile function bodies.
for(l=xtop; l; l=l->next)
if(l->n->op == ODCLFUNC)
funccompile(l->n, 0);
......@@ -275,6 +280,7 @@ main(int argc, char *argv[])
if(nsavederrors+nerrors == 0)
fninit(xtop);
// Phase 4b: Compile all closures.
while(closures) {
l = closures;
closures = nil;
......@@ -283,6 +289,7 @@ main(int argc, char *argv[])
}
}
// Phase 5: check external declarations.
for(l=externdcl; l; l=l->next)
if(l->n->op == ONAME)
typecheck(&l->n, Erv);
......@@ -1739,7 +1746,6 @@ lexfini(void)
}
nodfp = nod(ONAME, N, N);
nodfp->noescape = 1;
nodfp->type = types[TINT32];
nodfp->xoffset = 0;
nodfp->class = PPARAM;
......
......@@ -139,6 +139,10 @@ exprfmt(Fmt *f, Node *n, int prec)
fmtprint(f, "(%#N)", n->left);
break;
case ODDDARG:
fmtprint(f, "... argument");
break;
case OREGISTER:
fmtprint(f, "%R", n->val.u.reg);
break;
......
......@@ -528,7 +528,7 @@ typestruct(Type *t)
return pkglookup(name, typepkg);
}
static int
int
haspointers(Type *t)
{
Type *t1;
......
......@@ -59,7 +59,7 @@ typecheckselect(Node *sel)
break;
case OAS2RECV:
// convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok
// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
if(n->right->op != ORECV) {
yyerror("select assignment must have receive on right hand side");
break;
......@@ -73,6 +73,7 @@ typecheckselect(Node *sel)
case ORECV:
// convert <-c into OSELRECV(N, <-c)
n = nod(OSELRECV, N, n);
n->typecheck = 1;
ncase->left = n;
break;
......
......@@ -1094,8 +1094,8 @@ Jconv(Fmt *fp)
if(n->class != 0) {
s = "";
if (n->class & PHEAP) s = ",heap";
if ((n->class & ~PHEAP) < nelem(classnames))
if(n->class & PHEAP) s = ",heap";
if((n->class & ~PHEAP) < nelem(classnames))
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
else
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
......@@ -1107,8 +1107,29 @@ Jconv(Fmt *fp)
if(n->funcdepth != 0)
fmtprint(fp, " f(%d)", n->funcdepth);
if(n->noescape != 0)
fmtprint(fp, " ne(%d)", n->noescape);
switch(n->esc) {
case EscUnknown:
break;
case EscHeap:
fmtprint(fp, " esc(h)");
break;
case EscScope:
fmtprint(fp, " esc(s)");
break;
case EscNone:
fmtprint(fp, " esc(no)");
break;
case EscNever:
if(!c)
fmtprint(fp, " esc(N)");
break;
default:
fmtprint(fp, " esc(%d)", n->esc);
break;
}
if(n->escloopdepth)
fmtprint(fp, " ld(%d)", n->escloopdepth);
if(!c && n->typecheck != 0)
fmtprint(fp, " tc(%d)", n->typecheck);
......@@ -1523,7 +1544,7 @@ Nconv(Fmt *fp)
switch(n->op) {
default:
if (fp->flags & FmtShort)
if(fp->flags & FmtShort)
fmtprint(fp, "%O%hJ", n->op, n);
else
fmtprint(fp, "%O%J", n->op, n);
......@@ -1532,13 +1553,13 @@ Nconv(Fmt *fp)
case ONAME:
case ONONAME:
if(n->sym == S) {
if (fp->flags & FmtShort)
if(fp->flags & FmtShort)
fmtprint(fp, "%O%hJ", n->op, n);
else
fmtprint(fp, "%O%J", n->op, n);
break;
}
if (fp->flags & FmtShort)
if(fp->flags & FmtShort)
fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
else
fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
......@@ -3176,7 +3197,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
int isddd;
Val v;
if(debug['r'])
if(0 && debug['r'])
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
rcvr, method, newnam);
......@@ -3453,7 +3474,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
listsort(&l1, f);
listsort(&l2, f);
if ((*f)(l1->n, l2->n) < 0) {
if((*f)(l1->n, l2->n) < 0) {
*l = l1;
} else {
*l = l2;
......@@ -3469,7 +3490,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
// l1 is last one from l1 that is < l2
le = l1->next; // le is the rest of l1, first one that is >= l2
if (le != nil)
if(le != nil)
le->end = (*l)->end;
(*l)->end = l1; // cut *l at l1
......
......@@ -21,7 +21,6 @@ static void typecheckaste(int, Node*, int, Type*, NodeList*, char*);
static Type* lookdot1(Sym *s, Type *t, Type *f, int);
static int nokeys(NodeList*);
static void typecheckcomplit(Node**);
static void addrescapes(Node*);
static void typecheckas2(Node*);
static void typecheckas(Node*);
static void typecheckfunc(Node*);
......@@ -337,7 +336,7 @@ reswitch:
*/
case OIND:
ntop = Erv | Etype;
if(!(top & Eaddr))
if(!(top & Eaddr)) // The *x in &*x is not an indirect.
ntop |= Eindir;
l = typecheck(&n->left, ntop);
if((t = l->type) == T)
......@@ -535,7 +534,9 @@ reswitch:
l = n->left;
if((t = l->type) == T)
goto error;
if(!(top & Eindir) && !n->etype)
// top&Eindir means this is &x in *&x. (or the arg to built-in print)
// n->etype means code generator flagged it as non-escaping.
if(!(top & Eindir) && !n->etype && !debug['s'])
addrescapes(n->left);
n->type = ptrto(t);
goto ret;
......@@ -1028,6 +1029,8 @@ reswitch:
}
n->left = args->n;
n->right = args->next->n;
args = nil;
n->list = nil;
n->type = types[TINT];
typecheck(&n->left, Erv);
typecheck(&n->right, Erv);
......@@ -1038,7 +1041,7 @@ reswitch:
// copy([]byte, string)
if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
if (n->left->type->type == types[TUINT8])
if(n->left->type->type == types[TUINT8])
goto ret;
yyerror("arguments to copy have different element types: %lT and string", n->left->type);
goto error;
......@@ -1602,6 +1605,7 @@ lookdot(Node *n, Type *t, int dostrcmp)
if(!eqtype(rcvr, tt)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
checklvalue(n->left, "call pointer method on");
if(!debug['s'])
addrescapes(n->left);
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
......@@ -2156,82 +2160,6 @@ error:
lineno = lno;
}
/*
* the address of n has been taken and might be used after
* the current function returns. mark any local vars
* as needing to move to the heap.
*/
static void
addrescapes(Node *n)
{
char buf[100];
switch(n->op) {
default:
// probably a type error already.
// dump("addrescapes", n);
break;
case ONAME:
if(n->noescape)
break;
switch(n->class) {
case PPARAMREF:
addrescapes(n->defn);
break;
case PPARAM:
case PPARAMOUT:
// if func param, need separate temporary
// to hold heap pointer.
// the function type has already been checked
// (we're in the function body)
// so the param already has a valid xoffset.
// expression to refer to stack copy
n->stackparam = nod(OPARAM, n, N);
n->stackparam->type = n->type;
n->stackparam->addable = 1;
if(n->xoffset == BADWIDTH)
fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset;
n->xoffset = 0;
// fallthrough
case PAUTO:
n->class |= PHEAP;
n->addable = 0;
n->ullman = 2;
n->xoffset = 0;
// create stack variable to hold pointer to heap
n->heapaddr = nod(ONAME, N, N);
n->heapaddr->type = ptrto(n->type);
snprint(buf, sizeof buf, "&%S", n->sym);
n->heapaddr->sym = lookup(buf);
n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
n->heapaddr->ullman = 1;
n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
break;
}
break;
case OIND:
case ODOTPTR:
break;
case ODOT:
case OINDEX:
// ODOTPTR has already been introduced,
// so these are the non-pointer ODOT and OINDEX.
// In &x[0], if x is a slice, then x does not
// escape--the pointer inside x does, but that
// is always a heap pointer anyway.
if(!isslice(n->left->type))
addrescapes(n->left);
break;
}
}
/*
* lvalue etc
*/
......@@ -2462,7 +2390,6 @@ typecheckfunc(Node *n)
{
Type *t, *rcvr;
//dump("nname", n->nname);
typecheck(&n->nname, Erv | Easgn);
if((t = n->nname->type) == T)
return;
......@@ -2772,6 +2699,7 @@ typecheckdef(Node *n)
if(n->ntype != N) {
typecheck(&n->ntype, Etype);
n->type = n->ntype->type;
if(n->type == T) {
n->diag = 1;
goto ret;
......
This diff is collapsed.
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