OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [tags/] [gnu-dev/] [fsf-gcc-snapshot-1-mar-12/] [or1k-gcc/] [gcc/] [ipa-split.c] - Diff between revs 684 and 783

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 684 Rev 783
/* Function splitting pass
/* Function splitting pass
   Copyright (C) 2010, 2011, 2012
   Copyright (C) 2010, 2011, 2012
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
   Contributed by Jan Hubicka  <jh@suse.cz>
   Contributed by Jan Hubicka  <jh@suse.cz>
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC is free software; you can redistribute it and/or modify it under
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
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
Software Foundation; either version 3, or (at your option) any later
version.
version.
 
 
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.
for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
<http://www.gnu.org/licenses/>.  */
 
 
/* The purpose of this pass is to split function bodies to improve
/* The purpose of this pass is to split function bodies to improve
   inlining.  I.e. for function of the form:
   inlining.  I.e. for function of the form:
 
 
   func (...)
   func (...)
     {
     {
       if (cheap_test)
       if (cheap_test)
         something_small
         something_small
       else
       else
         something_big
         something_big
     }
     }
 
 
   Produce:
   Produce:
 
 
   func.part (...)
   func.part (...)
     {
     {
        something_big
        something_big
     }
     }
 
 
   func (...)
   func (...)
     {
     {
       if (cheap_test)
       if (cheap_test)
         something_small
         something_small
       else
       else
         func.part (...);
         func.part (...);
     }
     }
 
 
   When func becomes inlinable and when cheap_test is often true, inlining func,
   When func becomes inlinable and when cheap_test is often true, inlining func,
   but not fund.part leads to performance improvement similar as inlining
   but not fund.part leads to performance improvement similar as inlining
   original func while the code size growth is smaller.
   original func while the code size growth is smaller.
 
 
   The pass is organized in three stages:
   The pass is organized in three stages:
   1) Collect local info about basic block into BB_INFO structure and
   1) Collect local info about basic block into BB_INFO structure and
      compute function body estimated size and time.
      compute function body estimated size and time.
   2) Via DFS walk find all possible basic blocks where we can split
   2) Via DFS walk find all possible basic blocks where we can split
      and chose best one.
      and chose best one.
   3) If split point is found, split at the specified BB by creating a clone
   3) If split point is found, split at the specified BB by creating a clone
      and updating function to call it.
      and updating function to call it.
 
 
   The decisions what functions to split are in execute_split_functions
   The decisions what functions to split are in execute_split_functions
   and consider_split.
   and consider_split.
 
 
   There are several possible future improvements for this pass including:
   There are several possible future improvements for this pass including:
 
 
   1) Splitting to break up large functions
   1) Splitting to break up large functions
   2) Splitting to reduce stack frame usage
   2) Splitting to reduce stack frame usage
   3) Allow split part of function to use values computed in the header part.
   3) Allow split part of function to use values computed in the header part.
      The values needs to be passed to split function, perhaps via same
      The values needs to be passed to split function, perhaps via same
      interface as for nested functions or as argument.
      interface as for nested functions or as argument.
   4) Support for simple rematerialization.  I.e. when split part use
   4) Support for simple rematerialization.  I.e. when split part use
      value computed in header from function parameter in very cheap way, we
      value computed in header from function parameter in very cheap way, we
      can just recompute it.
      can just recompute it.
   5) Support splitting of nested functions.
   5) Support splitting of nested functions.
   6) Support non-SSA arguments.
   6) Support non-SSA arguments.
   7) There is nothing preventing us from producing multiple parts of single function
   7) There is nothing preventing us from producing multiple parts of single function
      when needed or splitting also the parts.  */
      when needed or splitting also the parts.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tree.h"
#include "tree.h"
#include "target.h"
#include "target.h"
#include "cgraph.h"
#include "cgraph.h"
#include "ipa-prop.h"
#include "ipa-prop.h"
#include "tree-flow.h"
#include "tree-flow.h"
#include "tree-pass.h"
#include "tree-pass.h"
#include "flags.h"
#include "flags.h"
#include "timevar.h"
#include "timevar.h"
#include "diagnostic.h"
#include "diagnostic.h"
#include "tree-dump.h"
#include "tree-dump.h"
#include "tree-inline.h"
#include "tree-inline.h"
#include "fibheap.h"
#include "fibheap.h"
#include "params.h"
#include "params.h"
#include "gimple-pretty-print.h"
#include "gimple-pretty-print.h"
#include "ipa-inline.h"
#include "ipa-inline.h"
 
 
/* Per basic block info.  */
/* Per basic block info.  */
 
 
typedef struct
typedef struct
{
{
  unsigned int size;
  unsigned int size;
  unsigned int time;
  unsigned int time;
} bb_info;
} bb_info;
DEF_VEC_O(bb_info);
DEF_VEC_O(bb_info);
DEF_VEC_ALLOC_O(bb_info,heap);
DEF_VEC_ALLOC_O(bb_info,heap);
 
 
static VEC(bb_info, heap) *bb_info_vec;
static VEC(bb_info, heap) *bb_info_vec;
 
 
/* Description of split point.  */
/* Description of split point.  */
 
 
struct split_point
struct split_point
{
{
  /* Size of the partitions.  */
  /* Size of the partitions.  */
  unsigned int header_time, header_size, split_time, split_size;
  unsigned int header_time, header_size, split_time, split_size;
 
 
  /* SSA names that need to be passed into spit function.  */
  /* SSA names that need to be passed into spit function.  */
  bitmap ssa_names_to_pass;
  bitmap ssa_names_to_pass;
 
 
  /* Basic block where we split (that will become entry point of new function.  */
  /* Basic block where we split (that will become entry point of new function.  */
  basic_block entry_bb;
  basic_block entry_bb;
 
 
  /* Basic blocks we are splitting away.  */
  /* Basic blocks we are splitting away.  */
  bitmap split_bbs;
  bitmap split_bbs;
 
 
  /* True when return value is computed on split part and thus it needs
  /* True when return value is computed on split part and thus it needs
     to be returned.  */
     to be returned.  */
  bool split_part_set_retval;
  bool split_part_set_retval;
};
};
 
 
/* Best split point found.  */
/* Best split point found.  */
 
 
struct split_point best_split_point;
struct split_point best_split_point;
 
 
/* Set of basic blocks that are not allowed to dominate a split point.  */
/* Set of basic blocks that are not allowed to dominate a split point.  */
 
 
static bitmap forbidden_dominators;
static bitmap forbidden_dominators;
 
 
static tree find_retval (basic_block return_bb);
static tree find_retval (basic_block return_bb);
 
 
/* Callback for walk_stmt_load_store_addr_ops.  If T is non-SSA automatic
/* Callback for walk_stmt_load_store_addr_ops.  If T is non-SSA automatic
   variable, check it if it is present in bitmap passed via DATA.  */
   variable, check it if it is present in bitmap passed via DATA.  */
 
 
static bool
static bool
test_nonssa_use (gimple stmt ATTRIBUTE_UNUSED, tree t, void *data)
test_nonssa_use (gimple stmt ATTRIBUTE_UNUSED, tree t, void *data)
{
{
  t = get_base_address (t);
  t = get_base_address (t);
 
 
  if (!t || is_gimple_reg (t))
  if (!t || is_gimple_reg (t))
    return false;
    return false;
 
 
  if (TREE_CODE (t) == PARM_DECL
  if (TREE_CODE (t) == PARM_DECL
      || (TREE_CODE (t) == VAR_DECL
      || (TREE_CODE (t) == VAR_DECL
          && auto_var_in_fn_p (t, current_function_decl))
          && auto_var_in_fn_p (t, current_function_decl))
      || TREE_CODE (t) == RESULT_DECL
      || TREE_CODE (t) == RESULT_DECL
      || TREE_CODE (t) == LABEL_DECL)
      || TREE_CODE (t) == LABEL_DECL)
    return bitmap_bit_p ((bitmap)data, DECL_UID (t));
    return bitmap_bit_p ((bitmap)data, DECL_UID (t));
 
 
  /* For DECL_BY_REFERENCE, the return value is actually a pointer.  We want
  /* For DECL_BY_REFERENCE, the return value is actually a pointer.  We want
     to pretend that the value pointed to is actual result decl.  */
     to pretend that the value pointed to is actual result decl.  */
  if ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t))
  if ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t))
      && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
      && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
      && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (t, 0))) == RESULT_DECL
      && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (t, 0))) == RESULT_DECL
      && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
      && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
    return
    return
      bitmap_bit_p ((bitmap)data,
      bitmap_bit_p ((bitmap)data,
                    DECL_UID (DECL_RESULT (current_function_decl)));
                    DECL_UID (DECL_RESULT (current_function_decl)));
 
 
  return false;
  return false;
}
}
 
 
/* Dump split point CURRENT.  */
/* Dump split point CURRENT.  */
 
 
static void
static void
dump_split_point (FILE * file, struct split_point *current)
dump_split_point (FILE * file, struct split_point *current)
{
{
  fprintf (file,
  fprintf (file,
           "Split point at BB %i\n"
           "Split point at BB %i\n"
           "  header time: %i header size: %i\n"
           "  header time: %i header size: %i\n"
           "  split time: %i split size: %i\n  bbs: ",
           "  split time: %i split size: %i\n  bbs: ",
           current->entry_bb->index, current->header_time,
           current->entry_bb->index, current->header_time,
           current->header_size, current->split_time, current->split_size);
           current->header_size, current->split_time, current->split_size);
  dump_bitmap (file, current->split_bbs);
  dump_bitmap (file, current->split_bbs);
  fprintf (file, "  SSA names to pass: ");
  fprintf (file, "  SSA names to pass: ");
  dump_bitmap (file, current->ssa_names_to_pass);
  dump_bitmap (file, current->ssa_names_to_pass);
}
}
 
 
/* Look for all BBs in header that might lead to the split part and verify
/* Look for all BBs in header that might lead to the split part and verify
   that they are not defining any non-SSA var used by the split part.
   that they are not defining any non-SSA var used by the split part.
   Parameters are the same as for consider_split.  */
   Parameters are the same as for consider_split.  */
 
 
static bool
static bool
verify_non_ssa_vars (struct split_point *current, bitmap non_ssa_vars,
verify_non_ssa_vars (struct split_point *current, bitmap non_ssa_vars,
                     basic_block return_bb)
                     basic_block return_bb)
{
{
  bitmap seen = BITMAP_ALLOC (NULL);
  bitmap seen = BITMAP_ALLOC (NULL);
  VEC (basic_block,heap) *worklist = NULL;
  VEC (basic_block,heap) *worklist = NULL;
  edge e;
  edge e;
  edge_iterator ei;
  edge_iterator ei;
  bool ok = true;
  bool ok = true;
 
 
  FOR_EACH_EDGE (e, ei, current->entry_bb->preds)
  FOR_EACH_EDGE (e, ei, current->entry_bb->preds)
    if (e->src != ENTRY_BLOCK_PTR
    if (e->src != ENTRY_BLOCK_PTR
        && !bitmap_bit_p (current->split_bbs, e->src->index))
        && !bitmap_bit_p (current->split_bbs, e->src->index))
      {
      {
        VEC_safe_push (basic_block, heap, worklist, e->src);
        VEC_safe_push (basic_block, heap, worklist, e->src);
        bitmap_set_bit (seen, e->src->index);
        bitmap_set_bit (seen, e->src->index);
      }
      }
 
 
  while (!VEC_empty (basic_block, worklist))
  while (!VEC_empty (basic_block, worklist))
    {
    {
      gimple_stmt_iterator bsi;
      gimple_stmt_iterator bsi;
      basic_block bb = VEC_pop (basic_block, worklist);
      basic_block bb = VEC_pop (basic_block, worklist);
 
 
      FOR_EACH_EDGE (e, ei, bb->preds)
      FOR_EACH_EDGE (e, ei, bb->preds)
        if (e->src != ENTRY_BLOCK_PTR
        if (e->src != ENTRY_BLOCK_PTR
            && bitmap_set_bit (seen, e->src->index))
            && bitmap_set_bit (seen, e->src->index))
          {
          {
            gcc_checking_assert (!bitmap_bit_p (current->split_bbs,
            gcc_checking_assert (!bitmap_bit_p (current->split_bbs,
                                                e->src->index));
                                                e->src->index));
            VEC_safe_push (basic_block, heap, worklist, e->src);
            VEC_safe_push (basic_block, heap, worklist, e->src);
          }
          }
      for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
      for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
        {
        {
          gimple stmt = gsi_stmt (bsi);
          gimple stmt = gsi_stmt (bsi);
          if (is_gimple_debug (stmt))
          if (is_gimple_debug (stmt))
            continue;
            continue;
          if (walk_stmt_load_store_addr_ops
          if (walk_stmt_load_store_addr_ops
              (stmt, non_ssa_vars, test_nonssa_use, test_nonssa_use,
              (stmt, non_ssa_vars, test_nonssa_use, test_nonssa_use,
               test_nonssa_use))
               test_nonssa_use))
            {
            {
              ok = false;
              ok = false;
              goto done;
              goto done;
            }
            }
          if (gimple_code (stmt) == GIMPLE_LABEL
          if (gimple_code (stmt) == GIMPLE_LABEL
              && test_nonssa_use (stmt, gimple_label_label (stmt),
              && test_nonssa_use (stmt, gimple_label_label (stmt),
                                  non_ssa_vars))
                                  non_ssa_vars))
          {
          {
            ok = false;
            ok = false;
            goto done;
            goto done;
          }
          }
        }
        }
      for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi))
      for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi))
        {
        {
          if (walk_stmt_load_store_addr_ops
          if (walk_stmt_load_store_addr_ops
              (gsi_stmt (bsi), non_ssa_vars, test_nonssa_use, test_nonssa_use,
              (gsi_stmt (bsi), non_ssa_vars, test_nonssa_use, test_nonssa_use,
               test_nonssa_use))
               test_nonssa_use))
            {
            {
              ok = false;
              ok = false;
              goto done;
              goto done;
            }
            }
        }
        }
      FOR_EACH_EDGE (e, ei, bb->succs)
      FOR_EACH_EDGE (e, ei, bb->succs)
        {
        {
          if (e->dest != return_bb)
          if (e->dest != return_bb)
            continue;
            continue;
          for (bsi = gsi_start_phis (return_bb); !gsi_end_p (bsi);
          for (bsi = gsi_start_phis (return_bb); !gsi_end_p (bsi);
               gsi_next (&bsi))
               gsi_next (&bsi))
            {
            {
              gimple stmt = gsi_stmt (bsi);
              gimple stmt = gsi_stmt (bsi);
              tree op = gimple_phi_arg_def (stmt, e->dest_idx);
              tree op = gimple_phi_arg_def (stmt, e->dest_idx);
 
 
              if (!is_gimple_reg (gimple_phi_result (stmt)))
              if (!is_gimple_reg (gimple_phi_result (stmt)))
                continue;
                continue;
              if (TREE_CODE (op) != SSA_NAME
              if (TREE_CODE (op) != SSA_NAME
                  && test_nonssa_use (stmt, op, non_ssa_vars))
                  && test_nonssa_use (stmt, op, non_ssa_vars))
                {
                {
                  ok = false;
                  ok = false;
                  goto done;
                  goto done;
                }
                }
            }
            }
        }
        }
    }
    }
