compiler: pass lvalue/rvalue context to back end for var exprs
authorThan McIntosh <thanm@google.com>
Tue, 6 Dec 2016 22:31:25 +0000 (22:31 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 6 Dec 2016 22:31:25 +0000 (22:31 +0000)
    Add a new flag on the Var_expression class that indicates
    whether the var reference appears in an "lvalue" context
    (for example, on the LHS of an assignment stmt) or an
    "rvalue" context (for example, as an argument of a call).

    Add a traversal pass that visits assignment stmt LHS subtrees
    so as to mark things prior to backend gen. Select the right
    context value in other places where Backend::var_expression is
    called.

    Reviewed-on: https://go-review.googlesource.com/33990

* go-gcc.cc (Gcc_backend::var_expression): Add Varexpr_context
parameter.

From-SVN: r243321

gcc/go/ChangeLog
gcc/go/go-gcc.cc
gcc/go/gofrontend/MERGE
gcc/go/gofrontend/backend.h
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/operator.h
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/types.cc

index bbae1a9f01756ac4d7324154ceba22f1419f2be1..0b116eb6f4e311ba7b469d88a7780c00c837545a 100644 (file)
@@ -1,3 +1,8 @@
+2016-12-06  Than McIntosh  <thanm@google.com>
+
+       * go-gcc.cc (Gcc_backend::var_expression): Add Varexpr_context
+       parameter.
+
 2016-11-22  Than McIntosh  <thanm@google.com>
 
        * go-gcc.cc (char_needs_encoding): Remove.
index dc0041339aaa81531b810f3db656c213d98600d4..f1ac52242a4bde3148e03ba737a8f50f00a954fb 100644 (file)
@@ -276,7 +276,7 @@ class Gcc_backend : public Backend
   { return this->make_expression(null_pointer_node); }
 
   Bexpression*
-  var_expression(Bvariable* var, Location);
+  var_expression(Bvariable* var, Varexpr_context, Location);
 
   Bexpression*
   indirect_expression(Btype*, Bexpression* expr, bool known_valid, Location);
@@ -1243,7 +1243,7 @@ Gcc_backend::zero_expression(Btype* btype)
 // An expression that references a variable.
 
 Bexpression*
-Gcc_backend::var_expression(Bvariable* var, Location location)
+Gcc_backend::var_expression(Bvariable* var, Varexpr_context, Location location)
 {
   tree ret = var->get_tree(location);
   if (ret == error_mark_node)
index 5529002b09003123e03f0a4c7d833589e9c1824a..0cb0f9c653f95ea65594d54a46ebd34d3ef2934a 100644 (file)
@@ -1,4 +1,4 @@
-b7bad96ce0af50a1129eaab9aa110d68a601917b
+2102112e26a21589455f940ec6b409766d942c62
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index e93cdfece763204f21442b8f227d60e2fe3ef0bb..e9a19128e5c2fa4119b323a27d095484dd69c814 100644 (file)
@@ -254,7 +254,7 @@ class Backend
 
   // Create a reference to a variable.
   virtual Bexpression*
-  var_expression(Bvariable* var, Location) = 0;
+  var_expression(Bvariable* var, Varexpr_context in_lvalue_pos, Location) = 0;
 
   // Create an expression that indirects through the pointer expression EXPR
   // (i.e., return the expression for *EXPR). KNOWN_VALID is true if the pointer
index 9740d324a865b963f7dbff989f7b57abda47a8aa..24f6b125c7d27e14b2356ee33f185c11c1ad134b 100644 (file)
@@ -760,7 +760,8 @@ Var_expression::do_get_backend(Translate_context* context)
   else
     go_unreachable();
 
-  Bexpression* ret = context->backend()->var_expression(bvar, loc);
+  Bexpression* ret =
+      context->backend()->var_expression(bvar, this->in_lvalue_pos_, loc);
   if (is_in_heap)
     ret = context->backend()->indirect_expression(btype, ret, true, loc);
   return ret;
@@ -887,7 +888,10 @@ Temporary_reference_expression::do_get_backend(Translate_context* context)
 {
   Gogo* gogo = context->gogo();
   Bvariable* bvar = this->statement_->get_backend_variable(context);
-  Bexpression* ret = gogo->backend()->var_expression(bvar, this->location());
+  Varexpr_context ve_ctxt = (this->is_lvalue_ ? VE_lvalue : VE_rvalue);
+
+  Bexpression* ret = gogo->backend()->var_expression(bvar, ve_ctxt,
+                                                     this->location());
 
   // The backend can't always represent the same set of recursive types
   // that the Go frontend can.  In some cases this means that a
@@ -958,11 +962,11 @@ Set_and_use_temporary_expression::do_get_backend(Translate_context* context)
   Location loc = this->location();
   Gogo* gogo = context->gogo();
   Bvariable* bvar = this->statement_->get_backend_variable(context);
-  Bexpression* var_ref = gogo->backend()->var_expression(bvar, loc);
+  Bexpression* lvar_ref = gogo->backend()->var_expression(bvar, VE_rvalue, loc);
 
   Bexpression* bexpr = this->expr_->get_backend(context);
-  Bstatement* set = gogo->backend()->assignment_statement(var_ref, bexpr, loc);
-  var_ref = gogo->backend()->var_expression(bvar, loc);
+  Bstatement* set = gogo->backend()->assignment_statement(lvar_ref, bexpr, loc);
+  Bexpression* var_ref = gogo->backend()->var_expression(bvar, VE_lvalue, loc);
   Bexpression* ret = gogo->backend()->compound_expression(set, var_ref, loc);
   return ret;
 }
@@ -1065,11 +1069,12 @@ Sink_expression::do_get_backend(Translate_context* context)
       this->bvar_ =
        gogo->backend()->temporary_variable(fn_ctx, context->bblock(), bt, NULL,
                                            false, loc, &decl);
-      Bexpression* var_ref = gogo->backend()->var_expression(this->bvar_, loc);
+      Bexpression* var_ref =
+          gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
       var_ref = gogo->backend()->compound_expression(decl, var_ref, loc);
       return var_ref;
     }
-  return gogo->backend()->var_expression(this->bvar_, loc);
+  return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
 }
 
 // Ast dump for sink expression.
