diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 99c8bd79d30..299dce638c2 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -5355,6 +5355,21 @@ arm_gen_constant (enum rtx_code code, machine_mode mode, rtx cond,
   return insns;
 }
 
+/* Return TRUE if op is a constant where both the low and top words are
+   suitable for RSB/RSC instructions.  This is never true for Thumb, since
+   we do not have RSC in that case.  */
+static bool
+arm_const_double_prefer_rsbs_rsc (rtx op)
+{
+  /* Thumb lacks RSC, so we never prefer that sequence.  */
+  if (TARGET_THUMB || !CONST_INT_P (op))
+    return false;
+  HOST_WIDE_INT hi, lo;
+  lo = UINTVAL (op) & 0xffffffffULL;
+  hi = UINTVAL (op) >> 32;
+  return const_ok_for_arm (lo) && const_ok_for_arm (hi);
+}
+
 /* Canonicalize a comparison so that we are more likely to recognize it.
    This can be done for a few constant compares, where we can make the
    immediate value easier to load.  */
@@ -5380,8 +5395,7 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
     {
 
       if (*code == GT || *code == LE
-	  || ((!TARGET_ARM || CONST_INT_P (*op1))
-	      && (*code == GTU || *code == LEU)))
+	  || *code == GTU || *code == LEU)
 	{
 	  /* Missing comparison.  First try to use an available
 	     comparison.  */
@@ -5392,10 +5406,13 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
 		{
 		case GT:
 		case LE:
-		  if (i != maxval
-		      && (!arm_const_double_by_immediates (*op1)
-			  || arm_const_double_by_immediates (GEN_INT (i + 1))))
+		  if (i != maxval)
 		    {
+		      /* Try to convert to GE/LT, unless that would be more
+			 expensive.  */
+		      if (!arm_const_double_by_immediates (GEN_INT (i + 1))
+			  && arm_const_double_prefer_rsbs_rsc (*op1))
+			return;
 		      *op1 = GEN_INT (i + 1);
 		      *code = *code == GT ? GE : LT;
 		      return;
@@ -5404,10 +5421,13 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
 
 		case GTU:
 		case LEU:
-		  if (i != ~((unsigned HOST_WIDE_INT) 0)
-		      && (!arm_const_double_by_immediates (*op1)
-			  || arm_const_double_by_immediates (GEN_INT (i + 1))))
+		  if (i != ~((unsigned HOST_WIDE_INT) 0))
 		    {
+		      /* Try to convert to GEU/LTU, unless that would
+			 be more expensive.  */
+		      if (!arm_const_double_by_immediates (GEN_INT (i + 1))
+			  && arm_const_double_prefer_rsbs_rsc (*op1))
+			return;
 		      *op1 = GEN_INT (i + 1);
 		      *code = *code == GTU ? GEU : LTU;
 		      return;
@@ -5419,7 +5439,6 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
 		}
 	    }
 
-	  /* If that did not work, reverse the condition.  */
 	  if (!op0_preserve_value)
 	    {
 	      std::swap (*op0, *op1);
@@ -15251,6 +15270,28 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
 	  || GET_CODE (x) == ROTATERT))
     return CC_SWPmode;
 
+  /* A widened compare of the sum of a value plus a carry against a
+     constant.  This is a representation of RSC.  We want to swap the
+     result of the comparison at output.  Not valid if the Z bit is
+     needed.  */
+  if (GET_MODE (x) == DImode
+      && GET_CODE (x) == PLUS
+      && arm_borrow_operation (XEXP (x, 1), DImode)
+      && CONST_INT_P (y)
+      && ((GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+	   && (op == LE || op == GT))
+	  || (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+	      && (op == LEU || op == GTU))))
+    return CC_SWPmode;
+
+  /* If X is a constant we want to use CC_RSBmode.  This is
+     non-canonical, but arm_gen_compare_reg uses this to generate the
+     correct canonical form.  */
+  if (GET_MODE (y) == SImode
+      && (REG_P (y) || GET_CODE (y) == SUBREG)
+      && CONST_INT_P (x))
+    return CC_RSBmode;
+
   /* This operation is performed swapped, but since we only rely on the Z
      flag we don't need an additional mode.  */
   if (GET_MODE (y) == SImode
@@ -15329,15 +15370,13 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
 	  || (TARGET_32BIT && GET_CODE (x) == ZERO_EXTRACT)))
     return CC_NOOVmode;
 
