]> Cypherpunks repositories - gostls13.git/commitdiff
gc: simplify escape analysis recursion
authorRuss Cox <rsc@golang.org>
Wed, 24 Aug 2011 20:47:32 +0000 (16:47 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 24 Aug 2011 20:47:32 +0000 (16:47 -0400)
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

src/cmd/gc/esc.c

index ddc121e3894c6eb78b762705b2a9257a53b6536e..916a08976d85c4209c5dc66937fd24dc19dba086 100644 (file)
@@ -12,7 +12,7 @@
 //
 // 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
 // pointer-containing nodes and store them in dst->escflowsrc.  For
 // variables assigned to a variable in an outer scope or used as a
 #include "go.h"
 
 static void escfunc(Node *func);
-static void escstmtlist(NodeList *stmts);
-static void escstmt(Node *stmt);
-static void escexpr(Node *dst, Node *expr);
-static void escexprcall(Node *dst, Node *callexpr);
-static void escflows(Node* dst, Node* src);
+static void esclist(NodeList *l);
+static void esc(Node *n);
+static void escassign(Node *dst, Node *src);
+static void esccall(Node*);
+static void escflows(Node *dst, Node *src);
 static void escflood(Node *dst);
 static void escwalk(int level, Node *dst, Node *src);
 static void esctag(Node *func);
@@ -48,7 +48,7 @@ static void esctag(Node *func);
 // flow to.
 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     pdepth;         // for debug printing in recursions.
 static int     floodgen;       // loop prevention in flood/walk
@@ -76,7 +76,7 @@ escapes(void)
 
        // visit the updstream of each dst, mark address nodes with
        // addrescapes, mark parameters unsafe
-       for (l = dsts; l; l=l->next)
+       for(l = dsts; l; l=l->next)
                escflood(l->n);
 
        // 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.
        // 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
                        continue;
 
                n = nod(OADDR, ll->n->closure, N);
                n->lineno = ll->n->lineno;
                typecheck(&n, Erv);
-               escexpr(curfn, n);
+               escassign(curfn, n);
        }
 
-       escstmtlist(curfn->nbody);
+       esclist(curfn->nbody);
        curfn = savefn;
        loopdepth = saveld;
 }
 
 static void
-escstmtlist(NodeList* stmts)
+esclist(NodeList *l)
 {
-       for(; stmts; stmts=stmts->next)
-               escstmt(stmts->n);
+       for(; l; l=l->next)
+               esc(l->n);
 }
 
 static void
-escstmt(Node *stmt)
+esc(Node *n)
 {
-       int cl, cr, lno;
-       NodeList *ll, *lr;
-       Node *dst;
+       int lno;
+       NodeList *ll, *lr, *l;
 
-       if(stmt == N)
+       if(n == N)
                return;
 
-       lno = setlineno(stmt);
+       lno = setlineno(n);
 
-       if(stmt->typecheck == 0 && stmt->op != ODCL) {   // TODO something with OAS2
-               dump("escstmt missing typecheck", stmt);
-               fatal("missing typecheck.");
-       }
+       if(n->op == OFOR)
+               loopdepth++;
 
-       // Common to almost all statements, and nil if n/a.
-       escstmtlist(stmt->ninit);
+       esclist(n->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)
-               print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth,
-                     (curfn && curfn->nname) ? curfn->nname->sym : S, stmt);
+               print("%L:[%d] %#S esc: %#N\n", lineno, loopdepth,
+                     (curfn && curfn->nname) ? curfn->nname->sym : S, n);
 
