Hi,
On Wed, Aug 25, 2021 at 7:58 PM Jan Hubicka <hubi...@ucw.cz> wrote: > Hi, > this patch adds logic needed to merge neighbouring accesses in ipa-modref > summaries. This helps analyzing array initializers and similar code. It > is > bit of work, since it breaks the fact that modref tree makes a good > lattice for > dataflow: the access ranges can be extended indefinitely. For this reason > I > added counter tracking number of adjustments and a cap to limit them > during the > dataflow. This triggers in: > void > recurse (char *p, int n) > { > *p = 0; > if (n) > recurse (p+1,n-1); > } > > Where we work now hard enugh to determine: > > access: Parm 0 param offset:0 offset:0 size:8 max_size:-1 adjusted 8 times > > which is correct access info saying that param0 can be accessed from byte 0 > in 8bit accesses with unknown max_size. > > --param max-modref-accesses is now hit 8 times instead of 45 before the > patch. > We hit --param param=modref-max-adjustments once for fft algorithm (where > the > recursion really seems to walk array) and max-bases once for late modref > and 9 > times for IPA modref (it works on types rather than alias sets so it is > more > likely to hit the limit). > > I would be happy for suggestions how to simplify the merging logic. It is > bit > convoluted since I need to know if I am going to adjust the range and need > to deal with poly_ints and possibly unknown sizes. > > Incrementally I will add logic on improving behaviour when limits are met > instead of just giving up on the analysis. > > With the patch I get following cc1plus stats: > > Alias oracle query stats: > refs_may_alias_p: 83135089 disambiguations, 101581194 queries > ref_maybe_used_by_call_p: 590484 disambiguations, 84157326 queries > call_may_clobber_ref_p: 345434 disambiguations, 349295 queries > nonoverlapping_component_refs_p: 0 disambiguations, 39520 queries > nonoverlapping_refs_since_match_p: 33266 disambiguations, 66411 must > overlaps, 100574 queries > aliasing_component_refs_p: 66251 disambiguations, 9920037 queries > TBAA oracle: 31033174 disambiguations 93485041 queries > 14359693 are in alias set 0 > 11930606 queries asked about the same object > 129 queries asked about the same alias set > 0 access volatile > 34218393 are dependent in the DAG > 1943046 are aritificially in conflict with void * > > Modref stats: > modref use: 26293 disambiguations, 705198 queries > modref clobber: 1828340 disambiguations, 21213011 queries > 4748965 tbaa queries (0.223870 per modref query) > 711083 base compares (0.033521 per modref query) > > PTA query stats: > pt_solution_includes: 13119524 disambiguations, 33183481 queries > pt_solutions_intersect: 1510541 disambiguations, 15368102 queries > > this would suggest quite large improvement over my last run > https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577962.html > (about 12% on overall disambiguation count) > > but I also updated my setup so part of the increase may be accounted for > diferent libraries. The overall size of modref access lists is about > halved on > cc1plus that looks promising though. It may be that we less often hit the > limit > on number of querries done in tree-ssa-alias. > > Bootstrapped/regtested x86_64-linux. > I plan to commit the patch after bit more testing. > > gcc/ChangeLog: > > * doc/invoke.texi: Document --param modref-max-adjustments > * ipa-modref-tree.c (test_insert_search_collapse): Update. > (test_merge): Update. > * ipa-modref-tree.h (struct modref_access_node): Add adjustments; > (modref_access_node::operator==): Fix handling of access ranges. > (modref_access_node::contains): Constify parameter; handle also > mismatched parm offsets. > (modref_access_node::update): New function. > (modref_access_node::merge0: New function. > (unspecified_modref_access_node): Update constructor. > (modref_ref_node::insert_access): Add record_adjustments parameter; > handle merging. > (modref_ref_node::try_merge_with): New private function. > (modref_tree::insert): New record_adjustments parameter. > (modref_tree::merge): New record_adjustments parameter. > (modref_tree::copy_from): Update. > * ipa-modref.c (dump_access): Dump adjustments field. > (get_access): Update constructor. > (record_access): Update call of insert. > (record_access_lto): Update call of insert. > (merge_call_side_effects): Add record_adjustments parameter. > (get_access_for_fnspec): Update. > (process_fnspec): Update. > (analyze_call): Update. > (analyze_function): Update. > (read_modref_records): Update. > (ipa_merge_modref_summary_after_inlining): Update. > (propagate_unknown_call): Update. > (modref_propagate_in_scc): Update. > * params.opt: (param-max-modref-adjustments=): New. > > gcc/testsuite/ChangeLog: > > * gcc.dg/ipa/modref-1.c: Update testcase. > * gcc.dg/tree-ssa/modref-4.c: Update testcase. > * gcc.dg/tree-ssa/modref-8.c: New test. > This patch is causing ICEs on arm: FAIL: g++.dg/torture/pr89303.C -O1 (internal compiler error) FAIL: g++.dg/torture/pr89303.C -O1 (test for excess errors) Excess errors: during GIMPLE pass: modref /gcc/testsuite/g++.dg/torture/pr89303.C:792:1: internal compiler error: in merge, at ipa-modref-tree.h:203 0xdc9b2b modref_access_node::merge(modref_access_node const&, bool) /gcc/ipa-modref-tree.h:203 0xdcbbb9 modref_ref_node<int>::try_merge_with(unsigned long) /gcc/ipa-modref-tree.h:397 0xdcc4aa modref_ref_node<int>::insert_access(modref_access_node, unsigned long, bool) /gcc/ipa-modref-tree.h:366 0xdcc71b modref_tree<int>::insert(int, int, modref_access_node, bool) /gcc/ipa-modref-tree.h:597 0xdc1312 record_access /gcc/ipa-modref.c:713 0xdc1e34 analyze_store /gcc/ipa-modref.c:1245 0xd00f2e walk_stmt_load_store_addr_ops(gimple*, void*, bool (*)(gimple*, tree_node*, tree_node*, void*), bool (*)(gimple*, tree_node*, tree_node*, void*), bool (*)(gimple*, tree_node*, tree_node*, void*)) /gcc/gimple-walk.c:767 0xdc6f4a analyze_stmt /gcc/ipa-modref.c:1269 0xdc6f4a analyze_function /gcc/ipa-modref.c:2131 0xdc860d execute /gcc/ipa-modref.c:2957 FAIL: 20_util/enable_shared_from_this/89303.cc (test for excess errors) Excess errors: during GIMPLE pass: modref /libstdc++-v3/testsuite/20_util/enable_shared_from_this/89303.cc:39: internal compiler error: in merge, at ipa-modref-tree.h:203 0xdc9b2b modref_access_node::merge(modref_access_node const&, bool) /gcc/ipa-modref-tree.h:203 0xdcbbb9 modref_ref_node<int>::try_merge_with(unsigned long) /gcc/ipa-modref-tree.h:397 0xdcc4aa modref_ref_node<int>::insert_access(modref_access_node, unsigned long, bool) /gcc/ipa-modref-tree.h:366 0xdcc71b modref_tree<int>::insert(int, int, modref_access_node, bool) /gcc/ipa-modref-tree.h:597 0xdc1312 record_access /gcc/ipa-modref.c:713 0xdc1e34 analyze_store /gcc/ipa-modref.c:1245 0xd00f2e walk_stmt_load_store_addr_ops(gimple*, void*, bool (*)(gimple*, tree_node*, tree_node*, void*), bool (*)(gimple*, tree_node*, tree_node*, void*), bool (*)(gimple*, tree_node*, tree_node*, void*)) /gcc/gimple-walk.c:767 0xdc6f4a analyze_stmt /gcc/ipa-modref.c:1269 0xdc6f4a analyze_function /gcc/ipa-modref.c:2131 0xdc860d execute /gcc/ipa-modref.c:2957 Can you have a look? thanks, Christophe > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi > index b8f5d9e1cce..b83bd902cec 100644 > --- a/gcc/doc/invoke.texi > +++ b/gcc/doc/invoke.texi > @@ -13423,6 +13423,10 @@ Setting to 0 disables the analysis completely. > @item modref-max-escape-points > Specifies the maximum number of escape points tracked by modref per > SSA-name. > > +@item modref-max-adjustments > +Specifies the maximum number the access range is enlarged during modref > dataflow > +analysis. > + > @item profile-func-internal-id > A parameter to control whether to use function internal id in profile > database lookup. If the value is 0, the compiler uses an id that > diff --git a/gcc/ipa-modref-tree.c b/gcc/ipa-modref-tree.c > index 64e57f52147..69395b0113c 100644 > --- a/gcc/ipa-modref-tree.c > +++ b/gcc/ipa-modref-tree.c > @@ -41,7 +41,7 @@ test_insert_search_collapse () > ASSERT_FALSE (t->every_base); > > /* Insert into an empty tree. */ > - t->insert (1, 2, a); > + t->insert (1, 2, a, false); > ASSERT_NE (t->bases, NULL); > ASSERT_EQ (t->bases->length (), 1); > ASSERT_FALSE (t->every_base); > @@ -59,7 +59,7 @@ test_insert_search_collapse () > ASSERT_EQ (ref_node->ref, 2); > > /* Insert when base exists but ref does not. */ > - t->insert (1, 3, a); > + t->insert (1, 3, a, false); > ASSERT_NE (t->bases, NULL); > ASSERT_EQ (t->bases->length (), 1); > ASSERT_EQ (t->search (1), base_node); > @@ -72,7 +72,7 @@ test_insert_search_collapse () > > /* Insert when base and ref exist, but access is not dominated by nor > dominates other accesses. */ > - t->insert (1, 2, a); > + t->insert (1, 2, a, false); > ASSERT_EQ (t->bases->length (), 1); > ASSERT_EQ (t->search (1), base_node); > > @@ -80,12 +80,12 @@ test_insert_search_collapse () > ASSERT_NE (ref_node, NULL); > > /* Insert when base and ref exist and access is dominated. */ > - t->insert (1, 2, a); > + t->insert (1, 2, a, false); > ASSERT_EQ (t->search (1), base_node); > ASSERT_EQ (base_node->search (2), ref_node); > > /* Insert ref to trigger ref list collapse for base 1. */ > - t->insert (1, 4, a); > + t->insert (1, 4, a, false); > ASSERT_EQ (t->search (1), base_node); > ASSERT_EQ (base_node->refs, NULL); > ASSERT_EQ (base_node->search (2), NULL); > @@ -93,7 +93,7 @@ test_insert_search_collapse () > ASSERT_TRUE (base_node->every_ref); > > /* Further inserts to collapsed ref list are ignored. */ > - t->insert (1, 5, a); > + t->insert (1, 5, a, false); > ASSERT_EQ (t->search (1), base_node); > ASSERT_EQ (base_node->refs, NULL); > ASSERT_EQ (base_node->search (2), NULL); > @@ -101,13 +101,13 @@ test_insert_search_collapse () > ASSERT_TRUE (base_node->every_ref); > > /* Insert base to trigger base list collapse. */ > - t->insert (5, 6, a); > + t->insert (5, 6, a, false); > ASSERT_TRUE (t->every_base); > ASSERT_EQ (t->bases, NULL); > ASSERT_EQ (t->search (1), NULL); > > /* Further inserts to collapsed base list are ignored. */ > - t->insert (7, 8, a); > + t->insert (7, 8, a, false); > ASSERT_TRUE (t->every_base); > ASSERT_EQ (t->bases, NULL); > ASSERT_EQ (t->search (1), NULL); > @@ -123,22 +123,22 @@ test_merge () > modref_access_node a = unspecified_modref_access_node; > > t1 = new modref_tree<alias_set_type>(3, 4, 1); > - t1->insert (1, 1, a); > - t1->insert (1, 2, a); > - t1->insert (1, 3, a); > - t1->insert (2, 1, a); > - t1->insert (3, 1, a); > + t1->insert (1, 1, a, false); > + t1->insert (1, 2, a, false); > + t1->insert (1, 3, a, false); > + t1->insert (2, 1, a, false); > + t1->insert (3, 1, a, false); > > t2 = new modref_tree<alias_set_type>(10, 10, 10); > - t2->insert (1, 2, a); > - t2->insert (1, 3, a); > - t2->insert (1, 4, a); > - t2->insert (3, 2, a); > - t2->insert (3, 3, a); > - t2->insert (3, 4, a); > - t2->insert (3, 5, a); > - > - t1->merge (t2, NULL); > + t2->insert (1, 2, a, false); > + t2->insert (1, 3, a, false); > + t2->insert (1, 4, a, false); > + t2->insert (3, 2, a, false); > + t2->insert (3, 3, a, false); > + t2->insert (3, 4, a, false); > + t2->insert (3, 5, a, false); > + > + t1->merge (t2, NULL, false); > > ASSERT_FALSE (t1->every_base); > ASSERT_NE (t1->bases, NULL); > diff --git a/gcc/ipa-modref-tree.h b/gcc/ipa-modref-tree.h > index 2e26b75e21f..6f6932f0875 100644 > --- a/gcc/ipa-modref-tree.h > +++ b/gcc/ipa-modref-tree.h > @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see > Again ref is an template to allow LTO streaming. > 3) Access: this level represent info about individual accesses. > Presently > we record whether access is through a dereference of a function > parameter > + and if so we record the access range. > */ > > #ifndef GCC_MODREF_TREE_H > @@ -57,6 +58,9 @@ struct GTY(()) modref_access_node > a function parameter. */ > int parm_index; > bool parm_offset_known; > + /* Number of times interval was extended during dataflow. > + This has to be limited in order to keep dataflow finite. */ > + unsigned char adjustments; > > /* Return true if access node holds no useful info. */ > bool useful_p () const > @@ -84,6 +88,8 @@ struct GTY(()) modref_access_node > && !known_eq (parm_offset, a.parm_offset)) > return false; > } > + if (range_info_useful_p () != a.range_info_useful_p ()) > + return false; > if (range_info_useful_p () > && (!known_eq (a.offset, offset) > || !known_eq (a.size, size) > @@ -92,16 +98,24 @@ struct GTY(()) modref_access_node > return true; > } > /* Return true A is a subaccess. */ > - bool contains (modref_access_node &a) const > + bool contains (const modref_access_node &a) const > { > - if (parm_index != a.parm_index) > - return false; > + poly_int64 aoffset_adj = 0; > if (parm_index >= 0) > { > - if (parm_offset_known > - && (!a.parm_offset_known > - || !known_eq (parm_offset, a.parm_offset))) > + if (parm_index != a.parm_index) > return false; > + if (parm_offset_known) > + { > + if (!a.parm_offset_known) > + return false; > + /* Accesses are never below parm_offset, so look > + for smaller offset. */ > + if (!known_le (parm_offset, a.parm_offset)) > + return false; > + aoffset_adj = (a.parm_offset - parm_offset) > + << LOG2_BITS_PER_UNIT; > + } > } > if (range_info_useful_p ()) > { > @@ -111,20 +125,181 @@ struct GTY(()) modref_access_node > to fit the store, so smaller or unknown sotre is more general > than large store. */ > if (known_size_p (size) > - && !known_le (size, a.size)) > + && (!known_size_p (a.size) > + || !known_le (size, a.size))) > return false; > if (known_size_p (max_size)) > - return known_subrange_p (a.offset, a.max_size, offset, > max_size); > + return known_subrange_p (a.offset + aoffset_adj, > + a.max_size, offset, max_size); > else > - return known_le (offset, a.offset); > + return known_le (offset, a.offset + aoffset_adj); > } > return true; > } > + /* Update access range to new parameters. > + If RECORD_ADJUSTMENTS is true, record number of changes in the access > + and if threshold is exceeded start dropping precision > + so only constantly many updates are possible. This makes dataflow > + to converge. */ > + void update (poly_int64 parm_offset1, > + poly_int64 offset1, poly_int64 size1, poly_int64 max_size1, > + bool record_adjustments) > + { > + if (known_eq (offset, offset1) > + && known_eq (size, size1) > + && known_eq (max_size, max_size1)) > + return; > + if (!record_adjustments > + || (++adjustments) < param_modref_max_adjustments) > + { > + parm_offset = parm_offset1; > + offset = offset1; > + size = size1; > + max_size = max_size1; > + } > + else > + { > + if (dump_file) > + fprintf (dump_file, > + "--param param=modref-max-adjustments limit > reached:"); > + if (!known_eq (parm_offset, parm_offset1)) > + { > + if (dump_file) > + fprintf (dump_file, " parm_offset cleared"); > + parm_offset_known = false; > + } > + if (!known_eq (size, size1)) > + { > + size = -1; > + if (dump_file) > + fprintf (dump_file, " size cleared"); > + } > + if (!known_eq (max_size, max_size1)) > + { > + max_size = -1; > + if (dump_file) > + fprintf (dump_file, " max_size cleared"); > + } > + if (!known_eq (offset, offset1)) > + { > + offset = 0; > + if (dump_file) > + fprintf (dump_file, " offset cleared"); > + } > + if (dump_file) > + fprintf (dump_file, "\n"); > + } > + } > + /* Merge in access A if it is possible to do without losing > + precision. Return true if successful. > + If RECORD_ADJUSTMENTs is true, remember how many interval > + was prolonged and punt when there are too many. */ > + bool merge (const modref_access_node &a, bool record_adjustments) > + { > + poly_int64 aoffset_adj = 0, offset_adj = 0; > + poly_int64 new_parm_offset = parm_offset; > + > + /* We assume that containment was tested earlier. */ > + gcc_checking_assert (!contains (a) && !a.contains (*this)); > + if (parm_index >= 0) > + { > + if (parm_index != a.parm_index) > + return false; > + if (parm_offset_known) > + { > + if (!a.parm_offset_known) > + return false; > + if (known_le (a.parm_offset, parm_offset)) > + { > + offset_adj = (parm_offset - a.parm_offset) > + << LOG2_BITS_PER_UNIT; > + aoffset_adj = 0; > + new_parm_offset = a.parm_offset; > + } > + else if (known_le (parm_offset, a.parm_offset)) > + { > + aoffset_adj = (a.parm_offset - parm_offset) > + << LOG2_BITS_PER_UNIT; > + offset_adj = 0; > + } > + else > + return false; > + } > + } > + /* See if we can merge ranges. */ > + if (range_info_useful_p ()) > + { > + poly_int64 offset1 = offset + offset_adj; > + poly_int64 aoffset1 = a.offset + aoffset_adj; > + > + /* In this case we have containment that should be > + handled earlier. */ > + gcc_checking_assert (a.range_info_useful_p ()); > + > + /* If a.size is less specified than size, merge only > + if intervals are otherwise equivalent. */ > + if (known_size_p (size) > + && (!known_size_p (a.size) || known_lt (a.size, size))) > + { > + if (((known_size_p (max_size) || known_size_p (a.max_size)) > + && !known_eq (max_size, a.max_size)) > + || !known_eq (offset1, aoffset1)) > + return false; > + update (new_parm_offset, offset1, a.size, max_size, > + record_adjustments); > + return true; > + } > + /* If sizes are same, we can extend the interval. */ > + if ((known_size_p (size) || known_size_p (a.size)) > + && !known_eq (size, a.size)) > + return false; > + if (known_le (offset1, aoffset1)) > + { > + if (!known_size_p (max_size)) > + { > + update (new_parm_offset, offset1, size, max_size, > + record_adjustments); > + return true; > + } > + else if (known_ge (offset1 + max_size, aoffset1)) > + { > + poly_int64 new_max_size = max_size; > + if (known_le (max_size, a.max_size + aoffset1 - offset1)) > + new_max_size = a.max_size + aoffset1 - offset1; > + update (new_parm_offset, offset1, size, new_max_size, > + record_adjustments); > + return true; > + } > + } > + else if (known_le (aoffset1, offset1)) > + { > + if (!known_size_p (a.max_size)) > + { > + update (new_parm_offset, aoffset1, size, a.max_size, > + record_adjustments); > + return true; > + } > + else if (known_ge (aoffset1 + a.max_size, offset1)) > + { > + poly_int64 new_max_size = a.max_size; > + if (known_le (a.max_size, max_size + offset1 - aoffset1)) > + new_max_size = max_size + offset1 - aoffset1; > + update (new_parm_offset, aoffset1, size, new_max_size, > + record_adjustments); > + return true; > + } > + } > + return false; > + } > + update (new_parm_offset, offset + offset_adj, > + size, max_size, record_adjustments); > + return true; > + } > }; > > /* Access node specifying no useful info. */ > const modref_access_node unspecified_modref_access_node > - = {0, -1, -1, 0, -1, false}; > + = {0, -1, -1, 0, -1, false, 0}; > > template <typename T> > struct GTY((user)) modref_ref_node > @@ -149,8 +324,10 @@ struct GTY((user)) modref_ref_node > > /* Insert access with OFFSET and SIZE. > Collapse tree if it has more than MAX_ACCESSES entries. > + If RECORD_ADJUSTMENTs is true avoid too many interval extensions. > Return true if record was changed. */ > - bool insert_access (modref_access_node a, size_t max_accesses) > + bool insert_access (modref_access_node a, size_t max_accesses, > + bool record_adjustments) > { > /* If this base->ref pair has no access information, bail out. */ > if (every_access) > @@ -176,7 +353,17 @@ struct GTY((user)) modref_ref_node > return false; > if (a.contains (*a2)) > { > - *a2 = a; > + a.adjustments = 0; > + a2->parm_index = a.parm_index; > + a2->parm_offset_known = a.parm_offset_known; > + a2->update (a.parm_offset, a.offset, a.size, a.max_size, > + record_adjustments); > + try_merge_with (i); > + return true; > + } > + if (a2->merge (a, record_adjustments)) > + { > + try_merge_with (i); > return true; > } > gcc_checking_assert (!(a == *a2)); > @@ -192,9 +379,28 @@ struct GTY((user)) modref_ref_node > collapse (); > return true; > } > + a.adjustments = 0; > vec_safe_push (accesses, a); > return true; > } > +private: > + /* Try to optimize the access list after entry INDEX was modified. */ > + void > + try_merge_with (size_t index) > + { > + modref_access_node *a2; > + size_t i; > + > + FOR_EACH_VEC_SAFE_ELT (accesses, i, a2) > + if (i != index) > + if ((*accesses)[index].contains (*a2) > + || (*accesses)[index].merge (*a2, false)) > + { > + if (index == accesses->length () - 1) > + index = i; > + accesses->unordered_remove (i); > + } > + } > }; > > /* Base of an access. */ > @@ -342,7 +548,8 @@ struct GTY((user)) modref_tree > > /* Insert memory access to the tree. > Return true if something changed. */ > - bool insert (T base, T ref, modref_access_node a) > + bool insert (T base, T ref, modref_access_node a, > + bool record_adjustments) > { > if (every_base) > return false; > @@ -387,7 +594,8 @@ struct GTY((user)) modref_tree > { > if (ref_node->every_access) > return changed; > - changed |= ref_node->insert_access (a, max_accesses); > + changed |= ref_node->insert_access (a, max_accesses, > + record_adjustments); > /* See if we failed to add useful access. */ > if (ref_node->every_access) > { > @@ -456,7 +664,8 @@ struct GTY((user)) modref_tree > PARM_MAP, if non-NULL, maps parm indexes of callee to caller. -2 is > used > to signalize that parameter is local and does not need to be tracked. > Return true if something has changed. */ > - bool merge (modref_tree <T> *other, vec <modref_parm_map> *parm_map) > + bool merge (modref_tree <T> *other, vec <modref_parm_map> *parm_map, > + bool record_accesses) > { > if (!other || every_base) > return false; > @@ -501,7 +710,8 @@ struct GTY((user)) modref_tree > { > changed |= insert (base_node->base, > ref_node->ref, > - unspecified_modref_access_node); > + unspecified_modref_access_node, > + record_accesses); > } > else > FOR_EACH_VEC_SAFE_ELT (ref_node->accesses, k, access_node) > @@ -525,7 +735,8 @@ struct GTY((user)) modref_tree > = (*parm_map) [a.parm_index].parm_index; > } > } > - changed |= insert (base_node->base, ref_node->ref, a); > + changed |= insert (base_node->base, ref_node->ref, a, > + record_accesses); > } > } > } > @@ -537,7 +748,7 @@ struct GTY((user)) modref_tree > /* Copy OTHER to THIS. */ > void copy_from (modref_tree <T> *other) > { > - merge (other, NULL); > + merge (other, NULL, false); > } > > /* Search BASE in tree; return NULL if failed. */ > diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c > index 6ab687a7ba0..0d5ab9c0561 100644 > --- a/gcc/ipa-modref.c > +++ b/gcc/ipa-modref.c > @@ -426,6 +426,8 @@ dump_access (modref_access_node *a, FILE *out) > print_dec ((poly_int64_pod)a->size, out, SIGNED); > fprintf (out, " max_size:"); > print_dec ((poly_int64_pod)a->max_size, out, SIGNED); > + if (a->adjustments) > + fprintf (out, " adjusted %i times", a->adjustments); > } > fprintf (out, "\n"); > } > @@ -656,7 +658,7 @@ get_access (ao_ref *ref) > > base = ao_ref_base (ref); > modref_access_node a = {ref->offset, ref->size, ref->max_size, > - 0, -1, false}; > + 0, -1, false, 0}; > if (TREE_CODE (base) == MEM_REF || TREE_CODE (base) == TARGET_MEM_REF) > { > tree memref = base; > @@ -708,7 +710,7 @@ record_access (modref_records *tt, ao_ref *ref) > fprintf (dump_file, " - Recording base_set=%i ref_set=%i > parm=%i\n", > base_set, ref_set, a.parm_index); > } > - tt->insert (base_set, ref_set, a); > + tt->insert (base_set, ref_set, a, false); > } > > /* IPA version of record_access_tree. */ > @@ -774,7 +776,7 @@ record_access_lto (modref_records_lto *tt, ao_ref *ref) > a.parm_index); > } > > - tt->insert (base_type, ref_type, a); > + tt->insert (base_type, ref_type, a, false); > } > > /* Returns true if and only if we should store the access to EXPR. > @@ -858,12 +860,15 @@ parm_map_for_arg (gimple *stmt, int i) > > /* Merge side effects of call STMT to function with CALLEE_SUMMARY > int CUR_SUMMARY. Return true if something changed. > - If IGNORE_STORES is true, do not merge stores. */ > + If IGNORE_STORES is true, do not merge stores. > + If RECORD_ADJUSTMENTS is true cap number of adjustments to > + a given access to make dataflow finite. */ > > bool > merge_call_side_effects (modref_summary *cur_summary, > gimple *stmt, modref_summary *callee_summary, > - bool ignore_stores, cgraph_node *callee_node) > + bool ignore_stores, cgraph_node *callee_node, > + bool record_adjustments) > { > auto_vec <modref_parm_map, 32> parm_map; > bool changed = false; > @@ -902,11 +907,13 @@ merge_call_side_effects (modref_summary *cur_summary, > fprintf (dump_file, "\n"); > > /* Merge with callee's summary. */ > - changed |= cur_summary->loads->merge (callee_summary->loads, &parm_map); > + changed |= cur_summary->loads->merge (callee_summary->loads, &parm_map, > + record_adjustments); > if (!ignore_stores) > { > changed |= cur_summary->stores->merge (callee_summary->stores, > - &parm_map); > + &parm_map, > + record_adjustments); > if (!cur_summary->writes_errno > && callee_summary->writes_errno) > { > @@ -941,7 +948,7 @@ get_access_for_fnspec (gcall *call, attr_fnspec > &fnspec, > } > modref_access_node a = {0, -1, -1, > map.parm_offset, map.parm_index, > - map.parm_offset_known}; > + map.parm_offset_known, 0}; > poly_int64 size_hwi; > if (size > && poly_int_tree_p (size, &size_hwi) > @@ -1044,12 +1051,14 @@ process_fnspec (modref_summary *cur_summary, > cur_summary->loads->insert (0, 0, > get_access_for_fnspec (call, > fnspec, i, > - map)); > + map), > + false); > if (cur_summary_lto) > cur_summary_lto->loads->insert (0, 0, > get_access_for_fnspec (call, > > fnspec, i, > - map)); > + map), > + false); > } > } > if (ignore_stores) > @@ -1077,12 +1086,14 @@ process_fnspec (modref_summary *cur_summary, > cur_summary->stores->insert (0, 0, > get_access_for_fnspec (call, > fnspec, > i, > - map)); > + map), > + false); > if (cur_summary_lto) > cur_summary_lto->stores->insert (0, 0, > get_access_for_fnspec (call, > > fnspec, i, > - > map)); > + map), > + false); > } > if (fnspec.errno_maybe_written_p () && flag_errno_math) > { > @@ -1168,7 +1179,7 @@ analyze_call (modref_summary *cur_summary, > modref_summary_lto *cur_summary_lto, > } > > merge_call_side_effects (cur_summary, stmt, callee_summary, > ignore_stores, > - callee_node); > + callee_node, false); > > return true; > } > @@ -2134,6 +2145,7 @@ analyze_function (function *f, bool ipa) > if (!ipa) > { > bool changed = true; > + bool first = true; > while (changed) > { > changed = false; > @@ -2144,13 +2156,14 @@ analyze_function (function *f, bool ipa) > ignore_stores_p (current_function_decl, > gimple_call_flags > (recursive_calls[i])), > - fnode); > + fnode, !first); > if (!summary->useful_p (ecf_flags, false)) > { > remove_summary (lto, nolto, ipa); > return; > } > } > + first = false; > } > } > if (summary && !summary->useful_p (ecf_flags)) > @@ -2501,11 +2514,11 @@ read_modref_records (lto_input_block *ib, struct > data_in *data_in, > } > } > modref_access_node a = {offset, size, max_size, parm_offset, > - parm_index, parm_offset_known}; > + parm_index, parm_offset_known, > false}; > if (nolto_ref_node) > - nolto_ref_node->insert_access (a, max_accesses); > + nolto_ref_node->insert_access (a, max_accesses, false); > if (lto_ref_node) > - lto_ref_node->insert_access (a, max_accesses); > + lto_ref_node->insert_access (a, max_accesses, false); > } > } > } > @@ -3187,16 +3200,18 @@ ipa_merge_modref_summary_after_inlining > (cgraph_edge *edge) > if (!ignore_stores) > { > if (to_info && callee_info) > - to_info->stores->merge (callee_info->stores, &parm_map); > + to_info->stores->merge (callee_info->stores, &parm_map, false); > if (to_info_lto && callee_info_lto) > - to_info_lto->stores->merge (callee_info_lto->stores, > &parm_map); > + to_info_lto->stores->merge (callee_info_lto->stores, &parm_map, > + false); > } > if (!(flags & (ECF_CONST | ECF_NOVOPS))) > { > if (to_info && callee_info) > - to_info->loads->merge (callee_info->loads, &parm_map); > + to_info->loads->merge (callee_info->loads, &parm_map, false); > if (to_info_lto && callee_info_lto) > - to_info_lto->loads->merge (callee_info_lto->loads, &parm_map); > + to_info_lto->loads->merge (callee_info_lto->loads, &parm_map, > + false); > } > } > > @@ -3346,7 +3361,7 @@ get_access_for_fnspec (cgraph_edge *e, attr_fnspec > &fnspec, > size = TYPE_SIZE_UNIT (get_parm_type (e->callee->decl, i)); > modref_access_node a = {0, -1, -1, > map.parm_offset, map.parm_index, > - map.parm_offset_known}; > + map.parm_offset_known, 0}; > poly_int64 size_hwi; > if (size > && poly_int_tree_p (size, &size_hwi) > @@ -3399,10 +3414,10 @@ propagate_unknown_call (cgraph_node *node, > } > if (cur_summary) > changed |= cur_summary->loads->insert > - (0, 0, get_access_for_fnspec (e, fnspec, i, map)); > + (0, 0, get_access_for_fnspec (e, fnspec, i, map), false); > if (cur_summary_lto) > changed |= cur_summary_lto->loads->insert > - (0, 0, get_access_for_fnspec (e, fnspec, i, map)); > + (0, 0, get_access_for_fnspec (e, fnspec, i, map), false); > } > } > if (ignore_stores_p (node->decl, ecf_flags)) > @@ -3429,10 +3444,10 @@ propagate_unknown_call (cgraph_node *node, > } > if (cur_summary) > changed |= cur_summary->stores->insert > - (0, 0, get_access_for_fnspec (e, fnspec, i, map)); > + (0, 0, get_access_for_fnspec (e, fnspec, i, map), false); > if (cur_summary_lto) > changed |= cur_summary_lto->stores->insert > - (0, 0, get_access_for_fnspec (e, fnspec, i, map)); > + (0, 0, get_access_for_fnspec (e, fnspec, i, map), false); > } > } > if (fnspec.errno_maybe_written_p () && flag_errno_math) > @@ -3491,6 +3506,7 @@ static void > modref_propagate_in_scc (cgraph_node *component_node) > { > bool changed = true; > + bool first = true; > int iteration = 0; > > while (changed) > @@ -3628,11 +3644,12 @@ modref_propagate_in_scc (cgraph_node > *component_node) > if (callee_summary) > { > changed |= cur_summary->loads->merge > - (callee_summary->loads, &parm_map); > + (callee_summary->loads, &parm_map, > !first); > if (!ignore_stores) > { > changed |= cur_summary->stores->merge > - (callee_summary->stores, &parm_map); > + (callee_summary->stores, &parm_map, > + !first); > if (!cur_summary->writes_errno > && callee_summary->writes_errno) > { > @@ -3644,11 +3661,13 @@ modref_propagate_in_scc (cgraph_node > *component_node) > if (callee_summary_lto) > { > changed |= cur_summary_lto->loads->merge > - (callee_summary_lto->loads, &parm_map); > + (callee_summary_lto->loads, &parm_map, > + !first); > if (!ignore_stores) > { > changed |= cur_summary_lto->stores->merge > - (callee_summary_lto->stores, > &parm_map); > + (callee_summary_lto->stores, > &parm_map, > + !first); > if (!cur_summary_lto->writes_errno > && callee_summary_lto->writes_errno) > { > @@ -3674,6 +3693,7 @@ modref_propagate_in_scc (cgraph_node *component_node) > } > } > iteration++; > + first = false; > } > if (dump_file) > fprintf (dump_file, > diff --git a/gcc/params.opt b/gcc/params.opt > index f414dc1a61c..223f0a02111 100644 > --- a/gcc/params.opt > +++ b/gcc/params.opt > @@ -1013,6 +1013,10 @@ Maximum depth of DFS walk used by modref escape > analysis. > Common Joined UInteger Var(param_modref_max_escape_points) Init(256) > Param Optimization > Maximum number of escape points tracked by modref per SSA-name. > > +-param=modref-max-adjustments= > +Common Joined UInteger Var(param_modref_max_adjustments) Init(8) > IntegerRange (0, 255) Param Optimization > +Maximum number of times a given range is adjusted during the dataflow > + > -param=tm-max-aggregate-size= > Common Joined UInteger Var(param_tm_max_aggregate_size) Init(9) Param > Optimization > Size in bytes after which thread-local aggregates should be instrumented > with the logging functions instead of save/restore pairs. > diff --git a/gcc/testsuite/gcc.dg/ipa/modref-1.c > b/gcc/testsuite/gcc.dg/ipa/modref-1.c > index 858567d35d5..5314e7dbbf7 100644 > --- a/gcc/testsuite/gcc.dg/ipa/modref-1.c > +++ b/gcc/testsuite/gcc.dg/ipa/modref-1.c > @@ -10,15 +10,15 @@ void a(char *ptr, char *ptr2) > __attribute__((noinline)) > void b(char *ptr) > { > - a(ptr+1,&ptr[2]); > + a(ptr+1,&ptr[3]); > } > > int main() > { > - char c[3]={0,1,0}; > + char c[4]={0,1,0,0}; > b(c); > - return c[0]+c[2]; > + return c[0]+c[3]; > } > /* Check that both param offsets are determined correctly. */ > /* { dg-final { scan-ipa-dump "param offset:1" "modref" } } */ > -/* { dg-final { scan-ipa-dump "param offset:2" "modref" } } */ > +/* { dg-final { scan-ipa-dump "param offset:3" "modref" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c > b/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c > index 3ac217bafb8..a2b3b1102ec 100644 > --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c > +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c > @@ -10,7 +10,7 @@ void a(char *ptr, char *ptr2) > __attribute__((noinline)) > void b(char *ptr) > { > - a(ptr+1,&ptr[2]); > + a(ptr+1,&ptr[3]); > } > > int main() > @@ -22,5 +22,5 @@ int main() > /* Check that both param offsets are determined correctly and the > computation > is optimized out. */ > /* { dg-final { scan-tree-dump "param offset:1" "modref1" } } */ > -/* { dg-final { scan-tree-dump "param offset:2" "modref1" } } */ > +/* { dg-final { scan-tree-dump "param offset:3" "modref1" } } */ > /* { dg-final { scan-tree-dump "return 0" "modref1" } } */ > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c > b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c > new file mode 100644 > index 00000000000..15ae4acc03f > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-8.c > @@ -0,0 +1,25 @@ > +/* { dg-options "-O2 --param modref-max-adjustments=8 > -fdump-tree-modref1" } */ > +/* { dg-do compile } */ > +void > +set (char *p) > +{ > + p[1]=1; > + p[0]=0; > + p[2]=2; > + p[4]=4; > + p[3]=3; > +} > + > +void > +recurse (char *p, int n) > +{ > + *p = 0; > + if (n) > + recurse (p+1,n-1); > +} > +/* { dg-final { scan-tree-dump-not "param=modref-max-accesses" "modref1" > } } */ > +/* { dg-final { scan-tree-dump "param=modref-max-adjustments" "modref1" } > } */ > +/* In set all accesses should merge together. */ > +/* { dg-final { scan-tree-dump "access: Parm 0 param offset:0 offset:0 > size:8 max_size:40" "modref1" } } */ > +/* In recurse we should cap the recrusion after 8 attempts and set > max_size to -1. */ > +/* { dg-final { scan-tree-dump "access: Parm 0 param offset:0 offset:0 > size:8 max_size:-1 adjusted 8 times" "modref1" } } */ >