/* Common block and equivalence list handling
- Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007
+ Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by Canqun Yang <canqun@nudt.edu.cn>
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
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.
#include "config.h"
#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 "toplev.h" /* For exact_log2. */
+#include "output.h" /* For decl_default_tls_model. */
#include "gfortran.h"
#include "trans.h"
#include "trans-types.h"
#include "trans-const.h"
+#include "target-memory.h"
/* Holds a single variable in an equivalence set. */
/* 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));
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[0] != '\0')
+ return get_identifier (com->binding_label);
if (strcmp (name, BLANK_COMMON_NAME) == 0)
return get_identifier (name);
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 (h->sym->declared_at.lb->location,
+ FIELD_DECL, name, h->field);
known_align = (offset & -offset) * BITS_PER_UNIT;
if (known_align == 0 || known_align > BIGGEST_ALIGNMENT)
known_align = BIGGEST_ALIGNMENT;
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;
+ new_type = build_qualified_type (TREE_TYPE (field), TYPE_QUAL_VOLATILE);
+ TREE_TYPE (field) = new_type;
+ }
+
h->field = field;
}
}
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;
tree size = TYPE_SIZE_UNIT (union_type);
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))
+ /* 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;
+ 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,
/* 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));
+ decl = build_decl (input_location,
+ VAR_DECL, get_identifier (com->name), union_type);
+ gfc_set_decl_assembler_name (decl, gfc_sym_mangled_common_id (com));
TREE_PUBLIC (decl) = 1;
TREE_STATIC (decl) = 1;
- DECL_ALIGN (decl) = BIGGEST_ALIGNMENT;
+ DECL_IGNORED_P (decl) = 1;
+ if (!com->is_bind_c)
+ 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 (TREE_CHAIN (field) == NULL_TREE)
+ DECL_ALIGN (decl) = TYPE_ALIGN (TREE_TYPE (field));
+ }
DECL_USER_ALIGN (decl) = 0;
GFC_DECL_COMMON_OR_EQUIV (decl) = 1;
}
+/* 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,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 = (unsigned char*)gfc_getmem ((size_t)length);
+ chk = (unsigned char*)gfc_getmem ((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)
+ gfc_merge_initializers (s->sym->ts, s->sym->value,
+ &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]));
+
+ gfc_free (data);
+ gfc_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_current_locus.lb->location,
+ 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. */
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;
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 = &TREE_CHAIN (field);
+ }
+
for (s = head; s; s = s->next)
{
build_field (s, union_type, rli);
if (s->sym->attr.save)
is_saved = true;
}
+
finish_record_layout (rli, true);
if (com)
if (is_init)
{
tree ctor, tmp;
- HOST_WIDE_INT offset = 0;
VEC(constructor_elt,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;
- }
- }
+ 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);
+
+ CONSTRUCTOR_APPEND_ELT (v, s->field, tmp);
+ }
+ }
+
gcc_assert (!VEC_empty (constructor_elt, v));
ctor = build_constructor (union_type, v);
TREE_CONSTANT (ctor) = 1;
- TREE_INVARIANT (ctor) = 1;
TREE_STATIC (ctor) = 1;
DECL_INITIAL (decl) = ctor;
{
tree var_decl;
- var_decl = build_decl (VAR_DECL, DECL_NAME (s->field),
+ var_decl = build_decl (s->sym->declared_at.lb->location,
+ 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);
+ 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;
+
+ /* 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 (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;
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;
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
bool saw_equiv;
common_segment = NULL;
+ offset = 0;
current_offset = 0;
+ align = 1;
max_align = 1;
saw_equiv = false;
"extension to COMMON '%s' at %L", sym->name,
common->name, &common->where);
- offset = align_segment (&align);
+ if (gfc_option.flag_align_commons)
+ offset = align_segment (&align);
if (offset & (max_align - 1))
{
/* 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 (gfc_option.warn_align_commons)
+ {
+ if (strcmp (common->name, BLANK_COMMON_NAME))
+ gfc_warning ("Padding of %d bytes required before '%s' in "
+ "COMMON '%s' at %L; reorder elements or use "
+ "-fno-align-commons", (int)offset,
+ s->sym->name, common->name, &common->where);
+ else
+ gfc_warning ("Padding of %d bytes required before '%s' 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. */
return;
}
- if (common_segment->offset != 0)
+ if (common_segment->offset != 0 && gfc_option.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 ("COMMON '%s' at %L requires %d bytes of padding at start; "
+ "reorder elements or use -fno-align-commons",
+ common->name, &common->where, (int)common_segment->offset);
+ else
+ gfc_warning ("COMMON at %L requires %d bytes of padding at start; "
+ "reorder elements or use -fno-align-commons",
+ &common->where, (int)common_segment->offset);
}
create_common (common, common_segment, saw_equiv);
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. */
- if (ns->proc_name != NULL)
- c->where = ns->proc_name->declared_at;
- else
- c->where = ns->blank_common.head->common_head->where;
-
+ c->where = ns->blank_common.head->common_head->where;
strcpy (c->name, BLANK_COMMON_NAME);
translate_common (c, ns->blank_common.head);
}