From 33951763a85087927068d553be6709cd03953a09 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Wed, 23 Nov 2016 14:31:13 +0000 Subject: [PATCH] Add more subreg offset helpers Provide versions of subreg_lowpart_offset and subreg_highpart_offset that work on mode sizes rather than modes. Also provide a routine that converts an lsb position to a subreg offset. The intent (in combination with later patches) is to move the handling of the BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN case into just two places, so that for other combinations we don't have to split offsets into words and subwords. gcc/ 2016-11-15 Richard Sandiford Alan Hayward David Sherwood * rtl.h (subreg_size_offset_from_lsb): Declare. (subreg_offset_from_lsb): New function. (subreg_size_lowpart_offset): Declare. (subreg_lowpart_offset): Turn into an inline function. (subreg_size_highpart_offset): Declare. (subreg_highpart_offset): Turn into an inline function. * emit-rtl.c (subreg_size_lowpart_offset): New function. (subreg_size_highpart_offset): Likewise * rtlanal.c (subreg_size_offset_from_lsb): Likewise. Co-Authored-By: Alan Hayward Co-Authored-By: David Sherwood From-SVN: r242755 --- gcc/ChangeLog | 14 ++++++++++++++ gcc/emit-rtl.c | 55 ++++++++++++++++++++++++++----------------------------- gcc/rtl.h | 44 ++++++++++++++++++++++++++++++++++++++++---- gcc/rtlanal.c | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 33 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bafdafb..aa1d22f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2016-11-23 Richard Sandiford + Alan Hayward + David Sherwood + + * rtl.h (subreg_size_offset_from_lsb): Declare. + (subreg_offset_from_lsb): New function. + (subreg_size_lowpart_offset): Declare. + (subreg_lowpart_offset): Turn into an inline function. + (subreg_size_highpart_offset): Declare. + (subreg_highpart_offset): Turn into an inline function. + * emit-rtl.c (subreg_size_lowpart_offset): New function. + (subreg_size_highpart_offset): Likewise + * rtlanal.c (subreg_size_offset_from_lsb): Likewise. + 2016-11-23 Richard Biener PR tree-optimization/78482 diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 9ea0c8f..04ce2d1 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -1478,44 +1478,41 @@ 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 (difference > 0) - { - if (WORDS_BIG_ENDIAN) - offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD; - if (BYTES_BIG_ENDIAN) - offset += difference % UNITS_PER_WORD; - } + if (outer_bytes > inner_bytes) + /* Paradoxical subregs always have a SUBREG_BYTE of 0. */ + return 0; - return offset; + 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 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 (difference > 0) - { - if (! WORDS_BIG_ENDIAN) - offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD; - if (! BYTES_BIG_ENDIAN) - offset += difference % UNITS_PER_WORD; - } - - return offset; + 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); } /* Return 1 iff X, assumed to be a SUBREG, diff --git a/gcc/rtl.h b/gcc/rtl.h index 21f4860..660d381 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -2178,6 +2178,24 @@ 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); + +/* 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_SHIFT *bits* between the lsb of the outer value and the lsb of + the inner value. This is the inverse of subreg_lsb_1 (which converts + byte offsets to bit shifts). */ + +inline unsigned int +subreg_offset_from_lsb (machine_mode outer_mode, + machine_mode inner_mode, + unsigned int lsb_shift) +{ + return subreg_size_offset_from_lsb (GET_MODE_SIZE (outer_mode), + GET_MODE_SIZE (inner_mode), lsb_shift); +} + 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,10 +2782,28 @@ 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_lowpart_offset (machine_mode, - machine_mode); -extern unsigned int subreg_highpart_offset (machine_mode, - machine_mode); +extern unsigned int subreg_size_lowpart_offset (unsigned int, unsigned int); + +/* Return the SUBREG_BYTE for an OUTERMODE lowpart of an INNERMODE value. */ + +inline unsigned int +subreg_lowpart_offset (machine_mode outermode, machine_mode innermode) +{ + return subreg_size_lowpart_offset (GET_MODE_SIZE (outermode), + GET_MODE_SIZE (innermode)); +} + +extern unsigned int subreg_size_highpart_offset (unsigned int, unsigned int); + +/* Return the SUBREG_BYTE for an OUTERMODE highpart of an INNERMODE value. */ + +inline unsigned int +subreg_highpart_offset (machine_mode outermode, machine_mode innermode) +{ + return subreg_size_highpart_offset (GET_MODE_SIZE (outermode), + GET_MODE_SIZE (innermode)); +} + extern int byte_lowpart_offset (machine_mode, machine_mode); extern rtx make_safe_from (rtx, rtx); extern rtx convert_memory_address_addr_space_1 (machine_mode, rtx, diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 7b8ff24..d29a3fe 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -3528,6 +3528,42 @@ 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_SHIFT *bits* between the lsb of the outer value and the + lsb of the inner value. This is the inverse of the calculation + performed by subreg_lsb_1 (which converts byte offsets to bit shifts). */ + +unsigned int +subreg_size_offset_from_lsb (unsigned int outer_bytes, + unsigned int inner_bytes, + unsigned int lsb_shift) +{ + /* A paradoxical subreg begins at bit position 0. */ + if (outer_bytes > inner_bytes) + { + gcc_checking_assert (lsb_shift == 0); + return 0; + } + + gcc_assert (lsb_shift % BITS_PER_UNIT == 0); + unsigned int lower_bytes = lsb_shift / 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); + } +} + /* 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. -- 2.7.4