OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / ipa-split.c
index 4fcbfe9..7e0769f 100644 (file)
@@ -46,7 +46,7 @@ along with GCC; see the file COPYING3.  If not see
      }
 
    When func becomes inlinable and when cheap_test is often true, inlining func,
-   but not fund.part leads to performance imrovement similar as inlining
+   but not fund.part leads to performance improvement similar as inlining
    original func while the code size growth is smaller.
 
    The pass is organized in three stages:
@@ -92,6 +92,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "fibheap.h"
 #include "params.h"
 #include "gimple-pretty-print.h"
+#include "ipa-inline.h"
 
 /* Per basic block info.  */
 
@@ -112,7 +113,7 @@ struct split_point
   /* Size of the partitions.  */
   unsigned int header_time, header_size, split_time, split_size;
 
-  /* SSA names that need to be passed into spit funciton.  */
+  /* SSA names that need to be passed into spit function.  */
   bitmap ssa_names_to_pass;
 
   /* Basic block where we split (that will become entry point of new function.  */
@@ -169,8 +170,9 @@ static void
 dump_split_point (FILE * file, struct split_point *current)
 {
   fprintf (file,
-          "Split point at BB %i header time:%i header size: %i"
-          " split time: %i split size: %i\n  bbs: ",
+          "Split point at BB %i\n"
+          "  header time: %i header size: %i\n"
+          "  split time: %i split size: %i\n  bbs: ",
           current->entry_bb->index, current->header_time,
           current->header_size, current->split_time, current->split_size);
   dump_bitmap (file, current->split_bbs);
@@ -285,7 +287,7 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
   edge_iterator ei;
   gimple_stmt_iterator bsi;
   unsigned int i;
-  int incomming_freq = 0;
+  int incoming_freq = 0;
   tree retval;
 
   if (dump_file && (dump_flags & TDF_DETAILS))
@@ -293,16 +295,16 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
 
   FOR_EACH_EDGE (e, ei, current->entry_bb->preds)
     if (!bitmap_bit_p (current->split_bbs, e->src->index))
-      incomming_freq += EDGE_FREQUENCY (e);
+      incoming_freq += EDGE_FREQUENCY (e);
 
   /* Do not split when we would end up calling function anyway.  */
-  if (incomming_freq
+  if (incoming_freq
       >= (ENTRY_BLOCK_PTR->frequency
          * PARAM_VALUE (PARAM_PARTIAL_INLINING_ENTRY_PROBABILITY) / 100))
     {
       if (dump_file && (dump_flags & TDF_DETAILS))
        fprintf (dump_file,
-                "  Refused: incomming frequency is too large.\n");
+                "  Refused: incoming frequency is too large.\n");
       return;
     }
 
@@ -313,8 +315,8 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
       return;
     }
 
-  /* Verify that PHI args on entry are either virutal or all their operands
-     incomming from header are the same.  */
+  /* Verify that PHI args on entry are either virtual or all their operands
+     incoming from header are the same.  */
   for (bsi = gsi_start_phis (current->entry_bb); !gsi_end_p (bsi); gsi_next (&bsi))
     {
       gimple stmt = gsi_stmt (bsi);
@@ -411,9 +413,6 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
                 "  Refused: split part has non-ssa uses\n");
       return;
     }
-  if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "  Accepted!\n");
-
   /* 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
      in the split part and add code to header to pass it around.
@@ -451,6 +450,30 @@ consider_split (struct split_point *current, bitmap non_ssa_vars,
   else
     current->split_part_set_retval = true;
 
+  /* 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.  */
+  if (return_bb != EXIT_BLOCK_PTR)
+    {
+      gimple_stmt_iterator 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)))
+           && !(retval
+                && current->split_part_set_retval
+                && TREE_CODE (retval) == SSA_NAME
+                && !DECL_BY_REFERENCE (DECL_RESULT (current_function_decl))
+                && SSA_NAME_DEF_STMT (retval) == gsi_stmt (psi)))
+         {
+           if (dump_file && (dump_flags & TDF_DETAILS))
+             fprintf (dump_file,
+                      "  Refused: return bb has extra PHIs\n");
+           return;
+         }
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  Accepted!\n");
+
   /* At the moment chose split point with lowest frequency and that leaves
      out smallest size of header.
      In future we might re-consider this heuristics.  */
