diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def
index f0eb8415b93..a6b520df32d 100644
--- a/gcc/config/arm/arm-modes.def
+++ b/gcc/config/arm/arm-modes.def
@@ -42,6 +42,9 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE)
    CC_Bmode should be used if only the C flag is correct after a subtract
      (eg after an unsigned borrow with carry-in propagation).
    (used for DImode signed comparisons).
+   CC_ADCmode is used when the carry is formed from the output of ADC for an
+     addtion.  In this case we cannot use the trick of comparing the sum
+     against one of the other operands.
    CCmode should be used otherwise.  */
 
 CC_MODE (CC_NOOV);
@@ -65,6 +68,7 @@ CC_MODE (CC_C);
 CC_MODE (CC_B);
 CC_MODE (CC_N);
 CC_MODE (CC_V);
+CC_MODE (CC_ADC);
 
 /* Vector modes.  */
 VECTOR_MODES (INT, 4);        /*            V4QI V2HI */
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 6da2a368d9f..eebbdc3d9c2 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -15387,6 +15387,14 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
       && (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y)))
     return CC_Cmode;
 
+  if (GET_MODE (x) == DImode
+      && GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 1)) == ZERO_EXTEND
+      && CONST_INT_P (y)
+      && UINTVAL (y) == 0x800000000
+      && (op == GEU || op == LTU))
+    return CC_ADCmode;
+
   if (GET_MODE (x) == DImode
       && (op == GE || op == LT)
       && GET_CODE (x) == SIGN_EXTEND
@@ -23952,6 +23960,14 @@ maybe_get_arm_condition_code (rtx comparison)
 	default: return ARM_NV;
 	}
 
