diff mbox

[1/n] Add conditional compare framework in middle-end

Message ID CACgzC7BSeXOPrusUkz4mSSJ9+at5cfT68aqsKRjkjMzL+Wji9Q@mail.gmail.com
State New
Headers show

Commit Message

Zhenqiang Chen Feb. 24, 2014, 9:18 a.m. UTC
Hi,

The patch (http://gcc.gnu.org/ml/gcc-patches/2014-11/msg03622.html) is
re-based (the arm port change is stripped as a separate patch), which
includes only the middle-end changes. The basic logic for the patch
is:

  1) Identify conditional compare candidates when expanding one GIMPLE_COND.
  2) For a candidate, it uses two target hooks to generate the expected codes:
     - gen_ccmp_first expands the first compare in CCMP.
     - gen_ccmp_next expands the following compares.

     Another hook select_ccmp_cmp_order is called to determine which compare
     is done first since not all combination of compares are legal in some
     target like ARM.  We might get more chance when swapping the compares.
     For AARCH64 and IA64, we do not need such check.

     During expanding, we must make sure that no instruction can clobber the
     CC reg except the compares.

     If the final result is not used in a COND_EXPR, it calls cstorecc4 pattern
      to store the CC to a general register.

Bootstrap and no make check regression on X86-64.

Is is OK for next stage1?

Thanks!
-Zhenqiang

ChangeLog:
2014-02-24  Zhenqiang Chen  <zhenqiang.chen@linaro.org>

    * cfgexpand.c (expand_gimple_cond): Check ccmp support.
    * doc/md.texi (ccmp): New index.
    * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
    TARGET_GEN_CCMP_NEXT): New hooks.
    * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
    TARGET_GEN_CCMP_NEXT): New hooks.
    * doc/tm.texi.in (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
    TARGET_GEN_CCMP_NEXT): New hooks.
    * expmed.c (emit_cstore): Make it global.
    * expr.c: Include tree-phinodes.h and ssa-iterators.h.
    (ccmp_candidate_p, used_in_cond_stmt_p, check_clobber_cc, clobber_cc_p,
    gen_ccmp_next, expand_ccmp_expr_1, expand_ccmp_expr): New functions.
    (expand_expr_real_1): Handle conditional compare.
    * optabs.c (get_rtx_code): Make it global and handle BIT_AND_EXPR and
    BIT_IOR_EXPR.
    * optabs.h (get_rtx_code, emit_cstore): New prototypes.
    * recog.c (ccmp_insn_p): New function.
    (simplify_while_replacing): Do not swap ccmp insn.
    * target.def (select_ccmp_cmp_order, gen_ccmp_first, gen_ccmp_next):
    Define hooks.
    * targhooks.c (default_select_ccmp_cmp_order): New function.
    * targhooks.h (default_select_ccmp_cmp_order): New prototypes.

Comments

Andrew Pinski June 15, 2014, 2:50 a.m. UTC | #1
On Mon, Feb 24, 2014 at 1:18 AM, Zhenqiang Chen
<zhenqiang.chen@linaro.org> wrote:
> Hi,
>
> The patch (http://gcc.gnu.org/ml/gcc-patches/2014-11/msg03622.html) is
> re-based (the arm port change is stripped as a separate patch), which
> includes only the middle-end changes. The basic logic for the patch
> is:
>
>   1) Identify conditional compare candidates when expanding one GIMPLE_COND.
>   2) For a candidate, it uses two target hooks to generate the expected codes:
>      - gen_ccmp_first expands the first compare in CCMP.
>      - gen_ccmp_next expands the following compares.
>
>      Another hook select_ccmp_cmp_order is called to determine which compare
>      is done first since not all combination of compares are legal in some
>      target like ARM.  We might get more chance when swapping the compares.
>      For AARCH64 and IA64, we do not need such check.
>
>      During expanding, we must make sure that no instruction can clobber the
>      CC reg except the compares.
>
>      If the final result is not used in a COND_EXPR, it calls cstorecc4 pattern
>       to store the CC to a general register.
>
> Bootstrap and no make check regression on X86-64.

I was testing this patch set out on AARCH64 with an addition patch to
define cstorecc4 and found an issue:
+  rtx target = gen_reg_rtx (word_mode);
+  tmp = emit_cstore (target, icode, NE, CCmode, CCmode,
+     0, tmp, const0_rtx, 1, word_mode);
+  if (tmp)
+    return tmp;

The wrong mode is being used in the above code.
The testcase is:
_Bool f(int a, int b)
{
  return a != 0 && b != 0;
}
---- CUT ----

I am working on the patch to fix this issue.

Thanks,
Andrew


>
> Is is OK for next stage1?
>
> Thanks!
> -Zhenqiang
>
> ChangeLog:
> 2014-02-24  Zhenqiang Chen  <zhenqiang.chen@linaro.org>
>
>     * cfgexpand.c (expand_gimple_cond): Check ccmp support.
>     * doc/md.texi (ccmp): New index.
>     * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
>     TARGET_GEN_CCMP_NEXT): New hooks.
>     * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
>     TARGET_GEN_CCMP_NEXT): New hooks.
>     * doc/tm.texi.in (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
>     TARGET_GEN_CCMP_NEXT): New hooks.
>     * expmed.c (emit_cstore): Make it global.
>     * expr.c: Include tree-phinodes.h and ssa-iterators.h.
>     (ccmp_candidate_p, used_in_cond_stmt_p, check_clobber_cc, clobber_cc_p,
>     gen_ccmp_next, expand_ccmp_expr_1, expand_ccmp_expr): New functions.
>     (expand_expr_real_1): Handle conditional compare.
>     * optabs.c (get_rtx_code): Make it global and handle BIT_AND_EXPR and
>     BIT_IOR_EXPR.
>     * optabs.h (get_rtx_code, emit_cstore): New prototypes.
>     * recog.c (ccmp_insn_p): New function.
>     (simplify_while_replacing): Do not swap ccmp insn.
>     * target.def (select_ccmp_cmp_order, gen_ccmp_first, gen_ccmp_next):
>     Define hooks.
>     * targhooks.c (default_select_ccmp_cmp_order): New function.
>     * targhooks.h (default_select_ccmp_cmp_order): New prototypes.
Andrew Pinski June 16, 2014, 3:11 a.m. UTC | #2
On Sat, Jun 14, 2014 at 7:50 PM, Andrew Pinski <pinskia@gmail.com> wrote:
> On Mon, Feb 24, 2014 at 1:18 AM, Zhenqiang Chen
> <zhenqiang.chen@linaro.org> wrote:
>> Hi,
>>
>> The patch (http://gcc.gnu.org/ml/gcc-patches/2014-11/msg03622.html) is
>> re-based (the arm port change is stripped as a separate patch), which
>> includes only the middle-end changes. The basic logic for the patch
>> is:
>>
>>   1) Identify conditional compare candidates when expanding one GIMPLE_COND.
>>   2) For a candidate, it uses two target hooks to generate the expected codes:
>>      - gen_ccmp_first expands the first compare in CCMP.
>>      - gen_ccmp_next expands the following compares.
>>
>>      Another hook select_ccmp_cmp_order is called to determine which compare
>>      is done first since not all combination of compares are legal in some
>>      target like ARM.  We might get more chance when swapping the compares.
>>      For AARCH64 and IA64, we do not need such check.
>>
>>      During expanding, we must make sure that no instruction can clobber the
>>      CC reg except the compares.
>>
>>      If the final result is not used in a COND_EXPR, it calls cstorecc4 pattern
>>       to store the CC to a general register.
>>
>> Bootstrap and no make check regression on X86-64.
>
> I was testing this patch set out on AARCH64 with an addition patch to
> define cstorecc4 and found an issue:
> +  rtx target = gen_reg_rtx (word_mode);
> +  tmp = emit_cstore (target, icode, NE, CCmode, CCmode,
> +     0, tmp, const0_rtx, 1, word_mode);
> +  if (tmp)
> +    return tmp;
>
> The wrong mode is being used in the above code.
> The testcase is:
> _Bool f(int a, int b)
> {
>   return a != 0 && b != 0;
> }
> ---- CUT ----
>
> I am working on the patch to fix this issue.


I have a fix and it is unrelated really to this patch except it is
just exposed by it.  I will be submitting it in the next few days.

