diff mbox

[10/10] aarch64: Handle ccmp in ifcvt to make it work with cmov

Message ID CACgzC7DZ+mdaB60n42JOXDSuNxYgxDk=PfjWrAbDFNDT-wXKnQ@mail.gmail.com
State New
Headers show

Commit Message

Zhenqiang Chen July 4, 2014, 6:54 a.m. UTC
Andrew,

Thanks for the input. Patch is updated with your codes. In addition,
add "cstorecc4" and 4 test cases. So it can optimized the following
example

int foo (int a, int b)
{
   return a > 0 && b < 7;
}
to:
    cmp    w0, wzr
    ccmp    w1, #6, #9, gt
    cset    w0, le

Bootstrap and no make check regression on qemu.

OK for trunk?

Thanks!
-Zhenqiang

2014-07-04  Zhenqiang Chen  <zhenqiang.chen@linaro.org>
            Andrew Pinski  <apinski@cavium.com>

        * config/aarch64/aarch64.md (cstorecc4): New.
        (mov<mode>cc): Handle ccmp_cc.
        * ccmp.c (used_in_cond_stmt_p): Hande ? expr.
        (expand_ccmp_expr): Set mode to the MODE of gimple_assign_lhs (g).
        * expr.c (expand_cond_expr_using_cmove): Handle CCmode.
        * ifcvt.c: #include "ccmp.h".
        (struct noce_if_info): Add a new field ccmp_p.
        (noce_emit_cmove): Allow ccmp condition.
        (noce_get_alt_condition): Call canonicalize_condition with ccmp_p.
        (noce_get_condition): Set ALLOW_CC_MODE to TRUE for ccmp.
        (noce_process_if_block): Set ccmp_p for ccmp.

testsuite/ChangeLog:
2014-07-04  Zhenqiang Chen  <zhenqiang.chen@linaro.org>

        * gcc.target/aarch64/ccmn-csel-1.c: New testcase.
        * gcc.target/aarch64/ccmn-csel-2.c: New testcase.
        * gcc.target/aarch64/ccmn-csel-3.c: New testcase.
        * gcc.target/aarch64/ccmp-csel-1.c: New testcase.
        * gcc.target/aarch64/ccmp-csel-2.c: New testcase.
        * gcc.target/aarch64/ccmp-csel-3.c: New testcase.
        * gcc.target/aarch64/ccmp-csel-4.c: New testcase.
        * gcc.target/aarch64/ccmp-csel-5.c: New testcase.
        * gcc.target/aarch64/ccmp-cset-1.c: New testcase.
        * gcc.target/aarch64/ccmp-cset-2.c: New testcase.


