Commit 44210a96 by Martin Jambor Committed by Martin Jambor

ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use agg_preserved flag instead.

2014-11-14  Martin Jambor  <mjambor@suse.cz>

	* ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use
	agg_preserved flag instead.
	(ipa_get_jf_ancestor_type_preserved): Likewise.
	(ipa_node_params): Rename known_vals to known_csts, update all users.
	New field known_contexts.
	(ipa_get_indirect_edge_target): Update prototype.
	(ipcp_poly_ctx_values_pool): Declare.
	(ipa_context_from_jfunc): Likewise.
	* ipa-inline.h (estimate_ipcp_clone_size_and_time): Updated prototype.
	* cgraph.h (ipa_polymorphic_call_context): New method equal_to.  New
	parameter newline of method dump.
	* ipa-cp.c (ctxlat): New field.
	(ipcp_values_pool): Renamed to ipcp_cst_values_pool, updated all users.
	(ipcp_poly_ctx_values_pool):New variable.
	(ipa_get_poly_ctx_lat): New function.
	(print_ipcp_constant_value): New overloaded function for contexts.
	(print_all_lattices): Also print contexts.
	(ipa_topo_info): New field contexts;
	(set_all_contains_variable): Also set the flag in the context lattice.
	(initialize_node_lattices): Likewise for flag bottom.
	(ipa_get_jf_ancestor_result): Removed BINFO handling.
	(ipa_value_from_jfunc): Likewise.
	(ipa_context_from_jfunc): New function.
	(values_equal_for_ipcp_p): New overloaded function for contexts.
	(allocate_and_init_ipcp_value): Construct the value.
	(allocate_and_init_ipcp_value): New overloaded function for contexts.
	(propagate_scalar_accross_jump_function): Removed handling of
	KNOWN_TYPE jump functions.
	(propagate_context_accross_jump_function): New function.
	(propagate_constants_accross_call): Also propagate contexts.
	(ipa_get_indirect_edge_target_1): Work on contexts rather than BINFOs.
	(ipa_get_indirect_edge_target): Likewise.
	(devirtualization_time_bonus): Likewise.
	(gather_context_independent_values): Create and populate known_contexts
	vector rather than known_binfos.
	(perform_estimation_of_a_value): Work on contexts rather than BINFOs.
	(estimate_local_effects): Likewise.
	(add_all_node_vals_to_toposort): Also add contexts to teir topological
	sort.
	(ipcp_propagate_stage): Also propagate effects of contexts.
	(ipcp_discover_new_direct_edges): Receive and pass known_contexts to
	ipa_get_indirect_edge_target_1.
	(cgraph_edge_brings_value_p): New overloaded function for contexts.
	(create_specialized_node): Work on contexts rather than BINFOs.
	(find_more_contexts_for_caller_subset): New function.
	(known_contexts_useful_p): New function.
	(copy_useful_known_contexts): Likewise.
	(modify_known_vectors_with_val): Likewise.
	(ipcp_val_in_agg_replacements_p): Renamed to
	ipcp_val_agg_replacement_ok_p, return true for all offset indicating
	non-aggregate.
	(ipcp_val_agg_replacement_ok_p): New overloaded function for contexts.
	(decide_about_value): Work on contexts rather than BINFOs.
	(decide_whether_version_node): Likewise.
	(ipcp_driver): Initialize the new alloc pool.
	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Prettify
	printing of edge contexts.
	(ipa_set_ancestor_jf): Replace assert with conditional setting of
	type_preserved to false.
	(update_jump_functions_after_inlining): Use access function instead of
	reading agg_preserved directly.  Store combined context in the ancestor
	case.
	(try_make_edge_direct_virtual_call): Work on contexts rather than
	BINFOs.
	(update_indirect_edges_after_inlining): Get context from
	ipa_context_from_jfunc.
	(ipa_free_node_params_substructures): Free also known_contexts.
	(ipa_free_all_structures_after_ipa_cp): Free the new alloc pool.
	(ipa_free_all_structures_after_iinln): Likewise.
	* ipa-inline-analysis.c (evaluate_properties_for_edge): Work on
	contexts rather than BINFOs.
	(estimate_edge_devirt_benefit): Likewise.
	(estimate_edge_size_and_time): Likewise.
	(estimate_calls_size_and_time): Likewise.
	(estimate_node_size_and_time): Likewise.
	(estimate_ipcp_clone_size_and_time): Likewise.
	(do_estimate_edge_time): Likewise.
	(do_estimate_edge_size): Likewise.
	(do_estimate_edge_hints): Likewise.
	* ipa-polymorphic-call.c (ipa_polymorphic_call_context::dump): New
	parameter newline, ouput newline only when it is set.
	(ipa_polymorphic_call_context::equal_to): New method.

testsuite/
	* g++.dg/ipa/devirt-11.C: Dont't run ipa-cp, remove times constraint
	from the dump scan.
	* g++.dg/ipa/devirt-21.C: Xfail.
	* g++.dg/ipa/devirt-24.C: Likewise.
	* g++.dg/ipa/devirt-10.C: Removed times constraint from the dump scan.
	* g++.dg/ipa/devirt-41.C: Updated the dump scan.
	* g++.dg/ipa/devirt-44.C: Likewise.
	* g++.dg/ipa/devirt-43.C: Xfail.

