From: Ian Lance Taylor Date: Fri, 13 May 2016 20:42:36 +0000 (+0000) Subject: escape: Implement the discovery phase. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=6eaba1b066d5a7e6628cb1f83d8c3ffa43df0e10;p=gcc.git escape: Implement the discovery phase. 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 --- diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 01f6e23183c..8e0fd0fb63e 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index 4a5c46c5522..01f3da72421 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -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 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 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::max(); + } + group.push_back(fn); + this->component_ids_[fn] = std::numeric_limits::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.