mirror of
https://github.com/golang/go
synced 2024-11-25 07:07:57 -07:00
gc: simplify escape analysis recursion
Merge escassign(N, x), escassign(x, N), escexpr(n), and escstmt(n) into esc(n). 1. Renaming https://golang.org/cl/4917050/diff2/1:2001/src/cmd/gc/esc.c 2. Merging. https://golang.org/cl/4917050/diff2/2001:4001/src/cmd/gc/esc.c 3. Simplify esccall. https://golang.org/cl/4917050/diff2/4001:5002/src/cmd/gc/esc.c R=lvd CC=golang-dev https://golang.org/cl/4917050
This commit is contained in:
parent
4946b420f2
commit
42687d6ce4
560
src/cmd/gc/esc.c
560
src/cmd/gc/esc.c
@ -12,7 +12,7 @@
|
|||||||
//
|
//
|
||||||
// Flag -s disables the old codepaths and switches on the code here:
|
// Flag -s disables the old codepaths and switches on the code here:
|
||||||
//
|
//
|
||||||
// First escfunc, escstmt and escexpr recurse over the ast of each
|
// First escfunc, esc and escassign recurse over the ast of each
|
||||||
// function to dig out flow(dst,src) edges between any
|
// function to dig out flow(dst,src) edges between any
|
||||||
// pointer-containing nodes and store them in dst->escflowsrc. For
|
// pointer-containing nodes and store them in dst->escflowsrc. For
|
||||||
// variables assigned to a variable in an outer scope or used as a
|
// variables assigned to a variable in an outer scope or used as a
|
||||||
@ -32,11 +32,11 @@
|
|||||||
#include "go.h"
|
#include "go.h"
|
||||||
|
|
||||||
static void escfunc(Node *func);
|
static void escfunc(Node *func);
|
||||||
static void escstmtlist(NodeList *stmts);
|
static void esclist(NodeList *l);
|
||||||
static void escstmt(Node *stmt);
|
static void esc(Node *n);
|
||||||
static void escexpr(Node *dst, Node *expr);
|
static void escassign(Node *dst, Node *src);
|
||||||
static void escexprcall(Node *dst, Node *callexpr);
|
static void esccall(Node*);
|
||||||
static void escflows(Node* dst, Node* src);
|
static void escflows(Node *dst, Node *src);
|
||||||
static void escflood(Node *dst);
|
static void escflood(Node *dst);
|
||||||
static void escwalk(int level, Node *dst, Node *src);
|
static void escwalk(int level, Node *dst, Node *src);
|
||||||
static void esctag(Node *func);
|
static void esctag(Node *func);
|
||||||
@ -48,7 +48,7 @@ static void esctag(Node *func);
|
|||||||
// flow to.
|
// flow to.
|
||||||
static Node theSink;
|
static Node theSink;
|
||||||
|
|
||||||
static NodeList* dsts; // all dst nodes
|
static NodeList* dsts; // all dst nodes
|
||||||
static int loopdepth; // for detecting nested loop scopes
|
static int loopdepth; // for detecting nested loop scopes
|
||||||
static int pdepth; // for debug printing in recursions.
|
static int pdepth; // for debug printing in recursions.
|
||||||
static int floodgen; // loop prevention in flood/walk
|
static int floodgen; // loop prevention in flood/walk
|
||||||
@ -76,7 +76,7 @@ escapes(void)
|
|||||||
|
|
||||||
// visit the updstream of each dst, mark address nodes with
|
// visit the updstream of each dst, mark address nodes with
|
||||||
// addrescapes, mark parameters unsafe
|
// addrescapes, mark parameters unsafe
|
||||||
for (l = dsts; l; l=l->next)
|
for(l = dsts; l; l=l->next)
|
||||||
escflood(l->n);
|
escflood(l->n);
|
||||||
|
|
||||||
// for all top level functions, tag the typenodes corresponding to the param nodes
|
// for all top level functions, tag the typenodes corresponding to the param nodes
|
||||||
@ -115,53 +115,59 @@ escfunc(Node *func)
|
|||||||
|
|
||||||
// walk will take the address of cvar->closure later and assign it to cvar.
|
// walk will take the address of cvar->closure later and assign it to cvar.
|
||||||
// handle that here by linking a fake oaddr node directly to the closure.
|
// handle that here by linking a fake oaddr node directly to the closure.
|
||||||
for (ll=curfn->cvars; ll; ll=ll->next) {
|
for(ll=curfn->cvars; ll; ll=ll->next) {
|
||||||
if(ll->n->op == OXXX) // see dcl.c:398
|
if(ll->n->op == OXXX) // see dcl.c:398
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
n = nod(OADDR, ll->n->closure, N);
|
n = nod(OADDR, ll->n->closure, N);
|
||||||
n->lineno = ll->n->lineno;
|
n->lineno = ll->n->lineno;
|
||||||
typecheck(&n, Erv);
|
typecheck(&n, Erv);
|
||||||
escexpr(curfn, n);
|
escassign(curfn, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
escstmtlist(curfn->nbody);
|
esclist(curfn->nbody);
|
||||||
curfn = savefn;
|
curfn = savefn;
|
||||||
loopdepth = saveld;
|
loopdepth = saveld;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
escstmtlist(NodeList* stmts)
|
esclist(NodeList *l)
|
||||||
{
|
{
|
||||||
for(; stmts; stmts=stmts->next)
|
for(; l; l=l->next)
|
||||||
escstmt(stmts->n);
|
esc(l->n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
escstmt(Node *stmt)
|
esc(Node *n)
|
||||||
{
|
{
|
||||||
int cl, cr, lno;
|
int lno;
|
||||||
NodeList *ll, *lr;
|
NodeList *ll, *lr, *l;
|
||||||
Node *dst;
|
|
||||||
|
|
||||||
if(stmt == N)
|
if(n == N)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lno = setlineno(stmt);
|
lno = setlineno(n);
|
||||||
|
|
||||||
if(stmt->typecheck == 0 && stmt->op != ODCL) { // TODO something with OAS2
|
if(n->op == OFOR)
|
||||||
dump("escstmt missing typecheck", stmt);
|
loopdepth++;
|
||||||
fatal("missing typecheck.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common to almost all statements, and nil if n/a.
|
esclist(n->ninit);
|
||||||
escstmtlist(stmt->ninit);
|
esclist(n->list);
|
||||||
|
esclist(n->rlist);
|
||||||
|
esc(n->ntest);
|
||||||
|
esc(n->nincr);
|
||||||
|
esclist(n->nbody);
|
||||||
|
esc(n->left);
|
||||||
|
esc(n->right);
|
||||||
|
|
||||||
|
if(n->op == OFOR)
|
||||||
|
loopdepth--;
|
||||||
|
|
||||||
if(debug['m'] > 1)
|
if(debug['m'] > 1)
|
||||||
print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth,
|
print("%L:[%d] %#S esc: %#N\n", lineno, loopdepth,
|
||||||
(curfn && curfn->nname) ? curfn->nname->sym : S, stmt);
|
(curfn && curfn->nname) ? curfn->nname->sym : S, n);
|
||||||
|
|
||||||
switch(stmt->op) {
|
switch(n->op) {
|
||||||
case ODCL:
|
case ODCL:
|
||||||
case ODCLFIELD:
|
case ODCLFIELD:
|
||||||
// a declaration ties the node to the current
|
// a declaration ties the node to the current
|
||||||
@ -170,182 +176,135 @@ escstmt(Node *stmt)
|
|||||||
// escflood to avoid storing redundant information
|
// escflood to avoid storing redundant information
|
||||||
// What does have to happen here is note if the name
|
// What does have to happen here is note if the name
|
||||||
// is declared inside a looping scope.
|
// is declared inside a looping scope.
|
||||||
stmt->left->escloopdepth = loopdepth;
|
if(n->left)
|
||||||
|
n->left->escloopdepth = loopdepth;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OLABEL: // TODO: new loop/scope only if there are backjumps to it.
|
case OLABEL: // TODO: new loop/scope only if there are backjumps to it.
|
||||||
loopdepth++;
|
loopdepth++;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OBLOCK:
|
|
||||||
escstmtlist(stmt->list);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OFOR:
|
|
||||||
if(stmt->ntest != N) {
|
|
||||||
escstmtlist(stmt->ntest->ninit);
|
|
||||||
escexpr(N, stmt->ntest);
|
|
||||||
}
|
|
||||||
escstmt(stmt->nincr);
|
|
||||||
loopdepth++;
|
|
||||||
escstmtlist(stmt->nbody);
|
|
||||||
loopdepth--;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ORANGE: // for <list> = range <right> { <nbody> }
|
case ORANGE: // for <list> = range <right> { <nbody> }
|
||||||
switch(stmt->type->etype) {
|
switch(n->type->etype) {
|
||||||
case TSTRING: // never flows
|
|
||||||
escexpr(stmt->list->n, N);
|
|
||||||
if(stmt->list->next)
|
|
||||||
escexpr(stmt->list->next->n, N);
|
|
||||||
escexpr(N, stmt->right);
|
|
||||||
break;
|
|
||||||
case TARRAY: // i, v = range sliceorarray
|
case TARRAY: // i, v = range sliceorarray
|
||||||
escexpr(stmt->list->n, N);
|
if(n->list->next)
|
||||||
if(stmt->list->next)
|
escassign(n->list->next->n, n->right);
|
||||||
escexpr(stmt->list->next->n, stmt->right);
|
|
||||||
break;
|
break;
|
||||||
case TMAP: // k [, v] = range map
|
case TMAP: // k [, v] = range map
|
||||||
escexpr(stmt->list->n, stmt->right);
|
escassign(n->list->n, n->right);
|
||||||
if(stmt->list->next)
|
if(n->list->next)
|
||||||
escexpr(stmt->list->next->n, stmt->right);
|
escassign(n->list->next->n, n->right);
|
||||||
break;
|
break;
|
||||||
case TCHAN: // v = range chan
|
case TCHAN: // v = range chan
|
||||||
escexpr(stmt->list->n, stmt->right);
|
escassign(n->list->n, n->right);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
loopdepth++;
|
loopdepth++;
|
||||||
escstmtlist(stmt->nbody);
|
esclist(n->nbody);
|
||||||
loopdepth--;
|
loopdepth--;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OIF:
|
|
||||||
escexpr(N, stmt->ntest);
|
|
||||||
escstmtlist(stmt->nbody);
|
|
||||||
escstmtlist(stmt->nelse);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OSELECT:
|
|
||||||
for(ll=stmt->list; ll; ll=ll->next) { // cases
|
|
||||||
escstmt(ll->n->left);
|
|
||||||
escstmtlist(ll->n->nbody);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OSELRECV2: // v, ok := <-ch ntest:ok
|
|
||||||
escexpr(N, stmt->ntest);
|
|
||||||
// fallthrough
|
|
||||||
case OSELRECV: // v := <-ch left: v right->op = ORECV
|
case OSELRECV: // v := <-ch left: v right->op = ORECV
|
||||||
escexpr(N, stmt->left);
|
escassign(n->left, n->right);
|
||||||
escexpr(stmt->left, stmt->right);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OSWITCH:
|
case OSWITCH:
|
||||||
if(stmt->ntest && stmt->ntest->op == OTYPESW) {
|
if(n->ntest && n->ntest->op == OTYPESW) {
|
||||||
for(ll=stmt->list; ll; ll=ll->next) { // cases
|
for(ll=n->list; ll; ll=ll->next) { // cases
|
||||||
// ntest->right is the argument of the .(type),
|
// ntest->right is the argument of the .(type),
|
||||||
// ll->n->nname is the variable per case
|
// ll->n->nname is the variable per case
|
||||||
escexpr(ll->n->nname, stmt->ntest->right);
|
escassign(ll->n->nname, n->ntest->right);
|
||||||
escstmtlist(ll->n->nbody);
|
esclist(ll->n->nbody);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
escexpr(N, stmt->ntest);
|
escassign(N, n->ntest);
|
||||||
for(ll=stmt->list; ll; ll=ll->next) { // cases
|
for(ll=n->list; ll; ll=ll->next) { // cases
|
||||||
for(lr=ll->n->list; lr; lr=lr->next)
|
for(lr=ll->n->list; lr; lr=lr->next)
|
||||||
escexpr(N, lr->n);
|
escassign(N, lr->n);
|
||||||
escstmtlist(ll->n->nbody);
|
esclist(ll->n->nbody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OAS:
|
case OAS:
|
||||||
case OASOP:
|
case OASOP:
|
||||||
escexpr(stmt->left, stmt->right);
|
escassign(n->left, n->right);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// escape analysis happens after typecheck, so the
|
|
||||||
// OAS2xxx have already been substituted.
|
|
||||||
case OAS2: // x,y = a,b
|
case OAS2: // x,y = a,b
|
||||||
cl = count(stmt->list);
|
if(count(n->list) == count(n->rlist))
|
||||||
cr = count(stmt->rlist);
|
for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
|
||||||
if(cl > 1 && cr == 1) {
|
escassign(ll->n, lr->n);
|
||||||
for(ll=stmt->list; ll; ll=ll->next)
|
|
||||||
escexpr(ll->n, stmt->rlist->n);
|
|
||||||
} else {
|
|
||||||
if(cl != cr)
|
|
||||||
fatal("escstmt: bad OAS2: %N", stmt);
|
|
||||||
for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=lr->next)
|
|
||||||
escexpr(ll->n, lr->n);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OAS2RECV: // v, ok = <-ch
|
case OAS2RECV: // v, ok = <-ch
|
||||||
case OAS2MAPR: // v, ok = m[k]
|
case OAS2MAPR: // v, ok = m[k]
|
||||||
case OAS2DOTTYPE: // v, ok = x.(type)
|
case OAS2DOTTYPE: // v, ok = x.(type)
|
||||||
escexpr(stmt->list->n, stmt->rlist->n);
|
case OAS2MAPW: // m[k] = x, ok
|
||||||
escexpr(stmt->list->next->n, N);
|
escassign(n->list->n, n->rlist->n);
|
||||||
break;
|
|
||||||
|
|
||||||
case OAS2MAPW: // m[k] = x, ok.. stmt->list->n is the INDEXMAP, k is handled in escexpr(dst...)
|
|
||||||
escexpr(stmt->list->n, stmt->rlist->n);
|
|
||||||
escexpr(N, stmt->rlist->next->n);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ORECV: // unary <-ch as statement
|
|
||||||
escexpr(N, stmt->left);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OSEND: // ch <- x
|
case OSEND: // ch <- x
|
||||||
escexpr(&theSink, stmt->right); // for now. TODO escexpr(stmt->left, stmt->right);
|
escassign(&theSink, n->right); // TODO: treat as *ch = x ?
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCOPY: // todo: treat as *dst=*src instead of as dst=src
|
|
||||||
escexpr(stmt->left, stmt->right);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OAS2FUNC: // x,y,z = f()
|
|
||||||
for(ll = stmt->list; ll; ll=ll->next)
|
|
||||||
escexpr(ll->n, N);
|
|
||||||
escexpr(N, stmt->rlist->n);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OCALLINTER:
|
|
||||||
case OCALLFUNC:
|
|
||||||
case OCALLMETH:
|
|
||||||
escexpr(N, stmt);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OPROC:
|
|
||||||
case ODEFER:
|
case ODEFER:
|
||||||
// stmt->left is a (pseud)ocall, stmt->left->left is
|
if(loopdepth == 1) // top level
|
||||||
// the function being called. if this defer is at
|
break;
|
||||||
// loopdepth >1, everything leaks. TODO this is
|
// arguments leak out of scope
|
||||||
// overly conservative, it's enough if it leaks to a
|
// TODO: leak to a dummy node instead
|
||||||
// fake node at the function's top level
|
// fallthrough
|
||||||
dst = &theSink;
|
case OPROC:
|
||||||
if (stmt->op == ODEFER && loopdepth <= 1)
|
// go f(x) - f and x escape
|
||||||
dst = nil;
|
escassign(&theSink, n->left->left);
|
||||||
escexpr(dst, stmt->left->left);
|
for(ll=n->left->list; ll; ll=ll->next)
|
||||||
for(ll=stmt->left->list; ll; ll=ll->next)
|
escassign(&theSink, ll->n);
|
||||||
escexpr(dst, ll->n);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ORETURN:
|
case ORETURN:
|
||||||
for(ll=stmt->list; ll; ll=ll->next)
|
for(ll=n->list; ll; ll=ll->next)
|
||||||
escexpr(&theSink, ll->n);
|
escassign(&theSink, ll->n);
|
||||||
break;
|
|
||||||
|
|
||||||
case OCLOSE:
|
|
||||||
case OPRINT:
|
|
||||||
case OPRINTN:
|
|
||||||
escexpr(N, stmt->left);
|
|
||||||
for(ll=stmt->list; ll; ll=ll->next)
|
|
||||||
escexpr(N, ll->n);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPANIC:
|
case OPANIC:
|
||||||
// Argument could leak through recover.
|
// Argument could leak through recover.
|
||||||
escexpr(&theSink, stmt->left);
|
escassign(&theSink, n->left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCOPY:
|
||||||
|
// left leaks to right, but the return value is harmless
|
||||||
|
// TODO: treat as *dst = *src, rather than as dst = src
|
||||||
|
escassign(n->left, n->right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OAPPEND:
|
||||||
|
// See TODO for OCOPY
|
||||||
|
for(ll=n->list->next; ll; ll=ll->next)
|
||||||
|
escassign(n->list->n, ll->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCALLMETH:
|
||||||
|
case OCALLFUNC:
|
||||||
|
case OCALLINTER:
|
||||||
|
esccall(n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCONV:
|
||||||
|
case OCONVNOP:
|
||||||
|
case OCONVIFACE:
|
||||||
|
escassign(n, n->left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OARRAYLIT:
|
||||||
|
case OSTRUCTLIT:
|
||||||
|
for(l=n->list; l; l=l->next)
|
||||||
|
escassign(n, l->n->right);
|
||||||
|
break;
|
||||||
|
case OMAPLIT:
|
||||||
|
for(l=n->list; l; l=l->next) {
|
||||||
|
escassign(n, l->n->left);
|
||||||
|
escassign(n, l->n->right);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,87 +316,82 @@ escstmt(Node *stmt)
|
|||||||
// evaluated in curfn. For expr==nil, dst must still be examined for
|
// evaluated in curfn. For expr==nil, dst must still be examined for
|
||||||
// evaluations inside it (e.g *f(x) = y)
|
// evaluations inside it (e.g *f(x) = y)
|
||||||
static void
|
static void
|
||||||
escexpr(Node *dst, Node *expr)
|
escassign(Node *dst, Node *src)
|
||||||
{
|
{
|
||||||
int lno;
|
int lno;
|
||||||
NodeList *ll;
|
NodeList *ll;
|
||||||
|
|
||||||
if(isblank(dst)) dst = N;
|
if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(debug['m'] > 1)
|
||||||
|
print("%L:[%d] %#S escassign: %hN = %hN\n", lineno, loopdepth,
|
||||||
|
(curfn && curfn->nname) ? curfn->nname->sym : S, dst, src);
|
||||||
|
|
||||||
// the lhs of an assignment needs recursive analysis too
|
// the lhs of an assignment needs recursive analysis too
|
||||||
// these are the only interesting cases
|
// these are the only interesting cases
|
||||||
// todo:check channel case
|
// todo:check channel case
|
||||||
if(dst) {
|
setlineno(dst);
|
||||||
setlineno(dst);
|
|
||||||
|
|
||||||
switch(dst->op) {
|
switch(dst->op) {
|
||||||
case OINDEX:
|
case OINDEX:
|
||||||
case OSLICE:
|
case OSLICE:
|
||||||
escexpr(N, dst->right);
|
// slice: "dst[x] = src" is like *(underlying array)[x] = src
|
||||||
|
// TODO maybe this never occurs b/c of OSLICEARR and it's inserted OADDR
|
||||||
// slice: "dst[x] = src" is like *(underlying array)[x] = src
|
if(!isfixedarray(dst->left->type))
|
||||||
// TODO maybe this never occurs b/c of OSLICEARR and it's inserted OADDR
|
goto doref;
|
||||||
if(!isfixedarray(dst->left->type))
|
// fallthrough; treat "dst[x] = src" as "dst = src"
|
||||||
goto doref;
|
case ODOT: // treat "dst.x = src" as "dst = src"
|
||||||
|
escassign(dst->left, src);
|
||||||
// fallthrough; treat "dst[x] = src" as "dst = src"
|
return;
|
||||||
case ODOT: // treat "dst.x = src" as "dst = src"
|
case OINDEXMAP:
|
||||||
escexpr(dst->left, expr);
|
escassign(&theSink, dst->right); // map key is put in map
|
||||||
return;
|
// fallthrough
|
||||||
|
case OIND:
|
||||||
case OINDEXMAP:
|
case ODOTPTR:
|
||||||
escexpr(&theSink, dst->right); // map key is put in map
|
case OSLICEARR: // ->left is the OADDR of the array
|
||||||
// fallthrough
|
doref:
|
||||||
case OIND:
|
// assignment to dereferences: for now we lose track
|
||||||
case ODOTPTR:
|
escassign(&theSink, src);
|
||||||
case OSLICEARR: // ->left is the OADDR of the array
|
|
||||||
doref:
|
|
||||||
escexpr(N, dst->left);
|
|
||||||
// assignment to dereferences: for now we lose track
|
|
||||||
escexpr(&theSink, expr);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(expr == N || expr->op == ONONAME || expr->op == OXXX)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(expr->typecheck == 0 && expr->op != OKEY) {
|
|
||||||
dump("escexpr missing typecheck", expr);
|
|
||||||
fatal("Missing typecheck.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lno = setlineno(expr);
|
if(src->typecheck == 0 && src->op != OKEY) {
|
||||||
|
dump("escassign missing typecheck", src);
|
||||||
|
fatal("escassign");
|
||||||
|
}
|
||||||
|
|
||||||
|
lno = setlineno(src);
|
||||||
pdepth++;
|
pdepth++;
|
||||||
|
|
||||||
if(debug['m'] > 1)
|
switch(src->op) {
|
||||||
print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth,
|
|
||||||
(curfn && curfn->nname) ? curfn->nname->sym : S, dst,
|
|
||||||
2*pdepth, ".\t.\t.\t.\t.\t", expr);
|
|
||||||
|
|
||||||
|
|
||||||
switch(expr->op) {
|
|
||||||
case OADDR: // dst = &x
|
case OADDR: // dst = &x
|
||||||
case OIND: // dst = *x
|
case OIND: // dst = *x
|
||||||
case ODOTPTR: // dst = (*x).f
|
case ODOTPTR: // dst = (*x).f
|
||||||
// restart the recursion at x to figure out where it came from
|
|
||||||
escexpr(expr->left, expr->left);
|
|
||||||
// fallthrough
|
|
||||||
case ONAME:
|
case ONAME:
|
||||||
case OPARAM:
|
case OPARAM:
|
||||||
// loopdepth was set in the defining statement or function header
|
// loopdepth was set in the defining statement or function header
|
||||||
escflows(dst, expr);
|
escflows(dst, src);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCONV:
|
||||||
|
case OCONVIFACE:
|
||||||
|
case OCONVNOP:
|
||||||
|
case ODOT:
|
||||||
|
case ODOTTYPE:
|
||||||
|
case ODOTTYPE2:
|
||||||
|
// Conversions, field access, slice all preserve the input value.
|
||||||
|
escassign(dst, src->left);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OARRAYLIT:
|
case OARRAYLIT:
|
||||||
case OSTRUCTLIT:
|
case OSTRUCTLIT:
|
||||||
case OMAPLIT:
|
case OMAPLIT:
|
||||||
expr->escloopdepth = loopdepth;
|
src->escloopdepth = loopdepth;
|
||||||
escflows(dst, expr);
|
escflows(dst, src);
|
||||||
for(ll=expr->list; ll; ll=ll->next) {
|
for(ll=src->list; ll; ll=ll->next) {
|
||||||
escexpr(expr, ll->n->left);
|
escassign(src, ll->n->left);
|
||||||
escexpr(expr, ll->n->right);
|
escassign(src, ll->n->right);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -445,54 +399,21 @@ escexpr(Node *dst, Node *expr)
|
|||||||
case OMAKEMAP:
|
case OMAKEMAP:
|
||||||
case OMAKESLICE:
|
case OMAKESLICE:
|
||||||
case ONEW:
|
case ONEW:
|
||||||
expr->curfn = curfn; // should have been done in parse, but patch it up here.
|
src->curfn = curfn; // should have been done in parse, but patch it up here.
|
||||||
expr->escloopdepth = loopdepth;
|
src->escloopdepth = loopdepth;
|
||||||
escflows(dst, expr);
|
escflows(dst, src);
|
||||||
// first arg is type, all others need checking
|
|
||||||
for(ll=expr->list->next; ll; ll=ll->next)
|
|
||||||
escexpr(N, ll->n);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCLOSURE:
|
case OCLOSURE:
|
||||||
expr->curfn = curfn; // should have been done in parse, but patch it up here.
|
src->curfn = curfn; // should have been done in parse, but patch it up here.
|
||||||
expr->escloopdepth = loopdepth;
|
src->escloopdepth = loopdepth;
|
||||||
escflows(dst, expr);
|
escflows(dst, src);
|
||||||
escfunc(expr);
|
escfunc(src);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// end of the leaf cases. no calls to escflows() in the cases below.
|
// end of the leaf cases. no calls to escflows() in the cases below.
|
||||||
|
|
||||||
|
|
||||||
case OCONV: // unaries that pass the value through
|
|
||||||
case OCONVIFACE:
|
|
||||||
case OCONVNOP:
|
|
||||||
case ODOTTYPE:
|
|
||||||
case ODOTTYPE2:
|
|
||||||
case ORECV: // leaks the whole channel
|
|
||||||
case ODOTMETH: // expr->right is just the field or method name
|
|
||||||
case ODOTINTER:
|
|
||||||
case ODOT:
|
|
||||||
escexpr(dst, expr->left);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OCOPY:
|
|
||||||
// left leaks to right, but the return value is harmless
|
|
||||||
// TODO: treat as *dst = *src, rather than as dst = src
|
|
||||||
escexpr(expr->left, expr->right);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OAPPEND:
|
case OAPPEND:
|
||||||
// See TODO for OCOPY
|
escassign(dst, src->list->n);
|
||||||
escexpr(dst, expr->list->n);
|
|
||||||
for(ll=expr->list->next; ll; ll=ll->next)
|
|
||||||
escexpr(expr->list->n, ll->n);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OCALLMETH:
|
|
||||||
case OCALLFUNC:
|
|
||||||
case OCALLINTER:
|
|
||||||
// Moved to separate function to isolate the hair.
|
|
||||||
escexprcall(dst, expr);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OSLICEARR: // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR
|
case OSLICEARR: // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR
|
||||||
@ -501,13 +422,7 @@ escexpr(Node *dst, Node *expr)
|
|||||||
case OINDEX:
|
case OINDEX:
|
||||||
case OINDEXMAP:
|
case OINDEXMAP:
|
||||||
// the big thing flows, the keys just need checking
|
// the big thing flows, the keys just need checking
|
||||||
escexpr(dst, expr->left);
|
escassign(dst, src->left);
|
||||||
escexpr(N, expr->right); // expr->right is the OKEY
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // all other harmless leaf, unary or binary cases end up here
|
|
||||||
escexpr(N, expr->left);
|
|
||||||
escexpr(N, expr->right);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,132 +431,105 @@ escexpr(Node *dst, Node *expr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// This is a bit messier than fortunate, pulled out of escexpr's big
|
// This is a bit messier than fortunate, pulled out of escassign's big
|
||||||
// switch for clarity. We either have the paramnodes, which may be
|
// switch for clarity. We either have the paramnodes, which may be
|
||||||
// connected to other things throug flows or we have the parameter type
|
// connected to other things throug flows or we have the parameter type
|
||||||
// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
|
// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
|
||||||
// different for methods vs plain functions and for imported vs
|
// different for methods vs plain functions and for imported vs
|
||||||
// this-package
|
// this-package
|
||||||
static void
|
static void
|
||||||
escexprcall(Node *dst, Node *expr)
|
esccall(Node *n)
|
||||||
{
|
{
|
||||||
NodeList *ll, *lr;
|
NodeList *ll, *lr;
|
||||||
Node *fn;
|
Node *a, *fn;
|
||||||
Type *t, *fntype, *thisarg, *inargs;
|
Type *t, *fntype;
|
||||||
|
|
||||||
fn = nil;
|
fn = N;
|
||||||
fntype = nil;
|
fntype = T;
|
||||||
|
switch(n->op) {
|
||||||
|
default:
|
||||||
|
fatal("esccall");
|
||||||
|
|
||||||
switch(expr->op) {
|
|
||||||
case OCALLFUNC:
|
case OCALLFUNC:
|
||||||
fn = expr->left;
|
fn = n->left;
|
||||||
escexpr(N, fn);
|
|
||||||
fntype = fn->type;
|
fntype = fn->type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCALLMETH:
|
case OCALLMETH:
|
||||||
fn = expr->left->right; // ODOTxx name
|
fn = n->left->right->sym->def;
|
||||||
fn = fn->sym->def; // resolve to definition if we have it
|
|
||||||
if(fn)
|
if(fn)
|
||||||
fntype = fn->type;
|
fntype = fn->type;
|
||||||
else
|
else
|
||||||
fntype = expr->left->type;
|
fntype = n->left->type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCALLINTER:
|
case OCALLINTER:
|
||||||
|
fntype = n->left->type;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
fatal("escexprcall called with non-call expression");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fn && fn->ntype) {
|
ll = n->list;
|
||||||
if(debug['m'] > 2)
|
if(n->list != nil && n->list->next == nil) {
|
||||||
print("escexprcall: have param nodes: %N\n", fn->ntype);
|
a = n->list->n;
|
||||||
|
if(a->type->etype == TSTRUCT && a->type->funarg) {
|
||||||
if(expr->op == OCALLMETH) {
|
// f(g()).
|
||||||
if(debug['m'] > 2)
|
// Since f's arguments are g's results and
|
||||||
print("escexprcall: this: %N\n",fn->ntype->left->left);
|
// all function results escape, we're done.
|
||||||
escexpr(fn->ntype->left->left, expr->left->left);
|
ll = nil;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fn && fn->ntype) {
|
||||||
|
// Local function. Incorporate into flow graph.
|
||||||
|
|
||||||
// lr->n is the dclfield, ->left is the ONAME param node
|
// Receiver.
|
||||||
for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
|
if(n->op != OCALLFUNC)
|
||||||
if(debug['m'] > 2)
|
escassign(fn->ntype->left->left, n->left->left);
|
||||||
print("escexprcall: field param: %N\n", lr->n->left);
|
|
||||||
|
for(ll=n->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
|
||||||
if (lr->n->left)
|
if (lr->n->left)
|
||||||
escexpr(lr->n->left, ll->n);
|
escassign(lr->n->left, ll->n);
|
||||||
else
|
else
|
||||||
escexpr(&theSink, ll->n);
|
escassign(&theSink, ll->n);
|
||||||
if(lr->n->left && !lr->n->left->isddd)
|
if(lr->n->left && !lr->n->left->isddd)
|
||||||
lr=lr->next;
|
lr=lr->next;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fntype) {
|
// Imported function. Use the escape tags.
|
||||||
if(debug['m'] > 2)
|
if(n->op != OCALLFUNC) {
|
||||||
print("escexprcall: have param types: %T\n", fntype);
|
t = getthisx(fntype)->type;
|
||||||
|
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
|
||||||
if(expr->op == OCALLMETH) {
|
escassign(&theSink, n->left->left);
|
||||||
thisarg = getthisx(fntype);
|
|
||||||
t = thisarg->type;
|
|
||||||
if(debug['m'] > 2)
|
|
||||||
print("escexprcall: this: %T\n", t);
|
|
||||||
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
|
|
||||||
escexpr(&theSink, expr->left->left);
|
|
||||||
else
|
|
||||||
escexpr(N, expr->left->left);
|
|
||||||
}
|
|
||||||
|
|
||||||
inargs = getinargx(fntype);
|
|
||||||
for(ll=expr->list, t=inargs->type; ll; ll=ll->next) {
|
|
||||||
if(debug['m'] > 2)
|
|
||||||
print("escexprcall: field type: %T\n", t);
|
|
||||||
if(!t->note || strcmp(t->note->s, safetag->s))
|
|
||||||
escexpr(&theSink, ll->n);
|
|
||||||
else
|
|
||||||
escexpr(N, ll->n);
|
|
||||||
if(t->down)
|
|
||||||
t=t->down;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
|
||||||
// fallthrough if we don't have enough information:
|
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
|
||||||
// can only assume all parameters are unsafe
|
escassign(&theSink, ll->n);
|
||||||
// OCALLINTER always ends up here
|
if(t->down)
|
||||||
|
t = t->down;
|
||||||
if(debug['m']>1 && expr->op != OCALLINTER) {
|
|
||||||
// dump("escexprcall", expr);
|
|
||||||
print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
escexpr(&theSink, expr->left->left); // the this argument
|
|
||||||
for(ll=expr->list; ll; ll=ll->next)
|
|
||||||
escexpr(&theSink, ll->n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the link src->dst in dst, throwing out some quick wins.
|
// Store the link src->dst in dst, throwing out some quick wins.
|
||||||
static void
|
static void
|
||||||
escflows(Node* dst, Node* src)
|
escflows(Node *dst, Node *src)
|
||||||
{
|
{
|
||||||
if(dst == nil || src == nil || dst == src)
|
if(dst == nil || src == nil || dst == src)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Don't bother building a graph for scalars.
|
// Don't bother building a graph for scalars.
|
||||||
if (src->type && !haspointers(src->type))
|
if(src->type && !haspointers(src->type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(debug['m']>2)
|
if(debug['m']>2)
|
||||||
print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
|
print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
|
||||||
|
|
||||||
// Assignments to global variables get lumped into theSink.
|
// Assignments to global variables get lumped into theSink.
|
||||||
if (dst->op == ONAME && dst->class == PEXTERN)
|
if(dst->op == ONAME && dst->class == PEXTERN)
|
||||||
dst = &theSink;
|
dst = &theSink;
|
||||||
|
|
||||||
if (dst->escflowsrc == nil) {
|
if(dst->escflowsrc == nil) {
|
||||||
dsts = list(dsts, dst);
|
dsts = list(dsts, dst);
|
||||||
dstcount++;
|
dstcount++;
|
||||||
}
|
}
|
||||||
@ -677,7 +565,7 @@ escflood(Node *dst)
|
|||||||
(dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
|
(dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
|
||||||
dst->escloopdepth);
|
dst->escloopdepth);
|
||||||
|
|
||||||
for (l = dst->escflowsrc; l; l=l->next) {
|
for(l = dst->escflowsrc; l; l=l->next) {
|
||||||
floodgen++;
|
floodgen++;
|
||||||
escwalk(0, dst, l->n);
|
escwalk(0, dst, l->n);
|
||||||
}
|
}
|
||||||
@ -686,10 +574,10 @@ escflood(Node *dst)
|
|||||||
static void
|
static void
|
||||||
escwalk(int level, Node *dst, Node *src)
|
escwalk(int level, Node *dst, Node *src)
|
||||||
{
|
{
|
||||||
NodeList* ll;
|
NodeList *ll;
|
||||||
int leaks;
|
int leaks;
|
||||||
|
|
||||||
if (src->escfloodgen == floodgen)
|
if(src->escfloodgen == floodgen)
|
||||||
return;
|
return;
|
||||||
src->escfloodgen = floodgen;
|
src->escfloodgen = floodgen;
|
||||||
|
|
||||||
@ -704,7 +592,7 @@ escwalk(int level, Node *dst, Node *src)
|
|||||||
|
|
||||||
switch(src->op) {
|
switch(src->op) {
|
||||||
case ONAME:
|
case ONAME:
|
||||||
if (src->class == PPARAM && leaks && src->esc == EscNone) {
|
if(src->class == PPARAM && leaks && src->esc == EscNone) {
|
||||||
src->esc = EscScope;
|
src->esc = EscScope;
|
||||||
if(debug['m'])
|
if(debug['m'])
|
||||||
print("%L:leaking param: %hN\n", src->lineno, src);
|
print("%L:leaking param: %hN\n", src->lineno, src);
|
||||||
@ -712,7 +600,7 @@ escwalk(int level, Node *dst, Node *src)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OADDR:
|
case OADDR:
|
||||||
if (leaks)
|
if(leaks)
|
||||||
addrescapes(src->left);
|
addrescapes(src->left);
|
||||||
escwalk(level-1, dst, src->left);
|
escwalk(level-1, dst, src->left);
|
||||||
break;
|
break;
|
||||||
@ -727,7 +615,7 @@ escwalk(int level, Node *dst, Node *src)
|
|||||||
escwalk(level+1, dst, src->left);
|
escwalk(level+1, dst, src->left);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ll=src->escflowsrc; ll; ll=ll->next)
|
for(ll=src->escflowsrc; ll; ll=ll->next)
|
||||||
escwalk(level, dst, ll->n);
|
escwalk(level, dst, ll->n);
|
||||||
|
|
||||||
pdepth--;
|
pdepth--;
|
||||||
@ -748,7 +636,7 @@ esctag(Node *func)
|
|||||||
|
|
||||||
switch (ll->n->esc) {
|
switch (ll->n->esc) {
|
||||||
case EscNone: // not touched by escflood
|
case EscNone: // not touched by escflood
|
||||||
if (haspointers(ll->n->type)) // don't bother tagging for scalars
|
if(haspointers(ll->n->type)) // don't bother tagging for scalars
|
||||||
ll->n->paramfld->note = safetag;
|
ll->n->paramfld->note = safetag;
|
||||||
case EscHeap: // touched by escflood, moved to heap
|
case EscHeap: // touched by escflood, moved to heap
|
||||||
case EscScope: // touched by escflood, value leaves scope
|
case EscScope: // touched by escflood, value leaves scope
|
||||||
|
Loading…
Reference in New Issue
Block a user