diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index e61dd3b5be6..180279efac6 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3992,6 +3992,10 @@ class Unary_expression : public Expression this->location()); } + bool + do_must_eval_subexpressions_in_order(int*) const + { return this->op_ == OPERATOR_MULT; } + bool do_is_addressable() const { return this->op_ == OPERATOR_MULT; } @@ -10036,6 +10040,13 @@ class Array_index_expression : public Expression this->location()); } + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + bool do_is_addressable() const; @@ -10461,6 +10472,13 @@ class String_index_expression : public Expression this->location()); } + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + tree do_get_tree(Translate_context*); diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h index 8cdf94d2e78..a1f03c429fc 100644 --- a/gcc/go/gofrontend/expressions.h +++ b/gcc/go/gofrontend/expressions.h @@ -582,6 +582,18 @@ class Expression must_eval_in_order() const { return this->do_must_eval_in_order(); } + // Return whether subexpressions of this expression must be + // evaluated in order. This is true of index expressions and + // pointer indirections. This sets *SKIP to the number of + // subexpressions to skip during traversing, as index expressions + // only requiring moving the index, not the array. + bool + must_eval_subexpressions_in_order(int* skip) const + { + *skip = 0; + return this->do_must_eval_subexpressions_in_order(skip); + } + // Return the tree for this expression. tree get_tree(Translate_context*); @@ -717,6 +729,13 @@ class Expression do_must_eval_in_order() const { return false; } + // Child class implements whether this expressions requires that + // subexpressions be evaluated in order. The child implementation + // may set *SKIP if it should be non-zero. + virtual bool + do_must_eval_subexpressions_in_order(int* /* skip */) const + { return false; } + // Child class implements conversion to tree. virtual tree do_get_tree(Translate_context*) = 0; @@ -1526,6 +1545,13 @@ class Index_expression : public Parser_expression this->location()); } + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + void do_dump_expression(Ast_dump_context*) const; @@ -1623,6 +1649,13 @@ class Map_index_expression : public Expression this->location()); } + bool + do_must_eval_subexpressions_in_order(int* skip) const + { + *skip = 1; + return true; + } + // A map index expression is an lvalue but it is not addressable. tree diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index c079fdf285e..abc629e12c3 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -652,6 +652,47 @@ Statement::make_assignment(Expression* lhs, Expression* rhs, return new Assignment_statement(lhs, rhs, location); } +// The Move_subexpressions class is used to move all top-level +// subexpressions of an expression. This is used for things like +// index expressions in which we must evaluate the index value before +// it can be changed by a multiple assignment. + +class Move_subexpressions : public Traverse +{ + public: + Move_subexpressions(int skip, Block* block) + : Traverse(traverse_expressions), + skip_(skip), block_(block) + { } + + protected: + int + expression(Expression**); + + private: + // The number of subexpressions to skip moving. This is used to + // avoid moving the array itself, as we only need to move the index. + int skip_; + // The block where new temporary variables should be added. + Block* block_; +}; + +int +Move_subexpressions::expression(Expression** pexpr) +{ + if (this->skip_ > 0) + --this->skip_; + else if ((*pexpr)->temporary_reference_expression() == NULL) + { + source_location loc = (*pexpr)->location(); + Temporary_statement* temp = Statement::make_temporary(NULL, *pexpr, loc); + this->block_->add_statement(temp); + *pexpr = Expression::make_temporary_reference(temp, loc); + } + // We only need to move top-level subexpressions. + return TRAVERSE_SKIP_COMPONENTS; +} + // The Move_ordered_evals class is used to find any subexpressions of // an expression that have an evaluation order dependency. It creates // temporary variables to hold them. @@ -679,6 +720,15 @@ Move_ordered_evals::expression(Expression** pexpr) // We have to look at subexpressions first. if ((*pexpr)->traverse_subexpressions(this) == TRAVERSE_EXIT) return TRAVERSE_EXIT; + + int i; + if ((*pexpr)->must_eval_subexpressions_in_order(&i)) + { + Move_subexpressions ms(i, this->block_); + if ((*pexpr)->traverse_subexpressions(&ms) == TRAVERSE_EXIT) + return TRAVERSE_EXIT; + } + if ((*pexpr)->must_eval_in_order()) { source_location loc = (*pexpr)->location(); @@ -901,10 +951,10 @@ Tuple_assignment_statement::do_lower(Gogo*, Named_object*, Block* enclosing, // First move out any subexpressions on the left hand side. The // right hand side will be evaluated in the required order anyhow. Move_ordered_evals moe(b); - for (Expression_list::const_iterator plhs = this->lhs_->begin(); + for (Expression_list::iterator plhs = this->lhs_->begin(); plhs != this->lhs_->end(); ++plhs) - (*plhs)->traverse_subexpressions(&moe); + Expression::traverse(&*plhs, &moe); std::vector temps; temps.reserve(this->lhs_->size());