+2015-01-16 Zhenqiang Chen <zhenqiang.chen@arm.com>
+
+ PR target/64015
+ * ccmp.c (expand_ccmp_next): New function.
+ (expand_ccmp_expr_1, expand_ccmp_expr): Handle operand insn sequence
+ and compare insn sequence.
+ * config/aarch64/aarch64.c (aarch64_code_to_ccmode,
+ aarch64_gen_ccmp_first, aarch64_gen_ccmp_next): New functions.
+ (TARGET_GEN_CCMP_FIRST, TARGET_GEN_CCMP_NEXT): New MICRO.
+ * config/aarch64/aarch64.md (*ccmp_and): Changed to ccmp_and<mode>.
+ (*ccmp_ior): Changed to ccmp_ior<mode>.
+ (cmp<mode>): New pattern.
+ * doc/tm.texi (TARGET_GEN_CCMP_FIRST, TARGET_GEN_CCMP_NEXT): Update
+ parameters.
+ * target.def (gen_ccmp_first, gen_ccmp_next): Update parameters.
+
2015-01-16 Ilya Tocar <ilya.tocar@intel.com>
* config/i386/avx2intrin.h (_mm256_bslli_epi128,
* If the final result is not used in a COND_EXPR (checked by function
used_in_cond_stmt_p), it calls cstorecc4 pattern to store the CC to a
- general register. */
+ general register.
+
+ Since the operands of the later compares might clobber CC reg, we do not
+ emit the insns during expand. We keep the insn sequences in two seq
+
+ * prep_seq, which includes all the insns to prepare the operands.
+ * gen_seq, which includes all the compare and conditional compares.
+
+ If all checks OK in expand_ccmp_expr, it emits insns in prep_seq, then
+ insns in gen_seq. */
/* Check whether G is a potential conditional compare candidate. */
static bool
return expand_cond;
}
+/* PREV is the CC flag from precvious compares. The function expands the
+ next compare based on G which ops previous compare with CODE.
+ PREP_SEQ returns all insns to prepare opearands for compare.
+ GEN_SEQ returnss all compare insns. */
+static rtx
+expand_ccmp_next (gimple g, enum tree_code code, rtx prev,
+ rtx *prep_seq, rtx *gen_seq)
+{
+ enum rtx_code rcode;
+ int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (g)));
+
+ gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR);
+
+ rcode = get_rtx_code (gimple_assign_rhs_code (g), unsignedp);
+
+ return targetm.gen_ccmp_next (prep_seq, gen_seq, prev, rcode,
+ gimple_assign_rhs1 (g),
+ gimple_assign_rhs2 (g),
+ get_rtx_code (code, 0));
+}
+
/* Expand conditional compare gimple G. A typical CCMP sequence is like:
CC0 = CMP (a, b);
CCn = CCMP (NE (CCn-1, 0), CMP (...));
hook gen_ccmp_first is used to expand the first compare.
- hook gen_ccmp_next is used to expand the following CCMP. */
+ hook gen_ccmp_next is used to expand the following CCMP.
+ PREP_SEQ returns all insns to prepare opearand.
+ GEN_SEQ returns all compare insns. */
static rtx
-expand_ccmp_expr_1 (gimple g)
+expand_ccmp_expr_1 (gimple g, rtx *prep_seq, rtx *gen_seq)
{
tree exp = gimple_assign_rhs_to_tree (g);
enum tree_code code = TREE_CODE (exp);
{
if (TREE_CODE_CLASS (code1) == tcc_comparison)
{
- int unsignedp0, unsignedp1;
- enum rtx_code rcode0, rcode1;
- rtx op0, op1, op2, op3, tmp;
+ int unsignedp0;
+ enum rtx_code rcode0;
unsignedp0 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs0)));
rcode0 = get_rtx_code (code0, unsignedp0);
- unsignedp1 = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs1)));
- rcode1 = get_rtx_code (code1, unsignedp1);
-
- expand_operands (gimple_assign_rhs1 (gs0),
- gimple_assign_rhs2 (gs0),
- NULL_RTX, &op0, &op1, EXPAND_NORMAL);
-
- /* Since the operands of GS1 might clobber CC reg, we expand the
- operands of GS1 before GEN_CCMP_FIRST. */
- expand_operands (gimple_assign_rhs1 (gs1),
- gimple_assign_rhs2 (gs1),
- NULL_RTX, &op2, &op3, EXPAND_NORMAL);
- tmp = targetm.gen_ccmp_first (rcode0, op0, op1);
+
+ tmp = targetm.gen_ccmp_first (prep_seq, gen_seq, rcode0,
+ gimple_assign_rhs1 (gs0),
+ gimple_assign_rhs2 (gs0));
if (!tmp)
return NULL_RTX;
- return targetm.gen_ccmp_next (tmp, rcode1, op2, op3,
- get_rtx_code (code, 0));
+ return expand_ccmp_next (gs1, code, tmp, prep_seq, gen_seq);
}
else
{
- rtx op0, op1;
- enum rtx_code rcode;
- int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs0)));
-
- rcode = get_rtx_code (gimple_assign_rhs_code (gs0), unsignedp);
-
- /* Hoist the preparation operations above the entire
- conditional compare sequence. */
- expand_operands (gimple_assign_rhs1 (gs0),
- gimple_assign_rhs2 (gs0),
- NULL_RTX, &op0, &op1, EXPAND_NORMAL);
-
- gcc_assert (code1 == BIT_AND_EXPR || code1 == BIT_IOR_EXPR);
+ tmp = expand_ccmp_expr_1 (gs1, prep_seq, gen_seq);
+ if (!tmp)
+ return NULL_RTX;
- /* Note: We swap the order to make the recursive function work. */
- tmp = expand_ccmp_expr_1 (gs1);
- if (tmp)
- return targetm.gen_ccmp_next (tmp, rcode, op0, op1,
- get_rtx_code (code, 0));
+ return expand_ccmp_next (gs0, code, tmp, prep_seq, gen_seq);
}
}
else
if (TREE_CODE_CLASS (gimple_assign_rhs_code (gs1)) == tcc_comparison)
{
- rtx op0, op1;
- enum rtx_code rcode;
- int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (gs1)));
-
- rcode = get_rtx_code (gimple_assign_rhs_code (gs1), unsignedp);
-
- /* Hoist the preparation operations above the entire
- conditional compare sequence. */
- expand_operands (gimple_assign_rhs1 (gs1),
- gimple_assign_rhs2 (gs1),
- NULL_RTX, &op0, &op1, EXPAND_NORMAL);
- tmp = expand_ccmp_expr_1 (gs0);
- if (tmp)
- return targetm.gen_ccmp_next (tmp, rcode, op0, op1,
- get_rtx_code (code, 0));
+ tmp = expand_ccmp_expr_1 (gs0, prep_seq, gen_seq);
+ if (!tmp)
+ return NULL_RTX;
+
+ return expand_ccmp_next (gs1, code, tmp, prep_seq, gen_seq);
}
else
{
{
rtx_insn *last;
rtx tmp;
+ rtx prep_seq, gen_seq;
+
+ prep_seq = gen_seq = NULL_RTX;
if (!ccmp_candidate_p (g))
return NULL_RTX;
last = get_last_insn ();
- tmp = expand_ccmp_expr_1 (g);
+ tmp = expand_ccmp_expr_1 (g, &prep_seq, &gen_seq);
if (tmp)
{
enum insn_code icode;
enum machine_mode cc_mode = CCmode;
-
tree lhs = gimple_assign_lhs (g);
+
/* TMP should be CC. If it is used in a GIMPLE_COND, just return it.
Note: Target needs to define "cbranchcc4". */
if (used_in_cond_stmt_p (lhs))
- return tmp;
+ {
+ emit_insn (prep_seq);
+ emit_insn (gen_seq);
+ return tmp;
+ }
#ifdef SELECT_CC_MODE
cc_mode = SELECT_CC_MODE (NE, tmp, const0_rtx);
icode = optab_handler (cstore_optab, cc_mode);
if (icode != CODE_FOR_nothing)
{
- tree lhs = gimple_assign_lhs (g);
enum machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
rtx target = gen_reg_rtx (mode);
+
+ emit_insn (prep_seq);
+ emit_insn (gen_seq);
+
tmp = emit_cstore (target, icode, NE, cc_mode, cc_mode,
0, tmp, const0_rtx, 1, mode);
if (tmp)
return default_use_by_pieces_infrastructure_p (size, align, op, speed_p);
}
+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;
+ }
+}
+
+static rtx
+aarch64_gen_ccmp_first (rtx *prep_seq, rtx *gen_seq,
+ int code, tree treeop0, tree treeop1)
+{
+ enum machine_mode op_mode, cmp_mode, cc_mode;
+ rtx op0, op1, cmp, target;
+ int unsignedp = TYPE_UNSIGNED (TREE_TYPE (treeop0));
+ enum insn_code icode;
+ struct expand_operand ops[4];
+
+ cc_mode = aarch64_code_to_ccmode ((enum rtx_code) code);
+ if (cc_mode == CCmode)
+ return NULL_RTX;
+
+ start_sequence ();
+ expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+
+ op_mode = GET_MODE (op0);
+ if (op_mode == VOIDmode)
+ op_mode = GET_MODE (op1);
+
+ switch (op_mode)
+ {
+ case QImode:
+ case HImode:
+ case SImode:
+ cmp_mode = SImode;
+ icode = CODE_FOR_cmpsi;
+ break;
+
+ case DImode:
+ cmp_mode = DImode;
+ icode = CODE_FOR_cmpdi;
+ break;
+
+ default:
+ end_sequence ();
+ return NULL_RTX;
+ }
+
+ op0 = prepare_operand (icode, op0, 2, op_mode, cmp_mode, unsignedp);
+ op1 = prepare_operand (icode, op1, 3, op_mode, cmp_mode, unsignedp);
+ if (!op0 || !op1)
+ {
+ end_sequence ();
+ return NULL_RTX;
+ }
+ *prep_seq = get_insns ();
+ end_sequence ();
+
+ cmp = gen_rtx_fmt_ee ((enum rtx_code) code, cmp_mode, op0, op1);
+ target = gen_rtx_REG (CCmode, CC_REGNUM);
+
+ create_output_operand (&ops[0], target, CCmode);
+ create_fixed_operand (&ops[1], cmp);
+ create_fixed_operand (&ops[2], op0);
+ create_fixed_operand (&ops[3], op1);
+
+ start_sequence ();
+ if (!maybe_expand_insn (icode, 4, ops))
+ {
+ end_sequence ();
+ return NULL_RTX;
+ }
+ *gen_seq = get_insns ();
+ end_sequence ();
+
+ return gen_rtx_REG (cc_mode, CC_REGNUM);
+}
+
+static rtx
+aarch64_gen_ccmp_next (rtx *prep_seq, rtx *gen_seq, rtx prev, int cmp_code,
+ tree treeop0, tree treeop1, int bit_code)
+{
+ rtx op0, op1, cmp0, cmp1, target;
+ enum machine_mode op_mode, cmp_mode, cc_mode;
+ int unsignedp = TYPE_UNSIGNED (TREE_TYPE (treeop0));
+ enum insn_code icode = CODE_FOR_ccmp_andsi;
+ struct expand_operand ops[6];
+
+ cc_mode = aarch64_code_to_ccmode ((enum rtx_code) cmp_code);
+ if (cc_mode == CCmode)
+ return NULL_RTX;
+
+ push_to_sequence ((rtx_insn*) *prep_seq);
+ expand_operands (treeop0, treeop1, NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+
+ op_mode = GET_MODE (op0);
+ if (op_mode == VOIDmode)
+ op_mode = GET_MODE (op1);
+
+ switch (op_mode)
+ {
+ case QImode:
+ case HImode:
+ case SImode:
+ cmp_mode = SImode;
+ icode = (enum rtx_code) bit_code == AND ? CODE_FOR_ccmp_andsi
+ : CODE_FOR_ccmp_iorsi;
+ break;
+
+ case DImode:
+ cmp_mode = DImode;
+ icode = (enum rtx_code) bit_code == AND ? CODE_FOR_ccmp_anddi
+ : CODE_FOR_ccmp_iordi;
+ break;
+
+ default:
+ end_sequence ();
+ return NULL_RTX;
+ }
+
+ op0 = prepare_operand (icode, op0, 2, op_mode, cmp_mode, unsignedp);
+ op1 = prepare_operand (icode, op1, 3, op_mode, cmp_mode, unsignedp);
+ if (!op0 || !op1)
+ {
+ end_sequence ();
+ return NULL_RTX;
+ }
+ *prep_seq = get_insns ();
+ end_sequence ();
+
+ target = gen_rtx_REG (cc_mode, CC_REGNUM);
+ cmp1 = gen_rtx_fmt_ee ((enum rtx_code) cmp_code, cmp_mode, op0, op1);
+ cmp0 = gen_rtx_fmt_ee (NE, cmp_mode, prev, const0_rtx);
+
+ create_fixed_operand (&ops[0], prev);
+ create_fixed_operand (&ops[1], target);
+ create_fixed_operand (&ops[2], op0);
+ create_fixed_operand (&ops[3], op1);
+ create_fixed_operand (&ops[4], cmp0);
+ create_fixed_operand (&ops[5], cmp1);
+
+ push_to_sequence ((rtx_insn*) *gen_seq);
+ if (!maybe_expand_insn (icode, 6, ops))
+ {
+ end_sequence ();
+ return NULL_RTX;
+ }
+
+ *gen_seq = get_insns ();
+ end_sequence ();
+
+ 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
+
/* Implement TARGET_SCHED_MACRO_FUSION_P. Return true if target supports
instruction fusion of some sort. */
""
"")
-(define_insn "*ccmp_and"
+(define_insn "ccmp_and<mode>"
[(set (match_operand 1 "ccmp_cc_register" "")
(compare
(and:SI
[(set_attr "type" "alus_sreg,alus_imm,alus_imm")]
)
-(define_insn "*ccmp_ior"
+(define_insn "ccmp_ior<mode>"
[(set (match_operand 1 "ccmp_cc_register" "")
(compare
(ior:SI
[(set_attr "type" "alus_sreg,alus_imm,alus_imm")]
)
+(define_expand "cmp<mode>"
+ [(set (match_operand 0 "cc_register" "")
+ (match_operator:CC 1 "aarch64_comparison_operator"
+ [(match_operand:GPI 2 "register_operand" "")
+ (match_operand:GPI 3 "aarch64_plus_operand" "")]))]
+ ""
+ {
+ operands[1] = gen_rtx_fmt_ee (COMPARE,
+ SELECT_CC_MODE (GET_CODE (operands[1]),
+ operands[2], operands[3]),
+ operands[2], operands[3]);
+ }
+)
+
(define_insn "*condjump"
[(set (pc) (if_then_else (match_operator 0 "aarch64_comparison_operator"
[(match_operand 1 "cc_register" "") (const_int 0)])
modes and they have different conditional execution capability, such as ARM.
@end deftypefn
-@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_FIRST (int @var{code}, rtx @var{op0}, rtx @var{op1})
-This function emits a comparison insn for the first of a sequence of
- conditional comparisions. It returns a comparison expression appropriate
- for passing to @code{gen_ccmp_next} or @code{cbranch_optab}. @var{code} is
+@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_FIRST (rtx *@var{prep_seq}, rtx *@var{gen_seq}, int @var{code}, tree @var{op0}, tree @var{op1})
+This function prepares to emit a comparison insn for the first compare in a
+ sequence of conditional comparisions. It returns a appropriate @code{CC}
+ for passing to @code{gen_ccmp_next} or @code{cbranch_optab}. The insns to
+ prepare the compare are saved in @var{prep_seq} and the compare insns are
+ saved in @var{gen_seq}. They will be emitted when all the compares in the
+ the conditional comparision are generated without error. @var{code} is
the @code{rtx_code} of the compare for @var{op0} and @var{op1}.
@end deftypefn
-@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_NEXT (rtx @var{prev}, int @var{cmp_code}, rtx @var{op0}, rtx @var{op1}, int @var{bit_code})
-This function emits a conditional comparison within a sequence of
- conditional comparisons. The @var{prev} expression is the result of a
- prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It may return
- @code{NULL} if the combination of @var{prev} and this comparison is
+@deftypefn {Target Hook} rtx TARGET_GEN_CCMP_NEXT (rtx *@var{prep_seq}, rtx *@var{gen_seq}, rtx @var{prev}, int @var{cmp_code}, tree @var{op0}, tree @var{op1}, int @var{bit_code})
+This function prepare to emit a conditional comparison within a sequence of
+ conditional comparisons. It returns a appropriate @code{CC} for passing to
+ @code{gen_ccmp_next} or @code{cbranch_optab}. The insns to prepare the
+ compare are saved in @var{prep_seq} and the compare insns are saved in
+ @var{gen_seq}. They will be emitted when all the compares in the conditional
+ comparision are generated without error. The @var{prev} expression is the
+ result of a prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It
+ may return @code{NULL} if the combination of @var{prev} and this comparison is
not supported, otherwise the result must be appropriate for passing to
@code{gen_ccmp_next} or @code{cbranch_optab}. @var{code} is the
@code{rtx_code} of the compare for @var{op0} and @var{op1}. @var{bit_code}
DEFHOOK
(gen_ccmp_first,
- "This function emits a comparison insn for the first of a sequence of\n\
- conditional comparisions. It returns a comparison expression appropriate\n\
- for passing to @code{gen_ccmp_next} or @code{cbranch_optab}. @var{code} is\n\
+ "This function prepares to emit a comparison insn for the first compare in a\n\
+ sequence of conditional comparisions. It returns a appropriate @code{CC}\n\
+ for passing to @code{gen_ccmp_next} or @code{cbranch_optab}. The insns to\n\
+ prepare the compare are saved in @var{prep_seq} and the compare insns are\n\
+ saved in @var{gen_seq}. They will be emitted when all the compares in the\n\
+ the conditional comparision are generated without error. @var{code} is\n\
the @code{rtx_code} of the compare for @var{op0} and @var{op1}.",
- rtx, (int code, rtx op0, rtx op1),
+ rtx, (rtx *prep_seq, rtx *gen_seq, int code, tree op0, tree op1),
NULL)
DEFHOOK
(gen_ccmp_next,
- "This function emits a conditional comparison within a sequence of\n\
- conditional comparisons. The @var{prev} expression is the result of a\n\
- prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It may return\n\
- @code{NULL} if the combination of @var{prev} and this comparison is\n\
+ "This function prepare to emit a conditional comparison within a sequence of\n\
+ conditional comparisons. It returns a appropriate @code{CC} for passing to\n\
+ @code{gen_ccmp_next} or @code{cbranch_optab}. The insns to prepare the\n\
+ compare are saved in @var{prep_seq} and the compare insns are saved in\n\
+ @var{gen_seq}. They will be emitted when all the compares in the conditional\n\
+ comparision are generated without error. The @var{prev} expression is the\n\
+ result of a prior call to @code{gen_ccmp_first} or @code{gen_ccmp_next}. It\n\
+ may return @code{NULL} if the combination of @var{prev} and this comparison is\n\
not supported, otherwise the result must be appropriate for passing to\n\
@code{gen_ccmp_next} or @code{cbranch_optab}. @var{code} is the\n\
@code{rtx_code} of the compare for @var{op0} and @var{op1}. @var{bit_code}\n\
is @code{AND} or @code{IOR}, which is the op on the two compares.",
- rtx, (rtx prev, int cmp_code, rtx op0, rtx op1, int bit_code),
+ rtx, (rtx *prep_seq, rtx *gen_seq, rtx prev, int cmp_code, tree op0, tree op1, int bit_code),
NULL)
/* Return a new value for loop unroll size. */
+2015-01-16 Zhenqiang Chen <zhenqiang.chen@arm.com>
+
+ * gcc.dg/pr64015.c: New test.
+
2015-01-16 Markus Trippelsdorf <markus@trippelsdorf.de>
PR ipa/64163
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 " } */
+
+int
+test (unsigned short a, unsigned char b)
+{
+ return a > 0xfff2 && b > 252;
+}
+
+/* { dg-final { scan-assembler "ccmp" { target aarch64*-*-* } } } */