tree-ssa-return.c   [plain text]


/* 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
};