diff mbox

Go patch committed: Use backend interface for unary expressions

Message ID mcrwqhpknbv.fsf@iant-glaptop.roam.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor Jan. 24, 2014, 10:49 p.m. UTC
This patch from Chris Manghane changes the Go frontend to use the
backend interface for unary expressions.  Bootstrapped and ran Go
testsuite on x86_64-unknown-linux-gnu.  Committed to mainline.

Ian


2014-01-24  Chris Manghane  <cmang@google.com>

	* go-gcc.cc (Gcc_backend::unary_expression): New function.
diff mbox

Patch

Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 206904)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -1464,6 +1464,10 @@  class Func_code_reference_expression : p
   do_traverse(Traverse*)
   { return TRAVERSE_CONTINUE; }
 
+  bool
+  do_is_immutable() const
+  { return true; }
+
   Type*
   do_type()
   { return Type::make_pointer_type(Type::make_void_type()); }
@@ -2941,6 +2945,10 @@  class Nil_expression : public Expression
   do_is_constant() const
   { return true; }
 
+  bool
+  do_is_immutable() const
+  { return true; }
+
   Type*
   do_type()
   { return Type::make_nil_type(); }
@@ -3682,10 +3690,17 @@  class Unary_expression : public Expressi
   Expression*
   do_lower(Gogo*, Named_object*, Statement_inserter*, int);
 
+  Expression*
+  do_flatten(Gogo*, Named_object*, Statement_inserter*);
+
   bool
   do_is_constant() const;
 
   bool
+  do_is_immutable() const
+  { return this->expr_->is_immutable(); }
+
+  bool
   do_numeric_constant_value(Numeric_constant*) const;
 
   Type*
@@ -3806,6 +3821,45 @@  Unary_expression::do_lower(Gogo*, Named_
   return this;
 }
 
+// Flatten expression if a nil check must be performed and create temporary
+// variables if necessary.
+
+Expression*
+Unary_expression::do_flatten(Gogo* gogo, Named_object*,
+                             Statement_inserter* inserter)
+{
+  Location location = this->location();
+  if (this->op_ == OPERATOR_MULT
+      && !this->expr_->is_variable())
+    {
+      go_assert(this->expr_->type()->points_to() != NULL);
+      Type* ptype = this->expr_->type()->points_to();
+      if (!ptype->is_void_type())
+        {
+          Btype* pbtype = ptype->get_backend(gogo);
+          size_t s = gogo->backend()->type_size(pbtype);
+          if (s >= 4096 || this->issue_nil_check_)
+            {
+              Temporary_statement* temp =
+                  Statement::make_temporary(NULL, this->expr_, location);
+              inserter->insert(temp);
+              this->expr_ =
+                  Expression::make_temporary_reference(temp, location);
+            }
+        }
+    }
+
+  if (this->create_temp_ && !this->expr_->is_variable())
+    {
+      Temporary_statement* temp =
+          Statement::make_temporary(NULL, this->expr_, location);
+      inserter->insert(temp);
+      this->expr_ = Expression::make_temporary_reference(temp, location);
+    }
+
+  return this;
+}
+
 // Return whether a unary expression is a constant.
 
 bool
