diff mbox

[2/n] Add conditional compare support for ARM

Message ID CACgzC7DtZMXPaCdShq0MC3gaKazWDUtNjHbLfvBm3ieT1rXTxw@mail.gmail.com
State New
Headers show

Commit Message

Zhenqiang Chen Feb. 24, 2014, 9:18 a.m. UTC
Hi,

Here is the patch for ARM to generate conditional compare during
expand, which is extracted from previous discussion
(http://gcc.gnu.org/ml/gcc-patches/2014-11/msg03622.html).

Bootstrap and no make check regression on ARM Chrome book.

Is it OK for next stage1?

Thanks!
-Zhenqiang

ChangeLog:
2014-02-24  Zhenqiang Chen  <zhenqiang.chen@linaro.org>

    * config/arm/arm-protos.h (arm_select_dominance_ccmp_mode,
    arm_ccmode_to_code): New prototypes.
    * config/arm/arm.c (arm_select_dominance_cc_mode_1): New function
    extracted from arm_select_dominance_cc_mode.
    (arm_ccmode_to_code, arm_code_to_ccmode, arm_convert_to_SImode,
    arm_select_dominance_ccmp_mode): New functions.
    (arm_select_ccmp_cmp_order, arm_gen_ccmp_first, arm_gen_ccmp_next):
    New hooks.
    (arm_select_dominance_cc_mode): Call arm_select_dominance_cc_mode_1.
    * config/arm/arm.md (cbranchcc4, cstorecc4, ccmp_and, ccmp_ior): New
    instruction patterns.
diff mbox

Patch

diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 13874ee..463e3d4 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -117,6 +117,9 @@  extern bool gen_movmem_ldrd_strd (rtx *);
 extern enum machine_mode arm_select_cc_mode (RTX_CODE, rtx, rtx);
 extern enum machine_mode arm_select_dominance_cc_mode (rtx, rtx,
 						       HOST_WIDE_INT);
+extern enum machine_mode arm_select_dominance_ccmp_mode (rtx, enum machine_mode,
+							 HOST_WIDE_INT);
+enum rtx_code arm_ccmode_to_code (enum machine_mode mode);
 extern rtx arm_gen_compare_reg (RTX_CODE, rtx, rtx, rtx);
 extern rtx arm_gen_return_addr_mask (void);
 extern void arm_reload_in_hi (rtx *);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index b562986..bf0d53c 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -287,6 +287,12 @@  static unsigned arm_add_stmt_cost (void *data, int count,
 static void arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
 					 bool op0_preserve_value);
 static unsigned HOST_WIDE_INT arm_asan_shadow_offset (void);
+static int arm_select_ccmp_cmp_order (int, int);
+static rtx arm_gen_ccmp_first (int, rtx, rtx);
+static rtx arm_gen_ccmp_next (rtx, int, rtx, rtx, int);
+static enum machine_mode arm_select_dominance_cc_mode_1 (enum rtx_code cond1,
+							 enum rtx_code cond2,
+							 HOST_WIDE_INT);
 
 /* Table of machine attributes.  */
 static const struct attribute_spec arm_attribute_table[] =
@@ -675,6 +681,15 @@  static const struct attribute_spec arm_attribute_table[] =
 #undef TARGET_CAN_USE_DOLOOP_P
 #define TARGET_CAN_USE_DOLOOP_P can_use_doloop_if_innermost
 
+#undef TARGET_SELECT_CCMP_CMP_ORDER
+#define TARGET_SELECT_CCMP_CMP_ORDER arm_select_ccmp_cmp_order
+
+#undef TARGET_GEN_CCMP_FIRST
+#define TARGET_GEN_CCMP_FIRST arm_gen_ccmp_first
+
+#undef TARGET_GEN_CCMP_NEXT
+#define TARGET_GEN_CCMP_NEXT arm_gen_ccmp_next
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 /* Obstack for minipool constant handling.  */
@@ -937,7 +952,6 @@  struct processors
   const struct tune_params *const tune;
 };
 
-
 #define ARM_PREFETCH_NOT_BENEFICIAL 0, -1, -1
 #define ARM_PREFETCH_BENEFICIAL(prefetch_slots,l1_size,l1_line_size) \
   prefetch_slots, \
