diff mbox series

[17/29,arm] Handle some constant comparisons using rsbs+rscs

Message ID 20191018194900.34795-18-Richard.Earnshaw@arm.com
State New
Headers show
Series Rewrite DImode arithmetic support | expand

Commit Message

Richard Earnshaw (lists) Oct. 18, 2019, 7:48 p.m. UTC
In a small number of cases it is preferable to handle comparisons with
constants using the sequence

	RSBS	tmp, Xlo, constlo
	RSCS	tmp, Xhi, consthi

which allows us to handle a small number of LE/GT/LEU/GEU cases when
changing the code to use LT/GE/LTU/GEU would make the constant more
expensive.  Sadly, we cannot do this on Thumb, since we need RSC, so we
now always use the incremented constant in that case since normally that
still works out cheaper than forcing the entire constant into a register.

Further investigation has also shown that the canonicalization of a
reverse subtract and compare is valid for signed as well as unsigned value,
so we relax the restriction on selecting CC_RSBmode to allow all types
of compare.

	* config/arm/arm.c (arm_const_double_prefer_rsbs_rsc): New function.
	(arm_canonicalize_comparison): For GT/LE/GTU/GEU, use the constant
	unchanged only if that will be cheaper.
	(arm_select_cc_mode): Recognize a swapped comparison that will
	be regenerated using RSBS or RSCS.  Relax restriction on selecting
	CC_RSBmode.
	(arm_gen_dicompare_reg): Handle LE/GT/LEU/GEU comparisons against
	a constant.
	(arm_gen_compare_reg): Handle compare (CONST, X) when the mode
	is CC_RSBmode.
	(maybe_get_arm_condition_code): CC_RSBmode now returns the same codes
	as CCmode.
	* config/arm/arm.md (rsb_imm_compare_scratch): New pattern.
	(rscsi3_<CC_EXTEND>out_scratch): New pattern.
---
 gcc/config/arm/arm.c  | 153 +++++++++++++++++++++++++++++-------------
 gcc/config/arm/arm.md |  27 ++++++++
 2 files changed, 134 insertions(+), 46 deletions(-)
diff mbox series

Patch

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")