Rework subreg_get_info

This isn't intended to change the behaviour, just rewrite the
existing logic in a different (and hopefully clearer) way.
The new form -- particularly the part based on the "block"
concept -- is easier to convert to polynomial sizes.

gcc/
2016-11-15  Richard Sandiford  <richard.sandiford@arm.com>
	    Alan Hayward  <alan.hayward@arm.com>
	    David Sherwood  <david.sherwood@arm.com>

	* rtlanal.c (subreg_get_info): Use more local variables.
	Remark that for HARD_REGNO_NREGS_HAS_PADDING, each scalar unit
	occupies at least one register.  Assume that full hard registers
	have consistent endianness.  Share previously-duplicated if block.
	Rework the main handling so that it operates on independently-
	addressable YMODE-sized blocks.  Use subreg_size_lowpart_offset
	to check lowpart offsets, without trying to find an equivalent
	integer mode first.  Handle WORDS_BIG_ENDIAN != REG_WORDS_BIG_ENDIAN
	as a final register-endianness correction.

Co-Authored-By: Alan Hayward <alan.hayward@arm.com>
Co-Authored-By: David Sherwood <david.sherwood@arm.com>

From-SVN: r242758
This commit is contained in:
Richard Sandiford 2016-11-23 14:35:14 +00:00 committed by Richard Sandiford
parent 712871dde1
commit 07ce146666
2 changed files with 80 additions and 76 deletions

View File

@ -1,3 +1,17 @@
2016-11-23 Richard Sandiford <richard.sandiford@arm.com>
Alan Hayward <alan.hayward@arm.com>
David Sherwood <david.sherwood@arm.com>
* rtlanal.c (subreg_get_info): Use more local variables.
Remark that for HARD_REGNO_NREGS_HAS_PADDING, each scalar unit
occupies at least one register. Assume that full hard registers
have consistent endianness. Share previously-duplicated if block.
Rework the main handling so that it operates on independently-
addressable YMODE-sized blocks. Use subreg_size_lowpart_offset
to check lowpart offsets, without trying to find an equivalent
integer mode first. Handle WORDS_BIG_ENDIAN != REG_WORDS_BIG_ENDIAN
as a final register-endianness correction.
2016-11-23 Segher Boessenkool <segher@kernel.crashing.org>
PR target/77881

View File

@ -3588,31 +3588,29 @@ subreg_get_info (unsigned int xregno, machine_mode xmode,
unsigned int offset, machine_mode ymode,
struct subreg_info *info)
{
int nregs_xmode, nregs_ymode;
int mode_multiple, nregs_multiple;
int offset_adj, y_offset, y_offset_adj;
int regsize_xmode, regsize_ymode;
bool rknown;
unsigned int nregs_xmode, nregs_ymode;
gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
rknown = false;
unsigned int xsize = GET_MODE_SIZE (xmode);
unsigned int ysize = GET_MODE_SIZE (ymode);
bool rknown = false;
/* If there are holes in a non-scalar mode in registers, we expect
that it is made up of its units concatenated together. */
/* If the register representation of a non-scalar mode has holes in it,
we expect the scalar units to be concatenated together, with the holes
distributed evenly among the scalar units. Each scalar unit must occupy
at least one register. */
if (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode))
{
machine_mode xmode_unit;
nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode);
xmode_unit = GET_MODE_INNER (xmode);
unsigned int nunits = GET_MODE_NUNITS (xmode);
machine_mode xmode_unit = GET_MODE_INNER (xmode);
gcc_assert (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode_unit));
gcc_assert (nregs_xmode
== (GET_MODE_NUNITS (xmode)
== (nunits
* HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode_unit)));
gcc_assert (hard_regno_nregs[xregno][xmode]
== (hard_regno_nregs[xregno][xmode_unit]
* GET_MODE_NUNITS (xmode)));
== hard_regno_nregs[xregno][xmode_unit] * nunits);
/* You can only ask for a SUBREG of a value with holes in the middle
if you don't cross the holes. (Such a SUBREG should be done by
@ -3622,11 +3620,9 @@ subreg_get_info (unsigned int xregno, machine_mode xmode,
3 for each part, but in memory it's two 128-bit parts.
Padding is assumed to be at the end (not necessarily the 'high part')
of each unit. */
if ((offset / GET_MODE_SIZE (xmode_unit) + 1
< GET_MODE_NUNITS (xmode))
if ((offset / GET_MODE_SIZE (xmode_unit) + 1 < nunits)
&& (offset / GET_MODE_SIZE (xmode_unit)
!= ((offset + GET_MODE_SIZE (ymode) - 1)
/ GET_MODE_SIZE (xmode_unit))))
!= ((offset + ysize - 1) / GET_MODE_SIZE (xmode_unit))))
{
info->representable_p = false;
rknown = true;
@ -3638,18 +3634,20 @@ subreg_get_info (unsigned int xregno, machine_mode xmode,
nregs_ymode = hard_regno_nregs[xregno][ymode];
/* Paradoxical subregs are otherwise valid. */
if (!rknown
&& offset == 0
&& GET_MODE_PRECISION (ymode) > GET_MODE_PRECISION (xmode))
if (!rknown && offset == 0 && ysize > xsize)
{
info->representable_p = true;
/* If this is a big endian paradoxical subreg, which uses more
actual hard registers than the original register, we must
return a negative offset so that we find the proper highpart
of the register. */
if (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
? REG_WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)
info->offset = nregs_xmode - nregs_ymode;
of the register.
We assume that the ordering of registers within a multi-register
value has a consistent endianness: if bytes and register words
have different endianness, the hard registers that make up a
multi-register value must be at least word-sized. */
if (REG_WORDS_BIG_ENDIAN)
info->offset = (int) nregs_xmode - (int) nregs_ymode;
else
info->offset = 0;
info->nregs = nregs_ymode;
@ -3660,31 +3658,23 @@ subreg_get_info (unsigned int xregno, machine_mode xmode,
modes, we cannot generally form this subreg. */
if (!HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode)
&& !HARD_REGNO_NREGS_HAS_PADDING (xregno, ymode)
&& (GET_MODE_SIZE (xmode) % nregs_xmode) == 0
&& (GET_MODE_SIZE (ymode) % nregs_ymode) == 0)
&& (xsize % nregs_xmode) == 0
&& (ysize % nregs_ymode) == 0)
{
regsize_xmode = GET_MODE_SIZE (xmode) / nregs_xmode;
regsize_ymode = GET_MODE_SIZE (ymode) / nregs_ymode;
if (!rknown && regsize_xmode > regsize_ymode && nregs_ymode > 1)
int regsize_xmode = xsize / nregs_xmode;
int regsize_ymode = ysize / nregs_ymode;
if (!rknown
&& ((nregs_ymode > 1 && regsize_xmode > regsize_ymode)
|| (nregs_xmode > 1 && regsize_ymode > regsize_xmode)))
{
info->representable_p = false;
info->nregs
= (GET_MODE_SIZE (ymode) + regsize_xmode - 1) / regsize_xmode;
info->offset = offset / regsize_xmode;
return;
}
if (!rknown && regsize_ymode > regsize_xmode && nregs_xmode > 1)
{
info->representable_p = false;
info->nregs
= (GET_MODE_SIZE (ymode) + regsize_xmode - 1) / regsize_xmode;
info->nregs = CEIL (ysize, regsize_xmode);
info->offset = offset / regsize_xmode;
return;
}
/* It's not valid to extract a subreg of mode YMODE at OFFSET that
would go outside of XMODE. */
if (!rknown
&& GET_MODE_SIZE (ymode) + offset > GET_MODE_SIZE (xmode))
if (!rknown && ysize + offset > xsize)
{
info->representable_p = false;
info->nregs = nregs_ymode;
@ -3704,7 +3694,7 @@ subreg_get_info (unsigned int xregno, machine_mode xmode,
info->representable_p = true;
info->nregs = nregs_ymode;
info->offset = offset / regsize_ymode;
gcc_assert (info->offset + info->nregs <= nregs_xmode);
gcc_assert (info->offset + info->nregs <= (int) nregs_xmode);
return;
}
}
@ -3723,47 +3713,47 @@ subreg_get_info (unsigned int xregno, machine_mode xmode,
}
}
/* This should always pass, otherwise we don't know how to verify
the constraint. These conditions may be relaxed but
subreg_regno_offset would need to be redesigned. */
gcc_assert ((GET_MODE_SIZE (xmode) % GET_MODE_SIZE (ymode)) == 0);
/* Set NUM_BLOCKS to the number of independently-representable YMODE
values there are in (reg:XMODE XREGNO). We can view the register
as consisting of this number of independent "blocks", where each
block occupies NREGS_YMODE registers and contains exactly one
representable YMODE value. */
gcc_assert ((nregs_xmode % nregs_ymode) == 0);
unsigned int num_blocks = nregs_xmode / nregs_ymode;
if (WORDS_BIG_ENDIAN != REG_WORDS_BIG_ENDIAN
&& GET_MODE_SIZE (xmode) > UNITS_PER_WORD)
{
HOST_WIDE_INT xsize = GET_MODE_SIZE (xmode);
HOST_WIDE_INT ysize = GET_MODE_SIZE (ymode);
HOST_WIDE_INT off_low = offset & (ysize - 1);
HOST_WIDE_INT off_high = offset & ~(ysize - 1);
offset = (xsize - ysize - off_high) | off_low;
}
/* The XMODE value can be seen as a vector of NREGS_XMODE
values. The subreg must represent a lowpart of given field.
Compute what field it is. */
offset_adj = offset;
offset_adj -= subreg_lowpart_offset (ymode,
mode_for_size (GET_MODE_BITSIZE (xmode)
/ nregs_xmode,
MODE_INT, 0));
/* Calculate the number of bytes in each block. This must always
be exact, otherwise we don't know how to verify the constraint.
These conditions may be relaxed but subreg_regno_offset would
need to be redesigned. */
gcc_assert ((xsize % num_blocks) == 0);
unsigned int bytes_per_block = xsize / num_blocks;
/* Size of ymode must not be greater than the size of xmode. */
mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
gcc_assert (mode_multiple != 0);
y_offset = offset / GET_MODE_SIZE (ymode);
y_offset_adj = offset_adj / GET_MODE_SIZE (ymode);
nregs_multiple = nregs_xmode / nregs_ymode;
gcc_assert ((offset_adj % GET_MODE_SIZE (ymode)) == 0);
gcc_assert ((mode_multiple % nregs_multiple) == 0);
/* Get the number of the first block that contains the subreg and the byte
offset of the subreg from the start of that block. */
unsigned int block_number = offset / bytes_per_block;
unsigned int subblock_offset = offset % bytes_per_block;
if (!rknown)
{
info->representable_p = (!(y_offset_adj % (mode_multiple / nregs_multiple)));
/* Only the lowpart of each block is representable. */
info->representable_p
= (subblock_offset
== subreg_size_lowpart_offset (ysize, bytes_per_block));
rknown = true;
}
info->offset = (y_offset / (mode_multiple / nregs_multiple)) * nregs_ymode;
/* We assume that the ordering of registers within a multi-register
value has a consistent endianness: if bytes and register words
have different endianness, the hard registers that make up a
multi-register value must be at least word-sized. */
if (WORDS_BIG_ENDIAN != REG_WORDS_BIG_ENDIAN)
/* The block number we calculated above followed memory endianness.
Convert it to register endianness by counting back from the end.
(Note that, because of the assumption above, each block must be
at least word-sized.) */
info->offset = (num_blocks - block_number - 1) * nregs_ymode;
else
info->offset = block_number * nregs_ymode;
info->nregs = nregs_ymode;
}