diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 525ba694cf0..4105673b69f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2003-11-16 Richard Sandiford + + * Makefile.in (expr.o): Depend on $(TARGET_H). + * target.h (return_in_msb): New target hook. + * target-def.h (TARGET_RETURN_IN_MSB): New macro. + (TARGET_CALLS): Include it. + * calls.c (shift_returned_value): New function. + (expand_call): Use it. + * expr.c: Include target.h. + (copy_blkmode_from_reg): Check targetm.calls.return_in_msb when + deciding what padding is needed. Change the name of the local + padding variable from big_endian_correction to padding_correction. + * stmt.c (shift_return_value): New function. + (expand_return): Use it. Adjust memory->register copy in the same + way as copy_blkmode_from_reg. Only change the return register's + mode if it was originally BLKmode. + * doc/tm.texi (TARGET_RETURN_IN_MSB): Document. + * config/mips/mips.c (TARGET_RETURN_IN_MSB): Define. + (mips_fpr_return_fields): New, split out from mips_function_value. + (mips_return_in_msb, mips_return_fpr_pair): New functions. + (mips_function_value): Rework to use the functions above. + * config/mips/irix6-libc-compat.c: Delete. + * config/mips/t-iris6 (LIB2FUNCS_STATIC_EXTRA): Undefine. + 2003-11-16 Kazu Hirata * doc/install.texi (--enable-checking): Update valgrind's URL. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a8f28656497..7edb2efedbb 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1578,7 +1578,7 @@ except.o : except.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) \ expr.o : expr.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) flags.h \ function.h $(REGS_H) $(EXPR_H) $(OPTABS_H) libfuncs.h $(INSN_ATTR_H) insn-config.h \ $(RECOG_H) output.h typeclass.h hard-reg-set.h toplev.h hard-reg-set.h \ - except.h reload.h $(GGC_H) langhooks.h intl.h $(TM_P_H) real.h + except.h reload.h $(GGC_H) langhooks.h intl.h $(TM_P_H) real.h $(TARGET_H) dojump.o : dojump.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(RTL_H) $(TREE_H) \ flags.h function.h $(EXPR_H) $(OPTABS_H) $(INSN_ATTR_H) insn-config.h \ langhooks.h diff --git a/gcc/calls.c b/gcc/calls.c index abd4d6bb58b..a3b86ade979 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -148,6 +148,7 @@ static int check_sibcall_argument_overlap (rtx, struct arg_data *, int); static int combine_pending_stack_adjustment_and_call (int, struct args_size *, int); static tree fix_unsafe_tree (tree); +static bool shift_returned_value (tree, rtx *); #ifdef REG_PARM_STACK_SPACE static rtx save_fixed_argument_area (int, rtx, int *, int *); @@ -2022,6 +2023,34 @@ fix_unsafe_tree (tree t) return t; } + +/* If function value *VALUE was returned at the most significant end of a + register, shift it towards the least significant end and convert it to + TYPE's mode. Return true and update *VALUE if some action was needed. + + TYPE is the type of the function's return value, which is known not + to have mode BLKmode. */ + +static bool +shift_returned_value (tree type, rtx *value) +{ + if (targetm.calls.return_in_msb (type)) + { + HOST_WIDE_INT shift; + + shift = (GET_MODE_BITSIZE (GET_MODE (*value)) + - BITS_PER_UNIT * int_size_in_bytes (type)); + if (shift > 0) + { + *value = expand_binop (GET_MODE (*value), lshr_optab, *value, + GEN_INT (shift), 0, 1, OPTAB_WIDEN); + *value = convert_to_mode (TYPE_MODE (type), *value, 0); + return true; + } + } + return false; +} + /* Generate all the code for a function call and return an rtx for its value. Store the value in TARGET (specified as an rtx) if convenient. @@ -3281,7 +3310,12 @@ expand_call (tree exp, rtx target, int ignore) sibcall_failure = 1; } else - target = copy_to_reg (valreg); + { + if (shift_returned_value (TREE_TYPE (exp), &valreg)) + sibcall_failure = 1; + + target = copy_to_reg (valreg); + } if (targetm.calls.promote_function_return(funtype)) { diff --git a/gcc/config/mips/irix6-libc-compat.c b/gcc/config/mips/irix6-libc-compat.c deleted file mode 100644 index 7d4140808a5..00000000000 --- a/gcc/config/mips/irix6-libc-compat.c +++ /dev/null @@ -1,84 +0,0 @@ -/* Compensate for inconsistent structure return conventions on IRIX 6. */ -/* Compile this one with gcc. */ -/* Copyright (C) 2001 Free Software Foundation, Inc. - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -In addition to the permissions in the GNU General Public License, the -Free Software Foundation gives you unlimited permission to link the -compiled version of this file into combinations with other programs, -and to distribute those combinations without any restriction coming -from the use of this file. (The General Public License restrictions -do apply in other respects; for example, they cover modification of -the file, and distribution when not linked into a combine -executable.) - -GCC is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* GCC doesn't correctly implement the structure and union return - conventions of the N32 and N64 ABIs on IRIX 6, as described in the - MIPSpro N32 ABI Handbook, ch. 2, Calling Convention Implementations, p.7. - The ABI requires that structures (or trailing parts of structures) smaller - than 8 bytes (a 64-bit register) are left-justified, whereas GCC - right-justifies them. - - While GCC is internally consistent, calling routines compiled with a - compiler that does implement the documented ABI (like SGIs MIPSpro C - compiler) doesn't work. This is primarily an issue for system libraries - like libc. Fortunately, there exist only very few routines that return - structures by value, so until the underlying bug is fixed, it is possible - to work around it by providing wrappers for the few affected routines. - - These wrappers rely on the fact that e.g. libc contains weak versions of - those routines, and the real implementation is provided by _-prefixed - variants. So we can provide our own versions, which will only be linked - if the application uses any of the affected functions, calling the private - variants and then shifting the result as required. - - This is a rewrite of code created by Andy Polyakov. */ - -#include "config.h" -#include "system.h" -#include "coretypes.h" -#include "tm.h" - -/* This must only be used for the N32 and N64 ABIs. O32 is correct. */ - -#if _MIPS_SIM == _ABIN32 || _MIPS_SIM == _ABI64 - -/* The affected return values need to be shifted by - - BITS_PER_WORD - (sizeof (value) * BITS_PER_UNIT). - - Since only 32-bit results are involved, the shift count is always 32. */ -#define SHIFT_BITS 32 - -extern machreg_t _inet_makeaddr (machreg_t, machreg_t); - -/* has - - struct in_addr inet_makeaddr (int, int); (IRIX 6.2) - struct in_addr inet_makeaddr (in_addr_t, in_addr_t); (IRIX 6.5) */ - -extern machreg_t inet_makeaddr (machreg_t, machreg_t); - -machreg_t -inet_makeaddr (machreg_t net, machreg_t lna) -{ - return _inet_makeaddr (net, lna) >> SHIFT_BITS; -} - -#endif /* _ABIN32 || _ABI64 */ diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 9b2bc2f257d..6096eddd245 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -255,6 +255,11 @@ static void mips_select_section (tree, int, unsigned HOST_WIDE_INT) ATTRIBUTE_UNUSED; static bool mips_in_small_data_p (tree); static void mips_encode_section_info (tree, rtx, int); +static int mips_fpr_return_fields (tree, tree *); +static bool mips_return_in_msb (tree); +static rtx mips_return_fpr_pair (enum machine_mode mode, + enum machine_mode mode1, HOST_WIDE_INT, + enum machine_mode mode2, HOST_WIDE_INT); static rtx mips16_gp_pseudo_reg (void); static void mips16_fp_args (FILE *, int, int); static void build_mips16_function_stub (FILE *); @@ -787,6 +792,8 @@ const struct mips_cpu_info mips_cpu_info_table[] = { #undef TARGET_BUILD_BUILTIN_VA_LIST #define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list +#undef TARGET_RETURN_IN_MSB +#define TARGET_RETURN_IN_MSB mips_return_in_msb struct gcc_target targetm = TARGET_INITIALIZER; @@ -7120,6 +7127,96 @@ mips_encode_section_info (tree decl, rtx rtl, int first) default_encode_section_info (decl, rtl, first); } +/* See whether VALTYPE is a record whose fields should be returned in + floating-point registers. If so, return the number of fields and + list them in FIELDS (which should have two elements). Return 0 + otherwise. + + For n32 & n64, a structure with one or two fields is returned in + floating-point registers as long as every field has a floating-point + type. */ + +static int +mips_fpr_return_fields (tree valtype, tree *fields) +{ + tree field; + int i; + + if (!TARGET_NEWABI) + return 0; + + if (TREE_CODE (valtype) != RECORD_TYPE) + return 0; + + i = 0; + for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field)) + { + if (TREE_CODE (field) != FIELD_DECL) + continue; + + if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE) + return 0; + + if (i == 2) + return 0; + + fields[i++] = field; + } + return i; +} + + +/* Implement TARGET_RETURN_IN_MSB. For n32 & n64, we should return + a value in the most significant part of $2/$3 if: + + - the target is big-endian; + + - the value has a structure or union type (we generalize this to + cover aggregates from other languages too); and + + - the structure is not returned in floating-point registers. */ + +static bool +mips_return_in_msb (tree valtype) +{ + tree fields[2]; + + return (TARGET_NEWABI + && TARGET_BIG_ENDIAN + && AGGREGATE_TYPE_P (valtype) + && mips_fpr_return_fields (valtype, fields) == 0); +} + + +/* Return a composite value in a pair of floating-point registers. + MODE1 and OFFSET1 are the mode and byte offset for the first value, + likewise MODE2 and OFFSET2 for the second. MODE is the mode of the + complete value. + + For n32 & n64, $f0 always holds the first value and $f2 the second. + Otherwise the values are packed together as closely as possible. */ + +static rtx +mips_return_fpr_pair (enum machine_mode mode, + enum machine_mode mode1, HOST_WIDE_INT offset1, + enum machine_mode mode2, HOST_WIDE_INT offset2) +{ + int inc; + + inc = (TARGET_NEWABI ? 2 : FP_INC); + return gen_rtx_PARALLEL + (mode, + gen_rtvec (2, + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (mode1, FP_RETURN), + GEN_INT (offset1)), + gen_rtx_EXPR_LIST (VOIDmode, + gen_rtx_REG (mode2, FP_RETURN + inc), + GEN_INT (offset2)))); + +} + + /* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls, VALTYPE is the return type and MODE is VOIDmode. For libcalls, VALTYPE is null and MODE is the mode of the return value. */ @@ -7128,121 +7225,63 @@ rtx mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED, enum machine_mode mode) { - int reg = GP_RETURN; - enum mode_class mclass; - int unsignedp = 1; - if (valtype) { + tree fields[2]; + int unsignedp; + mode = TYPE_MODE (valtype); unsignedp = TREE_UNSIGNED (valtype); /* Since we define PROMOTE_FUNCTION_RETURN, we must promote the mode just as PROMOTE_MODE does. */ mode = promote_mode (valtype, mode, &unsignedp, 1); - } - mclass = GET_MODE_CLASS (mode); - if (mclass == MODE_FLOAT && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE) - reg = FP_RETURN; - - else if (mclass == MODE_FLOAT && mode == TFmode) - /* long doubles are really split between f0 and f2, not f1. Eek. - Use DImode for each component, since GCC wants integer modes - for subregs. */ - return gen_rtx_PARALLEL - (VOIDmode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (DImode, FP_RETURN), - GEN_INT (0)), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (DImode, FP_RETURN + 2), - GEN_INT (GET_MODE_SIZE (mode) / 2)))); - - - else if (mclass == MODE_COMPLEX_FLOAT - && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2) - { - enum machine_mode cmode = GET_MODE_INNER (mode); - - return gen_rtx_PARALLEL - (VOIDmode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (cmode, FP_RETURN), - GEN_INT (0)), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (cmode, FP_RETURN + FP_INC), - GEN_INT (GET_MODE_SIZE (cmode))))); - } - - else if (valtype && TREE_CODE (valtype) == RECORD_TYPE - && mips_abi != ABI_32 - && mips_abi != ABI_O64 - && mips_abi != ABI_EABI) - { - /* A struct with only one or two floating point fields is returned in - the floating point registers. */ - tree field, fields[2]; - int i; - - for (i = 0, field = TYPE_FIELDS (valtype); field; - field = TREE_CHAIN (field)) + /* Handle structures whose fields are returned in $f0/$f2. */ + switch (mips_fpr_return_fields (valtype, fields)) { - if (TREE_CODE (field) != FIELD_DECL) - continue; + case 1: + return gen_rtx_REG (mode, FP_RETURN); - if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2) - break; - - fields[i++] = field; + case 2: + return mips_return_fpr_pair (mode, + TYPE_MODE (TREE_TYPE (fields[0])), + int_byte_position (fields[0]), + TYPE_MODE (TREE_TYPE (fields[1])), + int_byte_position (fields[1])); } - /* Must check i, so that we reject structures with no elements. */ - if (! field) + /* If a value is passed in the most significant part of a register, see + whether we have to round the mode up to a whole number of words. */ + if (mips_return_in_msb (valtype)) { - if (i == 1) + HOST_WIDE_INT size = int_size_in_bytes (valtype); + if (size % UNITS_PER_WORD != 0) { - /* The structure has DImode, but we don't allow DImode values - in FP registers, so we use a PARALLEL even though it isn't - strictly necessary. */ - enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0])); - - return gen_rtx_PARALLEL - (mode, - gen_rtvec (1, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (field_mode, - FP_RETURN), - const0_rtx))); - } - - else if (i == 2) - { - enum machine_mode first_mode - = TYPE_MODE (TREE_TYPE (fields[0])); - enum machine_mode second_mode - = TYPE_MODE (TREE_TYPE (fields[1])); - HOST_WIDE_INT first_offset = int_byte_position (fields[0]); - HOST_WIDE_INT second_offset = int_byte_position (fields[1]); - - return gen_rtx_PARALLEL - (mode, - gen_rtvec (2, - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (first_mode, - FP_RETURN), - GEN_INT (first_offset)), - gen_rtx_EXPR_LIST (VOIDmode, - gen_rtx_REG (second_mode, - FP_RETURN + 2), - GEN_INT (second_offset)))); + size += UNITS_PER_WORD - size % UNITS_PER_WORD; + mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0); } } } - return gen_rtx_REG (mode, reg); + if (GET_MODE_CLASS (mode) == MODE_FLOAT + && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE) + return gen_rtx_REG (mode, FP_RETURN); + + /* Handle long doubles for n32 & n64. */ + if (mode == TFmode) + return mips_return_fpr_pair (mode, + DImode, 0, + DImode, GET_MODE_SIZE (mode) / 2); + + if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT + && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2) + return mips_return_fpr_pair (mode, + GET_MODE_INNER (mode), 0, + GET_MODE_INNER (mode), + GET_MODE_SIZE (mode) / 2); + + return gen_rtx_REG (mode, GP_RETURN); } /* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE. Return diff --git a/gcc/config/mips/t-iris6 b/gcc/config/mips/t-iris6 index 0af637485d7..6987925987d 100644 --- a/gcc/config/mips/t-iris6 +++ b/gcc/config/mips/t-iris6 @@ -14,10 +14,6 @@ INSTALL_LIBGCC = install-multilib EXTRA_MULTILIB_PARTS=crtbegin.o crtend.o CRTSTUFF_T_CFLAGS=-g1 -# This is only needed in the static libgcc as a band-aid until gcc correctly -# implements the N32/N64 ABI structure passing conventions -LIB2FUNCS_STATIC_EXTRA = $(srcdir)/config/mips/irix6-libc-compat.c - LIB2FUNCS_EXTRA = $(srcdir)/config/mips/_tilib.c TPBIT = tp-bit.c diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi index 51167f83cb8..bfb97e3ee19 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -3903,6 +3903,18 @@ need more space than is implied by @code{FUNCTION_VALUE_REGNO_P} for saving and restoring an arbitrary return value. @end defmac +@deftypefn {Target Hook} bool TARGET_RETURN_IN_MSB (tree @var{type}) +This hook should return true if values of type @var{type} are returned +at the most significant end of a register (in other words, if they are +padded at the least significant end). You can assume that @var{type} +is returned in a register; the caller is required to check this. + +Note that the register provided by @code{FUNCTION_VALUE} must be able +to hold the complete return value. For example, if a 1-, 2- or 3-byte +structure is returned at the most significant end of a 4-byte register, +@code{FUNCTION_VALUE} should provide an @code{SImode} rtx. +@end deftypefn + @node Aggregate Return @subsection How Large Values Are Returned @cindex aggregates as return values diff --git a/gcc/expr.c b/gcc/expr.c index 0dc9c9cc99e..9424bb9d434 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -47,6 +47,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA #include "langhooks.h" #include "intl.h" #include "tm_p.h" +#include "target.h" /* Decide whether a function's arguments should be processed from first to last or from last to first. @@ -2121,10 +2122,10 @@ emit_group_store (rtx orig_dst, rtx src, tree type ATTRIBUTE_UNUSED, int ssize) set of registers starting with SRCREG into TGTBLK. If TGTBLK is null, a stack temporary is created. TGTBLK is returned. - The primary purpose of this routine is to handle functions - that return BLKmode structures in registers. Some machines - (the PA for example) want to return all small structures - in registers regardless of the structure's alignment. */ + The purpose of this routine is to handle functions that return + BLKmode structures in registers. Some machines (the PA for example) + want to return all small structures in registers regardless of the + structure's alignment. */ rtx copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type) @@ -2132,7 +2133,7 @@ copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type) unsigned HOST_WIDE_INT bytes = int_size_in_bytes (type); rtx src = NULL, dst = NULL; unsigned HOST_WIDE_INT bitsize = MIN (TYPE_ALIGN (type), BITS_PER_WORD); - unsigned HOST_WIDE_INT bitpos, xbitpos, big_endian_correction = 0; + unsigned HOST_WIDE_INT bitpos, xbitpos, padding_correction = 0; if (tgtblk == 0) { @@ -2150,13 +2151,20 @@ copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type) && GET_MODE_SIZE (GET_MODE (srcreg)) < UNITS_PER_WORD) srcreg = convert_to_mode (word_mode, srcreg, TREE_UNSIGNED (type)); - /* Structures whose size is not a multiple of a word are aligned - to the least significant byte (to the right). On a BYTES_BIG_ENDIAN - machine, this means we must skip the empty high order bytes when - calculating the bit offset. */ - if (BYTES_BIG_ENDIAN - && bytes % UNITS_PER_WORD) - big_endian_correction + /* If the structure doesn't take up a whole number of words, see whether + SRCREG is padded on the left or on the right. If it's on the left, + set PADDING_CORRECTION to the number of bits to skip. + + In most ABIs, the structure will be returned at the least end of + the register, which translates to right padding on little-endian + targets and left padding on big-endian targets. The opposite + holds if the structure is returned at the most significant + end of the register. */ + if (bytes % UNITS_PER_WORD != 0 + && (targetm.calls.return_in_msb (type) + ? !BYTES_BIG_ENDIAN + : BYTES_BIG_ENDIAN)) + padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD) * BITS_PER_UNIT)); /* Copy the structure BITSIZE bites at a time. @@ -2164,15 +2172,15 @@ copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type) We could probably emit more efficient code for machines which do not use strict alignment, but it doesn't seem worth the effort at the current time. */ - for (bitpos = 0, xbitpos = big_endian_correction; + for (bitpos = 0, xbitpos = padding_correction; bitpos < bytes * BITS_PER_UNIT; bitpos += bitsize, xbitpos += bitsize) { /* We need a new source operand each time xbitpos is on a - word boundary and when xbitpos == big_endian_correction + word boundary and when xbitpos == padding_correction (the first time through). */ if (xbitpos % BITS_PER_WORD == 0 - || xbitpos == big_endian_correction) + || xbitpos == padding_correction) src = operand_subword_force (srcreg, xbitpos / BITS_PER_WORD, GET_MODE (srcreg)); diff --git a/gcc/stmt.c b/gcc/stmt.c index 718f2558e7d..3c8e2863edd 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -403,6 +403,7 @@ static bool check_unique_operand_names (tree, tree); static char *resolve_operand_name_1 (char *, tree, tree); static void expand_null_return_1 (rtx); static enum br_predictor return_prediction (rtx); +static rtx shift_return_value (rtx); static void expand_value_return (rtx); static int tail_recursion_args (tree, tree); static void expand_cleanups (tree, int, int); @@ -2902,6 +2903,34 @@ return_prediction (rtx val) return PRED_NO_PREDICTION; } + +/* If the current function returns values in the most significant part + of a register, shift return value VAL appropriately. The mode of + the function's return type is known not to be BLKmode. */ + +static rtx +shift_return_value (rtx val) +{ + tree type; + + type = TREE_TYPE (DECL_RESULT (current_function_decl)); + if (targetm.calls.return_in_msb (type)) + { + rtx target; + HOST_WIDE_INT shift; + + target = DECL_RTL (DECL_RESULT (current_function_decl)); + shift = (GET_MODE_BITSIZE (GET_MODE (target)) + - BITS_PER_UNIT * int_size_in_bytes (type)); + if (shift > 0) + val = expand_binop (GET_MODE (target), ashl_optab, + gen_lowpart (GET_MODE (target), val), + GEN_INT (shift), target, 1, OPTAB_WIDEN); + } + return val; +} + + /* Generate RTL to return from the current function, with value VAL. */ static void @@ -3066,7 +3095,7 @@ expand_return (tree retval) { int i; unsigned HOST_WIDE_INT bitpos, xbitpos; - unsigned HOST_WIDE_INT big_endian_correction = 0; + unsigned HOST_WIDE_INT padding_correction = 0; unsigned HOST_WIDE_INT bytes = int_size_in_bytes (TREE_TYPE (retval_rhs)); int n_regs = (bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD; @@ -3083,25 +3112,33 @@ expand_return (tree retval) return; } - /* Structures whose size is not a multiple of a word are aligned - to the least significant byte (to the right). On a BYTES_BIG_ENDIAN - machine, this means we must skip the empty high order bytes when - calculating the bit offset. */ - if (BYTES_BIG_ENDIAN - && bytes % UNITS_PER_WORD) - big_endian_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD) - * BITS_PER_UNIT)); + /* If the structure doesn't take up a whole number of words, see + whether the register value should be padded on the left or on + the right. Set PADDING_CORRECTION to the number of padding + bits needed on the left side. + + In most ABIs, the structure will be returned at the least end of + the register, which translates to right padding on little-endian + targets and left padding on big-endian targets. The opposite + holds if the structure is returned at the most significant + end of the register. */ + if (bytes % UNITS_PER_WORD != 0 + && (targetm.calls.return_in_msb (TREE_TYPE (retval_rhs)) + ? !BYTES_BIG_ENDIAN + : BYTES_BIG_ENDIAN)) + padding_correction = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD) + * BITS_PER_UNIT)); /* Copy the structure BITSIZE bits at a time. */ - for (bitpos = 0, xbitpos = big_endian_correction; + for (bitpos = 0, xbitpos = padding_correction; bitpos < bytes * BITS_PER_UNIT; bitpos += bitsize, xbitpos += bitsize) { /* We need a new destination pseudo each time xbitpos is - on a word boundary and when xbitpos == big_endian_correction + on a word boundary and when xbitpos == padding_correction (the first time through). */ if (xbitpos % BITS_PER_WORD == 0 - || xbitpos == big_endian_correction) + || xbitpos == padding_correction) { /* Generate an appropriate register. */ dst = gen_reg_rtx (word_mode); @@ -3128,21 +3165,25 @@ expand_return (tree retval) BITS_PER_WORD); } - /* Find the smallest integer mode large enough to hold the - entire structure and use that mode instead of BLKmode - on the USE insn for the return register. */ - for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT); - tmpmode != VOIDmode; - tmpmode = GET_MODE_WIDER_MODE (tmpmode)) - /* Have we found a large enough mode? */ - if (GET_MODE_SIZE (tmpmode) >= bytes) - break; + tmpmode = GET_MODE (result_rtl); + if (tmpmode == BLKmode) + { + /* Find the smallest integer mode large enough to hold the + entire structure and use that mode instead of BLKmode + on the USE insn for the return register. */ + for (tmpmode = GET_CLASS_NARROWEST_MODE (MODE_INT); + tmpmode != VOIDmode; + tmpmode = GET_MODE_WIDER_MODE (tmpmode)) + /* Have we found a large enough mode? */ + if (GET_MODE_SIZE (tmpmode) >= bytes) + break; - /* No suitable mode found. */ - if (tmpmode == VOIDmode) - abort (); + /* No suitable mode found. */ + if (tmpmode == VOIDmode) + abort (); - PUT_MODE (result_rtl, tmpmode); + PUT_MODE (result_rtl, tmpmode); + } if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode)) result_reg_mode = word_mode; @@ -3175,7 +3216,7 @@ expand_return (tree retval) val = force_not_mem (val); emit_queue (); /* Return the calculated value, doing cleanups first. */ - expand_value_return (val); + expand_value_return (shift_return_value (val)); } else { diff --git a/gcc/target-def.h b/gcc/target-def.h index c4eeb934300..5007a562e8f 100644 --- a/gcc/target-def.h +++ b/gcc/target-def.h @@ -329,6 +329,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define TARGET_STRUCT_VALUE_RTX default_struct_value_rtx #define TARGET_RETURN_IN_MEMORY default_return_in_memory +#define TARGET_RETURN_IN_MSB hook_bool_tree_false #define TARGET_EXPAND_BUILTIN_SAVEREGS default_expand_builtin_saveregs #define TARGET_SETUP_INCOMING_VARARGS default_setup_incoming_varargs @@ -341,6 +342,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. TARGET_PROMOTE_PROTOTYPES, \ TARGET_STRUCT_VALUE_RTX, \ TARGET_RETURN_IN_MEMORY, \ + TARGET_RETURN_IN_MSB, \ TARGET_EXPAND_BUILTIN_SAVEREGS, \ TARGET_SETUP_INCOMING_VARARGS, \ TARGET_STRICT_ARGUMENT_NAMING, \ diff --git a/gcc/target.h b/gcc/target.h index fbea5207c90..10be2370269 100644 --- a/gcc/target.h +++ b/gcc/target.h @@ -429,6 +429,7 @@ struct gcc_target bool (*promote_prototypes) (tree fntype); rtx (*struct_value_rtx) (tree fndecl, int incoming); bool (*return_in_memory) (tree type, tree fndecl); + bool (*return_in_msb) (tree type); rtx (*expand_builtin_saveregs) (void); /* Returns pretend_argument_size. */ void (*setup_incoming_varargs) (CUMULATIVE_ARGS *ca, enum machine_mode mode,