compiler: Propagate escape info from closures to enclosed variables.
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 29 Apr 2015 22:14:34 +0000 (22:14 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 29 Apr 2015 22:14:34 +0000 (22:14 +0000)
If a closure escapes, the enclosed variables must escape via the
closure.  Reachability analysis had a bug where the enclosed
variables were not considered as reachable from the closure.

From-SVN: r222597

gcc/go/gofrontend/escape.cc
gcc/go/gofrontend/gogo.h

index 7c8955bb8a5a4ab8f9377283f353667670d23d31..d2e7ae12931c5bc9544bf61436ae2a0c187c8c2b 100644 (file)
@@ -1276,8 +1276,22 @@ Gogo::analyze_reachability()
       Node* m = worklist.front();
       worklist.pop_front();
 
-      for (std::set<Node*>::iterator n = m->edges().begin();
-          n != m->edges().end();
+      std::set<Node*> reachable = m->edges();
+      if (m->object()->is_function()
+         && m->object()->func_value()->needs_closure())
+       {
+         // If a closure escapes everything it closes over also escapes.
+         Function* closure = m->object()->func_value();
+         for (size_t i = 0; i < closure->closure_field_count(); i++)
+           {
+             Named_object* enclosed = closure->enclosing_var(i);
+             Node* enclosed_node = this->lookup_connection_node(enclosed);
+             go_assert(enclosed_node != NULL);
+             reachable.insert(enclosed_node);
+           }
+       }
+      for (std::set<Node*>::iterator n = reachable.begin();
+          n != reachable.end();
           ++n)
        {
          // If an object can be reached from a node with ESCAPE_GLOBAL,
@@ -1296,7 +1310,7 @@ Gogo::analyze_reachability()
        p != this->named_connection_nodes_.end();
        ++p)
     {
-      if (p->second->connection_node()->escape_state() == Node::ESCAPE_ARG)
+      if (p->second->connection_node()->escape_state() < Node::ESCAPE_NONE)
        worklist.push_back(p->second);
     }
 
@@ -1305,15 +1319,30 @@ Gogo::analyze_reachability()
       Node* m = worklist.front();
       worklist.pop_front();
 
-      for (std::set<Node*>::iterator n = m->edges().begin();
-          n != m->edges().end();
+      std::set<Node*> reachable = m->edges();
+      if (m->object()->is_function()
+         && m->object()->func_value()->needs_closure())
+       {
+         // If a closure escapes everything it closes over also escapes.
+         Function* closure = m->object()->func_value();
+         for (size_t i = 0; i < closure->closure_field_count(); i++)
+           {
+             Named_object* enclosed = closure->enclosing_var(i);
+             Node* enclosed_node = this->lookup_connection_node(enclosed);
+             go_assert(enclosed_node != NULL);
+             reachable.insert(enclosed_node);
+           }
+       }
+      for (std::set<Node*>::iterator n = reachable.begin();
+          n != reachable.end();
           ++n)
        {
          // If an object can be reached from a node with ESCAPE_ARG,
          // it is ESCAPE_ARG or ESCAPE_GLOBAL.
-         if ((*n)->connection_node()->escape_state() > Node::ESCAPE_ARG)
+         Node::Escapement_lattice e = m->connection_node()->escape_state();
+         if ((*n)->connection_node()->escape_state() > e)
            {
-             (*n)->connection_node()->set_escape_state(Node::ESCAPE_ARG);
+             (*n)->connection_node()->set_escape_state(e);
              worklist.push_back(*n);
            }
        }
index e30178d6c235adccefa15874e09f49e81dd1794b..ffc2440f8ffacac74015fadb39ec0000f6496901 100644 (file)
@@ -1042,6 +1042,11 @@ class Function
     this->is_unnamed_type_stub_method_ = true;
   }
 
+  // Return the amount of enclosed variables in this closure.
+  size_t
+  closure_field_count() const
+  { return this->closure_fields_.size(); }
+
   // Add a new field to the closure variable.
   void
   add_closure_field(Named_object* var, Location loc)