Support extended aggregate jump function in ipa-cp

2019-11-14  Feng Xue  <fxue@os.amperecomputing.com>

	PR ipa/91682
	* ipa-prop.h (jump_func_type): New value IPA_JF_LOAD_AGG.
	(ipa_load_agg_data, ipa_agg_value, ipa_agg_value_set): New structs.
	(ipa_agg_jf_item): Add new field jftype and type, redefine field value.
	(ipa_agg_jump_function): Remove member function equal_to.
	(ipa_agg_jump_function_p): Remove typedef.
	(ipa_copy_agg_values, ipa_release_agg_values): New functions.
	* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Dump
	information for aggregate jump function.
	(get_ssa_def_if_simple_copy): Add new parameter rhs_stmt to
	record last definition statement.
	(load_from_unmodified_param_or_agg): New function.
	(ipa_known_agg_contents_list): Add new field type and value, remove
	field constant.
	(build_agg_jump_func_from_list): Rename parameter const_count to
	value_count, build aggregate jump function from ipa_load_agg_data.
	(analyze_agg_content_value): New function.
	(extract_mem_content): Analyze memory store assignment to prepare
	information for aggregate jump function generation.
	(determine_known_aggregate_parts): Add new parameter fbi, remove
	parameter aa_walk_budeget_p.
	(update_jump_functions_after_inlining): Update aggregate jump function.
	(ipa_find_agg_cst_for_param): Change type of parameter agg.
	(try_make_edge_direct_simple_call): Add new parameter new_root.
	(try_make_edge_direct_virtual_call): Add new parameter new_root and
	new_root_info.
	(update_indirect_edges_after_inlining): Pass new argument to
	try_make_edge_direct_simple_call and try_make_edge_direct_virtual_call.
	(ipa_write_jump_function): Write aggregate jump function to file.
	(ipa_read_jump_function): Read aggregate jump function from file.
	(ipa_agg_value::equal_to): Migrate from ipa_agg_jf_item::equal_to.
	* ipa-cp.c (ipa_get_jf_arith_result): New function.
	(ipa_agg_value_from_node): Likewise.
	(ipa_agg_value_set_from_jfunc): Likewise.
	(propagate_vals_across_arith_jfunc): Likewise.
	(propagate_aggregate_lattice): Likewise.
	(ipa_get_jf_pass_through_result): Call ipa_get_jf_arith_result.
	(propagate_vals_across_pass_through): Call
	propagate_vals_across_arith_jfunc.
	(get_clone_agg_value): Move forward.
	(propagate_aggs_across_jump_function): Handle value propagation for
	aggregate jump function.
	(agg_jmp_p_vec_for_t_vec): Remove.
	(context_independent_aggregate_values): Replace vec<ipa_agg_jf_item>
	with vec<ipa_agg_value>.
	(copy_plats_to_inter, intersect_with_plats): Likewise.
	(agg_replacements_to_vector, intersect_with_agg_replacements): Likewise.
	(intersect_aggregate_with_edge): Likewise.
	(find_aggregate_values_for_callers_subset): Likewise.
	(cgraph_edge_brings_all_agg_vals_for_node): Likewise.
	(estimate_local_effects): Replace vec<ipa_agg_jump_function> and
	vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
	(gather_context_independent_values): Likewise.
	(perform_estimation_of_a_value, decide_whether_version_node): Likewise.
	* ipa-fnsummary.c (evaluate_conditions_for_known_args): Replace
	vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
	(evaluate_properties_for_edge): Likewise.
	(estimate_edge_devirt_benefit): Likewise.
	(estimate_edge_size_and_time):  Likewise.
	(estimate_calls_size_and_time): Likewise.
	(ipa_call_context::ipa_call_context): Likewise.
	(estimate_ipcp_clone_size_and_time):  Likewise.
	* ipa-fnsummary.h (ipa_call_context): Replace
	vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
	* ipa-inline-analysis.c (do_estimate_edge_time): Replace
	vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
	(do_estimate_edge_size): Likewise.
	(do_estimate_edge_hints): Likewise.

2019-11-14  Feng Xue  <fxue@os.amperecomputing.com>

        PR ipa/91682
        * gcc.dg/ipa/ipcp-agg-10.c: Change dg-scan string.
        * gcc.dg/ipa/ipcp-agg-11.c: New test.

From-SVN: r278193
This commit is contained in:
Feng Xue 2019-11-14 03:19:15 +00:00 committed by Feng Xue
parent 3e7cf2e6c0
commit eb270950ac
10 changed files with 1207 additions and 310 deletions

View File

@ -1,3 +1,74 @@
2019-11-14 Feng Xue <fxue@os.amperecomputing.com>
PR ipa/91682
* ipa-prop.h (jump_func_type): New value IPA_JF_LOAD_AGG.
(ipa_load_agg_data, ipa_agg_value, ipa_agg_value_set): New structs.
(ipa_agg_jf_item): Add new field jftype and type, redefine field value.
(ipa_agg_jump_function): Remove member function equal_to.
(ipa_agg_jump_function_p): Remove typedef.
(ipa_copy_agg_values, ipa_release_agg_values): New functions.
* ipa-prop.c (ipa_print_node_jump_functions_for_edge): Dump
information for aggregate jump function.
(get_ssa_def_if_simple_copy): Add new parameter rhs_stmt to
record last definition statement.
(load_from_unmodified_param_or_agg): New function.
(ipa_known_agg_contents_list): Add new field type and value, remove
field constant.
(build_agg_jump_func_from_list): Rename parameter const_count to
value_count, build aggregate jump function from ipa_load_agg_data.
(analyze_agg_content_value): New function.
(extract_mem_content): Analyze memory store assignment to prepare
information for aggregate jump function generation.
(determine_known_aggregate_parts): Add new parameter fbi, remove
parameter aa_walk_budeget_p.
(update_jump_functions_after_inlining): Update aggregate jump function.
(ipa_find_agg_cst_for_param): Change type of parameter agg.
(try_make_edge_direct_simple_call): Add new parameter new_root.
(try_make_edge_direct_virtual_call): Add new parameter new_root and
new_root_info.
(update_indirect_edges_after_inlining): Pass new argument to
try_make_edge_direct_simple_call and try_make_edge_direct_virtual_call.
(ipa_write_jump_function): Write aggregate jump function to file.
(ipa_read_jump_function): Read aggregate jump function from file.
(ipa_agg_value::equal_to): Migrate from ipa_agg_jf_item::equal_to.
* ipa-cp.c (ipa_get_jf_arith_result): New function.
(ipa_agg_value_from_node): Likewise.
(ipa_agg_value_set_from_jfunc): Likewise.
(propagate_vals_across_arith_jfunc): Likewise.
(propagate_aggregate_lattice): Likewise.
(ipa_get_jf_pass_through_result): Call ipa_get_jf_arith_result.
(propagate_vals_across_pass_through): Call
propagate_vals_across_arith_jfunc.
(get_clone_agg_value): Move forward.
(propagate_aggs_across_jump_function): Handle value propagation for
aggregate jump function.
(agg_jmp_p_vec_for_t_vec): Remove.
(context_independent_aggregate_values): Replace vec<ipa_agg_jf_item>
with vec<ipa_agg_value>.
(copy_plats_to_inter, intersect_with_plats): Likewise.
(agg_replacements_to_vector, intersect_with_agg_replacements): Likewise.
(intersect_aggregate_with_edge): Likewise.
(find_aggregate_values_for_callers_subset): Likewise.
(cgraph_edge_brings_all_agg_vals_for_node): Likewise.
(estimate_local_effects): Replace vec<ipa_agg_jump_function> and
vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
(gather_context_independent_values): Likewise.
(perform_estimation_of_a_value, decide_whether_version_node): Likewise.
* ipa-fnsummary.c (evaluate_conditions_for_known_args): Replace
vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
(evaluate_properties_for_edge): Likewise.
(estimate_edge_devirt_benefit): Likewise.
(estimate_edge_size_and_time): Likewise.
(estimate_calls_size_and_time): Likewise.
(ipa_call_context::ipa_call_context): Likewise.
(estimate_ipcp_clone_size_and_time): Likewise.
* ipa-fnsummary.h (ipa_call_context): Replace
vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
* ipa-inline-analysis.c (do_estimate_edge_time): Replace
vec<ipa_agg_jump_function_p> with vec<ipa_agg_value_set>.
(do_estimate_edge_size): Likewise.
(do_estimate_edge_hints): Likewise.
2019-11-13 Jan Hubicka <hubicka@ucw.cz>
* ipa-cp.c (propagate_vr_across_jump_function): Propagate also across

View File

