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]

[revised] Patch to add return_in_msb target hook


...needs a global or middle-end maintainer!

----

This patch adds support for the MIPS n32/n64 structure return conventions.
It's a repost and revision of:

    http://gcc.gnu.org/ml/gcc-patches/2003-09/msg00657.html

which has gone unreviewed for two months.

The revision cleans up one or two things in the original patch and
reduces the number of target-independent changes.  I hope this will
make it more acceptable.  The original message was also very short
on detail so I've tried to correct that below.

First of all, I think this change is important for 3.4 because:

   - It fixes an IRIX 6 ABI incompatiblity.  Although we try to ease
     the problem by providing wrappers for certain libc functions,
     the manual has promised a proper fix for some time.

   - We've already fixed other ABI problems since 3.3.  If 3.4 went out
     in its current state, it would be incompatible with both 3.3 and
     SGI's tools.  And if we made this change in 3.5 instead, we'd have
     three consecutive GCC releases with incompatible C ABIs.

     (I guess if this change really can't go into 3.4, we ought to wind
     back the other fixes.  It would be a shame though.)

   - After this change, the compat tests will pass if gcc compiles one
     side and the SGI tools compile the other (tested both ways, see link
     above for details).  So I think this really is the last major hurdle
     in complying with the n32 & n64 calling conventions.

   - 3.4 will be the first release to support mips64-linux-gnu.  It uses
     the same ABIs as IRIX 6 and it would be nice not to make major changes
     to them after the initial release.

I think the patch is safe for non-mips targets because all uses of
the new target hook are trivially no-ops in the default case.  The only
thing that _isn't_ a trivial no-op is the "tmpmode == BLKmode" change
in expand_return, see below for details.


Background
----------

The (big-endian) 32-bit MIPS ABI says that structures are passed at the
most significant end of a register (i.e., they're padded at the least
significant end).  n32 and n64 follow this approach but, unlike the
original ABI, they extend it to return values as well.  So if we have:

    struct s { char c[2]; };
    struct s f ();

then f's return value will be padded as follows:

              56     48    40    32    24    16     8     0
        +------+------+-----+-----+-----+-----+-----+-----+
    $4  | c[0] | c[1] | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
        +------+------+-----+-----+-----+-----+-----+-----+

At the moment, there is no support for this in gcc.  It just assumes
that all values are returned in the least significant part of a register.


So... this patch adds a new target hook, return_in_msb, which says
whether a particular type of value should be returned at the most
significant end of a register.  Like I said above, all uses of this
hook are trivially no-ops when the hook returns false.


target.calls.return_in_msb
--------------------------

Quoting from the tm.texi docs:

    @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


expand_call
-----------

On the caller side, the return value is handled by the following
code in expand_call().  I've added comments after each case to
say how it's affected to by the new hook:

    if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
        || ignore)
      target = const0_rtx;

        +-------------------------------------------------------------------
        | Return value ignored, no change needed.
        +-------------------------------------------------------------------

    else if (structure_value_addr)
      ...

        +-------------------------------------------------------------------
        | Value returned by invisible reference, no change needed.
        +-------------------------------------------------------------------

    else if (pcc_struct_value)
      {
        /* This is the special C++ case where we need to
           know what the true target was.  We take care to
           never use this value more than once in one expression.  */
        target = gen_rtx_MEM (TYPE_MODE (TREE_TYPE (exp)),
                              copy_to_reg (valreg));
        set_mem_attributes (target, exp, 1);
      }

        +-------------------------------------------------------------------
        | VALREG points to a memory copy, no change needed.
        | (Well, not unless a target pads pointers at the least
        | significant end, but I doubt any would.)
        +-------------------------------------------------------------------

