Skip to content

Commit 1162861

Browse files
committed
ipa: Compare jump functions in ICF (PR 113907)
In PR 113907 comment #58, Honza found a case where ICF thinks bodies of functions are equivalent but becaise of difference in aliases in a memory access, different aggregate jump functions are associated with supposedly equivalent call statements. This patch adds a way to compare jump functions and plugs it into ICF to avoid the issue. gcc/ChangeLog: 2024-03-20 Martin Jambor <[email protected]> PR ipa/113907 * ipa-prop.h (class ipa_vr): Declare new overload of a member function equal_p. (ipa_jump_functions_equivalent_p): Declare. * ipa-prop.cc (ipa_vr::equal_p): New function. (ipa_agg_pass_through_jf_equivalent_p): Likewise. (ipa_agg_jump_functions_equivalent_p): Likewise. (ipa_jump_functions_equivalent_p): Likewise. * ipa-cp.h (values_equal_for_ipcp_p): Declare. * ipa-cp.cc (values_equal_for_ipcp_p): Make function public. * ipa-icf-gimple.cc: Include alloc-pool.h, symbol-summary.h, sreal.h, ipa-cp.h and ipa-prop.h. (func_checker::compare_gimple_call): Comapre jump functions. gcc/testsuite/ChangeLog: 2024-03-20 Martin Jambor <[email protected]> PR ipa/113907 * gcc.dg/lto/pr113907_0.c: New. * gcc.dg/lto/pr113907_1.c: Likewise. * gcc.dg/lto/pr113907_2.c: Likewise.
1 parent feb6a2d commit 1162861

File tree

8 files changed

+267
-1
lines changed

8 files changed

+267
-1
lines changed

gcc/ipa-cp.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ ipcp_lattice<valtype>::is_single_const ()
201201

202202
/* Return true iff X and Y should be considered equal values by IPA-CP. */
203203

204-
static bool
204+
bool
205205
values_equal_for_ipcp_p (tree x, tree y)
206206
{
207207
gcc_checking_assert (x != NULL_TREE && y != NULL_TREE);

gcc/ipa-cp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,6 @@ class ipcp_param_lattices
289289
bool virt_call = false;
290290
};
291291

292+
bool values_equal_for_ipcp_p (tree x, tree y);
293+
292294
#endif /* IPA_CP_H */

gcc/ipa-icf-gimple.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,12 @@ along with GCC; see the file COPYING3. If not see
4141
#include "gimple-walk.h"
4242

4343
#include "tree-ssa-alias-compare.h"
44+
#include "alloc-pool.h"
45+
#include "symbol-summary.h"
4446
#include "ipa-icf-gimple.h"
47+
#include "sreal.h"
48+
#include "ipa-cp.h"
49+
#include "ipa-prop.h"
4550

4651
namespace ipa_icf_gimple {
4752

@@ -714,6 +719,31 @@ func_checker::compare_gimple_call (gcall *s1, gcall *s2)
714719
&& !compatible_types_p (TREE_TYPE (t1), TREE_TYPE (t2)))
715720
return return_false_with_msg ("GIMPLE internal call LHS type mismatch");
716721

722+
if (!gimple_call_internal_p (s1))
723+
{
724+
cgraph_edge *e1 = cgraph_node::get (m_source_func_decl)->get_edge (s1);
725+
cgraph_edge *e2 = cgraph_node::get (m_target_func_decl)->get_edge (s2);
726+
class ipa_edge_args *args1 = ipa_edge_args_sum->get (e1);
727+
class ipa_edge_args *args2 = ipa_edge_args_sum->get (e2);
728+
if ((args1 != nullptr) != (args2 != nullptr))
729+
return return_false_with_msg ("ipa_edge_args mismatch");
730+
if (args1)
731+
{
732+
int n1 = ipa_get_cs_argument_count (args1);
733+
int n2 = ipa_get_cs_argument_count (args2);
734+
if (n1 != n2)
735+
return return_false_with_msg ("ipa_edge_args nargs mismatch");
736+
for (int i = 0; i < n1; i++)
737+
{
738+
struct ipa_jump_func *jf1 = ipa_get_ith_jump_func (args1, i);
739+
struct ipa_jump_func *jf2 = ipa_get_ith_jump_func (args2, i);
740+
if (((jf1 != nullptr) != (jf2 != nullptr))
741+
|| (jf1 && !ipa_jump_functions_equivalent_p (jf1, jf2)))
742+
return return_false_with_msg ("jump function mismatch");
743+
}
744+
}
745+
}
746+
717747
return compare_operand (t1, t2, get_operand_access_type (&map, t1));
718748
}
719749

gcc/ipa-prop.cc

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,20 @@ ipa_vr::equal_p (const vrange &r) const
156156
return (types_compatible_p (m_type, r.type ()) && m_storage->equal_p (r));
157157
}
158158