@@ -3821,8 +3875,8 @@  Unary_expression::do_is_constant() const
   else if (this->op_ == OPERATOR_AND)
     {
       // Taking the address of a variable is constant if it is a
-      // global variable, not constant otherwise.  In other cases
-      // taking the address is probably not a constant.
+      // global variable, not constant otherwise.  In other cases taking the
+      // address is probably not a constant.
       Var_expression* ve = this->expr_->var_expression();
       if (ve != NULL)
 	{
@@ -4151,58 +4205,40 @@  Unary_expression::do_get_tree(Translate_
 	{
 	  Temporary_statement* temp = sut->temporary();
 	  Bvariable* bvar = temp->get_backend_variable(context);
-	  tree var_tree = var_to_tree(bvar);
-	  Expression* val = sut->expression();
-	  tree val_tree = val->get_tree(context);
-	  if (var_tree == error_mark_node || val_tree == error_mark_node)
-	    return error_mark_node;
-	  tree addr_tree = build_fold_addr_expr_loc(loc.gcc_location(),
-						    var_tree);
-	  return build2_loc(loc.gcc_location(), COMPOUND_EXPR,
-			    TREE_TYPE(addr_tree),
-			    build2_loc(sut->location().gcc_location(),
-				       MODIFY_EXPR, void_type_node,
-				       var_tree, val_tree),
-			    addr_tree);
+          Bexpression* bvar_expr = gogo->backend()->var_expression(bvar, loc);
+
+          Expression* val = sut->expression();
+          Bexpression* bval = tree_to_expr(val->get_tree(context));
+
+          Bstatement* bassign =
+              gogo->backend()->assignment_statement(bvar_expr, bval, loc);
+          Bexpression* bvar_addr =
+              gogo->backend()->address_expression(bvar_expr, loc);
+          Bexpression* ret =
+              gogo->backend()->compound_expression(bassign, bvar_addr, loc);
+          return expr_to_tree(ret);
 	}
     }
 
+  Bexpression* ret;
   tree expr = this->expr_->get_tree(context);
-  if (expr == error_mark_node)
-    return error_mark_node;
-
+  Bexpression* bexpr = tree_to_expr(expr);
+  Btype* btype = this->expr_->type()->get_backend(gogo);
   switch (this->op_)
     {
     case OPERATOR_PLUS:
-      return expr;
+      ret = bexpr;
+      break;
 
     case OPERATOR_MINUS:
-      {
-	tree type = TREE_TYPE(expr);
-	tree compute_type = excess_precision_type(type);
-	if (compute_type != NULL_TREE)
-	  expr = ::convert(compute_type, expr);
-	tree ret = fold_build1_loc(loc.gcc_location(), NEGATE_EXPR,
-				   (compute_type != NULL_TREE
-				    ? compute_type
-				    : type),
-				   expr);
-	if (compute_type != NULL_TREE)
-	  ret = ::convert(type, ret);
-	return ret;
-      }
+      ret = gogo->backend()->unary_expression(this->op_, bexpr, loc);
+      ret = gogo->backend()->convert_expression(btype, ret, loc);
+      break;
 
     case OPERATOR_NOT:
-      if (TREE_CODE(TREE_TYPE(expr)) == BOOLEAN_TYPE)
-	return fold_build1_loc(loc.gcc_location(), TRUTH_NOT_EXPR,
-                               TREE_TYPE(expr), expr);
-      else
-	return fold_build2_loc(loc.gcc_location(), NE_EXPR, boolean_type_node,
-                               expr, build_int_cst(TREE_TYPE(expr), 0));
-
     case OPERATOR_XOR:
-      return fold_build1_loc(loc.gcc_location(), BIT_NOT_EXPR, TREE_TYPE(expr),
-                             expr);
+      ret = gogo->backend()->unary_expression(this->op_, bexpr, loc);
+      break;
 
     case OPERATOR_AND:
       if (!this->create_temp_)
@@ -4211,127 +4247,91 @@  Unary_expression::do_get_tree(Translate_
 	  // where we would see one should have been moved onto the
 	  // heap at parse time.  Taking the address of a nonconstant
 	  // constructor will not do what the programmer expects.
-	  go_assert(TREE_CODE(expr) != CONSTRUCTOR || TREE_CONSTANT(expr));
-	  go_assert(TREE_CODE(expr) != ADDR_EXPR);
+
+          go_assert(!this->expr_->is_composite_literal()
+                    || this->expr_->is_immutable());
+          Unary_expression* ue = static_cast<Unary_expression*>(this->expr_);
+          go_assert(ue == NULL || ue->op() != OPERATOR_AND);
 	}
 
       // Build a decl for a constant constructor.
-      if (TREE_CODE(expr) == CONSTRUCTOR && TREE_CONSTANT(expr))
-	{
-	  tree decl = build_decl(this->location().gcc_location(), VAR_DECL,
-				 create_tmp_var_name("C"), TREE_TYPE(expr));
-	  DECL_EXTERNAL(decl) = 0;
-	  TREE_PUBLIC(decl) = 0;
-	  TREE_READONLY(decl) = 1;
-	  TREE_CONSTANT(decl) = 1;
-	  TREE_STATIC(decl) = 1;
-	  TREE_ADDRESSABLE(decl) = 1;
-	  DECL_ARTIFICIAL(decl) = 1;
-	  DECL_INITIAL(decl) = expr;
-	  rest_of_decl_compilation(decl, 1, 0);
-	  expr = decl;
-	}
-
-      if (this->create_temp_
-	  && !TREE_ADDRESSABLE(TREE_TYPE(expr))
-	  && (TREE_CODE(expr) == CONST_DECL || !DECL_P(expr))
-	  && TREE_CODE(expr) != INDIRECT_REF
-	  && TREE_CODE(expr) != COMPONENT_REF)
-	{
-	  if (current_function_decl != NULL)
-	    {
-	      tree tmp = create_tmp_var(TREE_TYPE(expr), get_name(expr));
-	      DECL_IGNORED_P(tmp) = 1;
-	      DECL_INITIAL(tmp) = expr;
-	      TREE_ADDRESSABLE(tmp) = 1;
-	      return build2_loc(loc.gcc_location(), COMPOUND_EXPR,
-				build_pointer_type(TREE_TYPE(expr)),
-				build1_loc(loc.gcc_location(), DECL_EXPR,
-					   void_type_node, tmp),
-				build_fold_addr_expr_loc(loc.gcc_location(),
-							 tmp));
-	    }
-	  else
-	    {
-	      tree tmp = build_decl(loc.gcc_location(), VAR_DECL,
-				    create_tmp_var_name("A"), TREE_TYPE(expr));
-	      DECL_EXTERNAL(tmp) = 0;
-	      TREE_PUBLIC(tmp) = 0;
-	      TREE_STATIC(tmp) = 1;
-	      DECL_ARTIFICIAL(tmp) = 1;
-	      TREE_ADDRESSABLE(tmp) = 1;
-	      tree make_tmp;
-	      if (!TREE_CONSTANT(expr))
-		make_tmp = fold_build2_loc(loc.gcc_location(), INIT_EXPR,
-					   void_type_node, tmp, expr);
-	      else
-		{
-		  TREE_READONLY(tmp) = 1;
-		  TREE_CONSTANT(tmp) = 1;
-		  DECL_INITIAL(tmp) = expr;
-		  make_tmp = NULL_TREE;
-		}
-	      rest_of_decl_compilation(tmp, 1, 0);
-	      tree addr = build_fold_addr_expr_loc(loc.gcc_location(), tmp);
-	      if (make_tmp == NULL_TREE)
-		return addr;
-	      return build2_loc(loc.gcc_location(), COMPOUND_EXPR,
-				TREE_TYPE(addr), make_tmp, addr);
-	    }
-	}
+      if ((this->expr_->is_composite_literal()
+           || this->expr_->string_expression() != NULL)
+          && this->expr_->is_immutable())
+        {
+          static unsigned int counter;
+          char buf[100];
+          snprintf(buf, sizeof buf, "C%u", counter);
+          ++counter;
+
+          Bvariable* decl =
+              gogo->backend()->immutable_struct(buf, true, false, btype, loc);
+          gogo->backend()->immutable_struct_set_init(decl, buf, true, false,
+                                                     btype, loc, bexpr);
+          bexpr = gogo->backend()->var_expression(decl, loc);
+        }
 
-      return build_fold_addr_expr_loc(loc.gcc_location(), expr);
+      go_assert(!this->create_temp_ || this->expr_->is_variable());
+      ret = gogo->backend()->address_expression(bexpr, loc);
+      break;
 
     case OPERATOR_MULT:
       {
-	go_assert(POINTER_TYPE_P(TREE_TYPE(expr)));
+        go_assert(this->expr_->type()->points_to() != NULL);
 
 	// If we are dereferencing the pointer to a large struct, we
 	// need to check for nil.  We don't bother to check for small
 	// structs because we expect the system to crash on a nil
 	// pointer dereference.	 However, if we know the address of this
 	// expression is being taken, we must always check for nil.
-	tree target_type_tree = TREE_TYPE(TREE_TYPE(expr));
-	if (!VOID_TYPE_P(target_type_tree))
+
+        Type* ptype = this->expr_->type()->points_to();
+        Btype* pbtype = ptype->get_backend(gogo);
+        if (!ptype->is_void_type())
 	  {
-	    HOST_WIDE_INT s = int_size_in_bytes(target_type_tree);
-	    if (s == -1 || s >= 4096 || this->issue_nil_check_)
+            size_t s = gogo->backend()->type_size(pbtype);
+	    if (s >= 4096 || this->issue_nil_check_)
 	      {
-		if (!DECL_P(expr))
-		  expr = save_expr(expr);
-		tree compare = fold_build2_loc(loc.gcc_location(), EQ_EXPR,
-					       boolean_type_node,
-					       expr,
-					       fold_convert(TREE_TYPE(expr),
-							    null_pointer_node));
+                go_assert(this->expr_->is_variable());
+
+                Expression* nil_expr = Expression::make_nil(loc);
+                Bexpression* nil = tree_to_expr(nil_expr->get_tree(context));
+                Bexpression* compare =
+                    gogo->backend()->binary_expression(OPERATOR_EQEQ, bexpr,
+                                                       nil, loc);
+
 		Expression* crash_expr =
 		    gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, loc);
-		tree crash = crash_expr->get_tree(context);
-		expr = fold_build2_loc(loc.gcc_location(), COMPOUND_EXPR,
-				       TREE_TYPE(expr), build3(COND_EXPR,
-							       void_type_node,
-							       compare, crash,
-							       NULL_TREE),
-				       expr);
+                Bexpression* crash =
+                    tree_to_expr(crash_expr->get_tree(context));
+                bexpr = gogo->backend()->conditional_expression(btype, compare,
+                                                                crash, bexpr,
+                                                                loc);
+
 	      }
 	  }
 
 	// If the type of EXPR is a recursive pointer type, then we
 	// need to insert a cast before indirecting.
-	if (VOID_TYPE_P(target_type_tree))
-	  {
-	    Type* pt = this->expr_->type()->points_to();
-	    tree ind = type_to_tree(pt->get_backend(gogo));
-	    expr = fold_convert_loc(loc.gcc_location(),
+        tree expr = expr_to_tree(bexpr);
+        tree target_type_tree = TREE_TYPE(TREE_TYPE(expr));
+        if (VOID_TYPE_P(target_type_tree))
+          {
+            tree ind = type_to_tree(pbtype);
+            expr = fold_convert_loc(loc.gcc_location(),
                                     build_pointer_type(ind), expr);
-	  }
+            bexpr = tree_to_expr(expr);
+          }
 
-	return build_fold_indirect_ref_loc(loc.gcc_location(), expr);
+        ret = gogo->backend()->indirect_expression(bexpr, false, loc);
       }
+      break;
 
     default:
       go_unreachable();
     }
+
+  return expr_to_tree(ret);
 }
 
 // Export a unary expression.
@@ -12232,6 +12232,9 @@  class Struct_construction_expression : p
   int
   do_traverse(Traverse* traverse);
 
+  bool
+  do_is_immutable() const;
+
   Type*
   do_type()
   { return this->type_; }
@@ -12334,6 +12337,23 @@  Struct_construction_expression::is_const
   return true;
 }
 
