Commit 8a90f559 by Ian Lance Taylor

compiler: omit a couple of write barriers

    
    Omit a write barrier for
        s = s[0:]
    for a slice s.  In this case the pointer is not changing and no write
    barrier is required.
    
    Omit a write barrier for
        s = append(s, v)
    in the case where len(s) < cap(s) (and similarly when appending more
    values).  When the slice has enough capacity the pointer is not
    changing and no write barrier is required.
    
    These changes are required to avoid write barriers in the method
    randomOrder.reset in the runtime package.  That method is called from
    procresize, at a point where we do not want to allocate memory.
    Otherwise that method can use a write barrier, allocate memory, and
    break TestReadMemStats.
    
    Reviewed-on: https://go-review.googlesource.com/134219

From-SVN: r264259
parent 6201be94
06e688ff6d829c8de3735e9f59b61b373afc596f acf852f838e6b99f907d84648be853fa2c374393
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.
...@@ -131,6 +131,40 @@ Expression::determine_type_no_context() ...@@ -131,6 +131,40 @@ Expression::determine_type_no_context()
this->do_determine_type(&context); this->do_determine_type(&context);
} }
// Return true if two expressions refer to the same variable or struct
// field. This can only be true when there are no side effects.
bool
Expression::is_same_variable(Expression* a, Expression* b)
{
if (a->classification() != b->classification())
return false;
Var_expression* av = a->var_expression();
if (av != NULL)
return av->named_object() == b->var_expression()->named_object();
Field_reference_expression* af = a->field_reference_expression();
if (af != NULL)
{
Field_reference_expression* bf = b->field_reference_expression();
return (af->field_index() == bf->field_index()
&& Expression::is_same_variable(af->expr(), bf->expr()));
}
Unary_expression* au = a->unary_expression();
if (au != NULL)
{
Unary_expression* bu = b->unary_expression();
return (au->op() == OPERATOR_MULT
&& bu->op() == OPERATOR_MULT
&& Expression::is_same_variable(au->operand(),
bu->operand()));
}
return false;
}
// Return an expression handling any conversions which must be done during // Return an expression handling any conversions which must be done during
// assignment. // assignment.
...@@ -7421,7 +7455,7 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function, ...@@ -7421,7 +7455,7 @@ Builtin_call_expression::do_flatten(Gogo* gogo, Named_object* function,
break; break;
case BUILTIN_APPEND: case BUILTIN_APPEND:
return this->flatten_append(gogo, function, inserter); return this->flatten_append(gogo, function, inserter, NULL, NULL);
case BUILTIN_COPY: case BUILTIN_COPY:
{ {
...@@ -7658,11 +7692,18 @@ Builtin_call_expression::lower_make(Statement_inserter* inserter) ...@@ -7658,11 +7692,18 @@ Builtin_call_expression::lower_make(Statement_inserter* inserter)
// Flatten a call to the predeclared append function. We do this in // Flatten a call to the predeclared append function. We do this in
// the flatten phase, not the lowering phase, so that we run after // the flatten phase, not the lowering phase, so that we run after
// type checking and after order_evaluations. // type checking and after order_evaluations. If ASSIGN_LHS is not
// NULL, this append is the right-hand-side of an assignment and
// ASSIGN_LHS is the left-hand-side; in that case, set LHS directly
// rather than returning a slice. This lets us omit a write barrier
// in common cases like a = append(a, ...) when the slice does not
// need to grow. ENCLOSING is not NULL iff ASSIGN_LHS is not NULL.
Expression* Expression*
Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
Statement_inserter* inserter) Statement_inserter* inserter,
Expression* assign_lhs,
Block* enclosing)
{ {
if (this->is_error_expression()) if (this->is_error_expression())
return this; return this;
...@@ -7679,6 +7720,8 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, ...@@ -7679,6 +7720,8 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
if (args->size() == 1) if (args->size() == 1)
{ {
// append(s) evaluates to s. // append(s) evaluates to s.
if (assign_lhs != NULL)
return NULL;
return args->front(); return args->front();
} }
...@@ -7795,14 +7838,46 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, ...@@ -7795,14 +7838,46 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
// FIXME: Mark this index as not requiring bounds checks. // FIXME: Mark this index as not requiring bounds checks.
ref = Expression::make_index(ref, zero, ref2, NULL, loc); ref = Expression::make_index(ref, zero, ref2, NULL, loc);
Expression* rhs = Expression::make_conditional(cond, call, ref, loc); if (assign_lhs == NULL)
{
Expression* rhs = Expression::make_conditional(cond, call, ref, loc);
gogo->lower_expression(function, inserter, &rhs);
gogo->flatten_expression(function, inserter, &rhs);
gogo->lower_expression(function, inserter, &rhs); ref = Expression::make_temporary_reference(s1tmp, loc);
gogo->flatten_expression(function, inserter, &rhs); Statement* assign = Statement::make_assignment(ref, rhs, loc);
inserter->insert(assign);
}
else
{
gogo->lower_expression(function, inserter, &cond);
gogo->flatten_expression(function, inserter, &cond);
gogo->lower_expression(function, inserter, &call);
gogo->flatten_expression(function, inserter, &call);
gogo->lower_expression(function, inserter, &ref);
gogo->flatten_expression(function, inserter, &ref);
Expression* lhs = Expression::make_temporary_reference(s1tmp, loc); Block* then_block = new Block(enclosing, loc);
Statement* assign = Statement::make_assignment(lhs, rhs, loc); Assignment_statement* assign =
inserter->insert(assign); Statement::make_assignment(assign_lhs, call, loc);
then_block->add_statement(assign);
Block* else_block = new Block(enclosing, loc);
assign = Statement::make_assignment(assign_lhs->copy(), ref, loc);
// This assignment will not change the pointer value, so it does
// not need a write barrier.
assign->set_omit_write_barrier();
else_block->add_statement(assign);
Statement* s = Statement::make_if_statement(cond, then_block,
else_block, loc);
inserter->insert(s);
ref = Expression::make_temporary_reference(s1tmp, loc);
assign = Statement::make_assignment(ref, assign_lhs->copy(), loc);
inserter->insert(assign);
}
if (this->is_varargs()) if (this->is_varargs())
{ {
...@@ -7839,12 +7914,17 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, ...@@ -7839,12 +7914,17 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
Expression* off = Expression::make_integer_ul(i, int_type, loc); Expression* off = Expression::make_integer_ul(i, int_type, loc);
ref2 = Expression::make_binary(OPERATOR_PLUS, ref2, off, loc); ref2 = Expression::make_binary(OPERATOR_PLUS, ref2, off, loc);
// FIXME: Mark this index as not requiring bounds checks. // FIXME: Mark this index as not requiring bounds checks.
lhs = Expression::make_index(ref, ref2, NULL, NULL, loc); Expression* lhs = Expression::make_index(ref, ref2, NULL, NULL,
loc);
gogo->lower_expression(function, inserter, &lhs); gogo->lower_expression(function, inserter, &lhs);
gogo->flatten_expression(function, inserter, &lhs); gogo->flatten_expression(function, inserter, &lhs);
// The flatten pass runs after the write barrier pass, so we // The flatten pass runs after the write barrier pass, so we
// need to insert a write barrier here if necessary. // need to insert a write barrier here if necessary.
if (!gogo->assign_needs_write_barrier(lhs)) // However, if ASSIGN_LHS is not NULL, we have been called
// directly before the write barrier pass.
Statement* assign;
if (assign_lhs != NULL
|| !gogo->assign_needs_write_barrier(lhs))
assign = Statement::make_assignment(lhs, *pa, loc); assign = Statement::make_assignment(lhs, *pa, loc);
else else
{ {
...@@ -7856,6 +7936,9 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function, ...@@ -7856,6 +7936,9 @@ Builtin_call_expression::flatten_append(Gogo* gogo, Named_object* function,
} }
} }
if (assign_lhs != NULL)
return NULL;
return Expression::make_temporary_reference(s1tmp, loc); return Expression::make_temporary_reference(s1tmp, loc);
} }
......
...@@ -869,6 +869,11 @@ class Expression ...@@ -869,6 +869,11 @@ class Expression
bool bool
is_local_variable() const; is_local_variable() const;
// Return true if two expressions refer to the same variable or
// struct field.
static bool
is_same_variable(Expression*, Expression*);
// Make the builtin function descriptor type, so that it can be // Make the builtin function descriptor type, so that it can be
// converted. // converted.
static void static void
...@@ -2402,6 +2407,10 @@ class Builtin_call_expression : public Call_expression ...@@ -2402,6 +2407,10 @@ class Builtin_call_expression : public Call_expression
static bool static bool
array_len_is_constant(Expression* expr); array_len_is_constant(Expression* expr);
Expression*
flatten_append(Gogo*, Named_object*, Statement_inserter*, Expression*,
Block*);
protected: protected:
// This overrides Call_expression::do_lower. // This overrides Call_expression::do_lower.
Expression* Expression*
...@@ -2459,8 +2468,6 @@ class Builtin_call_expression : public Call_expression ...@@ -2459,8 +2468,6 @@ class Builtin_call_expression : public Call_expression
Expression* Expression*
lower_make(Statement_inserter*); lower_make(Statement_inserter*);
Expression* flatten_append(Gogo*, Named_object*, Statement_inserter*);
bool bool
check_int_value(Expression*, bool is_length, bool* small); check_int_value(Expression*, bool is_length, bool* small);
......
...@@ -686,7 +686,7 @@ Assignment_statement::do_traverse_assignments(Traverse_assignments* tassign) ...@@ -686,7 +686,7 @@ Assignment_statement::do_traverse_assignments(Traverse_assignments* tassign)
} }
// Lower an assignment to a map index expression to a runtime function // Lower an assignment to a map index expression to a runtime function
// call. // call. Mark some slice assignments as not requiring a write barrier.
Statement* Statement*
Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
...@@ -750,6 +750,21 @@ Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, ...@@ -750,6 +750,21 @@ Assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
return Statement::make_block_statement(b, loc); return Statement::make_block_statement(b, loc);
} }
// An assignment of the form s = s[:n] does not require a write
// barrier, because the pointer value will not change.
Array_index_expression* aie = this->rhs_->array_index_expression();
if (aie != NULL
&& aie->end() != NULL
&& Expression::is_same_variable(this->lhs_, aie->array()))
{
Numeric_constant nc;
unsigned long ival;
if (aie->start()->numeric_constant_value(&nc)
&& nc.to_unsigned_long(&ival) == Numeric_constant::NC_UL_VALID
&& ival == 0)
this->omit_write_barrier_ = true;
}
return this; return this;
} }
...@@ -876,7 +891,7 @@ Assignment_statement::do_dump_statement(Ast_dump_context* ast_dump_context) ...@@ -876,7 +891,7 @@ Assignment_statement::do_dump_statement(Ast_dump_context* ast_dump_context)
// Make an assignment statement. // Make an assignment statement.
Statement* Assignment_statement*
Statement::make_assignment(Expression* lhs, Expression* rhs, Statement::make_assignment(Expression* lhs, Expression* rhs,
Location location) Location location)
{ {
......
...@@ -148,7 +148,7 @@ class Statement ...@@ -148,7 +148,7 @@ class Statement
make_temporary(Type*, Expression*, Location); make_temporary(Type*, Expression*, Location);
// Make an assignment statement. // Make an assignment statement.
static Statement* static Assignment_statement*
make_assignment(Expression*, Expression*, Location); make_assignment(Expression*, Expression*, Location);
// Make an assignment operation (+=, etc.). // Make an assignment operation (+=, etc.).
...@@ -562,7 +562,7 @@ class Assignment_statement : public Statement ...@@ -562,7 +562,7 @@ class Assignment_statement : public Statement
Assignment_statement(Expression* lhs, Expression* rhs, Assignment_statement(Expression* lhs, Expression* rhs,
Location location) Location location)
: Statement(STATEMENT_ASSIGNMENT, location), : Statement(STATEMENT_ASSIGNMENT, location),
lhs_(lhs), rhs_(rhs) lhs_(lhs), rhs_(rhs), omit_write_barrier_(false)
{ } { }
Expression* Expression*
...@@ -573,6 +573,14 @@ class Assignment_statement : public Statement ...@@ -573,6 +573,14 @@ class Assignment_statement : public Statement
rhs() const rhs() const
{ return this->rhs_; } { return this->rhs_; }
bool
omit_write_barrier() const
{ return this->omit_write_barrier_; }
void
set_omit_write_barrier()
{ this->omit_write_barrier_ = true; }
protected: protected:
int int
do_traverse(Traverse* traverse); do_traverse(Traverse* traverse);
...@@ -603,6 +611,8 @@ class Assignment_statement : public Statement ...@@ -603,6 +611,8 @@ class Assignment_statement : public Statement
Expression* lhs_; Expression* lhs_;
// Right hand side--the rvalue. // Right hand side--the rvalue.
Expression* rhs_; Expression* rhs_;
// True if we can omit a write barrier from this assignment.
bool omit_write_barrier_;
}; };
// A statement which creates and initializes a temporary variable. // A statement which creates and initializes a temporary variable.
......
...@@ -16,26 +16,87 @@ ...@@ -16,26 +16,87 @@
#include "runtime.h" #include "runtime.h"
#include "gogo.h" #include "gogo.h"
// Mark variables whose addresses are taken. This has to be done // Mark variables whose addresses are taken and do some other
// before the write barrier pass and after the escape analysis pass. // cleanups. This has to be done before the write barrier pass and
// It would be nice to do this elsewhere but there isn't an obvious // after the escape analysis pass. It would be nice to do this
// place. // elsewhere but there isn't an obvious place.
class Mark_address_taken : public Traverse class Mark_address_taken : public Traverse
{ {
public: public:
Mark_address_taken(Gogo* gogo) Mark_address_taken(Gogo* gogo)
: Traverse(traverse_expressions), : Traverse(traverse_functions
gogo_(gogo) | traverse_statements
| traverse_expressions),
gogo_(gogo), function_(NULL)
{ } { }
int int
function(Named_object*);
int
statement(Block*, size_t*, Statement*);
int
expression(Expression**); expression(Expression**);
private: private:
// General IR.
Gogo* gogo_; Gogo* gogo_;
// The function we are traversing.
Named_object* function_;
}; };
// Record a function.
int
Mark_address_taken::function(Named_object* no)
{
go_assert(this->function_ == NULL);
this->function_ = no;
int t = no->func_value()->traverse(this);
this->function_ = NULL;
if (t == TRAVERSE_EXIT)
return t;
return TRAVERSE_SKIP_COMPONENTS;
}
// Traverse a statement.
int
Mark_address_taken::statement(Block* block, size_t* pindex, Statement* s)
{
// If this is an assignment of the form s = append(s, ...), expand
// it now, so that we can assign it to the left hand side in the
// middle of the expansion and possibly skip a write barrier.
Assignment_statement* as = s->assignment_statement();
if (as != NULL && !as->lhs()->is_sink_expression())
{
Call_expression* rce = as->rhs()->call_expression();
if (rce != NULL
&& rce->builtin_call_expression() != NULL
&& (rce->builtin_call_expression()->code()
== Builtin_call_expression::BUILTIN_APPEND)
&& Expression::is_same_variable(as->lhs(), rce->args()->front()))
{
Statement_inserter inserter = Statement_inserter(block, pindex);
Expression* a =
rce->builtin_call_expression()->flatten_append(this->gogo_,
this->function_,
&inserter,
as->lhs(),
block);
go_assert(a == NULL);
// That does the assignment, so remove this statement.
Expression* e = Expression::make_boolean(true, s->location());
Statement* dummy = Statement::make_statement(e, true);
block->replace_statement(*pindex, dummy);
}
}
return TRAVERSE_CONTINUE;
}
// Mark variable addresses taken. // Mark variable addresses taken.
int int
...@@ -387,6 +448,10 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s) ...@@ -387,6 +448,10 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
case Statement::STATEMENT_ASSIGNMENT: case Statement::STATEMENT_ASSIGNMENT:
{ {
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();
......
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