From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1075) id 84D603857C67; Wed, 25 Aug 2021 19:43:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 84D603857C67 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Jan Hubicka To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-3142] Merge load/stores in ipa-modref summaries X-Act-Checkin: gcc X-Git-Author: Jan Hubicka X-Git-Refname: refs/heads/master X-Git-Oldrev: bb24717e5042b6e8a3847e780a8d215edb9c62f6 X-Git-Newrev: 5c85f29537662f1f4195a102cbf0182ffa32d8ac Message-Id: <20210825194346.84D603857C67@sourceware.org> Date: Wed, 25 Aug 2021 19:43:46 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 25 Aug 2021 19:43:46 -0000 https://gcc.gnu.org/g:5c85f29537662f1f4195a102cbf0182ffa32d8ac commit r12-3142-g5c85f29537662f1f4195a102cbf0182ffa32d8ac Author: Jan Hubicka Date: Wed Aug 25 21:43:07 2021 +0200 Merge load/stores in ipa-modref summaries 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. 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::merge): 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. Diff: --- gcc/doc/invoke.texi | 4 + gcc/ipa-modref-tree.c | 44 +++--- gcc/ipa-modref-tree.h | 247 ++++++++++++++++++++++++++++--- gcc/ipa-modref.c | 80 ++++++---- gcc/params.opt | 4 + gcc/testsuite/gcc.dg/ipa/modref-1.c | 8 +- gcc/testsuite/gcc.dg/tree-ssa/modref-4.c | 8 +- gcc/testsuite/gcc.dg/tree-ssa/modref-8.c | 25 ++++ 8 files changed, 342 insertions(+), 78 deletions(-) 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(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(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 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 *other, vec *parm_map) + bool merge (modref_tree *other, vec *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 *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 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..cec43d20533 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, 254) 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..a277c70677e 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-4.c @@ -10,17 +10,17 @@ 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[4]={0,1,2,0}; + char c[5]={0,1,2,0,0}; b(c); - return c[0]+c[3]; + return c[0]+c[4]; } /* 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" } } */