+// Return whether this struct is immutable.
+
+bool
+Struct_construction_expression::do_is_immutable() const
+{
+  if (this->vals_ == NULL)
+    return true;
+  for (Expression_list::const_iterator pv = this->vals_->begin();
+       pv != this->vals_->end();
+       ++pv)
+    {
+      if (*pv != NULL && !(*pv)->is_immutable())
+	return false;
+    }
+  return true;
+}
+
 // Final type determination.
 
 void
@@ -12546,6 +12566,9 @@  protected:
   int
   do_traverse(Traverse* traverse);
 
+  bool
+  do_is_immutable() const;
+
   Type*
   do_type()
   { return this->type_; }
@@ -12624,6 +12647,23 @@  Array_construction_expression::is_consta
   return true;
 }
 
+// Return whether this is an immutable array initializer.
+
+bool
+Array_construction_expression::do_is_immutable() const
+{
+  if (this->vals_ == NULL)
+    return true;
+  for (Expression_list::const_iterator pv = this->vals_->begin();
+       pv != this->vals_->end();
+       ++pv)
+    {
+      if (*pv != NULL && !(*pv)->is_immutable())
+	return false;
+    }
+  return true;
+}
+
 // Final type determination.
 
 void
@@ -14390,6 +14430,10 @@  class Type_descriptor_expression : publi
   do_type()
   { return Type::make_type_descriptor_ptr_type(); }
 
