diff --git a/gcc/ccmp.cc b/gcc/ccmp.cc
index 09d6b5595a4..7cb525addf4 100644
--- a/gcc/ccmp.cc
+++ b/gcc/ccmp.cc
@@ -90,9 +90,10 @@ ccmp_tree_comparison_p (tree t, basic_block bb)
    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.  */
+/* Check whether G is a potential conditional compare candidate; OUTER is true if
+   G is the outer most AND/IOR.  */
 static bool
-ccmp_candidate_p (gimple *g)
+ccmp_candidate_p (gimple *g, bool outer = false)
 {
   tree lhs, op0, op1;
   gimple *gs0, *gs1;
@@ -109,8 +110,9 @@ ccmp_candidate_p (gimple *g)
   lhs = gimple_assign_lhs (g);
   op0 = gimple_assign_rhs1 (g);
   op1 = gimple_assign_rhs2 (g);
-  if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME)
-      || !has_single_use (lhs))
+  if ((TREE_CODE (op0) != SSA_NAME) || (TREE_CODE (op1) != SSA_NAME))
+    return false;
+  if (!outer && !has_single_use (lhs))
     return false;
 
   bb = gimple_bb (g);
@@ -284,7 +286,7 @@ expand_ccmp_expr (gimple *g, machine_mode mode)
   rtx_insn *last;
   rtx tmp;
 
-  if (!ccmp_candidate_p (g))
+  if (!ccmp_candidate_p (g, true))
     return NULL_RTX;
 
   last = get_last_insn ();
diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index 1db22f0a1a3..293b9386192 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -74,6 +74,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "output.h"
 #include "builtins.h"
 #include "opts.h"
+#include "ccmp.h"
 
 /* Some systems use __main in a way incompatible with its use in gcc, in these
    cases use the macros NAME__MAIN to give a quoted symbol and SYMBOL__MAIN to
@@ -3858,6 +3859,50 @@ expand_clobber (tree lhs)
     }
 }
 
+/* A subroutine of expand_gimple_stmt_1, expanding one gimple assign statement
+   ASSIGN_STMT that has a SSA name on the lhs, using TARGET and TARGET_MODE
+   as the target if possible and mode for what mode we should expand into.  */
+static rtx
+expand_gimple_assign_ssa (gassign *assign_stmt,
+			  rtx target,
+			  machine_mode target_mode)
+{
+  rtx temp;
+  tree lhs = gimple_assign_lhs (assign_stmt);
+  /* Try to expand conditonal compare.  */
+  if (targetm.gen_ccmp_first
+      && gimple_assign_rhs_class (assign_stmt) == GIMPLE_BINARY_RHS)
+    {
+      machine_mode mode = TYPE_MODE (TREE_TYPE (lhs));
+      gcc_checking_assert (targetm.gen_ccmp_next != NULL);
+      temp = expand_ccmp_expr (assign_stmt, mode);
+      if (temp)
+	return temp;
+    }
+
+  struct separate_ops ops;
+  ops.code = gimple_assign_rhs_code (assign_stmt);
+  ops.type = TREE_TYPE (lhs);
+  switch (get_gimple_rhs_class (ops.code))
+    {
+      case GIMPLE_TERNARY_RHS:
+	ops.op2 = gimple_assign_rhs3 (assign_stmt);
+	/* Fallthru */
+      case GIMPLE_BINARY_RHS:
+	ops.op1 = gimple_assign_rhs2 (assign_stmt);
+	/* Fallthru */
+      case GIMPLE_UNARY_RHS:
+	ops.op0 = gimple_assign_rhs1 (assign_stmt);
+	break;
+      default:
+	gcc_unreachable ();
+     }
+  ops.location = gimple_location (assign_stmt);
+
+  return expand_expr_real_2 (&ops, target, target_mode,
+			     EXPAND_NORMAL);
+}
+
 /* A subroutine of expand_gimple_stmt, expanding one gimple statement
    STMT that doesn't require special handling for outgoing edges.  That
    is no tailcalls and no GIMPLE_COND.  */
