diff --git a/gcc/config/s390/s390-c.cc b/gcc/config/s390/s390-c.cc
index 269f4f8e978d..c126e6d323d7 100644
--- a/gcc/config/s390/s390-c.cc
+++ b/gcc/config/s390/s390-c.cc
@@ -409,6 +409,7 @@ s390_cpu_cpp_builtins (cpp_reader *pfile)
     cpp_define (pfile, "__LONG_DOUBLE_128__");
   cl_target_option_save (&opts, &global_options, &global_options_set);
   s390_cpu_cpp_builtins_internal (pfile, &opts, NULL);
+  cpp_define (pfile, "__GCC_ASM_FLAG_OUTPUTS__");
 }
 
 #if S390_USE_TARGET_ATTRIBUTE
diff --git a/gcc/config/s390/s390.cc b/gcc/config/s390/s390.cc
index 61c5f88de8af..a19dd7849b84 100644
--- a/gcc/config/s390/s390.cc
+++ b/gcc/config/s390/s390.cc
@@ -1877,6 +1877,97 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
 	  *code = new_code;
 	}
     }
+  /* Remove UNSPEC_CC_TO_INT from connectives.  This happens for
+     checks against multiple condition codes. */
+  if (GET_CODE (*op0) == AND
+      && GET_CODE (XEXP (*op0, 0)) == UNSPEC
+      && XINT (XEXP (*op0, 0), 1) == UNSPEC_CC_TO_INT
+      && XVECLEN (XEXP (*op0, 0), 0) == 1
+      && REGNO (XVECEXP (XEXP (*op0, 0), 0, 0)) == CC_REGNUM
+      && CONST_INT_P (XEXP (*op0, 1))
+      && CONST_INT_P (*op1)
+      && INTVAL (XEXP (*op0, 1)) == -3
+      && *code == EQ)
+    {
+      if (INTVAL (*op1) == 0)
+	{
+	  /* case cc == 0 || cc = 2 => mask = 0xa */
+	  *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+	  *op1 = gen_rtx_CONST_INT (VOIDmode, 0xa);
+	}
+      else if (INTVAL (*op1) == 1)
+	{
+	  /* case cc == 1 || cc == 3 => mask = 0x5 */
+	  *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+	  *op1 = gen_rtx_CONST_INT (VOIDmode, 0x5);
+	}
+    }
+  if (GET_CODE (*op0) == PLUS
+      && GET_CODE (XEXP (*op0, 0)) == UNSPEC
+      && XINT (XEXP (*op0, 0), 1) == UNSPEC_CC_TO_INT
+      && XVECLEN (XEXP (*op0, 0), 0) == 1
+      && REGNO (XVECEXP (XEXP (*op0, 0), 0, 0)) == CC_REGNUM
+      && CONST_INT_P (XEXP (*op0, 1))
+      && CONST_INT_P (*op1)
+      && (*code == LEU || *code == GTU))
+    {
+      if (INTVAL (*op1) == 1)
+	{
+	  if (INTVAL (XEXP (*op0, 1)) == -1)
+	    {
+	      /* case cc == 1 || cc == 2 => mask = 0x6 */
+	      *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+	      *op1 = gen_rtx_CONST_INT (VOIDmode, 0x6);
+	      *code = *code == GTU ? NE : EQ;
+	    }
+	  else if (INTVAL (XEXP (*op0, 1)) == -2)
+	    {
+	      /* case cc == 2 || cc == 3 => mask = 0x3 */
+	      *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+	      *op1 = gen_rtx_CONST_INT (VOIDmode, 0x3);
+	      *code = *code == GTU ? NE : EQ;
+	    }
+	}
+      else if (INTVAL (*op1) == 2
+	       && INTVAL (XEXP (*op0, 1)) == -1)
+	{
+	  /* case cc == 1 || cc == 2 || cc == 3 => mask = 0x7 */
+	  *op0 = XVECEXP (XEXP (*op0, 0), 0, 0);
+	  *op1 = gen_rtx_CONST_INT (VOIDmode, 0x7);
+	  *code = *code == GTU ? NE : EQ;
+	}
+    }
+  else if (*code == LEU || *code == GTU)
+    {
+      if (GET_CODE (*op0) == UNSPEC
+	  && XINT (*op0, 1) == UNSPEC_CC_TO_INT
+	  && XVECLEN (*op0, 0) == 1
+	  && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+	  && CONST_INT_P (*op1))
+	{
+	  if (INTVAL (*op1) == 1)
+	    {
+	      /* case cc == 0 || cc == 1 => mask = 0xc */
+	      *op0 = XVECEXP (*op0, 0, 0);
+	      *op1 = gen_rtx_CONST_INT (VOIDmode, 0xc);
+	      *code = *code == GTU ? NE : EQ;
+	    }
+	  else if (INTVAL (*op1) == 2)
+	    {
+	      /* case cc == 0 || cc == 1 || cc == 2 => mask = 0xd */
+	      *op0 = XVECEXP (*op0, 0, 0);
+	      *op1 = gen_rtx_CONST_INT (VOIDmode, 0xd);
+	      *code = *code == GTU ? NE : EQ;
+	    }
+	  else if (INTVAL (*op1) == 3)
+	    {
+	      /* always true */
+	      *op0 = const0_rtx;
+	      *op1 = const0_rtx;
+	      *code = *code == GTU ? NE : EQ;
+	    }
+	}
+    }
 
   /* Simplify cascaded EQ, NE with const0_rtx.  */
   if ((*code == NE || *code == EQ)
@@ -17428,22 +17519,60 @@ static rtx_insn *
 s390_md_asm_adjust (vec<rtx> &outputs, vec<rtx> &inputs,
 		    vec<machine_mode> &input_modes,
 		    vec<const char *> &constraints, vec<rtx> & /*clobbers*/,
-		    HARD_REG_SET & /*clobbered_regs*/, location_t /*loc*/)
+		    HARD_REG_SET &clobbered_regs, location_t loc)
 {
-  if (!TARGET_VXE)
-    /* Long doubles are stored in FPR pairs - nothing to do.  */
-    return NULL;
 
   rtx_insn *after_md_seq = NULL, *after_md_end = NULL;
+  bool saw_cc = false;
 
   unsigned ninputs = inputs.length ();
   unsigned noutputs = outputs.length ();
   for (unsigned i = 0; i < noutputs; i++)
     {
+      const char *constraint = constraints[i];
+      if (strncmp(constraint, "=@cc", 4) == 0)
+	{
+	  if (constraint[4] != 0)
+	    {
+	      error_at (loc, "invalid cc output constraint: %qs", constraint);
+	      continue;
+	    }
+	  if (saw_cc)
+	    {
+	      error_at (loc, "multiple cc output constraints not supported");
+	      continue;
+	    }
+	  if (TEST_HARD_REG_BIT(clobbered_regs, CC_REGNUM))
+	    {
+	      error_at (loc, "%<asm%> specifier for cc output conflicts with %<asm%> clobber list");
+	      continue;
+	    }
+	  rtx dest = outputs[i];
+	  if (GET_MODE(dest) != SImode)
+	    {
+	      error ("invalid type for cc output constraint");
+	      continue;
+	    }
+	  saw_cc = true;
+	  constraints[i] = "=c";
+	  outputs[i] = gen_rtx_REG (CCRAWmode, CC_REGNUM);
+
+	  push_to_sequence2 (after_md_seq, after_md_end);
+	  emit_insn (gen_rtx_SET (dest,
+				  gen_rtx_UNSPEC (SImode,
+						  gen_rtvec (1, outputs[i]),
+						  UNSPEC_CC_TO_INT)));
+	  after_md_seq = get_insns ();
+	  after_md_end = get_last_insn ();
+	  end_sequence ();
+	  continue;
+	}
+      if (!TARGET_VXE)
+	/* Long doubles are stored in FPR pairs - nothing to do.  */
+	continue;
       if (GET_MODE (outputs[i]) != TFmode)
 	/* Not a long double - nothing to do.  */
 	continue;
-      const char *constraint = constraints[i];
       bool allows_mem, allows_reg, is_inout;
       bool ok = parse_output_constraint (&constraint, i, ninputs, noutputs,
 					 &allows_mem, &allows_reg, &is_inout);
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 3e53a217d837..e432b2f708d7 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -6948,7 +6948,7 @@
     [(set (match_operand:SI 0 "register_operand" "")
 	  (match_operator:SI 1 "s390_eqne_operator"
            [(match_operand 2 "cc_reg_operand")
-	    (match_operand 3 "const0_operand")]))
+	    (match_operand 3 "const_mask_operand")]))
      (clobber (reg:CC CC_REGNUM))])]
   ""
   "machine_mode mode = GET_MODE (operands[2]);
