public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Go patch committed: Implement escape analysis discovery phase
@ 2016-05-13 20:42 Ian Lance Taylor
  0 siblings, 0 replies; only message in thread
From: Ian Lance Taylor @ 2016-05-13 20:42 UTC (permalink / raw)
  To: gcc-patches, gofrontend-dev

[-- Attachment #1: Type: text/plain, Size: 224 bytes --]

This patch by Chris Manghane implements the escape analysis discovery
phase in the Go frontend.  Escape analysis is still not turned on.
Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian

[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 6251 bytes --]

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 235988)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -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.
Index: gcc/go/gofrontend/escape.cc
===================================================================
--- gcc/go/gofrontend/escape.cc	(revision 235988)
+++ gcc/go/gofrontend/escape.cc	(working copy)
@@ -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()
 {
-  // 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.

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2016-05-13 20:42 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-05-13 20:42 Go patch committed: Implement escape analysis discovery phase Ian Lance Taylor

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).