Go patch committed: Escape analysis improvements

Message ID CAOyqgcUS4-Xfo_8zgJLzffS9o9PWgAYkQUVVv-FwJppVdDcpjg@mail.gmail.com
State New
Headers show
Series
  • Go patch committed: Escape analysis improvements
Related show

Commit Message

Ian Lance Taylor June 25, 2018, 10:23 p.m.
This patch to the Go frontend by Cherry Zhang adds some escape
analysis improvements to the Go frontend.  These are based on earlier
patches to the gc compiler.

- https://golang.org/cl/99335: unnamed receiver should not escape.

- https://golang.org/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).

- https://golang.org/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.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 261980)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -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.
Index: gcc/go/gofrontend/escape.cc
===================================================================
--- gcc/go/gofrontend/escape.cc	(revision 261819)
+++ gcc/go/gofrontend/escape.cc	(working copy)
@@ -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(Expre
 
     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
+                && ((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 == NULL)
-	  break;
+        if ((*pexpr)->unary_expression()->op() != OPERATOR_AND
+            && (*pexpr)->unary_expression()->op() != OPERATOR_MULT)
+          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;
-	  }
+        // 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* f
   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;