@@ -6957,15 +6957,15 @@
        rtx cond, ite;
 
        if (GET_CODE (operands[1]) == NE)
-	 cond = gen_rtx_NE (VOIDmode, operands[2], const0_rtx);
+	 cond = gen_rtx_NE (VOIDmode, operands[2], operands[3]);
        else
-	 cond = gen_rtx_EQ (VOIDmode, operands[2], const0_rtx);
+	 cond = gen_rtx_EQ (VOIDmode, operands[2], operands[3]);
        ite = gen_rtx_IF_THEN_ELSE (SImode, cond, const1_rtx, const0_rtx);
        emit_insn (gen_rtx_SET (operands[0], ite));
      }
    else
      {
-       if (mode != CCZ1mode)
+       if (mode != CCZ1mode || operands[3] != const0_rtx)
 	 FAIL;
        emit_insn (gen_sne (operands[0], operands[2]));
        if (GET_CODE (operands[1]) == EQ)
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index a3db9423ce66..1b1468fc1a5f 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10788,6 +10788,11 @@ sign flag set
 ``not'' @var{flag}, or inverted versions of those above
 @end table
 
+@item s390
+The flag output constraint for s390 is @samp{=@@cc}.  Only one such
+constraint is allowed.  The variable has to be stored in a @samp{int}
+variable.
+
 @end table
 
 @anchor{InputOperands}
diff --git a/gcc/testsuite/gcc.target/s390/ccor.c b/gcc/testsuite/gcc.target/s390/ccor.c
new file mode 100644
index 000000000000..31f30f60314e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/ccor.c
@@ -0,0 +1,88 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=zEC12 -mzarch" } */
+
+#define GENFUN1(C)                              \
+  int foo_ ##C(int x)                           \
+  {                                             \
+    int cc;					\
+    asm volatile ("ahi %[x],42\n"		\
+		  : [x] "+d"(x), "=@cc" (cc));	\
+    return cc == C ? 42 : 0;			\
+  }
+
+#define GENFUN2(C1,C2)                          \
+  int foo_ ##C1##C2(int x)                      \
+  {                                             \
+    int cc;					\
+    asm volatile ("ahi %[x],42\n"		\
+		  : [x] "+d"(x), "=@cc" (cc));	\
+    return cc == C1 || cc == C2 ? 42 : 0;	\
+  }
+
+#define GENFUN3(C1,C2,C3)                                               \
+  int foo_ ##C1##C2##C3(int x)                                          \
+  {                                                                     \
+    int cc;                                                             \
+    asm volatile ("ahi %[x],42\n"					\
+		  : [x] "+d"(x), "=@cc" (cc));				\
+    return cc == C1 || cc == C2 || cc == C3 ? 42 : 0;			\
+  }
+
+GENFUN1(0)
+
+/* { dg-final { scan-assembler {locrne} } } */
+
+GENFUN1(1)
+
+/* { dg-final { scan-assembler {locrnl} } } */
+
+GENFUN1(2)
+
+/* { dg-final { scan-assembler {locrnh} } } */
+
+GENFUN1(3)
+
+/* { dg-final { scan-assembler {locrno} } } */
+
+GENFUN2(0,1)
+
+/* { dg-final { scan-assembler {locrnle} } } */
+
+GENFUN2(0,2)
+
+/* { dg-final { scan-assembler {locrhe} } } */
+
+GENFUN2(0,3)
+
+/* currently unoptimized */
+
+GENFUN2(1,2)
+
+/* { dg-final { scan-assembler {locrnlh} } } */
+
+GENFUN2(1,3)
+
+/* { dg-final { scan-assembler {locrnhe} } } */
+
+GENFUN2(2,3)
+
+/* { dg-final { scan-assembler {locrle} } } */
+
+GENFUN3(0,1,2)
+
+/* { dg-final { scan-assembler {locrh} } } */
+
+GENFUN3(0,1,3)
+
+/* currently unoptimized */
+
+GENFUN3(0,2,3)
+
+/* currently unoptimized */
+
+GENFUN3(1,2,3)
+
+/* { dg-final { scan-assembler {locre} } } */
+
+/* for the unoptimized cases, we get an ipm */
+/* { dg-final { scan-assembler-times {ipm} 3 } } */
