compiler: record pointer var values to remove write barriers
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 23 Aug 2019 22:38:58 +0000 (22:38 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 23 Aug 2019 22:38:58 +0000 (22:38 +0000)
    Record when a local pointer variable is set to a value such that
    indirecting through the pointer does not require a write barrier.  Use
    that to eliminate write barriers when indirecting through that local
    pointer variable.  Only keep this information per-block, so it's not
    all that applicable.

    This reduces the number of write barriers generated when compiling the
    runtime package from 553 to 524.

    The point of this is to eliminate a bad write barrier in the bytes
    function in runtime/print.go.  Mark that function nowritebarrier so
    that the problem does not recur.

    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/191581

From-SVN: r274890

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/wb.cc
libgo/go/runtime/print.go

index 5c0244662bcd8d0cf6e5d8d9f00a86842d46c15d..73c75342feaed23a1edc03d6e7d23eea3c603e98 100644 (file)
@@ -1,4 +1,4 @@
-82d27f0f140f33406cf59c0fb262f6dba3077f8e
+c9ca1c6bf887c752cc75cf1ddaec8ddd1ec962d4
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 378dab57a696586af740126718e3d300f02967df..90a39a222b0347b3fed21cd2eb313b76642bfb06 100644 (file)
@@ -9039,7 +9039,7 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
          // directly before the write barrier pass.
          Statement* assign;
          if (assign_lhs != NULL
-             || !gogo->assign_needs_write_barrier(lhs))
+             || !gogo->assign_needs_write_barrier(lhs, NULL))
            assign = Statement::make_assignment(lhs, elem, loc);
          else
            {
index 4521763611674a9ab697e4c5ec9c9eb5c577fb11..087e89057ac8e961b96581732385ae4e8762a1fb 100644 (file)
@@ -771,7 +771,14 @@ class Gogo
   // Return whether an assignment that sets LHS to RHS needs a write
   // barrier.
   bool
-  assign_needs_write_barrier(Expression* lhs);
+  assign_needs_write_barrier(Expression* lhs,
+                             Unordered_set(const Named_object*)*);
+
+  // Return whether EXPR is the address of a variable that can be set
+  // without a write barrier.  That is, if this returns true, then an
+  // assignment to *EXPR does not require a write barrier.
+  bool
+  is_nonwb_pointer(Expression* expr, Unordered_set(const Named_object*)*);
 
   // Return an assignment that sets LHS to RHS using a write barrier.
   // This returns an if statement that checks whether write barriers
index 501ad6a8ca208426f715ef22d316eaed0fc538fb..41d8f94c0744197d014f1cb393c8d14be19c6da7 100644 (file)
@@ -402,13 +402,20 @@ class Write_barriers : public Traverse
 {
  public:
   Write_barriers(Gogo* gogo)
-    : Traverse(traverse_functions | traverse_variables | traverse_statements),
-      gogo_(gogo), function_(NULL), statements_added_()
+    : Traverse(traverse_functions
+              | traverse_blocks
+              | traverse_variables
+              | traverse_statements),
+      gogo_(gogo), function_(NULL), statements_added_(),
+      nonwb_pointers_()
   { }
 
   int
   function(Named_object*);
 
+  int
+  block(Block*);
+
   int
   variable(Named_object*);
 
@@ -422,6 +429,9 @@ class Write_barriers : public Traverse
   Function* function_;
   // Statements introduced.
   Statement_inserter::Statements statements_added_;
+  // Within a single block, pointer variables that point to values
+  // that do not need write barriers.
+  Unordered_set(const Named_object*) nonwb_pointers_;
 };
 
 // Traverse a function.  Just record it for later.
@@ -439,6 +449,16 @@ Write_barriers::function(Named_object* no)
   return TRAVERSE_SKIP_COMPONENTS;
 }
 
+// Traverse a block.  Clear anything we know about local pointer
+// variables.
+
+int
+Write_barriers::block(Block*)
+{
+  this->nonwb_pointers_.clear();
+  return TRAVERSE_CONTINUE;
+}
+
 // Insert write barriers for a global variable: ensure that variable
 // initialization is handled correctly.  This is rarely needed, since
 // we currently don't enable background GC until after all global
@@ -533,7 +553,16 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
        // local variables get declaration statements, and local
        // variables on the stack do not require write barriers.
        if (!var->is_in_heap())
-         break;
+          {
+           // If this is a pointer variable, and assigning through
+           // the initializer does not require a write barrier,
+           // record that fact.
+           if (var->type()->points_to() != NULL
+               && this->gogo_->is_nonwb_pointer(init, &this->nonwb_pointers_))
+             this->nonwb_pointers_.insert(no);
+
+           break;
+          }
 
        // Nothing to do if the variable does not contain any pointers.
        if (!var->type()->has_pointer())
