Commit db5f9da4 authored by Russ Cox's avatar Russ Cox

gc: tweak and enable escape analysis

-s now means *disable* escape analysis.

Fix escape leaks for struct/slice/map literals.
Add ... tracking.
Rewrite new(T) and slice literal into stack allocation when safe.

Add annotations to reflect.
Reflect is too chummy with the compiler,
so changes like these affect it more than they should.

R=lvd, dave, gustavo
CC=golang-dev
https://golang.org/cl/4954043
parent 2a808826
......@@ -39,8 +39,6 @@ Flags:
show entire file path when printing line numbers in errors
-I dir1 -I dir2
add dir1 and dir2 to the list of paths to check for imported packages
-N
disable optimization
-S
write assembly language text to standard output
-u
......
This diff is collapsed.
......@@ -98,7 +98,7 @@ addrescapes(Node *n)
if(n->class == PAUTO && n->esc == EscNever)
break;
if(!debug['s'] && n->esc != EscUnknown)
if(debug['s'] && n->esc != EscUnknown)
fatal("without escape analysis, only PAUTO's should have esc: %N", n);
switch(n->class) {
......@@ -121,8 +121,8 @@ addrescapes(Node *n)
fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset;
// fallthrough
case PAUTO:
case PAUTO:
n->class |= PHEAP;
n->addable = 0;
n->ullman = 2;
......@@ -136,10 +136,8 @@ addrescapes(Node *n)
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'])
if(!debug['s'])
n->esc = EscHeap;
if(debug['m'])
print("%L: moved to heap: %hN\n", n->lineno, n);
......
......@@ -211,6 +211,18 @@ enum
struct Node
{
// Tree structure.
// Generic recursive walks should follow these fields.
Node* left;
Node* right;
Node* ntest;
Node* nincr;
NodeList* ninit;
NodeList* nbody;
NodeList* nelse;
NodeList* list;
NodeList* rlist;
uchar op;
uchar ullman; // sethi/ullman number
uchar addable; // type of addressability - 0 is not addressable
......@@ -236,26 +248,10 @@ struct Node
uchar implicit; // don't show in printout
// most nodes
Node* left;
Node* right;
Type* type;
Type* realtype; // as determined by typecheck
NodeList* list;
NodeList* rlist;
Node* orig; // original form, for printing, and tracking copies of ONAMEs
// for-body
NodeList* ninit;
Node* ntest;
Node* nincr;
NodeList* nbody;
// if-body
NodeList* nelse;
// cases
Node* ncase;
// func
Node* nname;
Node* shortname;
......@@ -289,7 +285,6 @@ struct Node
// 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
......@@ -299,9 +294,25 @@ struct Node
int32 stkdelta; // offset added by stack frame compaction phase.
int32 ostk;
int32 iota;
uint32 walkgen;
};
#define N ((Node*)0)
EXTERN int32 walkgen;
/*
* Every node has a walkgen field.
* If you want to do a traversal of a node graph that
* might contain duplicates and want to avoid
* visiting the same nodes twice, increment walkgen
* before starting. Then before processing a node, do
*
* if(n->walkgen == walkgen)
* return;
* n->walkgen = walkgen;
*
* Such a walk cannot call another such walk recursively,
* because of the use of the global walkgen.
*/
EXTERN uint32 walkgen;
struct NodeList
{
......@@ -1189,6 +1200,7 @@ uint32 typehash(Type *t);
void ullmancalc(Node *n);
void umagic(Magic *m);
void warn(char *fmt, ...);
void warnl(int line, char *fmt, ...);
void yyerror(char *fmt, ...);
void yyerrorl(int line, char *fmt, ...);
......
......@@ -78,13 +78,17 @@ usage(void)
print("flags:\n");
// -A is allow use of "any" type, for bootstrapping
print(" -I DIR search for packages in DIR\n");
print(" -L show full path in file:line prints\n");
print(" -N disable optimizer\n");
print(" -S print the assembly language\n");
print(" -V print the compiler version\n");
print(" -d print declarations\n");
print(" -e no limit on number of errors printed\n");
print(" -f print stack frame structure\n");
print(" -h panic on an error\n");
print(" -m print about moves to heap\n");
print(" -o file specify output file\n");
print(" -S print the assembly language\n");
print(" -V print the compiler version\n");
print(" -s disable escape analysis\n");
print(" -u disable package unsafe\n");
print(" -w print the parse tree after typing\n");
print(" -x print lex tokens\n");
......@@ -273,7 +277,7 @@ main(int argc, char *argv[])
errorexit();
// Phase 3b: escape analysis.
if(debug['s'])
if(!debug['s'])
escapes();
// Phase 4: Compile function bodies.
......
......@@ -410,7 +410,6 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
dowidth(t);
if(ctxt != 0) {
// put everything into static array
vstat = staticname(t, ctxt);
arraylit(ctxt, 1, n, vstat, init);
......@@ -457,9 +456,15 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
vauto = nod(OXXX, N, N);
tempname(vauto, ptrto(t));
// set auto to point at new heap (3 assign)
a = nod(ONEW, N, N);
a->list = list1(typenod(t));
// set auto to point at new temp or heap (3 assign)
if(n->esc == EscNone) {
a = nod(OXXX, N, N);
tempname(a, t);
a = nod(OADDR, a, N);
} else {
a = nod(ONEW, N, N);
a->list = list1(typenod(t));
}
a = nod(OAS, vauto, a);
typecheck(&a, Etop);
walkexpr(&a, init);
......
......@@ -211,6 +211,16 @@ warn(char *fmt, ...)
hcrash();
}
void
warnl(int line, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
adderr(line, fmt, arg);
va_end(arg);
}
void
fatal(char *fmt, ...)
{
......@@ -485,6 +495,7 @@ nod(int op, Node *nleft, Node *nright)
n->lineno = parserline();
n->xoffset = BADWIDTH;
n->orig = n;
n->curfn = curfn;
return n;
}
......
......@@ -538,7 +538,7 @@ reswitch:
goto error;
// 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'])
if(debug['s'] && !(top & Eindir) && !n->etype)
addrescapes(n->left);
n->type = ptrto(t);
goto ret;
......@@ -1607,7 +1607,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'])
if(debug['s'])
addrescapes(n->left);
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
......
......@@ -13,7 +13,7 @@ static Node* makenewvar(Type*, NodeList**, Node**);
static Node* ascompatee1(int, Node*, Node*, NodeList**);
static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**);
static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**);
static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**);
static Node* convas(Node*, NodeList**);
static void heapmoves(void);
static NodeList* paramstoheap(Type **argin, int out);
......@@ -289,7 +289,7 @@ walkstmt(Node **np)
n->list = reorder3(ll);
break;
}
ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
n->list = ll;
break;
......@@ -484,7 +484,7 @@ walkexpr(Node **np, NodeList **init)
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
goto ret;
......@@ -501,7 +501,7 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
walkexprlist(n->list, init);
ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
goto ret;
......@@ -511,8 +511,8 @@ walkexpr(Node **np, NodeList **init)
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init);
lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
ll = concat(ll, lr);
n->left->left = N;
ullmancalc(n->left);
......@@ -977,7 +977,16 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case ONEW:
n = callnew(n->type->type);
if(n->esc == EscNone && n->type->type->width < (1<<16)) {
r = nod(OXXX, N, N);
tempname(r, n->type->type);
*init = list(*init, nod(OAS, r, N)); // zero temp
r = nod(OADDR, r, N);
typecheck(&r, Erv);
n = r;
} else {
n = callnew(n->type->type);
}
goto ret;
case OCMPSTR:
......@@ -1315,21 +1324,27 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
* package all the arguments that match a ... T parameter into a []T.
*/
static NodeList*
mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int esc)
{
Node *a, *n;
Type *tslice;
tslice = typ(TARRAY);
tslice->type = l->type->type;
tslice->bound = -1;
n = nod(OCOMPLIT, N, typenod(tslice));
n->list = lr0;
typecheck(&n, Erv);
if(n->type == T)
fatal("mkdotargslice: typecheck failed");
walkexpr(&n, init);
if(count(lr0) == 0) {
n = nodnil();
n->type = tslice;
} else {
n = nod(OCOMPLIT, N, typenod(tslice));
n->list = lr0;
n->esc = esc;
typecheck(&n, Erv);
if(n->type == T)
fatal("mkdotargslice: typecheck failed");
walkexpr(&n, init);
}
a = nod(OAS, nodarg(l, fp), n);
nn = list(nn, convas(a, init));
......@@ -1393,8 +1408,9 @@ dumpnodetypes(NodeList *l, char *what)
* func(expr-list)
*/
static NodeList*
ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
{
int esc;
Type *l, *ll;
Node *r, *a;
NodeList *nn, *lr0, *alist;
......@@ -1458,7 +1474,10 @@ loop:
// normal case -- make a slice of all
// remaining arguments and pass it to
// the ddd parameter.
nn = mkdotargslice(lr, nn, l, fp, init);
esc = EscUnknown;
if(call->right)
esc = call->right->esc;
nn = mkdotargslice(lr, nn, l, fp, init, esc);
goto ret;
}
......
......@@ -322,8 +322,31 @@ func packValue(flag uint32, typ *runtime.Type, word iword) Value {
return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))}
}
var dummy struct {
b bool
x interface{}
}
// Dummy annotation marking that the value x escapes,
// for use in cases where the reflect code is so clever that
// the compiler cannot follow.
func escapes(x interface{}) {
if dummy.b {
dummy.x = x
}
}
// valueFromAddr returns a Value using the given type and address.
func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value {
// TODO(rsc): Eliminate this terrible hack.
// The escape analysis knows that addr is a pointer
// but it doesn't see addr get passed to anything
// that keeps it. packValue keeps it, but packValue
// takes a uintptr (iword(addr)), and integers (non-pointers)
// are assumed not to matter. The escapes function works
// because return values always escape (for now).
escapes(addr)
if flag&flagAddr != 0 {
// Addressable, so the internal value is
// an interface containing a pointer to the real value.
......@@ -1678,6 +1701,14 @@ func ValueOf(i interface{}) Value {
if i == nil {
return Value{}
}
// TODO(rsc): Eliminate this terrible hack.
// In the call to packValue, eface.typ doesn't escape,
// and eface.word is an integer. So it looks like
// i (= eface) doesn't escape. But really it does,
// because eface.word is actually a pointer.
escapes(i)
// For an interface value with the noAddr bit set,
// the representation is identical to an empty interface.
eface := *(*emptyInterface)(unsafe.Pointer(&i))
......
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