On Tue Jul 14, 2026 at 3:57 PM IST, Raghesh Aloor wrote:
> On Thu Jul 9, 2026 at 2:49 PM IST, Richard Biener wrote:
> > On Tue, 30 Jun 2026, Raghesh Aloor wrote:
> >
> > > ---
> > >  gcc/tree-vect-loop.cc  | 396 ++++++++++++++++++++++++++++++++++++++++-
> > >  gcc/tree-vect-stmts.cc |  26 +++
> > >  gcc/tree-vectorizer.h  |   5 +
> > >  3 files changed, 426 insertions(+), 1 deletion(-)
> >
> >
> > This matches too much I think.  Consider the following (which
> > I don't see how to vectorize, but that's besides the point)
> >
> > void foo (int *p, int * __restrict q, int n)
> > {
> >   int res = 0;
> >   for (int i = 0; i < n; ++i)
> >     {
> >       if (p[i] > 0)
> >         res++;
> >       q[i] = res;
> >     }
> > }
> >
> > There's still that very same "conditional induction" but without
> > the masked store index.  We are not going to be able to code-generate
> > a vector containing 'res' (at least I don't see an easy way).
> >
> > Meta-info for this should be on the stmt_info of the PHI, not
> > in loop-vinfo.
> >
>
> Thanks Richard and Robin for your suggestions. I will first address the
> comments on the detection patch which has less confusions.
>
> Hi Richard,
>
> On the above example you gave: the current code already rejects it during
> detection. We do recognise the conditional increment
> (res = mask ? res+1 : res), but we then look for a matching .MASK_STORE
> indexed by that value. The store to q is not picked up — it is an ordinary
> store indexed by i, where res is the value being stored, not the index.
> Since we only classify the PHI when such a masked store is found,
> that example is never matched.
>
> Or are you suggesting a restructuring here? If so, is the following what you
> have in mind:
>
> Recognise the conditional-increment induction on its own, from the PHI alone,
> and store that information on the PHI's stmt_info (not in loop-vinfo).
>

Hi Richard,

This updated patch implements recognition only, as you suggested: during
scalar-cycle analysis we classify the mask ? n+1 : n loop-header PHI and
record the increment mask on its stmt_vec_info. Masked-store pairing is
deferred to a follow-up patch; we do not record anything in
loop_vec_info. Until we can vectorize this def type, vect_is_simple_use
rejects these PHIs so the loop fails cleanly.

This patch, we believe, is self-contained with a graceful exit so it
can land upstream on its own and mark clear progress toward the full
compress-store path.

Your foo example (res++ under a condition, then q[i] = res) is covered
by vect-predicated-index-2.c: the matcher does not fire because
if-conversion leaves a guarded add on the backedge, not mask ? res+1 :
res (predicated-index PHI: backedge is not COND_EXPR.). Even if we later
extend recognition to more increment forms, a follow-up pairing pass
would still need to reject uses where the counter is stored as a value
rather than serving only as a compress index.

One refactoring we may do - If-conversion will likely need the same
shape check (mask ? n+1 : n). Would you prefer the matcher reuse that
if possible, avoiding code duplication across two passes?

Thanks,
Raghesh

>From 584e2e5bdef4c308867c4d51a4733d2b9b965128 Mon Sep 17 00:00:00 2001
From: Raghesh Aloor <[email protected]>
Date: Mon, 4 May 2026 16:06:12 +0530
Subject: [PATCH v3 1/4] Detect predicated-index PHI

After if-conversion, a loop with a compress-store pattern  can leave a
loop-header PHI whose latch value has the form mask ? index + 1 : index
(with the index used only to address a masked store).  Recognize this
conditional-increment cycle during scalar-cycle analysis and classify
the PHI as vect_predicated_index_def, recording the increment mask on
the PHI's stmt_vec_info for a follow-up pass.

Until predicated-index PHIs can be vectorized, reject them in
vect_is_simple_use so the vectorizer fails gracefully instead of
mis-handling the def-use cycle.

gcc/ChangeLog:

        * tree-vectorizer.h (enum vect_def_type): Add
        vect_predicated_index_def.
        (struct _stmt_vec_info): Add
        predicated_index_mask.
        * tree-vect-loop.cc (vect_analyze_scalar_cycles_1): Detect
        predicated-index PHIs.
        (vect_analyze_predicated_index_phi,
        vect_predicated_index_plus_one, vect_pred_phi_strip_conversion):
        New helpers.
        * tree-vect-stmts.cc (vect_is_simple_use): Reject
        vect_predicated_index_def operands.

gcc/testsuite/ChangeLog:

        * gcc.dg/vect/vect-predicated-index-1.c: New test.
        * gcc.dg/vect/vect-predicated-index-2.c: New test.
---
 .../gcc.dg/vect/vect-predicated-index-1.c     |  25 ++
 .../gcc.dg/vect/vect-predicated-index-2.c     |  27 +++
 gcc/tree-vect-loop.cc                         | 219 ++++++++++++++++++
 gcc/tree-vect-stmts.cc                        |  12 +
 gcc/tree-vectorizer.h                         |   8 +
 5 files changed, 291 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-predicated-index-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-predicated-index-2.c

diff --git a/gcc/testsuite/gcc.dg/vect/vect-predicated-index-1.c 
b/gcc/testsuite/gcc.dg/vect/vect-predicated-index-1.c
new file mode 100644
index 00000000000..3797e52617c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-predicated-index-1.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target avx512f } */
+/* { dg-additional-options "-march=znver5" { target { x86_64-*-* i?86-*-* } } 
} */
+/* { dg-additional-options "-march=armv9-a" { target aarch64-*-* } } */
+
+/* Positive test for predicated-index PHI detection (vpcompress / compress
+   store pattern).  After if-conversion the pack index has the shape:
+
+     # j = PHI <j_1, 0>
+     ...
+     .MASK_STORE (base + j * size, mask, value)
+     j_1 = mask ? j + 1 : j;  */
+
+void
+foo (int * restrict q, const int * restrict a, int n)
+{
+  int j = 0;
+  for (int i = 0; i < n; i++)
+    if (a[i])
+      q[j++] = a[i];
+}
+
+/* { dg-final { scan-tree-dump "Detected predicated-index PHI." "vect" } } */
+/* { dg-final { scan-tree-dump "unsupported: predicated-index PHI." "vect" } } 
*/
diff --git a/gcc/testsuite/gcc.dg/vect/vect-predicated-index-2.c 
b/gcc/testsuite/gcc.dg/vect/vect-predicated-index-2.c
new file mode 100644
index 00000000000..b04e133ea8b
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-predicated-index-2.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target avx512f } */
+/* { dg-additional-options "-march=znver5" { target { x86_64-*-* i?86-*-* } } 
} */
+/* { dg-additional-options "-march=armv9-a" { target aarch64-*-* } } */
+
+/* Negative test: the same conditional increment exists, but the counter is
+   consumed as a stored value (q[i] = res) rather than only as a
+   compress-store index.  If-conversion keeps a plain add of a guarded
+   zero/one on the backedge instead of mask ? res + 1 : res, so the
+   predicated-index PHI matcher must not fire.  */
+
+void
+foo (int * restrict q, const int * restrict a, int n)
+{
+  int res = 0;
+
+  for (int i = 0; i < n; i++)
+    {
+      if (a[i])
+       res++;
+      q[i] = res;
+    }
+}
+
+/* { dg-final { scan-tree-dump-not "Detected predicated-index PHI." "vect" } } 
*/
+/* { dg-final { scan-tree-dump "predicated-index PHI: backedge is not 
COND_EXPR." "vect" } } */
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index a9335ed68bb..4abc8bbeec8 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -165,6 +165,138 @@ static stmt_vec_info vect_is_simple_reduction 
(loop_vec_info, stmt_vec_info,
                                               gphi **);
 
 
