diff gcc/go/gofrontend/escape.cc @ 131:84e7813d76e9

gcc-8.2
author mir3636
date Thu, 25 Oct 2018 07:37:49 +0900
parents 04ced10e8804
children 1830386684a0
line wrap: on
line diff
--- a/gcc/go/gofrontend/escape.cc	Fri Oct 27 22:46:09 2017 +0900
+++ b/gcc/go/gofrontend/escape.cc	Thu Oct 25 07:37:49 2018 +0900
@@ -15,9 +15,11 @@
 #include "expressions.h"
 #include "statements.h"
 #include "escape.h"
+#include "lex.h"
 #include "ast-dump.h"
 #include "go-optimize.h"
 #include "go-diagnostics.h"
+#include "go-sha1.h"
 
 // class Node.
 
@@ -34,6 +36,25 @@
     return this->object()->func_value()->type();
   else if (this->expr() != NULL)
     return this->expr()->type();
+  else if (this->is_indirect())
+    {
+      if (this->child()->type()->deref()->is_void_type())
+        // This is a void*. The referred type can be actually any type,
+        // which may also be pointer. We model it as another void*, so
+        // we don't lose pointer-ness.
+        return this->child()->type();
+      else if (this->child()->type()->is_slice_type())
+        // We model "indirect" of a slice as dereferencing its pointer
+        // field (to get element). Use element type here.
+        return this->child()->type()->array_type()->element_type();
+      else if (this->child()->type()->is_string_type())
+        return Type::lookup_integer_type("uint8");
+      else
+        return this->child()->type()->deref();
+    }
+  else if (this->statement() != NULL
+           && this->statement()->temporary_statement() != NULL)
+    return this->statement()->temporary_statement()->type();
   else
     return NULL;
 }
@@ -49,10 +70,44 @@
     return this->expr()->location();
   else if (this->statement() != NULL)
     return this->statement()->location();
+  else if (this->is_indirect())
+    return this->child()->location();
   else
     return Linemap::unknown_location();
 }
 
+// A helper for reporting; return the location where the underlying
+// object is defined.
+
+Location
+Node::definition_location() const
+{
+  if (this->object() != NULL && !this->object()->is_sink())
+    {
+      Named_object* no = this->object();
+      if (no->is_variable() || no->is_result_variable())
+        return no->location();
+    }
+  else if (this->expr() != NULL)
+    {
+      Var_expression* ve = this->expr()->var_expression();
+      if (ve != NULL)
+        {
+          Named_object* no = ve->named_object();
+          if (no->is_variable() || no->is_result_variable())
+            return no->location();
+        }
+      Enclosed_var_expression* eve = this->expr()->enclosed_var_expression();
+      if (eve != NULL)
+        {
+          Named_object* no = eve->variable();
+          if (no->is_variable() || no->is_result_variable())
+            return no->location();
+        }
+    }
+  return this->location();
+}
+
 // To match the cmd/gc debug output, strip away the packed prefixes on functions
 // and variable/expressions.
 
@@ -82,7 +137,7 @@
       Named_object* no = this->object();
       if (no->is_function() && no->func_value()->enclosing() != NULL)
 	return "func literal";
-      ss << no->name();
+      ss << no->message_name();
     }
   else if (this->expr() != NULL)
     {
@@ -101,7 +156,7 @@
 	}
       Ast_dump_context::dump_to_stream(this->expr(), &ss);
     }
-  else
+  else if (this->statement() != NULL)
     {
       Statement* s = this->statement();
       Goto_unnamed_statement* unnamed = s->goto_unnamed_statement();
@@ -130,17 +185,37 @@
 		}
 	    }
 	}
-      Ast_dump_context::dump_to_stream(s, &ss);
+      Temporary_statement* tmp = s->temporary_statement();
+      if (tmp != NULL)
+        {
+          // Temporary's format can never match gc's output, and
+          // temporaries are inserted differently anyway. We just
+          // print something convenient.
+          ss << "tmp." << (uintptr_t) tmp;
+          if (tmp->init() != NULL)
+            {
+              ss << " [ = ";
+              Ast_dump_context::dump_to_stream(tmp->init(), &ss);
+              ss << " ]";
+            }
+        }
+      else
+        Ast_dump_context::dump_to_stream(s, &ss);
     }
-
-  return strip_packed_prefix(gogo, ss.str());
+  else if (this->is_indirect())
+    return "*(" + this->child()->ast_format(gogo) + ")";
+
+  std::string s = strip_packed_prefix(gogo, ss.str());
+
+  // trim trailing space
+  return s.substr(0, s.find_last_not_of(' ') + 1);
 }
 
 // A helper for debugging; return this node's detailed format string.
 // This is an implementation of gc's Jconv with obj.FmtShort.
 
 std::string
-Node::details() const
+Node::details()
 {
   std::stringstream details;
 
@@ -223,10 +298,6 @@
       details << " esc(h)";
       break;
 
-    case Node::ESCAPE_SCOPE:
-      details << " esc(s)";
-      break;
-
     case Node::ESCAPE_NONE:
       details << " esc(no)";
       break;
@@ -295,6 +366,7 @@
 		  break;
 
 		case Runtime::MAKECHAN:
+		case Runtime::MAKECHAN64:
 		case Runtime::MAKEMAP:
 		case Runtime::MAKESLICE:
 		case Runtime::MAKESLICE64:
@@ -348,6 +420,8 @@
 	  break;
 	}
     }
+  if (this->is_indirect())
+    op << "*";
   return op.str();
 }
 
@@ -378,16 +452,51 @@
   return this->state_;
 }
 
+Node::~Node()
+{
+  if (this->state_ != NULL)
+    {
+      if (this->expr() == NULL || this->expr()->var_expression() == NULL)
+        // Var expression Node is excluded since it shares state with the
+        // underlying var Node.
+        delete this->state_;
+    }
+}
+
+int
+Node::encoding()
+{
+  if (this->expr() != NULL
+      && this->expr()->var_expression() != NULL)
+    {
+      // Get the underlying object's encoding.
+      Named_object* no = this->expr()->var_expression()->named_object();
+      int enc = Node::make_node(no)->encoding();
+      this->encoding_ = enc;
+    }
+  return this->encoding_;
+}
+
 void
 Node::set_encoding(int enc)
 {
   this->encoding_ = enc;
-  if (this->expr() != NULL
-      && this->expr()->var_expression() != NULL)
+  if (this->expr() != NULL)
     {
-      // Set underlying object as well.
-      Named_object* no = this->expr()->var_expression()->named_object();
-      Node::make_node(no)->set_encoding(enc);
+      if (this->expr()->var_expression() != NULL)
+        {
+          // Set underlying object as well.
+          Named_object* no = this->expr()->var_expression()->named_object();
+          Node::make_node(no)->set_encoding(enc);
+        }
+      else if (this->expr()->func_expression() != NULL)
+        {
+          // Propagate the escape state to the underlying
+          // closure (heap expression).
+          Expression* closure = this->expr()->func_expression()->closure();
+          if (closure != NULL)
+            Node::make_node(closure)->set_encoding(enc);
+        }
     }
 }
 
@@ -426,9 +535,17 @@
 	      Expression_list::iterator p = call->args()->begin();
 	      ++p;
 
+              Expression* e = *p;
+              if (e->temporary_reference_expression() != NULL)
+                {
+                  Temporary_reference_expression* tre = e->temporary_reference_expression();
+                  if (tre->statement() != NULL && tre->statement()->init() != NULL)
+                    e = tre->statement()->init();
+                }
+
 	      Numeric_constant nc;
 	      unsigned long v;
-	      if ((*p)->numeric_constant_value(&nc)
+	      if (e->numeric_constant_value(&nc)
 		  && nc.to_unsigned_long(&v) == Numeric_constant::NC_UL_VALID)
 		big = big || v >= (1 << 16);
 	    }
@@ -453,6 +570,7 @@
 std::map<Named_object*, Node*> Node::objects;
 std::map<Expression*, Node*> Node::expressions;
 std::map<Statement*, Node*> Node::statements;
+std::vector<Node*> Node::indirects;
 
 // Make a object node or return a cached node for this object.
 
@@ -496,13 +614,23 @@
   return n;
 }
 
+// Make an indirect node with given child.
+
+Node*
+Node::make_indirect_node(Node* child)
+{
+  Node* n = new Node(child);
+  Node::indirects.push_back(n);
+  return n;
+}
+
 // Returns the maximum of an exisiting escape value
 // (and its additional parameter flow flags) and a new escape type.
 
 int
 Node::max_encoding(int e, int etype)
 {
-  if ((e & ESCAPE_MASK) >= etype)
+  if ((e & ESCAPE_MASK) > etype)
     return e;
   if (etype == Node::ESCAPE_NONE || etype == Node::ESCAPE_RETURN)
     return (e & ~ESCAPE_MASK) | etype;
@@ -552,7 +680,6 @@
 {
   // The sink always escapes to heap and strictly lives outside of the
   // current function i.e. loop_depth == -1.
-  this->sink_->set_encoding(Node::ESCAPE_HEAP);
   Node::Escape_state* state = this->sink_->state(this, NULL);
   state->loop_depth = -1;
 }
@@ -563,25 +690,35 @@
   if (fn == NULL)
     return "<S>";
 
-  if (!fn->is_function()
-      || fn->func_value()->enclosing() == NULL)
+  if (!fn->is_function())
     return Gogo::unpack_hidden_name(fn->name());
 
-  // Closures are named ".$nested#" where # starts from 0 to distinguish
-  // between closures.  The cmd/gc closures are named in the format
-  // "enclosing.func#" where # starts from 1.  If this is a closure, format
-  // its name to match cmd/gc.
-  Named_object* enclosing = fn->func_value()->enclosing();
-
-  // Extract #.
-  std::string name = Gogo::unpack_hidden_name(fn->name());
-  int closure_num = Gogo::nested_function_num(fn->name());
-  closure_num++;
-
-  name = Gogo::unpack_hidden_name(enclosing->name());
-  char buf[200];
-  snprintf(buf, sizeof buf, "%s.func%d", name.c_str(), closure_num);
-  return buf;
+  std::string fnname = Gogo::unpack_hidden_name(fn->name());
+  if (fn->func_value()->is_method())
+    {
+      // Methods in gc compiler are named "T.m" or "(*T).m" where
+      // T is the receiver type. Add the receiver here.
+      Type* rt = fn->func_value()->type()->receiver()->type();
+      switch (rt->classification())
+	{
+	case Type::TYPE_NAMED:
+	  fnname = rt->named_type()->name() + "." + fnname;
+	  break;
+
+	case Type::TYPE_POINTER:
+	  {
+	    Named_type* nt = rt->points_to()->named_type();
+	    if (nt != NULL)
+	      fnname = "(*" + nt->name() + ")." + fnname;
+	    break;
+	  }
+
+	default:
+	  break;
+	}
+    }
+
+  return fnname;
 }
 
 // Return the name of the current function.
@@ -602,6 +739,7 @@
     return;
 
   Node::Escape_state* state = n->state(this, NULL);
+  state->retvals.clear();
   Location loc = n->location();
 
   int i = 0;
@@ -618,7 +756,8 @@
 	Named_object::make_variable(buf, NULL, dummy_var);
       Node* dummy_node = Node::make_node(dummy_no);
       // Initialize the state of the dummy output node.
-      dummy_node->state(this, NULL);
+      Node::Escape_state* dummy_node_state = dummy_node->state(this, NULL);
+      dummy_node_state->loop_depth = this->loop_depth_;
 
       // Add dummy node to the retvals of n.
       state->retvals.push_back(dummy_node);
@@ -627,20 +766,30 @@
 
 
 // Apply an indirection to N and return the result.
-// This really only works if N is an expression node; it essentially becomes
-// Node::make_node(n->expr()->deref()).  We need the escape context to set the
-// correct loop depth, however.
 
 Node*
 Escape_context::add_dereference(Node* n)
 {
-  // Just return the original node if we can't add an indirection.
-  if (n->object() != NULL || n->statement() != NULL)
-    return n;
-
-  Node* ind = Node::make_node(n->expr()->deref());
-  // Initialize the state if this node doesn't already exist.
-  ind->state(this, NULL);
+  Expression* e = n->expr();
+  Location loc = n->location();
+  Node* ind;
+  if (e != NULL
+      && e->type()->points_to() != NULL
+      && !e->type()->points_to()->is_void_type())
+    {
+      // We don't dereference void*, which can be actually any pointer type.
+      Expression* deref_expr = Expression::make_unary(OPERATOR_MULT, e, loc);
+      ind = Node::make_node(deref_expr);
+    }
+  else
+    // The gc compiler simply makes an OIND node. We can't do it
+    // for non-pointer type because that will result in a type error.
+    // Instead, we model this by making a node with a special flavor.
+    ind = Node::make_indirect_node(n);
+
+  // Initialize the state.
+  Node::Escape_state* state = ind->state(this, NULL);
+  state->loop_depth = n->state(this, NULL)->loop_depth;
   return ind;
 }
 
@@ -682,25 +831,98 @@
 
 // The -fgo-optimize-alloc flag activates this escape analysis.
 
-Go_optimize optimize_allocation_flag("allocs");
+Go_optimize optimize_allocation_flag("allocs", true);
+
+// A helper function to compute whether a function name has a
+// matching hash value.
+
+static bool
+escape_hash_match(std::string suffix, std::string name)
+{
+  if (suffix.empty())
+    return true;
+  if (suffix.at(0) == '-')
+    return !escape_hash_match(suffix.substr(1), name);
+
+  const char* p = name.c_str();
+  Go_sha1_helper* sha1_helper = go_create_sha1_helper();
+  sha1_helper->process_bytes(p, strlen(p));
+  std::string s = sha1_helper->finish();
+  delete sha1_helper;
+
+  int j = suffix.size() - 1;
+  for (int i = s.size() - 1; i >= 0; i--)
+    {
+      char c = s.at(i);
+      for (int k = 0; k < 8; k++, j--, c>>=1)
+        {
+          if (j < 0)
+            return true;
+          char bit = suffix.at(j) - '0';
+          if ((c&1) != bit)
+            return false;
+        }
+    }
+  return false;
+}
 
 // Analyze the program flow for escape information.
 
 void
 Gogo::analyze_escape()
 {
-  if (!optimize_allocation_flag.is_enabled() || saw_errors())
+  if (saw_errors())
+    return;
+
+  if (!optimize_allocation_flag.is_enabled()
+      && !this->compiling_runtime())
+    // We always run escape analysis when compiling runtime.
     return;
 
   // Discover strongly connected groups of functions to analyze for escape
   // information in this package.
   this->discover_analysis_sets();
 
+  if (!this->debug_escape_hash().empty())
+    std::cerr << "debug-escape-hash " << this->debug_escape_hash() << "\n";
+
   for (std::vector<Analysis_set>::iterator p = this->analysis_sets_.begin();
        p != this->analysis_sets_.end();
        ++p)
     {
       std::vector<Named_object*> stack = p->first;
+
+      if (!this->debug_escape_hash().empty())
+        {
+          bool match = false;
+          for (std::vector<Named_object*>::const_iterator fn = stack.begin();
+               fn != stack.end();
+               ++fn)
+            match = match || escape_hash_match(this->debug_escape_hash(), (*fn)->message_name());
+          if (!match)
+            {
+              // Escape analysis won't run on these functions, but still
+              // need to tag them, so the caller knows.
+              for (std::vector<Named_object*>::iterator fn = stack.begin();
+                   fn != stack.end();
+                   ++fn)
+                if ((*fn)->is_function())
+                  {
+                    Function_type* fntype = (*fn)->func_value()->type();
+                    fntype->set_is_tagged();
+
+                    std::cerr << "debug-escape-hash disables " << debug_function_name(*fn) << "\n";
+                  }
+
+              continue;
+            }
+          for (std::vector<Named_object*>::const_iterator fn = stack.begin();
+               fn != stack.end();
+               ++fn)
+            if ((*fn)->is_function())
+              std::cerr << "debug-escape-hash triggers " << debug_function_name(*fn) << "\n";
+        }
+
       Escape_context* context = new Escape_context(this, p->second);
 
       // Analyze the flow of each function; build the connection graph.
@@ -715,10 +937,43 @@
 
       // Propagate levels across each dst.  This is the flood phase.
       std::set<Node*> dsts = context->dsts();
+      Unordered_map(Node*, int) escapes;
       for (std::set<Node*>::iterator n = dsts.begin();
            n != dsts.end();
            ++n)
-        this->propagate_escape(context, *n);
+        {
+          escapes[*n] = (*n)->encoding();
+          this->propagate_escape(context, *n);
+        }
+      for (;;)
+        {
+          // Reflood if the roots' escape states increase. Run until fix point.
+          // This is rare.
+          bool done = true;
+          for (std::set<Node*>::iterator n = dsts.begin();
+               n != dsts.end();
+               ++n)
+            {
+              if ((*n)->object() == NULL
+                  && ((*n)->expr() == NULL
+                      || ((*n)->expr()->var_expression() == NULL
+                          && (*n)->expr()->enclosed_var_expression() == NULL
+                          && (*n)->expr()->func_expression() == NULL)))
+                continue;
+              if (escapes[*n] != (*n)->encoding())
+                {
+                  done = false;
+                  if (this->debug_escape_level() > 2)
+                    go_inform((*n)->location(), "Reflooding %s %s",
+                              debug_function_name((*n)->state(context, NULL)->fn).c_str(),
+                              (*n)->ast_format(this).c_str());
+                  escapes[*n] = (*n)->encoding();
+                  this->propagate_escape(context, *n);
+                }
+            }
+          if (done)
+            break;
+        }
 
       // Tag each exported function's parameters with escape information.
       for (std::vector<Named_object*>::iterator fn = stack.begin();
@@ -734,12 +989,11 @@
 	       ++n)
 	    {
 	      Node::Escape_state* state = (*n)->state(context, NULL);
-	      if (((*n)->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
+	      if ((*n)->encoding() == Node::ESCAPE_NONE)
 		go_inform((*n)->location(), "%s %s does not escape",
 			  strip_packed_prefix(this, debug_function_name(state->fn)).c_str(),
 			  (*n)->ast_format(this).c_str());
 	    }
-	  // TODO(cmang): Which objects in context->noesc actually don't escape.
 	}
       delete context;
     }
@@ -753,7 +1007,7 @@
 {
  public:
   Escape_analysis_discover(Gogo* gogo)
-    : Traverse(traverse_functions),
+    : Traverse(traverse_functions | traverse_func_declarations),
       gogo_(gogo), component_ids_()
   { }
 
@@ -761,6 +1015,9 @@
   function(Named_object*);
 
   int
+  function_declaration(Named_object*);
+
+  int
   visit(Named_object*);
 
   int
@@ -792,6 +1049,13 @@
   return TRAVERSE_CONTINUE;
 }
 
+int
+Escape_analysis_discover::function_declaration(Named_object* fn)
+{
+  this->visit(fn);
+  return TRAVERSE_CONTINUE;
+}
+
 // Visit a function FN, adding it to the current stack of functions
 // in this connected component.  If this is the root of the component,
 // create a set of functions to be analyzed later.
@@ -833,8 +1097,8 @@
   this->stack_.push(fn);
   min = this->visit_code(fn, min);
   if ((min == id || min == id + 1)
-      && fn->is_function()
-      && fn->func_value()->enclosing() == NULL)
+      && ((fn->is_function() && fn->func_value()->enclosing() == NULL)
+          || fn->is_function_declaration()))
     {
       bool recursive = min == id;
       std::vector<Named_object*> group;
@@ -1012,6 +1276,38 @@
   Named_object* fn_;
 };
 
+// Helper function to detect self assignment like the following.
+//
+// func (b *Buffer) Foo() {
+//   n, m := ...
+//   b.buf = b.buf[n:m]
+// }
+
+static bool
+is_self_assignment(Expression* lhs, Expression* rhs)
+{
+  Unary_expression* lue =
+    (lhs->field_reference_expression() != NULL
+     ? lhs->field_reference_expression()->expr()->unary_expression()
+     : lhs->unary_expression());
+  Var_expression* lve =
+    (lue != NULL && lue->op() == OPERATOR_MULT ? lue->operand()->var_expression() : NULL);
+  Array_index_expression* raie = rhs->array_index_expression();
+  String_index_expression* rsie = rhs->string_index_expression();
+  Expression* rarray =
+    (raie != NULL && raie->end() != NULL && raie->array()->type()->is_slice_type()
+     ? raie->array()
+     : (rsie != NULL && rsie->type()->is_string_type() ? rsie->string() : NULL));
+  Unary_expression* rue =
+    (rarray != NULL && rarray->field_reference_expression() != NULL
+     ? rarray->field_reference_expression()->expr()->unary_expression()
+     : (rarray != NULL ? rarray->unary_expression() : NULL));
+  Var_expression* rve =
+    (rue != NULL && rue->op() == OPERATOR_MULT ? rue->operand()->var_expression() : NULL);
+  return lve != NULL && rve != NULL
+         && lve->named_object() == rve->named_object();
+}
+
 // Model statements within a function as assignments and flows between nodes.
 
 int
@@ -1061,6 +1357,20 @@
       }
       break;
 
+    case Statement::STATEMENT_TEMPORARY:
+      {
+        Expression* init = s->temporary_statement()->init();
+        if (init != NULL)
+	  {
+	    Node* n = Node::make_node(init);
+	    if (s->temporary_statement()->value_escapes())
+	      this->assign(this->context_->sink(), n);
+	    else
+	      this->assign(Node::make_node(s), n);
+	  }
+      }
+      break;
+
     case Statement::STATEMENT_LABEL:
       {
 	Label_statement* label_stmt = s->label_statement();
@@ -1084,22 +1394,36 @@
       // Want to model the assignment of each case variable to the switched upon
       // variable.  This should be lowered into assignment statements; nothing
       // to here if that's the case.
-      // TODO(cmang): Verify.
       break;
 
     case Statement::STATEMENT_ASSIGNMENT:
       {
 	Assignment_statement* assn = s->assignment_statement();
-	Node* lhs = Node::make_node(assn->lhs());
-	Node* rhs = Node::make_node(assn->rhs());
-
-	// TODO(cmang): Add special case for escape analysis no-op:
-	// func (b *Buffer) Foo() {
-	// 	n, m := ...
-	//	b.buf = b.buf[n:m]
-	// }
-	// This is okay for now, it just means b escapes; it is conservative.
-	this->assign(lhs, rhs);
+        Expression* lhs = assn->lhs();
+        Expression* rhs = assn->rhs();
+        Node* lhs_node = Node::make_node(lhs);
+        Node* rhs_node = Node::make_node(rhs);
+
+        // Filter out the following special case.
+        //
+        // func (b *Buffer) Foo() {
+        //   n, m := ...
+        //   b.buf = b.buf[n:m]
+        // }
+        //
+        // This assignment is a no-op for escape analysis,
+        // it does not store any new pointers into b that were not already there.
+        // However, without this special case b will escape.
+        if (is_self_assignment(lhs, rhs))
+          {
+            if (debug_level != 0)
+              go_inform(s->location(), "%s ignoring self-assignment to %s",
+                        strip_packed_prefix(gogo, this->context_->current_function_name()).c_str(),
+                        lhs_node->ast_format(gogo).c_str());
+            break;
+          }
+
+        this->assign(lhs_node, rhs_node);
       }
       break;
 
@@ -1112,7 +1436,14 @@
 
     case Statement::STATEMENT_DEFER:
       if (this->context_->loop_depth() == 1)
-	break;
+        {
+          // Defer statement may need to allocate a thunk. When it is
+          // not inside a loop, this can be stack allocated, as it
+          // runs before the function finishes.
+          Node* n = Node::make_node(s);
+          n->set_encoding(Node::ESCAPE_NONE);
+          break;
+        }
       // fallthrough
 
     case Statement::STATEMENT_GO:
@@ -1139,14 +1470,41 @@
       }
       break;
 
-      // TODO(cmang): Associate returned values with dummy return nodes.
-
     default:
       break;
     }
   return TRAVERSE_SKIP_COMPONENTS;
 }
 
+// Helper function to emit moved-to-heap diagnostics.
+
+static void
+move_to_heap(Gogo* gogo, Expression *expr)
+{
+  Named_object* no;
+  if (expr->var_expression() != NULL)
+    no = expr->var_expression()->named_object();
+  else if (expr->enclosed_var_expression() != NULL)
+    no = expr->enclosed_var_expression()->variable();
+  else
+    return;
+
+  if ((no->is_variable()
+       && !no->var_value()->is_global())
+      || no->is_result_variable())
+    {
+      Node* n = Node::make_node(expr);
+      if (gogo->debug_escape_level() != 0)
+        go_inform(n->definition_location(),
+                  "moved to heap: %s",
+                  n->ast_format(gogo).c_str());
+      if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
+        go_error_at(expr->location(),
+                    "%s escapes to heap, not allowed in runtime",
+                    n->ast_format(gogo).c_str());
+    }
+}
+
 // Model expressions within a function as assignments and flows between nodes.
 
 int
@@ -1161,7 +1519,9 @@
       && n->is_big(this->context_))
     {
       if (debug_level > 1)
-	go_inform((*pexpr)->location(), "too large for stack");
+	go_inform((*pexpr)->location(), "%s too large for stack",
+                  n->ast_format(gogo).c_str());
+      move_to_heap(gogo, *pexpr);
       n->set_encoding(Node::ESCAPE_HEAP);
       (*pexpr)->address_taken(true);
       this->assign(this->context_->sink(), n);
@@ -1184,100 +1544,150 @@
     case Expression::EXPRESSION_CALL:
       {
 	Call_expression* call = (*pexpr)->call_expression();
-	this->call(call);
-
+        if (call->is_builtin())
+          {
+            Builtin_call_expression* bce = call->builtin_call_expression();
+            switch (bce->code())
+              {
+              case Builtin_call_expression::BUILTIN_PANIC:
+                {
+                  // Argument could leak through recover.
+                  Node* panic_arg = Node::make_node(call->args()->front());
+                  this->assign(this->context_->sink(), panic_arg);
+                }
+                break;
+
+              case Builtin_call_expression::BUILTIN_APPEND:
+                {
+                  // The contents being appended leak.
+                  if (call->is_varargs())
+                    {
+                      // append(slice1, slice2...) -- slice2 itself does not escape, but contents do
+                      Node* appended = Node::make_node(call->args()->back());
+                      this->assign_deref(this->context_->sink(), appended);
+                      if (debug_level > 2)
+                        go_inform((*pexpr)->location(),
+                                  "special treatment of append(slice1, slice2...)");
+                    }
+                  else
+                    {
+                      for (Expression_list::const_iterator pa =
+                             call->args()->begin() + 1;
+                           pa != call->args()->end();
+                           ++pa)
+                        {
+                          Node* arg = Node::make_node(*pa);
+                          this->assign(this->context_->sink(), arg);
+                        }
+                    }
+
+                  // The content of the original slice leaks as well.
+                  Node* appendee = Node::make_node(call->args()->front());
+                  this->assign_deref(this->context_->sink(), appendee);
+                }
+                break;
+
+              case Builtin_call_expression::BUILTIN_COPY:
+                {
+                  // Lose track of the copied content.
+                  Node* copied = Node::make_node(call->args()->back());
+                  this->assign_deref(this->context_->sink(), copied);
+                }
+                break;
+
+              default:
+                break;
+              }
+            break;
+          }
 	Func_expression* fe = call->fn()->func_expression();
 	if (fe != NULL && fe->is_runtime_function())
 	  {
 	    switch (fe->runtime_code())
 	      {
-	      case Runtime::GOPANIC:
-		{
-		  // Argument could leak through recover.
-		  Node* panic_arg = Node::make_node(call->args()->front());
-		  this->assign(this->context_->sink(), panic_arg);
-		}
-		break;
-
-	      case Runtime::GROWSLICE:
-		{
-		  // The contents being appended leak.
-		  if (call->is_varargs())
-		    {
-		      Node* appended = Node::make_node(call->args()->back());
-		      this->assign_deref(this->context_->sink(), appended);
-		    }
-		  else
-		    {
-		      for (Expression_list::const_iterator pa =
-			     call->args()->begin();
-			   pa != call->args()->end();
-			   ++pa)
-			{
-			  Node* arg = Node::make_node(*pa);
-			  this->assign(this->context_->sink(), arg);
-			}
-		    }
-
-		  if (debug_level > 2)
-		    go_error_at((*pexpr)->location(),
-			     "special treatment of append(slice1, slice2...)");
-
-		  // The content of the original slice leaks as well.
-		  Node* appendee = Node::make_node(call->args()->front());
-		  this->assign_deref(this->context_->sink(), appendee);
-		}
-		break;
-
-	      case Runtime::SLICECOPY:
-	      case Runtime::SLICESTRINGCOPY:
-	      case Runtime::TYPEDSLICECOPY:
-		{
-		  // Lose track of the copied content.
-		  Node* copied = Node::make_node(call->args()->back());
-		  this->assign_deref(this->context_->sink(), copied);
-		}
-		break;
-
 	      case Runtime::MAKECHAN:
+	      case Runtime::MAKECHAN64:
 	      case Runtime::MAKEMAP:
 	      case Runtime::MAKESLICE:
 	      case Runtime::MAKESLICE64:
-	      case Runtime::SLICEBYTETOSTRING:
-	      case Runtime::SLICERUNETOSTRING:
-	      case Runtime::STRINGTOSLICEBYTE:
-	      case Runtime::STRINGTOSLICERUNE:
-	      case Runtime::CONCATSTRINGS:
-	      case Runtime::CONCATSTRING2:
-	      case Runtime::CONCATSTRING3:
-	      case Runtime::CONCATSTRING4:
-	      case Runtime::CONCATSTRING5:
-	      case Runtime::CONSTRUCT_MAP:
-	      case Runtime::INTSTRING:
-		{
-		  Node* runtime_node = Node::make_node(fe);
-		  this->context_->track(runtime_node);
-		}
+                this->context_->track(n);
 		break;
 
+              case Runtime::MAPASSIGN:
+                {
+                  // Map key escapes. The last argument is the address
+                  // of the key.
+                  Node* key_node = Node::make_node(call->args()->back());
+                  this->assign_deref(this->context_->sink(), key_node);
+                }
+                break;
+
+              case Runtime::IFACEE2T2:
+              case Runtime::IFACEI2T2:
+                {
+                  // x, ok = v.(T), where T is non-pointer non-interface,
+                  // is lowered to
+                  // ok = IFACEI2T2(type, v, (void*)&tmp_x)
+                  // Here v flows to tmp_x.
+                  // Note: other IFACEX2Y2 returns the conversion result.
+                  // Those are handled in ::assign.
+                  Node* src_node = Node::make_node(call->args()->at(1));
+                  Node* dst_node;
+                  Expression* arg2 = call->args()->at(2);
+                  // Try to pull tmp_x out of the arg2 expression, and let v
+                  // flows into it, instead of simply dereference arg2,
+                  // which looks like dereference of an arbitrary pointer
+                  // and causes v immediately escape.
+                  // The expression form matches statement.cc,
+                  // Tuple_type_guard_assignment_statement::lower_to_object_type.
+                  Unary_expression* ue =
+                    (arg2->conversion_expression() != NULL
+                     ? arg2->conversion_expression()->expr()->unary_expression()
+                     : arg2->unary_expression());
+                  if (ue != NULL && ue->op() == OPERATOR_AND)
+                    {
+                      if (!ue->operand()->type()->has_pointer())
+                        // Don't bother flowing non-pointer.
+                        break;
+                      dst_node = Node::make_node(ue->operand());
+                    }
+                  else
+                    dst_node = this->context_->add_dereference(Node::make_node(arg2));
+                  this->assign(dst_node, src_node);
+                }
+                break;
+
 	      default:
 		break;
 	      }
 	  }
+        else
+          this->call(call);
       }
       break;
 
     case Expression::EXPRESSION_ALLOCATION:
-      {
-	// Same as above; this is Runtime::NEW.
-	Node* alloc_node = Node::make_node(*pexpr);
-	this->context_->track(alloc_node);
-      }
+      // This is Runtime::NEW.
+      this->context_->track(n);
+      break;
+
+    case Expression::EXPRESSION_STRING_CONCAT:
+      this->context_->track(n);
       break;
 
     case Expression::EXPRESSION_CONVERSION:
       {
 	Type_conversion_expression* tce = (*pexpr)->conversion_expression();
+        Type* ft = tce->expr()->type();
+        Type* tt = tce->type();
+        if ((ft->is_string_type() && tt->is_slice_type())
+            || (ft->is_slice_type() && tt->is_string_type())
+            || (ft->integer_type() != NULL && tt->is_string_type()))
+          {
+            // string([]byte), string([]rune), []byte(string), []rune(string), string(rune)
+            this->context_->track(n);
+            break;
+          }
 	Node* tce_node = Node::make_node(tce);
 	Node* converted = Node::make_node(tce->expr());
 	this->context_->track(tce_node);
@@ -1395,9 +1805,7 @@
 	    for (; p != sce->vals()->end(); ++p)
 	      {
 		Node* enclosed_node = Node::make_node(*p);
-		Node::Escape_state* state =
-		  enclosed_node->state(this->context_, NULL);
-		state->loop_depth = this->context_->loop_depth();
+		this->context_->track(enclosed_node);
 		this->assign(closure_node, enclosed_node);
 	      }
 	  }
@@ -1406,62 +1814,74 @@
 
     case Expression::EXPRESSION_UNARY:
       {
-	if ((*pexpr)->unary_expression()->op() != OPERATOR_AND)
-	  break;
-
-	Node* addr_node = Node::make_node(*pexpr);
-	this->context_->track(addr_node);
-
 	Expression* operand = (*pexpr)->unary_expression()->operand();
-	Named_object* var = NULL;
-	if (operand->var_expression() != NULL)
-	  var = operand->var_expression()->named_object();
-	else if (operand->enclosed_var_expression() != NULL)
-	  var = operand->enclosed_var_expression()->variable();
-	else if (operand->temporary_reference_expression() != NULL)
-	  {
-	    // Found in runtime/chanbarrier_test.go.  The address of a struct
-	    // reference is usually a heap expression, except when it is a part
-	    // of a case statement.  In that case, it is lowered into a
-	    // temporary reference and never linked to the heap expression that
-	    // initializes it.  In general, when taking the address of some
-	    // temporary, the analysis should really be looking at the initial
-	    // value of that temporary.
-	    Temporary_reference_expression* tre =
-	      operand->temporary_reference_expression();
-	    if (tre->statement() != NULL
-		&& tre->statement()->temporary_statement()->init() != NULL)
-	      {
-		Expression* init =
-		  tre->statement()->temporary_statement()->init();
-		Node* init_node = Node::make_node(init);
-		this->assign(addr_node, init_node);
-	      }
-	  }
-
-	if (var == NULL)
-	  break;
-
-	if (var->is_variable()
-	    && !var->var_value()->is_parameter())
-	  {
-	    // For &x, use the loop depth of x if known.
-	    Node::Escape_state* addr_state =
-	      addr_node->state(this->context_, NULL);
-	    Node* operand_node = Node::make_node(operand);
-	    Node::Escape_state* operand_state =
-	      operand_node->state(this->context_, NULL);
-	    if (operand_state->loop_depth != 0)
-	      addr_state->loop_depth = operand_state->loop_depth;
-	  }
-	else if ((var->is_variable()
-		  && var->var_value()->is_parameter())
-		 || var->is_result_variable())
-	  {
-	    Node::Escape_state* addr_state =
-	      addr_node->state(this->context_, NULL);
-	    addr_state->loop_depth = 1;
-	  }
+
+        if ((*pexpr)->unary_expression()->op() == OPERATOR_AND)
+          {
+            this->context_->track(n);
+
+            Named_object* var = NULL;
+            if (operand->var_expression() != NULL)
+              var = operand->var_expression()->named_object();
+            else if (operand->enclosed_var_expression() != NULL)
+              var = operand->enclosed_var_expression()->variable();
+
+            if (var != NULL
+                && ((var->is_variable() && var->var_value()->is_parameter())
+                    || var->is_result_variable()))
+              {
+                Node::Escape_state* addr_state = n->state(this->context_, NULL);
+                addr_state->loop_depth = 1;
+                break;
+              }
+          }
+
+        if ((*pexpr)->unary_expression()->op() != OPERATOR_AND
+            && (*pexpr)->unary_expression()->op() != OPERATOR_MULT)
+          break;
+
+        // For &x and *x, use the loop depth of x if known.
+        Node::Escape_state* expr_state = n->state(this->context_, NULL);
+        Node* operand_node = Node::make_node(operand);
+        Node::Escape_state* operand_state = operand_node->state(this->context_, NULL);
+        if (operand_state->loop_depth != 0)
+          expr_state->loop_depth = operand_state->loop_depth;
+      }
+      break;
+
+    case Expression::EXPRESSION_ARRAY_INDEX:
+      {
+        Array_index_expression* aie = (*pexpr)->array_index_expression();
+
+        // Propagate the loopdepth to element.
+        Node* array_node = Node::make_node(aie->array());
+        Node::Escape_state* elem_state = n->state(this->context_, NULL);
+        Node::Escape_state* array_state = array_node->state(this->context_, NULL);
+        elem_state->loop_depth = array_state->loop_depth;
+
+        if (aie->end() != NULL && !aie->array()->type()->is_slice_type())
+          {
+            // Slicing an array. This effectively takes the address of the array.
+            Expression* addr = Expression::make_unary(OPERATOR_AND, aie->array(),
+                                                      aie->location());
+            Node* addr_node = Node::make_node(addr);
+            n->set_child(addr_node);
+            this->context_->track(addr_node);
+
+            Node::Escape_state* addr_state = addr_node->state(this->context_, NULL);
+            addr_state->loop_depth = array_state->loop_depth;
+          }
+      }
+      break;
+
+    case Expression::EXPRESSION_FIELD_REFERENCE:
+      {
+        // Propagate the loopdepth to field.
+        Node* struct_node =
+          Node::make_node((*pexpr)->field_reference_expression()->expr());
+        Node::Escape_state* field_state = n->state(this->context_, NULL);
+        Node::Escape_state* struct_state = struct_node->state(this->context_, NULL);
+        field_state->loop_depth = struct_state->loop_depth;
       }
       break;
 
@@ -1527,6 +1947,17 @@
 	}
 
       this->context_->init_retvals(call_node, fntype);
+
+      // It could be a closure call that returns captured variable.
+      // Model this by flowing the func expression to result.
+      // See issue #14409.
+      Node* fn_node = Node::make_node(call->fn());
+      std::vector<Node*> retvals = call_node->state(this->context_, NULL)->retvals;
+      for (std::vector<Node*>::const_iterator p = retvals.begin();
+           p != retvals.end();
+           ++p)
+        this->assign_deref(*p, fn_node);
+
       return;
     }
 
@@ -1541,33 +1972,26 @@
 
       Function* f = fn->named_object()->func_value();
       const Bindings* callee_bindings = f->block()->bindings();
-
-      const Typed_identifier_list* results = fntype->results();
+      Function::Results* results = f->result_variables();
       if (results != NULL)
 	{
 	  // Setup output list on this call node.
 	  Node::Escape_state* state = call_node->state(this->context_, NULL);
-	  for (Typed_identifier_list::const_iterator p1 = results->begin();
+	  for (Function::Results::const_iterator p1 = results->begin();
 	       p1 != results->end();
 	       ++p1)
 	    {
-	      if (p1->name().empty() || Gogo::is_sink_name(p1->name()))
-		continue;
-
-	      Named_object* result_no =
-		callee_bindings->lookup_local(p1->name());
-	      go_assert(result_no != NULL);
-	      Node* result_node = Node::make_node(result_no);
+	      Node* result_node = Node::make_node(*p1);
 	      state->retvals.push_back(result_node);
 	    }
 	}
 
       std::vector<Node*>::iterator p = arg_nodes.begin();
-      if (fntype->is_method()
-	  && fntype->receiver()->type()->has_pointer())
+      if (fntype->is_method())
 	{
 	  std::string rcvr_name = fntype->receiver()->name();
-	  if (rcvr_name.empty() || Gogo::is_sink_name(rcvr_name))
+	  if (rcvr_name.empty() || Gogo::is_sink_name(rcvr_name)
+              || !fntype->receiver()->type()->has_pointer())
 	    ;
 	  else
 	    {
@@ -1575,7 +1999,17 @@
 		callee_bindings->lookup_local(fntype->receiver()->name());
 	      go_assert(rcvr_no != NULL);
 	      Node* rcvr_node = Node::make_node(rcvr_no);
-	      this->assign(rcvr_node, *p);
+              if (fntype->receiver()->type()->points_to() == NULL
+                  && (*p)->expr()->type()->points_to() != NULL)
+                // This is a call to a value method that has been lowered into a call
+                // to a pointer method.  Gccgo generates a pointer method for all
+                // method calls and takes the address of the value passed as the
+                // receiver then immediately dereferences it within the function.
+                // In this case, the receiver address does not escape; its content
+                // flows to the call.
+                this->assign_deref(rcvr_node, *p);
+              else
+                this->assign(rcvr_node, *p);
 	    }
 	  ++p;
 	}
@@ -1628,25 +2062,25 @@
   // Receiver.
   std::vector<Node*>::iterator p = arg_nodes.begin();
   if (fntype->is_method()
-      && fntype->receiver()->type()->has_pointer()
       && p != arg_nodes.end())
     {
       // First argument to call will be the receiver.
       std::string* note = fntype->receiver()->note();
       if (fntype->receiver()->type()->points_to() == NULL
-	  && (*p)->expr()->unary_expression() != NULL
-	  && (*p)->expr()->unary_expression()->op() == OPERATOR_AND)
-	{
-	  // This is a call to a value method that has been lowered into a call
-	  // to a pointer method.  Gccgo generates a pointer method for all
-	  // method calls and takes the address of the value passed as the
-	  // receiver then immediately dereferences it within the function.
-	  // In this case, the receiver does not escape.
-	}
+          && (*p)->expr()->type()->points_to() != NULL)
+        // This is a call to a value method that has been lowered into a call
+        // to a pointer method.  Gccgo generates a pointer method for all
+        // method calls and takes the address of the value passed as the
+        // receiver then immediately dereferences it within the function.
+        // In this case, the receiver address does not escape; its content
+        // flows to the call.
+        this->assign_from_note(note, call_state->retvals,
+                               this->context_->add_dereference(*p));
       else
 	{
 	  if (!Type::are_identical(fntype->receiver()->type(),
-			       (*p)->expr()->type(), true, NULL))
+				   (*p)->expr()->type(), Type::COMPARE_TAGS,
+				   NULL))
 	    {
 	      // This will be converted later, preemptively track it instead
 	      // of its conversion expression which will show up in a later pass.
@@ -1665,14 +2099,13 @@
 	   ++pn, ++p)
 	{
 	  if (!Type::are_identical(pn->type(), (*p)->expr()->type(),
-				   true, NULL))
+				   Type::COMPARE_TAGS, NULL))
 	    {
 	      // This will be converted later, preemptively track it instead
 	      // of its conversion expression which will show up in a later pass.
 	      this->context_->track(*p);
 	    }
 
-	  // TODO(cmang): Special care for varargs parameter?
 	  Type* t = pn->type();
 	  if (t != NULL
 	      && t->has_pointer())
@@ -1680,10 +2113,12 @@
 	      std::string* note = pn->note();
 	      int enc = this->assign_from_note(note, call_state->retvals, *p);
 	      if (enc == Node::ESCAPE_NONE
-		  && (call->is_deferred()
-		      || call->is_concurrent()))
+		  && !call->is_deferred()
+		  && !call->is_concurrent())
 		{
-		  // TODO(cmang): Mark the argument as strictly non-escaping.
+                  // TODO(cmang): Mark the argument as strictly non-escaping?
+                  // In the gc compiler this is for limiting the lifetime of
+                  // temporaries. We probably don't need this?
 		}
 	    }
 	}
@@ -1718,7 +2153,10 @@
 	      src->ast_format(gogo).c_str(), src->details().c_str(),
 	      src->op_format().c_str());
 
-  if (dst->expr() != NULL)
+  if (dst->is_indirect())
+    // Lose track of the dereference.
+    dst = this->context_->sink();
+  else if (dst->expr() != NULL)
     {
       // Analyze the lhs of the assignment.
       // Replace DST with this->context_->sink() if we can't track it.
@@ -1785,6 +2223,18 @@
 	  }
 	  break;
 
+        case Expression::EXPRESSION_TEMPORARY_REFERENCE:
+          {
+            // Temporary is tracked through the underlying Temporary_statement.
+            Temporary_statement* t =
+	      dst->expr()->temporary_reference_expression()->statement();
+	    if (t->value_escapes())
+	      dst = this->context_->sink();
+	    else
+	      dst = Node::make_node(t);
+          }
+          break;
+
 	default:
 	  // TODO(cmang): Add debugging info here: only a few expressions
 	  // should leave DST unmodified.
@@ -1792,12 +2242,17 @@
         }
     }
 