@@ -496,51 +519,43 @@ static basic_block
 find_return_bb (void)
 {
   edge e;
-  edge_iterator ei;
   basic_block return_bb = EXIT_BLOCK_PTR;
+  gimple_stmt_iterator bsi;
+  bool found_return = false;
+  tree retval = NULL_TREE;
 
-  if (EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 1)
-    FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
-      {
-       gimple_stmt_iterator bsi;
-       bool found_return = false;
-       tree retval = NULL_TREE;
+  if (!single_pred_p (EXIT_BLOCK_PTR))
+    return return_bb;
+
+  e = single_pred_edge (EXIT_BLOCK_PTR);
+  for (bsi = gsi_last_bb (e->src); !gsi_end_p (bsi); gsi_prev (&bsi))
+    {
+      gimple stmt = gsi_stmt (bsi);
+      if (gimple_code (stmt) == GIMPLE_LABEL || is_gimple_debug (stmt))
+       ;
+      else if (gimple_code (stmt) == GIMPLE_ASSIGN
+              && found_return
+              && gimple_assign_single_p (stmt)
+              && (auto_var_in_fn_p (gimple_assign_rhs1 (stmt),
+                                    current_function_decl)
+                  || is_gimple_min_invariant (gimple_assign_rhs1 (stmt)))
+              && retval == gimple_assign_lhs (stmt))
+       ;
+      else if (gimple_code (stmt) == GIMPLE_RETURN)
+       {
+         found_return = true;
+         retval = gimple_return_retval (stmt);
+       }
+      else
+       break;
+    }
+  if (gsi_end_p (bsi) && found_return)
+    return_bb = e->src;
 
-       for (bsi = gsi_last_bb (e->src); !gsi_end_p (bsi); gsi_prev (&bsi))
-         {
-           gimple stmt = gsi_stmt (bsi);
-           if (gimple_code (stmt) == GIMPLE_LABEL
-               || is_gimple_debug (stmt))
-             ;
-           else if (gimple_code (stmt) == GIMPLE_ASSIGN
-                    && found_return
-                    && gimple_assign_single_p (stmt)
-                    && (auto_var_in_fn_p (gimple_assign_rhs1 (stmt),
-                                          current_function_decl)
-                        || is_gimple_min_invariant
-                             (gimple_assign_rhs1 (stmt)))
-                    && retval == gimple_assign_lhs (stmt))
-             ;
-           else if (gimple_code (stmt) == GIMPLE_RETURN)
-             {
-               found_return = true;
-               retval = gimple_return_retval (stmt);
-             }
-           else
-             break;
-         }
-       if (gsi_end_p (bsi) && found_return)
-         {
-           if (retval)
-             return e->src;
-           else
-             return_bb = e->src;
-         }
-      }
   return return_bb;
 }
 
-/* Given return basicblock RETURN_BB, see where return value is really
+/* Given return basic block RETURN_BB, see where return value is really
    stored.  */
 static tree
 find_retval (basic_block return_bb)
