From 787732cdd12218f31dced23a9c427b29c721a9d6 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 25 Jun 2018 22:23:49 +0000 Subject: [PATCH] compiler: improve escape analysis This CL ports recent enhancements of the escape analysis in the gc compiler to gofrontend. - CL 99335: unnamed receiver should not escape. - CL 105257: propagate loop depth to field. This prevents it from escaping when a field's address is taken inside a loop (but not otherwise escape). - CL 107597: use element type for "indirection" of slice/string. This prevents the slice/string from escaping when only the element, in case that it is pointerless, flows to outer scope. Reviewed-on: https://go-review.googlesource.com/120760 From-SVN: r262120 --- gcc/go/gofrontend/MERGE | 2 +- gcc/go/gofrontend/escape.cc | 132 ++++++++++++++++++++++-------------- 2 files changed, 81 insertions(+), 53 deletions(-) diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index c7b0b13ddde..3d825803ae8 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -7008302f1f0eaa9508b2857185505d4dc7baac1e +baaaf1e0f1e9a54ea2dfe475154c85c83ec03740 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index 51e80e44645..e32ae5b475c 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -43,6 +43,12 @@ Node::type() const // which may also be pointer. We model it as another void*, so // we don't lose pointer-ness. return this->child()->type(); + else if (this->child()->type()->is_slice_type()) + // We model "indirect" of a slice as dereferencing its pointer + // field (to get element). Use element type here. + return this->child()->type()->array_type()->element_type(); + else if (this->child()->type()->is_string_type()) + return Type::lookup_integer_type("uint8"); else return this->child()->type()->deref(); } @@ -1811,60 +1817,77 @@ Escape_analysis_assign::expression(Expression** pexpr) case Expression::EXPRESSION_UNARY: { - if ((*pexpr)->unary_expression()->op() != OPERATOR_AND) - break; + Expression* operand = (*pexpr)->unary_expression()->operand(); - Node* addr_node = Node::make_node(*pexpr); - this->context_->track(addr_node); + if ((*pexpr)->unary_expression()->op() == OPERATOR_AND) + { + this->context_->track(n); - Expression* operand = (*pexpr)->unary_expression()->operand(); - Named_object* var = NULL; - if (operand->var_expression() != NULL) - var = operand->var_expression()->named_object(); - else if (operand->enclosed_var_expression() != NULL) - var = operand->enclosed_var_expression()->variable(); + Named_object* var = NULL; + if (operand->var_expression() != NULL) + var = operand->var_expression()->named_object(); + else if (operand->enclosed_var_expression() != NULL) + var = operand->enclosed_var_expression()->variable(); - if (var == NULL) - break; + if (var != NULL + && ((var->is_variable() && var->var_value()->is_parameter()) + || var->is_result_variable())) + { + Node::Escape_state* addr_state = n->state(this->context_, NULL); + addr_state->loop_depth = 1; + break; + } + } - if (var->is_variable() - && !var->var_value()->is_parameter()) - { - // For &x, use the loop depth of x if known. - Node::Escape_state* addr_state = - addr_node->state(this->context_, NULL); - Node* operand_node = Node::make_node(operand); - Node::Escape_state* operand_state = - operand_node->state(this->context_, NULL); - if (operand_state->loop_depth != 0) - addr_state->loop_depth = operand_state->loop_depth; - } - else if ((var->is_variable() - && var->var_value()->is_parameter()) - || var->is_result_variable()) - { - Node::Escape_state* addr_state = - addr_node->state(this->context_, NULL); - addr_state->loop_depth = 1; - } + if ((*pexpr)->unary_expression()->op() != OPERATOR_AND + && (*pexpr)->unary_expression()->op() != OPERATOR_MULT) + break; + + // For &x and *x, use the loop depth of x if known. + Node::Escape_state* expr_state = n->state(this->context_, NULL); + Node* operand_node = Node::make_node(operand); + Node::Escape_state* operand_state = operand_node->state(this->context_, NULL); + if (operand_state->loop_depth != 0) + expr_state->loop_depth = operand_state->loop_depth; } break; case Expression::EXPRESSION_ARRAY_INDEX: { Array_index_expression* aie = (*pexpr)->array_index_expression(); + + // Propagate the loopdepth to element. + Node* array_node = Node::make_node(aie->array()); + Node::Escape_state* elem_state = n->state(this->context_, NULL); + Node::Escape_state* array_state = array_node->state(this->context_, NULL); + elem_state->loop_depth = array_state->loop_depth; + if (aie->end() != NULL && !aie->array()->type()->is_slice_type()) { - // Slicing an array. + // Slicing an array. This effectively takes the address of the array. Expression* addr = Expression::make_unary(OPERATOR_AND, aie->array(), aie->location()); Node* addr_node = Node::make_node(addr); n->set_child(addr_node); this->context_->track(addr_node); + + Node::Escape_state* addr_state = addr_node->state(this->context_, NULL); + addr_state->loop_depth = array_state->loop_depth; } } break; + case Expression::EXPRESSION_FIELD_REFERENCE: + { + // Propagate the loopdepth to field. + Node* struct_node = + Node::make_node((*pexpr)->field_reference_expression()->expr()); + Node::Escape_state* field_state = n->state(this->context_, NULL); + Node::Escape_state* struct_state = struct_node->state(this->context_, NULL); + field_state->loop_depth = struct_state->loop_depth; + } + break; + default: break; } @@ -3288,28 +3311,33 @@ Escape_analysis_tag::tag(Named_object* fn) Function_type* fntype = fn->func_value()->type(); Bindings* bindings = fn->func_value()->block()->bindings(); - if (fntype->is_method() - && !fntype->receiver()->name().empty() - && !Gogo::is_sink_name(fntype->receiver()->name())) + if (fntype->is_method()) { - Named_object* rcvr_no = bindings->lookup(fntype->receiver()->name()); - go_assert(rcvr_no != NULL); - Node* rcvr_node = Node::make_node(rcvr_no); - switch ((rcvr_node->encoding() & ESCAPE_MASK)) - { - case Node::ESCAPE_NONE: // not touched by flood - case Node::ESCAPE_RETURN: - if (fntype->receiver()->type()->has_pointer()) - // Don't bother tagging for scalars. - fntype->add_receiver_note(rcvr_node->encoding()); - break; + if (fntype->receiver()->name().empty() + || Gogo::is_sink_name(fntype->receiver()->name())) + // Unnamed receiver is not used in the function body, does not escape. + fntype->add_receiver_note(Node::ESCAPE_NONE); + else + { + Named_object* rcvr_no = bindings->lookup(fntype->receiver()->name()); + go_assert(rcvr_no != NULL); + Node* rcvr_node = Node::make_node(rcvr_no); + switch ((rcvr_node->encoding() & ESCAPE_MASK)) + { + case Node::ESCAPE_NONE: // not touched by flood + case Node::ESCAPE_RETURN: + if (fntype->receiver()->type()->has_pointer()) + // Don't bother tagging for scalars. + fntype->add_receiver_note(rcvr_node->encoding()); + break; - case Node::ESCAPE_HEAP: // flooded, moved to heap. - break; + case Node::ESCAPE_HEAP: // flooded, moved to heap. + break; - default: - break; - } + default: + break; + } + } } int i = 0; -- 2.30.2