v3 is at https://gcc.gnu.org/pipermail/gcc-patches/2026-May/718050.html
Sorry for the screw-up with modifying the patch after the "final"
bootstrap + regtest.

Thanks,
Philipp.

On Mon, 25 May 2026 at 19:38, Philipp Tomsich <[email protected]> wrote:
>
> Hm.  On second thought this now looks fishy.
> It passed bootstrap and regtest (although that may have been before I
> changed to can_create_pseudo_p), but I'll go back and check again.
>
> On Mon, 25 May 2026 at 19:15, Andrew Pinski
> <[email protected]> wrote:
> >
> > On Mon, May 25, 2026 at 9:52 AM Philipp Tomsich
> > <[email protected]> wrote:
> > >
> > > For sub-int types (short, char), the sign/zero extension between the
> > > add+1 and the conditional select prevents combine from matching the
> > > csinc pattern, so a conditional increment such as
> > >
> > >   if (val < clipval) val++;
> > >
> > > is emitted as cmp; add; extend; csel instead of cmp; cinc; extend.
> > >
> > > Add two define_insn_and_split patterns that match the extended form
> > > combine fuses and split it into a wide cinc followed by the extension,
> > > exposing csinc2.  Computing the increment in the wider mode is safe
> > > because extend (truncate (val + cond)) equals the sub-int-mode add.
> > >
> > > *cinc_ext_<SHORT:mode>_<GPI:mode> handles the simple CC-mode case, where
> > > combine fuses (cmp; add #1; extend; csel) into
> > > (ANY_EXTEND (plus (comparison) (val))).  Saves one instruction.
> > >
> > > *cinc_ifelse_<SHORT:mode>_<GPI:mode> handles the CC_SWP case, where
> > > val += other precedes the increment and the extend is folded into the
> > > compare.  The match_dup on the base makes it sound by construction: both
> > > arms of the select extend the same value, so the false arm provably
> > > equals the extension of the base.  Saves two instructions.
> > >
> > > gcc/ChangeLog:
> > >
> > >         * config/aarch64/aarch64.md (*cinc_ext_<SHORT:mode>_<GPI:mode>):
> > >         New define_insn_and_split.
> > >         (*cinc_ifelse_<SHORT:mode>_<GPI:mode>): Likewise.
> > >
> > > gcc/testsuite/ChangeLog:
> > >
> > >         * gcc.target/aarch64/cinc-subint.c: New test.
> > >         * gcc.target/aarch64/cinc-subint-neg.c: New test.
> > >         * gcc.target/aarch64/cinc-subint-neg-2.c: New test.
> > >         * gcc.c-torture/execute/cond-incr-subint-1.c: New test.
> > >         * gcc.c-torture/execute/cond-incr-subint-2.c: New test.
> > >
> > > ---
> > >
> > > Changes in v2:
> > > - Reimplement with two define_insn_and_split patterns instead of four
> > >   peephole2 patterns, per Andrew Pinski's review; combine now forms the
> > >   cinc directly.  Drops the aarch64_peep_cinc_extend_p / _swp_p /
> > >   aarch64_emit_cinc_extend helpers and their aarch64-protos.h 
> > > declarations.
> > >   The emitted instruction is cinc (via *csinc2) rather than csinc.
> > > - Convert the codegen tests from scan-assembler to check-function-bodies.
> > > - Move the runtime test to the generic torture suite
> > >   (gcc.c-torture/execute), as it is not target-specific.
> > > - Add regression tests for cases that should not be transformed
> > >   (cinc-subint-neg-2.c, cond-incr-subint-2.c), exercising the match_dup
> > >   soundness of the CC_SWP pattern.
> > > - Rename the aarch64 tests to cinc-subint* (cinc, not csinc, is emitted).
> > >
> > >  gcc/config/aarch64/aarch64.md                 |  77 +++++++++++
> > >  .../execute/cond-incr-subint-1.c              | 126 ++++++++++++++++++
> > >  .../execute/cond-incr-subint-2.c              |  65 +++++++++
> > >  .../gcc.target/aarch64/cinc-subint-neg-2.c    |  59 ++++++++
> > >  .../gcc.target/aarch64/cinc-subint-neg.c      |  63 +++++++++
> > >  .../gcc.target/aarch64/cinc-subint.c          |  92 +++++++++++++
> > >  6 files changed, 482 insertions(+)
> > >  create mode 100644 
> > > gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-1.c
> > >  create mode 100644 
> > > gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-2.c
> > >  create mode 100644 gcc/testsuite/gcc.target/aarch64/cinc-subint-neg-2.c
> > >  create mode 100644 gcc/testsuite/gcc.target/aarch64/cinc-subint-neg.c
> > >  create mode 100644 gcc/testsuite/gcc.target/aarch64/cinc-subint.c
> > >
> > > diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
> > > index 9441e9d1eaed..d3be72b17bd0 100644
> > > --- a/gcc/config/aarch64/aarch64.md
> > > +++ b/gcc/config/aarch64/aarch64.md
> > > @@ -5032,6 +5032,83 @@ (define_insn "csinc3<mode>_insn"
> > >    [(set_attr "type" "csel")]
> > >  )
> > >
> > > +;; Conditional increment of a sub-int value.  For short/char, the sign or
> > > +;; zero extension between the add and the conditional select prevents
> > > +;; combine from forming cinc: combine fuses (cmp; add #1; extend; csel)
> > > +;; into (set X (ANY_EXTEND:GPI (plus:SHORT (cond) (subreg:SHORT val)))),
> > > +;; which has no insn pattern, and then splits it the wrong way.  Match 
> > > the
> > > +;; fused form and split it into a wide cinc followed by the extension,
> > > +;; exposing the cinc form (the *csinc2 pattern).  Computing the add in 
> > > GPI
> > > +;; mode is safe because extend (truncate (val + cond)) equals the 
> > > SHORT-mode
> > > +;; plus.
> > > +(define_insn_and_split "*cinc_ext_<SHORT:mode>_<GPI:mode>"
> > > +  [(set (match_operand:GPI 0 "register_operand" "=r")
> > > +       (ANY_EXTEND:GPI
> > > +         (plus:SHORT
> > > +           (match_operand:SHORT 1 "aarch64_comparison_operation" "")
> > > +           (match_operand:SHORT 2 "register_operand" "r"))))]
> > > +  ""
> > > +  "#"
> > > +  "&& can_create_pseudo_p ()"
> >
> > I am 99% sure this won't work. You can't have an insn that will match
> > and then fail to emit (or split) after reload.
> > You need a clobber/scratch register here rather than depending on
> > can_create_pseudo_p.
> >
> > > +  [(const_int 0)]
> > > +  {
> > > +    rtx op2 = operands[2];
> > > +    rtx val_gpi;
> > > +    if (SUBREG_P (op2) && subreg_lowpart_p (op2)
> > > +       && GET_MODE (SUBREG_REG (op2)) == <GPI:MODE>mode)
> > > +      val_gpi = SUBREG_REG (op2);
> > > +    else
> > > +      {
> > > +       val_gpi = gen_reg_rtx (<GPI:MODE>mode);
> > > +       convert_move (val_gpi, op2, <CODE> == ZERO_EXTEND);
> > > +      }
> >
> > Why do you need to extend here? Why not just take the paradoxical
> > lowpart instead? Since we don't care about the values in the upper
> > parts (the overflow part)?
> >
> > > +    rtx cond = gen_rtx_fmt_ee (GET_CODE (operands[1]), <GPI:MODE>mode,
> > > +                              XEXP (operands[1], 0), XEXP (operands[1], 
> > > 1));
> > > +    rtx tmp = gen_reg_rtx (<GPI:MODE>mode);
> > > +    emit_insn (gen_rtx_SET (tmp, gen_rtx_PLUS (<GPI:MODE>mode, cond, 
> > > val_gpi)));
> > > +    rtx narrow = gen_lowpart (<SHORT:MODE>mode, tmp);
> > > +    convert_move (operands[0], narrow, <CODE> == ZERO_EXTEND);
> > > +    DONE;
> > > +  }
> > > +)
> > > +
> > > +;; The CC_SWP variant, where "val += other" precedes the increment and 
> > > the
> > > +;; extension is folded into the compare.  Combine fuses this into a 
> > > select
> > > +;; whose arms are the extended base and the extended base + 1.  The 
> > > match_dup
> > > +;; on the base keeps the transform sound: both arms extend the same 
> > > value, so
> > > +;; the false arm is exactly the extension of the base.
> > > +(define_insn_and_split "*cinc_ifelse_<SHORT:mode>_<GPI:mode>"
> > > +  [(set (match_operand:GPI 0 "register_operand" "=r")
> > > +       (if_then_else:GPI
> > > +         (match_operand 1 "aarch64_comparison_operation" "")
> > > +         (ANY_EXTEND:GPI (plus:SHORT (match_operand:SHORT 2 
> > > "register_operand" "r")
> > > +                                     (const_int 1)))
> > > +         (ANY_EXTEND:GPI (match_dup 2))))]
> > > +  ""
> > > +  "#"
> > > +  "&& can_create_pseudo_p ()"
> > > +  [(const_int 0)]
> > > +  {
> > > +    rtx op2 = operands[2];
> > > +    rtx val_gpi;
> > > +    if (SUBREG_P (op2) && subreg_lowpart_p (op2)
> > > +       && GET_MODE (SUBREG_REG (op2)) == <GPI:MODE>mode)
> > > +      val_gpi = SUBREG_REG (op2);
> > > +    else
> > > +      {
> > > +       val_gpi = gen_reg_rtx (<GPI:MODE>mode);
> > > +       convert_move (val_gpi, op2, <CODE> == ZERO_EXTEND);
> > > +      }
> > > +    rtx cond = gen_rtx_fmt_ee (GET_CODE (operands[1]), <GPI:MODE>mode,
> > > +                              XEXP (operands[1], 0), XEXP (operands[1], 
> > > 1));
> > > +    rtx tmp = gen_reg_rtx (<GPI:MODE>mode);
> > > +    emit_insn (gen_rtx_SET (tmp, gen_rtx_PLUS (<GPI:MODE>mode, cond, 
> > > val_gpi)));
> > > +    rtx narrow = gen_lowpart (<SHORT:MODE>mode, tmp);
> > > +    convert_move (operands[0], narrow, <CODE> == ZERO_EXTEND);
> > > +    DONE;
> > > +  }
> > > +)
> > > +
> > >  (define_insn "*csinv3<mode>_insn"
> > >    [(set (match_operand:GPI 0 "register_operand" "=r")
> > >          (if_then_else:GPI
> > > diff --git a/gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-1.c 
> > > b/gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-1.c
> > > new file mode 100644
> > > index 000000000000..956c12fa7afa
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-1.c
> > > @@ -0,0 +1,126 @@
> > > +/* Runtime correctness for conditional-increment of sub-int types
> > > +   (cf. gcc.target/aarch64/cinc-subint.c, which checks the cinc codegen).
> > > +   Not target-specific: this only validates the computed values, so it
> > > +   lives in the generic torture suite and runs at every optimisation
> > > +   level on every target.  */
> > > +
> > > +#include <limits.h>
> > > +
> > > +__attribute__((noinline))
> > > +int f_short (short val, int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_schar (signed char val, int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_ushort (unsigned short val, unsigned int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_uchar (unsigned char val, unsigned int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_short_3arg (short val, short other, int clipval) {
> > > +  val += other;
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_schar_3arg (signed char val, signed char other, int clipval) {
> > > +  val += other;
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_ushort_3arg (unsigned short val, unsigned short other, unsigned 
> > > int clipval) {
> > > +  val += other;
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +__attribute__((noinline))
> > > +int f_uchar_3arg (unsigned char val, unsigned char other, unsigned int 
> > > clipval) {
> > > +  val += other;
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  /* f_short */
> > > +  if (f_short (5, 10) != 6) __builtin_abort ();
> > > +  if (f_short (10, 10) != 10) __builtin_abort ();
> > > +  if (f_short (15, 10) != 15) __builtin_abort ();
> > > +  if (f_short (9, 10) != 10) __builtin_abort ();
> > > +  if (f_short (SHRT_MAX, SHRT_MAX) != SHRT_MAX) __builtin_abort ();
> > > +  if (f_short (SHRT_MAX - 1, SHRT_MAX) != SHRT_MAX) __builtin_abort ();
> > > +  if (f_short (SHRT_MIN, SHRT_MIN) != SHRT_MIN) __builtin_abort ();
> > > +  if (f_short (SHRT_MIN, 0) != SHRT_MIN + 1) __builtin_abort ();
> > > +  if (f_short (-5, -3) != -4) __builtin_abort ();
> > > +  if (f_short (-3, -5) != -3) __builtin_abort ();
> > > +
> > > +  /* f_schar */
> > > +  if (f_schar (5, 10) != 6) __builtin_abort ();
> > > +  if (f_schar (10, 10) != 10) __builtin_abort ();
> > > +  if (f_schar (SCHAR_MAX, SCHAR_MAX) != SCHAR_MAX) __builtin_abort ();
> > > +  if (f_schar (SCHAR_MAX - 1, SCHAR_MAX) != SCHAR_MAX) __builtin_abort 
> > > ();
> > > +  if (f_schar (SCHAR_MIN, SCHAR_MIN) != SCHAR_MIN) __builtin_abort ();
> > > +  if (f_schar (SCHAR_MIN, 0) != SCHAR_MIN + 1) __builtin_abort ();
> > > +
> > > +  /* f_ushort */
> > > +  if (f_ushort (5, 10) != 6) __builtin_abort ();
> > > +  if (f_ushort (10, 10) != 10) __builtin_abort ();
> > > +  if (f_ushort (USHRT_MAX, USHRT_MAX) != USHRT_MAX) __builtin_abort ();
> > > +  if (f_ushort (USHRT_MAX - 1, USHRT_MAX) != USHRT_MAX) __builtin_abort 
> > > ();
> > > +  if (f_ushort (0, 1) != 1) __builtin_abort ();
> > > +  if (f_ushort (0, 0) != 0) __builtin_abort ();
> > > +
> > > +  /* f_uchar */
> > > +  if (f_uchar (5, 10) != 6) __builtin_abort ();
> > > +  if (f_uchar (10, 10) != 10) __builtin_abort ();
> > > +  if (f_uchar (UCHAR_MAX, UCHAR_MAX) != UCHAR_MAX) __builtin_abort ();
> > > +  if (f_uchar (UCHAR_MAX - 1, UCHAR_MAX) != UCHAR_MAX) __builtin_abort 
> > > ();
> > > +
> > > +  /* f_short_3arg */
> > > +  if (f_short_3arg (3, 4, 10) != 8) __builtin_abort ();
> > > +  if (f_short_3arg (5, 5, 10) != 10) __builtin_abort ();
> > > +  if (f_short_3arg (6, 5, 10) != 11) __builtin_abort ();
> > > +  if (f_short_3arg (32000, 1000, 0) != -32535) __builtin_abort ();
> > > +  if (f_short_3arg (-100, 50, 0) != -49) __builtin_abort ();
> > > +  if (f_short_3arg (-100, 50, -50) != -50) __builtin_abort ();
> > > +
> > > +  /* f_schar_3arg */
> > > +  if (f_schar_3arg (3, 4, 10) != 8) __builtin_abort ();
> > > +  if (f_schar_3arg (5, 5, 10) != 10) __builtin_abort ();
> > > +  if (f_schar_3arg (100, 100, 0) != -55) __builtin_abort ();
> > > +  if (f_schar_3arg (-50, 20, 0) != -29) __builtin_abort ();
> > > +
> > > +  /* f_ushort_3arg */
> > > +  if (f_ushort_3arg (3, 4, 10) != 8) __builtin_abort ();
> > > +  if (f_ushort_3arg (5, 5, 10) != 10) __builtin_abort ();
> > > +  if (f_ushort_3arg (6, 5, 10) != 11) __builtin_abort ();
> > > +  if (f_ushort_3arg (USHRT_MAX, 1, 1) != 1) __builtin_abort ();
> > > +  if (f_ushort_3arg (USHRT_MAX - 1, 1, USHRT_MAX) != USHRT_MAX) 
> > > __builtin_abort ();
> > > +
> > > +  /* f_uchar_3arg */
> > > +  if (f_uchar_3arg (3, 4, 10) != 8) __builtin_abort ();
> > > +  if (f_uchar_3arg (5, 5, 10) != 10) __builtin_abort ();
> > > +  if (f_uchar_3arg (UCHAR_MAX, 1, 1) != 1) __builtin_abort ();
> > > +  if (f_uchar_3arg (UCHAR_MAX - 1, 1, UCHAR_MAX) != UCHAR_MAX) 
> > > __builtin_abort ();
> > > +
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-2.c 
> > > b/gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-2.c
> > > new file mode 100644
> > > index 000000000000..fb667528389b
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.c-torture/execute/cond-incr-subint-2.c
> > > @@ -0,0 +1,65 @@
> > > +/* Runtime regression tests for cases that should not be transformed by 
> > > the
> > > +   sub-int cinc splitters (codegen checked in
> > > +   gcc.target/aarch64/cinc-subint-neg-2.c).  These shapes sit on the 
> > > edges
> > > +   of the *cinc_ifelse splitter's match_dup contract; two of them fold to
> > > +   cinc and four must not.  Whether or not a shape folds, the value must 
> > > be
> > > +   the same as the unoptimised computation -- an unwanted transform would
> > > +   change it.  Value-only, so it lives in the generic torture suite and 
> > > runs
> > > +   at every optimisation level on every target.  */
> > > +
> > > +__attribute__((noinline))
> > > +int ctl_short_3arg (short val, short other, int clip)
> > > +{ val += other; if (val < clip) val++; return val; }
> > > +
> > > +__attribute__((noinline))
> > > +int diff_base (short val, short other, int clip)
> > > +{ short inc = val + other; short alt = val - other;
> > > +  return (inc < clip) ? (short) (inc + 1) : alt; }
> > > +
> > > +__attribute__((noinline))
> > > +int inc2 (short val, short other, int clip)
> > > +{ val += other; if (val < clip) val += 2; return val; }
> > > +
> > > +__attribute__((noinline))
> > > +int dec1 (short val, short other, int clip)
> > > +{ val += other; if (val < clip) val--; return val; }
> > > +
> > > +__attribute__((noinline))
> > > +int rev (short val, short other, int clip)
> > > +{ short s = val + other; return (s < clip) ? (int) s : (int) (short) (s 
> > > + 1); }
> > > +
> > > +__attribute__((noinline))
> > > +int mixed_ext (short val, short other, int clip)
> > > +{ short s = val + other; return (s < clip) ? (int) (unsigned short) (s + 
> > > 1) : (int) s; }
> > > +
> > > +struct row { short a, b; int c, ctl, diff, i2, d1, rv, mx; };
> > > +
> > > +static const struct row tab[] = {
> > > +  {     3,    4,     10,      8,      8,      9,      6,      7,      8 
> > > },
> > > +  {     5,    5,     10,     10,      0,     10,     10,     11,     10 
> > > },
> > > +  {     6,    5,     10,     11,      1,     11,     11,     12,     11 
> > > },
> > > +  { 32000, 1000,      0, -32535, -32535, -32534, -32537, -32536,  33001 
> > > },
> > > +  {  -100,   50,      0,    -49,    -49,    -48,    -51,    -50,  65487 
> > > },
> > > +  {  -100,   50,    -50,    -50,   -150,    -50,    -50,    -49,    -50 
> > > },
> > > +  {   100,  -50,      0,     50,    150,     50,     50,     51,     50 
> > > },
> > > +  {     0,    0,      0,      0,      0,      0,      0,      1,      0 
> > > },
> > > +  { 32767,    1,  32767, -32767, -32767, -32766,  32767, -32768,  32769 
> > > },
> > > +  {-32768,   -1,      0,  32767, -32767,  32767,  32767, -32768,  32767 
> > > },
> > > +  {    10,  -20,     -5,     -9,     -9,     -8,    -11,    -10,  65527 
> > > },
> > > +};
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  for (unsigned i = 0; i < sizeof (tab) / sizeof (tab[0]); i++)
> > > +    {
> > > +      const struct row *r = &tab[i];
> > > +      if (ctl_short_3arg (r->a, r->b, r->c) != r->ctl) __builtin_abort 
> > > ();
> > > +      if (diff_base (r->a, r->b, r->c) != r->diff) __builtin_abort ();
> > > +      if (inc2 (r->a, r->b, r->c) != r->i2) __builtin_abort ();
> > > +      if (dec1 (r->a, r->b, r->c) != r->d1) __builtin_abort ();
> > > +      if (rev (r->a, r->b, r->c) != r->rv) __builtin_abort ();
> > > +      if (mixed_ext (r->a, r->b, r->c) != r->mx) __builtin_abort ();
> > > +    }
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/aarch64/cinc-subint-neg-2.c 
> > > b/gcc/testsuite/gcc.target/aarch64/cinc-subint-neg-2.c
> > > new file mode 100644
> > > index 000000000000..2aae4a80468a
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/aarch64/cinc-subint-neg-2.c
> > > @@ -0,0 +1,59 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +/* { dg-final { check-function-bodies "**" "" "" } } */
> > > +
> > > +/* Regression tests for cases that should not be transformed by the 
> > > sub-int
> > > +   cinc splitters (see cinc-subint.c).  The *cinc_ifelse splitter is 
> > > sound
> > > +   only because its match_dup forces both arms of the select to extend 
> > > the
> > > +   same base with the same extension.  These shapes exercise the edges of
> > > +   that contract: only the two genuine conditional-increment shapes may 
> > > fold
> > > +   to cinc; every near miss must stay csel.  The scan-assembler-times
> > > +   directives pin "exactly two fold, four do not", so an unwanted 
> > > transform
> > > +   (extra cinc) or a regression (missing cinc) both fail.
> > > +   cond-incr-subint-2.c checks the values at runtime.  */
> > > +
> > > +/* Genuine conditional increment -> folds to cinc.  */
> > > +/*
> > > +** ctl_short_3arg:
> > > +**     add     w0, w0, w1
> > > +**     cmp     w2, w0, sxth
> > > +**     cinc    w0, w0, gt
> > > +**     sxth    w0, w0
> > > +**     ret
> > > +*/
> > > +int ctl_short_3arg (short val, short other, int clip)
> > > +{ val += other; if (val < clip) val++; return val; }
> > > +
> > > +/* +1 on the false arm: combine canonicalises (swap arms, invert the
> > > +   condition) and folds correctly with the inverted condition.  */
> > > +/*
> > > +** rev:
> > > +**     add     w0, w0, w1
> > > +**     cmp     w2, w0, sxth
> > > +**     cinc    w0, w0, le
> > > +**     sxth    w0, w0
> > > +**     ret
> > > +*/
> > > +int rev (short val, short other, int clip)
> > > +{ short s = val + other; return (s < clip) ? (int) s : (int) (short) (s 
> > > + 1); }
> > > +
> > > +/* Different base in each arm: match_dup must reject -> csel.  */
> > > +int diff_base (short val, short other, int clip)
> > > +{ short inc = val + other; short alt = val - other;
> > > +  return (inc < clip) ? (short) (inc + 1) : alt; }
> > > +
> > > +/* Increment by two, not one -> csel.  */
> > > +int inc2 (short val, short other, int clip)
> > > +{ val += other; if (val < clip) val += 2; return val; }
> > > +
> > > +/* Decrement, not increment -> csel.  */
> > > +int dec1 (short val, short other, int clip)
> > > +{ val += other; if (val < clip) val--; return val; }
> > > +
> > > +/* Mixed extension (zero-extend one arm, sign-extend the other): the 
> > > single
> > > +   ANY_EXTEND iterator cannot match a mixed pair -> csel.  */
> > > +int mixed_ext (short val, short other, int clip)
> > > +{ short s = val + other; return (s < clip) ? (int) (unsigned short) (s + 
> > > 1) : (int) s; }
> > > +
> > > +/* { dg-final { scan-assembler-times "\tcinc\t" 2 } } */
> > > +/* { dg-final { scan-assembler-times "\tcsel\t" 4 } } */
> > > diff --git a/gcc/testsuite/gcc.target/aarch64/cinc-subint-neg.c 
> > > b/gcc/testsuite/gcc.target/aarch64/cinc-subint-neg.c
> > > new file mode 100644
> > > index 000000000000..614827312d2e
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/aarch64/cinc-subint-neg.c
> > > @@ -0,0 +1,63 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +/* { dg-final { check-function-bodies "**" "" "" } } */
> > > +
> > > +/* Negative tests for the sub-int cinc splitters in cinc-subint.c: 
> > > shapes that
> > > +   must NOT be turned into cinc.  check-function-bodies makes the 
> > > expected
> > > +   instruction explicit for each case.  */
> > > +
> > > +/* Increment by 2 instead of 1: not a cinc candidate -> csel.  */
> > > +/*
> > > +** neg_add2:
> > > +**     sxth    w0, w0
> > > +**     cmp     w0, w1
> > > +**     add     w1, w0, 2
> > > +**     sxth    w1, w1
> > > +**     csel    w0, w0, w1, ge
> > > +**     ret
> > > +*/
> > > +int neg_add2 (short val, int clipval) {
> > > +  if (val < clipval) val += 2;
> > > +  return val;
> > > +}
> > > +
> > > +/* Decrement instead of increment: not a cinc candidate -> csel.  */
> > > +/*
> > > +** neg_dec:
> > > +**     sxth    w0, w0
> > > +**     cmp     w0, w1
> > > +**     sub     w1, w0, #1
> > > +**     sxth    w1, w1
> > > +**     csel    w0, w0, w1, ge
> > > +**     ret
> > > +*/
> > > +int neg_dec (short val, int clipval) {
> > > +  if (val < clipval) val--;
> > > +  return val;
> > > +}
> > > +
> > > +/* Full-width int: combine forms cinc directly; verify no regression.  */
> > > +/*
> > > +** pos_fullwidth:
> > > +**     cmp     w0, w1
> > > +**     cinc    w0, w0, lt
> > > +**     ret
> > > +*/
> > > +int pos_fullwidth (int val, int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +/* Conditional assignment (not increment): csel, not cinc.  */
> > > +/*
> > > +** neg_assign:
> > > +**     sxth    w0, w0
> > > +**     sxth    w2, w2
> > > +**     cmp     w0, w1
> > > +**     csel    w0, w0, w2, ge
> > > +**     ret
> > > +*/
> > > +int neg_assign (short val, int clipval, short other) {
> > > +  if (val < clipval) val = other;
> > > +  return val;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/aarch64/cinc-subint.c 
> > > b/gcc/testsuite/gcc.target/aarch64/cinc-subint.c
> > > new file mode 100644
> > > index 000000000000..099895ec067a
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/aarch64/cinc-subint.c
> > > @@ -0,0 +1,92 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +/* { dg-final { check-function-bodies "**" "" "" } } */
> > > +
> > > +/* Conditional increment of a sub-int value should use cinc, not csel.
> > > +   For short/char the sign/zero extension between the add and the select
> > > +   blocks combine from forming cinc; a define_insn_and_split sinks the
> > > +   extension past the select to expose it.  */
> > > +
> > > +/*
> > > +** f_short:
> > > +**     sxth    w0, w0
> > > +**     cmp     w0, w1
> > > +**     cinc    w0, w0, lt
> > > +**     sxth    w0, w0
> > > +**     ret
> > > +*/
> > > +int f_short (short val, int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +/*
> > > +** f_schar:
> > > +**     sxtb    w0, w0
> > > +**     cmp     w0, w1
> > > +**     cinc    w0, w0, lt
> > > +**     sxtb    w0, w0
> > > +**     ret
> > > +*/
> > > +int f_schar (signed char val, int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +/*
> > > +** f_ushort:
> > > +**     and     w0, w0, 65535
> > > +**     cmp     w0, w1
> > > +**     cinc    w0, w0, cc
> > > +**     and     w0, w0, 65535
> > > +**     ret
> > > +*/
> > > +int f_ushort (unsigned short val, unsigned int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +/*
> > > +** f_uchar:
> > > +**     and     w0, w0, 255
> > > +**     cmp     w0, w1
> > > +**     cinc    w0, w0, cc
> > > +**     and     w0, w0, 255
> > > +**     ret
> > > +*/
> > > +int f_uchar (unsigned char val, unsigned int clipval) {
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +/* Three-argument variants where val += other precedes the conditional
> > > +   increment.  The addition produces a full-width result, so the compare
> > > +   uses CC_SWP mode with a folded sign/zero-extend.  */
> > > +
> > > +/*
> > > +** f_short_3arg:
> > > +**     add     w0, w0, w1
> > > +**     cmp     w2, w0, sxth
> > > +**     cinc    w0, w0, gt
> > > +**     sxth    w0, w0
> > > +**     ret
> > > +*/
> > > +int f_short_3arg (short val, short other, int clipval) {
> > > +  val += other;
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > +
> > > +/*
> > > +** f_schar_3arg:
> > > +**     add     w0, w0, w1
> > > +**     cmp     w2, w0, sxtb
> > > +**     cinc    w0, w0, gt
> > > +**     sxtb    w0, w0
> > > +**     ret
> > > +*/
> > > +int f_schar_3arg (signed char val, signed char other, int clipval) {
> > > +  val += other;
> > > +  if (val < clipval) val++;
> > > +  return val;
> > > +}
> > > --
> > > 2.34.1
> > >
> > > base-commit: 09e74059bdb12c784570d40b28a33f22090badfe
> > > branch: ptomsich/40-cinc-combine-splitter

Reply via email to