diff mbox

[3/n] Add conditional compare support for AARCH64

Message ID CACgzC7A=hQ-Pcz88vXRF+orm_OWm-kKaj3C=7AeBEiJrwk=Hcw@mail.gmail.com
State New
Headers show

Commit Message

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

Here is the patch to generate conditional compare instructions
(CCMP/CCMN) for aarch64 port.

No make check regression in qemu-aarch64 tests.

Is it OK for next stage1?

When the patch is OK, I will create another patch to support FCCMP/FCCMN.

Thanks!
-Zhenqiang

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

    * config/aarch64/aarch64-modes.def (CC_DNE, CC_DEQ, CC_DLE, CC_DLT,
    CC_DGE, CC_DGT, CC_DLEU, CC_DLTU, CC_DGEU, CC_DGTU): New modes.
    * config/aarch64/aarch64-protos.h (aarch64_uimm5, aarch64_output_ccmp):
    New prototypes.
    * config/aarch64/aarch64.c (aarch64_get_condition_code_1): New function
    extracted from aarch64_get_condition_code and hand ccmp.
    (aarch64_code_to_ccmode, aarch64_convert_mode, aarch64_code_to_nzcv
    aarch64_mode_to_condition_code, aarch64_output_ccmp, aarch64_uimm5):
    New functions.
    (aarch64_gen_ccmp_first, aarch64_gen_ccmp_next): New hooks.
    (aarch64_get_condition_code): Call aarch64_get_condition_code_1.
    (AARCH64_CC_V, AARCH64_CC_C, AARCH64_CC_Z, AARCH64_CC_N): New MICROs.
    * config/aarch64/aarch64.md (cbranchcc4, ccmp_and, ccmp_ior): New
    instruction patterns.
    * config/aarch64/constraints.md (Usn): New constrain.
    * config/aarch64/predicates.md (ccmp_cc_register, aarch64_ccmp_operand,
    aarch64_ccmp_immediate): New predicates.
diff mbox

Patch

diff --git a/gcc/config/aarch64/aarch64-modes.def b/gcc/config/aarch64/aarch64-modes.def
index 1d2cc76..71fd2f0 100644
--- a/gcc/config/aarch64/aarch64-modes.def
+++ b/gcc/config/aarch64/aarch64-modes.def
@@ -25,6 +25,16 @@  CC_MODE (CC_ZESWP); /* zero-extend LHS (but swap to make it RHS).  */
 CC_MODE (CC_SESWP); /* sign-extend LHS (but swap to make it RHS).  */
 CC_MODE (CC_NZ);    /* Only N and Z bits of condition flags are valid.  */
 CC_MODE (CC_Z);     /* Only Z bit of condition flags is valid.  */
+CC_MODE (CC_DNE);
+CC_MODE (CC_DEQ);
+CC_MODE (CC_DLE);
+CC_MODE (CC_DLT);
+CC_MODE (CC_DGE);
+CC_MODE (CC_DGT);
+CC_MODE (CC_DLEU);
+CC_MODE (CC_DLTU);
+CC_MODE (CC_DGEU);
+CC_MODE (CC_DGTU);
 
 /* Vector modes.  */
 VECTOR_MODES (INT, 8);        /*       V8QI V4HI V2SI.  */
diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h
index 5542f02..48b4c09 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -228,6 +228,9 @@  void aarch64_init_expanders (void);
 void aarch64_print_operand (FILE *, rtx, char);
 void aarch64_print_operand_address (FILE *, rtx);
 
+bool aarch64_uimm5 (HOST_WIDE_INT);
+const char* aarch64_output_ccmp (rtx *, bool, int);
+
 /* Initialize builtins for SIMD intrinsics.  */
 void init_aarch64_simd_builtins (void);
 
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index ea90311..c154d02 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -3361,14 +3361,8 @@  aarch64_select_cc_mode (RTX_CODE code, rtx x, rtx y)
 }
 
 static unsigned
-aarch64_get_condition_code (rtx x)
+aarch64_get_condition_code_1 (enum machine_mode mode, enum rtx_code comp_code)
 {
-  enum machine_mode mode = GET_MODE (XEXP (x, 0));
-  enum rtx_code comp_code = GET_CODE (x);
-
-  if (GET_MODE_CLASS (mode) != MODE_CC)
-    mode = SELECT_CC_MODE (comp_code, XEXP (x, 0), XEXP (x, 1));
-
   switch (mode)
     {
     case CCFPmode:
@@ -3391,6 +3385,27 @@  aarch64_get_condition_code (rtx x)
 	}
       break;
 
+    case CC_DNEmode:
+      return comp_code == NE ? AARCH64_NE : AARCH64_EQ;
+    case CC_DEQmode:
+      return comp_code == NE ? AARCH64_EQ : AARCH64_NE;
+    case CC_DGEmode:
+      return comp_code == NE ? AARCH64_GE : AARCH64_LT;
+    case CC_DLTmode:
+      return comp_code == NE ? AARCH64_LT : AARCH64_GE;
+    case CC_DGTmode:
+      return comp_code == NE ? AARCH64_GT : AARCH64_LE;
+    case CC_DLEmode:
+      return comp_code == NE ? AARCH64_LE : AARCH64_GT;
+    case CC_DGEUmode:
+      return comp_code == NE ? AARCH64_CS : AARCH64_CC;
+    case CC_DLTUmode:
+      return comp_code == NE ? AARCH64_CC : AARCH64_CS;
+    case CC_DGTUmode:
+      return comp_code == NE ? AARCH64_HI : AARCH64_LS;
+    case CC_DLEUmode:
+      return comp_code == NE ? AARCH64_LS : AARCH64_HI;
+
     case CCmode:
       switch (comp_code)
 	{
@@ -3454,6 +3469,20 @@  aarch64_get_condition_code (rtx x)
 }
 
 static unsigned
+aarch64_get_condition_code (rtx x)
+{
+  enum machine_mode mode = GET_MODE (XEXP (x, 0));
+  enum rtx_code comp_code = GET_CODE (x);
+
+  gcc_assert (!ccmp_cc_register (x, mode)
+	      || (comp_code == NE || comp_code == EQ));
+
+  if (GET_MODE_CLASS (mode) != MODE_CC)
+    mode = SELECT_CC_MODE (comp_code, XEXP (x, 0), XEXP (x, 1));
+  return aarch64_get_condition_code_1 (mode, comp_code);
+}
+
+static unsigned
 bit_count (unsigned HOST_WIDE_INT value)
 {
   unsigned count = 0;
@@ -8304,6 +8333,245 @@  aarch64_cannot_change_mode_class (enum machine_mode from,
   return true;
 }
 
+static enum machine_mode
+aarch64_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;
+    }
+}
+/* N Z C V.  */
+#define AARCH64_CC_V 1
+#define AARCH64_CC_C (1 << 1)
+#define AARCH64_CC_Z (1 << 2)
+#define AARCH64_CC_N (1 << 3)
+
+static unsigned int
+aarch64_code_to_nzcv (enum rtx_code code, bool inverse)
+{
+  switch (code)
+    {
+    case NE: /* NE, Z == 0.  */
+      return inverse ? AARCH64_CC_Z : 0;
+    case EQ: /* EQ, Z == 1.  */
+      return inverse ? 0 : AARCH64_CC_Z;
+    case LE: /* LE, !(Z == 0 && N == V).  */
+      return inverse ? AARCH64_CC_N | AARCH64_CC_V : AARCH64_CC_Z;
+    case GT: /* GT, Z == 0 && N == V.  */
+      return inverse ? AARCH64_CC_Z : AARCH64_CC_N | AARCH64_CC_V;
+    case LT: /* LT, N != V.  */
+      return inverse ? AARCH64_CC_N | AARCH64_CC_V : AARCH64_CC_N;
+    case GE: /* GE, N == V.  */
+      return inverse ? AARCH64_CC_N : AARCH64_CC_N | AARCH64_CC_V;
+    case LEU: /* LS, !(C == 1 && Z == 0).  */
+      return inverse ? AARCH64_CC_C: AARCH64_CC_Z;
+    case GTU: /* HI, C ==1 && Z == 0.  */
+      return inverse ? AARCH64_CC_Z : AARCH64_CC_C;
+    case LTU: /* CC, C == 0.  */
+      return inverse ? AARCH64_CC_C : 0;
+    case GEU: /* CS, C == 1.  */
+      return inverse ? 0 : AARCH64_CC_C;
+    default:
+      gcc_unreachable ();
+      return 0;
+    }
+}
+
+static unsigned
+aarch64_mode_to_condition_code (enum machine_mode mode, bool inverse)
+{
+  switch (mode)
+    {
+    case CC_DNEmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, EQ)
+		       : aarch64_get_condition_code_1 (CCmode, NE);
+    case CC_DEQmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, NE)
+		       : aarch64_get_condition_code_1 (CCmode, EQ);
+    case CC_DLEmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, GT)
+		       : aarch64_get_condition_code_1 (CCmode, LE);
+    case CC_DGTmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, LE)
+		       : aarch64_get_condition_code_1 (CCmode, GT);
+    case CC_DLTmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, GE)
+		       : aarch64_get_condition_code_1 (CCmode, LT);
+    case CC_DGEmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, LT)
+		       : aarch64_get_condition_code_1 (CCmode, GE);
+    case CC_DLEUmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, GTU)
+		       : aarch64_get_condition_code_1 (CCmode, LEU);
+    case CC_DGTUmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, LEU)
+		       : aarch64_get_condition_code_1 (CCmode, GTU);
+    case CC_DLTUmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, GEU)
+		       : aarch64_get_condition_code_1 (CCmode, LTU);
+    case CC_DGEUmode:
+      return inverse ? aarch64_get_condition_code_1 (CCmode, LTU)
+		       : aarch64_get_condition_code_1 (CCmode, GEU);
+    default:
+      gcc_unreachable ();
+    }
+}
+
+const char *
+aarch64_output_ccmp (rtx *operands, bool is_and, int which_alternative)
+{
+  char buf[32];
+  rtx cc = operands[0];
+  enum rtx_code code = GET_CODE (operands[5]);
+  unsigned char nzcv = aarch64_code_to_nzcv (code, is_and);
+  enum machine_mode mode = GET_MODE (cc);
+  unsigned int cond_code = aarch64_mode_to_condition_code (mode, !is_and);
+
+  if (GET_MODE (operands[2]) == SImode)
+    switch (which_alternative)
+      {
+      case 0:
+	snprintf (buf, sizeof (buf), "ccmp\t%%w2, %%w3, #%u, %s",
+		  nzcv, aarch64_condition_codes[cond_code]);
+	break;
+      case 1:
+	snprintf (buf, sizeof (buf), "ccmp\t%%w2, #%%3, #%u, %s",
+		  nzcv, aarch64_condition_codes[cond_code]);
+	break;
+      case 2:
+	snprintf (buf, sizeof (buf), "ccmn\t%%w2, #%%n3, #%u, %s",
+		  nzcv, aarch64_condition_codes[cond_code]);
+	break;
+      default:
+	gcc_unreachable ();
+      }
+  else
+    switch (which_alternative)
+      {
+      case 0:
+	snprintf (buf, sizeof (buf), "ccmp\t%%x2, %%x3, #%u, %s",
+		  nzcv, aarch64_condition_codes[cond_code]);
+	break;
+      case 1:
+	snprintf (buf, sizeof (buf), "ccmp\t%%x2, #%%3, #%u, %s",
+		  nzcv, aarch64_condition_codes[cond_code]);
+	break;
+      case 2:
+	snprintf (buf, sizeof (buf), "ccmn\t%%x2, #%%n3, #%u, %s",
+		  nzcv, aarch64_condition_codes[cond_code]);
+	break;
+      default:
+	gcc_unreachable ();
+      }
+
+  output_asm_insn (buf, operands);
+  return "";
+}
+
+/* Return true if val can be encoded as a 5-bit unsigned immediate.  */
+bool
+aarch64_uimm5 (HOST_WIDE_INT val)
+{
+  return (val & ((unsigned HOST_WIDE_INT) 0x1f)) == val;
+}
+
+static bool
+aarch64_convert_mode (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 && mode != DImode)
+    return false;
+
+  return true;
+}
+
+static rtx
+aarch64_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 (!aarch64_convert_mode (&op0, &op1, unsignedp)
+      || !register_operand (op0, GET_MODE (op0))
+      || !aarch64_ccmp_operand (op1, GET_MODE (op1)))
+    return NULL_RTX;
+
+  mode = aarch64_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
+aarch64_gen_ccmp_next (rtx prev, int cmp_code, rtx op0, rtx op1, int bit_code)
+{
+  rtx cmp0, cmp1, target, bit_op;
+  enum machine_mode mode = aarch64_code_to_ccmode ((enum rtx_code) cmp_code);
+  int unsignedp = cmp_code == LTU || cmp_code == LEU
+		  || cmp_code == GTU || cmp_code == GEU;
+
+  if (!aarch64_convert_mode (&op0, &op1, unsignedp)
+      || !register_operand (op0, GET_MODE (op0))
+      || !aarch64_ccmp_operand (op1, GET_MODE (op1)))
+    return NULL_RTX;
+
+  cmp1 = gen_rtx_fmt_ee ((enum rtx_code) cmp_code, SImode, op0, op1);
+
+  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;
+}
+
+#undef TARGET_GEN_CCMP_FIRST
+#define TARGET_GEN_CCMP_FIRST aarch64_gen_ccmp_first
+
+#undef TARGET_GEN_CCMP_NEXT
+#define TARGET_GEN_CCMP_NEXT aarch64_gen_ccmp_next
+
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST aarch64_address_cost
 
 /* Assembly output.  */
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 99a6ac8..8f5fd92 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -212,6 +212,52 @@ 
   "
 )
 