-  /* An unsigned comparison of ~reg with a const is really a special
+  /* A comparison of ~reg with a const is really a special
      canoncialization of compare (~const, reg), which is a reverse
      subtract operation.  We may not get here if CONST is 0, but that
      doesn't matter because ~0 isn't a valid immediate for RSB.  */
   if (GET_MODE (x) == SImode
       && GET_CODE (x) == NOT
-      && CONST_INT_P (y)
-      && (op == EQ || op == NE
-	  || op == LTU || op == LEU || op == GEU || op == GTU))
+      && CONST_INT_P (y))
     return CC_RSBmode;
 
   if (GET_MODE (x) == QImode && (op == EQ || op == NE))
@@ -15431,6 +15470,7 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 
     /* We don't currently handle DImode in thumb1, but rely on libgcc.  */
   gcc_assert (TARGET_32BIT);
+  gcc_assert (!CONST_INT_P (x));
 
   rtx x_lo = simplify_gen_subreg (SImode, x, DImode,
 				  subreg_lowpart_offset (SImode, DImode));
@@ -15445,9 +15485,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
     case EQ:
     case NE:
       {
-	/* We should never have X as a const_int in this case.  */
-	gcc_assert (!CONST_INT_P (x));
-
 	if (y_lo == const0_rtx || y_hi == const0_rtx)
 	  {
 	    if (y_lo != const0_rtx)
@@ -15525,10 +15562,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 	if (!arm_add_operand (y_lo, SImode))
 	  y_lo = force_reg (SImode, y_lo);
 
-	/* Just for now.  */
-	if (!register_operand (x_lo, SImode))
-	  x_lo = force_reg (SImode, x_lo);
-
 	rtx cmp1
 	  = gen_rtx_LTU (DImode,
 			 arm_gen_compare_reg (LTU, x_lo, y_lo, NULL_RTX),
@@ -15536,13 +15569,10 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 
 	if (!scratch)
 	  scratch = gen_rtx_SCRATCH (SImode);
+
 	if (!arm_not_operand (y_hi, SImode))
 	  y_hi = force_reg (SImode, y_hi);
 
-	/* Just for now.  */
-	if (!register_operand (x_hi, SImode))
-	  x_hi = force_reg (SImode, x_hi);
-
 	rtx_insn *insn;
 	if (y_hi == const0_rtx)
 	  insn = emit_insn (gen_cmpsi3_0_carryin_CC_NVout (scratch, x_hi,
@@ -15556,6 +15586,27 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 	return SET_DEST (single_set (insn));
       }
 
+    case LE:
+    case GT:
+      {
+	/* During expansion, we only expect to get here if y is a
+	   constant that we want to handle, otherwise we should have
+	   swapped the operands already.  */
+	gcc_assert (arm_const_double_prefer_rsbs_rsc (y));
+
+	if (!const_ok_for_arm (INTVAL (y_lo)))
+	  y_lo = force_reg (SImode, y_lo);
+
+	/* Perform a reverse subtract and compare.  */
+	rtx cmp1
+	  = gen_rtx_LTU (DImode,
+			 arm_gen_compare_reg (LTU, y_lo, x_lo, scratch),
+			 const0_rtx);
+	rtx_insn *insn = emit_insn (gen_rscsi3_CC_NVout_scratch (scratch, y_hi,
+								 x_hi, cmp1));
+	return SET_DEST (single_set (insn));
+      }
+
     case LTU:
     case GEU:
       {
@@ -15572,10 +15623,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 	if (!arm_add_operand (y_lo, SImode))
 	  y_lo = force_reg (SImode, y_lo);
 
-	/* Just for now.  */
-	if (!register_operand (x_lo, SImode))
-	  x_lo = force_reg (SImode, x_lo);
-
 	rtx cmp1
 	  = gen_rtx_LTU (DImode,
 			 arm_gen_compare_reg (LTU, x_lo, y_lo, NULL_RTX),
@@ -15586,10 +15633,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 	if (!arm_not_operand (y_hi, SImode))
 	  y_hi = force_reg (SImode, y_hi);
 
-	/* Just for now.  */
-	if (!register_operand (x_hi, SImode))
-	  x_hi = force_reg (SImode, x_hi);
-
 	rtx_insn *insn;
 	if (y_hi == const0_rtx)
 	  insn = emit_insn (gen_cmpsi3_0_carryin_CC_Bout (scratch, x_hi,
@@ -15607,6 +15650,28 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 	return SET_DEST (single_set (insn));
       }
 
+    case LEU:
+    case GTU:
+      {
+	/* During expansion, we only expect to get here if y is a
+	   constant that we want to handle, otherwise we should have
+	   swapped the operands already.  */
+	gcc_assert (arm_const_double_prefer_rsbs_rsc (y));
+
+	if (!const_ok_for_arm (INTVAL (y_lo)))
+	  y_lo = force_reg (SImode, y_lo);
+
+	/* Perform a reverse subtract and compare.  */
+	rtx cmp1
+	  = gen_rtx_LTU (DImode,
+			 arm_gen_compare_reg (LTU, y_lo, x_lo, scratch),
+			 const0_rtx);
+	y_hi = GEN_INT (0xffffffff & UINTVAL (y_hi));
+	rtx_insn *insn = emit_insn (gen_rscsi3_CC_Bout_scratch (scratch, y_hi,
+								x_hi, cmp1));
+	return SET_DEST (single_set (insn));
+      }
+
     default:
       break;
     }
@@ -15695,8 +15760,15 @@ arm_gen_compare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
 
   machine_mode mode = SELECT_CC_MODE (code, x, y);
   rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);
-
-  emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));
+  if (mode == CC_RSBmode)
+    {
+      if (!scratch)
+	scratch = gen_rtx_SCRATCH (SImode);
+      emit_insn (gen_rsb_imm_compare_scratch (scratch,
+					      GEN_INT (~UINTVAL (x)), y));
+    }
+  else
+    emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));
 
   return cc_reg;
 }
