This patch to the Go frontend reworks the static initializer code.  It
renames is_immutable to is_static_initializer to try to capture what
it really means.  It is more precise about when an address expression,
or a binary expression, can be a static initializer.  Stop checking
whether a type has pointers when deciding whether an initializer must
be read-write, just check whether it is being used to initialize a
global variable.  To make that work set the Translate_context function
to NULL for a global variable with a static initializer.

The effect of this is to let more global variables be initialized
directly, rather than being initialized in the generated init
function.

I'm doing this now because it is a prerequisite to copying the signal
table from the Go 1.7 runtime, as the signal table has to be
initialized before init functions are run.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 241868)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -1,4 +1,4 @@
-afe0456d25e3c6c0d91a8fd4c0fdfdbaa35cc251
+cac897bd27885c18a16dacfe27d5efd4526455c5
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc    (revision 241740)
+++ gcc/go/gofrontend/expressions.cc    (working copy)
@@ -536,10 +536,6 @@ class Error_expression : public Expressi
   { return true; }
 
   bool
-  do_is_immutable() const
-  { return true; }
-
-  bool
   do_numeric_constant_value(Numeric_constant* nc) const
   {
     nc->set_unsigned_long(NULL, 0);
@@ -1374,7 +1370,7 @@ class Func_code_reference_expression : p
   { return TRAVERSE_CONTINUE; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -1520,7 +1516,7 @@ class Boolean_expression : public Expres
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -1889,7 +1885,7 @@ class Integer_expression : public Expres
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -2285,7 +2281,7 @@ class Float_expression : public Expressi
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -2475,7 +2471,7 @@ class Complex_expression : public Expres
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -2691,7 +2687,7 @@ class Const_expression : public Expressi
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -3047,7 +3043,7 @@ class Nil_expression : public Expression
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -3284,10 +3280,11 @@ Type_conversion_expression::do_is_consta
   return true;
 }
 
-// Return whether a type conversion is immutable.
+// Return whether a type conversion can be used in a constant
+// initializer.
 
 bool
-Type_conversion_expression::do_is_immutable() const
+Type_conversion_expression::do_is_static_initializer() const
 {
   Type* type = this->type_;
   Type* expr_type = this->expr_->type();
@@ -3296,7 +3293,7 @@ Type_conversion_expression::do_is_immuta
       || expr_type->interface_type() != NULL)
     return false;
 
-  if (!this->expr_->is_immutable())
+  if (!this->expr_->is_static_initializer())
     return false;
 
   if (Type::are_identical(type, expr_type, false, NULL))
@@ -3542,10 +3539,11 @@ Unsafe_type_conversion_expression::do_tr
   return TRAVERSE_CONTINUE;
 }
 
-// Return whether an unsafe type conversion is immutable.
+// Return whether an unsafe type conversion can be used as a constant
+// initializer.
 
 bool
-Unsafe_type_conversion_expression::do_is_immutable() const
+Unsafe_type_conversion_expression::do_is_static_initializer() const
 {
   Type* type = this->type_;
   Type* expr_type = this->expr_->type();
@@ -3554,7 +3552,7 @@ Unsafe_type_conversion_expression::do_is
       || expr_type->interface_type() != NULL)
     return false;
 
-  if (!this->expr_->is_immutable())
+  if (!this->expr_->is_static_initializer())
     return false;
 
   if (Type::are_convertible(type, expr_type, NULL))
@@ -3855,6 +3853,44 @@ Unary_expression::do_is_constant() const
     return this->expr_->is_constant();
 }
 
+// Return whether a unary expression can be used as a constant
+// initializer.
+
+bool
+Unary_expression::do_is_static_initializer() const
+{
+  if (this->op_ == OPERATOR_MULT)
+    return false;
+  else if (this->op_ == OPERATOR_AND)
+    {
+      // The address of a global variable can used as a static
+      // initializer.
+      Var_expression* ve = this->expr_->var_expression();
+      if (ve != NULL)
+       {
+         Named_object* no = ve->named_object();
+         return no->is_variable() && no->var_value()->is_global();
+       }
+
+      // The address of a composite literal can be used as a static
+      // initializer if the composite literal is itself usable as a
+      // static initializer.
+      if (this->expr_->is_composite_literal()
+         && this->expr_->is_static_initializer())
+       return true;
+
+      // The address of a string constant can be used as a static
+      // initializer.  This can not be written in Go itself but this
+      // is used when building a type descriptor.
+      if (this->expr_->string_expression() != NULL)
+       return true;
+
+      return false;
+    }
+  else
+    return this->expr_->is_static_initializer();
+}
+
 // Apply unary opcode OP to UNC, setting NC.  Return true if this
 // could be done, false if not.  Issue errors for overflow.
 
@@ -4207,7 +4243,7 @@ Unary_expression::do_get_backend(Transla
          // constructor will not do what the programmer expects.
 
           go_assert(!this->expr_->is_composite_literal()
-                    || this->expr_->is_immutable());
+                    || this->expr_->is_static_initializer());
          if (this->expr_->classification() == EXPRESSION_UNARY)
            {
              Unary_expression* ue =
@@ -4245,8 +4281,7 @@ Unary_expression::do_get_backend(Transla
              // initialize the value once, so we can use this directly
              // rather than copying it.  In that case we can't make it
              // read-only, because the program is permitted to change it.
-             copy_to_heap = (at->element_type()->has_pointer()
-                             && !context->is_const());
+             copy_to_heap = context->function() != NULL;
            }
          Bvariable* implicit =
            gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap,
@@ -4257,8 +4292,8 @@ Unary_expression::do_get_backend(Transla
          bexpr = gogo->backend()->var_expression(implicit, loc);
        }
       else if ((this->expr_->is_composite_literal()
-           || this->expr_->string_expression() != NULL)
-          && this->expr_->is_immutable())
+               || this->expr_->string_expression() != NULL)
+              && this->expr_->is_static_initializer())
         {
          // Build a decl for a constant constructor.
           snprintf(buf, sizeof buf, "C%u", counter);
@@ -4426,6 +4461,33 @@ Binary_expression::do_traverse(Traverse*
   return Expression::traverse(&this->right_, traverse);
 }
 
+// Return whether this expression may be used as a static initializer.
+
+bool
+Binary_expression::do_is_static_initializer() const
+{
+  if (!this->left_->is_static_initializer()
+      || !this->right_->is_static_initializer())
+    return false;
+
+  // Addresses can be static initializers, but we can't implement
+  // arbitray binary expressions of them.
+  Unary_expression* lu = this->left_->unary_expression();
+  Unary_expression* ru = this->right_->unary_expression();
+  if (lu != NULL && lu->op() == OPERATOR_AND)
+    {
+      if (ru != NULL && ru->op() == OPERATOR_AND)
+       return this->op_ == OPERATOR_MINUS;
+      else
+       return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS;
+    }
+  else if (ru != NULL && ru->op() == OPERATOR_AND)
+    return this->op_ == OPERATOR_PLUS || this->op_ == OPERATOR_MINUS;
+
+  // Other cases should resolve in the backend.
+  return true;
+}
+
 // Return the type to use for a binary operation on operands of
 // LEFT_TYPE and RIGHT_TYPE.  These are the types of constants and as
 // such may be NULL or abstract.
@@ -6325,13 +6387,13 @@ String_concat_expression::do_is_constant
 }
 
 bool
-String_concat_expression::do_is_immutable() const
+String_concat_expression::do_is_static_initializer() const
 {
   for (Expression_list::const_iterator pe = this->exprs_->begin();
        pe != this->exprs_->end();
        ++pe)
     {
-      if (!(*pe)->is_immutable())
+      if (!(*pe)->is_static_initializer())
        return false;
     }
   return true;
@@ -12275,10 +12337,10 @@ Struct_construction_expression::is_const
   return true;
 }
 
-// Return whether this struct is immutable.
+// Return whether this struct can be used as a constant initializer.
 
 bool
-Struct_construction_expression::do_is_immutable() const
+Struct_construction_expression::do_is_static_initializer() const
 {
   if (this->vals() == NULL)
     return true;
@@ -12286,7 +12348,7 @@ Struct_construction_expression::do_is_im
        pv != this->vals()->end();
        ++pv)
     {
-      if (*pv != NULL && !(*pv)->is_immutable())
+      if (*pv != NULL && !(*pv)->is_static_initializer())
        return false;
     }
   return true;
@@ -12523,10 +12585,10 @@ Array_construction_expression::is_consta
   return true;
 }
 
-// Return whether this is an immutable array initializer.
+// Return whether this can be used a constant initializer.
 
 bool
-Array_construction_expression::do_is_immutable() const
+Array_construction_expression::do_is_static_initializer() const
 {
   if (this->vals() == NULL)
     return true;
@@ -12534,7 +12596,7 @@ Array_construction_expression::do_is_imm
        pv != this->vals()->end();
        ++pv)
     {
-      if (*pv != NULL && !(*pv)->is_immutable())
+      if (*pv != NULL && !(*pv)->is_static_initializer())
        return false;
     }
   return true;
@@ -12904,19 +12966,12 @@ Slice_construction_expression::do_get_ba
     }
 
   Location loc = this->location();
-  Array_type* array_type = this->type()->array_type();
-  Type* element_type = array_type->element_type();
 
-  bool is_constant_initializer = this->array_val_->is_immutable();
+  bool is_static_initializer = this->array_val_->is_static_initializer();
 
   // We have to copy the initial values into heap memory if we are in
-  // a function or if the values are not constants.  We also have to
-  // copy them if they may contain pointers in a non-constant context,
-  // as otherwise the garbage collector won't see them.
-  bool copy_to_heap = (context->function() != NULL
-                      || !is_constant_initializer
-                      || (element_type->has_pointer()
-                          && !context->is_const()));
+  // a function or if the values are not constants.
+  bool copy_to_heap = context->function() != NULL || !is_static_initializer;
 
   Expression* space;
 
@@ -14206,7 +14261,7 @@ class Type_descriptor_expression : publi
   { return Type::make_type_descriptor_ptr_type(); }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   void
@@ -14274,7 +14329,7 @@ class GC_symbol_expression : public Expr
   { return Type::lookup_integer_type("uintptr"); }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   void
@@ -14332,7 +14387,7 @@ class Type_info_expression : public Expr
 
  protected:
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -14913,7 +14968,7 @@ class Interface_mtable_expression : publ
   do_type();
 
   bool
-  is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   void
@@ -15104,7 +15159,7 @@ class Struct_field_offset_expression : p
 
  protected:
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h     (revision 241688)
+++ gcc/go/gofrontend/expressions.h     (working copy)
@@ -500,10 +500,20 @@ class Expression
   is_constant() const
   { return this->do_is_constant(); }
 
-  // Return whether this is an immutable expression.
+  // Return whether this expression can be used as a static
+  // initializer.  This is true for an expression that has only
+  // numbers and pointers to global variables or composite literals
+  // that do not require runtime initialization.  It is false if we
+  // must generate code to compute this expression when it is used to
+  // initialize a global variable.  This is not a language-level
+  // concept, but an implementation-level one.  If this expression is
+  // used to initialize a global variable, this is true if we can pass
+  // an initializer to the backend, false if we must generate code to
+  // initialize the variable.  It is always safe for this method to
+  // return false, but the resulting code may be less efficient.
   bool
-  is_immutable() const
-  { return this->do_is_immutable(); }
+  is_static_initializer() const
+  { return this->do_is_static_initializer(); }
 
   // If this is not a numeric constant, return false.  If it is one,
   // return true, and set VAL to hold the value.
@@ -991,9 +1001,10 @@ class Expression
   do_is_constant() const
   { return false; }
 
