aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2013-08-13 14:21:16 +0200
committerJan Hubicka <hubicka@gcc.gnu.org>2013-08-13 12:21:16 +0000
commit09ce36608d268b1a3124411a3ad2f701e5cf24ec (patch)
treee903dc537f27c5dd690cb3a98029321225e8f7b4 /gcc
parent537e035c48955b3ea88dcb3a4b0ff03ef4bb2e31 (diff)
downloadgcc-09ce36608d268b1a3124411a3ad2f701e5cf24ec.zip
gcc-09ce36608d268b1a3124411a3ad2f701e5cf24ec.tar.gz
gcc-09ce36608d268b1a3124411a3ad2f701e5cf24ec.tar.bz2
cgraph.c (cgraph_turn_edge_to_speculative): Return newly introduced edge; fix typo in sanity check.
* 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. From-SVN: r201683
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog23
-rw-r--r--gcc/cgraph.c48
-rw-r--r--gcc/cgraph.h15
-rw-r--r--gcc/ipa-inline-transform.c18
-rw-r--r--gcc/ipa-inline.c255
-rw-r--r--gcc/ipa-inline.h1
-rw-r--r--gcc/ipa.c156
-rw-r--r--gcc/value-prof.c36
8 files changed, 401 insertions, 151 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 0e8e6e6..508a14a 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,26 @@
+2013-08-13 Jan Hubicka <jh@suse.cz>
+
+ * 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.
+
2013-08-13 David Malcolm <dmalcolm@redhat.com>
* config/i386/t-i386 (i386.o): Rename stray PIPELINE_H to
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 50d13ab..a939ae8 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1040,9 +1040,11 @@ cgraph_set_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
At this time the function just creates the direct call,
the referencd representing the if conditional and attaches
- them all to the orginal indirect call statement. */
+ them all to the orginal indirect call statement.
-void
+ Return direct edge created. */
+
+struct cgraph_edge *
cgraph_turn_edge_to_speculative (struct cgraph_edge *e,
struct cgraph_node *n2,
gcov_type direct_count,
@@ -1073,6 +1075,7 @@ cgraph_turn_edge_to_speculative (struct cgraph_edge *e,
IPA_REF_ADDR, e->call_stmt);
ref->lto_stmt_uid = e->lto_stmt_uid;
ref->speculative = e->speculative;
+ return e2;
}
/* Speculative call consist of three components:
@@ -1107,7 +1110,7 @@ cgraph_speculative_call_info (struct cgraph_edge *e,
if (e2->call_stmt)
{
e = cgraph_edge (e->caller, e2->call_stmt);
- gcc_assert (!e->speculative && !e->indirect_unknown_callee);
+ gcc_assert (e->speculative && !e->indirect_unknown_callee);
}
else
for (e = e->caller->callees;
@@ -1147,7 +1150,7 @@ cgraph_redirect_edge_callee (struct cgraph_edge *e, struct cgraph_node *n)
Remove the speculative call sequence and return edge representing the call.
It is up to caller to redirect the call as appropriate. */
-static struct cgraph_edge *
+struct cgraph_edge *
cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl)
{
struct cgraph_edge *e2;
@@ -1159,12 +1162,21 @@ cgraph_resolve_speculation (struct cgraph_edge *edge, tree callee_decl)
{
if (dump_file)
{
- fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has "
- "turned out to have contradicitng known target ",
- xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
- xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
- print_generic_expr (dump_file, callee_decl, 0);
- fprintf (dump_file, "\n");
+ if (callee_decl)
+ {
+ fprintf (dump_file, "Speculative indirect call %s/%i => %s/%i has "
+ "turned out to have contradicting known target ",
+ xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
+ xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
+ print_generic_expr (dump_file, callee_decl, 0);
+ fprintf (dump_file, "\n");
+ }
+ else
+ {
+ fprintf (dump_file, "Removing speculative call %s/%i => %s/%i\n",
+ xstrdup (cgraph_node_name (edge->caller)), edge->caller->symbol.order,
+ xstrdup (cgraph_node_name (e2->callee)), e2->callee->symbol.order);
+ }
}
}
else
@@ -1264,12 +1276,24 @@ cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *e)
cgraph_speculative_call_info (e, e, e2, ref);
if (gimple_call_fndecl (e->call_stmt))
e = cgraph_resolve_speculation (e, gimple_call_fndecl (e->call_stmt));
- else
+ if (!gimple_check_call_matching_types (e->call_stmt, e->callee->symbol.decl,
+ true))
{
+ e = cgraph_resolve_speculation (e, NULL);
if (dump_file)
- fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i\n",
+ fprintf (dump_file, "Not expanding speculative call of %s/%i -> %s/%i\n"
+ "Type mismatch.\n",
xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order,
xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order);
+ }
+ else
+ {
+ if (dump_file)
+ fprintf (dump_file, "Expanding speculative call of %s/%i -> %s/%i count:"
+ HOST_WIDEST_INT_PRINT_DEC"\n",
+ xstrdup (cgraph_node_name (e->caller)), e->caller->symbol.order,
+ xstrdup (cgraph_node_name (e->callee)), e->callee->symbol.order,
+ (HOST_WIDEST_INT)e->count);
gcc_assert (e2->speculative);
push_cfun (DECL_STRUCT_FUNCTION (e->caller->symbol.decl));
new_stmt = gimple_ic (e->call_stmt, cgraph (ref->referred),
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index e430533..6c25bf2 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -726,7 +726,7 @@ bool cgraph_propagate_frequency (struct cgraph_node *node);
struct cgraph_node * cgraph_function_node (struct cgraph_node *,
enum availability *avail = NULL);
bool cgraph_get_body (struct cgraph_node *node);
-void
+struct cgraph_edge *
cgraph_turn_edge_to_speculative (struct cgraph_edge *,
struct cgraph_node *,
gcov_type, int);
@@ -783,6 +783,7 @@ struct cgraph_node *cgraph_function_versioning (struct cgraph_node *,
basic_block, const char *);
void tree_function_versioning (tree, tree, vec<ipa_replace_map_p, va_gc> *,
bool, bitmap, bool, bitmap, basic_block);
+struct cgraph_edge *cgraph_resolve_speculation (struct cgraph_edge *, tree);
/* In cgraphbuild.c */
unsigned int rebuild_cgraph_edges (void);
@@ -1398,4 +1399,16 @@ symtab_real_symbol_p (symtab_node node)
return false;
return true;
}
+
+/* Return true if NODE can be discarded by linker from the binary. */
+
+static inline bool
+symtab_can_be_discarded (symtab_node node)
+{
+ return (DECL_EXTERNAL (node->symbol.decl)
+ || (DECL_ONE_ONLY (node->symbol.decl)
+ && node->symbol.resolution != LDPR_PREVAILING_DEF
+ && node->symbol.resolution != LDPR_PREVAILING_DEF_IRONLY
+ && node->symbol.resolution != LDPR_PREVAILING_DEF_IRONLY_EXP));
+}
#endif /* GCC_CGRAPH_H */
diff --git a/gcc/ipa-inline-transform.c b/gcc/ipa-inline-transform.c
index 54b113a..8ead336 100644
--- a/gcc/ipa-inline-transform.c
+++ b/gcc/ipa-inline-transform.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3. If not see
int ncalls_inlined;
int nfunctions_inlined;
+bool speculation_removed;
/* Scale frequency of NODE edges by FREQ_SCALE. */
@@ -134,6 +135,7 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
bool update_original, int *overall_size)
{
struct cgraph_node *inlining_into;
+ struct cgraph_edge *next;
if (e->caller->global.inlined_to)
inlining_into = e->caller->global.inlined_to;
@@ -186,9 +188,17 @@ clone_inlined_nodes (struct cgraph_edge *e, bool duplicate,
e->callee->global.inlined_to = inlining_into;
/* Recursively clone all bodies. */
- for (e = e->callee->callees; e; e = e->next_callee)
- if (!e->inline_failed)
- clone_inlined_nodes (e, duplicate, update_original, overall_size);
+ for (e = e->callee->callees; e; e = next)
+ {
+ next = e->next_callee;
+ if (!e->inline_failed)
+ clone_inlined_nodes (e, duplicate, update_original, overall_size);
+ if (e->speculative && !speculation_useful_p (e, true))
+ {
+ cgraph_resolve_speculation (e, NULL);
+ speculation_removed = true;
+ }
+ }
}
@@ -218,6 +228,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
bool predicated = inline_edge_summary (e)->predicate != NULL;
#endif
+ speculation_removed = false;
/* Don't inline inlined edges. */
gcc_assert (e->inline_failed);
/* Don't even think of inlining inline clone. */
@@ -267,6 +278,7 @@ inline_call (struct cgraph_edge *e, bool update_original,
error due to INLINE_SIZE_SCALE roudoff errors. */
gcc_assert (!update_overall_summary || !overall_size || new_edges_found
|| abs (estimated_growth - (new_size - old_size)) <= 1
+ || speculation_removed
/* FIXME: a hack. Edges with false predicate are accounted
wrong, we should remove them from callgraph. */
|| predicated);
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 033b314..a9eb1ad 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;
}
}
}
diff --git a/gcc/ipa-inline.h b/gcc/ipa-inline.h
index b34cb52..000d1ab 100644
--- a/gcc/ipa-inline.h
+++ b/gcc/ipa-inline.h
@@ -226,6 +226,7 @@ inline_hints do_estimate_edge_hints (struct cgraph_edge *edge);
void initialize_growth_caches (void);
void free_growth_caches (void);
void compute_inline_parameters (struct cgraph_node *, bool);
+bool speculation_useful_p (struct cgraph_edge *e, bool anticipate_inlining);
/* In ipa-inline-transform.c */
bool inline_call (struct cgraph_edge *, bool, vec<cgraph_edge_p> *, int *, bool);
diff --git a/gcc/ipa.c b/gcc/ipa.c
index c870a6f..1578aed 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -768,11 +768,7 @@ bool
can_replace_by_local_alias (symtab_node node)
{
return (symtab_node_availability (node) > AVAIL_OVERWRITABLE
- && !DECL_EXTERNAL (node->symbol.decl)
- && (!DECL_ONE_ONLY (node->symbol.decl)
- || node->symbol.resolution == LDPR_PREVAILING_DEF
- || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY
- || node->symbol.resolution == LDPR_PREVAILING_DEF_IRONLY_EXP));
+ && !symtab_can_be_discarded (node));
}
/* Mark visibility of all functions.
@@ -1407,53 +1403,9 @@ ipa_profile (void)
bool something_changed = false;
int i;
gcov_type overall_time = 0, cutoff = 0, cumulated = 0, overall_size = 0;
-
- /* Produce speculative calls: we saved common traget from porfiling into
- e->common_target_id. Now, at link time, we can look up corresponding
- function node and produce speculative call. */
- if (in_lto_p)
- {
- struct cgraph_edge *e;
- struct cgraph_node *n,*n2;
-
- init_node_map (false);
- FOR_EACH_DEFINED_FUNCTION (n)
- {
- bool update = false;
-
- for (e = n->indirect_calls; e; e = e->next_callee)
- if (e->indirect_info->common_target_id)
- {
- n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
- if (n2)
- {
- if (dump_file)
- {
- fprintf (dump_file, "Indirect call -> direct call from"
- " other module %s/%i => %s/%i, prob %3.2f\n",
- xstrdup (cgraph_node_name (n)), n->symbol.order,
- xstrdup (cgraph_node_name (n2)), n2->symbol.order,
- e->indirect_info->common_target_probability
- / (float)REG_BR_PROB_BASE);
- }
- cgraph_turn_edge_to_speculative
- (e, n2,
- apply_scale (e->count,
- e->indirect_info->common_target_probability),
- apply_scale (e->frequency,
- e->indirect_info->common_target_probability));
- update = true;
- }
- else
- if (dump_file)
- fprintf (dump_file, "Function with profile-id %i not found.\n",
- e->indirect_info->common_target_id);
- }
- if (update)
- inline_update_overall_summary (n);
- }
- del_node_map ();
- }
+ struct cgraph_node *n,*n2;
+ int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
+ bool node_map_initialized = false;
if (dump_file)
dump_histogram (dump_file, histogram);
@@ -1523,6 +1475,106 @@ ipa_profile (void)
histogram.release();
free_alloc_pool (histogram_pool);
+ /* Produce speculative calls: we saved common traget from porfiling into
+ e->common_target_id. Now, at link time, we can look up corresponding
+ function node and produce speculative call. */
+
+ FOR_EACH_DEFINED_FUNCTION (n)
+ {
+ bool update = false;
+
+ for (e = n->indirect_calls; e; e = e->next_callee)
+ {
+ if (n->count)
+ nindirect++;
+ if (e->indirect_info->common_target_id)
+ {
+ if (!node_map_initialized)
+ init_node_map (false);
+ node_map_initialized = true;
+ ncommon++;
+ n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+ if (n2)
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "Indirect call -> direct call from"
+ " other module %s/%i => %s/%i, prob %3.2f\n",
+ xstrdup (cgraph_node_name (n)), n->symbol.order,
+ xstrdup (cgraph_node_name (n2)), n2->symbol.order,
+ e->indirect_info->common_target_probability
+ / (float)REG_BR_PROB_BASE);
+ }
+ if (e->indirect_info->common_target_probability
+ < REG_BR_PROB_BASE / 2)
+ {
+ nuseless++;
+ if (dump_file)
+ fprintf (dump_file,
+ "Not speculating: probability is too low.\n");
+ }
+ else if (!cgraph_maybe_hot_edge_p (e))
+ {
+ nuseless++;
+ if (dump_file)
+ fprintf (dump_file,
+ "Not speculating: call is cold.\n");
+ }
+ else if (cgraph_function_body_availability (n2)
+ <= AVAIL_OVERWRITABLE
+ && symtab_can_be_discarded ((symtab_node) n2))
+ {
+ nuseless++;
+ if (dump_file)
+ fprintf (dump_file,
+ "Not speculating: target is overwritable "
+ "and can be discarded.\n");
+ }
+ else
+ {
+ /* Target may be overwritable, but profile says that
+ control flow goes to this particular implementation
+ of N2. Speculate on the local alias to allow inlining.
+ */
+ if (!symtab_can_be_discarded ((symtab_node) n2))
+ n2 = cgraph (symtab_nonoverwritable_alias ((symtab_node)n2));
+ nconverted++;
+ cgraph_turn_edge_to_speculative
+ (e, n2,
+ apply_scale (e->count,
+ e->indirect_info->common_target_probability),
+ apply_scale (e->frequency,
+ e->indirect_info->common_target_probability));
+ update = true;
+ }
+ }
+ else
+ {
+ if (dump_file)
+ fprintf (dump_file, "Function with profile-id %i not found.\n",
+ e->indirect_info->common_target_id);
+ nunknown++;
+ }
+ }
+ }
+ if (update)
+ inline_update_overall_summary (n);
+ }
+ if (node_map_initialized)
+ del_node_map ();
+ if (dump_file && nindirect)
+ fprintf (dump_file,
+ "%i indirect calls trained.\n"
+ "%i (%3.2f%%) have common target.\n"
+ "%i (%3.2f%%) targets was not found.\n"
+ "%i (%3.2f%%) speculations seems useless.\n"
+ "%i (%3.2f%%) speculations produced.\n",
+ nindirect,
+ ncommon, ncommon * 100.0 / nindirect,
+ nunknown, nunknown * 100.0 / nindirect,
+ nuseless, nuseless * 100.0 / nindirect,
+ nconverted, nconverted * 100.0 / nindirect);
+
order_pos = ipa_reverse_postorder (order);
for (i = order_pos - 1; i >= 0; i--)
{
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index f110277..8aa9fcd 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1431,8 +1431,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
gimple stmt = gsi_stmt (*gsi);
histogram_value histogram;
gcov_type val, count, all, bb_all;
- gcov_type prob;
- gimple modify;
struct cgraph_node *direct_call;
if (gimple_code (stmt) != GIMPLE_CALL)
@@ -1452,12 +1450,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
count = histogram->hvalue.counters [1];
all = histogram->hvalue.counters [2];
- if (4 * count <= 3 * all)
- {
- gimple_remove_histogram_value (cfun, stmt, histogram);
- return false;
- }
-
bb_all = gimple_bb (stmt)->count;
/* The order of CHECK_COUNTER calls is important -
since check_counter can correct the third parameter
@@ -1469,10 +1461,9 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
return false;
}
- if (all > 0)
- prob = GCOV_COMPUTE_SCALE (count, all);
- else
- prob = 0;
+ if (4 * count <= 3 * all)
+ return false;
+
direct_call = find_func_by_profile_id ((int)val);
if (direct_call == NULL)
@@ -1488,12 +1479,21 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
}
return false;
}
- gimple_remove_histogram_value (cfun, stmt, histogram);
if (!check_ic_target (stmt, direct_call))
- return false;
-
- modify = gimple_ic (stmt, direct_call, prob, count, all);
+ {
+ if (dump_file)
+ {
+ fprintf (dump_file, "Indirect call -> direct call ");
+ print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
+ fprintf (dump_file, "=> ");
+ print_generic_expr (dump_file, direct_call->symbol.decl, TDF_SLIM);
+ fprintf (dump_file, " transformation skipped because of type mismatch");
+ print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+ }
+ gimple_remove_histogram_value (cfun, stmt, histogram);
+ return false;
+ }
if (dump_file)
{
@@ -1501,10 +1501,8 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
fprintf (dump_file, "=> ");
print_generic_expr (dump_file, direct_call->symbol.decl, TDF_SLIM);
- fprintf (dump_file, " transformation on insn ");
+ fprintf (dump_file, " transformation on insn postponned to ipa-profile");
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
- fprintf (dump_file, " to ");
- print_gimple_stmt (dump_file, modify, 0, TDF_SLIM);
fprintf (dump_file, "hist->count "HOST_WIDEST_INT_PRINT_DEC
" hist->all "HOST_WIDEST_INT_PRINT_DEC"\n", count, all);
}