compiler: rework static initializer code
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 9 Nov 2016 21:41:58 +0000 (21:41 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 9 Nov 2016 21:41:58 +0000 (21:41 +0000)
    Rename is_immutable to is_static_initializer to try to capture what it
    really means.  Be more precise about when an address expression, or a
    binary expression, can be a static initializer.  Don't check 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.

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

From-SVN: r242024

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo.cc

index 9049bc37476f20c6b023bd8e4004c928cac6d189..40046113d3f45a035c1a66207c126c16ce569f7b 100644 (file)
@@ -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 69f4e016ba420c182298c2834b616f4d36bb213b..d6fa04b621551b738117423c318e5469fab7bafc 100644 (file)
@@ -535,10 +535,6 @@ class Error_expression : public Expression
   do_is_constant() const
   { return true; }
 
-  bool
-  do_is_immutable() const
-  { return true; }
-
   bool
   do_numeric_constant_value(Numeric_constant* nc) const
   {
@@ -1374,7 +1370,7 @@ class Func_code_reference_expression : public Expression
   { return TRAVERSE_CONTINUE; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -1520,7 +1516,7 @@ class Boolean_expression : public Expression
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -1889,7 +1885,7 @@ class Integer_expression : public Expression
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -2285,7 +2281,7 @@ class Float_expression : public Expression
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -2475,7 +2471,7 @@ class Complex_expression : public Expression
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -2691,7 +2687,7 @@ class Const_expression : public Expression
   { 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_constant() const
   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_immutable() const
       || 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_traverse(Traverse* traverse)
   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_immutable() const
       || 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(Translate_context* context)
          // 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(Translate_context* context)
              // 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(Translate_context* context)
          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* 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() const
 }
 
 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_constant_struct() 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_immutable() const
        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_constant_array() const
   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_immutable() const
        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_backend(Translate_context* context)
     }
 
   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 : public Expression
   { 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 Expression
   { 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 Expression
 
  protected:
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
@@ -14913,7 +14968,7 @@ class Interface_mtable_expression : public Expression
   do_type();
 
   bool
-  is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   void
@@ -15104,7 +15159,7 @@ class Struct_field_offset_expression : public Expression
 
  protected:
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   Type*
index a02fd19feb34d855979d5767b7ef352c1854367d..96d314ffbda0269ef06f4ecb5c2bb804f170a3cf 100644 (file)
@@ -500,10 +500,20 @@ 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(); }
+  // 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_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 Expression
   { return true; }
 
   bool
-  do_is_immutable() const
+  do_is_static_initializer() const
   { return true; }
 
   bool
@@ -1595,7 +1606,7 @@ class Type_conversion_expression : public Expression
   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 : public 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 Expression
   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 Expression
   { 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 Expression
   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 : public Expression,
   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 9491e51258ed11a2349213000884cedf7a654e45..a166d1c5c4e91920795cdb9dd15492ac42e0b3a6 100644 (file)
@@ -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))