Commit 6ae361ae by Ian Lance Taylor

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
parent 457dac40
82d27f0f140f33406cf59c0fb262f6dba3077f8e c9ca1c6bf887c752cc75cf1ddaec8ddd1ec962d4
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -9039,7 +9039,7 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, ...@@ -9039,7 +9039,7 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
// directly before the write barrier pass. // directly before the write barrier pass.
Statement* assign; Statement* assign;
if (assign_lhs != NULL if (assign_lhs != NULL
|| !gogo->assign_needs_write_barrier(lhs)) || !gogo->assign_needs_write_barrier(lhs, NULL))
assign = Statement::make_assignment(lhs, elem, loc); assign = Statement::make_assignment(lhs, elem, loc);
else else
{ {
......
...@@ -771,7 +771,14 @@ class Gogo ...@@ -771,7 +771,14 @@ class Gogo
// Return whether an assignment that sets LHS to RHS needs a write // Return whether an assignment that sets LHS to RHS needs a write
// barrier. // barrier.
bool 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. // Return an assignment that sets LHS to RHS using a write barrier.
// This returns an if statement that checks whether write barriers // This returns an if statement that checks whether write barriers
......
...@@ -402,14 +402,21 @@ class Write_barriers : public Traverse ...@@ -402,14 +402,21 @@ class Write_barriers : public Traverse
{ {
public: public:
Write_barriers(Gogo* gogo) Write_barriers(Gogo* gogo)
: Traverse(traverse_functions | traverse_variables | traverse_statements), : Traverse(traverse_functions
gogo_(gogo), function_(NULL), statements_added_() | traverse_blocks
| traverse_variables
| traverse_statements),
gogo_(gogo), function_(NULL), statements_added_(),
nonwb_pointers_()
{ } { }
int int
function(Named_object*); function(Named_object*);
int int
block(Block*);
int
variable(Named_object*); variable(Named_object*);
int int
...@@ -422,6 +429,9 @@ class Write_barriers : public Traverse ...@@ -422,6 +429,9 @@ class Write_barriers : public Traverse
Function* function_; Function* function_;
// Statements introduced. // Statements introduced.
Statement_inserter::Statements statements_added_; 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. // Traverse a function. Just record it for later.
...@@ -439,6 +449,16 @@ Write_barriers::function(Named_object* no) ...@@ -439,6 +449,16 @@ Write_barriers::function(Named_object* no)
return TRAVERSE_SKIP_COMPONENTS; 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 // Insert write barriers for a global variable: ensure that variable
// initialization is handled correctly. This is rarely needed, since // initialization is handled correctly. This is rarely needed, since
// we currently don't enable background GC until after all global // 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) ...@@ -533,7 +553,16 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
// local variables get declaration statements, and local // local variables get declaration statements, and local
// variables on the stack do not require write barriers. // variables on the stack do not require write barriers.
if (!var->is_in_heap()) 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. // Nothing to do if the variable does not contain any pointers.
if (!var->type()->has_pointer()) if (!var->type()->has_pointer())
...@@ -578,15 +607,27 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s) ...@@ -578,15 +607,27 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
{ {
Assignment_statement* as = s->assignment_statement(); Assignment_statement* as = s->assignment_statement();
if (as->omit_write_barrier())
break;
Expression* lhs = as->lhs(); Expression* lhs = as->lhs();
Expression* rhs = as->rhs(); 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. // 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; break;
// Change the assignment to use a write barrier. // Change the assignment to use a write barrier.
...@@ -667,9 +708,13 @@ Gogo::write_barrier_variable() ...@@ -667,9 +708,13 @@ Gogo::write_barrier_variable()
} }
// Return whether an assignment that sets LHS needs a write barrier. // 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 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. // Nothing to do if the variable does not contain any pointers.
if (!lhs->type()->has_pointer()) if (!lhs->type()->has_pointer())
...@@ -738,22 +783,10 @@ Gogo::assign_needs_write_barrier(Expression* lhs) ...@@ -738,22 +783,10 @@ Gogo::assign_needs_write_barrier(Expression* lhs)
// Nothing to do for an assignment to *(convert(&x)) where // Nothing to do for an assignment to *(convert(&x)) where
// x is local variable or a temporary variable. // x is local variable or a temporary variable.
Unary_expression* ue = lhs->unary_expression(); Unary_expression* ue = lhs->unary_expression();
if (ue != NULL && ue->op() == OPERATOR_MULT) if (ue != NULL
{ && ue->op() == OPERATOR_MULT
Expression* expr = ue->operand(); && this->is_nonwb_pointer(ue->operand(), nonwb_pointers))
while (true) return false;
{
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());
}
// For a struct assignment, we don't need a write barrier if all the // For a struct assignment, we don't need a write barrier if all the
// pointer types can not be in the heap. // pointer types can not be in the heap.
...@@ -784,6 +817,40 @@ Gogo::assign_needs_write_barrier(Expression* lhs) ...@@ -784,6 +817,40 @@ Gogo::assign_needs_write_barrier(Expression* lhs)
return true; 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. // Return a statement that sets LHS to RHS using a write barrier.
// ENCLOSING is the enclosing block. // ENCLOSING is the enclosing block.
......
...@@ -35,6 +35,7 @@ import ( ...@@ -35,6 +35,7 @@ import (
// should use printhex instead of printuint (decimal). // should use printhex instead of printuint (decimal).
type hex uint64 type hex uint64
//go:nowritebarrier
func bytes(s string) (ret []byte) { func bytes(s string) (ret []byte) {
rp := (*slice)(unsafe.Pointer(&ret)) rp := (*slice)(unsafe.Pointer(&ret))
sp := stringStructOf(&s) sp := stringStructOf(&s)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment