This is bit modified patch I am testing. I added pre-computation of the
number of accesses, enabled the path for const functions (in case they
have memory operand), initialized alias sets and clarified the logic
around every_* and global_memory_accesses
PR tree-optimization/103168
(modref_summary::finalize): Initialize load_accesses.
* ipa-modref.h (struct modref_summary): Add load_accesses.
* tree-ssa-sccvn.c (visit_reference_op_call): Use modref
info to walk the virtual use->def chain to CSE pure
function calls.
* g++.dg/tree-ssa/pr103168.C: New testcase.
diff --git a/gcc/ipa-modref.c b/gcc/ipa-modref.c
index 4f9323165ea..595eb6e0d8f 100644
--- a/gcc/ipa-modref.c
+++ b/gcc/ipa-modref.c
@@ -725,6 +727,23 @@ modref_summary::finalize (tree fun)
break;
}
}
+ if (loads->every_base)
+ load_accesses = 1;
+ else
+ {
+ load_accesses = 0;
+ for (auto base_node : loads->bases)
+ {
+ if (base_node->every_ref)
+ load_accesses++;
+ else
+ for (auto ref_node : base_node->refs)
+ if (ref_node->every_access)
+ load_accesses++;
+ else
+ load_accesses += ref_node->accesses->length ();
+ }
+ }
}
/* Get function summary for FUNC if it exists, return NULL otherwise. */
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index f868eb6de07..a7937d74945 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -53,6 +53,8 @@ struct GTY(()) modref_summary
/* Flags coputed by finalize method. */
+ /* Total number of accesses in loads tree. */
+ unsigned int load_accesses;
/* global_memory_read is not set for functions calling functions
with !binds_to_current_def which, after interposition, may read global
memory but do nothing useful with it (except for crashing if some
diff --git a/gcc/testsuite/g++.dg/tree-ssa/pr103168.C
b/gcc/testsuite/g++.dg/tree-ssa/pr103168.C
new file mode 100644
index 00000000000..82924a3e3ce
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/pr103168.C
@@ -0,0 +1,24 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-fre1-details" }
+
+struct a
+{
+ int a;
+ static __attribute__ ((noinline))
+ int ret (int v) {return v;}
+
+ __attribute__ ((noinline))
+ int inca () {return a++;}
+};
+
+int
+test()
+{
+ struct a av;
+ av.a=1;
+ int val = av.ret (0) + av.inca();
+ av.a=2;
+ return val + av.ret(0) + av.inca();
+}
+
+/* { dg-final { scan-tree-dump-times "Replaced a::ret" 1 "fre1" } } */
diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c
index 149674e6a16..719f5184654 100644
--- a/gcc/tree-ssa-sccvn.c
+++ b/gcc/tree-ssa-sccvn.c
@@ -71,6 +71,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-loop-niter.h"
#include "builtins.h"
#include "fold-const-call.h"
+#include "ipa-modref-tree.h"
+#include "ipa-modref.h"
#include "tree-ssa-sccvn.h"
/* This algorithm is based on the SCC algorithm presented by Keith
@@ -5084,12 +5086,118 @@ visit_reference_op_call (tree lhs, gcall *stmt)
struct vn_reference_s vr1;
vn_reference_t vnresult = NULL;
tree vdef = gimple_vdef (stmt);
+ modref_summary *summary;
/* Non-ssa lhs is handled in copy_reference_ops_from_call. */
if (lhs && TREE_CODE (lhs) != SSA_NAME)
lhs = NULL_TREE;
vn_reference_lookup_call (stmt, &vnresult, &vr1);
+
+ /* If the lookup did not succeed for pure functions try to use
+ modref info to find a candidate to CSE to. */
+ const int accesses_limit = 8;
+ if (!vnresult
+ && !vdef
+ && lhs
+ && gimple_vuse (stmt)
+ && (((summary = get_modref_function_summary (stmt, NULL))
+ && !summary->global_memory_read
+ && summary->load_accesses < accesses_limit)
+ || gimple_call_flags (stmt) & ECF_CONST))
+ {
+ /* First search if we can do someting useful and build a
+ vector of all loads we have to check. */
+ bool unknown_memory_access = false;
+ auto_vec<ao_ref, accesses_limit> accesses;
+
+ if (summary)
+ {
+ for (auto base_node : summary->loads->bases)
+ if (unknown_memory_access)
+ break;
+ else for (auto ref_node : base_node->refs)
+ if (unknown_memory_access)
+ break;
+ else for (auto access_node : ref_node->accesses)
+ {
+ accesses.quick_grow (accesses.length () + 1);
+ if (!access_node.get_ao_ref (stmt, &accesses.last ()))
+ {
+ /* We could use get_call_arg (...) and initialize
+ a ref based on the argument and unknown offset in
+ some cases, but we have to get a ao_ref to
+ disambiguate against other stmts. */
+ unknown_memory_access = true;
+ break;
+ }
+ else
+ {
+ accesses.last ().base_alias_set = base_node->base;
+ accesses.last ().ref_alias_set = ref_node->ref;
+ }
+ }
+ }
+ if (!unknown_memory_access)
+ for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+ {
+ tree arg = gimple_call_arg (stmt, i);
+ if (TREE_CODE (arg) != SSA_NAME
+ && !is_gimple_min_invariant (arg))
+ {
+ if (accesses.length () >= accesses_limit)
+ {
+ unknown_memory_access = true;
+ break;
+ }
+ accesses.quick_grow (accesses.length () + 1);
+ ao_ref_init (&accesses.last (), arg);
+ }
+ }
+
+ /* Walk the VUSE->VDEF chain optimistically trying to find an entry
+ for the call in the hashtable. */
+ unsigned limit = (unknown_memory_access
+ ? 0
+ : (param_sccvn_max_alias_queries_per_access
+ / (accesses.length () + 1)));
+ while (limit > 0 && !vnresult && !SSA_NAME_IS_DEFAULT_DEF (vr1.vuse))
+ {
+ vr1.hashcode = vr1.hashcode - SSA_NAME_VERSION (vr1.vuse);
+ gimple *def = SSA_NAME_DEF_STMT (vr1.vuse);
+ /* ??? We could use fancy stuff like in walk_non_aliased_vuses, but
+ do not bother for now. */
+ if (is_a <gphi *> (def))
+ break;
+ vr1.vuse = vuse_ssa_val (gimple_vuse (def));
+ vr1.hashcode = vr1.hashcode + SSA_NAME_VERSION (vr1.vuse);
+ vn_reference_lookup_1 (&vr1, &vnresult);
+ }
+
+ /* If we found a candidate to CSE to verify it is valid. */
+ if (vnresult && !accesses.is_empty ())
+ {
+ tree vuse = vuse_ssa_val (gimple_vuse (stmt));
+ while (vnresult && vuse != vr1.vuse)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (vuse);
+ for (auto &ref : accesses)
+ {
+ /* ??? stmt_may_clobber_ref_p_1 does per stmt constant
+ analysis overhead that we might be able to cache. */
+ if (stmt_may_clobber_ref_p_1 (def, &ref, true))
+ {
+ vnresult = NULL;
+ break;
+ }
+ }
+ vuse = vuse_ssa_val (gimple_vuse (def));
+ }
+ }
+ if (vnresult)
+ statistics_counter_event (cfun, "Pure functions modref eliminated", 1);
+ }
+
if (vnresult)
{
if (vnresult->result_vdef && vdef)
@@ -5130,7 +5238,8 @@ visit_reference_op_call (tree lhs, gcall *stmt)
if (lhs)
changed |= set_ssa_val_to (lhs, lhs);
vr2 = XOBNEW (&vn_tables_obstack, vn_reference_s);
- vr2->vuse = vr1.vuse;
+ tree vuse = gimple_vuse (stmt);
+ vr2->vuse = vuse ? SSA_VAL (vuse) : NULL_TREE;
/* As we are not walking the virtual operand chain we know the
shared_lookup_references are still original so we can re-use
them here. */
@@ -5139,7 +5248,7 @@ visit_reference_op_call (tree lhs, gcall *stmt)
vr2->punned = vr1.punned;
vr2->set = vr1.set;
vr2->base_set = vr1.base_set;
- vr2->hashcode = vr1.hashcode;
+ vr2->hashcode = vn_reference_compute_hash (vr2);
vr2->result = lhs;
vr2->result_vdef = vdef_val;
vr2->value_id = 0;