@@ -24025,19 +24097,8 @@ maybe_get_arm_condition_code (rtx comparison)
 	default: return ARM_NV;
 	}
 
-    case E_CC_RSBmode:
-      switch (comp_code)
-	{
-	case NE: return ARM_NE;
-	case EQ: return ARM_EQ;
-	case GEU: return ARM_CS;
-	case GTU: return ARM_HI;
-	case LEU: return ARM_LS;
-	case LTU: return ARM_CC;
-	default: return ARM_NV;
-	}
-
     case E_CCmode:
+    case E_CC_RSBmode:
       switch (comp_code)
 	{
 	case NE: return ARM_NE;
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index f0ff4dda396..8607c6f95da 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -1363,6 +1363,33 @@ (define_insn "rsb_imm_compare"
    (set_attr "type" "alus_imm")]
 )
 
+;; Similarly, but the result is unused.
+(define_insn "rsb_imm_compare_scratch"
+  [(set (reg:CC_RSB CC_REGNUM)
+	(compare:CC_RSB (not:SI (match_operand:SI 2 "s_register_operand" "r"))
+			(match_operand 1 "arm_not_immediate_operand" "K")))
+   (clobber (match_scratch:SI 0 "=r"))]
+  "TARGET_32BIT"
+  "rsbs\\t%0, %2, #%B1"
+  [(set_attr "conds" "set")
+   (set_attr "type" "alus_imm")]
+)
+
+;; Compare the sum of a value plus a carry against a constant.  Uses
+;; RSC, so the result is swapped.  Only available on Arm
+(define_insn "rscsi3_<CC_EXTEND>out_scratch"
+  [(set (reg:CC_SWP CC_REGNUM)
+	(compare:CC_SWP
+	 (plus:DI (SE:DI (match_operand:SI 2 "s_register_operand" "r"))
+		  (match_operand:DI 3 "arm_borrow_operation" ""))
+	 (match_operand 1 "arm_immediate_operand" "I")))
+   (clobber (match_scratch:SI 0 "=r"))]
+  "TARGET_ARM"
+  "rscs\\t%0, %2, %1"
+  [(set_attr "conds" "set")
+   (set_attr "type" "alus_imm")]
+)
+
 (define_expand "subsf3"
   [(set (match_operand:SF           0 "s_register_operand")
 	(minus:SF (match_operand:SF 1 "s_register_operand")