-  // Return whether this is an immutable expression.
+  // Return whether this expression can be used as a constant
+  // initializer.
   virtual bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return false; }
 
   // Return whether this is a constant expression of numeric type, and
@@ -1508,7 +1519,7 @@ class String_expression : public Express
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -1595,7 +1606,7 @@ class Type_conversion_expression : publi
   do_is_constant() const;
 
   bool
-  do_is_immutable() const;
+  do_is_static_initializer() const;
 
   bool
   do_numeric_constant_value(Numeric_constant*) const;
@@ -1659,7 +1670,7 @@ class Unsafe_type_conversion_expression
   do_traverse(Traverse* traverse);
 
   bool
-  do_is_immutable() const;
+  do_is_static_initializer() const;
 
   Type*
   do_type()
@@ -1770,11 +1781,7 @@ class Unary_expression : public Expressi
   do_is_constant() const;
 
   bool
-  do_is_immutable() const
-  {
-    return (this->expr_->is_immutable()
-           || (this->op_ == OPERATOR_AND && this->expr_->is_variable()));
-  }
+  do_is_static_initializer() const;
 
   bool
   do_numeric_constant_value(Numeric_constant*) const;
@@ -1913,8 +1920,7 @@ class Binary_expression : public Express
   { return this->left_->is_constant() && this->right_->is_constant(); }
 
   bool
-  do_is_immutable() const
-  { return this->left_->is_immutable() && this->right_->is_immutable(); }
+  do_is_static_initializer() const;
 
   bool
   do_numeric_constant_value(Numeric_constant*) const;
@@ -2029,7 +2035,7 @@ class String_concat_expression : public
   do_is_constant() const;
 
   bool
-  do_is_immutable() const;
+  do_is_static_initializer() const;
 
   Type*
   do_type();
@@ -3295,7 +3301,7 @@ class Struct_construction_expression : p
   do_traverse(Traverse* traverse);
 
   bool
-  do_is_immutable() const;
+  do_is_static_initializer() const;
 
   Type*
   do_type()
@@ -3370,7 +3376,7 @@ protected:
   do_traverse(Traverse* traverse);
 
   bool
-  do_is_immutable() const;
+  do_is_static_initializer() const;
 
   Type*
   do_type()
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc   (revision 241341)
+++ gcc/go/gofrontend/gogo.cc   (working copy)
@@ -1299,29 +1299,35 @@ Gogo::write_globals()
               // The initializer is constant if it is the zero-value of the
               // variable's type or if the initial value is an immutable value
               // that is not copied to the heap.
-              bool is_constant_initializer = false;
+              bool is_static_initializer = false;
               if (var->init() == NULL)
-                is_constant_initializer = true;
+                is_static_initializer = true;
               else
                 {
                   Type* var_type = var->type();
                   Expression* init = var->init();
                   Expression* init_cast =
                       Expression::make_cast(var_type, init, var->location());
-                  is_constant_initializer =
-                      init_cast->is_immutable() && !var_type->has_pointer();
+                  is_static_initializer = init_cast->is_static_initializer();
                 }
 
              // Non-constant variable initializations might need to create
              // temporary variables, which will need the initialization
              // function as context.
-              if (!is_constant_initializer && init_fndecl == NULL)
-               init_fndecl = this->initialization_function_decl();
-              Bexpression* var_binit = var->get_init(this, init_fndecl);
+             Named_object* var_init_fn;
+             if (is_static_initializer)
+               var_init_fn = NULL;
+             else
+               {
+                 if (init_fndecl == NULL)
+                   init_fndecl = this->initialization_function_decl();
+                 var_init_fn = init_fndecl;
+               }
+              Bexpression* var_binit = var->get_init(this, var_init_fn);
 
               if (var_binit == NULL)
                ;
-             else if (is_constant_initializer)
+             else if (is_static_initializer)
                {
                  if (expression_requires(var->init(), NULL,
                                          this->var_depends_on(var), no))

Reply via email to