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
This commit is contained in:
parent
457dac4020
commit
6ae361ae45
5 changed files with 102 additions and 27 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue