Last year I made modref to track which functions are deterministic - i.e. they
produce same effects given same inputs (including memory) and which functions
have no side effects (which includes infinite loops, trapping etc.).

deterministic functions can be CSEed just as looping pure/const.
These are similar to reproducible/unsequenced attributes.

This patch implements tree-ssa-sccvn bits so we can now optimize non-pure
function. For example:

[[gnu::noinline]]
int dup(int *a) 
{
        a[0]=a[1];
        return a[2];
}
int
test (int *b)
{
        return dup (b) + dup (b);
}

Will now be optimized to return 2 * dup (b);

The sccvn bits are quite simple: visit_stmt has to allow VN lookups for
deterministic functions while visit_reference_op_call currently contains test
to give up if function has vdef or no lhs.  I think the vdef test was redundant
even before since pure/const function can return aggregate.  Newly even if 
there is
no lhs we can eliminate redundancies as in:

[[gnu::noinline]]
void dup(int *a) 
{
        a[0]=a[1];
}
void
test (int *b)
{
        dup (b);
        dup (b);
}

I think it is relatively useful feature to optimize out functions that compute 
some fields
of structure based on other fields.  It optimizes out 2.6K of clang text
segment.  This is not much, but I hope it will improve in future.  For example,
right now dup call can not do sanity checks.  Call to abort makes us believe
that all global memory is used and that will prevent optimizations. However
modref can keep track of the fact that global memory read only happens upon
termination and ignore those accesses while determining redundancy...

Also reproduce/unsequential makes it possible to ignore stores of function call 
itself
to detemrine redundancy.  I am not quite sure how to fit that into PRE.

bootstrapped/regtested x86_64-linux. OK?

gcc/ChangeLog:

        * ipa-modref.cc (get_modref_function_summary): Add extra fn paramaeter
        to pass value numbering info.
        (ipa_modref_can_remove_redundat_calls): New function.
        * ipa-modref.h (get_modref_function_summary): Update declaration.
        (ipa_modref_can_remove_redundat_calls): declare.
        * tree-ssa-sccvn.cc (visit_reference_op_call): Also try to optimize 
calls
        with vdef and no lhs.
        (visit_stmt): Use ipa_modref_can_remove_redundat_calls.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/modref-16.c: New test.
        * gcc.dg/tree-ssa/modref-17.c: New test.

diff --git a/gcc/ipa-modref.cc b/gcc/ipa-modref.cc
index 08a7740de94..50d18287219 100644
--- a/gcc/ipa-modref.cc
+++ b/gcc/ipa-modref.cc
@@ -766,12 +766,14 @@ get_modref_function_summary (cgraph_node *func)
 /* Get function summary for CALL if it exists, return NULL otherwise.
    If non-null set interposed to indicate whether function may not
    bind to current def.  In this case sometimes loads from function
-   needs to be ignored.  */
+   needs to be ignored.
+   If FN is non-NULL, it specifies indirect call target (for example when known
+   to value numbering).  */
 
 modref_summary *
-get_modref_function_summary (gcall *call, bool *interposed)
+get_modref_function_summary (gcall *call, bool *interposed, tree fn)
 {
-  tree callee = gimple_call_fndecl (call);
+  tree callee = fn ? fn : gimple_call_fndecl (call);
   if (!callee)
     return NULL;
   struct cgraph_node *node = cgraph_node::get (callee);
@@ -5648,4 +5650,33 @@ ipa_modref_callee_reads_no_memory_p (gcall *call)
   return false;
 }
 
+/* Return true if CALL can be optimized out provided that was called with
+   exactly same value number before.
+
+   If FN is non-NULL, it specifies indirect call target (for example when known
+   to value numbering).  */
+
+bool
+ipa_modref_can_remove_redundat_calls (gcall *call, tree fn)
+{
+  if (gimple_call_flags (call) & (ECF_CONST | ECF_PURE))
+    return true;
+  if (fn && (flags_from_decl_or_type (fn) & (ECF_CONST | ECF_PURE)))
+    return true;
+  /* If function has multiple ways to return, we would have to remember
+     the way it returned last time.
+     Terminating function execution (i.e. throwing external exception)
+     is OK.  */
+  basic_block bb = gimple_bb (call);
+  if (call == gsi_stmt (gsi_last_bb (bb)) && !single_succ_p (bb))
+    return false;
+  modref_summary *sum = get_modref_function_summary (call, NULL, fn);
+  /* If function is deterministic, we can remove redundant invocations.
+     However, in current implementaiton, we must track that loads and stores do
+     not overlap.  Special case the situation that function reads and writes
+     global memory and therefore we know nothing.  */
+  return sum && !sum->nondeterministic
+        && (!sum->global_memory_read || !sum->global_memory_written);
+}
+
 #include "gt-ipa-modref.h"
