OSDN Git Service

2010-11-24 Tobias Burnus <burnus@net-b.de>
[pf3gnuchains/gcc-fork.git] / gcc / ipa-inline.c
index 21e0b64..e8c78f9 100644 (file)
@@ -250,6 +250,10 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
       /* We may eliminate the need for out-of-line copy to be output.
         In that case just go ahead and re-use it.  */
       if (!e->callee->callers->next_caller
+         /* FIXME: When address is taken of DECL_EXTERNAL function we still can remove its
+            offline copy, but we would need to keep unanalyzed node in the callgraph so
+            references can point to it.  */
+         && !e->callee->address_taken
          && cgraph_can_remove_if_no_direct_calls_p (e->callee)
          /* Inlining might enable more devirtualizing, so we want to remove
             those only after all devirtualizable virtual calls are processed.
@@ -264,7 +268,7 @@ cgraph_clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
          && !cgraph_new_nodes)
        {
          gcc_assert (!e->callee->global.inlined_to);
-         if (e->callee->analyzed)
+         if (e->callee->analyzed && !DECL_EXTERNAL (e->callee->decl))
            {
              overall_size -= e->callee->global.size;
              nfunctions_inlined++;
@@ -400,6 +404,12 @@ cgraph_estimate_growth (struct cgraph_node *node)
   if (cgraph_will_be_removed_from_program_if_no_direct_calls (node)
       && !DECL_EXTERNAL (node->decl) && !self_recursive)
     growth -= node->global.size;
+  /* COMDAT functions are very often not shared across multiple units since they
+     come from various template instantiations.  Take this into account.  */
+  else  if (DECL_COMDAT (node->decl) && !self_recursive
+           && cgraph_can_remove_if_no_direct_calls_p (node))
+    growth -= (node->global.size
+              * (100 - PARAM_VALUE (PARAM_COMDAT_SHARING_PROBABILITY)) + 50) / 100;
 
   node->global.estimated_growth = growth;
   return growth;
@@ -1031,6 +1041,7 @@ add_new_edges_to_heap (fibheap_t heap, VEC (cgraph_edge_p, heap) *new_edges)
 
       gcc_assert (!edge->aux);
       if (edge->callee->local.inlinable
+         && edge->inline_failed
          && cgraph_default_inline_p (edge->callee, &edge->inline_failed))
         edge->aux = fibheap_insert (heap, cgraph_edge_badness (edge, false), edge);
     }
@@ -1402,6 +1413,14 @@ cgraph_flatten (struct cgraph_node *node)
          continue;
        }
 
+      if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
+         != gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
+       {
+         if (dump_file)
+           fprintf (dump_file, "Not inlining: SSA form does not match.\n");
+         continue;
+       }
+
       /* Inline the edge and flatten the inline clone.  Avoid
          recursing through the original node if the node was cloned.  */
       if (dump_file)
@@ -1448,7 +1467,8 @@ cgraph_decide_inlining (void)
        struct cgraph_edge *e;
 
        gcc_assert (inline_summary (node)->self_size == node->global.size);
-       initial_size += node->global.size;
+       if (!DECL_EXTERNAL (node->decl))
+         initial_size += node->global.size;
        for (e = node->callees; e; e = e->next_callee)
          if (max_count < e->count)
            max_count = e->count;
@@ -1511,6 +1531,7 @@ cgraph_decide_inlining (void)
 
          if (node->callers
              && !node->callers->next_caller
+             && !node->global.inlined_to
              && cgraph_will_be_removed_from_program_if_no_direct_calls (node)
              && node->local.inlinable
              && cgraph_function_body_availability (node) >= AVAIL_AVAILABLE
@@ -1571,16 +1592,15 @@ cgraph_decide_inlining (void)
   return 0;
 }
 
-/* Return true when N is leaf function.  Accept cheap (pure&const) builtins
+/* Return true when N is leaf function.  Accept cheap builtins
    in leaf functions.  */
+
 static bool
 leaf_node_p (struct cgraph_node *n)
 {
   struct cgraph_edge *e;
   for (e = n->callees; e; e = e->next_callee)
-    if (!DECL_BUILT_IN (e->callee->decl)
-       || (!TREE_READONLY (e->callee->decl)
-           || DECL_PURE_P (e->callee->decl)))
+    if (!is_inexpensive_builtin (e->callee->decl))
       return false;
   return true;
 }
@@ -1840,55 +1860,30 @@ struct gimple_opt_pass pass_early_inline =
  }
 };
 
-/* When inlining shall be performed.  */
-static bool
-cgraph_gate_ipa_early_inlining (void)
-{
-  return (flag_early_inlining
-         && !in_lto_p
-         && (flag_branch_probabilities || flag_test_coverage
-             || profile_arc_flag));
-}
 