On 23 June 2014 15:51,  <pinskia@gmail.com> wrote:
>
>
>> On Jun 23, 2014, at 12:37 AM, Zhenqiang Chen <zhenqiang.chen@linaro.org> wrote:
>>
>>> On 23 June 2014 15:09, Andrew Pinski <pinskia@gmail.com> wrote:
>>> On Mon, Jun 23, 2014 at 12:01 AM, Zhenqiang Chen
>>> <zhenqiang.chen@linaro.org> wrote:
>>>> Hi,
>>>>
>>>> The patch enhances ifcvt to handle conditional compare instruction
>>>> (ccmp) to make it work with cmov. For ccmp, ALLOW_CC_MODE is set to
>>>> TRUE when calling canonicalize_condition. And the backend does not
>>>> need to generate additional "compare (CC, 0)" for it.
>>>>
>>>> Bootstrap and no check regression on qemu.
>>>>
>>>> OK for trunk?
>>>>
>>>> Thanks!
>>>> -Zhenqiang
>>>>
>>>> ChangeLog:
>>>> 2014-06-23  Zhenqiang Chen  <zhenqiang.chen@linaro.org>
>>>>
>>>>        * config/aarch64/aarch64.md (mov<mode>cc): Handle ccmp_cc.
>>>>        * ifcvt.c: #include "ccmp.h".
>>>>        (struct noce_if_info): Add a new field ccmp_p.
>>>>        (noce_emit_cmove): Allow ccmp condition.
>>>>        (noce_get_alt_condition): Call canonicalize_condition with ccmp_p.
>>>>        (noce_get_condition): Set ALLOW_CC_MODE to TRUE for ccmp.
>>>>        (noce_process_if_block): Set ccmp_p for ccmp.
>>>>
>>>> testsuite/ChangeLog:
>>>> 2014-06-23  Zhenqiang Chen  <zhenqiang.chen@linaro.org>
>>>>
>>>>        * gcc.target/aarch64/ccmn-csel-1.c: New testcase.
>>>>        * gcc.target/aarch64/ccmn-csel-2.c: New testcase.
>>>>        * gcc.target/aarch64/ccmn-csel-3.c: New testcase.
>>>>        * gcc.target/aarch64/ccmp-csel-1.c: New testcase.
>>>>        * gcc.target/aarch64/ccmp-csel-2.c: New testcase.
>>>>        * gcc.target/aarch64/ccmp-csel-3.c: New testcase.
>>>>
>>>> diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
>>>> index fcc5559..82cc561 100644
>>>> --- a/gcc/config/aarch64/aarch64.md
>>>> +++ b/gcc/config/aarch64/aarch64.md
>>>> @@ -2459,15 +2459,19 @@
>>>>                           (match_operand:ALLI 3 "register_operand" "")))]
>>>>   ""
>>>>   {
>>>> -    rtx ccreg;
>>>>     enum rtx_code code = GET_CODE (operands[1]);
>>>>
>>>>     if (code == UNEQ || code == LTGT)
>>>>       FAIL;
>>>>
>>>> -    ccreg = aarch64_gen_compare_reg (code, XEXP (operands[1], 0),
>>>> -                                 XEXP (operands[1], 1));
>>>> -    operands[1] = gen_rtx_fmt_ee (code, VOIDmode, ccreg, const0_rtx);
>>>> +    if (!ccmp_cc_register (XEXP (operands[1], 0),
>>>> +                          GET_MODE (XEXP (operands[1], 0))))
>>>> +      {
>>>> +       rtx ccreg;
>>>> +       ccreg = aarch64_gen_compare_reg (code, XEXP (operands[1], 0),
>>>> +                                        XEXP (operands[1], 1));
>>>> +       operands[1] = gen_rtx_fmt_ee (code, VOIDmode, ccreg, const0_rtx);
>>>> +      }
>>>>   }
>>>> )
>>>>
>>>
>>>
>>> You should do the same thing for the FP one.  The change to aarch64.md
>>> is exactly the same patch which I came up with.
>>
>> Thanks for the comments.
>>
>> For AARCH64, we can mix INT and FP compares. But FP compare would be
>> slower than INT compare.
>
> One point is that this is not about fp compares but rather moving of fp register. The fp pattern is used for that.  So something like this would fail/ice:
> double f(double a, double b, int c, int d)
> {
>   return c>10&&d>20?a:b;
> }
>
> Thanks,
> Andrew
>
>
>
>>
>> CMP
>> FCCMP
>>
>> FCMP
>> CCMP
>>
>> FCMP
>> FCCMP
>>
>> I have no enough resource to collect benchmark results to approve them
>> valuable. So the patches did not handle FP at all. If you had approved
>> CCMP for FP valuable, I will work out a separate patch to support it.
>> Or you can share your patches.
>
> I need to l
>
>>
>> Thanks!
>> -Zhenqiang
>>
>>> For the rest I actually I have a late phi-opt pass which does the
>>> conversion into COND_EXPR.  That is I don't change ifcvt at all.
>>>
>>> And then I needed two more patches after that to get conditional
>>> compares to work with cmov's.
>>
>> Thanks. Any patch to improve ccmp is welcome.
>>
>> -Zhenqiang
>>
>>> The following patch which fixes up expand_cond_expr_using_cmove to
>>> handle CCmode correctly:
>>> --- a/gcc/expr.c
>>> +++ b/gcc/expr.c
>>> @@ -7989,7 +7989,9 @@ expand_cond_expr_using_cmove (tree treeop0
>>> ATTRIBUTE_UNUSED,
>>>       op00 = expand_normal (treeop0);
>>>       op01 = const0_rtx;
>>>       comparison_code = NE;
>>> -      comparison_mode = TYPE_MODE (TREE_TYPE (treeop0));
>>> +      comparison_mode = GET_MODE (op00);
>>> +      if (comparison_mode == VOIDmode)
>>> +       comparison_mode = TYPE_MODE (TREE_TYPE (treeop0));
>>>     }
>>>
>>>   if (GET_MODE (op1) != mode)
>>>
>>>
>>> --- CUT ---
>>> And then this one to have ccmp to be expanded from the tree level:
>>> index cfc4a16..056e9b0 100644 (file)
>>> --- a/gcc/expr.c
>>> +++ b/gcc/expr.c
>>> @@ -9300,26 +9300,36 @@ used_in_cond_stmt_p (tree exp)
>>>   imm_use_iterator ui;
>>>   gimple use_stmt;
>>>   FOR_EACH_IMM_USE_STMT (use_stmt, ui, exp)
>>> -    if (gimple_code (use_stmt) == GIMPLE_COND)
>>> -      {
>>> -       tree op1 = gimple_cond_rhs (use_stmt);
>>> -       /* TBD: If we can convert all
>>> -           _Bool t;
>>> +    {
>>> +      if (gimple_code (use_stmt) == GIMPLE_COND)
>>> +       {
>>> +         tree op1 = gimple_cond_rhs (use_stmt);
>>> +         /* TBD: If we can convert all
>>> +             _Bool t;
>>>
>>> -           if (t == 1)
>>> -             goto <bb 3>;
>>> -           else
>>> -             goto <bb 4>;
>>> -          to
>>> -           if (t != 0)
>>> -             goto <bb 3>;
>>> -           else
>>> -             goto <bb 4>;
>>> -          we can remove the following check.  */
>>> -       if (integer_zerop (op1))
>>> -         expand_cond = true;
>>> -       BREAK_FROM_IMM_USE_STMT (ui);
>>> -      }
>>> +             if (t == 1)
>>> +               goto <bb 3>;
>>> +             else
>>> +               goto <bb 4>;
>>> +            to
>>> +             if (t != 0)
>>> +               goto <bb 3>;
>>> +             else
>>> +               goto <bb 4>;
>>> +            we can remove the following check.  */
>>> +         if (integer_zerop (op1))
>>> +           expand_cond = true;
>>> +         BREAK_FROM_IMM_USE_STMT (ui);
>>> +       }
>>> +      /* a = EXP ? b : c is also an use in conditional
>>> +         statement. */
>>> +      else if (gimple_code (use_stmt) == GIMPLE_ASSIGN
>>> +              && gimple_expr_code (use_stmt) == COND_EXPR)
>>> +       {
>>> +         if (gimple_assign_rhs1 (use_stmt) == exp)
>>> +           expand_cond = true;
>>> +       }
>>> +    }
>>>   return expand_cond;
>>> }
>>>
>>> Thanks,
>>> Andrew Pinski
>>>
>>>
>>>> diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c
>>>> index 2ca2278..8ee1266 100644
>>>> --- a/gcc/ifcvt.c
>>>> +++ b/gcc/ifcvt.c
>>>> @@ -43,6 +43,7 @@
>>>> #include "vec.h"
>>>> #include "pointer-set.h"
>>>> #include "dbgcnt.h"
>>>> +#include "ccmp.h"
>>>>
>>>> #ifndef HAVE_conditional_move
>>>> #define HAVE_conditional_move 0
>>>> @@ -786,6 +787,9 @@ struct noce_if_info
>>>>
>>>>   /* Estimated cost of the particular branch instruction.  */
>>>>   int branch_cost;
>>>> +
>>>> +  /* The COND is a conditional compare or not.  */
>>>> +  bool ccmp_p;
>>>> };
>>>>
>>>> static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
>>>> @@ -1407,9 +1411,16 @@ noce_emit_cmove (struct noce_if_info *if_info,
>>>> rtx x, enum rtx_code code,
>>>>       end_sequence ();
>>>>     }
>>>>
>>>> -  /* Don't even try if the comparison operands are weird.  */
>>>> -  if (! general_operand (cmp_a, GET_MODE (cmp_a))
>>>> -      || ! general_operand (cmp_b, GET_MODE (cmp_b)))
>>>> +  /* Don't even try if the comparison operands are weird
>>>> +     except conditional compare.  */
>>>> +  if (if_info->ccmp_p)
>>>> +    {
>>>> +      if (!(GET_MODE_CLASS (GET_MODE (cmp_a)) == MODE_CC
>>>> +           || GET_MODE_CLASS (GET_MODE (cmp_b)) == MODE_CC))
>>>> +       return NULL_RTX;
>>>> +    }
>>>> +  else if (! general_operand (cmp_a, GET_MODE (cmp_a))
>>>> +          || ! general_operand (cmp_b, GET_MODE (cmp_b)))
>>>>     return NULL_RTX;
>>>>
>>>> #if HAVE_conditional_move
>>>> @@ -1849,7 +1860,7 @@ noce_get_alt_condition (struct noce_if_info
>>>> *if_info, rtx target,
>>>>     }
>>>>
>>>>   cond = canonicalize_condition (if_info->jump, cond, reverse,
>>>> -                                earliest, target, false, true);
>>>> +                                earliest, target, if_info->ccmp_p, true);
>>>>   if (! cond || ! reg_mentioned_p (target, cond))
>>>>     return NULL;
>>>>
>>>> @@ -2300,6 +2311,7 @@ noce_get_condition (rtx jump, rtx *earliest,
>>>> bool then_else_reversed)
>>>> {
>>>>   rtx cond, set, tmp;
>>>>   bool reverse;
>>>> +  int allow_cc_mode = false;
>>>>
>>>>   if (! any_condjump_p (jump))
>>>>     return NULL_RTX;
>>>> @@ -2333,10 +2345,21 @@ noce_get_condition (rtx jump, rtx *earliest,
>>>> bool then_else_reversed)
>>>>       return cond;
>>>>     }
>>>>
>>>> +  /* For conditional compare, set ALLOW_CC_MODE to TRUE.  */
>>>> +  if (targetm.gen_ccmp_first)
>>>> +    {
>>>> +      rtx prev = prev_nonnote_nondebug_insn (jump);
>>>> +      if (prev
>>>> +         && NONJUMP_INSN_P (prev)
>>>> +         && BLOCK_FOR_INSN (prev) == BLOCK_FOR_INSN (jump)
>>>> +         && ccmp_insn_p (prev))
>>>> +       allow_cc_mode = true;
>>>> +    }
>>>> +
>>>>   /* Otherwise, fall back on canonicalize_condition to do the dirty
>>>>      work of manipulating MODE_CC values and COMPARE rtx codes.  */
>>>>   tmp = canonicalize_condition (jump, cond, reverse, earliest,
>>>> -                               NULL_RTX, false, true);
>>>> +                               NULL_RTX, allow_cc_mode, true);
>>>>
>>>>   /* We don't handle side-effects in the condition, like handling
>>>>      REG_INC notes and making sure no duplicate conditions are emitted.  */
>>>> @@ -2577,6 +2600,11 @@ noce_process_if_block (struct noce_if_info *if_info)
>>>>   if_info->a = a;
>>>>   if_info->b = b;
>>>>
>>>> +  if (targetm.gen_ccmp_first)
>>>> +    if (GET_MODE_CLASS (GET_MODE (XEXP (if_info->cond, 0))) == MODE_CC
>>>> +       || GET_MODE_CLASS (GET_MODE (XEXP (if_info->cond, 1))) == MODE_CC)
>>>> +      if_info->ccmp_p = true;
>>>> +
>>>>   /* Try optimizations in some approximation of a useful order.  */
>>>>   /* ??? Should first look to see if X is live incoming at all.  If it
>>>>      isn't, we don't need anything but an unconditional set.  */
>>>> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmn-csel-1.c
>>>> b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-1.c
>>>> new file mode 100644
>>>> index 0000000..472c271
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-1.c
>>>> @@ -0,0 +1,10 @@
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options " -O2 " } */
>>>> +
>>>> +int foo (int a, int b, int c, int d)
>>>> +{
>>>> +  return a < b && c > -5 ? d : 7;
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-assembler "ccmn" } } */
>>>> +/* { dg-final { scan-assembler "csel" } } */
>>>> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmn-csel-2.c
>>>> b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-2.c
>>>> new file mode 100644
>>>> index 0000000..c7d41d5
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-2.c
>>>> @@ -0,0 +1,10 @@
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options " -O2 " } */
>>>> +
>>>> +int foo (int a, int b, int c, int d)
>>>> +{
>>>> +  return a < b && c > -9 ? d : c;
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-assembler "ccmn" } } */
>>>> +/* { dg-final { scan-assembler "csel" } } */
>>>> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmn-csel-3.c
>>>> b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-3.c
>>>> new file mode 100644
>>>> index 0000000..9350e5d
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-3.c
>>>> @@ -0,0 +1,10 @@
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options " -O2 " } */
>>>> +
>>>> +int foo (int a, int b, int c, int d)
>>>> +{
>>>> +  return a > b && c <= -2 ? 9 : 7;
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-assembler "ccmn" } } */
>>>> +/* { dg-final { scan-assembler "csel" } } */
>>>> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-1.c
>>>> b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-1.c
>>>> new file mode 100644
>>>> index 0000000..2d0929f
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-1.c
>>>> @@ -0,0 +1,10 @@
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options " -O2 " } */
>>>> +
>>>> +int foo (int a, int b, int c, int d)
>>>> +{
>>>> +  return a < b && c <= d ? d : 7;
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-assembler "ccmp" } } */
>>>> +/* { dg-final { scan-assembler "csel" } } */
>>>> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-2.c
>>>> b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-2.c
>>>> new file mode 100644
>>>> index 0000000..978aa64
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-2.c
>>>> @@ -0,0 +1,10 @@
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options " -O2 " } */
>>>> +
>>>> +int foo (int a, int b, int c, int d)
>>>> +{
>>>> +  return a < b && c > d ? d : c;
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-assembler "ccmp" } } */
>>>> +/* { dg-final { scan-assembler "csel" } } */
>>>> diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-3.c
>>>> b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-3.c
>>>> new file mode 100644
>>>> index 0000000..7db1cd5
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-3.c
>>>> @@ -0,0 +1,10 @@
>>>> +/* { dg-do compile } */
>>>> +/* { dg-options " -O2 " } */
>>>> +
>>>> +int foo (int a, int b, int c, int d)
>>>> +{
>>>> +  return a > b && c <= d ? 9 : 7;
>>>> +}
>>>> +
>>>> +/* { dg-final { scan-assembler "ccmp" } } */
>>>> +/* { dg-final { scan-assembler "csel" } } */
diff mbox

