summaryrefslogtreecommitdiff
path: root/gcc/config/s390/s390.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/s390/s390.c')
-rw-r--r--gcc/config/s390/s390.c200
1 files changed, 121 insertions, 79 deletions
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index 60e2070d15d..0b0e3600c66 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;