+    case E_CC_ADCmode:
+      switch (comp_code)
+	{
+	case GEU: return ARM_CS;
+	case LTU: return ARM_CC;
+	default: return ARM_NV;
+	}
+
     case E_CCmode:
     case E_CC_RSBmode:
       switch (comp_code)
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 4ea6f4b226c..9f0e43571fd 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -517,16 +517,165 @@ (define_expand "uaddvsi4"
 (define_expand "uaddvdi4"
   [(match_operand:DI 0 "s_register_operand")
    (match_operand:DI 1 "s_register_operand")
-   (match_operand:DI 2 "s_register_operand")
+   (match_operand:DI 2 "reg_or_int_operand")
    (match_operand 3 "")]
   "TARGET_32BIT"
 {
-  emit_insn (gen_adddi3_compareC (operands[0], operands[1], operands[2]));
-  arm_gen_unlikely_cbranch (LTU, CC_Cmode, operands[3]);
+  rtx lo_result, hi_result;
+  rtx lo_op1, hi_op1, lo_op2, hi_op2;
+  arm_decompose_di_binop (operands[1], operands[2], &lo_op1, &hi_op1,
+			  &lo_op2, &hi_op2);
+  lo_result = gen_lowpart (SImode, operands[0]);
+  hi_result = gen_highpart (SImode, operands[0]);
+
+  if (lo_op2 == const0_rtx)
+    {
+      emit_move_insn (lo_result, lo_op1);
+      if (!arm_add_operand (hi_op2, SImode))
+	hi_op2 = force_reg (SImode, hi_op2);
+
+      gen_uaddvsi4 (hi_result, hi_op1, hi_op2, operands[3]);
+    }
+  else
+    {
+      if (!arm_add_operand (lo_op2, SImode))
+	lo_op2 = force_reg (SImode, lo_op2);
+      if (!arm_not_operand (hi_op2, SImode))
+	hi_op2 = force_reg (SImode, hi_op2);
+
+      emit_insn (gen_addsi3_compare_op1 (lo_result, lo_op1, lo_op2));
+
+      if (hi_op2 == const0_rtx)
+        emit_insn (gen_addsi3_cin_cout_0 (hi_result, hi_op1));
+      else if (CONST_INT_P (hi_op2))
+        emit_insn (gen_addsi3_cin_cout_imm (hi_result, hi_op1, hi_op2));
+      else
+        emit_insn (gen_addsi3_cin_cout_reg (hi_result, hi_op1, hi_op2));
+
+      arm_gen_unlikely_cbranch (GEU, CC_ADCmode, operands[3]);
+    }
 
   DONE;
 })
 
+(define_expand "addsi3_cin_cout_reg"
+  [(parallel
+    [(set (match_dup 3)
+	  (compare:CC_ADC
+	   (plus:DI
+	    (plus:DI (match_dup 4)
+		     (zero_extend:DI (match_operand:SI 1 "s_register_operand")))
+	    (zero_extend:DI (match_operand:SI 2 "s_register_operand")))
+	   (const_int 4294967296)))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+		   (match_dup 2)))])]
+  "TARGET_32BIT"
+  {
+    operands[3] = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+    rtx ccin = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+    operands[4] = gen_rtx_LTU (DImode, ccin, const0_rtx);
+    operands[5] = gen_rtx_LTU (SImode, ccin, const0_rtx);
+  }
+)
+
+(define_insn "*addsi3_cin_cout_reg_insn"
+  [(set (reg:CC_ADC CC_REGNUM)
+	(compare:CC_ADC
+	 (plus:DI
+	  (plus:DI
+	   (match_operand:DI 3 "arm_carry_operation" "")
+	   (zero_extend:DI (match_operand:SI 1 "s_register_operand" "%0,r")))
+	  (zero_extend:DI (match_operand:SI 2 "s_register_operand" "l,r")))
+	(const_int 4294967296)))
+   (set (match_operand:SI 0 "s_register_operand" "=l,r")
+	(plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+			  (match_dup 1))
+		 (match_dup 2)))]
+  "TARGET_32BIT"
+  "@
+   adcs%?\\t%0, %0, %2
+   adcs%?\\t%0, %1, %2"
+  [(set_attr "type" "alus_sreg")
+   (set_attr "arch" "t2,*")
+   (set_attr "length" "2,4")]
+)
+
+(define_expand "addsi3_cin_cout_imm"
+  [(parallel
+    [(set (match_dup 3)
+	  (compare:CC_ADC
+	   (plus:DI
+	    (plus:DI (match_dup 4)
+		     (zero_extend:DI (match_operand:SI 1 "s_register_operand")))
+	    (match_dup 6))
+	   (const_int 4294967296)))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+		   (match_operand:SI 2 "arm_adcimm_operand")))])]
+  "TARGET_32BIT"
+  {
+    operands[3] = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+    rtx ccin = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+    operands[4] = gen_rtx_LTU (DImode, ccin, const0_rtx);
+    operands[5] = gen_rtx_LTU (SImode, ccin, const0_rtx);
+    operands[6] = GEN_INT (UINTVAL (operands[2]) & 0xffffffff);
+  }
+)
+
+(define_insn "*addsi3_cin_cout_imm_insn"
+  [(set (reg:CC_ADC CC_REGNUM)
+	(compare:CC_ADC
+	 (plus:DI
+	  (plus:DI
+	   (match_operand:DI 3 "arm_carry_operation" "")
+	   (zero_extend:DI (match_operand:SI 1 "s_register_operand" "r,r")))
+	  (match_operand:DI 5 "const_int_operand" "n,n"))
+	(const_int 4294967296)))
+   (set (match_operand:SI 0 "s_register_operand" "=r,r")
+	(plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+			  (match_dup 1))
+		 (match_operand:SI 2 "arm_adcimm_operand" "I,K")))]
+  "TARGET_32BIT
+   && (UINTVAL (operands[2]) & 0xffffffff) == UINTVAL (operands[5])"
+  "@
+   adcs%?\\t%0, %1, %2
+   sbcs%?\\t%0, %1, #%B2"
+  [(set_attr "type" "alus_imm")]
+)
+
+(define_expand "addsi3_cin_cout_0"
+  [(parallel
+    [(set (match_dup 2)
+	  (compare:CC_ADC
+	   (plus:DI (match_dup 3)
+		    (zero_extend:DI (match_operand:SI 1 "s_register_operand")))
+	   (const_int 4294967296)))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (match_dup 4) (match_dup 1)))])]
+  "TARGET_32BIT"
+  {
+    operands[2] = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+    rtx ccin = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+    operands[3] = gen_rtx_LTU (DImode, ccin, const0_rtx);
+    operands[4] = gen_rtx_LTU (SImode, ccin, const0_rtx);
+  }
+)
+
+(define_insn "*addsi3_cin_cout_0_insn"
+  [(set (reg:CC_ADC CC_REGNUM)
+	(compare:CC_ADC
+	 (plus:DI
+	  (match_operand:DI 2 "arm_carry_operation" "")
+	  (zero_extend:DI (match_operand:SI 1 "s_register_operand" "r")))
+	(const_int 4294967296)))
+   (set (match_operand:SI 0 "s_register_operand" "=r")
+	(plus:SI (match_operand:SI 3 "arm_carry_operation" "") (match_dup 1)))]
+  "TARGET_32BIT"
+  "adcs%?\\t%0, %1, #0"
+  [(set_attr "type" "alus_imm")]
+)
+
 (define_expand "addsi3"
   [(set (match_operand:SI          0 "s_register_operand")
 	(plus:SI (match_operand:SI 1 "s_register_operand")
@@ -636,22 +785,6 @@ (define_insn "addsi3_compareV"
    (set_attr "type" "alus_sreg")]
 )
 
-(define_insn "adddi3_compareC"
-  [(set (reg:CC_C CC_REGNUM)
-	(compare:CC_C
-	  (plus:DI
-	    (match_operand:DI 1 "register_operand" "r")
-	    (match_operand:DI 2 "register_operand" "r"))
-	  (match_dup 1)))
-   (set (match_operand:DI 0 "register_operand" "=&r")
-	(plus:DI (match_dup 1) (match_dup 2)))]
-  "TARGET_32BIT"
-  "adds\\t%Q0, %Q1, %Q2;adcs\\t%R0, %R1, %R2"
- [(set_attr "conds" "set")
-   (set_attr "length" "8")
-   (set_attr "type" "multiple")]
-)
-
 (define_insn "addsi3_compare0"
   [(set (reg:CC_NOOV CC_REGNUM)
 	(compare:CC_NOOV
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index d9470df8093..8a8f10ccb50 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -376,7 +376,7 @@ (define_special_predicate "arm_carry_operation"
     machine_mode ccmode = GET_MODE (op0);
     if (ccmode == CC_Cmode)
       return GET_CODE (op) == LTU;
-    else if (ccmode == CCmode || ccmode == CC_RSBmode)
+    else if (ccmode == CCmode || ccmode == CC_RSBmode || ccmode == CC_ADCmode)
       return GET_CODE (op) == GEU;
 
     return false;