@@ -1276,7 +1281,7 @@ Func_descriptor_expression::do_get_backend(Translate_context* context)
   Named_object* no = this->fn_;
   Location loc = no->location();
   if (this->dvar_ != NULL)
-    return context->backend()->var_expression(this->dvar_, loc);
+    return context->backend()->var_expression(this->dvar_, VE_rvalue, loc);
 
   Gogo* gogo = context->gogo();
   std::string var_name;
@@ -1330,7 +1335,7 @@ Func_descriptor_expression::do_get_backend(Translate_context* context)
     }
 
   this->dvar_ = bvar;
-  return gogo->backend()->var_expression(bvar, loc);
+  return gogo->backend()->var_expression(bvar, VE_rvalue, loc);
 }
 
 // Print a function descriptor expression.
@@ -4207,7 +4212,8 @@ Unary_expression::do_get_backend(Translate_context* context)
        {
          Temporary_statement* temp = sut->temporary();
          Bvariable* bvar = temp->get_backend_variable(context);
-          Bexpression* bvar_expr = gogo->backend()->var_expression(bvar, loc);
+          Bexpression* bvar_expr =
+              gogo->backend()->var_expression(bvar, VE_lvalue, loc);
           Bexpression* bval = sut->expression()->get_backend(context);
 
           Bstatement* bassign =
@@ -4294,7 +4300,7 @@ Unary_expression::do_get_backend(Translate_context* context)
          gogo->backend()->implicit_variable_set_init(implicit, buf, btype,
                                                      true, copy_to_heap, false,
                                                      bexpr);
-         bexpr = gogo->backend()->var_expression(implicit, loc);
+         bexpr = gogo->backend()->var_expression(implicit, VE_lvalue, loc);
 
          // If we are not copying a slice initializer to the heap,
          // then it can be changed by the program, so if it can
@@ -4304,7 +4310,7 @@ Unary_expression::do_get_backend(Translate_context* context)
              && this->expr_->type()->has_pointer())
            {
              Bexpression* root =
-               gogo->backend()->var_expression(implicit, loc);
+                  gogo->backend()->var_expression(implicit, VE_lvalue, loc);
              root = gogo->backend()->address_expression(root, loc);
              Type* type = Type::make_pointer_type(this->expr_->type());
              gogo->add_gc_root(Expression::make_backend(root, type, loc));
@@ -4324,7 +4330,7 @@ Unary_expression::do_get_backend(Translate_context* context)
                                                 true, false, btype, loc);
           gogo->backend()->immutable_struct_set_init(decl, buf, true, false,
                                                      btype, loc, bexpr);