Patch

diff --git a/gcc/ccmp.c b/gcc/ccmp.c
index e0670f1..6382974 100644
--- a/gcc/ccmp.c
+++ b/gcc/ccmp.c
@@ -25,6 +25,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tm_p.h"
 #include "tree.h"
 #include "stringpool.h"
+#include "stor-layout.h"
 #include "regs.h"
 #include "expr.h"
 #include "optabs.h"
@@ -133,6 +134,13 @@  used_in_cond_stmt_p (tree exp)
          expand_cond = true;
        BREAK_FROM_IMM_USE_STMT (ui);
       }
+    else if (gimple_code (use_stmt) == GIMPLE_ASSIGN
+            && gimple_expr_code (use_stmt) == COND_EXPR)
+      {
+       if (gimple_assign_rhs1 (use_stmt) == exp)
+         expand_cond = true;
+      }
+
   return expand_cond;
 }

@@ -274,9 +282,11 @@  expand_ccmp_expr (gimple g)
       icode = optab_handler (cstore_optab, cc_mode);
       if (icode != CODE_FOR_nothing)
        {
-         rtx target = gen_reg_rtx (word_mode);
-         tmp = emit_cstore (target, icode, NE, CCmode, CCmode,
-                            0, tmp, const0_rtx, 1, word_mode);
+         tree lhs = gimple_assign_lhs(g);
+         enum machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+         rtx target = gen_reg_rtx (mode);
+         tmp = emit_cstore (target, icode, NE, cc_mode, cc_mode,
+                            0, tmp, const0_rtx, 1, mode);
          if (tmp)
            return tmp;
        }
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index c25d940..88733f4 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -2337,6 +2337,19 @@ 
   "
 )