And from now on we're dealing with things returned by value:

    /* Handle calls that return values in multiple non-contiguous locations.
       The Irix 6 ABI has examples of this.  */
    else if (GET_CODE (valreg) == PARALLEL)
      ...

        +-------------------------------------------------------------------
        | VALREG isn't a register, so the new hook doesn't apply.
        +-------------------------------------------------------------------

    else if (target
             && GET_MODE (target) == TYPE_MODE (TREE_TYPE (exp))
             && GET_MODE (target) == GET_MODE (valreg))
      {
        /* 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.

           If they refer to the same register, this move will be a no-op,
           except when function inlining is being done.  */
        emit_move_insn (target, valreg);
        ...

        +-------------------------------------------------------------------
        | VALREG can be moved directly into the provided target.
        | I think this case can be ignored since (as I understand it)
        | there's no T for which:
        |
        |       TYPE_MODE (T) != BLKmode
        |    && GET_MODE_SIZE (TYPE_MODE (T)) != int_size_in_bytes (T)
        |
        | If that's wrong, then we should probably have a check like
        | [B] below.  This isn't needed for mips though.
        +-------------------------------------------------------------------

    else if (TYPE_MODE (TREE_TYPE (exp)) == BLKmode)
      {
        target = copy_blkmode_from_reg (target, valreg, TREE_TYPE (exp));
        ...

        +-------------------------------------------------------------------
        | VALREG is copied to memory.  We have to teach
  [A]   | copy_blkmode_from_reg() about the alternative register padding,
        | see below for details.
        +-------------------------------------------------------------------

    else
      target = copy_to_reg (valreg);

        +-------------------------------------------------------------------
        | In this case we have to check whether the value is passed in the
        | most significant part of VALREG.  If so, we must shift it right
        | by the appropriate amount and convert it to:
        |
        |       TYPE_MODE (TREE_TYPE (exp))
  [B]   |
        | ...which is the expected mode of the return value.
        |
        | For example, if an HImode structure is passed at the most
        | significant end of a DImode register, we should shift VALREG
        | right 48 bits and then convert it to HImode.
        +-------------------------------------------------------------------

The patch handles [B] through a new function, shift_returned_value.
This function is a no-op if return_in_msb is false.


copy_blkmode_from_reg
---------------------

[A] requires a change to copy_blkmode_from_reg.  As it stands, this function
is only used to copy a return value to memory.  The current version says:

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

The idea is that if X = (big_endian_correction / BITS_PER_UNIT),
the first real byte of data is in (subreg:QI SRCREG X).

However, if return_in_msb is true, the subreg offset above applies to
little-endian targets.  Big-endian targets should use an offset of 0.

So the change for return_in_msb is simple, we just need to replace:

    BYTES_BIG_ENDIAN

with:

    target.calls.return_in_msb ... ? !BYTES_BIG_ENDIAN : BYTES_BIG_ENDIAN

There are a couple of knock-on changes though:

   - In order to avoid the function call overhead when
     "bytes % UNITS_PER_WORD == 0", I've put that check first.

   - Since "big_endian_correction" becomes somewhat misleading after
     this change, I renamed it to "padding_correction".


expand_return
-------------

This handles the callee side and is simpler than expand_call():

    if (retval_rhs != 0
        && TYPE_MODE (TREE_TYPE (retval_rhs)) == BLKmode
        && GET_CODE (result_rtl) == REG)
      ...

        +-------------------------------------------------------------------
        | The equivalent of [A] above.  The code that follows is like an
  [C]   | inlined version of copy_blkmode_from_reg, except that the move is
        | from memory to registers, not the other way around.  More below.
        +-------------------------------------------------------------------

    else if (retval_rhs != 0
             && !VOID_TYPE_P (TREE_TYPE (retval_rhs))
             && (GET_CODE (result_rtl) == REG
                 || (GET_CODE (result_rtl) == PARALLEL)))
      ...

        +-------------------------------------------------------------------
        | In this case, we need to shift the return value left if
  [D]   | return_in_msb is true.  This is done through a new function,
        | shift_return_value, which is a no-op if return_in_msb is false.
        +-------------------------------------------------------------------
      }
    else
      {
        /* No cleanups or no hard reg used;
           calculate value into hard return reg.  */
        expand_expr (retval, const0_rtx, VOIDmode, 0);
        emit_queue ();
        expand_value_return (result_rtl);

        +-------------------------------------------------------------------
        | Not a register return value, no change needed.
        +-------------------------------------------------------------------

[C] needs the same sort of changes as copy_blkmode_from_reg.
There's the added complication of:

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

where result_rtl is the thing returned by FUNCTION_VALUE.  The problem
here is that tmpmode might be too small to hold a value that's passed
at the most significant end of a register.  In such cases, FUNCTION_VALUE
should have already given us the right mode, see docs quote above.

I've therefore guarded this block with:

    tmpmode = GET_MODE (result_rtl);
    if (tmpmode == BLKmode)
      ...

but otherwise left it unchanged.  I believe this is safe for all
targets (even buggy ones) because the equivalent code for the caller
side is already guarded in the same way:

    rtx
    hard_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
                         int outgoing ATTRIBUTE_UNUSED)
    {
      rtx val;

    #ifdef FUNCTION_OUTGOING_VALUE
      if (outgoing)
        val = FUNCTION_OUTGOING_VALUE (valtype, func);
      else
    #endif
        val = FUNCTION_VALUE (valtype, func);

--->  if (GET_CODE (val) == REG
--->      && GET_MODE (val) == BLKmode)
        {
          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)
            abort ();

          PUT_MODE (val, tmpmode);
        }

[Skip to "-----" if you're already convinced]

Or, to put it another way, the change could only affect other targets if:

    (a) FUNCTION_VALUE provided a non-BLKmode register for a BLKmode
        return type; and

    (b) either:
           (i) the mode provided by FUNCTION_VALUE has more words
               than the return type; or
          (ii) the mode provided by FUNCTION_VALUE is smaller than
               the return type.

I think this would be a bug, but since the patch is related to
ABI stuff, I guess it has to be considered.

So suppose (a) holds, and FUNCTION_VALUE returns a register of mode X.
>From the definition of hard_function_value() and expand_call() above,
a register of mode X will already be passed to copy_blkmode_from_reg().

If X has a word size or greater, copy_blkmode_from_reg() will extract
individual words using operand_subword_force().  expand_return() will
likewise use operand_subword().  So suppose X is too narrow or too
wide for the return type.  If this causes the patched expand_return()
to extract the the wrong word, it would already have the same effect
on copy_blkmode_from_reg().  And since the unpatched expand_return()
_never_ uses a tmpmode that is too narrow or too wide, the two
functions would not currently agree.

If X is smaller than a word, then copy_blkmode_from_reg() will
do the following:

  if (GET_MODE (srcreg) != BLKmode
      && GET_MODE_SIZE (GET_MODE (srcreg)) < UNITS_PER_WORD)
    srcreg = convert_to_mode (word_mode, srcreg, TREE_UNSIGNED (type));

So if X was too narrow to hold the return value, copy_blkmode_from_reg()
would already lose part of it.

-----

Also, as before, the patch fixes the way complex floats and doubles
are returned for n32 & n64.  At the moment we return them in $f0
and $f1, but it should be $f0 and $f2.

This patch has been bootstrapped & regression tested on
i686-pc-linux-gnu, mips-sgi-irix6.5 and mips64{,el}-linux-gnu.
Also tested on various mips*-elf targets.  OK to install?

BTW, Gerald has already reviewed the install.texi & web page side of this
(and approved them with one change).

Richard


	* 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.

Index: Makefile.in
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Makefile.in,v
retrieving revision 1.1193
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.1193 Makefile.in
*** Makefile.in	9 Nov 2003 09:27:29 -0000	1.1193
--- Makefile.in	16 Nov 2003 13:17:32 -0000
*************** except.o : except.c $(CONFIG_H) $(SYSTEM
*** 1578,1584 ****
  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
--- 1578,1584 ----
  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.69
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.69 target.h
*** target.h	2 Nov 2003 09:34:48 -0000	1.69
--- target.h	16 Nov 2003 13:17:32 -0000
*************** struct gcc_target
*** 429,434 ****
--- 429,435 ----
      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,
Index: target-def.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/target-def.h,v
retrieving revision 1.61
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.61 target-def.h
*** target-def.h	2 Nov 2003 09:34:48 -0000	1.61
--- target-def.h	16 Nov 2003 13:17:32 -0000
*************** #define TARGET_PROMOTE_PROTOTYPES defaul
*** 329,334 ****
--- 329,335 ----
  
  #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
*************** #define TARGET_CALLS {						\
*** 341,346 ****
--- 342,348 ----
     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: calls.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/calls.c,v
retrieving revision 1.304
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.304 calls.c
*** calls.c	7 Oct 2003 19:48:17 -0000	1.304
--- calls.c	16 Nov 2003 13:17:34 -0000
*************** static int check_sibcall_argument_overla
*** 148,153 ****
--- 148,154 ----
  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 *);
*************** fix_unsafe_tree (tree t)
*** 2000,2005 ****
--- 2001,2034 ----
    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.
*************** expand_call (tree exp, rtx target, int i
*** 3259,3265 ****
  	  sibcall_failure = 1;
  	}
        else
! 	target = copy_to_reg (valreg);
  
        if (targetm.calls.promote_function_return(funtype))
  	{
--- 3288,3299 ----
  	  sibcall_failure = 1;
  	}
        else
! 	{
! 	  if (shift_returned_value (TREE_TYPE (exp), &valreg))
! 	    sibcall_failure = 1;
! 
! 	  target = copy_to_reg (valreg);
! 	}
  
        if (targetm.calls.promote_function_return(funtype))
  	{
Index: expr.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/expr.c,v
retrieving revision 1.598
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.598 expr.c
*** expr.c	1 Nov 2003 00:59:53 -0000	1.598
--- expr.c	16 Nov 2003 13:17:37 -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,
*** 2121,2130 ****
     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)
--- 2122,2131 ----
     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.  */
  
  rtx
  copy_blkmode_from_reg (rtx tgtblk, rtx srcreg, tree type)
*************** copy_blkmode_from_reg (rtx tgtblk, rtx s
*** 2132,2138 ****
    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)
      {
--- 2133,2139 ----
    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
*** 2150,2162 ****
        && 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
        = (BITS_PER_WORD - ((bytes % UNITS_PER_WORD) * BITS_PER_UNIT));
  
    /* Copy the structure BITSIZE bites at a time.
--- 2151,2170 ----
        && GET_MODE_SIZE (GET_MODE (srcreg)) < UNITS_PER_WORD)
      srcreg = convert_to_mode (word_mode, srcreg, TREE_UNSIGNED (type));
  
!   /* 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.
*************** copy_blkmode_from_reg (rtx tgtblk, rtx s
*** 2164,2178 ****
       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));
  
--- 2172,2186 ----
       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: stmt.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/stmt.c,v
retrieving revision 1.334
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.334 stmt.c
*** stmt.c	9 Oct 2003 05:44:51 -0000	1.334
--- stmt.c	16 Nov 2003 13:17:38 -0000
*************** static bool check_unique_operand_names (
*** 403,408 ****
--- 403,409 ----
  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)
*** 2902,2907 ****
--- 2903,2936 ----
    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
*************** expand_return (tree retval)
*** 3066,3072 ****
      {
        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;
--- 3095,3101 ----
      {
        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)
*** 3083,3107 ****
  	  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));
  
        /* 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);
--- 3112,3144 ----
  	  return;
  	}
  
!       /* 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 = 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)
*** 3128,3148 ****
  			   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;
  
!       /* 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;
--- 3165,3189 ----
  			   BITS_PER_WORD);
  	}
  
!       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 ();
  
! 	  PUT_MODE (result_rtl, tmpmode);
! 	}
  
        if (GET_MODE_SIZE (tmpmode) < GET_MODE_SIZE (word_mode))
  	result_reg_mode = word_mode;
*************** expand_return (tree retval)
*** 3175,3181 ****
        val = force_not_mem (val);
        emit_queue ();
        /* Return the calculated value, doing cleanups first.  */
!       expand_value_return (val);
      }
    else
      {
--- 3216,3222 ----
        val = force_not_mem (val);
        emit_queue ();
        /* Return the calculated value, doing cleanups first.  */
!       expand_value_return (shift_return_value (val));
      }
    else
      {
Index: doc/tm.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/tm.texi,v
retrieving revision 1.265
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.265 tm.texi
*** doc/tm.texi	2 Nov 2003 09:34:54 -0000	1.265
--- doc/tm.texi	16 Nov 2003 13:17:42 -0000
*************** need more space than is implied by @code
*** 3903,3908 ****
--- 3903,3920 ----
  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
Index: config/mips/mips.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/mips.c,v
retrieving revision 1.343
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.343 mips.c
*** config/mips/mips.c	7 Nov 2003 08:14:32 -0000	1.343
--- config/mips/mips.c	16 Nov 2003 13:17:45 -0000
*************** static void mips_select_section (tree, i
*** 255,260 ****
--- 255,265 ----
  				  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 *);
*************** #define TARGET_INIT_LIBFUNCS mips_init_l
*** 787,792 ****
--- 792,799 ----
  
  #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;
  
*************** mips_encode_section_info (tree decl, rtx
*** 7120,7125 ****
--- 7127,7222 ----
    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.  */
*************** rtx
*** 7128,7248 ****
  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
--- 7225,7287 ----
  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);
  
!       /* 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 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))
  	{
! 	  HOST_WIDE_INT size = int_size_in_bytes (valtype);
! 	  if (size % UNITS_PER_WORD != 0)
  	    {
! 	      size += UNITS_PER_WORD - size % UNITS_PER_WORD;
! 	      mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
  	    }
  	}
      }
  
!   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
Index: config/mips/irix6-libc-compat.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/irix6-libc-compat.c,v
retrieving revision 1.8
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.8 irix6-libc-compat.c
*** config/mips/irix6-libc-compat.c	28 Oct 2003 03:47:37 -0000	1.8
--- config/mips/irix6-libc-compat.c	16 Nov 2003 13:17:45 -0000
***************
*** 1,84 ****
- /* 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 */
--- 0 ----
Index: config/mips/t-iris6
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/mips/t-iris6,v
retrieving revision 1.20
diff -c -p -F^\([(a-zA-Z0-9_]\|#define\) -r1.20 t-iris6
*** config/mips/t-iris6	12 Nov 2003 23:03:56 -0000	1.20
--- config/mips/t-iris6	16 Nov 2003 13:17:45 -0000
*************** INSTALL_LIBGCC = install-multilib
*** 14,23 ****
  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
--- 14,19 ----


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