done:
done:
  BITMAP_FREE (seen);
  BITMAP_FREE (seen);
  VEC_free (basic_block, heap, worklist);
  VEC_free (basic_block, heap, worklist);
  return ok;
  return ok;
}
}
 
 
/* If STMT is a call, check the callee against a list of forbidden
/* If STMT is a call, check the callee against a list of forbidden
   predicate functions.  If a match is found, look for uses of the
   predicate functions.  If a match is found, look for uses of the
   call result in condition statements that compare against zero.
   call result in condition statements that compare against zero.
   For each such use, find the block targeted by the condition
   For each such use, find the block targeted by the condition
   statement for the nonzero result, and set the bit for this block
   statement for the nonzero result, and set the bit for this block
   in the forbidden dominators bitmap.  The purpose of this is to avoid
   in the forbidden dominators bitmap.  The purpose of this is to avoid
   selecting a split point where we are likely to lose the chance
   selecting a split point where we are likely to lose the chance
   to optimize away an unused function call.  */
   to optimize away an unused function call.  */
 
 
static void
static void
check_forbidden_calls (gimple stmt)
check_forbidden_calls (gimple stmt)
{
{
  imm_use_iterator use_iter;
  imm_use_iterator use_iter;
  use_operand_p use_p;
  use_operand_p use_p;
  tree lhs;
  tree lhs;
 
 
  /* At the moment, __builtin_constant_p is the only forbidden
  /* At the moment, __builtin_constant_p is the only forbidden
     predicate function call (see PR49642).  */
     predicate function call (see PR49642).  */
  if (!gimple_call_builtin_p (stmt, BUILT_IN_CONSTANT_P))
  if (!gimple_call_builtin_p (stmt, BUILT_IN_CONSTANT_P))
    return;
    return;
 
 
  lhs = gimple_call_lhs (stmt);
  lhs = gimple_call_lhs (stmt);
 
 
  if (!lhs || TREE_CODE (lhs) != SSA_NAME)
  if (!lhs || TREE_CODE (lhs) != SSA_NAME)
    return;
    return;
 
 
  FOR_EACH_IMM_USE_FAST (use_p, use_iter, lhs)
  FOR_EACH_IMM_USE_FAST (use_p, use_iter, lhs)
    {
    {
      tree op1;
      tree op1;
      basic_block use_bb, forbidden_bb;
      basic_block use_bb, forbidden_bb;
      enum tree_code code;
      enum tree_code code;
      edge true_edge, false_edge;
      edge true_edge, false_edge;
      gimple use_stmt = USE_STMT (use_p);
      gimple use_stmt = USE_STMT (use_p);
 
 
      if (gimple_code (use_stmt) != GIMPLE_COND)
      if (gimple_code (use_stmt) != GIMPLE_COND)
        continue;
        continue;
 
 
      /* Assuming canonical form for GIMPLE_COND here, with constant
      /* Assuming canonical form for GIMPLE_COND here, with constant
         in second position.  */
         in second position.  */
      op1 = gimple_cond_rhs (use_stmt);
      op1 = gimple_cond_rhs (use_stmt);
      code = gimple_cond_code (use_stmt);
      code = gimple_cond_code (use_stmt);
      use_bb = gimple_bb (use_stmt);
      use_bb = gimple_bb (use_stmt);
 
 
      extract_true_false_edges_from_block (use_bb, &true_edge, &false_edge);
      extract_true_false_edges_from_block (use_bb, &true_edge, &false_edge);
 
 
      /* We're only interested in comparisons that distinguish
      /* We're only interested in comparisons that distinguish
         unambiguously from zero.  */
         unambiguously from zero.  */
      if (!integer_zerop (op1) || code == LE_EXPR || code == GE_EXPR)
      if (!integer_zerop (op1) || code == LE_EXPR || code == GE_EXPR)
        continue;
        continue;
 
 
      if (code == EQ_EXPR)
      if (code == EQ_EXPR)
        forbidden_bb = false_edge->dest;
        forbidden_bb = false_edge->dest;
      else
      else
        forbidden_bb = true_edge->dest;
        forbidden_bb = true_edge->dest;
 
 
      bitmap_set_bit (forbidden_dominators, forbidden_bb->index);
      bitmap_set_bit (forbidden_dominators, forbidden_bb->index);
    }
    }
}
}
 
 
/* If BB is dominated by any block in the forbidden dominators set,
/* If BB is dominated by any block in the forbidden dominators set,
   return TRUE; else FALSE.  */
   return TRUE; else FALSE.  */
 
 
static bool
static bool
dominated_by_forbidden (basic_block bb)
dominated_by_forbidden (basic_block bb)
{
{
  unsigned dom_bb;
  unsigned dom_bb;
  bitmap_iterator bi;
  bitmap_iterator bi;
 
 
  EXECUTE_IF_SET_IN_BITMAP (forbidden_dominators, 1, dom_bb, bi)
  EXECUTE_IF_SET_IN_BITMAP (forbidden_dominators, 1, dom_bb, bi)
    {
    {
      if (dominated_by_p (CDI_DOMINATORS, bb, BASIC_BLOCK (dom_bb)))
      if (dominated_by_p (CDI_DOMINATORS, bb, BASIC_BLOCK (dom_bb)))
        return true;
        return true;
    }
    }
 
 
  return false;
  return false;
}
}
 
 
/* We found an split_point CURRENT.  NON_SSA_VARS is bitmap of all non ssa
/* We found an split_point CURRENT.  NON_SSA_VARS is bitmap of all non ssa
   variables used and RETURN_BB is return basic block.
   variables used and RETURN_BB is return basic block.
   See if we can split function here.  */
   See if we can split function here.  */
 
 
