public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* PATCH:  mips16 float and complex support
@ 2007-05-23  0:37 Sandra Loosemore
  2007-05-23  7:59 ` Richard Sandiford
  0 siblings, 1 reply; 3+ messages in thread
From: Sandra Loosemore @ 2007-05-23  0:37 UTC (permalink / raw)
  To: GCC Patches

[-- Attachment #1: Type: text/plain, Size: 495 bytes --]

This patch does some major cleanup to fix bugs in the mips16 support for 
hardware floating point.  It also adds support for complex return values.  I've 
tested this in a mips32r2 -mabi=32 -mips16 configuration, where it cleans up 
120+ test failures, and also with -mfp64 added to that, where it gets rid of 
~250 failures.  There's also a new test case included to test all the 
combinations of stub functions for interoperability between mips16 and 
non-mips16 mode.

OK to commit?

-Sandra

[-- Attachment #2: 06-mips16-hardfloat.log --]
[-- Type: text/x-log, Size: 4919 bytes --]

2007-05-22  Sandra Loosemore  <sandra@codesourcery.com>
	    Nigel Stephens  <nigel@mips.com>
	    Richard Sandiford  <richard@codesourcery.com>

	gcc/
	Fix up MIPS16 hard float and add support for complex.

	* config/mips/mips.h (TARGET_HARD_FLOAT_ABI): New.
	(TARGET_SOFT_FLOAT_ABI): New.
	(TARGET_CPU_CPP_BUILTINS): Define __mips_hard_float and
	__mips_soft_float to reflect the ABI in use, not whether the
	FPU is directly accessible (e.g., in MIPS16 mode).
	(UNITS_PER_HWFPVALUE): Use TARGET_SOFT_FLOAT_ABI.
	(UNITS_PER_FPVALUE): Likewise.

	* config/mips/mips.c (mips_expand_call): Remove redundant
	TARGET_MIPS16 check.
	(function_arg_advance): When setting bits in cum->fp_code for
	MIPS16, don't subtract 1 from cum->arg_number, since it is now
	zero-based.
	(function_arg): Check for TARGET_MIPS16.
	(mips_return_mode_in_fpr_p): New.
	(mips16_call_stub_mode_suffix): New.
	(mips16_cfun_returns_in_fpr_p): New.
	(mips_save_reg_p): Use mips16_cfun_returns_in_fpr_p.
	(mips_output_function_prologue): Test mips16_hard_float, not
	!TARGET_SOFT_FLOAT, to decide when a function stub is required.
	(mips_expand_epilogue): Call MIPS16 helper routines to copy
	return value into a floating-point register.
	(mips_can_use_return_insn): Use mips16_cfun_returns_in_fpr_p.
	(mips_function_value): Rewrite to use mips_return_mode_in_fpr_p.
	(mips_arg_regno): New.
	(mips16_fp_args): Handle MIPS32r2 ISA which supports
	TARGET_FLOAT64, and use mfhc1/mthc1 to copy the most significant
	word of double arguments from or to the high bits of 64-bit
	floating point registers.
	(build_mips16_function_stub): Fill in DECL_RESULT for stubdecl.
	(mips16_fpret_double): New helper function.
	(build_mips16_call_stub): Use mips16_return_mode_in_fpr_p.  Add
	support for complex modes.  Fill in DECL_RESULT for stubdecl.
	(mips_init_libfuncs): Remove redundant TARGET_MIPS16 check.

	* config/mips/mips16.S 
	(RET, ARG1, ARG2): New.
	(MERGE_GPRf, MERGE_GPRt): New.
	(DELAYt, DELAYf): New.
	(MOVE_SF_BYTE0, MOVE_SI_BYTE0): New.
	(MOVE_SF_BYTE4, MOVE_SF_BYTE8): New.
	(MOVE_DF_BYTE0, MOVE_DF_BYTE8): New.
	(MOVE_SF_RET, MOVE_SC_RET, MOVE_DF_RET, MOVE_DC_RET, MOVE_SI_RET): New.
	(SFOP): Renamed to...
	(OPSF3): This, and macro-ified.  Updated all uses.
	(SFOP2): Renamed to...
	(OPSF2): This, and macro-ified.  Updated all uses.
	(SFCMP): Renamed to...
	(CMPSF): This, and macro-ified.  Updated all uses.
	(SFREVCMP): Renamed to...
	(REVCMPSF): This, and macro-ified.  Updated all uses.
	(__mips16_floatsisf, __mips16_fix_truncsfsi): Macro-ified.
	(LDDBL1, LDDBL2, RETDBL): Deleted.
	(DFOP): Renamed to...
	(OPDF3): This, and macro-ified.  Updated all uses.
	(DFOP2): Renamed to...
	(OPDF2): This, and macro-ified.  Updated all uses.
	(__mips16_extendsfdf2, __mips16_truncdfsf2): Macro-ified.
	(DFCMP): Renamed to...
	(CMPDF): This, and macro-ified.  Updated all uses.
	(DFREVCMP): Renamed to...
	(REVCMPDF): This, and macro-ified.  Updated all uses.
	(__mips16_floatsidf, __mips16_fix_truncdfsi): Macro-ified.
	(RET_FUNCTION): New.
	(__mips16_ret_sf, __mips16_ret_df): Macro-ified.
	(__mips16_ret_sc, __mips16_ret_dc): New.
	(STUB_ARGS_0, STUB_ARGS_1, STUB_ARGS_5, STUB_ARGS_9, STUB_ARGS_2,
	STUB_ARGS_6, STUB_ARGS_10): New.
	(CALL_STUB_NO_RET): New.
	(__mips16_call_stub_1): Macro-ified.
	(__mips16_call_stub_5): Macro-ified.
	(__mips16_call_stub_2): Macro-ified.
	(__mips16_call_stub_6): Macro-ified.
	(__mips16_call_stub_9): Macro-ified.
	(__mips16_call_stub_10): Macro-ified.
	(CALL_STUB_RET): New.
	(__mips16_call_stub_sf_0): Macro-ified.
	(__mips16_call_stub_sf_1): Macro-ified.
	(__mips16_call_stub_sf_5): Macro-ified.
	(__mips16_call_stub_sf_2): Macro-ified.
	(__mips16_call_stub_sf_6): Macro-ified.
	(__mips16_call_stub_sf_9): Macro-ified.
	(__mips16_call_stub_sf_10): Macro-ified.
	(__mips16_call_stub_df_0): Macro-ified.
	(__mips16_call_stub_df_1): Macro-ified.
	(__mips16_call_stub_df_5): Macro-ified.
	(__mips16_call_stub_df_2): Macro-ified.
	(__mips16_call_stub_df_6): Macro-ified.
	(__mips16_call_stub_df_9): Macro-ified.
	(__mips16_call_stub_df_10): Macro-ified.
	(__mips16_call_stub_sc_0): New.
	(__mips16_call_stub_sc_1): New.
	(__mips16_call_stub_sc_5): New.
	(__mips16_call_stub_sc_2): New.
	(__mips16_call_stub_sc_6): New.
	(__mips16_call_stub_sc_9): New.
	(__mips16_call_stub_sc_10): New.
	(__mips16_call_stub_dc_0): New.
	(__mips16_call_stub_dc_1): New.
	(__mips16_call_stub_dc_5): New.
	(__mips16_call_stub_dc_2): New.
	(__mips16_call_stub_dc_6): New.
	(__mips16_call_stub_dc_9): New.
	(__mips16_call_stub_dc_10): New.
	
	* config/mips/t-elf (LIB1ASMFUNCS): Add MIPS16 floating-point stubs.
	* config/mips/t-isa3264 (LIB1ASMFUNCS): Likewise.
	* config/mips/t-r2900 (LIB1ASMFUNCS): Likewise.

	gcc/testsuite/
	* gcc.target/mips/inter/mips16_stubs_1_main.c: New.
	* gcc.target/mips/inter/mips16_stubs_1_x.c: New.
	* gcc.target/mips/inter/mips16_stubs_1_y.c: New.
	* gcc.target/mips/inter/mips16-inter.exp: New.


[-- Attachment #3: 06-mips16-hardfloat.patch --]
[-- Type: text/x-patch, Size: 71643 bytes --]

Index: gcc/config/mips/mips.h
===================================================================
*** gcc/config/mips/mips.h	(revision 124934)
--- gcc/config/mips/mips.h	(working copy)
*************** extern const struct mips_rtx_cost_data *
*** 288,293 ****
--- 288,298 ----
  #define TARGET_OLDABI		    (mips_abi == ABI_32 || mips_abi == ABI_O64)
  #define TARGET_NEWABI		    (mips_abi == ABI_N32 || mips_abi == ABI_64)
  
+ /* Similar to TARGET_HARD_FLOAT and TARGET_SOFT_FLOAT, but reflect the ABI
+    in use rather than whether the FPU is directly accessible.  */
+ #define TARGET_HARD_FLOAT_ABI (TARGET_HARD_FLOAT || mips16_hard_float)
+ #define TARGET_SOFT_FLOAT_ABI (!TARGET_HARD_FLOAT_ABI)
+ 
  /* IRIX specific stuff.  */
  #define TARGET_IRIX	   0
  #define TARGET_IRIX6	   0
*************** extern const struct mips_rtx_cost_data *
*** 406,414 ****
  	  builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS64");	\
  	}							\
  								\
!       if (TARGET_HARD_FLOAT)					\
  	builtin_define ("__mips_hard_float");			\
!       else if (TARGET_SOFT_FLOAT)				\
  	builtin_define ("__mips_soft_float");			\
  								\
        if (TARGET_SINGLE_FLOAT)					\
--- 411,421 ----
  	  builtin_define ("_MIPS_ISA=_MIPS_ISA_MIPS64");	\
  	}							\
  								\
!       /* These defines reflect the ABI in use, not whether the  \
! 	 FPU is directly accessible.  */			\
!       if (TARGET_HARD_FLOAT_ABI)				\
  	builtin_define ("__mips_hard_float");			\
!       else							\
  	builtin_define ("__mips_soft_float");			\
  								\
        if (TARGET_SINGLE_FLOAT)					\
*************** extern const struct mips_rtx_cost_data *
*** 1033,1044 ****
  /* The largest size of value that can be held in floating-point
     registers and moved with a single instruction.  */
  #define UNITS_PER_HWFPVALUE \
!   (TARGET_SOFT_FLOAT ? 0 : MAX_FPRS_PER_FMT * UNITS_PER_FPREG)
  
  /* The largest size of value that can be held in floating-point
     registers.  */
  #define UNITS_PER_FPVALUE			\
!   (TARGET_SOFT_FLOAT ? 0			\
     : TARGET_SINGLE_FLOAT ? UNITS_PER_FPREG	\
     : LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT)
  
--- 1040,1051 ----
  /* The largest size of value that can be held in floating-point
     registers and moved with a single instruction.  */
  #define UNITS_PER_HWFPVALUE \
!   (TARGET_SOFT_FLOAT_ABI ? 0 : MAX_FPRS_PER_FMT * UNITS_PER_FPREG)
  
  /* The largest size of value that can be held in floating-point
     registers.  */
  #define UNITS_PER_FPVALUE			\
!   (TARGET_SOFT_FLOAT_ABI ? 0			\
     : TARGET_SINGLE_FLOAT ? UNITS_PER_FPREG	\
     : LONG_DOUBLE_TYPE_SIZE / BITS_PER_UNIT)
  
Index: gcc/config/mips/mips.c
===================================================================
*** gcc/config/mips/mips.c	(revision 124934)
--- gcc/config/mips/mips.c	(working copy)
*************** mips_expand_call (rtx result, rtx addr, 
*** 3490,3497 ****
        mips_load_call_address (addr, orig_addr, sibcall_p);
      }
  
!   if (TARGET_MIPS16
!       && mips16_hard_float
        && build_mips16_call_stub (result, addr, args_size,
  				 aux == 0 ? 0 : (int) GET_MODE (aux)))
      return;
--- 3490,3496 ----
        mips_load_call_address (addr, orig_addr, sibcall_p);
      }
  
!   if (mips16_hard_float
        && build_mips16_call_stub (result, addr, args_size,
  				 aux == 0 ? 0 : (int) GET_MODE (aux)))
      return;
*************** function_arg_advance (CUMULATIVE_ARGS *c
*** 3895,3901 ****
       for an explanation of what this code does.  It assumes the O32
       ABI, which passes at most 2 arguments in float registers.  */
    if (cum->arg_number < 2 && info.fpr_p)
!     cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2);
  
    if (mips_abi != ABI_EABI || !info.fpr_p)
      cum->num_gprs = info.reg_offset + info.reg_words;
--- 3894,3900 ----
       for an explanation of what this code does.  It assumes the O32
       ABI, which passes at most 2 arguments in float registers.  */
    if (cum->arg_number < 2 && info.fpr_p)
!     cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
  
    if (mips_abi != ABI_EABI || !info.fpr_p)
      cum->num_gprs = info.reg_offset + info.reg_words;
*************** function_arg (const CUMULATIVE_ARGS *cum
*** 4032,4038 ****
  	}
      }
  
!   if (!info.fpr_p)
      return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
    else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info.reg_offset > 0)
      /* In o32, the second argument is always passed in $f14
--- 4031,4037 ----
  	}
      }
  
!   if (!info.fpr_p || TARGET_MIPS16)
      return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
    else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info.reg_offset > 0)
      /* In o32, the second argument is always passed in $f14
*************** mips_global_pointer (void)
*** 6303,6308 ****
--- 6302,6352 ----
  }
  
  
+ /* Return true if the function return value MODE will get returned in a
+    floating-point register.  */
+ 
+ static bool
+ mips_return_mode_in_fpr_p (enum machine_mode mode)
+ {
+   return ((GET_MODE_CLASS (mode) == MODE_FLOAT
+ 	   || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
+ 	   || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ 	  && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
+ }
+ 
+ /* Return a two-character string representing a function floating
+    point return mode, used to name MIPS16 function stubs.  */
+ 
+ static const char *
+ mips16_call_stub_mode_suffix (enum machine_mode mode)
+ {
+   if (mode == SFmode)
+     return "sf";
+   else if (mode == DFmode)
+     return "df";
+   else if (mode == SCmode)
+     return "sc";
+   else if (mode == DCmode)
+     return "dc";
+   else if (mode == V2SFmode)
+     return "df";
+   else
+     gcc_unreachable ();
+ }
+ 
+ /* Return true if the current function returns its value in a floating-point
+    register in MIPS16 mode.  */
+ 
+ static bool
+ mips16_cfun_returns_in_fpr_p (void)
+ {
+   tree return_type = DECL_RESULT (current_function_decl);
+   return (mips16_hard_float
+ 	  && !aggregate_value_p (return_type, current_function_decl)
+  	  && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+ }
+ 
+ 
  /* Return true if the current function must save REGNO.  */
  
  static bool
*************** mips_save_reg_p (unsigned int regno)
*** 6337,6346 ****
  
    if (TARGET_MIPS16)
      {
-       tree return_type;
- 
-       return_type = DECL_RESULT (current_function_decl);
- 
        /* $18 is a special case in mips16 code.  It may be used to call
  	 a function which returns a floating point value, but it is
  	 marked in call_used_regs.  */
--- 6381,6386 ----
*************** mips_save_reg_p (unsigned int regno)
*** 6351,6360 ****
  	 value into the floating point registers if the return value is
  	 floating point.  */
        if (regno == GP_REG_FIRST + 31
! 	  && mips16_hard_float
! 	  && !aggregate_value_p (return_type, current_function_decl)
! 	  && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
! 	  && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
  	return true;
      }
  
--- 6391,6397 ----
  	 value into the floating point registers if the return value is
  	 floating point.  */
        if (regno == GP_REG_FIRST + 31
! 	  && mips16_cfun_returns_in_fpr_p ())
  	return true;
      }
  
*************** mips_output_function_prologue (FILE *fil
*** 6739,6745 ****
       floating point arguments.  The linker will arrange for any 32-bit
       functions to call this stub, which will then jump to the 16-bit
       function proper.  */
!   if (TARGET_MIPS16 && !TARGET_SOFT_FLOAT
        && current_function_args_info.fp_code != 0)
      build_mips16_function_stub (file);
  
--- 6776,6782 ----
       floating point arguments.  The linker will arrange for any 32-bit
       functions to call this stub, which will then jump to the 16-bit
       function proper.  */
!   if (mips16_hard_float
        && current_function_args_info.fp_code != 0)
      build_mips16_function_stub (file);
  
*************** mips_expand_epilogue (int sibcall_p)
*** 7069,7074 ****
--- 7106,7138 ----
        emit_jump_insn (gen_return ());
        return;
      }
+   
+   /* In mips16 mode, if the return value should go into a floating-point
+      register, we need to call a helper routine to copy it over.  */
+   if (mips16_cfun_returns_in_fpr_p ())
+     {
+       char *name;
+       rtx func;
+       rtx insn;
+       rtx retval;
+       rtx call;
+       tree id;
+       tree return_type;
+       enum machine_mode return_mode;
+ 
+       return_type = DECL_RESULT (current_function_decl);
+       return_mode = DECL_MODE (return_type);
+ 
+       name = ACONCAT (("__mips16_ret_",
+ 		       mips16_call_stub_mode_suffix (return_mode),
+ 		       NULL));
+       id = get_identifier (name);
+       func = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
+       retval = gen_rtx_REG (return_mode, GP_RETURN);
+       call = gen_call_value_internal (retval, func, const0_rtx);
+       insn = emit_call_insn (call);
+       use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
+     }
  
    /* Split the frame into two.  STEP1 is the amount of stack we should
       deallocate before restoring the registers.  STEP2 is the amount we
*************** mips_expand_epilogue (int sibcall_p)
*** 7175,7198 ****
  int
  mips_can_use_return_insn (void)
  {
-   tree return_type;
- 
    if (! reload_completed)
      return 0;
  
    if (regs_ever_live[31] || current_function_profile)
      return 0;
  
!   return_type = DECL_RESULT (current_function_decl);
! 
!   /* In mips16 mode, a function which returns a floating point value
       needs to arrange to copy the return value into the floating point
       registers.  */
!   if (TARGET_MIPS16
!       && mips16_hard_float
!       && ! aggregate_value_p (return_type, current_function_decl)
!       && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
!       && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
      return 0;
  
    if (cfun->machine->frame.initialized)
--- 7239,7254 ----
  int
  mips_can_use_return_insn (void)
  {
    if (! reload_completed)
      return 0;
  
    if (regs_ever_live[31] || current_function_profile)
      return 0;
  
!   /* In mips16 mode, a function that returns a floating point value
       needs to arrange to copy the return value into the floating point
       registers.  */
!   if (mips16_cfun_returns_in_fpr_p ())
      return 0;
  
    if (cfun->machine->frame.initialized)
*************** mips_function_value (tree valtype, tree 
*** 7617,7639 ****
  	return gen_rtx_REG (mode, GP_RETURN);
      }
  
!   if ((GET_MODE_CLASS (mode) == MODE_FLOAT
!        || GET_MODE_CLASS (mode) == MODE_VECTOR_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);
  }
--- 7673,7697 ----
  	return gen_rtx_REG (mode, GP_RETURN);
      }
  
!   if (!TARGET_MIPS16)
!     {
!       /* Handle long doubles for n32 & n64.  */
!       if (mode == TFmode)
! 	return mips_return_fpr_pair (mode,
! 				     DImode, 0,
! 				     DImode, GET_MODE_SIZE (mode) / 2);
! 
!       if (mips_return_mode_in_fpr_p (mode))
! 	{
! 	  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);
! 	  else
! 	    return gen_rtx_REG (mode, FP_RETURN);
! 	}
!     }
  
    return gen_rtx_REG (mode, GP_RETURN);
  }
*************** mips16_gp_pseudo_reg (void)
*** 7966,7971 ****
--- 8024,8047 ----
    return cfun->machine->mips16_gp_pseudo_rtx;
  }
  
+ /* INFO describes an argument that is pased in a single register value.
+    Return the register it uses, assuming that FPRs are available if
+    HARD_FLOAT_P.  */
+ 
+ static unsigned int
+ mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+ {
+   if (!info->fpr_p || !hard_float_p)
+     return GP_ARG_FIRST + info->reg_offset;
+   else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
+     /* In o32, the second argument is always passed in $f14
+        for TARGET_DOUBLE_FLOAT, regardless of whether the
+        first argument was a word or doubleword.  */
+     return FP_ARG_FIRST + 2;
+   else
+     return FP_ARG_FIRST + info->reg_offset;
+ }
+ 
  /* Write out code to move floating point arguments in or out of
     general registers.  Output the instructions to FILE.  FP_CODE is
     the code describing which arguments are present (see the comment at
*************** mips16_fp_args (FILE *file, int fp_code,
*** 7978,7983 ****
--- 8054,8060 ----
    const char *s;
    int gparg, fparg;
    unsigned int f;
+   CUMULATIVE_ARGS cum;
  
    /* This code only works for the original 32-bit ABI and the O64 ABI.  */
    gcc_assert (TARGET_OLDABI);
*************** mips16_fp_args (FILE *file, int fp_code,
*** 7986,8028 ****
      s = "mfc1";
    else
      s = "mtc1";
!   gparg = GP_ARG_FIRST;
!   fparg = FP_ARG_FIRST;
    for (f = (unsigned int) fp_code; f != 0; f >>= 2)
      {
        if ((f & 3) == 1)
! 	{
! 	  if ((fparg & 1) != 0)
! 	    ++fparg;
! 	  fprintf (file, "\t%s\t%s,%s\n", s,
! 		   reg_names[gparg], reg_names[fparg]);
! 	}
        else if ((f & 3) == 2)
! 	{
! 	  if (TARGET_64BIT)
! 	    fprintf (file, "\td%s\t%s,%s\n", s,
! 		     reg_names[gparg], reg_names[fparg]);
! 	  else
! 	    {
! 	      if ((fparg & 1) != 0)
! 		++fparg;
! 	      if (TARGET_BIG_ENDIAN)
! 		fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
! 			 reg_names[gparg], reg_names[fparg + 1], s,
! 			 reg_names[gparg + 1], reg_names[fparg]);
! 	      else
! 		fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
! 			 reg_names[gparg], reg_names[fparg], s,
! 			 reg_names[gparg + 1], reg_names[fparg + 1]);
! 	      ++gparg;
! 	      ++fparg;
! 	    }
! 	}
        else
  	gcc_unreachable ();
  
!       ++gparg;
!       ++fparg;
      }
  }
  
--- 8063,8112 ----
      s = "mfc1";
    else
      s = "mtc1";
! 
!   init_cumulative_args (&cum, NULL, NULL);
! 
    for (f = (unsigned int) fp_code; f != 0; f >>= 2)
      {
+       enum machine_mode mode;
+       struct mips_arg_info info;
+ 
        if ((f & 3) == 1)
! 	mode = SFmode;
        else if ((f & 3) == 2)
! 	mode = DFmode;
        else
  	gcc_unreachable ();
  
!       mips_arg_info (&cum, mode, NULL, true, &info);
!       gparg = mips_arg_regno (&info, false);
!       fparg = mips_arg_regno (&info, true);
! 
!       if (mode == SFmode)
! 	fprintf (file, "\t%s\t%s,%s\n", s,
! 		 reg_names[gparg], reg_names[fparg]);
!       else if (TARGET_64BIT)
! 	fprintf (file, "\td%s\t%s,%s\n", s,
! 		 reg_names[gparg], reg_names[fparg]);
!       else if (ISA_HAS_MXHC1)
! 	/* -mips32r2 -mfp64 */
! 	fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", 
! 		 s,
! 		 reg_names[gparg + (WORDS_BIG_ENDIAN ? 1 : 0)],
! 		 reg_names[fparg],
! 		 from_fp_p ? "mfhc1" : "mthc1",
! 		 reg_names[gparg + (WORDS_BIG_ENDIAN ? 0 : 1)],
! 		 reg_names[fparg]);
!       else if (TARGET_BIG_ENDIAN)
! 	fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
! 		 reg_names[gparg], reg_names[fparg + 1], s,
! 		 reg_names[gparg + 1], reg_names[fparg]);
!       else
! 	fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
! 		 reg_names[gparg], reg_names[fparg], s,
! 		 reg_names[gparg + 1], reg_names[fparg + 1]);
! 
!       function_arg_advance (&cum, mode, NULL, true);
      }
  }
  
*************** build_mips16_function_stub (FILE *file)
*** 8049,8054 ****
--- 8133,8139 ----
    stubdecl = build_decl (FUNCTION_DECL, stubid,
  			 build_function_type (void_type_node, NULL_TREE));
    DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+   DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
  
    fprintf (file, "\t# Stub function for %s (", current_function_name ());
    need_comma = 0;
*************** struct mips16_stub
*** 8122,8127 ****
--- 8207,8253 ----
  
  static struct mips16_stub *mips16_stubs;
  
+ /* Emit code to return a double value from a mips16 stub.  GPREG is the
+    first GP reg to use, FPREG is the first FP reg to use.  */
+ 
+ static void
+ mips16_fpret_double (int gpreg, int fpreg)
+ {
+   if (TARGET_64BIT)
+     fprintf (asm_out_file, "\tdmfc1\t%s,%s\n",
+  	     reg_names[gpreg], reg_names[fpreg]);
+   else if (TARGET_FLOAT64)
+     {
+       fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+  	       reg_names[gpreg + WORDS_BIG_ENDIAN],
+  	       reg_names[fpreg]);
+       fprintf (asm_out_file, "\tmfhc1\t%s,%s\n",
+  	       reg_names[gpreg + !WORDS_BIG_ENDIAN],
+  	       reg_names[fpreg]);
+     }
+   else
+     {
+       if (TARGET_BIG_ENDIAN)
+  	{
+  	  fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+  		   reg_names[gpreg + 0],
+  		   reg_names[fpreg + 1]);
+  	  fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+  		   reg_names[gpreg + 1],
+  		   reg_names[fpreg + 0]);
+  	}
+       else
+  	{
+ 	  fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+ 		   reg_names[gpreg + 0],
+  		   reg_names[fpreg + 0]);
+  	  fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+  		   reg_names[gpreg + 1],
+  		   reg_names[fpreg + 1]);
+  	}
+     }
+ }
+ 
  /* Build a call stub for a mips16 call.  A stub is needed if we are
     passing any floating point values which should go into the floating
     point registers.  If we are, and the call turns out to be to a
*************** static struct mips16_stub *mips16_stubs;
*** 8143,8149 ****
  int
  build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
  {
!   int fpret;
    const char *fnname;
    char *secname, *stubname;
    struct mips16_stub *l;
--- 8269,8275 ----
  int
  build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
  {
!   int fpret = 0;
    const char *fnname;
    char *secname, *stubname;
    struct mips16_stub *l;
*************** build_mips16_call_stub (rtx retval, rtx 
*** 8153,8166 ****
  
    /* We don't need to do anything if we aren't in mips16 mode, or if
       we were invoked with the -msoft-float option.  */
!   if (! TARGET_MIPS16 || ! mips16_hard_float)
      return 0;
  
    /* Figure out whether the value might come back in a floating point
       register.  */
!   fpret = (retval != 0
! 	   && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
! 	   && GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE);
  
    /* We don't need to do anything if there were no floating point
       arguments and the value will not be returned in a floating point
--- 8279,8291 ----
  
    /* We don't need to do anything if we aren't in mips16 mode, or if
       we were invoked with the -msoft-float option.  */
!   if (!mips16_hard_float)
      return 0;
  
    /* Figure out whether the value might come back in a floating point
       register.  */
!   if (retval)
!     fpret = mips_return_mode_in_fpr_p (GET_MODE (retval));
  
    /* We don't need to do anything if there were no floating point
       arguments and the value will not be returned in a floating point
*************** build_mips16_call_stub (rtx retval, rtx 
*** 8178,8188 ****
       require more sophisticated support.  */
    gcc_assert (TARGET_OLDABI);
  
-   /* We can only handle SFmode and DFmode floating point return
-      values.  */
-   if (fpret)
-     gcc_assert (GET_MODE (retval) == SFmode || GET_MODE (retval) == DFmode);
- 
    /* If we're calling via a function pointer, then we must always call
       via a stub.  There are magic stubs provided in libgcc.a for each
       of the required cases.  Each of them expects the function address
--- 8303,8308 ----
*************** build_mips16_call_stub (rtx retval, rtx 
*** 8197,8207 ****
        /* ??? If this code is modified to support other ABI's, we need
           to handle PARALLEL return values here.  */
  
!       sprintf (buf, "__mips16_call_stub_%s%d",
! 	       (fpret
! 		? (GET_MODE (retval) == SFmode ? "sf_" : "df_")
! 		: ""),
! 	       fp_code);
        id = get_identifier (buf);
        stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
  
--- 8317,8330 ----
        /* ??? If this code is modified to support other ABI's, we need
           to handle PARALLEL return values here.  */
  
!       if (fpret)
! 	sprintf (buf, "__mips16_call_stub_%s_%d",
! 		 mips16_call_stub_mode_suffix (GET_MODE (retval)),
! 		 fp_code);
!       else
! 	sprintf (buf, "__mips16_call_stub_%d",
! 		 fp_code);
! 
        id = get_identifier (buf);
        stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
  
*************** build_mips16_call_stub (rtx retval, rtx 
*** 8277,8282 ****
--- 8400,8406 ----
        stubdecl = build_decl (FUNCTION_DECL, stubid,
  			     build_function_type (void_type_node, NULL_TREE));
        DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+       DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
  
        fprintf (asm_out_file, "\t# Stub function to call %s%s (",
  	       (fpret
*************** build_mips16_call_stub (rtx retval, rtx 
*** 8339,8344 ****
--- 8463,8489 ----
  	  if (GET_MODE (retval) == SFmode)
  	    fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
  		     reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
+  	  else if (GET_MODE (retval) == SCmode)
+  	    {
+  	      fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+  		       reg_names[GP_REG_FIRST + 2],
+ 		       reg_names[FP_REG_FIRST + 0]);
+  	      fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+  		       reg_names[GP_REG_FIRST + 3],
+ 		       reg_names[FP_REG_FIRST + MAX_FPRS_PER_FMT]);
+  	    }
+  	  else if (GET_MODE (retval) == DFmode
+ 		   || GET_MODE (retval) == V2SFmode)
+  	    {
+  	      mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
+  	    }
+  	  else if (GET_MODE (retval) == DCmode)
+ 	    {
+  	      mips16_fpret_double (GP_REG_FIRST + 2,
+ 				   FP_REG_FIRST + 0);
+  	      mips16_fpret_double (GP_REG_FIRST + 4,
+ 				   FP_REG_FIRST + MAX_FPRS_PER_FMT);
+  	    }
  	  else
  	    {
  	      if (TARGET_BIG_ENDIAN)
*************** mips_init_libfuncs (void)
*** 9190,9196 ****
        set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
      }
  
!   if (TARGET_MIPS16 && mips16_hard_float)
      {
        set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
        set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
--- 9335,9341 ----
        set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
      }
  
!   if (mips16_hard_float)
      {
        set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
        set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
Index: gcc/config/mips/mips16.S
===================================================================
*** gcc/config/mips/mips16.S	(revision 124934)
--- gcc/config/mips/mips16.S	(working copy)
*************** Boston, MA 02110-1301, USA.  */
*** 49,117 ****
  
  #define ENDFN(NAME) .end NAME
  
! /* Single precision math.  */
  
! /* This macro defines a function which loads two single precision
!    values, performs an operation, and returns the single precision
!    result.  */
  
! #define SFOP(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	.set	noreorder;	\
! 	mtc1	$4,$f0;		\
! 	mtc1	$5,$f2;		\
! 	nop;			\
! 	OPCODE	$f0,$f0,$f2;	\
! 	mfc1	$2,$f0;		\
! 	j	$31;		\
! 	nop;			\
! 	.set	reorder;	\
  	ENDFN (NAME)
  
  #ifdef L_m16addsf3
! SFOP(__mips16_addsf3, add.s)
  #endif
  #ifdef L_m16subsf3
! SFOP(__mips16_subsf3, sub.s)
  #endif
  #ifdef L_m16mulsf3
! SFOP(__mips16_mulsf3, mul.s)
  #endif
  #ifdef L_m16divsf3
! SFOP(__mips16_divsf3, div.s)
  #endif
  
! #define SFOP2(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	.set	noreorder;	\
! 	mtc1	$4,$f0;		\
! 	nop;			\
! 	OPCODE	$f0,$f0;	\
! 	mfc1	$2,$f0;		\
! 	j	$31;		\
! 	nop;			\
! 	.set	reorder;	\
  	ENDFN (NAME)
  
  #ifdef L_m16negsf2
! SFOP2(__mips16_negsf2, neg.s)
  #endif
  #ifdef L_m16abssf2
! SFOP2(__mips16_abssf2, abs.s)
  #endif
  
! /* Single precision comparisons.  */
  
! /* This macro defines a function which loads two single precision
!    values, performs a floating point comparison, and returns the
!    specified values according to whether the comparison is true or
!    false.  */
  
! #define SFCMP(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	mtc1	$4,$f0;				\
! 	mtc1	$5,$f2;				\
! 	OPCODE	$f0,$f2;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
--- 49,252 ----
  
  #define ENDFN(NAME) .end NAME
  
! /* ARG1
! 	The FPR that holds the first floating-point argument.
  
!    ARG2
! 	The FPR that holds the second floating-point argument.
  
!    RET
! 	The FPR that holds a floating-point return value.  */
! 
! #define RET $f0
! #define ARG1 $f12
! #ifdef __mips64
! #define ARG2 $f13
! #else
! #define ARG2 $f14
! #endif
! 
! /* Set 64-bit register GPR so that its high 32 bits contain HIGH_FPR
!    and so that its low 32 bits contain LOW_FPR.  */
! #define MERGE_GPRf(GPR, HIGH_FPR, LOW_FPR)	\
! 	.set	noat;				\
! 	mfc1	GPR, HIGH_FPR;			\
! 	mfc1	$1, LOW_FPR;			\
! 	dsll	GPR, GPR, 32;			\
! 	or	GPR, GPR, $1;			\
! 	.set	at
! 
! /* Move the high 32 bits of GPR to HIGH_FPR and the low 32 bits of
!    GPR to LOW_FPR.  */
! #define MERGE_GPRt(GPR, HIGH_FPR, LOW_FPR)	\
! 	.set	noat;				\
! 	dsrl	$1, GPR, 32;			\
! 	mtc1	GPR, LOW_FPR;			\
! 	mtc1	$1, HIGH_FPR;			\
! 	.set	at
! 
! /* Jump to T, and use "OPCODE, OP2" to implement a delayed move.  */
! #define DELAYt(T, OPCODE, OP2)			\
! 	.set	noreorder;			\
! 	jr	T;				\
! 	OPCODE, OP2;				\
! 	.set	reorder
! 
! /* Use "OPCODE. OP2" and jump to T.  */
! #define DELAYf(T, OPCODE, OP2) OPCODE, OP2; jr T
! 
! /* MOVE_SF_BYTE0(D)
! 	Move the first single-precision floating-point argument between
! 	GPRs and FPRs.
! 
!    MOVE_SI_BYTE0(D)
! 	Likewise the first single-precision integer argument.
! 
!    MOVE_SF_BYTE4(D)
! 	Move the second single-precision floating-point argument between
! 	GPRs and FPRs, given that the first argument occupies 4 bytes.
! 
!    MOVE_SF_BYTE8(D)
! 	Move the second single-precision floating-point argument between
! 	GPRs and FPRs, given that the first argument occupies 8 bytes.
! 
!    MOVE_DF_BYTE0(D)
! 	Move the first double-precision floating-point argument between
! 	GPRs and FPRs.
! 
!    MOVE_DF_BYTE8(D)
! 	Likewise the second double-precision floating-point argument.
! 
!    MOVE_SF_RET(D, T)
! 	Likewise a single-precision floating-point return value,
! 	then jump to T.
! 
!    MOVE_SC_RET(D, T)
! 	Likewise a complex single-precision floating-point return value.
! 
!    MOVE_DF_RET(D, T)
! 	Likewise a double-precision floating-point return value.
! 
!    MOVE_DC_RET(D, T)
! 	Likewise a complex double-precision floating-point return value.
! 
!    MOVE_SI_RET(D, T)
! 	Likewise a single-precision integer return value.
! 
!    The D argument is "t" to move to FPRs and "f" to move from FPRs.
!    The return macros may assume that the target of the jump does not
!    use a floating-point register.  */
! 
! #define MOVE_SF_RET(D, T) DELAY##D (T, m##D##c1 $2,$f0)
! #define MOVE_SI_RET(D, T) DELAY##D (T, m##D##c1 $2,$f0)
! 
! #if defined(__mips64) && defined(__MIPSEB__)
! #define MOVE_SC_RET(D, T) MERGE_GPR##D ($2, $f0, $f1); jr T
! #elif defined(__mips64)
! /* The high 32 bits of $2 correspond to the second word in memory;
!    i.e. the imaginary part.  */
! #define MOVE_SC_RET(D, T) MERGE_GPR##D ($2, $f1, $f0); jr T
! #elif __mips_fpr == 64
! #define MOVE_SC_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f1)
! #else
! #define MOVE_SC_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f2)
! #endif
! 
! #if defined(__mips64)
! #define MOVE_SF_BYTE0(D) m##D##c1 $4,$f12
! #define MOVE_SF_BYTE4(D) m##D##c1 $5,$f13
! #define MOVE_SF_BYTE8(D) m##D##c1 $5,$f13
! #else
! #define MOVE_SF_BYTE0(D) m##D##c1 $4,$f12
! #define MOVE_SF_BYTE4(D) m##D##c1 $5,$f14
! #define MOVE_SF_BYTE8(D) m##D##c1 $6,$f14
! #endif
! #define MOVE_SI_BYTE0(D) MOVE_SF_BYTE0(D)
! 
! #if defined(__mips64)
! #define MOVE_DF_BYTE0(D) dm##D##c1 $4,$f12
! #define MOVE_DF_BYTE8(D) dm##D##c1 $5,$f13
! #define MOVE_DF_RET(D, T) DELAY##D (T, dm##D##c1 $2,$f0)
! #define MOVE_DC_RET(D, T) dm##D##c1 $3,$f1; MOVE_DF_RET (D, T)
! #elif __mips_fpr == 64 && defined(__MIPSEB__)
! #define MOVE_DF_BYTE0(D) m##D##c1 $5,$f12; m##D##hc1 $4,$f12
! #define MOVE_DF_BYTE8(D) m##D##c1 $7,$f14; m##D##hc1 $6,$f14
! #define MOVE_DF_RET(D, T) m##D##c1 $3,$f0; DELAY##D (T, m##D##hc1 $2,$f0)
! #define MOVE_DC_RET(D, T) m##D##c1 $5,$f1; m##D##hc1 $4,$f1; MOVE_DF_RET (D, T)
! #elif __mips_fpr == 64
! #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f12; m##D##hc1 $5,$f12
! #define MOVE_DF_BYTE8(D) m##D##c1 $6,$f14; m##D##hc1 $7,$f14
! #define MOVE_DF_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##hc1 $3,$f0)
! #define MOVE_DC_RET(D, T) m##D##c1 $4,$f1; m##D##hc1 $5,$f1; MOVE_DF_RET (D, T)
! #elif defined(__MIPSEB__)
! /* FPRs are little-endian.  */
! #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f13; m##D##c1 $5,$f12
! #define MOVE_DF_BYTE8(D) m##D##c1 $6,$f15; m##D##c1 $7,$f14
! #define MOVE_DF_RET(D, T) m##D##c1 $2,$f1; DELAY##D (T, m##D##c1 $3,$f0)
! #define MOVE_DC_RET(D, T) m##D##c1 $4,$f3; m##D##c1 $5,$f2; MOVE_DF_RET (D, T)
! #else
! #define MOVE_DF_BYTE0(D) m##D##c1 $4,$f12; m##D##c1 $5,$f13
! #define MOVE_DF_BYTE8(D) m##D##c1 $6,$f14; m##D##c1 $7,$f15
! #define MOVE_DF_RET(D, T) m##D##c1 $2,$f0; DELAY##D (T, m##D##c1 $3,$f1)
! #define MOVE_DC_RET(D, T) m##D##c1 $4,$f2; m##D##c1 $5,$f3; MOVE_DF_RET (D, T)
! #endif
! 
! /* Single-precision math.  */
! 
! /* Define a function NAME that loads two single-precision values,
!    performs FPU operation OPCODE on them, and returns the single-
!    precision result.  */
! 
! #define OPSF3(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	MOVE_SF_BYTE0 (t);	\
! 	MOVE_SF_BYTE4 (t);	\
! 	OPCODE	RET,ARG1,ARG2;	\
! 	MOVE_SF_RET (f, $31);	\
  	ENDFN (NAME)
  
  #ifdef L_m16addsf3
! OPSF3 (__mips16_addsf3, add.s)
  #endif
  #ifdef L_m16subsf3
! OPSF3 (__mips16_subsf3, sub.s)
  #endif
  #ifdef L_m16mulsf3
! OPSF3 (__mips16_mulsf3, mul.s)
  #endif
  #ifdef L_m16divsf3
! OPSF3 (__mips16_divsf3, div.s)
  #endif
  
! /* Define a function NAME that loads a single-precision value,
!    performs FPU operation OPCODE on it, and returns the single-
!    precision result.  */
! 
! #define OPSF2(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	MOVE_SF_BYTE0 (t);	\
! 	OPCODE	RET,ARG1;	\
! 	MOVE_SF_RET (f, $31);	\
  	ENDFN (NAME)
  
  #ifdef L_m16negsf2
! OPSF2 (__mips16_negsf2, neg.s)
  #endif
  #ifdef L_m16abssf2
! OPSF2 (__mips16_abssf2, abs.s)
  #endif
  
! /* Single-precision comparisons.  */
  
! /* Define a function NAME that loads two single-precision values,
!    performs floating point comparison OPCODE, and returns TRUE or
!    FALSE depending on the result.  */
  
! #define CMPSF(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	MOVE_SF_BYTE0 (t);			\
! 	MOVE_SF_BYTE4 (t);			\
! 	OPCODE	ARG1,ARG2;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
*************** STARTFN (NAME);					\
*** 119,131 ****
  	j	$31;				\
  	ENDFN (NAME)
  
! /* This macro is like SFCMP, but it reverses the comparison.  */
  
! #define SFREVCMP(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	mtc1	$4,$f0;				\
! 	mtc1	$5,$f2;				\
! 	OPCODE	$f2,$f0;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
--- 254,266 ----
  	j	$31;				\
  	ENDFN (NAME)
  
! /* Like CMPSF, but reverse the comparison operands.  */
  
! #define REVCMPSF(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	MOVE_SF_BYTE0 (t);			\
! 	MOVE_SF_BYTE4 (t);			\
! 	OPCODE	ARG2,ARG1;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
*************** STARTFN (NAME);					\
*** 134,322 ****
  	ENDFN (NAME)
  
  #ifdef L_m16eqsf2
! SFCMP(__mips16_eqsf2, c.eq.s, 0, 1)
  #endif
  #ifdef L_m16nesf2
! SFCMP(__mips16_nesf2, c.eq.s, 0, 1)
  #endif
  #ifdef L_m16gtsf2
! SFREVCMP(__mips16_gtsf2, c.lt.s, 1, 0)
  #endif
  #ifdef L_m16gesf2
! SFREVCMP(__mips16_gesf2, c.le.s, 0, -1)
  #endif
  #ifdef L_m16lesf2
! SFCMP(__mips16_lesf2, c.le.s, 0, 1)
  #endif
  #ifdef L_m16ltsf2
! SFCMP(__mips16_ltsf2, c.lt.s, -1, 0)
  #endif
  
! /* Single precision conversions.  */
  
  #ifdef L_m16fltsisf
  STARTFN (__mips16_floatsisf)
! 	.set	noreorder
! 	mtc1	$4,$f0
! 	nop
! 	cvt.s.w	$f0,$f0
! 	mfc1	$2,$f0
! 	j	$31
! 	nop
! 	.set	reorder
  	ENDFN (__mips16_floatsisf)
  #endif
  
  #ifdef L_m16fix_truncsfsi
  STARTFN (__mips16_fix_truncsfsi)
! 	.set	noreorder
! 	mtc1	$4,$f0
! 	nop
! 	trunc.w.s $f0,$f0,$4
! 	mfc1	$2,$f0
! 	j	$31
! 	nop
! 	.set	reorder
  	ENDFN (__mips16_fix_truncsfsi)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  
! /* The double precision operations.  We need to use different code
!    based on the preprocessor symbol __mips64, because the way in which
!    double precision values will change.  Without __mips64, the value
!    is passed in two 32-bit registers.  With __mips64, the value is
!    passed in a single 64-bit register.  */
! 
! /* Load the first double precision operand.  */
! 
! #if defined(__mips64)
! #define LDDBL1 dmtc1 $4,$f12
! #elif defined(__mipsfp64)
! #define LDDBL1 sw $4,0($29); sw $5,4($29); l.d $f12,0($29)
! #elif defined(__MIPSEB__)
! #define LDDBL1 mtc1 $4,$f13; mtc1 $5,$f12
! #else
! #define LDDBL1 mtc1 $4,$f12; mtc1 $5,$f13
! #endif
! 
! /* Load the second double precision operand.  */
  
! #if defined(__mips64)
! /* XXX this should be $6 for Algo arg passing model */
! #define LDDBL2 dmtc1 $5,$f14
! #elif defined(__mipsfp64)
! #define LDDBL2 sw $6,8($29); sw $7,12($29); l.d $f14,8($29)
! #elif defined(__MIPSEB__)
! #define LDDBL2 mtc1 $6,$f15; mtc1 $7,$f14
! #else
! #define LDDBL2 mtc1 $6,$f14; mtc1 $7,$f15
! #endif
  
! /* Move the double precision return value to the right place.  */
! 
! #if defined(__mips64)
! #define RETDBL dmfc1 $2,$f0
! #elif defined(__mipsfp64)
! #define RETDBL s.d $f0,0($29); lw $2,0($29); lw $3,4($29)
! #elif defined(__MIPSEB__)
! #define RETDBL mfc1 $2,$f1; mfc1 $3,$f0
! #else
! #define RETDBL mfc1 $2,$f0; mfc1 $3,$f1
! #endif
! 
! /* Double precision math.  */
! 
! /* This macro defines a function which loads two double precision
!    values, performs an operation, and returns the double precision
!    result.  */
! 
! #define DFOP(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	.set	noreorder;	\
! 	LDDBL1;			\
! 	LDDBL2;			\
! 	nop;			\
! 	OPCODE	$f0,$f12,$f14;	\
! 	RETDBL;			\
! 	j	$31;		\
! 	nop;			\
! 	.set	reorder;	\
  	ENDFN (NAME)
  
  #ifdef L_m16adddf3
! DFOP(__mips16_adddf3, add.d)
  #endif
  #ifdef L_m16subdf3
! DFOP(__mips16_subdf3, sub.d)
  #endif
  #ifdef L_m16muldf3
! DFOP(__mips16_muldf3, mul.d)
  #endif
  #ifdef L_m16divdf3
! DFOP(__mips16_divdf3, div.d)
  #endif
  
! #define DFOP2(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	.set	noreorder;	\
! 	LDDBL1;			\
! 	nop;			\
! 	OPCODE	$f0,$f12;	\
! 	RETDBL;			\
! 	j	$31;		\
! 	nop;			\
! 	.set	reorder;	\
  	ENDFN (NAME)
  
  #ifdef L_m16negdf2
! DFOP2(__mips16_negdf2, neg.d)
  #endif
  #ifdef L_m16absdf2
! DFOP2(__mips16_absdf2, abs.d)
  #endif
  
- 
  /* Conversions between single and double precision.  */
  
  #ifdef L_m16extsfdf2
  STARTFN (__mips16_extendsfdf2)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	nop
! 	cvt.d.s	$f0,$f12
! 	RETDBL
! 	j	$31
! 	nop
! 	.set	reorder
  	ENDFN (__mips16_extendsfdf2)
  #endif
  
  #ifdef L_m16trdfsf2
  STARTFN (__mips16_truncdfsf2)
! 	.set	noreorder
! 	LDDBL1
! 	nop
! 	cvt.s.d	$f0,$f12
! 	mfc1	$2,$f0
! 	j	$31
! 	nop
! 	.set	reorder
  	ENDFN (__mips16_truncdfsf2)
  #endif
  
! /* Double precision comparisons.  */
  
! /* This macro defines a function which loads two double precision
!    values, performs a floating point comparison, and returns the
!    specified values according to whether the comparison is true or
!    false.  */
  
! #define DFCMP(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	LDDBL1;					\
! 	LDDBL2;					\
! 	OPCODE	$f12,$f14;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
--- 269,386 ----
  	ENDFN (NAME)
  
  #ifdef L_m16eqsf2
! CMPSF (__mips16_eqsf2, c.eq.s, 0, 1)
  #endif
  #ifdef L_m16nesf2
! CMPSF (__mips16_nesf2, c.eq.s, 0, 1)
  #endif
  #ifdef L_m16gtsf2
! REVCMPSF (__mips16_gtsf2, c.lt.s, 1, 0)
  #endif
  #ifdef L_m16gesf2
! REVCMPSF (__mips16_gesf2, c.le.s, 0, -1)
  #endif
  #ifdef L_m16lesf2
! CMPSF (__mips16_lesf2, c.le.s, 0, 1)
  #endif
  #ifdef L_m16ltsf2
! CMPSF (__mips16_ltsf2, c.lt.s, -1, 0)
  #endif
  
! /* Single-precision conversions.  */
  
  #ifdef L_m16fltsisf
  STARTFN (__mips16_floatsisf)
! 	MOVE_SF_BYTE0 (t)
! 	cvt.s.w	RET,ARG1
! 	MOVE_SF_RET (f, $31)
  	ENDFN (__mips16_floatsisf)
  #endif
  
  #ifdef L_m16fix_truncsfsi
  STARTFN (__mips16_fix_truncsfsi)
! 	MOVE_SF_BYTE0 (t)
! 	trunc.w.s RET,ARG1,$4
! 	MOVE_SI_RET (f, $31)
  	ENDFN (__mips16_fix_truncsfsi)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  
! /* Double-precision math.  */
  
! /* Define a function NAME that loads two double-precision values,
!    performs FPU operation OPCODE on them, and returns the double-
!    precision result.  */
  
! #define OPDF3(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	MOVE_DF_BYTE0 (t);	\
! 	MOVE_DF_BYTE8 (t);	\
! 	OPCODE RET,ARG1,ARG2;	\
! 	MOVE_DF_RET (f, $31);	\
  	ENDFN (NAME)
  
  #ifdef L_m16adddf3
! OPDF3 (__mips16_adddf3, add.d)
  #endif
  #ifdef L_m16subdf3
! OPDF3 (__mips16_subdf3, sub.d)
  #endif
  #ifdef L_m16muldf3
! OPDF3 (__mips16_muldf3, mul.d)
  #endif
  #ifdef L_m16divdf3
! OPDF3 (__mips16_divdf3, div.d)
  #endif
  
! /* Define a function NAME that loads a double-precision value,
!    performs FPU operation OPCODE on it, and returns the double-
!    precision result.  */
! 
! #define OPDF2(NAME, OPCODE)	\
  STARTFN (NAME);			\
! 	MOVE_DF_BYTE0 (t);	\
! 	OPCODE RET,ARG1;	\
! 	MOVE_DF_RET (f, $31);	\
  	ENDFN (NAME)
  
  #ifdef L_m16negdf2
! OPDF2 (__mips16_negdf2, neg.d)
  #endif
  #ifdef L_m16absdf2
! OPDF2 (__mips16_absdf2, abs.d)
  #endif
  
  /* Conversions between single and double precision.  */
  
  #ifdef L_m16extsfdf2
  STARTFN (__mips16_extendsfdf2)
! 	MOVE_SF_BYTE0 (t)
! 	cvt.d.s	RET,ARG1
! 	MOVE_DF_RET (f, $31)
  	ENDFN (__mips16_extendsfdf2)
  #endif
  
  #ifdef L_m16trdfsf2
  STARTFN (__mips16_truncdfsf2)
! 	MOVE_DF_BYTE0 (t)
! 	cvt.s.d	RET,ARG1
! 	MOVE_SF_RET (f, $31)
  	ENDFN (__mips16_truncdfsf2)
  #endif
  
! /* Double-precision comparisons.  */
  
! /* Define a function NAME that loads two double-precision values,
!    performs floating point comparison OPCODE, and returns TRUE or
!    FALSE depending on the result.  */
  
! #define CMPDF(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	MOVE_DF_BYTE0 (t);			\
! 	MOVE_DF_BYTE8 (t);			\
! 	OPCODE	ARG1,ARG2;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
*************** STARTFN (NAME);					\
*** 324,336 ****
  	j	$31;				\
  	ENDFN (NAME)
  
! /* This macro is like DFCMP, but it reverses the comparison.  */
  
! #define DFREVCMP(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	LDDBL1;					\
! 	LDDBL2;					\
! 	OPCODE	$f14,$f12;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
--- 388,400 ----
  	j	$31;				\
  	ENDFN (NAME)
  
! /* Like CMPDF, but reverse the comparison operands.  */
  
! #define REVCMPDF(NAME, OPCODE, TRUE, FALSE)	\
  STARTFN (NAME);					\
! 	MOVE_DF_BYTE0 (t);			\
! 	MOVE_DF_BYTE8 (t);			\
! 	OPCODE	ARG2,ARG1;			\
  	li	$2,TRUE;			\
  	bc1t	1f;				\
  	li	$2,FALSE;			\
*************** STARTFN (NAME);					\
*** 339,512 ****
  	ENDFN (NAME)
  
  #ifdef L_m16eqdf2
! DFCMP(__mips16_eqdf2, c.eq.d, 0, 1)
  #endif
  #ifdef L_m16nedf2
! DFCMP(__mips16_nedf2, c.eq.d, 0, 1)
  #endif
  #ifdef L_m16gtdf2
! DFREVCMP(__mips16_gtdf2, c.lt.d, 1, 0)
  #endif
  #ifdef L_m16gedf2
! DFREVCMP(__mips16_gedf2, c.le.d, 0, -1)
  #endif
  #ifdef L_m16ledf2
! DFCMP(__mips16_ledf2, c.le.d, 0, 1)
  #endif
  #ifdef L_m16ltdf2
! DFCMP(__mips16_ltdf2, c.lt.d, -1, 0)
  #endif
  
! /* Double precision conversions.  */
  
  #ifdef L_m16fltsidf
  STARTFN (__mips16_floatsidf)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	nop
! 	cvt.d.w	$f0,$f12
! 	RETDBL
! 	j	$31
! 	nop
! 	.set	reorder
  	ENDFN (__mips16_floatsidf)
  #endif
  
  #ifdef L_m16fix_truncdfsi
  STARTFN (__mips16_fix_truncdfsi)
! 	.set	noreorder
! 	LDDBL1
! 	nop
! 	trunc.w.d $f0,$f12,$4
! 	mfc1	$2,$f0
! 	j	$31
! 	nop
! 	.set	reorder
  	ENDFN (__mips16_fix_truncdfsi)
  #endif
  #endif /* !__mips_single_float */
  
! /* These functions are used to return floating point values from
!    mips16 functions.  In this case we can put mtc1 in a jump delay slot,
!    because we know that the next instruction will not refer to a floating
!    point register.  */
  
  #ifdef L_m16retsf
! STARTFN (__mips16_ret_sf)
! 	.set	noreorder
! 	j	$31
! 	mtc1	$2,$f0
! 	.set	reorder
! 	ENDFN (__mips16_ret_sf)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  #ifdef L_m16retdf
! STARTFN (__mips16_ret_df)
! 	.set	noreorder
! #if defined(__mips64)
! 	j	$31
! 	dmtc1	$2,$f0
! #elif defined(__mipsfp64)
! 	sw	$2,0($29)
! 	sw	$3,4($29)
! 	l.d	$f0,0($29)
! #elif defined(__MIPSEB__)
! 	mtc1	$2,$f1
! 	j	$31
! 	mtc1	$3,$f0
! #else
! 	mtc1	$2,$f0
! 	j	$31
! 	mtc1	$3,$f1
  #endif
! 	.set	reorder
! 	ENDFN (__mips16_ret_df)
  #endif
  #endif /* !__mips_single_float */
  
  /* These functions are used by 16-bit code when calling via a function
!    pointer.  They must copy the floating point arguments from the gp
!    regs into the fp regs.  The function to call will be in $2.  The
!    exact set of floating point arguments to copy is encoded in the
!    function name; the final number is an fp_code, as described in
!    mips.h in the comment about CUMULATIVE_ARGS.  */
  
  #ifdef L_m16stub1
! /* (float) */
! STARTFN (__mips16_call_stub_1)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	j	$2
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_1)
  #endif
  
  #ifdef L_m16stub5
! /* (float, float) */
! STARTFN (__mips16_call_stub_5)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	mtc1	$5,$f14
! 	j	$2
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_5)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  
  #ifdef L_m16stub2
! /* (double) */
! STARTFN (__mips16_call_stub_2)
! 	.set	noreorder
! 	LDDBL1
! 	j	$2
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_2)
  #endif
  
  #ifdef L_m16stub6
! /* (double, float) */
! STARTFN (__mips16_call_stub_6)
! 	.set	noreorder
! 	LDDBL1
! 	mtc1	$6,$f14
! 	j	$2
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_6)
  #endif
  
  #ifdef L_m16stub9
! /* (float, double) */
! STARTFN (__mips16_call_stub_9)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	LDDBL2
! 	j	$2
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_9)
  #endif
  
  #ifdef L_m16stub10
! /* (double, double) */
! STARTFN (__mips16_call_stub_10)
! 	.set	noreorder
! 	LDDBL1
! 	LDDBL2
! 	j	$2
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_10)
  #endif
  #endif /* !__mips_single_float */
  
  /* Now we have the same set of functions, except that this time the
!    function being called returns an SFmode value.  The calling
     function will arrange to preserve $18, so these functions are free
     to use it to hold the return address.
  
--- 403,527 ----
  	ENDFN (NAME)
  
  #ifdef L_m16eqdf2
! CMPDF (__mips16_eqdf2, c.eq.d, 0, 1)
  #endif
  #ifdef L_m16nedf2
! CMPDF (__mips16_nedf2, c.eq.d, 0, 1)
  #endif
  #ifdef L_m16gtdf2
! REVCMPDF (__mips16_gtdf2, c.lt.d, 1, 0)
  #endif
  #ifdef L_m16gedf2
! REVCMPDF (__mips16_gedf2, c.le.d, 0, -1)
  #endif
  #ifdef L_m16ledf2
! CMPDF (__mips16_ledf2, c.le.d, 0, 1)
  #endif
  #ifdef L_m16ltdf2
! CMPDF (__mips16_ltdf2, c.lt.d, -1, 0)
  #endif
  
! /* Double-precision conversions.  */
  
  #ifdef L_m16fltsidf
  STARTFN (__mips16_floatsidf)
! 	MOVE_SI_BYTE0 (t)
! 	cvt.d.w	RET,ARG1
! 	MOVE_DF_RET (f, $31)
  	ENDFN (__mips16_floatsidf)
  #endif
  
  #ifdef L_m16fix_truncdfsi
  STARTFN (__mips16_fix_truncdfsi)
! 	MOVE_DF_BYTE0 (t)
! 	trunc.w.d RET,ARG1,$4
! 	MOVE_SI_RET (f, $31)
  	ENDFN (__mips16_fix_truncdfsi)
  #endif
  #endif /* !__mips_single_float */
  
! /* Define a function NAME that moves a return value of mode MODE from
!    FPRs to GPRs.  */
! 
! #define RET_FUNCTION(NAME, MODE)	\
! STARTFN (NAME);				\
! 	MOVE_##MODE##_RET (t, $31);	\
! 	ENDFN (NAME)
  
  #ifdef L_m16retsf
! RET_FUNCTION (__mips16_ret_sf, SF)
! #endif
! 
! #ifdef L_m16retsc
! RET_FUNCTION (__mips16_ret_sc, SC)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  #ifdef L_m16retdf
! RET_FUNCTION (__mips16_ret_df, DF)
  #endif
! 
! #ifdef L_m16retdc
! RET_FUNCTION (__mips16_ret_dc, DC)
  #endif
  #endif /* !__mips_single_float */
  
+ /* STUB_ARGS_X copies the arguments from GPRs to FPRs for argument
+    code X.  X is calculated as ARG1 + ARG2 * 4, where ARG1 and ARG2
+    classify the first and second arguments as follows:
+ 
+ 	1: a single-precision argument
+ 	2: a double-precision argument
+ 	0: no argument, or not one of the above.  */
+ 
+ #define STUB_ARGS_0						/* () */
+ #define STUB_ARGS_1 MOVE_SF_BYTE0 (t)				/* (sf) */
+ #define STUB_ARGS_5 MOVE_SF_BYTE0 (t); MOVE_SF_BYTE4 (t)	/* (sf, sf) */
+ #define STUB_ARGS_9 MOVE_SF_BYTE0 (t); MOVE_DF_BYTE8 (t)	/* (sf, df) */
+ #define STUB_ARGS_2 MOVE_DF_BYTE0 (t)				/* (df) */
+ #define STUB_ARGS_6 MOVE_DF_BYTE0 (t); MOVE_SF_BYTE8 (t)	/* (df, sf) */
+ #define STUB_ARGS_10 MOVE_DF_BYTE0 (t); MOVE_DF_BYTE8 (t)	/* (df, df) */
+ 
  /* These functions are used by 16-bit code when calling via a function
!    pointer.  They must copy the floating point arguments from the GPRs
!    to FPRs and then call function $2.  */
! 
! #define CALL_STUB_NO_RET(NAME, CODE)	\
! STARTFN (NAME);				\
! 	STUB_ARGS_##CODE;		\
! 	jr	$2;			\
! 	ENDFN (NAME)
  
  #ifdef L_m16stub1
! CALL_STUB_NO_RET (__mips16_call_stub_1, 1)
  #endif
  
  #ifdef L_m16stub5
! CALL_STUB_NO_RET (__mips16_call_stub_5, 5)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  
  #ifdef L_m16stub2
! CALL_STUB_NO_RET (__mips16_call_stub_2, 2)
  #endif
  
  #ifdef L_m16stub6
! CALL_STUB_NO_RET (__mips16_call_stub_6, 6)
  #endif
  
  #ifdef L_m16stub9
! CALL_STUB_NO_RET (__mips16_call_stub_9, 9)
  #endif
  
  #ifdef L_m16stub10
! CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
  #endif
  #endif /* !__mips_single_float */
  
  /* Now we have the same set of functions, except that this time the
!    function being called returns an SFmode, SCmode, DFmode or DCmode
!    value; we need to instantiate a set for each case.  The calling
     function will arrange to preserve $18, so these functions are free
     to use it to hold the return address.
  
*************** STARTFN (__mips16_call_stub_10)
*** 517,739 ****
     being called is 16 bits, in which case the copy is unnecessary;
     however, it's faster to always do the copy.  */
  
  #ifdef L_m16stubsf0
! /* () */
! STARTFN (__mips16_call_stub_sf_0)
! 	.set	noreorder
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_0)
  #endif
  
  #ifdef L_m16stubsf1
! /* (float) */
! STARTFN (__mips16_call_stub_sf_1)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_1)
  #endif
  
  #ifdef L_m16stubsf5
! /* (float, float) */
! STARTFN (__mips16_call_stub_sf_5)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	mtc1	$5,$f14
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_5)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  #ifdef L_m16stubsf2
! /* (double) */
! STARTFN (__mips16_call_stub_sf_2)
! 	.set	noreorder
! 	LDDBL1
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_2)
  #endif
  
  #ifdef L_m16stubsf6
! /* (double, float) */
! STARTFN (__mips16_call_stub_sf_6)
! 	.set	noreorder
! 	LDDBL1
! 	mtc1	$6,$f14
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_6)
  #endif
  
  #ifdef L_m16stubsf9
! /* (float, double) */
! STARTFN (__mips16_call_stub_sf_9)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	LDDBL2
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_9)
  #endif
  
  #ifdef L_m16stubsf10
! /* (double, double) */
! STARTFN (__mips16_call_stub_sf_10)
! 	.set	noreorder
! 	LDDBL1
! 	LDDBL2
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	mfc1	$2,$f0
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_sf_10)
  #endif
  
  /* Now we have the same set of functions again, except that this time
     the function being called returns an DFmode value.  */
  
  #ifdef L_m16stubdf0
! /* () */
! STARTFN (__mips16_call_stub_df_0)
! 	.set	noreorder
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_0)
  #endif
  
  #ifdef L_m16stubdf1
! /* (float) */
! STARTFN (__mips16_call_stub_df_1)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_1)
  #endif
  
! #ifdef L_m16stubdf2
! /* (double) */
! STARTFN (__mips16_call_stub_df_2)
! 	.set	noreorder
! 	LDDBL1
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_2)
  #endif
  
! #ifdef L_m16stubdf5
! /* (float, float) */
! STARTFN (__mips16_call_stub_df_5)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	mtc1	$5,$f14
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_5)
  #endif
  
  #ifdef L_m16stubdf6
! /* (double, float) */
! STARTFN (__mips16_call_stub_df_6)
! 	.set	noreorder
! 	LDDBL1
! 	mtc1	$6,$f14
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_6)
  #endif
  
  #ifdef L_m16stubdf9
! /* (float, double) */
! STARTFN (__mips16_call_stub_df_9)
! 	.set	noreorder
! 	mtc1	$4,$f12
! 	LDDBL2
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_9)
  #endif
  
  #ifdef L_m16stubdf10
! /* (double, double) */
! STARTFN (__mips16_call_stub_df_10)
! 	.set	noreorder
! 	LDDBL1
! 	LDDBL2
! 	move	$18,$31
! 	jal	$2
! 	nop
! 	RETDBL
! 	j	$18
! 	nop
! 	.set	reorder
! 	ENDFN (__mips16_call_stub_df_10)
  #endif
  #endif /* !__mips_single_float */
--- 532,674 ----
     being called is 16 bits, in which case the copy is unnecessary;
     however, it's faster to always do the copy.  */
  
+ #define CALL_STUB_RET(NAME, CODE, MODE)	\
+ STARTFN (NAME);				\
+ 	move	$18,$31;		\
+ 	STUB_ARGS_##CODE;		\
+ 	jalr	$2;			\
+ 	MOVE_##MODE##_RET (f, $18);	\
+ 	ENDFN (NAME)
+ 
+ /* First, instantiate the single-float set.  */
+ 
  #ifdef L_m16stubsf0
! CALL_STUB_RET (__mips16_call_stub_sf_0, 0, SF)
  #endif
  
  #ifdef L_m16stubsf1
! CALL_STUB_RET (__mips16_call_stub_sf_1, 1, SF)
  #endif
  
  #ifdef L_m16stubsf5
! CALL_STUB_RET (__mips16_call_stub_sf_5, 5, SF)
  #endif
  
  #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  #ifdef L_m16stubsf2
! CALL_STUB_RET (__mips16_call_stub_sf_2, 2, SF)
  #endif
  
  #ifdef L_m16stubsf6
! CALL_STUB_RET (__mips16_call_stub_sf_6, 6, SF)
  #endif
  
  #ifdef L_m16stubsf9
! CALL_STUB_RET (__mips16_call_stub_sf_9, 9, SF)
  #endif
  
  #ifdef L_m16stubsf10
! CALL_STUB_RET (__mips16_call_stub_sf_10, 10, SF)
  #endif
+ #endif /* !__mips_single_float */
+ 
  
  /* Now we have the same set of functions again, except that this time
     the function being called returns an DFmode value.  */
  
+ #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
  #ifdef L_m16stubdf0
! CALL_STUB_RET (__mips16_call_stub_df_0, 0, DF)
  #endif
  
  #ifdef L_m16stubdf1
! CALL_STUB_RET (__mips16_call_stub_df_1, 1, DF)
  #endif
  
! #ifdef L_m16stubdf5
! CALL_STUB_RET (__mips16_call_stub_df_5, 5, DF)
  #endif
  
! #ifdef L_m16stubdf2
! CALL_STUB_RET (__mips16_call_stub_df_2, 2, DF)
  #endif
  
  #ifdef L_m16stubdf6
! CALL_STUB_RET (__mips16_call_stub_df_6, 6, DF)
  #endif
  
  #ifdef L_m16stubdf9
! CALL_STUB_RET (__mips16_call_stub_df_9, 9, DF)
  #endif
  
  #ifdef L_m16stubdf10
! CALL_STUB_RET (__mips16_call_stub_df_10, 10, DF)
! #endif
! #endif /* !__mips_single_float */
! 
! 
! /* Ho hum.  Here we have the same set of functions again, this time
!    for when the function being called returns an SCmode value.  */
! 
! #ifdef L_m16stubsc0
! CALL_STUB_RET (__mips16_call_stub_sc_0, 0, SC)
! #endif
! 
! #ifdef L_m16stubsc1
! CALL_STUB_RET (__mips16_call_stub_sc_1, 1, SC)
! #endif
! 
! #ifdef L_m16stubsc5
! CALL_STUB_RET (__mips16_call_stub_sc_5, 5, SC)
! #endif
! 
! #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
! #ifdef L_m16stubsc2
! CALL_STUB_RET (__mips16_call_stub_sc_2, 2, SC)
! #endif
! 
! #ifdef L_m16stubsc6
! CALL_STUB_RET (__mips16_call_stub_sc_6, 6, SC)
! #endif
! 
! #ifdef L_m16stubsc9
! CALL_STUB_RET (__mips16_call_stub_sc_9, 9, SC)
! #endif
! 
! #ifdef L_m16stubsc10
! CALL_STUB_RET (__mips16_call_stub_sc_10, 10, SC)
! #endif
! #endif /* !__mips_single_float */
! 
! 
! /* Finally, another set of functions for DCmode.  */
! 
! #if !defined(__mips_single_float) && !defined(__SINGLE_FLOAT)
! #ifdef L_m16stubdc0
! CALL_STUB_RET (__mips16_call_stub_dc_0, 0, DC)
! #endif
! 
! #ifdef L_m16stubdc1
! CALL_STUB_RET (__mips16_call_stub_dc_1, 1, DC)
! #endif
! 
! #ifdef L_m16stubdc5
! CALL_STUB_RET (__mips16_call_stub_dc_5, 5, DC)
! #endif
! 
! #ifdef L_m16stubdc2
! CALL_STUB_RET (__mips16_call_stub_dc_2, 2, DC)
! #endif
! 
! #ifdef L_m16stubdc6
! CALL_STUB_RET (__mips16_call_stub_dc_6, 6, DC)
! #endif
! 
! #ifdef L_m16stubdc9
! CALL_STUB_RET (__mips16_call_stub_dc_9, 9, DC)
! #endif
! 
! #ifdef L_m16stubdc10
! CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
  #endif
  #endif /* !__mips_single_float */
Index: gcc/config/mips/t-elf
===================================================================
*** gcc/config/mips/t-elf	(revision 124934)
--- gcc/config/mips/t-elf	(working copy)
*************** LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m1
*** 19,29 ****
  	_m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
  	_m16fltsidf _m16fix_truncdfsi \
  	_m16retsf _m16retdf \
  	_m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
  	_m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
  	_m16stubsf9 _m16stubsf10 \
  	_m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
! 	_m16stubdf9 _m16stubdf10
  
  # We must build libgcc2.a with -G 0, in case the user wants to link
  # without the $gp register.
--- 19,34 ----
  	_m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
  	_m16fltsidf _m16fix_truncdfsi \
  	_m16retsf _m16retdf \
+ 	_m16retsc _m16retdc \
  	_m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
  	_m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
  	_m16stubsf9 _m16stubsf10 \
  	_m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
! 	_m16stubdf9 _m16stubdf10 \
! 	_m16stubsc0 _m16stubsc1 _m16stubsc2 _m16stubsc5 _m16stubsc6 \
! 	_m16stubsc9 _m16stubsc10 \
! 	_m16stubdc0 _m16stubdc1 _m16stubdc2 _m16stubdc5 _m16stubdc6 \
! 	_m16stubdc9 _m16stubdc10
  
  # We must build libgcc2.a with -G 0, in case the user wants to link
  # without the $gp register.
Index: gcc/config/mips/t-isa3264
===================================================================
*** gcc/config/mips/t-isa3264	(revision 124934)
--- gcc/config/mips/t-isa3264	(working copy)
*************** LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m1
*** 19,29 ****
  	_m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
  	_m16fltsidf _m16fix_truncdfsi \
  	_m16retsf _m16retdf \
  	_m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
  	_m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
  	_m16stubsf9 _m16stubsf10 \
  	_m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
! 	_m16stubdf9 _m16stubdf10
  
  # We must build libgcc2.a with -G 0, in case the user wants to link
  # without the $gp register.
--- 19,34 ----
  	_m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
  	_m16fltsidf _m16fix_truncdfsi \
  	_m16retsf _m16retdf \
+ 	_m16retsc _m16retdc \
  	_m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
  	_m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
  	_m16stubsf9 _m16stubsf10 \
  	_m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
! 	_m16stubdf9 _m16stubdf10 \
! 	_m16stubsc0 _m16stubsc1 _m16stubsc2 _m16stubsc5 _m16stubsc6 \
! 	_m16stubsc9 _m16stubsc10 \
! 	_m16stubdc0 _m16stubdc1 _m16stubdc2 _m16stubdc5 _m16stubdc6 \
! 	_m16stubdc9 _m16stubdc10
  
  # We must build libgcc2.a with -G 0, in case the user wants to link
  # without the $gp register.
Index: gcc/config/mips/t-r3900
===================================================================
*** gcc/config/mips/t-r3900	(revision 124934)
--- gcc/config/mips/t-r3900	(working copy)
*************** LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m1
*** 7,17 ****
  	_m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
  	_m16fltsidf _m16fix_truncdfsi \
  	_m16retsf _m16retdf \
  	_m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
  	_m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
  	_m16stubsf9 _m16stubsf10 \
  	_m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
! 	_m16stubdf9 _m16stubdf10
  
  # We must build libgcc2.a with -G 0, in case the user wants to link
  # without the $gp register.
--- 7,22 ----
  	_m16eqdf2 _m16nedf2 _m16gtdf2 _m16gedf2 _m16ledf2 _m16ltdf2 \
  	_m16fltsidf _m16fix_truncdfsi \
  	_m16retsf _m16retdf \
+ 	_m16retsc _m16retdc \
  	_m16stub1 _m16stub2 _m16stub5 _m16stub6 _m16stub9 _m16stub10 \
  	_m16stubsf0 _m16stubsf1 _m16stubsf2 _m16stubsf5 _m16stubsf6 \
  	_m16stubsf9 _m16stubsf10 \
  	_m16stubdf0 _m16stubdf1 _m16stubdf2 _m16stubdf5 _m16stubdf6 \
! 	_m16stubdf9 _m16stubdf10 \
! 	_m16stubsc0 _m16stubsc1 _m16stubsc2 _m16stubsc5 _m16stubsc6 \
! 	_m16stubsc9 _m16stubsc10 \
! 	_m16stubdc0 _m16stubdc1 _m16stubdc2 _m16stubdc5 _m16stubdc6 \
! 	_m16stubdc9 _m16stubdc10
  
  # We must build libgcc2.a with -G 0, in case the user wants to link
  # without the $gp register.
Index: gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_main.c
===================================================================
*** gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_main.c	(revision 0)
--- gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_main.c	(revision 0)
***************
*** 0 ****
--- 1,10 ----
+ extern void init (void);
+ extern void test (void);
+ 
+ int
+ main (void)
+ {
+   init ();
+   test ();
+   return 0;
+ }
Index: gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_x.c
===================================================================
*** gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_x.c	(revision 0)
--- gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_x.c	(revision 0)
***************
*** 0 ****
--- 1,176 ----
+ #include <stdlib.h>
+ 
+ /* All the function pointers are declared and initialized in
+    mips16-stubs-2.c.  */
+ 
+ extern double the_result;
+ 
+ extern void v0 (void);
+ extern void v1 (float);
+ extern void v5 (float, float);
+ extern void v9 (float, double);
+ extern void v2 (double);
+ extern void v6 (double, float);
+ extern void v10 (double, double);
+ 
+ extern float f0 (void);
+ extern float f1 (float);
+ extern float f5 (float, float);
+ extern float f9 (float, double);
+ extern float f2 (double);
+ extern float f6 (double, float);
+ extern float f10 (double, double);
+ 
+ extern double d0 (void);
+ extern double d1 (float);
+ extern double d5 (float, float);
+ extern double d9 (float, double);
+ extern double d2 (double);
+ extern double d6 (double, float);
+ extern double d10 (double, double);
+ 
+ extern _Complex float cf0 (void);
+ extern _Complex float cf1 (float);
+ extern _Complex float cf5 (float, float);
+ extern _Complex float cf9 (float, double);
+ extern _Complex float cf2 (double);
+ extern _Complex float cf6 (double, float);
+ extern _Complex float cf10 (double, double);
+ 
+ extern _Complex double cd0 (void);
+ extern _Complex double cd1 (float);
+ extern _Complex double cd5 (float, float);
+ extern _Complex double cd9 (float, double);
+ extern _Complex double cd2 (double);
+ extern _Complex double cd6 (double, float);
+ extern _Complex double cd10 (double, double);
+ 
+ extern void (*pv0) (void);
+ extern void (*pv1) (float);
+ extern void (*pv5) (float, float);
+ extern void (*pv9) (float, double);
+ extern void (*pv2) (double);
+ extern void (*pv6) (double, float);
+ extern void (*pv10) (double, double);
+ 
+ extern float (*pf0) (void);
+ extern float (*pf1) (float);
+ extern float (*pf5) (float, float);
+ extern float (*pf9) (float, double);
+ extern float (*pf2) (double);
+ extern float (*pf6) (double, float);
+ extern float (*pf10) (double, double);
+ 
+ extern double (*pd0) (void);
+ extern double (*pd1) (float);
+ extern double (*pd5) (float, float);
+ extern double (*pd9) (float, double);
+ extern double (*pd2) (double);
+ extern double (*pd6) (double, float);
+ extern double (*pd10) (double, double);
+ 
+ extern _Complex float (*pcf0) (void);
+ extern _Complex float (*pcf1) (float);
+ extern _Complex float (*pcf5) (float, float);
+ extern _Complex float (*pcf9) (float, double);
+ extern _Complex float (*pcf2) (double);
+ extern _Complex float (*pcf6) (double, float);
+ extern _Complex float (*pcf10) (double, double);
+ 
+ extern _Complex double (*pcd0) (void);
+ extern _Complex double (*pcd1) (float);
+ extern _Complex double (*pcd5) (float, float);
+ extern _Complex double (*pcd9) (float, double);
+ extern _Complex double (*pcd2) (double);
+ extern _Complex double (*pcd6) (double, float);
+ extern _Complex double (*pcd10) (double, double);
+ 
+ /* Macros for results checking.  */
+ #define CHECK_RESULT(x, y) if ((x) != (y)) abort ()
+ #define CHECK_VOID_RESULT(x, y)  CHECK_RESULT (((x), the_result), y)
+ 
+ /* Call functions through pointers and and check against expected results.  */
+ void
+ test (void)
+ {
+ 
+   CHECK_VOID_RESULT (v0 (), 1.0);
+   CHECK_VOID_RESULT (v1 (1.0), 2.0);
+   CHECK_VOID_RESULT (v5 (5.0, 6.0), 12.0);
+   CHECK_VOID_RESULT (v9 (9.0, 10.0), 20.0);
+   CHECK_VOID_RESULT (v2 (2.0), 3.0);
+   CHECK_VOID_RESULT (v6 (6.0, 7.0), 14.0);
+   CHECK_VOID_RESULT (v10 (10.0, 11.0), 22.0);
+ 
+   CHECK_RESULT (f0 (), 1.0);
+   CHECK_RESULT (f1 (1.0), 2.0);
+   CHECK_RESULT (f5 (5.0, 6.0), 12.0);
+   CHECK_RESULT (f9 (9.0, 10.0), 20.0);
+   CHECK_RESULT (f2 (2.0), 3.0);
+   CHECK_RESULT (f6 (6.0, 7.0), 14.0);
+   CHECK_RESULT (f10 (10.0, 11.0), 22.0);
+ 
+   CHECK_RESULT (d0 (), 1.0);
+   CHECK_RESULT (d1 (1.0), 2.0);
+   CHECK_RESULT (d5 (5.0, 6.0), 12.0);
+   CHECK_RESULT (d9 (9.0, 10.0), 20.0);
+   CHECK_RESULT (d2 (2.0), 3.0);
+   CHECK_RESULT (d6 (6.0, 7.0), 14.0);
+   CHECK_RESULT (d10 (10.0, 11.0), 22.0);
+ 
+   CHECK_RESULT (cf0 (), 1.0 + 0.0i);
+   CHECK_RESULT (cf1 (1.0), 2.0 + 1.0i);
+   CHECK_RESULT (cf5 (5.0, 6.0), 12.0 + 5.0i);
+   CHECK_RESULT (cf9 (9.0, 10.0), 20.0 + 9.0i);
+   CHECK_RESULT (cf2 (2.0), 3.0 + 2.0i);
+   CHECK_RESULT (cf6 (6.0, 7.0), 14.0 + 6.0i);
+   CHECK_RESULT (cf10 (10.0, 11.0), 22.0 + 10.0i);
+ 
+   CHECK_RESULT (cd0 (), 1.0 + 0.0i);
+   CHECK_RESULT (cd1 (1.0), 2.0 + 1.0i);
+   CHECK_RESULT (cd5 (5.0, 6.0), 12.0 + 5.0i);
+   CHECK_RESULT (cd9 (9.0, 10.0), 20.0 + 9.0i);
+   CHECK_RESULT (cd2 (2.0), 3.0 + 2.0i);
+   CHECK_RESULT (cd6 (6.0, 7.0), 14.0 + 6.0i);
+   CHECK_RESULT (cd10 (10.0, 11.0), 22.0 + 10.0i);
+ 
+   CHECK_VOID_RESULT ((*pv0) (), 1.0);
+   CHECK_VOID_RESULT ((*pv1) (1.0), 2.0);
+   CHECK_VOID_RESULT ((*pv5) (5.0, 6.0), 12.0);
+   CHECK_VOID_RESULT ((*pv9) (9.0, 10.0), 20.0);
+   CHECK_VOID_RESULT ((*pv2) (2.0), 3.0);
+   CHECK_VOID_RESULT ((*pv6) (6.0, 7.0), 14.0);
+   CHECK_VOID_RESULT ((*pv10) (10.0, 11.0), 22.0);
+ 
+   CHECK_RESULT ((*pf0) (), 1.0);
+   CHECK_RESULT ((*pf1) (1.0), 2.0);
+   CHECK_RESULT ((*pf5) (5.0, 6.0), 12.0);
+   CHECK_RESULT ((*pf9) (9.0, 10.0), 20.0);
+   CHECK_RESULT ((*pf2) (2.0), 3.0);
+   CHECK_RESULT ((*pf6) (6.0, 7.0), 14.0);
+   CHECK_RESULT ((*pf10) (10.0, 11.0), 22.0);
+ 
+   CHECK_RESULT ((*pd0) (), 1.0);
+   CHECK_RESULT ((*pd1) (1.0), 2.0);
+   CHECK_RESULT ((*pd5) (5.0, 6.0), 12.0);
+   CHECK_RESULT ((*pd9) (9.0, 10.0), 20.0);
+   CHECK_RESULT ((*pd2) (2.0), 3.0);
+   CHECK_RESULT ((*pd6) (6.0, 7.0), 14.0);
+   CHECK_RESULT ((*pd10) (10.0, 11.0), 22.0);
+ 
+   CHECK_RESULT ((*pcf0) (), 1.0 + 0.0i);
+   CHECK_RESULT ((*pcf1) (1.0), 2.0 + 1.0i);
+   CHECK_RESULT ((*pcf5) (5.0, 6.0), 12.0 + 5.0i);
+   CHECK_RESULT ((*pcf9) (9.0, 10.0), 20.0 + 9.0i);
+   CHECK_RESULT ((*pcf2) (2.0), 3.0 + 2.0i);
+   CHECK_RESULT ((*pcf6) (6.0, 7.0), 14.0 + 6.0i);
+   CHECK_RESULT ((*pcf10) (10.0, 11.0), 22.0 + 10.0i);
+ 
+   CHECK_RESULT ((*pcd0) (), 1.0 + 0.0i);
+   CHECK_RESULT ((*pcd1) (1.0), 2.0 + 1.0i);
+   CHECK_RESULT ((*pcd5) (5.0, 6.0), 12.0 + 5.0i);
+   CHECK_RESULT ((*pcd9) (9.0, 10.0), 20.0 + 9.0i);
+   CHECK_RESULT ((*pcd2) (2.0), 3.0 + 2.0i);
+   CHECK_RESULT ((*pcd6) (6.0, 7.0), 14.0 + 6.0i);
+   CHECK_RESULT ((*pcd10) (10.0, 11.0), 22.0 + 10.0i);
+ }
Index: gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_y.c
===================================================================
*** gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_y.c	(revision 0)
--- gcc/testsuite/gcc.target/mips/inter/mips16_stubs_1_y.c	(revision 0)
***************
*** 0 ****
--- 1,133 ----
+ /* All test functions return the sum of arguments, plus 1.
+    Void-returning functions put the result in the_result.
+    Complex-returning functions return their signature number as the
+    (constant) imaginary part of the result.  */
+ 
+ double the_result;
+ 
+ void v0 (void) 			{ the_result = 1.0; }
+ void v1 (float x)		{ the_result = 1.0 + x; }
+ void v5 (float x, float y) 	{ the_result = 1.0 + x + y; }
+ void v9 (float x, double y) 	{ the_result = 1.0 + x + y; }
+ void v2 (double x)		{ the_result = 1.0 + x; }
+ void v6 (double x, float y)	{ the_result = 1.0 + x + y; }
+ void v10 (double x, double y)  	{ the_result = 1.0 + x + y; }
+ 
+ float f0 (void) 		{ return 1.0; }
+ float f1 (float x)		{ return 1.0 + x; }
+ float f5 (float x, float y) 	{ return 1.0 + x + y; }
+ float f9 (float x, double y) 	{ return 1.0 + x + y; }
+ float f2 (double x)		{ return 1.0 + x; }
+ float f6 (double x, float y)	{ return 1.0 + x + y; }
+ float f10 (double x, double y)	{ return 1.0 + x + y; }
+ 
+ double d0 (void) 		{ return 1.0; }
+ double d1 (float x)		{ return 1.0 + x; }
+ double d5 (float x, float y) 	{ return 1.0 + x + y; }
+ double d9 (float x, double y) 	{ return 1.0 + x + y; }
+ double d2 (double x)		{ return 1.0 + x; }
+ double d6 (double x, float y)	{ return 1.0 + x + y; }
+ double d10 (double x, double y) { return 1.0 + x + y; }
+ 
+ _Complex float cf0 (void) 			{ return 1.0 + 0.0i; }
+ _Complex float cf1 (float x)			{ return 1.0 + x + 1.0i; }
+ _Complex float cf5 (float x, float y) 		{ return 1.0 + x + y + 5.0i; }
+ _Complex float cf9 (float x, double y) 		{ return 1.0 + x + y + 9.0i; }
+ _Complex float cf2 (double x)			{ return 1.0 + x + 2.0i; }
+ _Complex float cf6 (double x, float y)		{ return 1.0 + x + y + 6.0i; }
+ _Complex float cf10 (double x, double y)	{ return 1.0 + x + y + 10.0i; }
+ 
+ _Complex double cd0 (void) 			{ return 1.0 + 0.0i; }
+ _Complex double cd1 (float x)			{ return 1.0 + x + 1.0i; }
+ _Complex double cd5 (float x, float y) 		{ return 1.0 + x + y + 5.0i; }
+ _Complex double cd9 (float x, double y) 	{ return 1.0 + x + y + 9.0i; }
+ _Complex double cd2 (double x)			{ return 1.0 + x + 2.0i; }
+ _Complex double cd6 (double x, float y)		{ return 1.0 + x + y + 6.0i; }
+ _Complex double cd10 (double x, double y)	{ return 1.0 + x + y + 10.0i; }
+ 
+ 
+ /* Declare and initialize all the pointer-to-function variables.  */
+ 
+ void (*pv0) (void);
+ void (*pv1) (float);
+ void (*pv5) (float, float);
+ void (*pv9) (float, double);
+ void (*pv2) (double);
+ void (*pv6) (double, float);
+ void (*pv10) (double, double);
+ 
+ float (*pf0) (void);
+ float (*pf1) (float);
+ float (*pf5) (float, float);
+ float (*pf9) (float, double);
+ float (*pf2) (double);
+ float (*pf6) (double, float);
+ float (*pf10) (double, double);
+ 
+ double (*pd0) (void);
+ double (*pd1) (float);
+ double (*pd5) (float, float);
+ double (*pd9) (float, double);
+ double (*pd2) (double);
+ double (*pd6) (double, float);
+ double (*pd10) (double, double);
+ 
+ _Complex float (*pcf0) (void);
+ _Complex float (*pcf1) (float);
+ _Complex float (*pcf5) (float, float);
+ _Complex float (*pcf9) (float, double);
+ _Complex float (*pcf2) (double);
+ _Complex float (*pcf6) (double, float);
+ _Complex float (*pcf10) (double, double);
+ 
+ _Complex double (*pcd0) (void);
+ _Complex double (*pcd1) (float);
+ _Complex double (*pcd5) (float, float);
+ _Complex double (*pcd9) (float, double);
+ _Complex double (*pcd2) (double);
+ _Complex double (*pcd6) (double, float);
+ _Complex double (*pcd10) (double, double);
+ 
+ void
+ init (void)
+ {
+   pv0 = v0;
+   pv1 = v1;
+   pv5 = v5;
+   pv9 = v9;
+   pv2 = v2;
+   pv6 = v6;
+   pv10 = v10;
+ 
+   pf0 = f0;
+   pf1 = f1;
+   pf5 = f5;
+   pf9 = f9;
+   pf2 = f2;
+   pf6 = f6;
+   pf10 = f10;
+ 
+   pd0 = d0;
+   pd1 = d1;
+   pd5 = d5;
+   pd9 = d9;
+   pd2 = d2;
+   pd6 = d6;
+   pd10 = d10;
+ 
+   pcf0 = cf0;
+   pcf1 = cf1;
+   pcf5 = cf5;
+   pcf9 = cf9;
+   pcf2 = cf2;
+   pcf6 = cf6;
+   pcf10 = cf10;
+ 
+   pcd0 = cd0;
+   pcd1 = cd1;
+   pcd5 = cd5;
+   pcd9 = cd9;
+   pcd2 = cd2;
+   pcd6 = cd6;
+   pcd10 = cd10;
+ }
Index: gcc/testsuite/gcc.target/mips/inter/mips16-inter.exp
===================================================================
*** gcc/testsuite/gcc.target/mips/inter/mips16-inter.exp	(revision 0)
--- gcc/testsuite/gcc.target/mips/inter/mips16-inter.exp	(revision 0)
***************
*** 0 ****
--- 1,48 ----
+ # Run compatibility tests in which the "alt" compiler tries to force
+ # MIPS16 mode.
+ 
+ # We can only guarantee MIPS16 runtime support for certain targets.
+ if { ![istarget mipsisa*-*-elf*] && ![istarget mips64vr*-*-elf*] } {
+     return
+ }
+ 
+ # Save the old value of CFLAGS_FOR_TARGET, if any.
+ global saved_CFLAGS_FOR_TARGET
+ if { [info exists CFLAGS_FOR_TARGET] } {
+     set saved_CFLAGS_FOR_TARGET $CFLAGS_FOR_TARGET
+ } else {
+     unset -nocomplain saved_CFLAGS_FOR_TARGET
+ }
+ 
+ # The "alt" compiler is the normal compiler with an extra "-mips16" argument.
+ proc compat-use-alt-compiler { } {
+     global saved_CFLAGS_FOR_TARGET CFLAGS_FOR_TARGET
+ 
+     if { [info exists saved_CFLAGS_FOR_TARGET] } {
+ 	set CFLAGS_FOR_TARGET [concat $saved_CFLAGS_FOR_TARGET "-mips16"]
+     } else {
+ 	set CFLAGS_FOR_TARGET "-mips16"
+     }
+ }
+ 
+ # Make the compiler under test the default.
+ proc compat-use-tst-compiler { } {
+     global saved_CFLAGS_FOR_TARGET CFLAGS_FOR_TARGET
+ 
+     if { [info exists saved_CFLAGS_FOR_TARGET] } {
+ 	set CFLAGS_FOR_TARGET $saved_CFLAGS_FOR_TARGET
+     } else {
+ 	unset -nocomplain CFLAGS_FOR_TARGET
+     }
+ }
+ 
+ load_lib gcc.exp
+ load_lib compat.exp
+ 
+ gcc_init
+ foreach src [lsort [find $srcdir/$subdir mips16_*_main.c]] {
+     if { [runtest_file_p $runtests $src] } {
+ 	compat-execute $src "mips16_inter" 1
+     }
+ }
+ compat-use-tst-compiler

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: PATCH:  mips16 float and complex support
  2007-05-23  0:37 PATCH: mips16 float and complex support Sandra Loosemore
@ 2007-05-23  7:59 ` Richard Sandiford
  2007-05-23 20:05   ` Sandra Loosemore
  0 siblings, 1 reply; 3+ messages in thread
From: Richard Sandiford @ 2007-05-23  7:59 UTC (permalink / raw)
  To: Sandra Loosemore; +Cc: GCC Patches

Sandra Loosemore <sandra@codesourcery.com> writes:
> *************** function_arg (const CUMULATIVE_ARGS *cum
> *** 4032,4038 ****
>   	}
>       }
>   
> !   if (!info.fpr_p)
>       return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
>     else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info.reg_offset > 0)
>       /* In o32, the second argument is always passed in $f14
> --- 4031,4037 ----
>   	}
>       }
>   
> !   if (!info.fpr_p || TARGET_MIPS16)
>       return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
>     else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info.reg_offset > 0)
>       /* In o32, the second argument is always passed in $f14

Please move mips_arg_regno before this function and replace the above
if-else block with:

  return mips_arg_regno (mode, TARGET_HARD_FLOAT);

> + /* Return a two-character string representing a function floating
> +    point return mode, used to name MIPS16 function stubs.  */

Nit, but: "floating point" -> "floating-point"?  (Probably my fault.)

> + /* INFO describes an argument that is pased in a single register value.
> +    Return the register it uses, assuming that FPRs are available if
> +    HARD_FLOAT_P.  */

"pased" -> "passed" and "single register" -> "single-register".
(Definitely my fault, sorry.)

OK with those changes.  Thanks for putting so much effort into this.

Richard

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: PATCH:  mips16 float and complex support
  2007-05-23  7:59 ` Richard Sandiford
