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