@@ -578,15 +607,27 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
       {
        Assignment_statement* as = s->assignment_statement();
 
-       if (as->omit_write_barrier())
-         break;
-
        Expression* lhs = as->lhs();
        Expression* rhs = as->rhs();
 
+       // Keep track of variables whose values do not escape.
+       Var_expression* lhsve = lhs->var_expression();
+       if (lhsve != NULL && lhsve->type()->points_to() != NULL)
+         {
+           Named_object* no = lhsve->named_object();
+           if (this->gogo_->is_nonwb_pointer(rhs, &this->nonwb_pointers_))
+             this->nonwb_pointers_.insert(no);
+           else
+             this->nonwb_pointers_.erase(no);
+         }
+
+       if (as->omit_write_barrier())
+         break;
+
        // We may need to emit a write barrier for the assignment.
 
-       if (!this->gogo_->assign_needs_write_barrier(lhs))
+       if (!this->gogo_->assign_needs_write_barrier(lhs,
+                                                    &this->nonwb_pointers_))
          break;
 
        // Change the assignment to use a write barrier.
@@ -667,9 +708,13 @@ Gogo::write_barrier_variable()
 }
 
 // Return whether an assignment that sets LHS needs a write barrier.
+// NONWB_POINTERS is a set of variables that point to values that do
+// not need write barriers.
 
 bool
-Gogo::assign_needs_write_barrier(Expression* lhs)
+Gogo::assign_needs_write_barrier(
+    Expression* lhs,
+    Unordered_set(const Named_object*)* nonwb_pointers)
 {
   // Nothing to do if the variable does not contain any pointers.
   if (!lhs->type()->has_pointer())
@@ -738,22 +783,10 @@ Gogo::assign_needs_write_barrier(Expression* lhs)
   // Nothing to do for an assignment to *(convert(&x)) where
   // x is local variable or a temporary variable.
   Unary_expression* ue = lhs->unary_expression();
-  if (ue != NULL && ue->op() == OPERATOR_MULT)
-    {
-      Expression* expr = ue->operand();
-      while (true)
-        {
-          if (expr->conversion_expression() != NULL)
-            expr = expr->conversion_expression()->expr();
-          else if (expr->unsafe_conversion_expression() != NULL)
-            expr = expr->unsafe_conversion_expression()->expr();
-          else
-            break;
-        }
-      ue = expr->unary_expression();
-      if (ue != NULL && ue->op() == OPERATOR_AND)
-        return this->assign_needs_write_barrier(ue->operand());
-    }
+  if (ue != NULL
+      && ue->op() == OPERATOR_MULT
+      && this->is_nonwb_pointer(ue->operand(), nonwb_pointers))
+    return false;
 
   // For a struct assignment, we don't need a write barrier if all the
   // pointer types can not be in the heap.
@@ -784,6 +817,40 @@ Gogo::assign_needs_write_barrier(Expression* lhs)
   return true;
 }
 
+// Return whether EXPR is the address of a variable that can be set
+// without a write barrier.  That is, if this returns true, then an
+// assignment to *EXPR does not require a write barrier.
+// NONWB_POINTERS is a set of variables that point to values that do
+// not need write barriers.
+
+bool
+Gogo::is_nonwb_pointer(Expression* expr,
+                      Unordered_set(const Named_object*)* nonwb_pointers)
+{
+  while (true)
+    {
+      if (expr->conversion_expression() != NULL)
+       expr = expr->conversion_expression()->expr();
+      else if (expr->unsafe_conversion_expression() != NULL)
+       expr = expr->unsafe_conversion_expression()->expr();
+      else
+       break;
+    }
+
+  Var_expression* ve = expr->var_expression();
+  if (ve != NULL
+      && nonwb_pointers != NULL
+      && nonwb_pointers->find(ve->named_object()) != nonwb_pointers->end())
+    return true;
+
+  Unary_expression* ue = expr->unary_expression();
+  if (ue == NULL || ue->op() != OPERATOR_AND)
+    return false;
+  if (this->assign_needs_write_barrier(ue->operand(), nonwb_pointers))
+    return false;
+  return true;
+}
+
 // Return a statement that sets LHS to RHS using a write barrier.
 // ENCLOSING is the enclosing block.
 
index 8c396442862139627dd9973814c13eafcd8cad62..14d546d6d33c7fb5b42380b0a280a360f6feaa69 100644 (file)
@@ -35,6 +35,7 @@ import (
 // should use printhex instead of printuint (decimal).
 type hex uint64
 
+//go:nowritebarrier
 func bytes(s string) (ret []byte) {
        rp := (*slice)(unsafe.Pointer(&ret))
        sp := stringStructOf(&s)