159+
bool
160+
ipa_vr::equal_p (const ipa_vr &o) const
161+
{
162+
if (!known_p ())
163+
return !o.known_p ();
164+
165+
if (!types_compatible_p (m_type, o.m_type))
166+
return false;
167+
168+
Value_Range r;
169+
o.get_vrange (r);
170+
return m_storage->equal_p (r);
171+
}
172+
159173
void
160174
ipa_vr::get_vrange (Value_Range &r) const
161175
{
@@ -6057,4 +6071,157 @@ ipa_prop_cc_finalize (void)
60576071
ipa_node_params_sum = NULL;
60586072
}
60596073

6074+
/* Return true if the two pass_through components of two jump functions are
6075+
known to be equivalent. AGG_JF denotes whether they are part of aggregate
6076+
functions or not. The function can be used before the IPA phase of IPA-CP
6077+
or inlining because it cannot cope with refdesc changes these passes can
6078+
carry out. */
6079+
6080+
static bool
6081+
ipa_agg_pass_through_jf_equivalent_p (ipa_pass_through_data *ipt1,
6082+
ipa_pass_through_data *ipt2,
6083+
bool agg_jf)
6084+
6085+
{
6086+
gcc_assert (agg_jf ||
6087+
(!ipt1->refdesc_decremented && !ipt2->refdesc_decremented));
6088+
if (ipt1->operation != ipt2->operation
6089+
|| ipt1->formal_id != ipt2->formal_id
6090+
|| (!agg_jf && (ipt1->agg_preserved != ipt2->agg_preserved)))
6091+
return false;
6092+
if (((ipt1->operand != NULL_TREE) != (ipt2->operand != NULL_TREE))
6093+
|| (ipt1->operand
6094+
&& !values_equal_for_ipcp_p (ipt1->operand, ipt2->operand)))
6095+
return false;
6096+
return true;
6097+
}
6098+
6099+
/* Return true if the two aggregate jump functions are known to be equivalent.
6100+
The function can be used before the IPA phase of IPA-CP or inlining because
6101+
it cannot cope with refdesc changes these passes can carry out. */
6102+
6103+
static bool
6104+
ipa_agg_jump_functions_equivalent_p (ipa_agg_jf_item *ajf1,
6105+
ipa_agg_jf_item *ajf2)
6106+
{
6107+
if (ajf1->offset != ajf2->offset
6108+
|| ajf1->jftype != ajf2->jftype
6109+
|| !types_compatible_p (ajf1->type, ajf2->type))
6110+
return false;
6111+
6112+
switch (ajf1->jftype)
6113+
{
6114+
case IPA_JF_CONST:
6115+
if (!values_equal_for_ipcp_p (ajf1->value.constant,
6116+
ajf2->value.constant))
6117+
return false;
6118+
break;
6119+
case IPA_JF_PASS_THROUGH:
6120+
{
6121+
ipa_pass_through_data *ipt1 = &ajf1->value.pass_through;
6122+
ipa_pass_through_data *ipt2 = &ajf2->value.pass_through;
6123+
if (!ipa_agg_pass_through_jf_equivalent_p (ipt1, ipt2, true))
6124+
return false;
6125+
}
6126+
break;
6127+
case IPA_JF_LOAD_AGG:
6128+
{
6129+
ipa_load_agg_data *ila1 = &ajf1->value.load_agg;
6130+
ipa_load_agg_data *ila2 = &ajf2->value.load_agg;
6131+
if (!ipa_agg_pass_through_jf_equivalent_p (&ila1->pass_through,
6132+
&ila2->pass_through, true))
6133+
return false;
6134+
if (ila1->offset != ila2->offset
6135+
|| ila1->by_ref != ila2->by_ref
6136+
|| !types_compatible_p (ila1->type, ila2->type))
6137+
return false;
6138+
}
6139+
break;
6140+
default:
6141+
gcc_unreachable ();
6142+
}
6143+
return true;
6144+
}
6145+
6146+
/* Return true if the two jump functions are known to be equivalent. The
6147+
function can be used before the IPA phase of IPA-CP or inlining because it
6148+
cannot cope with refdesc changes these passes can carry out. */
6149+
6150+
bool
6151+
ipa_jump_functions_equivalent_p (ipa_jump_func *jf1, ipa_jump_func *jf2)
6152+
{
6153+
if (jf1->type != jf2->type)
6154+
return false;
6155+
6156+
switch (jf1->type)
6157+
{
6158+
case IPA_JF_UNKNOWN:
6159+
break;
6160+
case IPA_JF_CONST:
6161+
{
6162+
tree cst1 = ipa_get_jf_constant (jf1);
6163+
tree cst2 = ipa_get_jf_constant (jf2);
6164+
if (!values_equal_for_ipcp_p (cst1, cst2))
6165+
return false;
6166+
6167+
ipa_cst_ref_desc *rd1 = jfunc_rdesc_usable (jf1);
6168+
ipa_cst_ref_desc *rd2 = jfunc_rdesc_usable (jf2);
6169+
if (rd1 && rd2)
6170+
{
6171+
gcc_assert (rd1->refcount == 1
6172+
&& rd2->refcount == 1);
6173+
gcc_assert (!rd1->next_duplicate && !rd2->next_duplicate);
6174+
}
6175+
else if (rd1)
6176+
return false;
6177+
else if (rd2)
6178+
return false;
6179+
}
6180+
break;
6181+
case IPA_JF_PASS_THROUGH:
6182+
{
6183+
ipa_pass_through_data *ipt1 = &jf1->value.pass_through;
6184+
ipa_pass_through_data *ipt2 = &jf2->value.pass_through;
6185+
if (!ipa_agg_pass_through_jf_equivalent_p (ipt1, ipt2, false))
6186+
return false;
6187+
}
6188+
break;
6189+
case IPA_JF_ANCESTOR:
6190+
{
6191+
ipa_ancestor_jf_data *ia1 = &jf1->value.ancestor;
6192+
ipa_ancestor_jf_data *ia2 = &jf2->value.ancestor;
6193+
6194+
if (ia1->formal_id != ia2->formal_id
6195+
|| ia1->agg_preserved != ia2->agg_preserved
6196+
|| ia1->keep_null != ia2->keep_null
6197+
|| ia1->offset != ia2->offset)
6198+
return false;
6199+
}
6200+
break;
6201+
default:
6202+
gcc_unreachable ();
6203+
}
6204+
6205+
if (((jf1->m_vr != nullptr) != (jf2->m_vr != nullptr))
6206+
|| (jf1->m_vr && !jf1->m_vr->equal_p (*jf2->m_vr)))
6207+
return false;
6208+
6209+
unsigned alen = vec_safe_length (jf1->agg.items);
6210+
if (vec_safe_length (jf2->agg.items) != alen)
6211+
return false;
6212+
6213+
if (!alen)
6214+
return true;
6215+
6216+
if (jf1->agg.by_ref != jf2->agg.by_ref)
6217+
return false;
6218+
6219+
for (unsigned i = 0 ; i < alen; i++)
6220+
if (!ipa_agg_jump_functions_equivalent_p (&(*jf1->agg.items)[i],
6221+
&(*jf2->agg.items)[i]))
6222+
return false;
6223+
6224+
return true;
6225+
}
6226+
60606227
#include "gt-ipa-prop.h"

