summaryrefslogtreecommitdiff
path: root/gcc/ipa-inline.c
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2013-08-13 12:21:16 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2013-08-13 12:21:16 +0000
commit12d5ae9f36940d21c2d3804a81678b6ebc941d80 (patch)
treee903dc537f27c5dd690cb3a98029321225e8f7b4 /gcc/ipa-inline.c
parent936c99ae35bdc3ab7e01faae57b9570cf0d18781 (diff)
downloadgcc-12d5ae9f36940d21c2d3804a81678b6ebc941d80.tar.gz
* cgraph.c (cgraph_turn_edge_to_speculative): Return newly
introduced edge; fix typo in sanity check. (cgraph_resolve_speculation): Export; improve diagnostic. (cgraph_redirect_edge_call_stmt_to_callee): Better diagnostic; cancel speculation at type mismatch. * cgraph.h (cgraph_turn_edge_to_speculative): Update. (cgraph_resolve_speculation): Declare. (symtab_can_be_discarded): New function. * value-prof.c (gimple_ic_transform): Remove actual transform code. * ipa-inline-transform.c (speculation_removed): New global var. (clone_inlined_nodes): See if speculation can be removed. (inline_call): If speculations was removed, we growths may not match. * ipa-inline.c (can_inline_edge_p): Add DISREGARD_LIMITS parameter. (speculation_useful_p): New function. (resolve_noninline_speculation): New function. (inline_small_functions): Resolve useless speculations. * ipa-inline.h (speculation_useful_p): Declare * ipa.c (can_replace_by_local_alias): Simplify. (ipa_profile): Produce speculative calls in non-lto, too; add simple cost model; produce local aliases. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@201683 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/ipa-inline.c')
-rw-r--r--gcc/ipa-inline.c255
1 files changed, 191 insertions, 64 deletions
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 033b314b34a..a9eb1ad75a6 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -229,10 +229,13 @@ report_inline_failed_reason (struct cgraph_edge *e)
We check whether inlining is possible at all and whether
caller growth limits allow doing so.
- if REPORT is true, output reason to the dump file. */
+ if REPORT is true, output reason to the dump file.
+
+ if DISREGARD_LIMITES is true, ignore size limits.*/
static bool
-can_inline_edge_p (struct cgraph_edge *e, bool report)
+can_inline_edge_p (struct cgraph_edge *e, bool report,
+ bool disregard_limits = false)
{
bool inlinable = true;
enum availability avail;
@@ -309,6 +312,7 @@ can_inline_edge_p (struct cgraph_edge *e, bool report)
}
/* Check if caller growth allows the inlining. */
else if (!DECL_DISREGARD_INLINE_LIMITS (callee->symbol.decl)
+ && !disregard_limits
&& !lookup_attribute ("flatten",
DECL_ATTRIBUTES
(e->caller->global.inlined_to
@@ -1400,6 +1404,79 @@ heap_edge_removal_hook (struct cgraph_edge *e, void *data)
}
}
+/* Return true if speculation of edge E seems useful.
+ If ANTICIPATE_INLINING is true, be conservative and hope that E
+ may get inlined. */
+
+bool
+speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining)
+{
+ enum availability avail;
+ struct cgraph_node *target = cgraph_function_or_thunk_node (e->callee, &avail);
+ struct cgraph_edge *direct, *indirect;
+ struct ipa_ref *ref;
+
+ gcc_assert (e->speculative && !e->indirect_unknown_callee);
+
+ if (!cgraph_maybe_hot_edge_p (e))
+ return false;
+
+ /* See if IP optimizations found something potentially useful about the
+ function. For now we look only for CONST/PURE flags. Almost everything
+ else we propagate is useless. */
+ if (avail >= AVAIL_AVAILABLE)
+ {
+ int ecf_flags = flags_from_decl_or_type (target->symbol.decl);
+ if (ecf_flags & ECF_CONST)
+ {
+ cgraph_speculative_call_info (e, direct, indirect, ref);
+ if (!(indirect->indirect_info->ecf_flags & ECF_CONST))
+ return true;
+ }
+ else if (ecf_flags & ECF_PURE)
+ {
+ cgraph_speculative_call_info (e, direct, indirect, ref);
+ if (!(indirect->indirect_info->ecf_flags & ECF_PURE))
+ return true;
+ }
+ }
+ /* If we did not managed to inline the function nor redirect
+ to an ipa-cp clone (that are seen by having local flag set),
+ it is probably pointless to inline it unless hardware is missing
+ indirect call predictor. */
+ if (!anticipate_inlining && e->inline_failed && !target->local.local)
+ return false;
+ /* For overwritable targets there is not much to do. */
+ if (e->inline_failed && !can_inline_edge_p (e, false, true))
+ return false;
+ /* OK, speculation seems interesting. */
+ return true;
+}
+
+/* We know that EDGE is not going to be inlined.
+ See if we can remove speculation. */
+
+static void
+resolve_noninline_speculation (fibheap_t edge_heap, struct cgraph_edge *edge)
+{
+ if (edge->speculative && !speculation_useful_p (edge, false))
+ {
+ struct cgraph_node *node = edge->caller;
+ struct cgraph_node *where = node->global.inlined_to
+ ? node->global.inlined_to : node;
+ bitmap updated_nodes = BITMAP_ALLOC (NULL);
+
+ cgraph_resolve_speculation (edge, NULL);
+ reset_node_growth_cache (where);
+ reset_edge_caches (where);
+ inline_update_overall_summary (where);
+ update_caller_keys (edge_heap, where,
+ updated_nodes, NULL);
+ reset_node_growth_cache (where);
+ BITMAP_FREE (updated_nodes);
+ }
+}
+
/* We use greedy algorithm for inlining of small functions:
All inline candidates are put into prioritized heap ordered in
increasing badness.
@@ -1478,14 +1555,19 @@ inline_small_functions (void)
/* Populate the heeap with all edges we might inline. */
FOR_EACH_DEFINED_FUNCTION (node)
- if (!node->global.inlined_to)
- {
- if (dump_file)
- fprintf (dump_file, "Enqueueing calls of %s/%i.\n",
- cgraph_node_name (node), node->symbol.order);
+ {
+ bool update = false;
+ struct cgraph_edge *next;
- for (edge = node->callers; edge; edge = edge->next_caller)
+ if (dump_file)
+ fprintf (dump_file, "Enqueueing calls in %s/%i.\n",
+ cgraph_node_name (node), node->symbol.order);
+
+ for (edge = node->callees; edge; edge = next)
+ {
+ next = edge->next_callee;
if (edge->inline_failed
+ && !edge->aux
&& can_inline_edge_p (edge, true)
&& want_inline_small_function_p (edge, true)
&& edge->inline_failed)
@@ -1493,7 +1575,24 @@ inline_small_functions (void)
gcc_assert (!edge->aux);
update_edge_key (edge_heap, edge);
}
- }
+ if (edge->speculative && !speculation_useful_p (edge, edge->aux != NULL))
+ {
+ cgraph_resolve_speculation (edge, NULL);
+ update = true;
+ }
+ }
+ if (update)
+ {
+ struct cgraph_node *where = node->global.inlined_to
+ ? node->global.inlined_to : node;
+ inline_update_overall_summary (where);
+ reset_node_growth_cache (where);
+ reset_edge_caches (where);
+ update_caller_keys (edge_heap, where,
+ updated_nodes, NULL);
+ bitmap_clear (updated_nodes);
+ }
+ }
gcc_assert (in_lto_p
|| !max_count
@@ -1534,7 +1633,10 @@ inline_small_functions (void)
}
if (!can_inline_edge_p (edge, true))
- continue;
+ {
+ resolve_noninline_speculation (edge_heap, edge);
+ continue;
+ }
callee = cgraph_function_or_thunk_node (edge->callee, NULL);
growth = estimate_edge_growth (edge);
@@ -1568,11 +1670,15 @@ inline_small_functions (void)
{
edge->inline_failed = CIF_INLINE_UNIT_GROWTH_LIMIT;
report_inline_failed_reason (edge);
+ resolve_noninline_speculation (edge_heap, edge);
continue;
}
if (!want_inline_small_function_p (edge, true))
- continue;
+ {
+ resolve_noninline_speculation (edge_heap, edge);
+ continue;
+ }
/* Heuristics for inlining small functions works poorly for
recursive calls where we do efect similar to loop unrolling.
@@ -1588,6 +1694,7 @@ inline_small_functions (void)
? &new_indirect_edges : NULL))
{
edge->inline_failed = CIF_RECURSIVE_INLINING;
+ resolve_noninline_speculation (edge_heap, edge);
continue;
}
reset_edge_caches (where);
@@ -1596,6 +1703,7 @@ inline_small_functions (void)
if (flag_indirect_inlining)
add_new_edges_to_heap (edge_heap, new_indirect_edges);
update_callee_keys (edge_heap, where, updated_nodes);
+ bitmap_clear (updated_nodes);
}
else
{
@@ -1621,6 +1729,7 @@ inline_small_functions (void)
edge->inline_failed
= (DECL_DISREGARD_INLINE_LIMITS (edge->callee->symbol.decl)
? CIF_RECURSIVE_INLINING : CIF_UNSPECIFIED);
+ resolve_noninline_speculation (edge_heap, edge);
continue;
}
else if (depth && dump_file)
@@ -1773,6 +1882,7 @@ ipa_inline (void)
struct cgraph_node **order =
XCNEWVEC (struct cgraph_node *, cgraph_n_nodes);
int i;
+ int cold;
if (in_lto_p && optimize)
ipa_update_after_lto_read ();
@@ -1820,66 +1930,83 @@ ipa_inline (void)
code size will shrink because the out-of-line copy is eliminated.
We do this regardless on the callee size as long as function growth limits
are met. */
- if (flag_inline_functions_called_once)
+ if (dump_file)
+ fprintf (dump_file,
+ "\nDeciding on functions to be inlined into all callers and removing useless speculations:\n");
+
+ /* Inlining one function called once has good chance of preventing
+ inlining other function into the same callee. Ideally we should
+ work in priority order, but probably inlining hot functions first
+ is good cut without the extra pain of maintaining the queue.
+
+ ??? this is not really fitting the bill perfectly: inlining function
+ into callee often leads to better optimization of callee due to
+ increased context for optimization.
+ For example if main() function calls a function that outputs help
+ and then function that does the main optmization, we should inline
+ the second with priority even if both calls are cold by themselves.
+
+ We probably want to implement new predicate replacing our use of
+ maybe_hot_edge interpreted as maybe_hot_edge || callee is known
+ to be hot. */
+ for (cold = 0; cold <= 1; cold ++)
{
- int cold;
- if (dump_file)
- fprintf (dump_file,
- "\nDeciding on functions to be inlined into all callers:\n");
-
- /* Inlining one function called once has good chance of preventing
- inlining other function into the same callee. Ideally we should
- work in priority order, but probably inlining hot functions first
- is good cut without the extra pain of maintaining the queue.
-
- ??? this is not really fitting the bill perfectly: inlining function
- into callee often leads to better optimization of callee due to
- increased context for optimization.
- For example if main() function calls a function that outputs help
- and then function that does the main optmization, we should inline
- the second with priority even if both calls are cold by themselves.
-
- We probably want to implement new predicate replacing our use of
- maybe_hot_edge interpreted as maybe_hot_edge || callee is known
- to be hot. */
- for (cold = 0; cold <= 1; cold ++)
+ FOR_EACH_DEFINED_FUNCTION (node)
{
- FOR_EACH_DEFINED_FUNCTION (node)
+ struct cgraph_edge *edge, *next;
+ bool update=false;
+
+ for (edge = node->callees; edge; edge = next)
{
- if (want_inline_function_to_all_callers_p (node, cold))
+ next = edge->next_callee;
+ if (edge->speculative && !speculation_useful_p (edge, false))
{
- int num_calls = 0;
- struct cgraph_edge *e;
- for (e = node->callers; e; e = e->next_caller)
- num_calls++;
- while (node->callers && !node->global.inlined_to)
+ cgraph_resolve_speculation (edge, NULL);
+ update = true;
+ }
+ }
+ if (update)
+ {
+ struct cgraph_node *where = node->global.inlined_to
+ ? node->global.inlined_to : node;
+ reset_node_growth_cache (where);
+ reset_edge_caches (where);
+ inline_update_overall_summary (where);
+ }
+ if (flag_inline_functions_called_once
+ && want_inline_function_to_all_callers_p (node, cold))
+ {
+ int num_calls = 0;
+ struct cgraph_edge *e;
+ for (e = node->callers; e; e = e->next_caller)
+ num_calls++;
+ while (node->callers && !node->global.inlined_to)
+ {
+ struct cgraph_node *caller = node->callers->caller;
+
+ if (dump_file)
{
- struct cgraph_node *caller = node->callers->caller;
+ fprintf (dump_file,
+ "\nInlining %s size %i.\n",
+ cgraph_node_name (node),
+ inline_summary (node)->size);
+ fprintf (dump_file,
+ " Called once from %s %i insns.\n",
+ cgraph_node_name (node->callers->caller),
+ inline_summary (node->callers->caller)->size);
+ }
+ inline_call (node->callers, true, NULL, NULL, true);
+ if (dump_file)
+ fprintf (dump_file,
+ " Inlined into %s which now has %i size\n",
+ cgraph_node_name (caller),
+ inline_summary (caller)->size);
+ if (!num_calls--)
+ {
if (dump_file)
- {
- fprintf (dump_file,
- "\nInlining %s size %i.\n",
- cgraph_node_name (node),
- inline_summary (node)->size);
- fprintf (dump_file,
- " Called once from %s %i insns.\n",
- cgraph_node_name (node->callers->caller),
- inline_summary (node->callers->caller)->size);
- }
-
- inline_call (node->callers, true, NULL, NULL, true);
- if (dump_file)
- fprintf (dump_file,
- " Inlined into %s which now has %i size\n",
- cgraph_node_name (caller),
- inline_summary (caller)->size);
- if (!num_calls--)
- {
- if (dump_file)
- fprintf (dump_file, "New calls found; giving up.\n");
- break;
- }
+ fprintf (dump_file, "New calls found; giving up.\n");
+ break;
}
}
}