re PR lto/45375 ([meta-bug] Issues with building Mozilla (i.e. Firefox) with LTO)
authorJan Hubicka <hubicka@ucw.cz>
Tue, 20 Jan 2015 19:48:59 +0000 (20:48 +0100)
committerJan Hubicka <hubicka@gcc.gnu.org>
Tue, 20 Jan 2015 19:48:59 +0000 (19:48 +0000)
PR lto/45375
* ipa-inline.c: Include lto-streamer.h
(report_inline_failed_reason): Output source file differences and
flags on optimization/target node mismatch.
(can_inline_edge_p): Consider caller to be the outer inline function;
be less restrictive about matching opimize and optimize_size attributes.
(inline_account_function_p): Break out from ...
(inline_small_functions): ... here.
* ipa-inline-transform.c (clone_inlined_nodes): Use
inline_account_function_p.
(inline_call): Use optimize attribution; use inline_account_function_p.
(inline_transform): Use opt_for_fn.
* ipa-inline.h (inline_account_function_p): Declare.

From-SVN: r219909

gcc/ChangeLog
gcc/ipa-inline-transform.c
gcc/ipa-inline.c
gcc/ipa-inline.h

index 340f15d4aaecb083a44b3a857117bd2c5f3faa71..7b063f3cce2e10a71e2796e017f49dba05e0a273 100644 (file)
@@ -1,3 +1,19 @@
+2015-01-19  Jan Hubicka  <hubicka@ucw.cz>
+
+       PR lto/45375
+       * ipa-inline.c: Include lto-streamer.h
+       (report_inline_failed_reason): Output source file differences and
+       flags on optimization/target node mismatch.
+       (can_inline_edge_p): Consider caller to be the outer inline function;
+       be less restrictive about matching opimize and optimize_size attributes.
+       (inline_account_function_p): Break out from ...
+       (inline_small_functions): ... here.
+       * ipa-inline-transform.c (clone_inlined_nodes): Use
+       inline_account_function_p.
+       (inline_call): Use optimize attribution; use inline_account_function_p.
+       (inline_transform): Use opt_for_fn.
+       * ipa-inline.h (inline_account_function_p): Declare.
+
 2015-01-20  Jakub Jelinek  <jakub@redhat.com>
 
        PR debug/64663
index 1c4b23a1ebc198deac4ccf35205dc182cde345d0..235219dd82e4bd49d5d7ff38f4fc23f81bb6ff7a 100644 (file)
@@ -214,8 +214,10 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
             cgraph_remove_unreachable_functions gets rid of them.  */
          gcc_assert (!e->callee->global.inlined_to);
          e->callee->dissolve_same_comdat_group_list ();
-         if (e->callee->definition && !DECL_EXTERNAL (e->callee->decl))
+         if (e->callee->definition
+             && inline_account_function_p (e->callee))
            {
+             gcc_assert (!e->callee->alias);
              if (overall_size)
                *overall_size -= inline_summaries->get (e->callee)->size;
              nfunctions_inlined++;
@@ -330,7 +332,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
 
   old_size = inline_summaries->get (to)->size;
   inline_merge_summary (e);
-  if (optimize)
+  if (opt_for_fn (e->caller->decl, optimize))
     new_edges_found = ipa_propagate_indirect_call_infos (curr, new_edges);
   if (update_overall_summary)
    inline_update_overall_summary (to);
@@ -361,8 +363,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
 
   /* Account the change of overall unit size; external functions will be
      removed and are thus not accounted.  */
-  if (overall_size
-      && !DECL_EXTERNAL (to->decl))
+  if (overall_size && inline_account_function_p (to))
     *overall_size += new_size - old_size;
   ncalls_inlined++;
 
@@ -509,7 +510,7 @@ inline_transform (struct cgraph_node *node)
   node->remove_all_references ();
 
   timevar_push (TV_INTEGRATION);
-  if (node->callees && (optimize || has_inline))
+  if (node->callees && (opt_for_fn (node->decl, optimize) || has_inline))
     todo = optimize_inline_calls (current_function_decl);
   timevar_pop (TV_INTEGRATION);
 
index 6bfb1ea2aa48c8f29623ecdb8d2b97c0d2fb9277..c0ff329ef0974e86a6da370de3041bbc48593e6d 100644 (file)
@@ -145,6 +145,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cilk.h"
 #include "builtins.h"
 #include "fibonacci_heap.h"
+#include "lto-streamer.h"
 
 typedef fibonacci_heap <sreal, cgraph_edge> edge_heap_t;
 typedef fibonacci_node <sreal, cgraph_edge> edge_heap_node_t;
@@ -260,6 +261,23 @@ report_inline_failed_reason (struct cgraph_edge *e)
               xstrdup_for_dump (e->caller->name ()), e->caller->order,
               xstrdup_for_dump (e->callee->name ()), e->callee->order,
               cgraph_inline_failed_string (e->inline_failed));
