From: Zhenqiang Chen Date: Fri, 16 Jan 2015 11:48:00 +0000 (+0000) Subject: [AArch64] Enable CCMP support for AArch64, PR64015 resolved X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5f3bc026061e59f2722ae17f2329d005e0f95559;p=gcc.git [AArch64] Enable CCMP support for AArch64, PR64015 resolved gcc/ 2015-01-16 Zhenqiang Chen 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. (*ccmp_ior): Changed to ccmp_ior. (cmp): 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. gcc/testsuite/ 2015-01-16 Zhenqiang Chen * gcc.dg/pr64015.c: New test. From-SVN: r219723 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 27713b00752..198e5e11a8c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2015-01-16 Zhenqiang Chen + + 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. + (*ccmp_ior): Changed to ccmp_ior. + (cmp): 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 * config/i386/avx2intrin.h (_mm256_bslli_epi128, diff --git a/gcc/ccmp.c b/gcc/ccmp.c index 1130329ab30..903d5a865de 100644 --- a/gcc/ccmp.c +++ b/gcc/ccmp.c @@ -92,7 +92,16 @@ along with GCC; see the file COPYING3. If not see * 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 @@ -172,6 +181,27 @@ used_in_cond_stmt_p (tree exp) 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); @@ -180,9 +210,11 @@ used_in_cond_stmt_p (tree exp) 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); @@ -199,52 +231,27 @@ expand_ccmp_expr_1 (gimple g) { 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 @@ -254,21 +261,11 @@ expand_ccmp_expr_1 (gimple g) 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 { @@ -288,23 +285,30 @@ expand_ccmp_expr (gimple g) { 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); @@ -314,9 +318,12 @@ expand_ccmp_expr (gimple g) 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) diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 34cce91b24b..f3cf6ed0643 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -10381,6 +10381,198 @@ aarch64_use_by_pieces_infrastructure_p (unsigned HOST_WIDE_INT size, 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. */ diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index adfa46d0e08..c780e417d28 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -248,7 +248,7 @@ "" "") -(define_insn "*ccmp_and" +(define_insn "ccmp_and" [(set (match_operand 1 "ccmp_cc_register" "") (compare (and:SI @@ -267,7 +267,7 @@ [(set_attr "type" "alus_sreg,alus_imm,alus_imm")] ) -(define_insn "*ccmp_ior" +(define_insn "ccmp_ior" [(set (match_operand 1 "ccmp_cc_register" "") (compare (ior:SI @@ -286,6 +286,20 @@ [(set_attr "type" "alus_sreg,alus_imm,alus_imm")] ) +(define_expand "cmp" + [(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)]) diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 4e6c9476431..9c81fdb98d4 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -11259,18 +11259,25 @@ This target hook is required only when the target has several different 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} diff --git a/gcc/target.def b/gcc/target.def index 4aebcfeddd2..356f7c1c9a4 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -2542,24 +2542,31 @@ modes and they have different conditional execution capability, such as ARM.", 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. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 4e657a47908..72c6283755c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2015-01-16 Zhenqiang Chen + + * gcc.dg/pr64015.c: New test. + 2015-01-16 Markus Trippelsdorf PR ipa/64163 diff --git a/gcc/testsuite/gcc.dg/pr64015.c b/gcc/testsuite/gcc.dg/pr64015.c new file mode 100644 index 00000000000..daf839351d7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr64015.c @@ -0,0 +1,10 @@ +/* { 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*-*-* } } } */