mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-15 21:01:09 +08:00
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:
parent
3e7cf2e6c0
commit
eb270950ac
@ -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
|
||||
|
520
gcc/ipa-cp.c
520
gcc/ipa-cp.c
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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. */
|
||||
|
||||
|
578
gcc/ipa-prop.c
578
gcc/ipa-prop.c
@ -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);
|
||||
|
182
gcc/ipa-prop.h
182
gcc/ipa-prop.h
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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" } } */
|
||||
|
77
gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
Normal file
77
gcc/testsuite/gcc.dg/ipa/ipcp-agg-11.c
Normal 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" } } */
|
Loading…
x
Reference in New Issue
Block a user