gcc/ipa-prop.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ class GTY(()) ipa_vr
308308
tree type () const { return m_type; }
309309
void get_vrange (Value_Range &) const;
310310
bool equal_p (const vrange &) const;
311+
bool equal_p (const ipa_vr &) const;
311312
const vrange_storage *storage () const { return m_storage; }
312313
void streamer_read (lto_input_block *, class data_in *);
313314
void streamer_write (output_block *) const;
@@ -1278,5 +1279,7 @@ ipa_range_set_and_normalize (vrange &r, tree val)
12781279

12791280
bool ipa_return_value_range (Value_Range &range, tree decl);
12801281
void ipa_record_return_value_range (Value_Range val);
1282+
bool ipa_jump_functions_equivalent_p (ipa_jump_func *jf1, ipa_jump_func *jf2);
1283+
12811284

12821285
#endif /* IPA_PROP_H */
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* { dg-lto-do run } */
2+
/* { dg-lto-options {{-O3 -flto}} } */
3+
4+
struct bar {int a;};
5+
struct foo {int a;};
6+
struct barp {struct bar *f; struct bar *g;};
7+
extern struct foo **ptr;
8+
int test2 (void *);
9+
int test3 (void *);
10+
int
11+
testb(void)
12+
{
13+
struct bar *fp;
14+
test2 ((void *)&fp);
15+
fp = (void *) 0;
16+
(*ptr)++;
17+
test3 ((void *)&fp);
18+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
__attribute__((used)) int val,val2 = 1;
2+
3+
struct foo {int a;};
4+
5+
struct foo **ptr;
6+
7+
__attribute__ ((noipa))
8+
int
9+
test2 (void *a)
10+
{
11+
ptr = (struct foo **)a;
12+
}
13+
int test3 (void *a);
14+
15+
int
16+
test(void)
17+
{
18+
struct foo *fp;
19+
test2 ((void *)&fp);
20+
fp = (void *) 0;
21+
(*ptr)++;
22+
test3 ((void *)&fp);
23+
}
24+
25+
int testb (void);
26+
27+
int
28+
main()
29+
{
30+
for (int i = 0; i < val2; i++)
31+
if (val)
32+
testb ();
33+
else
34+
test();
35+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* { dg-options "-O3 -flto -fno-strict-aliasing" } */
2+
3+
__attribute__ ((noinline))
4+
int
5+
test3 (void *a)
6+
{
7+
if (!*(void **)a)
8+
__builtin_abort ();
9+
return 0;
10+
}
11+

0 commit comments

Comments
 (0)