diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 638c82df25f..c9abbb0f91d 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -15414,7 +15414,8 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
   if (GET_MODE (x) == DImode
       && (op == EQ || op == NE)
       && GET_CODE (x) == PLUS
-      && GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+      && (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+	  || GET_CODE (XEXP (x, 1)) == SIGN_EXTEND)
       && GET_CODE (y) == SIGN_EXTEND
       && GET_CODE (XEXP (y, 0)) == PLUS)
     return CC_Vmode;
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index be002f77382..e9e0ca925d2 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -505,18 +505,173 @@ (define_expand "addvsi4"
 })
 
 (define_expand "addvdi4"
-  [(match_operand:DI 0 "register_operand")
-   (match_operand:DI 1 "register_operand")
-   (match_operand:DI 2 "register_operand")
+  [(match_operand:DI 0 "s_register_operand")
+   (match_operand:DI 1 "s_register_operand")
+   (match_operand:DI 2 "reg_or_int_operand")
    (match_operand 3 "")]
   "TARGET_32BIT"
 {
-  emit_insn (gen_adddi3_compareV (operands[0], operands[1], operands[2]));
-  arm_gen_unlikely_cbranch (NE, CC_Vmode, 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);
+
+      emit_insn (gen_addvsi4 (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_vout_0 (hi_result, hi_op1));
+      else if (CONST_INT_P (hi_op2))
+        emit_insn (gen_addsi3_cin_vout_imm (hi_result, hi_op1, hi_op2));
+      else
+        emit_insn (gen_addsi3_cin_vout_reg (hi_result, hi_op1, hi_op2));
+
+      arm_gen_unlikely_cbranch (NE, CC_Vmode, operands[3]);
+    }
 
   DONE;
 })
 
+(define_expand "addsi3_cin_vout_reg"
+  [(parallel
+    [(set (match_dup 3)
+	  (compare:CC_V
+	   (plus:DI
+	    (plus:DI (match_dup 4)
+		     (sign_extend:DI (match_operand:SI 1 "s_register_operand")))
+	    (sign_extend:DI (match_operand:SI 2 "s_register_operand")))
+	   (sign_extend:DI (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+				    (match_dup 2)))))
+     (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_Vmode, 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_vout_reg_insn"
+  [(set (reg:CC_V CC_REGNUM)
+	(compare:CC_V
+	 (plus:DI
+	  (plus:DI
+	   (match_operand:DI 3 "arm_carry_operation" "")
+	   (sign_extend:DI (match_operand:SI 1 "s_register_operand" "%0,r")))
+	  (sign_extend:DI (match_operand:SI 2 "s_register_operand" "l,r")))
+	 (sign_extend:DI
+	  (plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+			    (match_dup 1))
+		   (match_dup 2)))))
+   (set (match_operand:SI 0 "s_register_operand" "=l,r")
+	(plus:SI (plus:SI (match_dup 4) (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_vout_imm"
+  [(parallel
+    [(set (match_dup 3)
+	  (compare:CC_V
+	   (plus:DI
+	    (plus:DI (match_dup 4)
+		     (sign_extend:DI (match_operand:SI 1 "s_register_operand")))
+	    (match_dup 2))
+	   (sign_extend:DI (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+				    (match_dup 2)))))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (plus:SI (match_dup 5) (match_dup 1))
+		   (match_operand 2 "arm_adcimm_operand")))])]
+  "TARGET_32BIT"
+  {
+    operands[3] = gen_rtx_REG (CC_Vmode, 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_vout_imm_insn"
+  [(set (reg:CC_V CC_REGNUM)
+	(compare:CC_V
+	 (plus:DI
+	  (plus:DI
+	   (match_operand:DI 3 "arm_carry_operation" "")
+	   (sign_extend:DI (match_operand:SI 1 "s_register_operand" "r,r")))
+	  (match_operand 2 "arm_adcimm_operand" "I,K"))
+	 (sign_extend:DI
+	  (plus:SI (plus:SI (match_operand:SI 4 "arm_carry_operation" "")
+			    (match_dup 1))
+		   (match_dup 2)))))
+   (set (match_operand:SI 0 "s_register_operand" "=r,r")
+	(plus:SI (plus:SI (match_dup 4) (match_dup 1))
+		 (match_dup 2)))]
+  "TARGET_32BIT"
+  "@
+   adcs%?\\t%0, %1, %2
+   sbcs%?\\t%0, %1, #%B2"
+  [(set_attr "type" "alus_imm")]
+)
+
+(define_expand "addsi3_cin_vout_0"
+  [(parallel
+    [(set (match_dup 2)
+	  (compare:CC_V
+	   (plus:DI (match_dup 3)
+		    (sign_extend:DI (match_operand:SI 1 "s_register_operand")))
+	   (sign_extend:DI (plus:SI (match_dup 4) (match_dup 1)))))
+     (set (match_operand:SI 0 "s_register_operand")
+	  (plus:SI (match_dup 4) (match_dup 1)))])]
+  "TARGET_32BIT"
+  {
+    operands[2] = gen_rtx_REG (CC_Vmode, 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_vout_0_insn"
+  [(set (reg:CC_V CC_REGNUM)
+	(compare:CC_V
+	 (plus:DI
+	  (match_operand:DI 2 "arm_carry_operation" "")
+	  (sign_extend:DI (match_operand:SI 1 "s_register_operand" "r")))
+	 (sign_extend:DI (plus:SI
+			  (match_operand:SI 3 "arm_carry_operation" "")
+			  (match_dup 1)))))
+   (set (match_operand:SI 0 "s_register_operand" "=r")
+	(plus:SI (match_dup 3) (match_dup 1)))]
+  "TARGET_32BIT"
+  "adcs%?\\t%0, %1, #0"
+  [(set_attr "type" "alus_imm")]
+)
+
 (define_expand "uaddvsi4"
   [(match_operand:SI 0 "s_register_operand")
    (match_operand:SI 1 "s_register_operand")
@@ -770,22 +925,6 @@ (define_insn_and_split "*arm_addsi3"
  ]
 )
 
-(define_insn "adddi3_compareV"
-  [(set (reg:CC_V CC_REGNUM)
-	(ne:CC_V
-	  (plus:TI
-	    (sign_extend:TI (match_operand:DI 1 "s_register_operand" "r"))
-	    (sign_extend:TI (match_operand:DI 2 "s_register_operand" "r")))
-	  (sign_extend:TI (plus:DI (match_dup 1) (match_dup 2)))))
-   (set (match_operand:DI 0 "s_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_compareV_reg"
   [(set (reg:CC_V CC_REGNUM)
 	(compare:CC_V
diff --git a/gcc/testsuite/gcc.dg/builtin-arith-overflow-3.c b/gcc/testsuite/gcc.dg/builtin-arith-overflow-3.c
new file mode 100644
index 00000000000..6feae1eb42f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-arith-overflow-3.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+static int cnt = 0;
+
+#define LL_MIN ((long long)(-__LONG_LONG_MAX__ - 1))
+
+#define SC1 (LL_MIN + 5)
+#define UC1 ((1ULL << (__LONG_LONG_WIDTH__ - 1)) | 5ULL)
+#define UC2 (~UC1)
+
+long long __attribute__ ((noinline, noclone))
+f1 (long long a)
+{
+  long long x;
+  if (__builtin_add_overflow (a, SC1, &x)) cnt++;
+  return x;
+}
+
+unsigned long long __attribute__ ((noinline, noclone))
+f2 (unsigned long long a)
+{
+  unsigned long long x;
+  if (__builtin_add_overflow (a, UC1, &x))
+    cnt++;
+  return x;
+}
+
+int main ()
+{
+  if (f1 (-5) != LL_MIN) __builtin_abort ();
+  if (cnt != 0) __builtin_abort ();
+  f1 (-6);
+  if (cnt != 1) __builtin_abort ();
+  cnt = 0;
+  if (f2 (UC2) != ~0ULL) __builtin_abort ();
+  if (cnt != 0) __builtin_abort ();
+  if (f2 (UC2 + 1) != 0) __builtin_abort ();
+  if (cnt != 1) __builtin_abort ();
+  return 0;
+}