-/* IPA pass wrapper for early inlining pass.  We need to run early inlining
-   before tree profiling so we have stand alone IPA pass for doing so.  */
-struct simple_ipa_opt_pass pass_ipa_early_inline =
-{
- {
-  SIMPLE_IPA_PASS,
-  "einline_ipa",                       /* name */
-  cgraph_gate_ipa_early_inlining,      /* gate */
-  NULL,                                        /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_INLINE_HEURISTICS,                        /* tv_id */
-  0,                                   /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_dump_cgraph                     /* todo_flags_finish */
- }
-};
+/* See if statement might disappear after inlining.
+   0 - means not eliminated
+   1 - half of statements goes away
+   2 - for sure it is eliminated.
+   We are not terribly sophisficated, basically looking for simple abstraction
+   penalty wrappers.  */
 
-/* See if statement might disappear after inlining.  We are not terribly
-   sophisficated, basically looking for simple abstraction penalty wrappers.  */
-
-static bool
-likely_eliminated_by_inlining_p (gimple stmt)
+static int
+eliminated_by_inlining_prob (gimple stmt)
 {
   enum gimple_code code = gimple_code (stmt);
   switch (code)
     {
       case GIMPLE_RETURN:
-        return true;
+        return 2;
       case GIMPLE_ASSIGN:
        if (gimple_num_ops (stmt) != 2)
-         return false;
+         return 0;
 
        /* Casts of parameters, loads from parameters passed by reference
-          and stores to return value or parameters are probably free after
-          inlining.  */
+          and stores to return value or parameters are often free after
+          inlining dua to SRA and further combining.
+          Assume that half of statements goes away.  */
        if (gimple_assign_rhs_code (stmt) == CONVERT_EXPR
            || gimple_assign_rhs_code (stmt) == NOP_EXPR
            || gimple_assign_rhs_code (stmt) == VIEW_CONVERT_EXPR
@@ -1930,11 +1925,11 @@ likely_eliminated_by_inlining_p (gimple stmt)
                && (is_gimple_reg (rhs) || is_gimple_min_invariant (rhs)))
              rhs_free = true;
            if (lhs_free && rhs_free)
-             return true;
+             return 1;
          }
-       return false;
+       return 0;
       default:
-       return false;
+       return 0;
     }
 }
 
@@ -1945,8 +1940,11 @@ estimate_function_body_sizes (struct cgraph_node *node)
 {
   gcov_type time = 0;
   gcov_type time_inlining_benefit = 0;
-  int size = 0;
-  int size_inlining_benefit = 0;
+  /* Estimate static overhead for function prologue/epilogue and alignment. */
+  int size = 2;
+  /* Benefits are scaled by probability of elimination that is in range
+     <0,2>.  */
+  int size_inlining_benefit = 2 * 2;
   basic_block bb;
   gimple_stmt_iterator bsi;
   struct function *my_function = DECL_STRUCT_FUNCTION (node->decl);
@@ -1967,6 +1965,7 @@ estimate_function_body_sizes (struct cgraph_node *node)
          gimple stmt = gsi_stmt (bsi);
          int this_size = estimate_num_insns (stmt, &eni_size_weights);
          int this_time = estimate_num_insns (stmt, &eni_time_weights);
+         int prob;
 
          if (dump_file && (dump_flags & TDF_DETAILS))
            {
@@ -1977,20 +1976,21 @@ estimate_function_body_sizes (struct cgraph_node *node)
          this_time *= freq;
          time += this_time;
          size += this_size;
-         if (likely_eliminated_by_inlining_p (stmt))
-           {
-             size_inlining_benefit += this_size;
-             time_inlining_benefit += this_time;
-             if (dump_file && (dump_flags & TDF_DETAILS))
-               fprintf (dump_file, "    Likely eliminated\n");
-           }
+         prob = eliminated_by_inlining_prob (stmt);
+         if (prob == 1 && dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "    50%% will be eliminated by inlining\n");
+         if (prob == 2 && dump_file && (dump_flags & TDF_DETAILS))
+           fprintf (dump_file, "    will eliminated by inlining\n");
+         size_inlining_benefit += this_size * prob;
+         time_inlining_benefit += this_time * prob;
          gcc_assert (time >= 0);
          gcc_assert (size >= 0);
        }
     }
   time = (time + CGRAPH_FREQ_BASE / 2) / CGRAPH_FREQ_BASE;
-  time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE / 2)
-                          / CGRAPH_FREQ_BASE);
+  time_inlining_benefit = ((time_inlining_benefit + CGRAPH_FREQ_BASE)
+                          / (CGRAPH_FREQ_BASE * 2));
+  size_inlining_benefit = (size_inlining_benefit + 1) / 2;
   if (dump_file)
     fprintf (dump_file, "Overall function body time: %i-%i size: %i-%i\n",
             (int)time, (int)time_inlining_benefit,