aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-return.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-return.c')
-rw-r--r--gcc/tree-ssa-return.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/gcc/tree-ssa-return.c b/gcc/tree-ssa-return.c
new file mode 100644
index 00000000000..a333c7038fb
--- /dev/null
+++ b/gcc/tree-ssa-return.c
@@ -0,0 +1,232 @@
+/* Optimization of return nodes by merging them into one basic block.
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+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 "errors.h"
+#include "ggc.h"
+#include "tree.h"
+#include "flags.h"
+#include "rtl.h"
+#include "tm_p.h"
+#include "basic-block.h"
+#include "timevar.h"
+#include "diagnostic.h"
+#include "tree-flow.h"
+#include "tree-pass.h"
+#include "tree-dump.h"
+
+static void tree_ssa_return (void);
+
+
+/* Build a temporary. Make sure and register it to be renamed. */
+
+static tree
+make_temp (tree type)
+{
+ tree t = create_tmp_var (type, NULL);
+ add_referenced_tmp_var (t);
+ bitmap_set_bit (vars_to_rename, var_ann (t)->uid);
+ return t;
+}
+
+
+static void
+tree_ssa_return (void)
+{
+ basic_block bb;
+
+ /* Search every basic block for return nodes we may be able to optimize. */
+ FOR_EACH_BB (bb)
+ {
+ tree returnstmt = last_stmt (bb);
+ edge pred = bb->pred;
+ basic_block bb_other = NULL;
+ tree returnstmt_other;
+ basic_block new_bb = NULL;
+ tree new_var = NULL;
+ block_stmt_iterator bsi, bsi_other, new_bsi;
+ tree ret_decl;
+ tree type;
+
+ /* If we have no predecessor, we can not do this for
+ the only basic block. */
+ if (!pred)
+ continue;
+
+ /* Cannot do this if there is no statements. */
+ if (!returnstmt)
+ continue;
+
+ if (TREE_CODE (returnstmt) == RETURN_EXPR)
+ ;
+ else
+ continue;
+
+ /* Only do this optimization for basic blocks which
+ have only one predecessor. */
+ if (!pred || pred->pred_next)
+ continue;
+
+ /* Only do this for predecessor which are only conditional ones. */
+ if (pred->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE))
+ ;
+ else
+ continue;
+
+ /* Cannot do this optimization if predecessor is abnormal. */
+ if (pred->flags & EDGE_ABNORMAL)
+ continue;
+
+ /* No reason to do this if we are not returning a value. */
+ if (TREE_OPERAND (returnstmt, 0) == NULL)
+ continue;
+
+ /* Make sure that the predecessor's successor is not an abnormal. */
+ if (pred->src->succ->flags & EDGE_ABNORMAL)
+ continue;
+
+ /* Find the other basic block which is connected to the predecessor. */
+ if (pred->src->succ->dest == bb)
+ bb_other = pred->src->succ->succ_next->dest;
+ else
+ bb_other = pred->src->succ->dest;
+
+
+ /* If we do not have a basic block for the other edge, just continue. */
+ if (!bb_other)
+ continue;
+
+
+ /* Only do the optimization if are bb_other is only linked from one BB. */
+ if (bb_other->pred->src != pred->src)
+ continue;
+ else if (bb_other->pred->pred_next)
+ continue;
+
+ returnstmt_other = last_stmt (bb_other);
+
+ /* Cannot do the optimization if the other basic block does not
+ end with a return. */
+ if (!returnstmt_other || TREE_CODE (returnstmt_other) != RETURN_EXPR)
+ continue;
+
+ /* No reason to do this if we are not returning a value. */
+ if (TREE_OPERAND (returnstmt_other, 0) == NULL)
+ continue;
+
+ /* If we do not have a modify expression on both returns, there is no point
+ in doing this optimization. */
+ if (TREE_CODE (TREE_OPERAND (returnstmt, 0)) != MODIFY_EXPR)
+ continue;
+
+ if (TREE_CODE (TREE_OPERAND (returnstmt_other, 0)) != MODIFY_EXPR)
+ continue;
+
+ /* Create the new basic block. */
+ new_bb = create_empty_bb (bb_other);
+
+ /* Redirect the two basic blocks (the ones with the returns)
+ to the new basic block. */
+ redirect_edge_and_branch (bb->succ, new_bb);
+ redirect_edge_and_branch (bb_other->succ, new_bb);
+
+ bsi = bsi_last (bb);
+ bsi_other = bsi_last (bb_other);
+
+
+ ret_decl = TREE_OPERAND (TREE_OPERAND (returnstmt, 0), 0);
+
+ type = TREE_TYPE (ret_decl);
+
+ /* Add the new variable to hold the return values. */
+ new_var = make_temp (type);
+
+ if (TREE_CODE (ret_decl) == SSA_NAME)
+ ret_decl = SSA_NAME_VAR (ret_decl);
+
+ /* Assign the new temp variable to hold the return value of
+ the first side of the branch. */
+ bsi_insert_after (&bsi, build (MODIFY_EXPR, type, new_var,
+ TREE_OPERAND (TREE_OPERAND (returnstmt, 0), 1)),
+ BSI_NEW_STMT);
+
+
+ /* Do the other side. */
+
+ bsi_insert_after (&bsi_other, build (MODIFY_EXPR, type, new_var,
+ TREE_OPERAND (TREE_OPERAND (returnstmt_other,
+ 0), 1)),
+ BSI_NEW_STMT);
+
+
+ /* Build the new basic block which continues the return. */
+ new_bsi = bsi_last (new_bb);
+ bsi_insert_after (&new_bsi, build1 (RETURN_EXPR, void_type_node,
+ build (MODIFY_EXPR, type, ret_decl,
+ new_var)), TSI_NEW_STMT);
+
+
+
+ /* Make sure that the return value gets renamed if needed */
+ bitmap_set_bit (vars_to_rename, var_ann (ret_decl)->uid);
+
+ /* The new basic block exits. */
+ make_edge (new_bb, EXIT_BLOCK_PTR, 0);
+
+ /* update the DOM info if we have to. */
+ if (dom_computed[CDI_DOMINATORS] >= DOM_CONS_OK)
+ set_immediate_dominator (CDI_DOMINATORS, new_bb, pred->src);
+ }
+
+
+ cleanup_tree_cfg ();
+}
+
+
+/* Always do these optimizations if we have SSA
+ trees to work on. */
+static bool
+gate_return (void)
+{
+ return 1;
+}
+
+struct tree_opt_pass pass_return =
+{
+ "return", /* name */
+ gate_return, /* gate */
+ tree_ssa_return, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_TREE_RETURN, /* tv_id */
+ PROP_cfg | PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func | TODO_ggc_collect /* todo_flags_finish */
+ | TODO_verify_ssa | TODO_rename_vars
+ | TODO_verify_flow
+};
+
+