Thanks,
Andrew

>
> Thanks,
> Andrew
>
>
>>
>> Is is OK for next stage1?
>>
>> Thanks!
>> -Zhenqiang
>>
>> ChangeLog:
>> 2014-02-24  Zhenqiang Chen  <zhenqiang.chen@linaro.org>
>>
>>     * cfgexpand.c (expand_gimple_cond): Check ccmp support.
>>     * doc/md.texi (ccmp): New index.
>>     * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
>>     TARGET_GEN_CCMP_NEXT): New hooks.
>>     * doc/tm.texi (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
>>     TARGET_GEN_CCMP_NEXT): New hooks.
>>     * doc/tm.texi.in (TARGET_SELECT_CCMP_CMP_ORDER, TARGET_GEN_CCMP_FIRST,
>>     TARGET_GEN_CCMP_NEXT): New hooks.
>>     * expmed.c (emit_cstore): Make it global.
>>     * expr.c: Include tree-phinodes.h and ssa-iterators.h.
>>     (ccmp_candidate_p, used_in_cond_stmt_p, check_clobber_cc, clobber_cc_p,
>>     gen_ccmp_next, expand_ccmp_expr_1, expand_ccmp_expr): New functions.
>>     (expand_expr_real_1): Handle conditional compare.
>>     * optabs.c (get_rtx_code): Make it global and handle BIT_AND_EXPR and
>>     BIT_IOR_EXPR.
>>     * optabs.h (get_rtx_code, emit_cstore): New prototypes.
>>     * recog.c (ccmp_insn_p): New function.
>>     (simplify_while_replacing): Do not swap ccmp insn.
>>     * target.def (select_ccmp_cmp_order, gen_ccmp_first, gen_ccmp_next):
>>     Define hooks.
>>     * targhooks.c (default_select_ccmp_cmp_order): New function.
>>     * targhooks.h (default_select_ccmp_cmp_order): New prototypes.
diff mbox

Patch

diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 06d494c..a57e11c 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2046,9 +2046,10 @@  expand_gimple_cond (basic_block bb, gimple stmt)
 	      op0 = gimple_assign_rhs1 (second);
 	      op1 = gimple_assign_rhs2 (second);
 	    }
