aboutsummaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2019-08-23 22:38:58 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2019-08-23 22:38:58 +0000
commit6ae361ae458d4056d6c36fa42f1775c417457b10 (patch)
tree2abbeb7a9dc1276b8a53259a89738319fdee0f45 /gcc/go
parent457dac402027dd7e14543fbd59a75858422cf6c6 (diff)
downloadgcc-6ae361ae458d4056d6c36fa42f1775c417457b10.zip
gcc-6ae361ae458d4056d6c36fa42f1775c417457b10.tar.gz
gcc-6ae361ae458d4056d6c36fa42f1775c417457b10.tar.bz2
compiler: record pointer var values to remove write barriers
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
Diffstat (limited to 'gcc/go')
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/expressions.cc2
-rw-r--r--gcc/go/gofrontend/gogo.h9
-rw-r--r--gcc/go/gofrontend/wb.cc115
4 files changed, 101 insertions, 27 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 5c02446..73c7534 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -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.
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index 378dab5..90a39a2 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -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
{
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 4521763..087e890 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -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
diff --git a/gcc/go/gofrontend/wb.cc b/gcc/go/gofrontend/wb.cc
index 501ad6a..41d8f94 100644
--- a/gcc/go/gofrontend/wb.cc
+++ b/gcc/go/gofrontend/wb.cc
@@ -402,14 +402,21 @@ 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*);
int
@@ -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.