+(define_expand "cstorecc4"
+  [(set (match_operand:SI 0 "register_operand")
+       (match_operator 1 "aarch64_comparison_operator"
+        [(match_operand 2 "ccmp_cc_register")
+         (match_operand 3 "aarch64_plus_operand")]))]
+  ""
+"{
+  operands[3] = const0_rtx;
+  emit_insn (gen_rtx_SET (SImode, operands[0], operands[1]));
+  DONE;
+}")
+
+
 (define_expand "cstore<mode>4"
   [(set (match_operand:SI 0 "register_operand" "")
        (match_operator:SI 1 "aarch64_comparison_operator"
@@ -2485,15 +2498,19 @@ 
                           (match_operand:ALLI 3 "register_operand" "")))]
   ""
   {
-    rtx ccreg;
     enum rtx_code code = GET_CODE (operands[1]);

     if (code == UNEQ || code == LTGT)
       FAIL;

-    ccreg = aarch64_gen_compare_reg (code, XEXP (operands[1], 0),
-                                 XEXP (operands[1], 1));
-    operands[1] = gen_rtx_fmt_ee (code, VOIDmode, ccreg, const0_rtx);
+    if (!ccmp_cc_register (XEXP (operands[1], 0),
+                          GET_MODE (XEXP (operands[1], 0))))
+      {
+       rtx ccreg;
+       ccreg = aarch64_gen_compare_reg (code, XEXP (operands[1], 0),
+                                        XEXP (operands[1], 1));
+       operands[1] = gen_rtx_fmt_ee (code, VOIDmode, ccreg, const0_rtx);
+      }
   }
 )
