]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/fortran/trans-common.c
Update copyright years.
[thirdparty/gcc.git] / gcc / fortran / trans-common.c
index 3b16e5e006554ccad5141eb60dec644ff781bba9..bf163bc4f52e7fec22d49804ed9fbbb5634ba8b0 100644 (file)
@@ -1,13 +1,12 @@
 /* Common block and equivalence list handling
-   Copyright (C) 2000, 2003, 2004, 2005, 2006
-   Free Software Foundation, Inc.
+   Copyright (C) 2000-2020 Free Software Foundation, Inc.
    Contributed by Canqun Yang <canqun@nudt.edu.cn>
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -16,9 +15,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */     
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */     
 
 /* The core algorithm is based on Andy Vaught's g95 tree.  Also the
    way to build UNION_TYPE is borrowed from Richard Henderson.
@@ -84,9 +82,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
    a diagonal matrix in the matrix formulation.
  
    Each segment is described by a chain of segment_info structures.  Each
-   segment_info structure describes the extents of a single varible within
+   segment_info structure describes the extents of a single variable within
    the segment.  This list is maintained in the order the elements are
-   positioned withing the segment.  If two elements have the same starting
+   positioned within the segment.  If two elements have the same starting
    offset the smaller will come first.  If they also have the same size their
    ordering is undefined. 
    
@@ -95,17 +93,20 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
    block for each merged equivalence list.  */
 
 #include "config.h"
+#define INCLUDE_MAP
 #include "system.h"
 #include "coretypes.h"
-#include "target.h"
-#include "tree.h"
-#include "toplev.h"
 #include "tm.h"
-#include "rtl.h"
+#include "tree.h"
 #include "gfortran.h"
 #include "trans.h"
+#include "stringpool.h"
+#include "fold-const.h"
+#include "stor-layout.h"
+#include "varasm.h"
 #include "trans-types.h"
 #include "trans-const.h"
+#include "target-memory.h"
 
 
 /* Holds a single variable in an equivalence set.  */
@@ -120,7 +121,10 @@ typedef struct segment_info
 } segment_info;
 
 static segment_info * current_segment;
-static gfc_namespace *gfc_common_ns = NULL;
+
+/* Store decl of all common blocks in this translation unit; the first
+   tree is the identifier.  */
+static std::map<tree, tree> gfc_map_of_all_commons;
 
 
 /* Make a segment_info based on a symbol.  */
@@ -132,10 +136,10 @@ get_segment_info (gfc_symbol * sym, HOST_WIDE_INT offset)
 
   /* Make sure we've got the character length.  */
   if (sym->ts.type == BT_CHARACTER)
-    gfc_conv_const_charlen (sym->ts.cl);
+    gfc_conv_const_charlen (sym->ts.u.cl);
 
   /* Create the segment_info and fill it in.  */
-  s = (segment_info *) gfc_getmem (sizeof (segment_info));
+  s = XCNEW (segment_info);
   s->sym = sym;
   /* We will use this type when building the segment aggregate type.  */
   s->field = gfc_sym_type (sym);
@@ -157,14 +161,14 @@ copy_equiv_list_to_ns (segment_info *c)
   gfc_equiv_info *s;
   gfc_equiv_list *l;
 
-  l = (gfc_equiv_list *) gfc_getmem (sizeof (gfc_equiv_list));
+  l = XCNEW (gfc_equiv_list);
 
   l->next = c->sym->ns->equiv_lists;
   c->sym->ns->equiv_lists = l;
 
   for (f = c; f; f = f->next)
     {
-      s = (gfc_equiv_info *) gfc_getmem (sizeof (gfc_equiv_info));
+      s = XCNEW (gfc_equiv_info);
       s->next = l->equiv;
       l->equiv = s;
       s->sym = f->sym;
@@ -216,21 +220,45 @@ add_segments (segment_info *list, segment_info *v)
   return list;
 }
 
+
 /* Construct mangled common block name from symbol name.  */
 
+/* We need the bind(c) flag to tell us how/if we should mangle the symbol
+   name.  There are few calls to this function, so few places that this
+   would need to be added.  At the moment, there is only one call, in
+   build_common_decl().  We can't attempt to look up the common block
+   because we may be building it for the first time and therefore, it won't
+   be in the common_root.  We also need the binding label, if it's bind(c).
+   Therefore, send in the pointer to the common block, so whatever info we
+   have so far can be used.  All of the necessary info should be available
+   in the gfc_common_head by now, so it should be accurate to test the
+   isBindC flag and use the binding label given if it is bind(c).
+
+   We may NOT know yet if it's bind(c) or not, but we can try at least.
+   Will have to figure out what to do later if it's labeled bind(c)
+   after this is called.  */
+
 static tree
