Makefile.in (expr.o): Depend on $(TARGET_H).

* 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
This commit is contained in:
Richard Sandiford 2003-11-16 19:10:09 +00:00 committed by Richard Sandiford
parent e0c99e151e
commit c988af2b8c
11 changed files with 300 additions and 227 deletions

View File

@ -1,3 +1,27 @@
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.

View File

@ -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

View File

@ -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))
{

View File

@ -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);
/* <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 */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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
{

View File

@ -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, \

View File

@ -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,