@@ -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 *);
@@ -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)
{
@@ -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))])]
+)