-gfc_sym_mangled_common_id (const char  *name)
+gfc_sym_mangled_common_id (gfc_common_head *com)
 {
   int has_underscore;
   char mangled_name[GFC_MAX_MANGLED_SYMBOL_LEN + 1];
+  char name[GFC_MAX_SYMBOL_LEN + 1];
+
+  /* Get the name out of the common block pointer.  */
+  strcpy (name, com->name);
+
+  /* If we're suppose to do a bind(c).  */
+  if (com->is_bind_c == 1 && com->binding_label)
+    return get_identifier (com->binding_label);
 
   if (strcmp (name, BLANK_COMMON_NAME) == 0)
     return get_identifier (name);
 
-  if (gfc_option.flag_underscoring)
+  if (flag_underscoring)
     {
       has_underscore = strchr (name, '_') != 0;
-      if (gfc_option.flag_second_underscore && has_underscore)
+      if (flag_second_underscore && has_underscore)
         snprintf (mangled_name, sizeof mangled_name, "%s__", name);
       else
         snprintf (mangled_name, sizeof mangled_name, "%s_", name);
@@ -254,8 +282,8 @@ build_field (segment_info *h, tree union_type, record_layout_info rli)
   unsigned HOST_WIDE_INT desired_align, known_align;
 
   name = get_identifier (h->sym->name);
-  field = build_decl (FIELD_DECL, name, h->field);
-  gfc_set_decl_location (field, &h->sym->declared_at);
+  field = build_decl (gfc_get_location (&h->sym->declared_at),
+                     FIELD_DECL, name, h->field);
   known_align = (offset & -offset) * BITS_PER_UNIT;
   if (known_align == 0 || known_align > BIGGEST_ALIGNMENT)
     known_align = BIGGEST_ALIGNMENT;
@@ -287,13 +315,23 @@ build_field (segment_info *h, tree union_type, record_layout_info rli)
       addr = gfc_create_var_np (pvoid_type_node, h->sym->name);
       TREE_STATIC (len) = 1;
       TREE_STATIC (addr) = 1;
-      DECL_INITIAL (len) = build_int_cst (NULL_TREE, -2);
+      DECL_INITIAL (len) = build_int_cst (gfc_charlen_type_node, -2);
       gfc_set_decl_location (len, &h->sym->declared_at);
       gfc_set_decl_location (addr, &h->sym->declared_at);
       GFC_DECL_STRING_LEN (field) = pushdecl_top_level (len);
       GFC_DECL_ASSIGN_ADDR (field) = pushdecl_top_level (addr);
     }
 
+  /* If this field is volatile, mark it.  */
+  if (h->sym->attr.volatile_)
+    {
+      tree new_type;
+      TREE_THIS_VOLATILE (field) = 1;
+      TREE_SIDE_EFFECTS (field) = 1;
+      new_type = build_qualified_type (TREE_TYPE (field), TYPE_QUAL_VOLATILE);
+      TREE_TYPE (field) = new_type;
+    }
+
   h->field = field;
 }
 
@@ -301,10 +339,10 @@ build_field (segment_info *h, tree union_type, record_layout_info rli)
 /* Get storage for local equivalence.  */
 
 static tree
-build_equiv_decl (tree union_type, bool is_init, bool is_saved)
+build_equiv_decl (tree union_type, bool is_init, bool is_saved, bool is_auto)
 {
   tree decl;
-  char name[15];
+  char name[18];
   static int serial = 0;
 
   if (is_init)
@@ -316,12 +354,13 @@ build_equiv_decl (tree union_type, bool is_init, bool is_saved)
     }
 
   snprintf (name, sizeof (name), "equiv.%d", serial++);
-  decl = build_decl (VAR_DECL, get_identifier (name), union_type);
+  decl = build_decl (input_location,
+                    VAR_DECL, get_identifier (name), union_type);
   DECL_ARTIFICIAL (decl) = 1;
   DECL_IGNORED_P (decl) = 1;
 
-  if (!gfc_can_put_var_on_stack (DECL_SIZE_UNIT (decl))
-      || is_saved)
+  if (!is_auto && (!gfc_can_put_var_on_stack (DECL_SIZE_UNIT (decl))
+      || is_saved))
     TREE_STATIC (decl) = 1;
 
   TREE_ADDRESSABLE (decl) = 1;