-       switch(stmt->op) {
+       switch(n->op) {
        case ODCL:
        case ODCLFIELD:
                // a declaration ties the node to the current
@@ -170,182 +176,135 @@ escstmt(Node *stmt)
                // escflood to avoid storing redundant information
                // What does have to happen here is note if the name
                // is declared inside a looping scope.
-               stmt->left->escloopdepth = loopdepth;
+               if(n->left)
+                       n->left->escloopdepth = loopdepth;
                break;
 
        case OLABEL:  // TODO: new loop/scope only if there are backjumps to it.
                loopdepth++;
                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> }
-               switch(stmt->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;
+               switch(n->type->etype) {
                case TARRAY:    // i, v = range sliceorarray
-                       escexpr(stmt->list->n, N);
-                       if(stmt->list->next)
-                               escexpr(stmt->list->next->n, stmt->right);
+                       if(n->list->next)
+                               escassign(n->list->next->n, n->right);
                        break;
                case TMAP:      // k [, v] = range map
-                       escexpr(stmt->list->n, stmt->right);
-                       if(stmt->list->next)
-                               escexpr(stmt->list->next->n, stmt->right);
+                       escassign(n->list->n, n->right);
+                       if(n->list->next)
+                               escassign(n->list->next->n, n->right);
                        break;
                case TCHAN:     // v = range chan
-                       escexpr(stmt->list->n, stmt->right);
+                       escassign(n->list->n, n->right);
                        break;
                }
                loopdepth++;
-               escstmtlist(stmt->nbody);
+               esclist(n->nbody);
                loopdepth--;
                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
-               escexpr(N, stmt->left);
-               escexpr(stmt->left, stmt->right);
+               escassign(n->left, n->right);
                break;
 
        case OSWITCH:
-               if(stmt->ntest && stmt->ntest->op == OTYPESW) {
-                       for(ll=stmt->list; ll; ll=ll->next) {  // cases
+               if(n->ntest && n->ntest->op == OTYPESW) {
+                       for(ll=n->list; ll; ll=ll->next) {  // cases
                                // ntest->right is the argument of the .(type),
                                // ll->n->nname is the variable per case
-                               escexpr(ll->n->nname, stmt->ntest->right);
-                               escstmtlist(ll->n->nbody);
+                               escassign(ll->n->nname, n->ntest->right);
+                               esclist(ll->n->nbody);
                        }
                } else {
-                       escexpr(N, stmt->ntest);
-                       for(ll=stmt->list; ll; ll=ll->next) {  // cases
+                       escassign(N, n->ntest);
+                       for(ll=n->list; ll; ll=ll->next) {  // cases
                                for(lr=ll->n->list; lr; lr=lr->next)
-                                       escexpr(N, lr->n);
-                               escstmtlist(ll->n->nbody);
+                                       escassign(N, lr->n);
+                               esclist(ll->n->nbody);
                        }
                }
                break;
 
        case OAS:
        case OASOP:
-               escexpr(stmt->left, stmt->right);
+               escassign(n->left, n->right);
                break;
 
-               // escape analysis happens after typecheck, so the
-               // OAS2xxx have already been substituted.
        case OAS2:      // x,y = a,b
-               cl = count(stmt->list);
-               cr = count(stmt->rlist);
-               if(cl > 1 && cr == 1) {
-                       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);
-               }
+               if(count(n->list) == count(n->rlist))
+                       for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
+                               escassign(ll->n, lr->n);
                break;
 
        case OAS2RECV:          // v, ok = <-ch
        case OAS2MAPR:          // v, ok = m[k]
        case OAS2DOTTYPE:       // v, ok = x.(type)
-               escexpr(stmt->list->n, stmt->rlist->n);
-               escexpr(stmt->list->next->n, N);
+       case OAS2MAPW:          // m[k] = x, ok
+               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);
+       case OSEND:             // ch <- x
+               escassign(&theSink, n->right);   // TODO: treat as *ch = x ?
                break;
 
-       case ORECV:             // unary <-ch as statement
-               escexpr(N, stmt->left);
+       case ODEFER:
+               if(loopdepth == 1)  // top level
+                       break;
+               // arguments leak out of scope
+               // TODO: leak to a dummy node instead
+               // fallthrough
+       case OPROC:
+               // go f(x) - f and x escape
+               escassign(&theSink, n->left->left);
+               for(ll=n->left->list; ll; ll=ll->next)
+                       escassign(&theSink, ll->n);
                break;
 
-       case OSEND:             // ch <- x
-               escexpr(&theSink, stmt->right);  // for now. TODO escexpr(stmt->left, stmt->right);
+       case ORETURN:
+               for(ll=n->list; ll; ll=ll->next)
+                       escassign(&theSink, ll->n);
                break;
 
-       case OCOPY:     // todo: treat as *dst=*src instead of as dst=src
-               escexpr(stmt->left, stmt->right);
+       case OPANIC:
+               // Argument could leak through recover.
+               escassign(&theSink, n->left);
                break;
 
-       case OAS2FUNC:  // x,y,z = f()
-               for(ll = stmt->list; ll; ll=ll->next)
-                       escexpr(ll->n, N);
-               escexpr(N, stmt->rlist->n);
+       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 OCALLINTER:
-       case OCALLFUNC:
-       case OCALLMETH:
-               escexpr(N, stmt);
+       case OAPPEND:
+               // See TODO for OCOPY
+               for(ll=n->list->next; ll; ll=ll->next)
+                       escassign(n->list->n, ll->n);
                break;
 
-       case OPROC:
-       case ODEFER:
-               // stmt->left is a (pseud)ocall, stmt->left->left is
-               // the function being called.  if this defer is at
-               // loopdepth >1, everything leaks.  TODO this is
-               // overly conservative, it's enough if it leaks to a
-               // fake node at the function's top level
-               dst = &theSink;
-               if (stmt->op == ODEFER && loopdepth <= 1)
-                       dst = nil;
-               escexpr(dst, stmt->left->left);
-               for(ll=stmt->left->list; ll; ll=ll->next)
-                       escexpr(dst, ll->n);
+       case OCALLMETH:
+       case OCALLFUNC:
+       case OCALLINTER:
+               esccall(n);
                break;
-
-       case ORETURN:
-               for(ll=stmt->list; ll; ll=ll->next)
-                       escexpr(&theSink, ll->n);
+       
+       case OCONV:
+       case OCONVNOP:
+       case OCONVIFACE:
+               escassign(n, n->left);
                break;
-
-       case OCLOSE:
-       case OPRINT:
-       case OPRINTN:
-               escexpr(N, stmt->left);
-               for(ll=stmt->list; ll; ll=ll->next)
-                       escexpr(N, ll->n);
+       
+       case OARRAYLIT:
+       case OSTRUCTLIT:
+               for(l=n->list; l; l=l->next)
+                       escassign(n, l->n->right);
                break;
-
-       case OPANIC:
-               // Argument could leak through recover.
-               escexpr(&theSink, stmt->left);
+       case OMAPLIT:
+               for(l=n->list; l; l=l->next) {
+                       escassign(n, l->n->left);
+                       escassign(n, l->n->right);
+               }
                break;
        }
 
@@ -357,87 +316,82 @@ escstmt(Node *stmt)
 // evaluated in curfn. For expr==nil, dst must still be examined for
 // evaluations inside it (e.g *f(x) = y)
 static void
-escexpr(Node *dst, Node *expr)
+escassign(Node *dst, Node *src)
 {
        int lno;
        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
        // these are the only interesting cases
        // todo:check channel case
-       if(dst) {
-               setlineno(dst);
-
-               switch(dst->op) {
-               case OINDEX:
-               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
-                       if(!isfixedarray(dst->left->type))
-                               goto doref;
-
-                       // fallthrough;  treat "dst[x] = src" as "dst = src"
-               case ODOT:            // treat "dst.x  = src" as "dst = src"
-                       escexpr(dst->left, expr);
-                       return;
-
-               case OINDEXMAP:
-                       escexpr(&theSink, dst->right);  // map key is put in map
-                       // fallthrough
-               case OIND:
-               case ODOTPTR:
-               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;
-               }
+       setlineno(dst);
 
-       }
-
-       if(expr == N || expr->op == ONONAME || expr->op == OXXX)
+       switch(dst->op) {
+       case OINDEX:
+       case OSLICE:
+               // slice:  "dst[x] = src"  is like *(underlying array)[x] = src
+               // TODO maybe this never occurs b/c of OSLICEARR and it's inserted OADDR
+               if(!isfixedarray(dst->left->type))
+                       goto doref;
+               // fallthrough;  treat "dst[x] = src" as "dst = src"
+       case ODOT:            // treat "dst.x  = src" as "dst = src"
+               escassign(dst->left, src);
                return;
+       case OINDEXMAP:
+               escassign(&theSink, dst->right);        // map key is put in map
+               // fallthrough
+       case OIND:
+       case ODOTPTR:
+       case OSLICEARR:  // ->left  is the OADDR of the array
+       doref:
+               // assignment to dereferences: for now we lose track
+               escassign(&theSink, src);
+               return;
+       }
 
-       if(expr->typecheck == 0 && expr->op != OKEY) {
-               dump("escexpr missing typecheck", expr);
-               fatal("Missing typecheck.");
+       if(src->typecheck == 0 && src->op != OKEY) {
+               dump("escassign missing typecheck", src);
+               fatal("escassign");
        }
 
-       lno = setlineno(expr);
+       lno = setlineno(src);
        pdepth++;
 
-       if(debug['m'] > 1)
-               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) {
+       switch(src->op) {
        case OADDR:     // dst = &x
        case OIND:      // dst = *x
        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 OPARAM:
                // 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;
 
        case OARRAYLIT:
        case OSTRUCTLIT:
        case OMAPLIT:
-               expr->escloopdepth = loopdepth;
-               escflows(dst, expr);
-               for(ll=expr->list; ll; ll=ll->next) {
-                       escexpr(expr, ll->n->left);
-                       escexpr(expr, ll->n->right);
+               src->escloopdepth = loopdepth;
+               escflows(dst, src);
+               for(ll=src->list; ll; ll=ll->next) {
+                       escassign(src, ll->n->left);
+                       escassign(src, ll->n->right);
                }
                break;
 
@@ -445,54 +399,21 @@ escexpr(Node *dst, Node *expr)
        case OMAKEMAP:
        case OMAKESLICE:
        case ONEW:
-               expr->curfn = curfn;  // should have been done in parse, but patch it up here.
-               expr->escloopdepth = loopdepth;
-               escflows(dst, expr);
-               // first arg is type, all others need checking
-               for(ll=expr->list->next; ll; ll=ll->next)
-                       escexpr(N, ll->n);
+               src->curfn = curfn;  // should have been done in parse, but patch it up here.
+               src->escloopdepth = loopdepth;
+               escflows(dst, src);
                break;
 
        case OCLOSURE:
-               expr->curfn = curfn;  // should have been done in parse, but patch it up here.
-               expr->escloopdepth = loopdepth;
-               escflows(dst, expr);
-               escfunc(expr);
+               src->curfn = curfn;  // should have been done in parse, but patch it up here.
+               src->escloopdepth = loopdepth;
+               escflows(dst, src);
+               escfunc(src);
                break;
 
        // 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:
-               // See TODO for OCOPY
-               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);
+               escassign(dst, src->list->n);
                break;
 
        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 OINDEXMAP:
                // the big thing flows, the keys just need checking
-               escexpr(dst, expr->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);
+               escassign(dst, src->left);
                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
 // connected to other things throug flows or we have the parameter type
 // nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
 // different for methods vs plain functions and for imported vs
 // this-package
 static void
-escexprcall(Node *dst, Node *expr)
+esccall(Node *n)
 {
        NodeList *ll, *lr;
-       Node *fn;
-       Type *t, *fntype, *thisarg, *inargs;
+       Node *a, *fn;
+       Type *t, *fntype;
 
-       fn = nil;
-       fntype = nil;
+       fn = N;
+       fntype = T;
+       switch(n->op) {
+       default:
+               fatal("esccall");
 
-       switch(expr->op) {
        case OCALLFUNC:
-               fn = expr->left;
-               escexpr(N, fn);
+               fn = n->left;
                fntype = fn->type;
                break;
 
        case OCALLMETH:
-               fn = expr->left->right;  // ODOTxx name
-               fn = fn->sym->def;       // resolve to definition if we have it
+               fn = n->left->right->sym->def;
                if(fn)
                        fntype = fn->type;
                else
-                       fntype = expr->left->type;
+                       fntype = n->left->type;
                break;
 
        case OCALLINTER:
+               fntype = n->left->type;
                break;
-
-       default:
-               fatal("escexprcall called with non-call expression");
        }
 
+       ll = n->list;
+       if(n->list != nil && n->list->next == nil) {
+               a = n->list->n;
+               if(a->type->etype == TSTRUCT && a->type->funarg) {
+                       // f(g()).
+                       // Since f's arguments are g's results and
+                       // all function results escape, we're done.
+                       ll = nil;
+               }
+       }
+                       
        if(fn && fn->ntype) {
-               if(debug['m'] > 2)
-                       print("escexprcall: have param nodes: %N\n", fn->ntype);
+               // Local function.  Incorporate into flow graph.
 
-               if(expr->op == OCALLMETH) {
-                       if(debug['m'] > 2)
-                               print("escexprcall: this: %N\n",fn->ntype->left->left);
-                       escexpr(fn->ntype->left->left, expr->left->left);
-               }
+               // Receiver.
+               if(n->op != OCALLFUNC)
+                       escassign(fn->ntype->left->left, n->left->left);
 
-               // lr->n is the dclfield, ->left is the ONAME param node
-               for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
-                       if(debug['m'] > 2)
-                               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)
-                               escexpr(lr->n->left, ll->n);
-                       else
-                               escexpr(&theSink, ll->n);
+                               escassign(lr->n->left, ll->n);
+                       else 
+                               escassign(&theSink, ll->n);
                        if(lr->n->left && !lr->n->left->isddd)
                                lr=lr->next;
                }
                return;
        }
 
-       if(fntype) {
-               if(debug['m'] > 2)
-                       print("escexprcall: have param types: %T\n", fntype);
-
-               if(expr->op == OCALLMETH) {
-                       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;
+       // Imported function.  Use the escape tags.
+       if(n->op != OCALLFUNC) {
+               t = getthisx(fntype)->type;
+               if(!t->note || strcmp(t->note->s, safetag->s) != 0)
+                       escassign(&theSink, n->left->left);
        }
-
-       // fallthrough if we don't have enough information:
-       // can only assume all parameters are unsafe
-       // OCALLINTER always ends up here
-
-       if(debug['m']>1 && expr->op != OCALLINTER) {
-               // dump("escexprcall", expr);
-               print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn);
+       for(t=getinargx(fntype)->type; ll; ll=ll->next) {
+               if(!t->note || strcmp(t->note->s, safetag->s) != 0)
+                       escassign(&theSink, ll->n);
+               if(t->down)
+                       t = t->down;
        }
-
-       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.
 static void
-escflows(Node* dst, Node* src)
+escflows(Node *dst, Node *src)
 {
        if(dst == nil || src == nil || dst == src)
                return;
 
        // Don't bother building a graph for scalars.
-       if (src->type && !haspointers(src->type))
+       if(src->type && !haspointers(src->type))
                return;
 
        if(debug['m']>2)
                print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
 
        // Assignments to global variables get lumped into theSink.
-       if (dst->op == ONAME && dst->class == PEXTERN)
+       if(dst->op == ONAME && dst->class == PEXTERN)
                dst = &theSink;
 
-       if (dst->escflowsrc == nil) {
+       if(dst->escflowsrc == nil) {
                dsts = list(dsts, dst);
                dstcount++;
        }
@@ -677,7 +565,7 @@ escflood(Node *dst)
                      (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
                      dst->escloopdepth);
 
-       for (l = dst->escflowsrc; l; l=l->next) {
+       for(l = dst->escflowsrc; l; l=l->next) {
                floodgen++;
                escwalk(0, dst, l->n);
        }
@@ -686,10 +574,10 @@ escflood(Node *dst)
 static void
 escwalk(int level, Node *dst, Node *src)
 {
-       NodeListll;
+       NodeList *ll;
        int leaks;
 
-       if (src->escfloodgen == floodgen)
+       if(src->escfloodgen == floodgen)
                return;
        src->escfloodgen = floodgen;
 
@@ -704,7 +592,7 @@ escwalk(int level, Node *dst, Node *src)
 
        switch(src->op) {
        case ONAME:
-               if (src->class == PPARAM && leaks && src->esc == EscNone) {
+               if(src->class == PPARAM && leaks && src->esc == EscNone) {
                        src->esc = EscScope;
                        if(debug['m'])
                                print("%L:leaking param: %hN\n", src->lineno, src);
@@ -712,7 +600,7 @@ escwalk(int level, Node *dst, Node *src)
                break;
 
        case OADDR:
-               if (leaks)
+               if(leaks)
                        addrescapes(src->left);
                escwalk(level-1, dst, src->left);
                break;
@@ -727,7 +615,7 @@ escwalk(int level, Node *dst, Node *src)
                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);
 
        pdepth--;
@@ -748,7 +636,7 @@ esctag(Node *func)
 
                switch (ll->n->esc) {
                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;
                case EscHeap:   // touched by escflood, moved to heap
                case EscScope:  // touched by escflood, value leaves scope