This adds a reduced cs-elim functionality which will be used
from phiopt. The design is similar to cond_store_replacement except
we are looking for a similar store right beore the condition or a
non-trapping (no data races or local variable) load right before
the conditional. This allows for 2 things, the reuse of the same
aliasing set and second is allows for use in phiopt in a secondary patch.

It supports the inserting on the edge for the case where there is
no load/stores before the condition if the store is a non-trapping memory
with no data races; this is a similar to what is done in cond_store_replacement
already. Thia part is not going to be used with phiopt though as it is too
complex to support there and it adds a conditional load for no benifit for
phiopt.

Note pr99473-1.c of a case where can be optimized even without
-fallow-store-data-races now. The store happens before the conditional
and inside the conditional, so afterwards there is only one store rather
than 2. The code was also handling it in sink in GCC 12 the same way too
but now handling it in cselim before sink1.

Changes since v1:
* v2: Add support for loads before the conditional and support
for doing the load on the edge. Fix formating and comments.

Bootstrapped and tested on x86_64-linux-gnu.

gcc/ChangeLog:

        * tree-ssa-phiopt.cc (may_alias_mem): New function.
        (can_trap_as_a_store): New funciton.
        (cond_store_replacement_limited): New function.
        (pass_cselim::execute): Call cond_store_replacement_limited before
        cond_store_replacement.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/pr99473-1.c: Remove -fallow-store-data-races as it is 
not
        needed with cond_store_replacement_limited.

Signed-off-by: Andrew Pinski <[email protected]>
---
 gcc/testsuite/gcc.dg/tree-ssa/pr99473-1.c |   2 +-
 gcc/tree-ssa-phiopt.cc                    | 271 ++++++++++++++++++++++
 2 files changed, 272 insertions(+), 1 deletion(-)

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr99473-1.c 
b/gcc/testsuite/gcc.dg/tree-ssa/pr99473-1.c
index 0fda5663a80..e930389e9c0 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr99473-1.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr99473-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -ftree-cselim -fallow-store-data-races 
-fdump-tree-cselim-details" } */
+/* { dg-options "-O2 -ftree-cselim -fdump-tree-cselim-details" } */
 
 void f (int*);
 
diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc
index e12dc7a8b0c..103547d538f 100644
--- a/gcc/tree-ssa-phiopt.cc
+++ b/gcc/tree-ssa-phiopt.cc
@@ -3386,6 +3386,272 @@ trailing_store_in_bb (basic_block bb, tree vdef, gphi 
*vphi, bool onlyonestore)
   return store;
 }
 
