diff --git a/gcc/config/s390/constraints.md b/gcc/config/s390/constraints.md
index 190cdc9..ee505d0 100644
--- a/gcc/config/s390/constraints.md
+++ b/gcc/config/s390/constraints.md
@@ -55,7 +55,12 @@
 ;;         D,S,H:   mode of the containing operand
 ;;         0,F:     value of the other parts (F - all bits set)
 ;;         --
-;;         xx[DS]q  satisfies s390_contiguous_bitmask_p for DImode or SImode
+;;         xxDq     satisfies s390_contiguous_bitmask_p for DImode
+;;                  (with possible wraparound of the one-bit range)
+;;         xxSw     satisfies s390_contiguous_bitmask_p for SImode
+;;                  (with possible wraparound of the one-bit range)
+;;         xxSq     satisfies s390_contiguous_bitmask_nowrap_p for SImode
+;;                  (without wraparound of the one-bit range)
 ;;
 ;;         The constraint matches if the specified part of a constant
 ;;         has a value different from its other parts.  If the letter x
@@ -346,15 +351,20 @@
   (and (match_code "const_int")
        (match_test "s390_N_constraint_str (\"xQH0\", ival)")))
 
-(define_constraint "NxxDq"
+(define_constraint "NxxDw"
   "@internal"
   (and (match_code "const_int")
-       (match_test "s390_contiguous_bitmask_p (ival, 64, NULL, NULL)")))
+       (match_test "s390_contiguous_bitmask_p (ival, true, 64, NULL, NULL)")))
 
 (define_constraint "NxxSq"
   "@internal"
   (and (match_code "const_int")
-       (match_test "s390_contiguous_bitmask_p (ival, 32, NULL, NULL)")))
+       (match_test "s390_contiguous_bitmask_p (ival, false, 32, NULL, NULL)")))
+
+(define_constraint "NxxSw"
+  "@internal"
+  (and (match_code "const_int")
+       (match_test "s390_contiguous_bitmask_p (ival, true, 32, NULL, NULL)")))
 
 ;;
 ;; Double-letter constraints starting with O follow.
diff --git a/gcc/config/s390/predicates.md b/gcc/config/s390/predicates.md
index 75e4cb8..5b57344 100644
--- a/gcc/config/s390/predicates.md
+++ b/gcc/config/s390/predicates.md
@@ -176,10 +176,20 @@
   return false;
 })
 
+; Predicate that always allows wraparound of the one-bit range.
 (define_predicate "contiguous_bitmask_operand"
   (match_code "const_int")
 {
-  return s390_contiguous_bitmask_p (INTVAL (op), GET_MODE_BITSIZE (mode), NULL, NULL);
+  return s390_contiguous_bitmask_p (INTVAL (op), true,
+                                    GET_MODE_BITSIZE (mode), NULL, NULL);
+})
+
+; Same without wraparound.
+(define_predicate "contiguous_bitmask_nowrap_operand"
+  (match_code "const_int")
+{
+  return s390_contiguous_bitmask_p
+    (INTVAL (op), false, GET_MODE_BITSIZE (mode), NULL, NULL);
 })
 
 ;; Return true if OP is ligitimate for any LOC instruction.
diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h
index 2ccf0bb..efef299 100644
--- a/gcc/config/s390/s390-protos.h
+++ b/gcc/config/s390/s390-protos.h
@@ -73,7 +73,7 @@ extern int s390_const_ok_for_constraint_p (HOST_WIDE_INT, int, const char *);
 extern int s390_const_double_ok_for_constraint_p (rtx, int, const char *);
 extern int s390_single_part (rtx, machine_mode, machine_mode, int);
 extern unsigned HOST_WIDE_INT s390_extract_part (rtx, machine_mode, int);
