This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]