diff options
-rw-r--r-- | gcc/ChangeLog | 24 | ||||
-rw-r--r-- | gcc/config/i386/i386.h | 9 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000.c | 16 | ||||
-rw-r--r-- | gcc/config/rs6000/rs6000.md | 16 | ||||
-rw-r--r-- | gcc/defaults.h | 5 | ||||
-rw-r--r-- | gcc/doc/tm.texi | 27 | ||||
-rw-r--r-- | gcc/rtlanal.c | 86 |
7 files changed, 123 insertions, 60 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index fbde18eb2a6..559be89c453 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2006-12-01 Joseph Myers <joseph@codesourcery.com> + David Edelsohn <edelsohn@gnu.org> + + PR target/24036 + * doc/tm.texi (HARD_REGNO_NREGS_HAS_PADDING, + HARD_REGNO_NREGS_WITH_PADDING): Document new target macros. + * defaults.h (HARD_REGNO_NREGS_HAS_PADDING, + HARD_REGNO_NREGS_WITH_PADDING): Define. + * config/i386/i386.h (HARD_REGNO_NREGS_HAS_PADDING, + HARD_REGNO_NREGS_WITH_PADDING): Define. + * rtlanal.c (subreg_regno_offset, subreg_offset_representable_p): + Use new macros to detect modes with holes; do not look at integer + units. + (subreg_offset_representable_p): Check for and disallow cases + where the modes use different numbers of bits from registers. + * config/rs6000/rs6000.c (rs6000_emit_move): Handle TFmode + constant for soft-float. + (rs6000_hard_regno_nregs): Use UNITS_PER_FP_WORD for e500 GPRs + containing doubles. + (rs6000_split_multireg_move): Use DFmode reg_mode for TFmode moves + in E500 double case. + * config/rs6000/rs6000.md (movtf): Allow soft-float. + (movtf_softfloat): New. + 2006-11-30 Richard Sandiford <richard@codesourcery.com> * simplify-rtx.c (simplify_rtx): Use simplify_subreg rather than diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h index 52118f98ab6..e30d6b7bdef 100644 --- a/gcc/config/i386/i386.h +++ b/gcc/config/i386/i386.h @@ -917,6 +917,15 @@ do { \ ? (TARGET_64BIT ? 4 : 6) \ : ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD))) +#define HARD_REGNO_NREGS_HAS_PADDING(REGNO, MODE) \ + ((TARGET_128BIT_LONG_DOUBLE && !TARGET_64BIT) \ + ? (FP_REGNO_P (REGNO) || SSE_REGNO_P (REGNO) || MMX_REGNO_P (REGNO) \ + ? 0 \ + : ((MODE) == XFmode || (MODE) == XCmode)) \ + : 0) + +#define HARD_REGNO_NREGS_WITH_PADDING(REGNO, MODE) ((MODE) == XFmode ? 4 : 8) + #define VALID_SSE2_REG_MODE(MODE) \ ((MODE) == V16QImode || (MODE) == V8HImode || (MODE) == V2DFmode \ || (MODE) == V2DImode || (MODE) == DFmode) diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index d607de26f84..977a66d265d 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -3822,9 +3822,6 @@ rs6000_hard_regno_nregs (int regno, enum machine_mode mode) if (FP_REGNO_P (regno)) return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD; - if (TARGET_E500_DOUBLE && mode == DFmode) - return 1; - if (SPE_SIMD_REGNO_P (regno) && TARGET_SPE && SPE_VECTOR_MODE (mode)) return (GET_MODE_SIZE (mode) + UNITS_PER_SPE_WORD - 1) / UNITS_PER_SPE_WORD; @@ -3832,6 +3829,14 @@ rs6000_hard_regno_nregs (int regno, enum machine_mode mode) return (GET_MODE_SIZE (mode) + UNITS_PER_ALTIVEC_WORD - 1) / UNITS_PER_ALTIVEC_WORD; + /* The value returned for SCmode in the E500 double case is 2 for + ABI compatibility; storing an SCmode value in a single register + would require function_arg and rs6000_spe_function_arg to handle + SCmode so as to pass the value correctly in a pair of + registers. */ + if (TARGET_E500_DOUBLE && FLOAT_MODE_P (mode) && mode != SCmode) + return (GET_MODE_SIZE (mode) + UNITS_PER_FP_WORD - 1) / UNITS_PER_FP_WORD; + return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; } @@ -4200,8 +4205,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode) /* 128-bit constant floating-point values on Darwin should really be loaded as two parts. */ - if (!TARGET_IEEEQUAD - && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128 + if (!TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128 && mode == TFmode && GET_CODE (operands[1]) == CONST_DOUBLE) { /* DImode is used, not DFmode, because simplify_gen_subreg doesn't @@ -12744,6 +12748,8 @@ rs6000_split_multireg_move (rtx dst, rtx src) reg_mode = DFmode; else if (ALTIVEC_REGNO_P (reg)) reg_mode = V16QImode; + else if (TARGET_E500_DOUBLE && mode == TFmode) + reg_mode = DFmode; else reg_mode = word_mode; reg_mode_size = GET_MODE_SIZE (reg_mode); diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index 7e1d0a19326..62541df0ccc 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -8518,8 +8518,7 @@ (define_expand "movtf" [(set (match_operand:TF 0 "general_operand" "") (match_operand:TF 1 "any_operand" ""))] - "!TARGET_IEEEQUAD - && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128" + "!TARGET_IEEEQUAD && TARGET_LONG_DOUBLE_128" "{ rs6000_emit_move (operands[0], operands[1], TFmode); DONE; }") ; It's important to list the o->f and f->o moves before f->f because @@ -8538,6 +8537,19 @@ { rs6000_split_multireg_move (operands[0], operands[1]); DONE; } [(set_attr "length" "8,8,8,20,20,16")]) +(define_insn_and_split "*movtf_softfloat" + [(set (match_operand:TF 0 "nonimmediate_operand" "=r,Y,r") + (match_operand:TF 1 "input_operand" "YGHF,r,r"))] + "!TARGET_IEEEQUAD + && (TARGET_SOFT_FLOAT || !TARGET_FPRS) && TARGET_LONG_DOUBLE_128 + && (gpc_reg_operand (operands[0], TFmode) + || gpc_reg_operand (operands[1], TFmode))" + "#" + "&& reload_completed" + [(pc)] +{ rs6000_split_multireg_move (operands[0], operands[1]); DONE; } + [(set_attr "length" "20,20,16")]) + (define_expand "extenddftf2" [(parallel [(set (match_operand:TF 0 "nonimmediate_operand" "") (float_extend:TF (match_operand:DF 1 "input_operand" ""))) diff --git a/gcc/defaults.h b/gcc/defaults.h index 3d470e94730..6af5f17bd7d 100644 --- a/gcc/defaults.h +++ b/gcc/defaults.h @@ -895,4 +895,9 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA #define INCOMING_FRAME_SP_OFFSET 0 #endif +#ifndef HARD_REGNO_NREGS_HAS_PADDING +#define HARD_REGNO_NREGS_HAS_PADDING(REGNO, MODE) 0 +#define HARD_REGNO_NREGS_WITH_PADDING(REGNO, MODE) -1 +#endif + #endif /* ! GCC_DEFAULTS_H */ diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 2a21a5890ef..634f5f170c6 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -1978,6 +1978,33 @@ definition of this macro is @end smallexample @end defmac +@defmac HARD_REGNO_NREGS_HAS_PADDING (@var{regno}, @var{mode}) +A C expression that is nonzero if a value of mode @var{mode}, stored +in memory, ends with padding that causes it to take up more space than +in registers starting at register number @var{regno} (as determined by +multiplying GCC's notion of the size of the register when containing +this mode by the number of registers returned by +@code{HARD_REGNO_NREGS}). By default this is zero. + +For example, if a floating-point value is stored in three 32-bit +registers but takes up 128 bits in memory, then this would be +nonzero. + +This macros only needs to be defined if there are cases where +@code{subreg_regno_offset} and @code{subreg_offset_representable_p} +would otherwise wrongly determine that a @code{subreg} can be +represented by an offset to the register number, when in fact such a +@code{subreg} would contain some of the padding not stored in +registers and so not be representable. +@end defmac + +@defmac HARD_REGNO_NREGS_WITH_PADDING (@var{regno}, @var{mode}) +For values of @var{regno} and @var{mode} for which +@code{HARD_REGNO_NREGS_HAS_PADDING} returns nonzero, a C expression +returning the greater number of registers required to hold the value +including any padding. In the example above, the value would be four. +@end defmac + @defmac REGMODE_NATURAL_SIZE (@var{mode}) Define this macro if the natural size of registers that hold values of mode @var{mode} is not the word size. It is a C expression that diff --git a/gcc/rtlanal.c b/gcc/rtlanal.c index 8a7c914022c..fd7fa017eec 100644 --- a/gcc/rtlanal.c +++ b/gcc/rtlanal.c @@ -2925,34 +2925,15 @@ unsigned int subreg_regno_offset (unsigned int xregno, enum machine_mode xmode, unsigned int offset, enum machine_mode ymode) { - int nregs_xmode, nregs_ymode, nregs_xmode_unit_int; + int nregs_xmode, nregs_ymode; int mode_multiple, nregs_multiple; int y_offset; - enum machine_mode xmode_unit, xmode_unit_int; gcc_assert (xregno < FIRST_PSEUDO_REGISTER); - if (GET_MODE_INNER (xmode) == VOIDmode) - xmode_unit = xmode; - else - xmode_unit = GET_MODE_INNER (xmode); - - if (FLOAT_MODE_P (xmode_unit)) - { - xmode_unit_int = int_mode_for_mode (xmode_unit); - if (xmode_unit_int == BLKmode) - /* It's probably bad to be here; a port should have an integer mode - that's the same size as anything of which it takes a SUBREG. */ - xmode_unit_int = xmode_unit; - } - else - xmode_unit_int = xmode_unit; - - nregs_xmode_unit_int = hard_regno_nregs[xregno][xmode_unit_int]; - /* Adjust nregs_xmode to allow for 'holes'. */ - if (nregs_xmode_unit_int != hard_regno_nregs[xregno][xmode_unit]) - nregs_xmode = nregs_xmode_unit_int * GET_MODE_NUNITS (xmode); + if (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode)) + nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode); else nregs_xmode = hard_regno_nregs[xregno][xmode]; @@ -2990,38 +2971,31 @@ bool subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode, unsigned int offset, enum machine_mode ymode) { - int nregs_xmode, nregs_ymode, nregs_xmode_unit, nregs_xmode_unit_int; + int nregs_xmode, nregs_ymode; int mode_multiple, nregs_multiple; int y_offset; - enum machine_mode xmode_unit, xmode_unit_int; + int regsize_xmode, regsize_ymode; gcc_assert (xregno < FIRST_PSEUDO_REGISTER); - if (GET_MODE_INNER (xmode) == VOIDmode) - xmode_unit = xmode; - else - xmode_unit = GET_MODE_INNER (xmode); - - if (FLOAT_MODE_P (xmode_unit)) - { - xmode_unit_int = int_mode_for_mode (xmode_unit); - if (xmode_unit_int == BLKmode) - /* It's probably bad to be here; a port should have an integer mode - that's the same size as anything of which it takes a SUBREG. */ - xmode_unit_int = xmode_unit; - } - else - xmode_unit_int = xmode_unit; - - nregs_xmode_unit = hard_regno_nregs[xregno][xmode_unit]; - nregs_xmode_unit_int = hard_regno_nregs[xregno][xmode_unit_int]; - /* If there are holes in a non-scalar mode in registers, we expect that it is made up of its units concatenated together. */ - if (nregs_xmode_unit != nregs_xmode_unit_int) + if (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode)) { - gcc_assert (nregs_xmode_unit * GET_MODE_NUNITS (xmode) - == hard_regno_nregs[xregno][xmode]); + enum machine_mode xmode_unit; + + nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode); + if (GET_MODE_INNER (xmode) == VOIDmode) + xmode_unit = xmode; + else + xmode_unit = GET_MODE_INNER (xmode); + gcc_assert (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode_unit)); + gcc_assert (nregs_xmode + == (GET_MODE_NUNITS (xmode) + * 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))); /* 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 @@ -3031,15 +3005,12 @@ subreg_offset_representable_p (unsigned int xregno, enum 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 (nregs_xmode_unit != nregs_xmode_unit_int - && (offset / GET_MODE_SIZE (xmode_unit_int) + 1 - < GET_MODE_NUNITS (xmode)) - && (offset / GET_MODE_SIZE (xmode_unit_int) + if ((offset / GET_MODE_SIZE (xmode_unit) + 1 + < GET_MODE_NUNITS (xmode)) + && (offset / GET_MODE_SIZE (xmode_unit) != ((offset + GET_MODE_SIZE (ymode) - 1) - / GET_MODE_SIZE (xmode_unit_int)))) + / GET_MODE_SIZE (xmode_unit)))) return false; - - nregs_xmode = nregs_xmode_unit_int * GET_MODE_NUNITS (xmode); } else nregs_xmode = hard_regno_nregs[xregno][xmode]; @@ -3053,6 +3024,15 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode, ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)) return true; + /* If registers store different numbers of bits in the different + modes, we cannot generally form this subreg. */ + regsize_xmode = GET_MODE_SIZE (xmode) / nregs_xmode; + regsize_ymode = GET_MODE_SIZE (ymode) / nregs_ymode; + if (regsize_xmode > regsize_ymode && nregs_ymode > 1) + return false; + if (regsize_ymode > regsize_xmode && nregs_xmode > 1) + return false; + /* Lowpart subregs are otherwise valid. */ if (offset == subreg_lowpart_offset (ymode, xmode)) return true; |