From e49aacaf3083a99dc266209ed91183e91b11ffad Mon Sep 17 00:00:00 2001 From: Chris Manghane Date: Fri, 29 Apr 2016 17:33:01 +0000 Subject: [PATCH] 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 --- gcc/go/ChangeLog | 4 + gcc/go/Make-lang.in | 2 - gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/dataflow.cc | 299 ------- gcc/go/gofrontend/dataflow.h | 91 --- gcc/go/gofrontend/escape.cc | 1669 --------------------------------------- gcc/go/gofrontend/escape.h | 310 -------- gcc/go/gofrontend/export.cc | 11 - gcc/go/gofrontend/export.h | 5 - gcc/go/gofrontend/go.cc | 4 - gcc/go/gofrontend/gogo.cc | 166 +--- gcc/go/gofrontend/gogo.h | 75 +- gcc/go/gofrontend/import.cc | 29 +- gcc/go/gofrontend/import.h | 5 - gcc/go/gofrontend/statements.cc | 28 - gcc/go/gofrontend/statements.h | 13 - gcc/go/gofrontend/types.h | 40 +- 17 files changed, 11 insertions(+), 2742 deletions(-) delete mode 100644 gcc/go/gofrontend/dataflow.cc delete mode 100644 gcc/go/gofrontend/dataflow.h delete mode 100644 gcc/go/gofrontend/escape.cc delete mode 100644 gcc/go/gofrontend/escape.h diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index f3b419b..c179e79 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,7 @@ +2016-04-29 Chris Manghane + + * Make-lang.in (GO_OBJS): Remove go/dataflow.o, go/escape.o. + 2016-04-18 Michael Matz * go-gcc.cc (Gcc_backend::implicit_variable): Use SET_DECL_ALIGN. diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in index 3240fe8..df27e21 100644 --- a/gcc/go/Make-lang.in +++ b/gcc/go/Make-lang.in @@ -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 \ diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 8dc8dc0..1874a2e 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/gcc/go/gofrontend/dataflow.cc b/gcc/go/gofrontend/dataflow.cc deleted file mode 100644 index bf1d54a..0000000 --- a/gcc/go/gofrontend/dataflow.cc +++ /dev/null @@ -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 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 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; -} diff --git a/gcc/go/gofrontend/dataflow.h b/gcc/go/gofrontend/dataflow.h deleted file mode 100644 index a75c8e6..0000000 --- a/gcc/go/gofrontend/dataflow.h +++ /dev/null @@ -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 Defs; - - // A list of refs. - typedef std::vector 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 Defmap; - - // Map from variables to a list of refs to the vairable. - typedef std::map Refmap; - - // Variable defs. - Defmap defs_; - // Variable refs; - Refmap refs_; -}; - - -#endif // !defined(GO_DATAFLOW_H) diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc deleted file mode 100644 index 3a57383..0000000 --- a/gcc/go/gofrontend/escape.cc +++ /dev/null @@ -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 - -#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::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 call_graph = gogo->call_graph(); - - // Generate GraphViz nodes for each node. - for (std::set::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 callees = (*p)->edges(); - for (std::set::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 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::const_iterator p1 = globals.begin(); - p1 != globals.end(); - ++p1) - (*p1)->connection_node()->dump_connection(this); - this->write_c_string("}\n"); - - std::set roots = this->gogo_->connection_roots(); - for (std::set::const_reverse_iterator p1 = roots.rbegin(); - p1 != roots.rend(); - ++p1) - { - std::set 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::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 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 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::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::const_iterator pos = positions.begin(); - params != NULL && pos != positions.end(); - ++pos) - { - std::string param_name; - if (*pos >= 0 && params->size() <= static_cast(*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 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::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::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 worklist; - - // Run reachability analysis on all globally escaping objects. - for (std::set::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 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::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 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::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::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(); -} diff --git a/gcc/go/gofrontend/escape.h b/gcc/go/gofrontend/escape.h deleted file mode 100644 index 42c79f6..0000000 --- a/gcc/go/gofrontend/escape.h +++ /dev/null @@ -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 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(); } - - // 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& - 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 - const Node_class* - convert() const - { - return (this->classification_ == node_classification - ? static_cast(this) - : NULL); - } - - template - Node_class* - convert() - { - return (this->classification_ == node_classification - ? static_cast(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" 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 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& - 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 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) diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc index e8617a1..5c0094d 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -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, "", e); - this->write_c_string(buf); - return; -} - // Add the builtin types to the export table. void diff --git a/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h index 92baa72..0526e9a 100644 --- a/gcc/go/gofrontend/export.h +++ b/gcc/go/gofrontend/export.h @@ -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&); diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index 98cf650..cd30cca 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -110,10 +110,6 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, if (only_check_syntax) return; - // Consider escape analysis information when deciding if a variable should - // live on the heap or on the stack. - ::gogo->optimize_allocations(filenames); - // Export global identifiers as appropriate. ::gogo->do_exports(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 5413cc9..e373d8b 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -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* linemap, int, int pointer_size) 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* linemap, int, int pointer_size) 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* linemap, int, int pointer_size) 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* linemap, int, int pointer_size) 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* linemap, int, int pointer_size) 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* linemap, int, int pointer_size) 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::string& label_name, 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, const std::string& name, 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* exp, const std::string& name, 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* exp, const std::string& name, 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(" 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::string* pname, *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(" 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::string* pname, } imp->require_c_string(")"); *pparameters = parameters; - *pparam_escapes = param_escapes; Typed_identifier_list* results; if (imp->peek_char() != ' ') diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 0b1f8ef..45c3857 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -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& - call_graph() const - { return this->call_graph_; } - - // Get the roots of each connection graph. - const std::set& - connection_roots() const - { return this->connection_roots_; } - - // Get the nodes that escape globally. - const std::set& - 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 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 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 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. diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index 1bb6ad8..8a6dd78 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -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("get_char() - '0'; - this->require_c_string(">"); - return Node::Escapement_lattice(escape_value); -} - // Register the builtin types. void diff --git a/gcc/go/gofrontend/import.h b/gcc/go/gofrontend/import.h index 7aa1fc9..2a9ac80 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -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); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index ba77fd6..c834444 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -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_types() error_at(this->location(), "invalid receive on send-only channel"); } -// Analyze the dataflow across each case statement. - -void -Select_clauses::Select_clause::analyze_dataflow(Dataflow* dataflow) -{ - if (this->is_default_) - return; - - // For a CommClause, the dataflow analysis should record a definition of - // VAR and CLOSEDVAR - if (this->var_ != NULL && !this->var_->is_sink()) - dataflow->add_def(this->var_, this->channel_, NULL, false); - if (this->closedvar_ != NULL && !this->closedvar_->is_sink()) - dataflow->add_def(this->closedvar_, this->channel_, NULL, false); -} - // Whether this clause may fall through to the statement which follows // the overall select statement. @@ -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. diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index cf847d0..498e0f0 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -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 Statement Unnamed_label* break_label(); - void - analyze_dataflow(Dataflow* dataflow) - { this->clauses_->analyze_dataflow(dataflow); } - protected: int do_traverse(Traverse* traverse) diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 8d2a982..bf673e6 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -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. -- 2.7.4