@@ -458,7 +458,7 @@ ipcp_lattice<valtype>::is_single_const ()
/* Return true iff X and Y should be considered equal values by IPA-CP. */
-static bool
+bool
values_equal_for_ipcp_p (tree x, tree y)
{
gcc_checking_assert (x != NULL_TREE && y != NULL_TREE);
@@ -42,7 +42,11 @@ along with GCC; see the file COPYING3. If not see
#include "tree-sra.h"
#include "tree-ssa-alias-compare.h"
+#include "alloc-pool.h"
+#include "symbol-summary.h"
#include "ipa-icf-gimple.h"
+#include "sreal.h"
+#include "ipa-prop.h"
namespace ipa_icf_gimple {
@@ -751,6 +755,31 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2)
&& !compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
return return_false_with_msg ("GIMPLE internal call LHS type mismatch");
+ if (!gimple_call_internal_p (s1))
+ {
+ cgraph_edge *e1 = cgraph_node::get (m_source_func_decl)->get_edge (s1);
+ cgraph_edge *e2 = cgraph_node::get (m_target_func_decl)->get_edge (s2);
+ class ipa_edge_args *args1 = ipa_edge_args_sum->get (e1);
+ class ipa_edge_args *args2 = ipa_edge_args_sum->get (e2);
+ if ((args1 != nullptr) != (args2 != nullptr))
+ return return_false_with_msg ("ipa_edge_args mismatch");
+ if (args1)
+ {
+ int n1 = ipa_get_cs_argument_count (args1);
+ int n2 = ipa_get_cs_argument_count (args2);
+ if (n1 != n2)
+ return return_false_with_msg ("ipa_edge_args nargs mismatch");
+ for (int i = 0; i < n1; i++)
+ {
+ struct ipa_jump_func *jf1 = ipa_get_ith_jump_func (args1, i);
+ struct ipa_jump_func *jf2 = ipa_get_ith_jump_func (args2, i);
+ if (((jf1 != nullptr) != (jf2 != nullptr))
+ || (jf1 && !ipa_jump_functions_equivalent_p (jf1, jf2)))
+ return return_false_with_msg ("jump function mismatch");
+ }
+ }
+ }
+
return compare_operand (t1, t2, get_operand_access_type (&map, t1));
}
@@ -6022,5 +6022,162 @@ ipcp_transform_function (struct cgraph_node *node)
return modified_mem_access ? TODO_update_ssa_only_virtuals : 0;
}
+/* Return true if the two pass_through components of two jump functions are
+ known to be equivalent. AGG_JF denotes whether they are part of aggregate
+ functions or not. The function can be used before the IPA phase of IPA-CP
+ or inlining because it cannot cope with refdesc changes these passes can
+ carry out. */
+
+static bool
+ipa_agg_pass_through_jf_equivalent_p (ipa_pass_through_data *ipt1,
+ ipa_pass_through_data *ipt2,
+ bool agg_jf)
+
+{
+ gcc_assert (agg_jf ||
+ (!ipt1->refdesc_decremented && !ipt2->refdesc_decremented));
+ if (ipt1->operation != ipt2->operation
+ || ipt1->formal_id != ipt2->formal_id
+ || (!agg_jf && (ipt1->agg_preserved != ipt2->agg_preserved)))
+ return false;
+ if (((ipt1->operand != NULL_TREE) != (ipt2->operand != NULL_TREE))
+ || (ipt1->operand
+ && !values_equal_for_ipcp_p (ipt1->operand, ipt2->operand)))
+ return false;
+ return true;
+}
+
+/* Return true if the two aggregate jump functions are known to be equivalent.
+ The function can be used before the IPA phase of IPA-CP or inlining because
+ it cannot cope with refdesc changes these passes can carry out. */
+
+static bool
+ipa_agg_jump_functions_equivalent_p (ipa_agg_jf_item *ajf1,
+ ipa_agg_jf_item *ajf2)
+{
+ if (ajf1->offset != ajf2->offset
+ || ajf1->jftype != ajf2->jftype
+ || !types_compatible_p (ajf1->type, ajf2->type))
+ return false;
+
+ switch (ajf1->jftype)
+ {
+ case IPA_JF_CONST:
+ if (!values_equal_for_ipcp_p (ajf1->value.constant,
+ ajf2->value.constant))
+ return false;
+ break;
+ case IPA_JF_PASS_THROUGH:
+ {
+ ipa_pass_through_data *ipt1 = &ajf1->value.pass_through;
+ ipa_pass_through_data *ipt2 = &ajf2->value.pass_through;
+ if (!ipa_agg_pass_through_jf_equivalent_p (ipt1, ipt2, true))
+ return false;
+ }
+ break;
+ case IPA_JF_LOAD_AGG:
+ {
+ ipa_load_agg_data *ila1 = &ajf1->value.load_agg;
+ ipa_load_agg_data *ila2 = &ajf2->value.load_agg;
+ if (!ipa_agg_pass_through_jf_equivalent_p (&ila1->pass_through,
+ &ila2->pass_through, true))
+ return false;
+ if (ila1->offset != ila2->offset
+ || ila1->by_ref != ila2->by_ref
+ || !types_compatible_p (ila1->type, ila2->type))
+ return false;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ return true;
+}
+
+/* Return true if the two jump functions are known to be equivalent. The
+ function can be used before the IPA phase of IPA-CP or inlining because it
+ cannot cope with refdesc changes these passes can carry out. */
+
+bool
+ipa_jump_functions_equivalent_p (ipa_jump_func *jf1, ipa_jump_func *jf2)
+{
+ if (jf1->type != jf2->type)
+ return false;
+
+ switch (jf1->type)
+ {
+ case IPA_JF_UNKNOWN:
+ break;
+ case IPA_JF_CONST:
+ {
+ tree cst1 = ipa_get_jf_constant (jf1);
+ tree cst2 = ipa_get_jf_constant (jf2);
+ if (!values_equal_for_ipcp_p (cst1, cst2))
+ return false;
+
+ ipa_cst_ref_desc *rd1 = jfunc_rdesc_usable (jf1);
+ ipa_cst_ref_desc *rd2 = jfunc_rdesc_usable (jf2);
+ if (rd1 && rd2)
+ {
+ gcc_assert (rd1->refcount == 1
+ && rd2->refcount == 1);
+ gcc_assert (!rd1->next_duplicate && !rd2->next_duplicate);
+ }
+ else if (rd1)
+ return false;
+ else if (rd2)
+ return false;
+ }
+ break;
+ case IPA_JF_PASS_THROUGH:
+ {
+ ipa_pass_through_data *ipt1 = &jf1->value.pass_through;
+ ipa_pass_through_data *ipt2 = &jf2->value.pass_through;
+ if (!ipa_agg_pass_through_jf_equivalent_p (ipt1, ipt2, false))
+ return false;
+ }
+ break;
+ case IPA_JF_ANCESTOR:
+ {
+ ipa_ancestor_jf_data *ia1 = &jf1->value.ancestor;
+ ipa_ancestor_jf_data *ia2 = &jf2->value.ancestor;
+
+ if (ia1->formal_id != ia2->formal_id
+ || ia1->agg_preserved != ia2->agg_preserved
+ || ia1->keep_null != ia2->keep_null
+ || ia1->offset != ia2->offset)
+ return false;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (((jf1->bits != nullptr) != (jf2->bits != nullptr))
+ || (jf1->bits && ((jf1->bits->value != jf2->bits->value)
+ || (jf1->bits->mask != jf2->bits->mask))))
+ return false;
+
+ if (((jf1->m_vr != nullptr) != (jf2->m_vr != nullptr))
+ || (jf1->m_vr && *jf1->m_vr != *jf2->m_vr))
+ return false;
+
+ unsigned alen = vec_safe_length (jf1->agg.items);
+ if (vec_safe_length (jf2->agg.items) != alen)
+ return false;
+
+ if (!alen)
+ return true;
+
+ if (jf1->agg.by_ref != jf2->agg.by_ref)
+ return false;
+
+ for (unsigned i = 0 ; i < alen; i++)
+ if (!ipa_agg_jump_functions_equivalent_p (&(*jf1->agg.items)[i],
+ &(*jf2->agg.items)[i]))
+ return false;
+
+ return true;
+}
#include "gt-ipa-prop.h"
@@ -1194,11 +1194,14 @@ bool ipcp_get_parm_bits (tree, tree *, widest_int *);
bool unadjusted_ptr_and_unit_offset (tree op, tree *ret,
poly_int64 *offset_ret);
+bool ipa_jump_functions_equivalent_p (ipa_jump_func *jf1, ipa_jump_func *jf2);
+
/* From tree-sra.cc: */
tree build_ref_for_offset (location_t, tree, poly_int64, bool, tree,
gimple_stmt_iterator *, bool);
/* In ipa-cp.cc */
void ipa_cp_cc_finalize (void);
+bool values_equal_for_ipcp_p (tree x, tree y);
#endif /* IPA_PROP_H */
new file mode 100644
@@ -0,0 +1,18 @@
+/* { dg-lto-do run } */
+/* { dg-lto-options {{-O3 -flto}} } */
+
+struct bar {int a;};
+struct foo {int a;};
+struct barp {struct bar *f; struct bar *g;};
+extern struct foo **ptr;
+int test2 (void *);
+int test3 (void *);
+int
+testb(void)
+{
+ struct bar *fp;
+ test2 ((void *)&fp);
+ fp = (void *) 0;
+ (*ptr)++;
+ test3 ((void *)&fp);
+}
new file mode 100644
@@ -0,0 +1,35 @@
+__attribute__((used)) int val,val2 = 1;
+
+struct foo {int a;};
+
+struct foo **ptr;
+
+__attribute__ ((noipa))
+int
+test2 (void *a)
+{
+ ptr = (struct foo **)a;
+}
+int test3 (void *a);
+
+int
+test(void)
+{
+ struct foo *fp;
+ test2 ((void *)&fp);
+ fp = (void *) 0;
+ (*ptr)++;
+ test3 ((void *)&fp);
+}
+
+int testb (void);
+
+int
+main()
+{
+ for (int i = 0; i < val2; i++)
+ if (val)
+ testb ();
+ else
+ test();
+}
new file mode 100644
@@ -0,0 +1,11 @@
+/* { dg-options "-O3 -flto -fno-strict-aliasing" } */
+
+__attribute__ ((noinline))
+int
+test3 (void *a)
+{
+ if (!*(void **)a)
+ __builtin_abort ();
+ return 0;
+}
+