Commit b496063d by Chris Manghane Committed by Ian Lance Taylor

compiler: Escape analysis.

By Chris Manghane.

Comprises three changes to gofrontend repository:

compiler: Add escape information to export data.

compiler: Stack-allocate non-escaping variables.

This change allows variables initialized through make or new
to be allocated on the stack via a temporary variable if they
do not escape their function. It also improves the analysis to
consider situations where variables escape in the standard
library and go testsuite such as:

*nested composite literals and composite literal arguments
*method receivers always escaping
*escape via statements in closures referring to enclosing variables
*escape via calls with multiple return results

compiler: Basic escape analysis for the go frontend.

This is an implementation of the algorithm described in
"Escape Analysis in Java" by Choi et. al.

It relies on dataflow information to discover variable
references to one another. Handles assignments to closures
and association between closures variables and the variables
of the enclosing scope.

Dataflow analysis does not discover references through range
statements e.g. for _, v := range a will not recognize that
all values of v are references to a.

	* Make-lang.in (GO_OBJS): Add go/escape.o.

From-SVN: r222188
parent c10b5ea0
2015-04-17 Chris Manghane <cmang@google.com>
* Make-lang.in (GO_OBJS): Add go/escape.o.
2015-02-02 Ian Lance Taylor <iant@google.com>
PR go/64836
......
......@@ -51,6 +51,7 @@ go-warn = $(STRICT_WARN)
GO_OBJS = \
go/ast-dump.o \
go/dataflow.o \
go/escape.o \
go/export.o \
go/expressions.o \
go/go-backend.o \
......
......@@ -169,8 +169,20 @@ Dataflow_traverse_statements::statement(Block* block, size_t* pindex,
Statement *statement)
{
Dataflow_traverse_assignment dta(this->dataflow_, statement);
if (!statement->traverse_assignments(&dta))
// For thunk statements, make sure to traverse the call expression to
// find any reference to a variable being used as an argument.
if (!statement->traverse_assignments(&dta)
|| statement->thunk_statement() != NULL)
{
// Case statements in selects will be lowered into temporaries at this
// point so our dataflow analysis will miss references between a/c and ch
// in case statements of the form a,c := <-ch. Do a special dataflow
// analysis for select statements here; the analysis for the blocks will
// be handled as usual.
if (statement->select_statement() != NULL)
statement->select_statement()->analyze_dataflow(this->dataflow_);
Dataflow_traverse_expressions dte(this->dataflow_, statement);
statement->traverse(block, pindex, &dte);
}
......@@ -195,12 +207,21 @@ Dataflow::Compare_vars::operator()(const Named_object* no1,
return false;
if (loc1 > loc2)
return true;
if (Linemap::is_predeclared_location(loc1))
return false;
if (no1 == no2)
if (no1 == no2
|| (no1->is_result_variable()
&& no2->is_result_variable())
|| ((no1->is_variable()
&& no1->var_value()->is_type_switch_var())
&& (no2->is_variable()
&& no2->var_value()->is_type_switch_var())))
return false;
// We can't have two variables with the same name in the same
// location.
// location unless they are type switch variables which share the same
// fake location.
go_unreachable();
}
......
// escape.h -- Go frontend escape analysis. -*- C++ -*-
// Copyright 2015 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 "go-system.h"
#include "string-dump.h"
class Call_node;
class Connection_node;
class Connection_dump_context;
class Gogo;
class Named_object;
// A basic escape analysis implementation for the Go frontend based on the
// algorithm from "Escape Analysis for Java" by Choi et. al in OOPSLA '99.
// This is a simplified version of the flow insensitive analysis with the goal
// of reducing the overhead cost of garbage collection by allocating objects
// on the stack when they do not escape the function they were created in.
//
// A major simplification is that the analysis only implements what Choi refers
// to as "deferred edges" which are used to used model assignments that copy
// references from one variable to another e.g. a := b. It is unnecessary to
// consider assignments to the fields of an object because, in general, if a
// field of an object escapes and must be heap-allocated, there is no way to
// heap-allocate that escaping field without heap-allocating the entire object.
// That is, for some globally escaping object GVAR, if there is an assignment
// of the form GVAR = t.f such that field f of object t escapes, it is likely
// that t must be heap-allocated as well. In the analysis, this assignment
// will be simplified to GVAR = t, which is imprecise but has the same effect.
// This is a general graph node representing a named object used in a call graph
// or connection graph. In a call graph, each named object is either a Function
// or Function_declaration representing a function called during the program
// execution (which isn't necessarily every function declared). In a connection
// graph, there is a node for each node in the call graph, which forms the root
// of that connection graph. Each connection graph root contains nodes whose
// objects are either variables used in the function defintion or are nested
// closures created within the function definition. The connection graph is
// a way of modeling the connectivity between all objects created in a given
// function as well as understanding the relationship between input arguments
// in the caller and the formal parameters in the callee.
class Node
{
public:
enum Node_classification
{
NODE_CALL,
NODE_CONNECTION
};
virtual ~Node();
// Make a call node for FUNCTION.
static Node*
make_call(Named_object* function);
// Make a connection node for OBJECT.
// Note: values in this enum appear in export data, and therefore MUST NOT
// change.
enum Escapement_lattice
{
// ESCAPE_GLOBAL means that the object escapes all functions globally.
ESCAPE_GLOBAL = 0,
// ESCAPE_ARG with respect to a function means that the object escapes that
// function it is created in via the function's arguments or results.
ESCAPE_ARG = 1,
// ESCAPE_NONE means that the object does not escape the function in which
// it was created.
ESCAPE_NONE = 2
};
// A list of states usually corresponding to a list of function parameters.
typedef std::vector<Escapement_lattice> Escape_states;
static Node*
make_connection(Named_object* object, Escapement_lattice e);
// Return the node classification.
Node_classification
classification() const
{ return this->classification_; }
// Return whether this is a call node.
bool
is_call() const
{ return this->classification_ == NODE_CALL; }
// Return whether this is a connection node.
bool
is_connection() const
{ return this->classification_ == NODE_CONNECTION; }
// If this is a connection node, return the Connection_node.
// Otherwise, return NULL.
Connection_node*
connection_node()
{ return this->convert<Connection_node, NODE_CONNECTION>(); }
// Return this node's unique id.
unsigned int
id() const
{ return this->id_; }
// Return this node's generated name for GraphViz.
virtual const std::string&
name() = 0;
// Return this node's generated label for GraphViz.
virtual const std::string&
label();
// Return the object this node represents.
Named_object*
object() const
{ return this->object_; }
void
add_edge(Node* v)
{ this->edges_.insert(v); }
const std::set<Node*>&
edges() const
{ return this->edges_; }
protected:
Node(Node_classification, Named_object* object);
const std::string&
get_name() const
{ return this->name_; }
void
set_name(const std::string& name)
{ this->name_ = name; }
const std::string&
get_label() const
{ return this->label_; }
void
set_label(const std::string& label)
{ this->label_ = label; }
private:
template<typename Node_class,
Node_classification node_classification>
const Node_class*
convert() const
{
return (this->classification_ == node_classification
? static_cast<const Node_class*>(this)
: NULL);
}
template<typename Node_class,
Node_classification node_classification>
Node_class*
convert()
{
return (this->classification_ == node_classification
? static_cast<Node_class*>(this)
: NULL);
}
// The classification of this node.
Node_classification classification_;
// A unique ID for this node.
unsigned int id_;
// The name for this node e.g. "Node<ID>" used as a GraphViz identifier.
std::string name_;
// The label for this node in the GraphViz representation.
std::string label_;
// The object represented by this node.
Named_object* object_;
// A distinct set of nodes that this node has edges to.
std::set<Node*> edges_;
};
// A node representing a function that might be called during program execution.
class Call_node : public Node
{
public:
Call_node(Named_object* function);
const std::string&
name();
};
// A node representing an object in the connection graph built for each function
// in the call graph.
class Connection_node : public Node
{
public:
Connection_node(Named_object* object, Escapement_lattice e)
: Node(NODE_CONNECTION, object),
escape_state_(e)
{ }
// Return this node's generated name for GraphViz.
const std::string&
name();
// Return this node's generated label for GraphViz.
const std::string&
label();
// Return the escape state for this node.
Escapement_lattice
escape_state() const
{ return this->escape_state_; }
// Set the escape state for this node.
void
set_escape_state(Escapement_lattice e)
{ this->escape_state_ = e; }
// Return the objects inside of this connection graph.
// This is empty for all connection nodes that are not the root of a
// connection graph. Each node in the call graph is a root of a connection
// graph.
const std::set<Node*>&
objects() const
{ return this->objects_; }
void
add_object(Node* object)
{ this->objects_.insert(object); }
void
dump_connection(Connection_dump_context*);
private:
// The escapement of this node.
Escapement_lattice escape_state_;
// The set of nodes contained within this connection node. If this node is
// not a root of a connection graph, this will be empty.
std::set<Node*> objects_;
};
// This class implements fgo-dump-calls. The Call graph dump of a Go program.
class Call_dump_context : public String_dump
{
public:
Call_dump_context(std::ostream* out = NULL);
// Initialize the dump context.
void
dump(Gogo*, const char* basename);
// Get dump output stream.
std::ostream&
ostream()
{ return *this->ostream_; }
// Implementation of String_dump interface.
void
write_c_string(const char*);
void
write_string(const std::string&);
private:
// Stream on output dump file.
std::ostream* ostream_;
Gogo* gogo_;
};
// This class implements fgo-dump-conns. The connection graph dump of
// the functions called in a Go program.
class Connection_dump_context : public String_dump
{
public:
Connection_dump_context(std::ostream* out = NULL);
// Initialize the dump context.
void
dump(Gogo*, const char* basename);
// Get dump output stream.
std::ostream&
ostream()
{ return *this->ostream_; }
// Implementation of String_dump interface.
void
write_c_string(const char*);
void
write_string(const std::string&);
private:
// Stream on output dump file.
std::ostream* ostream_;
Gogo* gogo_;
};
#endif // !defined(GO_ESCAPE_H)
......@@ -436,6 +436,17 @@ Export::write_type(const Type* type)
this->type_refs_[type] = index;
}
// Export escape information.
void
Export::write_escape(const Node::Escapement_lattice& e)
{
char buf[30];
snprintf(buf, sizeof buf, "<escape %d>", e);
this->write_c_string(buf);
return;
}
// Add the builtin types to the export table.
void
......
......@@ -7,6 +7,7 @@
#ifndef GO_EXPORT_H
#define GO_EXPORT_H
#include "escape.h"
#include "string-dump.h"
struct sha1_ctx;
......@@ -161,6 +162,10 @@ class Export : public String_dump
void
write_type(const Type*);
// Write out escape information.
void
write_escape(const Node::Escapement_lattice& e);
private:
Export(const Export&);
Export& operator=(const Export&);
......
......@@ -110,6 +110,10 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
if (only_check_syntax)
return;
// Consider escape analysis information when deciding if a variable should
// live on the heap or on the stack.
::gogo->optimize_allocations(filenames);
// Export global identifiers as appropriate.
::gogo->do_exports();
......
......@@ -7,6 +7,7 @@
#ifndef GO_GOGO_H
#define GO_GOGO_H
#include "escape.h"
#include "go-linemap.h"
class Traverse;
......@@ -125,6 +126,21 @@ class Gogo
linemap()
{ return this->linemap_; }
// Get the Call Graph.
const std::set<Node*>&
call_graph() const
{ return this->call_graph_; }
// Get the roots of each connection graph.
const std::set<Node*>&
connection_roots() const
{ return this->connection_roots_; }
// Get the nodes that escape globally.
const std::set<Node*>&
global_connections() const
{ return this->global_connections_; }
// Get the package name.
const std::string&
package_name() const;
......@@ -345,6 +361,22 @@ class Gogo
add_label_reference(const std::string&, Location,
bool issue_goto_errors);
// Add a FUNCTION to the call graph.
Node*
add_call_node(Named_object* function);
// Lookup the call node for FUNCTION.
Node*
lookup_call_node(Named_object* function) const;
// Add a connection node for OBJECT.
Node*
add_connection_node(Named_object* object);
// Lookup the connection node for OBJECT.
Node*
lookup_connection_node(Named_object* object) const;
// Return a snapshot of the current binding state.
Bindings_snapshot*
bindings_snapshot(Location);
......@@ -544,6 +576,26 @@ class Gogo
void
check_return_statements();
// Build call graph.
void
build_call_graph();
// Build connection graphs.
void
build_connection_graphs();
// Analyze reachability in the connection graphs.
void
analyze_reachability();
// Record escape information in function signatures for export data.
void
mark_escaping_signatures();
// Optimize variable allocation.
void
optimize_allocations(const char** filenames);
// Do all exports.
void
do_exports();
......@@ -579,6 +631,14 @@ class Gogo
void
dump_ast(const char* basename);
// Dump Call Graph if -fgo-dump-calls is set.
void
dump_call_graph(const char* basename);
// Dump Connection Graphs if -fgo-dump-connections is set.
void
dump_connection_graphs(const char* basename);
// Convert named types to the backend representation.
void
convert_named_types();
......@@ -684,6 +744,10 @@ class Gogo
// where they were defined.
typedef Unordered_map(std::string, Location) File_block_names;
// Type used to map named objects that refer to objects to the
// node that represent them in the escape analysis graphs.
typedef Unordered_map(Named_object*, Node*) Named_escape_nodes;
// Type used to queue writing a type specific function.
struct Specific_type_function
{
......@@ -716,6 +780,20 @@ class Gogo
// The global binding contour. This includes the builtin functions
// and the package we are compiling.
Bindings* globals_;
// The call graph for a program execution which represents the functions
// encountered and the caller-callee relationship between the functions.
std::set<Node*> call_graph_;
// The nodes that form the roots of the connection graphs for each called
// function and represent the connectivity relationship between all objects
// in the function.
std::set<Node*> connection_roots_;
// All connection nodes that have an escape state of ESCAPE_GLOBAL are a part
// of a special connection graph of only global variables.
std::set<Node*> global_connections_;
// Mapping from named objects to nodes in the call graph.
Named_escape_nodes named_call_nodes_;
// Mapping from named objects to nodes in a connection graph.
Named_escape_nodes named_connection_nodes_;
// The list of names we have seen in the file block.
File_block_names file_block_names_;
// Mapping from import file names to packages.
......@@ -886,7 +964,7 @@ class Block
class Function
{
public:
Function(Function_type* type, Function*, Block*, Location);
Function(Function_type* type, Named_object*, Block*, Location);
// Return the function's type.
Function_type*
......@@ -894,14 +972,14 @@ class Function
{ return this->type_; }
// Return the enclosing function if there is one.
Function*
enclosing()
Named_object*
enclosing() const
{ return this->enclosing_; }
// Set the enclosing function. This is used when building thunks
// for functions which call recover.
void
set_enclosing(Function* enclosing)
set_enclosing(Named_object* enclosing)
{
go_assert(this->enclosing_ == NULL);
this->enclosing_ = enclosing;
......@@ -1152,8 +1230,11 @@ class Function
// Import a function.
static void
import_func(Import*, std::string* pname, Typed_identifier** receiver,
Node::Escapement_lattice* rcvr_escape,
Typed_identifier_list** pparameters,
Typed_identifier_list** presults, bool* is_varargs);
Node::Escape_states** pparam_escapes,
Typed_identifier_list** presults, bool* is_varargs,
bool* has_escape_info);
private:
// Type for mapping from label names to Label objects.
......@@ -1169,7 +1250,7 @@ class Function
Function_type* type_;
// The enclosing function. This is NULL when there isn't one, which
// is the normal case.
Function* enclosing_;
Named_object* enclosing_;
// The result variables, if any.
Results* results_;
// If there is a closure, this is the list of variables which appear
......@@ -1414,7 +1495,11 @@ class Variable
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_ && !this->is_global_; }
{
return this->is_address_taken_
&& this->escapes_
&& !this->is_global_;
}
// Note that something takes the address of this variable.
void
......@@ -1432,6 +1517,16 @@ class Variable
set_non_escaping_address_taken()
{ this->is_non_escaping_address_taken_ = true; }
// Return whether this variable escapes the function it is declared in.
bool
escapes()
{ return this->escapes_; }
// Note that this variable does not escape the function it is declared in.
void
set_does_not_escape()
{ this->escapes_ = false; }
// Get the source location of the variable's declaration.
Location
location() const
......@@ -1525,6 +1620,11 @@ class Variable
this->type_from_chan_element_ = false;
}
// TRUE if this variable was created for a type switch clause.
bool
is_type_switch_var() const
{ return this->is_type_switch_var_; }
// Note that this variable was created for a type switch clause.
void
set_is_type_switch_var()
......@@ -1609,7 +1709,7 @@ class Variable
bool is_used_ : 1;
// Whether something takes the address of this variable. For a
// local variable this implies that the variable has to be on the
// heap.
// heap if it escapes from its function.
bool is_address_taken_ : 1;
// Whether something takes the address of this variable such that
// the address does not escape the function.
......@@ -1635,6 +1735,9 @@ class Variable
// True if this variable should be put in a unique section. This is
// used for field tracking.
bool in_unique_section_ : 1;
// Whether this variable escapes the function it is created in. This is
// true until shown otherwise.
bool escapes_ : 1;
};
// A variable which is really the name for a function return value, or
......@@ -1647,7 +1750,7 @@ class Result_variable
Location location)
: type_(type), function_(function), index_(index), location_(location),
backend_(NULL), is_address_taken_(false),
is_non_escaping_address_taken_(false)
is_non_escaping_address_taken_(false), escapes_(true)
{ }
// Get the type of the result variable.
......@@ -1690,11 +1793,24 @@ class Result_variable
void
set_non_escaping_address_taken()
{ this->is_non_escaping_address_taken_ = true; }
// Return whether this variable escapes the function it is declared in.
bool
escapes()
{ return this->escapes_; }
// Note that this variable does not escape the function it is declared in.
void
set_does_not_escape()
{ this->escapes_ = false; }
// Whether this variable should live in the heap.
bool
is_in_heap() const
{ return this->is_address_taken_; }
{
return this->is_address_taken_
&& this->escapes_;
}
// Set the function. This is used when cloning functions which call
// recover.
......@@ -1722,6 +1838,9 @@ class Result_variable
// Whether something takes the address of this variable such that
// the address does not escape the function.
bool is_non_escaping_address_taken_;
// Whether this variable escapes the function it is created in. This is
// true until shown otherwise.
bool escapes_;
};
// The value we keep for a named constant. This lets us hold a type
......
......@@ -502,16 +502,28 @@ Import::import_func(Package* package)
{
std::string name;
Typed_identifier* receiver;
Node::Escapement_lattice rcvr_escape;
Typed_identifier_list* parameters;
Node::Escape_states* param_escapes;
Typed_identifier_list* results;
bool is_varargs;
Function::import_func(this, &name, &receiver, &parameters, &results,
&is_varargs);
bool has_escape_info;
Function::import_func(this, &name, &receiver, &rcvr_escape, &parameters,
&param_escapes, &results, &is_varargs,
&has_escape_info);
Function_type *fntype = Type::make_function_type(receiver, parameters,
results, this->location_);
if (is_varargs)
fntype->set_is_varargs();
if (has_escape_info)
{
if (fntype->is_method())
fntype->set_receiver_escape_state(rcvr_escape);
fntype->set_parameter_escape_states(param_escapes);
fntype->set_has_escape_info();
}
Location loc = this->location_;
Named_object* no;
if (fntype->is_method())
......@@ -762,6 +774,19 @@ Import::read_type()
return type;
}
// Read escape info in the import stream.
Node::Escapement_lattice
Import::read_escape_info()
{
Stream* stream = this->stream_;
this->require_c_string("<escape ");
int escape_value = stream->get_char() - '0';
this->require_c_string(">");
return Node::Escapement_lattice(escape_value);
}
// Register the builtin types.
void
......
......@@ -7,6 +7,7 @@
#ifndef GO_IMPORT_H
#define GO_IMPORT_H
#include "escape.h"
#include "export.h"
#include "go-linemap.h"
......@@ -197,6 +198,10 @@ class Import
Type*
read_type();
// Read escape information.
Node::Escapement_lattice
read_escape_info();
private:
static Stream*
try_package_in_directory(const std::string&, Location);
......
......@@ -14,6 +14,7 @@
#include "backend.h"
#include "statements.h"
#include "ast-dump.h"
#include "dataflow.h"
// Class Statement.
......@@ -520,45 +521,7 @@ Statement::make_temporary(Type* type, Expression* init,
return new Temporary_statement(type, init, location);
}
// An assignment statement.
class Assignment_statement : public Statement
{
public:
Assignment_statement(Expression* lhs, Expression* rhs,
Location location)
: Statement(STATEMENT_ASSIGNMENT, location),
lhs_(lhs), rhs_(rhs)
{ }
protected:
int
do_traverse(Traverse* traverse);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
Statement*
do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
Bstatement*
do_get_backend(Translate_context*);
void
do_dump_statement(Ast_dump_context*) const;
private:
// Left hand side--the lvalue.
Expression* lhs_;
// Right hand side--the rvalue.
Expression* rhs_;
};
// Class Assignment_statement.
// Traversal.
......@@ -3150,41 +3113,7 @@ Statement::make_unnamed_label_statement(Unnamed_label* label)
return new Unnamed_label_statement(label);
}
// An if statement.
class If_statement : public Statement
{
public:
If_statement(Expression* cond, Block* then_block, Block* else_block,
Location location)
: Statement(STATEMENT_IF, location),
cond_(cond), then_block_(then_block), else_block_(else_block)
{ }
protected:
int
do_traverse(Traverse*);
void
do_determine_types();
void
do_check_types(Gogo*);
bool
do_may_fall_through() const;
Bstatement*
do_get_backend(Translate_context*);
void
do_dump_statement(Ast_dump_context*) const;
private:
Expression* cond_;
Block* then_block_;
Block* else_block_;
};
// Class If_statement.
// Traversal.
......@@ -4676,7 +4605,6 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function,
// through here.
this->is_lowered_ = true;
this->val_ = NULL;
this->var_ = NULL;
}
// Lower a default clause in a select statement.
......@@ -4840,6 +4768,22 @@ Select_clauses::Select_clause::check_types()
error_at(this->location(), "invalid receive on send-only channel");
}
// Analyze the dataflow across each case statement.
void
Select_clauses::Select_clause::analyze_dataflow(Dataflow* dataflow)
{
if (this->is_default_)
return;
// For a CommClause, the dataflow analysis should record a definition of
// VAR and CLOSEDVAR
if (this->var_ != NULL && !this->var_->is_sink())
dataflow->add_def(this->var_, this->channel_, NULL, false);
if (this->closedvar_ != NULL && !this->closedvar_->is_sink())
dataflow->add_def(this->closedvar_, this->channel_, NULL, false);
}
// Whether this clause may fall through to the statement which follows
// the overall select statement.
......@@ -4958,6 +4902,17 @@ Select_clauses::check_types()
p->check_types();
}
// Analyze the dataflow across each case statement.
void
Select_clauses::analyze_dataflow(Dataflow* dataflow)
{
for (Clauses::iterator p = this->clauses_.begin();
p != this->clauses_.end();
++p)
p->analyze_dataflow(dataflow);
}
// Return whether these select clauses fall through to the statement
// following the overall select statement.
......
......@@ -15,12 +15,14 @@ class Statement_inserter;
class Block;
class Function;
class Unnamed_label;
class Assignment_statement;
class Temporary_statement;
class Variable_declaration_statement;
class Expression_statement;
class Return_statement;
class Thunk_statement;
class Label_statement;
class If_statement;
class For_statement;
class For_range_statement;
class Switch_statement;
......@@ -45,6 +47,7 @@ class Bexpression;
class Bstatement;
class Bvariable;
class Ast_dump_context;
class Dataflow;
// This class is used to traverse assignments made by a statement
// which makes assignments.
......@@ -331,6 +334,22 @@ class Statement
is_block_statement() const
{ return this->classification_ == STATEMENT_BLOCK; }
// If this is an assignment statement, return it. Otherwise return
// NULL.
Assignment_statement*
assignment_statement()
{
return this->convert<Assignment_statement, STATEMENT_ASSIGNMENT>();
}
// If this is an temporary statement, return it. Otherwise return
// NULL.
Temporary_statement*
temporary_statement()
{
return this->convert<Temporary_statement, STATEMENT_TEMPORARY>();
}
// If this is a variable declaration statement, return it.
// Otherwise return NULL.
Variable_declaration_statement*
......@@ -363,6 +382,11 @@ class Statement
label_statement()
{ return this->convert<Label_statement, STATEMENT_LABEL>(); }
// If this is an if statement, return it. Otherwise return NULL.
If_statement*
if_statement()
{ return this->convert<If_statement, STATEMENT_IF>(); }
// If this is a for statement, return it. Otherwise return NULL.
For_statement*
for_statement()
......@@ -385,6 +409,11 @@ class Statement
type_switch_statement()
{ return this->convert<Type_switch_statement, STATEMENT_TYPE_SWITCH>(); }
// If this is a send statement, return it. Otherwise return NULL.
Send_statement*
send_statement()
{ return this->convert<Send_statement, STATEMENT_SEND>(); }
// If this is a select statement, return it. Otherwise return NULL.
Select_statement*
select_statement()
......@@ -507,6 +536,54 @@ class Statement
Location location_;
};
// An assignment statement.
class Assignment_statement : public Statement
{
public:
Assignment_statement(Expression* lhs, Expression* rhs,
Location location)
: Statement(STATEMENT_ASSIGNMENT, location),
lhs_(lhs), rhs_(rhs)
{ }
Expression*
lhs() const
{ return this->lhs_; }
Expression*
rhs() const
{ return this->rhs_; }
protected:
int
do_traverse(Traverse* traverse);
bool
do_traverse_assignments(Traverse_assignments*);
void
do_determine_types();
void
do_check_types(Gogo*);
Statement*
do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
Bstatement*
do_get_backend(Translate_context*);
void
do_dump_statement(Ast_dump_context*) const;
private:
// Left hand side--the lvalue.
Expression* lhs_;
// Right hand side--the rvalue.
Expression* rhs_;
};
// A statement which creates and initializes a temporary variable.
class Temporary_statement : public Statement
......@@ -697,6 +774,14 @@ class Send_statement : public Statement
channel_(channel), val_(val)
{ }
Expression*
channel()
{ return this->channel_; }
Expression*
val()
{ return this->val_; }
protected:
int
do_traverse(Traverse* traverse);
......@@ -775,6 +860,10 @@ class Select_clauses
void
check_types();
// Analyze the dataflow across each case statement.
void
analyze_dataflow(Dataflow*);
// Whether the select clauses may fall through to the statement
// which follows the overall select statement.
bool
......@@ -831,6 +920,10 @@ class Select_clauses
void
check_types();
// Analyze the dataflow across each case statement.
void
analyze_dataflow(Dataflow*);
// Return true if this is the default clause.
bool
is_default() const
......@@ -937,6 +1030,10 @@ class Select_statement : public Statement
Unnamed_label*
break_label();
void
analyze_dataflow(Dataflow* dataflow)
{ this->clauses_->analyze_dataflow(dataflow); }
protected:
int
do_traverse(Traverse* traverse)
......@@ -1108,6 +1205,46 @@ class Label_statement : public Statement
Label* label_;
};
// An if statement.
class If_statement : public Statement
{
public:
If_statement(Expression* cond, Block* then_block, Block* else_block,
Location location)
: Statement(STATEMENT_IF, location),
cond_(cond), then_block_(then_block), else_block_(else_block)
{ }
Expression*
condition() const
{ return this->cond_; }
protected:
int
do_traverse(Traverse*);
void
do_determine_types();
void
do_check_types(Gogo*);
bool
do_may_fall_through() const;
Bstatement*
do_get_backend(Translate_context*);
void
do_dump_statement(Ast_dump_context*) const;
private:
Expression* cond_;
Block* then_block_;
Block* else_block_;
};
// A for statement.
class For_statement : public Statement
......
......@@ -8,6 +8,7 @@
#define GO_TYPES_H
#include "go-linemap.h"
#include "escape.h"
class Gogo;
class Package;
......@@ -1778,7 +1779,7 @@ class Function_type : public Type
: Type(TYPE_FUNCTION),
receiver_(receiver), parameters_(parameters), results_(results),
location_(location), is_varargs_(false), is_builtin_(false),
fnbtype_(NULL)
has_escape_info_(false), fnbtype_(NULL), parameter_escape_states_(NULL)
{ }
// Get the receiver.
......@@ -1786,6 +1787,16 @@ class Function_type : public Type
receiver() const
{ return this->receiver_; }
// Get the escape state of the receiver.
const Node::Escapement_lattice&
receiver_escape_state() const
{ return this->receiver_escape_state_; }
// Set the escape state of the receiver.
void
set_receiver_escape_state(const Node::Escapement_lattice& e)
{ this->receiver_escape_state_ = e; }
// Get the return names and types.
const Typed_identifier_list*
results() const
......@@ -1796,6 +1807,16 @@ class Function_type : public Type
parameters() const
{ return this->parameters_; }
// Get the escape states associated with each parameter.
const Node::Escape_states*
parameter_escape_states() const
{ return this->parameter_escape_states_; }
// Set the escape states of the parameters.
void
set_parameter_escape_states(Node::Escape_states* states)
{ this->parameter_escape_states_ = states; }
// Whether this is a varargs function.
bool
is_varargs() const
......@@ -1806,6 +1827,11 @@ class Function_type : public Type
is_builtin() const
{ return this->is_builtin_; }
// Whether this contains escape information.
bool
has_escape_info() const
{ return this->has_escape_info_; }
// The location where this type was defined.
Location
location() const
......@@ -1836,6 +1862,11 @@ class Function_type : public Type
set_is_builtin()
{ this->is_builtin_ = true; }
// Record that this has escape information.
void
set_has_escape_info()
{ this->has_escape_info_ = true; }
// Import a function type.
static Function_type*
do_import(Import*);
......@@ -1947,9 +1978,16 @@ class Function_type : public Type
// Whether this is a special builtin function which can not simply
// be called. This is used for len, cap, etc.
bool is_builtin_;
// Whether escape information for the receiver and parameters has been
// recorded.
bool has_escape_info_;
// The backend representation of this type for backend function
// declarations and definitions.
Btype* fnbtype_;
// The escape state of the receiver.
Node::Escapement_lattice receiver_escape_state_;
// The escape states of each parameter.
Node::Escape_states* parameter_escape_states_;
};
// The type of a function's backend representation.
......
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