static void
static void
consider_split (struct split_point *current, bitmap non_ssa_vars,
consider_split (struct split_point *current, bitmap non_ssa_vars,
                basic_block return_bb)
                basic_block return_bb)
{
{
  tree parm;
  tree parm;
  unsigned int num_args = 0;
  unsigned int num_args = 0;
  unsigned int call_overhead;
  unsigned int call_overhead;
  edge e;
  edge e;
  edge_iterator ei;
  edge_iterator ei;
  gimple_stmt_iterator bsi;
  gimple_stmt_iterator bsi;
  unsigned int i;
  unsigned int i;
  int incoming_freq = 0;
  int incoming_freq = 0;
  tree retval;
  tree retval;
 
 
  if (dump_file && (dump_flags & TDF_DETAILS))
  if (dump_file && (dump_flags & TDF_DETAILS))
    dump_split_point (dump_file, current);
    dump_split_point (dump_file, current);
 
 
  FOR_EACH_EDGE (e, ei, current->entry_bb->preds)
  FOR_EACH_EDGE (e, ei, current->entry_bb->preds)
    if (!bitmap_bit_p (current->split_bbs, e->src->index))
    if (!bitmap_bit_p (current->split_bbs, e->src->index))
      incoming_freq += EDGE_FREQUENCY (e);
      incoming_freq += EDGE_FREQUENCY (e);
 
 
  /* Do not split when we would end up calling function anyway.  */
  /* Do not split when we would end up calling function anyway.  */
  if (incoming_freq
  if (incoming_freq
      >= (ENTRY_BLOCK_PTR->frequency
      >= (ENTRY_BLOCK_PTR->frequency
          * PARAM_VALUE (PARAM_PARTIAL_INLINING_ENTRY_PROBABILITY) / 100))
          * PARAM_VALUE (PARAM_PARTIAL_INLINING_ENTRY_PROBABILITY) / 100))
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "  Refused: incoming frequency is too large.\n");
                 "  Refused: incoming frequency is too large.\n");
      return;
      return;
    }
    }
 
 
  if (!current->header_size)
  if (!current->header_size)
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, "  Refused: header empty\n");
        fprintf (dump_file, "  Refused: header empty\n");
      return;
      return;
    }
    }
 
 
  /* Verify that PHI args on entry are either virtual or all their operands
  /* Verify that PHI args on entry are either virtual or all their operands
     incoming from header are the same.  */
     incoming from header are the same.  */
  for (bsi = gsi_start_phis (current->entry_bb); !gsi_end_p (bsi); gsi_next (&bsi))
  for (bsi = gsi_start_phis (current->entry_bb); !gsi_end_p (bsi); gsi_next (&bsi))
    {
    {
      gimple stmt = gsi_stmt (bsi);
      gimple stmt = gsi_stmt (bsi);
      tree val = NULL;
      tree val = NULL;
 
 
      if (!is_gimple_reg (gimple_phi_result (stmt)))
      if (!is_gimple_reg (gimple_phi_result (stmt)))
        continue;
        continue;
      for (i = 0; i < gimple_phi_num_args (stmt); i++)
      for (i = 0; i < gimple_phi_num_args (stmt); i++)
        {
        {
          edge e = gimple_phi_arg_edge (stmt, i);
          edge e = gimple_phi_arg_edge (stmt, i);
          if (!bitmap_bit_p (current->split_bbs, e->src->index))
          if (!bitmap_bit_p (current->split_bbs, e->src->index))
            {
            {
              tree edge_val = gimple_phi_arg_def (stmt, i);
              tree edge_val = gimple_phi_arg_def (stmt, i);
              if (val && edge_val != val)
              if (val && edge_val != val)
                {
                {
                  if (dump_file && (dump_flags & TDF_DETAILS))
                  if (dump_file && (dump_flags & TDF_DETAILS))
                    fprintf (dump_file,
                    fprintf (dump_file,
                             "  Refused: entry BB has PHI with multiple variants\n");
                             "  Refused: entry BB has PHI with multiple variants\n");
                  return;
                  return;
                }
                }
              val = edge_val;
              val = edge_val;
            }
            }
        }
        }
    }
    }
 
 
 
 
  /* See what argument we will pass to the split function and compute
  /* See what argument we will pass to the split function and compute
     call overhead.  */
     call overhead.  */
  call_overhead = eni_size_weights.call_cost;
  call_overhead = eni_size_weights.call_cost;
  for (parm = DECL_ARGUMENTS (current_function_decl); parm;
  for (parm = DECL_ARGUMENTS (current_function_decl); parm;
       parm = DECL_CHAIN (parm))
       parm = DECL_CHAIN (parm))
    {
    {
      if (!is_gimple_reg (parm))
      if (!is_gimple_reg (parm))
        {
        {
          if (bitmap_bit_p (non_ssa_vars, DECL_UID (parm)))
          if (bitmap_bit_p (non_ssa_vars, DECL_UID (parm)))
            {
            {
              if (dump_file && (dump_flags & TDF_DETAILS))
              if (dump_file && (dump_flags & TDF_DETAILS))
                fprintf (dump_file,
                fprintf (dump_file,
                         "  Refused: need to pass non-ssa param values\n");
                         "  Refused: need to pass non-ssa param values\n");
              return;
              return;
            }
            }
        }
        }
      else if (gimple_default_def (cfun, parm)
      else if (gimple_default_def (cfun, parm)
               && bitmap_bit_p (current->ssa_names_to_pass,
               && bitmap_bit_p (current->ssa_names_to_pass,
                                SSA_NAME_VERSION (gimple_default_def
                                SSA_NAME_VERSION (gimple_default_def
                                                  (cfun, parm))))
                                                  (cfun, parm))))
        {
        {
          if (!VOID_TYPE_P (TREE_TYPE (parm)))
          if (!VOID_TYPE_P (TREE_TYPE (parm)))
            call_overhead += estimate_move_cost (TREE_TYPE (parm));
            call_overhead += estimate_move_cost (TREE_TYPE (parm));
          num_args++;
          num_args++;
        }
        }
    }
    }
  if (!VOID_TYPE_P (TREE_TYPE (current_function_decl)))
  if (!VOID_TYPE_P (TREE_TYPE (current_function_decl)))
    call_overhead += estimate_move_cost (TREE_TYPE (current_function_decl));
    call_overhead += estimate_move_cost (TREE_TYPE (current_function_decl));
 
 
  if (current->split_size <= call_overhead)
  if (current->split_size <= call_overhead)
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "  Refused: split size is smaller than call overhead\n");
                 "  Refused: split size is smaller than call overhead\n");
      return;
      return;
    }
    }
  if (current->header_size + call_overhead
  if (current->header_size + call_overhead
      >= (unsigned int)(DECL_DECLARED_INLINE_P (current_function_decl)
      >= (unsigned int)(DECL_DECLARED_INLINE_P (current_function_decl)
                        ? MAX_INLINE_INSNS_SINGLE
                        ? MAX_INLINE_INSNS_SINGLE
                        : MAX_INLINE_INSNS_AUTO))
                        : MAX_INLINE_INSNS_AUTO))
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "  Refused: header size is too large for inline candidate\n");
                 "  Refused: header size is too large for inline candidate\n");
      return;
      return;
    }
    }
 
 
  /* FIXME: we currently can pass only SSA function parameters to the split
  /* FIXME: we currently can pass only SSA function parameters to the split
     arguments.  Once parm_adjustment infrastructure is supported by cloning,
     arguments.  Once parm_adjustment infrastructure is supported by cloning,
     we can pass more than that.  */
     we can pass more than that.  */
  if (num_args != bitmap_count_bits (current->ssa_names_to_pass))
  if (num_args != bitmap_count_bits (current->ssa_names_to_pass))
    {
    {
 
 
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "  Refused: need to pass non-param values\n");
                 "  Refused: need to pass non-param values\n");
      return;
      return;
    }
    }
 
 
  /* When there are non-ssa vars used in the split region, see if they
  /* When there are non-ssa vars used in the split region, see if they
     are used in the header region.  If so, reject the split.
     are used in the header region.  If so, reject the split.
     FIXME: we can use nested function support to access both.  */
     FIXME: we can use nested function support to access both.  */
  if (!bitmap_empty_p (non_ssa_vars)
  if (!bitmap_empty_p (non_ssa_vars)
      && !verify_non_ssa_vars (current, non_ssa_vars, return_bb))
      && !verify_non_ssa_vars (current, non_ssa_vars, return_bb))
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "  Refused: split part has non-ssa uses\n");
                 "  Refused: split part has non-ssa uses\n");
      return;
      return;
    }
    }
 
 
  /* If the split point is dominated by a forbidden block, reject
  /* If the split point is dominated by a forbidden block, reject
     the split.  */
     the split.  */
  if (!bitmap_empty_p (forbidden_dominators)
  if (!bitmap_empty_p (forbidden_dominators)
      && dominated_by_forbidden (current->entry_bb))
      && dominated_by_forbidden (current->entry_bb))
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "  Refused: split point dominated by forbidden block\n");
                 "  Refused: split point dominated by forbidden block\n");
      return;
      return;
    }
    }
 
 
  /* See if retval used by return bb is computed by header or split part.
  /* See if retval used by return bb is computed by header or split part.
     When it is computed by split part, we need to produce return statement
     When it is computed by split part, we need to produce return statement
     in the split part and add code to header to pass it around.
     in the split part and add code to header to pass it around.
 
 
     This is bit tricky to test:
     This is bit tricky to test:
       1) When there is no return_bb or no return value, we always pass
       1) When there is no return_bb or no return value, we always pass
          value around.
          value around.
       2) Invariants are always computed by caller.
       2) Invariants are always computed by caller.
       3) For SSA we need to look if defining statement is in header or split part
       3) For SSA we need to look if defining statement is in header or split part
       4) For non-SSA we need to look where the var is computed. */
       4) For non-SSA we need to look where the var is computed. */
  retval = find_retval (return_bb);
  retval = find_retval (return_bb);
  if (!retval)
  if (!retval)
    current->split_part_set_retval = true;
    current->split_part_set_retval = true;
  else if (is_gimple_min_invariant (retval))
  else if (is_gimple_min_invariant (retval))
    current->split_part_set_retval = false;
    current->split_part_set_retval = false;
  /* Special case is value returned by reference we record as if it was non-ssa
  /* Special case is value returned by reference we record as if it was non-ssa
     set to result_decl.  */
     set to result_decl.  */
  else if (TREE_CODE (retval) == SSA_NAME
  else if (TREE_CODE (retval) == SSA_NAME
           && TREE_CODE (SSA_NAME_VAR (retval)) == RESULT_DECL
           && TREE_CODE (SSA_NAME_VAR (retval)) == RESULT_DECL
           && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
           && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
    current->split_part_set_retval
    current->split_part_set_retval
       = bitmap_bit_p (non_ssa_vars, DECL_UID (SSA_NAME_VAR (retval)));
       = bitmap_bit_p (non_ssa_vars, DECL_UID (SSA_NAME_VAR (retval)));
  else if (TREE_CODE (retval) == SSA_NAME)
  else if (TREE_CODE (retval) == SSA_NAME)
    current->split_part_set_retval
    current->split_part_set_retval
      = (!SSA_NAME_IS_DEFAULT_DEF (retval)
      = (!SSA_NAME_IS_DEFAULT_DEF (retval)
         && (bitmap_bit_p (current->split_bbs,
         && (bitmap_bit_p (current->split_bbs,
                          gimple_bb (SSA_NAME_DEF_STMT (retval))->index)
                          gimple_bb (SSA_NAME_DEF_STMT (retval))->index)
             || gimple_bb (SSA_NAME_DEF_STMT (retval)) == return_bb));
             || gimple_bb (SSA_NAME_DEF_STMT (retval)) == return_bb));
  else if (TREE_CODE (retval) == PARM_DECL)
  else if (TREE_CODE (retval) == PARM_DECL)
    current->split_part_set_retval = false;
    current->split_part_set_retval = false;
  else if (TREE_CODE (retval) == VAR_DECL
  else if (TREE_CODE (retval) == VAR_DECL
           || TREE_CODE (retval) == RESULT_DECL)
           || TREE_CODE (retval) == RESULT_DECL)
    current->split_part_set_retval
    current->split_part_set_retval
      = bitmap_bit_p (non_ssa_vars, DECL_UID (retval));
      = bitmap_bit_p (non_ssa_vars, DECL_UID (retval));
  else
  else
    current->split_part_set_retval = true;
    current->split_part_set_retval = true;
 
 
  /* split_function fixes up at most one PHI non-virtual PHI node in return_bb,
  /* split_function fixes up at most one PHI non-virtual PHI node in return_bb,
     for the return value.  If there are other PHIs, give up.  */
     for the return value.  If there are other PHIs, give up.  */
  if (return_bb != EXIT_BLOCK_PTR)
  if (return_bb != EXIT_BLOCK_PTR)
    {
    {
      gimple_stmt_iterator psi;
      gimple_stmt_iterator psi;
 
 
      for (psi = gsi_start_phis (return_bb); !gsi_end_p (psi); gsi_next (&psi))
      for (psi = gsi_start_phis (return_bb); !gsi_end_p (psi); gsi_next (&psi))
        if (is_gimple_reg (gimple_phi_result (gsi_stmt (psi)))
        if (is_gimple_reg (gimple_phi_result (gsi_stmt (psi)))
            && !(retval
            && !(retval
                 && current->split_part_set_retval
                 && current->split_part_set_retval
                 && TREE_CODE (retval) == SSA_NAME
                 && TREE_CODE (retval) == SSA_NAME
                 && !DECL_BY_REFERENCE (DECL_RESULT (current_function_decl))
                 && !DECL_BY_REFERENCE (DECL_RESULT (current_function_decl))
                 && SSA_NAME_DEF_STMT (retval) == gsi_stmt (psi)))
                 && SSA_NAME_DEF_STMT (retval) == gsi_stmt (psi)))
          {
          {
            if (dump_file && (dump_flags & TDF_DETAILS))
            if (dump_file && (dump_flags & TDF_DETAILS))
              fprintf (dump_file,
              fprintf (dump_file,
                       "  Refused: return bb has extra PHIs\n");
                       "  Refused: return bb has extra PHIs\n");
            return;
            return;
          }
          }
    }
    }
 
 
  if (dump_file && (dump_flags & TDF_DETAILS))
  if (dump_file && (dump_flags & TDF_DETAILS))
    fprintf (dump_file, "  Accepted!\n");
    fprintf (dump_file, "  Accepted!\n");
 
 
  /* At the moment chose split point with lowest frequency and that leaves
  /* At the moment chose split point with lowest frequency and that leaves
     out smallest size of header.
     out smallest size of header.
     In future we might re-consider this heuristics.  */
     In future we might re-consider this heuristics.  */
  if (!best_split_point.split_bbs
  if (!best_split_point.split_bbs
      || best_split_point.entry_bb->frequency > current->entry_bb->frequency
      || best_split_point.entry_bb->frequency > current->entry_bb->frequency
      || (best_split_point.entry_bb->frequency == current->entry_bb->frequency
      || (best_split_point.entry_bb->frequency == current->entry_bb->frequency
          && best_split_point.split_size < current->split_size))
          && best_split_point.split_size < current->split_size))
 
 
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, "  New best split point!\n");
        fprintf (dump_file, "  New best split point!\n");
      if (best_split_point.ssa_names_to_pass)
      if (best_split_point.ssa_names_to_pass)
        {
        {
          BITMAP_FREE (best_split_point.ssa_names_to_pass);
          BITMAP_FREE (best_split_point.ssa_names_to_pass);
          BITMAP_FREE (best_split_point.split_bbs);
          BITMAP_FREE (best_split_point.split_bbs);
        }
        }
      best_split_point = *current;
      best_split_point = *current;
      best_split_point.ssa_names_to_pass = BITMAP_ALLOC (NULL);
      best_split_point.ssa_names_to_pass = BITMAP_ALLOC (NULL);
      bitmap_copy (best_split_point.ssa_names_to_pass,
      bitmap_copy (best_split_point.ssa_names_to_pass,
                   current->ssa_names_to_pass);
                   current->ssa_names_to_pass);
      best_split_point.split_bbs = BITMAP_ALLOC (NULL);
      best_split_point.split_bbs = BITMAP_ALLOC (NULL);
      bitmap_copy (best_split_point.split_bbs, current->split_bbs);
      bitmap_copy (best_split_point.split_bbs, current->split_bbs);
    }
    }
}
}
 
 
/* Return basic block containing RETURN statement.  We allow basic blocks
/* Return basic block containing RETURN statement.  We allow basic blocks
   of the form:
   of the form:
   <retval> = tmp_var;
   <retval> = tmp_var;
   return <retval>
   return <retval>
   but return_bb can not be more complex than this.
   but return_bb can not be more complex than this.
   If nothing is found, return EXIT_BLOCK_PTR.
   If nothing is found, return EXIT_BLOCK_PTR.
 
 
   When there are multiple RETURN statement, chose one with return value,
   When there are multiple RETURN statement, chose one with return value,
   since that one is more likely shared by multiple code paths.
   since that one is more likely shared by multiple code paths.
 
 
   Return BB is special, because for function splitting it is the only
   Return BB is special, because for function splitting it is the only
   basic block that is duplicated in between header and split part of the
   basic block that is duplicated in between header and split part of the
   function.
   function.
 
 
   TODO: We might support multiple return blocks.  */
   TODO: We might support multiple return blocks.  */
 
 
static basic_block
static basic_block
find_return_bb (void)
find_return_bb (void)
{
{
  edge e;
  edge e;
  basic_block return_bb = EXIT_BLOCK_PTR;
  basic_block return_bb = EXIT_BLOCK_PTR;
  gimple_stmt_iterator bsi;
  gimple_stmt_iterator bsi;
  bool found_return = false;
  bool found_return = false;
  tree retval = NULL_TREE;
  tree retval = NULL_TREE;
 
 
  if (!single_pred_p (EXIT_BLOCK_PTR))
  if (!single_pred_p (EXIT_BLOCK_PTR))
    return return_bb;
    return return_bb;
 
 
  e = single_pred_edge (EXIT_BLOCK_PTR);
  e = single_pred_edge (EXIT_BLOCK_PTR);
  for (bsi = gsi_last_bb (e->src); !gsi_end_p (bsi); gsi_prev (&bsi))
  for (bsi = gsi_last_bb (e->src); !gsi_end_p (bsi); gsi_prev (&bsi))
    {
    {
      gimple stmt = gsi_stmt (bsi);
      gimple stmt = gsi_stmt (bsi);
      if (gimple_code (stmt) == GIMPLE_LABEL
      if (gimple_code (stmt) == GIMPLE_LABEL
          || is_gimple_debug (stmt)
          || is_gimple_debug (stmt)
          || gimple_clobber_p (stmt))
          || gimple_clobber_p (stmt))
        ;
        ;
      else if (gimple_code (stmt) == GIMPLE_ASSIGN
      else if (gimple_code (stmt) == GIMPLE_ASSIGN
               && found_return
               && found_return
               && gimple_assign_single_p (stmt)
               && gimple_assign_single_p (stmt)
               && (auto_var_in_fn_p (gimple_assign_rhs1 (stmt),
               && (auto_var_in_fn_p (gimple_assign_rhs1 (stmt),
                                     current_function_decl)
                                     current_function_decl)
                   || is_gimple_min_invariant (gimple_assign_rhs1 (stmt)))
                   || is_gimple_min_invariant (gimple_assign_rhs1 (stmt)))
               && retval == gimple_assign_lhs (stmt))
               && retval == gimple_assign_lhs (stmt))
        ;
        ;
      else if (gimple_code (stmt) == GIMPLE_RETURN)
      else if (gimple_code (stmt) == GIMPLE_RETURN)
        {
        {
          found_return = true;
          found_return = true;
          retval = gimple_return_retval (stmt);
          retval = gimple_return_retval (stmt);
        }
        }
      else
      else
        break;
        break;
    }
    }
  if (gsi_end_p (bsi) && found_return)
  if (gsi_end_p (bsi) && found_return)
    return_bb = e->src;
    return_bb = e->src;
 
 
  return return_bb;
  return return_bb;
}
}
 
 
/* Given return basic block RETURN_BB, see where return value is really
/* Given return basic block RETURN_BB, see where return value is really
   stored.  */
   stored.  */
