@@ -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. */
@@ -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);
@@ -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. */
@@ -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)])
@@ -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."
@@ -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")