+      if ((e->inline_failed == CIF_TARGET_OPTION_MISMATCH
+          || e->inline_failed == CIF_OPTIMIZATION_MISMATCH)
+         && e->caller->lto_file_data
+         && e->callee->function_symbol ()->lto_file_data)
+       {
+         fprintf (dump_file, "  LTO objects: %s, %s\n",
+                  e->caller->lto_file_data->file_name,
+                  e->callee->function_symbol ()->lto_file_data->file_name);
+       }
+      if (e->inline_failed == CIF_TARGET_OPTION_MISMATCH)
+       cl_target_option_print_diff
+        (dump_file, 2, target_opts_for_fn (e->caller->decl),
+          target_opts_for_fn (e->callee->ultimate_alias_target ()->decl));
+      if (e->inline_failed == CIF_OPTIMIZATION_MISMATCH)
+       cl_optimization_print_diff
+         (dump_file, 2, opts_for_fn (e->caller->decl),
+          opts_for_fn (e->callee->ultimate_alias_target ()->decl));
     }
 }
 
@@ -297,10 +315,12 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
   bool inlinable = true;
   enum availability avail;
   cgraph_node *callee = e->callee->ultimate_alias_target (&avail);
-  tree caller_tree = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (e->caller->decl);
+  cgraph_node *caller = e->caller->global.inlined_to
+                       ? e->caller->global.inlined_to : e->caller;
+  tree caller_tree = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (caller->decl);
   tree callee_tree
     = callee ? DECL_FUNCTION_SPECIFIC_OPTIMIZATION (callee->decl) : NULL;
-  struct function *caller_fun = e->caller->get_fun ();
+  struct function *caller_fun = caller->get_fun ();
   struct function *callee_fun = callee ? callee->get_fun () : NULL;
 
   gcc_assert (e->inline_failed);
@@ -333,9 +353,9 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
       inlinable = false;
     }
   /* Don't inline if the functions have different EH personalities.  */
-  else if (DECL_FUNCTION_PERSONALITY (e->caller->decl)
+  else if (DECL_FUNCTION_PERSONALITY (caller->decl)
           && DECL_FUNCTION_PERSONALITY (callee->decl)
-          && (DECL_FUNCTION_PERSONALITY (e->caller->decl)
+          && (DECL_FUNCTION_PERSONALITY (caller->decl)
               != DECL_FUNCTION_PERSONALITY (callee->decl)))
     {
       e->inline_failed = CIF_EH_PERSONALITY;
@@ -344,7 +364,7 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
   /* TM pure functions should not be inlined into non-TM_pure
      functions.  */
   else if (is_tm_pure (callee->decl)
-          && !is_tm_pure (e->caller->decl))
+          && !is_tm_pure (caller->decl))
     {
       e->inline_failed = CIF_UNSPECIFIED;
       inlinable = false;
@@ -360,14 +380,14 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
       inlinable = false;
     }
   /* Check compatibility of target optimization options.  */
-  else if (!targetm.target_option.can_inline_p (e->caller->decl,
+  else if (!targetm.target_option.can_inline_p (caller->decl,
                                                callee->decl))
     {
       e->inline_failed = CIF_TARGET_OPTION_MISMATCH;
       inlinable = false;
     }
   /* Don't inline a function with mismatched sanitization attributes. */
-  else if (!sanitize_attrs_match_for_inline_p (e->caller->decl, callee->decl))
+  else if (!sanitize_attrs_match_for_inline_p (caller->decl, callee->decl))
     {
       e->inline_failed = CIF_ATTRIBUTE_MISMATCH;
       inlinable = false;
@@ -376,10 +396,7 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
   else if (!DECL_DISREGARD_INLINE_LIMITS (callee->decl)
           && !disregard_limits
           && !lookup_attribute ("flatten",
-                                DECL_ATTRIBUTES
-                                  (e->caller->global.inlined_to
-                                   ? e->caller->global.inlined_to->decl
-                                   : e->caller->decl))
+                                DECL_ATTRIBUTES (caller->decl))
            && !caller_growth_limits (e))
     inlinable = false;
   /* Don't inline a function with a higher optimization level than the
@@ -387,16 +404,62 @@ can_inline_edge_p (struct cgraph_edge *e, bool report,
      optimization attribute.  */
   else if (caller_tree != callee_tree)
     {
-      if (((opt_for_fn (e->caller->decl, optimize)
-           > opt_for_fn (callee->decl, optimize))
-           || (opt_for_fn (e->caller->decl, optimize_size)
-               != opt_for_fn (callee->decl, optimize_size)))
-         /* gcc.dg/pr43564.c.  Look at forced inline even in -O0.  */
-         && !DECL_DISREGARD_INLINE_LIMITS (callee->decl))
+      /* gcc.dg/pr43564.c.  Look at forced inline even in -O0.  */
+      if (DECL_DISREGARD_INLINE_LIMITS (callee->decl))
+       ;
+      /* When user added an attribute, honnor it.  */
+      else if ((lookup_attribute ("optimize", DECL_ATTRIBUTES (caller->decl))
+               || lookup_attribute ("optimize",
+                                    DECL_ATTRIBUTES (callee->decl)))
+              && ((opt_for_fn (caller->decl, optimize)
+                  > opt_for_fn (callee->decl, optimize))
+                  || (opt_for_fn (caller->decl, optimize_size)
+                      != opt_for_fn (callee->decl, optimize_size))))
        {
          e->inline_failed = CIF_OPTIMIZATION_MISMATCH;
          inlinable = false;
        }
+      /* If mismatch is caused by merging two LTO units with different
+        optimizationflags we want to be bit nicer.  However never inline
+        if one of functions is not optimized at all.  */
+      else if (!opt_for_fn (callee->decl, optimize)
+              || !opt_for_fn (caller->decl, optimize))
+       {
+         e->inline_failed = CIF_OPTIMIZATION_MISMATCH;
+         inlinable = false;
+       }
+      /* If callee is optimized for size and caller is not, allow inlining if
+        code shrinks or we are in MAX_INLINE_INSNS_SINGLE limit and callee
+        is inline (and thus likely an unified comdat).  This will allow caller
+        to run faster.  */
+      else if (opt_for_fn (callee->decl, optimize_size)
+              > opt_for_fn (caller->decl, optimize_size))
+       {
+         int growth = estimate_edge_growth (e);
+         if (growth > 0
+             && (!DECL_DECLARED_INLINE_P (callee->decl)
+                 && growth >= MAX (MAX_INLINE_INSNS_SINGLE,
+                                   MAX_INLINE_INSNS_AUTO)))
+           {
+             e->inline_failed = CIF_OPTIMIZATION_MISMATCH;
+             inlinable = false;
+           }
+       }
+      /* If callee is more aggressively optimized for performance than caller,
+        we generally want to inline only cheap (runtime wise) functions.  */
+      else if (opt_for_fn (callee->decl, optimize_size)
+              < opt_for_fn (caller->decl, optimize_size)
+              || (opt_for_fn (callee->decl, optimize)
+                  >= opt_for_fn (caller->decl, optimize)))
+       {
+         if (estimate_edge_time (e)
+             >= 20 + inline_edge_summary (e)->call_stmt_time)
+           {
+             e->inline_failed = CIF_OPTIMIZATION_MISMATCH;
+             inlinable = false;
+           }
+       }
+
     }
 
   if (!inlinable && report)
@@ -1507,6 +1570,18 @@ resolve_noninline_speculation (edge_heap_t *edge_heap, struct cgraph_edge *edge)
     }
 }
 
+/* Return true if NODE should be accounted for overall size estimate.
+   Skip all nodes optimized for size so we can measure the growth of hot
+   part of program no matter of the padding.  */
+
+bool
+inline_account_function_p (struct cgraph_node *node)
+{
+   return (!DECL_EXTERNAL (node->decl)
+          && !opt_for_fn (node->decl, optimize_size)
+          && node->frequency != NODE_FREQUENCY_UNLIKELY_EXECUTED);
+}
+
 /* We use greedy algorithm for inlining of small functions:
    All inline candidates are put into prioritized heap ordered in
    increasing badness.
@@ -1540,17 +1615,15 @@ inline_small_functions (void)
   FOR_EACH_DEFINED_FUNCTION (node)
     if (!node->global.inlined_to)
       {
-       if (node->has_gimple_body_p ()
-           || node->thunk.thunk_p)
+       if (!node->alias && node->analyzed
+           && (node->has_gimple_body_p () || node->thunk.thunk_p))
          {
            struct inline_summary *info = inline_summaries->get (node);
            struct ipa_dfs_info *dfs = (struct ipa_dfs_info *) node->aux;
 
            /* Do not account external functions, they will be optimized out
               if not inlined.  Also only count the non-cold portion of program.  */
-           if (!DECL_EXTERNAL (node->decl)
-               && !opt_for_fn (node->decl, optimize_size)
-               && node->frequency != NODE_FREQUENCY_UNLIKELY_EXECUTED)
+           if (inline_account_function_p (node))
              initial_size += info->size;
            info->growth = estimate_growth (node);
            if (dfs && dfs->next_cycle)
index ea7cb09270985b3479327e8d28ab386d07987107..dfc0053838ef356c2dca0f732c43b4f5336c20d2 100644 (file)
@@ -256,6 +256,8 @@ void free_growth_caches (void);
 void compute_inline_parameters (struct cgraph_node *, bool);
 bool speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining);
 unsigned int early_inliner (function *fun);
+bool inline_account_function_p (struct cgraph_node *node);
+
 
 /* In ipa-inline-transform.c  */
 bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge *> *, int *, bool,