summaryrefslogtreecommitdiff
path: root/gcc/c-simplify.c
diff options
context:
space:
mode:
authordnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-05-13 06:41:07 +0000
committerdnovillo <dnovillo@138bc75d-0d04-0410-961f-82ee72b054a4>2004-05-13 06:41:07 +0000
commit4ee9c6840ad3fc92a9034343278a1e476ad6872a (patch)
treea2568888a519c077427b133de9ece5879a8484a5 /gcc/c-simplify.c
parentebb338380ab170c91e64d38038e6b5ce930d69a1 (diff)
downloadgcc-4ee9c6840ad3fc92a9034343278a1e476ad6872a.tar.gz
Merge tree-ssa-20020619-branch into mainline.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@81764 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/c-simplify.c')
-rw-r--r--gcc/c-simplify.c1102
1 files changed, 1102 insertions, 0 deletions
diff --git a/gcc/c-simplify.c b/gcc/c-simplify.c
new file mode 100644
index 00000000000..bd50b5e025b
--- /dev/null
+++ b/gcc/c-simplify.c
@@ -0,0 +1,1102 @@
+/* Tree lowering pass. This pass gimplifies the tree representation built
+ by the C-based front ends. The structure of gimplified, or
+ language-independent, trees is dictated by the grammar described in this
+ file.
+ Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ Lowering of expressions contributed by Sebastian Pop <s.pop@laposte.net>
+ Re-written to support lowering of whole function trees, documentation
+ and miscellaneous cleanups by Diego Novillo <dnovillo@redhat.com>
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "errors.h"
+#include "varray.h"
+#include "c-tree.h"
+#include "c-common.h"
+#include "tree-simple.h"
+#include "hard-reg-set.h"
+#include "basic-block.h"
+#include "tree-flow.h"
+#include "tree-inline.h"
+#include "diagnostic.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+#include "flags.h"
+#include "rtl.h"
+#include "toplev.h"
+#include "tree-dump.h"
+#include "c-pretty-print.h"
+#include "cgraph.h"
+
+
+/* The gimplification pass converts the language-dependent trees
+ (ld-trees) emitted by the parser into language-independent trees
+ (li-trees) that are the target of SSA analysis and transformations.
+
+ Language-independent trees are based on the SIMPLE intermediate
+ representation used in the McCAT compiler framework:
+
+ "Designing the McCAT Compiler Based on a Family of Structured
+ Intermediate Representations,"
+ L. Hendren, C. Donawa, M. Emami, G. Gao, Justiani, and B. Sridharan,
+ Proceedings of the 5th International Workshop on Languages and
+ Compilers for Parallel Computing, no. 757 in Lecture Notes in
+ Computer Science, New Haven, Connecticut, pp. 406-420,
+ Springer-Verlag, August 3-5, 1992.
+
+ http://www-acaps.cs.mcgill.ca/info/McCAT/McCAT.html
+
+ Basically, we walk down gimplifying the nodes that we encounter. As we
+ walk back up, we check that they fit our constraints, and copy them
+ into temporaries if not. */
+
+/* Local declarations. */
+
+static enum gimplify_status gimplify_expr_stmt (tree *);
+static enum gimplify_status gimplify_decl_stmt (tree *);
+static enum gimplify_status gimplify_for_stmt (tree *, tree *);
+static enum gimplify_status gimplify_while_stmt (tree *);
+static enum gimplify_status gimplify_do_stmt (tree *);
+static enum gimplify_status gimplify_if_stmt (tree *);
+static enum gimplify_status gimplify_switch_stmt (tree *);
+static enum gimplify_status gimplify_return_stmt (tree *);
+static enum gimplify_status gimplify_stmt_expr (tree *);
+static enum gimplify_status gimplify_compound_literal_expr (tree *);
+#if defined ENABLE_CHECKING
+static int is_last_stmt_of_scope (tree);
+#endif
+static enum gimplify_status gimplify_block (tree *, tree *);
+static enum gimplify_status gimplify_cleanup (tree *, tree *);
+static tree gimplify_c_loop (tree, tree, tree, bool);
+static void push_context (void);
+static void pop_context (void);
+static tree c_build_bind_expr (tree, tree);
+static void add_block_to_enclosing (tree);
+static void gimplify_condition (tree *);
+
+enum bc_t { bc_break = 0, bc_continue = 1 };
+static tree begin_bc_block (enum bc_t);
+static tree finish_bc_block (tree, tree);
+static tree build_bc_goto (enum bc_t);
+
+static struct c_gimplify_ctx
+{
+ /* For handling break and continue. */
+ tree current_bc_label;
+ tree bc_id[2];
+} *ctxp;
+
+static void
+push_context (void)
+{
+ if (ctxp)
+ abort ();
+ ctxp = (struct c_gimplify_ctx *) xcalloc (1, sizeof (struct c_gimplify_ctx));
+ ctxp->bc_id[bc_continue] = get_identifier ("continue");
+ ctxp->bc_id[bc_break] = get_identifier ("break");
+}
+
+static void
+pop_context (void)
+{
+ if (!ctxp || ctxp->current_bc_label)
+ abort ();
+ free (ctxp);
+ ctxp = NULL;
+}
+
+/* Gimplification of statement trees. */
+
+/* Convert the tree representation of FNDECL from C frontend trees to
+ GENERIC. */
+
+void
+c_genericize (tree fndecl)
+{
+ FILE *dump_file;
+ int local_dump_flags;
+ struct cgraph_node *cgn;
+
+ /* Dump the C-specific tree IR. */
+ dump_file = dump_begin (TDI_original, &local_dump_flags);
+ if (dump_file)
+ {
+ fprintf (dump_file, "\n;; Function %s",
+ (*lang_hooks.decl_printable_name) (fndecl, 2));
+ fprintf (dump_file, " (%s)\n",
+ IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (fndecl)));
+ fprintf (dump_file, ";; enabled by -%s\n", dump_flag_name (TDI_original));
+ fprintf (dump_file, "\n");
+
+ if (local_dump_flags & TDF_RAW)
+ dump_node (DECL_SAVED_TREE (fndecl),
+ TDF_SLIM | local_dump_flags, dump_file);
+ else
+ print_c_tree (dump_file, DECL_SAVED_TREE (fndecl));
+ fprintf (dump_file, "\n");
+
+ dump_end (TDI_original, dump_file);
+ }
+
+ /* Go ahead and gimplify for now. */
+ push_context ();
+ gimplify_function_tree (fndecl);
+ pop_context ();
+
+ /* Dump the genericized tree IR. */
+ dump_function (TDI_generic, fndecl);
+
+ /* Genericize all nested functions now. We do things in this order so
+ that items like VLA sizes are expanded properly in the context of
+ the correct function. */
+ cgn = cgraph_node (fndecl);
+ for (cgn = cgn->nested; cgn ; cgn = cgn->next_nested)
+ c_genericize (cgn->decl);
+}
+
+/* Entry point for the tree lowering pass. Recursively scan
+ *STMT_P and convert it to a GIMPLE tree. */
+
+int
+c_gimplify_stmt (tree *stmt_p)
+{
+ tree stmt, next;
+ tree outer_pre = NULL_TREE;
+
+ /* PRE and POST are tree chains that contain the side-effects of the
+ gimplified tree. For instance, given the expression tree:
+
+ c = ++a * 3 + b++;
+
+ After gimplification, the tree will be re-written as:
+
+ a = a + 1;
+ t1 = a * 3; <-- PRE
+ c = t1 + b;
+ b = b + 1; <-- POST */
+
+ for (stmt = *stmt_p; stmt && stmt != error_mark_node; stmt = next)
+ {
+ tree pre, post;
+ int saved_stmts_are_full_exprs_p;
+ location_t stmt_locus;
+ enum gimplify_status ret;
+
+ /* Set up context appropriately for handling this statement. */
+ saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
+ prep_stmt (stmt);
+ stmt_locus = input_location;
+
+ pre = NULL_TREE;
+ post = NULL_TREE;
+
+ next = TREE_CHAIN (stmt);
+
+ switch (TREE_CODE (stmt))
+ {
+ case COMPOUND_STMT:
+ stmt = COMPOUND_BODY (stmt);
+ ret = GS_OK;
+ break;
+
+ case SCOPE_STMT:
+ ret = gimplify_block (&stmt, &next);
+ break;
+
+ case FOR_STMT:
+ ret = gimplify_for_stmt (&stmt, &next);
+ break;
+
+ case WHILE_STMT:
+ ret = gimplify_while_stmt (&stmt);
+ break;
+
+ case DO_STMT:
+ ret = gimplify_do_stmt (&stmt);
+ break;
+
+ case IF_STMT:
+ ret = gimplify_if_stmt (&stmt);
+ break;
+
+ case SWITCH_STMT:
+ ret = gimplify_switch_stmt (&stmt);
+ break;
+
+ case EXPR_STMT:
+ ret = gimplify_expr_stmt (&stmt);
+ break;
+
+ case RETURN_STMT:
+ ret = gimplify_return_stmt (&stmt);
+ break;
+
+ case DECL_STMT:
+ ret = gimplify_decl_stmt (&stmt);
+ break;
+
+ case LABEL_STMT:
+ stmt = build1 (LABEL_EXPR, void_type_node, LABEL_STMT_LABEL (stmt));
+ ret = GS_OK;
+ break;
+
+ case GOTO_STMT:
+ stmt = build1 (GOTO_EXPR, void_type_node, GOTO_DESTINATION (stmt));
+ ret = GS_OK;
+ break;
+
+ case CASE_LABEL:
+ {
+ tree label = create_artificial_label ();
+ stmt = build (CASE_LABEL_EXPR, void_type_node,
+ CASE_LOW (stmt), CASE_HIGH (stmt), label);
+ ret = GS_OK;
+ }
+ break;
+
+ case CONTINUE_STMT:
+ stmt = build_bc_goto (bc_continue);
+ ret = GS_OK;
+ break;
+
+ case BREAK_STMT:
+ stmt = build_bc_goto (bc_break);
+ ret = GS_OK;
+ break;
+
+ case CLEANUP_STMT:
+ ret = gimplify_cleanup (&stmt, &next);
+ break;
+
+ case ASM_STMT:
+ {
+ tree new_stmt = build (ASM_EXPR, void_type_node, ASM_STRING (stmt),
+ ASM_OUTPUTS (stmt), ASM_INPUTS (stmt),
+ ASM_CLOBBERS (stmt));
+ ASM_INPUT_P (new_stmt) = ASM_INPUT_P (stmt);
+ ASM_VOLATILE_P (new_stmt) = ASM_VOLATILE_P (stmt);
+ stmt = new_stmt;
+ ret = GS_OK;
+ }
+ break;
+
+ default:
+ if (lang_gimplify_stmt && (*lang_gimplify_stmt) (&stmt, &next))
+ {
+ ret = GS_OK;
+ break;
+ }
+
+ fprintf (stderr, "unhandled statement node in c_gimplify_stmt:\n");
+ debug_tree (stmt);
+ abort ();
+ break;
+ }
+
+ switch (ret)
+ {
+ case GS_ERROR:
+ goto cont;
+ case GS_OK:
+ gimplify_stmt (&stmt);
+ break;
+ case GS_ALL_DONE:
+ break;
+ default:
+ abort ();
+ }
+
+ /* PRE and POST now contain a list of statements for all the
+ side-effects in STMT. */
+
+ append_to_statement_list (stmt, &pre);
+ append_to_statement_list (post, &pre);
+ annotate_all_with_locus (&pre, stmt_locus);
+
+ append_to_statement_list (pre, &outer_pre);
+ cont:
+ /* Restore saved state. */
+ current_stmt_tree ()->stmts_are_full_exprs_p
+ = saved_stmts_are_full_exprs_p;
+ }
+ append_to_statement_list (stmt, &outer_pre);
+ *stmt_p = outer_pre;
+
+ return GS_ALL_DONE;
+}
+
+static void
+add_block_to_enclosing (tree block)
+{
+ tree enclosing;
+
+ for (enclosing = gimple_current_bind_expr ();
+ enclosing; enclosing = TREE_CHAIN (enclosing))
+ if (BIND_EXPR_BLOCK (enclosing))
+ break;
+
+ enclosing = BIND_EXPR_BLOCK (enclosing);
+ BLOCK_SUBBLOCKS (enclosing) = chainon (BLOCK_SUBBLOCKS (enclosing), block);
+}
+
+/* Genericize a scope by creating a new BIND_EXPR.
+ BLOCK is either a BLOCK representing the scope or a chain of _DECLs.
+ In the latter case, we need to create a new BLOCK and add it to the
+ BLOCK_SUBBLOCKS of the enclosing block.
+ BODY is a chain of C _STMT nodes for the contents of the scope, to be
+ genericized. */
+
+static tree
+c_build_bind_expr (tree block, tree body)
+{
+ tree decls, bind;
+
+ if (block == NULL_TREE)
+ decls = NULL_TREE;
+ else if (TREE_CODE (block) == BLOCK)
+ decls = BLOCK_VARS (block);
+ else
+ {
+ decls = block;
+ if (DECL_ARTIFICIAL (decls))
+ block = NULL_TREE;
+ else
+ {
+ block = make_node (BLOCK);
+ BLOCK_VARS (block) = decls;
+ add_block_to_enclosing (block);
+ }
+ }
+
+ if (!body)
+ body = build_empty_stmt ();
+
+ bind = build (BIND_EXPR, void_type_node, decls, body, block);
+ TREE_SIDE_EFFECTS (bind) = 1;
+
+ return bind;
+}
+
+/* Genericize a syntactic block by removing the bracketing SCOPE_STMTs and
+ wrapping the intervening code in a BIND_EXPR. This function assumes
+ that matching SCOPE_STMTs will always appear in the same statement
+ sequence. */
+
+static enum gimplify_status
+gimplify_block (tree *stmt_p, tree *next_p)
+{
+ tree *p;
+ tree block;
+ tree bind;
+ int depth;
+ location_t stmt_locus;
+
+ if (!SCOPE_BEGIN_P (*stmt_p))
+ {
+ /* Can wind up mismatched with syntax errors. */
+ if (!errorcount && !sorrycount)
+ abort ();
+ *stmt_p = NULL;
+ return GS_ERROR;
+ }
+
+ block = SCOPE_STMT_BLOCK (*stmt_p);
+
+ /* Find the matching ending SCOPE_STMT. */
+ depth = 1;
+ for (p = &TREE_CHAIN (*stmt_p);; p = &TREE_CHAIN (*p))
+ {
+ if (*p == NULL)
+ break;
+ if (TREE_CODE (*p) == SCOPE_STMT)
+ {
+ if (SCOPE_BEGIN_P (*p))
+ ++depth;
+ else if (--depth == 0)
+ break;
+ }
+ }
+
+ stmt_locus = input_location;
+ if (*p)
+ {
+ if (SCOPE_STMT_BLOCK (*p) != block)
+ abort ();
+ if (EXPR_LOCUS (*p))
+ stmt_locus = *EXPR_LOCUS (*p);
+ *next_p = TREE_CHAIN (*p);
+ *p = NULL_TREE;
+ }
+ else
+ {
+ /* Can wind up mismatched with syntax errors. */
+ if (!errorcount && !sorrycount)
+ abort ();
+ }
+
+ bind = c_build_bind_expr (block, TREE_CHAIN (*stmt_p));
+ *stmt_p = bind;
+ input_location = stmt_locus;
+
+ return GS_OK;
+}
+
+/* Genericize a CLEANUP_STMT. Just wrap everything from here to the end of
+ the block in a TRY_FINALLY_EXPR. Or a TRY_CATCH_EXPR, if it's an
+ EH-only cleanup. */
+
+static enum gimplify_status
+gimplify_cleanup (tree *stmt_p, tree *next_p)
+{
+ tree stmt = *stmt_p;
+ tree body = TREE_CHAIN (stmt);
+ tree cleanup = CLEANUP_EXPR (stmt);
+ enum tree_code code
+ = (CLEANUP_EH_ONLY (stmt) ? TRY_CATCH_EXPR : TRY_FINALLY_EXPR);
+
+ if (!body)
+ body = build_empty_stmt ();
+ if (!cleanup)
+ cleanup = build_empty_stmt ();
+
+ *stmt_p = build (code, void_type_node, body, cleanup);
+ *next_p = NULL_TREE;
+
+ return GS_OK;
+}
+
+/* Gimplify an EXPR_STMT node.
+
+ STMT is the statement node.
+
+ PRE_P points to the list where side effects that must happen before
+ STMT should be stored.
+
+ POST_P points to the list where side effects that must happen after
+ STMT should be stored. */
+
+static enum gimplify_status
+gimplify_expr_stmt (tree *stmt_p)
+{
+ tree stmt = EXPR_STMT_EXPR (*stmt_p);
+
+ if (stmt == error_mark_node)
+ stmt = NULL;
+
+ /* Gimplification of a statement expression will nullify the
+ statement if all its side effects are moved to *PRE_P and *POST_P.
+
+ In this case we will not want to emit the gimplified statement.
+ However, we may still want to emit a warning, so we do that before
+ gimplification. */
+ if (stmt && (extra_warnings || warn_unused_value))
+ {
+ if (!TREE_SIDE_EFFECTS (stmt))
+ {
+ if (!IS_EMPTY_STMT (stmt)
+ && !VOID_TYPE_P (TREE_TYPE (stmt))
+ && !TREE_NO_WARNING (stmt))
+ warning ("statement with no effect");
+ }
+ else if (warn_unused_value)
+ {
+ /* Kludge for 20020220-2.c. warn_if_unused_value shouldn't use
+ the stmt file location info. */
+ set_file_and_line_for_stmt (input_location);
+ warn_if_unused_value (stmt);
+ }
+ }
+
+ if (stmt == NULL_TREE)
+ stmt = build_empty_stmt ();
+ else if (stmts_are_full_exprs_p ())
+ stmt = build1 (CLEANUP_POINT_EXPR, void_type_node, stmt);
+
+ *stmt_p = stmt;
+
+ return GS_OK;
+}
+
+/* If the condition for a loop (or the like) is a decl, it will be a
+ TREE_LIST where the TREE_PURPOSE is a DECL_STMT and the TREE_VALUE is
+ a use of the decl. Turn such a thing into a COMPOUND_EXPR. */
+
+static void
+gimplify_condition (tree *cond_p)
+{
+ tree cond = *cond_p;
+ if (cond && TREE_CODE (cond) == TREE_LIST)
+ {
+ tree decl = TREE_PURPOSE (cond);
+ tree value = TREE_VALUE (cond);
+ c_gimplify_stmt (&decl);
+ *cond_p = build (COMPOUND_EXPR, TREE_TYPE (value), decl, value);
+ }
+}
+
+/* Begin a scope which can be exited by a break or continue statement. BC
+ indicates which.
+
+ Just creates a label and pushes it into the current context. */
+
+static tree
+begin_bc_block (enum bc_t bc)
+{
+ tree label = create_artificial_label ();
+ DECL_NAME (label) = ctxp->bc_id[bc];
+ TREE_CHAIN (label) = ctxp->current_bc_label;
+ ctxp->current_bc_label = label;
+ return label;
+}
+
+/* Finish a scope which can be exited by a break or continue statement.
+ LABEL was returned from the most recent call to begin_bc_block. BODY is
+ an expression for the contents of the scope.
+
+ If we saw a break (or continue) in the scope, append a LABEL_EXPR to
+ body. Otherwise, just forget the label. */
+
+static tree
+finish_bc_block (tree label, tree body)
+{
+ if (label != ctxp->current_bc_label)
+ abort ();
+
+ if (TREE_USED (label))
+ {
+ tree t, sl = NULL;
+
+ /* Clear the name so flow can delete the label. */
+ DECL_NAME (label) = NULL_TREE;
+ t = build1 (LABEL_EXPR, void_type_node, label);
+
+ append_to_statement_list (body, &sl);
+ append_to_statement_list (t, &sl);
+ body = sl;
+ }
+
+ ctxp->current_bc_label = TREE_CHAIN (label);
+ TREE_CHAIN (label) = NULL_TREE;
+ return body;
+}
+
+/* Build a GOTO_EXPR to represent a break or continue statement. BC
+ indicates which. */
+
+static tree
+build_bc_goto (enum bc_t bc)
+{
+ tree label;
+ tree target_name = ctxp->bc_id[bc];
+
+ /* Look for the appropriate type of label. */
+ for (label = ctxp->current_bc_label;
+ label;
+ label = TREE_CHAIN (label))
+ if (DECL_NAME (label) == target_name)
+ break;
+
+ if (label == NULL_TREE)
+ {
+ if (bc == bc_break)
+ error ("break statement not within loop or switch");
+ else
+ error ("continue statement not within loop or switch");
+
+ return NULL_TREE;
+ }
+
+ /* Mark the label used for finish_bc_block. */
+ TREE_USED (label) = 1;
+ return build1 (GOTO_EXPR, void_type_node, label);
+}
+
+/* Build a generic representation of one of the C loop forms. COND is the
+ loop condition or NULL_TREE. BODY is the (possibly compound) statement
+ controlled by the loop. INCR is the increment expression of a for-loop,
+ or NULL_TREE. COND_IS_FIRST indicates whether the condition is
+ evaluated before the loop body as in while and for loops, or after the
+ loop body as in do-while loops. */
+
+static tree
+gimplify_c_loop (tree cond, tree body, tree incr, bool cond_is_first)
+{
+ tree top, entry, exit, cont_block, break_block, stmt_list, t;
+ location_t stmt_locus;
+
+ stmt_locus = input_location;
+
+ /* Detect do { ... } while (0) and don't generate loop construct. */
+ if (!cond_is_first && cond && integer_zerop (cond))
+ top = cond = NULL;
+ else
+ {
+ /* If we use a LOOP_EXPR here, we have to feed the whole thing
+ back through the main gimplifier to lower it. Given that we
+ have to gimplify the loop body NOW so that we can resolve
+ break/continue stmts, seems easier to just expand to gotos. */
+ top = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+ }
+
+ break_block = begin_bc_block (bc_break);
+
+ if (top)
+ {
+ /* If we have an exit condition, then we build an IF with gotos either
+ out of the loop, or to the top of it. If there's no exit condition,
+ then we just build a jump back to the top. */
+ exit = build_and_jump (&LABEL_EXPR_LABEL (top));
+ if (cond)
+ {
+ gimplify_condition (&cond);
+ t = build_bc_goto (bc_break);
+ exit = build (COND_EXPR, void_type_node, cond, exit, t);
+ exit = fold (exit);
+ gimplify_stmt (&exit);
+ }
+ }
+ else
+ exit = NULL_TREE;
+
+ cont_block = begin_bc_block (bc_continue);
+
+ gimplify_stmt (&body);
+ if (incr && stmts_are_full_exprs_p ())
+ incr = fold (build1 (CLEANUP_POINT_EXPR, void_type_node, incr));
+ gimplify_stmt (&incr);
+
+ body = finish_bc_block (cont_block, body);
+
+ stmt_list = NULL;
+
+ if (cond_is_first && cond)
+ {
+ entry = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+ t = build_and_jump (&LABEL_EXPR_LABEL (entry));
+ append_to_statement_list (t, &stmt_list);
+ }
+ else
+ entry = NULL_TREE;
+
+ append_to_statement_list (top, &stmt_list);
+ append_to_statement_list (body, &stmt_list);
+ append_to_statement_list (incr, &stmt_list);
+ append_to_statement_list (entry, &stmt_list);
+ append_to_statement_list (exit, &stmt_list);
+
+ annotate_all_with_locus (&stmt_list, stmt_locus);
+
+ return finish_bc_block (break_block, stmt_list);
+}
+
+/* Gimplify a FOR_STMT node. Move the stuff in the for-init-stmt into the
+ prequeue and hand off to gimplify_c_loop. */
+
+static enum gimplify_status
+gimplify_for_stmt (tree *stmt_p, tree *next_p)
+{
+ tree stmt = *stmt_p;
+ tree init = FOR_INIT_STMT (stmt);
+
+ if (init)
+ {
+ /* Reorganize the statements so that we do the right thing with a
+ CLEANUP_STMT. We want the FOR_STMT and nothing else to be in the
+ scope of the cleanup, so play with pointers to accomplish that. */
+ FOR_INIT_STMT (stmt) = NULL_TREE;
+ chainon (init, stmt);
+ *stmt_p = init;
+ *next_p = TREE_CHAIN (stmt);
+ TREE_CHAIN (stmt) = NULL_TREE;
+ c_gimplify_stmt (stmt_p);
+ }
+ else
+ *stmt_p = gimplify_c_loop (FOR_COND (stmt), FOR_BODY (stmt),
+ FOR_EXPR (stmt), 1);
+
+ return GS_ALL_DONE;
+}
+
+/* Gimplify a WHILE_STMT node. */
+
+static enum gimplify_status
+gimplify_while_stmt (tree *stmt_p)
+{
+ tree stmt = *stmt_p;
+ *stmt_p = gimplify_c_loop (WHILE_COND (stmt), WHILE_BODY (stmt),
+ NULL_TREE, 1);
+ return GS_ALL_DONE;
+}
+
+/* Gimplify a DO_STMT node. */
+
+static enum gimplify_status
+gimplify_do_stmt (tree *stmt_p)
+{
+ tree stmt = *stmt_p;
+ *stmt_p = gimplify_c_loop (DO_COND (stmt), DO_BODY (stmt),
+ NULL_TREE, 0);
+ return GS_ALL_DONE;
+}
+
+/* Genericize an IF_STMT by turning it into a COND_EXPR. */
+
+static enum gimplify_status
+gimplify_if_stmt (tree *stmt_p)
+{
+ tree stmt, then_, else_;
+
+ stmt = *stmt_p;
+ restart:
+ then_ = THEN_CLAUSE (stmt);
+ else_ = ELSE_CLAUSE (stmt);
+
+ if (!then_)
+ then_ = build_empty_stmt ();
+ if (!else_)
+ else_ = build_empty_stmt ();
+
+ stmt = build (COND_EXPR, void_type_node, IF_COND (stmt), then_, else_);
+ gimplify_condition (& TREE_OPERAND (stmt, 0));
+ *stmt_p = stmt;
+
+ /* Handle properly nested if-else chains via iteration instead of
+ mutual recursion between gimplify.c and c-simplify.c. */
+ annotate_with_locus (stmt, input_location);
+ if (TREE_CODE (else_) == IF_STMT && !TREE_CHAIN (else_))
+ {
+ stmt_p = &COND_EXPR_ELSE (stmt);
+ stmt = else_;
+ prep_stmt (stmt);
+ goto restart;
+ }
+
+ return GS_OK;
+}
+
+/* Genericize a SWITCH_STMT by turning it into a SWITCH_EXPR. */
+
+static enum gimplify_status
+gimplify_switch_stmt (tree *stmt_p)
+{
+ tree stmt = *stmt_p;
+ tree break_block, body;
+ location_t stmt_locus = input_location;
+
+ break_block = begin_bc_block (bc_break);
+
+ gimplify_condition (&SWITCH_COND (stmt));
+
+ body = SWITCH_BODY (stmt);
+ if (!body)
+ body = build_empty_stmt ();
+
+ *stmt_p = build (SWITCH_EXPR, SWITCH_TYPE (stmt), SWITCH_COND (stmt),
+ body, NULL_TREE);
+ annotate_with_locus (*stmt_p, stmt_locus);
+ gimplify_stmt (stmt_p);
+
+ *stmt_p = finish_bc_block (break_block, *stmt_p);
+ return GS_ALL_DONE;
+}
+
+/* Genericize a RETURN_STMT by turning it into a RETURN_EXPR. */
+
+static enum gimplify_status
+gimplify_return_stmt (tree *stmt_p)
+{
+ tree expr = RETURN_STMT_EXPR (*stmt_p);
+ expr = build1 (RETURN_EXPR, void_type_node, expr);
+ if (stmts_are_full_exprs_p ())
+ expr = build1 (CLEANUP_POINT_EXPR, void_type_node, expr);
+ *stmt_p = expr;
+ return GS_OK;
+}
+
+/* Gimplifies a DECL_STMT node T.
+
+ If a declaration V has an initial value I, create an expression 'V = I'
+ and insert it after the DECL_STMT.
+
+ PRE_P is a queue for effects that should happen before the DECL_STMT.
+
+ MID_P is a queue for effects that should happen after the DECL_STMT,
+ but before uses of the initialized decl.
+
+ POST_P is a queue for effects that should happen after uses of the
+ initialized decl.
+
+ Usually these last two will be the same, but they may need to be
+ different if the DECL_STMT is somehow embedded in an expression. */
+
+static enum gimplify_status
+gimplify_decl_stmt (tree *stmt_p)
+{
+ tree stmt = *stmt_p;
+ tree decl = DECL_STMT_DECL (stmt);
+ tree pre = NULL_TREE;
+ tree post = NULL_TREE;
+
+ if (TREE_TYPE (decl) == error_mark_node)
+ {
+ *stmt_p = NULL;
+ return GS_ERROR;
+ }
+
+ if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ tree type = TREE_TYPE (decl);
+ if (TYPE_SIZE_UNIT (type)
+ && !TREE_CONSTANT (TYPE_SIZE_UNIT (type)))
+ {
+ /* This is a variable-sized array type. Simplify its size. */
+ tree temp = TYPE_SIZE_UNIT (type);
+ gimplify_expr (&temp, &pre, &post, is_gimple_val, fb_rvalue);
+ }
+ }
+
+ if (TREE_CODE (decl) == VAR_DECL && !DECL_EXTERNAL (decl))
+ {
+ tree init = DECL_INITIAL (decl);
+
+ if (!TREE_CONSTANT (DECL_SIZE (decl)))
+ {
+ tree pt_type = build_pointer_type (TREE_TYPE (decl));
+ tree alloc, size;
+
+ /* This is a variable-sized decl. Simplify its size and mark it
+ for deferred expansion. Note that mudflap depends on the format
+ of the emitted code: see mx_register_decls(). */
+
+ size = get_initialized_tmp_var (DECL_SIZE_UNIT (decl), &pre, &post);
+ DECL_DEFER_OUTPUT (decl) = 1;
+ alloc = build_function_call_expr
+ (implicit_built_in_decls[BUILT_IN_STACK_ALLOC],
+ tree_cons (NULL_TREE,
+ build1 (ADDR_EXPR, pt_type, decl),
+ tree_cons (NULL_TREE, size, NULL_TREE)));
+ append_to_compound_expr (alloc, &pre);
+ }
+
+ if (init && init != error_mark_node)
+ {
+ if (!TREE_STATIC (decl))
+ {
+ /* Do not warn about int x = x; as it is a GCC extension
+ to turn off this warning but only if warn_init_self
+ is zero. */
+ if (init == decl && !warn_init_self)
+ TREE_NO_WARNING (decl) = 1;
+
+ DECL_INITIAL (decl) = NULL_TREE;
+ init = build (MODIFY_EXPR, void_type_node, decl, init);
+ if (stmts_are_full_exprs_p ())
+ init = build1 (CLEANUP_POINT_EXPR, void_type_node, init);
+ append_to_compound_expr (init, &pre);
+ }
+ else
+ {
+ /* We must still examine initializers for static variables
+ as they may contain a label address. */
+ walk_tree (&init, force_labels_r, NULL, NULL);
+ }
+ }
+
+ /* This decl isn't mentioned in the enclosing block, so add it to the
+ list of temps. FIXME it seems a bit of a kludge to say that
+ anonymous artificial vars aren't pushed, but everything else is. */
+ if (DECL_ARTIFICIAL (decl) && DECL_NAME (decl) == NULL_TREE)
+ gimple_add_tmp_var (decl);
+ }
+
+ append_to_compound_expr (post, &pre);
+ *stmt_p = pre;
+ return GS_OK;
+}
+
+/* Gimplification of expression trees. */
+
+/* Gimplify a C99 compound literal expression. This just means adding the
+ DECL_STMT before the current EXPR_STMT and using its anonymous decl
+ instead. */
+
+static enum gimplify_status
+gimplify_compound_literal_expr (tree *expr_p)
+{
+ tree decl_s = COMPOUND_LITERAL_EXPR_DECL_STMT (*expr_p);
+ tree decl = DECL_STMT_DECL (decl_s);
+
+ /* This decl isn't mentioned in the enclosing block, so add it to the
+ list of temps. FIXME it seems a bit of a kludge to say that
+ anonymous artificial vars aren't pushed, but everything else is. */
+ if (DECL_NAME (decl) == NULL_TREE)
+ gimple_add_tmp_var (decl);
+
+ gimplify_decl_stmt (&decl_s);
+ *expr_p = decl_s ? decl_s : decl;
+ return GS_OK;
+}
+
+/* Do C-specific gimplification. Args are as for gimplify_expr. */
+
+int
+c_gimplify_expr (tree *expr_p, tree *pre_p ATTRIBUTE_UNUSED,
+ tree *post_p ATTRIBUTE_UNUSED)
+{
+ enum tree_code code = TREE_CODE (*expr_p);
+
+ if (STATEMENT_CODE_P (code))
+ return c_gimplify_stmt (expr_p);
+
+ switch (code)
+ {
+ case COMPOUND_LITERAL_EXPR:
+ return gimplify_compound_literal_expr (expr_p);
+
+ case STMT_EXPR:
+ return gimplify_stmt_expr (expr_p);
+
+ default:
+ return GS_UNHANDLED;
+ }
+}
+
+/* Returns the final EXPR_STMT which represents the return value of a
+ STMT_EXPR, or NULL_TREE if none. */
+
+tree
+stmt_expr_last_stmt (tree stmt_expr)
+{
+ tree body = STMT_EXPR_STMT (stmt_expr);
+ tree last_stmt, substmt;
+
+ /* Splice the last expression out of the STMT chain. */
+ last_stmt = NULL_TREE;
+ for (substmt = COMPOUND_BODY (body); substmt;
+ substmt = TREE_CHAIN (substmt))
+ if (TREE_CODE (substmt) != SCOPE_STMT)
+ last_stmt = substmt;
+
+ if (last_stmt == NULL_TREE
+ || TREE_CODE (last_stmt) != EXPR_STMT
+ || (TREE_TYPE (last_stmt)
+ && VOID_TYPE_P (TREE_TYPE (last_stmt))))
+ {
+ location_t loc;
+ if (last_stmt && EXPR_LOCUS (last_stmt))
+ loc = *EXPR_LOCUS (last_stmt);
+ else if (EXPR_LOCUS (stmt_expr))
+ loc = *EXPR_LOCUS (stmt_expr);
+ else
+ loc = input_location;
+ warning ("%Hstatement-expressions should end with a "
+ "non-void expression", &loc);
+ last_stmt = NULL_TREE;
+ }
+
+#if defined ENABLE_CHECKING
+ if (last_stmt && !is_last_stmt_of_scope (last_stmt))
+ abort ();
+#endif
+
+ return last_stmt;
+}
+
+/* Gimplify a STMT_EXPR. EXPR_P points to the expression to gimplify.
+ After gimplification, if the STMT_EXPR returns a value, EXPR_P will
+ point to a new temporary that holds that value; otherwise it will be
+ null.
+
+ PRE_P points to the list where side effects that must happen before
+ *EXPR_P should be stored. */
+
+static enum gimplify_status
+gimplify_stmt_expr (tree *expr_p)
+{
+ tree body = STMT_EXPR_STMT (*expr_p);
+
+ if (VOID_TYPE_P (TREE_TYPE (*expr_p)))
+ {
+ *expr_p = body;
+ return c_gimplify_stmt (expr_p);
+ }
+ else
+ {
+ tree last_stmt = stmt_expr_last_stmt (*expr_p);
+ tree last_expr = NULL_TREE;
+
+ if (last_stmt)
+ {
+ last_expr = EXPR_STMT_EXPR (last_stmt);
+
+ if (stmts_are_full_exprs_p ())
+ last_expr = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (last_expr),
+ last_expr);
+ EXPR_STMT_EXPR (last_stmt) = NULL_TREE;
+ }
+
+ /* Genericize the block. */
+ c_gimplify_stmt (&body);
+
+ /* Now retrofit that last expression into the BIND_EXPR. */
+ if (last_expr)
+ {
+ tree *sub_p;
+
+ if (!STMT_EXPR_NO_SCOPE (*expr_p))
+ {
+ /* Our BIND_EXPR will always be hidden within
+ a STATEMENT_LIST. Discard that. */
+ body = expr_first (body);
+ sub_p = &BIND_EXPR_BODY (body);
+
+ /* Append the last expression to the end of the BIND_EXPR.
+ We'll now re-process this, and let voidify_wrapper_expr
+ do its job. */
+ append_to_statement_list_force (last_expr, sub_p);
+ TREE_TYPE (body) = TREE_TYPE (last_expr);
+ }
+ else
+ append_to_compound_expr (last_expr, &body);
+ }
+
+ *expr_p = body;
+ return GS_OK;
+ }
+}
+
+/* Code generation. */
+
+/* Miscellaneous helpers. */
+
+#if defined ENABLE_CHECKING
+/* Return nonzero if STMT is the last statement of its scope. */
+
+static int
+is_last_stmt_of_scope (tree stmt)
+{
+ return (TREE_CHAIN (stmt) == NULL_TREE
+ || (TREE_CODE (TREE_CHAIN (stmt)) == SCOPE_STMT
+ && SCOPE_END_P (TREE_CHAIN (stmt))));
+}
+#endif