@ -1287,23 +1287,23 @@ initialize_node_lattices (struct cgraph_node *node)
}
}
/* Return the result of a (possibly arithmetic) pass through jump function
JFUNC on the constant value INPUT. RES_TYPE is the type of the parameter
to which the result is passed. Return NULL_TREE if that cannot be
determined or be considered an interprocedural invariant. */
/* Return the result of a (possibly arithmetic) operation on the constant
value INPUT. OPERAND is 2nd operand for binary operation. RES_TYPE is
the type of the parameter to which the result is passed. Return
NULL_TREE if that cannot be determined or be considered an
interprocedural invariant. */
static tree
ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
tree res_type)
ipa_get_jf_arith_result (enum tree_code opcode, tree input, tree operand,
tree res_type)
{
tree res;
if (ipa_get_jf_pass_through_operation (jfunc) == NOP_EXPR)
if (opcode == NOP_EXPR)
return input;
if (!is_gimple_ip_invariant (input))
return NULL_TREE;
tree_code opcode = ipa_get_jf_pass_through_operation (jfunc);
if (!res_type)
{
if (TREE_CODE_CLASS (opcode) == tcc_comparison)
@ -1317,8 +1317,7 @@ ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
if (TREE_CODE_CLASS (opcode) == tcc_unary)
res = fold_unary (opcode, res_type, input);
else
res = fold_binary (opcode, res_type, input,
ipa_get_jf_pass_through_operand (jfunc));
res = fold_binary (opcode, res_type, input, operand);
if (res && !is_gimple_ip_invariant (res))
return NULL_TREE;
@ -1326,6 +1325,21 @@ ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
return res;
}
/* Return the result of a (possibly arithmetic) pass through jump function
JFUNC on the constant value INPUT. RES_TYPE is the type of the parameter
to which the result is passed. Return NULL_TREE if that cannot be
determined or be considered an interprocedural invariant. */
static tree
ipa_get_jf_pass_through_result (struct ipa_jump_func *jfunc, tree input,
tree res_type)
{
return ipa_get_jf_arith_result (ipa_get_jf_pass_through_operation (jfunc),
input,
ipa_get_jf_pass_through_operand (jfunc),
res_type);
}
/* Return the result of an ancestor jump function JFUNC on the constant value
INPUT. Return NULL_TREE if that cannot be determined. */
@ -1459,6 +1473,146 @@ ipa_context_from_jfunc (ipa_node_params *info, cgraph_edge *cs, int csidx,
return ctx;
}
/* See if NODE is a clone with a known aggregate value at a given OFFSET of a
parameter with the given INDEX. */
static tree
get_clone_agg_value (struct cgraph_node *node, HOST_WIDE_INT offset,
int index)
{
struct ipa_agg_replacement_value *aggval;
aggval = ipa_get_agg_replacements_for_node (node);
while (aggval)
{
if (aggval->offset == offset
&& aggval->index == index)
return aggval->value;
aggval = aggval->next;
}
return NULL_TREE;
}
/* Determine whether ITEM, jump function for an aggregate part, evaluates to a
single known constant value and if so, return it. Otherwise return NULL.
NODE and INFO describes the caller node or the one it is inlined to, and
its related info. */
static tree
ipa_agg_value_from_node (class ipa_node_params *info,
struct cgraph_node *node,
struct ipa_agg_jf_item *item)
{
tree value = NULL_TREE;
int src_idx;
if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN)
return NULL_TREE;
if (item->jftype == IPA_JF_CONST)
return item->value.constant;
gcc_checking_assert (item->jftype == IPA_JF_PASS_THROUGH
|| item->jftype == IPA_JF_LOAD_AGG);
src_idx = item->value.pass_through.formal_id;
if (info->ipcp_orig_node)
{
if (item->jftype == IPA_JF_PASS_THROUGH)
value = info->known_csts[src_idx];
else
value = get_clone_agg_value (node, item->value.load_agg.offset,
src_idx);
}
else if (info->lattices)
{
class ipcp_param_lattices *src_plats
= ipa_get_parm_lattices (info, src_idx);
if (item->jftype == IPA_JF_PASS_THROUGH)
{
struct ipcp_lattice<tree> *lat = &src_plats->itself;
if (!lat->is_single_const ())
return NULL_TREE;
value = lat->values->value;
}
else if (src_plats->aggs
&& !src_plats->aggs_bottom
&& !src_plats->aggs_contain_variable
&& src_plats->aggs_by_ref == item->value.load_agg.by_ref)
{
struct ipcp_agg_lattice *aglat;
for (aglat = src_plats->aggs; aglat; aglat = aglat->next)
{
if (aglat->offset > item->value.load_agg.offset)
break;
if (aglat->offset == item->value.load_agg.offset)
{
if (aglat->is_single_const ())
value = aglat->values->value;
break;
}
}
}
}
if (!value)
return NULL_TREE;
if (item->jftype == IPA_JF_LOAD_AGG)
{
tree load_type = item->value.load_agg.type;
tree value_type = TREE_TYPE (value);
/* Ensure value type is compatible with load type. */
if (!useless_type_conversion_p (load_type, value_type))
return NULL_TREE;
}
return ipa_get_jf_arith_result (item->value.pass_through.operation,
value,
item->value.pass_through.operand,
item->type);
}
/* Determine whether AGG_JFUNC evaluates to a set of known constant value for
an aggregate and if so, return it. Otherwise return an empty set. NODE
and INFO describes the caller node or the one it is inlined to, and its
related info. */
struct ipa_agg_value_set
ipa_agg_value_set_from_jfunc (class ipa_node_params *info, cgraph_node *node,
struct ipa_agg_jump_function *agg_jfunc)
{
struct ipa_agg_value_set agg;
struct ipa_agg_jf_item *item;
int i;
agg.items = vNULL;
agg.by_ref = agg_jfunc->by_ref;
FOR_EACH_VEC_SAFE_ELT (agg_jfunc->items, i, item)
{
tree value = ipa_agg_value_from_node (info, node, item);
if (value)
{
struct ipa_agg_value value_item;
value_item.offset = item->offset;
value_item.value = value;
agg.items.safe_push (value_item);
}
}
return agg;
}
/* 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
the same time. */
@ -1638,6 +1792,57 @@ ipcp_lattice<valtype>::add_value (valtype newval, cgraph_edge *cs,
return true;
}
/* Propagate values through an arithmetic transformation described by a jump
function associated with edge CS, taking values from SRC_LAT and putting
them into DEST_LAT. OPND1_TYPE is expected type for the values in SRC_LAT.
OPND2 is a constant value if transformation is a binary operation.
SRC_OFFSET specifies offset in an aggregate if SRC_LAT describes lattice of
a part of the aggregate. SRC_IDX is the index of the source parameter.
RES_TYPE is the value type of result being propagated into. Return true if
DEST_LAT changed. */
static bool
propagate_vals_across_arith_jfunc (cgraph_edge *cs,
enum tree_code opcode,
tree opnd1_type,
tree opnd2,
ipcp_lattice<tree> *src_lat,
ipcp_lattice<tree> *dest_lat,
HOST_WIDE_INT src_offset,
int src_idx,
tree res_type)
{
ipcp_value<tree> *src_val;
bool ret = false;
/* Do not create new values when propagating within an SCC because if there
are arithmetic functions with circular dependencies, there is infinite
number of them and we would just make lattices bottom. If this condition
is ever relaxed we have to detect self-feeding recursive calls in
cgraph_edge_brings_value_p in a smarter way. */
if (opcode != NOP_EXPR && ipa_edge_within_scc (cs))
ret = dest_lat->set_contains_variable ();
else
for (src_val = src_lat->values; src_val; src_val = src_val->next)
{
tree opnd1 = src_val->value;
tree cstval = NULL_TREE;
/* Skip source values that is incompatible with specified type. */
if (!opnd1_type
|| useless_type_conversion_p (opnd1_type, TREE_TYPE (opnd1)))
cstval = ipa_get_jf_arith_result (opcode, opnd1, opnd2, res_type);
if (cstval)
ret |= dest_lat->add_value (cstval, cs, src_val, src_idx,
src_offset);
else
ret |= dest_lat->set_contains_variable ();
}
return ret;
}
/* Propagate values through a pass-through jump function JFUNC associated with
edge CS, taking values from SRC_LAT and putting them into DEST_LAT. SRC_IDX
is the index of the source parameter. PARM_TYPE is the type of the
@ -1649,30 +1854,11 @@ propagate_vals_across_pass_through (cgraph_edge *cs, ipa_jump_func *jfunc,
ipcp_lattice<tree> *dest_lat, int src_idx,
tree parm_type)
{
ipcp_value<tree> *src_val;
bool ret = false;
/* Do not create new values when propagating within an SCC because if there
are arithmetic functions with circular dependencies, there is infinite
number of them and we would just make lattices bottom. If this condition
is ever relaxed we have to detect self-feeding recursive calls in
cgraph_edge_brings_value_p in a smarter way. */
if ((ipa_get_jf_pass_through_operation (jfunc) != NOP_EXPR)
&& ipa_edge_within_scc (cs))
ret = dest_lat->set_contains_variable ();
else
for (src_val = src_lat->values; src_val; src_val = src_val->next)
{
tree cstval = ipa_get_jf_pass_through_result (jfunc, src_val->value,
parm_type);
if (cstval)
ret |= dest_lat->add_value (cstval, cs, src_val, src_idx);
else
ret |= dest_lat->set_contains_variable ();
}
return ret;
return propagate_vals_across_arith_jfunc (cs,
ipa_get_jf_pass_through_operation (jfunc),
NULL_TREE,
ipa_get_jf_pass_through_operand (jfunc),
src_lat, dest_lat, -1, src_idx, parm_type);
}
/* Propagate values through an ancestor jump function JFUNC associated with
@ -1835,7 +2021,6 @@ propagate_context_across_jump_function (cgraph_edge *cs,
added_sth = true;
}
}
}
prop_fail:
@ -2218,6 +2403,85 @@ agg_pass_through_permissible_p (class ipcp_param_lattices *src_plats,
|| ipa_get_jf_pass_through_agg_preserved (jfunc));
}
/* Propagate values through ITEM, jump function for a part of an aggregate,
into corresponding aggregate lattice AGLAT. CS is the call graph edge
associated with the jump function. Return true if AGLAT changed in any
way. */
static bool
propagate_aggregate_lattice (struct cgraph_edge *cs,
struct ipa_agg_jf_item *item,
struct ipcp_agg_lattice *aglat)
{
class ipa_node_params *caller_info;
class ipcp_param_lattices *src_plats;
struct ipcp_lattice<tree> *src_lat;
HOST_WIDE_INT src_offset;
int src_idx;
tree load_type;
bool ret;
if (item->jftype == IPA_JF_CONST)
{
tree value = item->value.constant;
gcc_checking_assert (is_gimple_ip_invariant (value));
return aglat->add_value (value, cs, NULL, 0);
}
gcc_checking_assert (item->jftype == IPA_JF_PASS_THROUGH
|| item->jftype == IPA_JF_LOAD_AGG);
caller_info = IPA_NODE_REF (cs->caller);
src_idx = item->value.pass_through.formal_id;
src_plats = ipa_get_parm_lattices (caller_info, src_idx);
if (item->jftype == IPA_JF_PASS_THROUGH)
{
load_type = NULL_TREE;
src_lat = &src_plats->itself;
src_offset = -1;
}
else
{
HOST_WIDE_INT load_offset = item->value.load_agg.offset;
struct ipcp_agg_lattice *src_aglat;
for (src_aglat = src_plats->aggs; src_aglat; src_aglat = src_aglat->next)
if (src_aglat->offset >= load_offset)
break;
load_type = item->value.load_agg.type;
if (!src_aglat
|| src_aglat->offset > load_offset
|| src_aglat->size != tree_to_shwi (TYPE_SIZE (load_type))
|| src_plats->aggs_by_ref != item->value.load_agg.by_ref)
return aglat->set_contains_variable ();
src_lat = src_aglat;
src_offset = load_offset;
}
if (src_lat->bottom
|| (!ipcp_versionable_function_p (cs->caller)
&& !src_lat->is_single_const ()))
return aglat->set_contains_variable ();
ret = propagate_vals_across_arith_jfunc (cs,
item->value.pass_through.operation,
load_type,
item->value.pass_through.operand,
src_lat, aglat,
src_offset,
src_idx,
item->type);
if (src_lat->contains_variable)
ret |= aglat->set_contains_variable ();
return ret;
}
/* Propagate scalar values across jump function JFUNC that is associated with
edge CS and put the values into DEST_LAT. */
@ -2285,15 +2549,14 @@ propagate_aggs_across_jump_function (struct cgraph_edge *cs,
{
HOST_WIDE_INT val_size;
if (item->offset < 0)
if (item->offset < 0 || item->jftype == IPA_JF_UNKNOWN)
continue;
gcc_checking_assert (is_gimple_ip_invariant (item->value));
val_size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (item->value)));
val_size = tree_to_shwi (TYPE_SIZE (item->type));
if (merge_agg_lats_step (dest_plats, item->offset, val_size,
&aglat, pre_existing, &ret))
{
ret |= (*aglat)->add_value (item->value, cs, NULL, 0, 0);
ret |= propagate_aggregate_lattice (cs, item, *aglat);
aglat = &(*aglat)->next;
}
else if (dest_plats->aggs_bottom)
@ -2410,7 +2673,7 @@ static tree
ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<ipa_agg_value_set> known_aggs,
struct ipa_agg_replacement_value *agg_reps,
bool *speculative)
{
@ -2448,9 +2711,9 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
}
if (!t)
{
struct ipa_agg_jump_function *agg;
struct ipa_agg_value_set *agg;
if (known_aggs.length () > (unsigned int) param_index)
agg = known_aggs[param_index];
agg = &known_aggs[param_index];
else
agg = NULL;
bool from_global_constant;
@ -2504,8 +2767,7 @@ ipa_get_indirect_edge_target_1 (struct cgraph_edge *ie,
if (!t && known_aggs.length () > (unsigned int) param_index
&& !ie->indirect_info->by_ref)
{
struct ipa_agg_jump_function *agg;
agg = known_aggs[param_index];
struct ipa_agg_value_set *agg = &known_aggs[param_index];
t = ipa_find_agg_cst_for_param (agg, known_csts[param_index],
ie->indirect_info->offset, true);
}
@ -2627,7 +2889,7 @@ tree
ipa_get_indirect_edge_target (struct cgraph_edge *ie,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<ipa_agg_value_set> known_aggs,
bool *speculative)
{
return ipa_get_indirect_edge_target_1 (ie, known_csts, known_contexts,
@ -2641,7 +2903,7 @@ static int
devirtualization_time_bonus (struct cgraph_node *node,
vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
vec<ipa_agg_value_set> known_aggs)
{
struct cgraph_edge *ie;
int res = 0;
@ -2776,25 +3038,25 @@ good_cloning_opportunity_p (struct cgraph_node *node, int time_benefit,
/* Return all context independent values from aggregate lattices in PLATS in a
vector. Return NULL if there are none. */
static vec<ipa_agg_jf_item, va_gc> *
static vec<ipa_agg_value>
context_independent_aggregate_values (class ipcp_param_lattices *plats)
{
vec<ipa_agg_jf_item, va_gc> *res = NULL;
vec<ipa_agg_value> res = vNULL;
if (plats->aggs_bottom
|| plats->aggs_contain_variable
|| plats->aggs_count == 0)
return NULL;
return vNULL;
for (struct ipcp_agg_lattice *aglat = plats->aggs;
aglat;
aglat = aglat->next)
if (aglat->is_single_const ())
{
struct ipa_agg_jf_item item;
struct ipa_agg_value item;
item.offset = aglat->offset;
item.value = aglat->values->value;
vec_safe_push (res, item);
res.safe_push (item);
}
return res;
}
@ -2810,7 +3072,7 @@ gather_context_independent_values (class ipa_node_params *info,
vec<tree> *known_csts,
vec<ipa_polymorphic_call_context>
*known_contexts,
vec<ipa_agg_jump_function> *known_aggs,
vec<ipa_agg_value_set> *known_aggs,
int *removable_params_cost)
{
int i, count = ipa_get_param_count (info);
@ -2860,40 +3122,20 @@ gather_context_independent_values (class ipa_node_params *info,
if (known_aggs)
{
vec<ipa_agg_jf_item, va_gc> *agg_items;
struct ipa_agg_jump_function *ajf;
vec<ipa_agg_value> agg_items;
struct ipa_agg_value_set *agg;
agg_items = context_independent_aggregate_values (plats);
ajf = &(*known_aggs)[i];
ajf->items = agg_items;
ajf->by_ref = plats->aggs_by_ref;
ret |= agg_items != NULL;
agg = &(*known_aggs)[i];
agg->items = agg_items;
agg->by_ref = plats->aggs_by_ref;
ret |= !agg_items.is_empty ();
}
}
return ret;
}
/* The current interface in ipa-inline-analysis requires a pointer vector.
Create it.
FIXME: That interface should be re-worked, this is slightly silly. Still,
I'd like to discuss how to change it first and this demonstrates the
issue. */
static vec<ipa_agg_jump_function_p>
agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> known_aggs)
{
vec<ipa_agg_jump_function_p> ret;
struct ipa_agg_jump_function *ajf;
int i;
ret.create (known_aggs.length ());
FOR_EACH_VEC_ELT (known_aggs, i, ajf)
ret.quick_push (ajf);
return ret;
}
/* Perform time and size measurement of NODE with the context given in
KNOWN_CSTS, KNOWN_CONTEXTS and KNOWN_AGGS, calculate the benefit and cost
given BASE_TIME of the node without specialization, REMOVABLE_PARAMS_COST of
@ -2903,7 +3145,7 @@ agg_jmp_p_vec_for_t_vec (vec<ipa_agg_jump_function> known_aggs)
static void
perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs_ptrs,
vec<ipa_agg_value_set> known_aggs,
int removable_params_cost,
int est_move_cost, ipcp_value_base *val)
{
@ -2912,7 +3154,7 @@ perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
ipa_hints hints;
estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
known_aggs_ptrs, &size, &time,
known_aggs, &size, &time,
&base_time, &hints);
base_time -= time;
if (base_time > 65535)
@ -2926,7 +3168,7 @@ perform_estimation_of_a_value (cgraph_node *node, vec<tree> known_csts,
else
time_benefit = base_time.to_int ()
+ devirtualization_time_bonus (node, known_csts, known_contexts,
known_aggs_ptrs)
known_aggs)
+ hint_time_bonus (hints)
+ removable_params_cost + est_move_cost;
@ -2952,8 +3194,7 @@ estimate_local_effects (struct cgraph_node *node)
int i, count = ipa_get_param_count (info);
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;
vec<ipa_agg_value_set> known_aggs;
bool always_const;
int removable_params_cost;
@ -2966,9 +3207,8 @@ estimate_local_effects (struct cgraph_node *node)
always_const = gather_context_independent_values (info, &known_csts,
&known_contexts, &known_aggs,
&removable_params_cost);
known_aggs_ptrs = agg_jmp_p_vec_for_t_vec (known_aggs);
int devirt_bonus = devirtualization_time_bonus (node, known_csts,
known_contexts, known_aggs_ptrs);
known_contexts, known_aggs);
if (always_const || devirt_bonus
|| (removable_params_cost && node->can_change_signature))
{
@ -2981,7 +3221,7 @@ estimate_local_effects (struct cgraph_node *node)
node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
false);
estimate_ipcp_clone_size_and_time (node, known_csts, known_contexts,
known_aggs_ptrs, &size, &time,
known_aggs, &size, &time,
&base_time, &hints);
time -= devirt_bonus;
time -= hint_time_bonus (hints);
@ -3044,7 +3284,7 @@ estimate_local_effects (struct cgraph_node *node)
int emc = estimate_move_cost (TREE_TYPE (val->value), true);
perform_estimation_of_a_value (node, known_csts, known_contexts,
known_aggs_ptrs,
known_aggs,
removable_params_cost, emc, val);
if (dump_file && (dump_flags & TDF_DETAILS))
@ -3079,7 +3319,7 @@ estimate_local_effects (struct cgraph_node *node)
{
known_contexts[i] = val->value;
perform_estimation_of_a_value (node, known_csts, known_contexts,
known_aggs_ptrs,
known_aggs,
removable_params_cost, 0, val);
if (dump_file && (dump_flags & TDF_DETAILS))
@ -3098,13 +3338,13 @@ estimate_local_effects (struct cgraph_node *node)
for (i = 0; i < count; i++)
{
class ipcp_param_lattices *plats = ipa_get_parm_lattices (info, i);
struct ipa_agg_jump_function *ajf;
struct ipa_agg_value_set *agg;
struct ipcp_agg_lattice *aglat;
if (plats->aggs_bottom || !plats->aggs)
continue;
ajf = &known_aggs[i];
agg = &known_aggs[i];
for (aglat = plats->aggs; aglat; aglat = aglat->next)
{
ipcp_value<tree> *val;
@ -3116,14 +3356,14 @@ estimate_local_effects (struct cgraph_node *node)
for (val = aglat->values; val; val = val->next)
{
struct ipa_agg_jf_item item;
struct ipa_agg_value item;
item.offset = aglat->offset;
item.value = val->value;
vec_safe_push (ajf->items, item);
agg->items.safe_push (item);
perform_estimation_of_a_value (node, known_csts, known_contexts,
known_aggs_ptrs,
known_aggs,
removable_params_cost, 0, val);
if (dump_file && (dump_flags & TDF_DETAILS))
@ -3139,18 +3379,14 @@ estimate_local_effects (struct cgraph_node *node)
val->local_time_benefit, val->local_size_cost);
}
ajf->items->pop ();
agg->items.pop ();
}
}
}
for (i = 0; i < count; i++)
vec_free (known_aggs[i].items);
known_csts.release ();
known_contexts.release ();
known_aggs.release ();
known_aggs_ptrs.release ();
ipa_release_agg_values (known_aggs);
}
@ -3532,26 +3768,6 @@ edge_clone_summary_t::duplicate (cgraph_edge *src_edge, cgraph_edge *dst_edge,
src_data->next_clone = dst_edge;
}
/* See if NODE is a clone with a known aggregate value at a given OFFSET of a
parameter with the given INDEX. */
static tree
get_clone_agg_value (struct cgraph_node *node, HOST_WIDE_INT offset,
int index)
{
struct ipa_agg_replacement_value *aggval;
aggval = ipa_get_agg_replacements_for_node (node);
while (aggval)
{
if (aggval->offset == offset
&& aggval->index == index)
return aggval->value;
aggval = aggval->next;
}
return NULL_TREE;
}
/* Return true is NODE is DEST or its clone for all contexts. */
static bool
@ -4237,10 +4453,10 @@ find_more_contexts_for_caller_subset (cgraph_node *node,
/* Go through PLATS and create a vector of values consisting of values and
offsets (minus OFFSET) of lattices that contain only a single value. */
static vec<ipa_agg_jf_item>
static vec<ipa_agg_value>
copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
{
vec<ipa_agg_jf_item> res = vNULL;
vec<ipa_agg_value> res = vNULL;
if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom)
return vNULL;
@ -4248,7 +4464,7 @@ copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
for (struct ipcp_agg_lattice *aglat = plats->aggs; aglat; aglat = aglat->next)
if (aglat->is_single_const ())
{
struct ipa_agg_jf_item ti;
struct ipa_agg_value ti;
ti.offset = aglat->offset - offset;
ti.value = aglat->values->value;
res.safe_push (ti);
@ -4261,11 +4477,11 @@ copy_plats_to_inter (class ipcp_param_lattices *plats, HOST_WIDE_INT offset)
static void
intersect_with_plats (class ipcp_param_lattices *plats,
vec<ipa_agg_jf_item> *inter,
vec<ipa_agg_value> *inter,
HOST_WIDE_INT offset)
{
struct ipcp_agg_lattice *aglat;
struct ipa_agg_jf_item *item;
struct ipa_agg_value *item;
int k;
if (!plats->aggs || plats->aggs_contain_variable || plats->aggs_bottom)
@ -4303,18 +4519,18 @@ intersect_with_plats (class ipcp_param_lattices *plats,
/* Copy aggregate replacement values of NODE (which is an IPA-CP clone) to the
vector result while subtracting OFFSET from the individual value offsets. */
static vec<ipa_agg_jf_item>
static vec<ipa_agg_value>
agg_replacements_to_vector (struct cgraph_node *node, int index,
HOST_WIDE_INT offset)
{
struct ipa_agg_replacement_value *av;
vec<ipa_agg_jf_item> res = vNULL;
vec<ipa_agg_value> res = vNULL;
for (av = ipa_get_agg_replacements_for_node (node); av; av = av->next)
if (av->index == index
&& (av->offset - offset) >= 0)
{
struct ipa_agg_jf_item item;
struct ipa_agg_value item;
gcc_checking_assert (av->value);
item.offset = av->offset - offset;
item.value = av->value;
@ -4330,11 +4546,11 @@ agg_replacements_to_vector (struct cgraph_node *node, int index,
static void
intersect_with_agg_replacements (struct cgraph_node *node, int index,
vec<ipa_agg_jf_item> *inter,
vec<ipa_agg_value> *inter,
HOST_WIDE_INT offset)
{
struct ipa_agg_replacement_value *srcvals;
struct ipa_agg_jf_item *item;
struct ipa_agg_value *item;
int i;
srcvals = ipa_get_agg_replacements_for_node (node);
@ -4371,9 +4587,9 @@ intersect_with_agg_replacements (struct cgraph_node *node, int index,
copy all incoming values to it. If we determine we ended up with no values
whatsoever, return a released vector. */
static vec<ipa_agg_jf_item>
static vec<ipa_agg_value>
intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
vec<ipa_agg_jf_item> inter)
vec<ipa_agg_value> inter)
{
struct ipa_jump_func *jfunc;
jfunc = ipa_get_ith_jump_func (IPA_EDGE_REF (cs), index);
@ -4454,12 +4670,26 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
}
else if (jfunc->agg.items)
{
struct ipa_agg_jf_item *item;
class ipa_node_params *caller_info = IPA_NODE_REF (cs->caller);
struct ipa_agg_value *item;
int k;
if (!inter.exists ())
for (unsigned i = 0; i < jfunc->agg.items->length (); i++)
inter.safe_push ((*jfunc->agg.items)[i]);
{
struct ipa_agg_jf_item *agg_item = &(*jfunc->agg.items)[i];
tree value = ipa_agg_value_from_node (caller_info, cs->caller,
agg_item);
if (value)
{
struct ipa_agg_value agg_value;
agg_value.offset = agg_item->offset;
agg_value.value = value;
inter.safe_push (agg_value);
}
}
else
FOR_EACH_VEC_ELT (inter, k, item)
{
@ -4477,9 +4707,10 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
break;
if (ti->offset == item->offset)
{
gcc_checking_assert (ti->value);
if (values_equal_for_ipcp_p (item->value,
ti->value))
tree value = ipa_agg_value_from_node (caller_info,
cs->caller, ti);
if (value
&& values_equal_for_ipcp_p (item->value, value))
found = true;
break;
}
@ -4492,7 +4723,7 @@ intersect_aggregates_with_edge (struct cgraph_edge *cs, int index,
else
{
inter.release ();
return vec<ipa_agg_jf_item>();
return vNULL;
}
return inter;
}
@ -4525,8 +4756,8 @@ find_aggregate_values_for_callers_subset (struct cgraph_node *node,
for (i = 0; i < count; i++)
{
struct cgraph_edge *cs;
vec<ipa_agg_jf_item> inter = vNULL;
struct ipa_agg_jf_item *item;
vec<ipa_agg_value> inter = vNULL;
struct ipa_agg_value *item;
class ipcp_param_lattices *plats = ipa_get_parm_lattices (dest_info, i);
int j;
@ -4633,7 +4864,7 @@ cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
for (i = 0; i < count; i++)
{
static vec<ipa_agg_jf_item> values = vec<ipa_agg_jf_item>();
static vec<ipa_agg_value> values = vNULL;
class ipcp_param_lattices *plats;
bool interesting = false;
for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
@ -4656,7 +4887,7 @@ cgraph_edge_brings_all_agg_vals_for_node (struct cgraph_edge *cs,
for (struct ipa_agg_replacement_value *av = aggval; av; av = av->next)
if (aggval->index == i)
{
struct ipa_agg_jf_item *item;
struct ipa_agg_value *item;
int j;
bool found = false;
FOR_EACH_VEC_ELT (values, j, item)
@ -4894,7 +5125,6 @@ decide_whether_version_node (struct cgraph_node *node)
int i, count = ipa_get_param_count (info);
vec<tree> known_csts;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function> known_aggs = vNULL;
bool ret = false;
if (count == 0)
@ -4905,8 +5135,7 @@ decide_whether_version_node (struct cgraph_node *node)
node->dump_name ());
gather_context_independent_values (info, &known_csts, &known_contexts,
info->do_clone_for_all_contexts ? &known_aggs
: NULL, NULL);
NULL, NULL);
for (i = 0; i < count;i++)
{
@ -4975,9 +5204,6 @@ decide_whether_version_node (struct cgraph_node *node)
info = IPA_NODE_REF (node);
info->do_clone_for_all_contexts = false;
IPA_NODE_REF (clone)->is_all_contexts_clone = true;
for (i = 0; i < count; i++)
vec_free (known_aggs[i].items);
known_aggs.release ();
ret = true;
}
else

View File

@ -306,9 +306,9 @@ set_hint_predicate (predicate **p, predicate new_predicate)
the fact that parameter is indeed a constant.
KNOWN_VALS is partial mapping of parameters of NODE to constant values.
KNOWN_AGGS is a vector of aggreggate jump functions for each parameter.
Return clause of possible truths. When INLINE_P is true, assume that we are
inlining.
KNOWN_AGGS is a vector of aggreggate known offset/value set for each
parameter. Return clause of possible truths. When INLINE_P is true, assume
that we are inlining.
ERROR_MARK means compile time invariant. */
@ -316,8 +316,7 @@ static void
evaluate_conditions_for_known_args (struct cgraph_node *node,
bool inline_p,
vec<tree> known_vals,
vec<ipa_agg_jump_function_p>
known_aggs,
vec<ipa_agg_value_set> known_aggs,
clause_t *ret_clause,
clause_t *ret_nonspec_clause)
{
@ -349,7 +348,7 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
if (c->agg_contents)
{
struct ipa_agg_jump_function *agg;
struct ipa_agg_value_set *agg;
if (c->code == predicate::changed
&& !c->by_ref
@ -358,7 +357,7 @@ evaluate_conditions_for_known_args (struct cgraph_node *node,
if (known_aggs.exists ())
{
agg = known_aggs[c->operand_num];
agg = &known_aggs[c->operand_num];
val = ipa_find_agg_cst_for_param (agg, known_vals[c->operand_num],
c->offset, c->by_ref);
}
@ -445,12 +444,12 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
vec<tree> *known_vals_ptr,
vec<ipa_polymorphic_call_context>
*known_contexts_ptr,
vec<ipa_agg_jump_function_p> *known_aggs_ptr)
vec<ipa_agg_value_set> *known_aggs_ptr)
{
struct cgraph_node *callee = e->callee->ultimate_alias_target ();
class ipa_fn_summary *info = ipa_fn_summaries->get (callee);
vec<tree> known_vals = vNULL;
vec<ipa_agg_jump_function_p> known_aggs = vNULL;
vec<ipa_agg_value_set> known_aggs = vNULL;
class ipa_edge_args *args;
if (clause_ptr)
@ -465,14 +464,16 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
&& ((clause_ptr && info->conds) || known_vals_ptr || known_contexts_ptr)
&& (args = IPA_EDGE_REF (e)) != NULL)
{
struct cgraph_node *caller;
class ipa_node_params *caller_parms_info, *callee_pi;
class ipa_call_summary *es = ipa_call_summaries->get (e);
int i, count = ipa_get_cs_argument_count (args);
if (e->caller->inlined_to)
caller_parms_info = IPA_NODE_REF (e->caller->inlined_to);
caller = e->caller->inlined_to;
else
caller_parms_info = IPA_NODE_REF (e->caller);
caller = e->caller;
caller_parms_info = IPA_NODE_REF (caller);
callee_pi = IPA_NODE_REF (callee);
if (count && (info->conds || known_vals_ptr))
@ -508,10 +509,9 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
if (known_contexts_ptr)
(*known_contexts_ptr)[i]
= ipa_context_from_jfunc (caller_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. */
known_aggs[i] = &jf->agg;
known_aggs[i] = ipa_agg_value_set_from_jfunc (caller_parms_info,
caller, &jf->agg);
}
else
gcc_assert (callee->thunk.thunk_p);
@ -545,7 +545,7 @@ evaluate_properties_for_edge (struct cgraph_edge *e, bool inline_p,
if (known_aggs_ptr)
*known_aggs_ptr = known_aggs;
else
known_aggs.release ();
ipa_release_agg_values (known_aggs);
}
@ -2838,7 +2838,7 @@ estimate_edge_devirt_benefit (struct cgraph_edge *ie,
int *size, int *time,
vec<tree> known_vals,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
vec<ipa_agg_value_set> known_aggs)
{
tree target;
struct cgraph_node *callee;
@ -2887,7 +2887,7 @@ estimate_edge_size_and_time (struct cgraph_edge *e, int *size, int *min_size,
int prob,
vec<tree> known_vals,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<ipa_agg_value_set> known_aggs,
ipa_hints *hints)
{
class ipa_call_summary *es = ipa_call_summaries->get (e);
@ -2923,7 +2923,7 @@ estimate_calls_size_and_time (struct cgraph_node *node, int *size,
clause_t possible_truths,
vec<tree> known_vals,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs)
vec<ipa_agg_value_set> known_aggs)
{
struct cgraph_edge *e;
for (e = node->callees; e; e = e->next_callee)
@ -2983,7 +2983,7 @@ ipa_call_context::ipa_call_context (cgraph_node *node,
vec<tree> known_vals,
vec<ipa_polymorphic_call_context>
known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<ipa_agg_value_set> known_aggs,
vec<inline_param_summary>
inline_param_summary)
: m_node (node), m_possible_truths (possible_truths),
@ -3057,9 +3057,9 @@ ipa_call_context::duplicate_from (const ipa_call_context &ctx)
for (unsigned int i = 0; i < n; i++)
if (ipa_is_param_used_by_indirect_call (params_summary, i)
&& ctx.m_known_aggs[i])
&& !ctx.m_known_aggs[i].is_empty ())
{
m_known_aggs = ctx.m_known_aggs.copy ();
m_known_aggs = ipa_copy_agg_values (ctx.m_known_aggs);
break;
}
}
@ -3078,7 +3078,7 @@ ipa_call_context::release (bool all)
return;
m_known_vals.release ();
m_known_contexts.release ();
m_known_aggs.release ();
ipa_release_agg_values (m_known_aggs);
if (all)
m_inline_param_summary.release ();
}
@ -3179,19 +3179,22 @@ ipa_call_context::equal_to (const ipa_call_context &ctx)
{
if (!ipa_is_param_used_by_indirect_call (params_summary, i))
continue;
if (i >= m_known_aggs.length () || !m_known_aggs[i])
if (i >= m_known_aggs.length () || m_known_aggs[i].is_empty ())
{
if (i < ctx.m_known_aggs.length () && ctx.m_known_aggs[i])
if (i < ctx.m_known_aggs.length ()
&& !ctx.m_known_aggs[i].is_empty ())
return false;
continue;
}
if (i >= ctx.m_known_aggs.length () || !ctx.m_known_aggs[i])
if (i >= ctx.m_known_aggs.length ()
|| ctx.m_known_aggs[i].is_empty ())
{
if (i < m_known_aggs.length () && m_known_aggs[i])
if (i < m_known_aggs.length ()
&& !m_known_aggs[i].is_empty ())
return false;
continue;
}
if (m_known_aggs[i] != ctx.m_known_aggs[i])
if (!m_known_aggs[i].equal_to (ctx.m_known_aggs[i]))
return false;
}
}
@ -3348,7 +3351,7 @@ estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
vec<tree> known_vals,
vec<ipa_polymorphic_call_context>
known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<ipa_agg_value_set> known_aggs,
int *ret_size, sreal *ret_time,
sreal *ret_nonspec_time,
ipa_hints *hints)

View File

@ -293,7 +293,7 @@ public:
clause_t nonspec_possible_truths,
vec<tree> known_vals,
vec<ipa_polymorphic_call_context> known_contexts,
vec<ipa_agg_jump_function_p> known_aggs,
vec<ipa_agg_value_set> known_aggs,
vec<inline_param_summary> m_inline_param_summary);
ipa_call_context ()
: m_node(NULL)
@ -329,7 +329,7 @@ private:
/* Vector describing known polymorphic call contexts. */
vec<ipa_polymorphic_call_context> m_known_contexts;
/* Vector describing known aggregate values. */
vec<ipa_agg_jump_function_p> m_known_aggs;
vec<ipa_agg_value_set> m_known_aggs;
};
extern fast_call_summary <ipa_call_summary *, va_heap> *ipa_call_summaries;
@ -345,7 +345,7 @@ void inline_analyze_function (struct cgraph_node *node);
void estimate_ipcp_clone_size_and_time (struct cgraph_node *,
vec<tree>,
vec<ipa_polymorphic_call_context>,
vec<ipa_agg_jump_function_p>,
vec<ipa_agg_value_set>,
int *, sreal *, sreal *,
ipa_hints *);
void ipa_merge_fn_summary_after_inlining (struct cgraph_edge *edge);
@ -360,7 +360,7 @@ void evaluate_properties_for_edge (struct cgraph_edge *e,
vec<tree> *known_vals_ptr,
vec<ipa_polymorphic_call_context>
*known_contexts_ptr,
vec<ipa_agg_jump_function_p> *);
vec<ipa_agg_value_set> *);
void ipa_fnsummary_c_finalize (void);
HOST_WIDE_INT ipa_get_stack_frame_offset (struct cgraph_node *node);

View File

@ -188,7 +188,7 @@ do_estimate_edge_time (struct cgraph_edge *edge)
clause_t clause, nonspec_clause;
vec<tree> known_vals;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
vec<ipa_agg_value_set> known_aggs;
class ipa_call_summary *es = ipa_call_summaries->get (edge);
int min_size = -1;
@ -308,7 +308,7 @@ do_estimate_edge_size (struct cgraph_edge *edge)
clause_t clause, nonspec_clause;
vec<tree> known_vals;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
vec<ipa_agg_value_set> known_aggs;
/* When we do caching, use do_estimate_edge_time to populate the entry. */
@ -347,7 +347,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)
clause_t clause, nonspec_clause;
vec<tree> known_vals;
vec<ipa_polymorphic_call_context> known_contexts;
vec<ipa_agg_jump_function_p> known_aggs;
vec<ipa_agg_value_set> known_aggs;
/* When we do caching, use do_estimate_edge_time to populate the entry. */

View File

@ -360,18 +360,45 @@ ipa_print_node_jump_functions_for_edge (FILE *f, struct cgraph_edge *cs)
fprintf (f, " Aggregate passed by %s:\n",
jump_func->agg.by_ref ? "reference" : "value");
FOR_EACH_VEC_SAFE_ELT (jump_func->agg.items, j, item)
FOR_EACH_VEC_ELT (*jump_func->agg.items, j, item)
{
fprintf (f, " offset: " HOST_WIDE_INT_PRINT_DEC ", ",
item->offset);
if (TYPE_P (item->value))
fprintf (f, "clobber of " HOST_WIDE_INT_PRINT_DEC " bits",
tree_to_uhwi (TYPE_SIZE (item->value)));
else
fprintf (f, "type: ");
print_generic_expr (f, item->type);
fprintf (f, ", ");
if (item->jftype == IPA_JF_PASS_THROUGH)
fprintf (f, "PASS THROUGH: %d,",
item->value.pass_through.formal_id);
else if (item->jftype == IPA_JF_LOAD_AGG)
{
fprintf (f, "cst: ");
print_generic_expr (f, item->value);
fprintf (f, "LOAD AGG: %d",
item->value.pass_through.formal_id);
fprintf (f, " [offset: " HOST_WIDE_INT_PRINT_DEC ", by %s],",
item->value.load_agg.offset,
item->value.load_agg.by_ref ? "reference"
: "value");
}
if (item->jftype == IPA_JF_PASS_THROUGH
|| item->jftype == IPA_JF_LOAD_AGG)
{
fprintf (f, " op %s",
get_tree_code_name (item->value.pass_through.operation));
if (item->value.pass_through.operation != NOP_EXPR)
{
fprintf (f, " ");
print_generic_expr (f, item->value.pass_through.operand);
}
}
else if (item->jftype == IPA_JF_CONST)
{
fprintf (f, "CONST: ");
print_generic_expr (f, item->value.constant);
}
else if (item->jftype == IPA_JF_UNKNOWN)
fprintf (f, "UNKNOWN: " HOST_WIDE_INT_PRINT_DEC " bits",
tree_to_uhwi (TYPE_SIZE (item->type)));
fprintf (f, "\n");
}
}
@ -1138,6 +1165,67 @@ ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
return false;
}
/* If STMT is an assignment that loads a value from a parameter declaration,
or from an aggregate passed as the parameter either by value or reference,
return the index of the parameter in ipa_node_params. Otherwise return -1.
FBI holds gathered information about the function. INFO describes
parameters of the function, STMT is the assignment statement. If it is a
memory load from an aggregate, *OFFSET_P is filled with offset within the
aggregate, and *BY_REF_P specifies whether the aggregate is passed by
reference. */
static int
load_from_unmodified_param_or_agg (struct ipa_func_body_info *fbi,
class ipa_node_params *info,
gimple *stmt,
HOST_WIDE_INT *offset_p,
bool *by_ref_p)
{
int index = load_from_unmodified_param (fbi, info->descriptors, stmt);
poly_int64 size;
/* Load value from a parameter declaration. */
if (index >= 0)
{
*offset_p = -1;
return index;
}
if (!gimple_assign_load_p (stmt))
return -1;
tree rhs = gimple_assign_rhs1 (stmt);
/* Skip memory reference containing VIEW_CONVERT_EXPR. */
for (tree t = rhs; handled_component_p (t); t = TREE_OPERAND (t, 0))
if (TREE_CODE (t) == VIEW_CONVERT_EXPR)
return -1;
/* Skip memory reference containing bit-field. */
if (TREE_CODE (rhs) == BIT_FIELD_REF
|| contains_bitfld_component_ref_p (rhs))
return -1;
if (!ipa_load_from_parm_agg (fbi, info->descriptors, stmt, rhs, &index,
offset_p, &size, by_ref_p))
return -1;
gcc_assert (!maybe_ne (tree_to_poly_int64 (TYPE_SIZE (TREE_TYPE (rhs))),
size));
if (!*by_ref_p)
{
tree param_type = ipa_get_type (info, index);
if (!param_type || !AGGREGATE_TYPE_P (param_type))
return -1;
}
else if (TREE_THIS_VOLATILE (rhs))
return -1;
return index;
}
/* Given that an actual argument is an SSA_NAME (given in NAME) and is a result
of an assignment statement STMT, try to determine whether we are actually
handling any of the following cases and construct an appropriate jump
@ -1441,11 +1529,11 @@ type_like_member_ptr_p (tree type, tree *method_ptr, tree *delta)
}
/* If RHS is an SSA_NAME and it is defined by a simple copy assign statement,
return the rhs of its defining statement. Otherwise return RHS as it
is. */
return the rhs of its defining statement, and this statement is stored in
*RHS_STMT. Otherwise return RHS as it is. */
static inline tree
get_ssa_def_if_simple_copy (tree rhs)
get_ssa_def_if_simple_copy (tree rhs, gimple **rhs_stmt)
{
while (TREE_CODE (rhs) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (rhs))
{
@ -1455,25 +1543,31 @@ get_ssa_def_if_simple_copy (tree rhs)
rhs = gimple_assign_rhs1 (def_stmt);
else
break;
*rhs_stmt = def_stmt;
}
return rhs;
}
/* Simple linked list, describing known contents of an aggregate before
call. */
/* Simple linked list, describing contents of an aggregate before call. */
struct ipa_known_agg_contents_list
{
/* Offset and size of the described part of the aggregate. */
HOST_WIDE_INT offset, size;
/* Known constant value or NULL if the contents is known to be unknown. */
tree constant;
/* Type of the described part of the aggregate. */
tree type;
/* Known constant value or jump function data describing contents. */
struct ipa_load_agg_data value;
/* Pointer to the next structure in the list. */
struct ipa_known_agg_contents_list *next;
};
/* Add a known content item into a linked list of ipa_known_agg_contents_list
structure, in which all elements are sorted ascendingly by offset. */
/* Add an aggregate content item into a linked list of
ipa_known_agg_contents_list structure, in which all elements
are sorted ascendingly by offset. */
static inline void
add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
@ -1493,7 +1587,7 @@ add_to_agg_contents_list (struct ipa_known_agg_contents_list **plist,
*plist = item;
}
/* Check whether a given known content is clobbered by certain element in
/* Check whether a given aggregate content is clobbered by certain element in
a linked list of ipa_known_agg_contents_list. */
static inline bool
@ -1513,29 +1607,195 @@ clobber_by_agg_contents_list_p (struct ipa_known_agg_contents_list *list,
}
/* Build aggregate jump function from LIST, assuming there are exactly
CONST_COUNT constant entries there and that offset of the passed argument
VALUE_COUNT entries there and that offset of the passed argument
is ARG_OFFSET and store it into JFUNC. */
static void
build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
int const_count, HOST_WIDE_INT arg_offset,
int value_count, HOST_WIDE_INT arg_offset,
struct ipa_jump_func *jfunc)
{
vec_alloc (jfunc->agg.items, const_count);
while (list)
vec_alloc (jfunc->agg.items, value_count);
for (; list; list = list->next)
{
if (list->constant)
struct ipa_agg_jf_item item;
tree operand = list->value.pass_through.operand;
if (list->value.pass_through.formal_id >= 0)
{
struct ipa_agg_jf_item item;
item.offset = list->offset - arg_offset;
gcc_assert ((item.offset % BITS_PER_UNIT) == 0);
item.value = unshare_expr_without_location (list->constant);
jfunc->agg.items->quick_push (item);
/* Content value is derived from some formal paramerter. */
if (list->value.offset >= 0)
item.jftype = IPA_JF_LOAD_AGG;
else
item.jftype = IPA_JF_PASS_THROUGH;
item.value.load_agg = list->value;
if (operand)
item.value.pass_through.operand
= unshare_expr_without_location (operand);
}
list = list->next;
else if (operand)
{
/* Content value is known constant. */
item.jftype = IPA_JF_CONST;
item.value.constant = unshare_expr_without_location (operand);
}
else
continue;
item.type = list->type;
gcc_assert (tree_to_shwi (TYPE_SIZE (list->type)) == list->size);
item.offset = list->offset - arg_offset;
gcc_assert ((item.offset % BITS_PER_UNIT) == 0);
jfunc->agg.items->quick_push (item);
}
}
/* Given an assignment statement STMT, try to collect information into
AGG_VALUE that will be used to construct jump function for RHS of the
assignment, from which content value of an aggregate part comes.
Besides constant and simple pass-through jump functions, also try to
identify whether it matches the following pattern that can be described by
a load-value-from-aggregate jump function, which is a derivative of simple
pass-through jump function.
foo (int *p)
{
...
*(q_5 + 4) = *(p_3(D) + 28) op 1;
bar (q_5);
}
Here IPA_LOAD_AGG_DATA data structure is informative enough to describe
constant, simple pass-through and load-vale-from-aggregate. If value
is constant, it will be kept in field OPERAND, and field FORMAL_ID is
set to -1. For simple pass-through and load-value-from-aggregate, field
FORMAL_ID specifies the related formal parameter index, and field
OFFSET can be used to distinguish them, -1 means simple pass-through,
otherwise means load-value-from-aggregate. */
static void
analyze_agg_content_value (struct ipa_func_body_info *fbi,
struct ipa_load_agg_data *agg_value,
gimple *stmt)
{
tree lhs = gimple_assign_lhs (stmt);
tree rhs1 = gimple_assign_rhs1 (stmt);
enum tree_code code;
int index = -1;
/* Initialize jump function data for the aggregate part. */
memset (agg_value, 0, sizeof (*agg_value));
agg_value->pass_through.operation = NOP_EXPR;
agg_value->pass_through.formal_id = -1;
agg_value->offset = -1;
if (AGGREGATE_TYPE_P (TREE_TYPE (lhs)) /* TODO: Support aggregate type. */
|| TREE_THIS_VOLATILE (lhs)
|| TREE_CODE (lhs) == BIT_FIELD_REF
|| contains_bitfld_component_ref_p (lhs))
return;
/* Skip SSA copies. */
while (gimple_assign_rhs_class (stmt) == GIMPLE_SINGLE_RHS)
{
if (TREE_CODE (rhs1) != SSA_NAME || SSA_NAME_IS_DEFAULT_DEF (rhs1))
break;
stmt = SSA_NAME_DEF_STMT (rhs1);
if (!is_gimple_assign (stmt))
return;
rhs1 = gimple_assign_rhs1 (stmt);
}
code = gimple_assign_rhs_code (stmt);
switch (gimple_assign_rhs_class (stmt))
{
case GIMPLE_SINGLE_RHS:
if (is_gimple_ip_invariant (rhs1))
{
agg_value->pass_through.operand = rhs1;
return;
}
code = NOP_EXPR;
break;
case GIMPLE_UNARY_RHS:
/* NOTE: A GIMPLE_UNARY_RHS operation might not be tcc_unary
(truth_not_expr is example), GIMPLE_BINARY_RHS does not imply
tcc_binary, this subtleness is somewhat misleading.
Since tcc_unary is widely used in IPA-CP code to check an operation
with one operand, here we only allow tc_unary operation to avoid
possible problem. Then we can use (opclass == tc_unary) or not to
distinguish unary and binary. */
if (TREE_CODE_CLASS (code) != tcc_unary || CONVERT_EXPR_CODE_P (code))
return;
rhs1 = get_ssa_def_if_simple_copy (rhs1, &stmt);
break;
case GIMPLE_BINARY_RHS:
{
gimple *rhs1_stmt = stmt;
gimple *rhs2_stmt = stmt;
tree rhs2 = gimple_assign_rhs2 (stmt);
rhs1 = get_ssa_def_if_simple_copy (rhs1, &rhs1_stmt);
rhs2 = get_ssa_def_if_simple_copy (rhs2, &rhs2_stmt);
if (is_gimple_ip_invariant (rhs2))
{
agg_value->pass_through.operand = rhs2;
stmt = rhs1_stmt;
}
else if (is_gimple_ip_invariant (rhs1))
{
if (TREE_CODE_CLASS (code) == tcc_comparison)
code = swap_tree_comparison (code);
else if (!commutative_tree_code (code))
return;
agg_value->pass_through.operand = rhs1;
stmt = rhs2_stmt;
rhs1 = rhs2;
}
else
return;
if (TREE_CODE_CLASS (code) != tcc_comparison
&& !useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs1)))
return;
}
break;
default:
return;
}
if (TREE_CODE (rhs1) != SSA_NAME)
index = load_from_unmodified_param_or_agg (fbi, fbi->info, stmt,
&agg_value->offset,
&agg_value->by_ref);
else if (SSA_NAME_IS_DEFAULT_DEF (rhs1))
index = ipa_get_param_decl_index (fbi->info, SSA_NAME_VAR (rhs1));
if (index >= 0)
{
if (agg_value->offset >= 0)
agg_value->type = TREE_TYPE (rhs1);
agg_value->pass_through.formal_id = index;
agg_value->pass_through.operation = code;
}
else
agg_value->pass_through.operand = NULL_TREE;
}
/* If STMT is a memory store to the object whose address is BASE, extract
information (offset, size, and value) into CONTENT, and return true,
otherwise we conservatively assume the whole object is modified with
@ -1543,26 +1803,19 @@ build_agg_jump_func_from_list (struct ipa_known_agg_contents_list *list,
is expected to be in form of MEM_REF expression. */
static bool
extract_mem_content (gimple *stmt, tree base, bool check_ref,
extract_mem_content (struct ipa_func_body_info *fbi,
gimple *stmt, tree base, bool check_ref,
struct ipa_known_agg_contents_list *content)
{
HOST_WIDE_INT lhs_offset, lhs_size;
tree lhs, rhs, lhs_base;
bool reverse;
if (!gimple_assign_single_p (stmt))
if (!is_gimple_assign (stmt))
return false;
lhs = gimple_assign_lhs (stmt);
rhs = gimple_assign_rhs1 (stmt);
if (!is_gimple_reg_type (TREE_TYPE (rhs))
|| TREE_CODE (lhs) == BIT_FIELD_REF
|| contains_bitfld_component_ref_p (lhs))
return false;
lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset,
&lhs_size, &reverse);
tree lhs = gimple_assign_lhs (stmt);
tree lhs_base = get_ref_base_and_extent_hwi (lhs, &lhs_offset, &lhs_size,
&reverse);
if (!lhs_base)
return false;
@ -1576,39 +1829,37 @@ extract_mem_content (gimple *stmt, tree base, bool check_ref,
else if (lhs_base != base)
return false;
rhs = get_ssa_def_if_simple_copy (rhs);
content->size = lhs_size;
content->offset = lhs_offset;
content->constant = is_gimple_ip_invariant (rhs) ? rhs : NULL_TREE;
content->size = lhs_size;
content->type = TREE_TYPE (lhs);
content->next = NULL;
analyze_agg_content_value (fbi, &content->value, stmt);
return true;
}
/* Traverse statements from CALL backwards, scanning whether an aggregate given
in ARG is filled in with constant values. ARG can either be an aggregate
expression or a pointer to an aggregate. ARG_TYPE is the type of the
aggregate. JFUNC is the jump function into which the constants are
subsequently stored. AA_WALK_BUDGET_P points to limit on number of
statements we allow get_continuation_for_phi to examine. */
in ARG is filled in constants or values that are derived from caller's
formal parameter in the way described by some kinds of jump functions. FBI
is the context of the caller function for interprocedural analysis. ARG can
either be an aggregate expression or a pointer to an aggregate. ARG_TYPE is
the type of the aggregate, JFUNC is the jump function for the aggregate. */
static void
determine_known_aggregate_parts (gcall *call, tree arg,
determine_known_aggregate_parts (struct ipa_func_body_info *fbi,
gcall *call, tree arg,
tree arg_type,
struct ipa_jump_func *jfunc,
unsigned *aa_walk_budget_p)
struct ipa_jump_func *jfunc)
{
struct ipa_known_agg_contents_list *list = NULL, *all_list = NULL;
bitmap visited = NULL;
int item_count = 0, const_count = 0;
int ipa_max_agg_items = param_ipa_max_agg_items;
int item_count = 0, value_count = 0;
HOST_WIDE_INT arg_offset, arg_size;
tree arg_base;
bool check_ref, by_ref;
ao_ref r;
if (ipa_max_agg_items == 0)
if (param_ipa_max_agg_items == 0)
return;
/* The function operates in three stages. First, we prepare check_ref, r,
@ -1680,7 +1931,7 @@ determine_known_aggregate_parts (gcall *call, tree arg,
if (gimple_code (stmt) == GIMPLE_PHI)
{
dom_vuse = get_continuation_for_phi (stmt, &r, true,
*aa_walk_budget_p,
fbi->aa_walk_budget,
&visited, false, NULL, NULL);
continue;
}
@ -1690,12 +1941,13 @@ determine_known_aggregate_parts (gcall *call, tree arg,
struct ipa_known_agg_contents_list *content
= XALLOCA (struct ipa_known_agg_contents_list);
if (!extract_mem_content (stmt, arg_base, check_ref, content))
if (!extract_mem_content (fbi, stmt, arg_base, check_ref, content))
break;
/* Now we get a dominating virtual operand, and need to check
whether its value is clobbered any other dominating one. */
if (content->constant
if ((content->value.pass_through.formal_id >= 0
|| content->value.pass_through.operand)
&& !clobber_by_agg_contents_list_p (all_list, content))
{
struct ipa_known_agg_contents_list *copy
@ -1705,14 +1957,14 @@ determine_known_aggregate_parts (gcall *call, tree arg,
operands, whose definitions can finally reach the call. */
add_to_agg_contents_list (&list, (*copy = *content, copy));
if (++const_count == ipa_max_agg_items)
if (++value_count == param_ipa_max_agg_items)
break;
}
/* Add to the list consisting of all dominating virtual operands. */
add_to_agg_contents_list (&all_list, content);
if (++item_count == 2 * ipa_max_agg_items)
if (++item_count == 2 * param_ipa_max_agg_items)
break;
}
dom_vuse = gimple_vuse (stmt);
@ -1723,12 +1975,12 @@ determine_known_aggregate_parts (gcall *call, tree arg,
/* Third stage just goes over the list and creates an appropriate vector of
ipa_agg_jf_item structures out of it, of course only if there are
any known constants to begin with. */
any meaningful items to begin with. */
if (const_count)
if (value_count)
{
jfunc->agg.by_ref = by_ref;
build_agg_jump_func_from_list (list, const_count, arg_offset, jfunc);
build_agg_jump_func_from_list (list, value_count, arg_offset, jfunc);
}
}
@ -2020,8 +2272,7 @@ ipa_compute_jump_functions_for_edge (struct ipa_func_body_info *fbi,
|| !ipa_get_jf_ancestor_agg_preserved (jfunc))
&& (AGGREGATE_TYPE_P (TREE_TYPE (arg))
|| POINTER_TYPE_P (param_type)))
determine_known_aggregate_parts (call, arg, param_type, jfunc,
&fbi->aa_walk_budget);
determine_known_aggregate_parts (fbi, call, arg, param_type, jfunc);
}
if (!useful_context)
vec_free (args->polymorphic_call_contexts);
@ -2680,6 +2931,72 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
class ipa_polymorphic_call_context *dst_ctx
= ipa_get_ith_polymorhic_call_context (args, i);
if (dst->agg.items)
{
struct ipa_agg_jf_item *item;
int j;
FOR_EACH_VEC_ELT (*dst->agg.items, j, item)
{
int dst_fid;
struct ipa_jump_func *src;
if (item->jftype != IPA_JF_PASS_THROUGH
&& item->jftype != IPA_JF_LOAD_AGG)
continue;
dst_fid = item->value.pass_through.formal_id;
if (dst_fid >= ipa_get_cs_argument_count (top))
{
item->jftype = IPA_JF_UNKNOWN;
continue;
}
item->value.pass_through.formal_id = -1;
src = ipa_get_ith_jump_func (top, dst_fid);
if (src->type == IPA_JF_CONST)
{
if (item->jftype == IPA_JF_PASS_THROUGH
&& item->value.pass_through.operation == NOP_EXPR)
{
item->jftype = IPA_JF_CONST;
item->value.constant = src->value.constant.value;
continue;
}
}
else if (src->type == IPA_JF_PASS_THROUGH
&& src->value.pass_through.operation == NOP_EXPR)
{
if (item->jftype == IPA_JF_PASS_THROUGH
|| !item->value.load_agg.by_ref
|| src->value.pass_through.agg_preserved)
item->value.pass_through.formal_id
= src->value.pass_through.formal_id;
}
else if (src->type == IPA_JF_ANCESTOR)
{
if (item->jftype == IPA_JF_PASS_THROUGH)
{
if (!src->value.ancestor.offset)
item->value.pass_through.formal_id
= src->value.ancestor.formal_id;
}
else if (src->value.ancestor.agg_preserved)
{
gcc_checking_assert (item->value.load_agg.by_ref);
item->value.pass_through.formal_id
= src->value.ancestor.formal_id;
item->value.load_agg.offset
+= src->value.ancestor.offset;
}
}
if (item->value.pass_through.formal_id < 0)
item->jftype = IPA_JF_UNKNOWN;
}
}
if (dst->type == IPA_JF_ANCESTOR)
{
struct ipa_jump_func *src;
@ -2719,8 +3036,11 @@ update_jump_functions_after_inlining (struct cgraph_edge *cs,
}
}
if (src->agg.items
&& (dst->value.ancestor.agg_preserved || !src->agg.by_ref))
/* Parameter and argument in ancestor jump function must be pointer
type, which means access to aggregate must be by-reference. */
gcc_assert (!src->agg.items || src->agg.by_ref);
if (src->agg.items && dst->value.ancestor.agg_preserved)
{
struct ipa_agg_jf_item *item;
int j;
@ -3112,18 +3432,19 @@ ipa_find_agg_cst_from_init (tree scalar, HOST_WIDE_INT offset, bool by_ref)
return find_constructor_constant_at_offset (DECL_INITIAL (scalar), offset);
}
/* Retrieve value from aggregate jump function AGG or static initializer of
SCALAR (which can be NULL) for the given OFFSET or return NULL if there is
none. BY_REF specifies whether the value has to be passed by reference or
by value. If FROM_GLOBAL_CONSTANT is non-NULL, then the boolean it points
to is set to true if the value comes from an initializer of a constant. */
/* Retrieve value from AGG, a set of known offset/value for an aggregate or
static initializer of SCALAR (which can be NULL) for the given OFFSET or
return NULL if there is none. BY_REF specifies whether the value has to be
passed by reference or by value. If FROM_GLOBAL_CONSTANT is non-NULL, then
the boolean it points to is set to true if the value comes from an
initializer of a constant. */
tree
ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
ipa_find_agg_cst_for_param (struct ipa_agg_value_set *agg, tree scalar,
HOST_WIDE_INT offset, bool by_ref,
bool *from_global_constant)
{
struct ipa_agg_jf_item *item;
struct ipa_agg_value *item;
int i;
if (scalar)
@ -3141,7 +3462,7 @@ ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
|| by_ref != agg->by_ref)
return NULL;
FOR_EACH_VEC_SAFE_ELT (agg->items, i, item)
FOR_EACH_VEC_ELT (agg->items, i, item)
if (item->offset == offset)
{
/* Currently we do not have clobber values, return NULL for them once
@ -3237,11 +3558,13 @@ try_decrement_rdesc_refcount (struct ipa_jump_func *jfunc)
pointer formal parameter described by jump function JFUNC. TARGET_TYPE is
the type of the parameter to which the result of JFUNC is passed. If it can
be determined, return the newly direct edge, otherwise return NULL.
NEW_ROOT_INFO is the node info that JFUNC lattices are relative to. */
NEW_ROOT and NEW_ROOT_INFO is the node and its info that JFUNC lattices are
relative to. */
static struct cgraph_edge *
try_make_edge_direct_simple_call (struct cgraph_edge *ie,
struct ipa_jump_func *jfunc, tree target_type,
struct cgraph_node *new_root,
class ipa_node_params *new_root_info)
{
struct cgraph_edge *cs;
@ -3251,10 +3574,14 @@ try_make_edge_direct_simple_call (struct cgraph_edge *ie,
if (agg_contents)
{
bool from_global_constant;
target = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
ipa_agg_value_set agg = ipa_agg_value_set_from_jfunc (new_root_info,
new_root,
&jfunc->agg);
target = ipa_find_agg_cst_for_param (&agg, scalar,
ie->indirect_info->offset,
ie->indirect_info->by_ref,
&from_global_constant);
agg.release ();
if (target
&& !from_global_constant
&& !ie->indirect_info->guaranteed_unmodified)
@ -3308,12 +3635,16 @@ ipa_impossible_devirt_target (struct cgraph_edge *ie, tree target)
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. CTX describes the polymorphic context that the
parameter the call is based on brings along with it. */
parameter the call is based on brings along with it. NEW_ROOT and
NEW_ROOT_INFO is the node and its info that JFUNC lattices are relative
to. */
static struct cgraph_edge *
try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
struct ipa_jump_func *jfunc,
class ipa_polymorphic_call_context ctx)
class ipa_polymorphic_call_context ctx,
struct cgraph_node *new_root,
class ipa_node_params *new_root_info)
{
tree target = NULL;
bool speculative = false;
@ -3331,9 +3662,13 @@ try_make_edge_direct_virtual_call (struct cgraph_edge *ie,
unsigned HOST_WIDE_INT offset;
tree scalar = (jfunc->type == IPA_JF_CONST) ? ipa_get_jf_constant (jfunc)
: NULL;
tree t = ipa_find_agg_cst_for_param (&jfunc->agg, scalar,
ipa_agg_value_set agg = ipa_agg_value_set_from_jfunc (new_root_info,
new_root,
&jfunc->agg);
tree t = ipa_find_agg_cst_for_param (&agg, scalar,
ie->indirect_info->offset,
true);
agg.release ();
if (t && vtable_pointer_value_to_vtable (t, &vtable, &offset))
{
bool can_refer;
@ -3424,14 +3759,15 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
{
class ipa_edge_args *top;
struct cgraph_edge *ie, *next_ie, *new_direct_edge;
struct cgraph_node *new_root;
class ipa_node_params *new_root_info, *inlined_node_info;
bool res = false;
ipa_check_create_edge_args ();
top = IPA_EDGE_REF (cs);
new_root_info = IPA_NODE_REF (cs->caller->inlined_to
? cs->caller->inlined_to
: cs->caller);
new_root = cs->caller->inlined_to
? cs->caller->inlined_to : cs->caller;
new_root_info = IPA_NODE_REF (new_root);
inlined_node_info = IPA_NODE_REF (cs->callee->function_symbol ());
for (ie = node->indirect_calls; ie; ie = next_ie)
@ -3470,13 +3806,16 @@ update_indirect_edges_after_inlining (struct cgraph_edge *cs,
{
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);
new_direct_edge = try_make_edge_direct_virtual_call (ie, jfunc, ctx,
new_root,
new_root_info);
}
else
{
tree target_type = ipa_get_type (inlined_node_info, param_index);
new_direct_edge = try_make_edge_direct_simple_call (ie, jfunc,
target_type,
new_root,
new_root_info);
}
@ -4202,6 +4541,8 @@ ipa_write_jump_function (struct output_block *ob,
bp_pack_value (&bp, jump_func->value.ancestor.agg_preserved, 1);
streamer_write_bitpack (&bp);
break;
default:
fatal_error (UNKNOWN_LOCATION, "invalid jump function in LTO stream");
}
count = vec_safe_length (jump_func->agg.items);
@ -4215,8 +4556,36 @@ ipa_write_jump_function (struct output_block *ob,
FOR_EACH_VEC_SAFE_ELT (jump_func->agg.items, i, item)
{
stream_write_tree (ob, item->type, true);
streamer_write_uhwi (ob, item->offset);
stream_write_tree (ob, item->value, true);
streamer_write_uhwi (ob, item->jftype);
switch (item->jftype)
{
case IPA_JF_UNKNOWN:
break;
case IPA_JF_CONST:
stream_write_tree (ob, item->value.constant, true);
break;
case IPA_JF_PASS_THROUGH:
case IPA_JF_LOAD_AGG:
streamer_write_uhwi (ob, item->value.pass_through.operation);
streamer_write_uhwi (ob, item->value.pass_through.formal_id);
if (TREE_CODE_CLASS (item->value.pass_through.operation)
!= tcc_unary)
stream_write_tree (ob, item->value.pass_through.operand, true);
if (item->jftype == IPA_JF_LOAD_AGG)
{
stream_write_tree (ob, item->value.load_agg.type, true);
streamer_write_uhwi (ob, item->value.load_agg.offset);
bp = bitpack_create (ob->main_stream);
bp_pack_value (&bp, item->value.load_agg.by_ref, 1);
streamer_write_bitpack (&bp);
}
break;
default:
fatal_error (UNKNOWN_LOCATION,
"invalid jump function in LTO stream");
}
}
bp = bitpack_create (ob->main_stream);
@ -4313,8 +4682,39 @@ ipa_read_jump_function (class lto_input_block *ib,
for (i = 0; i < count; i++)
{
struct ipa_agg_jf_item item;
item.type = stream_read_tree (ib, data_in);
item.offset = streamer_read_uhwi (ib);
item.value = stream_read_tree (ib, data_in);
item.jftype = (enum jump_func_type) streamer_read_uhwi (ib);
switch (item.jftype)
{
case IPA_JF_UNKNOWN:
break;
case IPA_JF_CONST:
item.value.constant = stream_read_tree (ib, data_in);
break;
case IPA_JF_PASS_THROUGH:
case IPA_JF_LOAD_AGG:
operation = (enum tree_code) streamer_read_uhwi (ib);
item.value.pass_through.operation = operation;
item.value.pass_through.formal_id = streamer_read_uhwi (ib);
if (TREE_CODE_CLASS (operation) == tcc_unary)
item.value.pass_through.operand = NULL_TREE;
else
item.value.pass_through.operand = stream_read_tree (ib, data_in);
if (item.jftype == IPA_JF_LOAD_AGG)
{
struct bitpack_d bp;
item.value.load_agg.type = stream_read_tree (ib, data_in);
item.value.load_agg.offset = streamer_read_uhwi (ib);
bp = streamer_read_bitpack (ib);
item.value.load_agg.by_ref = bp_unpack_value (&bp, 1);
}
break;
default:
fatal_error (UNKNOWN_LOCATION,
"invalid jump function in LTO stream");
}
if (prevails)
jump_func->agg.items->quick_push (item);
}
@ -5357,9 +5757,9 @@ ipcp_transform_function (struct cgraph_node *node)
}
/* Return true if OTHER describes same agg item. */
/* Return true if OTHER describes same agg value. */
bool
ipa_agg_jf_item::equal_to (const ipa_agg_jf_item &other)
ipa_agg_value::equal_to (const ipa_agg_value &other)
{
return offset == other.offset
&& operand_equal_p (value, other.value, 0);

View File

@ -39,6 +39,15 @@ along with GCC; see the file COPYING3. If not see
argument.
Unknown - neither of the above.
IPA_JF_LOAD_AGG is a compound pass-through jump function, in which primary
operation on formal parameter is memory dereference that loads a value from
a part of an aggregate, which is represented or pointed to by the formal
parameter. Moreover, an additional unary/binary operation can be applied on
the loaded value, and final result is passed as actual argument of callee
(e.g. *(param_1(D) + 4) op 24 ). It is meant to describe usage of aggregate
parameter or by-reference parameter referenced in argument passing, commonly
found in C++ and Fortran.
IPA_JF_ANCESTOR is a special pass-through jump function, which means that
the result is an address of a part of the object pointed to by the formal
parameter to which the function refers. It is mainly intended to represent
@ -60,6 +69,7 @@ enum jump_func_type
IPA_JF_UNKNOWN = 0, /* newly allocated and zeroed jump functions default */
IPA_JF_CONST, /* represented by field costant */
IPA_JF_PASS_THROUGH, /* represented by field pass_through */
IPA_JF_LOAD_AGG, /* represented by field load_agg */
IPA_JF_ANCESTOR /* represented by field ancestor */
};
@ -97,6 +107,26 @@ struct GTY(()) ipa_pass_through_data
unsigned agg_preserved : 1;
};
/* Structure holding data required to describe a load-value-from-aggregate
jump function. */
struct GTY(()) ipa_load_agg_data
{
/* Inherit from pass through jump function, describing unary/binary
operation on the value loaded from aggregate that is represented or
pointed to by the formal parameter, specified by formal_id in this
pass_through jump function data structure. */
struct ipa_pass_through_data pass_through;
/* Type of the value loaded from the aggregate. */
tree type;
/* Offset at which the value is located within the aggregate. */
HOST_WIDE_INT offset;
/* True if loaded by reference (the aggregate is pointed to by the formal
parameter) or false if loaded by value (the aggregate is represented
by the formal parameter). */
bool by_ref;
};
/* Structure holding data required to describe an ancestor pass-through
jump function. */
@ -110,58 +140,139 @@ struct GTY(()) ipa_ancestor_jf_data
unsigned agg_preserved : 1;
};
/* An element in an aggegate part of a jump function describing a known value
at a given offset. When it is part of a pass-through jump function with
agg_preserved set or an ancestor jump function with agg_preserved set, all
unlisted positions are assumed to be preserved but the value can be a type
node, which means that the particular piece (starting at offset and having
the size of the type) is clobbered with an unknown value. When
agg_preserved is false or the type of the containing jump function is
different, all unlisted parts are assumed to be unknown and all values must
fulfill is_gimple_ip_invariant. */
/* A jump function for an aggregate part at a given offset, which describes how
it content value is generated. All unlisted positions are assumed to have a
value defined in an unknown way. */
struct GTY(()) ipa_agg_jf_item
{
/* The offset for the aggregate part. */
HOST_WIDE_INT offset;
/* Data type of the aggregate part. */
tree type;
/* Jump function type. */
enum jump_func_type jftype;
/* Represents a value of jump function. constant represents the actual constant
in constant jump function content. pass_through is used only in simple pass
through jump function context. load_agg is for load-value-from-aggregate
jump function context. */
union jump_func_agg_value
{
tree GTY ((tag ("IPA_JF_CONST"))) constant;
struct ipa_pass_through_data GTY ((tag ("IPA_JF_PASS_THROUGH"))) pass_through;
struct ipa_load_agg_data GTY ((tag ("IPA_JF_LOAD_AGG"))) load_agg;
} GTY ((desc ("%1.jftype"))) value;
};
/* Jump functions describing a set of aggregate contents. */
struct GTY(()) ipa_agg_jump_function
{
/* Description of the individual jump function item. */
vec<ipa_agg_jf_item, va_gc> *items;
/* True if the data was passed by reference (as opposed to by value). */
bool by_ref;
};
/* An element in an aggregate part describing a known value at a given offset.
All unlisted positions are assumed to be unknown and all listed values must
fulfill is_gimple_ip_invariant. */
struct ipa_agg_value
{
/* The offset at which the known value is located within the aggregate. */
HOST_WIDE_INT offset;
/* The known constant or type if this is a clobber. */
/* The known constant. */
tree value;
/* Return true if OTHER describes same agg item. */
bool equal_to (const ipa_agg_jf_item &other);
/* Return true if OTHER describes same agg value. */
bool equal_to (const ipa_agg_value &other);
};
/* Structure describing a set of known offset/value for aggregate. */
/* Aggregate jump function - i.e. description of contents of aggregates passed
either by reference or value. */
struct GTY(()) ipa_agg_jump_function
struct ipa_agg_value_set
{
/* Description of the individual items. */
vec<ipa_agg_jf_item, va_gc> *items;
/* True if the data was passed by reference (as opposed to by value). */
/* Description of the individual item. */
vec<ipa_agg_value> items;
/* True if the data was passed by reference (as opposed to by value). */
bool by_ref;
/* Return true if OTHER describes same agg items. */
bool equal_to (const ipa_agg_jump_function &other)
/* Return true if OTHER describes same agg values. */
bool equal_to (const ipa_agg_value_set &other)
{
if (by_ref != other.by_ref)
return false;
if (items != NULL && other.items == NULL)
if (items.length () != other.items.length ())
return false;
if (!items)
return other.items == NULL;
if (items->length () != other.items->length ())
return false;
for (unsigned int i = 0; i < items->length (); i++)
if (!(*items)[i].equal_to ((*other.items)[i]))
for (unsigned int i = 0; i < items.length (); i++)
if (!items[i].equal_to (other.items[i]))
return false;
return true;
}
/* Return true if there is any value for aggregate. */
bool is_empty () const
{
return items.is_empty ();
}
ipa_agg_value_set copy () const
{
ipa_agg_value_set new_copy;
new_copy.items = items.copy ();
new_copy.by_ref = by_ref;
return new_copy;
}
void release ()
{
items.release ();
}
};
typedef struct ipa_agg_jump_function *ipa_agg_jump_function_p;
/* Return copy of a vec<ipa_agg_value_set>. */
static inline vec<ipa_agg_value_set>
ipa_copy_agg_values (const vec<ipa_agg_value_set> &aggs)
{
vec<ipa_agg_value_set> aggs_copy = vNULL;
if (!aggs.is_empty ())
{
ipa_agg_value_set *agg;
int i;
aggs_copy.reserve_exact (aggs.length ());
FOR_EACH_VEC_ELT (aggs, i, agg)
aggs_copy.quick_push (agg->copy ());
}
return aggs_copy;
}
/* For vec<ipa_agg_value_set>, DO NOT call release(), use below function
instead. Because ipa_agg_value_set contains a field of vector type, we
should release this child vector in each element before reclaiming the
whole vector. */
static inline void
ipa_release_agg_values (vec<ipa_agg_value_set> &aggs)
{
ipa_agg_value_set *agg;
int i;
FOR_EACH_VEC_ELT (aggs, i, agg)
agg->release ();
aggs.release ();
}
/* Information about zero/non-zero bits. */
class GTY(()) ipa_bits
@ -193,8 +304,8 @@ public:
types of jump functions supported. */
struct GTY (()) ipa_jump_func
{
/* Aggregate contants description. See struct ipa_agg_jump_function and its
description. */
/* Aggregate jump function description. See struct ipa_agg_jump_function
and its description. */
struct ipa_agg_jump_function agg;
/* Information about zero/non-zero bits. The pointed to structure is shared
@ -857,9 +968,9 @@ 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>,
vec<ipa_agg_value_set>,
bool *);
struct cgraph_edge *ipa_make_edge_direct_to_target (struct cgraph_edge *, tree,
bool speculative = false);
@ -872,7 +983,7 @@ ipa_bits *ipa_get_ipa_bits_for_value (const widest_int &value,
void ipa_analyze_node (struct cgraph_node *);
/* Aggregate jump function related functions. */
tree ipa_find_agg_cst_for_param (struct ipa_agg_jump_function *agg, tree scalar,
tree ipa_find_agg_cst_for_param (struct ipa_agg_value_set *agg, tree scalar,
HOST_WIDE_INT offset, bool by_ref,
bool *from_global_constant = NULL);
bool ipa_load_from_parm_agg (struct ipa_func_body_info *fbi,
@ -918,6 +1029,9 @@ ipa_polymorphic_call_context ipa_context_from_jfunc (ipa_node_params *,
cgraph_edge *,
int,
ipa_jump_func *);
ipa_agg_value_set ipa_agg_value_set_from_jfunc (ipa_node_params *,
cgraph_node *,
ipa_agg_jump_function *);
void ipa_dump_param (FILE *, class ipa_node_params *info, int i);
void ipa_release_body_info (struct ipa_func_body_info *);
tree ipa_get_callee_param_type (struct cgraph_edge *e, int i);

View File

@ -1,3 +1,9 @@
2019-11-14 Feng Xue <fxue@os.amperecomputing.com>
PR ipa/91682
* gcc.dg/ipa/ipcp-agg-10.c: Change dg-scan string.
* gcc.dg/ipa/ipcp-agg-11.c: New test.
2019-11-14 Jakub Jelinek <jakub@redhat.com>
PR ipa/92421

View File

@ -72,7 +72,7 @@ int caller2(void)
return sum;
}
/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 1" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 2" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, cst: 3" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 64, cst: 4" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 1" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 2" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 0, type: int, CONST: 3" 1 "cp" } } */
/* { dg-final { scan-ipa-dump-times "offset: 64, type: int, CONST: 4" 1 "cp" } } */

View File

@ -0,0 +1,77 @@
/* { dg-do compile } */
/* { dg-options "-O3 -fno-ipa-sra -fdump-ipa-cp-details -fno-early-inlining" } */
/* { dg-add-options bind_pic_locally } */
struct S
{
int a, b, c;
};
void *blah(int, void *);
#define foo_body(p)\
{ \
int i, c = (p)->c; \
int b = (p)->b; \
void *v = (void *) (p); \
\
for (i= 0; i< c; i++) \
v = blah(b + i, v); \
}
static void __attribute__ ((noinline))
foo_v (struct S s)
{
foo_body (&s);
}
static void __attribute__ ((noinline))
foo_r (struct S *p)
{
foo_body (p);
}
static void
goo_v (int a, int *p)
{
struct S s;
s.a = 101;
s.b = a % 7;
s.c = *p + 6;
foo_v (s);
}
static void
goo_r (int a, struct S n)
{
struct S s;
s.a = 1;
s.b = a + 5;
s.c = -n.b;
foo_r (&s);
}
void
entry ()
{
int a;
int v;
struct S s;
a = 9;
v = 3;
goo_v (a, &v);
a = 100;
s.b = 18;
goo_r (a, s);
}
/* { dg-final { scan-ipa-dump "offset: 0, type: int, CONST: 1" "cp" } } */
/* { dg-final { scan-ipa-dump "offset: 32, type: int, PASS THROUGH: 0, op plus_expr 5" "cp" } } */
/* { dg-final { scan-ipa-dump "offset: 64, type: int, LOAD AGG: 1 \\\[offset: 32, by value], op negate_expr" "cp" } } */
/* { dg-final { scan-ipa-dump "offset: 0, type: int, CONST: 101" "cp" } } */
/* { dg-final { scan-ipa-dump "offset: 32, type: int, PASS THROUGH: 0, op trunc_mod_expr 7" "cp" } } */
/* { dg-final { scan-ipa-dump "offset: 64, type: int, LOAD AGG: 1 \\\[offset: 0, by reference], op plus_expr 6" "cp" } } */
/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[0]=1, 0\\\[32]=105, 0\\\[64]=-18" "cp" } } */
/* { dg-final { scan-ipa-dump "Aggregate replacements: 0\\\[0]=101, 0\\\[32]=2, 0\\\[64]=9" "cp" } } */