> > 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)
It happens on 32bit arches only it seems. For some reason we end up merging access: Parm 0 param offset:12 offset:0 size:96 max_size:96 access: Parm 0 param offset:0 offset:0 size:96 max_size:96 as access: Parm 0 param offset:0 offset:0 size:96 max_size:192 which is correct but we already have access: Parm 0 param offset:0 offset:0 size:32 max_size:192 in the list and merging asserts since we have proper subaccess which is supposed to be handled earlier. try_merge_with does not consider the case but there is already proble with both access: Parm 0 param offset:12 offset:0 size:96 max_size:96 access: Parm 0 param offset:0 offset:0 size:32 max_size:192 being in the list since the first is subaccess of the second. So after lunch I will need to debug how those two gets into the list at first place... Honza > 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" } } */ > >