static tree
static tree
find_retval (basic_block return_bb)
find_retval (basic_block return_bb)
{
{
  gimple_stmt_iterator bsi;
  gimple_stmt_iterator bsi;
  for (bsi = gsi_start_bb (return_bb); !gsi_end_p (bsi); gsi_next (&bsi))
  for (bsi = gsi_start_bb (return_bb); !gsi_end_p (bsi); gsi_next (&bsi))
    if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN)
    if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN)
      return gimple_return_retval (gsi_stmt (bsi));
      return gimple_return_retval (gsi_stmt (bsi));
    else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN
    else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN
             && !gimple_clobber_p (gsi_stmt (bsi)))
             && !gimple_clobber_p (gsi_stmt (bsi)))
      return gimple_assign_rhs1 (gsi_stmt (bsi));
      return gimple_assign_rhs1 (gsi_stmt (bsi));
  return NULL;
  return NULL;
}
}
 
 
/* Callback for walk_stmt_load_store_addr_ops.  If T is non-SSA automatic
/* Callback for walk_stmt_load_store_addr_ops.  If T is non-SSA automatic
   variable, mark it as used in bitmap passed via DATA.
   variable, mark it as used in bitmap passed via DATA.
   Return true when access to T prevents splitting the function.  */
   Return true when access to T prevents splitting the function.  */
 
 
static bool
static bool
mark_nonssa_use (gimple stmt ATTRIBUTE_UNUSED, tree t, void *data)
mark_nonssa_use (gimple stmt ATTRIBUTE_UNUSED, tree t, void *data)
{
{
  t = get_base_address (t);
  t = get_base_address (t);
 
 
  if (!t || is_gimple_reg (t))
  if (!t || is_gimple_reg (t))
    return false;
    return false;
 
 
  /* At present we can't pass non-SSA arguments to split function.
  /* At present we can't pass non-SSA arguments to split function.
     FIXME: this can be relaxed by passing references to arguments.  */
     FIXME: this can be relaxed by passing references to arguments.  */
  if (TREE_CODE (t) == PARM_DECL)
  if (TREE_CODE (t) == PARM_DECL)
    {
    {
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
        fprintf (dump_file,
                 "Cannot split: use of non-ssa function parameter.\n");
                 "Cannot split: use of non-ssa function parameter.\n");
      return true;
      return true;
    }
    }
 
 
  if ((TREE_CODE (t) == VAR_DECL
  if ((TREE_CODE (t) == VAR_DECL
       && auto_var_in_fn_p (t, current_function_decl))
       && auto_var_in_fn_p (t, current_function_decl))
      || TREE_CODE (t) == RESULT_DECL
      || TREE_CODE (t) == RESULT_DECL
      || TREE_CODE (t) == LABEL_DECL)
      || TREE_CODE (t) == LABEL_DECL)
    bitmap_set_bit ((bitmap)data, DECL_UID (t));
    bitmap_set_bit ((bitmap)data, DECL_UID (t));
 
 
  /* For DECL_BY_REFERENCE, the return value is actually a pointer.  We want
  /* For DECL_BY_REFERENCE, the return value is actually a pointer.  We want
     to pretend that the value pointed to is actual result decl.  */
     to pretend that the value pointed to is actual result decl.  */
  if ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t))
  if ((TREE_CODE (t) == MEM_REF || INDIRECT_REF_P (t))
      && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
      && TREE_CODE (TREE_OPERAND (t, 0)) == SSA_NAME
      && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (t, 0))) == RESULT_DECL
      && TREE_CODE (SSA_NAME_VAR (TREE_OPERAND (t, 0))) == RESULT_DECL
      && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
      && DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
    return
    return
      bitmap_bit_p ((bitmap)data,
      bitmap_bit_p ((bitmap)data,
                    DECL_UID (DECL_RESULT (current_function_decl)));
                    DECL_UID (DECL_RESULT (current_function_decl)));
 
 
  return false;
  return false;
}
}
 
 
/* Compute local properties of basic block BB we collect when looking for
/* Compute local properties of basic block BB we collect when looking for
   split points.  We look for ssa defs and store them in SET_SSA_NAMES,
   split points.  We look for ssa defs and store them in SET_SSA_NAMES,
   for ssa uses and store them in USED_SSA_NAMES and for any non-SSA automatic
   for ssa uses and store them in USED_SSA_NAMES and for any non-SSA automatic
   vars stored in NON_SSA_VARS.
   vars stored in NON_SSA_VARS.
 
 
   When BB has edge to RETURN_BB, collect uses in RETURN_BB too.
   When BB has edge to RETURN_BB, collect uses in RETURN_BB too.
 
 
   Return false when BB contains something that prevents it from being put into
   Return false when BB contains something that prevents it from being put into
   split function.  */
   split function.  */
 
 
static bool
static bool
visit_bb (basic_block bb, basic_block return_bb,
visit_bb (basic_block bb, basic_block return_bb,
          bitmap set_ssa_names, bitmap used_ssa_names,
          bitmap set_ssa_names, bitmap used_ssa_names,
          bitmap non_ssa_vars)
          bitmap non_ssa_vars)
{
{
  gimple_stmt_iterator bsi;
  gimple_stmt_iterator bsi;
  edge e;
  edge e;
  edge_iterator ei;
  edge_iterator ei;
  bool can_split = true;
  bool can_split = true;
 
 
  for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
  for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
    {
    {
      gimple stmt = gsi_stmt (bsi);
      gimple stmt = gsi_stmt (bsi);
      tree op;
      tree op;
      ssa_op_iter iter;
      ssa_op_iter iter;
      tree decl;
      tree decl;
 
 
      if (is_gimple_debug (stmt))
      if (is_gimple_debug (stmt))
        continue;
        continue;
 
 
      if (gimple_clobber_p (stmt))
      if (gimple_clobber_p (stmt))
        continue;
        continue;
 
 
      /* FIXME: We can split regions containing EH.  We can not however
      /* FIXME: We can split regions containing EH.  We can not however
         split RESX, EH_DISPATCH and EH_POINTER referring to same region
         split RESX, EH_DISPATCH and EH_POINTER referring to same region
         into different partitions.  This would require tracking of
         into different partitions.  This would require tracking of
         EH regions and checking in consider_split_point if they
         EH regions and checking in consider_split_point if they
         are not used elsewhere.  */
         are not used elsewhere.  */
      if (gimple_code (stmt) == GIMPLE_RESX)
      if (gimple_code (stmt) == GIMPLE_RESX)
        {
        {
          if (dump_file && (dump_flags & TDF_DETAILS))
          if (dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, "Cannot split: resx.\n");
            fprintf (dump_file, "Cannot split: resx.\n");
          can_split = false;
          can_split = false;
        }
        }
      if (gimple_code (stmt) == GIMPLE_EH_DISPATCH)
      if (gimple_code (stmt) == GIMPLE_EH_DISPATCH)
        {
        {
          if (dump_file && (dump_flags & TDF_DETAILS))
          if (dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file, "Cannot split: eh dispatch.\n");
            fprintf (dump_file, "Cannot split: eh dispatch.\n");
          can_split = false;
          can_split = false;
        }
        }
 
 
      /* Check builtins that prevent splitting.  */
      /* Check builtins that prevent splitting.  */
      if (gimple_code (stmt) == GIMPLE_CALL
      if (gimple_code (stmt) == GIMPLE_CALL
          && (decl = gimple_call_fndecl (stmt)) != NULL_TREE
          && (decl = gimple_call_fndecl (stmt)) != NULL_TREE
          && DECL_BUILT_IN (decl)
          && DECL_BUILT_IN (decl)
          && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
          && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL)
        switch (DECL_FUNCTION_CODE (decl))
        switch (DECL_FUNCTION_CODE (decl))
          {
          {
          /* FIXME: once we will allow passing non-parm values to split part,
          /* FIXME: once we will allow passing non-parm values to split part,
             we need to be sure to handle correct builtin_stack_save and
             we need to be sure to handle correct builtin_stack_save and
             builtin_stack_restore.  At the moment we are safe; there is no
             builtin_stack_restore.  At the moment we are safe; there is no
             way to store builtin_stack_save result in non-SSA variable
             way to store builtin_stack_save result in non-SSA variable
             since all calls to those are compiler generated.  */
             since all calls to those are compiler generated.  */
          case BUILT_IN_APPLY:
          case BUILT_IN_APPLY:
          case BUILT_IN_APPLY_ARGS:
          case BUILT_IN_APPLY_ARGS:
          case BUILT_IN_VA_START:
          case BUILT_IN_VA_START:
            if (dump_file && (dump_flags & TDF_DETAILS))
            if (dump_file && (dump_flags & TDF_DETAILS))
              fprintf (dump_file,
              fprintf (dump_file,
                       "Cannot split: builtin_apply and va_start.\n");
                       "Cannot split: builtin_apply and va_start.\n");
            can_split = false;
            can_split = false;
            break;
            break;
          case BUILT_IN_EH_POINTER:
          case BUILT_IN_EH_POINTER:
            if (dump_file && (dump_flags & TDF_DETAILS))
            if (dump_file && (dump_flags & TDF_DETAILS))
              fprintf (dump_file, "Cannot split: builtin_eh_pointer.\n");
              fprintf (dump_file, "Cannot split: builtin_eh_pointer.\n");
            can_split = false;
            can_split = false;
            break;
            break;
          default:
          default:
            break;
            break;
          }
          }
 
 
      FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_DEF)
      FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_DEF)
        bitmap_set_bit (set_ssa_names, SSA_NAME_VERSION (op));
        bitmap_set_bit (set_ssa_names, SSA_NAME_VERSION (op));
      FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
      FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_USE)
        bitmap_set_bit (used_ssa_names, SSA_NAME_VERSION (op));
        bitmap_set_bit (used_ssa_names, SSA_NAME_VERSION (op));
      can_split &= !walk_stmt_load_store_addr_ops (stmt, non_ssa_vars,
      can_split &= !walk_stmt_load_store_addr_ops (stmt, non_ssa_vars,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use);
                                                   mark_nonssa_use);
    }
    }
  for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi))
  for (bsi = gsi_start_phis (bb); !gsi_end_p (bsi); gsi_next (&bsi))
    {
    {
      gimple stmt = gsi_stmt (bsi);
      gimple stmt = gsi_stmt (bsi);
      unsigned int i;
      unsigned int i;
 
 
      if (is_gimple_debug (stmt))
      if (is_gimple_debug (stmt))
        continue;
        continue;
      if (!is_gimple_reg (gimple_phi_result (stmt)))
      if (!is_gimple_reg (gimple_phi_result (stmt)))
        continue;
        continue;
      bitmap_set_bit (set_ssa_names,
      bitmap_set_bit (set_ssa_names,
                      SSA_NAME_VERSION (gimple_phi_result (stmt)));
                      SSA_NAME_VERSION (gimple_phi_result (stmt)));
      for (i = 0; i < gimple_phi_num_args (stmt); i++)
      for (i = 0; i < gimple_phi_num_args (stmt); i++)
        {
        {
          tree op = gimple_phi_arg_def (stmt, i);
          tree op = gimple_phi_arg_def (stmt, i);
          if (TREE_CODE (op) == SSA_NAME)
          if (TREE_CODE (op) == SSA_NAME)
            bitmap_set_bit (used_ssa_names, SSA_NAME_VERSION (op));
            bitmap_set_bit (used_ssa_names, SSA_NAME_VERSION (op));
        }
        }
      can_split &= !walk_stmt_load_store_addr_ops (stmt, non_ssa_vars,
      can_split &= !walk_stmt_load_store_addr_ops (stmt, non_ssa_vars,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use);
                                                   mark_nonssa_use);
    }
    }
  /* Record also uses coming from PHI operand in return BB.  */
  /* Record also uses coming from PHI operand in return BB.  */
  FOR_EACH_EDGE (e, ei, bb->succs)
  FOR_EACH_EDGE (e, ei, bb->succs)
    if (e->dest == return_bb)
    if (e->dest == return_bb)
      {
      {
        for (bsi = gsi_start_phis (return_bb); !gsi_end_p (bsi); gsi_next (&bsi))
        for (bsi = gsi_start_phis (return_bb); !gsi_end_p (bsi); gsi_next (&bsi))
          {
          {
            gimple stmt = gsi_stmt (bsi);
            gimple stmt = gsi_stmt (bsi);
            tree op = gimple_phi_arg_def (stmt, e->dest_idx);
            tree op = gimple_phi_arg_def (stmt, e->dest_idx);
 
 
            if (is_gimple_debug (stmt))
            if (is_gimple_debug (stmt))
              continue;
              continue;
            if (!is_gimple_reg (gimple_phi_result (stmt)))
            if (!is_gimple_reg (gimple_phi_result (stmt)))
              continue;
              continue;
            if (TREE_CODE (op) == SSA_NAME)
            if (TREE_CODE (op) == SSA_NAME)
              bitmap_set_bit (used_ssa_names, SSA_NAME_VERSION (op));
              bitmap_set_bit (used_ssa_names, SSA_NAME_VERSION (op));
            else
            else
              can_split &= !mark_nonssa_use (stmt, op, non_ssa_vars);
              can_split &= !mark_nonssa_use (stmt, op, non_ssa_vars);
          }
          }
      }
      }
  return can_split;
  return can_split;
}
}
 
 
/* Stack entry for recursive DFS walk in find_split_point.  */
/* Stack entry for recursive DFS walk in find_split_point.  */
 
 
typedef struct
typedef struct
{
{
  /* Basic block we are examining.  */
  /* Basic block we are examining.  */
  basic_block bb;
  basic_block bb;
 
 
  /* SSA names set and used by the BB and all BBs reachable
  /* SSA names set and used by the BB and all BBs reachable
     from it via DFS walk.  */
     from it via DFS walk.  */
  bitmap set_ssa_names, used_ssa_names;
  bitmap set_ssa_names, used_ssa_names;
  bitmap non_ssa_vars;
  bitmap non_ssa_vars;
 
 
  /* All BBS visited from this BB via DFS walk.  */
  /* All BBS visited from this BB via DFS walk.  */
  bitmap bbs_visited;
  bitmap bbs_visited;
 
 
  /* Last examined edge in DFS walk.  Since we walk unoriented graph,
  /* Last examined edge in DFS walk.  Since we walk unoriented graph,
     the value is up to sum of incoming and outgoing edges of BB.  */
     the value is up to sum of incoming and outgoing edges of BB.  */
  unsigned int edge_num;
  unsigned int edge_num;
 
 
  /* Stack entry index of earliest BB reachable from current BB
  /* Stack entry index of earliest BB reachable from current BB
     or any BB visited later in DFS walk.  */
     or any BB visited later in DFS walk.  */
  int earliest;
  int earliest;
 
 
  /* Overall time and size of all BBs reached from this BB in DFS walk.  */
  /* Overall time and size of all BBs reached from this BB in DFS walk.  */
  int overall_time, overall_size;
  int overall_time, overall_size;
 
 
  /* When false we can not split on this BB.  */
  /* When false we can not split on this BB.  */
  bool can_split;
  bool can_split;
} stack_entry;
} stack_entry;
DEF_VEC_O(stack_entry);
DEF_VEC_O(stack_entry);
DEF_VEC_ALLOC_O(stack_entry,heap);
DEF_VEC_ALLOC_O(stack_entry,heap);
 
 
 
 
/* Find all articulations and call consider_split on them.
/* Find all articulations and call consider_split on them.
   OVERALL_TIME and OVERALL_SIZE is time and size of the function.
   OVERALL_TIME and OVERALL_SIZE is time and size of the function.
 
 
   We perform basic algorithm for finding an articulation in a graph
   We perform basic algorithm for finding an articulation in a graph
   created from CFG by considering it to be an unoriented graph.
   created from CFG by considering it to be an unoriented graph.
 
 
   The articulation is discovered via DFS walk. We collect earliest
   The articulation is discovered via DFS walk. We collect earliest
   basic block on stack that is reachable via backward edge.  Articulation
   basic block on stack that is reachable via backward edge.  Articulation
   is any basic block such that there is no backward edge bypassing it.
   is any basic block such that there is no backward edge bypassing it.
   To reduce stack usage we maintain heap allocated stack in STACK vector.
   To reduce stack usage we maintain heap allocated stack in STACK vector.
   AUX pointer of BB is set to index it appears in the stack or -1 once
   AUX pointer of BB is set to index it appears in the stack or -1 once
   it is visited and popped off the stack.
   it is visited and popped off the stack.
 
 
   The algorithm finds articulation after visiting the whole component
   The algorithm finds articulation after visiting the whole component
   reachable by it.  This makes it convenient to collect information about
   reachable by it.  This makes it convenient to collect information about
   the component used by consider_split.  */
   the component used by consider_split.  */
 
 