-	  /* If jumps are cheap turn some more codes into
-	     jumpy sequences.  */
-	  else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4)
+	  /* If jumps are cheap and the target does not support conditional
+	     compare, turn some more codes into jumpy sequences.  */
+	  else if (BRANCH_COST (optimize_insn_for_speed_p (), false) < 4
+		   && (targetm.gen_ccmp_first == NULL))
 	    {
 	      if ((code2 == BIT_AND_EXPR
 		   && TYPE_PRECISION (TREE_TYPE (op0)) == 1
diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi
index 746acc2..dfcdc61 100644
--- a/gcc/doc/md.texi
+++ b/gcc/doc/md.texi
@@ -6206,6 +6206,42 @@  A typical @code{ctrap} pattern looks like
   "@dots{}")
 @end smallexample
 
+@cindex @code{ccmp} instruction pattern
+@item @samp{ccmp}
+Conditional compare instruction.  Operand 2 and 5 are RTLs which perform
+two comparisons.  Operand 1 is AND or IOR, which operates on the result of
+Operand 2 and 5.  Operand 0 is the result of operand 1.
+It uses recursive method to support more than two compares.  e.g.
+
+  CC0 = CMP (a, b);
+  CC1 = CCMP (NE (CC0, 0), CMP (e, f));
+  ...
+  CCn = CCMP (NE (CCn-1, 0), CMP (...));
+
+Two target hooks are used to generate conditional compares.  GEN_CCMP_FISRT
+is used to generate the first CMP.  And GEN_CCMP_NEXT is used to generate the
+following CCMPs.  Operand 1 is AND or IOR.  Operand 3 is the result of
+GEN_CCMP_FISRT or a previous GEN_CCMP_NEXT.  Operand 2 is NE.
+Operand 4, 5 and 6 is another compare expression.
+
+A typical CCMP pattern looks like
+
+@smallexample
+(define_insn "*ccmp_and_ior"
+  [(set (match_operand 6 "dominant_cc_register" "")
+        (compare
+         (match_operator 1
+          (match_operator 2 "comparison_operator"
+           [(match_operand 3 "dominant_cc_register")
+            (const_int 0)])
+          (match_operator 4 "comparison_operator"
+           [(match_operand 5 "register_operand")
+            (match_operand 6 "compare_operand"]))
+         (const_int 0)))]
+  ""
+  "@dots{}")
+@end smallexample
+
 @cindex @code{prefetch} instruction pattern
 @item @samp{prefetch}
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index f204936..0aedc31 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11180,6 +11180,32 @@  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} int TARGET_SELECT_CCMP_CMP_ORDER (int @var{code1}, int @var{code2})
+For some target (like ARM), the order of two compares is sensitive for
+conditional compare.  cmp0-cmp1 might be an invalid combination.  But when
+swapping the order, cmp1-cmp0 is valid.  The function will return
+   1: Keep current order.
+  -1: Swap the two compares.
+   0: Invalid combination.
+@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 to @code{cbranch_optab}.
+ @code{unsignedp} is used when converting @code{op0} and @code{op1}'s mode.
+@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 @code{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 @code{prev} and this comparison is
+ not supported, otherwise the result must be appropriate for passing to
+ @code{gen_ccmp_next} or @code{cbranch_optab}.  @code{bit_code}
+ is AND or IOR, which is the op on the two compares.
+@end deftypefn
+
 @deftypefn {Target Hook} unsigned TARGET_LOOP_UNROLL_ADJUST (unsigned @var{nunroll}, struct loop *@var{loop})
 This target hook returns a new value for the number of times @var{loop}
 should be unrolled. The parameter @var{nunroll} is the number of times
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 50f412c..ecbb8b6 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -8283,6 +8283,12 @@  build_type_attribute_variant (@var{mdecl},
 
 @hook TARGET_HAVE_CONDITIONAL_EXECUTION
 
+@hook TARGET_SELECT_CCMP_CMP_ORDER
+
+@hook TARGET_GEN_CCMP_FIRST
+
+@hook TARGET_GEN_CCMP_NEXT
+
 @hook TARGET_LOOP_UNROLL_ADJUST
 
 @defmac POWI_MAX_MULTS
diff --git a/gcc/expmed.c b/gcc/expmed.c
index 7c1c979..4ff90ce 100644
--- a/gcc/expmed.c
+++ b/gcc/expmed.c
@@ -5160,7 +5160,7 @@  expand_and (enum machine_mode mode, rtx op0, rtx op1, rtx target)
 }
 
 /* Helper function for emit_store_flag.  */
-static rtx
+rtx
 emit_cstore (rtx target, enum insn_code icode, enum rtx_code code,
 	     enum machine_mode mode, enum machine_mode compare_mode,
 	     int unsignedp, rtx x, rtx y, int normalizep,
diff --git a/gcc/expr.c b/gcc/expr.c
index f6708da..d2c6f6b 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -67,6 +67,8 @@  along with GCC; see the file COPYING3.  If not see
 #include "params.h"
 #include "tree-ssa-address.h"
 #include "cfgexpand.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
 
 /* Decide whether a function's arguments should be processed
    from first to last or from last to first.
@@ -9228,6 +9230,299 @@  expand_expr_real_2 (sepops ops, rtx target, enum machine_mode tmode,
 }
 #undef REDUCE_BIT_FIELD
 
+/* The following functions expand conditional compare (CCMP) instructions.
+   Here is a short description about the over all algorithm:
+     * ccmp_candidate_p is used to identify the CCMP candidate
+
+     * expand_ccmp_expr is the main entry, which calls expand_ccmp_expr_1
+       to expand CCMP.
+
+     * expand_ccmp_expr_1 uses a recursive algorithm to expand CCMP.
+       It calls two target hooks gen_ccmp_first and gen_ccmp_next to generate
+       CCMP instructions.
+	 - gen_ccmp_first expands the first compare in CCMP.
+	 - gen_ccmp_next expands the following compares.
+
+       Another hook select_ccmp_cmp_order is called to determine which compare
+       is done first since not all combination of compares are legal in some
+       target like ARM.  We might get more chance when swapping the compares.
+
+       During expanding, we must make sure that no instruction can clobber the
+       CC reg except the compares.  So clobber_cc_p and check_clobber_cc are
+       introduced to do the check.
+
+     * 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.  */
+
+/* Check whether G is a potential conditional compare candidate.  */
+static bool
+ccmp_candidate_p (gimple g)
+{
+  tree rhs = gimple_assign_rhs_to_tree (g);
+  tree lhs, op0, op1;
+  gimple gs0, gs1;
+  enum tree_code tcode, tcode0, tcode1;
+  tcode = TREE_CODE (rhs);
+
+  if (tcode != BIT_AND_EXPR && tcode != BIT_IOR_EXPR)
+    return false;
+
+  lhs = gimple_assign_lhs (g);
+  op0 = TREE_OPERAND (rhs, 0);
+  op1 = TREE_OPERAND (rhs, 1);
+
+  if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME)
+      || !has_single_use (lhs))
+    return false;
+
+  gs0 = get_gimple_for_ssa_name (op0);
+  gs1 = get_gimple_for_ssa_name (op1);
+  if (!gs0 || !gs1 || !is_gimple_assign (gs0) || !is_gimple_assign (gs1)
+      /* g, gs0 and gs1 must be in the same basic block, since current stage
+	 is out-of-ssa.  We can not guarantee the correctness when forwording
+	 the gs0 and gs1 into g whithout DATAFLOW analysis.  */
+      || gimple_bb (gs0) != gimple_bb (gs1)
+      || gimple_bb (gs0) != gimple_bb (g))
+    return false;
+
+  if (!(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0)))
+       || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs0))))
+      || !(INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1)))
+	   || POINTER_TYPE_P (TREE_TYPE (gimple_assign_rhs1 (gs1)))))
+    return false;
+
+  tcode0 = gimple_assign_rhs_code (gs0);
+  tcode1 = gimple_assign_rhs_code (gs1);
+  if (TREE_CODE_CLASS (tcode0) == tcc_comparison
+      && TREE_CODE_CLASS (tcode1) == tcc_comparison)
+    return true;
+  if (TREE_CODE_CLASS (tcode0) == tcc_comparison
+      && ccmp_candidate_p (gs1))
+    return true;
+  else if (TREE_CODE_CLASS (tcode1) == tcc_comparison
+	   && ccmp_candidate_p (gs0))
+    return true;
+  /* We skip ccmp_candidate_p (gs1) && ccmp_candidate_p (gs0) since
+     there is no way to set the CC flag.  */
+  return false;
+}
+
+/* Check whether EXP is used in a GIMPLE_COND statement or not.  */
+static bool
+used_in_cond_stmt_p (tree exp)
+{
+  bool expand_cond = false;
+  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 (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);
+      }
+  return expand_cond;
+}
+
+/* If SETTER clobber CC reg, set DATA to TRUE.  */
+
+static void
+check_clobber_cc (rtx reg, const_rtx setter, void *data)
+{
+  if (GET_CODE (setter) == CLOBBER && GET_MODE (reg) == CCmode)
+    *(bool *)data = true;
+}
+
+/* Check whether INSN and all its NEXT_INSN clobber CC reg or not.  */
+
+static bool
+clobber_cc_p (rtx insn)
+{
+  bool clobber = false;
+  for (; insn; insn = NEXT_INSN (insn))
+    {
+      note_stores (PATTERN (insn), check_clobber_cc, &clobber);
+      if (clobber)
+	return true;
+    }
+  return false;
+}
+
+/* Help function to generate conditional compare.  PREV is the result of
+   GEN_CCMP_FIRST or GEN_CCMP_NEXT.  G is the next compare.
+   CODE is BIT_AND_EXPR or BIT_IOR_EXPR.  */
+
+static rtx
+gen_ccmp_next (rtx prev, gimple g, enum tree_code code)
+{
+  rtx op0, op1;
+  int unsignedp = TYPE_UNSIGNED (TREE_TYPE (gimple_assign_rhs1 (g)));
+  enum rtx_code rcode = get_rtx_code (gimple_assign_rhs_code (g), unsignedp);
+  rtx last = get_last_insn ();
+
+  expand_operands (gimple_assign_rhs1 (g),
+		   gimple_assign_rhs2 (g),
+		   NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+
+  /* If any operand clobbers CC reg, we will give up.  */
+  if (clobber_cc_p (NEXT_INSN (last)))
+    return NULL_RTX;
+
+  return targetm.gen_ccmp_next (prev, rcode, op0, op1, get_rtx_code (code, 0));
+}
+
+/* Expand conditional compare gimple G.  A typical CCMP sequence is like:
+
+     CC0 = CMP (a, b);
+     CC1 = CCMP (NE (CC0, 0), CMP (e, f));
+     ...
+     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.  */
+
+static rtx
+expand_ccmp_expr_1 (gimple g)
+{
+  tree exp = gimple_assign_rhs_to_tree (g);
+  enum tree_code code = TREE_CODE (exp);
+  gimple gs0 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 0));
+  gimple gs1 = get_gimple_for_ssa_name (TREE_OPERAND (exp, 1));
+  rtx tmp;
+  enum tree_code code0 = gimple_assign_rhs_code (gs0);
+  enum tree_code code1 = gimple_assign_rhs_code (gs1);
+
+  gcc_assert (code == BIT_AND_EXPR || code == BIT_IOR_EXPR);
+  gcc_assert (gs0 && gs1 && is_gimple_assign (gs0) && is_gimple_assign (gs1));
+
+  if (TREE_CODE_CLASS (code0) == tcc_comparison)
+    {
+      if (TREE_CODE_CLASS (code1) == tcc_comparison)
+	{
+	  int unsignedp0, unsignedp1, cmp_order;
+	  enum rtx_code rcode0, rcode1, rcode;
+	  rtx op0, op1, op2, op3, tmp;
+	  gimple first, next;
+
+	  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);
+
+	  /* For some target (like ARM), the order of two compares is sensitive
+	     for conditional compare.  cmp0-cmp1 might be an invalid combination.
+	     But when swapping the order, cmp1-cmp0 is valid.  The target hook
+	     select_ccmp_cmp_order will return
+		 1: Keep current order.
+		-1: Swap the two compares.
+		 0: Invalid combination.  */
+
+	  cmp_order = targetm.select_ccmp_cmp_order (rcode0, rcode1);
+	  /* Invalid combination.  */
+	  if (!cmp_order)
+	    return NULL_RTX;
+	  /* Swap the compare order.  Do gs1 first.  */
+	  if (cmp_order == -1)
+	    {
+	      first = gs1;
+	      next = gs0;
+	      rcode = rcode1;
+	    }
+	  else
+	    {
+	      first = gs0;
+	      next = gs1;
+	      rcode = rcode0;
+	    }
+	    expand_operands (gimple_assign_rhs1 (first),
+			     gimple_assign_rhs2 (first),
+			     NULL_RTX, &op0, &op1, EXPAND_NORMAL);
+
+	    /* Since the operands of NEXT might clobber CC reg, we expand the
+	       operands of NEXT before GEN_CCMP_FIRST.  */
+	    expand_operands (gimple_assign_rhs1 (next),
+			     gimple_assign_rhs2 (next),
+			     NULL_RTX, &op2, &op3, EXPAND_NORMAL);
+	    tmp = targetm.gen_ccmp_first (rcode, op0, op1);
+	    if (!tmp)
+	      return NULL_RTX;
+
+	    return targetm.gen_ccmp_next (tmp,
+					  rcode == rcode1 ? rcode0 : rcode1,
+					  op2, op3, get_rtx_code (code, 0));
+	}
+      gcc_assert (code1 == BIT_AND_EXPR || code1 == BIT_IOR_EXPR);
+      tmp = expand_ccmp_expr_1 (gs1);
+      if (tmp)
+	return gen_ccmp_next (tmp, gs0, code);
+    }
+  else
+    {
+      gcc_assert (gimple_assign_rhs_code (gs0) == BIT_AND_EXPR
+                  || gimple_assign_rhs_code (gs0) == BIT_IOR_EXPR);
+      if (TREE_CODE_CLASS (gimple_assign_rhs_code (gs1)) == tcc_comparison)
+	{
+	  tmp = expand_ccmp_expr_1 (gs0);
+	  if (tmp)
+	    return gen_ccmp_next (tmp, gs1, code);
+	}
+      else
+	{
+	  gcc_assert (gimple_assign_rhs_code (gs1) == BIT_AND_EXPR
+		      || gimple_assign_rhs_code (gs1) == BIT_IOR_EXPR);
+	}
+    }
+
+  return NULL_RTX;
+}
+
+static rtx
+expand_ccmp_expr (gimple g)
+{
+  rtx last = get_last_insn ();
+  tree lhs = gimple_assign_lhs (g);
+  rtx tmp = expand_ccmp_expr_1 (g);
+
+  if (tmp)
+    {
+      enum insn_code icode;
+      /* 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;
+
+      /* If TMP is not used in a GIMPLE_COND, store it with a csctorecc4_optab.
+	 Note: Target needs to define "cstorecc4".  */
+      icode = optab_handler (cstore_optab, CCmode);
+      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);
+	  if (tmp)
+	    return tmp;
+	}
+    }
+
+  /* Clean up.  */
+  delete_insns_since (last);
+  return NULL_RTX;
+}
 
 /* Return TRUE if expression STMT is suitable for replacement.  
    Never consider memory loads as replaceable, because those don't ever lead 
@@ -9392,10 +9687,22 @@  expand_expr_real_1 (tree exp, rtx target, enum machine_mode tmode,
 	{
 	  rtx r;
 	  location_t saved_loc = curr_insn_location ();
+	  tree rhs = gimple_assign_rhs_to_tree (g);
 
 	  set_curr_insn_location (gimple_location (g));
-	  r = expand_expr_real (gimple_assign_rhs_to_tree (g), target,
-				tmode, modifier, NULL, inner_reference_p);
+
+	  if ((targetm.gen_ccmp_first != NULL) && ccmp_candidate_p (g))
+	    {
+	      gcc_checking_assert (targetm.gen_ccmp_next != NULL);
+	      r = expand_ccmp_expr (g);
+	      if (!r)
+		r = expand_expr_real (rhs, target, tmode, modifier,
+				      NULL, inner_reference_p);
+	    }
+	  else
+	    r = expand_expr_real (rhs, target, tmode, modifier,
+				  NULL, inner_reference_p);
+
 	  set_curr_insn_location (saved_loc);
 	  if (REG_P (r) && !REG_EXPR (r))
 	    set_reg_attrs_for_decl_rtl (SSA_NAME_VAR (exp), r);
diff --git a/gcc/optabs.c b/gcc/optabs.c
index cec25a4..7fdbf47 100644
--- a/gcc/optabs.c
+++ b/gcc/optabs.c
@@ -6398,7 +6398,7 @@  gen_cond_trap (enum rtx_code code, rtx op1, rtx op2, rtx tcode)
 /* Return rtx code for TCODE. Use UNSIGNEDP to select signed
    or unsigned operation code.  */
 