+  bool
+  do_is_immutable() const
+  { return true; }
+
   void
   do_determine_type(const Type_context*)
   { }
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h	(revision 206904)
+++ gcc/go/gofrontend/expressions.h	(working copy)
@@ -403,6 +403,11 @@  class Expression
   is_constant() const
   { return this->do_is_constant(); }
 
+  // Return whether this is an immutable expression.
+  bool
+  is_immutable() const
+  { return this->do_is_immutable(); }
+
   // If this is not a numeric constant, return false.  If it is one,
   // return true, and set VAL to hold the value.
   bool
@@ -758,6 +763,11 @@  class Expression
   do_is_constant() const
   { return false; }
 
+  // Return whether this is an immutable expression.
+  virtual bool
+  do_is_immutable() const
+  { return false; }
+
   // Return whether this is a constant expression of numeric type, and
   // set the Numeric_constant to the value.
   virtual bool
@@ -1196,6 +1206,10 @@  class String_expression : public Express
   { return true; }
 
   bool
+  do_is_immutable() const
+  { return true; }
+
+  bool
   do_string_constant_value(std::string* val) const
   {
     *val = this->val_;
Index: gcc/go/gofrontend/backend.h
===================================================================
--- gcc/go/gofrontend/backend.h	(revision 206904)
+++ gcc/go/gofrontend/backend.h	(working copy)
@@ -298,6 +298,12 @@  class Backend
                          Bexpression* then_expr, Bexpression* else_expr,
                          Location) = 0;
 
