@@ -1108,6 +1108,89 @@ (define_insn "*sub<mode>3<setnz>"
[(set_attr "slottable" "yes,yes,yes,yes,no,no")
(set_attr "cc<ccnz>" "normal,normal,clobber,clobber,normal,normal")])
+;; Extend versions (zero/sign) of normal add/sub (no side-effects).
+
+;; QImode to HImode
+;; FIXME: GCC should widen.
+
+(define_insn "*extopqihi"
+ [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
+ (match_operator:HI
+ 3 "cris_additive_operand_extend_operator"
+ [(match_operand:HI 1 "register_operand" "0,0,0,r")
+ (match_operator:HI
+ 4 "cris_extend_operator"
+ [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")])]))
+ (clobber (reg:CC CRIS_CC0_REGNUM))]
+ "GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
+ && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)"
+ "@
+ %x3%E4.%m4 %2,%0
+ %x3%E4.%m4 %2,%0
+ %x3%E4.%m4 %2,%0
+ %x3%E4.%m4 %2,%1,%0"
+ [(set_attr "slottable" "yes,yes,no,no")
+ (set_attr "cc" "clobber")])
+
+(define_insn "*extop<mode>si<setnz>"
+ [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
+ (match_operator:SI
+ 3 "cris_operand_extend_operator"
+ [(match_operand:SI 1 "register_operand" "0,0,0,r")
+ (match_operator:SI
+ 4 "cris_extend_operator"
+ [(match_operand:BW 2 "nonimmediate_operand" "r,Q>,m,!To")])]))
+ (clobber (reg:CC CRIS_CC0_REGNUM))]
+ "(GET_CODE (operands[3]) != UMIN || GET_CODE (operands[4]) == ZERO_EXTEND)
+ && GET_MODE_SIZE (GET_MODE (operands[0])) <= UNITS_PER_WORD
+ && (operands[1] != frame_pointer_rtx || GET_CODE (operands[3]) != PLUS)"
+ "@
+ %x3%E4<m> %2,%0
+ %x3%E4<m> %2,%0
+ %x3%E4<m> %2,%0
+ %x3%E4<m> %2,%1,%0"
+ [(set_attr "slottable" "yes,yes,no,no")])
+
+;; We may have swapped operands for add or bound.
+;; For commutative operands, these are the canonical forms.
+
+;; QImode to HImode
+
+(define_insn "*addxqihi_swap"
+ [(set (match_operand:HI 0 "register_operand" "=r,r,r,r")
+ (plus:HI
+ (match_operator:HI
+ 3 "cris_extend_operator"
+ [(match_operand:QI 2 "nonimmediate_operand" "r,Q>,m,!To")])
+ (match_operand:HI 1 "register_operand" "0,0,0,r")))
+ (clobber (reg:CC CRIS_CC0_REGNUM))]
+ "operands[1] != frame_pointer_rtx"
+ "@
+ add%e3.b %2,%0
+ add%e3.b %2,%0
+ add%e3.b %2,%0
+ add%e3.b %2,%1,%0"
+ [(set_attr "slottable" "yes,yes,no,no")
+ (set_attr "cc" "clobber")])
+
+(define_insn "*extop<mode>si<setnz>_swap"
+ [(set (match_operand:SI 0 "register_operand" "=r,r,r,r")
+ (match_operator:SI
+ 4 "cris_plus_or_bound_operator"
+ [(match_operator:SI
+ 3 "cris_extend_operator"
+ [(match_operand:BW 2 "nonimmediate_operand" "r,Q>,m,!To")])
+ (match_operand:SI 1 "register_operand" "0,0,0,r")]))
+ (clobber (reg:CC CRIS_CC0_REGNUM))]
+ "(GET_CODE (operands[4]) != UMIN || GET_CODE (operands[3]) == ZERO_EXTEND)
+ && operands[1] != frame_pointer_rtx"
+ "@
+ %x4%E3<m> %2,%0
+ %x4%E3<m> %2,%0
+ %x4%E3<m> %2,%0
+ %x4%E3<m> %2,%1,%0"
+ [(set_attr "slottable" "yes,yes,no,no")])
+
;; This is the special case when we use what corresponds to the
;; instruction above in "casesi". Do *not* change it to use the generic
;; pattern and "REG 15" as pc; I did that and it led to madness and
new file mode 100644
@@ -0,0 +1,37 @@
+/* Check that we produce sign- and zero-extended additions and
+ subtractions, and that no (eliminable) test- or compare-instructions
+ are used. */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not {\tcmp|\ttest|\tsub\.|\tadd\.|\tmovs} } } */
+/* { dg-final { scan-assembler "\tadds" } } */
+/* { dg-final { scan-assembler "\tsubs" } } */
+
+#ifndef t
+#define t signed char
+#define s _sc
+#endif
+
+#ifndef t2
+#define t2 int
+#endif
+
+#ifndef f
+#define f0(a, s) a ## s
+#define f(a, s) f0(a, s)
+#endif
+
+extern void g(int);
+
+t2 f(a, s) (t2 a, t *b, int *c)
+{
+ t2 d = a + *b;
+ *c = d == 0;
+ return d;
+}
+t2 f(b, s) (t2 a, t *b, int *c)
+{
+ t2 d = a - *b;
+ *c = d == 0;
+ return d;
+}
new file mode 100644
@@ -0,0 +1,26 @@
+/* Check that we produce sign- and zero-extended additions and
+ subtractions, and that no (eliminable) test- or compare-instructions
+ are used. */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not {\tcmp|\ttest|\tsub\.|\tadd\.|\tmovu|\tmovs} } } */
+/* { dg-final { scan-assembler-times "\tadds" 1 } } */
+/* { dg-final { scan-assembler-times "\tsubs" 1 } } */
+/* { dg-final { scan-assembler-times "\taddu" 2 } } */
+/* { dg-final { scan-assembler-times "\tsubu" 2 } } */
+
+#define t unsigned char
+#define s _uc
+#include "pr93372-36.c"
+
+#undef t
+#undef s
+#define t signed short int
+#define s _ss
+#include "pr93372-36.c"
+
+#undef t
+#undef s
+#define t unsigned short int
+#define s _us
+#include "pr93372-36.c"
new file mode 100644
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-final { scan-assembler-not {\tcmp|\tsub\.|\tadd\.|\tmovu|\tmovs} } } */
+/* { dg-final { scan-assembler-times "\ttest\.w" 4 } } */
+/* { dg-final { scan-assembler-times "\tadds" 1 } } */
+/* { dg-final { scan-assembler-times "\tsubs" 1 } } */
+/* { dg-final { scan-assembler-times "\taddu" 1 } } */
+/* { dg-final { scan-assembler-times "\tsubu" 1 } } */
+
+/* Check that we produce sign- and zero-extended additions and
+ subtractions, also for 8-bit to 16-bit results. Note that we can't
+ eliminate compare insns, as the condition codes reflect the 32-bit
+ result.
+ This test-case is brittle, as with the presence of compare
+ instructions, there are several optimal instruction sequence, some of
+ which match the non-matcher patterns and do not contain the matching
+ patterns. */
+
+#define t unsigned char
+#define t2 unsigned short
+#define s _us
+#include "pr93372-36.c"
+
+#undef t
+#undef s
+#undef t2
+#define t signed char
+#define t2 signed short
+#define s _ss
+#include "pr93372-36.c"