| /* equiv.c -- Implementation File (module.c template V1.0) |
| Copyright (C) 1995, 1996, 1997, 1998, 2003 |
| Free Software Foundation, Inc. |
| Contributed by James Craig Burley. |
| |
| 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. */ |
| ffetargetOffset new_size; |
| |
| 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)) |
| { |
| /* 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 the offset for this storage area is zero (it cannot be positive), |
| that means the alignment/modulo info is already correct. Otherwise, |
| the alignment info is correct, but the modulo info reflects a |
| zero offset, so fix it. */ |
| |
| if (ffestorag_offset (st) < 0) |
| { |
| /* Calculate the initial padding necessary to preserve |
| the alignment/modulo requirements for the storage area. |
| These requirements are themselves kept track of in the |
| record for the storage area as a whole, but really pertain |
| to offset 0 of that area, which is where the root symbol |
| was originally placed. |
| |
| The goal here is to have the offset and size for the area |
| faithfully reflect the area itself, not extra requirements |
| like alignment. So to meet the alignment requirements, |
| the modulo for the area should be set as if the area had an |
| alignment requirement of alignment/0 and was aligned/padded |
| downward to meet the alignment requirements of the area at |
| offset zero, the amount of padding needed being the desired |
| value for the modulo of the area. */ |
| |
| alignment = ffestorag_alignment (st); |
| modulo = ffestorag_modulo (st); |
| |
| /* Since we want to move the whole area *down* (lower memory |
| addresses) as required by the alignment/modulo paid, negate |
| the offset to ffetarget_align, which assumes aligning *up* |
| is desired. */ |
| pad = ffetarget_align (&alignment, &modulo, |
| - ffestorag_offset (st), |
| alignment, 0); |
| ffestorag_set_modulo (st, pad); |
| } |
| |
| 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); |
| |
| if (ffebld_op (subscript) == FFEBLD_opANY) |
| return FALSE; |
| |
| 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)); |
| |
| if (ffebld_op (dim) == FFEBLD_opANY) |
| return FALSE; |
| |
| assert (ffebld_op (dim) == FFEBLD_opBOUNDS); |
| low = ffebld_left (dim); |
| high = ffebld_right (dim); |
| |
| if (low == NULL) |
| lowbound = 1; |
| else |
| { |
| if (ffebld_op (low) == FFEBLD_opANY) |
| return FALSE; |
| |
| assert (ffebld_op (low) == FFEBLD_opCONTER); |
| assert (ffeinfo_basictype (ffebld_info (low)) |
| == FFEINFO_basictypeINTEGER); |
| assert (ffeinfo_kindtype (ffebld_info (low)) |
| == FFEINFO_kindtypeINTEGERDEFAULT); |
| lowbound |
| = ffebld_constant_integerdefault (ffebld_conter (low)); |
| } |
| |
| if (ffebld_op (high) == FFEBLD_opANY) |
| return FALSE; |
| |
| 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_opANY) |
| return FALSE; |
| 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 |
| { |
| if (ffebld_op (begin) == FFEBLD_opANY) |
| return FALSE; |
| 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_exec_transition -- Do the hard work on all the equivalence objects |
| |
| ffeequiv_exec_transition(); */ |
| |
| void |
| ffeequiv_exec_transition (void) |
| { |
| 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 (void) |
| { |
| 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 (void) |
| { |
| 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; |
| } |
| } |
| } |
| } |