diff options
Diffstat (limited to 'gcc/tree-ssa-return.c')
-rw-r--r-- | gcc/tree-ssa-return.c | 232 |
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 +}; + + |