-  if (src->expr() != NULL)
+  if (src->object() != NULL)
+    this->flows(dst, src);
+  else if (src->is_indirect())
+    this->flows(dst, src);
+  else if (src->expr() != NULL)
     {
       Expression* e = src->expr();
       switch (e->classification())
         {
 	case Expression::EXPRESSION_VAR_REFERENCE:
+        case Expression::EXPRESSION_ENCLOSED_VAR_REFERENCE:
 	  // DST = var
 	case Expression::EXPRESSION_HEAP:
 	  // DST = &T{...}.
@@ -1812,6 +2267,8 @@
 	  // DST = new(T).
 	case Expression::EXPRESSION_BOUND_METHOD:
 	  // DST = x.M.
+        case Expression::EXPRESSION_STRING_CONCAT:
+          // DST = str1 + str2
 	  this->flows(dst, src);
 	  break;
 
@@ -1823,73 +2280,35 @@
 	  }
 	  break;
 
-	case Expression::EXPRESSION_ENCLOSED_VAR_REFERENCE:
-	  {
-	    Named_object* var = e->enclosed_var_expression()->variable();
-	    Node* var_node = Node::make_node(var);
-	    this->flows(dst, var_node);
-	  }
-	  break;
-
 	case Expression::EXPRESSION_CALL:
 	  {
 	    Call_expression* call = e->call_expression();
+            if (call->is_builtin())
+              {
+                Builtin_call_expression* bce = call->builtin_call_expression();
+                if (bce->code() == Builtin_call_expression::BUILTIN_APPEND)
+                  {
+                    // Append returns the first argument.
+                    // The subsequent arguments are already leaked because
+                    // they are operands to append.
+                    Node* appendee = Node::make_node(call->args()->front());
+                    this->assign(dst, appendee);
+                  }
+                break;
+              }
 	    Func_expression* fe = call->fn()->func_expression();
 	    if (fe != NULL && fe->is_runtime_function())
 	      {
 		switch (fe->runtime_code())
 		  {
-		  case Runtime::GROWSLICE:
-		    {
-		      // Append returns the first argument.
-		      // The subsequent arguments are already leaked because
-		      // they are operands to append.
-		      Node* appendee = Node::make_node(call->args()->front());
-		      this->assign(dst, appendee);
-		      break;
-		    }
-
 		  case Runtime::MAKECHAN:
+		  case Runtime::MAKECHAN64:
 		  case Runtime::MAKEMAP:
 		  case Runtime::MAKESLICE:
 		  case Runtime::MAKESLICE64:
 		    // DST = make(...).
-		  case Runtime::SLICEBYTETOSTRING:
-		    // DST = string([]byte{...}).
-		  case Runtime::SLICERUNETOSTRING:
-		    // DST = string([]int{...}).
-		  case Runtime::STRINGTOSLICEBYTE:
-		    // DST = []byte(str).
-		  case Runtime::STRINGTOSLICERUNE:
-		    // DST = []rune(str).
-		  case Runtime::CONCATSTRINGS:
-		  case Runtime::CONCATSTRING2:
-		  case Runtime::CONCATSTRING3:
-		  case Runtime::CONCATSTRING4:
-		  case Runtime::CONCATSTRING5:
-		    // DST = str1 + str2
-		  case Runtime::CONSTRUCT_MAP:
-		    // When building a map literal's backend representation.
-		    // Likely never seen here and covered in
-		    // Expression::EXPRESSION_MAP_CONSTRUCTION.
-		  case Runtime::INTSTRING:
-		    // DST = string(i).
-		  case Runtime::IFACEE2E2:
-		  case Runtime::IFACEI2E2:
-		  case Runtime::IFACEE2I2:
-		  case Runtime::IFACEI2I2:
-		  case Runtime::IFACEE2T2P:
-		  case Runtime::IFACEI2T2P:
-		  case Runtime::IFACEE2T2:
-		  case Runtime::IFACEI2T2:
-		  case Runtime::REQUIREITAB:
-		    // All versions of interface conversion that might result
-		    // from a type assertion.  Some of these are the result of
-		    // a tuple type assertion statement and may not be covered
-		    // by the case in Expression::EXPRESSION_CONVERSION or
-		    // Expression::EXPRESSION_TYPE_GUARD.
-		    this->flows(dst, src);
-		    break;
+                    this->flows(dst, src);
+                    break;
 
 		  default:
 		    break;
@@ -1909,28 +2328,85 @@
 		break;
 	      }
 
-	    // TODO(cmang): Handle case from issue 4529.
-	    // Node* call_node = Node::make_node(e);
-	    // Node::Escape_state* call_state = call_node->state(this->context_, NULL);
-	    // std::vector<Node*> retvals = call_state->retvals;
-	    // for (std::vector<Node*>::const_iterator p = retvals.begin();
-	    //	    p != retvals.end();
-	    //	    ++p)
-	    //	 this->flows(dst, *p);
+            // Result flows to dst.
+            Node* call_node = Node::make_node(e);
+            Node::Escape_state* call_state = call_node->state(this->context_, NULL);
+            std::vector<Node*> retvals = call_state->retvals;
+            for (std::vector<Node*>::const_iterator p = retvals.begin();
+                 p != retvals.end();
+                 ++p)
+              this->flows(dst, *p);
 	  }
 	  break;
 
+        case Expression::EXPRESSION_CALL_RESULT:
+          {
+            Call_result_expression* cre = e->call_result_expression();
+            Call_expression* call = cre->call()->call_expression();
+            if (call->is_builtin())
+              break;
+            if (call->fn()->func_expression() != NULL
+                && call->fn()->func_expression()->is_runtime_function())
+              {
+                switch (call->fn()->func_expression()->runtime_code())
+                  {
+                  case Runtime::IFACEE2E2:
+                  case Runtime::IFACEI2E2:
+                  case Runtime::IFACEE2I2:
+                  case Runtime::IFACEI2I2:
+                  case Runtime::IFACEE2T2P:
+                  case Runtime::IFACEI2T2P:
+                    {
+                      // x, ok = v.(T), where T is a pointer or interface,
+                      // is lowered to
+                      // x, ok = IFACEI2E2(v), or
+                      // x, ok = IFACEI2I2(type, v)
+                      // The last arg flows to the first result.
+                      // Note: IFACEX2T2 has different signature, handled by
+                      // ::expression.
+                      if (cre->index() != 0)
+                        break;
+                      Node* arg_node = Node::make_node(call->args()->back());
+                      this->assign(dst, arg_node);
+                    }
+                    break;
+
+                  default:
+                    break;
+                  }
+                break;
+              }
+
+            Node* call_node = Node::make_node(call);
+            Node* ret_node = call_node->state(context_, NULL)->retvals[cre->index()];
+            this->assign(dst, ret_node);
+          }
+          break;
+
 	case Expression::EXPRESSION_FUNC_REFERENCE:
 	  if (e->func_expression()->closure() != NULL)
-	    {
-	      // If SRC is a reference to a function closure, DST flows into
-	      // the underyling closure variable.
-	      Expression* closure = e->func_expression()->closure();
-	      Node* closure_node = Node::make_node(closure);
-	      this->flows(dst, closure_node);
-	    }
+            this->flows(dst, src);
 	  break;
 
+        case Expression::EXPRESSION_CONVERSION:
+          {
+            Type_conversion_expression* tce = e->conversion_expression();
+            Type* ft = tce->expr()->type();
+            Type* tt = tce->type();
+            if ((ft->is_string_type() && tt->is_slice_type())
+                || (ft->is_slice_type() && tt->is_string_type())
+                || (ft->integer_type() != NULL && tt->is_string_type()))
+              {
+                // string([]byte), string([]rune), []byte(string), []rune(string), string(rune)
+                this->flows(dst, src);
+                break;
+              }
+            // Conversion preserves input value.
+            Expression* underlying = tce->expr();
+            this->assign(dst, Node::make_node(underlying));
+          }
+          break;
+
 	case Expression::EXPRESSION_FIELD_REFERENCE:
 	  {
 	    // A non-pointer can't escape from a struct.
@@ -1939,7 +2415,6 @@
 	  }
 	  // Fall through.
 
-	case Expression::EXPRESSION_CONVERSION:
 	case Expression::EXPRESSION_TYPE_GUARD:
 	case Expression::EXPRESSION_ARRAY_INDEX:
 	case Expression::EXPRESSION_STRING_INDEX:
@@ -1956,15 +2431,23 @@
 		    break;
 		  }
 	      }
-	    else if (e->conversion_expression() != NULL)
-	      left = e->conversion_expression()->expr();
 	    else if (e->type_guard_expression() != NULL)
 	      left = e->type_guard_expression()->expr();
 	    else if (e->array_index_expression() != NULL)
 	      {
 		Array_index_expression* aie = e->array_index_expression();
-		if (e->type()->is_slice_type())
-		  left = aie->array();
+		if (aie->end() != NULL)
+                  // slicing
+                  if (aie->array()->type()->is_slice_type())
+                    left = aie->array();
+                  else
+                    {
+                      // slicing an array
+                      // The gc compiler has an implicit address operator.
+                      go_assert(src->child() != NULL);
+                      this->assign(dst, src->child());
+                      break;
+                    }
 		else if (!aie->array()->type()->is_slice_type())
 		  {
 		    // Indexing an array preserves the input value.
@@ -1981,15 +2464,9 @@
 	    else if (e->string_index_expression() != NULL)
 	      {
 		String_index_expression* sie = e->string_index_expression();
-		if (e->type()->is_slice_type())
+		if (e->type()->is_string_type())
+                  // slicing
 		  left = sie->string();
-		else if (!sie->string()->type()->is_slice_type())
-		  {
-		    // Indexing a string preserves the input value.
-		    Node* string_node = Node::make_node(sie->string());
-		    this->assign(dst, string_node);
-		    break;
-		  }
 		else
 		  {
 		    this->flows(dst, src);
@@ -2012,6 +2489,7 @@
 	      case OPERATOR_PLUS:
 	      case OPERATOR_MINUS:
 	      case OPERATOR_XOR:
+              case OPERATOR_OR:
 	      case OPERATOR_MULT:
 	      case OPERATOR_DIV:
 	      case OPERATOR_MOD:
@@ -2063,13 +2541,7 @@
 	case Expression::EXPRESSION_TEMPORARY_REFERENCE:
 	  {
 	    Statement* temp = e->temporary_reference_expression()->statement();
-	    if (temp != NULL
-		&& temp->temporary_statement()->init() != NULL)
-	      {
-		Expression* init = temp->temporary_statement()->init();
-		Node* init_node = Node::make_node(init);
-		this->assign(dst, init_node);
-	      }
+            this->assign(dst, Node::make_node(temp));
 	  }
 	  break;
 
@@ -2079,6 +2551,8 @@
 	  break;
 	}
     }
+  else if (src->statement() != NULL && src->statement()->temporary_statement() != NULL)
+    this->flows(dst, src);
 }
 
 // Model the assignment of DST to an indirection of SRC.
@@ -2101,36 +2575,6 @@
 	  // or numeric constants.
 	  return;
 
-	case Expression::EXPRESSION_FIXED_ARRAY_CONSTRUCTION:
-	case Expression::EXPRESSION_SLICE_CONSTRUCTION:
-	case Expression::EXPRESSION_STRUCT_CONSTRUCTION:
-	  {
-	    // Dereferencing an array, slice, or struct is like accessing each
-	    // of its values.  In this situation, we model the flow from src to
-	    // dst where src is one of the above as a flow from each of src's
-	    // values to dst.
-	    Expression* e = src->expr();
-	    Expression_list* vals = NULL;
-	    if (e->slice_literal() != NULL)
-	      vals = e->slice_literal()->vals();
-	    else if (e->array_literal() != NULL)
-	      vals = e->array_literal()->vals();
-	    else
-	      vals = e->struct_literal()->vals();
-
-	    if (vals != NULL)
-	      {
-		for (Expression_list::const_iterator p = vals->begin();
-		     p != vals->end();
-		     ++p)
-		  {
-		    if ((*p) != NULL)
-		      this->assign(dst, Node::make_node(*p));
-		  }
-	      }
-	  }
-	  return;
-
 	default:
 	  break;
         }
@@ -2169,7 +2613,8 @@
     }
 
   if (this->context_->gogo()->debug_escape_level() > 2)
