Commit f714ecf5 by Jan Hubicka Committed by Jan Hubicka

cgraph.h (cgraph_node): Add predicate prevailing_p.


	* cgraph.h (cgraph_node): Add predicate prevailing_p.
	(cgraph_edge): Add predicate possible_call_in_translation_unit_p.
	* ipa-prop.c (ipa_write_jump_function): Optimize streaming of ADDR_EXPR.
	(ipa_read_jump_function): Add prevails parameter; optimize streaming.
	(ipa_read_edge_info): Break out from ...
	(ipa_read_node_info): ... here; optimize streaming.
	* cgraph.c (cgraph_edge::possibly_call_in_translation_unit_p): New
	predicate.

From-SVN: r267175
parent 6263c29d
2018-12-15 Jan Hubicka <hubicka@ucw.cz> 2018-12-15 Jan Hubicka <hubicka@ucw.cz>
* cgraph.h (cgraph_node): Add predicate prevailing_p.
(cgraph_edge): Add predicate possible_call_in_translation_unit_p.
* ipa-prop.c (ipa_write_jump_function): Optimize streaming of ADDR_EXPR.
(ipa_read_jump_function): Add prevails parameter; optimize streaming.
(ipa_read_edge_info): Break out from ...
(ipa_read_node_info): ... here; optimize streaming.
* cgraph.c (cgraph_edge::possibly_call_in_translation_unit_p): New
predicate.
2018-12-15 Jan Hubicka <hubicka@ucw.cz>
* ipa-utils.c (ipa_merge_profiles): Do no merging when source function * ipa-utils.c (ipa_merge_profiles): Do no merging when source function
has zero count. has zero count.
...@@ -3766,6 +3766,41 @@ cgraph_edge::sreal_frequency () ...@@ -3766,6 +3766,41 @@ cgraph_edge::sreal_frequency ()
: caller->count); : caller->count);
} }
/* During LTO stream in this can be used to check whether call can possibly
be internal to the current translation unit. */
bool
cgraph_edge::possibly_call_in_translation_unit_p (void)
{
gcc_checking_assert (in_lto_p && caller->prevailing_p ());
/* While incremental linking we may end up getting function body later. */
if (flag_incremental_link == INCREMENTAL_LINK_LTO)
return true;
/* We may be smarter here and avoid stremaing in indirect calls we can't
track, but that would require arranging stremaing the indirect call
summary first. */
if (!callee)
return true;
/* If calle is local to the original translation unit, it will be defined. */
if (!TREE_PUBLIC (callee->decl) && !DECL_EXTERNAL (callee->decl))
return true;
/* Otherwise we need to lookup prevailing symbol (symbol table is not merged,
yet) and see if it is a definition. In fact we may also resolve aliases,
but that is probably not too important. */
symtab_node *node = callee;
for (int n = 10; node->previous_sharing_asm_name && n ; n--)
node = node->previous_sharing_asm_name;
if (node->previous_sharing_asm_name)
node = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (callee->decl));
gcc_assert (TREE_PUBLIC (node->decl));
return node->get_availability () >= AVAIL_AVAILABLE;
}
/* A stashed copy of "symtab" for use by selftest::symbol_table_test. /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
This needs to be a global so that it can be a GC root, and thus This needs to be a global so that it can be a GC root, and thus
prevent the stashed copy from being garbage-collected if the GC runs prevent the stashed copy from being garbage-collected if the GC runs
......
...@@ -308,6 +308,10 @@ public: ...@@ -308,6 +308,10 @@ public:
/* Return availability of NODE when referenced from REF. */ /* Return availability of NODE when referenced from REF. */
enum availability get_availability (symtab_node *ref = NULL); enum availability get_availability (symtab_node *ref = NULL);
/* During LTO stream-in this predicate can be used to check whether node
in question prevails in the linking to save some memory usage. */
bool prevailing_p (void);
/* Return true if NODE binds to current definition in final executable /* Return true if NODE binds to current definition in final executable
when referenced from REF. If REF is NULL return conservative value when referenced from REF. If REF is NULL return conservative value
for any reference. */ for any reference. */
...@@ -1730,6 +1734,10 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"), ...@@ -1730,6 +1734,10 @@ struct GTY((chain_next ("%h.next_caller"), chain_prev ("%h.prev_caller"),
after passes that don't update the cgraph. */ after passes that don't update the cgraph. */
static void rebuild_references (void); static void rebuild_references (void);
/* During LTO stream in this can be used to check whether call can possibly
be internal to the current translation unit. */
bool possibly_call_in_translation_unit_p (void);
/* Expected number of executions: calculated in profile.c. */ /* Expected number of executions: calculated in profile.c. */
profile_count count; profile_count count;
cgraph_node *caller; cgraph_node *caller;
...@@ -3357,6 +3365,15 @@ xstrdup_for_dump (const char *transient_str) ...@@ -3357,6 +3365,15 @@ xstrdup_for_dump (const char *transient_str)
return ggc_strdup (transient_str); return ggc_strdup (transient_str);
} }
/* During LTO stream-in this predicate can be used to check whether node
in question prevails in the linking to save some memory usage. */
inline bool
symtab_node::prevailing_p (void)
{
return definition && ((!TREE_PUBLIC (decl) && !DECL_EXTERNAL (decl))
|| previous_sharing_asm_name == NULL);
}
extern GTY(()) symbol_table *saved_symtab; extern GTY(()) symbol_table *saved_symtab;
#if CHECKING_P #if CHECKING_P
......
...@@ -4053,8 +4053,15 @@ ipa_write_jump_function (struct output_block *ob, ...@@ -4053,8 +4053,15 @@ ipa_write_jump_function (struct output_block *ob,
struct ipa_agg_jf_item *item; struct ipa_agg_jf_item *item;
struct bitpack_d bp; struct bitpack_d bp;
int i, count; int i, count;
int flag = 0;
streamer_write_uhwi (ob, jump_func->type); /* ADDR_EXPRs are very comon IP invariants; save some streamer data
as well as WPA memory by handling them specially. */
if (jump_func->type == IPA_JF_CONST
&& TREE_CODE (jump_func->value.constant.value) == ADDR_EXPR)
flag = 1;
streamer_write_uhwi (ob, jump_func->type * 2 + flag);
switch (jump_func->type) switch (jump_func->type)
{ {
case IPA_JF_UNKNOWN: case IPA_JF_UNKNOWN:
...@@ -4062,7 +4069,10 @@ ipa_write_jump_function (struct output_block *ob, ...@@ -4062,7 +4069,10 @@ ipa_write_jump_function (struct output_block *ob,
case IPA_JF_CONST: case IPA_JF_CONST:
gcc_assert ( gcc_assert (
EXPR_LOCATION (jump_func->value.constant.value) == UNKNOWN_LOCATION); EXPR_LOCATION (jump_func->value.constant.value) == UNKNOWN_LOCATION);
stream_write_tree (ob, jump_func->value.constant.value, true); stream_write_tree (ob,
flag
? TREE_OPERAND (jump_func->value.constant.value, 0)
: jump_func->value.constant.value, true);
break; break;
case IPA_JF_PASS_THROUGH: case IPA_JF_PASS_THROUGH:
streamer_write_uhwi (ob, jump_func->value.pass_through.operation); streamer_write_uhwi (ob, jump_func->value.pass_through.operation);
...@@ -4131,20 +4141,28 @@ static void ...@@ -4131,20 +4141,28 @@ static void
ipa_read_jump_function (struct lto_input_block *ib, ipa_read_jump_function (struct lto_input_block *ib,
struct ipa_jump_func *jump_func, struct ipa_jump_func *jump_func,
struct cgraph_edge *cs, struct cgraph_edge *cs,
struct data_in *data_in) struct data_in *data_in,
bool prevails)
{ {
enum jump_func_type jftype; enum jump_func_type jftype;
enum tree_code operation; enum tree_code operation;
int i, count; int i, count;
int val = streamer_read_uhwi (ib);
bool flag = val & 1;
jftype = (enum jump_func_type) streamer_read_uhwi (ib); jftype = (enum jump_func_type) (val / 2);
switch (jftype) switch (jftype)
{ {
case IPA_JF_UNKNOWN: case IPA_JF_UNKNOWN:
ipa_set_jf_unknown (jump_func); ipa_set_jf_unknown (jump_func);
break; break;
case IPA_JF_CONST: case IPA_JF_CONST:
ipa_set_jf_constant (jump_func, stream_read_tree (ib, data_in), cs); {
tree t = stream_read_tree (ib, data_in);
if (flag && prevails)
t = build_fold_addr_expr (t);
ipa_set_jf_constant (jump_func, t, cs);
}
break; break;
case IPA_JF_PASS_THROUGH: case IPA_JF_PASS_THROUGH:
operation = (enum tree_code) streamer_read_uhwi (ib); operation = (enum tree_code) streamer_read_uhwi (ib);
...@@ -4177,10 +4195,13 @@ ipa_read_jump_function (struct lto_input_block *ib, ...@@ -4177,10 +4195,13 @@ ipa_read_jump_function (struct lto_input_block *ib,
ipa_set_ancestor_jf (jump_func, offset, formal_id, agg_preserved); ipa_set_ancestor_jf (jump_func, offset, formal_id, agg_preserved);
break; break;
} }
default:
fatal_error (UNKNOWN_LOCATION, "invalid jump function in LTO stream");
} }
count = streamer_read_uhwi (ib); count = streamer_read_uhwi (ib);
vec_alloc (jump_func->agg.items, count); if (prevails)
vec_alloc (jump_func->agg.items, count);
if (count) if (count)
{ {
struct bitpack_d bp = streamer_read_bitpack (ib); struct bitpack_d bp = streamer_read_bitpack (ib);
...@@ -4191,7 +4212,8 @@ ipa_read_jump_function (struct lto_input_block *ib, ...@@ -4191,7 +4212,8 @@ ipa_read_jump_function (struct lto_input_block *ib,
struct ipa_agg_jf_item item; struct ipa_agg_jf_item item;
item.offset = streamer_read_uhwi (ib); item.offset = streamer_read_uhwi (ib);
item.value = stream_read_tree (ib, data_in); item.value = stream_read_tree (ib, data_in);
jump_func->agg.items->quick_push (item); if (prevails)
jump_func->agg.items->quick_push (item);
} }
struct bitpack_d bp = streamer_read_bitpack (ib); struct bitpack_d bp = streamer_read_bitpack (ib);
...@@ -4200,7 +4222,8 @@ ipa_read_jump_function (struct lto_input_block *ib, ...@@ -4200,7 +4222,8 @@ ipa_read_jump_function (struct lto_input_block *ib,
{ {
widest_int value = streamer_read_widest_int (ib); widest_int value = streamer_read_widest_int (ib);
widest_int mask = streamer_read_widest_int (ib); widest_int mask = streamer_read_widest_int (ib);
ipa_set_jfunc_bits (jump_func, value, mask); if (prevails)
ipa_set_jfunc_bits (jump_func, value, mask);
} }
else else
jump_func->bits = NULL; jump_func->bits = NULL;
...@@ -4213,7 +4236,8 @@ ipa_read_jump_function (struct lto_input_block *ib, ...@@ -4213,7 +4236,8 @@ ipa_read_jump_function (struct lto_input_block *ib,
VR_LAST); VR_LAST);
tree min = stream_read_tree (ib, data_in); tree min = stream_read_tree (ib, data_in);
tree max = stream_read_tree (ib, data_in); tree max = stream_read_tree (ib, data_in);
ipa_set_jfunc_vr (jump_func, type, min, max); if (prevails)
ipa_set_jfunc_vr (jump_func, type, min, max);
} }
else else
jump_func->m_vr = NULL; jump_func->m_vr = NULL;
...@@ -4345,24 +4369,48 @@ ipa_write_node_info (struct output_block *ob, struct cgraph_node *node) ...@@ -4345,24 +4369,48 @@ ipa_write_node_info (struct output_block *ob, struct cgraph_node *node)
} }
} }
/* If jump functions points to node we possibly can propagate into. /* Stream in edge E from IB. */
At this moment symbol table is still not merged, but the prevailing
symbol is always first in the list. */
static bool static void
jump_function_useful_p (symtab_node *node) ipa_read_edge_info (struct lto_input_block *ib,
struct data_in *data_in,
struct cgraph_edge *e, bool prevails)
{ {
/* While incremental linking we may end up getting function body later. */ int count = streamer_read_uhwi (ib);
if (flag_incremental_link == INCREMENTAL_LINK_LTO) bool contexts_computed = count & 1;
return true;
if (!TREE_PUBLIC (node->decl) && !DECL_EXTERNAL (node->decl)) count /= 2;
return true; if (!count)
for (int n = 10; node->previous_sharing_asm_name && n ; n--) return;
node = node->previous_sharing_asm_name; if (prevails && e->possibly_call_in_translation_unit_p ())
if (node->previous_sharing_asm_name) {
node = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (node->decl)); struct ipa_edge_args *args = IPA_EDGE_REF (e);
gcc_assert (TREE_PUBLIC (node->decl)); vec_safe_grow_cleared (args->jump_functions, count);
return node->definition; if (contexts_computed)
vec_safe_grow_cleared (args->polymorphic_call_contexts, count);
for (int k = 0; k < count; k++)
{
ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), e,
data_in, prevails);
if (contexts_computed)
ipa_get_ith_polymorhic_call_context (args, k)->stream_in
(ib, data_in);
}
}
else
{
for (int k = 0; k < count; k++)
{
struct ipa_jump_func dummy;
ipa_read_jump_function (ib, &dummy, e,
data_in, prevails);
if (contexts_computed)
{
struct ipa_polymorphic_call_context ctx;
ctx.stream_in (ib, data_in);
}
}
}
} }
/* Stream in NODE info from IB. */ /* Stream in NODE info from IB. */
...@@ -4371,82 +4419,50 @@ static void ...@@ -4371,82 +4419,50 @@ static void
ipa_read_node_info (struct lto_input_block *ib, struct cgraph_node *node, ipa_read_node_info (struct lto_input_block *ib, struct cgraph_node *node,
struct data_in *data_in) struct data_in *data_in)
{ {
struct ipa_node_params *info = IPA_NODE_REF (node);
int k; int k;
struct cgraph_edge *e; struct cgraph_edge *e;
struct bitpack_d bp; struct bitpack_d bp;
bool prevails = node->prevailing_p ();
ipa_alloc_node_params (node, streamer_read_uhwi (ib)); struct ipa_node_params *info = prevails ? IPA_NODE_REF (node) : NULL;
for (k = 0; k < ipa_get_param_count (info); k++) int param_count = streamer_read_uhwi (ib);
(*info->descriptors)[k].move_cost = streamer_read_uhwi (ib); if (prevails)
{
ipa_alloc_node_params (node, param_count);
for (k = 0; k < param_count; k++)
(*info->descriptors)[k].move_cost = streamer_read_uhwi (ib);
if (ipa_get_param_count (info) != 0)
info->analysis_done = true;
info->node_enqueued = false;
}
else
for (k = 0; k < param_count; k++)
streamer_read_uhwi (ib);
bp = streamer_read_bitpack (ib); bp = streamer_read_bitpack (ib);
if (ipa_get_param_count (info) != 0) for (k = 0; k < param_count; k++)
info->analysis_done = true;
info->node_enqueued = false;
for (k = 0; k < ipa_get_param_count (info); k++)
ipa_set_param_used (info, k, bp_unpack_value (&bp, 1));
for (k = 0; k < ipa_get_param_count (info); k++)
{ {
ipa_set_controlled_uses (info, k, streamer_read_hwi (ib)); bool used = bp_unpack_value (&bp, 1);
(*info->descriptors)[k].decl_or_type = stream_read_tree (ib, data_in);
if (prevails)
ipa_set_param_used (info, k, used);
} }
for (e = node->callees; e; e = e->next_callee) for (k = 0; k < param_count; k++)
{ {
struct ipa_edge_args *args = IPA_EDGE_REF (e); int nuses = streamer_read_hwi (ib);
int count = streamer_read_uhwi (ib); tree type = stream_read_tree (ib, data_in);
bool contexts_computed = count & 1;
count /= 2;
if (!count)
continue;
if (!jump_function_useful_p (e->callee))
{
for (k = 0; k < count; k++)
{
struct ipa_jump_func dummy;
ipa_read_jump_function (ib, &dummy, e, data_in);
if (contexts_computed)
{
struct ipa_polymorphic_call_context ctx;
ctx.stream_in (ib, data_in);
}
}
continue;
}
vec_safe_grow_cleared (args->jump_functions, count);
if (contexts_computed)
vec_safe_grow_cleared (args->polymorphic_call_contexts, count);
for (k = 0; k < ipa_get_cs_argument_count (args); k++) if (prevails)
{ {
ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), e, ipa_set_controlled_uses (info, k, nuses);
data_in); (*info->descriptors)[k].decl_or_type = type;
if (contexts_computed)
ipa_get_ith_polymorhic_call_context (args, k)->stream_in (ib, data_in);
} }
} }
for (e = node->callees; e; e = e->next_callee)
ipa_read_edge_info (ib, data_in, e, prevails);
for (e = node->indirect_calls; e; e = e->next_callee) for (e = node->indirect_calls; e; e = e->next_callee)
{ {
struct ipa_edge_args *args = IPA_EDGE_REF (e); ipa_read_edge_info (ib, data_in, e, prevails);
int count = streamer_read_uhwi (ib);
bool contexts_computed = count & 1;
count /= 2;
if (count)
{
vec_safe_grow_cleared (args->jump_functions, count);
if (contexts_computed)
vec_safe_grow_cleared (args->polymorphic_call_contexts, count);
for (k = 0; k < ipa_get_cs_argument_count (args); k++)
{
ipa_read_jump_function (ib, ipa_get_ith_jump_func (args, k), e,
data_in);
if (contexts_computed)
ipa_get_ith_polymorhic_call_context (args, k)->stream_in (ib, data_in);
}
}
ipa_read_indirect_edge_info (ib, data_in, e); ipa_read_indirect_edge_info (ib, data_in, e);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment