re PR lto/45375 ([meta-bug] Issues with building Mozilla (i.e. Firefox) with LTO)

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
This commit is contained in:
Jan Hubicka 2015-01-20 20:48:59 +01:00 committed by Jan Hubicka
parent 17cb42833c
commit bb1e543c64
4 changed files with 119 additions and 27 deletions

View 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

View 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);

View 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)

View 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,