@@ -343,30 +382,36 @@ build_equiv_decl (tree union_type, bool is_init, bool is_saved)
 static tree
 build_common_decl (gfc_common_head *com, tree union_type, bool is_init)
 {
-  gfc_symbol *common_sym;
-  tree decl;
-
-  /* Create a namespace to store symbols for common blocks.  */
-  if (gfc_common_ns == NULL)
-    gfc_common_ns = gfc_get_namespace (NULL, 0);
+  tree decl, identifier;
 
-  gfc_get_symbol (com->name, gfc_common_ns, &common_sym);
-  decl = common_sym->backend_decl;
+  identifier = gfc_sym_mangled_common_id (com);
+  decl = gfc_map_of_all_commons.count(identifier)
+        ? gfc_map_of_all_commons[identifier] : NULL_TREE;
 
   /* Update the size of this common block as needed.  */
   if (decl != NULL_TREE)
     {
       tree size = TYPE_SIZE_UNIT (union_type);
+
+      /* Named common blocks of the same name shall be of the same size
+        in all scoping units of a program in which they appear, but
+        blank common blocks may be of different sizes.  */
+      if (!tree_int_cst_equal (DECL_SIZE_UNIT (decl), size)
+         && strcmp (com->name, BLANK_COMMON_NAME))
+       gfc_warning (0, "Named COMMON block %qs at %L shall be of the "
+                    "same size as elsewhere (%lu vs %lu bytes)", com->name,
+                    &com->where,
+                    (unsigned long) TREE_INT_CST_LOW (size),
+                    (unsigned long) TREE_INT_CST_LOW (DECL_SIZE_UNIT (decl)));
+
       if (tree_int_cst_lt (DECL_SIZE_UNIT (decl), size))
-        {
-          /* Named common blocks of the same name shall be of the same size
-             in all scoping units of a program in which they appear, but
-             blank common blocks may be of different sizes.  */
-          if (strcmp (com->name, BLANK_COMMON_NAME))
-           gfc_warning ("Named COMMON block '%s' at %L shall be of the "
-                        "same size", com->name, &com->where);
-          DECL_SIZE_UNIT (decl) = size;
-        }
+       {
+         DECL_SIZE (decl) = TYPE_SIZE (union_type);
+         DECL_SIZE_UNIT (decl) = size;
+         SET_DECL_MODE (decl, TYPE_MODE (union_type));
+         TREE_TYPE (decl) = union_type;
+         layout_decl (decl, 0);
+       }
      }
 
   /* If this common block has been declared in a previous program unit,
@@ -378,22 +423,52 @@ build_common_decl (gfc_common_head *com, tree union_type, bool is_init)
   /* If there is no backend_decl for the common block, build it.  */
   if (decl == NULL_TREE)
     {
-      decl = build_decl (VAR_DECL, get_identifier (com->name), union_type);
-      SET_DECL_ASSEMBLER_NAME (decl, gfc_sym_mangled_common_id (com->name));
+      if (com->is_bind_c == 1 && com->binding_label)
+       decl = build_decl (input_location, VAR_DECL, identifier, union_type);
+      else
+       {
+         decl = build_decl (input_location, VAR_DECL, get_identifier (com->name),
+                            union_type);
+         gfc_set_decl_assembler_name (decl, identifier);
+       }
+
       TREE_PUBLIC (decl) = 1;
       TREE_STATIC (decl) = 1;
-      DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
+      DECL_IGNORED_P (decl) = 1;
+      if (!com->is_bind_c)
+       SET_DECL_ALIGN (decl, BIGGEST_ALIGNMENT);
+      else
+        {
+         /* Do not set the alignment for bind(c) common blocks to
+            BIGGEST_ALIGNMENT because that won't match what C does.  Also,
+            for common blocks with one element, the alignment must be
+            that of the field within the common block in order to match
+            what C will do.  */
+         tree field = NULL_TREE;
+         field = TYPE_FIELDS (TREE_TYPE (decl));
+         if (DECL_CHAIN (field) == NULL_TREE)
+           SET_DECL_ALIGN (decl, TYPE_ALIGN (TREE_TYPE (field)));
+       }
       DECL_USER_ALIGN (decl) = 0;
       GFC_DECL_COMMON_OR_EQUIV (decl) = 1;
 
       gfc_set_decl_location (decl, &com->where);
 
-      if (com->threadprivate && targetm.have_tls)
-       DECL_TLS_MODEL (decl) = decl_default_tls_model (decl);
+      if (com->threadprivate)
+       set_decl_tls_model (decl, decl_default_tls_model (decl));
+
+      if (com->omp_declare_target_link)
+       DECL_ATTRIBUTES (decl)
+         = tree_cons (get_identifier ("omp declare target link"),
+                      NULL_TREE, DECL_ATTRIBUTES (decl));
+      else if (com->omp_declare_target)
+       DECL_ATTRIBUTES (decl)
+         = tree_cons (get_identifier ("omp declare target"),
+                      NULL_TREE, DECL_ATTRIBUTES (decl));
 
       /* Place the back end declaration for this common block in
          GLOBAL_BINDING_LEVEL.  */
-      common_sym->backend_decl = pushdecl_top_level (decl);
+      gfc_map_of_all_commons[identifier] = pushdecl_top_level (decl);
     }
 
   /* Has no initial values.  */
@@ -413,19 +488,130 @@ build_common_decl (gfc_common_head *com, tree union_type, bool is_init)
 }
 
 
