escape: Implement the discovery phase.
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 13 May 2016 20:42:36 +0000 (20:42 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 13 May 2016 20:42:36 +0000 (20:42 +0000)
    Implements the discovery phase according the SCC algorithm used in
    gc/esc.go.  Traverse all functions to find components that are either
    trivial or recursive.  The discovered function groups will be analyzed
    from the bottom-up to allow for an inter-procedural analysis.

    Reviewed-on: https://go-review.googlesource.com/18221

From-SVN: r236224

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/escape.cc

index 01f6e23183cc5b24d2cd2db8f2f192ea412f69b5..8e0fd0fb63e46a546fc853a1fbf5bdf35d09930f 100644 (file)
@@ -1,4 +1,4 @@
-7f5a9fde801eb755a5252fd4ff588b0a47475bd3
+a87af72757d9a2e4479062a459a41d4540398005
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 4a5c46c55224f16c98a56f34b258ecb3fc9a21c6..01f3da7242162a8b1a8cf47157e0d91c9e4590b0 100644 (file)
@@ -304,14 +304,193 @@ Gogo::analyze_escape()
     }
 }
 
     }
 }
 
+// Traverse the program, discovering the functions that are roots of strongly
+// connected components.  The goal of this phase to produce a set of functions
+// that must be analyzed in order.
+
+class Escape_analysis_discover : public Traverse
+{
+ public:
+  Escape_analysis_discover(Gogo* gogo)
+    : Traverse(traverse_functions),
+      gogo_(gogo), component_ids_()
+  { }
+
+  int
+  function(Named_object*);
+
+  int
+  visit(Named_object*);
+
+  int
+  visit_code(Named_object*, int);
+
+ private:
+  // A counter used to generate the ID for the function node in the graph.
+  static int id;
+
+  // Type used to map functions to an ID in a graph of connected components.
+  typedef Unordered_map(Named_object*, int) Component_ids;
+
+  // The Go IR.
+  Gogo* gogo_;
+  // The list of functions encountered during connected component discovery.
+  Component_ids component_ids_;
+  // The stack of functions that this component consists of.
+  std::stack<Named_object*> stack_;
+};
+
+int Escape_analysis_discover::id = 0;
+
+// Visit each function.
+
+int
+Escape_analysis_discover::function(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.
+//
+// Finding these sets is finding strongly connected components
+// in the static call graph.  The algorithm for doing that is taken
+// from Sedgewick, Algorithms, Second Edition, p. 482, with two
+// adaptations.
+//
+// First, a closure (fn->func_value()->enclosing() == NULL) cannot be the
+// root of a connected component.  Refusing to use it as a root
+// forces it into the component of the function in which it appears.
+// This is more convenient for escape analysis.
+//
+// Second, each function becomes two virtual nodes in the graph,
+// with numbers n and n+1. We record the function's node number as n
+// but search from node n+1. If the search tells us that the component
+// number (min) is n+1, we know that this is a trivial component: one function
+// plus its closures. If the search tells us that the component number is
+// n, then there was a path from node n+1 back to node n, meaning that
+// the function set is mutually recursive. The escape analysis can be
+// more precise when analyzing a single non-recursive function than
+// when analyzing a set of mutually recursive functions.
+
+int
+Escape_analysis_discover::visit(Named_object* fn)
+{
+  Component_ids::const_iterator p = this->component_ids_.find(fn);
+  if (p != this->component_ids_.end())
+    // Already visited.
+    return p->second;
+
+  this->id++;
+  int id = this->id;
+  this->component_ids_[fn] = id;
+  this->id++;
+  int min = this->id;
+
+  this->stack_.push(fn);
+  min = this->visit_code(fn, min);
+  if ((min == id || min == id + 1)
+      && fn->is_function()
+      && fn->func_value()->enclosing() == NULL)
+    {
+      bool recursive = min == id;
+      std::vector<Named_object*> group;
+
+      for (; !this->stack_.empty(); this->stack_.pop())
+       {
+         Named_object* n = this->stack_.top();
+         if (n == fn)
+           {
+             this->stack_.pop();
+             break;
+           }
+
+         group.push_back(n);
+         this->component_ids_[n] = std::numeric_limits<int>::max();
+       }
+      group.push_back(fn);
+      this->component_ids_[fn] = std::numeric_limits<int>::max();
+
+      std::reverse(group.begin(), group.end());
+      this->gogo_->add_analysis_set(group, recursive);
+    }
+
+  return min;
+}
+
+// Helper class for discovery step.  Traverse expressions looking for
+// function calls and closures to visit during the discovery step.
+
+class Escape_discover_expr : public Traverse
+{
+ public:
+  Escape_discover_expr(Escape_analysis_discover* ead, int min)
+    : Traverse(traverse_expressions),
+      ead_(ead), min_(min)
+  { }
+
+  int
+  min()
+  { return this->min_; }
+
+  int
+  expression(Expression** pexpr);
+
+ private:
+  // The original discovery analysis.
+  Escape_analysis_discover* ead_;
+  // The minimum component ID in this group.
+  int min_;
+};
+
+// Visit any calls or closures found when discovering expressions.
+
+int
+Escape_discover_expr::expression(Expression** pexpr)
+{
+  Expression* e = *pexpr;
+  Named_object* fn = NULL;
+  if (e->call_expression() != NULL
+      && e->call_expression()->fn()->func_expression() != NULL)
+    {
+      // Method call or function call.
+      fn = e->call_expression()->fn()->func_expression()->named_object();
+    }
+  else if (e->func_expression() != NULL
+           && e->func_expression()->closure() != NULL)
+    {
+      // Closure.
+      fn = e->func_expression()->named_object();
+    }
+
+  if (fn != NULL)
+    this->min_ = std::min(this->min_, this->ead_->visit(fn));
+  return TRAVERSE_CONTINUE;
+}
+
+// Visit the body of each function, returns ID of the minimum connected
+// component found in the body.
+
+int
+Escape_analysis_discover::visit_code(Named_object* fn, int min)
+{
+  if (!fn->is_function())
+    return min;
+
+  Escape_discover_expr ede(this, min);
+  fn->func_value()->traverse(&ede);
+  return ede.min();
+}
+
 // Discover strongly connected groups of functions to analyze.
 
 void
 Gogo::discover_analysis_sets()
 {
 // Discover strongly connected groups of functions to analyze.
 
 void
 Gogo::discover_analysis_sets()
 {
-  // TODO(cmang): Implement Analysis_set discovery traversal.
-  // Escape_analysis_discover(this);
-  // this->traverse(&ead);
+  Escape_analysis_discover ead(this);
+  this->traverse(&ead);
 }
 
 // Build a connectivity graph between nodes in the function being analyzed.
 }
 
 // Build a connectivity graph between nodes in the function being analyzed.