// 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 flows; // If the node is a function call, the list of nodes returned. std::vector 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 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& 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& 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 dsts_; // All the nodes that were noted as possibly not escaping in this context. std::vector 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)