Commit 2c418e1f by Ian Lance Taylor

escape: Add escape graph nodes.

    
    Introduces the nodes used to model connectivity in the escape graph
    and related state: a node's escape level and an encoding that will
    be added to import and export data.
    
    Reviewed-on: https://go-review.googlesource.com/18268

From-SVN: r235988
parent 5e5b938a
33f1d1d151721305ba37f3e23652d21310f868af
7f5a9fde801eb755a5252fd4ff588b0a47475bd3
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -4,9 +4,263 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include <limits>
#include <stack>
#include "gogo.h"
#include "types.h"
#include "expressions.h"
#include "statements.h"
#include "escape.h"
// class Node.
// Return the node's type, if it makes sense for it to have one.
Type*
Node::type() const
{
if (this->object() != NULL
&& this->object()->is_variable())
return this->object()->var_value()->type();
else if (this->object() != NULL
&& this->object()->is_function())
return this->object()->func_value()->type();
else if (this->expr() != NULL)
return this->expr()->type();
else
return NULL;
}
// A helper for reporting; return this node's location.
Location
Node::location() const
{
if (this->object() != NULL && !this->object()->is_sink())
return this->object()->location();
else if (this->expr() != NULL)
return this->expr()->location();
else if (this->statement() != NULL)
return this->statement()->location();
else
return Linemap::unknown_location();
}
// Return this node's state, creating it if has not been initialized.
Node::Escape_state*
Node::state(Escape_context* context, Named_object* fn)
{
if (this->state_ == NULL)
{
if (this->expr() != NULL && this->expr()->var_expression() != NULL)
{
// Tie state of variable references to underlying variables.
Named_object* var_no = this->expr()->var_expression()->named_object();
Node* var_node = Node::make_node(var_no);
this->state_ = var_node->state(context, fn);
}
else
{
this->state_ = new Node::Escape_state;
if (fn == NULL)
fn = context->current_function();
this->state_->fn = fn;
}
}
go_assert(this->state_ != NULL);
return this->state_;
}
void
Node::set_encoding(int enc)
{
this->encoding_ = enc;
if (this->expr() != NULL
&& this->expr()->var_expression() != NULL)
{
// Set underlying object as well.
Named_object* no = this->expr()->var_expression()->named_object();
Node::make_node(no)->set_encoding(enc);
}
}
bool
Node::is_sink() const
{
if (this->object() != NULL
&& this->object()->is_sink())
return true;
else if (this->expr() != NULL
&& this->expr()->is_sink_expression())
return true;
return false;
}
std::map<Named_object*, Node*> Node::objects;
std::map<Expression*, Node*> Node::expressions;
std::map<Statement*, Node*> Node::statements;
// Make a object node or return a cached node for this object.
Node*
Node::make_node(Named_object* no)
{
if (Node::objects.find(no) != Node::objects.end())
return Node::objects[no];
Node* n = new Node(no);
std::pair<Named_object*, Node*> val(no, n);
Node::objects.insert(val);
return n;
}
// Make an expression node or return a cached node for this expression.
Node*
Node::make_node(Expression* e)
{
if (Node::expressions.find(e) != Node::expressions.end())
return Node::expressions[e];
Node* n = new Node(e);
std::pair<Expression*, Node*> val(e, n);
Node::expressions.insert(val);
return n;
}
// Make a statement node or return a cached node for this statement.
Node*
Node::make_node(Statement* s)
{
if (Node::statements.find(s) != Node::statements.end())
return Node::statements[s];
Node* n = new Node(s);
std::pair<Statement*, Node*> val(s, n);
Node::statements.insert(val);
return n;
}
// Returns the maximum of an exisiting escape value
// (and its additional parameter flow flags) and a new escape type.
int
Node::max_encoding(int e, int etype)
{
if ((e & ESCAPE_MASK) >= etype)
return e;
if (etype == Node::ESCAPE_NONE || etype == Node::ESCAPE_RETURN)
return (e & ~ESCAPE_MASK) | etype;
return etype;
}
// Return a modified encoding for an input parameter that flows into an
// output parameter.
// Class Escape_context.
Escape_context::Escape_context(Gogo* gogo, bool recursive)
: gogo_(gogo), current_function_(NULL), recursive_(recursive),
sink_(Node::make_node(Named_object::make_sink())), loop_depth_(0)
{
// The sink always escapes to heap and strictly lives outside of the
// current function i.e. loop_depth == -1.
this->sink_->set_encoding(Node::ESCAPE_HEAP);
Node::Escape_state* state = this->sink_->state(this, NULL);
state->loop_depth = -1;
}
// Initialize the dummy return values for this Node N using the results
// in FNTYPE.
void
Escape_context::init_retvals(Node* n, Function_type* fntype)
{
if (fntype == NULL || fntype->results() == NULL)
return;
Node::Escape_state* state = n->state(this, NULL);
Location loc = n->location();
int i = 0;
char buf[50];
for (Typed_identifier_list::const_iterator p = fntype->results()->begin();
p != fntype->results()->end();
++p, ++i)
{
snprintf(buf, sizeof buf, ".out%d", i);
Variable* dummy_var = new Variable(p->type(), NULL, false, false,
false, loc);
dummy_var->set_is_used();
Named_object* dummy_no =
Named_object::make_variable(buf, NULL, dummy_var);
Node* dummy_node = Node::make_node(dummy_no);
// Initialize the state of the dummy output node.
dummy_node->state(this, NULL);
// Add dummy node to the retvals of n.
state->retvals.push_back(dummy_node);
}
}
// Apply an indirection to N and return the result.
// This really only works if N is an expression node; it essentially becomes
// Node::make_node(n->expr()->deref()). We need the escape context to set the
// correct loop depth, however.
Node*
Escape_context::add_dereference(Node* n)
{
// Just return the original node if we can't add an indirection.
if (n->object() != NULL || n->statement() != NULL)
return n;
Node* ind = Node::make_node(n->expr()->deref());
// Initialize the state if this node doesn't already exist.
ind->state(this, NULL);
return ind;
}
void
Escape_context::track(Node* n)
{
n->set_encoding(Node::ESCAPE_NONE);
// Initialize this node's state if it hasn't been encountered
// before.
Node::Escape_state* state = n->state(this, NULL);
state->loop_depth = this->loop_depth_;
this->noesc_.push_back(n);
}
// Return the string representation of an escapement encoding.
std::string
Escape_note::make_tag(int encoding)
{
char buf[50];
snprintf(buf, sizeof buf, "esc:0x%x", encoding);
return buf;
}
// Return the escapement encoding for a string tag.
int
Escape_note::parse_tag(std::string* tag)
{
if (tag == NULL || tag->substr(0, 4) != "esc:")
return Node::ESCAPE_UNKNOWN;
int encoding = (int)strtol(tag->substr(4).c_str(), NULL, 0);
if (encoding == 0)
return Node::ESCAPE_UNKNOWN;
return encoding;
}
// Analyze the program flow for escape information.
void
......@@ -21,7 +275,7 @@ Gogo::analyze_escape()
++p)
{
std::vector<Named_object*> stack = p->first;
Escape_context* context = new Escape_context(p->second);
Escape_context* context = new Escape_context(this, p->second);
// Analyze the flow of each function; build the connection graph.
// This is the assign phase.
......@@ -33,13 +287,12 @@ Gogo::analyze_escape()
this->assign_connectivity(context, *fn);
}
// TODO(cmang): Introduce escape node.
// Propagate levels across each dst. This is the flood phase.
// std::vector<Node*> dsts = context->dsts();
// for (std::vector<Node*>::iterator n = dsts.begin();
// n != dsts.end();
// ++n)
// this->propagate_escape(context, *n);
std::set<Node*> dsts = context->dsts();
for (std::set<Node*>::iterator n = dsts.begin();
n != dsts.end();
++n)
this->propagate_escape(context, *n);
// Tag each exported function's parameters with escape information.
for (std::vector<Named_object*>::iterator fn = stack.begin();
......@@ -71,11 +324,10 @@ Gogo::assign_connectivity(Escape_context*, Named_object*)
// TODO(cmang): Analyze the current function's body.
}
// Propagate escape information across the nodes modeled in this Analysis_set,
// TODO(cmang): Introduce escape analysis node.
// Propagate escape information across the nodes modeled in this Analysis_set.
void
Gogo::propagate_escape(Escape_context*)
Gogo::propagate_escape(Escape_context*, Node*)
{
// TODO(cmang): Do a breadth-first traversal of a node's upstream, adjusting
// the Level appropriately.
......
......@@ -51,6 +51,7 @@ class Bvariable;
class Blabel;
class Bfunction;
class Escape_context;
class Node;
// This file declares the basic classes used to hold the internal
// representation of Go which is built by the parser.
......@@ -570,7 +571,7 @@ class Gogo
// Traverse the objects in the connecitivty graph from the sink, adjusting the
// escape levels of each object.
void
propagate_escape(Escape_context*);
propagate_escape(Escape_context*, Node*);
// Add notes about the escape level of a function's input and output
// parameters for exporting and importing top level functions.
......
......@@ -8,6 +8,7 @@
#define GO_TYPES_H
#include "go-linemap.h"
#include "escape.h"
class Gogo;
class Package;
......@@ -1324,7 +1325,7 @@ class Typed_identifier
public:
Typed_identifier(const std::string& name, Type* type,
Location location)
: name_(name), type_(type), location_(location)
: name_(name), type_(type), location_(location), note_(NULL)
{ }
// Get the name.
......@@ -1351,6 +1352,16 @@ class Typed_identifier
this->type_ = type;
}
// Get the escape note.
std::string*
note() const
{ return this->note_; }
// Set the escape note.
void
set_note(const std::string& note)
{ this->note_ = new std::string(note); }
private:
// Identifier name.
std::string name_;
......@@ -1358,6 +1369,9 @@ class Typed_identifier
Type* type_;
// The location where the name was seen.
Location location_;
// Escape note for this typed identifier. Used when importing and exporting
// functions.
std::string* note_;
};
// A list of Typed_identifiers.
......@@ -1422,6 +1436,10 @@ class Typed_identifier_list
back() const
{ return this->entries_.back(); }
Typed_identifier&
at(size_t i)
{ return this->entries_.at(i); }
const Typed_identifier&
at(size_t i) const
{ return this->entries_.at(i); }
......@@ -1778,7 +1796,7 @@ class Function_type : public Type
: Type(TYPE_FUNCTION),
receiver_(receiver), parameters_(parameters), results_(results),
location_(location), is_varargs_(false), is_builtin_(false),
fnbtype_(NULL)
fnbtype_(NULL), is_tagged_(false)
{ }
// Get the receiver.
......@@ -1786,6 +1804,11 @@ class Function_type : public Type
receiver() const
{ return this->receiver_; }
// Add an escape note for the receiver.
void
add_receiver_note(int encoding)
{ this->receiver_->set_note(Escape_note::make_tag(encoding)); }
// Get the return names and types.
const Typed_identifier_list*
results() const
......@@ -1796,6 +1819,21 @@ class Function_type : public Type
parameters() const
{ return this->parameters_; }
// Add an escape note for the ith parameter.
void
add_parameter_note(int index, int encoding)
{ this->parameters_->at(index).set_note(Escape_note::make_tag(encoding)); }
// Whether this function has been tagged during escape analysis.
bool
is_tagged() const
{ return this->is_tagged_; }
// Mark this function as tagged after analyzing its escape.
void
set_is_tagged()
{ this->is_tagged_ = true; }
// Whether this is a varargs function.
bool
is_varargs() const
......@@ -1950,6 +1988,9 @@ class Function_type : public Type
// The backend representation of this type for backend function
// declarations and definitions.
Btype* fnbtype_;
// Whether this function has been analyzed by escape analysis. If this is
// TRUE, this function type's parameters contain a summary of the analysis.
bool is_tagged_;
};
// The type of a function's backend representation.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment