From: Kyrylo Tkachov <[email protected]>

factor_out_conditional_load currently only runs from the sink pass.  Also call
it from phiopt itself when the join block of a diamond load PHI <*P, *Q> lies
outside any loop: there is nothing to loop-vectorise there, so commoning the
two arm loads into a single P' = PHI <P, Q>; result = *P' is a pure win and is
cheap to attempt.

This is restricted to:
  * the late phiopt run (!early_p), since after inlining the region may yet
    become part of a loop where the commoned load could inhibit vectorisation;
  * SSA-name arm pointers (a new REQUIRE_SSA_ARGS argument to
    factor_out_conditional_load), so that phiprop does not undo it.

This triggers a few 100s times in SPEC2026 with no harmful effects. The
llvm benchmarks improves slightly on my aarch64 machine (1.6%)

Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk after the prerequisites?

Thanks,
Kyrill

Signed-off-by: Kyrylo Tkachov <[email protected]>

gcc/
        PR tree-optimization/125557
        * tree-ssa-phiopt.h (conditional_load_factorable_p)
        (factor_out_conditional_load): Add a REQUIRE_SSA_ARGS parameter
        defaulting to false.
        * tree-ssa-phiopt.cc (conditional_load_factorable_p): Honour
        REQUIRE_SSA_ARGS.
        (factor_out_conditional_load): Pass it through.
        (pass_phiopt::execute): Call factor_out_conditional_load for
        out-of-loop diamonds in the late run.

gcc/testsuite/

        PR tree-optimization/125557
        * gcc.dg/tree-ssa/phiopt-factor-load-1.c: New test.
---
 .../gcc.dg/tree-ssa/phiopt-factor-load-1.c    | 22 ++++++++++
 gcc/tree-ssa-phiopt.cc                        | 42 ++++++++++++++-----
 gcc/tree-ssa-phiopt.h                         |  8 ++--
 3 files changed, 59 insertions(+), 13 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/phiopt-factor-load-1.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/phiopt-factor-load-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/phiopt-factor-load-1.c
new file mode 100644
index 00000000000..fe6a5e9cf3f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/phiopt-factor-load-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-phiopt2" } */
+
+/* PR tree-optimization/125557: outside any loop a diamond load PHI <*p, *q> is
+   factored by phiopt itself (not only by the sink pass) into a pointer PHI
+   P' = PHI <p, q> followed by a single load *P', removing the duplicate load.
+   The arm pointers here are SSA names, so phiprop does not undo it.  */
+
+int
+f (int *p, int *q, int c)
+{
+  int x;
+  if (c)
+    x = *p;
+  else
+    x = *q;
+  return x;
+}
+
+/* The two arm loads collapse to a pointer PHI feeding one load of its result. 
 */
+/* { dg-final { scan-tree-dump {= PHI <p_[0-9]+\(D\)} "phiopt2" } } */
+/* { dg-final { scan-tree-dump-times {= \*_[0-9]+;} 1 "phiopt2" } } */
diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
index c2cdc54f977..44523267626 100644
--- a/gcc/tree-ssa-phiopt.cc
+++ b/gcc/tree-ssa-phiopt.cc
@@ -437,13 +437,15 @@ remove_factored_arm_defs (gimple *d0, gimple *d1)
 }
 
 /* Return true if factor_out_conditional_load (below) would factor the load PHI
-   at MERGE.  E0/E1 are the arm->MERGE edges.  This is 
factor_out_conditional_load's
-   eligibility test, split out so callers (the sink pass) can gate on 
commonability
-   without mutating: the two MUST stay in lockstep, otherwise a caller could 
common
-   a load the sink then cannot finish if-converting.  */
+   at MERGE.  E0/E1 are the arm->MERGE edges; when REQUIRE_SSA_ARGS the two arm
+   pointers must themselves be SSA names.  This is 
factor_out_conditional_load's
+   eligibility test, split out so callers (the sink pass) can gate on
+   commonability without mutating: the two MUST stay in lockstep, otherwise a
+   caller could common a load the sink then cannot finish if-converting.  */
 
 bool
-conditional_load_factorable_p (edge e0, edge e1, basic_block merge, gphi *phi)
+conditional_load_factorable_p (edge e0, edge e1, basic_block merge, gphi *phi,
+                              bool require_ssa_args)
 {
   if (virtual_operand_p (gimple_phi_result (phi))
       || gimple_phi_num_args (phi) != 2
@@ -480,6 +482,13 @@ conditional_load_factorable_p (edge e0, edge e1, 
basic_block merge, gphi *phi)
   if (!types_compatible_p (TREE_TYPE (p0), TREE_TYPE (p1)))
     return false;
 
+  /* The phiopt caller (REQUIRE_SSA_ARGS) only commons SSA-name pointers: with 
a
+     non-SSA arm pointer phiprop would propagate the load back into the arms 
and
+     undo the transform.  The sink caller has no such issue and passes false.  
*/
+  if (require_ssa_args
+      && (TREE_CODE (p0) != SSA_NAME || TREE_CODE (p1) != SSA_NAME))
+    return false;
+
   return true;
 }
 
@@ -489,12 +498,15 @@ conditional_load_factorable_p (edge e0, edge e1, 
basic_block merge, gphi *phi)
    load *P' replacing the PHI.  No speculative load is introduced (the load 
uses
    whichever pointer the taken edge selected).  Together with operand factoring
    this turns a conditional load into one reg-offset load.  E0/E1 are the
-   arm->MERGE edges.  Returns true if the load was factored out.  */
+   arm->MERGE edges.  REQUIRE_SSA_ARGS is as in conditional_load_factorable_p
+   (the phiopt caller sets it so phiprop cannot undo the transform).  Returns
+   true if the load was factored out.  */
 
 bool
-factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi)
+factor_out_conditional_load (edge e0, edge e1, basic_block merge, gphi *phi,
+                            bool require_ssa_args)
 {
-  if (!conditional_load_factorable_p (e0, e1, merge, phi))
+  if (!conditional_load_factorable_p (e0, e1, merge, phi, require_ssa_args))
     return false;
 
   /* Re-derive the loads and pointers validated by the predicate above.  */
@@ -4407,8 +4419,18 @@ pass_phiopt::execute (function *)
            {
              gphi *phi = as_a <gphi *> (gsi_stmt (gsi));
 
-             if (factor_out_conditional_operation (e1, e2, merge, phi,
-                 cond_stmt, early_p))
+             bool factored
+               = factor_out_conditional_operation (e1, e2, merge, phi,
+                                                   cond_stmt, early_p);
+             /* Outside any loop a diamond load PHI <*P, *Q> cannot be
+                loop-vectorised, so commoning it into P' = PHI <P, Q>; *P' is a
+                pure win.  Do it only when not in the early run (the region may
+                yet become a loop after inlining) and only for SSA-name 
pointers
+                so phiprop cannot undo it.  */
+             if (!factored && !early_p && bb_loop_depth (merge) == 0)
+               factored = factor_out_conditional_load (e1, e2, merge, phi,
+                                                       
/*require_ssa_args=*/true);
+             if (factored)
                {
                  /* Start over if there was an operation that was factored out 
because the new phi might have another opportunity.  */
                  phis = phi_nodes (merge);
diff --git a/gcc/tree-ssa-phiopt.h b/gcc/tree-ssa-phiopt.h
index 7383095f5a5..8f9b9edcdad 100644
--- a/gcc/tree-ssa-phiopt.h
+++ b/gcc/tree-ssa-phiopt.h
@@ -25,11 +25,13 @@ extern int find_different_opnum (const gimple_match_op 
&arg0_op,
 /* True if factor_out_conditional_load would factor the load PHI at MERGE; lets
    the sink pass gate on commonability without mutating.  */
 extern bool conditional_load_factorable_p (edge e0, edge e1, basic_block merge,
-                                          gphi *phi);
+                                          gphi *phi,
+                                          bool require_ssa_args = false);
 /* Factor a load out of a "load PHI" PHI <*P, *Q> into P' = PHI <P, Q>;
-   result = *P'.  */
+   result = *P'.  When REQUIRE_SSA_ARGS, only do so when P and Q are SSA names
+   (the phiopt caller sets this so phiprop cannot undo the transform).  */
 extern bool factor_out_conditional_load (edge e0, edge e1, basic_block merge,
-                                        gphi *phi);
+                                        gphi *phi, bool require_ssa_args = 
false);
 /* Factor a common operation (unary, conversion, or N-ary with one differing
    operand) out of a 2-argument PHI.  */
 extern bool factor_out_conditional_operation (edge e0, edge e1,
-- 
2.50.1 (Apple Git-155)

Reply via email to