static void
static void
find_split_points (int overall_time, int overall_size)
find_split_points (int overall_time, int overall_size)
{
{
  stack_entry first;
  stack_entry first;
  VEC(stack_entry, heap) *stack = NULL;
  VEC(stack_entry, heap) *stack = NULL;
  basic_block bb;
  basic_block bb;
  basic_block return_bb = find_return_bb ();
  basic_block return_bb = find_return_bb ();
  struct split_point current;
  struct split_point current;
 
 
  current.header_time = overall_time;
  current.header_time = overall_time;
  current.header_size = overall_size;
  current.header_size = overall_size;
  current.split_time = 0;
  current.split_time = 0;
  current.split_size = 0;
  current.split_size = 0;
  current.ssa_names_to_pass = BITMAP_ALLOC (NULL);
  current.ssa_names_to_pass = BITMAP_ALLOC (NULL);
 
 
  first.bb = ENTRY_BLOCK_PTR;
  first.bb = ENTRY_BLOCK_PTR;
  first.edge_num = 0;
  first.edge_num = 0;
  first.overall_time = 0;
  first.overall_time = 0;
  first.overall_size = 0;
  first.overall_size = 0;
  first.earliest = INT_MAX;
  first.earliest = INT_MAX;
  first.set_ssa_names = 0;
  first.set_ssa_names = 0;
  first.used_ssa_names = 0;
  first.used_ssa_names = 0;
  first.bbs_visited = 0;
  first.bbs_visited = 0;
  VEC_safe_push (stack_entry, heap, stack, &first);
  VEC_safe_push (stack_entry, heap, stack, &first);
  ENTRY_BLOCK_PTR->aux = (void *)(intptr_t)-1;
  ENTRY_BLOCK_PTR->aux = (void *)(intptr_t)-1;
 
 
  while (!VEC_empty (stack_entry, stack))
  while (!VEC_empty (stack_entry, stack))
    {
    {
      stack_entry *entry = VEC_last (stack_entry, stack);
      stack_entry *entry = VEC_last (stack_entry, stack);
 
 
      /* We are walking an acyclic graph, so edge_num counts
      /* We are walking an acyclic graph, so edge_num counts
         succ and pred edges together.  However when considering
         succ and pred edges together.  However when considering
         articulation, we want to have processed everything reachable
         articulation, we want to have processed everything reachable
         from articulation but nothing that reaches into it.  */
         from articulation but nothing that reaches into it.  */
      if (entry->edge_num == EDGE_COUNT (entry->bb->succs)
      if (entry->edge_num == EDGE_COUNT (entry->bb->succs)
          && entry->bb != ENTRY_BLOCK_PTR)
          && entry->bb != ENTRY_BLOCK_PTR)
        {
        {
          int pos = VEC_length (stack_entry, stack);
          int pos = VEC_length (stack_entry, stack);
          entry->can_split &= visit_bb (entry->bb, return_bb,
          entry->can_split &= visit_bb (entry->bb, return_bb,
                                        entry->set_ssa_names,
                                        entry->set_ssa_names,
                                        entry->used_ssa_names,
                                        entry->used_ssa_names,
                                        entry->non_ssa_vars);
                                        entry->non_ssa_vars);
          if (pos <= entry->earliest && !entry->can_split
          if (pos <= entry->earliest && !entry->can_split
              && dump_file && (dump_flags & TDF_DETAILS))
              && dump_file && (dump_flags & TDF_DETAILS))
            fprintf (dump_file,
            fprintf (dump_file,
                     "found articulation at bb %i but can not split\n",
                     "found articulation at bb %i but can not split\n",
                     entry->bb->index);
                     entry->bb->index);
          if (pos <= entry->earliest && entry->can_split)
          if (pos <= entry->earliest && entry->can_split)
             {
             {
               if (dump_file && (dump_flags & TDF_DETAILS))
               if (dump_file && (dump_flags & TDF_DETAILS))
                 fprintf (dump_file, "found articulation at bb %i\n",
                 fprintf (dump_file, "found articulation at bb %i\n",
                          entry->bb->index);
                          entry->bb->index);
               current.entry_bb = entry->bb;
               current.entry_bb = entry->bb;
               current.ssa_names_to_pass = BITMAP_ALLOC (NULL);
               current.ssa_names_to_pass = BITMAP_ALLOC (NULL);
               bitmap_and_compl (current.ssa_names_to_pass,
               bitmap_and_compl (current.ssa_names_to_pass,
                                 entry->used_ssa_names, entry->set_ssa_names);
                                 entry->used_ssa_names, entry->set_ssa_names);
               current.header_time = overall_time - entry->overall_time;
               current.header_time = overall_time - entry->overall_time;
               current.header_size = overall_size - entry->overall_size;
               current.header_size = overall_size - entry->overall_size;
               current.split_time = entry->overall_time;
               current.split_time = entry->overall_time;
               current.split_size = entry->overall_size;
               current.split_size = entry->overall_size;
               current.split_bbs = entry->bbs_visited;
               current.split_bbs = entry->bbs_visited;
               consider_split (&current, entry->non_ssa_vars, return_bb);
               consider_split (&current, entry->non_ssa_vars, return_bb);
               BITMAP_FREE (current.ssa_names_to_pass);
               BITMAP_FREE (current.ssa_names_to_pass);
             }
             }
        }
        }
      /* Do actual DFS walk.  */
      /* Do actual DFS walk.  */
      if (entry->edge_num
      if (entry->edge_num
          < (EDGE_COUNT (entry->bb->succs)
          < (EDGE_COUNT (entry->bb->succs)
             + EDGE_COUNT (entry->bb->preds)))
             + EDGE_COUNT (entry->bb->preds)))
        {
        {
          edge e;
          edge e;
          basic_block dest;
          basic_block dest;
          if (entry->edge_num < EDGE_COUNT (entry->bb->succs))
          if (entry->edge_num < EDGE_COUNT (entry->bb->succs))
            {
            {
              e = EDGE_SUCC (entry->bb, entry->edge_num);
              e = EDGE_SUCC (entry->bb, entry->edge_num);
              dest = e->dest;
              dest = e->dest;
            }
            }
          else
          else
            {
            {
              e = EDGE_PRED (entry->bb, entry->edge_num
              e = EDGE_PRED (entry->bb, entry->edge_num
                             - EDGE_COUNT (entry->bb->succs));
                             - EDGE_COUNT (entry->bb->succs));
              dest = e->src;
              dest = e->src;
            }
            }
 
 
          entry->edge_num++;
          entry->edge_num++;
 
 
          /* New BB to visit, push it to the stack.  */
          /* New BB to visit, push it to the stack.  */
          if (dest != return_bb && dest != EXIT_BLOCK_PTR
          if (dest != return_bb && dest != EXIT_BLOCK_PTR
              && !dest->aux)
              && !dest->aux)
            {
            {
              stack_entry new_entry;
              stack_entry new_entry;
 
 
              new_entry.bb = dest;
              new_entry.bb = dest;
              new_entry.edge_num = 0;
              new_entry.edge_num = 0;
              new_entry.overall_time
              new_entry.overall_time
                 = VEC_index (bb_info, bb_info_vec, dest->index)->time;
                 = VEC_index (bb_info, bb_info_vec, dest->index)->time;
              new_entry.overall_size
              new_entry.overall_size
                 = VEC_index (bb_info, bb_info_vec, dest->index)->size;
                 = VEC_index (bb_info, bb_info_vec, dest->index)->size;
              new_entry.earliest = INT_MAX;
              new_entry.earliest = INT_MAX;
              new_entry.set_ssa_names = BITMAP_ALLOC (NULL);
              new_entry.set_ssa_names = BITMAP_ALLOC (NULL);
              new_entry.used_ssa_names = BITMAP_ALLOC (NULL);
              new_entry.used_ssa_names = BITMAP_ALLOC (NULL);
              new_entry.bbs_visited = BITMAP_ALLOC (NULL);
              new_entry.bbs_visited = BITMAP_ALLOC (NULL);
              new_entry.non_ssa_vars = BITMAP_ALLOC (NULL);
              new_entry.non_ssa_vars = BITMAP_ALLOC (NULL);
              new_entry.can_split = true;
              new_entry.can_split = true;
              bitmap_set_bit (new_entry.bbs_visited, dest->index);
              bitmap_set_bit (new_entry.bbs_visited, dest->index);
              VEC_safe_push (stack_entry, heap, stack, &new_entry);
              VEC_safe_push (stack_entry, heap, stack, &new_entry);
              dest->aux = (void *)(intptr_t)VEC_length (stack_entry, stack);
              dest->aux = (void *)(intptr_t)VEC_length (stack_entry, stack);
            }
            }
          /* Back edge found, record the earliest point.  */
          /* Back edge found, record the earliest point.  */
          else if ((intptr_t)dest->aux > 0
          else if ((intptr_t)dest->aux > 0
                   && (intptr_t)dest->aux < entry->earliest)
                   && (intptr_t)dest->aux < entry->earliest)
            entry->earliest = (intptr_t)dest->aux;
            entry->earliest = (intptr_t)dest->aux;
        }
        }
      /* We are done with examining the edges.  Pop off the value from stack
      /* We are done with examining the edges.  Pop off the value from stack
         and merge stuff we accumulate during the walk.  */
         and merge stuff we accumulate during the walk.  */
      else if (entry->bb != ENTRY_BLOCK_PTR)
      else if (entry->bb != ENTRY_BLOCK_PTR)
        {
        {
          stack_entry *prev = VEC_index (stack_entry, stack,
          stack_entry *prev = VEC_index (stack_entry, stack,
                                         VEC_length (stack_entry, stack) - 2);
                                         VEC_length (stack_entry, stack) - 2);
 
 
          entry->bb->aux = (void *)(intptr_t)-1;
          entry->bb->aux = (void *)(intptr_t)-1;
          prev->can_split &= entry->can_split;
          prev->can_split &= entry->can_split;
          if (prev->set_ssa_names)
          if (prev->set_ssa_names)
            {
            {
              bitmap_ior_into (prev->set_ssa_names, entry->set_ssa_names);
              bitmap_ior_into (prev->set_ssa_names, entry->set_ssa_names);
              bitmap_ior_into (prev->used_ssa_names, entry->used_ssa_names);
              bitmap_ior_into (prev->used_ssa_names, entry->used_ssa_names);
              bitmap_ior_into (prev->bbs_visited, entry->bbs_visited);
              bitmap_ior_into (prev->bbs_visited, entry->bbs_visited);
              bitmap_ior_into (prev->non_ssa_vars, entry->non_ssa_vars);
              bitmap_ior_into (prev->non_ssa_vars, entry->non_ssa_vars);
            }
            }
          if (prev->earliest > entry->earliest)
          if (prev->earliest > entry->earliest)
            prev->earliest = entry->earliest;
            prev->earliest = entry->earliest;
          prev->overall_time += entry->overall_time;
          prev->overall_time += entry->overall_time;
          prev->overall_size += entry->overall_size;
          prev->overall_size += entry->overall_size;
          BITMAP_FREE (entry->set_ssa_names);
          BITMAP_FREE (entry->set_ssa_names);
          BITMAP_FREE (entry->used_ssa_names);
          BITMAP_FREE (entry->used_ssa_names);
          BITMAP_FREE (entry->bbs_visited);
          BITMAP_FREE (entry->bbs_visited);
          BITMAP_FREE (entry->non_ssa_vars);
          BITMAP_FREE (entry->non_ssa_vars);
          VEC_pop (stack_entry, stack);
          VEC_pop (stack_entry, stack);
        }
        }
      else
      else
        VEC_pop (stack_entry, stack);
        VEC_pop (stack_entry, stack);
    }
    }
  ENTRY_BLOCK_PTR->aux = NULL;
  ENTRY_BLOCK_PTR->aux = NULL;
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    bb->aux = NULL;
    bb->aux = NULL;
  VEC_free (stack_entry, heap, stack);
  VEC_free (stack_entry, heap, stack);
  BITMAP_FREE (current.ssa_names_to_pass);
  BITMAP_FREE (current.ssa_names_to_pass);
}
}
 
 
/* Split function at SPLIT_POINT.  */
/* Split function at SPLIT_POINT.  */
 
 
static void
static void
split_function (struct split_point *split_point)
split_function (struct split_point *split_point)
{
{
  VEC (tree, heap) *args_to_pass = NULL;
  VEC (tree, heap) *args_to_pass = NULL;
  bitmap args_to_skip;
  bitmap args_to_skip;
  tree parm;
  tree parm;
  int num = 0;
  int num = 0;
  struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
  struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
  basic_block return_bb = find_return_bb ();
  basic_block return_bb = find_return_bb ();
  basic_block call_bb;
  basic_block call_bb;
  gimple_stmt_iterator gsi;
  gimple_stmt_iterator gsi;
  gimple call;
  gimple call;
  edge e;
  edge e;
  edge_iterator ei;
  edge_iterator ei;
  tree retval = NULL, real_retval = NULL;
  tree retval = NULL, real_retval = NULL;
  bool split_part_return_p = false;
  bool split_part_return_p = false;
  gimple last_stmt = NULL;
  gimple last_stmt = NULL;
  unsigned int i;
  unsigned int i;
  tree arg;
  tree arg;
 
 
  if (dump_file)
  if (dump_file)
    {
    {
      fprintf (dump_file, "\n\nSplitting function at:\n");
      fprintf (dump_file, "\n\nSplitting function at:\n");
      dump_split_point (dump_file, split_point);
      dump_split_point (dump_file, split_point);
    }
    }
 
 
  if (cur_node->local.can_change_signature)
  if (cur_node->local.can_change_signature)
    args_to_skip = BITMAP_ALLOC (NULL);
    args_to_skip = BITMAP_ALLOC (NULL);
  else
  else
    args_to_skip = NULL;
    args_to_skip = NULL;
 
 
  /* Collect the parameters of new function and args_to_skip bitmap.  */
  /* Collect the parameters of new function and args_to_skip bitmap.  */
  for (parm = DECL_ARGUMENTS (current_function_decl);
  for (parm = DECL_ARGUMENTS (current_function_decl);
       parm; parm = DECL_CHAIN (parm), num++)
       parm; parm = DECL_CHAIN (parm), num++)
    if (args_to_skip
    if (args_to_skip
        && (!is_gimple_reg (parm)
        && (!is_gimple_reg (parm)
            || !gimple_default_def (cfun, parm)
            || !gimple_default_def (cfun, parm)
            || !bitmap_bit_p (split_point->ssa_names_to_pass,
            || !bitmap_bit_p (split_point->ssa_names_to_pass,
                              SSA_NAME_VERSION (gimple_default_def (cfun,
                              SSA_NAME_VERSION (gimple_default_def (cfun,
                                                                    parm)))))
                                                                    parm)))))
      bitmap_set_bit (args_to_skip, num);
      bitmap_set_bit (args_to_skip, num);
    else
    else
      {
      {
        /* This parm might not have been used up to now, but is going to be
        /* This parm might not have been used up to now, but is going to be
           used, hence register it.  */
           used, hence register it.  */
        add_referenced_var (parm);
        add_referenced_var (parm);
        if (is_gimple_reg (parm))
        if (is_gimple_reg (parm))
          {
          {
            arg = gimple_default_def (cfun, parm);
            arg = gimple_default_def (cfun, parm);
            if (!arg)
            if (!arg)
              {
              {
                arg = make_ssa_name (parm, gimple_build_nop ());
                arg = make_ssa_name (parm, gimple_build_nop ());
                set_default_def (parm, arg);
                set_default_def (parm, arg);
              }
              }
          }
          }
        else
        else
          arg = parm;
          arg = parm;
 
 
        if (!useless_type_conversion_p (DECL_ARG_TYPE (parm), TREE_TYPE (arg)))
        if (!useless_type_conversion_p (DECL_ARG_TYPE (parm), TREE_TYPE (arg)))
          arg = fold_convert (DECL_ARG_TYPE (parm), arg);
          arg = fold_convert (DECL_ARG_TYPE (parm), arg);
        VEC_safe_push (tree, heap, args_to_pass, arg);
        VEC_safe_push (tree, heap, args_to_pass, arg);
      }
      }
 
 
  /* See if the split function will return.  */
  /* See if the split function will return.  */
  FOR_EACH_EDGE (e, ei, return_bb->preds)
  FOR_EACH_EDGE (e, ei, return_bb->preds)
    if (bitmap_bit_p (split_point->split_bbs, e->src->index))
    if (bitmap_bit_p (split_point->split_bbs, e->src->index))
      break;
      break;
  if (e)
  if (e)
    split_part_return_p = true;
    split_part_return_p = true;
 
 
  /* Add return block to what will become the split function.
  /* Add return block to what will become the split function.
     We do not return; no return block is needed.  */
     We do not return; no return block is needed.  */
  if (!split_part_return_p)
  if (!split_part_return_p)
    ;
    ;
  /* We have no return block, so nothing is needed.  */
  /* We have no return block, so nothing is needed.  */
  else if (return_bb == EXIT_BLOCK_PTR)
  else if (return_bb == EXIT_BLOCK_PTR)
    ;
    ;
  /* When we do not want to return value, we need to construct
  /* When we do not want to return value, we need to construct
     new return block with empty return statement.
     new return block with empty return statement.
     FIXME: Once we are able to change return type, we should change function
     FIXME: Once we are able to change return type, we should change function
     to return void instead of just outputting function with undefined return
     to return void instead of just outputting function with undefined return
     value.  For structures this affects quality of codegen.  */
     value.  For structures this affects quality of codegen.  */
  else if (!split_point->split_part_set_retval
  else if (!split_point->split_part_set_retval
           && find_retval (return_bb))
           && find_retval (return_bb))
    {
    {
      bool redirected = true;
      bool redirected = true;
      basic_block new_return_bb = create_basic_block (NULL, 0, return_bb);
      basic_block new_return_bb = create_basic_block (NULL, 0, return_bb);
      gimple_stmt_iterator gsi = gsi_start_bb (new_return_bb);
      gimple_stmt_iterator gsi = gsi_start_bb (new_return_bb);
      gsi_insert_after (&gsi, gimple_build_return (NULL), GSI_NEW_STMT);
      gsi_insert_after (&gsi, gimple_build_return (NULL), GSI_NEW_STMT);
      while (redirected)
      while (redirected)
        {
        {
          redirected = false;
          redirected = false;
          FOR_EACH_EDGE (e, ei, return_bb->preds)
          FOR_EACH_EDGE (e, ei, return_bb->preds)
            if (bitmap_bit_p (split_point->split_bbs, e->src->index))
            if (bitmap_bit_p (split_point->split_bbs, e->src->index))
              {
              {
                new_return_bb->count += e->count;
                new_return_bb->count += e->count;
                new_return_bb->frequency += EDGE_FREQUENCY (e);
                new_return_bb->frequency += EDGE_FREQUENCY (e);
                redirect_edge_and_branch (e, new_return_bb);
                redirect_edge_and_branch (e, new_return_bb);
                redirected = true;
                redirected = true;
                break;
                break;
              }
              }
        }
        }
      e = make_edge (new_return_bb, EXIT_BLOCK_PTR, 0);
      e = make_edge (new_return_bb, EXIT_BLOCK_PTR, 0);
      e->probability = REG_BR_PROB_BASE;
      e->probability = REG_BR_PROB_BASE;
      e->count = new_return_bb->count;
      e->count = new_return_bb->count;
      bitmap_set_bit (split_point->split_bbs, new_return_bb->index);
      bitmap_set_bit (split_point->split_bbs, new_return_bb->index);
    }
    }
  /* When we pass around the value, use existing return block.  */
  /* When we pass around the value, use existing return block.  */
  else
  else
    bitmap_set_bit (split_point->split_bbs, return_bb->index);
    bitmap_set_bit (split_point->split_bbs, return_bb->index);
 
 
  /* If RETURN_BB has virtual operand PHIs, they must be removed and the
  /* If RETURN_BB has virtual operand PHIs, they must be removed and the
     virtual operand marked for renaming as we change the CFG in a way that
     virtual operand marked for renaming as we change the CFG in a way that
     tree-inline is not able to compensate for.
     tree-inline is not able to compensate for.
 
 
     Note this can happen whether or not we have a return value.  If we have
     Note this can happen whether or not we have a return value.  If we have
     a return value, then RETURN_BB may have PHIs for real operands too.  */
     a return value, then RETURN_BB may have PHIs for real operands too.  */
  if (return_bb != EXIT_BLOCK_PTR)
  if (return_bb != EXIT_BLOCK_PTR)
    {
    {
      bool phi_p = false;
      bool phi_p = false;
      for (gsi = gsi_start_phis (return_bb); !gsi_end_p (gsi);)
      for (gsi = gsi_start_phis (return_bb); !gsi_end_p (gsi);)
        {
        {
          gimple stmt = gsi_stmt (gsi);
          gimple stmt = gsi_stmt (gsi);
          if (is_gimple_reg (gimple_phi_result (stmt)))
          if (is_gimple_reg (gimple_phi_result (stmt)))
            {
            {
              gsi_next (&gsi);
              gsi_next (&gsi);
              continue;
              continue;
            }
            }
          mark_virtual_phi_result_for_renaming (stmt);
          mark_virtual_phi_result_for_renaming (stmt);
          remove_phi_node (&gsi, true);
          remove_phi_node (&gsi, true);
          phi_p = true;
          phi_p = true;
        }
        }
      /* In reality we have to rename the reaching definition of the
      /* In reality we have to rename the reaching definition of the
         virtual operand at return_bb as we will eventually release it
         virtual operand at return_bb as we will eventually release it
         when we remove the code region we outlined.
         when we remove the code region we outlined.
         So we have to rename all immediate virtual uses of that region
         So we have to rename all immediate virtual uses of that region
         if we didn't see a PHI definition yet.  */
         if we didn't see a PHI definition yet.  */
      /* ???  In real reality we want to set the reaching vdef of the
      /* ???  In real reality we want to set the reaching vdef of the
         entry of the SESE region as the vuse of the call and the reaching
         entry of the SESE region as the vuse of the call and the reaching
         vdef of the exit of the SESE region as the vdef of the call.  */
         vdef of the exit of the SESE region as the vdef of the call.  */
      if (!phi_p)
      if (!phi_p)
        for (gsi = gsi_start_bb (return_bb); !gsi_end_p (gsi); gsi_next (&gsi))
        for (gsi = gsi_start_bb (return_bb); !gsi_end_p (gsi); gsi_next (&gsi))
          {
          {
            gimple stmt = gsi_stmt (gsi);
            gimple stmt = gsi_stmt (gsi);
            if (gimple_vuse (stmt))
            if (gimple_vuse (stmt))
              {
              {
                gimple_set_vuse (stmt, NULL_TREE);
                gimple_set_vuse (stmt, NULL_TREE);
                update_stmt (stmt);
                update_stmt (stmt);
              }
              }
            if (gimple_vdef (stmt))
            if (gimple_vdef (stmt))
              break;
              break;
          }
          }
    }
    }
 
 
  /* Now create the actual clone.  */
  /* Now create the actual clone.  */
  rebuild_cgraph_edges ();
  rebuild_cgraph_edges ();
  node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
  node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
                                     !split_part_return_p,
                                     !split_part_return_p,
                                     split_point->split_bbs,
                                     split_point->split_bbs,
                                     split_point->entry_bb, "part");
                                     split_point->entry_bb, "part");
  /* For usual cloning it is enough to clear builtin only when signature
  /* For usual cloning it is enough to clear builtin only when signature
     changes.  For partial inlining we however can not expect the part
     changes.  For partial inlining we however can not expect the part
     of builtin implementation to have same semantic as the whole.  */
     of builtin implementation to have same semantic as the whole.  */
  if (DECL_BUILT_IN (node->decl))
  if (DECL_BUILT_IN (node->decl))
    {
    {
      DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
      DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
      DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
      DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
    }
    }
  cgraph_node_remove_callees (cur_node);
  cgraph_node_remove_callees (cur_node);
  if (!split_part_return_p)
  if (!split_part_return_p)
    TREE_THIS_VOLATILE (node->decl) = 1;
    TREE_THIS_VOLATILE (node->decl) = 1;
  if (dump_file)
  if (dump_file)
    dump_function_to_file (node->decl, dump_file, dump_flags);
    dump_function_to_file (node->decl, dump_file, dump_flags);
 
 
  /* Create the basic block we place call into.  It is the entry basic block
  /* Create the basic block we place call into.  It is the entry basic block
     split after last label.  */
     split after last label.  */
  call_bb = split_point->entry_bb;
  call_bb = split_point->entry_bb;
  for (gsi = gsi_start_bb (call_bb); !gsi_end_p (gsi);)
  for (gsi = gsi_start_bb (call_bb); !gsi_end_p (gsi);)
    if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
    if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
      {
      {
        last_stmt = gsi_stmt (gsi);
        last_stmt = gsi_stmt (gsi);
        gsi_next (&gsi);
        gsi_next (&gsi);
      }
      }
    else
    else
      break;
      break;
  e = split_block (split_point->entry_bb, last_stmt);
  e = split_block (split_point->entry_bb, last_stmt);
  remove_edge (e);
  remove_edge (e);
 
 
  /* Produce the call statement.  */
  /* Produce the call statement.  */
  gsi = gsi_last_bb (call_bb);
  gsi = gsi_last_bb (call_bb);
  FOR_EACH_VEC_ELT (tree, args_to_pass, i, arg)
  FOR_EACH_VEC_ELT (tree, args_to_pass, i, arg)
    if (!is_gimple_val (arg))
    if (!is_gimple_val (arg))
      {
      {
        arg = force_gimple_operand_gsi (&gsi, arg, true, NULL_TREE,
        arg = force_gimple_operand_gsi (&gsi, arg, true, NULL_TREE,
                                        false, GSI_CONTINUE_LINKING);
                                        false, GSI_CONTINUE_LINKING);
        VEC_replace (tree, args_to_pass, i, arg);
        VEC_replace (tree, args_to_pass, i, arg);
      }
      }
  call = gimple_build_call_vec (node->decl, args_to_pass);
  call = gimple_build_call_vec (node->decl, args_to_pass);
  gimple_set_block (call, DECL_INITIAL (current_function_decl));
  gimple_set_block (call, DECL_INITIAL (current_function_decl));
 
 
  /* We avoid address being taken on any variable used by split part,
  /* We avoid address being taken on any variable used by split part,
     so return slot optimization is always possible.  Moreover this is
     so return slot optimization is always possible.  Moreover this is
     required to make DECL_BY_REFERENCE work.  */
     required to make DECL_BY_REFERENCE work.  */
  if (aggregate_value_p (DECL_RESULT (current_function_decl),
  if (aggregate_value_p (DECL_RESULT (current_function_decl),
                         TREE_TYPE (current_function_decl)))
                         TREE_TYPE (current_function_decl)))
    gimple_call_set_return_slot_opt (call, true);
    gimple_call_set_return_slot_opt (call, true);
 
 
  /* Update return value.  This is bit tricky.  When we do not return,
  /* Update return value.  This is bit tricky.  When we do not return,
     do nothing.  When we return we might need to update return_bb
     do nothing.  When we return we might need to update return_bb
     or produce a new return statement.  */
     or produce a new return statement.  */
  if (!split_part_return_p)
  if (!split_part_return_p)
    gsi_insert_after (&gsi, call, GSI_NEW_STMT);
    gsi_insert_after (&gsi, call, GSI_NEW_STMT);
  else
  else
    {
    {
      e = make_edge (call_bb, return_bb,
      e = make_edge (call_bb, return_bb,
                     return_bb == EXIT_BLOCK_PTR ? 0 : EDGE_FALLTHRU);
                     return_bb == EXIT_BLOCK_PTR ? 0 : EDGE_FALLTHRU);
      e->count = call_bb->count;
      e->count = call_bb->count;
      e->probability = REG_BR_PROB_BASE;
      e->probability = REG_BR_PROB_BASE;
 
 
      /* If there is return basic block, see what value we need to store
      /* If there is return basic block, see what value we need to store
         return value into and put call just before it.  */
         return value into and put call just before it.  */
      if (return_bb != EXIT_BLOCK_PTR)
      if (return_bb != EXIT_BLOCK_PTR)
        {
        {
          real_retval = retval = find_retval (return_bb);
          real_retval = retval = find_retval (return_bb);
 
 
          if (real_retval && split_point->split_part_set_retval)
          if (real_retval && split_point->split_part_set_retval)
            {
            {
              gimple_stmt_iterator psi;
              gimple_stmt_iterator psi;
 
 
              /* See if we need new SSA_NAME for the result.
              /* See if we need new SSA_NAME for the result.
                 When DECL_BY_REFERENCE is true, retval is actually pointer to
                 When DECL_BY_REFERENCE is true, retval is actually pointer to
                 return value and it is constant in whole function.  */
                 return value and it is constant in whole function.  */
              if (TREE_CODE (retval) == SSA_NAME
              if (TREE_CODE (retval) == SSA_NAME
                  && !DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
                  && !DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
                {
                {
                  retval = make_ssa_name (SSA_NAME_VAR (retval), call);
                  retval = make_ssa_name (SSA_NAME_VAR (retval), call);
 
 
                  /* See if there is PHI defining return value.  */
                  /* See if there is PHI defining return value.  */
                  for (psi = gsi_start_phis (return_bb);
                  for (psi = gsi_start_phis (return_bb);
                       !gsi_end_p (psi); gsi_next (&psi))
                       !gsi_end_p (psi); gsi_next (&psi))
                    if (is_gimple_reg (gimple_phi_result (gsi_stmt (psi))))
                    if (is_gimple_reg (gimple_phi_result (gsi_stmt (psi))))
                      break;
                      break;
 
 
                  /* When there is PHI, just update its value.  */
                  /* When there is PHI, just update its value.  */
                  if (TREE_CODE (retval) == SSA_NAME
                  if (TREE_CODE (retval) == SSA_NAME
                      && !gsi_end_p (psi))
                      && !gsi_end_p (psi))
                    add_phi_arg (gsi_stmt (psi), retval, e, UNKNOWN_LOCATION);
                    add_phi_arg (gsi_stmt (psi), retval, e, UNKNOWN_LOCATION);
                  /* Otherwise update the return BB itself.
                  /* Otherwise update the return BB itself.
                     find_return_bb allows at most one assignment to return value,
                     find_return_bb allows at most one assignment to return value,
                     so update first statement.  */
                     so update first statement.  */
                  else
                  else
                    {
                    {
                      gimple_stmt_iterator bsi;
                      gimple_stmt_iterator bsi;
                      for (bsi = gsi_start_bb (return_bb); !gsi_end_p (bsi);
                      for (bsi = gsi_start_bb (return_bb); !gsi_end_p (bsi);
                           gsi_next (&bsi))
                           gsi_next (&bsi))
                        if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN)
                        if (gimple_code (gsi_stmt (bsi)) == GIMPLE_RETURN)
                          {
                          {
                            gimple_return_set_retval (gsi_stmt (bsi), retval);
                            gimple_return_set_retval (gsi_stmt (bsi), retval);
                            break;
                            break;
                          }
                          }
                        else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN
                        else if (gimple_code (gsi_stmt (bsi)) == GIMPLE_ASSIGN
                                 && !gimple_clobber_p (gsi_stmt (bsi)))
                                 && !gimple_clobber_p (gsi_stmt (bsi)))
                          {
                          {
                            gimple_assign_set_rhs1 (gsi_stmt (bsi), retval);
                            gimple_assign_set_rhs1 (gsi_stmt (bsi), retval);
                            break;
                            break;
                          }
                          }
                      update_stmt (gsi_stmt (bsi));
                      update_stmt (gsi_stmt (bsi));
                    }
                    }
                }
                }
              if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
              if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
                {
                {
                  gimple_call_set_lhs (call, build_simple_mem_ref (retval));
                  gimple_call_set_lhs (call, build_simple_mem_ref (retval));
                  gsi_insert_after (&gsi, call, GSI_NEW_STMT);
                  gsi_insert_after (&gsi, call, GSI_NEW_STMT);
                }
                }
              else
              else
                {
                {
                  tree restype;
                  tree restype;
                  restype = TREE_TYPE (DECL_RESULT (current_function_decl));
                  restype = TREE_TYPE (DECL_RESULT (current_function_decl));
                  gsi_insert_after (&gsi, call, GSI_NEW_STMT);
                  gsi_insert_after (&gsi, call, GSI_NEW_STMT);
                  if (!useless_type_conversion_p (TREE_TYPE (retval), restype))
                  if (!useless_type_conversion_p (TREE_TYPE (retval), restype))
                    {
                    {
                      gimple cpy;
                      gimple cpy;
                      tree tem = create_tmp_reg (restype, NULL);
                      tree tem = create_tmp_reg (restype, NULL);
                      tem = make_ssa_name (tem, call);
                      tem = make_ssa_name (tem, call);
                      cpy = gimple_build_assign_with_ops (NOP_EXPR, retval,
                      cpy = gimple_build_assign_with_ops (NOP_EXPR, retval,
                                                          tem, NULL_TREE);
                                                          tem, NULL_TREE);
                      gsi_insert_after (&gsi, cpy, GSI_NEW_STMT);
                      gsi_insert_after (&gsi, cpy, GSI_NEW_STMT);
                      retval = tem;
                      retval = tem;
                    }
                    }
                  gimple_call_set_lhs (call, retval);
                  gimple_call_set_lhs (call, retval);
                  update_stmt (call);
                  update_stmt (call);
                }
                }
            }
            }
          else
          else
            gsi_insert_after (&gsi, call, GSI_NEW_STMT);
            gsi_insert_after (&gsi, call, GSI_NEW_STMT);
        }
        }
      /* We don't use return block (there is either no return in function or
      /* We don't use return block (there is either no return in function or
         multiple of them).  So create new basic block with return statement.
         multiple of them).  So create new basic block with return statement.
         */
         */
      else
      else
        {
        {
          gimple ret;
          gimple ret;
          if (split_point->split_part_set_retval
          if (split_point->split_part_set_retval
              && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
              && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
            {
            {
              retval = DECL_RESULT (current_function_decl);
              retval = DECL_RESULT (current_function_decl);
 
 
              /* We use temporary register to hold value when aggregate_value_p
              /* We use temporary register to hold value when aggregate_value_p
                 is false.  Similarly for DECL_BY_REFERENCE we must avoid extra
                 is false.  Similarly for DECL_BY_REFERENCE we must avoid extra
                 copy.  */
                 copy.  */
              if (!aggregate_value_p (retval, TREE_TYPE (current_function_decl))
              if (!aggregate_value_p (retval, TREE_TYPE (current_function_decl))
                  && !DECL_BY_REFERENCE (retval))
                  && !DECL_BY_REFERENCE (retval))
                retval = create_tmp_reg (TREE_TYPE (retval), NULL);
                retval = create_tmp_reg (TREE_TYPE (retval), NULL);
              if (is_gimple_reg (retval))
              if (is_gimple_reg (retval))
                {
                {
                  /* When returning by reference, there is only one SSA name
                  /* When returning by reference, there is only one SSA name
                     assigned to RESULT_DECL (that is pointer to return value).
                     assigned to RESULT_DECL (that is pointer to return value).
                     Look it up or create new one if it is missing.  */
                     Look it up or create new one if it is missing.  */
                  if (DECL_BY_REFERENCE (retval))
                  if (DECL_BY_REFERENCE (retval))
                    {
                    {
                      tree retval_name;
                      tree retval_name;
                      if ((retval_name = gimple_default_def (cfun, retval))
                      if ((retval_name = gimple_default_def (cfun, retval))
                          != NULL)
                          != NULL)
                        retval = retval_name;
                        retval = retval_name;
                      else
                      else
                        {
                        {
                          retval_name = make_ssa_name (retval,
                          retval_name = make_ssa_name (retval,
                                                       gimple_build_nop ());
                                                       gimple_build_nop ());
                          set_default_def (retval, retval_name);
                          set_default_def (retval, retval_name);
                          retval = retval_name;
                          retval = retval_name;
                        }
                        }
                    }
                    }
                  /* Otherwise produce new SSA name for return value.  */
                  /* Otherwise produce new SSA name for return value.  */
                  else
                  else
                    retval = make_ssa_name (retval, call);
                    retval = make_ssa_name (retval, call);
                }
                }
              if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
              if (DECL_BY_REFERENCE (DECL_RESULT (current_function_decl)))
                gimple_call_set_lhs (call, build_simple_mem_ref (retval));
                gimple_call_set_lhs (call, build_simple_mem_ref (retval));
              else
              else
                gimple_call_set_lhs (call, retval);
                gimple_call_set_lhs (call, retval);
            }
            }
          gsi_insert_after (&gsi, call, GSI_NEW_STMT);
          gsi_insert_after (&gsi, call, GSI_NEW_STMT);
          ret = gimple_build_return (retval);
          ret = gimple_build_return (retval);
          gsi_insert_after (&gsi, ret, GSI_NEW_STMT);
          gsi_insert_after (&gsi, ret, GSI_NEW_STMT);
        }
        }
    }
    }
  free_dominance_info (CDI_DOMINATORS);
  free_dominance_info (CDI_DOMINATORS);
  free_dominance_info (CDI_POST_DOMINATORS);
  free_dominance_info (CDI_POST_DOMINATORS);
  compute_inline_parameters (node, true);
  compute_inline_parameters (node, true);
}
}
 
 
/* Execute function splitting pass.  */
/* Execute function splitting pass.  */
 
 
static unsigned int
static unsigned int
execute_split_functions (void)
execute_split_functions (void)
{
{
  gimple_stmt_iterator bsi;
  gimple_stmt_iterator bsi;
  basic_block bb;
  basic_block bb;
  int overall_time = 0, overall_size = 0;
  int overall_time = 0, overall_size = 0;
  int todo = 0;
  int todo = 0;
  struct cgraph_node *node = cgraph_get_node (current_function_decl);
  struct cgraph_node *node = cgraph_get_node (current_function_decl);
 
 
  if (flags_from_decl_or_type (current_function_decl)
  if (flags_from_decl_or_type (current_function_decl)
      & (ECF_NORETURN|ECF_MALLOC))
      & (ECF_NORETURN|ECF_MALLOC))
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: noreturn/malloc function.\n");
        fprintf (dump_file, "Not splitting: noreturn/malloc function.\n");
      return 0;
      return 0;
    }
    }
  if (MAIN_NAME_P (DECL_NAME (current_function_decl)))
  if (MAIN_NAME_P (DECL_NAME (current_function_decl)))
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: main function.\n");
        fprintf (dump_file, "Not splitting: main function.\n");
      return 0;
      return 0;
    }
    }
  /* This can be relaxed; function might become inlinable after splitting
  /* This can be relaxed; function might become inlinable after splitting
     away the uninlinable part.  */
     away the uninlinable part.  */
  if (!inline_summary (node)->inlinable)
  if (!inline_summary (node)->inlinable)
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: not inlinable.\n");
        fprintf (dump_file, "Not splitting: not inlinable.\n");
      return 0;
      return 0;
    }
    }
  if (DECL_DISREGARD_INLINE_LIMITS (node->decl))
  if (DECL_DISREGARD_INLINE_LIMITS (node->decl))
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: disregarding inline limits.\n");
        fprintf (dump_file, "Not splitting: disregarding inline limits.\n");
      return 0;
      return 0;
    }
    }
  /* This can be relaxed; most of versioning tests actually prevents
  /* This can be relaxed; most of versioning tests actually prevents
     a duplication.  */
     a duplication.  */
  if (!tree_versionable_function_p (current_function_decl))
  if (!tree_versionable_function_p (current_function_decl))
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: not versionable.\n");
        fprintf (dump_file, "Not splitting: not versionable.\n");
      return 0;
      return 0;
    }
    }
  /* FIXME: we could support this.  */
  /* FIXME: we could support this.  */
  if (DECL_STRUCT_FUNCTION (current_function_decl)->static_chain_decl)
  if (DECL_STRUCT_FUNCTION (current_function_decl)->static_chain_decl)
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: nested function.\n");
        fprintf (dump_file, "Not splitting: nested function.\n");
      return 0;
      return 0;
    }
    }
 
 
  /* See if it makes sense to try to split.
  /* See if it makes sense to try to split.
     It makes sense to split if we inline, that is if we have direct calls to
     It makes sense to split if we inline, that is if we have direct calls to
     handle or direct calls are possibly going to appear as result of indirect
     handle or direct calls are possibly going to appear as result of indirect
     inlining or LTO.  Also handle -fprofile-generate as LTO to allow non-LTO
     inlining or LTO.  Also handle -fprofile-generate as LTO to allow non-LTO
     training for LTO -fprofile-use build.
     training for LTO -fprofile-use build.
 
 
     Note that we are not completely conservative about disqualifying functions
     Note that we are not completely conservative about disqualifying functions
     called once.  It is possible that the caller is called more then once and
     called once.  It is possible that the caller is called more then once and
     then inlining would still benefit.  */
     then inlining would still benefit.  */
  if ((!node->callers || !node->callers->next_caller)
  if ((!node->callers || !node->callers->next_caller)
      && !node->address_taken
      && !node->address_taken
      && (!flag_lto || !node->local.externally_visible))
      && (!flag_lto || !node->local.externally_visible))
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: not called directly "
        fprintf (dump_file, "Not splitting: not called directly "
                 "or called once.\n");
                 "or called once.\n");
      return 0;
      return 0;
    }
    }
 
 
  /* FIXME: We can actually split if splitting reduces call overhead.  */
  /* FIXME: We can actually split if splitting reduces call overhead.  */
  if (!flag_inline_small_functions
  if (!flag_inline_small_functions
      && !DECL_DECLARED_INLINE_P (current_function_decl))
      && !DECL_DECLARED_INLINE_P (current_function_decl))
    {
    {
      if (dump_file)
      if (dump_file)
        fprintf (dump_file, "Not splitting: not autoinlining and function"
        fprintf (dump_file, "Not splitting: not autoinlining and function"
                 " is not inline.\n");
                 " is not inline.\n");
      return 0;
      return 0;
    }
    }
 
 
  /* Initialize bitmap to track forbidden calls.  */
  /* Initialize bitmap to track forbidden calls.  */
  forbidden_dominators = BITMAP_ALLOC (NULL);
  forbidden_dominators = BITMAP_ALLOC (NULL);
  calculate_dominance_info (CDI_DOMINATORS);
  calculate_dominance_info (CDI_DOMINATORS);
 
 
  /* Compute local info about basic blocks and determine function size/time.  */
  /* Compute local info about basic blocks and determine function size/time.  */
  VEC_safe_grow_cleared (bb_info, heap, bb_info_vec, last_basic_block + 1);
  VEC_safe_grow_cleared (bb_info, heap, bb_info_vec, last_basic_block + 1);
  memset (&best_split_point, 0, sizeof (best_split_point));
  memset (&best_split_point, 0, sizeof (best_split_point));
  FOR_EACH_BB (bb)
  FOR_EACH_BB (bb)
    {
    {
      int time = 0;
      int time = 0;
      int size = 0;
      int size = 0;
      int freq = compute_call_stmt_bb_frequency (current_function_decl, bb);
      int freq = compute_call_stmt_bb_frequency (current_function_decl, bb);
 
 
      if (dump_file && (dump_flags & TDF_DETAILS))
      if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file, "Basic block %i\n", bb->index);
        fprintf (dump_file, "Basic block %i\n", bb->index);
 
 
      for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
      for (bsi = gsi_start_bb (bb); !gsi_end_p (bsi); gsi_next (&bsi))
        {
        {
          int this_time, this_size;
          int this_time, this_size;
          gimple stmt = gsi_stmt (bsi);
          gimple stmt = gsi_stmt (bsi);
 
 
          this_size = estimate_num_insns (stmt, &eni_size_weights);
          this_size = estimate_num_insns (stmt, &eni_size_weights);
          this_time = estimate_num_insns (stmt, &eni_time_weights) * freq;
          this_time = estimate_num_insns (stmt, &eni_time_weights) * freq;
          size += this_size;
          size += this_size;
          time += this_time;
          time += this_time;
          check_forbidden_calls (stmt);
          check_forbidden_calls (stmt);
 
 
          if (dump_file && (dump_flags & TDF_DETAILS))
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
            {
              fprintf (dump_file, "  freq:%6i size:%3i time:%3i ",
              fprintf (dump_file, "  freq:%6i size:%3i time:%3i ",
                       freq, this_size, this_time);
                       freq, this_size, this_time);
              print_gimple_stmt (dump_file, stmt, 0, 0);
              print_gimple_stmt (dump_file, stmt, 0, 0);
            }
            }
        }
        }
      overall_time += time;
      overall_time += time;
      overall_size += size;
      overall_size += size;
      VEC_index (bb_info, bb_info_vec, bb->index)->time = time;
      VEC_index (bb_info, bb_info_vec, bb->index)->time = time;
      VEC_index (bb_info, bb_info_vec, bb->index)->size = size;
      VEC_index (bb_info, bb_info_vec, bb->index)->size = size;
    }
    }
  find_split_points (overall_time, overall_size);
  find_split_points (overall_time, overall_size);
  if (best_split_point.split_bbs)
  if (best_split_point.split_bbs)
    {
    {
      split_function (&best_split_point);
      split_function (&best_split_point);
      BITMAP_FREE (best_split_point.ssa_names_to_pass);
      BITMAP_FREE (best_split_point.ssa_names_to_pass);
      BITMAP_FREE (best_split_point.split_bbs);
      BITMAP_FREE (best_split_point.split_bbs);
      todo = TODO_update_ssa | TODO_cleanup_cfg;
      todo = TODO_update_ssa | TODO_cleanup_cfg;
    }
    }
  BITMAP_FREE (forbidden_dominators);
  BITMAP_FREE (forbidden_dominators);
  VEC_free (bb_info, heap, bb_info_vec);
  VEC_free (bb_info, heap, bb_info_vec);
  bb_info_vec = NULL;
  bb_info_vec = NULL;
  return todo;
  return todo;
}
}
 
 
/* Gate function splitting pass.  When doing profile feedback, we want
/* Gate function splitting pass.  When doing profile feedback, we want
   to execute the pass after profiling is read.  So disable one in
   to execute the pass after profiling is read.  So disable one in
   early optimization.  */
   early optimization.  */
 
 
