-33f1d1d151721305ba37f3e23652d21310f868af
+7f5a9fde801eb755a5252fd4ff588b0a47475bd3
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
// 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
++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.
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();
// 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.
#ifndef GO_ESCAPE_H
#define GO_ESCAPE_H
+#include "gogo.h"
+
class Named_object;
+class Expression;
+class Statement;
+class Escape_context;
+
+// There can be loops in the escape graph that lead to arbitrary recursions.
+// See comment in gc/esc.go.
+static const int MIN_LEVEL = -2;
+
+// Level models the escapement of a Node using two integers that are computed
+// by backwards-analyzing the flow of a function from its sink and increasing or
+// decreasing based on dereferences and addressing, respectively.
+// One integer, known as the level's VALUE (think absolute value), is just the
+// sum of indirections (via referencing or dereferencing) applied to the Node.
+// The second, known as the level's SUFFIX_VALUE, is the amount of indirections
+// applied after some data has been copied from the node. When accessing a
+// field F of an object O and then applying indirections, for example, the field
+// access O.F is assumed to copy that data from O before applying indirections.
+// With this, even if O.F escapes, it might mean that the content of O escape,
+// but not the object O itself.
+
+class Level
+{
+public:
+ Level()
+ : value_(0), suffix_value_(0)
+ { }
+
+ Level(int value, int suffix)
+ : value_(value), suffix_value_(suffix)
+ { }
+
+ // Return this level's value.
+ int
+ value() const
+ { return this->value_; }
+
+ // Return this level's suffix value.
+ int
+ suffix_value() const
+ { return this->suffix_value_; }
+
+ // Increase the level because a node is referenced.
+ Level
+ increase() const
+ {
+ if (this->value_ <= MIN_LEVEL)
+ return Level(MIN_LEVEL, 0);
+
+ return Level(this->value_ + 1, this->suffix_value_ + 1);
+ }
+
+ // Decrease the level because a node is dereferenced.
+ Level
+ decrease() const
+ {
+ if (this->value_ <= MIN_LEVEL)
+ return Level(MIN_LEVEL, 0);
+
+ return Level(this->value_ - 1, this->suffix_value_ - 1);
+ }
+
+ // Model a node being copied.
+ Level
+ copy() const
+ {
+ return Level(this->value_, std::max(this->suffix_value_, 0));
+ }
+
+ // Return a level with the minimum values of this level and l.
+ Level
+ min(const Level& l) const
+ {
+ return Level(std::min(this->value_, l.value()),
+ std::min(this->suffix_value_, l.suffix_value()));
+ }
+
+ // Compare two levels for equality.
+ bool
+ operator==(const Level& l) const
+ {
+ return (this->value_ == l.value()
+ && this->suffix_value_ == l.suffix_value());
+ }
+
+ // Create a level from an integer value.
+ static Level
+ From(int i)
+ {
+ if (i <= MIN_LEVEL)
+ return Level(MIN_LEVEL, 0);
+ return Level(i, 0);
+ }
+
+private:
+ // The sum of all indirects (-1) and references (+1) applied to a Node.
+ int value_;
+ // The sum of all indirects (-1) abd references (+1) applied to a copied Node.
+ int suffix_value_;
+};
+
+// A node in the escape graph. This node is an alias to a particular node
+// in the Go parse tree. Specifically, it can represent an expression node,
+// a statement node, or a named object node (a variable or function).
+
+class Node
+{
+ public:
+ // This classification represents type of nodes in the Go parse tree that are
+ // interesting during the analysis.
+ enum Node_classification
+ {
+ NODE_OBJECT,
+ NODE_EXPRESSION,
+ NODE_STATEMENT
+ };
+
+ // The state necessary to keep track of how a node escapes.
+ struct Escape_state
+ {
+ // The current function.
+ Named_object* fn;
+ // A list of source nodes that flow into this node.
+ std::set<Node*> flows;
+ // If the node is a function call, the list of nodes returned.
+ std::vector<Node*> retvals;
+ // The node's loop depth.
+ int loop_depth;
+ // There is an extra loop depth in the flood phase used to account for
+ // variables referenced across closures. This is the maximum value of the
+ // extra loop depth seen during the flood that touches this node.
+ int max_extra_loop_depth;
+ // The node's level.
+ Level level;
+ // An ID given to a node when it is encountered as a flow from the current
+ // dst node. This is used to avoid infinite recursion of cyclic nodes.
+ int flood_id;
+
+ Escape_state()
+ : fn(NULL), loop_depth(0), max_extra_loop_depth(0), flood_id(0)
+ { }
+ };
+
+ // Note: values in this enum appear in export data, and therefore MUST NOT
+ // change.
+ enum Escapement_encoding
+ {
+ ESCAPE_UNKNOWN,
+ // Does not escape to heap, result, or parameters.
+ ESCAPE_NONE,
+ // Is returned or reachable from a return statement.
+ ESCAPE_RETURN,
+ // Allocated in an inner loop, assigned to an outer loop,
+ // which allows construction of non-escaping but arbitrarily large linked
+ // data structures (i.e., not eligible for allocation in a fixed-size stack
+ // stack frame).
+ ESCAPE_SCOPE,
+ // Reachable from the heap.
+ ESCAPE_HEAP,
+ // By construction will not escape.
+ ESCAPE_NEVER
+ };
+
+ // Multiple constructors for each classification.
+ Node(Named_object* no)
+ : classification_(NODE_OBJECT), state_(NULL), encoding_(ESCAPE_UNKNOWN)
+ { this->u_.object_val = no; }
+
+ Node(Expression* e)
+ : classification_(NODE_EXPRESSION), state_(NULL), encoding_(ESCAPE_UNKNOWN)
+ { this->u_.expression_val = e; }
+
+ Node(Statement* s)
+ : classification_(NODE_STATEMENT), state_(NULL), encoding_(ESCAPE_UNKNOWN)
+ { this->u_.statement_val = s; }
+
+ // Return this node's type.
+ Type*
+ type() const;
+
+ // Return this node's location.
+ Location
+ location() const;
+
+ // Return this node's escape state.
+ Escape_state*
+ state(Escape_context* context, Named_object* fn);
+
+ // Return this node's escape encoding.
+ int
+ encoding() const
+ { return this->encoding_; }
+
+ // Set the node's escape encoding.
+ void
+ set_encoding(int enc);
+
+ // Is this node a sink?
+ bool
+ is_sink() const;
+
+ // Methods to return the underlying value in the Node union.
+ Named_object*
+ object() const
+ {
+ return (this->classification_ == NODE_OBJECT
+ ? this->u_.object_val
+ : NULL);
+ }
+
+ Expression*
+ expr() const
+ {
+ return (this->classification_ == NODE_EXPRESSION
+ ? this->u_.expression_val
+ : NULL);
+ }
+
+ Statement*
+ statement() const
+ {
+ return (this->classification_ == NODE_STATEMENT
+ ? this->u_.statement_val
+ : NULL);
+ }
+
+ // Static creation methods for each value supported in the union.
+ static Node*
+ make_node(Named_object*);
+
+ static Node*
+ make_node(Expression*);
+
+ static Node*
+ make_node(Statement*);
+
+ // Return the maximum of an existing escape encoding E and a new
+ // escape type.
+ static int
+ max_encoding(int e, int etype);
+
+ private:
+ // The classification of this Node.
+ Node_classification classification_;
+ // The value union.
+ union
+ {
+ // If NODE_OBJECT.
+ Named_object* object_val;
+ // If NODE_EXPRESSION.
+ Expression* expression_val;
+ // If NODE_STATEMENT.
+ Statement* statement_val;
+ } u_;
+ // The node's escape state.
+ Escape_state* state_;
+ // The node's escape encoding.
+ // The encoding:
+ // | Return Encoding: (width - ESCAPE_RETURN_BITS) |
+ // | Content Escapes bit: 1 |
+ // | Escapement_encoding: ESCAPE_BITS |
+ int encoding_;
+
+ // Cache all the Nodes created via Node::make_node to make the API simpler.
+ static std::map<Named_object*, Node*> objects;
+ static std::map<Expression*, Node*> expressions;
+ static std::map<Statement*, Node*> statements;
+};
+
+// The amount of bits used for the escapement encoding.
+static const int ESCAPE_BITS = 3;
+
+// Mask used to extract encoding.
+static const int ESCAPE_MASK = (1 << ESCAPE_BITS) - 1;
+
+// Value obtained by indirect of parameter escapes to heap.
+static const int ESCAPE_CONTENT_ESCAPES = 1 << ESCAPE_BITS;
+
+// The amount of bits used in encoding of return values.
+static const int ESCAPE_RETURN_BITS = ESCAPE_BITS + 1;
+
+// For each output, the number of bits for a tag.
+static const int ESCAPE_BITS_PER_OUTPUT_IN_TAG = 3;
+
+// The bit max to extract a single tag.
+static const int ESCAPE_BITS_MASK_FOR_TAG = (1 << ESCAPE_BITS_PER_OUTPUT_IN_TAG) - 1;
+
+// The largest level that can be stored in a tag.
+static const int ESCAPE_MAX_ENCODED_LEVEL = ESCAPE_BITS_MASK_FOR_TAG - 1;
+
+// A helper for converting escape notes from encoded integers to a
+// textual format and vice-versa.
+
+class Escape_note
+{
+ public:
+ // Return the string representation of an escapement encoding.
+ static std::string
+ make_tag(int encoding);
+
+ // Return the escapement encoding for a string tag.
+ static int
+ parse_tag(std::string* tag);
+};
// The escape context for a set of functions being analyzed.
class Escape_context
{
public:
- Escape_context(bool recursive)
- : current_function_(NULL), recursive_(recursive)
- { }
+ Escape_context(Gogo* gogo, bool recursive);
+
+ // Return the Go IR.
+ Gogo*
+ gogo() const
+ { return this->gogo_; }
// Return the current function being analyzed.
Named_object*
recursive() const
{ return this->recursive_; }
+ // Return the special sink node for this context.
+ Node*
+ sink()
+ { return this->sink_; }
+
+ // Return the current loop depth.
+ int
+ loop_depth() const
+ { return this->loop_depth_; }
+
+ // Increase the loop depth.
+ void
+ increase_loop_depth()
+ { this->loop_depth_++; }
+
+ // Decrease the loop depth.
+ void
+ decrease_loop_depth()
+ { this->loop_depth_--; }
+
+ void
+ set_loop_depth(int depth)
+ { this->loop_depth_ = depth; }
+
+ // Return the destination nodes encountered in this context.
+ const std::set<Node*>&
+ dsts() const
+ { return this->dsts_; }
+
+ // Add a destination node.
+ void
+ add_dst(Node* dst)
+ { this->dsts_.insert(dst); }
+
+ // Return the nodes initially marked as non-escaping before flooding.
+ const std::vector<Node*>&
+ non_escaping_nodes() const
+ { return this->noesc_; }
+
+ // Initialize the dummy return values for this Node N using the results
+ // in FNTYPE.
+ void
+ init_retvals(Node* n, Function_type* fntype);
+
+ // Return the indirection of Node N.
+ Node*
+ add_dereference(Node* n);
+
+ // Keep track of possibly non-escaping node N.
+ void
+ track(Node* n);
+
+ int
+ flood_id() const
+ { return this->flood_id_; }
+
+ void
+ increase_flood_id()
+ { this->flood_id_++; }
+
+ int
+ pdepth() const
+ { return this->pdepth_; }
+
+ void
+ increase_pdepth()
+ { this->pdepth_++; }
+
+ void
+ decrease_pdepth()
+ { this->pdepth_--; }
+
private:
+ // The Go IR.
+ Gogo* gogo_;
// The current function being analyzed.
Named_object* current_function_;
// Return whether this is the context for a recursive function or a group of mutually
// recursive functions.
bool recursive_;
+ // The sink for this escape context. Nodes whose reference objects created
+ // outside the current function are assigned to the sink as well as nodes that
+ // the analysis loses track of.
+ Node* sink_;
+ // Used to detect nested loop scopes.
+ int loop_depth_;
+ // All the destination nodes considered in this set of analyzed functions.
+ std::set<Node*> dsts_;
+ // All the nodes that were noted as possibly not escaping in this context.
+ std::vector<Node*> noesc_;
+ // An ID given to each dst and the flows discovered through DFS of that dst.
+ // This is used to avoid infinite recursion from nodes that point to each
+ // other within the flooding phase.
+ int flood_id_;
+ // The current level of recursion within a flooded section; used to debug.
+ int pdepth_;
};
#endif // !defined(GO_ESCAPE_H)
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.
// 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.
#define GO_TYPES_H
#include "go-linemap.h"
+#include "escape.h"
class Gogo;
class Package;
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.
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_;
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.
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); }
: 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.
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
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
// 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.