* 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.
From-SVN: r73652
+2003-11-16 Richard Sandiford <rsandifo@redhat.com>
+
+ * 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 <kazu@cs.umass.edu>
* doc/install.texi (--enable-checking): Update valgrind's URL.
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
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 *);
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.
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))
{
+++ /dev/null
-/* 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);
-
-/* <arpa/inet.h> 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 */
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 *);
#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;
\f
default_encode_section_info (decl, rtl, first);
}
\f
+/* 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. */
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))
- {
- if (TREE_CODE (field) != FIELD_DECL)
- continue;
- if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2)
- break;
+ /* Handle structures whose fields are returned in $f0/$f2. */
+ switch (mips_fpr_return_fields (valtype, fields))
+ {
+ case 1:
+ return gen_rtx_REG (mode, FP_RETURN);
- 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
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
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
#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.
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)
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)
{
&& 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.
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));
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);
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
{
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;
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);
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;
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
{
#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
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, \
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,