+/* Return a field that is the size of the union, if an equivalence has
+   overlapping initializers.  Merge the initializers into a single
+   initializer for this new field, then free the old ones.  */ 
+
+static tree
+get_init_field (segment_info *head, tree union_type, tree *field_init,
+               record_layout_info rli)
+{
+  segment_info *s;
+  HOST_WIDE_INT length = 0;
+  HOST_WIDE_INT offset = 0;
+  unsigned HOST_WIDE_INT known_align, desired_align;
+  bool overlap = false;
+  tree tmp, field;
+  tree init;
+  unsigned char *data, *chk;
+  vec<constructor_elt, va_gc> *v = NULL;
+
+  tree type = unsigned_char_type_node;
+  int i;
+
+  /* Obtain the size of the union and check if there are any overlapping
+     initializers.  */
+  for (s = head; s; s = s->next)
+    {
+      HOST_WIDE_INT slen = s->offset + s->length;
+      if (s->sym->value)
+       {
+         if (s->offset < offset)
+            overlap = true;
+         offset = slen;
+       }
+      length = length < slen ? slen : length;
+    }
+
+  if (!overlap)
+    return NULL_TREE;
+
+  /* Now absorb all the initializer data into a single vector,
+     whilst checking for overlapping, unequal values.  */
+  data = XCNEWVEC (unsigned char, (size_t)length);
+  chk = XCNEWVEC (unsigned char, (size_t)length);
+
+  /* TODO - change this when default initialization is implemented.  */
+  memset (data, '\0', (size_t)length);
+  memset (chk, '\0', (size_t)length);
+  for (s = head; s; s = s->next)
+    if (s->sym->value)
+      {
+       locus *loc = NULL;
+       if (s->sym->ns->equiv && s->sym->ns->equiv->eq)
+         loc = &s->sym->ns->equiv->eq->expr->where;
+       gfc_merge_initializers (s->sym->ts, s->sym->value, loc,
+                             &data[s->offset],
+                             &chk[s->offset],
+                            (size_t)s->length);
+      }
+  
+  for (i = 0; i < length; i++)
+    CONSTRUCTOR_APPEND_ELT (v, NULL, build_int_cst (type, data[i]));
+
+  free (data);
+  free (chk);
+
+  /* Build a char[length] array to hold the initializers.  Much of what
+     follows is borrowed from build_field, above.  */
+
+  tmp = build_int_cst (gfc_array_index_type, length - 1);
+  tmp = build_range_type (gfc_array_index_type,
+                         gfc_index_zero_node, tmp);
+  tmp = build_array_type (type, tmp);
+  field = build_decl (gfc_get_location (&gfc_current_locus),
+                     FIELD_DECL, NULL_TREE, tmp);
+
+  known_align = BIGGEST_ALIGNMENT;
+
+  desired_align = update_alignment_for_field (rli, field, known_align);
+  if (desired_align > known_align)
+    DECL_PACKED (field) = 1;
+
+  DECL_FIELD_CONTEXT (field) = union_type;
+  DECL_FIELD_OFFSET (field) = size_int (0);
+  DECL_FIELD_BIT_OFFSET (field) = bitsize_zero_node;
+  SET_DECL_OFFSET_ALIGN (field, known_align);
+
+  rli->offset = size_binop (MAX_EXPR, rli->offset,
+                            size_binop (PLUS_EXPR,
+                                        DECL_FIELD_OFFSET (field),
+                                        DECL_SIZE_UNIT (field)));
+
+  init = build_constructor (TREE_TYPE (field), v);
+  TREE_CONSTANT (init) = 1;
+
+  *field_init = init;
+
+  for (s = head; s; s = s->next)
+    {
+      if (s->sym->value == NULL)
+       continue;
+
+      gfc_free_expr (s->sym->value);
+      s->sym->value = NULL;
+    }
+
+  return field;
+}
+
+
 /* Declare memory for the common block or local equivalence, and create
    backend declarations for all of the elements.  */
 
 static void
