From 1dea42c94bf801399e23d7e952fa058f3541de4c Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 29 Apr 2015 22:40:07 +0000 Subject: [PATCH] compiler: Consider multi-result calls in escape analysis. When building connection graphs between objects, the analysis only handled calls of the form `call(...)` or `var := call(...)`. Functions with multiple results being used e.g. `var, _ = call(...)` were not analyzed, causing some escaping variables to be marked as non-escaping. From-SVN: r222598 --- gcc/go/gofrontend/escape.cc | 162 +++++++++++++++++++++++++++++------- 1 file changed, 132 insertions(+), 30 deletions(-) diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index d2e7ae12931..1acd29cd70a 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -989,21 +989,21 @@ Build_connection_graphs::variable(Named_object* var) 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) + Expression* rhs = assn->rhs(); + if (rhs->is_composite_literal() + || rhs->heap_expression() != NULL) + this->handle_composite_literal(var, rhs); + + if (rhs->call_result_expression() != NULL) { // V's initialization will be a call result if // V, V1 := call(VAR). // There are no useful edges to make from V, but we want // to make sure we handle the call that references VAR. - Expression* call = - assn->rhs()->call_result_expression()->call(); - this->handle_call(var, call); + rhs = rhs->call_result_expression()->call(); } - else if (assn->rhs()->call_expression() != NULL) - this->handle_call(var, assn->rhs()); + if (rhs->call_expression() != NULL) + this->handle_call(var, rhs); // If there is no standalone variable on the rhs, this could be a // binary expression, which isn't interesting for analysis or a @@ -1038,8 +1038,12 @@ Build_connection_graphs::variable(Named_object* var) break; case Statement::STATEMENT_EXPRESSION: - this->handle_call(var, - p->statement->expression_statement()->expr()); + { + Expression* call = p->statement->expression_statement()->expr(); + if (call->call_result_expression() != NULL) + call = call->call_result_expression()->call(); + this->handle_call(var, call); + } break; case Statement::STATEMENT_GO: @@ -1064,10 +1068,17 @@ Build_connection_graphs::variable(Named_object* var) 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()); + Expression* left = comp->left(); + Expression* right = comp->right(); + + if (left->call_result_expression() != NULL) + left = left->call_result_expression()->call(); + if (left->call_expression() != NULL) + this->handle_call(var, left); + if (right->call_result_expression() != NULL) + right = right->call_result_expression()->call(); + if (right->call_expression() != NULL) + this->handle_call(var, right); } } break; @@ -1092,16 +1103,10 @@ Build_connection_graphs::variable(Named_object* var) // 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) + + if (init->call_result_expression() != NULL) + init = init->call_result_expression()->call(); + if (init->call_expression() != NULL) this->handle_call(var, init); } break; @@ -1148,18 +1153,46 @@ Build_connection_graphs::statement(Block*, size_t*, Statement* s) if (lhs_no == NULL) break; - if (assn->rhs()->func_expression() != NULL) + Expression* rhs = assn->rhs(); + if (rhs->temporary_reference_expression() != NULL) + rhs = rhs->temporary_reference_expression()->statement()->init(); + if (rhs == NULL) + break; + + if (rhs->call_result_expression() != NULL) + rhs = rhs->call_result_expression()->call(); + if (rhs->call_expression() != NULL) + { + // It's not clear what variables we are trying to find references to + // so just use the arguments to this call. + Expression_list* args = rhs->call_expression()->args(); + if (args == NULL) + break; + + for (Expression_list::const_iterator p = args->begin(); + p != args->end(); + ++p) + { + Named_object* no = this->resolve_var_reference(*p); + if (no != NULL) { + Node* lhs_node = this->gogo_->add_connection_node(lhs_no); + Node* rhs_node = this->gogo_->add_connection_node(no); + lhs_node->add_edge(rhs_node); + } + } + + this->handle_call(lhs_no, rhs); + } + else if (rhs->func_expression() != NULL) { Node* lhs_node = this->gogo_->add_connection_node(lhs_no); - Named_object* fn = assn->rhs()->func_expression()->named_object(); + Named_object* fn = 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()); + Named_object* rhs_no = this->resolve_var_reference(rhs); if (rhs_no != NULL) { Node* lhs_node = this->gogo_->add_connection_node(lhs_no); @@ -1188,6 +1221,8 @@ Build_connection_graphs::statement(Block*, size_t*, Statement* s) case Statement::STATEMENT_EXPRESSION: { Expression* expr = s->expression_statement()->expr(); + if (expr->call_result_expression() != NULL) + expr = expr->call_result_expression()->call(); if (expr->call_expression() != NULL) { // It's not clear what variables we are trying to find references to @@ -1208,6 +1243,73 @@ Build_connection_graphs::statement(Block*, size_t*, Statement* s) } break; + case Statement::STATEMENT_GO: + case Statement::STATEMENT_DEFER: + { + // Any variable referenced via a go or defer statement escapes to + // a different goroutine. + Expression* call = s->thunk_statement()->call(); + if (call->call_expression() != NULL) + { + // It's not clear what variables we are trying to find references to + // so just use the arguments to this call. + Expression_list* args = call->call_expression()->args(); + if (args == NULL) + break; + + for (Expression_list::const_iterator p = args->begin(); + p != args->end(); + ++p) + { + Named_object* no = this->resolve_var_reference(*p); + if (no != NULL) + this->handle_call(no, call); + } + } + } + break; + + case Statement::STATEMENT_VARIABLE_DECLARATION: + { + Variable_declaration_statement* decl = + s->variable_declaration_statement(); + Named_object* decl_no = decl->var(); + Variable* v = decl_no->var_value(); + + Expression* init = v->init(); + if (init == NULL) + break; + + if (init->is_composite_literal() + || init->heap_expression() != NULL) + { + // Create edges between DECL_NO and each named object in the + // composite literal. + this->handle_composite_literal(decl_no, init); + } + + if (init->call_result_expression() != NULL) + init = init->call_result_expression()->call(); + if (init->call_expression() != NULL) + { + // It's not clear what variables we are trying to find references to + // so just use the arguments to this call. + Expression_list* args = init->call_expression()->args(); + if (args == NULL) + break; + + for (Expression_list::const_iterator p = args->begin(); + p != args->end(); + ++p) + { + Named_object* no = this->resolve_var_reference(*p); + if (no != NULL) + this->handle_call(no, init); + } + } + } + break; + default: break; } -- 2.30.2