-          bexpr = gogo->backend()->var_expression(decl, loc);
+          bexpr = gogo->backend()->var_expression(decl, VE_lvalue, loc);
         }
 
       go_assert(!this->create_temp_ || this->expr_->is_variable());
@@ -14116,7 +14122,7 @@ Heap_expression::do_get_backend(Translate_context* context)
   Bvariable* space_temp =
     gogo->backend()->temporary_variable(fndecl, context->bblock(), btype,
                                        space, true, loc, &decl);
-  space = gogo->backend()->var_expression(space_temp, loc);
+  space = gogo->backend()->var_expression(space_temp, VE_lvalue, loc);
   Btype* expr_btype = this->expr_->type()->get_backend(gogo);
   Bexpression* ref =
     gogo->backend()->indirect_expression(expr_btype, space, true, loc);
@@ -14124,7 +14130,7 @@ Heap_expression::do_get_backend(Translate_context* context)
   Bexpression* bexpr = this->expr_->get_backend(context);
   Bstatement* assn = gogo->backend()->assignment_statement(ref, bexpr, loc);
   decl = gogo->backend()->compound_statement(decl, assn);
-  space = gogo->backend()->var_expression(space_temp, loc);
+  space = gogo->backend()->var_expression(space_temp, VE_rvalue, loc);
   return gogo->backend()->compound_expression(decl, space, loc);
 }
 
@@ -15063,7 +15069,8 @@ Interface_mtable_expression::do_get_backend(Translate_context* context)
   Gogo* gogo = context->gogo();
   Location loc = Linemap::predeclared_location();
   if (this->bvar_ != NULL)
-    return gogo->backend()->var_expression(this->bvar_, this->location());
+    return gogo->backend()->var_expression(this->bvar_, VE_rvalue,
+                                           this->location());
 
   const Typed_identifier_list* interface_methods = this->itype_->methods();
   go_assert(!interface_methods->empty());
@@ -15099,7 +15106,8 @@ Interface_mtable_expression::do_get_backend(Translate_context* context)
       this->bvar_ =
           gogo->backend()->immutable_struct_reference(mangled_name, asm_name,
                                                       btype, loc);
-      return gogo->backend()->var_expression(this->bvar_, this->location());
+      return gogo->backend()->var_expression(this->bvar_, VE_rvalue,
+                                             this->location());
     }
 
   // The first element is the type descriptor.
@@ -15147,7 +15155,7 @@ Interface_mtable_expression::do_get_backend(Translate_context* context)
                                                  !is_public, btype, loc);
   gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false,
                                              !is_public, btype, loc, ctor);
-  return gogo->backend()->var_expression(this->bvar_, loc);
+  return gogo->backend()->var_expression(this->bvar_, VE_lvalue, loc);
 }
 
 void
