Commit e49aacaf by Chris Manghane Committed by Ian Lance Taylor

escape: Remove previously existing analysis.

    
	* Make-lang.in (GO_OBJS): Remove go/dataflow.o, go/escape.o.

    Reviewed-on: https://go-review.googlesource.com/18261

From-SVN: r235649
parent 52d11a4b
2016-04-29 Chris Manghane <cmang@google.com>
* Make-lang.in (GO_OBJS): Remove go/dataflow.o, go/escape.o.
2016-04-18 Michael Matz <matz@suse.de> 2016-04-18 Michael Matz <matz@suse.de>
* go-gcc.cc (Gcc_backend::implicit_variable): Use SET_DECL_ALIGN. * go-gcc.cc (Gcc_backend::implicit_variable): Use SET_DECL_ALIGN.
......
...@@ -50,8 +50,6 @@ go-warn = $(STRICT_WARN) ...@@ -50,8 +50,6 @@ go-warn = $(STRICT_WARN)
GO_OBJS = \ GO_OBJS = \
go/ast-dump.o \ go/ast-dump.o \
go/dataflow.o \
go/escape.o \
go/export.o \ go/export.o \
go/expressions.o \ go/expressions.o \
go/go-backend.o \ go/go-backend.o \
......
50b2b468a85045c66d60112dc094c31ec4897123 46b108136c0d102f181f0cc7c398e3db8c4d08a3
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
// dataflow.cc -- Go frontend dataflow.
// Copyright 2009 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.
#include "go-system.h"
#include "gogo.h"
#include "expressions.h"
#include "statements.h"
#include "dataflow.h"
// This class is used to traverse the tree to look for uses of
// variables.
class Dataflow_traverse_expressions : public Traverse
{
public:
Dataflow_traverse_expressions(Dataflow* dataflow, Statement* statement)
: Traverse(traverse_blocks | traverse_expressions),
dataflow_(dataflow), statement_(statement)
{ }
protected:
// Only look at top-level expressions: do not descend into blocks.
// They will be examined via Dataflow_traverse_statements.
int
block(Block*)
{ return TRAVERSE_SKIP_COMPONENTS; }
int
expression(Expression**);
private:
// The dataflow information.
Dataflow* dataflow_;
// The Statement in which we are looking.
Statement* statement_;
};
// Given an expression, return the Named_object that it refers to, if
// it is a local variable.
static Named_object*
get_var(Expression* expr)
{
Var_expression* ve = expr->var_expression();
if (ve == NULL)
return NULL;
Named_object* no = ve->named_object();
go_assert(no->is_variable() || no->is_result_variable());
if (no->is_variable() && no->var_value()->is_global())
return NULL;
return no;
}
// Look for a reference to a variable in an expression.
int
Dataflow_traverse_expressions::expression(Expression** expr)
{
Named_object* no = get_var(*expr);
if (no != NULL)
this->dataflow_->add_ref(no, this->statement_);
return TRAVERSE_CONTINUE;
}
// This class is used to handle an assignment statement.
class Dataflow_traverse_assignment : public Traverse_assignments
{
public:
Dataflow_traverse_assignment(Dataflow* dataflow, Statement* statement)
: dataflow_(dataflow), statement_(statement)
{ }
protected:
void
initialize_variable(Named_object*);
void
assignment(Expression** lhs, Expression** rhs);
void
value(Expression**, bool, bool);
private:
// The dataflow information.
Dataflow* dataflow_;
// The Statement in which we are looking.
Statement* statement_;
};
// Handle a variable initialization.
void
Dataflow_traverse_assignment::initialize_variable(Named_object* var)
{
Expression* init = var->var_value()->init();
this->dataflow_->add_def(var, init, this->statement_, true);
if (init != NULL)
{
Expression* e = init;
this->value(&e, true, true);
go_assert(e == init);
}
}
// Handle an assignment in a statement.
void
Dataflow_traverse_assignment::assignment(Expression** plhs, Expression** prhs)
{
Named_object* no = get_var(*plhs);
if (no != NULL)
{
Expression* rhs = prhs == NULL ? NULL : *prhs;
this->dataflow_->add_def(no, rhs, this->statement_, false);
}
else
{
// If this is not a variable it may be some computed lvalue, and
// we want to look for references to variables in that lvalue.
this->value(plhs, false, false);
}
if (prhs != NULL)
this->value(prhs, true, false);
}
// Handle a value in a statement.
void
Dataflow_traverse_assignment::value(Expression** pexpr, bool, bool)
{
Named_object* no = get_var(*pexpr);
if (no != NULL)
this->dataflow_->add_ref(no, this->statement_);
else
{
Dataflow_traverse_expressions dte(this->dataflow_, this->statement_);
Expression::traverse(pexpr, &dte);
}
}
// This class is used to traverse the tree to look for statements.
class Dataflow_traverse_statements : public Traverse
{
public:
Dataflow_traverse_statements(Dataflow* dataflow)
: Traverse(traverse_statements),
dataflow_(dataflow)
{ }
protected:
int
statement(Block*, size_t* pindex, Statement*);
private:
// The dataflow information.
Dataflow* dataflow_;
};
// For each Statement, we look for expressions.
int
Dataflow_traverse_statements::statement(Block* block, size_t* pindex,
Statement *statement)
{
Dataflow_traverse_assignment dta(this->dataflow_, statement);
// 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);
}
return TRAVERSE_CONTINUE;
}
// Compare variables.
bool
Dataflow::Compare_vars::operator()(const Named_object* no1,
const Named_object* no2) const
{
if (no1->name() < no2->name())
return true;
if (no1->name() > no2->name())
return false;
// We can have two different variables with the same name.
Location loc1 = no1->location();
Location loc2 = no2->location();
if (loc1 < loc2)
return false;
if (loc1 > loc2)
return true;
if (Linemap::is_predeclared_location(loc1))
return false;
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 unless they are type switch variables which share the same
// fake location.
go_unreachable();
}
// Class Dataflow.
Dataflow::Dataflow()
: defs_(), refs_()
{
}
// Build the dataflow information.
void
Dataflow::initialize(Gogo* gogo)
{
Dataflow_traverse_statements dts(this);
gogo->traverse(&dts);
}
// Add a definition of a variable.
void
Dataflow::add_def(Named_object* var, Expression* val, Statement* statement,
bool is_init)
{
Defs* defnull = NULL;
std::pair<Defmap::iterator, bool> ins =
this->defs_.insert(std::make_pair(var, defnull));
if (ins.second)
ins.first->second = new Defs;
Def def;
def.statement = statement;
def.val = val;
def.is_init = is_init;
ins.first->second->push_back(def);
}
// Add a reference to a variable.
void
Dataflow::add_ref(Named_object* var, Statement* statement)
{
Refs* refnull = NULL;
std::pair<Refmap::iterator, bool> ins =
this->refs_.insert(std::make_pair(var, refnull));
if (ins.second)
ins.first->second = new Refs;
Ref ref;
ref.statement = statement;
ins.first->second->push_back(ref);
}
// Return the definitions of a variable.
const Dataflow::Defs*
Dataflow::find_defs(Named_object* var) const
{
Defmap::const_iterator p = this->defs_.find(var);
if (p == this->defs_.end())
return NULL;
else
return p->second;
}
// Return the references of a variable.
const Dataflow::Refs*
Dataflow::find_refs(Named_object* var) const
{
Refmap::const_iterator p = this->refs_.find(var);
if (p == this->refs_.end())
return NULL;
else
return p->second;
}
// dataflow.h -- Go frontend dataflow. -*- C++ -*-
// Copyright 2009 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_DATAFLOW_H
#define GO_DATAFLOW_H
class Expression;
class Named_object;
class Statement;
// Dataflow information about the Go program.
class Dataflow
{
public:
// A variable definition.
struct Def
{
// The statement where the variable is defined.
Statement* statement;
// The value to which the variable is set. This may be NULL.
Expression* val;
// Whether this is an initialization of the variable.
bool is_init;
};
// A variable reference.
struct Ref
{
// The statement where the variable is referenced.
Statement* statement;
};
// A list of defs.
typedef std::vector<Def> Defs;
// A list of refs.
typedef std::vector<Ref> Refs;
Dataflow();
// Initialize the dataflow information.
void
initialize(Gogo*);
// Add a definition of a variable. STATEMENT assigns a value to
// VAR. VAL is the value if it is known, NULL otherwise.
void
add_def(Named_object* var, Expression* val, Statement* statement,
bool is_init);
// Add a reference to a variable. VAR is the variable, and
// STATEMENT is the statement which refers to it.
void
add_ref(Named_object* var, Statement* statement);
// Return the definitions of VAR--the places where it is set.
const Defs*
find_defs(Named_object* var) const;
// Return the references to VAR--the places where it is used.
const Refs*
find_refs(Named_object* var) const;
private:
// Order variables in the map.
struct Compare_vars
{
bool
operator()(const Named_object*, const Named_object*) const;
};
// Map from variables to a list of defs of the variable. We use a
// map rather than a hash table because the order in which we
// process variables may affect the resulting code.
typedef std::map<Named_object*, Defs*, Compare_vars> Defmap;
// Map from variables to a list of refs to the vairable.
typedef std::map<Named_object*, Refs*, Compare_vars> Refmap;
// Variable defs.
Defmap defs_;
// Variable refs;
Refmap refs_;
};
#endif // !defined(GO_DATAFLOW_H)
// escape.cc -- Go frontend escape analysis.
// 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.
#include "go-system.h"
#include <fstream>
#include "go-c.h"
#include "go-dump.h"
#include "go-optimize.h"
#include "types.h"
#include "statements.h"
#include "expressions.h"
#include "dataflow.h"
#include "gogo.h"
#include "escape.h"
// Class Node.
Node::Node(Node_classification classification, Named_object* object)
: classification_(classification), object_(object)
{
// Give every node a unique ID for representation purposes.
static int count;
this->id_ = count++;
}
Node::~Node()
{
}
// Make a call node for FUNCTION.
Node*
Node::make_call(Named_object* function)
{
return new Call_node(function);
}
// Make a connection node for OBJECT.
Node*
Node::make_connection(Named_object* object, Escapement_lattice e)
{
return new Connection_node(object, e);
}
// Return this node's label, which will be the name seen in the graphical
// representation.
const std::string&
Node::label()
{
if (this->label_.empty())
{
this->label_ = "[label=\"";
this->label_ += this->object_->name();
this->label_ += "\"]";
}
return this->label_;
}
// Class Call_node.
Call_node::Call_node(Named_object* function)
: Node(NODE_CALL, function)
{ go_assert(function->is_function() || function->is_function_declaration()); }
const std::string&
Call_node::name()
{
if (this->get_name().empty())
{
char buf[30];
snprintf(buf, sizeof buf, "CallNode%d", this->id());
this->set_name(std::string(buf));
}
return this->get_name();
}
// Class Connection_node.
const std::string&
Connection_node::name()
{
if (this->get_name().empty())
{
char buf[30];
snprintf(buf, sizeof buf, "ConnectionNode%d", this->id());
this->set_name(std::string(buf));
}
return this->get_name();
}
const std::string&
Connection_node::label()
{
if (this->get_label().empty())
{
std::string label = "[label=\"";
label += this->object()->name();
label += "\",color=";
switch (this->escape_state_)
{
case ESCAPE_GLOBAL:
label += "red";
break;
case ESCAPE_ARG:
label += "blue";
break;
case ESCAPE_NONE:
label += "black";
break;
}
label += "]";
this->set_label(label);
}
return this->get_label();
}
// Dump a connection node and its edges to a dump file.
void
Connection_node::dump_connection(Connection_dump_context* cdc)
{
cdc->write_string(this->name() + this->label());
cdc->write_c_string("\n");
for (std::set<Node*>::const_iterator p = this->edges().begin();
p != this->edges().end();
++p)
{
cdc->write_string(this->name());
cdc->write_c_string("->");
if ((*p)->object()->is_function())
{
char buf[100];
snprintf(buf, sizeof buf, "dummy%d[lhead=cluster%d]",
(*p)->id(), (*p)->id());
cdc->write_c_string(buf);
}
else
cdc->write_string((*p)->name());
cdc->write_c_string("\n");
}
}
// The -fgo-dump-calls flag to activate call graph dumps in GraphViz DOT format.
Go_dump call_graph_dump_flag("calls");
// Class Call_dump_context.
Call_dump_context::Call_dump_context(std::ostream* out)
: ostream_(out), gogo_(NULL)
{ }
// Dump files will be named %basename%.calls.dot
const char* kCallDumpFileExtension = ".calls.dot";
// Dump the call graph in DOT format.
void
Call_dump_context::dump(Gogo* gogo, const char* basename)
{
std::ofstream* out = new std::ofstream();
std::string dumpname(basename);
dumpname += kCallDumpFileExtension;
out->open(dumpname.c_str());
if (out->fail())
{
error("cannot open %s:%m, -fgo-dump-calls ignored", dumpname.c_str());
return;
}
this->gogo_ = gogo;
this->ostream_ = out;
this->write_string("digraph CallGraph {\n");
std::set<Node*> call_graph = gogo->call_graph();
// Generate GraphViz nodes for each node.
for (std::set<Node*>::const_iterator p = call_graph.begin();
p != call_graph.end();
++p)
{
this->write_string((*p)->name() + (*p)->label());
this->write_c_string("\n");
// Generate a graphical representation of the caller-callee relationship.
std::set<Node*> callees = (*p)->edges();
for (std::set<Node*>::const_iterator ce = callees.begin();
ce != callees.end();
++ce)
{
this->write_string((*p)->name() + "->" + (*ce)->name());
this->write_c_string("\n");
}
}
this->write_string("}");
out->close();
}
// Dump the Call Graph of the program to the dump file.
void Gogo::dump_call_graph(const char* basename)
{
if (::call_graph_dump_flag.is_enabled())
{
Call_dump_context cdc;
cdc.dump(this, basename);
}
}
// Implementation of String_dump interface.
void
Call_dump_context::write_c_string(const char* s)
{
this->ostream() << s;
}
void
Call_dump_context::write_string(const std::string& s)
{
this->ostream() << s;
}
// The -fgo-dump-conns flag to activate connection graph dumps in
// GraphViz DOT format.
Go_dump connection_graph_dump_flag("conns");
// Class Connection_dump_context.
Connection_dump_context::Connection_dump_context(std::ostream* out)
: ostream_(out), gogo_(NULL)
{ }
// Dump files will be named %basename%.conns.dot
const char* kConnectionDumpFileExtension = ".conns.dot";
// Dump the connection graph in DOT format.
void
Connection_dump_context::dump(Gogo* gogo, const char* basename)
{
std::ofstream* out = new std::ofstream();
std::string dumpname(basename);
dumpname += kConnectionDumpFileExtension;
out->open(dumpname.c_str());
if (out->fail())
{
error("cannot open %s:%m, -fgo-dump-conns ignored", dumpname.c_str());
return;
}
this->gogo_ = gogo;
this->ostream_ = out;
this->write_string("digraph ConnectionGraph {\n");
this->write_string("compound=true\n");
// Dump global objects.
std::set<Node*> globals = this->gogo_->global_connections();
this->write_c_string("subgraph globals{\n");
this->write_c_string("label=\"NonLocalGraph\"\n");
this->write_c_string("color=red\n");
for (std::set<Node*>::const_iterator p1 = globals.begin();
p1 != globals.end();
++p1)
(*p1)->connection_node()->dump_connection(this);
this->write_c_string("}\n");
std::set<Node*> roots = this->gogo_->connection_roots();
for (std::set<Node*>::const_reverse_iterator p1 = roots.rbegin();
p1 != roots.rend();
++p1)
{
std::set<Node*> objects = (*p1)->connection_node()->objects();
char buf[150];
snprintf(buf, sizeof buf, "subgraph cluster%d", (*p1)->id());
this->write_c_string(buf);
this->write_string("{\n");
snprintf(buf, sizeof buf, "dummy%d[shape=point,style=invis]\n",
(*p1)->id());
this->write_c_string(buf);
this->write_string("label = \"" + (*p1)->object()->name() + "\"\n");
for (std::set<Node*>::const_iterator p2 = objects.begin();
p2 != objects.end();
++p2)
(*p2)->connection_node()->dump_connection(this);
this->write_string("}\n");
}
this->write_string("}");
out->close();
}
void
Gogo::dump_connection_graphs(const char* basename)
{
if (::connection_graph_dump_flag.is_enabled())
{
Connection_dump_context cdc;
cdc.dump(this, basename);
}
}
// Implementation of String_dump interface.
void
Connection_dump_context::write_c_string(const char* s)
{
this->ostream() << s;
}
void
Connection_dump_context::write_string(const std::string& s)
{
this->ostream() << s;
}
// A traversal class used to build a call graph for this program.
class Build_call_graph : public Traverse
{
public:
Build_call_graph(Gogo* gogo)
: Traverse(traverse_functions
| traverse_expressions),
gogo_(gogo), current_function_(NULL)
{ }
int
function(Named_object*);
int
expression(Expression**);
private:
// The IR.
Gogo* gogo_;
// The current function being traversed, for reference when traversing the
// function body.
Named_object* current_function_;
};
// Add each function to the call graph and then traverse each function's
// body to find callee functions.
int
Build_call_graph::function(Named_object* fn)
{
this->gogo_->add_call_node(fn);
go_assert(this->current_function_ == NULL);
this->current_function_ = fn;
fn->func_value()->traverse(this);
this->current_function_ = NULL;
return TRAVERSE_CONTINUE;
}
// Find function calls and add them as callees to CURRENT_FUNCTION.
int
Build_call_graph::expression(Expression** pexpr)
{
if (this->current_function_ == NULL)
return TRAVERSE_CONTINUE;
Expression* expr = *pexpr;
Named_object* fn;
if (expr->call_expression() != NULL)
{
Func_expression* func = expr->call_expression()->fn()->func_expression();
if (func == NULL)
{
// This is probably a variable holding a function value or a closure.
return TRAVERSE_CONTINUE;
}
fn = func->named_object();
}
else if (expr->func_expression() != NULL)
fn = expr->func_expression()->named_object();
else
return TRAVERSE_CONTINUE;
Node* caller = this->gogo_->lookup_call_node(this->current_function_);
go_assert(caller != NULL);
// Create the callee here if it hasn't been seen yet. This could also be a
// function defined in another package.
Node* callee = this->gogo_->add_call_node(fn);
caller->add_edge(callee);
return TRAVERSE_CONTINUE;
}
// Build the call graph.
void
Gogo::build_call_graph()
{
Build_call_graph build_calls(this);
this->traverse(&build_calls);
}
// A traversal class used to build a connection graph for each node in the
// call graph.
class Build_connection_graphs : public Traverse
{
public:
Build_connection_graphs(Gogo* gogo)
: Traverse(traverse_variables
| traverse_statements),
gogo_(gogo), dataflow_(new Dataflow), current_function_(NULL)
{
// Collect dataflow information for this program.
this->dataflow_->initialize(this->gogo_);
}
void
set_current_function(Named_object* function)
{ this->current_function_ = function; }
int
variable(Named_object*);
int
statement(Block*, size_t*, Statement*);
private:
// Handle a call EXPR referencing OBJECT.
void
handle_call(Named_object* object, Expression* expr);
// Get the initialization values of a composite literal EXPR.
Expression_list*
get_composite_arguments(Expression* expr);
// Handle defining OBJECT as a composite literal EXPR.
void
handle_composite_literal(Named_object* object, Expression* expr);
// Handle analysis of the left and right operands of a binary expression
// with respect to OBJECT.
void
handle_binary(Named_object* object, Expression* expr);
// Resolve the outermost named object of EXPR if there is one.
Named_object*
resolve_var_reference(Expression* expr);
// The IR.
Gogo* gogo_;
// The Dataflow information for this program.
Dataflow* dataflow_;
// The current function whose connection graph is being built.
Named_object* current_function_;
};
// Given an expression, return the outermost Named_object that it refers to.
// This is used to model the simplification between assignments in our analysis.
Named_object*
Build_connection_graphs::resolve_var_reference(Expression* expr)
{
bool done = false;
Expression* orig = expr;
while (!done)
{
// The goal of this loop is to find the variable being referenced, p,
// when the expression is:
switch (expr->classification())
{
case Expression::EXPRESSION_UNARY:
// &p or *p
expr = expr->unary_expression()->operand();
break;
case Expression::EXPRESSION_ARRAY_INDEX:
// p[i][j]
expr = expr->array_index_expression()->array();
break;
case Expression::EXPRESSION_FIELD_REFERENCE:
// p.i.j
orig = expr;
expr = expr->field_reference_expression()->expr();
break;
case Expression::EXPRESSION_RECEIVE:
// <- p
expr = expr->receive_expression()->channel();
break;
case Expression::EXPRESSION_BOUND_METHOD:
// p.c
expr = expr->bound_method_expression()->first_argument();
break;
case Expression::EXPRESSION_CALL:
// p.c()
expr = expr->call_expression()->fn();
break;
case Expression::EXPRESSION_TEMPORARY_REFERENCE:
// This is used after lowering, so try to retrieve the original
// expression that might have been lowered into a temporary statement.
expr = expr->temporary_reference_expression()->statement()->init();
if (expr == NULL)
return NULL;
break;
case Expression::EXPRESSION_SET_AND_USE_TEMPORARY:
expr = expr->set_and_use_temporary_expression()->expression();
break;
case Expression::EXPRESSION_COMPOUND:
// p && q
expr = expr->compound_expression()->init();
break;
case Expression::EXPRESSION_CONDITIONAL:
// if p {
expr = expr->conditional_expression()->condition();
break;
case Expression::EXPRESSION_CONVERSION:
// T(p)
expr = expr->conversion_expression()->expr();
break;
case Expression::EXPRESSION_TYPE_GUARD:
// p.(T)
expr = expr->type_guard_expression()->expr();
break;
case Expression::EXPRESSION_UNSAFE_CONVERSION:
{
Expression* e = expr->unsafe_conversion_expression()->expr();
if (e->call_result_expression() != NULL
&& e->call_result_expression()->index() == 0)
{
// a, ok := p.(T) gets lowered into a call to one of the interface
// to type conversion functions instead of a type guard expression.
// We only want to make a connection between a and p, the bool
// result should not escape because p escapes.
e = e->call_result_expression()->call();
Named_object* fn =
e->call_expression()->fn()->func_expression()->named_object();
std::string fn_name = fn->name();
if (fn->package() == NULL
&& fn->is_function_declaration()
&& !fn->func_declaration_value()->asm_name().empty())
{
if (fn_name == "ifaceI2E2"
|| fn_name == "ifaceI2I2")
e = e->call_expression()->args()->at(0);
else if (fn_name == "ifaceE2I2"
|| fn_name == "ifaceI2I2"
|| fn_name == "ifaceE2T2P"
|| fn_name == "ifaceI2T2P"
|| fn_name == "ifaceE2T2"
|| fn_name == "ifaceI2T2")
e = e->call_expression()->args()->at(1);
}
}
expr = e;
}
break;
default:
done = true;
break;
}
}
Var_expression* ve = expr->var_expression();
if (ve != NULL)
{
Named_object* no = ve->named_object();
go_assert(no->is_variable() || no->is_result_variable());
if (no->is_variable()
&& no->var_value()->is_closure()
&& this->current_function_->func_value()->needs_closure())
{
// CURRENT_FUNCTION is a closure and NO is being set to a
// variable in the enclosing function.
Named_object* closure = this->current_function_;
// If NO is a closure variable, the expression is a field
// reference to the enclosed variable.
Field_reference_expression* fre =
orig->deref()->field_reference_expression();
if (fre == NULL)
return NULL;
unsigned int closure_index = fre->field_index();
no = closure->func_value()->enclosing_var(closure_index - 1);
}
return no;
}
return NULL;
}
// For a call that references OBJECT, associate the OBJECT argument with the
// appropriate call parameter.
void
Build_connection_graphs::handle_call(Named_object* object, Expression* e)
{
// Only call expression statements are interesting
// e.g. 'func(var)' for which we can show var does not escape.
Call_expression* ce = e->call_expression();
if (ce == NULL)
return;
else if (ce->args() == NULL)
{
if (ce->fn()->interface_field_reference_expression() != NULL)
{
// This is a call to an interface method with no arguments. OBJECT
// must be the receiver and we assume it escapes.
Connection_node* rcvr_node =
this->gogo_->add_connection_node(object)->connection_node();
rcvr_node->set_escape_state(Node::ESCAPE_ARG);
}
return;
}
// If the function call that references OBJECT is unknown, we must be
// conservative and assume every argument escapes. A function call is unknown
// if it is a call to a function stored in a variable or a call to an
// interface method.
if (ce->fn()->func_expression() == NULL)
{
for (Expression_list::const_iterator arg = ce->args()->begin();
arg != ce->args()->end();
++arg)
{
Named_object* arg_no = this->resolve_var_reference(*arg);
if (arg_no != NULL)
{
Connection_node* arg_node =
this->gogo_->add_connection_node(arg_no)->connection_node();
arg_node->set_escape_state(Node::ESCAPE_ARG);
}
else if ((*arg)->call_expression() != NULL)
this->handle_call(object, *arg);
}
return;
}
Named_object* callee = ce->fn()->func_expression()->named_object();
Function_type* fntype;
if (callee->is_function())
fntype = callee->func_value()->type();
else
fntype = callee->func_declaration_value()->type();
Node* callee_node = this->gogo_->lookup_connection_node(callee);
if (callee_node == NULL && callee->is_function())
{
// Might be a nested closure that hasn't been analyzed yet.
Named_object* currfn = this->current_function_;
callee_node = this->gogo_->add_connection_node(callee);
this->current_function_ = callee;
callee->func_value()->traverse(this);
this->current_function_ = currfn;
}
// First find which arguments OBJECT is to CALLEE. Given a function call,
// OBJECT could be an argument multiple times e.g. CALLEE(OBJECT, OBJECT).
// TODO(cmang): This should be done by the Dataflow analysis so we don't have
// to do it each time we see a function call. FIXME.
Expression_list* args = ce->args()->copy();
if (fntype->is_varargs()
&& args->back()->slice_literal() != NULL)
{
// Is the function is varargs, the last argument is lowered into a slice
// containing all original arguments. We want to traverse the original
// arguments here.
Slice_construction_expression* sce = args->back()->slice_literal();
for (Expression_list::const_iterator p = sce->vals()->begin();
p != sce->vals()->end();
++p)
{
if (*p != NULL)
args->push_back(*p);
}
}
// ARG_POSITION is just a counter used to keep track of the index in the list
// of arguments to this call. In a method call, the receiver will always be
// the first argument. When looking at the function type, it will not be the
// first element in the parameter list; instead, the receiver will be
// non-NULL. For convenience, mark the position of the receiver argument
// as negative.
int arg_position = fntype->is_method() ? -1 : 0;
std::list<int> positions;
for (Expression_list::const_iterator p = args->begin();
p != args->end();
++p, ++arg_position)
{
Expression* arg = *p;
// An argument might be a chain of method calls, some of which are
// converted from value to pointer types. Just remove the unary
// conversion if it exists.
if (arg->unary_expression() != NULL)
arg = arg->unary_expression()->operand();
// The reference to OBJECT might be in a nested call argument.
if (arg->call_expression() != NULL)
this->handle_call(object, arg);
std::vector<Named_object*> objects;
if (arg->is_composite_literal()
|| arg->heap_expression() != NULL)
{
// For a call that has a composite literal as an argument, traverse
// the initializers of the composite literal for extra objects to
// associate with a parameter in this function.
Expression_list* comp_args = this->get_composite_arguments(arg);
if (comp_args == NULL)
continue;
for (size_t i = 0; i < comp_args->size(); ++i)
{
Expression* comp_arg = comp_args->at(i);
if (comp_arg == NULL)
continue;
else if (comp_arg->is_composite_literal()
|| comp_arg->heap_expression() != NULL)
{
// Of course, there are situations where a composite literal
// initialization value is also a composite literal.
Expression_list* nested_args =
this->get_composite_arguments(comp_arg);
if (nested_args != NULL)
comp_args->append(nested_args);
}
Named_object* no = this->resolve_var_reference(comp_arg);
if (no != NULL)
objects.push_back(no);
}
}
else
{
Named_object* arg_no = this->resolve_var_reference(arg);
if (arg_no != NULL)
objects.push_back(arg_no);
}
// There are no variables to consider for this parameter.
if (objects.empty())
continue;
for (std::vector<Named_object*>::const_iterator p1 = objects.begin();
p1 != objects.end();
++p1)
{
// If CALLEE is defined in another package and we have imported escape
// information about its parameters, update the escape state of this
// argument appropriately. If there is no escape information for this
// function, we have to assume all arguments escape.
if (callee->package() != NULL
|| fntype->is_builtin())
{
Node::Escapement_lattice param_escape = Node::ESCAPE_NONE;
if (fntype->has_escape_info())
{
if (arg_position == -1)
{
// Use the escape info from the receiver.
param_escape = fntype->receiver_escape_state();
}
else if (fntype->parameters() != NULL)
{
const Node::Escape_states* states =
fntype->parameter_escape_states();
int param_size = fntype->parameters()->size();
if (arg_position >= param_size)
{
go_assert(fntype->is_varargs());
param_escape = states->back();
}
else
param_escape =
fntype->parameter_escape_states()->at(arg_position);
}
}
else
param_escape = Node::ESCAPE_ARG;
Connection_node* arg_node =
this->gogo_->add_connection_node(*p1)->connection_node();
if (arg_node->escape_state() > param_escape)
arg_node->set_escape_state(param_escape);
}
if (*p1 == object)
positions.push_back(arg_position);
}
}
// If OBJECT was not found in CALLEE's arguments, OBJECT is likely a
// subexpression of one of the arguments e.g. CALLEE(a[OBJECT]). This call
// does not give any useful information about whether OBJECT escapes.
if (positions.empty())
return;
// The idea here is to associate the OBJECT in the caller context with the
// parameter in the callee context. This also needs to consider varargs.
// This only works with functions with arguments.
if (!callee->is_function())
return;
// Use the bindings in the callee to lookup the associated parameter.
const Bindings* callee_bindings = callee->func_value()->block()->bindings();
// Next find the corresponding named parameters in the function signature.
const Typed_identifier_list* params = fntype->parameters();
for (std::list<int>::const_iterator pos = positions.begin();
params != NULL && pos != positions.end();
++pos)
{
std::string param_name;
if (*pos >= 0 && params->size() <= static_cast<size_t>(*pos))
{
// There were more arguments than there are parameters. This must be
// varargs and the argument corresponds to the last parameter.
go_assert(fntype->is_varargs());
param_name = params->back().name();
}
else if (*pos < 0)
{
// We adjust the recorded position of method arguments by one to
// account for the receiver, so *pos == -1 implies this is the
// receiver and this must be a method call.
go_assert(fntype->is_method() && fntype->receiver() != NULL);
param_name = fntype->receiver()->name();
}
else
param_name = params->at(*pos).name();
if (Gogo::is_sink_name(param_name) || param_name.empty())
continue;
// Get the object for the associated parameter in this binding.
Named_object* param_no = callee_bindings->lookup_local(param_name);
go_assert(param_no != NULL);
// Add an edge from ARG_NODE in the caller context to the PARAM_NODE in
// the callee context.
if (object->is_variable() && object->var_value()->is_closure())
{
int position = *pos;
if (fntype->is_method())
++position;
// Calling a function within a closure with a closure argument.
// Resolve the real variable using the closure argument.
object = this->resolve_var_reference(ce->args()->at(position));
}
Node* arg_node = this->gogo_->add_connection_node(object);
Node* param_node = this->gogo_->add_connection_node(param_no);
param_node->add_edge(arg_node);
}
// This is a method call with one argument: the receiver.
if (params == NULL)
{
go_assert(positions.size() == 1);
std::string rcvr_name = fntype->receiver()->name();
if (Gogo::is_sink_name(rcvr_name) || rcvr_name.empty())
return;
Named_object* rcvr_no = callee_bindings->lookup_local(rcvr_name);
Node* arg_node = this->gogo_->add_connection_node(object);
Node* rcvr_node = this->gogo_->add_connection_node(rcvr_no);
rcvr_node->add_edge(arg_node);
}
}
// Given a composite literal expression, return the initialization values.
// This is used to handle situations where call and composite literal
// expressions have nested composite literals as arguments/initializers.
Expression_list*
Build_connection_graphs::get_composite_arguments(Expression* expr)
{
// A heap expression is just any expression that takes the address of a
// composite literal.
if (expr->heap_expression() != NULL)
expr = expr->heap_expression()->expr();
switch (expr->classification())
{
case Expression::EXPRESSION_STRUCT_CONSTRUCTION:
return expr->struct_literal()->vals();
case Expression::EXPRESSION_FIXED_ARRAY_CONSTRUCTION:
return expr->array_literal()->vals();
case Expression::EXPRESSION_SLICE_CONSTRUCTION:
return expr->slice_literal()->vals();
case Expression::EXPRESSION_MAP_CONSTRUCTION:
return expr->map_literal()->vals();
default:
return NULL;
}
}
// Given an OBJECT defined as a composite literal EXPR, create edges between
// OBJECT and all variables referenced in EXPR.
void
Build_connection_graphs::handle_composite_literal(Named_object* object,
Expression* expr)
{
Expression_list* args = this->get_composite_arguments(expr);
if (args == NULL)
return;
std::vector<Named_object*> composite_args;
for (Expression_list::const_iterator p = args->begin();
p != args->end();
++p)
{
if (*p == NULL)
continue;
else if ((*p)->call_expression() != NULL)
this->handle_call(object, *p);
else if ((*p)->func_expression() != NULL)
composite_args.push_back((*p)->func_expression()->named_object());
else if ((*p)->is_composite_literal()
|| (*p)->heap_expression() != NULL)
this->handle_composite_literal(object, *p);
Named_object* no = this->resolve_var_reference(*p);
if (no != NULL)
composite_args.push_back(no);
}
Node* object_node = this->gogo_->add_connection_node(object);
for (std::vector<Named_object*>::const_iterator p = composite_args.begin();
p != composite_args.end();
++p)
{
Node* arg_node = this->gogo_->add_connection_node(*p);
object_node->add_edge(arg_node);
}
}
// Given an OBJECT reference in a binary expression E, analyze the left and
// right operands for possible edges.
void
Build_connection_graphs::handle_binary(Named_object* object, Expression* e)
{
Binary_expression* be = e->binary_expression();
go_assert(be != NULL);
Expression* left = be->left();
Expression* right = be->right();
if (left->call_result_expression() != NULL)
left = left->call_result_expression()->call();
if (left->call_expression() != NULL)
this->handle_call(object, left);
else if (left->binary_expression() != NULL)
this->handle_binary(object, left);
if (right->call_result_expression() != NULL)
right = right->call_result_expression()->call();
if (right->call_expression() != NULL)
this->handle_call(object, right);
else if (right->binary_expression() != NULL)
this->handle_binary(object, right);
}
// Create connection nodes for each variable in a called function.
int
Build_connection_graphs::variable(Named_object* var)
{
Node* var_node = this->gogo_->add_connection_node(var);
Node* root = this->gogo_->lookup_connection_node(this->current_function_);
go_assert(root != NULL);
// Add VAR to the set of objects in CURRENT_FUNCTION's connection graph.
root->connection_node()->add_object(var_node);
// A function's results always escape.
if (var->is_result_variable())
var_node->connection_node()->set_escape_state(Node::ESCAPE_ARG);
// Create edges from a variable to its definitions.
const Dataflow::Defs* defs = this->dataflow_->find_defs(var);
if (defs != NULL)
{
for (Dataflow::Defs::const_iterator p = defs->begin();
p != defs->end();
++p)
{
Expression* def = p->val;
if (def == NULL)
continue;
if (def->conversion_expression() != NULL)
def = def->conversion_expression()->expr();
if (def->func_expression() != NULL)
{
// VAR is being defined as a function object.
Named_object* fn = def->func_expression()->named_object();
Node* fn_node = this->gogo_->add_connection_node(fn);
var_node->add_edge(fn_node);
}
else if(def->is_composite_literal()
|| def->heap_expression() != NULL)
this->handle_composite_literal(var, def);
Named_object* ref = this->resolve_var_reference(def);
if (ref == NULL)
continue;
Node* ref_node = this->gogo_->add_connection_node(ref);
var_node->add_edge(ref_node);
}
}
// Create edges from a reference to a variable.
const Dataflow::Refs* refs = this->dataflow_->find_refs(var);
if (refs != NULL)
{
for (Dataflow::Refs::const_iterator p = refs->begin();
p != refs->end();
++p)
{
switch (p->statement->classification())
{
case Statement::STATEMENT_ASSIGNMENT:
{
Assignment_statement* assn =
p->statement->assignment_statement();
Named_object* lhs_no = this->resolve_var_reference(assn->lhs());
Named_object* rhs_no = this->resolve_var_reference(assn->rhs());
Expression* rhs = assn->rhs();
if (rhs->is_composite_literal()
|| rhs->heap_expression() != NULL)
this->handle_composite_literal(var, rhs);
if (rhs->call_result_expression() != NULL)
{
// V's initialization will be a call result if
// V, V1 := call(VAR).
// There are no useful edges to make from V, but we want
// to make sure we handle the call that references VAR.
rhs = rhs->call_result_expression()->call();
}
if (rhs->call_expression() != NULL)
this->handle_call(var, rhs);
// If there is no standalone variable on the rhs, this could be a
// binary expression, which isn't interesting for analysis or a
// composite literal or call expression, which we handled above.
// If the underlying variable on the rhs isn't VAR then it is
// likely an indexing expression where VAR is the index.
if(lhs_no == NULL
|| rhs_no == NULL
|| rhs_no != var)
break;
Node* def_node = this->gogo_->add_connection_node(lhs_no);
def_node->add_edge(var_node);
}
break;
case Statement::STATEMENT_SEND:
{
Send_statement* send = p->statement->send_statement();
Named_object* chan_no = this->resolve_var_reference(send->channel());
Named_object* val_no = resolve_var_reference(send->val());
if (chan_no == NULL || val_no == NULL)
break;
Node* chan_node = this->gogo_->add_connection_node(chan_no);
Node* val_node = this->gogo_->add_connection_node(val_no);
chan_node->add_edge(val_node);
}
break;
case Statement::STATEMENT_EXPRESSION:
{
Expression* call = p->statement->expression_statement()->expr();
if (call->call_result_expression() != NULL)
call = call->call_result_expression()->call();
this->handle_call(var, call);
}
break;
case Statement::STATEMENT_GO:
case Statement::STATEMENT_DEFER:
// Any variable referenced via a go or defer statement escapes to
// a different goroutine.
if (var_node->connection_node()->escape_state() > Node::ESCAPE_ARG)
var_node->connection_node()->set_escape_state(Node::ESCAPE_ARG);
this->handle_call(var, p->statement->thunk_statement()->call());
break;
case Statement::STATEMENT_IF:
{
// If this is a reference via an if statement, it is interesting
// if there is a function call in the condition. References in
// the then and else blocks would be discovered in an earlier
// case.
If_statement* if_stmt = p->statement->if_statement();
Expression* cond = if_stmt->condition();
if (cond->call_expression() != NULL)
this->handle_call(var, cond);
else if (cond->binary_expression() != NULL)
this->handle_binary(var, cond);
}
break;
case Statement::STATEMENT_VARIABLE_DECLARATION:
{
// VAR could be referenced as the initialization for another
// variable, V e.g. V := call(VAR) or V := &T{field: VAR}.
Variable_declaration_statement* decl =
p->statement->variable_declaration_statement();
Named_object* decl_no = decl->var();
Variable* v = decl_no->var_value();
Expression* init = v->init();
if (init == NULL)
break;
if (init->is_composite_literal()
|| init->heap_expression() != NULL)
{
// Create edges between DECL_NO and each named object in the
// composite literal.
this->handle_composite_literal(decl_no, init);
}
if (init->call_result_expression() != NULL)
init = init->call_result_expression()->call();
if (init->call_expression() != NULL)
this->handle_call(var, init);
else if (init->binary_expression() != NULL)
this->handle_binary(var, init);
}
break;
case Statement::STATEMENT_TEMPORARY:
{
// A call to a function with mutliple results that references VAR
// will be lowered into a temporary at this point. Make sure the
// call that references VAR is handled.
Expression* init = p->statement->temporary_statement()->init();
if (init == NULL)
break;
else if (init->call_result_expression() != NULL)
{
Expression* call = init->call_result_expression()->call();
this->handle_call(var, call);
}
}
default:
break;
}
}
}
return TRAVERSE_CONTINUE;
}
// Traverse statements to find interesting references that might have not
// been recorded in the dataflow analysis. For example, many statements
// in closures are not properly recorded during dataflow analysis. This should
// handle all of the cases handled above in statements that reference a
// variable. FIXME.
int
Build_connection_graphs::statement(Block*, size_t*, Statement* s)
{
switch(s->classification())
{
case Statement::STATEMENT_ASSIGNMENT:
{
Assignment_statement* assn = s->assignment_statement();
Named_object* lhs_no = this->resolve_var_reference(assn->lhs());
if (lhs_no == NULL)
break;
Expression* rhs = assn->rhs();
if (rhs->temporary_reference_expression() != NULL)
rhs = rhs->temporary_reference_expression()->statement()->init();
if (rhs == NULL)
break;
if (rhs->call_result_expression() != NULL)
rhs = rhs->call_result_expression()->call();
if (rhs->call_expression() != NULL)
{
// It's not clear what variables we are trying to find references to
// so just use the arguments to this call.
Expression_list* args = rhs->call_expression()->args();
if (args == NULL)
break;
for (Expression_list::const_iterator p = args->begin();
p != args->end();
++p)
{
Named_object* no = this->resolve_var_reference(*p);
if (no != NULL) {
Node* lhs_node = this->gogo_->add_connection_node(lhs_no);
Node* rhs_node = this->gogo_->add_connection_node(no);
lhs_node->add_edge(rhs_node);
}
}
this->handle_call(lhs_no, rhs);
}
else if (rhs->func_expression() != NULL)
{
Node* lhs_node = this->gogo_->add_connection_node(lhs_no);
Named_object* fn = rhs->func_expression()->named_object();
Node* fn_node = this->gogo_->add_connection_node(fn);
lhs_node->add_edge(fn_node);
}
else
{
Named_object* rhs_no = this->resolve_var_reference(rhs);
if (rhs_no != NULL)
{
Node* lhs_node = this->gogo_->add_connection_node(lhs_no);
Node* rhs_node = this->gogo_->add_connection_node(rhs_no);
lhs_node->add_edge(rhs_node);
}
}
}
break;
case Statement::STATEMENT_SEND:
{
Send_statement* send = s->send_statement();
Named_object* chan_no = this->resolve_var_reference(send->channel());
Named_object* val_no = this->resolve_var_reference(send->val());
if (chan_no == NULL || val_no == NULL)
break;
Node* chan_node = this->gogo_->add_connection_node(chan_no);
Node* val_node = this->gogo_->add_connection_node(val_no);
chan_node->add_edge(val_node);
}
break;
case Statement::STATEMENT_EXPRESSION:
{
Expression* expr = s->expression_statement()->expr();
if (expr->call_result_expression() != NULL)
expr = expr->call_result_expression()->call();
if (expr->call_expression() != NULL)
{
// It's not clear what variables we are trying to find references to
// so just use the arguments to this call.
Expression_list* args = expr->call_expression()->args();
if (args == NULL)
break;
for (Expression_list::const_iterator p = args->begin();
p != args->end();
++p)
{
Named_object* no = this->resolve_var_reference(*p);
if (no != NULL)
this->handle_call(no, expr);
}
}
}
break;
case Statement::STATEMENT_GO:
case Statement::STATEMENT_DEFER:
{
// Any variable referenced via a go or defer statement escapes to
// a different goroutine.
Expression* call = s->thunk_statement()->call();
if (call->call_expression() != NULL)
{
// It's not clear what variables we are trying to find references to
// so just use the arguments to this call.
Expression_list* args = call->call_expression()->args();
if (args == NULL)
break;
for (Expression_list::const_iterator p = args->begin();
p != args->end();
++p)
{
Named_object* no = this->resolve_var_reference(*p);
if (no != NULL)
this->handle_call(no, call);
}
}
}
break;
case Statement::STATEMENT_VARIABLE_DECLARATION:
{
Variable_declaration_statement* decl =
s->variable_declaration_statement();
Named_object* decl_no = decl->var();
Variable* v = decl_no->var_value();
Expression* init = v->init();
if (init == NULL)
break;
if (init->is_composite_literal()
|| init->heap_expression() != NULL)
{
// Create edges between DECL_NO and each named object in the
// composite literal.
this->handle_composite_literal(decl_no, init);
}
if (init->call_result_expression() != NULL)
init = init->call_result_expression()->call();
if (init->call_expression() != NULL)
{
// It's not clear what variables we are trying to find references to
// so just use the arguments to this call.
Expression_list* args = init->call_expression()->args();
if (args == NULL)
break;
for (Expression_list::const_iterator p = args->begin();
p != args->end();
++p)
{
Named_object* no = this->resolve_var_reference(*p);
if (no != NULL)
this->handle_call(no, init);
}
}
}
break;
default:
break;
}
return TRAVERSE_CONTINUE;
}
// Build the connection graphs for each function present in the call graph.
void
Gogo::build_connection_graphs()
{
Build_connection_graphs build_conns(this);
for (std::set<Node*>::const_iterator p = this->call_graph_.begin();
p != this->call_graph_.end();
++p)
{
Named_object* func = (*p)->object();
go_assert(func->is_function() || func->is_function_declaration());
Function_type* fntype;
if (func->is_function())
fntype = func->func_value()->type();
else
fntype = func->func_declaration_value()->type();
if (fntype->is_builtin())
continue;
this->add_connection_node(func);
build_conns.set_current_function(func);
if (func->is_function())
{
// A pointer receiver of a method always escapes from the method.
if (fntype->is_method() &&
fntype->receiver()->type()->points_to() != NULL)
{
const Bindings* callee_bindings =
func->func_value()->block()->bindings();
std::string rcvr_name = fntype->receiver()->name();
if (Gogo::is_sink_name(rcvr_name) || rcvr_name.empty())
return;
Named_object* rcvr_no = callee_bindings->lookup_local(rcvr_name);
Node* rcvr_node = this->add_connection_node(rcvr_no);
rcvr_node->connection_node()->set_escape_state(Node::ESCAPE_ARG);
}
func->func_value()->traverse(&build_conns);
}
}
}
void
Gogo::analyze_reachability()
{
std::list<Node*> worklist;
// Run reachability analysis on all globally escaping objects.
for (std::set<Node*>::const_iterator p = this->global_connections_.begin();
p != this->global_connections_.end();
++p)
worklist.push_back(*p);
while (!worklist.empty())
{
Node* m = worklist.front();
worklist.pop_front();
std::set<Node*> reachable = m->edges();
if (m->object()->is_function()
&& m->object()->func_value()->needs_closure())
{
// If a closure escapes everything it closes over also escapes.
Function* closure = m->object()->func_value();
for (size_t i = 0; i < closure->closure_field_count(); i++)
{
Named_object* enclosed = closure->enclosing_var(i);
Node* enclosed_node = this->lookup_connection_node(enclosed);
go_assert(enclosed_node != NULL);
reachable.insert(enclosed_node);
}
}
for (std::set<Node*>::iterator n = reachable.begin();
n != reachable.end();
++n)
{
// If an object can be reached from a node with ESCAPE_GLOBAL,
// it also must ESCAPE_GLOBAL.
if ((*n)->connection_node()->escape_state() != Node::ESCAPE_GLOBAL)
{
(*n)->connection_node()->set_escape_state(Node::ESCAPE_GLOBAL);
worklist.push_back(*n);
}
}
}
// Run reachability analysis on all objects that escape via arguments.
for (Named_escape_nodes::const_iterator p =
this->named_connection_nodes_.begin();
p != this->named_connection_nodes_.end();
++p)
{
if (p->second->connection_node()->escape_state() < Node::ESCAPE_NONE)
worklist.push_back(p->second);
}
while (!worklist.empty())
{
Node* m = worklist.front();
worklist.pop_front();
std::set<Node*> reachable = m->edges();
if (m->object()->is_function()
&& m->object()->func_value()->needs_closure())
{
// If a closure escapes everything it closes over also escapes.
Function* closure = m->object()->func_value();
for (size_t i = 0; i < closure->closure_field_count(); i++)
{
Named_object* enclosed = closure->enclosing_var(i);
Node* enclosed_node = this->lookup_connection_node(enclosed);
go_assert(enclosed_node != NULL);
reachable.insert(enclosed_node);
}
}
for (std::set<Node*>::iterator n = reachable.begin();
n != reachable.end();
++n)
{
// If an object can be reached from a node with ESCAPE_ARG,
// it is ESCAPE_ARG or ESCAPE_GLOBAL.
Node::Escapement_lattice e = m->connection_node()->escape_state();
if ((*n)->connection_node()->escape_state() > e)
{
(*n)->connection_node()->set_escape_state(e);
worklist.push_back(*n);
}
}
}
}
// Iterate over all functions analyzed in the analysis, recording escape
// information for each receiver and parameter.
void
Gogo::mark_escaping_signatures()
{
for (std::set<Node*>::const_iterator p = this->call_graph_.begin();
p != this->call_graph_.end();
++p)
{
Named_object* fn = (*p)->object();
if (!fn->is_function())
continue;
Function* func = fn->func_value();
Function_type* fntype = func->type();
const Bindings* bindings = func->block()->bindings();
// If this is a method, set the escape state of the receiver.
if (fntype->is_method())
{
std::string rcvr_name = fntype->receiver()->name();
if (rcvr_name.empty() || Gogo::is_sink_name(rcvr_name))
fntype->set_receiver_escape_state(Node::ESCAPE_NONE);
else
{
Named_object* rcvr_no = bindings->lookup_local(rcvr_name);
go_assert(rcvr_no != NULL);
Node* rcvr_node = this->lookup_connection_node(rcvr_no);
if (rcvr_node != NULL)
{
Node::Escapement_lattice e =
rcvr_node->connection_node()->escape_state();
fntype->set_receiver_escape_state(e);
}
else
fntype->set_receiver_escape_state(Node::ESCAPE_NONE);
}
fntype->set_has_escape_info();
}
const Typed_identifier_list* params = fntype->parameters();
if (params == NULL)
continue;
fntype->set_has_escape_info();
Node::Escape_states* param_escape_states = new Node::Escape_states;
for (Typed_identifier_list::const_iterator p1 = params->begin();
p1 != params->end();
++p1)
{
std::string param_name = p1->name();
if (param_name.empty() || Gogo::is_sink_name(param_name))
param_escape_states->push_back(Node::ESCAPE_NONE);
else
{
Named_object* param_no = bindings->lookup_local(param_name);
go_assert(param_no != NULL);
Node* param_node = this->lookup_connection_node(param_no);
if (param_node == NULL)
{
param_escape_states->push_back(Node::ESCAPE_NONE);
continue;
}
Node::Escapement_lattice e =
param_node->connection_node()->escape_state();
param_escape_states->push_back(e);
}
}
go_assert(params->size() == param_escape_states->size());
fntype->set_parameter_escape_states(param_escape_states);
}
}
class Optimize_allocations : public Traverse
{
public:
Optimize_allocations(Gogo* gogo)
: Traverse(traverse_variables),
gogo_(gogo)
{ }
int
variable(Named_object*);
private:
// The IR.
Gogo* gogo_;
};
// The -fgo-optimize-alloc flag activates this escape analysis.
Go_optimize optimize_allocation_flag("allocs");
// Propagate escape information for each variable.
int
Optimize_allocations::variable(Named_object* var)
{
Node* var_node = this->gogo_->lookup_connection_node(var);
if (var_node == NULL
|| var_node->connection_node()->escape_state() != Node::ESCAPE_NONE)
return TRAVERSE_CONTINUE;
if (var->is_variable())
{
var->var_value()->set_does_not_escape();
if (var->var_value()->init() != NULL
&& var->var_value()->init()->allocation_expression() != NULL)
{
Allocation_expression* alloc =
var->var_value()->init()->allocation_expression();
alloc->set_allocate_on_stack();
}
}
return TRAVERSE_CONTINUE;
}
// Perform escape analysis on this program and optimize allocations using
// the derived information if -fgo-optimize-allocs.
void
Gogo::optimize_allocations(const char** filenames)
{
if (!::optimize_allocation_flag.is_enabled() || saw_errors())
return;
// Build call graph for this program.
this->build_call_graph();
// Dump the call graph for this program if -fgo-dump-calls is enabled.
this->dump_call_graph(filenames[0]);
// Build the connection graphs for this program.
this->build_connection_graphs();
// Dump the connection graphs if -fgo-dump-connections is enabled.
this->dump_connection_graphs(filenames[0]);
// Given the connection graphs for this program, perform a reachability
// analysis to determine what objects escape.
this->analyze_reachability();
// Propagate escape information to variables and variable initializations.
Optimize_allocations optimize_allocs(this);
this->traverse(&optimize_allocs);
// Store escape information for a function's receivers and parameters in the
// function's signature for use when exporting package information.
this->mark_escaping_signatures();
}
// 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,17 +436,6 @@ Export::write_type(const Type* type) ...@@ -436,17 +436,6 @@ Export::write_type(const Type* type)
this->type_refs_[type] = index; 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. // Add the builtin types to the export table.
void void
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#ifndef GO_EXPORT_H #ifndef GO_EXPORT_H
#define GO_EXPORT_H #define GO_EXPORT_H
#include "escape.h"
#include "string-dump.h" #include "string-dump.h"
struct sha1_ctx; struct sha1_ctx;
...@@ -162,10 +161,6 @@ class Export : public String_dump ...@@ -162,10 +161,6 @@ class Export : public String_dump
void void
write_type(const Type*); write_type(const Type*);
// Write out escape information.
void
write_escape(const Node::Escapement_lattice& e);
private: private:
Export(const Export&); Export(const Export&);
Export& operator=(const Export&); Export& operator=(const Export&);
......
...@@ -110,10 +110,6 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, ...@@ -110,10 +110,6 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
if (only_check_syntax) if (only_check_syntax)
return; 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. // Export global identifiers as appropriate.
::gogo->do_exports(); ::gogo->do_exports();
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#include "runtime.h" #include "runtime.h"
#include "import.h" #include "import.h"
#include "export.h" #include "export.h"
#include "escape.h"
#include "backend.h" #include "backend.h"
#include "gogo.h" #include "gogo.h"
...@@ -156,19 +155,11 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -156,19 +155,11 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc);
new_type->set_is_varargs(); new_type->set_is_varargs();
new_type->set_is_builtin(); new_type->set_is_builtin();
Node::Escape_states* new_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
new_type->set_parameter_escape_states(new_escapes);
new_type->set_has_escape_info();
this->globals_->add_function_declaration("new", NULL, new_type, loc); this->globals_->add_function_declaration("new", NULL, new_type, loc);
Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc);
make_type->set_is_varargs(); make_type->set_is_varargs();
make_type->set_is_builtin(); make_type->set_is_builtin();
Node::Escape_states* make_escapes =
new Node::Escape_states(2, Node::ESCAPE_NONE);
make_type->set_parameter_escape_states(make_escapes);
make_type->set_has_escape_info();
this->globals_->add_function_declaration("make", NULL, make_type, loc); this->globals_->add_function_declaration("make", NULL, make_type, loc);
Typed_identifier_list* len_result = new Typed_identifier_list(); Typed_identifier_list* len_result = new Typed_identifier_list();
...@@ -176,10 +167,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -176,10 +167,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
Function_type* len_type = Type::make_function_type(NULL, NULL, len_result, Function_type* len_type = Type::make_function_type(NULL, NULL, len_result,
loc); loc);
len_type->set_is_builtin(); len_type->set_is_builtin();
Node::Escape_states* len_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
len_type->set_parameter_escape_states(len_escapes);
len_type->set_has_escape_info();
this->globals_->add_function_declaration("len", NULL, len_type, loc); this->globals_->add_function_declaration("len", NULL, len_type, loc);
Typed_identifier_list* cap_result = new Typed_identifier_list(); Typed_identifier_list* cap_result = new Typed_identifier_list();
...@@ -187,26 +174,16 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -187,26 +174,16 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
Function_type* cap_type = Type::make_function_type(NULL, NULL, len_result, Function_type* cap_type = Type::make_function_type(NULL, NULL, len_result,
loc); loc);
cap_type->set_is_builtin(); cap_type->set_is_builtin();
Node::Escape_states* cap_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
cap_type->set_parameter_escape_states(cap_escapes);
cap_type->set_has_escape_info();
this->globals_->add_function_declaration("cap", NULL, cap_type, loc); this->globals_->add_function_declaration("cap", NULL, cap_type, loc);
Function_type* print_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* print_type = Type::make_function_type(NULL, NULL, NULL, loc);
print_type->set_is_varargs(); print_type->set_is_varargs();
print_type->set_is_builtin(); print_type->set_is_builtin();
Node::Escape_states* print_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
print_type->set_parameter_escape_states(print_escapes);
print_type->set_has_escape_info();
this->globals_->add_function_declaration("print", NULL, print_type, loc); this->globals_->add_function_declaration("print", NULL, print_type, loc);
print_type = Type::make_function_type(NULL, NULL, NULL, loc); print_type = Type::make_function_type(NULL, NULL, NULL, loc);
print_type->set_is_varargs(); print_type->set_is_varargs();
print_type->set_is_builtin(); print_type->set_is_builtin();
print_type->set_parameter_escape_states(print_escapes);
print_type->set_has_escape_info();
this->globals_->add_function_declaration("println", NULL, print_type, loc); this->globals_->add_function_declaration("println", NULL, print_type, loc);
Type *empty = Type::make_empty_interface_type(loc); Type *empty = Type::make_empty_interface_type(loc);
...@@ -215,10 +192,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -215,10 +192,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
Function_type *panic_type = Type::make_function_type(NULL, panic_parms, Function_type *panic_type = Type::make_function_type(NULL, panic_parms,
NULL, loc); NULL, loc);
panic_type->set_is_builtin(); panic_type->set_is_builtin();
Node::Escape_states* panic_escapes =
new Node::Escape_states(1, Node::ESCAPE_ARG);
panic_type->set_parameter_escape_states(panic_escapes);
panic_type->set_has_escape_info();
this->globals_->add_function_declaration("panic", NULL, panic_type, loc); this->globals_->add_function_declaration("panic", NULL, panic_type, loc);
Typed_identifier_list* recover_result = new Typed_identifier_list(); Typed_identifier_list* recover_result = new Typed_identifier_list();
...@@ -232,10 +205,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -232,10 +205,6 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc);
close_type->set_is_varargs(); close_type->set_is_varargs();
close_type->set_is_builtin(); close_type->set_is_builtin();
Node::Escape_states* close_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
close_type->set_parameter_escape_states(close_escapes);
close_type->set_has_escape_info();
this->globals_->add_function_declaration("close", NULL, close_type, loc); this->globals_->add_function_declaration("close", NULL, close_type, loc);
Typed_identifier_list* copy_result = new Typed_identifier_list(); Typed_identifier_list* copy_result = new Typed_identifier_list();
...@@ -244,56 +213,31 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -244,56 +213,31 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
copy_result, loc); copy_result, loc);
copy_type->set_is_varargs(); copy_type->set_is_varargs();
copy_type->set_is_builtin(); copy_type->set_is_builtin();
Node::Escape_states* copy_escapes =
new Node::Escape_states(2, Node::ESCAPE_NONE);
copy_type->set_parameter_escape_states(copy_escapes);
copy_type->set_has_escape_info();
this->globals_->add_function_declaration("copy", NULL, copy_type, loc); this->globals_->add_function_declaration("copy", NULL, copy_type, loc);
Function_type* append_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* append_type = Type::make_function_type(NULL, NULL, NULL, loc);
append_type->set_is_varargs(); append_type->set_is_varargs();
append_type->set_is_builtin(); append_type->set_is_builtin();
Node::Escape_states* append_escapes = new Node::Escape_states;
append_escapes->push_back(Node::ESCAPE_ARG);
append_escapes->push_back(Node::ESCAPE_NONE);
append_type->set_parameter_escape_states(append_escapes);
append_type->set_has_escape_info();
this->globals_->add_function_declaration("append", NULL, append_type, loc); this->globals_->add_function_declaration("append", NULL, append_type, loc);
Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc);
complex_type->set_is_varargs(); complex_type->set_is_varargs();
complex_type->set_is_builtin(); complex_type->set_is_builtin();
Node::Escape_states* complex_escapes =
new Node::Escape_states(2, Node::ESCAPE_NONE);
complex_type->set_parameter_escape_states(complex_escapes);
complex_type->set_has_escape_info();
this->globals_->add_function_declaration("complex", NULL, complex_type, loc); this->globals_->add_function_declaration("complex", NULL, complex_type, loc);
Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc);
real_type->set_is_varargs(); real_type->set_is_varargs();
real_type->set_is_builtin(); real_type->set_is_builtin();
Node::Escape_states* real_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
real_type->set_parameter_escape_states(real_escapes);
real_type->set_has_escape_info();
this->globals_->add_function_declaration("real", NULL, real_type, loc); this->globals_->add_function_declaration("real", NULL, real_type, loc);
Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc);
imag_type->set_is_varargs(); imag_type->set_is_varargs();
imag_type->set_is_builtin(); imag_type->set_is_builtin();
Node::Escape_states* imag_escapes =
new Node::Escape_states(1, Node::ESCAPE_NONE);
imag_type->set_parameter_escape_states(imag_escapes);
imag_type->set_has_escape_info();
this->globals_->add_function_declaration("imag", NULL, imag_type, loc); this->globals_->add_function_declaration("imag", NULL, imag_type, loc);
Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc); Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc);
delete_type->set_is_varargs(); delete_type->set_is_varargs();
delete_type->set_is_builtin(); delete_type->set_is_builtin();
Node::Escape_states* delete_escapes =
new Node::Escape_states(2, Node::ESCAPE_NONE);
delete_type->set_parameter_escape_states(delete_escapes);
delete_type->set_has_escape_info();
this->globals_->add_function_declaration("delete", NULL, delete_type, loc); this->globals_->add_function_declaration("delete", NULL, delete_type, loc);
} }
...@@ -1889,74 +1833,6 @@ Gogo::add_label_reference(const std::string& label_name, ...@@ -1889,74 +1833,6 @@ Gogo::add_label_reference(const std::string& label_name,
issue_goto_errors); issue_goto_errors);
} }
// Add a function to the call graph.
Node*
Gogo::add_call_node(Named_object* function)
{
Node* call = this->lookup_call_node(function);
if (call == NULL)
{
call = Node::make_call(function);
this->call_graph_.insert(call);
this->named_call_nodes_[function] = call;
}
return call;
}
// Find the call node that represents FUNCTION. Return NULL if it does not
// exist.
Node*
Gogo::lookup_call_node(Named_object* function) const
{
Named_escape_nodes::const_iterator p = this->named_call_nodes_.find(function);
if (p == this->named_call_nodes_.end())
return NULL;
return p->second;
}
// Add a connection node for OBJECT.
Node*
Gogo::add_connection_node(Named_object* object)
{
Node* connection = this->lookup_connection_node(object);
if (connection == NULL)
{
connection = Node::make_connection(object, Node::ESCAPE_NONE);
// Each global variable is a part of the global connection graph.
if (object->is_variable()
&& object->var_value()->is_global())
{
connection->connection_node()->set_escape_state(Node::ESCAPE_GLOBAL);
this->global_connections_.insert(connection);
}
// Each function declaration or definition is the root of its own
// connection graph. This means closures will have their own
// connection graph that objects in the enclosing function might
// refer to.
if (object->is_function() || object->is_function_declaration())
this->connection_roots_.insert(connection);
this->named_connection_nodes_[object] = connection;
}
return connection;
}
// Find the connection node for OBJECT. Return NULL if it does not exist.
Node*
Gogo::lookup_connection_node(Named_object* object) const
{
Named_escape_nodes::const_iterator p =
this->named_connection_nodes_.find(object);
if (p == this->named_connection_nodes_.end())
return NULL;
return p->second;
}
// Return the current binding state. // Return the current binding state.
Bindings_snapshot* Bindings_snapshot*
...@@ -4918,13 +4794,6 @@ Function::export_func_with_type(Export* exp, const std::string& name, ...@@ -4918,13 +4794,6 @@ Function::export_func_with_type(Export* exp, const std::string& name,
exp->write_c_string("("); exp->write_c_string("(");
const Typed_identifier* receiver = fntype->receiver(); const Typed_identifier* receiver = fntype->receiver();
exp->write_name(receiver->name()); exp->write_name(receiver->name());
if (fntype->has_escape_info())
{
exp->write_c_string(" ");
exp->write_escape(fntype->receiver_escape_state());
}
exp->write_c_string(" "); exp->write_c_string(" ");
exp->write_type(receiver->type()); exp->write_type(receiver->type());
exp->write_c_string(") "); exp->write_c_string(") ");
...@@ -4948,13 +4817,6 @@ Function::export_func_with_type(Export* exp, const std::string& name, ...@@ -4948,13 +4817,6 @@ Function::export_func_with_type(Export* exp, const std::string& name,
else else
exp->write_c_string(", "); exp->write_c_string(", ");
exp->write_name(p->name()); exp->write_name(p->name());
if (fntype->has_escape_info())
{
exp->write_c_string(" ");
exp->write_escape(fntype->parameter_escape_states()->at(i));
}
exp->write_c_string(" "); exp->write_c_string(" ");
if (!is_varargs || p + 1 != parameters->end()) if (!is_varargs || p + 1 != parameters->end())
exp->write_type(p->type()); exp->write_type(p->type());
...@@ -5002,29 +4864,17 @@ Function::export_func_with_type(Export* exp, const std::string& name, ...@@ -5002,29 +4864,17 @@ Function::export_func_with_type(Export* exp, const std::string& name,
void void
Function::import_func(Import* imp, std::string* pname, Function::import_func(Import* imp, std::string* pname,
Typed_identifier** preceiver, Typed_identifier** preceiver,
Node::Escapement_lattice* rcvr_escape,
Typed_identifier_list** pparameters, Typed_identifier_list** pparameters,
Node::Escape_states** pparam_escapes,
Typed_identifier_list** presults, Typed_identifier_list** presults,
bool* is_varargs, bool* has_escape_info) bool* is_varargs)
{ {
*has_escape_info = false;
imp->require_c_string("func "); imp->require_c_string("func ");
*preceiver = NULL; *preceiver = NULL;
*rcvr_escape = Node::ESCAPE_NONE;
if (imp->peek_char() == '(') if (imp->peek_char() == '(')
{ {
imp->require_c_string("("); imp->require_c_string("(");
std::string name = imp->read_name(); std::string name = imp->read_name();
if (imp->match_c_string(" <escape")){
*has_escape_info = true;
imp->require_c_string(" ");
*rcvr_escape = imp->read_escape_info();
}
imp->require_c_string(" "); imp->require_c_string(" ");
Type* rtype = imp->read_type(); Type* rtype = imp->read_type();
*preceiver = new Typed_identifier(name, rtype, imp->location()); *preceiver = new Typed_identifier(name, rtype, imp->location());
...@@ -5034,27 +4884,16 @@ Function::import_func(Import* imp, std::string* pname, ...@@ -5034,27 +4884,16 @@ Function::import_func(Import* imp, std::string* pname,
*pname = imp->read_identifier(); *pname = imp->read_identifier();
Typed_identifier_list* parameters; Typed_identifier_list* parameters;
Node::Escape_states* param_escapes;
*is_varargs = false; *is_varargs = false;
imp->require_c_string(" ("); imp->require_c_string(" (");
if (imp->peek_char() == ')') if (imp->peek_char() == ')')
{
parameters = NULL; parameters = NULL;
param_escapes = NULL;
}
else else
{ {
parameters = new Typed_identifier_list(); parameters = new Typed_identifier_list();
param_escapes = new Node::Escape_states();
while (true) while (true)
{ {
std::string name = imp->read_name(); std::string name = imp->read_name();
if (imp->match_c_string(" <escape")){
*has_escape_info = true;
imp->require_c_string(" ");
param_escapes->push_back(imp->read_escape_info());
}
imp->require_c_string(" "); imp->require_c_string(" ");
if (imp->match_c_string("...")) if (imp->match_c_string("..."))
...@@ -5076,7 +4915,6 @@ Function::import_func(Import* imp, std::string* pname, ...@@ -5076,7 +4915,6 @@ Function::import_func(Import* imp, std::string* pname,
} }
imp->require_c_string(")"); imp->require_c_string(")");
*pparameters = parameters; *pparameters = parameters;
*pparam_escapes = param_escapes;
Typed_identifier_list* results; Typed_identifier_list* results;
if (imp->peek_char() != ' ') if (imp->peek_char() != ' ')
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#ifndef GO_GOGO_H #ifndef GO_GOGO_H
#define GO_GOGO_H #define GO_GOGO_H
#include "escape.h"
#include "go-linemap.h" #include "go-linemap.h"
class Traverse; class Traverse;
...@@ -126,21 +125,6 @@ class Gogo ...@@ -126,21 +125,6 @@ class Gogo
linemap() linemap()
{ return this->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. // Get the package name.
const std::string& const std::string&
package_name() const; package_name() const;
...@@ -361,22 +345,6 @@ class Gogo ...@@ -361,22 +345,6 @@ class Gogo
add_label_reference(const std::string&, Location, add_label_reference(const std::string&, Location,
bool issue_goto_errors); 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. // Return a snapshot of the current binding state.
Bindings_snapshot* Bindings_snapshot*
bindings_snapshot(Location); bindings_snapshot(Location);
...@@ -576,26 +544,6 @@ class Gogo ...@@ -576,26 +544,6 @@ class Gogo
void void
check_return_statements(); 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. // Do all exports.
void void
do_exports(); do_exports();
...@@ -730,10 +678,6 @@ class Gogo ...@@ -730,10 +678,6 @@ class Gogo
// where they were defined. // where they were defined.
typedef Unordered_map(std::string, Location) File_block_names; 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. // Type used to queue writing a type specific function.
struct Specific_type_function struct Specific_type_function
{ {
...@@ -766,20 +710,6 @@ class Gogo ...@@ -766,20 +710,6 @@ class Gogo
// The global binding contour. This includes the builtin functions // The global binding contour. This includes the builtin functions
// and the package we are compiling. // and the package we are compiling.
Bindings* globals_; 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. // The list of names we have seen in the file block.
File_block_names file_block_names_; File_block_names file_block_names_;
// Mapping from import file names to packages. // Mapping from import file names to packages.
...@@ -1215,11 +1145,8 @@ class Function ...@@ -1215,11 +1145,8 @@ class Function
// Import a function. // Import a function.
static void static void
import_func(Import*, std::string* pname, Typed_identifier** receiver, import_func(Import*, std::string* pname, Typed_identifier** receiver,
Node::Escapement_lattice* rcvr_escape,
Typed_identifier_list** pparameters, Typed_identifier_list** pparameters,
Node::Escape_states** pparam_escapes, Typed_identifier_list** presults, bool* is_varargs);
Typed_identifier_list** presults, bool* is_varargs,
bool* has_escape_info);
private: private:
// Type for mapping from label names to Label objects. // Type for mapping from label names to Label objects.
......
...@@ -502,28 +502,16 @@ Import::import_func(Package* package) ...@@ -502,28 +502,16 @@ Import::import_func(Package* package)
{ {
std::string name; std::string name;
Typed_identifier* receiver; Typed_identifier* receiver;
Node::Escapement_lattice rcvr_escape;
Typed_identifier_list* parameters; Typed_identifier_list* parameters;
Node::Escape_states* param_escapes;
Typed_identifier_list* results; Typed_identifier_list* results;
bool is_varargs; bool is_varargs;
bool has_escape_info; Function::import_func(this, &name, &receiver,
Function::import_func(this, &name, &receiver, &rcvr_escape, &parameters, &parameters, &results, &is_varargs);
&param_escapes, &results, &is_varargs,
&has_escape_info);
Function_type *fntype = Type::make_function_type(receiver, parameters, Function_type *fntype = Type::make_function_type(receiver, parameters,
results, this->location_); results, this->location_);
if (is_varargs) if (is_varargs)
fntype->set_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_; Location loc = this->location_;
Named_object* no; Named_object* no;
if (fntype->is_method()) if (fntype->is_method())
...@@ -774,19 +762,6 @@ Import::read_type() ...@@ -774,19 +762,6 @@ Import::read_type()
return 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. // Register the builtin types.
void void
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#ifndef GO_IMPORT_H #ifndef GO_IMPORT_H
#define GO_IMPORT_H #define GO_IMPORT_H
#include "escape.h"
#include "export.h" #include "export.h"
#include "go-linemap.h" #include "go-linemap.h"
...@@ -198,10 +197,6 @@ class Import ...@@ -198,10 +197,6 @@ class Import
Type* Type*
read_type(); read_type();
// Read escape information.
Node::Escapement_lattice
read_escape_info();
private: private:
static Stream* static Stream*
try_package_in_directory(const std::string&, Location); try_package_in_directory(const std::string&, Location);
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
#include "backend.h" #include "backend.h"
#include "statements.h" #include "statements.h"
#include "ast-dump.h" #include "ast-dump.h"
#include "dataflow.h"
// Class Statement. // Class Statement.
...@@ -4821,22 +4820,6 @@ Select_clauses::Select_clause::check_types() ...@@ -4821,22 +4820,6 @@ Select_clauses::Select_clause::check_types()
error_at(this->location(), "invalid receive on send-only channel"); 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 // Whether this clause may fall through to the statement which follows
// the overall select statement. // the overall select statement.
...@@ -4955,17 +4938,6 @@ Select_clauses::check_types() ...@@ -4955,17 +4938,6 @@ Select_clauses::check_types()
p->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 // Return whether these select clauses fall through to the statement
// following the overall select statement. // following the overall select statement.
......
...@@ -47,7 +47,6 @@ class Bexpression; ...@@ -47,7 +47,6 @@ class Bexpression;
class Bstatement; class Bstatement;
class Bvariable; class Bvariable;
class Ast_dump_context; class Ast_dump_context;
class Dataflow;
// This class is used to traverse assignments made by a statement // This class is used to traverse assignments made by a statement
// which makes assignments. // which makes assignments.
...@@ -860,10 +859,6 @@ class Select_clauses ...@@ -860,10 +859,6 @@ class Select_clauses
void void
check_types(); check_types();
// Analyze the dataflow across each case statement.
void
analyze_dataflow(Dataflow*);
// Whether the select clauses may fall through to the statement // Whether the select clauses may fall through to the statement
// which follows the overall select statement. // which follows the overall select statement.
bool bool
...@@ -920,10 +915,6 @@ class Select_clauses ...@@ -920,10 +915,6 @@ class Select_clauses
void void
check_types(); check_types();
// Analyze the dataflow across each case statement.
void
analyze_dataflow(Dataflow*);
// Return true if this is the default clause. // Return true if this is the default clause.
bool bool
is_default() const is_default() const
...@@ -1030,10 +1021,6 @@ class Select_statement : public Statement ...@@ -1030,10 +1021,6 @@ class Select_statement : public Statement
Unnamed_label* Unnamed_label*
break_label(); break_label();
void
analyze_dataflow(Dataflow* dataflow)
{ this->clauses_->analyze_dataflow(dataflow); }
protected: protected:
int int
do_traverse(Traverse* traverse) do_traverse(Traverse* traverse)
......
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#define GO_TYPES_H #define GO_TYPES_H
#include "go-linemap.h" #include "go-linemap.h"
#include "escape.h"
class Gogo; class Gogo;
class Package; class Package;
...@@ -1779,7 +1778,7 @@ class Function_type : public Type ...@@ -1779,7 +1778,7 @@ class Function_type : public Type
: Type(TYPE_FUNCTION), : Type(TYPE_FUNCTION),
receiver_(receiver), parameters_(parameters), results_(results), receiver_(receiver), parameters_(parameters), results_(results),
location_(location), is_varargs_(false), is_builtin_(false), location_(location), is_varargs_(false), is_builtin_(false),
has_escape_info_(false), fnbtype_(NULL), parameter_escape_states_(NULL) fnbtype_(NULL)
{ } { }
// Get the receiver. // Get the receiver.
...@@ -1787,16 +1786,6 @@ class Function_type : public Type ...@@ -1787,16 +1786,6 @@ class Function_type : public Type
receiver() const receiver() const
{ return this->receiver_; } { 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. // Get the return names and types.
const Typed_identifier_list* const Typed_identifier_list*
results() const results() const
...@@ -1807,16 +1796,6 @@ class Function_type : public Type ...@@ -1807,16 +1796,6 @@ class Function_type : public Type
parameters() const parameters() const
{ return this->parameters_; } { 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. // Whether this is a varargs function.
bool bool
is_varargs() const is_varargs() const
...@@ -1827,11 +1806,6 @@ class Function_type : public Type ...@@ -1827,11 +1806,6 @@ class Function_type : public Type
is_builtin() const is_builtin() const
{ return this->is_builtin_; } { 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. // The location where this type was defined.
Location Location
location() const location() const
...@@ -1862,11 +1836,6 @@ class Function_type : public Type ...@@ -1862,11 +1836,6 @@ class Function_type : public Type
set_is_builtin() set_is_builtin()
{ this->is_builtin_ = true; } { this->is_builtin_ = true; }
// Record that this has escape information.
void
set_has_escape_info()
{ this->has_escape_info_ = true; }
// Import a function type. // Import a function type.
static Function_type* static Function_type*
do_import(Import*); do_import(Import*);
...@@ -1978,16 +1947,9 @@ class Function_type : public Type ...@@ -1978,16 +1947,9 @@ class Function_type : public Type
// Whether this is a special builtin function which can not simply // Whether this is a special builtin function which can not simply
// be called. This is used for len, cap, etc. // be called. This is used for len, cap, etc.
bool is_builtin_; 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 // The backend representation of this type for backend function
// declarations and definitions. // declarations and definitions.
Btype* fnbtype_; 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. // 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