+  // Return an expression for the unary operation OP EXPR.
+  // Supported values of OP are (from operators.h):
+  //    MINUS, NOT, XOR.
+  virtual Bexpression*
+  unary_expression(Operator op, Bexpression* expr, Location) = 0;
+
   // Return an expression for the binary operation LEFT OP RIGHT.
   // Supported values of OP are (from operators.h):
   //    EQEQ, NOTEQ, LT, LE, GT, GE, PLUS, MINUS, OR, XOR, MULT, DIV, MOD,
Index: gcc/go/go-gcc.cc
===================================================================
--- gcc/go/go-gcc.cc	(revision 206904)
+++ gcc/go/go-gcc.cc	(working copy)
@@ -254,6 +254,9 @@  class Gcc_backend : public Backend
                          Location);
 
   Bexpression*
+  unary_expression(Operator, Bexpression*, Location);
+
+  Bexpression*
   binary_expression(Operator, Bexpression*, Bexpression*, Location);
 
   // Statements.
@@ -1081,6 +1084,47 @@  Gcc_backend::conditional_expression(Btyp
   return this->make_expression(ret);
 }
 
+// Return an expression for the unary operation OP EXPR.
+
+Bexpression*
+Gcc_backend::unary_expression(Operator op, Bexpression* expr, Location location)
+{
+  tree expr_tree = expr->get_tree();
+  if (expr_tree == error_mark_node
+      || TREE_TYPE(expr_tree) == error_mark_node)
+    return this->error_expression();
+
+  tree type_tree = TREE_TYPE(expr_tree);
+  enum tree_code code;
+  switch (op)
+    {
+    case OPERATOR_MINUS:
+      {
+        tree computed_type = excess_precision_type(type_tree);
+        if (computed_type != NULL_TREE)
+          {
+            expr_tree = convert(computed_type, expr_tree);
+            type_tree = computed_type;
+          }
+        code = NEGATE_EXPR;
+        break;
+      }
+    case OPERATOR_NOT:
+      code = TRUTH_NOT_EXPR;
+      break;
+    case OPERATOR_XOR:
+      code = BIT_NOT_EXPR;
+      break;
+    default:
+      gcc_unreachable();
+      break;
+    }
+
+  tree ret = fold_build1_loc(location.gcc_location(), code, type_tree,
+                             expr_tree);
+  return this->make_expression(ret);
+}
+
 // Convert a gofrontend operator to an equivalent tree_code.
 
 static enum tree_code