-static enum rtx_code
+enum rtx_code
 get_rtx_code (enum tree_code tcode, bool unsignedp)
 {
   enum rtx_code code;
@@ -6448,6 +6448,12 @@  get_rtx_code (enum tree_code tcode, bool unsignedp)
       code = LTGT;
       break;
 
+    case BIT_AND_EXPR:
+      code = AND;
+      break;
+    case BIT_IOR_EXPR:
+      code = IOR;
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/optabs.h b/gcc/optabs.h
index 8ecaa41..4426270 100644
--- a/gcc/optabs.h
+++ b/gcc/optabs.h
@@ -91,7 +91,7 @@  extern rtx expand_widen_pattern_expr (sepops ops, rtx op0, rtx op1, rtx wide_op,
 extern rtx expand_ternary_op (enum machine_mode mode, optab ternary_optab,
 			      rtx op0, rtx op1, rtx op2, rtx target,
 			      int unsignedp);
-
+extern enum rtx_code get_rtx_code (enum tree_code tcode, bool unsignedp);
 /* Expand a binary operation given optab and rtx operands.  */
 extern rtx expand_binop (enum machine_mode, optab, rtx, rtx, rtx, int,
 			 enum optab_methods);
@@ -556,4 +556,9 @@  extern void gen_satfractuns_conv_libfunc (convert_optab, const char *,
 					  enum machine_mode);
 extern void init_tree_optimization_optabs (tree);
 
+extern rtx emit_cstore (rtx target, enum insn_code icode, enum rtx_code code,
+			enum machine_mode mode, enum machine_mode compare_mode,
+			int unsignedp, rtx x, rtx y, int normalizep,
+			enum machine_mode target_mode);
+
 #endif /* GCC_OPTABS_H */
diff --git a/gcc/recog.c b/gcc/recog.c
index f9040dc..32a2648 100644
--- a/gcc/recog.c
+++ b/gcc/recog.c
@@ -556,6 +556,19 @@  cancel_changes (int num)
 #define CODE_FOR_extzv	CODE_FOR_nothing
 #endif
 
+static bool
+ccmp_insn_p (rtx object)
+{
+  rtx x = PATTERN (object);
+  if (targetm.gen_ccmp_first
+      && GET_CODE (x) == SET
+      && GET_CODE (XEXP (x, 1)) == COMPARE
+      && (GET_CODE (XEXP (XEXP (x, 1), 0)) == IOR
+	  || GET_CODE (XEXP (XEXP (x, 1), 0)) == AND))
+    return true;
+  return false;
+}
+
 /* A subroutine of validate_replace_rtx_1 that tries to simplify the resulting
    rtx.  */
 
@@ -567,7 +580,8 @@  simplify_while_replacing (rtx *loc, rtx to, rtx object,
   enum rtx_code code = GET_CODE (x);
   rtx new_rtx = NULL_RTX;
 
-  if (SWAPPABLE_OPERANDS_P (x)
+  /* Do not swap compares in conditional compare instruction.  */
+  if (SWAPPABLE_OPERANDS_P (x) && !ccmp_insn_p (object)
       && swap_commutative_operands_p (XEXP (x, 0), XEXP (x, 1)))
     {
       validate_unshare_change (object, loc,
diff --git a/gcc/target.def b/gcc/target.def
index 3a64cd1..e34d4a1 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2334,6 +2334,38 @@  modes and they have different conditional execution capability, such as ARM.",
  bool, (void),
  default_have_conditional_execution)
 
+DEFHOOK
+(select_ccmp_cmp_order,
+ "For some target (like ARM), the order of two compares is sensitive for\n\
+conditional compare.  cmp0-cmp1 might be an invalid combination.  But when\n\
+swapping the order, cmp1-cmp0 is valid.  The function will return\n\
+   1: Keep current order.\n\
+  -1: Swap the two compares.\n\
+   0: Invalid combination.",
+ int, (int code1, int code2),
+ default_select_ccmp_cmp_order)
+
+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 to @code{cbranch_optab}.\n\
+ @code{unsignedp} is used when converting @code{op0} and @code{op1}'s mode.",
+ rtx, (int code, rtx op0, rtx op1),
+ NULL)
+
+DEFHOOK
+(gen_ccmp_next,
+ "This function emits a conditional comparison within a sequence of\n\
+ conditional comparisons.  The @code{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 @code{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}.  @code{bit_code}\n\
+ is AND or IOR, which is the op on the two compares.",
+ rtx, (rtx prev, int cmp_code, rtx op0, rtx op1, int bit_code),
+ NULL)
+
 /* Return a new value for loop unroll size.  */
 DEFHOOK
 (loop_unroll_adjust,
diff --git a/gcc/targhooks.c b/gcc/targhooks.c
index f3b5d56..014fd85 100644
--- a/gcc/targhooks.c
+++ b/gcc/targhooks.c
@@ -76,6 +76,12 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-ssanames.h"
 #include "insn-codes.h"
 
+/* Default return 1 to keep current order.  */
+int
+default_select_ccmp_cmp_order (int, int)
+{
+  return 1;
+}
 
 bool
 default_legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index 9dd4c83..00456dc 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -208,3 +208,4 @@  extern tree build_va_arg_indirect_ref (tree);
 extern tree std_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
 extern bool can_use_doloop_if_innermost (double_int, double_int,
 					 unsigned int, bool);
+extern int default_select_ccmp_cmp_order (int, int);