-create_common (gfc_common_head *com, segment_info * head, bool saw_equiv)
+create_common (gfc_common_head *com, segment_info *head, bool saw_equiv)
 {
   segment_info *s, *next_s;
   tree union_type;
   tree *field_link;
+  tree field;
+  tree field_init = NULL_TREE;
   record_layout_info rli;
   tree decl;
   bool is_init = false;
   bool is_saved = false;
+  bool is_auto = false;
 
   /* Declare the variables inside the common block.
      If the current common block contains any equivalence object, then
@@ -440,13 +626,27 @@ create_common (gfc_common_head *com, segment_info * head, bool saw_equiv)
   rli = start_record_layout (union_type);
   field_link = &TYPE_FIELDS (union_type);
 
+  /* Check for overlapping initializers and replace them with a single,
+     artificial field that contains all the data.  */
+  if (saw_equiv)
+    field = get_init_field (head, union_type, &field_init, rli);
+  else
+    field = NULL_TREE;
+
+  if (field != NULL_TREE)
+    {
+      is_init = true;
+      *field_link = field;
+      field_link = &DECL_CHAIN (field);
+    }
+
   for (s = head; s; s = s->next)
     {
       build_field (s, union_type, rli);
 
       /* Link the field into the type.  */
       *field_link = s->field;
-      field_link = &TREE_CHAIN (s->field);
+      field_link = &DECL_CHAIN (s->field);
 
       /* Has initial value.  */
       if (s->sym->value)
@@ -455,56 +655,55 @@ create_common (gfc_common_head *com, segment_info * head, bool saw_equiv)
       /* Has SAVE attribute.  */
       if (s->sym->attr.save)
         is_saved = true;
+
+      /* Has AUTOMATIC attribute.  */
+      if (s->sym->attr.automatic)
+       is_auto = true;
     }
+
   finish_record_layout (rli, true);
 
   if (com)
     decl = build_common_decl (com, union_type, is_init);
   else
-    decl = build_equiv_decl (union_type, is_init, is_saved);
+    decl = build_equiv_decl (union_type, is_init, is_saved, is_auto);
 
   if (is_init)
     {
       tree ctor, tmp;
-      HOST_WIDE_INT offset = 0;
-      VEC(constructor_elt,gc) *v = NULL;
+      vec<constructor_elt, va_gc> *v = NULL;
 
-      for (s = head; s; s = s->next)
-        {
-          if (s->sym->value)
-            {
-              if (s->offset < offset)
-                {
-                   /* We have overlapping initializers.  It could either be
-                      partially initialized arrays (legal), or the user
-                      specified multiple initial values (illegal).
-                      We don't implement this yet, so bail out.  */
-                  gfc_todo_error ("Initialization of overlapping variables");
-                }
-             /* Add the initializer for this field.  */
-             tmp = gfc_conv_initializer (s->sym->value, &s->sym->ts,
-                 TREE_TYPE (s->field), s->sym->attr.dimension,
-                 s->sym->attr.pointer || s->sym->attr.allocatable);
-
-             CONSTRUCTOR_APPEND_ELT (v, s->field, tmp);
-              offset = s->offset + s->length;
-            }
-        }
-      gcc_assert (!VEC_empty (constructor_elt, v));
+      if (field != NULL_TREE && field_init != NULL_TREE)
+       CONSTRUCTOR_APPEND_ELT (v, field, field_init);
+      else
+       for (s = head; s; s = s->next)
+         {
+           if (s->sym->value)
+             {
+               /* Add the initializer for this field.  */
+               tmp = gfc_conv_initializer (s->sym->value, &s->sym->ts,
+                                           TREE_TYPE (s->field),
+                                           s->sym->attr.dimension,
+                                           s->sym->attr.pointer
+                                           || s->sym->attr.allocatable, false);
+
+               CONSTRUCTOR_APPEND_ELT (v, s->field, tmp);
+             }
+         }
+
+      gcc_assert (!v->is_empty ());
       ctor = build_constructor (union_type, v);
       TREE_CONSTANT (ctor) = 1;
-      TREE_INVARIANT (ctor) = 1;
       TREE_STATIC (ctor) = 1;
       DECL_INITIAL (decl) = ctor;
 
-#ifdef ENABLE_CHECKING
-      {
-       tree field, value;
-       unsigned HOST_WIDE_INT idx;
-       FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), idx, field, value)
-         gcc_assert (TREE_CODE (field) == FIELD_DECL);
-      }
-#endif
+      if (flag_checking)
+       {
+         tree field, value;
+         unsigned HOST_WIDE_INT idx;
+         FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), idx, field, value)
+           gcc_assert (TREE_CODE (field) == FIELD_DECL);
+       }
     }
 
   /* Build component reference for each variable.  */
@@ -512,25 +711,34 @@ create_common (gfc_common_head *com, segment_info * head, bool saw_equiv)
     {
       tree var_decl;
 
-      var_decl = build_decl (VAR_DECL, DECL_NAME (s->field),
+      var_decl = build_decl (gfc_get_location (&s->sym->declared_at),
+                            VAR_DECL, DECL_NAME (s->field),
                             TREE_TYPE (s->field));
-      gfc_set_decl_location (var_decl, &s->sym->declared_at);
-      TREE_PUBLIC (var_decl) = TREE_PUBLIC (decl);
       TREE_STATIC (var_decl) = TREE_STATIC (decl);
-      TREE_USED (var_decl) = TREE_USED (decl);
+      /* Mark the variable as used in order to avoid warnings about
+        unused variables.  */
+      TREE_USED (var_decl) = 1;
+      if (s->sym->attr.use_assoc)
+       DECL_IGNORED_P (var_decl) = 1;
       if (s->sym->attr.target)
        TREE_ADDRESSABLE (var_decl) = 1;
-      /* This is a fake variable just for debugging purposes.  */
-      TREE_ASM_WRITTEN (var_decl) = 1;
-
-      if (com)
+      /* Fake variables are not visible from other translation units.  */
+      TREE_PUBLIC (var_decl) = 0;
+      gfc_finish_decl_attrs (var_decl, &s->sym->attr);
+
+      /* To preserve identifier names in COMMON, chain to procedure
+         scope unless at top level in a module definition.  */
+      if (com
+          && s->sym->ns->proc_name
+          && s->sym->ns->proc_name->attr.flavor == FL_MODULE)
        var_decl = pushdecl_top_level (var_decl);
       else
        gfc_add_decl_to_function (var_decl);
 
       SET_DECL_VALUE_EXPR (var_decl,
-                          build3 (COMPONENT_REF, TREE_TYPE (s->field),
-                                  decl, s->field, NULL_TREE));
+                          fold_build3_loc (input_location, COMPONENT_REF,
+                                           TREE_TYPE (s->field),
+                                           decl, s->field, NULL_TREE));
       DECL_HAS_VALUE_EXPR_P (var_decl) = 1;
       GFC_DECL_COMMON_OR_EQUIV (var_decl) = 1;
 
@@ -545,7 +753,7 @@ create_common (gfc_common_head *com, segment_info * head, bool saw_equiv)
       s->sym->backend_decl = var_decl;
 
       next_s = s->next;
-      gfc_free (s);
+      free (s);
     }
 }
 
@@ -606,13 +814,21 @@ element_number (gfc_array_ref *ar)
       if (ar->dimen_type[i] != DIMEN_ELEMENT)
         gfc_internal_error ("element_number(): Bad dimension type");
 