+/* Takes a MEM and changes the aliasing set to be zero on it.
+   This handles all variants include decls. */
+static tree
+may_alias_mem (tree mem)
+{
+  mem = unshare_expr (mem);
+  tree *basep = &mem;
+  while (handled_component_p (*basep))
+    basep = &TREE_OPERAND (*basep, 0);
+  if (TREE_CODE (*basep) == MEM_REF
+      || TREE_CODE (*basep) == TARGET_MEM_REF)
+    TREE_OPERAND (*basep, 1)
+      = fold_convert (ptr_type_node, TREE_OPERAND (*basep, 1));
+  else
+    *basep = build2 (MEM_REF, TREE_TYPE (*basep),
+                    build_fold_addr_expr (*basep),
+                    build_zero_cst (ptr_type_node));
+  return mem;
+}
+
+/* Returns true if LHS is known not to trap as a store.  */
+static bool
+can_trap_as_a_store (tree lhs)
+{
+  tree lhsbase = get_base_address (lhs);
+  if (tree_could_trap_p (lhs))
+    return true;
+  /* tree_could_trap_p is a predicate for loads, so check
+     for readonly memory explicitly.  */
+  if ((DECL_P (lhsbase) && TREE_READONLY (lhsbase))
+      || TREE_CODE (lhsbase) == STRING_CST)
+    return true;
+  return false;
+}
+
+/* Do the main work of a limited conditional store replacement.
+   This recognized pattern like so:
+
+   COND_BB:
+     store = a_1;
+     // no loads
+     if (cond) goto MIDDLE_BB; else goto JOIN_BB (edge E1)
+   MIDDLE_BB:
+     something // no loads
+     store = a_2;
+     something // no loads
+     fallthrough (edge E0)
+   JOIN_BB:
+     some more
+
+  This is a limited form of the full cond_store_replacement
+  to be allowed from use from phiopt and can be done
+  without calculating the non-trapping cases.  */
+static bool
+cond_store_replacement_limited (basic_block middle_bb, basic_block join_bb,
+                               basic_block cond_bb,
+                               edge e0, edge e1,
+                               bool caninsert_edge)
+{
+  tree lhs, rhs;
+  location_t locus;
+  /* Currently don't handle more than 2 incoming edges
+     into the merge bb. */
+  if (EDGE_COUNT (join_bb->preds) > 2)
+    return false;
+  /* The middle bb needs to have a single predecessor of the cond_bb.  */
+  if (!single_pred_p (middle_bb))
+    return false;
+  gphi *vphi = get_virtual_phi (join_bb);
+  if (!vphi)
+    return false;
+  tree middle_vdef = gimple_phi_arg_def_from_edge (vphi, e0);
+  /* Check if middle_bb contains of only one store.  */
+  gimple *store_middle;
+  store_middle = trailing_store_in_bb (middle_bb, middle_vdef,
+                                      vphi, true);
+
+  if (!store_middle
+      || !gimple_assign_single_p (store_middle)
+      || gimple_has_volatile_ops (store_middle))
+    return false;
+
+  locus = gimple_location (store_middle);
+  lhs = gimple_assign_lhs (store_middle);
+  rhs = gimple_assign_rhs1 (store_middle);
+  if ((!REFERENCE_CLASS_P (lhs)
+       && !DECL_P (lhs))
+      || !is_gimple_reg_type (TREE_TYPE (lhs)))
+    return false;
+  if (TREE_CODE (rhs) != SSA_NAME)
+    return false;
+
+  /* Three cases that can be handled:
+     1) the lhs is stored to right before the condition.
+       Will remove the store before the condition.
+     2) Or the lhs is loaded from right before the condition.
+     3) Neither of these. (this will insert a load in the other edge)
+     For case 2 and 3, check for data races.
+     For case 2, the load can either be based on a local variable
+     or a known non-trapping decl.
+     For case 3, the store needs to known to be non-trapping.  */
+  tree vuse = gimple_vuse (store_middle);
+  gimple *beforestore = nullptr;
+  gimple *vdef_before = SSA_NAME_DEF_STMT (vuse);
+  tree other_rhs = nullptr;
+
+  /* See if there is a store before the condition case.  */
+  if (gimple_assign_single_p (vdef_before))
+    {
+      tree beforelhs = gimple_assign_lhs (vdef_before);
+      /* Only allow the store to be right before the condition.  */
+      if (gimple_bb (vdef_before) == cond_bb
+         /* An exact match is only supported.
+            FIXME: Allow for clique/base mismatch?  */
+         && operand_equal_p (lhs, beforelhs))
+       {
+         /* The vuse of the of store in the middle should be also
+            the entry in the phi for the other edge.  */
+         gcc_assert (vuse == gimple_phi_arg_def_from_edge (vphi, e1));
+         tree vuse = gimple_vuse (store_middle);
+         imm_use_iterator iter;
+         gimple *use_stmt;
+         /* There can't be any loads between the store and
+            the previous store as that might depend on the store.
+            FIXME: use alias oracle to check dependancies.  */
+         FOR_EACH_IMM_USE_STMT (use_stmt, iter, vuse)
+           {
+             if (use_stmt != store_middle
+                 && use_stmt != vphi)
+               return false;
+           }
+         other_rhs = gimple_assign_rhs1 (vdef_before);
+         beforestore = vdef_before;
+       }
+    }
+  /*
+    case 2:
+    a = local_var[n];
+    if (b)
+      local_var[n] = c;
+
+    case 3:
+    if (b)
+      nontrapping = c;
+
+    For case 3, nontrapping needs to satisfy tree_could_trap_p.
+    In both cases ref_can_have_store_data_races needs to be satisfy.
+  */
+
+  /* Maybe the load/local non-escaped variable case.  */
+  if (!other_rhs)
+    {
+      tree lhsbase = get_base_address (lhs);
+      /* If this store ref can't have data races, a store
+        that was conditional can't become unconditional.  */
+      if (ref_can_have_store_data_races (lhs))
+       return false;
+      tree vuse = gimple_vuse (store_middle);
+      imm_use_iterator iter;
+      gimple *use_stmt;
+      /* Try to find the load before the store that matches
+        if we have a local variable or a non trapping store.  */
+      if ((auto_var_p (lhsbase) && !TREE_ADDRESSABLE (lhsbase))
+         || !can_trap_as_a_store (lhs))
+       {
+         FOR_EACH_IMM_USE_STMT (use_stmt, iter, vuse)
+           {
+             if (gimple_bb (use_stmt) != cond_bb)
+               continue;
+             /* Looking for a load only.  */
+             if (!gimple_assign_load_p (use_stmt))
+               continue;
+             tree rhs = gimple_assign_rhs1 (use_stmt);
+             if (!operand_equal_p (rhs, lhs))
+               continue;
+             other_rhs = gimple_assign_lhs (use_stmt);
+             lhs = may_alias_mem (lhs);
+             break;
+           }
+       }
+      if (!other_rhs)
+       {
+         gassign *new_stmt;
+         /* If not allowing inserting on the edge, then don't.  */
+         if (!caninsert_edge)
+           return false;
+         /* If LHS is an access to a local variable without address-taken
+            (or when we allow data races) and known not to trap, we could
+            always safely move down the store.  */
+         if (can_trap_as_a_store (lhs))
+           return false;
+         lhs = may_alias_mem (lhs);
+         /* Insert a load from the memory of the store to the temporary
+            on the edge which did not contain the store.  */
+         other_rhs = make_temp_ssa_name (TREE_TYPE (lhs), NULL, "cstore");
+         new_stmt = gimple_build_assign (other_rhs, lhs);
+         gimple_set_location (new_stmt, locus);
+         /* Set the vuse for the new load.  */
+         gimple_set_vuse (new_stmt,
+                          gimple_phi_arg_def (vphi, e1->dest_idx));
+         lhs = unshare_expr (lhs);
+         {
+           /* Set the no-warning bit on the rhs of the load to avoid
+              uninit warnings.  */
+           tree rhs1 = gimple_assign_rhs1 (new_stmt);
+           suppress_warning (rhs1, OPT_Wuninitialized);
+         }
+         gsi_insert_on_edge (e1, new_stmt);
+       }
+    }
+
+  gphi *newphi;
+  gassign *new_stmt;
+  gimple_stmt_iterator gsi;
+  /* Now we've checked the constraints, so do the transformation:
+     1) Remove the store(s).  */
+  gsi = gsi_for_stmt (store_middle);
+  unlink_stmt_vdef (store_middle);
+  gsi_remove (&gsi, true);
+  release_defs (store_middle);
+
+  if (beforestore)
+    {
+      gsi = gsi_for_stmt (beforestore);
+      unlink_stmt_vdef (beforestore);
+      gsi_remove (&gsi, true);
+      release_defs (beforestore);
+    }
+
+
+  /* 2) Create a PHI node at the join block, with one argument
+       holding the old RHS, and the other holding the temporary
+       where we stored the old memory contents.  */
+  tree phiname = make_temp_ssa_name (TREE_TYPE (lhs), NULL, "cstore");
+  newphi = create_phi_node (phiname, join_bb);
+  add_phi_arg (newphi, rhs, e0, locus);
+  add_phi_arg (newphi, other_rhs, e1, locus);
+
+  /* 3. Create the new store.  */
+  new_stmt = gimple_build_assign (lhs, phiname);
+
+  /* Update the vdef for the new store statement. */
+  tree newvphilhs = make_ssa_name (gimple_vop (cfun));
+  tree vdef = gimple_phi_result (vphi);
+  gimple_set_vuse (new_stmt, newvphilhs);
+  gimple_set_vdef (new_stmt, vdef);
+  gimple_phi_set_result (vphi, newvphilhs);
+  SSA_NAME_DEF_STMT (vdef) = new_stmt;
+  update_stmt (vphi);
+
+  gsi = gsi_after_labels (join_bb);
+  gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "\nConditional store replacement happened!");
+      if (beforestore)
+       fprintf (dump_file, "\nRemoved the store before the condition.");
+      fprintf (dump_file, "\nInserted a new PHI statement in joint block:\n");
+      print_gimple_stmt (dump_file, new_stmt, 0, TDF_VOPS|TDF_MEMSYMS);
+    }
+  statistics_counter_event (cfun, "conditional store replacement", 1);
+  return true;
+}
+
+
 /* Return the only store in MIDDLE_BB as the candidate store for cselim.  
Return
    NULL if no candidate can be found.  */
 
@@ -4632,6 +4898,11 @@ pass_cselim::execute (function *)
       if (EDGE_COUNT (bb2->preds) > 2)
        return;
 
+      if (cond_store_replacement_limited (bb1, bb2, bb, e1, e2, true))
+       {
+         cfgchanged = true;
+         return;
+       }
       gimple *assign = cselim_candidate (bb1, bb2, e1);
       if (cond_store_replacement (bb1, bb2, e1, e2, assign, nontrap))
        cfgchanged = true;
-- 
2.43.0

Reply via email to