@@ -630,11 +645,10 @@ visit_bb (basic_block bb, basic_block return_bb,
         into different partitions.  This would require tracking of
         EH regions and checking in consider_split_point if they 
         are not used elsewhere.  */
-      if (gimple_code (stmt) == GIMPLE_RESX
-         && stmt_can_throw_external (stmt))
+      if (gimple_code (stmt) == GIMPLE_RESX)
        {
          if (dump_file && (dump_flags & TDF_DETAILS))
-           fprintf (dump_file, "Cannot split: external resx.\n");
+           fprintf (dump_file, "Cannot split: resx.\n");
          can_split = false;
        }
       if (gimple_code (stmt) == GIMPLE_EH_DISPATCH)
@@ -657,6 +671,7 @@ visit_bb (basic_block bb, basic_block return_bb,
             way to store builtin_stack_save result in non-SSA variable
             since all calls to those are compiler generated.  */
          case BUILT_IN_APPLY:
+         case BUILT_IN_APPLY_ARGS:
          case BUILT_IN_VA_START:
            if (dump_file && (dump_flags & TDF_DETAILS))
              fprintf (dump_file,
@@ -703,7 +718,7 @@ visit_bb (basic_block bb, basic_block return_bb,
                                                   mark_nonssa_use,
                                                   mark_nonssa_use);
     }
-  /* Record also uses comming from PHI operand in return BB.  */
+  /* Record also uses coming from PHI operand in return BB.  */
   FOR_EACH_EDGE (e, ei, bb->succs)
     if (e->dest == return_bb)
       {
@@ -741,11 +756,11 @@ typedef struct
   bitmap bbs_visited;
 
   /* Last examined edge in DFS walk.  Since we walk unoriented graph,
-     the value is up to sum of incomming and outgoing edges of BB.  */
+     the value is up to sum of incoming and outgoing edges of BB.  */
   unsigned int edge_num;
 
   /* Stack entry index of earliest BB reachable from current BB
-     or any BB visited later in DFS valk.  */
+     or any BB visited later in DFS walk.  */
   int earliest;
 
   /* Overall time and size of all BBs reached from this BB in DFS walk.  */
@@ -888,8 +903,8 @@ find_split_points (int overall_time, int overall_size)
                   && (intptr_t)dest->aux < entry->earliest)
            entry->earliest = (intptr_t)dest->aux;
        }
-      /* We are done with examing the edges. pop off the value from stack and
-        merge stuff we cummulate during the walk.  */
+      /* We are done with examining the edges.  Pop off the value from stack
+        and merge stuff we accumulate during the walk.  */
       else if (entry->bb != ENTRY_BLOCK_PTR)
        {
          stack_entry *prev = VEC_index (stack_entry, stack,
@@ -930,10 +945,10 @@ static void
 split_function (struct split_point *split_point)
 {
   VEC (tree, heap) *args_to_pass = NULL;
-  bitmap args_to_skip = BITMAP_ALLOC (NULL);
+  bitmap args_to_skip;
   tree parm;
   int num = 0;
-  struct cgraph_node *node;
+  struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
   basic_block return_bb = find_return_bb ();
   basic_block call_bb;
   gimple_stmt_iterator gsi;
@@ -943,7 +958,6 @@ split_function (struct split_point *split_point)
   tree retval = NULL, real_retval = NULL;
   bool split_part_return_p = false;
   gimple last_stmt = NULL;
-  bool conv_needed = false;
   unsigned int i;
   tree arg;
 
@@ -953,23 +967,40 @@ split_function (struct split_point *split_point)
       dump_split_point (dump_file, split_point);
     }
 
+  if (cur_node->local.can_change_signature)
+    args_to_skip = BITMAP_ALLOC (NULL);
+  else
+    args_to_skip = NULL;
+
   /* Collect the parameters of new function and args_to_skip bitmap.  */
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm; parm = DECL_CHAIN (parm), num++)
-    if (!is_gimple_reg (parm)
-       || !gimple_default_def (cfun, parm)
-       || !bitmap_bit_p (split_point->ssa_names_to_pass,
-                         SSA_NAME_VERSION (gimple_default_def (cfun, parm))))
+    if (args_to_skip
+       && (!is_gimple_reg (parm)
+           || !gimple_default_def (cfun, parm)
+           || !bitmap_bit_p (split_point->ssa_names_to_pass,
+                             SSA_NAME_VERSION (gimple_default_def (cfun,
+                                                                   parm)))))
       bitmap_set_bit (args_to_skip, num);
     else
       {
-       arg = gimple_default_def (cfun, parm);
-       if (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm))
-           != TYPE_MAIN_VARIANT (TREE_TYPE (arg)))
+       /* This parm might not have been used up to now, but is going to be
+          used, hence register it.  */
+       add_referenced_var (parm);
+       if (is_gimple_reg (parm))
          {
-           conv_needed = true;
-           arg = fold_convert (DECL_ARG_TYPE (parm), arg);
+           arg = gimple_default_def (cfun, parm);
+           if (!arg)
+             {
+               arg = make_ssa_name (parm, gimple_build_nop ());
+               set_default_def (parm, arg);
+             }
          }
+       else
+         arg = parm;
+
+       if (!useless_type_conversion_p (DECL_ARG_TYPE (parm), TREE_TYPE (arg)))
+         arg = fold_convert (DECL_ARG_TYPE (parm), arg);
        VEC_safe_push (tree, heap, args_to_pass, arg);
       }
 