-      mpz_sub (n, *get_mpz (ar->start[i]), *get_mpz (as->lower[i]));
+      if (as && as->lower[i])
+       mpz_sub (n, *get_mpz (ar->start[i]), *get_mpz (as->lower[i]));
+      else
+       mpz_sub_ui (n, *get_mpz (ar->start[i]), 1);
  
       mpz_mul (n, n, multiplier);
       mpz_add (offset, offset, n);
  
-      mpz_sub (extent, *get_mpz (as->upper[i]), *get_mpz (as->lower[i]));
-      mpz_add_ui (extent, extent, 1);
+      if (as && as->upper[i] && as->lower[i])
+       {
+         mpz_sub (extent, *get_mpz (as->upper[i]), *get_mpz (as->lower[i]));
+         mpz_add_ui (extent, extent, 1);
+       }
+      else
+       mpz_set_ui (extent, 0);
  
       if (mpz_sgn (extent) < 0)
         mpz_set_ui (extent, 0);
@@ -659,7 +875,7 @@ calculate_offset (gfc_expr *e)
           case AR_ELEMENT:
            n = element_number (&reference->u.ar);
            if (element_type->type == BT_CHARACTER)
-             gfc_conv_const_charlen (element_type->cl);
+             gfc_conv_const_charlen (element_type->u.cl);
            element_size =
               int_size_in_bytes (gfc_typenode_for_spec (element_type));
            offset += n * element_size;
@@ -714,8 +930,8 @@ confirm_condition (segment_info *s1, gfc_equiv *eq1, segment_info *s2,
   offset2 = calculate_offset (eq2->expr);
 
   if (s1->offset + offset1 != s2->offset + offset2)
-    gfc_error ("Inconsistent equivalence rules involving '%s' at %L and "
-              "'%s' at %L", s1->sym->name, &s1->sym->declared_at,
+    gfc_error ("Inconsistent equivalence rules involving %qs at %L and "
+              "%qs at %L", s1->sym->name, &s1->sym->declared_at,
               s2->sym->name, &s2->sym->declared_at);
 }
 
@@ -737,6 +953,59 @@ add_condition (segment_info *f, gfc_equiv *eq1, gfc_equiv *eq2)
     confirm_condition (f, eq1, n, eq2);
 }
 
+static void
+accumulate_equivalence_attributes (symbol_attribute *dummy_symbol, gfc_equiv *e)
+{
+  symbol_attribute attr = e->expr->symtree->n.sym->attr;
+
+  dummy_symbol->dummy |= attr.dummy;
+  dummy_symbol->pointer |= attr.pointer;
+  dummy_symbol->target |= attr.target;
+  dummy_symbol->external |= attr.external;
+  dummy_symbol->intrinsic |= attr.intrinsic;
+  dummy_symbol->allocatable |= attr.allocatable;
+  dummy_symbol->elemental |= attr.elemental;
+  dummy_symbol->recursive |= attr.recursive;
+  dummy_symbol->in_common |= attr.in_common;
+  dummy_symbol->result |= attr.result;
+  dummy_symbol->in_namelist |= attr.in_namelist;
+  dummy_symbol->optional |= attr.optional;
+  dummy_symbol->entry |= attr.entry;
+  dummy_symbol->function |= attr.function;
+  dummy_symbol->subroutine |= attr.subroutine;
+  dummy_symbol->dimension |= attr.dimension;
+  dummy_symbol->in_equivalence |= attr.in_equivalence;
+  dummy_symbol->use_assoc |= attr.use_assoc;
+  dummy_symbol->cray_pointer |= attr.cray_pointer;
+  dummy_symbol->cray_pointee |= attr.cray_pointee;
+  dummy_symbol->data |= attr.data;
+  dummy_symbol->value |= attr.value;
+  dummy_symbol->volatile_ |= attr.volatile_;
+  dummy_symbol->is_protected |= attr.is_protected;
+  dummy_symbol->is_bind_c |= attr.is_bind_c;
+  dummy_symbol->procedure |= attr.procedure;
+  dummy_symbol->proc_pointer |= attr.proc_pointer;
+  dummy_symbol->abstract |= attr.abstract;
+  dummy_symbol->asynchronous |= attr.asynchronous;
+  dummy_symbol->codimension |= attr.codimension;
+  dummy_symbol->contiguous |= attr.contiguous;
+  dummy_symbol->generic |= attr.generic;
+  dummy_symbol->automatic |= attr.automatic;
+  dummy_symbol->threadprivate |= attr.threadprivate;
+  dummy_symbol->omp_declare_target |= attr.omp_declare_target;
+  dummy_symbol->omp_declare_target_link |= attr.omp_declare_target_link;
+  dummy_symbol->oacc_declare_copyin |= attr.oacc_declare_copyin;
+  dummy_symbol->oacc_declare_create |= attr.oacc_declare_create;
+  dummy_symbol->oacc_declare_deviceptr |= attr.oacc_declare_deviceptr;
+  dummy_symbol->oacc_declare_device_resident
+    |= attr.oacc_declare_device_resident;
+
+  /* Not strictly correct, but probably close enough.  */
+  if (attr.save > dummy_symbol->save)
+    dummy_symbol->save = attr.save;
+  if (attr.access > dummy_symbol->access)
+    dummy_symbol->access = attr.access;
+}
 
 /* Given a segment element, search through the equivalence lists for unused
    conditions that involve the symbol.  Add these rules to the segment.  */
