From 07ce14666601681f7638e8e955740566ce3386ec Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Wed, 23 Nov 2016 14:35:14 +0000 Subject: [PATCH] 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 Alan Hayward David Sherwood * 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 Co-Authored-By: David Sherwood From-SVN: r242758 --- gcc/ChangeLog | 14 +++++ gcc/rtlanal.c | 144 +++++++++++++++++++++++--------------------------- 2 files changed, 81 insertions(+), 77 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9f1a5c2e0ba..a931d1ef03a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2016-11-23 Richard Sandiford + Alan Hayward + David Sherwood + + * 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 PR target/77881 diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index d29a3fe75f7..17dbb1eaeb1 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -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) - { - 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) + 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->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)); - - /* 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); + /* 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; + + /* 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; } -- 2.30.2