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


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

PATCH: mips16 float and complex support


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

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

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