-    go_inform(src->location(), "assignfromtag:: src=  em=%s",
+    go_inform(src->location(), "assignfromtag:: src=%s em=%s",
+              src->ast_format(context_->gogo()).c_str(),
 	      Escape_note::make_tag(enc).c_str());
 
   if (enc == Node::ESCAPE_UNKNOWN)
@@ -2222,8 +2667,7 @@
 Escape_analysis_assign::flows(Node* dst, Node* src)
 {
   // Don't bother capturing the flow from scalars.
-  if (src->expr() != NULL
-      && !src->expr()->type()->has_pointer())
+  if (src->type() != NULL && !src->type()->has_pointer())
     return;
 
   // Don't confuse a blank identifier with the sink.
@@ -2234,8 +2678,7 @@
   Node::Escape_state* src_state = src->state(this->context_, NULL);
   if (dst == src
       || dst_state == src_state
-      || dst_state->flows.find(src) != dst_state->flows.end()
-      || src_state->flows.find(dst) != src_state->flows.end())
+      || dst_state->flows.find(src) != dst_state->flows.end())
     return;
 
   Gogo* gogo = this->context_->gogo();
@@ -2271,6 +2714,7 @@
 	{
 	  Node* res_node = Node::make_node(*p);
 	  Node::Escape_state* res_state = res_node->state(context, fn);
+          res_state->fn = fn;
 	  res_state->loop_depth = 0;
 
 	  // If this set of functions is recursive, we lose track of the return values.
@@ -2299,6 +2743,7 @@
       go_assert(param_no != NULL);
       Node* param_node = Node::make_node(param_no);
       Node::Escape_state* param_state = param_node->state(context, fn);
+      param_state->fn = fn;
       param_state->loop_depth = 1;
 
       if (!p->type()->has_pointer())
@@ -2309,9 +2754,10 @@
       if (fn->package() != NULL)
 	param_node->set_encoding(Node::ESCAPE_HEAP);
       else
-	param_node->set_encoding(Node::ESCAPE_NONE);
-
-      // TODO(cmang): Track this node in no_escape list.
+        {
+          param_node->set_encoding(Node::ESCAPE_NONE);
+          context->track(param_node);
+        }
     }
 
   Escape_analysis_loop el;
@@ -2432,10 +2878,12 @@
   else
     dst_no = dst->object();
   bool dst_is_result = dst_no != NULL && dst_no->is_result_variable();
+  Node::Escape_state* dst_state = dst->state(this->context_, NULL);
 
   if (src_is_param
       && dst_is_result
-      && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_SCOPE)
+      && src_state->fn == dst_state->fn
+      && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_HEAP)
       && dst->encoding() != Node::ESCAPE_HEAP)
     {
       // This case handles:
@@ -2446,13 +2894,13 @@
       if (debug_level != 0)
 	{
 	  if (debug_level == 1)
-	    go_inform(src->location(),
+	    go_inform(src->definition_location(),
 		      "leaking param: %s to result %s level=%d",
 		      src->ast_format(gogo).c_str(),
 		      dst->ast_format(gogo).c_str(),
 		      level.value());
 	  else
-	    go_inform(src->location(),
+	    go_inform(src->definition_location(),
 		      "leaking param: %s to result %s level={%d %d}",
 		      src->ast_format(gogo).c_str(),
 		      dst->ast_format(gogo).c_str(),
@@ -2486,7 +2934,7 @@
   // escape from struct.
   if (src_is_param
       && dst->encoding() == Node::ESCAPE_HEAP
-      && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_SCOPE)
+      && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_HEAP)
       && level.value() > 0)
     {
       int enc =
@@ -2494,20 +2942,23 @@
 			   Node::ESCAPE_NONE);
       src->set_encoding(enc);
       if (debug_level != 0)
-	go_inform(src->location(), "mark escaped content: %s",
+	go_inform(src->definition_location(), "mark escaped content: %s",
 		  src->ast_format(gogo).c_str());
     }
 
   // A src object leaks if its value or address is assigned to a dst object
   // in a different scope (at a different loop depth).
-  Node::Escape_state* dst_state = dst->state(this->context_, NULL);
   bool src_leaks = (level.value() <= 0
 		    && level.suffix_value() <= 0
 		    && dst_state->loop_depth < mod_loop_depth);
+  src_leaks = src_leaks || (level.value() <= 0
+              && (dst->encoding() & ESCAPE_MASK) == Node::ESCAPE_HEAP);
+  // old src encoding, used to prevent duplicate error messages
+  int osrcesc = src->encoding();
 
   if (src_is_param
       && (src_leaks || dst_state->loop_depth < 0)
-      && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_SCOPE))
+      && (src->encoding() & ESCAPE_MASK) < int(Node::ESCAPE_HEAP))
     {
       if (level.suffix_value() > 0)
 	{
@@ -2515,15 +2966,16 @@
 	    Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES),
 			       Node::ESCAPE_NONE);
 	  src->set_encoding(enc);
-	  if (debug_level != 0)
-	    go_inform(src->location(), "leaking param content: %s",
+	  if (debug_level != 0 && osrcesc != src->encoding())
+	    go_inform(src->definition_location(), "leaking param content: %s",
 		      src->ast_format(gogo).c_str());
 	}
       else
 	{
 	  if (debug_level != 0)
-	    go_inform(src->location(), "leaking param");
-	  src->set_encoding(Node::ESCAPE_SCOPE);
+	    go_inform(src->definition_location(), "leaking param: %s",
+                      src->ast_format(gogo).c_str());
+	  src->set_encoding(Node::ESCAPE_HEAP);
 	}
     }
   else if (src->expr() != NULL)
@@ -2557,22 +3009,20 @@
 	  if (src_leaks)
 	    {
 	      src->set_encoding(Node::ESCAPE_HEAP);
-	      if (debug_level != 0)
-		{
-		  go_inform(underlying->location(), "moved to heap: %s",
-                            underlying_node->ast_format(gogo).c_str());
-
-		  if (debug_level > 1)
-		    go_inform(src->location(),
-			      "%s escapes to heap, level={%d %d}, "
-			      "dst.eld=%d, src.eld=%d",
-			      src->ast_format(gogo).c_str(), level.value(),
-			      level.suffix_value(), dst_state->loop_depth,
-			      mod_loop_depth);
-		  else
-		    go_inform(src->location(), "%s escapes to heap",
-			      src->ast_format(gogo).c_str());
-		}
+              if (osrcesc != src->encoding())
+                {
+                  move_to_heap(gogo, underlying);
+                  if (debug_level > 1)
+                    go_inform(src->location(),
+                              "%s escapes to heap, level={%d %d}, "
+                              "dst.eld=%d, src.eld=%d",
+                              src->ast_format(gogo).c_str(), level.value(),
+                              level.suffix_value(), dst_state->loop_depth,
+                              mod_loop_depth);
+                  else if (debug_level > 0)
+                    go_inform(src->location(), "%s escapes to heap",
+                              src->ast_format(gogo).c_str());
+                }
 
 	      this->flood(level.decrease(), dst,
 			  underlying_node, mod_loop_depth);
@@ -2600,7 +3050,7 @@
 	  if (src_leaks)
 	    {
 	      src->set_encoding(Node::ESCAPE_HEAP);
-	      if (debug_level != 0)
+	      if (debug_level != 0 && osrcesc != src->encoding())
 		go_inform(src->location(), "%s escapes to heap",
 			  src->ast_format(gogo).c_str());
 	      extra_loop_depth = mod_loop_depth;
@@ -2609,82 +3059,123 @@
       else if (e->call_expression() != NULL)
 	{
 	  Call_expression* call = e->call_expression();
-	  if (call->fn()->func_expression() != NULL)
-	    {
-	      Func_expression* func = call->fn()->func_expression();
-	      if (func->is_runtime_function())
-		{
-		  switch (func->runtime_code())
-		    {
-		    case Runtime::GROWSLICE:
-		      {
-			// Propagate escape information to appendee.
-			Expression* appendee = call->args()->front();
-			this->flood(level, dst, Node::make_node(appendee), -1);
-		      }
-		      break;
-
-		    case Runtime::MAKECHAN:
-		    case Runtime::MAKEMAP:
-		    case Runtime::MAKESLICE:
-		    case Runtime::MAKESLICE64:
-		    case Runtime::SLICEBYTETOSTRING:
-		    case Runtime::SLICERUNETOSTRING:
-		    case Runtime::STRINGTOSLICEBYTE:
-		    case Runtime::STRINGTOSLICERUNE:
-		    case Runtime::CONCATSTRINGS:
-		    case Runtime::CONCATSTRING2:
-		    case Runtime::CONCATSTRING3:
-		    case Runtime::CONCATSTRING4:
-		    case Runtime::CONCATSTRING5:
-		    case Runtime::CONSTRUCT_MAP:
-		    case Runtime::INTSTRING:
-		    case Runtime::REQUIREITAB:
-		      // All runtime calls that involve allocation of memory
-		      // except new.  Runtime::NEW gets lowered into an
-		      // allocation expression.
-		      if (src_leaks)
-			{
-			  src->set_encoding(Node::ESCAPE_HEAP);
-			  if (debug_level != 0)
-			    go_inform(src->location(), "%s escapes to heap",
-				      src->ast_format(gogo).c_str());
-			  extra_loop_depth = mod_loop_depth;
-			}
-		      break;
-
-		    default:
-		      break;
-		    }
-		}
-	      else if (src_leaks
-		       && (func->closure() != NULL
-			   || func->bound_method_expression() != NULL))
-		{
-		  // A closure or bound method; we lost track of actual function
-		  // so if this leaks, this call must be done on the heap.
-		  src->set_encoding(Node::ESCAPE_HEAP);
-		  if (debug_level != 0)
-		    go_inform(src->location(), "%s escapes to heap",
-			      src->ast_format(gogo).c_str());
-		}
-	    }
+          if (call->is_builtin())
+            {
+              Builtin_call_expression* bce = call->builtin_call_expression();
+              if (bce->code() == Builtin_call_expression::BUILTIN_APPEND)
+                {
+                  // Propagate escape information to appendee.
+                  Expression* appendee = call->args()->front();
+                  this->flood(level, dst, Node::make_node(appendee), -1);
+                }
+            }
+          else if (call->fn()->func_expression() != NULL
+                   && call->fn()->func_expression()->is_runtime_function())
+            {
+              switch (call->fn()->func_expression()->runtime_code())
+                {
+                case Runtime::MAKECHAN:
+		case Runtime::MAKECHAN64:
+                case Runtime::MAKEMAP:
+                case Runtime::MAKESLICE:
+                case Runtime::MAKESLICE64:
+                  if (src_leaks)
+                    {
+                      src->set_encoding(Node::ESCAPE_HEAP);
+                      if (debug_level != 0 && osrcesc != src->encoding())
+                        go_inform(src->location(), "%s escapes to heap",
+                                  src->ast_format(gogo).c_str());
+                      extra_loop_depth = mod_loop_depth;
+                    }
+                  break;
+
+                default:
+                  break;
+                }
+            }
+          else if (src_state->retvals.size() > 0)
+            {
+              // In this case a link went directly to a call, but should really go
+              // to the dummy .outN outputs that were created for the call that
+              // themselves link to the inputs with levels adjusted.
+              // See e.g. #10466.
+              // This can only happen with functions returning a single result.
+              go_assert(src_state->retvals.size() == 1);
+              if (debug_level > 2)
+                go_inform(src->location(), "[%d] dst %s escwalk replace src: %s with %s",
+                          this->context_->loop_depth(),
+                          dst->ast_format(gogo).c_str(),
+                          src->ast_format(gogo).c_str(),
+                          src_state->retvals[0]->ast_format(gogo).c_str());
+              src = src_state->retvals[0];
+              src_state = src->state(this->context_, NULL);
+            }
 	}
       else if (e->allocation_expression() != NULL && src_leaks)
 	{
 	  // Calls to Runtime::NEW get lowered into an allocation expression.
 	  src->set_encoding(Node::ESCAPE_HEAP);
-	  if (debug_level != 0)
+	  if (debug_level != 0 && osrcesc != src->encoding())
 	    go_inform(src->location(), "%s escapes to heap",
                       src->ast_format(gogo).c_str());
+          extra_loop_depth = mod_loop_depth;
 	}
+      else if ((e->map_literal() != NULL
+               || e->string_concat_expression() != NULL
+               || (e->func_expression() != NULL && e->func_expression()->closure() != NULL)
+               || e->bound_method_expression() != NULL)
+               && src_leaks)
+        {
+          src->set_encoding(Node::ESCAPE_HEAP);
+          if (debug_level != 0 && osrcesc != src->encoding())
+            go_inform(src->location(), "%s escapes to heap",
+                      src->ast_format(gogo).c_str());
+          extra_loop_depth = mod_loop_depth;
+        }
+      else if (e->conversion_expression() != NULL && src_leaks)
+        {
+          Type_conversion_expression* tce = e->conversion_expression();
+          Type* ft = tce->expr()->type();
+          Type* tt = tce->type();
+          if ((ft->is_string_type() && tt->is_slice_type())
+              || (ft->is_slice_type() && tt->is_string_type())
+              || (ft->integer_type() != NULL && tt->is_string_type()))
+            {
+              // string([]byte), string([]rune), []byte(string), []rune(string), string(rune)
+              src->set_encoding(Node::ESCAPE_HEAP);
+              if (debug_level != 0 && osrcesc != src->encoding())
+                go_inform(src->location(), "%s escapes to heap",
+                          src->ast_format(gogo).c_str());
+              extra_loop_depth = mod_loop_depth;
+            }
+        }
+      else if (e->array_index_expression() != NULL
+               && !e->array_index_expression()->array()->type()->is_slice_type())
+        {
+          Array_index_expression* aie = e->array_index_expression();
+          if (aie->end() != NULL)
+            {
+              // Slicing an array.
+              // Flow its implicit address-of node to DST.
+              this->flood(level, dst, src->child(), -1);
+            }
+          else
+            {
+              // Indexing an array.
+              // An array element flowing to DST behaves like the array
+              // flowing to DST.
+              Expression* underlying = e->array_index_expression()->array();
+              Node* underlying_node = Node::make_node(underlying);
+              this->flood(level, dst, underlying_node, -1);
+            }
+        }
       else if ((e->field_reference_expression() != NULL
 		&& e->field_reference_expression()->expr()->unary_expression() == NULL)
 	       || e->type_guard_expression() != NULL
 	       || (e->array_index_expression() != NULL
-		   && e->type()->is_slice_type())
+		   && e->array_index_expression()->end() != NULL)
 	       || (e->string_index_expression() != NULL
-		   && e->type()->is_slice_type()))
+		   && e->type()->is_string_type()))
 	{
 	  Expression* underlying;
 	  if (e->field_reference_expression() != NULL)
@@ -2713,14 +3204,7 @@
 	      underlying = underlying->unary_expression()->operand();
 	    }
 	  else if (e->array_index_expression() != NULL)