@@ -1016,26 +1047,57 @@ split_function (struct split_point *split_point)
       e->probability = REG_BR_PROB_BASE;
       e->count = new_return_bb->count;
       bitmap_set_bit (split_point->split_bbs, new_return_bb->index);
-      /* We change CFG in a way tree-inline is not able to compensate on while
-        updating PHIs.  There are only virtuals in return_bb, so recompute
-        them.  */
+    }
+  /* When we pass around the value, use existing return block.  */
+  else
+    bitmap_set_bit (split_point->split_bbs, return_bb->index);
+
+  /* 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
+     tree-inline is not able to compensate for.
+
+     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.  */
+  if (return_bb != EXIT_BLOCK_PTR)
+    {
+      bool phi_p = false;
       for (gsi = gsi_start_phis (return_bb); !gsi_end_p (gsi);)
        {
          gimple stmt = gsi_stmt (gsi);
-         gcc_assert (!is_gimple_reg (gimple_phi_result (stmt)));
+         if (is_gimple_reg (gimple_phi_result (stmt)))
+           {
+             gsi_next (&gsi);
+             continue;
+           }
          mark_virtual_phi_result_for_renaming (stmt);
          remove_phi_node (&gsi, true);
+         phi_p = true;
        }
+      /* In reality we have to rename the reaching definition of the
+        virtual operand at return_bb as we will eventually release it
+        when we remove the code region we outlined.
+        So we have to rename all immediate virtual uses of that region
+        if we didn't see a PHI definition yet.  */
+      /* ???  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
+        vdef of the exit of the SESE region as the vdef of the call.  */
+      if (!phi_p)
+       for (gsi = gsi_start_bb (return_bb); !gsi_end_p (gsi); gsi_next (&gsi))
+         {
+           gimple stmt = gsi_stmt (gsi);
+           if (gimple_vuse (stmt))
+             {
+               gimple_set_vuse (stmt, NULL_TREE);
+               update_stmt (stmt);
+             }
+           if (gimple_vdef (stmt))
+             break;
+         }
     }
-  /* When we pass aorund the value, use existing return block.  */
-  else
-    bitmap_set_bit (split_point->split_bbs, return_bb->index);
 
   /* Now create the actual clone.  */
   rebuild_cgraph_edges ();
-  node = cgraph_function_versioning (cgraph_node (current_function_decl),
-                                    NULL, NULL,
-                                    args_to_skip,
+  node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
                                     split_point->split_bbs,
                                     split_point->entry_bb, "part");
   /* For usual cloning it is enough to clear builtin only when signature
@@ -1046,7 +1108,7 @@ split_function (struct split_point *split_point)
       DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
       DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
     }
-  cgraph_node_remove_callees (cgraph_node (current_function_decl));
+  cgraph_node_remove_callees (cur_node);
   if (!split_part_return_p)
     TREE_THIS_VOLATILE (node->decl) = 1;
   if (dump_file)
@@ -1068,14 +1130,13 @@ split_function (struct split_point *split_point)
 
   /* Produce the call statement.  */
   gsi = gsi_last_bb (call_bb);