@@ -3971,37 +4016,17 @@ expand_gimple_stmt_1 (gimple *stmt)
 	  {
 	    rtx target, temp;
 	    bool nontemporal = gimple_assign_nontemporal_move_p (assign_stmt);
-	    struct separate_ops ops;
 	    bool promoted = false;
 
 	    target = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
 	    if (GET_CODE (target) == SUBREG && SUBREG_PROMOTED_VAR_P (target))
 	      promoted = true;
 
-	    ops.code = gimple_assign_rhs_code (assign_stmt);
-	    ops.type = TREE_TYPE (lhs);
-	    switch (get_gimple_rhs_class (ops.code))
-	      {
-		case GIMPLE_TERNARY_RHS:
-		  ops.op2 = gimple_assign_rhs3 (assign_stmt);
-		  /* Fallthru */
-		case GIMPLE_BINARY_RHS:
-		  ops.op1 = gimple_assign_rhs2 (assign_stmt);
-		  /* Fallthru */
-		case GIMPLE_UNARY_RHS:
-		  ops.op0 = gimple_assign_rhs1 (assign_stmt);
-		  break;
-		default:
-		  gcc_unreachable ();
-	      }
-	    ops.location = gimple_location (stmt);
-
-	    /* If we want to use a nontemporal store, force the value to
-	       register first.  If we store into a promoted register,
-	       don't directly expand to target.  */
+	   /* If we want to use a nontemporal store, force the value to
+	      register first.  If we store into a promoted register,
+	      don't directly expand to target.  */
 	    temp = nontemporal || promoted ? NULL_RTX : target;
-	    temp = expand_expr_real_2 (&ops, temp, GET_MODE (target),
-				       EXPAND_NORMAL);
+	    temp = expand_gimple_assign_ssa (assign_stmt, temp, GET_MODE (target));
 
 	    if (temp == target)
 	      ;
@@ -4013,7 +4038,7 @@ expand_gimple_stmt_1 (gimple *stmt)
 		if (CONSTANT_P (temp) && GET_MODE (temp) == VOIDmode)
 		  {
 		    temp = convert_modes (GET_MODE (target),
-					  TYPE_MODE (ops.type),
+					  TYPE_MODE (TREE_TYPE (lhs)),
 					  temp, unsignedp);
 		    temp = convert_modes (GET_MODE (SUBREG_REG (target)),
 					  GET_MODE (target), temp, unsignedp);
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_3.c b/gcc/testsuite/gcc.target/aarch64/ccmp_3.c
new file mode 100644
index 00000000000..a2b47fbee14
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp_3.c
@@ -0,0 +1,20 @@
+/* { dg-options "-O2" } */
+/* PR target/100942 */
+
+void foo(void);
+int f1(int a, int b)
+{
+  int c = a == 0 || b == 0;
+  if (c) foo();
+  return c;
+}
+
+/* We should get one cmp followed by ccmp and one cset. */
+/* { dg-final { scan-assembler "\tccmp\t" } } */
+/* { dg-final { scan-assembler-times "\tcset\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 1 } } */
+/* And not get 2 cmps and 2 (or more cset) and orr and a cbnz. */
+/* { dg-final { scan-assembler-not "\torr\t" } } */
+/* { dg-final { scan-assembler-not "\tcbnz\t" } } */
+/* { dg-final { scan-assembler-not "\tcbz\t" } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_4.c b/gcc/testsuite/gcc.target/aarch64/ccmp_4.c
new file mode 100644
index 00000000000..bc0f57a7c59
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp_4.c
@@ -0,0 +1,35 @@
+/* { dg-options "-O2" } */
+/* PR target/100942 */
+
+void foo(void);
+int f1(int a, int b, int d)
+{
+  int c = a < 8 || b < 9;
+  int e = d < 11 || c;
+  if (e) foo();
+  return c;
+}
+
+/*
+  We really should get:
+        cmp     w0, 7
+        ccmp    w1, 8, 4, gt
+        cset    w0, le
+        ccmp    w2, 10, 4, gt
+        ble     .L11
+
+  But we currently get:
+        cmp     w0, 7
+        ccmp    w1, 8, 4, gt
+        cset    w0, le
+        cmp     w0, 0
+        ccmp    w2, 10, 4, eq
+        ble     .L11
+  The middle cmp is not needed.
+ */
+
+/* We should end up with only one cmp and 2 ccmp and 1 cset but currently we get 2 cmp
+   though. */
+/* { dg-final { scan-assembler-times "\tccmp\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tcset\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 1 { xfail *-*-* } } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/ccmp_5.c b/gcc/testsuite/gcc.target/aarch64/ccmp_5.c
new file mode 100644
index 00000000000..7e52ae4f322
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/ccmp_5.c
@@ -0,0 +1,20 @@
+
+/* { dg-options "-O2" } */
+/* PR target/100942 */
+void f1(int a, int b, _Bool *x)
+{
+  x[0] = x[1] = a == 0 || b == 0;
+}
+
+void f2(int a, int b, int *x)
+{
+  x[0] = x[1] = a == 0 || b == 0;
+}
+
+
+/* Both functions should be using ccmp rather than 2 cset/orr.  */
+/* { dg-final { scan-assembler-times "\tccmp\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tcset\t" 2 } } */
+/* { dg-final { scan-assembler-times "\tcmp\t" 2 } } */
+/* { dg-final { scan-assembler-not "\torr\t" } } */
+
