===================================================================
@@ -50,8 +50,6 @@ go-warn = $(STRICT_WARN)
GO_OBJS = \
go/ast-dump.o \
- go/dataflow.o \
- go/escape.o \
go/export.o \
go/expressions.o \
go/go-backend.o \
===================================================================
@@ -1,4 +1,4 @@
-50b2b468a85045c66d60112dc094c31ec4897123
+46b108136c0d102f181f0cc7c398e3db8c4d08a3
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
===================================================================
@@ -1,299 +0,0 @@
-// 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;
-}
===================================================================
@@ -1,91 +0,0 @@
-// 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)
===================================================================
@@ -1,1669 +0,0 @@
-// 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();
-}
===================================================================
@@ -1,310 +0,0 @@
-// 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)
this->type_refs_[type] = index;
}
-// Export escape information.
-
-void
-Export::write_escape(const Node::Escapement_lattice& e)
-{
- char buf[30];
- snprintf(buf, sizeof buf, "<escape %d>", e);
- this->write_c_string(buf);
- return;
-}
-
// Add the builtin types to the export table.
void
===================================================================
@@ -7,7 +7,6 @@
#ifndef GO_EXPORT_H
#define GO_EXPORT_H
-#include "escape.h"
#include "string-dump.h"
struct sha1_ctx;
@@ -162,10 +161,6 @@ class Export : public String_dump
void
write_type(const Type*);
- // Write out escape information.
- void
- write_escape(const Node::Escapement_lattice& e);
-
private:
Export(const Export&);
Export& operator=(const Export&);
===================================================================
@@ -110,10 +110,6 @@ go_parse_input_files(const char** filena
if (only_check_syntax)
return;
- // Consider escape analysis information when deciding if a variable should
- // live on the heap or on the stack.
- ::gogo->optimize_allocations(filenames);
-
// Export global identifiers as appropriate.
::gogo->do_exports();
===================================================================
@@ -18,7 +18,6 @@
#include "runtime.h"
#include "import.h"
#include "export.h"
-#include "escape.h"
#include "backend.h"
#include "gogo.h"
@@ -156,19 +155,11 @@ Gogo::Gogo(Backend* backend, Linemap* li
Function_type* new_type = Type::make_function_type(NULL, NULL, NULL, loc);
new_type->set_is_varargs();
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);
Function_type* make_type = Type::make_function_type(NULL, NULL, NULL, loc);
make_type->set_is_varargs();
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);
Typed_identifier_list* len_result = new Typed_identifier_list();
@@ -176,10 +167,6 @@ Gogo::Gogo(Backend* backend, Linemap* li
Function_type* len_type = Type::make_function_type(NULL, NULL, len_result,
loc);
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);
Typed_identifier_list* cap_result = new Typed_identifier_list();
@@ -187,26 +174,16 @@ Gogo::Gogo(Backend* backend, Linemap* li
Function_type* cap_type = Type::make_function_type(NULL, NULL, len_result,
loc);
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);
Function_type* print_type = Type::make_function_type(NULL, NULL, NULL, loc);
print_type->set_is_varargs();
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);
print_type = Type::make_function_type(NULL, NULL, NULL, loc);
print_type->set_is_varargs();
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);
Type *empty = Type::make_empty_interface_type(loc);
@@ -215,10 +192,6 @@ Gogo::Gogo(Backend* backend, Linemap* li
Function_type *panic_type = Type::make_function_type(NULL, panic_parms,
NULL, loc);
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);
Typed_identifier_list* recover_result = new Typed_identifier_list();
@@ -232,10 +205,6 @@ Gogo::Gogo(Backend* backend, Linemap* li
Function_type* close_type = Type::make_function_type(NULL, NULL, NULL, loc);
close_type->set_is_varargs();
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);
Typed_identifier_list* copy_result = new Typed_identifier_list();
@@ -244,56 +213,31 @@ Gogo::Gogo(Backend* backend, Linemap* li
copy_result, loc);
copy_type->set_is_varargs();
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);
Function_type* append_type = Type::make_function_type(NULL, NULL, NULL, loc);
append_type->set_is_varargs();
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);
Function_type* complex_type = Type::make_function_type(NULL, NULL, NULL, loc);
complex_type->set_is_varargs();
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);
Function_type* real_type = Type::make_function_type(NULL, NULL, NULL, loc);
real_type->set_is_varargs();
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);
Function_type* imag_type = Type::make_function_type(NULL, NULL, NULL, loc);
imag_type->set_is_varargs();
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);
Function_type* delete_type = Type::make_function_type(NULL, NULL, NULL, loc);
delete_type->set_is_varargs();
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);
}
@@ -1889,74 +1833,6 @@ Gogo::add_label_reference(const std::str
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.
Bindings_snapshot*
@@ -4918,13 +4794,6 @@ Function::export_func_with_type(Export*
exp->write_c_string("(");
const Typed_identifier* receiver = fntype->receiver();
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_type(receiver->type());
exp->write_c_string(") ");
@@ -4948,13 +4817,6 @@ Function::export_func_with_type(Export*
else
exp->write_c_string(", ");
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(" ");
if (!is_varargs || p + 1 != parameters->end())
exp->write_type(p->type());
@@ -5002,29 +4864,17 @@ Function::export_func_with_type(Export*
void
Function::import_func(Import* imp, std::string* pname,
Typed_identifier** preceiver,
- Node::Escapement_lattice* rcvr_escape,
Typed_identifier_list** pparameters,
- Node::Escape_states** pparam_escapes,
Typed_identifier_list** presults,
- bool* is_varargs, bool* has_escape_info)
+ bool* is_varargs)
{
- *has_escape_info = false;
-
imp->require_c_string("func ");
*preceiver = NULL;
- *rcvr_escape = Node::ESCAPE_NONE;
if (imp->peek_char() == '(')
{
imp->require_c_string("(");
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(" ");
Type* rtype = imp->read_type();
*preceiver = new Typed_identifier(name, rtype, imp->location());
@@ -5034,27 +4884,16 @@ Function::import_func(Import* imp, std::
*pname = imp->read_identifier();
Typed_identifier_list* parameters;
- Node::Escape_states* param_escapes;
*is_varargs = false;
imp->require_c_string(" (");
if (imp->peek_char() == ')')
- {
- parameters = NULL;
- param_escapes = NULL;
- }
+ parameters = NULL;
else
{
parameters = new Typed_identifier_list();
- param_escapes = new Node::Escape_states();
while (true)
{
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(" ");
if (imp->match_c_string("..."))
@@ -5076,7 +4915,6 @@ Function::import_func(Import* imp, std::
}
imp->require_c_string(")");
*pparameters = parameters;
- *pparam_escapes = param_escapes;
Typed_identifier_list* results;
if (imp->peek_char() != ' ')
===================================================================
@@ -7,7 +7,6 @@
#ifndef GO_GOGO_H
#define GO_GOGO_H
-#include "escape.h"
#include "go-linemap.h"
class Traverse;
@@ -126,21 +125,6 @@ class Gogo
linemap()
{ return this->linemap_; }
- // Get the Call Graph.
- const std::set<Node*>&
- call_graph() const
- { return this->call_graph_; }
-
- // Get the roots of each connection graph.
- const std::set<Node*>&
- connection_roots() const
- { return this->connection_roots_; }
-
- // Get the nodes that escape globally.
- const std::set<Node*>&
- global_connections() const
- { return this->global_connections_; }
-
// Get the package name.
const std::string&
package_name() const;
@@ -361,22 +345,6 @@ class Gogo
add_label_reference(const std::string&, Location,
bool issue_goto_errors);
- // Add a FUNCTION to the call graph.
- Node*
- add_call_node(Named_object* function);
-
- // Lookup the call node for FUNCTION.
- Node*
- lookup_call_node(Named_object* function) const;
-
- // Add a connection node for OBJECT.
- Node*
- add_connection_node(Named_object* object);
-
- // Lookup the connection node for OBJECT.
- Node*
- lookup_connection_node(Named_object* object) const;
-
// Return a snapshot of the current binding state.
Bindings_snapshot*
bindings_snapshot(Location);
@@ -576,26 +544,6 @@ class Gogo
void
check_return_statements();
- // Build call graph.
- void
- build_call_graph();
-
- // Build connection graphs.
- void
- build_connection_graphs();
-
- // Analyze reachability in the connection graphs.
- void
- analyze_reachability();
-
- // Record escape information in function signatures for export data.
- void
- mark_escaping_signatures();
-
- // Optimize variable allocation.
- void
- optimize_allocations(const char** filenames);
-
// Do all exports.
void
do_exports();
@@ -730,10 +678,6 @@ class Gogo
// where they were defined.
typedef Unordered_map(std::string, Location) File_block_names;
- // Type used to map named objects that refer to objects to the
- // node that represent them in the escape analysis graphs.
- typedef Unordered_map(Named_object*, Node*) Named_escape_nodes;
-
// Type used to queue writing a type specific function.
struct Specific_type_function
{
@@ -766,20 +710,6 @@ class Gogo
// The global binding contour. This includes the builtin functions
// and the package we are compiling.
Bindings* globals_;
- // The call graph for a program execution which represents the functions
- // encountered and the caller-callee relationship between the functions.
- std::set<Node*> call_graph_;
- // The nodes that form the roots of the connection graphs for each called
- // function and represent the connectivity relationship between all objects
- // in the function.
- std::set<Node*> connection_roots_;
- // All connection nodes that have an escape state of ESCAPE_GLOBAL are a part
- // of a special connection graph of only global variables.
- std::set<Node*> global_connections_;
- // Mapping from named objects to nodes in the call graph.
- Named_escape_nodes named_call_nodes_;
- // Mapping from named objects to nodes in a connection graph.
- Named_escape_nodes named_connection_nodes_;
// The list of names we have seen in the file block.
File_block_names file_block_names_;
// Mapping from import file names to packages.
@@ -1215,11 +1145,8 @@ class Function
// Import a function.
static void
import_func(Import*, std::string* pname, Typed_identifier** receiver,
- Node::Escapement_lattice* rcvr_escape,
Typed_identifier_list** pparameters,
- Node::Escape_states** pparam_escapes,
- Typed_identifier_list** presults, bool* is_varargs,
- bool* has_escape_info);
+ Typed_identifier_list** presults, bool* is_varargs);
private:
// Type for mapping from label names to Label objects.
===================================================================
@@ -502,28 +502,16 @@ Import::import_func(Package* package)
{
std::string name;
Typed_identifier* receiver;
- Node::Escapement_lattice rcvr_escape;
Typed_identifier_list* parameters;
- Node::Escape_states* param_escapes;
Typed_identifier_list* results;
bool is_varargs;
- bool has_escape_info;
- Function::import_func(this, &name, &receiver, &rcvr_escape, ¶meters,
- ¶m_escapes, &results, &is_varargs,
- &has_escape_info);
+ Function::import_func(this, &name, &receiver,
+ ¶meters, &results, &is_varargs);
Function_type *fntype = Type::make_function_type(receiver, parameters,
results, this->location_);
if (is_varargs)
fntype->set_is_varargs();
- if (has_escape_info)
- {
- if (fntype->is_method())
- fntype->set_receiver_escape_state(rcvr_escape);
- fntype->set_parameter_escape_states(param_escapes);
- fntype->set_has_escape_info();
- }
-
Location loc = this->location_;
Named_object* no;
if (fntype->is_method())
@@ -774,19 +762,6 @@ Import::read_type()
return type;
}
-// Read escape info in the import stream.
-
-Node::Escapement_lattice
-Import::read_escape_info()
-{
- Stream* stream = this->stream_;
- this->require_c_string("<escape ");
-
- int escape_value = stream->get_char() - '0';
- this->require_c_string(">");
- return Node::Escapement_lattice(escape_value);
-}
-
// Register the builtin types.
void
===================================================================
@@ -7,7 +7,6 @@
#ifndef GO_IMPORT_H
#define GO_IMPORT_H
-#include "escape.h"
#include "export.h"
#include "go-linemap.h"
@@ -198,10 +197,6 @@ class Import
Type*
read_type();
- // Read escape information.
- Node::Escapement_lattice
- read_escape_info();
-
private:
static Stream*
try_package_in_directory(const std::string&, Location);
===================================================================
@@ -14,7 +14,6 @@
#include "backend.h"
#include "statements.h"
#include "ast-dump.h"
-#include "dataflow.h"
// Class Statement.
@@ -4821,22 +4820,6 @@ Select_clauses::Select_clause::check_typ
error_at(this->location(), "invalid receive on send-only channel");
}
-// Analyze the dataflow across each case statement.
-
-void
-Select_clauses::Select_clause::analyze_dataflow(Dataflow* dataflow)
-{
- if (this->is_default_)
- return;
-
- // For a CommClause, the dataflow analysis should record a definition of
- // VAR and CLOSEDVAR
- if (this->var_ != NULL && !this->var_->is_sink())
- dataflow->add_def(this->var_, this->channel_, NULL, false);
- if (this->closedvar_ != NULL && !this->closedvar_->is_sink())
- dataflow->add_def(this->closedvar_, this->channel_, NULL, false);
-}
-
// Whether this clause may fall through to the statement which follows
// the overall select statement.
@@ -4955,17 +4938,6 @@ Select_clauses::check_types()
p->check_types();
}
-// Analyze the dataflow across each case statement.
-
-void
-Select_clauses::analyze_dataflow(Dataflow* dataflow)
-{
- for (Clauses::iterator p = this->clauses_.begin();
- p != this->clauses_.end();
- ++p)
- p->analyze_dataflow(dataflow);
-}
-
// Return whether these select clauses fall through to the statement
// following the overall select statement.
===================================================================
@@ -47,7 +47,6 @@ class Bexpression;
class Bstatement;
class Bvariable;
class Ast_dump_context;
-class Dataflow;
// This class is used to traverse assignments made by a statement
// which makes assignments.
@@ -860,10 +859,6 @@ class Select_clauses
void
check_types();
- // Analyze the dataflow across each case statement.
- void
- analyze_dataflow(Dataflow*);
-
// Whether the select clauses may fall through to the statement
// which follows the overall select statement.
bool
@@ -920,10 +915,6 @@ class Select_clauses
void
check_types();
- // Analyze the dataflow across each case statement.
- void
- analyze_dataflow(Dataflow*);
-
// Return true if this is the default clause.
bool
is_default() const
@@ -1030,10 +1021,6 @@ class Select_statement : public Statemen
Unnamed_label*
break_label();
- void
- analyze_dataflow(Dataflow* dataflow)
- { this->clauses_->analyze_dataflow(dataflow); }
-
protected:
int
do_traverse(Traverse* traverse)
===================================================================
@@ -8,7 +8,6 @@
#define GO_TYPES_H
#include "go-linemap.h"
-#include "escape.h"
class Gogo;
class Package;
@@ -1779,7 +1778,7 @@ class Function_type : public Type
: Type(TYPE_FUNCTION),
receiver_(receiver), parameters_(parameters), results_(results),
location_(location), is_varargs_(false), is_builtin_(false),
- has_escape_info_(false), fnbtype_(NULL), parameter_escape_states_(NULL)
+ fnbtype_(NULL)
{ }
// Get the receiver.
@@ -1787,16 +1786,6 @@ class Function_type : public Type
receiver() const
{ return this->receiver_; }
- // Get the escape state of the receiver.
- const Node::Escapement_lattice&
- receiver_escape_state() const
- { return this->receiver_escape_state_; }
-
- // Set the escape state of the receiver.
- void
- set_receiver_escape_state(const Node::Escapement_lattice& e)
- { this->receiver_escape_state_ = e; }
-
// Get the return names and types.
const Typed_identifier_list*
results() const
@@ -1807,16 +1796,6 @@ class Function_type : public Type
parameters() const
{ return this->parameters_; }
- // Get the escape states associated with each parameter.
- const Node::Escape_states*
- parameter_escape_states() const
- { return this->parameter_escape_states_; }
-
- // Set the escape states of the parameters.
- void
- set_parameter_escape_states(Node::Escape_states* states)
- { this->parameter_escape_states_ = states; }
-
// Whether this is a varargs function.
bool
is_varargs() const
@@ -1827,11 +1806,6 @@ class Function_type : public Type
is_builtin() const
{ return this->is_builtin_; }
- // Whether this contains escape information.
- bool
- has_escape_info() const
- { return this->has_escape_info_; }
-
// The location where this type was defined.
Location
location() const
@@ -1862,11 +1836,6 @@ class Function_type : public Type
set_is_builtin()
{ this->is_builtin_ = true; }
- // Record that this has escape information.
- void
- set_has_escape_info()
- { this->has_escape_info_ = true; }
-
// Import a function type.
static Function_type*
do_import(Import*);
@@ -1978,16 +1947,9 @@ class Function_type : public Type
// Whether this is a special builtin function which can not simply
// be called. This is used for len, cap, etc.
bool is_builtin_;
- // Whether escape information for the receiver and parameters has been
- // recorded.
- bool has_escape_info_;
// The backend representation of this type for backend function
// declarations and definitions.
Btype* fnbtype_;
- // The escape state of the receiver.
- Node::Escapement_lattice receiver_escape_state_;
- // The escape states of each parameter.
- Node::Escape_states* parameter_escape_states_;
};
// The type of a function's backend representation.