-extern bool s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT, int, int *, int *);
+extern bool s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT, bool, int, int *, int *);
 extern bool s390_contiguous_bitmask_vector_p (rtx, int *, int *);
 extern bool s390_bytemask_vector_p (rtx, unsigned *);
 extern bool s390_split_ok_p (rtx, rtx, machine_mode, int);
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 1abb30b..5a1612e 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -2260,67 +2260,107 @@ s390_single_part (rtx op,
 }
 
 /* Return true if IN contains a contiguous bitfield in the lower SIZE
-   bits and no other bits are set in IN.  POS and LENGTH can be used
-   to obtain the start position and the length of the bitfield.
+   bits and no other bits are set in (the lower SIZE bits of) IN.
 
-   POS gives the position of the first bit of the bitfield counting
-   from the lowest order bit starting with zero.  In order to use this
-   value for S/390 instructions this has to be converted to "bits big
-   endian" style.  */
+   PSTART and PEND can be used to obtain the start and end
+   position (inclusive) of the bitfield relative to 64
+   bits. *PSTART / *PEND gives the position of the first/last bit
+   of the bitfield counting from the highest order bit starting
+   with zero.  */
 
 bool
-s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size,
-			   int *pos, int *length)
-{
-  int tmp_pos = 0;
-  int tmp_length = 0;
-  int i;
-  unsigned HOST_WIDE_INT mask = 1ULL;
-  bool contiguous = false;
+s390_contiguous_bitmask_nowrap_p (unsigned HOST_WIDE_INT in, int size,
+				  int *pstart, int *pend)
+{
+  int start;
+  int end = -1;
+  int lowbit = sizeof (HOST_WIDE_INT) * BITS_PER_UNIT - 1;
+  int highbit = sizeof (HOST_WIDE_INT) * BITS_PER_UNIT - size;
+  unsigned HOST_WIDE_INT bitmask = 1ULL;
+
+  gcc_assert (!!pstart == !!pend);
+  for (start = lowbit; start >= highbit; bitmask <<= 1, start--)
+    if (end == -1)
+      {
+	/* Look for the rightmost bit of a contiguous range of ones.  */
+	if (bitmask & in)
+	  /* Found it.  */
+	  end = start;
+      }
+    else
+      {
+	/* Look for the firt zero bit after the range of ones.  */
+	if (! (bitmask & in))
+	  /* Found it.  */
+	  break;
+      }
+  /* We're one past the last one-bit.  */
+  start++;
 
-  for (i = 0; i < size; mask <<= 1, i++)
+  if (end == -1)
+    /* No one bits found.  */
+    return false;
+
+  if (start > highbit)
     {
-      if (contiguous)
-	{
-	  if (mask & in)
-	    tmp_length++;
-	  else
-	    break;
-	}
-      else
-	{
-	  if (mask & in)
-	    {
-	      contiguous = true;
-	      tmp_length++;
-	    }
-	  else
-	    tmp_pos++;
-	}
+      unsigned HOST_WIDE_INT mask;
+
+      /* Calculate a mask for all bits beyond the contiguous bits.  */
+      mask = ((~(0ULL) >> highbit) & (~(0ULL) << (lowbit - start + 1)));
+      if (mask & in)
+	/* There are more bits set beyond the first range of one bits.  */
+	return false;
     }
 
-  if (!tmp_length)
-    return false;
+  if (pstart)
+    {
+      *pstart = start;
+      *pend = end;
+    }
+
+  return true;
+}
 
-  /* Calculate a mask for all bits beyond the contiguous bits.  */
-  mask = (-1LL & ~(((1ULL << (tmp_length + tmp_pos - 1)) << 1) - 1));
+/* Same as s390_contiguous_bitmask_nowrap_p but also returns true
+   if ~IN contains a contiguous bitfield.  In that case, *END is <
+   *START.
 
-  if ((unsigned)size < sizeof (HOST_WIDE_INT) * BITS_PER_UNIT)
-    mask &= (HOST_WIDE_INT_1U << size) - 1;
+   If WRAP_P is true, a bitmask that wraps around is also tested.
+   When a wraparoud occurs *START is greater than *END (in
+   non-null pointers), and the uppermost (64 - SIZE) bits are thus
+   part of the range.  If WRAP_P is false, no wraparound is
+   tested.  */
 
-  if (mask & in)
-    return false;
+bool
+s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, bool wrap_p,
+			   int size, int *start, int *end)
+{
+  int bs = sizeof (HOST_WIDE_INT) * BITS_PER_UNIT;
+  bool b;
 
-  if (tmp_length + tmp_pos - 1 > size)
+  gcc_assert (!!start == !!end);
+  if ((in & ((~(0ULL)) >> (bs - size))) == 0)
+    /* This cannot be expressed as a contiguous bitmask.  Exit early because
+       the second call of s390_contiguous_bitmask_nowrap_p would accept this as
+       a valid bitmask.  */
     return false;
+  b = s390_contiguous_bitmask_nowrap_p (in, size, start, end);
+  if (b)
+    return true;
+  if (! wrap_p)
+    return false;
+  b = s390_contiguous_bitmask_nowrap_p (~in, size, start, end);
+  if (b && start)
+    {
+      int s = *start;
+      int e = *end;
 
-  if (length)
-    *length = tmp_length;
-
-  if (pos)
-    *pos = tmp_pos;
+      gcc_assert (s >= 1);
+      *start = ((e + 1) & (bs - 1));
+      *end = ((s - 1 + bs) & (bs - 1));
+    }
 
-  return true;
+  return b;
 }
 
 /* Return true if OP contains the same contiguous bitfield in *all*
@@ -2336,9 +2376,11 @@ bool
 s390_contiguous_bitmask_vector_p (rtx op, int *start, int *end)
 {
   unsigned HOST_WIDE_INT mask;
-  int length, size;
+  int size;
   rtx elt;
+  bool b;
 
+  gcc_assert (!!start == !!end);
   if (!const_vec_duplicate_p (op, &elt)
       || !CONST_INT_P (elt))
     return false;
@@ -2350,25 +2392,21 @@ s390_contiguous_bitmask_vector_p (rtx op, int *start, int *end)
     return false;
 
   mask = UINTVAL (elt);
-  if (s390_contiguous_bitmask_p (mask, size, start,
-				 end != NULL ? &length : NULL))
-    {
-      if (end != NULL)
-	*end = *start + length - 1;
-      return true;
-    }
-  /* 0xff00000f style immediates can be covered by swapping start and
-     end indices in vgm.  */
-  if (s390_contiguous_bitmask_p (~mask, size, start,
-				 end != NULL ? &length : NULL))
+
+  b = s390_contiguous_bitmask_p (mask, true, size, start, end);
+  if (b)
     {
-      if (end != NULL)
-	*end = *start - 1;
-      if (start != NULL)
-	*start = *start + length;
+      if (start)
+	{
+	  int bs = sizeof (HOST_WIDE_INT) * BITS_PER_UNIT;
+
+	  *start -= (bs - size);
+	  *end -= (bs - size);
+	}
       return true;
     }
