diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index d2aa482b2e081dc2f3e0eb2f68c6d43eab205068..1c010560c19f49a5f28ba599bfbb9dba914fb1d2 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,14 @@
+2020-04-17  Jakub Jelinek  <jakub@redhat.com>
+	    Jeff Law  <law@redhat.com>
+
+	PR target/94567
+	* config/i386/i386.md (*testqi_ext_3): Use CCZmode rather than
+	CCNOmode in ix86_match_ccmode if len is equal to <MODE>mode precision,
+	or pos + len >= 32, or pos + len is equal to operands[2] precision
+	and operands[2] is not a register operand.  During splitting perform
+	SImode AND if operands[0] doesn't have CCZmode and pos + len is
+	equal to mode precision.
+
 2020-04-17  Richard Biener  <rguenther@suse.de>
 
 	PR other/94629
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 3051624d89fdaa01c4a95d7546a99df70723f350..b426c21d3ddd071a3579a3ed6c219b00c2d71584 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -8730,10 +8730,38 @@
 	   && INTVAL (operands[3]) > 32
 	   && INTVAL (operands[3]) + INTVAL (operands[4]) == 64))
    && ix86_match_ccmode (insn,
-			 /* *testdi_1 requires CCZmode if the mask has bit
+			 /* If zero_extract mode precision is the same
+			    as len, the SF of the zero_extract
+			    comparison will be the most significant
+			    extracted bit, but this could be matched
+			    after splitting only for pos 0 len all bits
+			    trivial extractions.  Require CCZmode.  */
+			 (GET_MODE_PRECISION (<MODE>mode)
+			  == INTVAL (operands[3]))
+			 /* Otherwise, require CCZmode if we'd use a mask
+			    with the most significant bit set and can't
+			    widen it to wider mode.  *testdi_1 also
+			    requires CCZmode if the mask has bit
 			    31 set and all bits above it clear.  */
-			 GET_MODE (operands[2]) == DImode
-			 && INTVAL (operands[3]) + INTVAL (operands[4]) == 32
+			 || (INTVAL (operands[3]) + INTVAL (operands[4])
+			     >= 32)
+			 /* We can't widen also if val is not a REG.  */
+			 || (INTVAL (operands[3]) + INTVAL (operands[4])
+			     == GET_MODE_PRECISION (GET_MODE (operands[2]))
+			     && !register_operand (operands[2],
+						   GET_MODE (operands[2])))
+			 /* And we shouldn't widen if
+			    TARGET_PARTIAL_REG_STALL.  */
+			 || (TARGET_PARTIAL_REG_STALL
+			     && (INTVAL (operands[3]) + INTVAL (operands[4])
+				 >= (paradoxical_subreg_p (operands[2])
+				     && (GET_MODE_CLASS
+					  (GET_MODE (SUBREG_REG (operands[2])))
+					 == MODE_INT)
+				     ? GET_MODE_PRECISION
+					 (GET_MODE (SUBREG_REG (operands[2])))
+				     : GET_MODE_PRECISION
+					 (GET_MODE (operands[2])))))
 			 ? CCZmode : CCNOmode)"
   "#"
   "&& 1"
@@ -8750,7 +8778,10 @@
 
       /* Narrow paradoxical subregs to prevent partial register stalls.  */
       if (GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (submode)
-	  && GET_MODE_CLASS (submode) == MODE_INT)
+	  && GET_MODE_CLASS (submode) == MODE_INT
+	  && (GET_MODE (operands[0]) == CCZmode
+	      || pos + len < GET_MODE_PRECISION (submode)
+	      || REG_P (SUBREG_REG (val))))
 	{
 	  val = SUBREG_REG (val);
 	  mode = submode;
@@ -8758,14 +8789,32 @@
     }
 
   /* Small HImode tests can be converted to QImode.  */
-  if (register_operand (val, HImode) && pos + len <= 8)
+  if (pos + len <= 8
+      && register_operand (val, HImode))
     {
-      val = gen_lowpart (QImode, val);
-      mode = QImode;
+      rtx nval = gen_lowpart (QImode, val);
+      if (!MEM_P (nval)
+	  || GET_MODE (operands[0]) == CCZmode
+	  || pos + len < 8)
+	{
+	  val = nval;
+	  mode = QImode;
+	}
     }
 
   gcc_assert (pos + len <= GET_MODE_PRECISION (mode));
 
+  /* If the mask is going to have the sign bit set in the mode
+     we want to do the comparison in and user isn't interested just
+     in the zero flag, then we must widen the target mode.  */
+  if (pos + len == GET_MODE_PRECISION (mode)
+      && GET_MODE (operands[0]) != CCZmode)
+    {
+      gcc_assert (pos + len < 32 && !MEM_P (val));
+      mode = SImode;
+      val = gen_lowpart (mode, val);
+    }
+
   wide_int mask
     = wi::shifted_mask (pos, len, false, GET_MODE_PRECISION (mode));
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index b98c72cdd2af6164310dfbd11ded84c39617ab7f..830ee92357e5bac7429b3b5026eb1378550ee30f 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2020-04-17  Jakub Jelinek  <jakub@redhat.com>
+	    Jeff Law  <law@redhat.com>
+
+	PR target/94567
+	* gcc.c-torture/execute/pr94567.c: New test.
+
 2020-04-17  Nathan Sidwell  <nathan@acm.org>
 
 	PR c++/94608
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr94567.c b/gcc/testsuite/gcc.c-torture/execute/pr94567.c
new file mode 100644
index 0000000000000000000000000000000000000000..679d73d2ef42392eedbbb57cbac805db45a3be89
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr94567.c
@@ -0,0 +1,26 @@
+/* PR target/94567 */
+
+volatile int a = 1, b;
+short c, d = 4, f = 2, g;
+unsigned short e = 53736;
+
+int
+foo (int i, int j)
+{
+  return i && j ? 0 : i + j;
+}
+
+int
+main ()
+{
+  for (; a; a = 0)
+    {
+      unsigned short k = e;
+      g = k >> 3;
+      if (foo (g < (f || c), b))
+	d = 0;
+    }
+  if (d != 4)
+    __builtin_abort ();
+  return 0;
+}