/* Build expressions with type checking for C compiler.
- Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996
+ 1997, 1998, 2000 Free Software Foundation, Inc.
This file is part of GNU CC.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA. */
/* This file is part of the C front end.
both their declarations and the expansion of statements using them. */
#include "config.h"
-#include <stdio.h>
+#include "system.h"
#include "tree.h"
#include "c-tree.h"
#include "flags.h"
-
-static void expand_stmt_with_iterators_1 ();
-static tree collect_iterators();
-static void iterator_loop_prologue ();
-static void iterator_loop_epilogue ();
-static void add_ixpansion ();
-static void delete_ixpansion();
-static int top_level_ixpansion_p ();
-static void istack_sublevel_to_current ();
+#include "obstack.h"
+#include "rtl.h"
+#include "toplev.h"
+#include "expr.h"
\f
-void
-iterator_for_loop_start (idecl)
- tree idecl;
+/*
+ KEEPING TRACK OF EXPANSIONS
+
+ In order to clean out expansions corresponding to statements inside
+ "{(...)}" constructs we have to keep track of all expansions. The
+ cleanup is needed when an automatic, or implicit, expansion on
+ iterator, say X, happens to a statement which contains a {(...)}
+ form with a statement already expanded on X. In this case we have
+ to go back and cleanup the inner expansion. This can be further
+ complicated by the fact that {(...)} can be nested.
+
+ To make this cleanup possible, we keep lists of all expansions, and
+ to make it work for nested constructs, we keep a stack. The list at
+ the top of the stack (ITER_STACK.CURRENT_LEVEL) corresponds to the
+ currently parsed level. All expansions of the levels below the
+ current one are kept in one list whose head is pointed to by
+ ITER_STACK.SUBLEVEL_FIRST (SUBLEVEL_LAST is there for making merges
+ easy). The process works as follows:
+
+ -- On "({" a new node is added to the stack by PUSH_ITERATOR_STACK.
+ The sublevel list is not changed at this point.
+
+ -- On "})" the list for the current level is appended to the sublevel
+ list.
+
+ -- On ";" sublevel lists are appended to the current level lists.
+ The reason is this: if they have not been superseded by the
+ expansion at the current level, they still might be
+ superseded later by the expansion on the higher level.
+ The levels do not have to distinguish levels below, so we
+ can merge the lists together. */
+
+struct ixpansion
{
- iterator_loop_prologue (idecl, 0, 0);
-}
+ tree ixdecl; /* Iterator decl */
+ rtx ixprologue_start; /* First insn of epilogue. NULL means */
+ /* explicit (FOR) expansion*/
+ rtx ixprologue_end;
+ rtx ixepilogue_start;
+ rtx ixepilogue_end;
+ struct ixpansion *next; /* Next in the list */
+};
+
+struct iter_stack_node
+{
+ struct ixpansion *first; /* Head of list of ixpansions */
+ struct ixpansion *last; /* Last node in list of ixpansions */
+ struct iter_stack_node *next; /* Next level iterator stack node */
+};
+
+struct iter_stack_node *iter_stack;
+struct iter_stack_node sublevel_ixpansions;
+
+/* A special obstack, and a pointer to the start of
+ all the data in it (so we can free everything easily). */
+static struct obstack ixp_obstack;
+static char *ixp_firstobj;
+
+/* During collect_iterators, a list of SAVE_EXPRs already scanned. */
+static tree save_exprs;
+
+static void expand_stmt_with_iterators_1 PARAMS ((tree, tree));
+static tree collect_iterators PARAMS ((tree, tree));
+static void iterator_loop_prologue PARAMS ((tree, rtx *, rtx *));
+static void iterator_loop_epilogue PARAMS ((tree, rtx *, rtx *));
+static int top_level_ixpansion_p PARAMS ((void));
+static void isn_append PARAMS ((struct iter_stack_node *,
+ struct iter_stack_node *));
+static void istack_sublevel_to_current PARAMS ((void));
+static void add_ixpansion PARAMS ((tree, rtx, rtx, rtx, rtx));
+static void delete_ixpansion PARAMS ((tree));
+\f
+/* Initialize our obstack once per compilation. */
void
-iterator_for_loop_end (idecl)
- tree idecl;
+init_iterators ()
{
- iterator_loop_epilogue (idecl, 0, 0);
+ gcc_obstack_init (&ixp_obstack);
+ ixp_firstobj = (char *) obstack_alloc (&ixp_obstack, 0);
}
+/* Handle the start of an explicit `for' loop for iterator IDECL. */
+
void
-iterator_for_loop_record (idecl)
+iterator_for_loop_start (idecl)
tree idecl;
{
+ ITERATOR_BOUND_P (idecl) = 1;
add_ixpansion (idecl, 0, 0, 0, 0);
+ iterator_loop_prologue (idecl, 0, 0);
}
+/* Handle the end of an explicit `for' loop for iterator IDECL. */
-/*
- ITERATOR DECLS
-
-Iterators are implemented as integer decls with a special flag set
-(rms's idea). This makes eliminates the need for special type
-checking. The flag is accesed using the ITERATOR_P macro. Each
-iterator's limit is saved as a decl with a special name. The decl is
-initialized with the limit value -- this way we get all the necessary
-semantical processing for free by calling finish decl. We might still
-eliminate that decl later -- it takes up time and space and, more
-importantly, produces strange error messages when something is wrong
-with the initializing expresison. */
-
-tree
-build_iterator_decl (id, limit)
- tree id, limit;
+void
+iterator_for_loop_end (idecl)
+ tree idecl;
{
- tree type = integer_type_node, lim_decl;
- tree t1, t2, t3;
- tree start_node, limit_node, step_node;
- tree decl;
-
- if (limit)
- {
- limit_node = save_expr (limit);
- SAVE_EXPR_CONTEXT (limit_node) = current_function_decl;
- }
- else
- abort ();
- lim_decl = build_limit_decl (id, limit_node);
- push_obstacks_nochange ();
- decl = build_decl (VAR_DECL, id, type);
- ITERATOR_P (decl) = 1;
- ITERATOR_LIMIT (decl) = lim_decl;
- finish_decl (pushdecl (decl), 0, 0);
- return decl;
+ iterator_loop_epilogue (idecl, 0, 0);
+ ITERATOR_BOUND_P (idecl) = 0;
}
\f
/*
ITERATOR RTL EXPANSIONS
- Expanding simple statements with iterators is pretty straightforward:
- collect (collect_iterators) the list of all "free" iterators in the
- statement and for each of them add a special prologue before and an
- epilogue after the expansion for the statement. Iterator is "free" if
- it has not been "bound" by a FOR operator. The rtx associated with the
- iterator's decl is used as the loop counter. Special processing is
- used for "{(...)}" constructs: each iterator expansion is registered
- (by "add_ixpansion" function) and inner expansions are superseded by
- outer ones. The cleanup of superseded expansions is done by a call to
- delete_ixpansion. */
+ Expanding simple statements with iterators is straightforward:
+ collect the list of all free iterators in the statement, and
+ generate a loop for each of them.
+
+ An iterator is "free" if it has not been "bound" by a FOR
+ operator. The DECL_RTL of the iterator is the loop counter. */
+
+/* Expand a statement STMT, possibly containing iterator usage, into RTL. */
void
iterator_expand (stmt)
tree stmt;
{
- tree iter_list = collect_iterators (stmt, NULL_TREE);
+ tree iter_list;
+ save_exprs = NULL_TREE;
+ iter_list = collect_iterators (stmt, NULL_TREE);
expand_stmt_with_iterators_1 (stmt, iter_list);
istack_sublevel_to_current ();
}
}
-/* Return a list containing all the free (i.e. not bound by "for"
- statement or anaccumulator) iterators mentioned in EXP,
- plus those in LIST. Duplicates are avoided. */
+/* Return a list containing all the free (i.e. not bound by a
+ containing `for' statement) iterators mentioned in EXP, plus those
+ in LIST. Do not add duplicate entries to the list. */
static tree
collect_iterators (exp, list)
return list;
}
+ case SAVE_EXPR:
+ /* In each scan, scan a given save_expr only once. */
+ if (value_member (exp, save_exprs))
+ return list;
+
+ save_exprs = tree_cons (NULL_TREE, exp, save_exprs);
+ return collect_iterators (TREE_OPERAND (exp, 0), list);
+
/* we do not automatically iterate blocks -- one must */
/* use the FOR construct to do that */
return list;
default:
- switch (TREE_CODE_CLASS (code))
+ switch (TREE_CODE_CLASS (TREE_CODE (exp)))
{
case '1':
+ return collect_iterators (TREE_OPERAND (exp, 0), list);
+
case '2':
case '<':
+ return collect_iterators (TREE_OPERAND (exp, 0),
+ collect_iterators (TREE_OPERAND (exp, 1),
+ list));
+
case 'e':
case 'r':
{
- int num_args = tree_code_length[code];
+ int num_args = tree_code_length[(int) TREE_CODE (exp)];
int i;
- the_list = (tree) 0;
+
+ /* Some tree codes have RTL, not trees, as operands. */
+ switch (TREE_CODE (exp))
+ {
+ case CALL_EXPR:
+ num_args = 2;
+ break;
+ case METHOD_CALL_EXPR:
+ num_args = 3;
+ break;
+ case WITH_CLEANUP_EXPR:
+ num_args = 1;
+ break;
+ case RTL_EXPR:
+ return list;
+ default:
+ break;
+ }
+
for (i = 0; i < num_args; i++)
list = collect_iterators (TREE_OPERAND (exp, i), list);
return list;
}
+ default:
+ return list;
}
}
}
tree idecl;
rtx *start_note, *end_note;
{
+ tree expr;
+
/* Force the save_expr in DECL_INITIAL to be calculated
if it hasn't been calculated yet. */
- expand_expr (DECL_INITIAL (idecl), 0, VOIDmode, 0);
+ expand_expr (DECL_INITIAL (idecl), const0_rtx, VOIDmode,
+ EXPAND_NORMAL);
if (DECL_RTL (idecl) == 0)
expand_decl (idecl);
if (start_note)
*start_note = emit_note (0, NOTE_INSN_DELETED);
+
/* Initialize counter. */
- expand_expr (build_modify_expr (idecl, NOP_EXPR, integer_zero_node),
- 0, VOIDmode, 0);
+ expr = build (MODIFY_EXPR, TREE_TYPE (idecl), idecl, integer_zero_node);
+ TREE_SIDE_EFFECTS (expr) = 1;
+ expand_expr (expr, const0_rtx, VOIDmode, EXPAND_NORMAL);
expand_start_loop_continue_elsewhere (1);
DECL_RTL is zeroed unless we are inside "({...})". The reason for that is
described below.
- When we create two (or more) loops based on the same IDECL, and both
- inside the same "({...})" construct, we must be prepared to delete
- both of the loops and create a single one on the level above, i.e.
- enclosing the "({...})". The new loop has to use the same counter rtl
- because the references to the iterator decl (IDECL) have already been
- expanded as references to the counter rtl.
+ When we create two (or more) loops based on the same IDECL, and
+ both inside the same "({...})" construct, we must be prepared to
+ delete both of the loops and create a single one on the level
+ above, i.e. enclosing the "({...})". The new loop has to use the
+ same counter rtl because the references to the iterator decl
+ (IDECL) have already been expanded as references to the counter
+ rtl.
It is incorrect to use the same counter reg in different functions,
and it is desirable to use different counters in disjoint loops
- when we know there's no need to combine them
- (because then they can get allocated separately). */
+ when we know there's no need to combine them (because then they can
+ get allocated separately). */
static void
iterator_loop_epilogue (idecl, start_note, end_note)
*start_note = emit_note (0, NOTE_INSN_DELETED);
expand_loop_continue_here ();
incr = build_binary_op (PLUS_EXPR, idecl, integer_one_node, 0);
- expand_expr (build_modify_expr (idecl, NOP_EXPR, incr));
+ incr = build (MODIFY_EXPR, TREE_TYPE (idecl), idecl, incr);
+ TREE_SIDE_EFFECTS (incr) = 1;
+ expand_expr (incr, const0_rtx, VOIDmode, EXPAND_NORMAL);
test = build_binary_op (LT_EXPR, idecl, DECL_INITIAL (idecl), 0);
expand_exit_loop_if_false (0, test);
expand_end_loop ();
ITERATOR_BOUND_P (idecl) = 0;
/* we can reset rtl since there is not chance that this expansion */
- /* would be superceded by a higher level one */
- if (top_level_ixpansion_p ())
+ /* would be superseded by a higher level one */
+ /* but don't do this if the decl is static, since we need to share */
+ /* the same decl in that case. */
+ if (top_level_ixpansion_p () && ! TREE_STATIC (idecl))
DECL_RTL (idecl) = 0;
if (end_note)
*end_note = emit_note (0, NOTE_INSN_DELETED);
}
\f
-/*
- KEEPING TRACK OF EXPANSIONS
-
- In order to clean out expansions corresponding to statements inside
- "{(...)}" constructs we have to keep track of all expansions. The
- cleanup is needed when an automatic, or implicit, expansion on
- iterator, say X, happens to a statement which contains a {(...)}
- form with a statement already expanded on X. In this case we have
- to go back and cleanup the inner expansion. This can be further
- complicated by the fact that {(...)} can be nested.
-
- To make this cleanup possible, we keep lists of all expansions, and
- to make it work for nested constructs, we keep a stack. The list at
- the top of the stack (ITER_STACK.CURRENT_LEVEL) corresponds to the
- currently parsed level. All expansions of the levels below the
- current one are kept in one list whose head is pointed to by
- ITER_STACK.SUBLEVEL_FIRST (SUBLEVEL_LAST is there for making merges
- easy). The process works as follows:
-
- -- On "({" a new node is added to the stack by PUSH_ITERATOR_STACK.
- The sublevel list is not changed at this point.
-
- -- On "})" the list for the current level is appended to the sublevel
- list.
-
- -- On ";" sublevel lists are appended to the current level lists.
- The reason is this: if they have not been superseded by the
- expansion at the current level, they still might be
- superseded later by the expansion on the higher level.
- The levels do not have to distinguish levels below, so we
- can merge the lists together. */
-
-struct ixpansion
-{
- tree ixdecl; /* Iterator decl */
- rtx ixprologue_start; /* First insn of epilogue. NULL means */
- /* explicit (FOR) expansion*/
- rtx ixprologue_end;
- rtx ixepilogue_start;
- rtx ixepilogue_end;
- struct ixpansion *next; /* Next in the list */
-};
-
-static struct obstack ixp_obstack;
-
-static char *ixp_firstobj;
-
-struct iter_stack_node
-{
- struct ixpansion *first; /* Head of list of ixpansions */
- struct ixpansion *last; /* Last node in list of ixpansions */
- struct iter_stack_node *next; /* Next level iterator stack node */
-};
-
-struct iter_stack_node *iter_stack;
-
-struct iter_stack_node sublevel_ixpansions;
-
-/** Return true if we are not currently inside a "({...})" construct */
+/* Return true if we are not currently inside a "({...})" construct. */
static int
top_level_ixpansion_p ()
push_iterator_stack ()
{
struct iter_stack_node *new_top
- = (struct iter_stack_node*)
+ = (struct iter_stack_node *)
obstack_alloc (&ixp_obstack, sizeof (struct iter_stack_node));
new_top->first = 0;
\f
/* Record an iterator expansion ("ixpansion") for IDECL.
- The remaining paramters are the notes in the loop entry
+ The remaining parameters are the notes in the loop entry
and exit rtl. */
static void
tree idecl;
rtx pro_start, pro_end, epi_start, epi_end;
{
- struct ixpansion* newix;
+ struct ixpansion *newix;
/* Do nothing if we are not inside "({...})",
as in that case this expansion can't need subsequent RTL modification. */
if (iter_stack == 0)
return;
- newix = (struct ixpansion*) obstack_alloc (&ixp_obstack,
- sizeof (struct ixpansion));
+ newix = (struct ixpansion *) obstack_alloc (&ixp_obstack,
+ sizeof (struct ixpansion));
newix->ixdecl = idecl;
newix->ixprologue_start = pro_start;
newix->ixprologue_end = pro_end;
delete_ixpansion (idecl)
tree idecl;
{
- struct ixpansion* previx = 0, *ix;
+ struct ixpansion *previx = 0, *ix;
for (ix = sublevel_ixpansions.first; ix; ix = ix->next)
if (ix->ixdecl == idecl)
if (ix->ixprologue_start == 0)
error_with_decl (idecl,
- "`for (%s)' appears within implicit iteration")
+ "`for (%s)' appears within implicit iteration");
else
{
rtx insn;
previx = ix;
}
\f
-/*
-We initialize iterators obstack once per file
-*/
-
-init_iterators ()
-{
- gcc_obstack_init (&ixp_obstack);
- ixp_firstobj = (char *) obstack_alloc (&ixp_obstack, 0);
-}
-
#ifdef DEBUG_ITERATORS
-/*
-The functions below are for use from source level debugger.
-They print short forms of iterator lists and the iterator stack.
-*/
+/* The functions below are for use from source level debugger.
+ They print short forms of iterator lists and the iterator stack. */
+
+/* Print the name of the iterator D. */
-/* Print the name of the iterator D */
void
-PRDECL (D)
- tree D;
+prdecl (d)
+ tree d;
{
- if (D)
+ if (d)
{
- if (TREE_CODE (D) == VAR_DECL)
+ if (TREE_CODE (d) == VAR_DECL)
{
- tree tname = DECL_NAME (D);
+ tree tname = DECL_NAME (d);
char *dname = IDENTIFIER_POINTER (tname);
fprintf (stderr, dname);
}
else
- fprintf (stderr, "<<Not a Decl!!!>>");
+ fprintf (stderr, "<<?>>");
}
else
- fprintf (stderr, "<<NULL!!>>");
+ fprintf (stderr, "<<0>>");
}
/* Print Iterator List -- names only */
tree head;
{
tree current, next;
- for (current=head; current; current = next)
+ for (current = head; current; current = next)
{
tree node = TREE_VALUE (current);
- PRDECL (node);
+ prdecl (node);
next = TREE_CHAIN (current);
if (next) fprintf (stderr, ",");
}
for (current=head; current; current = next)
{
tree node = current->ixdecl;
- PRDECL (node);
+ prdecl (node);
next = current->next;
if (next)
fprintf (stderr, ",");
return head;
}
-/* Print Iterator Stack*/
+/* Print Iterator Stack. */
void
pis ()
stack_node = stack_node->next)
pixl (stack_node->first);
}
-#endif
+
+#endif /* DEBUG_ITERATORS */