index f31d4a6d0077b65a15fbd1ff526e50cd8d4292f5..98e21151cc602fb8674eed774706dd2c665e1624 100644 (file)
@@ -1282,7 +1282,7 @@ class Var_expression : public Expression
  public:
   Var_expression(Named_object* variable, Location location)
     : Expression(EXPRESSION_VAR_REFERENCE, location),
-      variable_(variable)
+      variable_(variable), in_lvalue_pos_(VE_rvalue)
   { }
 
   // Return the variable.
@@ -1290,6 +1290,16 @@ class Var_expression : public Expression
   named_object() const
   { return this->variable_; }
 
+  // Does this var expression appear in an lvalue (assigned-to) context?
+  bool
+  in_lvalue_pos() const
+  { return this->in_lvalue_pos_ == VE_lvalue; }
+
+  // Mark a var_expression as appearing in an lvalue context.
+  void
+  set_in_lvalue_pos()
+  { this->in_lvalue_pos_ = VE_lvalue; }
+
  protected:
   Expression*
   do_lower(Gogo*, Named_object*, Statement_inserter*, int);
@@ -1320,6 +1330,8 @@ class Var_expression : public Expression
  private:
   // The variable we are referencing.
   Named_object* variable_;
+  // Set to TRUE if var expression appears in lvalue context
+  Varexpr_context in_lvalue_pos_;
 };
 
 // A reference to a variable within an enclosing function.
