Commit bb14e19c by Ian Lance Taylor

compiler: bring escape analysis mostly in line with gc compiler

    
    This CL ports the latest (~Go 1.10) escape analysis code from
    the gc compiler. Changes include:
    
    - In the gc compiler, the variable expression is represented
      with the variable node itself (ONAME). It is the same node
      used in the AST for multiple var expressions for the same
      variable. In our case, the var expressions nodes are distinct
      nodes. We need to propagate the escape state from/to the
      underlying variable in getter and setter. We already do it in
      the setter. Do it in the getter as well.
    
    - At the point of escape analysis, some AST constructs have not
      been lowered to runtime calls, for example, map literal
      construction and some builtin calls. Change the analysis to
      work on the non-lowered AST constructs instead of call
      expressions for them. For this to work, the analysis needs to
      look into Builtin_call_expression. Move its class definition
      from expressions.cc to expressions.h, and add necessary
      accessors. Also fix bugs in other runtime call handlings
      (selectsend, ifaceX2Y2, etc.).
    
    - Handle closures properly. The analysis tracks the function
      reference expression, and the escape state is propagated to
      the underlying heap expression for get_backend to do stack
      allocation for non-escaping closures.
    
    - Fix add_dereference. Before, this was doing expr->deref(),
      which undoes an indirection instead of add one. In the gc
      compiler, it adds a level of indirection, which is modeled as
      an OIND node regardless of the type of the expression. We
      can't do this for non-pointer typed expression, otherwise it
      will result in a type error. Instead, we model it with a
      special flavor of Node, "indirect". The flood phase handles
      this by incrementing its level.
    
    - Slicing of an array was not handled correctly. The gc compiler
      has an implicit (compiler inserted) OADDR node for the array,
      so the analysis is actually performed on the address of the
      array. We don't have this implicit address-of expression in
      the AST. Instead, we model this by adding an implicit child to
      the Node of the Array_index_expression representing slicing of
      an array.
    
    - Array_index_expression may represent indexing or slicing. The
      code distinguishes them by looking at whether the type of the
      expression is a slice. This does not work if the slice element
      is a slice. Instead, check whether its end() is NULL.
    
    - Temporary references was handled only in a limited case, as
      part of address-of expression. This CL handles it in general.
      The analysis uses the Temporary_statement as the point of
      tracking, and forwards Temporary_reference_expression to the
      underlying statement when needed.
    
    - Handle call return value flows, escpecially multiple return
      values. This includes porting part of CL 8202, CL 20102, and
      other fixes.
    
    - Support go:noescape pragma.
    
    - Add special handling for self assignment like
      b.buf = b.buf[m:n]. (CL 3162)
    
    - Remove ESCAPE_SCOPE, which was treated essentially the same as
      ESCAPE_HEAP, and was removed from the gc compiler. (CL 32130)
    
    - Run flood phase until fix point. (CL 30693)
    
    - Unnamed parameters do not escape. (CL 38600)
    
    - Various small bug fixes and improvements.
    
    "make check-go" passes except the one test in math/big, when the
    escape analysis is on. The escape analysis is still not run by
    default.
    
    Reviewed-on: https://go-review.googlesource.com/83876