static bool
static bool
gate_split_functions (void)
gate_split_functions (void)
{
{
  return (flag_partial_inlining
  return (flag_partial_inlining
          && !profile_arc_flag && !flag_branch_probabilities);
          && !profile_arc_flag && !flag_branch_probabilities);
}
}
 
 
struct gimple_opt_pass pass_split_functions =
struct gimple_opt_pass pass_split_functions =
{
{
 {
 {
  GIMPLE_PASS,
  GIMPLE_PASS,
  "fnsplit",                            /* name */
  "fnsplit",                            /* name */
  gate_split_functions,                 /* gate */
  gate_split_functions,                 /* gate */
  execute_split_functions,              /* execute */
  execute_split_functions,              /* execute */
  NULL,                                 /* sub */
  NULL,                                 /* sub */
  NULL,                                 /* next */
  NULL,                                 /* next */
  0,                                     /* static_pass_number */
  0,                                     /* static_pass_number */
  TV_IPA_FNSPLIT,                       /* tv_id */
  TV_IPA_FNSPLIT,                       /* tv_id */
  PROP_cfg,                             /* properties_required */
  PROP_cfg,                             /* properties_required */
  0,                                     /* properties_provided */
  0,                                     /* properties_provided */
  0,                                     /* properties_destroyed */
  0,                                     /* properties_destroyed */
  0,                                     /* todo_flags_start */
  0,                                     /* todo_flags_start */
  TODO_verify_all                       /* todo_flags_finish */
  TODO_verify_all                       /* todo_flags_finish */
 }
 }
};
};
 
 
/* Gate feedback driven function splitting pass.
/* Gate feedback driven function splitting pass.
   We don't need to split when profiling at all, we are producing
   We don't need to split when profiling at all, we are producing
   lousy code anyway.  */
   lousy code anyway.  */
 
 