-  return false;
+  else
+    return false;
 }
 
 /* Return true if C consists only of byte chunks being either 0 or
@@ -2422,14 +2460,21 @@ s390_bytemask_vector_p (rtx op, unsigned *mask)
 bool
 s390_extzv_shift_ok (int bitsize, int rotl, unsigned HOST_WIDE_INT contig)
 {
-  int pos, len;
+  int start, end;
   bool ok;
 
-  ok = s390_contiguous_bitmask_p (contig, bitsize, &pos, &len);
+  ok = s390_contiguous_bitmask_nowrap_p (contig, bitsize, &start, &end);
   gcc_assert (ok);
 
-  return ((rotl >= 0 && rotl <= pos)
-	  || (rotl < 0 && -rotl <= bitsize - len - pos));
+  if (rotl >= 0)
+    return (64 - end >= rotl);
+  else
+    {
+      /* Translate "- rotate right" in BITSIZE mode to "rotate left" in
+	 DIMode.  */
+      rotl = -rotl + (64 - bitsize);
+      return (start >= rotl);
+    }
 }
 
 /* Check whether we can (and want to) split a double-word
@@ -7441,16 +7486,17 @@ print_operand (FILE *file, rtx x, int code)
 	case 'e': case 'f':
 	case 's': case 't':
 	  {
-	    int pos, len;
+	    int start, end;
+	    int len;
 	    bool ok;
 
 	    len = (code == 's' || code == 'e' ? 64 : 32);
-	    ok = s390_contiguous_bitmask_p (ival, len, &pos, &len);
+	    ok = s390_contiguous_bitmask_p (ival, true, len, &start, &end);
 	    gcc_assert (ok);
 	    if (code == 's' || code == 't')
-	      ival = 64 - pos - len;
+	      ival = start;
 	    else
-	      ival = 64 - 1 - pos;
+	      ival = end;
 	  }
 	  break;
 	default:
@@ -7490,16 +7536,12 @@ print_operand (FILE *file, rtx x, int code)
 	case 'e':
 	case 's':
 	  {
-	    int start, stop, inner_len;
+	    int start, end;
 	    bool ok;
 
-	    inner_len = GET_MODE_UNIT_BITSIZE (GET_MODE (x));
-	    ok = s390_contiguous_bitmask_vector_p (x, &start, &stop);
+	    ok = s390_contiguous_bitmask_vector_p (x, &start, &end);
 	    gcc_assert (ok);
-	    if (code == 's' || code == 't')
-	      ival = inner_len - stop - 1;
-	    else
-	      ival = inner_len - start - 1;
+	    ival = (code == 's') ? start : end;
 	    fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival);
 	  }
 	  break;
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 21ff33e..2848b41 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -6962,7 +6962,7 @@
   [(set (reg CC_REGNUM)
         (compare
 	  (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d,0,    d")
-                  (match_operand:DI 2 "general_operand"      " d,d,T,NxxDq"))
+                  (match_operand:DI 2 "general_operand"      " d,d,T,NxxDw"))
           (const_int 0)))
    (set (match_operand:DI 0 "register_operand"               "=d,d,d,    d")
         (and:DI (match_dup 1) (match_dup 2)))]
@@ -6980,7 +6980,7 @@
   [(set (reg CC_REGNUM)
         (compare
 	  (and:DI (match_operand:DI 1 "nonimmediate_operand" "%0,d,0,    d")
-                  (match_operand:DI 2 "general_operand"      " d,d,T,NxxDq"))
+                  (match_operand:DI 2 "general_operand"      " d,d,T,NxxDw"))
                  (const_int 0)))
    (clobber (match_scratch:DI 0                              "=d,d,d,    d"))]
   "TARGET_ZARCH
@@ -7003,7 +7003,7 @@
 	  (match_operand:DI 1 "nonimmediate_operand"
             "%d,o,    0,    0,    0,    0,    0,    0,0,d,0,    d,    0,0")
           (match_operand:DI 2 "general_operand"
-            "M, M,N0HDF,N1HDF,N2HDF,N3HDF,N0SDF,N1SDF,d,d,T,NxxDq,NxQDF,Q")))
+            "M, M,N0HDF,N1HDF,N2HDF,N3HDF,N0SDF,N1SDF,d,d,T,NxxDw,NxQDF,Q")))
    (clobber (reg:CC CC_REGNUM))]
   "TARGET_ZARCH && s390_logical_operator_ok_p (operands)"
   "@
@@ -7134,7 +7134,7 @@
         (and:SI (match_operand:SI 1 "nonimmediate_operand"
 			    "%d,o,    0,    0, 0,0,d,0,0,    d,    0,0")
                 (match_operand:SI 2 "general_operand"
-			    " M,M,N0HSF,N1HSF,Os,d,d,R,T,NxxSq,NxQSF,Q")))
+			    " M,M,N0HSF,N1HSF,Os,d,d,R,T,NxxSw,NxQSF,Q")))
    (clobber (reg:CC CC_REGNUM))]
   "TARGET_ZARCH && s390_logical_operator_ok_p (operands)"
   "@