From-SVN: r255976
parent e2a29465
66de779004bdaafefc27e4132324a47d86a0f122
83fc0e440b8c151edc5b1c67006257aad522ca04
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -51,7 +51,7 @@ public:
suffix_value() const
{ return this->suffix_value_; }
// Increase the level because a node is referenced.
// Increase the level because a node is dereferenced.
Level
increase() const
{
......@@ -61,7 +61,7 @@ public:
return Level(this->value_ + 1, this->suffix_value_ + 1);
}
// Decrease the level because a node is dereferenced.
// Decrease the level because a node is referenced.
Level
decrease() const
{
......@@ -104,9 +104,9 @@ public:
}
private:
// The sum of all indirects (-1) and references (+1) applied to a Node.
// The sum of all references (-1) and indirects (+1) applied to a Node.
int value_;
// The sum of all indirects (-1) abd references (+1) applied to a copied Node.
// The sum of all references (-1) abd indirects (+1) applied to a copied Node.
int suffix_value_;
};
......@@ -123,7 +123,10 @@ class Node
{
NODE_OBJECT,
NODE_EXPRESSION,
NODE_STATEMENT
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.
......@@ -161,11 +164,6 @@ class Node
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.
......@@ -174,17 +172,25 @@ class Node
// Multiple constructors for each classification.
Node(Named_object* no)
: classification_(NODE_OBJECT), state_(NULL), encoding_(ESCAPE_UNKNOWN)
: 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)
: 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)
: 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)
{}
// Return this node's type.
Type*
type() const;
......@@ -203,7 +209,7 @@ class Node
// Return this node's detailed format string.
std::string
details() const;
details();
std::string
op_format() const;
......@@ -214,8 +220,7 @@ class Node
// Return this node's escape encoding.
int
encoding() const
{ return this->encoding_; }
encoding();
// Set the node's escape encoding.
void
......@@ -252,6 +257,22 @@ class Node
: 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*);
......@@ -262,6 +283,9 @@ class Node
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
......@@ -294,6 +318,10 @@ class Node
// | 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 std::map<Named_object*, Node*> objects;
static std::map<Expression*, Node*> expressions;
......
......@@ -7113,114 +7113,6 @@ Expression::make_bound_method(Expression* expr, const Method* method,
// Class Builtin_call_expression. This is used for a call to a
// builtin function.
class Builtin_call_expression : public Call_expression
{
public:
Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
bool is_varargs, Location location);
protected:
// This overrides Call_expression::do_lower.
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_flatten(Gogo*, Named_object*, Statement_inserter*);
bool
do_is_constant() const;
bool
do_numeric_constant_value(Numeric_constant*) const;
bool
do_discarding_value();
Type*
do_type();
void
do_determine_type(const Type_context*);
void
do_check_types(Gogo*);
Expression*
do_copy();
Bexpression*
do_get_backend(Translate_context*);
void
do_export(Export*) const;
virtual bool
do_is_recover_call() const;
virtual void
do_set_recover_arg(Expression*);
private:
// The builtin functions.
enum Builtin_function_code
{
BUILTIN_INVALID,
// Predeclared builtin functions.
BUILTIN_APPEND,
BUILTIN_CAP,
BUILTIN_CLOSE,
BUILTIN_COMPLEX,
BUILTIN_COPY,
BUILTIN_DELETE,
BUILTIN_IMAG,
BUILTIN_LEN,
BUILTIN_MAKE,
BUILTIN_NEW,
BUILTIN_PANIC,
BUILTIN_PRINT,
BUILTIN_PRINTLN,
BUILTIN_REAL,
BUILTIN_RECOVER,
// Builtin functions from the unsafe package.
BUILTIN_ALIGNOF,
BUILTIN_OFFSETOF,
BUILTIN_SIZEOF
};
Expression*
one_arg() const;
bool
check_one_arg();
static Type*
real_imag_type(Type*);
static Type*
complex_type(Type*);
Expression*
lower_make(Statement_inserter*);
Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*);
bool
check_int_value(Expression*, bool is_length, bool* small);
// A pointer back to the general IR structure. This avoids a global
// variable, or passing it around everywhere.
Gogo* gogo_;
// The builtin function being called.
Builtin_function_code code_;
// Used to stop endless loops when the length of an array uses len
// or cap of the array itself.
mutable bool seen_;
// Whether the argument is set for calls to BUILTIN_RECOVER.
bool recover_arg_is_set_;
};
Builtin_call_expression::Builtin_call_expression(Gogo* gogo,
Expression* fn,
Expression_list* args,
......
......@@ -39,6 +39,7 @@ class Unary_expression;
class Binary_expression;
class String_concat_expression;
class Call_expression;
class Builtin_call_expression;
class Call_result_expression;
class Func_expression;
class Func_descriptor_expression;
......@@ -2246,6 +2247,15 @@ class Call_expression : public Expression
set_is_multi_value_arg()
{ this->is_multi_value_arg_ = true; }
// Whether this is a call to builtin function.
virtual bool
is_builtin()
{ return false; }
// Convert to a Builtin_call_expression, or return NULL.
inline Builtin_call_expression*
builtin_call_expression();
protected:
int
do_traverse(Traverse*);
......@@ -2351,6 +2361,133 @@ class Call_expression : public Expression
bool is_flattened_;
};
// A call expression to a builtin function.
class Builtin_call_expression : public Call_expression
{
public:
Builtin_call_expression(Gogo* gogo, Expression* fn, Expression_list* args,
bool is_varargs, Location location);
// The builtin functions.
enum Builtin_function_code
{
BUILTIN_INVALID,
// Predeclared builtin functions.
BUILTIN_APPEND,
BUILTIN_CAP,
BUILTIN_CLOSE,
BUILTIN_COMPLEX,
BUILTIN_COPY,
BUILTIN_DELETE,
BUILTIN_IMAG,
BUILTIN_LEN,
BUILTIN_MAKE,
BUILTIN_NEW,
BUILTIN_PANIC,
BUILTIN_PRINT,
BUILTIN_PRINTLN,
BUILTIN_REAL,
BUILTIN_RECOVER,
// Builtin functions from the unsafe package.
BUILTIN_ALIGNOF,
BUILTIN_OFFSETOF,
BUILTIN_SIZEOF
};
Builtin_function_code
code()
{ return this->code_; }
// This overrides Call_expression::is_builtin.
bool
is_builtin()
{ return true; }
protected:
// This overrides Call_expression::do_lower.
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
Expression*
do_flatten(Gogo*, Named_object*, Statement_inserter*);
bool
do_is_constant() const;
bool
do_numeric_constant_value(Numeric_constant*) const;
bool
do_discarding_value();
Type*
do_type();
void
do_determine_type(const Type_context*);
void
do_check_types(Gogo*);
Expression*
do_copy();
Bexpression*
do_get_backend(Translate_context*);
void
do_export(Export*) const;
virtual bool
do_is_recover_call() const;
virtual void
do_set_recover_arg(Expression*);
private:
Expression*
one_arg() const;
bool
check_one_arg();
static Type*
real_imag_type(Type*);
static Type*
complex_type(Type*);
Expression*
lower_make(Statement_inserter*);
Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*);
bool
check_int_value(Expression*, bool is_length, bool* small);
// A pointer back to the general IR structure. This avoids a global
// variable, or passing it around everywhere.
Gogo* gogo_;
// The builtin function being called.
Builtin_function_code code_;
// Used to stop endless loops when the length of an array uses len
// or cap of the array itself.
mutable bool seen_;
// Whether the argument is set for calls to BUILTIN_RECOVER.
bool recover_arg_is_set_;
};
inline Builtin_call_expression*
Call_expression::builtin_call_expression()
{
return (this->is_builtin()
? static_cast<Builtin_call_expression*>(this)
: NULL);
}
// A single result from a call which returns multiple results.
class Call_result_expression : public Expression
......
......@@ -1577,6 +1577,11 @@ class Function_declaration
set_asm_name(const std::string& asm_name)
{ this->asm_name_ = asm_name; }
// Return the pragmas for this function.
unsigned int
pragmas() const
{ return this->pragmas_; }
// Set the pragmas for this function.
void
set_pragmas(unsigned int pragmas)
......
......@@ -438,26 +438,18 @@ Runtime::name_to_code(const std::string& name)
{
Function code = Runtime::NUMBER_OF_FUNCTIONS;
// Aliases seen in function declaration code.
// TODO(cmang): Add other aliases.
if (name == "new")
code = Runtime::NEW;
else if (name == "close")
code = Runtime::CLOSE;
else if (name == "copy")
code = Runtime::SLICECOPY;
else if (name == "append")
code = Runtime::GROWSLICE;
else if (name == "delete")
code = Runtime::MAPDELETE;
else
// Look through the known names for a match.
for (size_t i = 0; i < Runtime::NUMBER_OF_FUNCTIONS; i++)
{
// Look through the known names for a match.
for (size_t i = 0; i < Runtime::NUMBER_OF_FUNCTIONS; i++)
{
if (strcmp(runtime_functions[i].name, name.c_str()) == 0)
code = static_cast<Runtime::Function>(i);
}
const char* runtime_function_name = runtime_functions[i].name;
if (strcmp(runtime_function_name, name.c_str()) == 0)
code = static_cast<Runtime::Function>(i);
// The names in the table have "runtime." prefix. We may be
// called with a name without the prefix. Try matching
// without the prefix as well.
if (strncmp(runtime_function_name, "runtime.", 8) == 0
&& strcmp(runtime_function_name + 8, name.c_str()) == 0)
code = static_cast<Runtime::Function>(i);
}
return code;
}
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