aboutsummaryrefslogtreecommitdiff
path: root/gcc/go/gofrontend/escape.h
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/go/gofrontend/escape.h')
-rw-r--r--gcc/go/gofrontend/escape.h442
1 files changed, 442 insertions, 0 deletions
diff --git a/gcc/go/gofrontend/escape.h b/gcc/go/gofrontend/escape.h
new file mode 100644
index 00000000000..c409acb310c
--- /dev/null
+++ b/gcc/go/gofrontend/escape.h
@@ -0,0 +1,442 @@
+// escape.h -- Go escape analysis (based on Go compiler algorithm).
+
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#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(Gogo* gogo, bool recursive);
+
+ // Return the Go IR.
+ Gogo*
+ gogo() const
+ { return this->gogo_; }
+
+ // Return the current function being analyzed.
+ Named_object*
+ current_function() const
+ { return this->current_function_; }
+
+ // Change the function being analyzed.
+ void
+ set_current_function(Named_object* fn)
+ { this->current_function_ = fn; }
+
+ // Return true if this is the context for a mutually recursive set of functions.
+ bool
+ 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)