| // 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 dereferenced. |
| 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 referenced. |
| 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 references (-1) and indirects (+1) applied to a Node. |
| int value_; |
| // The sum of all references (-1) abd indirects (+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, |
| // A "fake" node that models the indirection of its child node. |
| // This node does not correspond to an AST node. |
| NODE_INDIRECT |
| }; |
| |
| // 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, |
| // 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), |
| child_(NULL) |
| { this->u_.object_val = no; } |
| |
| Node(Expression* e) |
| : classification_(NODE_EXPRESSION), state_(NULL), encoding_(ESCAPE_UNKNOWN), |
| child_(NULL) |
| { this->u_.expression_val = e; } |
| |
| Node(Statement* s) |
| : classification_(NODE_STATEMENT), state_(NULL), encoding_(ESCAPE_UNKNOWN), |
| child_(NULL) |
| { this->u_.statement_val = s; } |
| |
| Node(Node *n) |
| : classification_(NODE_INDIRECT), state_(NULL), encoding_(ESCAPE_UNKNOWN), |
| child_(n) |
| {} |
| |
| ~Node(); |
| |
| // Return this node's type. |
| Type* |
| type() const; |
| |
| // Return this node's location. |
| Location |
| location() const; |
| |
| // Return the location where the node's underlying object is defined. |
| Location |
| definition_location() const; |
| |
| // Return this node's AST formatted string. |
| std::string |
| ast_format(Gogo*) const; |
| |
| // Return this node's detailed format string. |
| std::string |
| details(); |
| |
| std::string |
| op_format() const; |
| |
| // Return this node's escape state. |
| Escape_state* |
| state(Escape_context* context, Named_object* fn); |
| |
| // Return this node's escape encoding. |
| int |
| encoding(); |
| |
| // Set the node's escape encoding. |
| void |
| set_encoding(int enc); |
| |
| bool |
| is_big(Escape_context*) const; |
| |
| 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); |
| } |
| |
| bool |
| is_indirect() const |
| { return this->classification_ == NODE_INDIRECT; } |
| |
| // Return its child node. |
| // Child node is used only in indirect node, and in expression node |
| // representing slicing an array. |
| Node* |
| child() const |
| { return this->child_; } |
| |
| // Set the child node. |
| void |
| set_child(Node* n) |
| { this->child_ = n; } |
| |
| // 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*); |
| |
| static Node* |
| make_indirect_node(Node*); |
| |
| // Return the maximum of an existing escape encoding E and a new |
| // escape type. |
| static int |
| max_encoding(int e, int etype); |
| |
| // Return a modified encoding for an input parameter that flows into an |
| // output parameter. |
| static int |
| note_inout_flows(int e, int index, Level level); |
| |
| // Reclaim nodes. |
| static void |
| reclaim_nodes(); |
| |
| 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_; |
| |
| // Child node, used only in indirect node, and expression node representing |
| // slicing an array. |
| Node* child_; |
| |
| // Cache all the Nodes created via Node::make_node to make the API simpler. |
| static Unordered_map(Named_object*, Node*) objects; |
| static Unordered_map(Expression*, Node*) expressions; |
| static Unordered_map(Statement*, Node*) statements; |
| |
| // Collection of all NODE_INDIRECT Nodes, used for reclaiming memory. This |
| // is not a cache -- each make_indirect_node will make a fresh Node. |
| static std::vector<Node*> indirects; |
| }; |
| |
| // 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 the name of the current function. |
| std::string |
| current_function_name() const; |
| |
| // 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) |