@@ -1478,44 +1478,59 @@ gen_highpart_mode (machine_mode outermode, machine_mode innermode, rtx exp)
subreg_highpart_offset (outermode, innermode));
}
-/* Return the SUBREG_BYTE for an OUTERMODE lowpart of an INNERMODE value. */
+/* Return the SUBREG_BYTE for a lowpart subreg whose outer mode has
+ OUTER_BYTES bytes and whose inner mode has INNER_BYTES bytes. */
unsigned int
-subreg_lowpart_offset (machine_mode outermode, machine_mode innermode)
+subreg_size_lowpart_offset (unsigned int outer_bytes, unsigned int inner_bytes)
{
- unsigned int offset = 0;
- int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
+ if (outer_bytes > inner_bytes)
+ /* Paradoxical subregs always have a SUBREG_BYTE of 0. */
+ return 0;
- if (difference > 0)
- {
- if (WORDS_BIG_ENDIAN)
- offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
- if (BYTES_BIG_ENDIAN)
- offset += difference % UNITS_PER_WORD;
- }
+ if (BYTES_BIG_ENDIAN && WORDS_BIG_ENDIAN)
+ return inner_bytes - outer_bytes;
+ else if (!BYTES_BIG_ENDIAN && !WORDS_BIG_ENDIAN)
+ return 0;
+ else
+ return subreg_size_offset_from_lsb (outer_bytes, inner_bytes, 0);
+}
+
+/* Return the SUBREG_BYTE for an OUTERMODE lowpart of an INNERMODE value. */
- return offset;
+unsigned int
+subreg_lowpart_offset (machine_mode outermode, machine_mode innermode)
+{
+ return subreg_size_lowpart_offset (GET_MODE_SIZE (outermode),
+ GET_MODE_SIZE (innermode));
}
-/* Return offset in bytes to get OUTERMODE high part
- of the value in mode INNERMODE stored in memory in target format. */
+/* Return the SUBREG_BYTE for a highpart subreg whose outer mode has
+ OUTER_BYTES bytes and whose inner mode has INNER_BYTES bytes. */
+
unsigned int
-subreg_highpart_offset (machine_mode outermode, machine_mode innermode)
+subreg_size_highpart_offset (unsigned int outer_bytes,
+ unsigned int inner_bytes)
{
- unsigned int offset = 0;
- int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode));
+ gcc_assert (inner_bytes >= outer_bytes);
- gcc_assert (GET_MODE_SIZE (innermode) >= GET_MODE_SIZE (outermode));
+ if (BYTES_BIG_ENDIAN && WORDS_BIG_ENDIAN)
+ return 0;
+ else if (!BYTES_BIG_ENDIAN && !WORDS_BIG_ENDIAN)
+ return inner_bytes - outer_bytes;
+ else
+ return subreg_size_offset_from_lsb (outer_bytes, inner_bytes,
+ (inner_bytes - outer_bytes)
+ * BITS_PER_UNIT);
+}
- if (difference > 0)
- {
- if (! WORDS_BIG_ENDIAN)
- offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD;
- if (! BYTES_BIG_ENDIAN)
- offset += difference % UNITS_PER_WORD;
- }
+/* Return the SUBREG_BYTE for an OUTERMODE highpart of an INNERMODE value. */
- return offset;
+unsigned int
+subreg_highpart_offset (machine_mode outermode, machine_mode innermode)
+{
+ return subreg_size_highpart_offset (GET_MODE_SIZE (outermode),
+ GET_MODE_SIZE (innermode));
}
/* Return 1 iff X, assumed to be a SUBREG,
@@ -2178,6 +2178,10 @@ extern void get_full_rtx_cost (rtx, machine_mode, enum rtx_code, int,
extern unsigned int subreg_lsb (const_rtx);
extern unsigned int subreg_lsb_1 (machine_mode, machine_mode,
unsigned int);
+extern unsigned int subreg_size_offset_from_lsb (unsigned int, unsigned int,
+ unsigned int);
+extern unsigned int subreg_offset_from_lsb (machine_mode, machine_mode,
+ unsigned int);
extern unsigned int subreg_regno_offset (unsigned int, machine_mode,
unsigned int, machine_mode);
extern bool subreg_offset_representable_p (unsigned int, machine_mode,
@@ -2764,8 +2768,10 @@ extern rtx operand_subword (rtx, unsigned int, int, machine_mode);
extern rtx operand_subword_force (rtx, unsigned int, machine_mode);
extern bool paradoxical_subreg_p (const_rtx);
extern int subreg_lowpart_p (const_rtx);
+extern unsigned int subreg_size_lowpart_offset (unsigned int, unsigned int);
extern unsigned int subreg_lowpart_offset (machine_mode,
machine_mode);
+extern unsigned int subreg_size_highpart_offset (unsigned int, unsigned int);
extern unsigned int subreg_highpart_offset (machine_mode,
machine_mode);
extern int byte_lowpart_offset (machine_mode, machine_mode);
@@ -3528,6 +3528,55 @@ subreg_lsb (const_rtx x)
SUBREG_BYTE (x));
}
+/* Return the subreg byte offset for a subreg whose outer value has
+ OUTER_BYTES bytes, whose inner value has INNER_BYTES bytes,
+ and where there are LSB bits between the lsb of the outer value
+ and the lsb of the inner value. */
+
+unsigned int
+subreg_size_offset_from_lsb (unsigned int outer_bytes,
+ unsigned int inner_bytes,
+ unsigned int lsb)
+{
+ /* A paradoxical subreg begins at bit position 0. */
+ if (outer_bytes > inner_bytes)
+ {
+ gcc_checking_assert (lsb == 0);
+ return 0;
+ }
+
+ gcc_assert (lsb % BITS_PER_UNIT == 0);
+ unsigned int lower_bytes = lsb / BITS_PER_UNIT;
+ unsigned int upper_bytes = inner_bytes - (lower_bytes + outer_bytes);
+ if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN)
+ return upper_bytes;
+ else if (!WORDS_BIG_ENDIAN && !BYTES_BIG_ENDIAN)
+ return lower_bytes;
+ else
+ {
+ unsigned int lower_word_part = lower_bytes & -UNITS_PER_WORD;
+ unsigned int upper_word_part = upper_bytes & -UNITS_PER_WORD;
+ if (WORDS_BIG_ENDIAN)
+ return upper_word_part + (lower_bytes - lower_word_part);
+ else
+ return lower_word_part + (upper_bytes - upper_word_part);
+ }
+}
+
+/* Return the subreg byte offset for a subreg whose outer mode is
+ OUTER_MODE, whose inner mode is INNER_MODE, and where there are
+ LSB bits between the lsb of the outer value and the lsb of the
+ inner value. This is the inverse of subreg_lsb_1. */
+
+unsigned int
+subreg_offset_from_lsb (machine_mode outer_mode,
+ machine_mode inner_mode,
+ unsigned int lsb)
+{
+ return subreg_size_offset_from_lsb (GET_MODE_SIZE (outer_mode),
+ GET_MODE_SIZE (inner_mode), lsb);
+}
+
/* Fill in information about a subreg of a hard register.
xregno - A regno of an inner hard subreg_reg (or what will become one).
xmode - The mode of xregno.