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.
Both patterns carry a scratch register and may split before or after
reload, feeding the cinc a paradoxical lowpart of the operand whose
upper bits are dead. Carrying the scratch (rather than gating on
can_create_pseudo_p) keeps the split valid after reload as well.
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.
Signed-off-by: Philipp Tomsich <[email protected]>
---
Changes in v4:
- Produce the cinc and the extension from the split's output template
rather than emitting them in the prepare-statements (per review feedback).
- Use gen_lowpart_SUBREG for the result's narrow view and drop the
explanatory comments (per review feedback).
Changes in v3:
- Carry a scratch register instead of gating the split on
can_create_pseudo_p, so a matched insn can always split: before reload
the scratch is given a pseudo, after reload it is the allocated register
(per review feedback).
- Feed the cinc a paradoxical lowpart of the operand rather than extending
it, since the upper bits are dead (per review feedback).
Changes in v2:
- Reimplement with two define_insn_and_split patterns instead of four
peephole2 patterns, following review feedback; 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 | 51 +++++++
.../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, 456 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..5d42529dde89 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -5032,6 +5032,57 @@ (define_insn "csinc3<mode>_insn"
[(set_attr "type" "csel")]
)
+;; Conditional increment of a sub-int value: split the fused
+;; ANY_EXTEND (plus:SHORT (cond) (val)) into a wide cinc and the extension.
+(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"))))
+ (clobber (match_scratch:GPI 3 "=r"))]
+ ""
+ "#"
+ "&& 1"
+ [(set (match_dup 3) (plus:GPI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (ANY_EXTEND:GPI (match_dup 4)))]
+ {
+ if (GET_CODE (operands[3]) == SCRATCH)
+ operands[3] = gen_reg_rtx (<GPI:MODE>mode);
+ operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]), <GPI:MODE>mode,
+ XEXP (operands[1], 0), XEXP (operands[1], 1));
+ /* Can't use gen_lowpart_SUBREG: the operand may already be a subreg. */
+ operands[2] = lowpart_subreg (<GPI:MODE>mode, operands[2],
<SHORT:MODE>mode);
+ operands[4] = gen_lowpart_SUBREG (<SHORT:MODE>mode, operands[3]);
+ }
+)
+
+;; As above for the CC_SWP "val += other" variant; the match_dup on the base
+;; keeps both select arms on the same value.
+(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))))
+ (clobber (match_scratch:GPI 3 "=r"))]
+ ""
+ "#"
+ "&& 1"
+ [(set (match_dup 3) (plus:GPI (match_dup 1) (match_dup 2)))
+ (set (match_dup 0) (ANY_EXTEND:GPI (match_dup 4)))]
+ {
+ if (GET_CODE (operands[3]) == SCRATCH)
+ operands[3] = gen_reg_rtx (<GPI:MODE>mode);
+ operands[1] = gen_rtx_fmt_ee (GET_CODE (operands[1]), <GPI:MODE>mode,
+ XEXP (operands[1], 0), XEXP (operands[1], 1));
+ /* Can't use gen_lowpart_SUBREG: the operand may already be a subreg. */
+ operands[2] = lowpart_subreg (<GPI:MODE>mode, operands[2],
<SHORT:MODE>mode);
+ operands[4] = gen_lowpart_SUBREG (<SHORT:MODE>mode, operands[3]);
+ }
+)
+
(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