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
This commit is contained in:
Ian Lance Taylor 2016-05-13 20:42:36 +00:00
parent 54ece5e295
commit 6eaba1b066
2 changed files with 183 additions and 4 deletions

View 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.

View 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()
{
// 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.