aboutsummaryrefslogtreecommitdiff
path: root/gcc/f/equiv.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/f/equiv.c')
-rw-r--r--gcc/f/equiv.c1444
1 files changed, 1444 insertions, 0 deletions
diff --git a/gcc/f/equiv.c b/gcc/f/equiv.c
new file mode 100644
index 0000000..7dd2344
--- /dev/null
+++ b/gcc/f/equiv.c
@@ -0,0 +1,1444 @@
+/* equiv.c -- Implementation File (module.c template V1.0)
+ Copyright (C) 1995-1997 Free Software Foundation, Inc.
+ Contributed by James Craig Burley (burley@gnu.ai.mit.edu).
+
+This file is part of GNU Fortran.
+
+GNU Fortran 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 version.
+
+GNU Fortran is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or 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 GNU Fortran; see the file COPYING. If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.
+
+ Related Modules:
+ None
+
+ Description:
+ Handles the EQUIVALENCE relationships in a program unit.
+
+ Modifications:
+*/
+
+#define FFEEQUIV_DEBUG 0
+
+/* Include files. */
+
+#include "proj.h"
+#include "equiv.h"
+#include "bad.h"
+#include "bld.h"
+#include "com.h"
+#include "data.h"
+#include "global.h"
+#include "lex.h"
+#include "malloc.h"
+#include "symbol.h"
+
+/* Externals defined here. */
+
+
+/* Simple definitions and enumerations. */
+
+
+/* Internal typedefs. */
+
+
+/* Private include files. */
+
+
+/* Internal structure definitions. */
+
+struct _ffeequiv_list_
+ {
+ ffeequiv first;
+ ffeequiv last;
+ };
+
+/* Static objects accessed by functions in this module. */
+
+static struct _ffeequiv_list_ ffeequiv_list_;
+
+/* Static functions (internal). */
+
+static void ffeequiv_destroy_ (ffeequiv eq);
+static void ffeequiv_layout_local_ (ffeequiv eq);
+static bool ffeequiv_offset_ (ffetargetOffset *offset, ffesymbol s,
+ ffebld expr, bool subtract,
+ ffetargetOffset adjust, bool no_precede);
+
+/* Internal macros. */
+
+
+static void
+ffeequiv_destroy_ (ffeequiv victim)
+{
+ ffebld list;
+ ffebld item;
+ ffebld expr;
+
+ for (list = victim->list; list != NULL; list = ffebld_trail (list))
+ {
+ for (item = ffebld_head (list); item != NULL; item = ffebld_trail (item))
+ {
+ ffesymbol sym;
+
+ expr = ffebld_head (item);
+ sym = ffeequiv_symbol (expr);
+ if (sym == NULL)
+ continue;
+ if (ffesymbol_equiv (sym) != NULL)
+ ffesymbol_set_equiv (sym, NULL);
+ }
+ }
+ ffeequiv_kill (victim);
+}
+
+/* ffeequiv_layout_local_ -- Lay out storage for local equivalenced vars
+
+ ffeequiv eq;
+ ffeequiv_layout_local_(eq);
+
+ Makes a single master ffestorag object that contains all the vars
+ in the equivalence, and makes subordinate ffestorag objects for the
+ vars with the correct offsets.
+
+ The resulting var offsets are relative not necessarily to 0 -- the
+ are relative to the offset of the master area, which might be 0 or
+ negative, but should never be positive. */
+
+static void
+ffeequiv_layout_local_ (ffeequiv eq)
+{
+ ffestorag st; /* Equivalence storage area. */
+ ffebld list; /* List of list of equivalences. */
+ ffebld item; /* List of equivalences. */
+ ffebld root_exp; /* Expression for root sym. */
+ ffestorag root_st; /* Storage for root. */
+ ffesymbol root_sym; /* Root itself. */
+ ffebld rooted_exp; /* Expression for rooted sym in an eqlist. */
+ ffestorag rooted_st; /* Storage for rooted. */
+ ffesymbol rooted_sym; /* Rooted symbol itself. */
+ ffetargetOffset eqlist_offset;/* Offset for eqlist from rooted sym. */
+ ffetargetAlign alignment;
+ ffetargetAlign modulo;
+ ffetargetAlign pad;
+ ffetargetOffset size;
+ ffetargetOffset num_elements;
+ bool new_storage; /* Established new storage info. */
+ bool need_storage; /* Have need for more storage info. */
+ bool init;
+
+ assert (eq != NULL);
+
+ if (ffeequiv_common (eq) != NULL)
+ { /* Put in common due to programmer error. */
+ ffeequiv_destroy_ (eq);
+ return;
+ }
+
+ /* Find the symbol for the first valid item in the list of lists, use that
+ as the root symbol. Doesn't matter if it won't end up at the beginning
+ of the list, though. */
+
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, "Equiv1:\n");
+#endif
+
+ root_sym = NULL;
+ root_exp = NULL;
+
+ for (list = ffeequiv_list (eq);
+ list != NULL;
+ list = ffebld_trail (list))
+ { /* For every equivalence list in the list of
+ equivs */
+ for (item = ffebld_head (list);
+ item != NULL;
+ item = ffebld_trail (item))
+ { /* For every equivalence item in the list */
+ ffetargetOffset ign; /* Ignored. */
+
+ root_exp = ffebld_head (item);
+ root_sym = ffeequiv_symbol (root_exp);
+ if (root_sym == NULL)
+ continue; /* Ignore me. */
+
+ assert (ffesymbol_storage (root_sym) == NULL); /* No storage yet. */
+
+ if (!ffeequiv_offset_ (&ign, root_sym, root_exp, FALSE, 0, FALSE))
+ {
+ /* We can't just eliminate this one symbol from the list
+ of candidates, because it might be the only one that
+ ties all these equivs together. So just destroy the
+ whole list. */
+
+ ffeequiv_destroy_ (eq);
+ return;
+ }
+
+ break; /* Use first valid eqv expr for root exp/sym. */
+ }
+ if (root_sym != NULL)
+ break;
+ }
+
+ if (root_sym == NULL)
+ {
+ ffeequiv_destroy_ (eq);
+ return;
+ }
+
+
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, " Root: `%s'\n", ffesymbol_text (root_sym));
+#endif
+
+ /* We've got work to do, so make the LOCAL storage object that'll hold all
+ the equivalenced vars inside it. */
+
+ st = ffestorag_new (ffestorag_list_master ());
+ ffestorag_set_parent (st, NULL); /* Initializations happen here. */
+ ffestorag_set_init (st, NULL);
+ ffestorag_set_accretion (st, NULL);
+ ffestorag_set_offset (st, 0); /* Assume equiv will be at root offset 0 for now. */
+ ffestorag_set_alignment (st, 1);
+ ffestorag_set_modulo (st, 0);
+ ffestorag_set_type (st, FFESTORAG_typeLOCAL);
+ ffestorag_set_basictype (st, ffesymbol_basictype (root_sym));
+ ffestorag_set_kindtype (st, ffesymbol_kindtype (root_sym));
+ ffestorag_set_typesymbol (st, root_sym);
+ ffestorag_set_is_save (st, ffeequiv_is_save (eq));
+ if (ffesymbol_is_save (root_sym))
+ ffestorag_update_save (st);
+ ffestorag_set_is_init (st, ffeequiv_is_init (eq));
+ if (ffesymbol_is_init (root_sym))
+ ffestorag_update_init (st);
+ ffestorag_set_symbol (st, root_sym); /* Assume this will be the root until
+ we know better (used only to generate
+ the internal name for the aggregate area,
+ e.g. for debugging). */
+
+ /* Make the EQUIV storage object for the root symbol. */
+
+ if (ffesymbol_rank (root_sym) == 0)
+ num_elements = 1;
+ else
+ num_elements = ffebld_constant_integerdefault (ffebld_conter
+ (ffesymbol_arraysize (root_sym)));
+ ffetarget_layout (ffesymbol_text (root_sym), &alignment, &modulo, &size,
+ ffesymbol_basictype (root_sym), ffesymbol_kindtype (root_sym),
+ ffesymbol_size (root_sym), num_elements);
+ ffestorag_set_size (st, size); /* Set initial size of aggregate area. */
+
+ pad = ffetarget_align (ffestorag_ptr_to_alignment (st),
+ ffestorag_ptr_to_modulo (st), 0, alignment,
+ modulo);
+ assert (pad == 0);
+
+ root_st = ffestorag_new (ffestorag_list_equivs (st));
+ ffestorag_set_parent (root_st, st); /* Initializations happen there. */
+ ffestorag_set_init (root_st, NULL);
+ ffestorag_set_accretion (root_st, NULL);
+ ffestorag_set_symbol (root_st, root_sym);
+ ffestorag_set_size (root_st, size);
+ ffestorag_set_offset (root_st, 0); /* Will not change; always 0 relative to itself! */
+ ffestorag_set_alignment (root_st, alignment);
+ ffestorag_set_modulo (root_st, modulo);
+ ffestorag_set_type (root_st, FFESTORAG_typeEQUIV);
+ ffestorag_set_basictype (root_st, ffesymbol_basictype (root_sym));
+ ffestorag_set_kindtype (root_st, ffesymbol_kindtype (root_sym));
+ ffestorag_set_typesymbol (root_st, root_sym);
+ ffestorag_set_is_save (root_st, FALSE); /* Assume FALSE, then... */
+ if (ffestorag_is_save (st)) /* ...update to TRUE if needed. */
+ ffestorag_update_save (root_st);
+ ffestorag_set_is_init (root_st, FALSE); /* Assume FALSE, then... */
+ if (ffestorag_is_init (st)) /* ...update to TRUE if needed. */
+ ffestorag_update_init (root_st);
+ ffesymbol_set_storage (root_sym, root_st);
+ ffesymbol_signal_unreported (root_sym);
+ init = ffesymbol_is_init (root_sym);
+
+ /* Now that we know the root (offset=0) symbol, revisit all the lists and
+ do the actual storage allocation. Keep doing this until we've gone
+ through them all without making any new storage objects. */
+
+ do
+ {
+ new_storage = FALSE;
+ need_storage = FALSE;
+ for (list = ffeequiv_list (eq);
+ list != NULL;
+ list = ffebld_trail (list))
+ { /* For every equivalence list in the list of
+ equivs */
+ /* Now find a "rooted" symbol in this list. That is, find the
+ first item we can that is valid and whose symbol already
+ has a storage area, because that means we know where it
+ belongs in the equivalence area and can then allocate the
+ rest of the items in the list accordingly. */
+
+ rooted_sym = NULL;
+ rooted_exp = NULL;
+ eqlist_offset = 0;
+
+ for (item = ffebld_head (list);
+ item != NULL;
+ item = ffebld_trail (item))
+ { /* For every equivalence item in the list */
+ rooted_exp = ffebld_head (item);
+ rooted_sym = ffeequiv_symbol (rooted_exp);
+ if ((rooted_sym == NULL)
+ || ((rooted_st = ffesymbol_storage (rooted_sym)) == NULL))
+ {
+ rooted_sym = NULL;
+ continue; /* Ignore me. */
+ }
+
+ need_storage = TRUE; /* Somebody is likely to need
+ storage. */
+
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, " Rooted: `%s' at %" ffetargetOffset_f "d\n",
+ ffesymbol_text (rooted_sym),
+ ffestorag_offset (rooted_st));
+#endif
+
+ /* The offset of this symbol from the equiv's root symbol
+ is already known, and the size of this symbol is already
+ incorporated in the size of the equiv's aggregate area.
+ What we now determine is the offset of this equivalence
+ _list_ from the equiv's root symbol.
+
+ For example, if we know that A is at offset 16 from the
+ root symbol, given EQUIVALENCE (B(24),A(2)), we're looking
+ at A(2), meaning that the offset for this equivalence list
+ is 20 (4 bytes beyond the beginning of A, assuming typical
+ array types, dimensions, and type info). */
+
+ if (!ffeequiv_offset_ (&eqlist_offset, rooted_sym, rooted_exp, FALSE,
+ ffestorag_offset (rooted_st), FALSE))
+
+ { /* Can't use this one. */
+ ffesymbol_set_equiv (rooted_sym, NULL);/* Equiv area slated for
+ death. */
+ rooted_sym = NULL;
+ continue; /* Something's wrong with eqv expr, try another. */
+ }
+
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, " Eqlist offset: %" ffetargetOffset_f "d\n",
+ eqlist_offset);
+#endif
+
+ break;
+ }
+
+ /* If no rooted symbol, it means this list has no roots -- yet.
+ So, forget this list this time around, but we'll get back
+ to it after the outer loop iterates at least one more time,
+ and, ultimately, it will have a root. */
+
+ if (rooted_sym == NULL)
+ {
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, "No roots.\n");
+#endif
+ continue;
+ }
+
+ /* We now have a rooted symbol/expr and the offset of this equivalence
+ list from the root symbol. The other expressions in this
+ list all identify an initial storage unit that must have the
+ same offset. */
+
+ for (item = ffebld_head (list);
+ item != NULL;
+ item = ffebld_trail (item))
+ { /* For every equivalence item in the list */
+ ffebld item_exp; /* Expression for equivalence. */
+ ffestorag item_st; /* Storage for var. */
+ ffesymbol item_sym; /* Var itself. */
+ ffetargetOffset item_offset; /* Offset for var from root. */
+
+ item_exp = ffebld_head (item);
+ item_sym = ffeequiv_symbol (item_exp);
+ if ((item_sym == NULL)
+ || (ffesymbol_equiv (item_sym) == NULL))
+ continue; /* Ignore me. */
+
+ if (item_sym == rooted_sym)
+ continue; /* Rooted sym already set up. */
+
+ if (!ffeequiv_offset_ (&item_offset, item_sym, item_exp, TRUE,
+ eqlist_offset, FALSE))
+ {
+ ffesymbol_set_equiv (item_sym, NULL); /* Don't bother with me anymore. */
+ continue;
+ }
+
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, " Item `%s' at %" ffetargetOffset_f "d",
+ ffesymbol_text (item_sym), item_offset);
+#endif
+
+ if (ffesymbol_rank (item_sym) == 0)
+ num_elements = 1;
+ else
+ num_elements = ffebld_constant_integerdefault (ffebld_conter
+ (ffesymbol_arraysize (item_sym)));
+ ffetarget_layout (ffesymbol_text (item_sym), &alignment, &modulo,
+ &size, ffesymbol_basictype (item_sym),
+ ffesymbol_kindtype (item_sym), ffesymbol_size (item_sym),
+ num_elements);
+ pad = ffetarget_align (ffestorag_ptr_to_alignment (st),
+ ffestorag_ptr_to_modulo (st),
+ item_offset, alignment, modulo);
+ if (pad != 0)
+ {
+ ffebad_start (FFEBAD_EQUIV_ALIGN);
+ ffebad_string (ffesymbol_text (item_sym));
+ ffebad_finish ();
+ ffesymbol_set_equiv (item_sym, NULL); /* Don't bother with me anymore. */
+ continue;
+ }
+
+ /* If the variable's offset is less than the offset for the
+ aggregate storage area, it means it has to expand backwards
+ -- i.e. the new known starting point of the area precedes the
+ old one. This can't happen with COMMON areas (the standard,
+ and common sense, disallow it), but it is normal for local
+ EQUIVALENCE areas.
+
+ Also handle choosing the "documented" rooted symbol for this
+ area here. It's the symbol at the bottom (lowest offset)
+ of the aggregate area, with ties going to the name that would
+ sort to the top of the list of ties. */
+
+ if (item_offset == ffestorag_offset (st))
+ {
+ if ((item_sym != ffestorag_symbol (st))
+ && (strcmp (ffesymbol_text (item_sym),
+ ffesymbol_text (ffestorag_symbol (st)))
+ < 0))
+ ffestorag_set_symbol (st, item_sym);
+ }
+ else if (item_offset < ffestorag_offset (st))
+ {
+ ffetargetOffset new_size;
+
+ /* Increase size of equiv area to start for lower offset relative
+ to root symbol. */
+
+ if (!ffetarget_offset_add (&new_size,
+ ffestorag_offset (st) - item_offset,
+ ffestorag_size (st)))
+ ffetarget_offset_overflow (ffesymbol_text (s));
+ else
+ ffestorag_set_size (st, new_size);
+
+ ffestorag_set_symbol (st, item_sym);
+ ffestorag_set_offset (st, item_offset);
+
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, " [eq offset=%" ffetargetOffset_f
+ "d, size=%" ffetargetOffset_f "d]",
+ item_offset, new_size);
+#endif
+ }
+
+ if ((item_st = ffesymbol_storage (item_sym)) == NULL)
+ { /* Create new ffestorag object, extend equiv
+ area. */
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, ".\n");
+#endif
+ new_storage = TRUE;
+ item_st = ffestorag_new (ffestorag_list_equivs (st));
+ ffestorag_set_parent (item_st, st); /* Initializations
+ happen there. */
+ ffestorag_set_init (item_st, NULL);
+ ffestorag_set_accretion (item_st, NULL);
+ ffestorag_set_symbol (item_st, item_sym);
+ ffestorag_set_size (item_st, size);
+ ffestorag_set_offset (item_st, item_offset);
+ ffestorag_set_alignment (item_st, alignment);
+ ffestorag_set_modulo (item_st, modulo);
+ ffestorag_set_type (item_st, FFESTORAG_typeEQUIV);
+ ffestorag_set_basictype (item_st, ffesymbol_basictype (item_sym));
+ ffestorag_set_kindtype (item_st, ffesymbol_kindtype (item_sym));
+ ffestorag_set_typesymbol (item_st, item_sym);
+ ffestorag_set_is_save (item_st, FALSE); /* Assume FALSE... */
+ if (ffestorag_is_save (st)) /* ...update TRUE */
+ ffestorag_update_save (item_st); /* if needed. */
+ ffestorag_set_is_init (item_st, FALSE); /* Assume FALSE... */
+ if (ffestorag_is_init (st)) /* ...update TRUE */
+ ffestorag_update_init (item_st); /* if needed. */
+ ffesymbol_set_storage (item_sym, item_st);
+ ffesymbol_signal_unreported (item_sym);
+ if (ffesymbol_is_init (item_sym))
+ init = TRUE;
+
+ /* Determine new size of equiv area, complain if overflow. */
+
+ if (!ffetarget_offset_add (&size, item_offset, size)
+ || !ffetarget_offset_add (&size, -ffestorag_offset (st), size))
+ ffetarget_offset_overflow (ffesymbol_text (s));
+ else if (size > ffestorag_size (st))
+ ffestorag_set_size (st, size);
+ ffestorag_update (st, item_sym, ffesymbol_basictype (item_sym),
+ ffesymbol_kindtype (item_sym));
+ }
+ else
+ {
+#if FFEEQUIV_DEBUG
+ fprintf (stderr, " (was %" ffetargetOffset_f "d).\n",
+ ffestorag_offset (item_st));
+#endif
+ /* Make sure offset agrees with known offset. */
+ if (item_offset != ffestorag_offset (item_st))
+ {
+ char io1[40];
+ char io2[40];
+
+ sprintf (&io1[0], "%" ffetargetOffset_f "d", item_offset);
+ sprintf (&io2[0], "%" ffetargetOffset_f "d", ffestorag_offset (item_st));
+ ffebad_start (FFEBAD_EQUIV_MISMATCH);
+ ffebad_string (ffesymbol_text (item_sym));
+ ffebad_string (ffesymbol_text (root_sym));
+ ffebad_string (io1);
+ ffebad_string (io2);
+ ffebad_finish ();
+ }
+ }
+ ffesymbol_set_equiv (item_sym, NULL); /* Don't bother with me anymore. */
+ } /* (For every equivalence item in the list) */
+ ffebld_set_head (list, NULL); /* Don't do this list again. */
+ } /* (For every equivalence list in the list of
+ equivs) */
+ } while (new_storage && need_storage);
+
+ ffesymbol_set_equiv (root_sym, NULL); /* This one has storage now. */
+
+ ffeequiv_kill (eq); /* Fully processed, no longer needed. */
+
+ if (init)
+ ffedata_gather (st); /* Gather subordinate inits into one init. */
+}
+
+/* ffeequiv_offset_ -- Determine offset from start of symbol
+
+ ffetargetOffset offset;
+ ffesymbol s; // Symbol for error reporting.
+ ffebld expr; // opSUBSTR, opARRAYREF, opSYMTER, opANY.
+ bool subtract; // FALSE means add to adjust, TRUE means subtract from it.
+ ffetargetOffset adjust; // Helps keep answer in pos range (unsigned).
+ if (!ffeequiv_offset_(&offset,s,expr,subtract,adjust))
+ // error doing the calculation, message already printed
+
+ Returns the offset represented by the SUBSTR, ARRAYREF, or SUBSTR/ARRAYREF
+ combination added-to/subtracted-from the adjustment specified. If there
+ is an error of some kind, returns FALSE, else returns TRUE. Note that
+ only the first storage unit specified is considered; A(1:1) and A(1:2000)
+ have the same first storage unit and so return the same offset. */
+
+static bool
+ffeequiv_offset_ (ffetargetOffset *offset, ffesymbol s UNUSED,
+ ffebld expr, bool subtract, ffetargetOffset adjust,
+ bool no_precede)
+{
+ ffetargetIntegerDefault value = 0;
+ ffetargetOffset cval; /* Converted value. */
+ ffesymbol sym;
+
+ if (expr == NULL)
+ return FALSE;
+
+again: /* :::::::::::::::::::: */
+
+ switch (ffebld_op (expr))
+ {
+ case FFEBLD_opANY:
+ return FALSE;
+
+ case FFEBLD_opSYMTER:
+ {
+ ffetargetOffset size; /* Size of a single unit. */
+ ffetargetAlign a; /* Ignored. */
+ ffetargetAlign m; /* Ignored. */
+
+ sym = ffebld_symter (expr);
+ if (ffesymbol_basictype (sym) == FFEINFO_basictypeANY)
+ return FALSE;
+
+ ffetarget_layout (ffesymbol_text (sym), &a, &m, &size,
+ ffesymbol_basictype (sym),
+ ffesymbol_kindtype (sym), 1, 1);
+
+ if (value < 0)
+ { /* Really invalid, as in A(-2:5), but in case
+ it's wanted.... */
+ if (!ffetarget_offset (&cval, -value))
+ return FALSE;
+
+ if (!ffetarget_offset_multiply (&cval, cval, size))
+ return FALSE;
+
+ if (subtract)
+ return ffetarget_offset_add (offset, cval, adjust);
+
+ if (no_precede && (cval > adjust))
+ {
+ neg: /* :::::::::::::::::::: */
+ ffebad_start (FFEBAD_COMMON_NEG);
+ ffebad_string (ffesymbol_text (sym));
+ ffebad_finish ();
+ return FALSE;
+ }
+ return ffetarget_offset_add (offset, -cval, adjust);
+ }
+
+ if (!ffetarget_offset (&cval, value))
+ return FALSE;
+
+ if (!ffetarget_offset_multiply (&cval, cval, size))
+ return FALSE;
+
+ if (!subtract)
+ return ffetarget_offset_add (offset, cval, adjust);
+
+ if (no_precede && (cval > adjust))
+ goto neg; /* :::::::::::::::::::: */
+
+ return ffetarget_offset_add (offset, -cval, adjust);
+ }
+
+ case FFEBLD_opARRAYREF:
+ {
+ ffebld symexp = ffebld_left (expr);
+ ffebld subscripts = ffebld_right (expr);
+ ffebld dims;
+ ffetargetIntegerDefault width;
+ ffetargetIntegerDefault arrayval;
+ ffetargetIntegerDefault lowbound;
+ ffetargetIntegerDefault highbound;
+ ffebld subscript;
+ ffebld dim;
+ ffebld low;
+ ffebld high;
+ int rank = 0;
+
+ if (ffebld_op (symexp) != FFEBLD_opSYMTER)
+ return FALSE;
+
+ sym = ffebld_symter (symexp);
+ if (ffesymbol_basictype (sym) == FFEINFO_basictypeANY)
+ return FALSE;
+
+ if (ffesymbol_size (sym) == FFETARGET_charactersizeNONE)
+ width = 1;
+ else
+ width = ffesymbol_size (sym);
+ dims = ffesymbol_dims (sym);
+
+ while (subscripts != NULL)
+ {
+ ++rank;
+ if (dims == NULL)
+ {
+ ffebad_start (FFEBAD_EQUIV_MANY);
+ ffebad_string (ffesymbol_text (sym));
+ ffebad_finish ();
+ return FALSE;
+ }
+
+ subscript = ffebld_head (subscripts);
+ dim = ffebld_head (dims);
+
+ assert (ffebld_op (subscript) == FFEBLD_opCONTER);
+ assert (ffeinfo_basictype (ffebld_info (subscript))
+ == FFEINFO_basictypeINTEGER);
+ assert (ffeinfo_kindtype (ffebld_info (subscript))
+ == FFEINFO_kindtypeINTEGERDEFAULT);
+ arrayval = ffebld_constant_integerdefault (ffebld_conter
+ (subscript));
+
+ assert (ffebld_op (dim) == FFEBLD_opBOUNDS);
+ low = ffebld_left (dim);
+ high = ffebld_right (dim);
+
+ if (low == NULL)
+ lowbound = 1;
+ else
+ {
+ assert (ffeinfo_basictype (ffebld_info (low))
+ == FFEINFO_basictypeINTEGER);
+ assert (ffeinfo_kindtype (ffebld_info (low))
+ == FFEINFO_kindtypeINTEGERDEFAULT);
+ lowbound
+ = ffebld_constant_integerdefault (ffebld_conter (low));
+ }
+
+ assert (ffebld_op (high) == FFEBLD_opCONTER);
+ assert (ffeinfo_basictype (ffebld_info (high))
+ == FFEINFO_basictypeINTEGER);
+ assert (ffeinfo_kindtype (ffebld_info (high))
+ == FFEINFO_kindtypeINTEGER1);
+ highbound
+ = ffebld_constant_integerdefault (ffebld_conter (high));
+
+ if ((arrayval < lowbound) || (arrayval > highbound))
+ {
+ char rankstr[10];
+
+ sprintf (rankstr, "%d", rank);
+ ffebad_start (FFEBAD_EQUIV_SUBSCRIPT);
+ ffebad_string (ffesymbol_text (sym));
+ ffebad_string (rankstr);
+ ffebad_finish ();
+ }
+
+ subscripts = ffebld_trail (subscripts);
+ dims = ffebld_trail (dims);
+
+ value += width * (arrayval - lowbound);
+ if (subscripts != NULL)
+ width *= highbound - lowbound + 1;
+ }
+
+ if (dims != NULL)
+ {
+ ffebad_start (FFEBAD_EQUIV_FEW);
+ ffebad_string (ffesymbol_text (sym));
+ ffebad_finish ();
+ return FALSE;
+ }
+
+ expr = symexp;
+ }
+ goto again; /* :::::::::::::::::::: */
+
+ case FFEBLD_opSUBSTR:
+ {
+ ffebld begin = ffebld_head (ffebld_right (expr));
+
+ expr = ffebld_left (expr);
+ if (ffebld_op (expr) == FFEBLD_opARRAYREF)
+ sym = ffebld_symter (ffebld_left (expr));
+ else if (ffebld_op (expr) == FFEBLD_opSYMTER)
+ sym = ffebld_symter (expr);
+ else
+ sym = NULL;
+
+ if ((sym != NULL)
+ && (ffesymbol_basictype (sym) == FFEINFO_basictypeANY))
+ return FALSE;
+
+ if (begin == NULL)
+ value = 0;
+ else
+ {
+ assert (ffebld_op (begin) == FFEBLD_opCONTER);
+ assert (ffeinfo_basictype (ffebld_info (begin))
+ == FFEINFO_basictypeINTEGER);
+ assert (ffeinfo_kindtype (ffebld_info (begin))
+ == FFEINFO_kindtypeINTEGERDEFAULT);
+
+ value = ffebld_constant_integerdefault (ffebld_conter (begin));
+
+ if ((value < 1)
+ || ((sym != NULL)
+ && (value > ffesymbol_size (sym))))
+ {
+ ffebad_start (FFEBAD_EQUIV_RANGE);
+ ffebad_string (ffesymbol_text (sym));
+ ffebad_finish ();
+ }
+
+ --value;
+ }
+ if ((sym != NULL)
+ && (ffesymbol_basictype (sym) != FFEINFO_basictypeCHARACTER))
+ {
+ ffebad_start (FFEBAD_EQUIV_SUBSTR);
+ ffebad_string (ffesymbol_text (sym));
+ ffebad_finish ();
+ value = 0;
+ }
+ }
+ goto again; /* :::::::::::::::::::: */
+
+ default:
+ assert ("bad op" == NULL);
+ return FALSE;
+ }
+
+}
+
+/* ffeequiv_add -- Add list of equivalences to list of lists for eq object
+
+ ffeequiv eq;
+ ffebld list;
+ ffelexToken t; // points to first item in equivalence list
+ ffeequiv_add(eq,list,t);
+
+ Check the list to make sure only one common symbol is involved (even
+ if multiple times) and agrees with the common symbol for the equivalence
+ object (or it has no common symbol until now). Prepend (or append, it
+ doesn't matter) the list to the list of lists for the equivalence object.
+ Otherwise report an error and return. */
+
+void
+ffeequiv_add (ffeequiv eq, ffebld list, ffelexToken t)
+{
+ ffebld item;
+ ffesymbol symbol;
+ ffesymbol common = ffeequiv_common (eq);
+
+ for (item = list; item != NULL; item = ffebld_trail (item))
+ {
+ symbol = ffeequiv_symbol (ffebld_head (item));
+
+ if (ffesymbol_common (symbol) != NULL) /* Is symbol known in COMMON yet? */
+ {
+ if (common == NULL)
+ common = ffesymbol_common (symbol);
+ else if (common != ffesymbol_common (symbol))
+ {
+ /* Yes, and symbol disagrees with others on the COMMON area. */
+ ffebad_start (FFEBAD_EQUIV_COMMON);
+ ffebad_here (0, ffelex_token_where_line (t), ffelex_token_where_column (t));
+ ffebad_string (ffesymbol_text (common));
+ ffebad_string (ffesymbol_text (ffesymbol_common (symbol)));
+ ffebad_finish ();
+ return;
+ }
+ }
+ }
+
+ if ((common != NULL)
+ && (ffeequiv_common (eq) == NULL)) /* Is COMMON involved already? */
+ ffeequiv_set_common (eq, common); /* No, but it is now. */
+
+ for (item = list; item != NULL; item = ffebld_trail (item))
+ {
+ symbol = ffeequiv_symbol (ffebld_head (item));
+
+ if (ffesymbol_equiv (symbol) == NULL)
+ ffesymbol_set_equiv (symbol, eq);
+ else
+ assert (ffesymbol_equiv (symbol) == eq);
+
+ if (ffesymbol_common (symbol) == NULL) /* Is symbol in a COMMON
+ area? */
+ { /* No (at least not yet). */
+ if (ffesymbol_is_save (symbol))
+ ffeequiv_update_save (eq); /* EQUIVALENCE has >=1 SAVEd entity. */
+ if (ffesymbol_is_init (symbol))
+ ffeequiv_update_init (eq); /* EQUIVALENCE has >=1 init'd entity. */
+ continue; /* Nothing more to do here. */
+ }
+
+#if FFEGLOBAL_ENABLED
+ if (ffesymbol_is_init (symbol))
+ ffeglobal_init_common (ffesymbol_common (symbol), t);
+#endif
+
+ if (ffesymbol_is_save (ffesymbol_common (symbol)))
+ ffeequiv_update_save (eq); /* EQUIVALENCE is in a SAVEd COMMON block. */
+ if (ffesymbol_is_init (ffesymbol_common (symbol)))
+ ffeequiv_update_init (eq); /* EQUIVALENCE is in a init'd COMMON block. */
+ }
+
+ ffeequiv_set_list (eq, ffebld_new_item (list, ffeequiv_list (eq)));
+}
+
+/* ffeequiv_dump -- Dump info on equivalence object
+
+ ffeequiv eq;
+ ffeequiv_dump(eq); */
+
+void
+ffeequiv_dump (ffeequiv eq)
+{
+ if (ffeequiv_common (eq) != NULL)
+ fprintf (dmpout, "(common %s) ", ffesymbol_text (ffeequiv_common (eq)));
+ ffebld_dump (ffeequiv_list (eq));
+}
+
+/* ffeequiv_exec_transition -- Do the hard work on all the equivalence objects
+
+ ffeequiv_exec_transition(); */
+
+void
+ffeequiv_exec_transition ()
+{
+ while (ffeequiv_list_.first != (ffeequiv) &ffeequiv_list_.first)
+ ffeequiv_layout_local_ (ffeequiv_list_.first);
+}
+
+/* ffeequiv_init_2 -- Initialize for new program unit
+
+ ffeequiv_init_2();
+
+ Initializes the list of equivalences. */
+
+void
+ffeequiv_init_2 ()
+{
+ ffeequiv_list_.first = (ffeequiv) &ffeequiv_list_.first;
+ ffeequiv_list_.last = (ffeequiv) &ffeequiv_list_.first;
+}
+
+/* ffeequiv_kill -- Kill equivalence object after removing from list
+
+ ffeequiv eq;
+ ffeequiv_kill(eq);
+
+ Removes equivalence object from master list, then kills it. */
+
+void
+ffeequiv_kill (ffeequiv victim)
+{
+ victim->next->previous = victim->previous;
+ victim->previous->next = victim->next;
+ if (ffe_is_do_internal_checks ())
+ {
+ ffebld list;
+ ffebld item;
+ ffebld expr;
+
+ /* Assert that nobody our victim points to still points to it. */
+
+ assert ((victim->common == NULL)
+ || (ffesymbol_equiv (victim->common) == NULL));
+
+ for (list = victim->list; list != NULL; list = ffebld_trail (list))
+ {
+ for (item = ffebld_head (list); item != NULL; item = ffebld_trail (item))
+ {
+ ffesymbol sym;
+
+ expr = ffebld_head (item);
+ sym = ffeequiv_symbol (expr);
+ if (sym == NULL)
+ continue;
+ assert (ffesymbol_equiv (sym) != victim);
+ }
+ }
+ }
+ malloc_kill_ks (ffe_pool_program_unit (), victim, sizeof (*victim));
+}
+
+/* ffeequiv_layout_cblock -- Lay out storage for common area
+
+ ffestorag st;
+ if (ffeequiv_layout_cblock(st))
+ // at least one equiv'd symbol has init/accretion expr.
+
+ Now that the explicitly COMMONed variables in the common area (whose
+ ffestorag object is passed) have been laid out, lay out the storage
+ for all variables equivalenced into the area by making subordinate
+ ffestorag objects for them. */
+
+bool
+ffeequiv_layout_cblock (ffestorag st)
+{
+ ffesymbol s = ffestorag_symbol (st); /* CBLOCK symbol. */
+ ffebld list; /* List of explicit common vars, in order, in
+ s. */
+ ffebld item; /* List of list of equivalences in a given
+ explicit common var. */
+ ffebld root; /* Expression for (1st) explicit common var
+ in list of eqs. */
+ ffestorag rst; /* Storage for root. */
+ ffetargetOffset root_offset; /* Offset for root into common area. */
+ ffesymbol sr; /* Root itself. */
+ ffeequiv seq; /* Its equivalence object, if any. */
+ ffebld var; /* Expression for equivalence. */
+ ffestorag vst; /* Storage for var. */
+ ffetargetOffset var_offset; /* Offset for var into common area. */
+ ffesymbol sv; /* Var itself. */
+ ffebld altroot; /* Alternate root. */
+ ffesymbol altrootsym; /* Alternate root symbol. */
+ ffetargetAlign alignment;
+ ffetargetAlign modulo;
+ ffetargetAlign pad;
+ ffetargetOffset size;
+ ffetargetOffset num_elements;
+ bool new_storage; /* Established new storage info. */
+ bool need_storage; /* Have need for more storage info. */
+ bool ok;
+ bool init = FALSE;
+
+ assert (st != NULL);
+ assert (ffestorag_type (st) == FFESTORAG_typeCBLOCK);
+ assert (ffesymbol_kind (ffestorag_symbol (st)) == FFEINFO_kindCOMMON);
+
+ for (list = ffesymbol_commonlist (ffestorag_symbol (st));
+ list != NULL;
+ list = ffebld_trail (list))
+ { /* For every variable in the common area */
+ assert (ffebld_op (ffebld_head (list)) == FFEBLD_opSYMTER);
+ sr = ffebld_symter (ffebld_head (list));
+ if ((seq = ffesymbol_equiv (sr)) == NULL)
+ continue; /* No equivalences to process. */
+ rst = ffesymbol_storage (sr);
+ if (rst == NULL)
+ {
+ assert (ffesymbol_kind (sr) == FFEINFO_kindANY);
+ continue;
+ }
+ ffesymbol_set_equiv (sr, NULL); /* Cancel ref to equiv obj. */
+ do
+ {
+ new_storage = FALSE;
+ need_storage = FALSE;
+ for (item = ffeequiv_list (seq); /* Get list of equivs. */
+ item != NULL;
+ item = ffebld_trail (item))
+ { /* For every eqv list in the list of equivs
+ for the variable */
+ altroot = NULL;
+ altrootsym = NULL;
+ for (root = ffebld_head (item);
+ root != NULL;
+ root = ffebld_trail (root))
+ { /* For every equivalence item in the list */
+ sv = ffeequiv_symbol (ffebld_head (root));
+ if (sv == sr)
+ break; /* Found first mention of "rooted" symbol. */
+ if (ffesymbol_storage (sv) != NULL)
+ {
+ altroot = root; /* If no mention, use this guy
+ instead. */
+ altrootsym = sv;
+ }
+ }
+ if (root != NULL)
+ {
+ root = ffebld_head (root); /* Lose its opITEM. */
+ ok = ffeequiv_offset_ (&root_offset, sr, root, FALSE,
+ ffestorag_offset (rst), TRUE);
+ /* Equiv point prior to start of common area? */
+ }
+ else if (altroot != NULL)
+ {
+ /* Equiv point prior to start of common area? */
+ root = ffebld_head (altroot);
+ ok = ffeequiv_offset_ (&root_offset, altrootsym, root,
+ FALSE,
+ ffestorag_offset (ffesymbol_storage (altrootsym)),
+ TRUE);
+ ffesymbol_set_equiv (altrootsym, NULL);
+ }
+ else
+ /* No rooted symbol in list of equivalences! */
+ { /* Assume this was due to opANY and ignore
+ this list for now. */
+ need_storage = TRUE;
+ continue;
+ }
+
+ /* We now know the root symbol and the operating offset of that
+ root into the common area. The other expressions in the
+ list all identify an initial storage unit that must have the
+ same offset. */
+
+ for (var = ffebld_head (item);
+ var != NULL;
+ var = ffebld_trail (var))
+ { /* For every equivalence item in the list */
+ if (ffebld_head (var) == root)
+ continue; /* Except root, of course. */
+ sv = ffeequiv_symbol (ffebld_head (var));
+ if (sv == NULL)
+ continue; /* Except erroneous stuff (opANY). */
+ ffesymbol_set_equiv (sv, NULL); /* Don't need this ref
+ anymore. */
+ if (!ok
+ || !ffeequiv_offset_ (&var_offset, sv,
+ ffebld_head (var), TRUE,
+ root_offset, TRUE))
+ continue; /* Can't do negative offset wrt COMMON. */
+
+ if (ffesymbol_rank (sv) == 0)
+ num_elements = 1;
+ else
+ num_elements = ffebld_constant_integerdefault
+ (ffebld_conter (ffesymbol_arraysize (sv)));
+ ffetarget_layout (ffesymbol_text (sv), &alignment,
+ &modulo, &size,
+ ffesymbol_basictype (sv),
+ ffesymbol_kindtype (sv),
+ ffesymbol_size (sv), num_elements);
+ pad = ffetarget_align (ffestorag_ptr_to_alignment (st),
+ ffestorag_ptr_to_modulo (st),
+ var_offset, alignment, modulo);
+ if (pad != 0)
+ {
+ ffebad_start (FFEBAD_EQUIV_ALIGN);
+ ffebad_string (ffesymbol_text (sv));
+ ffebad_finish ();
+ continue;
+ }
+
+ if ((vst = ffesymbol_storage (sv)) == NULL)
+ { /* Create new ffestorag object, extend
+ cblock. */
+ new_storage = TRUE;
+ vst = ffestorag_new (ffestorag_list_equivs (st));
+ ffestorag_set_parent (vst, st); /* Initializations
+ happen there. */
+ ffestorag_set_init (vst, NULL);
+ ffestorag_set_accretion (vst, NULL);
+ ffestorag_set_symbol (vst, sv);
+ ffestorag_set_size (vst, size);
+ ffestorag_set_offset (vst, var_offset);
+ ffestorag_set_alignment (vst, alignment);
+ ffestorag_set_modulo (vst, modulo);
+ ffestorag_set_type (vst, FFESTORAG_typeEQUIV);
+ ffestorag_set_basictype (vst, ffesymbol_basictype (sv));
+ ffestorag_set_kindtype (vst, ffesymbol_kindtype (sv));
+ ffestorag_set_typesymbol (vst, sv);
+ ffestorag_set_is_save (vst, FALSE); /* Assume FALSE... */
+ if (ffestorag_is_save (st)) /* ...update TRUE */
+ ffestorag_update_save (vst); /* if needed. */
+ ffestorag_set_is_init (vst, FALSE); /* Assume FALSE... */
+ if (ffestorag_is_init (st)) /* ...update TRUE */
+ ffestorag_update_init (vst); /* if needed. */
+ if (!ffetarget_offset_add (&size, var_offset, size))
+ /* Find one size of common block, complain if
+ overflow. */
+ ffetarget_offset_overflow (ffesymbol_text (s));
+ else if (size > ffestorag_size (st))
+ /* Extend common. */
+ ffestorag_set_size (st, size);
+ ffesymbol_set_storage (sv, vst);
+ ffesymbol_set_common (sv, s);
+ ffesymbol_signal_unreported (sv);
+ ffestorag_update (st, sv, ffesymbol_basictype (sv),
+ ffesymbol_kindtype (sv));
+ if (ffesymbol_is_init (sv))
+ init = TRUE;
+ }
+ else
+ {
+ /* Make sure offset agrees with known offset. */
+ if (var_offset != ffestorag_offset (vst))
+ {
+ char io1[40];
+ char io2[40];
+
+ sprintf (&io1[0], "%" ffetargetOffset_f "d", var_offset);
+ sprintf (&io2[0], "%" ffetargetOffset_f "d", ffestorag_offset (vst));
+ ffebad_start (FFEBAD_EQUIV_MISMATCH);
+ ffebad_string (ffesymbol_text (sv));
+ ffebad_string (ffesymbol_text (s));
+ ffebad_string (io1);
+ ffebad_string (io2);
+ ffebad_finish ();
+ }
+ }
+ } /* (For every equivalence item in the list) */
+ } /* (For every eqv list in the list of equivs
+ for the variable) */
+ }
+ while (new_storage && need_storage);
+
+ ffeequiv_kill (seq); /* Kill equiv obj. */
+ } /* (For every variable in the common area) */
+
+ return init;
+}
+
+/* ffeequiv_merge -- Merge two equivalence objects, return the merged result
+
+ ffeequiv eq1;
+ ffeequiv eq2;
+ ffelexToken t; // points to current equivalence item forcing the merge.
+ eq1 = ffeequiv_merge(eq1,eq2,t);
+
+ If the two equivalence objects can be merged, they are, all the
+ ffesymbols in their lists of lists are adjusted to point to the merged
+ equivalence object, and the merged object is returned.
+
+ Otherwise, the two equivalence objects have different non-NULL common
+ symbols, so the merge cannot take place. An error message is issued and
+ NULL is returned. */
+
+ffeequiv
+ffeequiv_merge (ffeequiv eq1, ffeequiv eq2, ffelexToken t)
+{
+ ffebld list;
+ ffebld eqs;
+ ffesymbol symbol;
+ ffebld last = NULL;
+
+ /* If both equivalence objects point to different common-based symbols,
+ complain. Of course, one or both might have NULL common symbols now,
+ and get COMMONed later, but the COMMON statement handler checks for
+ this. */
+
+ if ((ffeequiv_common (eq1) != NULL) && (ffeequiv_common (eq2) != NULL)
+ && (ffeequiv_common (eq1) != ffeequiv_common (eq2)))
+ {
+ ffebad_start (FFEBAD_EQUIV_COMMON);
+ ffebad_here (0, ffelex_token_where_line (t), ffelex_token_where_column (t));
+ ffebad_string (ffesymbol_text (ffeequiv_common (eq1)));
+ ffebad_string (ffesymbol_text (ffeequiv_common (eq2)));
+ ffebad_finish ();
+ return NULL;
+ }
+
+ /* Make eq1 the new, merged object (arbitrarily). */
+
+ if (ffeequiv_common (eq1) == NULL)
+ ffeequiv_set_common (eq1, ffeequiv_common (eq2));
+
+ /* If the victim object has any init'ed entities, so does the new object. */
+
+ if (eq2->is_init)
+ eq1->is_init = TRUE;
+
+#if FFEGLOBAL_ENABLED
+ if (eq1->is_init && (ffeequiv_common (eq1) != NULL))
+ ffeglobal_init_common (ffeequiv_common (eq1), t);
+#endif
+
+ /* If the victim object has any SAVEd entities, then the new object has
+ some. */
+
+ if (ffeequiv_is_save (eq2))
+ ffeequiv_update_save (eq1);
+
+ /* If the victim object has any init'd entities, then the new object has
+ some. */
+
+ if (ffeequiv_is_init (eq2))
+ ffeequiv_update_init (eq1);
+
+ /* Adjust all the symbols in the list of lists of equivalences for the
+ victim equivalence object so they point to the new merged object
+ instead. */
+
+ for (list = ffeequiv_list (eq2); list != NULL; list = ffebld_trail (list))
+ {
+ for (eqs = ffebld_head (list); eqs != NULL; eqs = ffebld_trail (eqs))
+ {
+ symbol = ffeequiv_symbol (ffebld_head (eqs));
+ if (ffesymbol_equiv (symbol) == eq2)
+ ffesymbol_set_equiv (symbol, eq1);
+ else
+ assert (ffesymbol_equiv (symbol) == eq1); /* Can see a sym > once. */
+ }
+
+ /* For convenience, remember where the last ITEM in the outer list is. */
+
+ if (ffebld_trail (list) == NULL)
+ {
+ last = list;
+ break;
+ }
+ }
+
+ /* Append the list of lists in the new, merged object to the list of lists
+ in the victim object, then use the new combined list in the new merged
+ object. */
+
+ ffebld_set_trail (last, ffeequiv_list (eq1));
+ ffeequiv_set_list (eq1, ffeequiv_list (eq2));
+
+ /* Unlink and kill the victim object. */
+
+ ffeequiv_kill (eq2);
+
+ return eq1; /* Return the new merged object. */
+}
+
+/* ffeequiv_new -- Create new equivalence object, put in list
+
+ ffeequiv eq;
+ eq = ffeequiv_new();
+
+ Creates a new equivalence object and adds it to the list of equivalence
+ objects. */
+
+ffeequiv
+ffeequiv_new ()
+{
+ ffeequiv eq;
+
+ eq = malloc_new_ks (ffe_pool_program_unit (), "ffeequiv", sizeof (*eq));
+ eq->next = (ffeequiv) &ffeequiv_list_.first;
+ eq->previous = ffeequiv_list_.last;
+ ffeequiv_set_common (eq, NULL); /* No COMMON area yet. */
+ ffeequiv_set_list (eq, NULL); /* No list of lists of equivalences yet. */
+ ffeequiv_set_is_save (eq, FALSE);
+ ffeequiv_set_is_init (eq, FALSE);
+ eq->next->previous = eq;
+ eq->previous->next = eq;
+
+ return eq;
+}
+
+/* ffeequiv_symbol -- Return symbol for equivalence expression
+
+ ffesymbol symbol;
+ ffebld expr;
+ symbol = ffeequiv_symbol(expr);
+
+ Finds the terminal SYMTER in an equivalence expression and returns the
+ ffesymbol for it. */
+
+ffesymbol
+ffeequiv_symbol (ffebld expr)
+{
+ assert (expr != NULL);
+
+again: /* :::::::::::::::::::: */
+
+ switch (ffebld_op (expr))
+ {
+ case FFEBLD_opARRAYREF:
+ case FFEBLD_opSUBSTR:
+ expr = ffebld_left (expr);
+ goto again; /* :::::::::::::::::::: */
+
+ case FFEBLD_opSYMTER:
+ return ffebld_symter (expr);
+
+ case FFEBLD_opANY:
+ return NULL;
+
+ default:
+ assert ("bad eq expr" == NULL);
+ return NULL;
+ }
+}
+
+/* ffeequiv_update_init -- Update the INIT flag for the area to TRUE
+
+ ffeequiv eq;
+ ffeequiv_update_init(eq);
+
+ If the INIT flag for the <eq> object is already set, return. Else,
+ set it TRUE and call ffe*_update_init for all objects contained in
+ this one. */
+
+void
+ffeequiv_update_init (ffeequiv eq)
+{
+ ffebld list; /* Current list in list of lists. */
+ ffebld item; /* Current item in current list. */
+ ffebld expr; /* Expression in head of current item. */
+
+ if (eq->is_init)
+ return;
+
+ eq->is_init = TRUE;
+
+ if ((eq->common != NULL)
+ && !ffesymbol_is_init (eq->common))
+ ffesymbol_update_init (eq->common); /* Shouldn't be needed. */
+
+ for (list = eq->list; list != NULL; list = ffebld_trail (list))
+ {
+ for (item = ffebld_head (list); item != NULL; item = ffebld_trail (item))
+ {
+ expr = ffebld_head (item);
+
+ again: /* :::::::::::::::::::: */
+
+ switch (ffebld_op (expr))
+ {
+ case FFEBLD_opANY:
+ break;
+
+ case FFEBLD_opSYMTER:
+ if (!ffesymbol_is_init (ffebld_symter (expr)))
+ ffesymbol_update_init (ffebld_symter (expr));
+ break;
+
+ case FFEBLD_opARRAYREF:
+ expr = ffebld_left (expr);
+ goto again; /* :::::::::::::::::::: */
+
+ case FFEBLD_opSUBSTR:
+ expr = ffebld_left (expr);
+ goto again; /* :::::::::::::::::::: */
+
+ default:
+ assert ("bad op for ffeequiv_update_init" == NULL);
+ break;
+ }
+ }
+ }
+}
+
+/* ffeequiv_update_save -- Update the SAVE flag for the area to TRUE
+
+ ffeequiv eq;
+ ffeequiv_update_save(eq);
+
+ If the SAVE flag for the <eq> object is already set, return. Else,
+ set it TRUE and call ffe*_update_save for all objects contained in
+ this one. */
+
+void
+ffeequiv_update_save (ffeequiv eq)
+{
+ ffebld list; /* Current list in list of lists. */
+ ffebld item; /* Current item in current list. */
+ ffebld expr; /* Expression in head of current item. */
+
+ if (eq->is_save)
+ return;
+
+ eq->is_save = TRUE;
+
+ if ((eq->common != NULL)
+ && !ffesymbol_is_save (eq->common))
+ ffesymbol_update_save (eq->common); /* Shouldn't be needed. */
+
+ for (list = eq->list; list != NULL; list = ffebld_trail (list))
+ {
+ for (item = ffebld_head (list); item != NULL; item = ffebld_trail (item))
+ {
+ expr = ffebld_head (item);
+
+ again: /* :::::::::::::::::::: */
+
+ switch (ffebld_op (expr))
+ {
+ case FFEBLD_opANY:
+ break;
+
+ case FFEBLD_opSYMTER:
+ if (!ffesymbol_is_save (ffebld_symter (expr)))
+ ffesymbol_update_save (ffebld_symter (expr));
+ break;
+
+ case FFEBLD_opARRAYREF:
+ expr = ffebld_left (expr);
+ goto again; /* :::::::::::::::::::: */
+
+ case FFEBLD_opSUBSTR:
+ expr = ffebld_left (expr);
+ goto again; /* :::::::::::::::::::: */
+
+ default:
+ assert ("bad op for ffeequiv_update_save" == NULL);
+ break;
+ }
+ }
+ }
+}