+(define_expand "cbranchcc4"
+  [(set (pc) (if_then_else
+	      (match_operator 0 "aarch64_comparison_operator"
+	       [(match_operand 1 "cc_register" "")
+	        (const_int 0)])
+	      (label_ref (match_operand 3 "" ""))
+	      (pc)))]
+  ""
+  " ")
+
+(define_insn "*ccmp_and"
+  [(set (match_operand 6 "ccmp_cc_register" "")
+	(compare
+	 (and:SI
+	  (match_operator 4 "aarch64_comparison_operator"
+	   [(match_operand 0 "ccmp_cc_register" "")
+	    (match_operand 1 "aarch64_plus_operand" "")])
+	  (match_operator 5 "aarch64_comparison_operator"
+	   [(match_operand:GPI 2 "register_operand" "r,r,r")
+	    (match_operand:GPI 3 "aarch64_ccmp_operand" "r,Uss,Usn")]))
+	 (const_int 0)))]
+  ""
+  {
+    return aarch64_output_ccmp (operands, true, which_alternative);
+  }
+  [(set_attr "type" "alus_reg,alus_imm,alus_imm")]
+)
+
+(define_insn "*ccmp_ior"
+  [(set (match_operand 6 "ccmp_cc_register" "")
+	(compare
+	 (ior:SI
+	  (match_operator 4 "aarch64_comparison_operator"
+	   [(match_operand 0 "ccmp_cc_register" "")
+	    (match_operand 1 "aarch64_plus_operand" "")])
+	  (match_operator 5 "aarch64_comparison_operator"
+	   [(match_operand:GPI 2 "register_operand" "r,r,r")
+	    (match_operand:GPI 3 "aarch64_ccmp_operand" "r,Uss,Usn")]))
+	 (const_int 0)))]
+  ""
+  {
+    return aarch64_output_ccmp (operands, false, which_alternative);
+  }
+  [(set_attr "type" "alus_reg,alus_imm,alus_imm")]
+)
+
 (define_insn "*condjump"
   [(set (pc) (if_then_else (match_operator 0 "aarch64_comparison_operator"
 			    [(match_operand 1 "cc_register" "") (const_int 0)])
diff --git a/gcc/config/aarch64/constraints.md b/gcc/config/aarch64/constraints.md
index 12ab570..6679efa 100644
--- a/gcc/config/aarch64/constraints.md
+++ b/gcc/config/aarch64/constraints.md
@@ -86,6 +86,11 @@ 
   (and (match_code "const_int")
        (match_test "(unsigned HOST_WIDE_INT) ival < 32")))
 
+(define_constraint "Usn"
+ "A constant that can be used with a CCMN operation (once negated)."
+ (and (match_code "const_int")
+      (match_test "aarch64_uimm5 (-ival)")))
+
 (define_constraint "Usd"
   "@internal
   A constraint that matches an immediate shift constant in DImode."
diff --git a/gcc/config/aarch64/predicates.md b/gcc/config/aarch64/predicates.md
index c8e27d8..4b9f84b 100644
--- a/gcc/config/aarch64/predicates.md
+++ b/gcc/config/aarch64/predicates.md
@@ -26,6 +26,32 @@ 
 			      && GET_MODE_CLASS (GET_MODE (op)) == MODE_CC"))))
 )
 
+(define_special_predicate "ccmp_cc_register"
+  (and (match_code "reg")
+       (and (match_test "REGNO (op) == CC_REGNUM")
+	    (ior (match_test "mode == GET_MODE (op)")
+		 (match_test "mode == VOIDmode
+			      && (GET_MODE (op) == CC_DNEmode
+				  || GET_MODE (op) == CC_DEQmode
+				  || GET_MODE (op) == CC_DLEmode
+				  || GET_MODE (op) == CC_DLTmode
+				  || GET_MODE (op) == CC_DGEmode
+				  || GET_MODE (op) == CC_DGTmode
+				  || GET_MODE (op) == CC_DLEUmode
+				  || GET_MODE (op) == CC_DLTUmode
+				  || GET_MODE (op) == CC_DGEUmode
+				  || GET_MODE (op) == CC_DGTUmode)"))))
+)
+
+(define_predicate "aarch64_ccmp_immediate"
+  (and (match_code "const_int")
+       (ior (match_test "aarch64_uimm5 (INTVAL (op))")
+	    (match_test "aarch64_uimm5 (-INTVAL (op))"))))
+
+(define_predicate "aarch64_ccmp_operand"
+  (ior (match_operand 0 "register_operand")
+       (match_operand 0 "aarch64_ccmp_immediate")))
+
 (define_predicate "aarch64_simd_register"
   (and (match_code "reg")
        (ior (match_test "REGNO_REG_CLASS (REGNO (op)) == FP_LO_REGS")