Commit 1df5fce4 by Ian Lance Taylor

compiler: avoid introducing redundant write barriers

    
    The traversal used by the write barrier insertion phase can sometimes
    wind up visiting new statements inserted during the traversal, which
    then results in duplicate / redundant write barrier guards. Example
    program to reproduce:
    
      package small
      type S struct {
            N *S
            K int
      }
      var G *S = &S{N: nil, K: 101}
    
    This patch changes the traversal code to keep track of statements
    already added and avoid processing them again later in the traversal.
    
    Fixes golang/go#25867
    
    Reviewed-on: https://go-review.googlesource.com/118637

From-SVN: r261568
parent a7bf6c08
1f07926263b6d14edb6abd6a00e6385190d30d0e c3ef5bbf4e4271216b6f22621269d458599e8087
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.
...@@ -8444,6 +8444,9 @@ Traverse::function_declaration(Named_object*) ...@@ -8444,6 +8444,9 @@ Traverse::function_declaration(Named_object*)
void void
Statement_inserter::insert(Statement* s) Statement_inserter::insert(Statement* s)
{ {
if (this->statements_added_ != NULL)
this->statements_added_->insert(s);
if (this->block_ != NULL) if (this->block_ != NULL)
{ {
go_assert(this->pindex_ != NULL); go_assert(this->pindex_ != NULL);
......
...@@ -3419,19 +3419,24 @@ class Traverse ...@@ -3419,19 +3419,24 @@ class Traverse
class Statement_inserter class Statement_inserter
{ {
public: public:
typedef Unordered_set(Statement*) Statements;
// Empty constructor. // Empty constructor.
Statement_inserter() Statement_inserter()
: block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL) : block_(NULL), pindex_(NULL), gogo_(NULL), var_(NULL),
statements_added_(NULL)
{ } { }
// Constructor for a statement in a block. // Constructor for a statement in a block.
Statement_inserter(Block* block, size_t *pindex) Statement_inserter(Block* block, size_t *pindex, Statements *added = NULL)
: block_(block), pindex_(pindex), gogo_(NULL), var_(NULL) : block_(block), pindex_(pindex), gogo_(NULL), var_(NULL),
statements_added_(added)
{ } { }
// Constructor for a global variable. // Constructor for a global variable.
Statement_inserter(Gogo* gogo, Variable* var) Statement_inserter(Gogo* gogo, Variable* var, Statements *added = NULL)
: block_(NULL), pindex_(NULL), gogo_(gogo), var_(var) : block_(NULL), pindex_(NULL), gogo_(gogo), var_(var),
statements_added_(added)
{ go_assert(var->is_global()); } { go_assert(var->is_global()); }
// We use the default copy constructor and assignment operator. // We use the default copy constructor and assignment operator.
...@@ -3451,6 +3456,8 @@ class Statement_inserter ...@@ -3451,6 +3456,8 @@ class Statement_inserter
Gogo* gogo_; Gogo* gogo_;
// The global variable, when looking at an initializer expression. // The global variable, when looking at an initializer expression.
Variable* var_; Variable* var_;
// If non-null, a set to record new statements inserted (non-owned).
Statements* statements_added_;
}; };
// When translating the gogo IR into the backend data structure, this // When translating the gogo IR into the backend data structure, this
......
...@@ -213,7 +213,7 @@ class Write_barriers : public Traverse ...@@ -213,7 +213,7 @@ 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 | traverse_variables | traverse_statements),
gogo_(gogo), function_(NULL) gogo_(gogo), function_(NULL), statements_added_()
{ } { }
int int
...@@ -230,6 +230,8 @@ class Write_barriers : public Traverse ...@@ -230,6 +230,8 @@ class Write_barriers : public Traverse
Gogo* gogo_; Gogo* gogo_;
// Current function. // Current function.
Function* function_; Function* function_;
// Statements introduced.
Statement_inserter::Statements statements_added_;
}; };
// Traverse a function. Just record it for later. // Traverse a function. Just record it for later.
...@@ -298,9 +300,10 @@ Write_barriers::variable(Named_object* no) ...@@ -298,9 +300,10 @@ Write_barriers::variable(Named_object* no)
Location loc = init->location(); Location loc = init->location();
Expression* ref = Expression::make_var_reference(no, loc); Expression* ref = Expression::make_var_reference(no, loc);
Statement_inserter inserter(this->gogo_, var); Statement_inserter inserter(this->gogo_, var, &this->statements_added_);
Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter, Statement* s = this->gogo_->assign_with_write_barrier(NULL, NULL, &inserter,
ref, init, loc); ref, init, loc);
this->statements_added_.insert(s);
var->add_preinit_statement(this->gogo_, s); var->add_preinit_statement(this->gogo_, s);
var->clear_init(); var->clear_init();
...@@ -313,6 +316,9 @@ Write_barriers::variable(Named_object* no) ...@@ -313,6 +316,9 @@ Write_barriers::variable(Named_object* no)
int int
Write_barriers::statement(Block* block, size_t* pindex, Statement* s) Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
{ {
if (this->statements_added_.find(s) != this->statements_added_.end())
return TRAVERSE_SKIP_COMPONENTS;
switch (s->classification()) switch (s->classification())
{ {
default: default:
...@@ -355,7 +361,7 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s) ...@@ -355,7 +361,7 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
Function* function = this->function_; Function* function = this->function_;
Location loc = init->location(); Location loc = init->location();
Statement_inserter inserter(block, pindex); Statement_inserter inserter(block, pindex, &this->statements_added_);
// Insert the variable declaration statement with no // Insert the variable declaration statement with no
// initializer, so that the variable exists. // initializer, so that the variable exists.
...@@ -370,6 +376,7 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s) ...@@ -370,6 +376,7 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
&inserter, &inserter,
ref, init, ref, init,
loc); loc);
this->statements_added_.insert(assign);
// Replace the old variable declaration statement with the new // Replace the old variable declaration statement with the new
// initialization. // initialization.
...@@ -391,12 +398,14 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s) ...@@ -391,12 +398,14 @@ Write_barriers::statement(Block* block, size_t* pindex, Statement* s)
// Change the assignment to use a write barrier. // Change the assignment to use a write barrier.
Function* function = this->function_; Function* function = this->function_;
Location loc = as->location(); Location loc = as->location();
Statement_inserter inserter = Statement_inserter(block, pindex); Statement_inserter inserter =
Statement_inserter(block, pindex, &this->statements_added_);
Statement* assign = this->gogo_->assign_with_write_barrier(function, Statement* assign = this->gogo_->assign_with_write_barrier(function,
block, block,
&inserter, &inserter,
lhs, rhs, lhs, rhs,
loc); loc);
this->statements_added_.insert(assign);
block->replace_statement(*pindex, assign); block->replace_statement(*pindex, assign);
} }
break; break;
......
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