From-SVN: r217587
parent c0cb5055
2014-11-14 Martin Jambor <mjambor@suse.cz>
* ipa-prop.h (ipa_get_jf_pass_through_type_preserved): use
agg_preserved flag instead.
(ipa_get_jf_ancestor_type_preserved): Likewise.
(ipa_node_params): Rename known_vals to known_csts, update all users.
New field known_contexts.
(ipa_get_indirect_edge_target): Update prototype.
(ipcp_poly_ctx_values_pool): Declare.
(ipa_context_from_jfunc): Likewise.
* ipa-inline.h (estimate_ipcp_clone_size_and_time): Updated prototype.
* cgraph.h (ipa_polymorphic_call_context): New method equal_to. New
parameter newline of method dump.
* ipa-cp.c (ctxlat): New field.
(ipcp_values_pool): Renamed to ipcp_cst_values_pool, updated all users.
(ipcp_poly_ctx_values_pool):New variable.
(ipa_get_poly_ctx_lat): New function.
(print_ipcp_constant_value): New overloaded function for contexts.
(print_all_lattices): Also print contexts.
(ipa_topo_info): New field contexts;
(set_all_contains_variable): Also set the flag in the context lattice.
(initialize_node_lattices): Likewise for flag bottom.
(ipa_get_jf_ancestor_result): Removed BINFO handling.
(ipa_value_from_jfunc): Likewise.
(ipa_context_from_jfunc): New function.
(values_equal_for_ipcp_p): New overloaded function for contexts.
(allocate_and_init_ipcp_value): Construct the value.
(allocate_and_init_ipcp_value): New overloaded function for contexts.
(propagate_scalar_accross_jump_function): Removed handling of
KNOWN_TYPE jump functions.
(propagate_context_accross_jump_function): New function.
(propagate_constants_accross_call): Also propagate contexts.
(ipa_get_indirect_edge_target_1): Work on contexts rather than BINFOs.
(ipa_get_indirect_edge_target): Likewise.
(devirtualization_time_bonus): Likewise.
(gather_context_independent_values): Create and populate known_contexts
vector rather than known_binfos.
(perform_estimation_of_a_value): Work on contexts rather than BINFOs.
(estimate_local_effects): Likewise.
(add_all_node_vals_to_toposort): Also add contexts to teir topological
sort.
(ipcp_propagate_stage): Also propagate effects of contexts.
(ipcp_discover_new_direct_edges): Receive and pass known_contexts to
ipa_get_indirect_edge_target_1.
(cgraph_edge_brings_value_p): New overloaded function for contexts.
(create_specialized_node): Work on contexts rather than BINFOs.
(find_more_contexts_for_caller_subset): New function.
(known_contexts_useful_p): New function.
(copy_useful_known_contexts): Likewise.
(modify_known_vectors_with_val): Likewise.
(ipcp_val_in_agg_replacements_p): Renamed to
ipcp_val_agg_replacement_ok_p, return true for all offset indicating
non-aggregate.
(ipcp_val_agg_replacement_ok_p): New overloaded function for contexts.
(decide_about_value): Work on contexts rather than BINFOs.
(decide_whether_version_node): Likewise.
(ipcp_driver): Initialize the new alloc pool.
* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Prettify
printing of edge contexts.
(ipa_set_ancestor_jf): Replace assert with conditional setting of
type_preserved to false.
(update_jump_functions_after_inlining): Use access function instead of
reading agg_preserved directly. Store combined context in the ancestor
case.
(try_make_edge_direct_virtual_call): Work on contexts rather than
BINFOs.
(update_indirect_edges_after_inlining): Get context from
ipa_context_from_jfunc.
(ipa_free_node_params_substructures): Free also known_contexts.
(ipa_free_all_structures_after_ipa_cp): Free the new alloc pool.
(ipa_free_all_structures_after_iinln): Likewise.
* ipa-inline-analysis.c (evaluate_properties_for_edge): Work on
contexts rather than BINFOs.
(estimate_edge_devirt_benefit): Likewise.
(estimate_edge_size_and_time): Likewise.
(estimate_calls_size_and_time): Likewise.
(estimate_node_size_and_time): Likewise.
(estimate_ipcp_clone_size_and_time): Likewise.
(do_estimate_edge_time): Likewise.
(do_estimate_edge_size): Likewise.
(do_estimate_edge_hints): Likewise.
* ipa-polymorphic-call.c (ipa_polymorphic_call_context::dump): New
parameter newline, ouput newline only when it is set.
(ipa_polymorphic_call_context::equal_to): New method.
2014-11-14 Martin Jambor <mjambor@suse.cz>
* ipa-cp.c (ipcp_value_source): Converted to a template class. All
users converted to the same specialization as the using class/function
or specialization on tree.
......@@ -1387,9 +1387,12 @@ public:
/* Return TRUE if context is fully useless. */
bool useless_p () const;
/* Return TRUE if this context conveys the same information as X. */
bool equal_to (const ipa_polymorphic_call_context &x) const;
/* Dump human readable context to F. */
void dump (FILE *f) const;
/* Dump human readable context to F. If NEWLINE is true, it will be
terminated by a newline. */
void dump (FILE *f, bool newline = true) const;
void DEBUG_FUNCTION debug () const;
/* LTO streaming. */
......
......@@ -258,6 +258,8 @@ class ipcp_param_lattices
public:
/* Lattice describing the value of the parameter itself. */
ipcp_lattice<tree> itself;
/* Lattice describing the the polymorphic contexts of a parameter. */
ipcp_lattice<ipa_polymorphic_call_context> ctxlat;
/* Lattices describing aggregate parts. */
ipcp_agg_lattice *aggs;
/* Number of aggregate lattices */
......@@ -278,7 +280,8 @@ public:
/* Allocation pools for values and their sources in ipa-cp. */
alloc_pool ipcp_values_pool;
alloc_pool ipcp_cst_values_pool;
alloc_pool ipcp_poly_ctx_values_pool;
alloc_pool ipcp_sources_pool;
alloc_pool ipcp_agg_lattice_pool;
......@@ -310,6 +313,15 @@ ipa_get_scalar_lat (struct ipa_node_params *info, int i)
return &plats->itself;
}
/* Return the lattice corresponding to the scalar value of the Ith formal
parameter of the function described by INFO. */
static inline ipcp_lattice<ipa_polymorphic_call_context> *
ipa_get_poly_ctx_lat (struct ipa_node_params *info, int i)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
return &plats->ctxlat;
}
/* Return whether LAT is a lattice with a single constant and without an
undefined value. */
......@@ -343,6 +355,14 @@ print_ipcp_constant_value (FILE * f, tree v)
print_generic_expr (f, v, 0);
}
/* Print V which is extracted from a value in a lattice to F. */
static void
print_ipcp_constant_value (FILE * f, ipa_polymorphic_call_context v)
{
v.dump(f, false);
}
/* Print a lattice LAT to F. */
template <typename valtype>
......@@ -427,7 +447,8 @@ print_all_lattices (FILE * f, bool dump_sources, bool dump_benefits)
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
fprintf (f, " param [%d]: ", i);
plats->itself.print (f, dump_sources, dump_benefits);
fprintf (f, " ctxs: ");
plats->ctxlat.print (f, dump_sources, dump_benefits);
if (plats->virt_call)
fprintf (f, " virt_call flag set\n");
......@@ -639,6 +660,7 @@ public:
int nnodes, stack_top;
value_topo_info<tree> constants;
value_topo_info<ipa_polymorphic_call_context> contexts;
ipa_topo_info () : order(NULL), stack(NULL), nnodes(0), stack_top(0),
constants ()
......@@ -750,9 +772,10 @@ set_agg_lats_contain_variable (struct ipcp_param_lattices *plats)
static inline bool
set_all_contains_variable (struct ipcp_param_lattices *plats)
{
bool ret = !plats->itself.contains_variable || !plats->aggs_contain_variable;
plats->itself.contains_variable = true;
plats->aggs_contain_variable = true;
bool ret;
ret = plats->itself.set_contains_variable ();
ret |= plats->ctxlat.set_contains_variable ();
ret |= set_agg_lats_contain_variable (plats);
return ret;
}
......@@ -787,6 +810,7 @@ initialize_node_lattices (struct cgraph_node *node)
if (disable)
{
plats->itself.set_to_bottom ();
plats->ctxlat.set_to_bottom ();
set_agg_lats_to_bottom (plats);
}
else
......@@ -853,28 +877,8 @@ ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input)
static tree
ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input)
{
if (TREE_CODE (input) == TREE_BINFO)
{
if (!ipa_get_jf_ancestor_type_preserved (jfunc))
return NULL;
/* FIXME: At LTO we can't propagate to non-polymorphic type, because
we have no ODR equivalency on those. This should be fixed by
propagating on types rather than binfos that would make type
matching here unnecesary. */
if (in_lto_p
&& (TREE_CODE (ipa_get_jf_ancestor_type (jfunc)) != RECORD_TYPE
|| !TYPE_BINFO (ipa_get_jf_ancestor_type (jfunc))
|| !BINFO_VTABLE (TYPE_BINFO (ipa_get_jf_ancestor_type (jfunc)))))
{
if (!ipa_get_jf_ancestor_offset (jfunc))
return input;
return NULL;
}
return get_binfo_at_offset (input,
ipa_get_jf_ancestor_offset (jfunc),
ipa_get_jf_ancestor_type (jfunc));
}
else if (TREE_CODE (input) == ADDR_EXPR)
gcc_checking_assert (TREE_CODE (input) != TREE_BINFO);
if (TREE_CODE (input) == ADDR_EXPR)
{
tree t = TREE_OPERAND (input, 0);
t = build_ref_for_offset (EXPR_LOCATION (t), t,
......@@ -888,9 +892,9 @@ ipa_get_jf_ancestor_result (struct ipa_jump_func *jfunc, tree input)
return NULL_TREE;
}
/* Determine whether JFUNC evaluates to a known value (that is either a
constant or a binfo) and if so, return it. Otherwise return NULL. INFO
describes the caller node so that pass-through jump functions can be
/* Determine whether JFUNC evaluates to a single known constant value and if
so, return it. Otherwise return NULL. INFO describes the caller node or
the one it is inlined to, so that pass-through jump functions can be
evaluated. */
tree
......@@ -898,8 +902,6 @@ ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc)
{
if (jfunc->type == IPA_JF_CONST)
return ipa_get_jf_constant (jfunc);
else if (jfunc->type == IPA_JF_KNOWN_TYPE)
return ipa_binfo_from_known_type_jfunc (jfunc);
else if (jfunc->type == IPA_JF_PASS_THROUGH
|| jfunc->type == IPA_JF_ANCESTOR)
{
......@@ -912,7 +914,7 @@ ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc)
idx = ipa_get_jf_ancestor_formal_id (jfunc);
if (info->ipcp_orig_node)
input = info->known_vals[idx];
input = info->known_csts[idx];
else
{
ipcp_lattice<tree> *lat;
......@@ -940,6 +942,68 @@ ipa_value_from_jfunc (struct ipa_node_params *info, struct ipa_jump_func *jfunc)
return NULL_TREE;
}
/* Determie whether JFUNC evaluates to single known polymorphic context, given
that INFO describes the caller node or the one it is inlined to, CS is the
call graph edge corresponding to JFUNC and CSIDX index of the described
parameter. */
ipa_polymorphic_call_context
ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
ipa_jump_func *jfunc)
{
ipa_edge_args *args = IPA_EDGE_REF (cs);
ipa_polymorphic_call_context ctx;
ipa_polymorphic_call_context *edge_ctx
= cs ? ipa_get_ith_polymorhic_call_context (args, csidx) : NULL;
if (edge_ctx && !edge_ctx->useless_p ())
ctx = *edge_ctx;
if (jfunc->type == IPA_JF_PASS_THROUGH
|| jfunc->type == IPA_JF_ANCESTOR)
{
ipa_polymorphic_call_context srcctx;
int srcidx;
if (jfunc->type == IPA_JF_PASS_THROUGH)
{
if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR
|| !ipa_get_jf_pass_through_type_preserved (jfunc))
return ctx;
srcidx = ipa_get_jf_pass_through_formal_id (jfunc);
}
else
{
if (!ipa_get_jf_ancestor_type_preserved (jfunc))
return ctx;
srcidx = ipa_get_jf_ancestor_formal_id (jfunc);
}
if (info->ipcp_orig_node)
{
if (info->known_contexts.exists ())
srcctx = info->known_contexts[srcidx];
}
else
{
if (!info->lattices)
{
gcc_checking_assert (!flag_ipa_cp);
return ctx;
}
ipcp_lattice<ipa_polymorphic_call_context> *lat;
lat = ipa_get_poly_ctx_lat (info, srcidx);
if (!lat->is_single_const ())
return ctx;
srcctx = lat->values->value;
}
if (srcctx.useless_p ())
return ctx;
if (jfunc->type == IPA_JF_ANCESTOR)
srcctx.offset_by (ipa_get_jf_ancestor_offset (jfunc));
ctx.combine_with (srcctx);
}
return ctx;
}
/* If checking is enabled, verify that no lattice is in the TOP state, i.e. not
bottom, not containing a variable component and without any known value at
......@@ -1000,6 +1064,16 @@ values_equal_for_ipcp_p (tree x, tree y)
return operand_equal_p (x, y, 0);
}
/* Return true iff X and Y should be considered equal contexts by IPA-CP. */
static bool
values_equal_for_ipcp_p (ipa_polymorphic_call_context x,
ipa_polymorphic_call_context y)
{
return x.equal_to (y);
}
/* Add a new value source to the value represented by THIS, marking that a
value comes from edge CS and (if the underlying jump function is a
pass-through or an ancestor one) from a caller value SRC_VAL of a caller
......@@ -1031,7 +1105,22 @@ allocate_and_init_ipcp_value (tree source)
{
ipcp_value<tree> *val;
val = new (pool_alloc (ipcp_values_pool)) ipcp_value<tree>;
val = new (pool_alloc (ipcp_cst_values_pool)) ipcp_value<tree>;
memset (val, 0, sizeof (*val));
val->value = source;
return val;
}
/* Allocate a new ipcp_value holding a polymorphic context, initialize its
value to SOURCE and clear all other fields. */
static ipcp_value<ipa_polymorphic_call_context> *
allocate_and_init_ipcp_value (ipa_polymorphic_call_context source)
{
ipcp_value<ipa_polymorphic_call_context> *val;
val = new (pool_alloc (ipcp_poly_ctx_values_pool))
ipcp_value<ipa_polymorphic_call_context>;
memset (val, 0, sizeof (*val));
val->value = source;
return val;
......@@ -1171,19 +1260,9 @@ propagate_scalar_accross_jump_function (struct cgraph_edge *cs,
if (dest_lat->bottom)
return false;
if (jfunc->type == IPA_JF_CONST
|| jfunc->type == IPA_JF_KNOWN_TYPE)
{
tree val;
if (jfunc->type == IPA_JF_KNOWN_TYPE)
if (jfunc->type == IPA_JF_CONST)
{
val = ipa_binfo_from_known_type_jfunc (jfunc);
if (!val)
return dest_lat->set_contains_variable ();
}
else
val = ipa_get_jf_constant (jfunc);
tree val = ipa_get_jf_constant (jfunc);
return dest_lat->add_value (val, cs, NULL, 0);
}
else if (jfunc->type == IPA_JF_PASS_THROUGH
......@@ -1227,6 +1306,93 @@ propagate_scalar_accross_jump_function (struct cgraph_edge *cs,
return dest_lat->set_contains_variable ();
}
/* Propagate scalar values across jump function JFUNC that is associated with
edge CS and describes argument IDX and put the values into DEST_LAT. */
static bool
propagate_context_accross_jump_function (cgraph_edge *cs,
ipa_jump_func *jfunc, int idx,
ipcp_lattice<ipa_polymorphic_call_context> *dest_lat)
{
ipa_edge_args *args = IPA_EDGE_REF (cs);
if (dest_lat->bottom)
return false;
bool ret = false;
bool added_sth = false;
ipa_polymorphic_call_context edge_ctx, *edge_ctx_ptr
= ipa_get_ith_polymorhic_call_context (args, idx);
if (edge_ctx_ptr)
{
edge_ctx = *edge_ctx_ptr;
edge_ctx.clear_speculation ();
}
if (jfunc->type == IPA_JF_PASS_THROUGH
|| jfunc->type == IPA_JF_ANCESTOR)
{
struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
int src_idx;
ipcp_lattice<ipa_polymorphic_call_context> *src_lat;
/* TODO: Once we figure out how to propagate speculations, it will
probably be a good idea to switch to speculation if type_preserved is
not set instead of punting. */
if (jfunc->type == IPA_JF_PASS_THROUGH)
{
if (ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR
|| !ipa_get_jf_pass_through_type_preserved (jfunc))
goto prop_fail;
src_idx = ipa_get_jf_pass_through_formal_id (jfunc);
}
else
{
if (!ipa_get_jf_ancestor_type_preserved (jfunc))
goto prop_fail;
src_idx = ipa_get_jf_ancestor_formal_id (jfunc);
}
src_lat = ipa_get_poly_ctx_lat (caller_info, src_idx);
/* If we would need to clone the caller and cannot, do not propagate. */
if (!ipcp_versionable_function_p (cs->caller)
&& (src_lat->contains_variable
|| (src_lat->values_count > 1)))
goto prop_fail;
if (src_lat->contains_variable)
ret |= dest_lat->set_contains_variable ();
ipcp_value<ipa_polymorphic_call_context> *src_val;
for (src_val = src_lat->values; src_val; src_val = src_val->next)
{
ipa_polymorphic_call_context cur = src_val->value;
if (jfunc->type == IPA_JF_ANCESTOR)
cur.offset_by (ipa_get_jf_ancestor_offset (jfunc));
/* TODO: Perhaps attempt to look up some used OTR type? */
cur.clear_speculation ();
if (!edge_ctx.useless_p ())
cur.combine_with (edge_ctx);
if (!cur.useless_p ())
{
ret |= dest_lat->add_value (cur, cs, src_val, src_idx);
added_sth = true;
}
}
}
prop_fail:
if (!added_sth)
{
if (!edge_ctx.useless_p ())
ret |= dest_lat->add_value (edge_ctx, cs);
else
ret |= dest_lat->set_contains_variable ();
}
return ret;
}
/* If DEST_PLATS already has aggregate items, check that aggs_by_ref matches
NEW_AGGS_BY_REF and if not, mark all aggs as bottoms and return true (in all
other cases, return false). If there are no aggregate items, set
......@@ -1561,6 +1727,8 @@ propagate_constants_accross_call (struct cgraph_edge *cs)
{
ret |= propagate_scalar_accross_jump_function (cs, jump_func,
&dest_plats->itself);
ret |= propagate_context_accross_jump_function (cs, jump_func, i,
&dest_plats->ctxlat);
ret |= propagate_aggs_accross_jump_function (cs, jump_func,
dest_plats);
}
......@@ -1572,25 +1740,24 @@ propagate_constants_accross_call (struct cgraph_edge *cs)
}
/* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS
(which can contain both constants and binfos), KNOWN_BINFOS, KNOWN_AGGS or
(which can contain both constants and binfos), KNOWN_CONTEXTS, KNOWN_AGGS or
AGG_REPS return the destination. The latter three can be NULL. If AGG_REPS
is not NULL, KNOWN_AGGS is ignored. */
static tree
ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
struct ipa_agg_replacement_value *agg_reps)
{
int param_index = ie->indirect_info->param_index;
HOST_WIDE_INT token, anc_offset;
tree otr_type;
HOST_WIDE_INT anc_offset;
tree t;
tree target = NULL;
if (param_index == -1
|| known_vals.length () <= (unsigned int) param_index)
|| known_csts.length () <= (unsigned int) param_index)
return NULL_TREE;
if (!ie->indirect_info->polymorphic)
......@@ -1625,7 +1792,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
t = NULL;
}
else
t = known_vals[param_index];
t = known_csts[param_index];
if (t &&
TREE_CODE (t) == ADDR_EXPR
......@@ -1639,9 +1806,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
return NULL_TREE;
gcc_assert (!ie->indirect_info->agg_contents);
token = ie->indirect_info->otr_token;
anc_offset = ie->indirect_info->offset;
otr_type = ie->indirect_info->otr_type;
t = NULL;
......@@ -1695,19 +1860,30 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
}
}
/* Did we work out BINFO via type propagation? */
if (!t && known_binfos.length () > (unsigned int) param_index)
t = known_binfos[param_index];
/* Or do we know the constant value of pointer? */
if (!t)
t = known_vals[param_index];
/* Do we know the constant value of pointer? */
if (!t)
return NULL_TREE;
t = known_csts[param_index];
if (TREE_CODE (t) != TREE_BINFO)
gcc_checking_assert (!t || TREE_CODE (t) != TREE_BINFO);
ipa_polymorphic_call_context context;
if (known_contexts.length () > (unsigned int) param_index)
{
context = known_contexts[param_index];
if (t)
{
ipa_polymorphic_call_context context (t, ie->indirect_info->otr_type,
ipa_polymorphic_call_context ctx2 = ipa_polymorphic_call_context
(t, ie->indirect_info->otr_type, anc_offset);
if (!ctx2.useless_p ())
context.combine_with (ctx2, ie->indirect_info->otr_type);
}
}
else if (t)
context = ipa_polymorphic_call_context (t, ie->indirect_info->otr_type,
anc_offset);
else
return NULL_TREE;
vec <cgraph_node *>targets;
bool final;
......@@ -1721,16 +1897,6 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
target = targets[0]->decl;
else
target = ipa_impossible_devirt_target (ie, NULL_TREE);
}
else
{
tree binfo;
binfo = get_binfo_at_offset (t, anc_offset, otr_type);
if (!binfo)
return NULL_TREE;
target = gimple_get_virt_method_for_binfo (token, binfo);
}
if (target && !possible_polymorphic_call_target_p (ie,
cgraph_node::get (target)))
......@@ -1740,27 +1906,27 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
}
/* If an indirect edge IE can be turned into a direct one based on KNOWN_VALS
(which can contain both constants and binfos), KNOWN_BINFOS (which can be
NULL) or KNOWN_AGGS (which also can be NULL) return the destination. */
/* If an indirect edge IE can be turned into a direct one based on KNOWN_CSTS,
KNOWN_CONTEXTS (which can be vNULL) or KNOWN_AGGS (which also can be vNULL)
return the destination. */
tree
ipa_get_indirect_edge_target (struct cgraph_edge *ie,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
{
return ipa_get_indirect_edge_target_1 (ie, known_vals, known_binfos,
return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
known_aggs, NULL);
}
/* Calculate devirtualization time bonus for NODE, assuming we know KNOWN_CSTS
and KNOWN_BINFOS. */
and KNOWN_CONTEXTS. */
static int
devirtualization_time_bonus (struct cgraph_node *node,
vec<tree> known_csts,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
{
struct cgraph_edge *ie;
......@@ -1773,7 +1939,7 @@ devirtualization_time_bonus (struct cgraph_node *node,
enum availability avail;
tree target;
target = ipa_get_indirect_edge_target (ie, known_csts, known_binfos,
target = ipa_get_indirect_edge_target (ie, known_csts, known_contexts,
known_aggs);
if (!target)
continue;
......@@ -1890,15 +2056,17 @@ context_independent_aggregate_values (struct ipcp_param_lattices *plats)
return res;
}
/* Allocate KNOWN_CSTS, KNOWN_BINFOS and, if non-NULL, KNOWN_AGGS and populate
them with values of parameters that are known independent of the context.
INFO describes the function. If REMOVABLE_PARAMS_COST is non-NULL, the
movement cost of all removable parameters will be stored in it. */
/* Allocate KNOWN_CSTS, KNOWN_CONTEXTS and, if non-NULL, KNOWN_AGGS and
populate them with values of parameters that are known independent of the
context. INFO describes the function. If REMOVABLE_PARAMS_COST is
non-NULL, the movement cost of all removable parameters will be stored in
it. */
static bool
gather_context_independent_values (struct ipa_node_params *info,
vec<tree> *known_csts,
vec<tree> *known_binfos,
vec<ipa_polymorphic_call_context>
*known_contexts,
vec<ipa_agg_jump_function> *known_aggs,
int *removable_params_cost)
{
......@@ -1906,9 +2074,9 @@ gather_context_independent_values (struct ipa_node_params *info,
bool ret = false;
known_csts->create (0);
known_binfos->create (0);
known_contexts->create (0);
known_csts->safe_grow_cleared (count);
known_binfos->safe_grow_cleared (count);
known_contexts->safe_grow_cleared (count);
if (known_aggs)
{
known_aggs->create (0);
......@@ -1926,28 +2094,25 @@ gather_context_independent_values (struct ipa_node_params *info,
if (lat->is_single_const ())
{
ipcp_value<tree> *val = lat->values;
if (TREE_CODE (val->value) != TREE_BINFO)
{
gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO);
(*known_csts)[i] = val->value;
if (removable_params_cost)
*removable_params_cost
+= estimate_move_cost (TREE_TYPE (val->value), false);
ret = true;
}
else if (plats->virt_call)
{
(*known_binfos)[i] = val->value;
ret = true;
}
else if (removable_params_cost
&& !ipa_is_param_used (info, i))
*removable_params_cost += ipa_get_param_move_cost (info, i);
}
else if (removable_params_cost
&& !ipa_is_param_used (info, i))
*removable_params_cost
+= ipa_get_param_move_cost (info, i);
ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
if (ctxlat->is_single_const ())
{
(*known_contexts)[i] = ctxlat->values->value;
ret = true;
}
if (known_aggs)
{
vec<ipa_agg_jf_item, va_gc> *agg_items;
......@@ -1985,14 +2150,14 @@ agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> known_aggs)
}
/* Perform time and size measurement of NODE with the context given in
KNOWN_CSTS, KNOWN_BINFOS and KNOWN_AGGS, calculate the benefit and cost
KNOWN_CSTS, KNOWN_CONTEXTS and KNOWN_AGGS, calculate the benefit and cost
given BASE_TIME of the node without specialization, REMOVABLE_PARAMS_COST of
all context-independent removable parameters and EST_MOVE_COST of estimated
movement of the considered parameter and store it into VAL. */
static void
perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs_ptrs,
int base_time, int removable_params_cost,
int est_move_cost, ipcp_value_base *val)
......@@ -2000,11 +2165,11 @@ perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
int time, size, time_benefit;
inline_hints hints;
estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos,
estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
known_aggs_ptrs, &size, &time,
&hints);
time_benefit = base_time - time
+ devirtualization_time_bonus (node, known_csts, known_binfos,
+ devirtualization_time_bonus (node, known_csts, known_contexts,
known_aggs_ptrs)
+ hint_time_bonus (hints)
+ removable_params_cost + est_move_cost;
......@@ -2029,7 +2194,8 @@ estimate_local_effects (struct cgraph_node *node)
{
struct ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
vec<tree> known_csts, known_binfos;
vec<tree> known_csts;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function> known_aggs;
vec<ipa_agg_jump_function_p> known_aggs_ptrs;
bool always_const;
......@@ -2044,7 +2210,7 @@ estimate_local_effects (struct cgraph_node *node)
node->name (), node->order, base_time);
always_const = gather_context_independent_values (info, &known_csts,
&known_binfos, &known_aggs,
&known_contexts, &known_aggs,
&removable_params_cost);
known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs);
if (always_const)
......@@ -2056,9 +2222,9 @@ estimate_local_effects (struct cgraph_node *node)
init_caller_stats (&stats);
node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
false);
estimate_ipcp_clone_size_and_time (node, known_csts, known_binfos,
estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
known_aggs_ptrs, &size, &time, &hints);
time -= devirtualization_time_bonus (node, known_csts, known_binfos,
time -= devirtualization_time_bonus (node, known_csts, known_contexts,
known_aggs_ptrs);
time -= hint_time_bonus (hints);
time -= removable_params_cost;
......@@ -2104,38 +2270,60 @@ estimate_local_effects (struct cgraph_node *node)
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
ipcp_value<tree> *val;
int emc;
if (lat->bottom
|| !lat->values
|| known_csts[i]
|| known_binfos[i])
|| known_csts[i])
continue;
for (val = lat->values; val; val = val->next)
{
if (TREE_CODE (val->value) != TREE_BINFO)
{
gcc_checking_assert (TREE_CODE (val->value) != TREE_BINFO);
known_csts[i] = val->value;
known_binfos[i] = NULL_TREE;
emc = estimate_move_cost (TREE_TYPE (val->value), true);
}
else if (plats->virt_call)
int emc = estimate_move_cost (TREE_TYPE (val->value), true);
perform_estimation_of_a_value (node, known_csts, known_contexts,
known_aggs_ptrs, base_time,
removable_params_cost, emc, val);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " - estimates for value ");
print_ipcp_constant_value (dump_file, val->value);
fprintf (dump_file, " for ");
ipa_dump_param (dump_file, info, i);
fprintf (dump_file, ": time_benefit: %i, size: %i\n",
val->local_time_benefit, val->local_size_cost);
}
}
known_csts[i] = NULL_TREE;
known_binfos[i] = val->value;
emc = 0;
}
else
for (i = 0; i < count; i++)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
if (!plats->virt_call)
continue;
ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
ipcp_value<ipa_polymorphic_call_context> *val;
if (ctxlat->bottom
|| !ctxlat->values
|| !known_contexts[i].useless_p ())
continue;
perform_estimation_of_a_value (node, known_csts, known_binfos,
for (val = ctxlat->values; val; val = val->next)
{
known_contexts[i] = val->value;
perform_estimation_of_a_value (node, known_csts, known_contexts,
known_aggs_ptrs, base_time,
removable_params_cost, emc, val);
removable_params_cost, 0, val);
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " - estimates for value ");
fprintf (dump_file, " - estimates for polymorphic context ");
print_ipcp_constant_value (dump_file, val->value);
fprintf (dump_file, " for ");
ipa_dump_param (dump_file, info, i);
......@@ -2143,8 +2331,7 @@ estimate_local_effects (struct cgraph_node *node)
val->local_time_benefit, val->local_size_cost);
}
}
known_binfos[i] = NULL_TREE;
known_csts[i] = NULL_TREE;
known_contexts[i] = ipa_polymorphic_call_context ();
}
for (i = 0; i < count ; i++)
......@@ -2174,7 +2361,7 @@ estimate_local_effects (struct cgraph_node *node)
item.value = val->value;
vec_safe_push (ajf->items, item);
perform_estimation_of_a_value (node, known_csts, known_binfos,
perform_estimation_of_a_value (node, known_csts, known_contexts,
known_aggs_ptrs, base_time,
removable_params_cost, 0, val);
......@@ -2200,7 +2387,7 @@ estimate_local_effects (struct cgraph_node *node)
vec_free (known_aggs[i].items);
known_csts.release ();
known_binfos.release ();
known_contexts.release ();
known_aggs.release ();
known_aggs_ptrs.release ();
}
......@@ -2274,18 +2461,31 @@ add_all_node_vals_to_toposort (cgraph_node *node, ipa_topo_info *topo)
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
struct ipcp_agg_lattice *aglat;
ipcp_value<tree> *val;
if (!lat->bottom)
{
ipcp_value<tree> *val;
for (val = lat->values; val; val = val->next)
topo->constants.add_val (val);
}
if (!plats->aggs_bottom)
for (aglat = plats->aggs; aglat; aglat = aglat->next)
if (!aglat->bottom)
{
ipcp_value<tree> *val;
for (val = aglat->values; val; val = val->next)
topo->constants.add_val (val);
}
ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
if (!ctxlat->bottom)
{
ipcp_value<ipa_polymorphic_call_context> *ctxval;
for (ctxval = ctxlat->values; ctxval; ctxval = ctxval->next)
topo->contexts.add_val (ctxval);
}
}
}
/* One pass of constants propagation along the call graph edges, from callers
......@@ -2389,8 +2589,8 @@ value_topo_info<valtype>::propagate_effects ()
}
/* Propagate constants, binfos and their effects from the summaries
interprocedurally. */
/* Propagate constants, polymorphic contexts and their effects from the
summaries interprocedurally. */
static void
ipcp_propagate_stage (struct ipa_topo_info *topo)
......@@ -2435,6 +2635,7 @@ ipcp_propagate_stage (struct ipa_topo_info *topo)
ipcp_verify_propagated_values ();
#endif
topo->constants.propagate_effects ();
topo->contexts.propagate_effects ();
if (dump_file)
{
......@@ -2444,11 +2645,13 @@ ipcp_propagate_stage (struct ipa_topo_info *topo)
}
/* Discover newly direct outgoing edges from NODE which is a new clone with
known KNOWN_VALS and make them direct. */
known KNOWN_CSTS and make them direct. */
static void
ipcp_discover_new_direct_edges (struct cgraph_node *node,
vec<tree> known_vals,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context>
known_contexts,
struct ipa_agg_replacement_value *aggvals)
{
struct cgraph_edge *ie, *next_ie;
......@@ -2459,8 +2662,8 @@ ipcp_discover_new_direct_edges (struct cgraph_node *node,
tree target;
next_ie = ie->next_callee;
target = ipa_get_indirect_edge_target_1 (ie, known_vals, vNULL, vNULL,
aggvals);
target = ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
vNULL, aggvals);
if (target)
{
bool agg_contents = ie->indirect_info->agg_contents;
......@@ -2589,7 +2792,7 @@ cgraph_edge_brings_value_p (struct cgraph_edge *cs,
{
tree t;
if (src->offset == -1)
t = caller_info->known_vals[src->index];
t = caller_info->known_csts[src->index];
else
t = get_clone_agg_value (cs->caller, src->offset, src->index);
return (t != NULL_TREE
......@@ -2618,6 +2821,35 @@ cgraph_edge_brings_value_p (struct cgraph_edge *cs,
}
}
/* Return true if edge CS does bring about the value described by SRC. */
static bool
cgraph_edge_brings_value_p (struct cgraph_edge *cs,
ipcp_value_source<ipa_polymorphic_call_context>
*src)
{
struct ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
cgraph_node *real_dest = cs->callee->function_symbol ();
struct ipa_node_params *dst_info = IPA_NODE_REF (real_dest);
if ((dst_info->ipcp_orig_node && !dst_info->is_all_contexts_clone)
|| caller_info->node_dead)
return false;
if (!src->val)
return true;
if (caller_info->ipcp_orig_node)
return (caller_info->known_contexts.length () > (unsigned) src->index)
&& values_equal_for_ipcp_p (src->val->value,
caller_info->known_contexts[src->index]);
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (caller_info,
src->index);
return plats->ctxlat.is_single_const ()
&& values_equal_for_ipcp_p (src->val->value,
plats->ctxlat.values->value);
}
/* Get the next clone in the linked list of clones of an edge. */
static inline struct cgraph_edge *
......@@ -2847,12 +3079,14 @@ update_specialized_profile (struct cgraph_node *new_node,
dump_profile_updates (orig_node, new_node);
}
/* Create a specialized version of NODE with known constants and types of
parameters in KNOWN_VALS and redirect all edges in CALLERS to it. */
/* Create a specialized version of NODE with known constants in KNOWN_CSTS,
known contexts in KNOWN_CONTEXTS and known aggregate values in AGGVALS and
redirect all edges in CALLERS to it. */
static struct cgraph_node *
create_specialized_node (struct cgraph_node *node,
vec<tree> known_vals,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
struct ipa_agg_replacement_value *aggvals,
vec<cgraph_edge *> callers)
{
......@@ -2870,10 +3104,9 @@ create_specialized_node (struct cgraph_node *node,
args_to_skip = BITMAP_GGC_ALLOC ();
for (i = 0; i < count; i++)
{
tree t = known_vals[i];
tree t = known_csts[i];
if ((t && TREE_CODE (t) != TREE_BINFO)
|| !ipa_is_param_used (info, i))
if (t || !ipa_is_param_used (info, i))
bitmap_set_bit (args_to_skip, i);
}
}
......@@ -2886,11 +3119,12 @@ create_specialized_node (struct cgraph_node *node,
for (i = 0; i < count ; i++)
{
tree t = known_vals[i];
if (t && TREE_CODE (t) != TREE_BINFO)
tree t = known_csts[i];
if (t)
{
struct ipa_replace_map *replace_map;
gcc_checking_assert (TREE_CODE (t) != TREE_BINFO);
replace_map = get_replacement_map (info, t, i);
if (replace_map)
vec_safe_push (replace_trees, replace_map);
......@@ -2907,6 +3141,15 @@ create_specialized_node (struct cgraph_node *node,
{
fprintf (dump_file, " the new node is %s/%i.\n",
new_node->name (), new_node->order);
if (known_contexts.exists ())
{
for (i = 0; i < count ; i++)
if (!known_contexts[i].useless_p ())
{
fprintf (dump_file, " known ctx %i is ", i);
known_contexts[i].dump (dump_file);
}
}
if (aggvals)
ipa_dump_agg_replacement_values (dump_file, aggvals);
}
......@@ -2914,21 +3157,21 @@ create_specialized_node (struct cgraph_node *node,
update_profiling_info (node, new_node);
new_info = IPA_NODE_REF (new_node);
new_info->ipcp_orig_node = node;
new_info->known_vals = known_vals;
new_info->known_csts = known_csts;
new_info->known_contexts = known_contexts;
ipcp_discover_new_direct_edges (new_node, known_vals, aggvals);
ipcp_discover_new_direct_edges (new_node, known_csts, known_contexts, aggvals);
callers.release ();
return new_node;
}
/* Given a NODE, and a subset of its CALLERS, try to populate blanks slots in
KNOWN_VALS with constants and types that are also known for all of the
CALLERS. */
KNOWN_CSTS with constants that are also known for all of the CALLERS. */
static void
find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
vec<tree> known_vals,
vec<tree> known_csts,
vec<cgraph_edge *> callers)
{
struct ipa_node_params *info = IPA_NODE_REF (node);
......@@ -2940,7 +3183,7 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
tree newval = NULL_TREE;
int j;
if (ipa_get_scalar_lat (info, i)->bottom || known_vals[i])
if (ipa_get_scalar_lat (info, i)->bottom || known_csts[i])
continue;
FOR_EACH_VEC_ELT (callers, j, cs)
......@@ -2977,8 +3220,77 @@ find_more_scalar_values_for_callers_subset (struct cgraph_node *node,
fprintf (dump_file, "\n");
}
known_vals[i] = newval;
known_csts[i] = newval;
}
}
}
/* Given a NODE and a subset of its CALLERS, try to populate plank slots in
KNOWN_CONTEXTS with polymorphic contexts that are also known for all of the
CALLERS. */
static void
find_more_contexts_for_caller_subset (cgraph_node *node,
vec<ipa_polymorphic_call_context>
*known_contexts,
vec<cgraph_edge *> callers)
{
ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
for (i = 0; i < count ; i++)
{
cgraph_edge *cs;
if (ipa_get_poly_ctx_lat (info, i)->bottom
|| (known_contexts->exists ()
&& !(*known_contexts)[i].useless_p ()))
continue;
ipa_polymorphic_call_context newval;
bool found = false;
int j;
FOR_EACH_VEC_ELT (callers, j, cs)
{
if (i >= ipa_get_cs_argument_count (IPA_EDGE_REF (cs)))
return;
ipa_jump_func *jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs),
i);
ipa_polymorphic_call_context ctx;
ctx = ipa_context_from_jfunc (IPA_NODE_REF (cs->caller), cs, i,
jfunc);
ctx.clear_speculation ();
if (ctx.useless_p ()
|| (found && !values_equal_for_ipcp_p (newval, ctx)))
{
found = false;
break;
}
else if (!found)
{
found = true;
newval = ctx;
}
}
if (found)
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
fprintf (dump_file, " adding an extra known polymorphic "
"context ");
print_ipcp_constant_value (dump_file, newval);
fprintf (dump_file, " for ");
ipa_dump_param (dump_file, info, i);
fprintf (dump_file, "\n");
}
if (!known_contexts->exists ())
known_contexts->safe_grow_cleared (ipa_get_param_count (info));
(*known_contexts)[i] = newval;
}
}
}
......@@ -3355,7 +3667,7 @@ cgraph_edge_brings_all_scalars_for_node (struct cgraph_edge *cs,
struct ipa_jump_func *jump_func;
tree val, t;
val = dest_info->known_vals[i];
val = dest_info->known_csts[i];
if (!val)
continue;
......@@ -3489,28 +3801,70 @@ perhaps_add_new_callers (cgraph_node *node, ipcp_value<valtype> *val)
update_specialized_profile (val->spec_node, node, redirected_sum);
}
/* Return true if KNOWN_CONTEXTS contain at least one useful context. */
static bool
known_contexts_useful_p (vec<ipa_polymorphic_call_context> known_contexts)
{
ipa_polymorphic_call_context *ctx;
int i;
FOR_EACH_VEC_ELT (known_contexts, i, ctx)
if (!ctx->useless_p ())
return true;
return false;
}
/* Copy KNOWN_BINFOS to KNOWN_VALS. */
/* Return a copy of KNOWN_CSTS if it is not empty, otherwise return vNULL. */
static vec<ipa_polymorphic_call_context>
copy_useful_known_contexts (vec<ipa_polymorphic_call_context> known_contexts)
{
if (known_contexts_useful_p (known_contexts))
return known_contexts.copy ();
else
return vNULL;
}
/* Copy KNOWN_CSTS and modify the copy according to VAL and INDEX. If
non-empty, replace KNOWN_CONTEXTS with its copy too. */
static void
move_binfos_to_values (vec<tree> known_vals,
vec<tree> known_binfos)
modify_known_vectors_with_val (vec<tree> *known_csts,
vec<ipa_polymorphic_call_context> *known_contexts,
ipcp_value<tree> *val,
int index)
{
tree t;
int i;
*known_csts = known_csts->copy ();
*known_contexts = copy_useful_known_contexts (*known_contexts);
(*known_csts)[index] = val->value;
}
for (i = 0; known_binfos.iterate (i, &t); i++)
if (t)
known_vals[i] = t;
/* Replace KNOWN_CSTS with its copy. Also copy KNOWN_CONTEXTS and modify the
copy according to VAL and INDEX. */
static void
modify_known_vectors_with_val (vec<tree> *known_csts,
vec<ipa_polymorphic_call_context> *known_contexts,
ipcp_value<ipa_polymorphic_call_context> *val,
int index)
{
*known_csts = known_csts->copy ();
*known_contexts = known_contexts->copy ();
(*known_contexts)[index] = val->value;
}
/* Return true if there is a replacement equivalent to VALUE, INDEX and OFFSET
among those in the AGGVALS list. */
/* Return true if OFFSET indicates this was not an aggregate value or there is
a replacement equivalent to VALUE, INDEX and OFFSET among those in the
AGGVALS list. */
DEBUG_FUNCTION bool
ipcp_val_in_agg_replacements_p (struct ipa_agg_replacement_value *aggvals,
ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *aggvals,
int index, HOST_WIDE_INT offset, tree value)
{
if (offset == -1)
return true;
while (aggvals)
{
if (aggvals->index == index
......@@ -3522,22 +3876,32 @@ ipcp_val_in_agg_replacements_p (struct ipa_agg_replacement_value *aggvals,
return false;
}
/* Return true if offset is minus one because source of a polymorphic contect
cannot be an aggregate value. */
DEBUG_FUNCTION bool
ipcp_val_agg_replacement_ok_p (ipa_agg_replacement_value *,
int , HOST_WIDE_INT offset,
ipa_polymorphic_call_context)
{
return offset == -1;
}
/* Decide wheter to create a special version of NODE for value VAL of parameter
at the given INDEX. If OFFSET is -1, the value is for the parameter itself,
otherwise it is stored at the given OFFSET of the parameter. KNOWN_CSTS,
KNOWN_BINFOS and KNOWN_AGGS describe the other already known values. */
KNOWN_CONTEXTS and KNOWN_AGGS describe the other already known values. */
template <typename valtype>
static bool
decide_about_value (struct cgraph_node *node, int index, HOST_WIDE_INT offset,
ipcp_value<valtype> *val, vec<tree> known_csts,
vec<tree> known_binfos)
vec<ipa_polymorphic_call_context> known_contexts)
{
struct ipa_agg_replacement_value *aggvals;
int freq_sum, caller_count;
gcov_type count_sum;
vec<cgraph_edge *> callers;
vec<tree> kv;
if (val->spec_node)
{
......@@ -3583,16 +3947,20 @@ decide_about_value (struct cgraph_node *node, int index, HOST_WIDE_INT offset,
node->name (), node->order);
callers = gather_edges_for_value (val, caller_count);
kv = known_csts.copy ();
move_binfos_to_values (kv, known_binfos);
if (offset == -1)
kv[index] = val->value;
find_more_scalar_values_for_callers_subset (node, kv, callers);
modify_known_vectors_with_val (&known_csts, &known_contexts, val, index);
else
{
known_csts = known_csts.copy ();
known_contexts = copy_useful_known_contexts (known_contexts);
}
find_more_scalar_values_for_callers_subset (node, known_csts, callers);
find_more_contexts_for_caller_subset (node, &known_contexts, callers);
aggvals = find_aggregate_values_for_callers_subset (node, callers);
gcc_checking_assert (offset == -1
|| ipcp_val_in_agg_replacements_p (aggvals, index,
gcc_checking_assert (ipcp_val_agg_replacement_ok_p (aggvals, index,
offset, val->value));
val->spec_node = create_specialized_node (node, kv, aggvals, callers);
val->spec_node = create_specialized_node (node, known_csts, known_contexts,
aggvals, callers);
overall_size += val->local_size_cost;
/* TODO: If for some lattice there is only one other known value
......@@ -3608,7 +3976,8 @@ decide_whether_version_node (struct cgraph_node *node)
{
struct ipa_node_params *info = IPA_NODE_REF (node);
int i, count = ipa_get_param_count (info);
vec<tree> known_csts, known_binfos;
vec<tree> known_csts;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function> known_aggs = vNULL;
bool ret = false;
......@@ -3619,7 +3988,7 @@ decide_whether_version_node (struct cgraph_node *node)
fprintf (dump_file, "\nEvaluating opportunities for %s/%i.\n",
node->name (), node->order);
gather_context_independent_values (info, &known_csts, &known_binfos,
gather_context_independent_values (info, &known_csts, &known_contexts,
info->do_clone_for_all_contexts ? &known_aggs
: NULL, NULL);
......@@ -3627,14 +3996,16 @@ decide_whether_version_node (struct cgraph_node *node)
{
struct ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
ipcp_lattice<tree> *lat = &plats->itself;
ipcp_value<tree> *val;
ipcp_lattice<ipa_polymorphic_call_context> *ctxlat = &plats->ctxlat;
if (!lat->bottom
&& !known_csts[i]
&& !known_binfos[i])
&& !known_csts[i])
{
ipcp_value<tree> *val;
for (val = lat->values; val; val = val->next)
ret |= decide_about_value (node, i, -1, val, known_csts,
known_binfos);
known_contexts);
}
if (!plats->aggs_bottom)
{
......@@ -3648,8 +4019,18 @@ decide_whether_version_node (struct cgraph_node *node)
|| !aglat->is_single_const ()))
for (val = aglat->values; val; val = val->next)
ret |= decide_about_value (node, i, aglat->offset, val,
known_csts, known_binfos);
known_csts, known_contexts);
}
if (!ctxlat->bottom
&& known_contexts[i].useless_p ())
{
ipcp_value<ipa_polymorphic_call_context> *val;
for (val = ctxlat->values; val; val = val->next)
ret |= decide_about_value (node, i, -1, val, known_csts,
known_contexts);
}
info = IPA_NODE_REF (node);
}
......@@ -3664,8 +4045,13 @@ decide_whether_version_node (struct cgraph_node *node)
node->order);
callers = node->collect_callers ();
move_binfos_to_values (known_csts, known_binfos);
clone = create_specialized_node (node, known_csts,
if (!known_contexts_useful_p (known_contexts))
{
known_contexts.release ();
known_contexts = vNULL;
}
clone = create_specialized_node (node, known_csts, known_contexts,
known_aggs_to_agg_replacement_list (known_aggs),
callers);
info = IPA_NODE_REF (node);
......@@ -3677,9 +4063,11 @@ decide_whether_version_node (struct cgraph_node *node)
ret = true;
}
else
{
known_csts.release ();
known_contexts.release ();
}
known_binfos.release ();
return ret;
}
......@@ -3803,8 +4191,11 @@ ipcp_driver (void)
edge_removal_hook_holder =
symtab->add_edge_removal_hook (&ipcp_edge_removal_hook, NULL);
ipcp_values_pool = create_alloc_pool ("IPA-CP values",
ipcp_cst_values_pool = create_alloc_pool ("IPA-CP constant values",
sizeof (ipcp_value<tree>), 32);
ipcp_poly_ctx_values_pool = create_alloc_pool
("IPA-CP polymorphic contexts",
sizeof (ipcp_value<ipa_polymorphic_call_context>), 32);
ipcp_sources_pool = create_alloc_pool ("IPA-CP value sources",
sizeof (ipcp_value_source<tree>), 64);
ipcp_agg_lattice_pool = create_alloc_pool ("IPA_CP aggregate lattices",
......
......@@ -895,7 +895,8 @@ static void
evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
clause_t *clause_ptr,
vec<tree> *known_vals_ptr,
vec<tree> *known_binfos_ptr,
vec<ipa_polymorphic_call_context>
*known_contexts_ptr,
vec<ipa_agg_jump_function_p> *known_aggs_ptr)
{
struct cgraph_node *callee = e->callee->ultimate_alias_target ();
......@@ -907,12 +908,12 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
*clause_ptr = inline_p ? 0 : 1 << predicate_not_inlined_condition;
if (known_vals_ptr)
known_vals_ptr->create (0);
if (known_binfos_ptr)
known_binfos_ptr->create (0);
if (known_contexts_ptr)
known_contexts_ptr->create (0);
if (ipa_node_params_vector.exists ()
&& !e->call_stmt_cannot_inline_p
&& ((clause_ptr && info->conds) || known_vals_ptr || known_binfos_ptr))
&& ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr))
{
struct ipa_node_params *parms_info;
struct ipa_edge_args *args = IPA_EDGE_REF (e);
......@@ -928,8 +929,8 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
known_vals.safe_grow_cleared (count);
if (count && (info->conds || known_aggs_ptr))
known_aggs.safe_grow_cleared (count);
if (count && known_binfos_ptr)
known_binfos_ptr->safe_grow_cleared (count);
if (count && known_contexts_ptr)
known_contexts_ptr->safe_grow_cleared (count);
for (i = 0; i < count; i++)
{
......@@ -937,14 +938,16 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
tree cst = ipa_value_from_jfunc (parms_info, jf);
if (cst)
{
if (known_vals.exists () && TREE_CODE (cst) != TREE_BINFO)
gcc_checking_assert (TREE_CODE (cst) != TREE_BINFO);
if (known_vals.exists ())
known_vals[i] = cst;
else if (known_binfos_ptr != NULL
&& TREE_CODE (cst) == TREE_BINFO)
(*known_binfos_ptr)[i] = cst;
}
else if (inline_p && !es->param[i].change_prob)
known_vals[i] = error_mark_node;
if (known_contexts_ptr)
(*known_contexts_ptr)[i] = ipa_context_from_jfunc (parms_info, e,
i, jf);
/* TODO: When IPA-CP starts propagating and merging aggregate jump
functions, use its knowledge of the caller too, just like the
scalar case above. */
......@@ -2969,14 +2972,14 @@ make_pass_inline_parameters (gcc::context *ctxt)
}
/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS and
KNOWN_BINFOS. */
/* Estimate benefit devirtualizing indirect edge IE, provided KNOWN_VALS,
KNOWN_CONTEXTS and KNOWN_AGGS. */
static bool
estimate_edge_devirt_benefit (struct cgraph_edge *ie,
int *size, int *time,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
{
tree target;
......@@ -2984,12 +2987,12 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
struct inline_summary *isummary;
enum availability avail;
if (!known_vals.exists () && !known_binfos.exists ())
if (!known_vals.exists () && !known_contexts.exists ())
return false;
if (!flag_indirect_inlining)
return false;
target = ipa_get_indirect_edge_target (ie, known_vals, known_binfos,
target = ipa_get_indirect_edge_target (ie, known_vals, known_contexts,
known_aggs);
if (!target)
return false;
......@@ -3013,7 +3016,7 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
/* Increase SIZE, MIN_SIZE (if non-NULL) and TIME for size and time needed to
handle edge E with probability PROB.
Set HINTS if edge may be devirtualized.
KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS describe context of the call
KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS describe context of the call
site. */
static inline void
......@@ -3021,7 +3024,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size,
int *time,
int prob,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
inline_hints *hints)
{
......@@ -3031,7 +3034,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size,
int cur_size;
if (!e->callee
&& estimate_edge_devirt_benefit (e, &call_size, &call_time,
known_vals, known_binfos, known_aggs)
known_vals, known_contexts, known_aggs)
&& hints && e->maybe_hot_p ())
*hints |= INLINE_HINT_indirect_call;
cur_size = call_size * INLINE_SIZE_SCALE;
......@@ -3047,9 +3050,8 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size,
/* Increase SIZE, MIN_SIZE and TIME for size and time needed to handle all
calls in NODE.
POSSIBLE_TRUTHS, KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS describe context of
the call site. */
calls in NODE. POSSIBLE_TRUTHS, KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS
describe context of the call site. */
static void
estimate_calls_size_and_time (struct cgraph_node *node, int *size,
......@@ -3057,7 +3059,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size,
inline_hints *hints,
clause_t possible_truths,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
{
struct cgraph_edge *e;
......@@ -3074,14 +3076,14 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size,
estimate_edge_size_and_time (e, size,
es->predicate ? NULL : min_size,
time, REG_BR_PROB_BASE,
known_vals, known_binfos,
known_vals, known_contexts,
known_aggs, hints);
}
else
estimate_calls_size_and_time (e->callee, size, min_size, time,
hints,
possible_truths,
known_vals, known_binfos,
known_vals, known_contexts,
known_aggs);
}
}
......@@ -3093,14 +3095,14 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size,
estimate_edge_size_and_time (e, size,
es->predicate ? NULL : min_size,
time, REG_BR_PROB_BASE,
known_vals, known_binfos, known_aggs,
known_vals, known_contexts, known_aggs,
hints);
}
}
/* Estimate size and time needed to execute NODE assuming
POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_BINFOS
POSSIBLE_TRUTHS clause, and KNOWN_VALS, KNOWN_AGGS and KNOWN_CONTEXTS
information about NODE's arguments. If non-NULL use also probability
information present in INLINE_PARAM_SUMMARY vector.
Additionally detemine hints determined by the context. Finally compute
......@@ -3112,7 +3114,7 @@ static void
estimate_node_size_and_time (struct cgraph_node *node,
clause_t possible_truths,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
int *ret_size, int *ret_min_size, int *ret_time,
inline_hints *ret_hints,
......@@ -3189,7 +3191,7 @@ estimate_node_size_and_time (struct cgraph_node *node,
hints |= INLINE_HINT_declared_inline;
estimate_calls_size_and_time (node, &size, &min_size, &time, &hints, possible_truths,
known_vals, known_binfos, known_aggs);
known_vals, known_contexts, known_aggs);
gcc_checking_assert (size >= 0);
gcc_checking_assert (time >= 0);
time = RDIV (time, INLINE_TIME_SCALE);
......@@ -3212,13 +3214,14 @@ estimate_node_size_and_time (struct cgraph_node *node,
/* Estimate size and time needed to execute callee of EDGE assuming that
parameters known to be constant at caller of EDGE are propagated.
KNOWN_VALS and KNOWN_BINFOS are vectors of assumed known constant values
KNOWN_VALS and KNOWN_CONTEXTS are vectors of assumed known constant values
and types for parameters. */
void
estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
vec<tree> known_vals,
vec<tree> known_binfos,
vec<ipa_polymorphic_call_context>
known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
int *ret_size, int *ret_time,
inline_hints *hints)
......@@ -3227,7 +3230,7 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
clause = evaluate_conditions_for_known_args (node, false, known_vals,
known_aggs);
estimate_node_size_and_time (node, clause, known_vals, known_binfos,
estimate_node_size_and_time (node, clause, known_vals, known_contexts,
known_aggs, ret_size, NULL, ret_time, hints, vNULL);
}
......@@ -3672,7 +3675,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
struct cgraph_node *callee;
clause_t clause;
vec<tree> known_vals;
vec<tree> known_binfos;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
struct inline_edge_summary *es = inline_edge_summary (edge);
int min_size;
......@@ -3681,9 +3684,9 @@ do_estimate_edge_time (struct cgraph_edge *edge)
gcc_checking_assert (edge->inline_failed);
evaluate_properties_for_edge (edge, true,
&clause, &known_vals, &known_binfos,
&clause, &known_vals, &known_contexts,
&known_aggs);
estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
estimate_node_size_and_time (callee, clause, known_vals, known_contexts,
known_aggs, &size, &min_size, &time, &hints, es->param);
/* When we have profile feedback, we can quite safely identify hot
......@@ -3697,7 +3700,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
hints |= INLINE_HINT_known_hot;
known_vals.release ();
known_binfos.release ();
known_contexts.release ();
known_aggs.release ();
gcc_checking_assert (size >= 0);
gcc_checking_assert (time >= 0);
......@@ -3728,7 +3731,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
struct cgraph_node *callee;
clause_t clause;
vec<tree> known_vals;
vec<tree> known_binfos;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
/* When we do caching, use do_estimate_edge_time to populate the entry. */
......@@ -3746,12 +3749,12 @@ do_estimate_edge_size (struct cgraph_edge *edge)
/* Early inliner runs without caching, go ahead and do the dirty work. */
gcc_checking_assert (edge->inline_failed);
evaluate_properties_for_edge (edge, true,
&clause, &known_vals, &known_binfos,
&clause, &known_vals, &known_contexts,
&known_aggs);
estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
estimate_node_size_and_time (callee, clause, known_vals, known_contexts,
known_aggs, &size, NULL, NULL, NULL, vNULL);
known_vals.release ();
known_binfos.release ();
known_contexts.release ();
known_aggs.release ();
return size;
}
......@@ -3767,7 +3770,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
struct cgraph_node *callee;
clause_t clause;
vec<tree> known_vals;
vec<tree> known_binfos;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
/* When we do caching, use do_estimate_edge_time to populate the entry. */
......@@ -3785,12 +3788,12 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
/* Early inliner runs without caching, go ahead and do the dirty work. */
gcc_checking_assert (edge->inline_failed);
evaluate_properties_for_edge (edge, true,
&clause, &known_vals, &known_binfos,
&clause, &known_vals, &known_contexts,
&known_aggs);
estimate_node_size_and_time (callee, clause, known_vals, known_binfos,
estimate_node_size_and_time (callee, clause, known_vals, known_contexts,
known_aggs, NULL, NULL, NULL, &hints, vNULL);
known_vals.release ();
known_binfos.release ();
known_contexts.release ();
known_aggs.release ();
hints |= simple_edge_hints (edge);
return hints;
......
......@@ -223,7 +223,8 @@ void initialize_inline_failed (struct cgraph_edge *);
int estimate_time_after_inlining (struct cgraph_node *, struct cgraph_edge *);
int estimate_size_after_inlining (struct cgraph_node *, struct cgraph_edge *);
void estimate_ipcp_clone_size_and_time (struct cgraph_node *,
vec<tree>, vec<tree>,
vec<tree>,
vec<ipa_polymorphic_call_context>,
vec<ipa_agg_jump_function_p>,
int *, int *, inline_hints *);
int do_estimate_growth (struct cgraph_node *);
......
......@@ -599,10 +599,11 @@ decl_maybe_in_construction_p (tree base, tree outer_type,
return false;
}
/* Dump human readable context to F. */
/* Dump human readable context to F. If NEWLINE is true, it will be terminated
by a newline. */
void
ipa_polymorphic_call_context::dump (FILE *f) const
ipa_polymorphic_call_context::dump (FILE *f, bool newline) const
{
fprintf (f, " ");
if (invalid)
......@@ -634,6 +635,7 @@ ipa_polymorphic_call_context::dump (FILE *f) const
speculative_offset);
}
}
if (newline)
fprintf(f, "\n");
}
......@@ -2130,3 +2132,47 @@ ipa_polymorphic_call_context::possible_dynamic_type_change (bool in_poly_cdtor,
else if (in_poly_cdtor)
maybe_in_construction = true;
}
/* Return TRUE if this context conveys the same information as OTHER. */
bool
ipa_polymorphic_call_context::equal_to
(const ipa_polymorphic_call_context &x) const
{
if (useless_p ())
return x.useless_p ();
if (invalid)
return x.invalid;
if (x.useless_p () || x.invalid)
return false;
if (outer_type)
{
if (!x.outer_type
|| !types_odr_comparable (outer_type, x.outer_type)
|| !types_same_for_odr (outer_type, x.outer_type)
|| offset != x.offset
|| maybe_in_construction != x.maybe_in_construction
|| maybe_derived_type != x.maybe_derived_type
|| dynamic != x.dynamic)
return false;
}
else if (x.outer_type)
return false;
if (speculative_outer_type)
{
if (!x.speculative_outer_type
|| !types_odr_comparable (speculative_outer_type,
x.speculative_outer_type)
|| !types_same_for_odr (speculative_outer_type,
x.speculative_outer_type)
|| speculative_offset != x.speculative_offset
|| speculative_maybe_derived_type != x.speculative_maybe_derived_type)
return false;
}
else if (x.speculative_outer_type)
return false;
return true;
}
......@@ -380,8 +380,14 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs)
fprintf (f, "\n");
}
}
if (IPA_EDGE_REF (cs)->polymorphic_call_contexts)
ipa_get_ith_polymorhic_call_context (IPA_EDGE_REF (cs), i)->dump (f);
struct ipa_polymorphic_call_context *ctx
= ipa_get_ith_polymorhic_call_context (IPA_EDGE_REF (cs), i);
if (ctx && !ctx->useless_p ())
{
fprintf (f, " Context: ");
ctx->dump (dump_file);
}
}
}
......@@ -559,7 +565,8 @@ ipa_set_ancestor_jf (struct ipa_jump_func *jfunc, HOST_WIDE_INT offset,
type = NULL_TREE;
if (type)
type = TYPE_MAIN_VARIANT (type);
gcc_assert (!type_preserved || contains_polymorphic_type_p (type));
if (!type || !contains_polymorphic_type_p (type))
type_preserved = false;
jfunc->type = IPA_JF_ANCESTOR;
jfunc->value.ancestor.formal_id = formal_id;
jfunc->value.ancestor.offset = offset;
......@@ -2622,9 +2629,12 @@ combine_known_type_and_ancestor_jfs (struct ipa_jump_func *src,
+ ipa_get_jf_ancestor_offset (dst);
combined_type = ipa_get_jf_ancestor_type (dst);
if (combined_type)
ipa_set_jf_known_type (dst, combined_offset,
ipa_get_jf_known_type_base_type (src),
combined_type);
else
dst->type = IPA_JF_UNKNOWN;
}
/* Update the jump functions associated with call graph edge E when the call
......@@ -2669,7 +2679,7 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
struct ipa_polymorphic_call_context ctx = *src_ctx;
/* TODO: Make type preserved safe WRT contexts. */
if (!dst->value.ancestor.agg_preserved)
if (!ipa_get_jf_ancestor_type_preserved (dst))
ctx.possible_dynamic_type_change (e->in_polymorphic_cdtor);
ctx.offset_by (dst->value.ancestor.offset);
if (!ctx.useless_p ())
......@@ -2678,6 +2688,7 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
count);
dst_ctx = ipa_get_ith_polymorhic_call_context (args, i);
}
dst_ctx->combine_with (ctx);
}
if (src->agg.items
......@@ -2739,7 +2750,7 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
struct ipa_polymorphic_call_context ctx = *src_ctx;
/* TODO: Make type preserved safe WRT contexts. */
if (!dst->value.ancestor.agg_preserved)
if (!ipa_get_jf_pass_through_type_preserved (dst))
ctx.possible_dynamic_type_change (e->in_polymorphic_cdtor);
if (!ctx.useless_p ())
{
......@@ -3152,41 +3163,24 @@ ipa_impossible_devirt_target (struct cgraph_edge *ie, tree target)
/* Try to find a destination for indirect edge IE that corresponds to a virtual
call based on a formal parameter which is described by jump function JFUNC
and if it can be determined, make it direct and return the direct edge.
Otherwise, return NULL. NEW_ROOT_INFO is the node info that JFUNC lattices
are relative to. */
Otherwise, return NULL. CTX describes the polymorphic context that the
parameter the call is based on brings along with it. */
static struct cgraph_edge *
try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
struct ipa_jump_func *jfunc,
struct ipa_node_params *new_root_info,
struct ipa_polymorphic_call_context *ctx_ptr)
struct ipa_polymorphic_call_context ctx)
{
tree binfo, target = NULL;
tree target = NULL;
bool speculative = false;
bool updated = false;
if (!flag_devirtualize)
return NULL;
/* If this is call of a function parameter, restrict its type
based on knowlede of the context. */
if (ctx_ptr && !ie->indirect_info->by_ref)
{
struct ipa_polymorphic_call_context ctx = *ctx_ptr;
ctx.offset_by (ie->indirect_info->offset);
if (ie->indirect_info->vptr_changed)
ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
ie->indirect_info->otr_type);
updated = ie->indirect_info->context.combine_with
(ctx, ie->indirect_info->otr_type);
}
gcc_assert (!ie->indirect_info->by_ref);
/* Try to do lookup via known virtual table pointer value. */
if (!ie->indirect_info->by_ref
&& (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively))
if (!ie->indirect_info->vptr_changed || flag_devirtualize_speculatively)
{
tree vtable;
unsigned HOST_WIDE_INT offset;
......@@ -3217,27 +3211,19 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
}
}
binfo = ipa_value_from_jfunc (new_root_info, jfunc);
if (binfo && TREE_CODE (binfo) != TREE_BINFO)
{
struct ipa_polymorphic_call_context ctx (binfo,
ie->indirect_info->otr_type,
ie->indirect_info->offset);
updated |= ie->indirect_info->context.combine_with
(ctx, ie->indirect_info->otr_type);
}
if (updated)
{
ipa_polymorphic_call_context context (ie);
ipa_polymorphic_call_context ie_context (ie);
vec <cgraph_node *>targets;
bool final;
ctx.offset_by (ie->indirect_info->offset);
if (ie->indirect_info->vptr_changed)
ctx.possible_dynamic_type_change (ie->in_polymorphic_cdtor,
ie->indirect_info->otr_type);
ctx.combine_with (ie_context, ie->indirect_info->otr_type);
targets = possible_polymorphic_call_targets
(ie->indirect_info->otr_type,
ie->indirect_info->otr_token,
context, &final);
ctx, &final);
if (final && targets.length () <= 1)
{
if (targets.length () == 1)
......@@ -3248,7 +3234,8 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
else if (!target && flag_devirtualize_speculatively
&& !ie->speculative && ie->maybe_hot_p ())
{
cgraph_node *n = try_speculative_devirtualization (ie->indirect_info->otr_type,
cgraph_node *n;
n = try_speculative_devirtualization (ie->indirect_info->otr_type,
ie->indirect_info->otr_token,
ie->indirect_info->context);
if (n)
......@@ -3257,27 +3244,11 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
speculative = true;
}
}
}
if (binfo && TREE_CODE (binfo) == TREE_BINFO)
{
binfo = get_binfo_at_offset (binfo, ie->indirect_info->offset,
ie->indirect_info->otr_type);
if (binfo)
{
tree t = gimple_get_virt_method_for_binfo (ie->indirect_info->otr_token,
binfo);
if (t)
{
target = t;
speculative = false;
}
}
}
if (target)
{
if (!possible_polymorphic_call_target_p (ie, cgraph_node::get_create (target)))
if (!possible_polymorphic_call_target_p
(ie, cgraph_node::get_create (target)))
{
if (speculative)
return NULL;
......@@ -3336,11 +3307,9 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
new_direct_edge = NULL;
else if (ici->polymorphic)
{
ipa_polymorphic_call_context *ctx;
ctx = ipa_get_ith_polymorhic_call_context (top, param_index);
new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc,
new_root_info,
ctx);
ipa_polymorphic_call_context ctx;
ctx = ipa_context_from_jfunc (new_root_info, cs, param_index, jfunc);
new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx);
}
else
new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
......@@ -3474,7 +3443,7 @@ propagate_controlled_uses (struct cgraph_edge *cs)
{
struct cgraph_node *n;
struct ipa_ref *ref;
tree t = new_root_info->known_vals[src_idx];
tree t = new_root_info->known_csts[src_idx];
if (t && TREE_CODE (t) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (t, 0)) == FUNCTION_DECL
......@@ -3617,7 +3586,8 @@ ipa_free_node_params_substructures (struct ipa_node_params *info)
free (info->lattices);
/* Lattice values and their sources are deallocated with their alocation
pool. */
info->known_vals.release ();
info->known_csts.release ();
info->known_contexts.release ();
memset (info, 0, sizeof (*info));
}
......@@ -3892,7 +3862,8 @@ ipa_free_all_structures_after_ipa_cp (void)
ipa_free_all_edge_args ();
ipa_free_all_node_params ();
free_alloc_pool (ipcp_sources_pool);
free_alloc_pool (ipcp_values_pool);
free_alloc_pool (ipcp_cst_values_pool);
free_alloc_pool (ipcp_poly_ctx_values_pool);
free_alloc_pool (ipcp_agg_lattice_pool);
ipa_unregister_cgraph_hooks ();
if (ipa_refdesc_pool)
......@@ -3911,8 +3882,10 @@ ipa_free_all_structures_after_iinln (void)
ipa_unregister_cgraph_hooks ();
if (ipcp_sources_pool)
free_alloc_pool (ipcp_sources_pool);
if (ipcp_values_pool)
free_alloc_pool (ipcp_values_pool);
if (ipcp_cst_values_pool)
free_alloc_pool (ipcp_cst_values_pool);
if (ipcp_poly_ctx_values_pool)
free_alloc_pool (ipcp_poly_ctx_values_pool);
if (ipcp_agg_lattice_pool)
free_alloc_pool (ipcp_agg_lattice_pool);
if (ipa_refdesc_pool)
......
......@@ -278,13 +278,14 @@ ipa_get_jf_pass_through_agg_preserved (struct ipa_jump_func *jfunc)
return jfunc->value.pass_through.agg_preserved;
}
/* Return the type_preserved flag of a pass through jump function JFUNC. */
/* Return true if pass through jump function JFUNC preserves type
information. */
static inline bool
ipa_get_jf_pass_through_type_preserved (struct ipa_jump_func *jfunc)
{
gcc_checking_assert (jfunc->type == IPA_JF_PASS_THROUGH);
return jfunc->value.pass_through.type_preserved;
return jfunc->value.pass_through.agg_preserved;
}
/* Return the offset of an ancestor jump function JFUNC. */
......@@ -324,13 +325,13 @@ ipa_get_jf_ancestor_agg_preserved (struct ipa_jump_func *jfunc)
return jfunc->value.ancestor.agg_preserved;
}
/* Return the type_preserved flag of an ancestor jump function JFUNC. */
/* Return true if ancestor jump function JFUNC presrves type information. */
static inline bool
ipa_get_jf_ancestor_type_preserved (struct ipa_jump_func *jfunc)
{
gcc_checking_assert (jfunc->type == IPA_JF_ANCESTOR);
return jfunc->value.ancestor.type_preserved;
return jfunc->value.ancestor.agg_preserved;
}
/* Summary describing a single formal parameter. */
......@@ -363,9 +364,12 @@ struct ipa_node_params
/* Only for versioned nodes this field would not be NULL,
it points to the node that IPA cp cloned from. */
struct cgraph_node *ipcp_orig_node;
/* If this node is an ipa-cp clone, these are the known values that describe
what it has been specialized for. */
vec<tree> known_vals;
/* If this node is an ipa-cp clone, these are the known constants that
describe what it has been specialized for. */
vec<tree> known_csts;
/* If this node is an ipa-cp clone, these are the known polymorphic contexts
that describe what it has been specialized for. */
vec<ipa_polymorphic_call_context> known_contexts;
/* Whether the param uses analysis and jump function computation has already
been performed. */
unsigned analysis_done : 1;
......@@ -592,7 +596,7 @@ bool ipa_propagate_indirect_call_infos (struct cgraph_edge *cs,
/* Indirect edge and binfo processing. */
tree ipa_get_indirect_edge_target (struct cgraph_edge *ie,
vec<tree> ,
vec<tree> ,
vec<ipa_polymorphic_call_context>,
vec<ipa_agg_jump_function_p> );
struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
bool speculative = false);
......@@ -615,7 +619,8 @@ void ipa_print_node_jump_functions (FILE *f, struct cgraph_node *node);
void ipa_print_all_jump_functions (FILE * f);
void ipcp_verify_propagated_values (void);
extern alloc_pool ipcp_values_pool;
extern alloc_pool ipcp_cst_values_pool;
extern alloc_pool ipcp_poly_ctx_values_pool;
extern alloc_pool ipcp_sources_pool;
extern alloc_pool ipcp_agg_lattice_pool;
......@@ -716,6 +721,10 @@ int ipa_get_param_decl_index (struct ipa_node_params *, tree);
tree ipa_value_from_jfunc (struct ipa_node_params *info,
struct ipa_jump_func *jfunc);
unsigned int ipcp_transform_function (struct cgraph_node *node);
ipa_polymorphic_call_context ipa_context_from_jfunc (ipa_node_params *,
cgraph_edge *,
int,
ipa_jump_func *);
void ipa_dump_param (FILE *, struct ipa_node_params *info, int i);
bool ipa_modify_expr (tree *, bool, ipa_parm_adjustment_vec);
ipa_parm_adjustment *ipa_get_adjustment_candidate (tree **, bool *,
......
2014-11-14 Martin Jambor <mjambor@suse.cz>
* g++.dg/ipa/devirt-11.C: Dont't run ipa-cp, remove times constraint
from the dump scan.
* g++.dg/ipa/devirt-21.C: Xfail.
* g++.dg/ipa/devirt-24.C: Likewise.
* g++.dg/ipa/devirt-10.C: Removed times constraint from the dump scan.
* g++.dg/ipa/devirt-41.C: Updated the dump scan.
* g++.dg/ipa/devirt-44.C: Likewise.
* g++.dg/ipa/devirt-43.C: Xfail.
2014-11-14 Jonathan Wakely <jwakely@redhat.com>
* g++.dg/abi/abi-tag11.C: New.
......
......@@ -27,8 +27,6 @@ struct wxBufferedPaintDC : public wxBufferedDC {
void OnPaint(wxPaintEvent & event) {
wxBufferedPaintDC dc;
}
/* IPA-CP should really discover both cases, but for time being the second is handled by inliner. */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 2 "cp" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
/* { dg-final { cleanup-ipa-dump "cp" } } */
/* { dg-do compile } */
/* { dg-options "-O2 -fdump-ipa-inline -fno-devirtualize-speculatively" } */
/* { dg-options "-O2 -fno-ipa-cp -fdump-ipa-inline -fno-devirtualize-speculatively" } */
int baz ();
struct A
{
......@@ -42,7 +42,5 @@ bar ()
baz ();
c + d;
}
/* While inlining function called once we should devirtualize a new call to fn3.
Because fn2 is already removed, we should not devirtualize. */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */
/* { dg-final { scan-ipa-dump "Discovered a virtual call to a known target" "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
......@@ -37,5 +37,5 @@ main()
{
class C c;
}
/* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" { xfail *-*-* } } } */
/* { dg-final { scan-ipa-dump "Discovered a virtual call to" "cp" } } */
/* { dg-final { cleanup-ipa-dump "cp" } } */
......@@ -36,7 +36,7 @@ C *b = new (C);
sort(f, *b);
}
}
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" { xfail *-*-* } } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
/* { dg-final { scan-ipa-dump-times "Aggregate passed by reference" 1 "cp" } } */
/* { dg-final { cleanup-ipa-dump "cp" } } */
......@@ -26,6 +26,6 @@ main()
Because the type is in static storage, we know it won't change type in dostuff
and from callstack we can tell that is is not in construction/destruction. */
/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */
/* { dg-final { scan-ipa-dump "Second type is base of first" "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target" 1 "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
......@@ -23,5 +23,5 @@ t(struct B *b)
of type B. This makes A fully specified and we know C::foo is unlikely.
FIXME: We could most probably can devirtualize unconditonally because dereference of b in
&b->a makes the type known. GIMPLE does not represent this. */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a speculative target" 1 "inline" { xfail *-*-* } } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
......@@ -26,7 +26,7 @@ main()
/* Here one invocation of foo is while type is in construction, while other is not.
Check that we handle that. */
/* { dg-final { scan-ipa-dump-times "First type is base of second" 1 "inline" } } */
/* { dg-final { scan-ipa-dump "Second type is base of first" "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*A::foo" 1 "inline" } } */
/* { dg-final { scan-ipa-dump-times "Discovered a virtual call to a known target\[^\\n\]*B::foo" 1 "inline" } } */
/* { dg-final { cleanup-ipa-dump "inline" } } */
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