diff --git a/gcc/expr.c b/gcc/expr.c
index 4c31521..88928aa 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -7986,7 +7986,9 @@  expand_cond_expr_using_cmove (tree treeop0
ATTRIBUTE_UNUSED,
       op00 = expand_normal (treeop0);
       op01 = const0_rtx;
       comparison_code = NE;
-      comparison_mode = TYPE_MODE (TREE_TYPE (treeop0));
+      comparison_mode = GET_MODE (op00);
+      if (comparison_mode == VOIDmode)
+       comparison_mode = TYPE_MODE (TREE_TYPE (treeop0));
     }

   if (GET_MODE (op1) != mode)
diff --git a/gcc/ifcvt.c b/gcc/ifcvt.c
index 2ca2278..733d583 100644
--- a/gcc/ifcvt.c
+++ b/gcc/ifcvt.c
@@ -43,6 +43,7 @@ 
 #include "vec.h"
 #include "pointer-set.h"
 #include "dbgcnt.h"
+#include "ccmp.h"

 #ifndef HAVE_conditional_move
 #define HAVE_conditional_move 0
@@ -786,6 +787,9 @@  struct noce_if_info

   /* Estimated cost of the particular branch instruction.  */
   int branch_cost;
+
+  /* The COND is a conditional compare or not.  */
+  bool ccmp_p;
 };

 static rtx noce_emit_store_flag (struct noce_if_info *, rtx, int, int);
@@ -1407,9 +1411,16 @@  noce_emit_cmove (struct noce_if_info *if_info,
rtx x, enum rtx_code code,
       end_sequence ();
     }

-  /* Don't even try if the comparison operands are weird.  */
-  if (! general_operand (cmp_a, GET_MODE (cmp_a))
-      || ! general_operand (cmp_b, GET_MODE (cmp_b)))
+  /* Don't even try if the comparison operands are weird
+     except conditional compare.  */
+  if (if_info->ccmp_p)
+    {
+      if (!(GET_MODE_CLASS (GET_MODE (cmp_a)) == MODE_CC
+           || GET_MODE_CLASS (GET_MODE (cmp_b)) == MODE_CC))
+       return NULL_RTX;
+    }
+  else if (! general_operand (cmp_a, GET_MODE (cmp_a))
+          || ! general_operand (cmp_b, GET_MODE (cmp_b)))
     return NULL_RTX;

 #if HAVE_conditional_move
@@ -1849,7 +1860,7 @@  noce_get_alt_condition (struct noce_if_info
*if_info, rtx target,
     }

   cond = canonicalize_condition (if_info->jump, cond, reverse,
-                                earliest, target, false, true);
+                                earliest, target, if_info->ccmp_p, true);
   if (! cond || ! reg_mentioned_p (target, cond))
     return NULL;

@@ -2300,6 +2311,7 @@  noce_get_condition (rtx jump, rtx *earliest,
bool then_else_reversed)
 {
   rtx cond, set, tmp;
   bool reverse;
+  int allow_cc_mode = false;

   if (! any_condjump_p (jump))
     return NULL_RTX;
@@ -2333,10 +2345,21 @@  noce_get_condition (rtx jump, rtx *earliest,
bool then_else_reversed)
       return cond;
     }

+  /* For conditional compare, set ALLOW_CC_MODE to TRUE.  */
+  if (targetm.gen_ccmp_first)
+    {
+      rtx prev = prev_nonnote_nondebug_insn (jump);
+      if (prev
+         && NONJUMP_INSN_P (prev)
+         && BLOCK_FOR_INSN (prev) == BLOCK_FOR_INSN (jump)
+         && ccmp_insn_p (prev))
+       allow_cc_mode = true;
+    }
+
   /* Otherwise, fall back on canonicalize_condition to do the dirty
      work of manipulating MODE_CC values and COMPARE rtx codes.  */
   tmp = canonicalize_condition (jump, cond, reverse, earliest,
-                               NULL_RTX, false, true);
+                               NULL_RTX, allow_cc_mode, true);

   /* We don't handle side-effects in the condition, like handling
      REG_INC notes and making sure no duplicate conditions are emitted.  */
@@ -2577,6 +2600,11 @@  noce_process_if_block (struct noce_if_info *if_info)
   if_info->a = a;
   if_info->b = b;

+  if (targetm.gen_ccmp_first)
+    if (GET_MODE_CLASS (GET_MODE (XEXP (if_info->cond, 0))) == MODE_CC
+       || GET_MODE_CLASS (GET_MODE (XEXP (if_info->cond, 1))) == MODE_CC)
+      if_info->ccmp_p = true;
+
   /* Try optimizations in some approximation of a useful order.  */
   /* ??? Should first look to see if X is live incoming at all.  If it
      isn't, we don't need anything but an unconditional set.  */

diff --git a/gcc/testsuite/gcc.target/aarch64/ccmn-csel-1.c
b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-1.c
new file mode 100644
index 0000000..472c271
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-1.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return a < b && c > -5 ? d : 7;
+}
+
+/* { dg-final { scan-assembler "ccmn" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmn-csel-2.c
b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-2.c
new file mode 100644
index 0000000..c7d41d5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-2.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return a < b && c > -9 ? d : c;
+}
+
+/* { dg-final { scan-assembler "ccmn" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmn-csel-3.c
b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-3.c
new file mode 100644
index 0000000..9350e5d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmn-csel-3.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return a > b && c <= -2 ? 9 : 7;
+}
+
+/* { dg-final { scan-assembler "ccmn" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-1.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-1.c
new file mode 100644
index 0000000..2d0929f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-1.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return a < b && c <= d ? d : 7;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-2.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-2.c
new file mode 100644
index 0000000..978aa64
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-2.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return a < b && c > d ? d : c;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-3.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-3.c
new file mode 100644
index 0000000..7db1cd5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-3.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return a > b && c <= d ? 9 : 7;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-4.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-4.c
new file mode 100644
index 0000000..51b1ce8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-4.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b, int c, int d)
+{
+  return (a > 0 && b < 7) ? c : d;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-csel-5.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-5.c
new file mode 100644
index 0000000..04d27fa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-csel-5.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+_Bool foo (int a, int b, _Bool c, _Bool d)
+{
+  return (a > 0 && b < 7) ? c : d;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "csel" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-cset-1.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-cset-1.c
new file mode 100644
index 0000000..fa3dd5b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-cset-1.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+int foo (int a, int b)
+{
+  return a > 0 && b < 7;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "cset" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp-cset-2.c
b/gcc/testsuite/gcc.target/aarch64/ccmp-cset-2.c
new file mode 100644
index 0000000..afcd4d0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp-cset-2.c
@@ -0,0 +1,10 @@ 
+/* { dg-do compile } */
+/* { dg-options " -O2 " } */
+
+_Bool foo (int a, int b)
+{
+  return a > 0 && b < 7;
+}
+
+/* { dg-final { scan-assembler "ccmp" } } */
+/* { dg-final { scan-assembler "cset" } } */