@ 2007-05-23 20:05   ` Sandra Loosemore
  0 siblings, 0 replies; 3+ messages in thread
From: Sandra Loosemore @ 2007-05-23 20:05 UTC (permalink / raw)
  To: GCC Patches, richard

[-- Attachment #1: Type: text/plain, Size: 168 bytes --]

Richard Sandiford wrote:

> OK with those changes.  Thanks for putting so much effort into this.

OK, the patch has been committed with the suggested tweaks.

-Sandra


[-- Attachment #2: 06-mips16-hardfloat.log --]
[-- Type: text/x-log, Size: 4914 bytes --]

2007-05-23  Sandra Loosemore  <sandra@codesourcery.com>
	    Nigel Stephens  <nigel@mips.com>
	    Richard Sandiford  <richard@codesourcery.com>

	gcc/
	Fix up MIPS16 hard float and add support for complex.

	* config/mips/mips.h (TARGET_HARD_FLOAT_ABI): New.
	(TARGET_SOFT_FLOAT_ABI): New.
	(TARGET_CPU_CPP_BUILTINS): Define __mips_hard_float and
	__mips_soft_float to reflect the ABI in use, not whether the
	FPU is directly accessible (e.g., in MIPS16 mode).
	(UNITS_PER_HWFPVALUE): Use TARGET_SOFT_FLOAT_ABI.
	(UNITS_PER_FPVALUE): Likewise.

	* config/mips/mips.c (mips_expand_call): Remove redundant
	TARGET_MIPS16 check.
	(mips_arg_regno): New.
	(function_arg_advance): When setting bits in cum->fp_code for
	MIPS16, don't subtract 1 from cum->arg_number, since it is now
	zero-based.
	(function_arg): Use mips_arg_regno.
	(mips_return_mode_in_fpr_p): New.
	(mips16_call_stub_mode_suffix): New.
	(mips16_cfun_returns_in_fpr_p): New.
	(mips_save_reg_p): Use mips16_cfun_returns_in_fpr_p.
	(mips_output_function_prologue): Test mips16_hard_float, not
	!TARGET_SOFT_FLOAT, to decide when a function stub is required.
	(mips_expand_epilogue): Call MIPS16 helper routines to copy
	return value into a floating-point register.
	(mips_can_use_return_insn): Use mips16_cfun_returns_in_fpr_p.
	(mips_function_value): Rewrite to use mips_return_mode_in_fpr_p.
	(mips16_fp_args): Handle MIPS32r2 ISA which supports
	TARGET_FLOAT64, and use mfhc1/mthc1 to copy the most significant
	word of double arguments from or to the high bits of 64-bit
	floating point registers.
	(build_mips16_function_stub): Fill in DECL_RESULT for stubdecl.
	(mips16_fpret_double): New helper function.
	(build_mips16_call_stub): Use mips16_return_mode_in_fpr_p.  Add
	support for complex modes.  Fill in DECL_RESULT for stubdecl.
	(mips_init_libfuncs): Remove redundant TARGET_MIPS16 check.

	* config/mips/mips16.S 
	(RET, ARG1, ARG2): New.
	(MERGE_GPRf, MERGE_GPRt): New.
	(DELAYt, DELAYf): New.
	(MOVE_SF_BYTE0, MOVE_SI_BYTE0): New.
	(MOVE_SF_BYTE4, MOVE_SF_BYTE8): New.
	(MOVE_DF_BYTE0, MOVE_DF_BYTE8): New.
	(MOVE_SF_RET, MOVE_SC_RET, MOVE_DF_RET, MOVE_DC_RET, MOVE_SI_RET): New.
	(SFOP): Renamed to...
	(OPSF3): This, and macro-ified.  Updated all uses.
	(SFOP2): Renamed to...
	(OPSF2): This, and macro-ified.  Updated all uses.
	(SFCMP): Renamed to...
	(CMPSF): This, and macro-ified.  Updated all uses.
	(SFREVCMP): Renamed to...
	(REVCMPSF): This, and macro-ified.  Updated all uses.
	(__mips16_floatsisf, __mips16_fix_truncsfsi): Macro-ified.
	(LDDBL1, LDDBL2, RETDBL): Deleted.
	(DFOP): Renamed to...
	(OPDF3): This, and macro-ified.  Updated all uses.
	(DFOP2): Renamed to...
	(OPDF2): This, and macro-ified.  Updated all uses.
	(__mips16_extendsfdf2, __mips16_truncdfsf2): Macro-ified.
	(DFCMP): Renamed to...
	(CMPDF): This, and macro-ified.  Updated all uses.
	(DFREVCMP): Renamed to...
	(REVCMPDF): This, and macro-ified.  Updated all uses.
	(__mips16_floatsidf, __mips16_fix_truncdfsi): Macro-ified.
	(RET_FUNCTION): New.
	(__mips16_ret_sf, __mips16_ret_df): Macro-ified.
	(__mips16_ret_sc, __mips16_ret_dc): New.
	(STUB_ARGS_0, STUB_ARGS_1, STUB_ARGS_5, STUB_ARGS_9, STUB_ARGS_2,
	STUB_ARGS_6, STUB_ARGS_10): New.
	(CALL_STUB_NO_RET): New.
	(__mips16_call_stub_1): Macro-ified.
	(__mips16_call_stub_5): Macro-ified.
	(__mips16_call_stub_2): Macro-ified.
	(__mips16_call_stub_6): Macro-ified.
	(__mips16_call_stub_9): Macro-ified.
	(__mips16_call_stub_10): Macro-ified.
	(CALL_STUB_RET): New.
	(__mips16_call_stub_sf_0): Macro-ified.
	(__mips16_call_stub_sf_1): Macro-ified.
	(__mips16_call_stub_sf_5): Macro-ified.
	(__mips16_call_stub_sf_2): Macro-ified.
	(__mips16_call_stub_sf_6): Macro-ified.
	(__mips16_call_stub_sf_9): Macro-ified.
	(__mips16_call_stub_sf_10): Macro-ified.
	(__mips16_call_stub_df_0): Macro-ified.
	(__mips16_call_stub_df_1): Macro-ified.
	(__mips16_call_stub_df_5): Macro-ified.
	(__mips16_call_stub_df_2): Macro-ified.
	(__mips16_call_stub_df_6): Macro-ified.
	(__mips16_call_stub_df_9): Macro-ified.
	(__mips16_call_stub_df_10): Macro-ified.
	(__mips16_call_stub_sc_0): New.
	(__mips16_call_stub_sc_1): New.
	(__mips16_call_stub_sc_5): New.
	(__mips16_call_stub_sc_2): New.
	(__mips16_call_stub_sc_6): New.
	(__mips16_call_stub_sc_9): New.
	(__mips16_call_stub_sc_10): New.
	(__mips16_call_stub_dc_0): New.
	(__mips16_call_stub_dc_1): New.
	(__mips16_call_stub_dc_5): New.
	(__mips16_call_stub_dc_2): New.
	(__mips16_call_stub_dc_6): New.
	(__mips16_call_stub_dc_9): New.
	(__mips16_call_stub_dc_10): New.
	
	* config/mips/t-elf (LIB1ASMFUNCS): Add MIPS16 floating-point stubs.
	* config/mips/t-isa3264 (LIB1ASMFUNCS): Likewise.
	* config/mips/t-r2900 (LIB1ASMFUNCS): Likewise.

	gcc/testsuite/
	* gcc.target/mips/inter/mips16_stubs_1_main.c: New.
	* gcc.target/mips/inter/mips16_stubs_1_x.c: New.
	* gcc.target/mips/inter/mips16_stubs_1_y.c: New.
	* gcc.target/mips/inter/mips16-inter.exp: New.


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-05-23 20:05 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-05-23  0:37 PATCH: mips16 float and complex support Sandra Loosemore
2007-05-23  7:59 ` Richard Sandiford
2007-05-23 20:05   ` Sandra Loosemore

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).