This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Patch for n32/n64 structure returns
- From: Richard Sandiford <rsandifo at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: 11 Sep 2003 00:18:35 +0100
- Subject: Patch for n32/n64 structure returns
This patch adds support for the n32/n64 structure return conventions.
It uses a new target hook, return_in_msb, to decide whether a register
return value should be shifted to the most significant end.
For consistency with return_in_memory, return_in_msb is passed both the
return type and the function type. The latter isn't needed for mips though.
The patch also fixes complex returns for n32/n64, which previously used
$f0/$f1 rather than $f0/$f2.
Bootstrapped & regression tested on i686-pc-linux-gnu, mips64{,el}-linux-gnu,
and mips-sgi-irix6.5{,o32}. Regression tested on mipsisa64-elf.
I also used the MIPSpro compatibility tests described here:
http://gcc.gnu.org/ml/gcc-patches/2003-08/msg01483.html
The struct-return-{2,3} failures are now fixed. struct-return-10
and struct-by-value-10 still fail because of the MIPSpro bug described
in the linked message.
OK to install? If so, I'll whip up something for changes.html.
Richard
[ In more detail, return_in_msb needs three main changes:
- Shift non-BLKmode return values.
- Adjust the padding of BLKmode registers in copy_blkmode_from_reg()
and expand_return().
- Adjust the mode-searching code in hard_function_value() and
expand_return().
-- The purpose of this code is to find an integer mode that is big
enough for a BLKmode return register. return_in_msb adds an extra
requirement here: if the value is returned in the most significant
part of the register, the mode should be word_mode or bigger.
We could enforce this by checking specifically for return_in_msb.
However, it seemed more elegant to start the search at word_mode
instead. ]
* Makefile.in (expr.o): Depend on $(TARGET_H).
* target.h (calls.return_in_msb): New hook.
* target-def.h (TARGET_RETURN_IN_MSB): New macro.
(TARGET_CALLS): Include it.
* expr.h (copy_blkmode_from_reg): Add an extra tree argument.
* calls.c (shift_returned_value): New function.
(expand_call): Use it. Update calls to copy_blkmode_from_reg.
* explow.c (hard_function_value): Start mode search at word_mode.
* expr.c: Include target.h.
(copy_blkmode_from_reg): Add fntype argument. Look at return_in_msb
when deciding whether to use an offset for the register side.
Rename big_endian_correction to padding_correction.
* integrate.c (expand_inline_function): Pass extra argument to
copy_blkmode_from_reg.
* stmt.c (shift_return_value): New function.
(expand_return): As for copy_blkmode_from_reg. Don't replace
BLKmode return values with anything smaller than word_mode.
Adjust pseudo copies accordingly. Use shift_return_value.
* doc/tm.texi (TARGET_RETURN_IN_MSB): Document.
* config/mips/mips.c (TARGET_RETURN_IN_MSB): Define.
(mips_fpr_return_fields, mips_return_in_msb): New functions.
(mips_return_fpr_pair): New, mostly split out from...
(mips_function_value): ...here. Use word_mode where appropriate
for mips_return_in_msb. Fix handling of complex values for n32/n64.
* config/mips/t-iris6 (LIB2FUNCS_STATIC_EXTRA): Undefine.
* config/mips/irix6-libc-compat.c: Delete.
Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.1150
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.1150 Makefile.in
*** Makefile.in 4 Sep 2003 03:17:39 -0000 1.1150
--- Makefile.in 10 Sep 2003 22:31:52 -0000
*************** except.o : except.c $(CONFIG_H) $(SYSTEM
*** 1542,1548 ****
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
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
--- 1542,1548 ----
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 $(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
Index: target.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target.h,v
retrieving revision 1.62
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.62 target.h
*** target.h 4 Sep 2003 03:17:51 -0000 1.62
--- target.h 10 Sep 2003 22:31:53 -0000
*************** struct gcc_target
*** 394,399 ****
--- 394,400 ----
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, tree fndecl);
rtx (*expand_builtin_saveregs) (void);
/* Returns pretend_argument_size. */
void (*setup_incoming_varargs) (CUMULATIVE_ARGS *ca, enum machine_mode mode,
Index: target-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target-def.h,v
retrieving revision 1.55
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.55 target-def.h
*** target-def.h 4 Sep 2003 03:17:51 -0000 1.55
--- target-def.h 10 Sep 2003 22:31:53 -0000
*************** #define TARGET_PROMOTE_PROTOTYPES defaul
*** 311,316 ****
--- 311,317 ----
#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_tree_false
#define TARGET_EXPAND_BUILTIN_SAVEREGS default_expand_builtin_saveregs
#define TARGET_SETUP_INCOMING_VARARGS default_setup_incoming_varargs
*************** #define TARGET_CALLS { \
*** 323,328 ****
--- 324,330 ----
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, \
Index: expr.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.h,v
retrieving revision 1.146
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.146 expr.h
*** expr.h 4 Sep 2003 03:17:46 -0000 1.146
--- expr.h 10 Sep 2003 22:31:53 -0000
*************** extern void emit_group_move (rtx, rtx);
*** 415,421 ****
extern void emit_group_store (rtx, rtx, tree, int);
/* Copy BLKmode object from a set of registers. */
! extern rtx copy_blkmode_from_reg (rtx, rtx, tree);
/* Mark REG as holding a parameter for the next CALL_INSN. */
extern void use_reg (rtx *, rtx);
--- 415,421 ----
extern void emit_group_store (rtx, rtx, tree, int);
/* Copy BLKmode object from a set of registers. */
! extern rtx copy_blkmode_from_reg (rtx, rtx, tree, tree);
/* Mark REG as holding a parameter for the next CALL_INSN. */
extern void use_reg (rtx *, rtx);
Index: calls.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/calls.c,v
retrieving revision 1.297
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.297 calls.c
*** calls.c 4 Sep 2003 03:17:45 -0000 1.297
--- calls.c 10 Sep 2003 22:31:55 -0000
*************** static int check_sibcall_argument_overla
*** 152,157 ****
--- 152,158 ----
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, tree, rtx *);
#ifdef REG_PARM_STACK_SPACE
static rtx save_fixed_argument_area (int, rtx, int *, int *);
*************** fix_unsafe_tree (tree t)
*** 2009,2014 ****
--- 2010,2045 ----
return t;
}
+
+ /* If function value *VALUE was returned in the most significant part of
+ a register, shift it into the least significant part. Return true
+ and update *VALUE if some action was needed.
+
+ TYPE is the type of *VALUE and FNTYPE is the type of the function.
+ *VALUE is known not to have mode BLKmode. */
+
+ static bool
+ shift_returned_value (tree type, tree fntype, rtx *value)
+ {
+ HOST_WIDE_INT bitsize;
+
+ if (targetm.calls.return_in_msb (type, fntype)
+ && (bitsize = int_size_in_bytes (type) * BITS_PER_UNIT) < BITS_PER_WORD)
+ {
+ /* FUNCTION_VALUE should provide us with a word_mode register
+ in this case. */
+ if (GET_MODE (*value) != word_mode)
+ abort ();
+
+ *value = expand_binop (word_mode, lshr_optab, *value,
+ GEN_INT (BITS_PER_WORD - bitsize),
+ 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.
*************** expand_call (tree exp, rtx target, int i
*** 3224,3229 ****
--- 3255,3263 ----
&& GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
&& GET_MODE (target) == GET_MODE (valreg))
{
+ if (shift_returned_value (TREE_TYPE (exp), funtype, &valreg))
+ sibcall_failure = 1;
+
/* TARGET and VALREG cannot be equal at this point because the
latter would not have REG_FUNCTION_VALUE_P true, while the
former would if it were referring to the same register.
*************** expand_call (tree exp, rtx target, int i
*** 3240,3252 ****
}
else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
{
! target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
/* We can not support sibling calls for this case. */
sibcall_failure = 1;
}
else
! target = copy_to_reg (valreg);
if (targetm.calls.promote_function_return(funtype))
{
--- 3274,3292 ----
}
else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
{
! target = copy_blkmode_from_reg (target, valreg,
! TREE_TYPE (exp), funtype);
/* We can not support sibling calls for this case. */
sibcall_failure = 1;
}
else
! {
! if (shift_returned_value (TREE_TYPE (exp), funtype, &valreg))
! sibcall_failure = 1;
!
! target = copy_to_reg (valreg);
! }
if (targetm.calls.promote_function_return(funtype))
{
Index: explow.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/explow.c,v
retrieving revision 1.116
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.116 explow.c
*** explow.c 29 Jun 2003 16:21:57 -0000 1.116
--- explow.c 10 Sep 2003 22:31:55 -0000
*************** hard_function_value (tree valtype, tree
*** 1554,1571 ****
unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
enum machine_mode tmpmode;
! /* int_size_in_bytes can return -1. We don't need a check here
! since the value of bytes will be large enough that no mode
! will match and we will abort later in this function. */
! 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)
--- 1554,1571 ----
unsigned HOST_WIDE_INT bytes = int_size_in_bytes (valtype);
enum machine_mode tmpmode;
! /* Find a mode large enough to hold the value. Use at least
! word_mode in order to cope with return_in_msb.
! Note that int_size_in_bytes can return -1. We don't need a
! check here since the value of bytes will be large enough that
! no mode will match and we will abort later in this function. */
!
! for (tmpmode = word_mode;
tmpmode != VOIDmode;
tmpmode = GET_MODE_WIDER_MODE (tmpmode))
! if (GET_MODE_SIZE (tmpmode) >= bytes)
! break;
/* No suitable mode found. */
if (tmpmode == VOIDmode)
Index: expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.c,v
retrieving revision 1.582
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.582 expr.c
*** expr.c 4 Sep 2003 03:17:46 -0000 1.582
--- expr.c 10 Sep 2003 22:31:58 -0000
*************** 02111-1307, USA. */
*** 47,52 ****
--- 47,53 ----
#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.
*************** emit_group_store (rtx orig_dst, rtx src,
*** 2534,2551 ****
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. */
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;
if (tgtblk == 0)
{
--- 2535,2554 ----
set of registers starting with SRCREG into TGTBLK. If TGTBLK
is null, a stack temporary is created. TGTBLK is returned.
! 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.
!
! FNTYPE is the type of the function that is returning the structure. */
rtx
! copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type, tree fntype)
{
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, padding_correction = 0;
if (tgtblk == 0)
{
*************** copy_blkmode_from_reg (rtx tgtblk, rtx s
*** 2567,2575 ****
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));
/* Copy the structure BITSIZE bites at a time.
--- 2570,2580 ----
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 % UNITS_PER_WORD != 0
! && (targetm.calls.return_in_msb (type, fntype)
! ? !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.
*************** copy_blkmode_from_reg (rtx tgtblk, rtx s
*** 2577,2591 ****
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;
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
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
! || xbitpos == big_endian_correction)
src = operand_subword_force (srcreg, xbitpos / BITS_PER_WORD,
GET_MODE (srcreg));
--- 2582,2596 ----
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 = 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 == padding_correction
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
! || xbitpos == padding_correction)
src = operand_subword_force (srcreg, xbitpos / BITS_PER_WORD,
GET_MODE (srcreg));
Index: integrate.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/integrate.c,v
retrieving revision 1.234
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.234 integrate.c
*** integrate.c 4 Sep 2003 03:17:46 -0000 1.234
--- integrate.c 10 Sep 2003 22:31:59 -0000
*************** expand_inline_function (tree fndecl, tre
*** 1284,1290 ****
if (target
&& TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode
&& ! aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl)), fndecl))
! target = copy_blkmode_from_reg (0, target, TREE_TYPE (TREE_TYPE (fndecl)));
if (structure_value_addr)
{
--- 1284,1291 ----
if (target
&& TYPE_MODE (TREE_TYPE (TREE_TYPE (fndecl))) == BLKmode
&& ! aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl)), fndecl))
! target = copy_blkmode_from_reg (0, target, TREE_TYPE (TREE_TYPE (fndecl)),
! TREE_TYPE (fndecl));
if (structure_value_addr)
{
Index: stmt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/stmt.c,v
retrieving revision 1.328
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.328 stmt.c
*** stmt.c 4 Sep 2003 03:17:50 -0000 1.328
--- stmt.c 10 Sep 2003 22:32:01 -0000
*************** static bool check_unique_operand_names (
*** 405,410 ****
--- 405,411 ----
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_prediction (rtx val)
*** 2908,2913 ****
--- 2909,2946 ----
return PRED_NO_PREDICTION;
}
+
+ /* If the current function returns values in the most significant part
+ of a register, shift return value VAL appropriately. VAL's mode is
+ known not to be BLKmode. */
+
+ static rtx
+ shift_return_value (rtx val)
+ {
+ HOST_WIDE_INT bitsize;
+ rtx target;
+ tree type;
+
+ target = DECL_RTL (DECL_RESULT (current_function_decl));
+ type = TREE_TYPE (DECL_RESULT (current_function_decl));
+
+ if (targetm.calls.return_in_msb (type, current_function_decl)
+ && (bitsize = int_size_in_bytes (type) * BITS_PER_UNIT) < BITS_PER_WORD)
+ {
+ /* The caller should have provided us with a word-mode value
+ in this case. */
+ if (GET_MODE (target) != word_mode)
+ abort ();
+
+ val = expand_binop (word_mode, ashl_optab,
+ gen_lowpart (word_mode, val),
+ GEN_INT (BITS_PER_WORD - bitsize),
+ target, 1, OPTAB_WIDEN);
+ }
+ return val;
+ }
+
+
/* Generate RTL to return from the current function, with value VAL. */
static void
*************** expand_return (tree retval)
*** 3072,3078 ****
{
int i;
unsigned HOST_WIDE_INT bitpos, xbitpos;
! unsigned HOST_WIDE_INT big_endian_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;
--- 3105,3111 ----
{
int i;
unsigned HOST_WIDE_INT bitpos, xbitpos;
! 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;
*************** expand_return (tree retval)
*** 3081,3087 ****
rtx *result_pseudos = alloca (sizeof (rtx) * n_regs);
rtx result_reg, src = NULL_RTX, dst = NULL_RTX;
rtx result_val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0);
! enum machine_mode tmpmode, result_reg_mode;
if (bytes == 0)
{
--- 3114,3120 ----
rtx *result_pseudos = alloca (sizeof (rtx) * n_regs);
rtx result_reg, src = NULL_RTX, dst = NULL_RTX;
rtx result_val = expand_expr (retval_rhs, NULL_RTX, VOIDmode, 0);
! enum machine_mode result_reg_mode;
if (bytes == 0)
{
*************** expand_return (tree retval)
*** 3093,3113 ****
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));
/* Copy the structure BITSIZE bits at a time. */
! for (bitpos = 0, xbitpos = big_endian_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
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
! || xbitpos == big_endian_correction)
{
/* Generate an appropriate register. */
dst = gen_reg_rtx (word_mode);
--- 3126,3149 ----
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 % UNITS_PER_WORD != 0
! && (targetm.calls.return_in_msb (TREE_TYPE (retval_rhs),
! current_function_decl)
! ? !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 = 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 == padding_correction
(the first time through). */
if (xbitpos % BITS_PER_WORD == 0
! || xbitpos == padding_correction)
{
/* Generate an appropriate register. */
dst = gen_reg_rtx (word_mode);
*************** expand_return (tree retval)
*** 3136,3159 ****
/* 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 ();
! PUT_MODE (result_rtl, tmpmode);
!
! if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
! result_reg_mode = word_mode;
! else
! result_reg_mode = tmpmode;
result_reg = gen_reg_rtx (result_reg_mode);
emit_queue ();
--- 3172,3193 ----
/* 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.
!
! Use word_mode for subword structures so that we can easily
! move the pseudo created above. This also helps us to handle
! return_in_msb. */
! for (result_reg_mode = word_mode;
! result_reg_mode != VOIDmode;
! result_reg_mode = GET_MODE_WIDER_MODE (result_reg_mode))
! if (GET_MODE_SIZE (result_reg_mode) >= bytes)
break;
/* No suitable mode found. */
! if (result_reg_mode == VOIDmode)
abort ();
! PUT_MODE (result_rtl, result_reg_mode);
result_reg = gen_reg_rtx (result_reg_mode);
emit_queue ();
*************** expand_return (tree retval)
*** 3161,3169 ****
emit_move_insn (operand_subword (result_reg, i, 0, result_reg_mode),
result_pseudos[i]);
- if (tmpmode != result_reg_mode)
- result_reg = gen_lowpart (tmpmode, result_reg);
-
expand_value_return (result_reg);
}
else if (retval_rhs != 0
--- 3195,3200 ----
*************** expand_return (tree retval)
*** 3181,3187 ****
val = force_not_mem (val);
emit_queue ();
/* Return the calculated value, doing cleanups first. */
! expand_value_return (val);
}
else
{
--- 3212,3218 ----
val = force_not_mem (val);
emit_queue ();
/* Return the calculated value, doing cleanups first. */
! expand_value_return (shift_return_value (val));
}
else
{
Index: config/mips/mips.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.c,v
retrieving revision 1.309
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.309 mips.c
*** config/mips/mips.c 4 Sep 2003 09:55:30 -0000 1.309
--- config/mips/mips.c 10 Sep 2003 22:32:04 -0000
*************** static void mips_select_section (tree, i
*** 235,240 ****
--- 235,245 ----
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, 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 void mips16_fp_args (FILE *, int, int);
static void build_mips16_function_stub (FILE *);
static void mips16_optimize_gp (void);
*************** #define TARGET_ASM_FILE_END mips_file_en
*** 778,783 ****
--- 783,791 ----
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+ #undef TARGET_RETURN_IN_MSB
+ #define TARGET_RETURN_IN_MSB mips_return_in_msb
+
#ifdef TARGET_IRIX6
#undef TARGET_SECTION_TYPE_FLAGS
#define TARGET_SECTION_TYPE_FLAGS iris6_section_type_flags
*************** mips_encode_section_info (tree decl, rtx
*** 7435,7440 ****
--- 7443,7538 ----
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 that has less than three 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 func ATTRIBUTE_UNUSED)
+ {
+ 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. */
*************** rtx
*** 7443,7563 ****
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)
{
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;
! fields[i++] = field;
}
! /* Must check i, so that we reject structures with no elements. */
! if (! field)
! {
! if (i == 1)
! {
! /* 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))));
! }
! }
}
! return gen_rtx_REG (mode, reg);
}
/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE. Return
--- 7541,7599 ----
mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
enum machine_mode mode)
{
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);
+ /* Use word_mode when returning a small structure in the most
+ significant part of the register. */
+ if (mode != BLKmode
+ && GET_MODE_SIZE (mode) < UNITS_PER_WORD
+ && targetm.calls.return_in_msb (valtype, func))
+ mode = word_mode;
! /* 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);
! 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]));
}
+ }
! if (GET_MODE_CLASS (mode) == MODE_FLOAT)
! {
! /* 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_SIZE (mode) <= UNITS_PER_HWFPVALUE)
! return gen_rtx_REG (mode, FP_RETURN);
}
! else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
! 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
Index: config/mips/t-iris6
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/t-iris6,v
retrieving revision 1.18
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.18 t-iris6
*** config/mips/t-iris6 1 Apr 2003 21:45:28 -0000 1.18
--- config/mips/t-iris6 10 Sep 2003 22:32:04 -0000
*************** INSTALL_LIBGCC = install-multilib
*** 15,24 ****
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
--- 15,20 ----
Index: config/mips/irix6-libc-compat.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/irix6-libc-compat.c,v
retrieving revision 1.6
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.6 irix6-libc-compat.c
*** config/mips/irix6-libc-compat.c 27 Aug 2003 07:05:17 -0000 1.6
--- config/mips/irix6-libc-compat.c 10 Sep 2003 22:32:04 -0000
***************
*** 1,82 ****
- /* 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 GNU CC.
-
- GNU CC 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.)
-
- GNU CC 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 GNU CC; 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) */
-
- machreg_t
- inet_makeaddr (machreg_t net, machreg_t lna)
- {
- return _inet_makeaddr (net, lna) >> SHIFT_BITS;
- }
-
- #endif /* _ABIN32 || _ABI64 */
--- 0 ----
Index: doc/tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.255
diff -c -d -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.255 tm.texi
*** doc/tm.texi 4 Sep 2003 03:18:03 -0000 1.255
--- doc/tm.texi 10 Sep 2003 22:32:08 -0000
*************** be returned in memory. You should inste
*** 3929,3934 ****
--- 3929,3948 ----
to indicate this.
@end deftypefn
+ @deftypefn {Target Hook} bool TARGET_RETURN_IN_MSB (tree @var{type}, tree @var{fntype})
+ This hook should return nonzero if values of type @var{type} are returned
+ in the most significant part 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.
+
+ @var{fntype} is the type of the function being called. It is null
+ for libcalls.
+
+ If the hook returns true when @var{type} is smaller than a word,
+ but does not have mode @code{BLKmode}, @code{FUNCTION_VALUE} must
+ provide a @code{word_mode} register.
+ @end deftypefn
+
@defmac DEFAULT_PCC_STRUCT_RETURN
Define this macro to be 1 if all structure and union return values must be
in memory. Since this results in slower code, this should be defined