diff --git a/gcc/ccmp.c b/gcc/ccmp.c
index 9c239e2..f26bc36 100644
--- a/gcc/ccmp.c
+++ b/gcc/ccmp.c
@@ -68,7 +68,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
@@ -148,6 +157,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);
@@ -156,9 +186,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);
@@ -175,52 +207,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
@@ -230,21 +237,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
 	{
@@ -264,23 +261,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);
@@ -290,13 +294,22 @@ 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);
+
+	  start_sequence ();
 	  tmp = emit_cstore (target, icode, NE, cc_mode, cc_mode,
 			     0, tmp, const0_rtx, 1, mode);
 	  if (tmp)
-	    return tmp;
+	    {
+	      rtx seq = get_insns ();
+	      end_sequence ();
+	      emit_insn (prep_seq);
+	      emit_insn (gen_seq);
+	      emit_insn (seq);
+	      return tmp;
+	    }
+	  end_sequence ();
 	}
     }
   /* Clean up.  */
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 9f7cccc..31aa970 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -10244,6 +10244,199 @@ 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 = code == LTU || code == LEU || code == GTU || code == GEU;
+  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 = cmp_code == LTU || cmp_code == LEU
+		  || cmp_code == GTU || cmp_code == GEU;
+  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 597ff8c..d13231e 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -256,7 +256,7 @@
   ""
   "")
 
-(define_insn "*ccmp_and"
+(define_insn "ccmp_and<mode>"
   [(set (match_operand 1 "ccmp_cc_register" "")
 	(compare
 	 (and:SI
@@ -275,7 +275,7 @@
   [(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
@@ -294,6 +294,20 @@
   [(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)])
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index c54fc71..4ef4409 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -11234,18 +11234,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 7c0296d..3770f37 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2536,24 +2536,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/gcc.dg/pr64015.c b/gcc/testsuite/gcc.dg/pr64015.c
new file mode 100644
index 0000000..daf8393
--- /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*-*-* } } } */