diff --git a/gcc/ipa-modref.h b/gcc/ipa-modref.h
index a0eb63a0afa..5bb4172ef9e 100644
--- a/gcc/ipa-modref.h
+++ b/gcc/ipa-modref.h
@@ -72,10 +72,12 @@ struct GTY(()) modref_summary
 };
 
 modref_summary *get_modref_function_summary (cgraph_node *func);
-modref_summary *get_modref_function_summary (gcall *call, bool *interposed);
+modref_summary *get_modref_function_summary (gcall *call, bool *interposed,
+                                            tree fn = NULL);
 void ipa_modref_cc_finalize ();
 void ipa_merge_modref_summary_after_inlining (cgraph_edge *e);
 bool ipa_modref_callee_reads_no_memory_p (gcall *call);
+bool ipa_modref_can_remove_redundat_calls (gcall *call, tree fn = NULL);
 
 /* All flags that are implied by the ECF_CONST functions.  */
 static const int implicit_const_eaf_flags
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-16.c 
b/gcc/testsuite/gcc.dg/tree-ssa/modref-16.c
new file mode 100644
index 00000000000..702349bbc02
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-16.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-release_ssa"  } */
+[[gnu::noinline]]
+int dup(int *a) 
+{
+       a[0]=a[1];
+       return a[2];
+}
+int
+test (int *b)
+{
+       return dup (b) + dup (b);
+}
+/* { dg-final { scan-tree-dump-times "dup .b" 1 "release_ssa"} } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/modref-17.c 
b/gcc/testsuite/gcc.dg/tree-ssa/modref-17.c
new file mode 100644
index 00000000000..703cb46fabb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/modref-17.c
@@ -0,0 +1,13 @@
+/* { dg-options "-O2 -fdump-tree-release_ssa"  } */
+[[gnu::noinline]]
+void dup(int *a) 
+{
+       a[0]=a[1];
+}
+void
+test (int *b)
+{
+       dup (b);
+       dup (b);
+}
+/* { dg-final { scan-tree-dump-times "dup .b" 1 "release_ssa"} } */
diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
index e93acb44200..6e31f8b3cfe 100644
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
@@ -5567,8 +5567,6 @@ visit_reference_op_call (tree lhs, gcall *stmt)
      modref info to find a candidate to CSE to.  */
   const unsigned accesses_limit = 8;
   if (!vnresult
-      && !vdef
-      && lhs
       && gimple_vuse (stmt)
       && (((summary = get_modref_function_summary (stmt, NULL))
           && !summary->global_memory_read
@@ -6354,19 +6352,18 @@ visit_stmt (gimple *stmt, bool backedges_varying_p = 
false)
 
       /* Pick up flags from a devirtualization target.  */
       tree fn = gimple_call_fn (stmt);
-      int extra_fnflags = 0;
       if (fn && TREE_CODE (fn) == SSA_NAME)
        {
          fn = SSA_VAL (fn);
          if (TREE_CODE (fn) == ADDR_EXPR
              && TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL)
-           extra_fnflags = flags_from_decl_or_type (TREE_OPERAND (fn, 0));
+           fn = TREE_OPERAND (fn, 0);
+         else
+           fn = NULL;
        }
-      if ((/* Calls to the same function with the same vuse
-             and the same operands do not necessarily return the same
-             value, unless they're pure or const.  */
-          ((gimple_call_flags (call_stmt) | extra_fnflags)
-           & (ECF_PURE | ECF_CONST))
+      else
+       fn = NULL;
+      if ((ipa_modref_can_remove_redundat_calls (call_stmt, fn)
           /* If calls have a vdef, subsequent calls won't have
              the same incoming vuse.  So, if 2 calls with vdef have the
              same vuse, we know they're not subsequent.

Reply via email to