+/* Function vect_pred_phi_strip_conversion.
+
+   This function check if CUR is an assigment statement and if it is an SSA
+   whose defining statement is NOP/CONVERT/VIEW_CONVERT/NON_LVALUE to SSA.
+   Stores that rhs SSA into CUR
+
+     t_1 = (unsigned int) n;
+
+     Stores n into CUR.  */
+static bool
+vect_pred_phi_strip_conversion (tree *cur)
+{
+  if (TREE_CODE (*cur) != SSA_NAME)
+    return false;
+
+  gimple *def = SSA_NAME_DEF_STMT (*cur);
+  gassign *assign = dyn_cast <gassign *> (def);
+  if (!assign)
+    return false;
+
+  tree_code rhs_code = gimple_assign_rhs_code (assign);
+  if (rhs_code != NOP_EXPR
+      && rhs_code != CONVERT_EXPR
+      && rhs_code != VIEW_CONVERT_EXPR
+      && rhs_code != NON_LVALUE_EXPR)
+    return false;
+
+  tree rhs1 = gimple_assign_rhs1 (assign);
+  if (TREE_CODE (rhs1) != SSA_NAME)
+    return false;
+
+  *cur = rhs1;
+  return true;
+}
+
+/* Function vect_predicated_index_plus_one.
+
+   This function checks if one of the arms of the COND_EXPR,
+   PRED_PHI_BACK_EDGE_ASGN_STMT is PRED_PHI itself and the other is
+   PRED_PHI + 1.  Note that the increment can be passed through cast
+   instructions as in the below example.  So we need to traverse through them.
+
+     # n = PHI <n_2, ..>
+     ...
+     addr = base + n;
+     .MASK_STORE (addr, mask, ..)
+     t_1 = (unsigned int) n;
+     t_2 = t1_1 + 1;
+     n_1 = int (t_2);
+     n_2 = mask ? n_1 : n;  */
+static bool
+vect_predicated_index_plus_one (tree &pred_phi,
+                          gassign *pred_phi_back_edge_asgn_stmt)
+{
+  /* Check that one of the arms of COND_EXPR is pred-phi (n) and the other is
+     an increment value (n_1 = n + 1).  */
+  tree true_val = gimple_assign_rhs2 (pred_phi_back_edge_asgn_stmt);
+  tree false_val = gimple_assign_rhs3 (pred_phi_back_edge_asgn_stmt);
+  tree pred_phi_inc_val = NULL_TREE;
+  if (true_val == pred_phi)
+    pred_phi_inc_val = false_val;
+  else if (false_val == pred_phi)
+    pred_phi_inc_val = true_val;
+  else
+    return false;
+  if (TREE_CODE (pred_phi_inc_val) != SSA_NAME)
+    return false;
+
+  /* Check if n1 = n + 1?. We may need to strip the cast instructions to reach
+     the PLUS_EXPR, traversing until CUR goes out of current basic block.  */
+  basic_block bb = gimple_bb (pred_phi_back_edge_asgn_stmt);
+  tree cur = pred_phi_inc_val;
+
+  while (TREE_CODE (cur) == SSA_NAME)
+    {
+      gimple *def = SSA_NAME_DEF_STMT (cur);
+      if (gimple_bb (def) != bb)
+       return false;
+      /* CUR should be an assignment statement.  */
+      gassign *cur_asgn = dyn_cast <gassign *> (def);
+      if (!cur_asgn)
+       return false;
+      /* Stop traversing further if we reach a PLUS_EXPR.  */
+      if (gimple_assign_rhs_code (cur_asgn) == PLUS_EXPR)
+       {
+         /* Check for +1 increment.  It can be either t_1 + 1 or 1 + t_1.  */
+         tree rhs1 = gimple_assign_rhs1 (cur_asgn);
+         tree rhs2 = gimple_assign_rhs2 (cur_asgn);
+         tree plus_base = NULL_TREE;
+         if (TREE_CODE (rhs1) == SSA_NAME && TREE_CODE (rhs2) == INTEGER_CST
+             && integer_onep (rhs2))
+           plus_base = rhs1;
+         else if (TREE_CODE (rhs2) == SSA_NAME
+                  && TREE_CODE (rhs1) == INTEGER_CST && integer_onep (rhs1))
+           plus_base = rhs2;
+         /* If plus_base is not set we dont have a +1 pattern.  */
+         if (!plus_base)
+           return false;
+
+         /* Check if plus_base is pred_phi itself.  We may still need to strip
+            some casts to reach the actual base, traversing until plus_base
+            goes out of the current basic block.  */
+         while (TREE_CODE (plus_base) == SSA_NAME)
+           {
+             gimple *base_def = SSA_NAME_DEF_STMT (plus_base);
+             if (gimple_bb (base_def) != bb)
+               break;
+             if (!vect_pred_phi_strip_conversion (&plus_base))
+               break;
+           }
+         /* The stripped base should be pred_phi.  */
+         if (plus_base == pred_phi)
+           {
+             if (dump_enabled_p ())
+               dump_printf_loc (MSG_NOTE, vect_location,
+                                "predicated-index PHI: update is +1.\n");
+             return true;
+           }
+         /* We don't have +1 pattern.  */
+         return false;
+       }
+      /* If we reach a non-PLUS_EXPR check if we can continue walk through
+        casts.  */
+      if (vect_pred_phi_strip_conversion (&cur))
+       continue;
+      /* We cannot reach a PLUS_EXPR.  */
+      return false;
+    }
+
+  return false;
+}
+
 /* Function vect_is_simple_iv_evolution.
 
    FORNOW: A simple evolution of an induction variables in the loop is
@@ -333,6 +465,85 @@ vect_phi_first_order_recurrence_p (loop_vec_info 
loop_vinfo, class loop *loop,
   return true;
 }
 
+/* Function vect_analyze_predicated_index_phi.
+
+   Recognize the conditional-increment induction cycle: a loop-header PHI whose
+   back-edge value is the PHI incremented by one under a mask.  On success
+   STMT_INFO is annotated with the mask for a follow-up pass that will pair
+   the cycle with a matching IFN_MASK_STORE.  An outline of the shape 
recognized
+   here is:
+
+     # n = PHI <0, n_2>
+     ...
+     n_1 = n + 1;
+     ...
+     n_2 = mask ? n_1 : n;  */
+static bool
+vect_analyze_predicated_index_phi (class loop *loop, gphi *phi,
+                                  stmt_vec_info stmt_info)
+{
+  /* Require a loop-header PHI.  */
+  if (gimple_bb (phi) != loop->header)
+    return false;
+
+  tree pred_phi = PHI_RESULT (phi);
+  /* PHI should be an integer.  */
+  if (TREE_CODE (pred_phi) != SSA_NAME
+      || TREE_CODE (TREE_TYPE (pred_phi)) != INTEGER_TYPE)
+    return false;
+
+  /* Restricting to zero initializatin from preheader for now.  TODO: Can be
+     generalized to arbitrary integer starts.  */
+  tree pre_init = PHI_ARG_DEF_FROM_EDGE (phi, loop_preheader_edge (loop));
+  if (TREE_CODE (pre_init) != INTEGER_CST || !integer_zerop (pre_init))
+    return false;
+
+  /* The incoming value for pred-phi must be in the if-converted form.
+     n_2 = mask ? n_1 : n; TODO: This may be generalized later for more
+     patterns.  */
+  /* Get the incoming value (n_2) of pred-phi through backedge.  */
+  tree pred_phi_back_edge_val
+    = PHI_ARG_DEF_FROM_EDGE (phi, loop_latch_edge (loop));
+  if (TREE_CODE (pred_phi_back_edge_val) != SSA_NAME)
+    return false;
+  /* Check if pred_phi_back_edge_val is a COND_EXPR.  */
+  gimple *pred_phi_back_edge_stmt = SSA_NAME_DEF_STMT (pred_phi_back_edge_val);
+  if (!pred_phi_back_edge_stmt)
+    return false;
+  gassign *pred_phi_back_edge_asgn_stmt
+    = dyn_cast <gassign *> (pred_phi_back_edge_stmt);
+  if (!pred_phi_back_edge_asgn_stmt
+      || gimple_assign_rhs_code (pred_phi_back_edge_asgn_stmt) != COND_EXPR)
+    {
+      if (dump_enabled_p ())
+       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                        "predicated-index PHI: backedge is not COND_EXPR.\n");
+      return false;
+    }
+
+  /* Check that one of the arms of COND_EXPR is pred-phi (n) and the other is
+     an increment value (n_1 = n + 1).  */
+  if (!vect_predicated_index_plus_one (pred_phi, pred_phi_back_edge_asgn_stmt))
+    {
+      if (dump_enabled_p ())
+       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                        "predicated-index PHI: update is not +1.\n");
+      return false;
+    }
+
+  /* The conditional-increment cycle is recognized.  Record the mask governing
+     the increment on the PHI's stmt_info.  */
+  tree mask = gimple_assign_rhs1 (pred_phi_back_edge_asgn_stmt);
+  STMT_VINFO_PREDICATED_INDEX_MASK (stmt_info) = mask;
+
+  if (dump_enabled_p ())
+    dump_printf_loc (MSG_NOTE, vect_location,
+                    "predicated-index PHI: recognized conditional-increment "
+                    "cycle %G\n", (gimple *) phi);
+
+  return true;
+}
+
 /* Function vect_analyze_scalar_cycles_1.
 
    Examine the cross iteration def-use cycles of scalar variables
@@ -479,6 +690,14 @@ vect_analyze_scalar_cycles_1 (loop_vec_info loop_vinfo, 
class loop *loop)
        }
       else if (vect_phi_first_order_recurrence_p (loop_vinfo, loop, phi))
        STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_first_order_recurrence;
+      else if (loop == LOOP_VINFO_LOOP (loop_vinfo)
+              && vect_analyze_predicated_index_phi (loop, phi, stmt_vinfo))
+       {
+         STMT_VINFO_DEF_TYPE (stmt_vinfo) = vect_predicated_index_def;
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_NOTE, vect_location,
+                            "Detected predicated-index PHI.\n");
+       }
       else
         if (dump_enabled_p ())
           dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index 488b9754bed..8dfb1ca8522 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -13937,12 +13937,24 @@ vect_is_simple_use (tree operand, vec_info *vinfo, 
enum vect_def_type *dt,
        case vect_condition_def:
          dump_printf (MSG_NOTE, "control flow\n");
          break;
+       case vect_predicated_index_def:
+         dump_printf (MSG_NOTE, "predicated index\n");
+         break;
        case vect_unknown_def_type:
          dump_printf (MSG_NOTE, "unknown\n");
          break;
        }
     }
 
+  /* TODO: Till predicated-index PHI is handled, exit gracefully.  */
+  if (*dt == vect_predicated_index_def)
+    {
+      if (dump_enabled_p ())
+       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                        "unsupported: predicated-index PHI.\n");
+      return false;
+    }
+
   if (*dt == vect_unknown_def_type)
     {
       if (dump_enabled_p ())
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index d4eb20f1a10..332a19aed28 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -76,6 +76,7 @@ enum vect_def_type {
   vect_nested_cycle,
   vect_first_order_recurrence,
   vect_condition_def,
+  vect_predicated_index_def,
   vect_unknown_def_type
 };
 
@@ -1666,6 +1667,12 @@ public:
 
   /* True if this is only suitable for SLP vectorization.  */
   bool slp_vect_only_p;
+
+  /* On a predicated-index PHI (vect_predicated_index_def) the mask under which
+     the index is conditionally incremented, as recognized by
+     vect_analyze_predicated_index_phi.  Reserved for a follow-up pass that
+     pairs the cycle with a matching masked store.  */
+  tree predicated_index_mask;
 };
 
 /* Information about a gather/scatter call.  */
@@ -1745,6 +1752,7 @@ struct gather_scatter_info {
 #define STMT_VINFO_REDUC_DEF(S)                (S)->reduc_def
 #define STMT_VINFO_SLP_VECT_ONLY(S)     (S)->slp_vect_only_p
 #define STMT_VINFO_REDUC_VECTYPE_IN(S)  (S)->reduc_vectype_in
+#define STMT_VINFO_PREDICATED_INDEX_MASK(S) (S)->predicated_index_mask
 
 #define DR_GROUP_FIRST_ELEMENT(S) \
   (gcc_checking_assert ((S)->dr_aux.dr), (S)->first_element)

Reply via email to