-	    {
-	      underlying = e->array_index_expression()->array();
-	      if (!underlying->type()->is_slice_type())
-		{
-		  Node* underlying_node = Node::make_node(underlying);
-		  this->flood(level, dst, underlying_node, 1);
-		}
-	    }
+	    underlying = e->array_index_expression()->array();
 	  else if (e->map_index_expression() != NULL)
 	    underlying = e->map_index_expression()->map();
 	  else
@@ -2730,9 +3214,15 @@
 	  Node* underlying_node = Node::make_node(underlying);
 	  this->flood(level.increase(), dst, underlying_node, -1);
 	}
-
-      // TODO(cmang): Add case for Issue #10466.
+      else if (e->temporary_reference_expression() != NULL)
+        {
+          Statement* t = e->temporary_reference_expression()->statement();
+          this->flood(level, dst, Node::make_node(t), -1);
+        }
     }
+  else if (src->is_indirect())
+    // Increase the level for a dereference.
+    this->flood(level.increase(), dst, src->child(), -1);
 
   level = level.copy();
   for (std::set<Node*>::const_iterator p = src_state->flows.begin();
@@ -2749,6 +3239,13 @@
 void
 Gogo::propagate_escape(Escape_context* context, Node* dst)
 {
+  if (dst->object() == NULL
+      && (dst->expr() == NULL
+          || (dst->expr()->var_expression() == NULL
+              && dst->expr()->enclosed_var_expression() == NULL
+              && dst->expr()->func_expression() == NULL)))
+    return;
+
   Node::Escape_state* state = dst->state(context, NULL);
   Gogo* gogo = context->gogo();
   if (gogo->debug_escape_level() > 1)
@@ -2788,39 +3285,61 @@
 {
   // External functions are assumed unsafe
   // unless //go:noescape is given before the declaration.
-  if (fn->package() != NULL || !fn->is_function())
+  if (fn->package() != NULL)
+    return;
+
+  if (fn->is_function_declaration())
     {
-      // TODO(cmang): Implement //go:noescape directive for external functions;
-      // mark input parameters as not escaping.
-      return;
+      Function_declaration* fdcl = fn->func_declaration_value();
+      if ((fdcl->pragmas() & GOPRAGMA_NOESCAPE) != 0)
+        {
+          Function_type* fntype = fdcl->type();
+          if (fntype->parameters() != NULL)
+            {
+              const Typed_identifier_list* til = fntype->parameters();
+              int i = 0;
+              for (Typed_identifier_list::const_iterator p = til->begin();
+                   p != til->end();
+                   ++p, ++i)
+                if (p->type()->has_pointer())
+                  fntype->add_parameter_note(i, Node::ESCAPE_NONE);
+            }
+        }
     }
 
+  if (!fn->is_function())
+    return;
+
   Function_type* fntype = fn->func_value()->type();
   Bindings* bindings = fn->func_value()->block()->bindings();
 
-  if (fntype->is_method()
-      && !fntype->receiver()->name().empty()
-      && !Gogo::is_sink_name(fntype->receiver()->name()))
+  if (fntype->is_method())
     {
-      Named_object* rcvr_no = bindings->lookup(fntype->receiver()->name());
-      go_assert(rcvr_no != NULL);
-      Node* rcvr_node = Node::make_node(rcvr_no);
-      switch ((rcvr_node->encoding() & ESCAPE_MASK))
-	{
-	case Node::ESCAPE_NONE: // not touched by flood
-	case Node::ESCAPE_RETURN:
-	  if (fntype->receiver()->type()->has_pointer())
-	    // Don't bother tagging for scalars.
-	    fntype->add_receiver_note(rcvr_node->encoding());
-	  break;
-
-	case Node::ESCAPE_HEAP: // flooded, moved to heap.
-	case Node::ESCAPE_SCOPE: // flooded, value leaves scope.
-	  break;
-
-	default:
-	  break;
-	}
+      if (fntype->receiver()->name().empty()
+          || Gogo::is_sink_name(fntype->receiver()->name()))
+        // Unnamed receiver is not used in the function body, does not escape.
+        fntype->add_receiver_note(Node::ESCAPE_NONE);
+      else
+        {
+          Named_object* rcvr_no = bindings->lookup(fntype->receiver()->name());
+          go_assert(rcvr_no != NULL);
+          Node* rcvr_node = Node::make_node(rcvr_no);
+          switch ((rcvr_node->encoding() & ESCAPE_MASK))
+            {
+            case Node::ESCAPE_NONE: // not touched by flood
+            case Node::ESCAPE_RETURN:
+              if (fntype->receiver()->type()->has_pointer())
+                // Don't bother tagging for scalars.
+                fntype->add_receiver_note(rcvr_node->encoding());
+              break;
+
+            case Node::ESCAPE_HEAP: // flooded, moved to heap.
+              break;
+
+            default:
+              break;
+            }
+        }
     }
 
   int i = 0;
@@ -2832,7 +3351,12 @@
 	   ++p, ++i)
 	{
 	  if (p->name().empty() || Gogo::is_sink_name(p->name()))
-	    continue;
+            {
+              // Parameter not used in the function body, does not escape.
+              if (p->type()->has_pointer())
+                fntype->add_parameter_note(i, Node::ESCAPE_NONE);
+              continue;
+            }
 
 	  Named_object* param_no = bindings->lookup(p->name());
 	  go_assert(param_no != NULL);
@@ -2847,7 +3371,6 @@
 	      break;
 
 	    case Node::ESCAPE_HEAP: // flooded, moved to heap.
-	    case Node::ESCAPE_SCOPE: // flooded, value leaves scope.
 	      break;
 
 	    default:
@@ -2867,3 +3390,39 @@
   Escape_analysis_tag eat(context);
   eat.tag(fn);
 }
+
+// Reclaim memory of escape analysis Nodes.
+
+void
+Gogo::reclaim_escape_nodes()
+{
+  Node::reclaim_nodes();
+}
+
+void
+Node::reclaim_nodes()
+{
+  for (std::map<Named_object*, Node*>::iterator p = Node::objects.begin();
+       p != Node::objects.end();
+       ++p)
+    delete p->second;
+  Node::objects.clear();
+
+  for (std::map<Expression*, Node*>::iterator p = Node::expressions.begin();
+       p != Node::expressions.end();
+       ++p)
+    delete p->second;
+  Node::expressions.clear();
+
+  for (std::map<Statement*, Node*>::iterator p = Node::statements.begin();
+       p != Node::statements.end();
+       ++p)
+    delete p->second;
+  Node::statements.clear();
+
+  for (std::vector<Node*>::iterator p = Node::indirects.begin();
+       p != Node::indirects.end();
+       ++p)
+    delete *p;
+  Node::indirects.clear();
+}