Bootstrap and regtest are still running.  If those are successful and
there are no further comments I will push this one in the coming days.

-- >8 --

gcc/ChangeLog:

        * config/s390/s390-protos.h (s390_emit_compare): Add mode
        parameter for the resulting RTX.
        * config/s390/s390.cc (s390_emit_compare): Dito.
        (s390_emit_compare_and_swap): Change.
        (s390_expand_vec_strlen): Change.
        (s390_expand_cs_hqi): Change.
        (s390_expand_split_stack_prologue): Change.
        * config/s390/s390.md (*add<mode>3_carry1_cc): Renamed to ...
        (add<mode>3_carry1_cc): this and in order to use the
        corresponding gen function, encode CC mode into pattern.
        (*sub<mode>3_borrow_cc): Renamed to ...
        (sub<mode>3_borrow_cc): this and in order to use the
        corresponding gen function, encode CC mode into pattern.
        (*add<mode>3_alc_carry1_cc): Renamed to ...
        (add<mode>3_alc_carry1_cc): this and in order to use the
        corresponding gen function, encode CC mode into pattern.
        (sub<mode>3_slb_borrow1_cc): New.
        (uaddc<mode>5): New.
        (usubc<mode>5): New.

gcc/testsuite/ChangeLog:

        * gcc.target/s390/uaddc-1.c: New test.
        * gcc.target/s390/uaddc-2.c: New test.
        * gcc.target/s390/uaddc-3.c: New test.
        * gcc.target/s390/usubc-1.c: New test.
        * gcc.target/s390/usubc-2.c: New test.
        * gcc.target/s390/usubc-3.c: New test.
---
 gcc/config/s390/s390-protos.h           |   2 +-
 gcc/config/s390/s390.cc                 |  20 +--
 gcc/config/s390/s390.md                 | 115 +++++++++++++----
 gcc/testsuite/gcc.target/s390/uaddc-1.c | 156 ++++++++++++++++++++++++
 gcc/testsuite/gcc.target/s390/uaddc-2.c |  25 ++++
 gcc/testsuite/gcc.target/s390/uaddc-3.c |  27 ++++
 gcc/testsuite/gcc.target/s390/usubc-1.c | 156 ++++++++++++++++++++++++
 gcc/testsuite/gcc.target/s390/usubc-2.c |  25 ++++
 gcc/testsuite/gcc.target/s390/usubc-3.c |  29 +++++
 9 files changed, 519 insertions(+), 36 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/uaddc-1.c
 create mode 100644 gcc/testsuite/gcc.target/s390/uaddc-2.c
 create mode 100644 gcc/testsuite/gcc.target/s390/uaddc-3.c
 create mode 100644 gcc/testsuite/gcc.target/s390/usubc-1.c
 create mode 100644 gcc/testsuite/gcc.target/s390/usubc-2.c
 create mode 100644 gcc/testsuite/gcc.target/s390/usubc-3.c

diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h
index e7ac59d17da..b8604394391 100644
--- a/gcc/config/s390/s390-protos.h
+++ b/gcc/config/s390/s390-protos.h
@@ -86,7 +86,7 @@ extern int tls_symbolic_operand (rtx);
 extern bool s390_match_ccmode (rtx_insn *, machine_mode);
 extern machine_mode s390_tm_ccmode (rtx, rtx, bool);
 extern machine_mode s390_select_ccmode (enum rtx_code, rtx, rtx);
-extern rtx s390_emit_compare (enum rtx_code, rtx, rtx);
+extern rtx s390_emit_compare (machine_mode, enum rtx_code, rtx, rtx);
 extern rtx_insn *s390_emit_jump (rtx, rtx);
 extern bool symbolic_reference_mentioned_p (rtx);
 extern bool tls_symbolic_reference_mentioned_p (rtx);
diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index c9172d1153a..4c8bf21539c 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -2029,9 +2029,9 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx 
*op1,
    the IF_THEN_ELSE of the conditional branch testing the result.  */
 
 rtx
-s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
+s390_emit_compare (machine_mode mode, enum rtx_code code, rtx op0, rtx op1)
 {
-  machine_mode mode = s390_select_ccmode (code, op0, op1);
+  machine_mode cc_mode = s390_select_ccmode (code, op0, op1);
   rtx cc;
 
   /* Force OP1 into register in order to satisfy VXE TFmode patterns.  */
@@ -2043,17 +2043,17 @@ s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
       /* Do not output a redundant compare instruction if a
         compare_and_swap pattern already computed the result and the
         machine modes are compatible.  */
-      gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), mode)
+      gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), cc_mode)
                  == GET_MODE (op0));
       cc = op0;
     }
   else
     {
-      cc = gen_rtx_REG (mode, CC_REGNUM);
-      emit_insn (gen_rtx_SET (cc, gen_rtx_COMPARE (mode, op0, op1)));
+      cc = gen_rtx_REG (cc_mode, CC_REGNUM);
+      emit_insn (gen_rtx_SET (cc, gen_rtx_COMPARE (cc_mode, op0, op1)));
     }
 
-  return gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx);
+  return gen_rtx_fmt_ee (code, mode, cc, const0_rtx);
 }
 
 /* If MEM is not a legitimate compare-and-swap memory operand, return a new
@@ -2103,7 +2103,7 @@ s390_emit_compare_and_swap (enum rtx_code code, rtx old, 
rtx mem,
     default:
       gcc_unreachable ();
     }
-  return s390_emit_compare (code, cc, const0_rtx);
+  return s390_emit_compare (VOIDmode, code, cc, const0_rtx);
 }
 
 /* Emit a jump instruction to TARGET and return it.  If COND is
@@ -6647,7 +6647,7 @@ s390_expand_vec_strlen (rtx target, rtx string, rtx 
alignment)
      Now we have to check whether the resulting index lies within the
      bytes actually part of the string.  */
 
-  cond = s390_emit_compare (GT, convert_to_mode (Pmode, len, 1),
+  cond = s390_emit_compare (VOIDmode, GT, convert_to_mode (Pmode, len, 1),
                            highest_index_to_load_reg);
   s390_load_address (highest_index_to_load_reg,
                     gen_rtx_PLUS (Pmode, highest_index_to_load_reg,
@@ -7845,7 +7845,7 @@ s390_expand_cs_hqi (machine_mode mode, rtx btarget, rtx 
vtarget, rtx mem,
       tmp = copy_to_reg (val);
       force_expand_binop (SImode, and_optab, res, ac.modemaski, val,
                          1, OPTAB_DIRECT);
-      cc = s390_emit_compare (NE, val, tmp);
+      cc = s390_emit_compare (VOIDmode, NE, val, tmp);
       s390_emit_jump (csloop, cc);
 
       /* Failed.  */
@@ -12694,7 +12694,7 @@ s390_expand_split_stack_prologue (void)
        }
 
       /* Compare the (maybe adjusted) guard with the stack pointer.  */
-      cc = s390_emit_compare (LT, stack_pointer_rtx, guard);
+      cc = s390_emit_compare (VOIDmode, LT, stack_pointer_rtx, guard);
     }
 
   call_done = gen_label_rtx ();
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 4a225ae24f3..5f2f97dad45 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -6001,14 +6001,14 @@
                         z10_super_E1,z10_super_E1,z10_super_E1")])
 
 ; alr, alfi, slfi, al, aly, alrk, alhsik, algr, algfi, slgfi, alg, alsi, 
algsi, algrk, alghsik
-(define_insn "*add<mode>3_carry1_cc"
-  [(set (reg CC_REGNUM)
-        (compare (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" "%0,d, 
0, 0,d,0,0,0")
-                          (match_operand:GPR 2 "general_operand"      " 
d,d,Op,On,K,R,T,C"))
-                 (match_dup 1)))
-   (set (match_operand:GPR 0 "nonimmediate_operand"                    "=d,d, 
d, d,d,d,d,d")
+(define_insn "add<mode>3_carry1_cc"
+  [(set (reg:CCL1 CC_REGNUM)
+        (compare:CCL1 (plus:GPR (match_operand:GPR 1 "nonimmediate_operand" 
"%0,d, 0, 0,d,0,0,0")
+                               (match_operand:GPR 2 "general_operand"      " 
d,d,Op,On,K,R,T,C"))
+                     (match_dup 1)))
+   (set (match_operand:GPR 0 "nonimmediate_operand"                         
"=d,d, d, d,d,d,d,d")
         (plus:GPR (match_dup 1) (match_dup 2)))]
-  "s390_match_ccmode (insn, CCL1mode)"
+  ""
   "@
    al<g>r\t%0,%2
    al<g>rk\t%0,%1,%2
@@ -6541,14 +6541,14 @@
    (set_attr "z10prop" "z10_super_c_E1,*,z10_super_E1,z10_super_E1")])
 
 ; slr, sl, sly, slgr, slg, slrk, slgrk
-(define_insn "*sub<mode>3_borrow_cc"
-  [(set (reg CC_REGNUM)
-        (compare (minus:GPR (match_operand:GPR 1 "register_operand" "0,d,0,0")
-                           (match_operand:GPR 2 "general_operand"  "d,d,R,T"))
-                 (match_dup 1)))
-   (set (match_operand:GPR 0 "register_operand"                    "=d,d,d,d")
+(define_insn "sub<mode>3_borrow_cc"
+  [(set (reg:CCL2 CC_REGNUM)
+        (compare:CCL2 (minus:GPR (match_operand:GPR 1 "register_operand" 
"0,d,0,0")
+                                (match_operand:GPR 2 "general_operand"  
"d,d,R,T"))
+                     (match_dup 1)))
+   (set (match_operand:GPR 0 "register_operand"                         
"=d,d,d,d")
         (minus:GPR (match_dup 1) (match_dup 2)))]
-  "s390_match_ccmode (insn, CCL2mode)"
+  ""
   "@
    sl<g>r\t%0,%2
    sl<g>rk\t%0,%1,%2
@@ -6754,22 +6754,43 @@
 ; add(di|si)cc instruction pattern(s).
 ;
 
+(define_expand "uaddc<mode>5"
+  [(match_operand:GPR 0 "register_operand")
+   (match_operand:GPR 1 "nonimmediate_operand")
+   (match_operand:GPR 2 "nonimmediate_operand")
+   (match_operand:GPR 3 "nonimmediate_operand")
+   (match_operand:GPR 4 "general_operand")]
+  "TARGET_Z196"
+{
+  if (operands[4] == const0_rtx)
+    emit_insn (gen_add<mode>3_carry1_cc (operands[0], operands[2], 
operands[3]));
+  else
+    {
+      operands[4] = force_reg (<MODE>mode, operands[4]);
+      rtx alc_cond = s390_emit_compare (<MODE>mode, GTU, operands[4], 
const0_rtx);
+      emit_insn (gen_add<mode>3_alc_carry1_cc (operands[0], operands[2], 
operands[3], alc_cond));
+    }
+  rtx mov_cond = gen_rtx_LTU (<MODE>mode, gen_rtx_REG (CCL1mode, CC_REGNUM), 
const0_rtx);
+  emit_insn (gen_mov<mode>cc (operands[1], mov_cond, const1_rtx, const0_rtx));
+  DONE;
+})
+
 ; the following 4 patterns are used when the result of an add with
 ; carry is checked for an overflow condition
 
 ; op1 + op2 + c < op1
 
 ; alcr, alc, alcgr, alcg
-(define_insn "*add<mode>3_alc_carry1_cc"
-  [(set (reg CC_REGNUM)
-        (compare
+(define_insn "add<mode>3_alc_carry1_cc"
+  [(set (reg:CCL1 CC_REGNUM)
+        (compare:CCL1
           (plus:GPR (plus:GPR (match_operand:GPR 3 "s390_alc_comparison" "")
                               (match_operand:GPR 1 "nonimmediate_operand" 
"%0,0"))
                     (match_operand:GPR 2 "general_operand" "d,T"))
           (match_dup 1)))
    (set (match_operand:GPR 0 "register_operand" "=d,d")
         (plus:GPR (plus:GPR (match_dup 3) (match_dup 1)) (match_dup 2)))]
-  "s390_match_ccmode (insn, CCL1mode)"
+  ""
   "@
    alc<g>r\t%0,%2
    alc<g>\t%0,%2"
@@ -6854,6 +6875,31 @@
    alc<g>\t%0,%2"
   [(set_attr "op_type"  "RRE,RXY")])
 
+(define_expand "usubc<mode>5"
+  [(match_operand:GPR 0 "register_operand")
+   (match_operand:GPR 1 "nonimmediate_operand")
+   (match_operand:GPR 2 "nonimmediate_operand")
+   (match_operand:GPR 3 "nonimmediate_operand")
+   (match_operand:GPR 4 "general_operand")]
+  "TARGET_Z196"
+{
+  if (operands[4] == const0_rtx)
+    {
+      operands[2] = force_reg (<MODE>mode, operands[2]);
+      emit_insn (gen_sub<mode>3_borrow_cc (operands[0], operands[2], 
operands[3]));
+    }
+  else
+    {
+      rtx tmp = gen_reg_rtx (<MODE>mode);
+      emit_insn (gen_xor<mode>3 (tmp, operands[4], const1_rtx));
+      rtx slb_cond = s390_emit_compare (<MODE>mode, LEU, tmp, const0_rtx);
+      emit_insn (gen_sub<mode>3_slb_borrow1_cc (operands[0], operands[2], 
operands[3], slb_cond));
+    }
+  rtx mov_cond = gen_rtx_GTU (<MODE>mode, gen_rtx_REG (CCL2mode, CC_REGNUM), 
const0_rtx);
+  emit_insn (gen_mov<mode>cc (operands[1], mov_cond, const1_rtx, const0_rtx));
+  DONE;
+})
+
 ; slbr, slb, slbgr, slbg
 (define_insn "*sub<mode>3_slb_cc"
   [(set (reg CC_REGNUM)
@@ -6885,6 +6931,23 @@
   [(set_attr "op_type"  "RRE,RXY")
    (set_attr "z10prop" "z10_c,*")])
 
+; slbr, slb, slbgr, slbg
+(define_insn "sub<mode>3_slb_borrow1_cc"
+  [(set (reg:CCL2 CC_REGNUM)
+       (compare:CCL2
+         (minus:GPR (minus:GPR (match_operand:GPR 1 "nonimmediate_operand" 
"0,0")
+                               (match_operand:GPR 2 "general_operand" "d,T"))
+                    (match_operand:GPR 3 "s390_slb_comparison" ""))
+         (match_dup 1)))
+   (set (match_operand:GPR 0 "register_operand" "=d,d")
+       (minus:GPR (minus:GPR (match_dup 1) (match_dup 2)) (match_dup 3)))]
+  ""
+  "@
+   slb<g>r\t%0,%2
+   slb<g>\t%0,%2"
+  [(set_attr "op_type"  "RRE,RXY")
+   (set_attr "z10prop" "z10_c,*")])
+
 (define_expand "add<mode>cc"
   [(match_operand:GPR 0 "register_operand" "")
    (match_operand 1 "comparison_operator" "")
@@ -7018,7 +7081,8 @@
 
   /* Emit the comparison insn in case we do not already have a comparison 
result.  */
   if (!s390_comparison (operands[1], VOIDmode))
-    operands[1] = s390_emit_compare (GET_CODE (operands[1]),
+    operands[1] = s390_emit_compare (VOIDmode,
+                                    GET_CODE (operands[1]),
                                     XEXP (operands[1], 0),
                                     XEXP (operands[1], 1));
 })
@@ -7036,7 +7100,8 @@
   /* Emit the comparison insn in case we do not already have a comparison
      result. */
   if (!s390_comparison (operands[1], VOIDmode))
-    operands[1] = s390_emit_compare (GET_CODE (operands[1]),
+    operands[1] = s390_emit_compare (VOIDmode,
+                             GET_CODE (operands[1]),
                              XEXP (operands[1], 0),
                              XEXP (operands[1], 1));
 
@@ -9424,7 +9489,7 @@
                       (pc)))]
   ""
   "s390_emit_jump (operands[3],
-    s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2]));
+    s390_emit_compare (VOIDmode, GET_CODE (operands[0]), operands[1], 
operands[2]));
    DONE;")
 
 (define_expand "cbranch<mode>4"
@@ -9436,7 +9501,7 @@
                       (pc)))]
   "TARGET_HARD_FLOAT"
   "s390_emit_jump (operands[3],
-    s390_emit_compare (GET_CODE (operands[0]), operands[1], operands[2]));
+    s390_emit_compare (VOIDmode, GET_CODE (operands[0]), operands[1], 
operands[2]));
    DONE;")
 
 (define_expand "cbranchcc4"
@@ -9591,7 +9656,7 @@
             (match_operand 3 "const0_operand" ""))]
   ""
   {
-    rtx cond = s390_emit_compare (GET_CODE (operands[0]),
+    rtx cond = s390_emit_compare (VOIDmode, GET_CODE (operands[0]),
                                   operands[1], operands[2]);
     emit_insn (gen_condtrap (cond, XEXP (cond, 0)));
     DONE;
@@ -9604,7 +9669,7 @@
             (match_operand 3 "const0_operand" ""))]
   ""
   {
-    rtx cond = s390_emit_compare (GET_CODE (operands[0]),
+    rtx cond = s390_emit_compare (VOIDmode, GET_CODE (operands[0]),
                                   operands[1], operands[2]);
     emit_insn (gen_condtrap (cond, XEXP (cond, 0)));
     DONE;
@@ -12245,7 +12310,7 @@
     emit_insn (gen_subdi3 (reg, stack_pointer_rtx, operands[0]));
   else
     emit_insn (gen_subsi3 (reg, stack_pointer_rtx, operands[0]));
-  cc = s390_emit_compare (GT, reg, guard);
+  cc = s390_emit_compare (VOIDmode, GT, reg, guard);
   s390_emit_jump (operands[1], cc);
 
   DONE;
diff --git a/gcc/testsuite/gcc.target/s390/uaddc-1.c 
b/gcc/testsuite/gcc.target/s390/uaddc-1.c
new file mode 100644
index 00000000000..bec55942ea8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/uaddc-1.c
@@ -0,0 +1,156 @@
+/* { dg-do run { target { s390_useable_hw } } } */
+/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" }  
*/
+/* { dg-final { scan-tree-dump-times "\\.UADDC \\(" 4 "optimized" { target 
lp64 } } } */
+/* { dg-final { scan-tree-dump-times "\\.UADDC \\(" 2 "optimized" { target { ! 
lp64 } } } } */
+/* { dg-final { scan-assembler-times "\\talcr\\t" 1 } } */
+/* { dg-final { scan-assembler-times "\\talc\\t" 1 } } */
+/* { dg-final { scan-assembler-times "\\talcgr\\t" 1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "\\talcg\\t" 1 { target lp64 } } } */
+
+#include <assert.h>
+
+unsigned int __attribute__ ((noipa))
+uaddc (unsigned int x, unsigned int y, _Bool carry_in, _Bool *carry_out)
+{
+  unsigned int r;
+  _Bool c1 = __builtin_add_overflow (x, y, &r);
+  _Bool c2 = __builtin_add_overflow (r, carry_in, &r);
+  *carry_out = c1 | c2;
+  return r;
+}
+
+void
+test_int (void)
+{
+  _Bool c;
+  unsigned int r;
+
+  r = uaddc (0xf0000000u, 0x0fffffffu, 0, &c);
+  assert (r == 0xffffffffu && !c);
+
+  r = uaddc (0xf0000000u, 0x0fffffffu, 1, &c);
+  assert (r == 0 && c);
+
+  r = uaddc (0xf0000001u, 0x0fffffffu, 0, &c);
+  assert (r == 0 && c);
+
+  r = uaddc (0xf0000001u, 0x0fffffffu, 1, &c);
+  assert (r == 1 && c);
+}
+
+unsigned int __attribute__ ((noipa))
+uaddc_mem (unsigned int *x, unsigned int *y, _Bool carry_in, _Bool *carry_out)
+{
+  unsigned int r;
+  _Bool c1 = __builtin_add_overflow (*x, *y, &r);
+  _Bool c2 = __builtin_add_overflow (r, carry_in, &r);
+  *carry_out = c1 | c2;
+  return r;
+}
+
+void
+test_int_mem (void)
+{
+  _Bool c;
+  unsigned int r, x, y;
+
+  x = 0xf0000000u;
+  y = 0x0fffffffu;
+  r = uaddc_mem (&x, &y, 0, &c);
+  assert (r == 0xffffffffu && !c);
+
+  x = 0xf0000000u;
+  y = 0x0fffffffu;
+  r = uaddc_mem (&x, &y, 1, &c);
+  assert (r == 0 && c);
+
+  x = 0xf0000001u;
+  y = 0x0fffffffu;
+  r = uaddc_mem (&x, &y, 0, &c);
+  assert (r == 0 && c);
+
+  x = 0xf0000001u;
+  y = 0x0fffffffu;
+  r = uaddc_mem (&x, &y, 1, &c);
+  assert (r == 1 && c);
+}
+
+#ifdef __s390x__
+unsigned long __attribute__ ((noipa))
+uaddcl (unsigned long x, unsigned long y, _Bool carry_in, _Bool *carry_out)
+{
+  unsigned long r;
+  _Bool c1 = __builtin_add_overflow (x, y, &r);
+  _Bool c2 = __builtin_add_overflow (r, carry_in, &r);
+  *carry_out = c1 | c2;
+  return r;
+}
+
+void
+test_long (void)
+{
+  _Bool c;
+  unsigned long r;
+
+  r = uaddcl (0xf000000000000000u, 0x0fffffffffffffffu, 0, &c);
+  assert (r == 0xffffffffffffffffu && !c);
+
+  r = uaddcl (0xf000000000000000u, 0x0fffffffffffffffu, 1, &c);
+  assert (r == 0 && c);
+
+  r = uaddcl (0xf000000000000001u, 0x0fffffffffffffffu, 0, &c);
+  assert (r == 0 && c);
+
+  r = uaddcl (0xf000000000000001u, 0x0fffffffffffffffu, 1, &c);
+  assert (r == 1 && c);
+}
+
+unsigned long __attribute__ ((noipa))
+uaddcl_mem (unsigned long *x, unsigned long *y, _Bool carry_in, _Bool 
*carry_out)
+{
+  unsigned long r;
+  _Bool c1 = __builtin_add_overflow (*x, *y, &r);
+  _Bool c2 = __builtin_add_overflow (r, carry_in, &r);
+  *carry_out = c1 | c2;
+  return r;
+}
+
+void
+test_long_mem (void)
+{
+  _Bool c;
+  unsigned long r, x, y;
+
+  x = 0xf000000000000000u;
+  y = 0x0fffffffffffffffu;
+  r = uaddcl_mem (&x, &y, 0, &c);
+  assert (r == 0xffffffffffffffffu && !c);
+
+  x = 0xf000000000000000u;
+  y = 0x0fffffffffffffffu;
+  r = uaddcl_mem (&x, &y, 1, &c);
+  assert (r == 0 && c);
+
+  x = 0xf000000000000001u;
+  y = 0x0fffffffffffffffu;
+  r = uaddcl_mem (&x, &y, 0, &c);
+  assert (r == 0 && c);
+
+  x = 0xf000000000000001u;
+  y = 0x0fffffffffffffffu;
+  r = uaddcl_mem (&x, &y, 1, &c);
+  assert (r == 1 && c);
+}
+#endif
+
+int
+main (void)
+{
+  test_int ();
+  test_int_mem ();
+#ifdef __s390x__
+  test_long ();
+  test_long_mem ();
+#endif
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/uaddc-2.c 
b/gcc/testsuite/gcc.target/s390/uaddc-2.c
new file mode 100644
index 00000000000..da88733c835
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/uaddc-2.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" }  
*/
+/* { dg-final { scan-tree-dump-times "\\.UADDC \\(" 6 "optimized" } } */
+/* { dg-final { scan-assembler-times "\\talcr\\t" 6 { target { ! lp64 } } } } 
*/
+/* { dg-final { scan-assembler-times "\\talcr\\t" 3 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "\\talcgr\\t" 3 { target lp64 } } } */
+
+#define TEST(T, V, OP) \
+  unsigned T \
+  uaddc_##T##_##V (unsigned T x, unsigned T y, _Bool carry_in, _Bool 
*carry_out) \
+  { \
+    unsigned T r; \
+    _Bool c1 = __builtin_add_overflow (x, y, &r); \
+    _Bool c2 = __builtin_add_overflow (r, carry_in, &r); \
+    *carry_out = c1 OP c2; \
+    return r; \
+  }
+
+TEST(int, 1, |)
+TEST(int, 2, ||)
+TEST(int, 3, ^)
+
+TEST(long, 1, |)
+TEST(long, 2, ||)
+TEST(long, 3, ^)
diff --git a/gcc/testsuite/gcc.target/s390/uaddc-3.c 
b/gcc/testsuite/gcc.target/s390/uaddc-3.c
new file mode 100644
index 00000000000..3237fc208ff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/uaddc-3.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" }  
*/
+/* { dg-final { scan-tree-dump-times "\\.UADDC \\(\[^,\]+, \[^,\]+, 0\\)" 1 
"optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\.UADDC \\(\[^,\]+, \[^,\]+, _.+\\)" 1 
"optimized" } } */
+/* { dg-final { scan-assembler-times "alg\t" 1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "ag\t" 2 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "al\t" 1 { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler-times "a\t" 2 { target { ! lp64 } } } } */
+
+static unsigned long
+uaddc (unsigned long x, unsigned long y, unsigned long carry_in, unsigned long 
*carry_out)
+{
+  unsigned long r;
+  unsigned long c1 = __builtin_add_overflow (x, y, &r);
+  unsigned long c2 = __builtin_add_overflow (r, carry_in, &r);
+  *carry_out = c1 + c2;
+  return r;
+}
+
+void
+test (unsigned long *p, unsigned long *q)
+{
+  unsigned long c;
+  p[0] = uaddc (p[0], q[0], 0, &c);
+  p[1] = uaddc (p[1], q[1], c, &c);
+}
+
diff --git a/gcc/testsuite/gcc.target/s390/usubc-1.c 
b/gcc/testsuite/gcc.target/s390/usubc-1.c
new file mode 100644
index 00000000000..608b5ea89bf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/usubc-1.c
@@ -0,0 +1,156 @@
+/* { dg-do run { target { s390_useable_hw } } } */
+/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" }  
*/
+/* { dg-final { scan-tree-dump-times "\\.USUBC \\(" 4 "optimized" { target 
lp64 } } } */
+/* { dg-final { scan-tree-dump-times "\\.USUBC \\(" 2 "optimized" { target { ! 
lp64 } } } } */
+/* { dg-final { scan-assembler-times "\\tslbr\\t" 1 } } */
+/* { dg-final { scan-assembler-times "\\tslb\\t" 1 } } */
+/* { dg-final { scan-assembler-times "\\tslbgr\\t" 1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "\\tslbg\\t" 1 { target lp64 } } } */
+
+#include <assert.h>
+
+unsigned int __attribute__ ((noipa))
+usubc (unsigned int x, unsigned int y, _Bool borrow_in, _Bool *borrow_out)
+{
+  unsigned int r;
+  _Bool b1 = __builtin_sub_overflow (x, y, &r);
+  _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r);
+  *borrow_out = b1 | b2;
+  return r;
+}
+
+void
+test_int (void)
+{
+  _Bool b;
+  unsigned int r;
+
+  r = usubc (0xfu, 0xfu, 0, &b);
+  assert (r == 0 && !b);
+
+  r = usubc (0xfu, 0xfu, 1, &b);
+  assert (r == 0xffffffffu && b);
+
+  r = usubc (0xfu, 0xffu, 0, &b);
+  assert (r == 0xffffff10u && b);
+
+  r = usubc (0xfu, 0xffu, 1, &b);
+  assert (r == 0xffffff0fu && b);
+}
+
+unsigned int __attribute__ ((noipa))
+usubc_mem (unsigned int *x, unsigned int *y, _Bool borrow_in, _Bool 
*borrow_out)
+{
+  unsigned int r;
+  _Bool b1 = __builtin_sub_overflow (*x, *y, &r);
+  _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r);
+  *borrow_out = b1 | b2;
+  return r;
+}
+
+void
+test_int_mem (void)
+{
+  _Bool b;
+  unsigned int r, x, y;
+
+  x = 0xfu;
+  y = 0xfu;
+  r = usubc_mem (&x, &y, 0, &b);
+  assert (r == 0 && !b);
+
+  x = 0xfu;
+  y = 0xfu;
+  r = usubc_mem (&x, &y, 1, &b);
+  assert (r == 0xffffffffu && b);
+
+  x = 0xfu;
+  y = 0xffu;
+  r = usubc_mem (&x, &y, 0, &b);
+  assert (r == 0xffffff10u && b);
+
+  x = 0xfu;
+  y = 0xffu;
+  r = usubc_mem (&x, &y, 1, &b);
+  assert (r == 0xffffff0fu && b);
+}
+
+#ifdef __s390x__
+unsigned long __attribute__ ((noipa))
+usubcl (unsigned long x, unsigned long y, _Bool borrow_in, _Bool *borrow_out)
+{
+  unsigned long r;
+  _Bool b1 = __builtin_sub_overflow (x, y, &r);
+  _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r);
+  *borrow_out = b1 | b2;
+  return r;
+}
+
+void
+test_long (void)
+{
+  _Bool b;
+  unsigned long r;
+
+  r = usubcl (0xfu, 0xfu, 0, &b);
+  assert (r == 0 && !b);
+
+  r = usubcl (0xfu, 0xfu, 1, &b);
+  assert (r == 0xffffffffffffffffu && b);
+
+  r = usubcl (0xfu, 0xffu, 0, &b);
+  assert (r == 0xffffffffffffff10u && b);
+
+  r = usubcl (0xfu, 0xffu, 1, &b);
+  assert (r == 0xffffffffffffff0fu && b);
+}
+
+unsigned long __attribute__ ((noipa))
+usubcl_mem (unsigned long *x, unsigned long *y, _Bool borrow_in, _Bool 
*borrow_out)
+{
+  unsigned long r;
+  _Bool b1 = __builtin_sub_overflow (*x, *y, &r);
+  _Bool b2 = __builtin_sub_overflow (r, borrow_in, &r);
+  *borrow_out = b1 | b2;
+  return r;
+}
+
+void
+test_long_mem (void)
+{
+  _Bool b;
+  unsigned long r, x, y;
+
+  x = 0xfu;
+  y = 0xfu;
+  r = usubcl_mem (&x, &y, 0, &b);
+  assert (r == 0 && !b);
+
+  x = 0xfu;
+  y = 0xfu;
+  r = usubcl_mem (&x, &y, 1, &b);
+  assert (r == 0xffffffffffffffffu && b);
+
+  x = 0xfu;
+  y = 0xffu;
+  r = usubcl_mem (&x, &y, 0, &b);
+  assert (r == 0xffffffffffffff10u && b);
+
+  x = 0xfu;
+  y = 0xffu;
+  r = usubcl_mem (&x, &y, 1, &b);
+  assert (r == 0xffffffffffffff0fu && b);
+}
+#endif
+
+int
+main (void)
+{
+  test_int ();
+  test_int_mem ();
+#ifdef __s390x__
+  test_long ();
+  test_long_mem ();
+#endif
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/s390/usubc-2.c 
b/gcc/testsuite/gcc.target/s390/usubc-2.c
new file mode 100644
index 00000000000..382581e4d2b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/usubc-2.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" }  
*/
+/* { dg-final { scan-tree-dump-times "\\.USUBC \\(" 6 "optimized" } } */
+/* { dg-final { scan-assembler-times "\\tslbr\\t" 6 { target { ! lp64 } } } } 
*/
+/* { dg-final { scan-assembler-times "\\tslbr\\t" 3 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "\\tslbgr\\t" 3 { target lp64 } } } */
+
+#define TEST(T, V, OP) \
+  unsigned T \
+  uaddc_##T##_##V (unsigned T x, unsigned T y, _Bool carry_in, _Bool 
*carry_out) \
+  { \
+    unsigned T r; \
+    _Bool c1 = __builtin_sub_overflow (x, y, &r); \
+    _Bool c2 = __builtin_sub_overflow (r, carry_in, &r); \
+    *carry_out = c1 OP c2; \
+    return r; \
+  }
+
+TEST(int, 1, |)
+TEST(int, 2, ||)
+TEST(int, 3, ^)
+
+TEST(long, 1, |)
+TEST(long, 2, ||)
+TEST(long, 3, ^)
diff --git a/gcc/testsuite/gcc.target/s390/usubc-3.c 
b/gcc/testsuite/gcc.target/s390/usubc-3.c
new file mode 100644
index 00000000000..04a3e82c686
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/usubc-3.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -march=z13 -mzarch -save-temps -fdump-tree-optimized" }  
*/
+/* { dg-final { scan-tree-dump-times "\\.USUBC \\(\[^,\]+, \[^,\]+, 0\\)" 1 
"optimized" } } */
+/* { dg-final { scan-tree-dump-times "\\.USUBC \\(\[^,\]+, \[^,\]+, _.+\\)" 1 
"optimized" } } */
+/* { dg-final { scan-assembler-times "slg\t" 1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "alcgr\t" 1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "slbg\t" 1 { target lp64 } } } */
+/* { dg-final { scan-assembler-times "sl\t" 1 { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler-times "alcr\t" 1 { target { ! lp64 } } } } */
+/* { dg-final { scan-assembler-times "slb\t" 1 { target { ! lp64 } } } } */
+
+static unsigned long
+usubc (unsigned long x, unsigned long y, unsigned long carry_in, unsigned long 
*carry_out)
+{
+  unsigned long r;
+  unsigned long c1 = __builtin_sub_overflow (x, y, &r);
+  unsigned long c2 = __builtin_sub_overflow (r, carry_in, &r);
+  *carry_out = c1 + c2;
+  return r;
+}
+
+
+void
+test (unsigned long *p, unsigned long *q)
+{
+  unsigned long c;
+  p[0] = usubc (p[0], q[0], 0, &c);
+  p[1] = usubc (p[1], q[1], c, &c);
+}
-- 
2.47.0

Reply via email to