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:
parent
e0c99e151e
commit
c988af2b8c
@ -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.
|
||||
|
@ -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
|
||||
|
36
gcc/calls.c
36
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))
|
||||
{
|
||||
|
@ -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 */
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
38
gcc/expr.c
38
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));
|
||||
|
||||
|
93
gcc/stmt.c
93
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
|
||||
{
|
||||
|
@ -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, \
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user