-  if (conv_needed)
-    FOR_EACH_VEC_ELT (tree, args_to_pass, i, arg)
-      if (!is_gimple_val (arg))
-       {
-         arg = force_gimple_operand_gsi (&gsi, arg, true, NULL_TREE,
-                                         false, GSI_NEW_STMT);
-         VEC_replace (tree, args_to_pass, i, arg);
-       }
+  FOR_EACH_VEC_ELT (tree, args_to_pass, i, arg)
+    if (!is_gimple_val (arg))
+      {
+       arg = force_gimple_operand_gsi (&gsi, arg, true, NULL_TREE,
+                                       false, GSI_CONTINUE_LINKING);
+       VEC_replace (tree, args_to_pass, i, arg);
+      }
   call = gimple_build_call_vec (node->decl, args_to_pass);
   gimple_set_block (call, DECL_INITIAL (current_function_decl));
 
@@ -1148,11 +1209,31 @@ split_function (struct split_point *split_point)
                    }
                }
              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);
+               }
              else
-               gimple_call_set_lhs (call, retval);
+               {
+                 tree restype;
+                 restype = TREE_TYPE (DECL_RESULT (current_function_decl));
+                 gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+                 if (!useless_type_conversion_p (TREE_TYPE (retval), restype))
+                   {
+                     gimple cpy;
+                     tree tem = create_tmp_reg (restype, NULL);
+                     tem = make_ssa_name (tem, call);
+                     cpy = gimple_build_assign_with_ops (NOP_EXPR, retval,
+                                                         tem, NULL_TREE);
+                     gsi_insert_after (&gsi, cpy, GSI_NEW_STMT);
+                     retval = tem;
+                   }
+                 gimple_call_set_lhs (call, retval);
+                 update_stmt (call);
+               }
            }
-          gsi_insert_after (&gsi, call, GSI_NEW_STMT);
+         else
+           gsi_insert_after (&gsi, call, GSI_NEW_STMT);
        }
       /* 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.
@@ -1206,7 +1287,7 @@ split_function (struct split_point *split_point)
     }
   free_dominance_info (CDI_DOMINATORS);
   free_dominance_info (CDI_POST_DOMINATORS);
-  compute_inline_parameters (node);
+  compute_inline_parameters (node, true);
 }
 
 /* Execute function splitting pass.  */
@@ -1218,7 +1299,7 @@ execute_split_functions (void)
   basic_block bb;
   int overall_time = 0, overall_size = 0;
   int todo = 0;
-  struct cgraph_node *node = cgraph_node (current_function_decl);
+  struct cgraph_node *node = cgraph_get_node (current_function_decl);
 
   if (flags_from_decl_or_type (current_function_decl) & ECF_NORETURN)
     {
@@ -1234,16 +1315,16 @@ execute_split_functions (void)
     }
   /* This can be relaxed; function might become inlinable after splitting
      away the uninlinable part.  */
-  if (!node->local.inlinable)
+  if (!inline_summary (node)->inlinable)
     {
       if (dump_file)
        fprintf (dump_file, "Not splitting: not inlinable.\n");
       return 0;
     }
-  if (node->local.disregard_inline_limits)
+  if (DECL_DISREGARD_INLINE_LIMITS (node->decl))
     {
       if (dump_file)
-       fprintf (dump_file, "Not splitting: disregading inline limits.\n");
+       fprintf (dump_file, "Not splitting: disregarding inline limits.\n");
       return 0;
     }
   /* This can be relaxed; most of versioning tests actually prevents
@@ -1364,7 +1445,7 @@ struct gimple_opt_pass pass_split_functions =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func                       /* todo_flags_finish */
+  TODO_verify_all                              /* todo_flags_finish */
  }
 };
 
@@ -1405,6 +1486,6 @@ struct gimple_opt_pass pass_feedback_split_functions =
   0,                                   /* properties_provided */
   0,                                   /* properties_destroyed */
   0,                                   /* todo_flags_start */
-  TODO_dump_func                       /* todo_flags_finish */
+  TODO_verify_all                              /* todo_flags_finish */
  }
 };