index d685bcaeef668a5f37debbb13c68f601083f13ab..e9cc6b43f940882a5064acde1db5d3ec19f20431 100644 (file)
@@ -1369,7 +1369,7 @@ Gogo::write_globals()
                 {
                   Location loc = var->location();
                   Bexpression* var_expr =
-                      this->backend()->var_expression(bvar, loc);
+                      this->backend()->var_expression(bvar, VE_lvalue, loc);
                   var_init_stmt =
                       this->backend()->assignment_statement(var_expr, var_binit,
                                                             loc);
@@ -5734,7 +5734,8 @@ Function::return_value(Gogo* gogo, Named_object* named_function,
     {
       Named_object* no = (*this->results_)[i];
       Bvariable* bvar = no->get_backend_variable(gogo, named_function);
-      Bexpression* val = gogo->backend()->var_expression(bvar, location);
+      Bexpression* val = gogo->backend()->var_expression(bvar, VE_rvalue,
+                                                         location);
       if (no->result_var_value()->is_in_heap())
        {
          Btype* bt = no->result_var_value()->type()->get_backend(gogo);
@@ -6563,7 +6564,8 @@ Variable::get_init_block(Gogo* gogo, Named_object* function,
           Expression* val_expr =
               Expression::make_cast(this->type(), this->init_, loc);
           Bexpression* val = val_expr->get_backend(&context);
-          Bexpression* var_ref = gogo->backend()->var_expression(var_decl, loc);
+          Bexpression* var_ref =
+              gogo->backend()->var_expression(var_decl, VE_lvalue, loc);
           decl_init = gogo->backend()->assignment_statement(var_ref, val, loc);
        }
     }
index f3e0fd0743469622cee5d5a56f3775b8f6618122..e0a97d05f311565d6bc4acf5a8ad31551680c0c6 100644 (file)
@@ -63,4 +63,10 @@ enum Operator
   OPERATOR_RSQUARE     // ]
 };
 
+// Whether a variable expression appears in lvalue (assignment) context.
+enum Varexpr_context {
+  VE_rvalue,
+  VE_lvalue
+};
+
 #endif // !defined(GO_OPERATOR_H)
index e25fd6b3bdff5950edf0e3b535813f98654aec45..c7b4fe82cebe49ce270645deda7c2ba2453c739c 100644 (file)
@@ -825,6 +825,80 @@ Assignment_statement::do_flatten(Gogo*, Named_object*, Block*,
   return this;
 }
 
+
+// Helper class to locate a root Var_expression within an expression
+// tree and mark it as being in an "lvalue" or assignment
+// context. Examples:
+//
+//    x, y = 40, foo(w)
+//    x[2] = bar(v)
+//    x.z.w[blah(v + u)], y.another = 2, 3
+//
+// In the code above, vars "x" and "y" appear in lvalue / assignment
+// context, whereas the other vars "v", "u", etc are in rvalue context.
+//
+// Note: at the moment the Var_expression version of "do_copy()"
+// defaults to returning the original object, not a new object,
+// meaning that a given Var_expression can be referenced from more
+// than one place in the tree. This means that when we want to mark a
+// Var_expression as having lvalue semantics, we need to make a copy
+// of it. Example:
+//
+//    mystruct.myfield += 42
+//
+// When this is lowered to eliminate the += operator, we get a tree
+//
+//    mystruct.myfield = mystruct.field + 42
+//
+// in which the "mystruct" same Var_expression is referenced on both
+// LHS and RHS subtrees. This in turn means that if we try to mark the
+// LHS Var_expression the RHS Var_expression will also be marked.  To
+// address this issue, the code below clones any var_expression before
+// applying an lvalue marking.
+//
+
+class Mark_lvalue_varexprs : public Traverse
+{
+ public:
+  Mark_lvalue_varexprs()
+    : Traverse(traverse_expressions)
+  { }
+
+ protected:
+  int
+  expression(Expression**);
+
+ private:
+};
+
+int Mark_lvalue_varexprs::expression(Expression** ppexpr)
+{
+  Expression* e = *ppexpr;
+
+  Var_expression* ve = e->var_expression();
+  if (ve)
+    {
+      ve = new Var_expression(ve->named_object(), ve->location());
+      ve->set_in_lvalue_pos();
+      *ppexpr = ve;
+      return TRAVERSE_EXIT;
+    }
+
+  Field_reference_expression* fre = e->field_reference_expression();
+  if (fre != NULL)
+    return TRAVERSE_CONTINUE;
+
+  Array_index_expression* aie = e->array_index_expression();
+  if (aie != NULL)
+    {
+      Mark_lvalue_varexprs mlve;
+      aie->array()->traverse_subexpressions(&mlve);
+      return TRAVERSE_EXIT;
+    }
+
+  return TRAVERSE_EXIT;
+}
+
 // Convert an assignment statement to the backend representation.
 
 Bstatement*
@@ -836,6 +910,9 @@ Assignment_statement::do_get_backend(Translate_context* context)
       return context->backend()->expression_statement(rhs);
     }
 
+  Mark_lvalue_varexprs mlve;
+  Expression::traverse(&this->lhs_, &mlve);
+
   Bexpression* lhs = this->lhs_->get_backend(context);
   Expression* conv =
       Expression::convert_for_assignment(context->gogo(), this->lhs_->type(),
index 33d3460e49389d8d7fffcc31f58135fcf2ac69e1..d540acb35b3dcd01777555ef8028b1323dfa02a8 100644 (file)
@@ -1173,7 +1173,8 @@ Type::type_descriptor_pointer(Gogo* gogo, Location location)
       go_assert(t->type_descriptor_var_ != NULL);
     }
   Bexpression* var_expr =
-      gogo->backend()->var_expression(t->type_descriptor_var_, location);
+      gogo->backend()->var_expression(t->type_descriptor_var_,
+                                      VE_rvalue, location);
   return gogo->backend()->address_expression(var_expr, location);
 }
 
@@ -2146,7 +2147,7 @@ Type::gc_symbol_pointer(Gogo* gogo)
     }
   Location bloc = Linemap::predeclared_location();
   Bexpression* var_expr =
-      gogo->backend()->var_expression(t->gc_symbol_var_, bloc);
+      gogo->backend()->var_expression(t->gc_symbol_var_, VE_rvalue, bloc);
   return gogo->backend()->address_expression(var_expr, bloc);
 }