From: Chris Manghane Date: Fri, 17 Apr 2015 17:10:12 +0000 (+0000) Subject: compiler: Escape analysis. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b496063dcd8bf5041e101de4532297803fa997b2;p=gcc.git compiler: Escape analysis. By Chris Manghane. Comprises three changes to gofrontend repository: compiler: Add escape information to export data. compiler: Stack-allocate non-escaping variables. This change allows variables initialized through make or new to be allocated on the stack via a temporary variable if they do not escape their function. It also improves the analysis to consider situations where variables escape in the standard library and go testsuite such as: *nested composite literals and composite literal arguments *method receivers always escaping *escape via statements in closures referring to enclosing variables *escape via calls with multiple return results compiler: Basic escape analysis for the go frontend. This is an implementation of the algorithm described in "Escape Analysis in Java" by Choi et. al. It relies on dataflow information to discover variable references to one another. Handles assignments to closures and association between closures variables and the variables of the enclosing scope. Dataflow analysis does not discover references through range statements e.g. for _, v := range a will not recognize that all values of v are references to a. * Make-lang.in (GO_OBJS): Add go/escape.o. From-SVN: r222188 --- diff --git a/gcc/go/ChangeLog b/gcc/go/ChangeLog index 1cee68cb76f..e0268c685e2 100644 --- a/gcc/go/ChangeLog +++ b/gcc/go/ChangeLog @@ -1,3 +1,7 @@ +2015-04-17 Chris Manghane + + * Make-lang.in (GO_OBJS): Add go/escape.o. + 2015-02-02 Ian Lance Taylor PR go/64836 diff --git a/gcc/go/Make-lang.in b/gcc/go/Make-lang.in index 6c5968a13f7..34470875cfd 100644 --- a/gcc/go/Make-lang.in +++ b/gcc/go/Make-lang.in @@ -51,6 +51,7 @@ go-warn = $(STRICT_WARN) GO_OBJS = \ go/ast-dump.o \ go/dataflow.o \ + go/escape.o \ go/export.o \ go/expressions.o \ go/go-backend.o \ diff --git a/gcc/go/gofrontend/dataflow.cc b/gcc/go/gofrontend/dataflow.cc index 572ab3631cd..bf1d54ab261 100644 --- a/gcc/go/gofrontend/dataflow.cc +++ b/gcc/go/gofrontend/dataflow.cc @@ -169,8 +169,20 @@ Dataflow_traverse_statements::statement(Block* block, size_t* pindex, Statement *statement) { Dataflow_traverse_assignment dta(this->dataflow_, statement); - if (!statement->traverse_assignments(&dta)) + + // For thunk statements, make sure to traverse the call expression to + // find any reference to a variable being used as an argument. + if (!statement->traverse_assignments(&dta) + || statement->thunk_statement() != NULL) { + // Case statements in selects will be lowered into temporaries at this + // point so our dataflow analysis will miss references between a/c and ch + // in case statements of the form a,c := <-ch. Do a special dataflow + // analysis for select statements here; the analysis for the blocks will + // be handled as usual. + if (statement->select_statement() != NULL) + statement->select_statement()->analyze_dataflow(this->dataflow_); + Dataflow_traverse_expressions dte(this->dataflow_, statement); statement->traverse(block, pindex, &dte); } @@ -195,12 +207,21 @@ Dataflow::Compare_vars::operator()(const Named_object* no1, return false; if (loc1 > loc2) return true; + if (Linemap::is_predeclared_location(loc1)) + return false; - if (no1 == no2) + if (no1 == no2 + || (no1->is_result_variable() + && no2->is_result_variable()) + || ((no1->is_variable() + && no1->var_value()->is_type_switch_var()) + && (no2->is_variable() + && no2->var_value()->is_type_switch_var()))) return false; // We can't have two variables with the same name in the same - // location. + // location unless they are type switch variables which share the same + // fake location. go_unreachable(); } diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc new file mode 100644 index 00000000000..7c8955bb8a5 --- /dev/null +++ b/gcc/go/gofrontend/escape.cc @@ -0,0 +1,1481 @@ +// 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); + + // 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; + + 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 || ce->args() == NULL) + 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); + } + } + 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; + bool param_is_interface = false; + 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(); + param_is_interface = + (params->at(*pos).type()->interface_type() != NULL); + } + + 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); + + // Act conservatively when an argument is converted into an interface + // value. FIXME. + if (param_is_interface) + param_node->connection_node()->set_escape_state(Node::ESCAPE_ARG); + 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)->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); + } +} + +// 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) + { + if (p->val == NULL) + continue; + + if (p->val->func_expression() != NULL) + { + // VAR is being defined as a function object. + Named_object* fn = p->val->func_expression()->named_object(); + Node* fn_node = this->gogo_->add_connection_node(fn); + var_node->add_edge(fn_node); + } + else if(p->val->is_composite_literal() + || p->val->heap_expression() != NULL) + this->handle_composite_literal(var, p->val); + + Named_object* ref = this->resolve_var_reference(p->val); + 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()); + + if (assn->rhs()->is_composite_literal() + || assn->rhs()->heap_expression() != NULL) + this->handle_composite_literal(var, assn->rhs()); + else if (assn->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. + Expression* call = + assn->rhs()->call_result_expression()->call(); + this->handle_call(var, call); + } + else if (assn->rhs()->call_expression() != NULL) + this->handle_call(var, assn->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; + + var_node->connection_node()->set_escape_state(Node::ESCAPE_ARG); + + 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: + this->handle_call(var, + p->statement->expression_statement()->expr()); + 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) + { + Binary_expression* comp = cond->binary_expression(); + if (comp->left()->call_expression() != NULL) + this->handle_call(var, comp->left()); + if (comp->right()->call_expression() != NULL) + this->handle_call(var, comp->right()); + } + } + 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); + } + else if (init->call_result_expression() != NULL) + { + // V's initialization will be a call result if + // V, V1 := call(VAR). + // There's no useful edges to make from V or V1, but we want + // to make sure we handle the call that references VAR. + Expression* call = init->call_result_expression()->call(); + this->handle_call(var, call); + } + else if (init->call_expression() != NULL) + this->handle_call(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; + + if (assn->rhs()->func_expression() != NULL) + { + Node* lhs_node = this->gogo_->add_connection_node(lhs_no); + Named_object* fn = assn->rhs()->func_expression()->named_object(); + Node* fn_node = this->gogo_->add_connection_node(fn); + lhs_node->add_edge(fn_node); + } + else if (assn->rhs()->call_expression() != NULL) + this->handle_call(lhs_no, assn->rhs()->call_expression()); + else + { + Named_object* rhs_no = this->resolve_var_reference(assn->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_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; + + 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(); + + for (std::set::iterator n = m->edges().begin(); + n != m->edges().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_ARG) + worklist.push_back(p->second); + } + + while (!worklist.empty()) + { + Node* m = worklist.front(); + worklist.pop_front(); + + for (std::set::iterator n = m->edges().begin(); + n != m->edges().end(); + ++n) + { + // If an object can be reached from a node with ESCAPE_ARG, + // it is ESCAPE_ARG or ESCAPE_GLOBAL. + if ((*n)->connection_node()->escape_state() > Node::ESCAPE_ARG) + { + (*n)->connection_node()->set_escape_state(Node::ESCAPE_ARG); + 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()) + { + if (var->var_value()->is_address_taken()) + 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(); + } + } + else if (var->is_result_variable() + && var->result_var_value()->is_address_taken()) + var->result_var_value()->set_does_not_escape(); + + 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 new file mode 100644 index 00000000000..42c79f61fad --- /dev/null +++ b/gcc/go/gofrontend/escape.h @@ -0,0 +1,310 @@ +// 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 5c0094d8795..e8617a1f9da 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -436,6 +436,17 @@ Export::write_type(const Type* type) this->type_refs_[type] = index; } +// Export escape information. + +void +Export::write_escape(const Node::Escapement_lattice& e) +{ + char buf[30]; + snprintf(buf, sizeof buf, "", 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 0526e9a3f6d..92baa722c50 100644 --- a/gcc/go/gofrontend/export.h +++ b/gcc/go/gofrontend/export.h @@ -7,6 +7,7 @@ #ifndef GO_EXPORT_H #define GO_EXPORT_H +#include "escape.h" #include "string-dump.h" struct sha1_ctx; @@ -161,6 +162,10 @@ class Export : public String_dump void write_type(const Type*); + // Write out escape information. + void + write_escape(const Node::Escapement_lattice& e); + private: Export(const Export&); Export& operator=(const Export&); diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 40d9aa7726b..53edb99a2db 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3017,100 +3017,7 @@ Expression::make_iota() return &iota_expression; } -// A type conversion expression. - -class Type_conversion_expression : public Expression -{ - public: - Type_conversion_expression(Type* type, Expression* expr, - Location location) - : Expression(EXPRESSION_CONVERSION, location), - type_(type), expr_(expr), may_convert_function_types_(false) - { } - - // Return the type to which we are converting. - Type* - type() const - { return this->type_; } - - // Return the expression which we are converting. - Expression* - expr() const - { return this->expr_; } - - // Permit converting from one function type to another. This is - // used internally for method expressions. - void - set_may_convert_function_types() - { - this->may_convert_function_types_ = true; - } - - // Import a type conversion expression. - static Expression* - do_import(Import*); - - protected: - int - do_traverse(Traverse* traverse); - - Expression* - do_lower(Gogo*, Named_object*, Statement_inserter*, int); - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - bool - do_is_constant() const; - - bool - do_is_immutable() const; - - bool - do_numeric_constant_value(Numeric_constant*) const; - - bool - do_string_constant_value(std::string*) const; - - Type* - do_type() - { return this->type_; } - - void - do_determine_type(const Type_context*) - { - Type_context subcontext(this->type_, false); - this->expr_->determine_type(&subcontext); - } - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { - return new Type_conversion_expression(this->type_, this->expr_->copy(), - this->location()); - } - - Bexpression* - do_get_backend(Translate_context* context); - - void - do_export(Export*) const; - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The type to convert to. - Type* type_; - // The expression to convert. - Expression* expr_; - // True if this is permitted to convert function types. This is - // used internally for method expressions. - bool may_convert_function_types_; -}; +// Class Type_conversion_expression. // Traversal. @@ -3300,6 +3207,15 @@ Type_conversion_expression::do_string_constant_value(std::string* val) const return false; } +// Determine the resulting type of the conversion. + +void +Type_conversion_expression::do_determine_type(const Type_context*) +{ + Type_context subcontext(this->type_, false); + this->expr_->determine_type(&subcontext); +} + // Check that types are convertible. void @@ -3750,6 +3666,19 @@ Unary_expression::do_flatten(Gogo* gogo, Named_object*, } } + if (this->op_ == OPERATOR_AND) + { + if (this->expr_->var_expression() != NULL) + { + Named_object* var = this->expr_->var_expression()->named_object(); + if (var->is_variable()) + this->escapes_ = var->var_value()->escapes(); + if (var->is_result_variable()) + this->escapes_ = var->result_var_value()->escapes(); + } + this->expr_->address_taken(this->escapes_); + } + if (this->create_temp_ && !this->expr_->is_variable()) { Temporary_statement* temp = @@ -4070,10 +3999,7 @@ Unary_expression::do_check_types(Gogo*) } } else - { - this->expr_->address_taken(this->escapes_); - this->expr_->issue_nil_check(); - } + this->expr_->issue_nil_check(); break; case OPERATOR_MULT: @@ -6360,13 +6286,15 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, Function_type* new_fntype = orig_fntype->copy_with_names(); - Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype, + std::string thunk_name = Gogo::thunk_name(); + Named_object* new_no = gogo->start_function(thunk_name, new_fntype, false, loc); Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc); cvar->set_is_used(); cvar->set_is_closure(); - Named_object* cp = Named_object::make_variable("$closure", NULL, cvar); + Named_object* cp = Named_object::make_variable("$closure" + thunk_name, + NULL, cvar); new_no->func_value()->set_closure_var(cp); gogo->start_block(loc); @@ -8080,7 +8008,9 @@ Builtin_call_expression::do_copy() { Call_expression* bce = new Builtin_call_expression(this->gogo_, this->fn()->copy(), - this->args()->copy(), + (this->args() == NULL + ? NULL + : this->args()->copy()), this->is_varargs(), this->location()); @@ -9505,52 +9435,7 @@ Expression::make_call(Expression* fn, Expression_list* args, bool is_varargs, return new Call_expression(fn, args, is_varargs, location); } -// A single result from a call which returns multiple results. - -class Call_result_expression : public Expression -{ - public: - Call_result_expression(Call_expression* call, unsigned int index) - : Expression(EXPRESSION_CALL_RESULT, call->location()), - call_(call), index_(index) - { } - - protected: - int - do_traverse(Traverse*); - - Type* - do_type(); - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { - return new Call_result_expression(this->call_->call_expression(), - this->index_); - } - - bool - do_must_eval_in_order() const - { return true; } - - Bexpression* - do_get_backend(Translate_context*); - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The underlying call expression. - Expression* call_; - // Which result we want. - unsigned int index_; -}; +// Class Call_result_expression. // Traverse a call result. @@ -9813,85 +9698,7 @@ Expression::make_index(Expression* left, Expression* start, Expression* end, return new Index_expression(left, start, end, cap, location); } -// An array index. This is used for both indexing and slicing. - -class Array_index_expression : public Expression -{ - public: - Array_index_expression(Expression* array, Expression* start, - Expression* end, Expression* cap, Location location) - : Expression(EXPRESSION_ARRAY_INDEX, location), - array_(array), start_(start), end_(end), cap_(cap), type_(NULL) - { } - - protected: - int - do_traverse(Traverse*); - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - Type* - do_type(); - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { - return Expression::make_array_index(this->array_->copy(), - this->start_->copy(), - (this->end_ == NULL - ? NULL - : this->end_->copy()), - (this->cap_ == NULL - ? NULL - : this->cap_->copy()), - this->location()); - } - - bool - do_must_eval_subexpressions_in_order(int* skip) const - { - *skip = 1; - return true; - } - - bool - do_is_addressable() const; - - void - do_address_taken(bool escapes) - { this->array_->address_taken(escapes); } - - void - do_issue_nil_check() - { this->array_->issue_nil_check(); } - - Bexpression* - do_get_backend(Translate_context*); - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The array we are getting a value from. - Expression* array_; - // The start or only index. - Expression* start_; - // The end index of a slice. This may be NULL for a simple array - // index, or it may be a nil expression for the length of the array. - Expression* end_; - // The capacity argument of a slice. This may be NULL for an array index or - // slice. - Expression* cap_; - // The type of the expression. - Type* type_; -}; +// Class Array_index_expression. // Array index traversal. @@ -11187,13 +10994,15 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo, Function_type* new_fntype = orig_fntype->copy_with_names(); - Named_object* new_no = gogo->start_function(Gogo::thunk_name(), new_fntype, + std::string thunk_name = Gogo::thunk_name(); + Named_object* new_no = gogo->start_function(thunk_name, new_fntype, false, loc); Variable* cvar = new Variable(closure_type, NULL, false, false, false, loc); cvar->set_is_used(); cvar->set_is_closure(); - Named_object* cp = Named_object::make_variable("$closure", NULL, cvar); + Named_object* cp = Named_object::make_variable("$closure" + thunk_name, + NULL, cvar); new_no->func_value()->set_closure_var(cp); gogo->start_block(loc); @@ -11593,43 +11402,45 @@ Expression::make_selector(Expression* left, const std::string& name, return new Selector_expression(left, name, location); } -// Implement the builtin function new. +// Class Allocation_expression. -class Allocation_expression : public Expression +int +Allocation_expression::do_traverse(Traverse* traverse) { - public: - Allocation_expression(Type* type, Location location) - : Expression(EXPRESSION_ALLOCATION, location), - type_(type) - { } - - protected: - int - do_traverse(Traverse* traverse) - { return Type::traverse(this->type_, traverse); } - - Type* - do_type() - { return Type::make_pointer_type(this->type_); } + return Type::traverse(this->type_, traverse); +} - void - do_determine_type(const Type_context*) - { } +Type* +Allocation_expression::do_type() +{ + return Type::make_pointer_type(this->type_); +} - Expression* - do_copy() - { return new Allocation_expression(this->type_, this->location()); } +// Make a copy of an allocation expression. - Bexpression* - do_get_backend(Translate_context*); +Expression* +Allocation_expression::do_copy() +{ + Allocation_expression* alloc = + new Allocation_expression(this->type_, this->location()); + if (this->allocate_on_stack_) + alloc->set_allocate_on_stack(); + return alloc; +} - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The type we are allocating. - Type* type_; -}; +Expression* +Allocation_expression::do_flatten(Gogo*, Named_object*, + Statement_inserter* inserter) +{ + if (this->allocate_on_stack_) + { + this->stack_temp_ = Statement::make_temporary(this->type_, NULL, + this->location()); + this->stack_temp_->set_is_address_taken(); + inserter->insert(this->stack_temp_); + } + return this; +} // Return the backend representation for an allocation expression. @@ -11638,6 +11449,15 @@ Allocation_expression::do_get_backend(Translate_context* context) { Gogo* gogo = context->gogo(); Location loc = this->location(); + + if (this->stack_temp_ != NULL) + { + Expression* ref = + Expression::make_temporary_reference(this->stack_temp_, loc); + ref = Expression::make_unary(OPERATOR_AND, ref, loc); + return ref->get_backend(context); + } + Bexpression* space = gogo->allocate_memory(this->type_, loc)->get_backend(context); Btype* pbtype = gogo->backend()->pointer_type(this->type_->get_backend(gogo)); @@ -11663,80 +11483,7 @@ Expression::make_allocation(Type* type, Location location) return new Allocation_expression(type, location); } -// Construct a struct. - -class Struct_construction_expression : public Expression -{ - public: - Struct_construction_expression(Type* type, Expression_list* vals, - Location location) - : Expression(EXPRESSION_STRUCT_CONSTRUCTION, location), - type_(type), vals_(vals), traverse_order_(NULL) - { } - - // Set the traversal order, used to ensure that we implement the - // order of evaluation rules. Takes ownership of the argument. - void - set_traverse_order(std::vector* traverse_order) - { this->traverse_order_ = traverse_order; } - - // Return whether this is a constant initializer. - bool - is_constant_struct() const; - - protected: - int - do_traverse(Traverse* traverse); - - bool - do_is_immutable() const; - - Type* - do_type() - { return this->type_; } - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { - Struct_construction_expression* ret = - new Struct_construction_expression(this->type_, - (this->vals_ == NULL - ? NULL - : this->vals_->copy()), - this->location()); - if (this->traverse_order_ != NULL) - ret->set_traverse_order(this->traverse_order_); - return ret; - } - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - Bexpression* - do_get_backend(Translate_context*); - - void - do_export(Export*) const; - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The type of the struct to construct. - Type* type_; - // The list of values, in order of the fields in the struct. A NULL - // entry means that the field should be zero-initialized. - Expression_list* vals_; - // If not NULL, the order in which to traverse vals_. This is used - // so that we implement the order of evaluation rules correctly. - std::vector* traverse_order_; -}; +// Class Struct_construction_expression. // Traversal. @@ -12003,80 +11750,7 @@ Expression::make_struct_composite_literal(Type* type, Expression_list* vals, return new Struct_construction_expression(type, vals, location); } -// Construct an array. This class is not used directly; instead we -// use the child classes, Fixed_array_construction_expression and -// Slice_construction_expression. - -class Array_construction_expression : public Expression -{ - protected: - Array_construction_expression(Expression_classification classification, - Type* type, - const std::vector* indexes, - Expression_list* vals, Location location) - : Expression(classification, location), - type_(type), indexes_(indexes), vals_(vals) - { go_assert(indexes == NULL || indexes->size() == vals->size()); } - - public: - // Return whether this is a constant initializer. - bool - is_constant_array() const; - - // Return the number of elements. - size_t - element_count() const - { return this->vals_ == NULL ? 0 : this->vals_->size(); } - -protected: - virtual int - do_traverse(Traverse* traverse); - - bool - do_is_immutable() const; - - Type* - do_type() - { return this->type_; } - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - void - do_export(Export*) const; - - // The indexes. - const std::vector* - indexes() - { return this->indexes_; } - - // The list of values. - Expression_list* - vals() - { return this->vals_; } - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - // Get the backend constructor for the array values. - Bexpression* - get_constructor(Translate_context* context, Btype* btype); - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The type of the array to construct. - Type* type_; - // The list of indexes into the array, one for each value. This may - // be NULL, in which case the indexes start at zero and increment. - const std::vector* indexes_; - // The list of values. This may be NULL if there are no values. - Expression_list* vals_; -}; +// Class Array_construction_expression. // Traversal. @@ -12332,34 +12006,14 @@ Array_construction_expression::do_dump_expression( } -// Construct a fixed array. - -class Fixed_array_construction_expression : - public Array_construction_expression -{ - public: - Fixed_array_construction_expression(Type* type, - const std::vector* indexes, - Expression_list* vals, Location location) - : Array_construction_expression(EXPRESSION_FIXED_ARRAY_CONSTRUCTION, - type, indexes, vals, location) - { go_assert(type->array_type() != NULL && !type->is_slice_type()); } - - protected: - Expression* - do_copy() - { - return new Fixed_array_construction_expression(this->type(), - this->indexes(), - (this->vals() == NULL - ? NULL - : this->vals()->copy()), - this->location()); - } +// Class Fixed_array_construction_expression. - Bexpression* - do_get_backend(Translate_context*); -}; +Fixed_array_construction_expression::Fixed_array_construction_expression( + Type* type, const std::vector* indexes, + Expression_list* vals, Location location) + : Array_construction_expression(EXPRESSION_FIXED_ARRAY_CONSTRUCTION, + type, indexes, vals, location) +{ go_assert(type->array_type() != NULL && !type->is_slice_type()); } // Return the backend representation for constructing a fixed array. @@ -12379,60 +12033,34 @@ Expression::make_array_composite_literal(Type* type, Expression_list* vals, return new Fixed_array_construction_expression(type, NULL, vals, location); } -// Construct a slice. +// Class Slice_construction_expression. -class Slice_construction_expression : public Array_construction_expression +Slice_construction_expression::Slice_construction_expression( + Type* type, const std::vector* indexes, + Expression_list* vals, Location location) + : Array_construction_expression(EXPRESSION_SLICE_CONSTRUCTION, + type, indexes, vals, location), + valtype_(NULL) { - public: - Slice_construction_expression(Type* type, - const std::vector* indexes, - Expression_list* vals, Location location) - : Array_construction_expression(EXPRESSION_SLICE_CONSTRUCTION, - type, indexes, vals, location), - valtype_(NULL) - { - go_assert(type->is_slice_type()); - - unsigned long lenval; - Expression* length; - if (vals == NULL || vals->empty()) - lenval = 0; - else - { - if (this->indexes() == NULL) - lenval = vals->size(); - else - lenval = indexes->back() + 1; - } - Type* int_type = Type::lookup_integer_type("int"); - length = Expression::make_integer_ul(lenval, int_type, location); - Type* element_type = type->array_type()->element_type(); - this->valtype_ = Type::make_array_type(element_type, length); - } - - protected: - // Note that taking the address of a slice literal is invalid. - - int - do_traverse(Traverse* traverse); + go_assert(type->is_slice_type()); - Expression* - do_copy() - { - return new Slice_construction_expression(this->type(), this->indexes(), - (this->vals() == NULL - ? NULL - : this->vals()->copy()), - this->location()); - } + unsigned long lenval; + Expression* length; + if (vals == NULL || vals->empty()) + lenval = 0; + else + { + if (this->indexes() == NULL) + lenval = vals->size(); + else + lenval = indexes->back() + 1; + } + Type* int_type = Type::lookup_integer_type("int"); + length = Expression::make_integer_ul(lenval, int_type, location); + Type* element_type = type->array_type()->element_type(); + this->valtype_ = Type::make_array_type(element_type, length); +} - Bexpression* - do_get_backend(Translate_context*); - - private: - // The type of the values in this slice. - Type* valtype_; -}; // Traversal. @@ -12514,63 +12142,7 @@ Expression::make_slice_composite_literal(Type* type, Expression_list* vals, return new Slice_construction_expression(type, NULL, vals, location); } -// Construct a map. - -class Map_construction_expression : public Expression -{ - public: - Map_construction_expression(Type* type, Expression_list* vals, - Location location) - : Expression(EXPRESSION_MAP_CONSTRUCTION, location), - type_(type), vals_(vals), element_type_(NULL), constructor_temp_(NULL) - { go_assert(vals == NULL || vals->size() % 2 == 0); } - - protected: - int - do_traverse(Traverse* traverse); - - Expression* - do_flatten(Gogo*, Named_object*, Statement_inserter*); - - Type* - do_type() - { return this->type_; } - - void - do_determine_type(const Type_context*); - - void - do_check_types(Gogo*); - - Expression* - do_copy() - { - return new Map_construction_expression(this->type_, - (this->vals_ == NULL - ? NULL - : this->vals_->copy()), - this->location()); - } - - Bexpression* - do_get_backend(Translate_context*); - - void - do_export(Export*) const; - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The type of the map to construct. - Type* type_; - // The list of values. - Expression_list* vals_; - // The type of the key-value pair struct for each map element. - Struct_type* element_type_; - // A temporary reference to the variable storing the constructor initializer. - Temporary_statement* constructor_temp_; -}; +// Class Map_construction_expression. // Traversal. @@ -13680,53 +13252,11 @@ Expression::make_type_guard(Expression* expr, Type* type, // Class Heap_expression. -// When you take the address of an escaping expression, it is allocated -// on the heap. This class implements that. +// Return the type of the expression stored on the heap. -class Heap_expression : public Expression -{ - public: - Heap_expression(Expression* expr, Location location) - : Expression(EXPRESSION_HEAP, location), - expr_(expr) - { } - - protected: - int - do_traverse(Traverse* traverse) - { return Expression::traverse(&this->expr_, traverse); } - - Type* - do_type() - { return Type::make_pointer_type(this->expr_->type()); } - - void - do_determine_type(const Type_context*) - { this->expr_->determine_type_no_context(); } - - Expression* - do_copy() - { - return Expression::make_heap_expression(this->expr_->copy(), - this->location()); - } - - Bexpression* - do_get_backend(Translate_context*); - - // We only export global objects, and the parser does not generate - // this in global scope. - void - do_export(Export*) const - { go_unreachable(); } - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The expression which is being put on the heap. - Expression* expr_; -}; +Type* +Heap_expression::do_type() +{ return Type::make_pointer_type(this->expr_->type()); } // Return the backend representation for allocating an expression on the heap. @@ -14964,48 +14494,7 @@ Expression::make_label_addr(Label* label, Location location) return new Label_addr_expression(label, location); } -// Conditional expressions. - -class Conditional_expression : public Expression -{ - public: - Conditional_expression(Expression* cond, Expression* then_expr, - Expression* else_expr, Location location) - : Expression(EXPRESSION_CONDITIONAL, location), - cond_(cond), then_(then_expr), else_(else_expr) - {} - - protected: - int - do_traverse(Traverse*); - - Type* - do_type(); - - void - do_determine_type(const Type_context*); - - Expression* - do_copy() - { - return new Conditional_expression(this->cond_->copy(), this->then_->copy(), - this->else_->copy(), this->location()); - } - - Bexpression* - do_get_backend(Translate_context* context); - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The condition to be checked. - Expression* cond_; - // The expression to execute if the condition is true. - Expression* then_; - // The expression to execute if the condition is false. - Expression* else_; -}; +// Class Conditional_expression. // Traversal. @@ -15084,44 +14573,7 @@ Expression::make_conditional(Expression* cond, Expression* then, return new Conditional_expression(cond, then, else_expr, location); } -// Compound expressions. - -class Compound_expression : public Expression -{ - public: - Compound_expression(Expression* init, Expression* expr, Location location) - : Expression(EXPRESSION_COMPOUND, location), init_(init), expr_(expr) - {} - - protected: - int - do_traverse(Traverse*); - - Type* - do_type(); - - void - do_determine_type(const Type_context*); - - Expression* - do_copy() - { - return new Compound_expression(this->init_->copy(), this->expr_->copy(), - this->location()); - } - - Bexpression* - do_get_backend(Translate_context* context); - - void - do_dump_expression(Ast_dump_context*) const; - - private: - // The expression that is evaluated first and discarded. - Expression* init_; - // The expression that is evaluated and returned. - Expression* expr_; -}; +// Class Compound_expression. // Traversal. diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index fbccdf04c1c..0d7ad5ae8fe 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -31,19 +31,31 @@ class Var_expression; class Temporary_reference_expression; class Set_and_use_temporary_expression; class String_expression; +class Type_conversion_expression; class Unary_expression; class Binary_expression; class Call_expression; +class Call_result_expression; class Func_expression; class Func_descriptor_expression; class Unknown_expression; class Index_expression; +class Array_index_expression; class Map_index_expression; class Bound_method_expression; class Field_reference_expression; class Interface_field_reference_expression; +class Allocation_expression; +class Struct_construction_expression; +class Array_construction_expression; +class Fixed_array_construction_expression; +class Slice_construction_expression; +class Map_construction_expression; class Type_guard_expression; +class Heap_expression; class Receive_expression; +class Conditional_expression; +class Compound_expression; class Numeric_constant; class Named_object; class Export; @@ -553,6 +565,12 @@ class Expression string_expression() { return this->convert(); } + // If this is a conversion expression, return the Type_conversion_expression + // structure. Otherwise, return NULL. + Type_conversion_expression* + conversion_expression() + { return this->convert(); } + // Return whether this is the expression nil. bool is_nil_expression() const @@ -582,6 +600,13 @@ class Expression call_expression() { return this->convert(); } + // If this is a call_result expression, return the Call_result_expression + // structure. Otherwise, return NULL. This is a controlled dynamic + // cast. + Call_result_expression* + call_result_expression() + { return this->convert(); } + // If this is an expression which refers to a function, return the // Func_expression structure. Otherwise, return NULL. Func_expression* @@ -611,6 +636,13 @@ class Expression index_expression() { return this->convert(); } + // If this is an expression which refers to indexing in a array, + // return the Array_index_expression structure. Otherwise, return + // NULL. + Array_index_expression* + array_index_expression() + { return this->convert(); } + // If this is an expression which refers to indexing in a map, // return the Map_index_expression structure. Otherwise, return // NULL. @@ -643,18 +675,78 @@ class Expression EXPRESSION_INTERFACE_FIELD_REFERENCE>(); } + // If this is an allocation expression, return the Allocation_expression + // structure. Otherwise, return NULL. + Allocation_expression* + allocation_expression() + { return this->convert(); } + + // If this is a struct composite literal, return the + // Struct_construction_expression structure. Otherwise, return NULL. + Struct_construction_expression* + struct_literal() + { + return this->convert(); + } + + // If this is a array composite literal, return the + // Array_construction_expression structure. Otherwise, return NULL. + Fixed_array_construction_expression* + array_literal() + { + return this->convert(); + } + + // If this is a slice composite literal, return the + // Slice_construction_expression structure. Otherwise, return NULL. + Slice_construction_expression* + slice_literal() + { + return this->convert(); + } + + // If this is a map composite literal, return the + // Map_construction_expression structure. Otherwise, return NULL. + Map_construction_expression* + map_literal() + { + return this->convert(); + } + // If this is a type guard expression, return the // Type_guard_expression structure. Otherwise, return NULL. Type_guard_expression* type_guard_expression() { return this->convert(); } + // If this is a heap expression, returhn the Heap_expression structure. + // Otherwise, return NULL. + Heap_expression* + heap_expression() + { return this->convert(); } + // If this is a receive expression, return the Receive_expression // structure. Otherwise, return NULL. Receive_expression* receive_expression() { return this->convert(); } + // If this is a conditional expression, return the Conditional_expression + // structure. Otherwise, return NULL. + Conditional_expression* + conditional_expression() + { return this->convert(); } + + // If this is a compound expression, return the Compound_expression structure. + // Otherwise, return NULL. + Compound_expression* + compound_expression() + { return this->convert(); } + // Return true if this is a composite literal. bool is_composite_literal() const; @@ -1322,6 +1414,97 @@ class String_expression : public Expression Type* type_; }; +// A type conversion expression. + +class Type_conversion_expression : public Expression +{ + public: + Type_conversion_expression(Type* type, Expression* expr, + Location location) + : Expression(EXPRESSION_CONVERSION, location), + type_(type), expr_(expr), may_convert_function_types_(false) + { } + + // Return the type to which we are converting. + Type* + type() const + { return this->type_; } + + // Return the expression which we are converting. + Expression* + expr() const + { return this->expr_; } + + // Permit converting from one function type to another. This is + // used internally for method expressions. + void + set_may_convert_function_types() + { + this->may_convert_function_types_ = true; + } + + // Import a type conversion expression. + static Expression* + do_import(Import*); + + protected: + int + do_traverse(Traverse* traverse); + + Expression* + do_lower(Gogo*, Named_object*, Statement_inserter*, int); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + bool + do_is_constant() const; + + bool + do_is_immutable() const; + + bool + do_numeric_constant_value(Numeric_constant*) const; + + bool + do_string_constant_value(std::string*) const; + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Type_conversion_expression(this->type_, this->expr_->copy(), + this->location()); + } + + Bexpression* + do_get_backend(Translate_context* context); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type to convert to. + Type* type_; + // The expression to convert. + Expression* expr_; + // True if this is permitted to convert function types. This is + // used internally for method expressions. + bool may_convert_function_types_; +}; + // A Unary expression. class Unary_expression : public Expression @@ -1827,6 +2010,57 @@ class Call_expression : public Expression bool is_flattened_; }; +// A single result from a call which returns multiple results. + +class Call_result_expression : public Expression +{ + public: + Call_result_expression(Call_expression* call, unsigned int index) + : Expression(EXPRESSION_CALL_RESULT, call->location()), + call_(call), index_(index) + { } + + Expression* + call() const + { return this->call_; } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Call_result_expression(this->call_->call_expression(), + this->index_); + } + + bool + do_must_eval_in_order() const + { return true; } + + Bexpression* + do_get_backend(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The underlying call expression. + Expression* call_; + // Which result we want. + unsigned int index_; +}; + // An expression which represents a pointer to a function. class Func_expression : public Expression @@ -2074,6 +2308,95 @@ class Index_expression : public Parser_expression bool is_lvalue_; }; +// An array index. This is used for both indexing and slicing. + +class Array_index_expression : public Expression +{ + public: + Array_index_expression(Expression* array, Expression* start, + Expression* end, Expression* cap, Location location) + : Expression(EXPRESSION_ARRAY_INDEX, location), + array_(array), start_(start), end_(end), cap_(cap), type_(NULL) + { } + + // Return the array. + Expression* + array() + { return this->array_; } + + const Expression* + array() const + { return this->array_; } + + protected: + int + do_traverse(Traverse*); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return Expression::make_array_index(this->array_->copy(), + this->start_->copy(), + (this->end_ == NULL + ? NULL + : this->end_->copy()), + (this->cap_ == NULL + ? NULL + : this->cap_->copy()), + this->location()); + } + + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + + bool + do_is_addressable() const; + + void + do_address_taken(bool escapes) + { this->array_->address_taken(escapes); } + + void + do_issue_nil_check() + { this->array_->issue_nil_check(); } + + Bexpression* + do_get_backend(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The array we are getting a value from. + Expression* array_; + // The start or only index. + Expression* start_; + // The end index of a slice. This may be NULL for a simple array + // index, or it may be a nil expression for the length of the array. + Expression* end_; + // The capacity argument of a slice. This may be NULL for an array index or + // slice. + Expression* cap_; + // The type of the expression. + Type* type_; +}; + // An index into a map. class Map_index_expression : public Expression @@ -2456,6 +2779,327 @@ class Interface_field_reference_expression : public Expression std::string name_; }; +// Implement the builtin function new. + +class Allocation_expression : public Expression +{ + public: + Allocation_expression(Type* type, Location location) + : Expression(EXPRESSION_ALLOCATION, location), + type_(type), allocate_on_stack_(false), stack_temp_(NULL) + { } + + void + set_allocate_on_stack() + { this->allocate_on_stack_ = true; } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*) + { } + + Expression* + do_copy(); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Bexpression* + do_get_backend(Translate_context*); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type we are allocating. + Type* type_; + // Whether or not this is a stack allocation. + bool allocate_on_stack_; + // If this memory is stack allocated, it will use the address of STACK_TEMP. + // Otherwise, STACK_TEMP is NULL. + Temporary_statement* stack_temp_; +}; + +// Construct a struct. + +class Struct_construction_expression : public Expression +{ + public: + Struct_construction_expression(Type* type, Expression_list* vals, + Location location) + : Expression(EXPRESSION_STRUCT_CONSTRUCTION, location), + type_(type), vals_(vals), traverse_order_(NULL) + { } + + // Set the traversal order, used to ensure that we implement the + // order of evaluation rules. Takes ownership of the argument. + void + set_traverse_order(std::vector* traverse_order) + { this->traverse_order_ = traverse_order; } + + // Return whether this is a constant initializer. + bool + is_constant_struct() const; + + Expression_list* + vals() const + { return this->vals_; } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_is_immutable() const; + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + Struct_construction_expression* ret = + new Struct_construction_expression(this->type_, + (this->vals_ == NULL + ? NULL + : this->vals_->copy()), + this->location()); + if (this->traverse_order_ != NULL) + ret->set_traverse_order(this->traverse_order_); + return ret; + } + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Bexpression* + do_get_backend(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the struct to construct. + Type* type_; + // The list of values, in order of the fields in the struct. A NULL + // entry means that the field should be zero-initialized. + Expression_list* vals_; + // If not NULL, the order in which to traverse vals_. This is used + // so that we implement the order of evaluation rules correctly. + std::vector* traverse_order_; +}; + +// Construct an array. This class is not used directly; instead we +// use the child classes, Fixed_array_construction_expression and +// Slice_construction_expression. + +class Array_construction_expression : public Expression +{ + protected: + Array_construction_expression(Expression_classification classification, + Type* type, + const std::vector* indexes, + Expression_list* vals, Location location) + : Expression(classification, location), + type_(type), indexes_(indexes), vals_(vals) + { go_assert(indexes == NULL || indexes->size() == vals->size()); } + + public: + // Return whether this is a constant initializer. + bool + is_constant_array() const; + + // Return the number of elements. + size_t + element_count() const + { return this->vals_ == NULL ? 0 : this->vals_->size(); } + + // The list of values. + Expression_list* + vals() const + { return this->vals_; } + +protected: + virtual int + do_traverse(Traverse* traverse); + + bool + do_is_immutable() const; + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + void + do_export(Export*) const; + + // The indexes. + const std::vector* + indexes() + { return this->indexes_; } + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + // Get the backend constructor for the array values. + Bexpression* + get_constructor(Translate_context* context, Btype* btype); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the array to construct. + Type* type_; + // The list of indexes into the array, one for each value. This may + // be NULL, in which case the indexes start at zero and increment. + const std::vector* indexes_; + // The list of values. This may be NULL if there are no values. + Expression_list* vals_; +}; + +// Construct a fixed array. + +class Fixed_array_construction_expression : + public Array_construction_expression +{ + public: + Fixed_array_construction_expression(Type* type, + const std::vector* indexes, + Expression_list* vals, Location location); + + protected: + Expression* + do_copy() + { + return new Fixed_array_construction_expression(this->type(), + this->indexes(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); + } + + Bexpression* + do_get_backend(Translate_context*); +}; + +// Construct a slice. + +class Slice_construction_expression : public Array_construction_expression +{ + public: + Slice_construction_expression(Type* type, + const std::vector* indexes, + Expression_list* vals, Location location); + protected: + // Note that taking the address of a slice literal is invalid. + + int + do_traverse(Traverse* traverse); + + Expression* + do_copy() + { + return new Slice_construction_expression(this->type(), this->indexes(), + (this->vals() == NULL + ? NULL + : this->vals()->copy()), + this->location()); + } + + Bexpression* + do_get_backend(Translate_context*); + + private: + // The type of the values in this slice. + Type* valtype_; +}; + +// Construct a map. + +class Map_construction_expression : public Expression +{ + public: + Map_construction_expression(Type* type, Expression_list* vals, + Location location) + : Expression(EXPRESSION_MAP_CONSTRUCTION, location), + type_(type), vals_(vals), element_type_(NULL), constructor_temp_(NULL) + { go_assert(vals == NULL || vals->size() % 2 == 0); } + + Expression_list* + vals() const + { return this->vals_; } + + protected: + int + do_traverse(Traverse* traverse); + + Expression* + do_flatten(Gogo*, Named_object*, Statement_inserter*); + + Type* + do_type() + { return this->type_; } + + void + do_determine_type(const Type_context*); + + void + do_check_types(Gogo*); + + Expression* + do_copy() + { + return new Map_construction_expression(this->type_, + (this->vals_ == NULL + ? NULL + : this->vals_->copy()), + this->location()); + } + + Bexpression* + do_get_backend(Translate_context*); + + void + do_export(Export*) const; + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The type of the map to construct. + Type* type_; + // The list of values. + Expression_list* vals_; + // The type of the key-value pair struct for each map element. + Struct_type* element_type_; + // A temporary reference to the variable storing the constructor initializer. + Temporary_statement* constructor_temp_; +}; + // A type guard expression. class Type_guard_expression : public Expression @@ -2514,6 +3158,58 @@ class Type_guard_expression : public Expression Type* type_; }; +// Class Heap_expression. + +// When you take the address of an escaping expression, it is allocated +// on the heap. This class implements that. + +class Heap_expression : public Expression +{ + public: + Heap_expression(Expression* expr, Location location) + : Expression(EXPRESSION_HEAP, location), + expr_(expr) + { } + + Expression* + expr() const + { return this->expr_; } + + protected: + int + do_traverse(Traverse* traverse) + { return Expression::traverse(&this->expr_, traverse); } + + Type* + do_type(); + void + do_determine_type(const Type_context*) + { this->expr_->determine_type_no_context(); } + + Expression* + do_copy() + { + return Expression::make_heap_expression(this->expr_->copy(), + this->location()); + } + + Bexpression* + do_get_backend(Translate_context*); + + // We only export global objects, and the parser does not generate + // this in global scope. + void + do_export(Export*) const + { go_unreachable(); } + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The expression which is being put on the heap. + Expression* expr_; +}; + // A receive expression. class Receive_expression : public Expression @@ -2574,6 +3270,96 @@ class Receive_expression : public Expression Temporary_statement* temp_receiver_; }; +// Conditional expressions. + +class Conditional_expression : public Expression +{ + public: + Conditional_expression(Expression* cond, Expression* then_expr, + Expression* else_expr, Location location) + : Expression(EXPRESSION_CONDITIONAL, location), + cond_(cond), then_(then_expr), else_(else_expr) + {} + + Expression* + condition() const + { return this->cond_; } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { + return new Conditional_expression(this->cond_->copy(), this->then_->copy(), + this->else_->copy(), this->location()); + } + + Bexpression* + do_get_backend(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The condition to be checked. + Expression* cond_; + // The expression to execute if the condition is true. + Expression* then_; + // The expression to execute if the condition is false. + Expression* else_; +}; + +// Compound expressions. + +class Compound_expression : public Expression +{ + public: + Compound_expression(Expression* init, Expression* expr, Location location) + : Expression(EXPRESSION_COMPOUND, location), init_(init), expr_(expr) + {} + + Expression* + init() const + { return this->init_; } + + protected: + int + do_traverse(Traverse*); + + Type* + do_type(); + + void + do_determine_type(const Type_context*); + + Expression* + do_copy() + { + return new Compound_expression(this->init_->copy(), this->expr_->copy(), + this->location()); + } + + Bexpression* + do_get_backend(Translate_context* context); + + void + do_dump_expression(Ast_dump_context*) const; + + private: + // The expression that is evaluated first and discarded. + Expression* init_; + // The expression that is evaluated and returned. + Expression* expr_; +}; + // A numeric constant. This is used both for untyped constants and // for constants that have a type. diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index cd30cca2657..98cf6501af6 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -110,6 +110,10 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, if (only_check_syntax) return; + // Consider escape analysis information when deciding if a variable should + // live on the heap or on the stack. + ::gogo->optimize_allocations(filenames); + // Export global identifiers as appropriate. ::gogo->do_exports(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index a42803dd830..d7a3651fa32 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -10,14 +10,15 @@ #include "go-c.h" #include "go-dump.h" +#include "go-optimize.h" #include "lex.h" #include "types.h" #include "statements.h" #include "expressions.h" -#include "dataflow.h" #include "runtime.h" #include "import.h" #include "export.h" +#include "escape.h" #include "backend.h" #include "gogo.h" @@ -158,11 +159,19 @@ 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(); @@ -170,6 +179,10 @@ 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(); @@ -177,16 +190,26 @@ 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); @@ -195,6 +218,10 @@ 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(); @@ -208,6 +235,10 @@ 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(); @@ -216,31 +247,56 @@ 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); } @@ -1604,9 +1660,9 @@ Gogo::start_function(const std::string& name, Function_type* type, Block* block = new Block(NULL, location); - Function* enclosing = (at_top_level + Named_object* enclosing = (at_top_level ? NULL - : this->functions_.back().function->func_value()); + : this->functions_.back().function); Function* function = new Function(type, enclosing, block, location); @@ -1904,6 +1960,74 @@ 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* @@ -4455,7 +4579,7 @@ Gogo::convert_named_types_in_bindings(Bindings* bindings) // Class Function. -Function::Function(Function_type* type, Function* enclosing, Block* block, +Function::Function(Function_type* type, Named_object* enclosing, Block* block, Location location) : type_(type), enclosing_(enclosing), results_(NULL), closure_var_(NULL), block_(block), location_(location), labels_(), @@ -4809,6 +4933,13 @@ 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(") "); @@ -4820,17 +4951,25 @@ Function::export_func_with_type(Export* exp, const std::string& name, const Typed_identifier_list* parameters = fntype->parameters(); if (parameters != NULL) { + size_t i = 0; bool is_varargs = fntype->is_varargs(); bool first = true; for (Typed_identifier_list::const_iterator p = parameters->begin(); p != parameters->end(); - ++p) + ++p, ++i) { if (first) first = false; 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()); @@ -4878,17 +5017,29 @@ 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* is_varargs, bool* has_escape_info) { + *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()); @@ -4898,16 +5049,27 @@ 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; + { + parameters = NULL; + param_escapes = 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("...")) @@ -4929,6 +5091,7 @@ 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() != ' ') @@ -5765,7 +5928,7 @@ Variable::Variable(Type* type, Expression* init, bool is_global, type_from_init_tuple_(false), type_from_range_index_(false), type_from_range_value_(false), type_from_chan_element_(false), is_type_switch_var_(false), determined_type_(false), - in_unique_section_(false) + in_unique_section_(false), escapes_(true) { go_assert(type != NULL || init != NULL); go_assert(!is_parameter || init == NULL); diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 6d082367928..e30178d6c23 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -7,6 +7,7 @@ #ifndef GO_GOGO_H #define GO_GOGO_H +#include "escape.h" #include "go-linemap.h" class Traverse; @@ -125,6 +126,21 @@ class Gogo linemap() { return this->linemap_; } + // Get the Call Graph. + const std::set& + 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; @@ -345,6 +361,22 @@ class Gogo add_label_reference(const std::string&, Location, bool issue_goto_errors); + // Add a FUNCTION to the call graph. + Node* + add_call_node(Named_object* function); + + // Lookup the call node for FUNCTION. + Node* + lookup_call_node(Named_object* function) const; + + // Add a connection node for OBJECT. + Node* + add_connection_node(Named_object* object); + + // Lookup the connection node for OBJECT. + Node* + lookup_connection_node(Named_object* object) const; + // Return a snapshot of the current binding state. Bindings_snapshot* bindings_snapshot(Location); @@ -544,6 +576,26 @@ class Gogo void check_return_statements(); + // Build call graph. + void + build_call_graph(); + + // Build connection graphs. + void + build_connection_graphs(); + + // Analyze reachability in the connection graphs. + void + analyze_reachability(); + + // Record escape information in function signatures for export data. + void + mark_escaping_signatures(); + + // Optimize variable allocation. + void + optimize_allocations(const char** filenames); + // Do all exports. void do_exports(); @@ -579,6 +631,14 @@ class Gogo void dump_ast(const char* basename); + // Dump Call Graph if -fgo-dump-calls is set. + void + dump_call_graph(const char* basename); + + // Dump Connection Graphs if -fgo-dump-connections is set. + void + dump_connection_graphs(const char* basename); + // Convert named types to the backend representation. void convert_named_types(); @@ -684,6 +744,10 @@ class Gogo // where they were defined. typedef Unordered_map(std::string, Location) File_block_names; + // Type used to map named objects that refer to objects to the + // node that represent them in the escape analysis graphs. + typedef Unordered_map(Named_object*, Node*) Named_escape_nodes; + // Type used to queue writing a type specific function. struct Specific_type_function { @@ -716,6 +780,20 @@ class Gogo // The global binding contour. This includes the builtin functions // and the package we are compiling. Bindings* globals_; + // The call graph for a program execution which represents the functions + // encountered and the caller-callee relationship between the functions. + std::set 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. @@ -886,7 +964,7 @@ class Block class Function { public: - Function(Function_type* type, Function*, Block*, Location); + Function(Function_type* type, Named_object*, Block*, Location); // Return the function's type. Function_type* @@ -894,14 +972,14 @@ class Function { return this->type_; } // Return the enclosing function if there is one. - Function* - enclosing() + Named_object* + enclosing() const { return this->enclosing_; } // Set the enclosing function. This is used when building thunks // for functions which call recover. void - set_enclosing(Function* enclosing) + set_enclosing(Named_object* enclosing) { go_assert(this->enclosing_ == NULL); this->enclosing_ = enclosing; @@ -1152,8 +1230,11 @@ class Function // Import a function. static void import_func(Import*, std::string* pname, Typed_identifier** receiver, + Node::Escapement_lattice* rcvr_escape, Typed_identifier_list** pparameters, - Typed_identifier_list** presults, bool* is_varargs); + Node::Escape_states** pparam_escapes, + Typed_identifier_list** presults, bool* is_varargs, + bool* has_escape_info); private: // Type for mapping from label names to Label objects. @@ -1169,7 +1250,7 @@ class Function Function_type* type_; // The enclosing function. This is NULL when there isn't one, which // is the normal case. - Function* enclosing_; + Named_object* enclosing_; // The result variables, if any. Results* results_; // If there is a closure, this is the list of variables which appear @@ -1414,7 +1495,11 @@ class Variable // Whether this variable should live in the heap. bool is_in_heap() const - { return this->is_address_taken_ && !this->is_global_; } + { + return this->is_address_taken_ + && this->escapes_ + && !this->is_global_; + } // Note that something takes the address of this variable. void @@ -1432,6 +1517,16 @@ class Variable set_non_escaping_address_taken() { this->is_non_escaping_address_taken_ = true; } + // Return whether this variable escapes the function it is declared in. + bool + escapes() + { return this->escapes_; } + + // Note that this variable does not escape the function it is declared in. + void + set_does_not_escape() + { this->escapes_ = false; } + // Get the source location of the variable's declaration. Location location() const @@ -1525,6 +1620,11 @@ class Variable this->type_from_chan_element_ = false; } + // TRUE if this variable was created for a type switch clause. + bool + is_type_switch_var() const + { return this->is_type_switch_var_; } + // Note that this variable was created for a type switch clause. void set_is_type_switch_var() @@ -1609,7 +1709,7 @@ class Variable bool is_used_ : 1; // Whether something takes the address of this variable. For a // local variable this implies that the variable has to be on the - // heap. + // heap if it escapes from its function. bool is_address_taken_ : 1; // Whether something takes the address of this variable such that // the address does not escape the function. @@ -1635,6 +1735,9 @@ class Variable // True if this variable should be put in a unique section. This is // used for field tracking. bool in_unique_section_ : 1; + // Whether this variable escapes the function it is created in. This is + // true until shown otherwise. + bool escapes_ : 1; }; // A variable which is really the name for a function return value, or @@ -1647,7 +1750,7 @@ class Result_variable Location location) : type_(type), function_(function), index_(index), location_(location), backend_(NULL), is_address_taken_(false), - is_non_escaping_address_taken_(false) + is_non_escaping_address_taken_(false), escapes_(true) { } // Get the type of the result variable. @@ -1690,11 +1793,24 @@ class Result_variable void set_non_escaping_address_taken() { this->is_non_escaping_address_taken_ = true; } + + // Return whether this variable escapes the function it is declared in. + bool + escapes() + { return this->escapes_; } + + // Note that this variable does not escape the function it is declared in. + void + set_does_not_escape() + { this->escapes_ = false; } // Whether this variable should live in the heap. bool is_in_heap() const - { return this->is_address_taken_; } + { + return this->is_address_taken_ + && this->escapes_; + } // Set the function. This is used when cloning functions which call // recover. @@ -1722,6 +1838,9 @@ class Result_variable // Whether something takes the address of this variable such that // the address does not escape the function. bool is_non_escaping_address_taken_; + // Whether this variable escapes the function it is created in. This is + // true until shown otherwise. + bool escapes_; }; // The value we keep for a named constant. This lets us hold a type diff --git a/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc index e1fba78818f..1bb6ad8acf6 100644 --- a/gcc/go/gofrontend/import.cc +++ b/gcc/go/gofrontend/import.cc @@ -502,16 +502,28 @@ Import::import_func(Package* package) { std::string name; Typed_identifier* receiver; + Node::Escapement_lattice rcvr_escape; Typed_identifier_list* parameters; + Node::Escape_states* param_escapes; Typed_identifier_list* results; bool is_varargs; - Function::import_func(this, &name, &receiver, ¶meters, &results, - &is_varargs); + bool has_escape_info; + Function::import_func(this, &name, &receiver, &rcvr_escape, ¶meters, + ¶m_escapes, &results, &is_varargs, + &has_escape_info); Function_type *fntype = Type::make_function_type(receiver, parameters, results, this->location_); if (is_varargs) fntype->set_is_varargs(); + if (has_escape_info) + { + if (fntype->is_method()) + fntype->set_receiver_escape_state(rcvr_escape); + fntype->set_parameter_escape_states(param_escapes); + fntype->set_has_escape_info(); + } + Location loc = this->location_; Named_object* no; if (fntype->is_method()) @@ -762,6 +774,19 @@ Import::read_type() return type; } +// Read escape info in the import stream. + +Node::Escapement_lattice +Import::read_escape_info() +{ + Stream* stream = this->stream_; + this->require_c_string("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 2a9ac80e7e2..7aa1fc9b623 100644 --- a/gcc/go/gofrontend/import.h +++ b/gcc/go/gofrontend/import.h @@ -7,6 +7,7 @@ #ifndef GO_IMPORT_H #define GO_IMPORT_H +#include "escape.h" #include "export.h" #include "go-linemap.h" @@ -197,6 +198,10 @@ class Import Type* read_type(); + // Read escape information. + Node::Escapement_lattice + read_escape_info(); + private: static Stream* try_package_in_directory(const std::string&, Location); diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index e1cc75e7e04..a44145da23e 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -14,6 +14,7 @@ #include "backend.h" #include "statements.h" #include "ast-dump.h" +#include "dataflow.h" // Class Statement. @@ -520,45 +521,7 @@ Statement::make_temporary(Type* type, Expression* init, return new Temporary_statement(type, init, location); } -// An assignment statement. - -class Assignment_statement : public Statement -{ - public: - Assignment_statement(Expression* lhs, Expression* rhs, - Location location) - : Statement(STATEMENT_ASSIGNMENT, location), - lhs_(lhs), rhs_(rhs) - { } - - protected: - int - do_traverse(Traverse* traverse); - - bool - do_traverse_assignments(Traverse_assignments*); - - void - do_determine_types(); - - void - do_check_types(Gogo*); - - Statement* - do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); - - Bstatement* - do_get_backend(Translate_context*); - - void - do_dump_statement(Ast_dump_context*) const; - - private: - // Left hand side--the lvalue. - Expression* lhs_; - // Right hand side--the rvalue. - Expression* rhs_; -}; +// Class Assignment_statement. // Traversal. @@ -3150,41 +3113,7 @@ Statement::make_unnamed_label_statement(Unnamed_label* label) return new Unnamed_label_statement(label); } -// An if statement. - -class If_statement : public Statement -{ - public: - If_statement(Expression* cond, Block* then_block, Block* else_block, - Location location) - : Statement(STATEMENT_IF, location), - cond_(cond), then_block_(then_block), else_block_(else_block) - { } - - protected: - int - do_traverse(Traverse*); - - void - do_determine_types(); - - void - do_check_types(Gogo*); - - bool - do_may_fall_through() const; - - Bstatement* - do_get_backend(Translate_context*); - - void - do_dump_statement(Ast_dump_context*) const; - - private: - Expression* cond_; - Block* then_block_; - Block* else_block_; -}; +// Class If_statement. // Traversal. @@ -4676,7 +4605,6 @@ Select_clauses::Select_clause::lower(Gogo* gogo, Named_object* function, // through here. this->is_lowered_ = true; this->val_ = NULL; - this->var_ = NULL; } // Lower a default clause in a select statement. @@ -4840,6 +4768,22 @@ Select_clauses::Select_clause::check_types() error_at(this->location(), "invalid receive on send-only channel"); } +// Analyze the dataflow across each case statement. + +void +Select_clauses::Select_clause::analyze_dataflow(Dataflow* dataflow) +{ + if (this->is_default_) + return; + + // For a CommClause, the dataflow analysis should record a definition of + // VAR and CLOSEDVAR + if (this->var_ != NULL && !this->var_->is_sink()) + dataflow->add_def(this->var_, this->channel_, NULL, false); + if (this->closedvar_ != NULL && !this->closedvar_->is_sink()) + dataflow->add_def(this->closedvar_, this->channel_, NULL, false); +} + // Whether this clause may fall through to the statement which follows // the overall select statement. @@ -4958,6 +4902,17 @@ Select_clauses::check_types() p->check_types(); } +// Analyze the dataflow across each case statement. + +void +Select_clauses::analyze_dataflow(Dataflow* dataflow) +{ + for (Clauses::iterator p = this->clauses_.begin(); + p != this->clauses_.end(); + ++p) + p->analyze_dataflow(dataflow); +} + // Return whether these select clauses fall through to the statement // following the overall select statement. diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h index 8b8b99b4cfd..cf847d04d17 100644 --- a/gcc/go/gofrontend/statements.h +++ b/gcc/go/gofrontend/statements.h @@ -15,12 +15,14 @@ class Statement_inserter; class Block; class Function; class Unnamed_label; +class Assignment_statement; class Temporary_statement; class Variable_declaration_statement; class Expression_statement; class Return_statement; class Thunk_statement; class Label_statement; +class If_statement; class For_statement; class For_range_statement; class Switch_statement; @@ -45,6 +47,7 @@ class Bexpression; class Bstatement; class Bvariable; class Ast_dump_context; +class Dataflow; // This class is used to traverse assignments made by a statement // which makes assignments. @@ -331,6 +334,22 @@ class Statement is_block_statement() const { return this->classification_ == STATEMENT_BLOCK; } + // If this is an assignment statement, return it. Otherwise return + // NULL. + Assignment_statement* + assignment_statement() + { + return this->convert(); + } + + // If this is an temporary statement, return it. Otherwise return + // NULL. + Temporary_statement* + temporary_statement() + { + return this->convert(); + } + // If this is a variable declaration statement, return it. // Otherwise return NULL. Variable_declaration_statement* @@ -363,6 +382,11 @@ class Statement label_statement() { return this->convert(); } + // If this is an if statement, return it. Otherwise return NULL. + If_statement* + if_statement() + { return this->convert(); } + // If this is a for statement, return it. Otherwise return NULL. For_statement* for_statement() @@ -385,6 +409,11 @@ class Statement type_switch_statement() { return this->convert(); } + // If this is a send statement, return it. Otherwise return NULL. + Send_statement* + send_statement() + { return this->convert(); } + // If this is a select statement, return it. Otherwise return NULL. Select_statement* select_statement() @@ -507,6 +536,54 @@ class Statement Location location_; }; +// An assignment statement. + +class Assignment_statement : public Statement +{ + public: + Assignment_statement(Expression* lhs, Expression* rhs, + Location location) + : Statement(STATEMENT_ASSIGNMENT, location), + lhs_(lhs), rhs_(rhs) + { } + + Expression* + lhs() const + { return this->lhs_; } + + Expression* + rhs() const + { return this->rhs_; } + + protected: + int + do_traverse(Traverse* traverse); + + bool + do_traverse_assignments(Traverse_assignments*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + Statement* + do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*); + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + // Left hand side--the lvalue. + Expression* lhs_; + // Right hand side--the rvalue. + Expression* rhs_; +}; + // A statement which creates and initializes a temporary variable. class Temporary_statement : public Statement @@ -697,6 +774,14 @@ class Send_statement : public Statement channel_(channel), val_(val) { } + Expression* + channel() + { return this->channel_; } + + Expression* + val() + { return this->val_; } + protected: int do_traverse(Traverse* traverse); @@ -775,6 +860,10 @@ class Select_clauses void check_types(); + // Analyze the dataflow across each case statement. + void + analyze_dataflow(Dataflow*); + // Whether the select clauses may fall through to the statement // which follows the overall select statement. bool @@ -831,6 +920,10 @@ class Select_clauses void check_types(); + // Analyze the dataflow across each case statement. + void + analyze_dataflow(Dataflow*); + // Return true if this is the default clause. bool is_default() const @@ -937,6 +1030,10 @@ class Select_statement : public Statement Unnamed_label* break_label(); + void + analyze_dataflow(Dataflow* dataflow) + { this->clauses_->analyze_dataflow(dataflow); } + protected: int do_traverse(Traverse* traverse) @@ -1108,6 +1205,46 @@ class Label_statement : public Statement Label* label_; }; +// An if statement. + +class If_statement : public Statement +{ + public: + If_statement(Expression* cond, Block* then_block, Block* else_block, + Location location) + : Statement(STATEMENT_IF, location), + cond_(cond), then_block_(then_block), else_block_(else_block) + { } + + Expression* + condition() const + { return this->cond_; } + + protected: + int + do_traverse(Traverse*); + + void + do_determine_types(); + + void + do_check_types(Gogo*); + + bool + do_may_fall_through() const; + + Bstatement* + do_get_backend(Translate_context*); + + void + do_dump_statement(Ast_dump_context*) const; + + private: + Expression* cond_; + Block* then_block_; + Block* else_block_; +}; + // A for statement. class For_statement : public Statement diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 82d50812228..042548f65f9 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -8,6 +8,7 @@ #define GO_TYPES_H #include "go-linemap.h" +#include "escape.h" class Gogo; class Package; @@ -1778,7 +1779,7 @@ class Function_type : public Type : Type(TYPE_FUNCTION), receiver_(receiver), parameters_(parameters), results_(results), location_(location), is_varargs_(false), is_builtin_(false), - fnbtype_(NULL) + has_escape_info_(false), fnbtype_(NULL), parameter_escape_states_(NULL) { } // Get the receiver. @@ -1786,6 +1787,16 @@ class Function_type : public Type receiver() const { return this->receiver_; } + // Get the escape state of the receiver. + const Node::Escapement_lattice& + receiver_escape_state() const + { return this->receiver_escape_state_; } + + // Set the escape state of the receiver. + void + set_receiver_escape_state(const Node::Escapement_lattice& e) + { this->receiver_escape_state_ = e; } + // Get the return names and types. const Typed_identifier_list* results() const @@ -1796,6 +1807,16 @@ class Function_type : public Type parameters() const { return this->parameters_; } + // Get the escape states associated with each parameter. + const Node::Escape_states* + parameter_escape_states() const + { return this->parameter_escape_states_; } + + // Set the escape states of the parameters. + void + set_parameter_escape_states(Node::Escape_states* states) + { this->parameter_escape_states_ = states; } + // Whether this is a varargs function. bool is_varargs() const @@ -1806,6 +1827,11 @@ class Function_type : public Type is_builtin() const { return this->is_builtin_; } + // Whether this contains escape information. + bool + has_escape_info() const + { return this->has_escape_info_; } + // The location where this type was defined. Location location() const @@ -1836,6 +1862,11 @@ class Function_type : public Type set_is_builtin() { this->is_builtin_ = true; } + // Record that this has escape information. + void + set_has_escape_info() + { this->has_escape_info_ = true; } + // Import a function type. static Function_type* do_import(Import*); @@ -1947,9 +1978,16 @@ class Function_type : public Type // Whether this is a special builtin function which can not simply // be called. This is used for len, cap, etc. bool is_builtin_; + // Whether escape information for the receiver and parameters has been + // recorded. + bool has_escape_info_; // The backend representation of this type for backend function // declarations and definitions. Btype* fnbtype_; + // The escape state of the receiver. + Node::Escapement_lattice receiver_escape_state_; + // The escape states of each parameter. + Node::Escape_states* parameter_escape_states_; }; // The type of a function's backend representation.