@@ -14554,7 +14568,13 @@  arm_select_dominance_cc_mode (rtx x, rtx y, HOST_WIDE_INT cond_or)
       cond1 = cond2;
       cond2 = temp;
     }
+  return arm_select_dominance_cc_mode_1 (cond1, cond2, cond_or);
+}
 
+static enum machine_mode
+arm_select_dominance_cc_mode_1 (enum rtx_code cond1, enum rtx_code cond2,
+				HOST_WIDE_INT cond_or)
+{
   switch (cond1)
     {
     case EQ:
@@ -14635,8 +14655,7 @@  arm_select_dominance_cc_mode (rtx x, rtx y, HOST_WIDE_INT cond_or)
 	  gcc_unreachable ();
 	}
 
-    /* The remaining cases only occur when both comparisons are the
-       same.  */
+    /* The remaining cases only occur when both comparisons are the same.  */
     case NE:
       gcc_assert (cond1 == cond2);
       return CC_DNEmode;
@@ -14662,6 +14681,198 @@  arm_select_dominance_cc_mode (rtx x, rtx y, HOST_WIDE_INT cond_or)
     }
 }
 
+enum rtx_code
+arm_ccmode_to_code (enum machine_mode mode)
+{
+  switch (mode)
+    {
+    case CC_DNEmode:
+      return NE;
+    case CC_DEQmode:
+      return EQ;
+    case CC_DLEmode:
+      return LE;
+    case CC_DLTmode:
+      return LT;
+    case CC_DGEmode:
+      return GE;
+    case CC_DGTmode:
+      return GT;
+    case CC_DLEUmode:
+      return LEU;
+    case CC_DLTUmode:
+      return LTU;
+    case CC_DGEUmode:
+      return GEU;
+    case CC_DGTUmode:
+      return GTU;
+    default:
+      return UNKNOWN;
+    }
+}
+
+static enum machine_mode
+arm_code_to_ccmode (enum rtx_code code)
+{
+  switch (code)
+    {
+    case NE:
+      return CC_DNEmode;
+    case EQ:
+      return CC_DEQmode;
+    case LE:
+      return CC_DLEmode;
+    case LT:
+      return CC_DLTmode;
+    case GE:
+      return CC_DGEmode;
+    case GT:
+      return CC_DGTmode;
+    case LEU:
+      return CC_DLEUmode;
+    case LTU:
+      return CC_DLTUmode;
+    case GEU:
+      return CC_DGEUmode;
+    case GTU:
+      return CC_DGTUmode;
+    default:
+      return CCmode;
+    }
+}
+
+/* MODE is the CC mode result of the previous conditional compare.
+   X is the next compare.  */
+enum machine_mode
+arm_select_dominance_ccmp_mode (rtx x, enum machine_mode mode,
+			 	HOST_WIDE_INT cond_or)
+{
+  enum rtx_code cond1 = arm_ccmode_to_code (mode);
+  enum rtx_code cond2;
+
+  if (cond1 == UNKNOWN)
+    return CCmode;
+
+  /* Currently we will probably get the wrong result if the individual
+     comparisons are not simple.  */
+  if (arm_select_cc_mode (cond2 = GET_CODE (x), XEXP (x, 0), XEXP (x, 1))
+      != CCmode)
+    return CCmode;
+
+  /* If the comparisons are not equal, and one doesn't dominate the other,
+     then we can't do this.  Since there is a conditional compare before
+     current insn, we can not swap the compares.  So we have to check the
+     dominate relation separately for DOM_CC_X_OR_Y and DOM_CC_X_AND_Y.  */
+  if (cond1 != cond2
+      && !(cond_or == DOM_CC_X_OR_Y ? comparison_dominates_p (cond1, cond2)
+				      : comparison_dominates_p (cond2, cond1)))
+    return CCmode;
+
+  if (cond_or == DOM_CC_X_OR_Y)
+    return arm_select_dominance_cc_mode_1 (cond1, cond2, cond_or);
+  else
+    return arm_select_dominance_cc_mode_1 (cond2, cond1, cond_or);
+}
+
+/* COND1 and COND2 should be enum rtx_code, which represent two compares.
+   There are order sensitive for conditional compare.  It returns
+      1: Keep current order.
+     -1: Swap the two compares.
+      0: Invalid combination.  */
+
+static int
+arm_select_ccmp_cmp_order (int cond1, int cond2)
+{
+  /* THUMB1 does not support conditional compare.  */
+  if (TARGET_THUMB1)
+    return 0;
+
+  if (cond1 == cond2)
+    return 1;
+  if (comparison_dominates_p ((enum rtx_code) cond1, (enum rtx_code) cond2))
+    return -1;
+  if (comparison_dominates_p ((enum rtx_code) cond2, (enum rtx_code) cond1))
+    return 1;
+
+  return 0;
+}
+
+/* Conver QImode or HImode OP0 and OP1 to SImode.  If the final OP0 and OP1
+   are not SImode, return FALSE.  */
+static bool
+arm_convert_to_SImode (rtx* op0, rtx* op1, int unsignedp)
+{
+  enum machine_mode mode;
+
+  mode = GET_MODE (*op0);
+  if (mode == VOIDmode)
+    mode = GET_MODE (*op1);
+
+  if (mode == QImode || mode == HImode)
+    {
+      *op0 = convert_modes (SImode, mode, *op0, unsignedp);
+      *op1 = convert_modes (SImode, mode, *op1, unsignedp);
+    }
+  else if (mode != SImode)
+    return false;
+
+  return true;
+}
+
+static rtx
+arm_gen_ccmp_first (int code, rtx op0, rtx op1)
+{
+  enum machine_mode mode;
+  rtx cmp, target;
+  int unsignedp = code == LTU || code == LEU || code == GTU || code == GEU;
+
+  if (!arm_convert_to_SImode (&op0, &op1, unsignedp)
+      || !s_register_operand (op0, SImode)
+      || !arm_add_operand (op1, SImode))
+    return NULL_RTX;
+
+  mode = arm_code_to_ccmode ((enum rtx_code) code);
+  if (mode == CCmode)
+    return NULL_RTX;
+
+  cmp = gen_rtx_fmt_ee (COMPARE, CCmode, op0, op1);
+  target = gen_rtx_REG (mode, CC_REGNUM);
+  emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM), cmp));
+  return target;
+}
+
+static rtx
+arm_gen_ccmp_next (rtx prev, int cmp_code, rtx op0, rtx op1, int bit_code)
+{
+  rtx cmp0, cmp1, target, bit_op;
+  HOST_WIDE_INT cond_or;
+  enum machine_mode mode;
+  int unsignedp = cmp_code == LTU || cmp_code == LEU
+		  || cmp_code == GTU || cmp_code == GEU;
+
+  if (!arm_convert_to_SImode (&op0, &op1, unsignedp)
+      || !s_register_operand (op0, SImode)
+      || !arm_add_operand (op1, SImode))
+    return NULL_RTX;
+
+  cmp1 = gen_rtx_fmt_ee ((enum rtx_code) cmp_code, SImode, op0, op1);
+  cond_or = bit_code == AND ? DOM_CC_X_AND_Y : DOM_CC_X_OR_Y;
+  mode = arm_select_dominance_ccmp_mode (cmp1, GET_MODE (prev), cond_or);
+  if (mode == CCmode)
+    return NULL_RTX;
+
+  cmp0 = gen_rtx_fmt_ee (NE, SImode, prev, const0_rtx);
+
+  bit_op = gen_rtx_fmt_ee ((enum rtx_code) bit_code, SImode, cmp0, cmp1);
+
+  /* Generate insn to match ccmp_and/ccmp_ior.  */
+  target = gen_rtx_REG (mode, CC_REGNUM);
+  emit_insn (gen_rtx_SET (VOIDmode, target,
+			  gen_rtx_fmt_ee (COMPARE, VOIDmode,
+					  bit_op, const0_rtx)));
+  return target;
+}
+
 enum machine_mode
 arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
 {
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index 2ddda02..ef55bd3 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -12926,3 +12926,122 @@ 
 (include "sync.md")
 ;; Fixed-point patterns
 (include "arm-fixed.md")
+
+(define_expand "cbranchcc4"
+  [(set (pc) (if_then_else
+	      (match_operator 0 "expandable_comparison_operator"
+	       [(match_operand 1 "dominant_cc_register" "")
+		(const_int 0)])
+	      (label_ref (match_operand 3 "" ""))
+	      (pc)))]
+  "TARGET_32BIT"
+  " ")
+
+(define_expand "cstorecc4"
+  [(set (match_operand:SI 0 "s_register_operand")
+	(match_operator 1 "" [(match_operand 2 "")
+			      (match_operand 3 "")]))]
+  "TARGET_32BIT"
+"{
+  enum machine_mode mode = GET_MODE (operands[2]);
+  if (mode != CCmode)
+    {
+      operands[2] = gen_rtx_REG (CCmode, CC_REGNUM);
+      operands[3] = const0_rtx;
+      operands[1] = gen_rtx_fmt_ee (arm_ccmode_to_code (mode),
+				    SImode, operands[2], operands[3]);
+    }
+  emit_insn (gen_rtx_SET (SImode, operands[0], operands[1]));
+  DONE;
+}")
+
+;; The first compare in this pattern is the result of a previous CCMP.
+;; We can not swap it.  And we only need its flag.
+(define_insn "*ccmp_and"
+  [(set (match_operand 6 "dominant_cc_register" "")
+	(compare
+	 (and:SI
+	  (match_operator 4 "expandable_comparison_operator"
+	   [(match_operand 0 "dominant_cc_register" "")
+	    (match_operand:SI 1 "arm_add_operand" "")])
+	  (match_operator:SI 5 "arm_comparison_operator"
+	   [(match_operand:SI 2 "s_register_operand"
+	        "l,r,r,r,r")
+	    (match_operand:SI 3 "arm_add_operand"
+	        "lPy,rI,L,rI,L")]))
+	 (const_int 0)))]
+  "TARGET_32BIT"
+  {
+    static const char *const cmp2[2] =
+    {
+      "cmp%d4\t%2, %3",
+      "cmn%d4\t%2, #%n3"
+    };
+    static const char *const ite = "it\t%d4";
+    static const int cmp_idx[5] = {0, 0, 1, 0, 1};
+
+    if (TARGET_THUMB2)
+      output_asm_insn (ite, operands);
+
+    output_asm_insn (cmp2[cmp_idx[which_alternative]], operands);
+    return "";
+  }
+  [(set_attr "conds" "set")
+   (set_attr "predicable" "no")
+   (set_attr "arch" "t2,t2,t2,any,any")
+   (set_attr_alternative "length"
+      [(const_int 4)
+       (const_int 6)
+       (const_int 6)
+       (if_then_else (eq_attr "is_thumb" "no")
+           (const_int 4)
+           (const_int 6))
+       (if_then_else (eq_attr "is_thumb" "no")
+           (const_int 4)
+           (const_int 6))])]
+)
+
+;; The first compare in this pattern is the result of a previous CCMP.
+;; We can not swap it.  And we only need its flag.
+(define_insn "*ccmp_ior"
+  [(set (match_operand 6 "dominant_cc_register" "")
+	(compare
+	 (ior:SI
+	  (match_operator 4 "expandable_comparison_operator"
+	   [(match_operand 0 "dominant_cc_register" "")
+	    (match_operand:SI 1 "arm_add_operand" "")])
+	  (match_operator:SI 5 "arm_comparison_operator"
+	   [(match_operand:SI 2 "s_register_operand"
+	        "l,r,r,r,r")
+	    (match_operand:SI 3 "arm_add_operand"
+	        "lPy,rI,L,rI,L")]))
+	 (const_int 0)))]
+  "TARGET_32BIT"
+  {
+    static const char *const cmp2[2] =
+    {
+      "cmp%D4\t%2, %3",
+      "cmn%D4\t%2, #%n3"
+    };
+    static const char *const ite = "it\t%D4";
+    static const int cmp_idx[5] = {0, 0, 1, 0, 1};
+
+    if (TARGET_THUMB2)
+      output_asm_insn (ite, operands);
+
+    output_asm_insn (cmp2[cmp_idx[which_alternative]], operands);
+    return "";
+  }
+  [(set_attr "conds" "set")
+   (set_attr "arch" "t2,t2,t2,any,any")
+   (set_attr_alternative "length"
+      [(const_int 4)
+       (const_int 6)
+       (const_int 6)
+       (if_then_else (eq_attr "is_thumb" "no")
+           (const_int 4)
+           (const_int 6))
+       (if_then_else (eq_attr "is_thumb" "no")
+           (const_int 4)
+           (const_int 6))])]
+)