@@ -754,9 +1023,12 @@ find_equivalence (segment_info *n)
       eq = NULL;
 
       /* Search the equivalence list, including the root (first) element
-         for the symbol that owns the segment.  */
+        for the symbol that owns the segment.  */
+      symbol_attribute dummy_symbol;
+      memset (&dummy_symbol, 0, sizeof (dummy_symbol));
       for (e2 = e1; e2; e2 = e2->eq)
        {
+         accumulate_equivalence_attributes (&dummy_symbol, e2);
          if (!e2->used && e2->expr->symtree->n.sym == n->sym)
            {
              eq = e2;
@@ -764,6 +1036,8 @@ find_equivalence (segment_info *n)
            }
        }
 
+      gfc_check_conflict (&dummy_symbol, e1->expr->symtree->name, &e1->expr->where);
+
       /* Go to the next root element.  */
       if (eq == NULL)
        continue;
@@ -785,21 +1059,19 @@ find_equivalence (segment_info *n)
 }
 
 
-  /* Add all symbols equivalenced within a segment.  We need to scan the
+/* Add all symbols equivalenced within a segment.  We need to scan the
    segment list multiple times to include indirect equivalences.  Since
    a new segment_info can inserted at the beginning of the segment list,
    depending on its offset, we have to force a final pass through the
-   loop by demanding that completion sees a pass with no matches; ie.
+   loop by demanding that completion sees a pass with no matches; i.e.,
    all symbols with equiv_built set and no new equivalences found.  */
 
 static void
 add_equivalences (bool *saw_equiv)
 {
   segment_info *f;
-  bool seen_one, more;
+  bool more = TRUE;
 
-  seen_one = false;
-  more = TRUE;
   while (more)
     {
       more = FALSE;
@@ -808,7 +1080,7 @@ add_equivalences (bool *saw_equiv)
          if (!f->sym->equiv_built)
            {
              f->sym->equiv_built = 1;
-             seen_one = find_equivalence (f);
+             bool seen_one = find_equivalence (f);
              if (seen_one)
                {
                  *saw_equiv = true;
@@ -827,7 +1099,7 @@ add_equivalences (bool *saw_equiv)
    Sets *palign to the required alignment.  */
 
 static HOST_WIDE_INT
-align_segment (unsigned HOST_WIDE_INT * palign)
+align_segment (unsigned HOST_WIDE_INT *palign)
 {
   segment_info *s;
   unsigned HOST_WIDE_INT offset;
@@ -847,8 +1119,8 @@ align_segment (unsigned HOST_WIDE_INT * palign)
          if (this_offset & (max_align - 1))
            {
              /* Aligning this field would misalign a previous field.  */
-             gfc_error ("The equivalence set for variable '%s' "
-                        "declared at %L violates alignment requirents",
+             gfc_error ("The equivalence set for variable %qs "
+                        "declared at %L violates alignment requirements",
                         s->sym->name, &s->sym->declared_at);
            }
          offset += this_offset;
@@ -864,7 +1136,7 @@ align_segment (unsigned HOST_WIDE_INT * palign)
 /* Adjust segment offsets by the given amount.  */
 
 static void
-apply_segment_offset (segment_info * s, HOST_WIDE_INT offset)
+apply_segment_offset (segment_info *s, HOST_WIDE_INT offset)
 {
   for (; s; s = s->next)
     s->offset += offset;
@@ -886,12 +1158,12 @@ translate_common (gfc_common_head *common, gfc_symbol *var_list)
   HOST_WIDE_INT offset;
   HOST_WIDE_INT current_offset;
   unsigned HOST_WIDE_INT align;
-  unsigned HOST_WIDE_INT max_align;
   bool saw_equiv;
 
   common_segment = NULL;
+  offset = 0;
   current_offset = 0;
-  max_align = 1;
+  align = 1;
   saw_equiv = false;
 
   /* Add symbols to the segment.  */
@@ -912,8 +1184,8 @@ translate_common (gfc_common_head *common, gfc_symbol *var_list)
          /* Verify that it ended up where we expect it.  */
          if (s->offset != current_offset)
            {
-             gfc_error ("Equivalence for '%s' does not match ordering of "
-                        "COMMON '%s' at %L", sym->name,
+             gfc_error ("Equivalence for %qs does not match ordering of "
+                        "COMMON %qs at %L", sym->name,
                         common->name, &common->where);
            }
        }
@@ -927,32 +1199,38 @@ translate_common (gfc_common_head *common, gfc_symbol *var_list)
          add_equivalences (&saw_equiv);
 
          if (current_segment->offset < 0)
-           gfc_error ("The equivalence set for '%s' cause an invalid "
-                      "extension to COMMON '%s' at %L", sym->name,
+           gfc_error ("The equivalence set for %qs cause an invalid "
+                      "extension to COMMON %qs at %L", sym->name,
                       common->name, &common->where);
 
-         offset = align_segment (&align);
+         if (flag_align_commons)
+           offset = align_segment (&align);
 
-         if (offset & (max_align - 1))
+         if (offset)
            {
              /* The required offset conflicts with previous alignment
                 requirements.  Insert padding immediately before this
                 segment.  */
-             gfc_warning ("Padding of %d bytes required before '%s' in "
-                          "COMMON '%s' at %L", (int)offset, s->sym->name,
-                          common->name, &common->where);
-           }
-         else
-           {
-             /* Offset the whole common block.  */
-             apply_segment_offset (common_segment, offset);
+             if (warn_align_commons)
+               {
+                 if (strcmp (common->name, BLANK_COMMON_NAME))
+                   gfc_warning (OPT_Walign_commons,
+                                "Padding of %d bytes required before %qs in "
+                                "COMMON %qs at %L; reorder elements or use "
+                                "%<-fno-align-commons%>", (int)offset,
+                                s->sym->name, common->name, &common->where);
+                 else
+                   gfc_warning (OPT_Walign_commons,
+                                "Padding of %d bytes required before %qs in "
+                                "COMMON at %L; reorder elements or use "
+                                "%<-fno-align-commons%>", (int)offset,
+                                s->sym->name, &common->where);
+               }
            }
 
          /* Apply the offset to the new segments.  */
          apply_segment_offset (current_segment, offset);
          current_offset += offset;
-         if (max_align < align)
-           max_align = align;
 
          /* Add the new segments to the common block.  */
          common_segment = add_segments (common_segment, current_segment);
@@ -962,10 +1240,25 @@ translate_common (gfc_common_head *common, gfc_symbol *var_list)
       current_offset += s->length;
     }
 
-  if (common_segment->offset != 0)
+  if (common_segment == NULL)
+    {
+      gfc_error ("COMMON %qs at %L does not exist",
+                common->name, &common->where);
+      return;
+    }
+
+  if (common_segment->offset != 0 && warn_align_commons)
     {
-      gfc_warning ("COMMON '%s' at %L requires %d bytes of padding at start",
-                  common->name, &common->where, (int)common_segment->offset);
+      if (strcmp (common->name, BLANK_COMMON_NAME))
+       gfc_warning (OPT_Walign_commons,
+                    "COMMON %qs at %L requires %d bytes of padding; "
+                    "reorder elements or use %<-fno-align-commons%>",
+                    common->name, &common->where, (int)common_segment->offset);
+      else
+       gfc_warning (OPT_Walign_commons,
+                    "COMMON at %L requires %d bytes of padding; "
+                    "reorder elements or use %<-fno-align-commons%>",
+                    &common->where, (int)common_segment->offset);
     }
 
   create_common (common, common_segment, saw_equiv);
@@ -992,7 +1285,8 @@ finish_equivalences (gfc_namespace *ns)
         sym = z->expr->symtree->n.sym;
         current_segment = get_segment_info (sym, 0);
 
-        /* All objects directly or indirectly equivalenced with this symbol.  */
+        /* All objects directly or indirectly equivalenced with this
+          symbol.  */
         add_equivalences (&dummy);
 
        /* Align the block.  */
@@ -1003,17 +1297,22 @@ finish_equivalences (gfc_namespace *ns)
 
        apply_segment_offset (current_segment, offset);
 
-       /* Create the decl. If this is a module equivalence, it has a unique
-          name, pointed to by z->module. This is written to a gfc_common_header
-          to push create_common into using build_common_decl, so that the
-          equivalence appears as an external symbol. Otherwise, a local
-          declaration is built using build_equiv_decl.*/
+       /* Create the decl.  If this is a module equivalence, it has a
+          unique name, pointed to by z->module.  This is written to a
+          gfc_common_header to push create_common into using
+          build_common_decl, so that the equivalence appears as an
+          external symbol.  Otherwise, a local declaration is built using
+          build_equiv_decl.  */
        if (z->module)
          {
            c = gfc_get_common_head ();
            /* We've lost the real location, so use the location of the
-            enclosing procedure.  */
-           c->where = ns->proc_name->declared_at;
+              enclosing procedure.  If we're in a BLOCK DATA block, then
+              use the location in the sym_root.  */
+           if (ns->proc_name)
+             c->where = ns->proc_name->declared_at;
+           else if (ns->is_block_data)
+             c->where = ns->sym_root->n.sym->declared_at;
            strcpy (c->name, z->module);
          }
        else
@@ -1047,19 +1346,18 @@ gfc_trans_common (gfc_namespace *ns)
   if (ns->blank_common.head != NULL)
     {
       c = gfc_get_common_head ();
-      /* We've lost the real location, so use the location of the
-        enclosing procedure.  */
-      c->where = ns->proc_name->declared_at;
+      c->where = ns->blank_common.head->common_head->where;
       strcpy (c->name, BLANK_COMMON_NAME);
       translate_common (c, ns->blank_common.head);
     }
+
   /* Translate all named common blocks.  */
   gfc_traverse_symtree (ns->common_root, named_common);
 
-  /* Commit the newly created symbols for common blocks.  */
-  gfc_commit_symbols ();
-
   /* Translate local equivalence.  */
   finish_equivalences (ns);
+
+  /* Commit the newly created symbols for common blocks and module
+     equivalences.  */
+  gfc_commit_symbols ();
 }