static bool
static bool
gate_feedback_split_functions (void)
gate_feedback_split_functions (void)
{
{
  return (flag_partial_inlining
  return (flag_partial_inlining
          && flag_branch_probabilities);
          && flag_branch_probabilities);
}
}
 
 
/* Execute function splitting pass.  */
/* Execute function splitting pass.  */
 
 
static unsigned int
static unsigned int
execute_feedback_split_functions (void)
execute_feedback_split_functions (void)
{
{
  unsigned int retval = execute_split_functions ();
  unsigned int retval = execute_split_functions ();
  if (retval)
  if (retval)
    retval |= TODO_rebuild_cgraph_edges;
    retval |= TODO_rebuild_cgraph_edges;
  return retval;
  return retval;
}
}
 
 
struct gimple_opt_pass pass_feedback_split_functions =
struct gimple_opt_pass pass_feedback_split_functions =
{
{
 {
 {
  GIMPLE_PASS,
  GIMPLE_PASS,
  "feedback_fnsplit",                   /* name */
  "feedback_fnsplit",                   /* name */
  gate_feedback_split_functions,        /* gate */
  gate_feedback_split_functions,        /* gate */
  execute_feedback_split_functions,     /* execute */
  execute_feedback_split_functions,     /* execute */
  NULL,                                 /* sub */
  NULL,                                 /* sub */
  NULL,                                 /* next */
  NULL,                                 /* next */
  0,                                     /* static_pass_number */
  0,                                     /* static_pass_number */
  TV_IPA_FNSPLIT,                       /* tv_id */
  TV_IPA_FNSPLIT,                       /* tv_id */
  PROP_cfg,                             /* properties_required */
  PROP_cfg,                             /* properties_required */
  0,                                     /* properties_provided */
  0,                                     /* properties_provided */
  0,                                     /* properties_destroyed */
  0,                                     /* properties_destroyed */
  0,                                     /* todo_flags_start */
  0,                                     /* todo_flags_start */
  TODO_verify_all                       /* todo_flags_finish */
  TODO_verify_all                       /* todo_flags_finish */
 }
 }
};
};
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.