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]

[committed] Add MIPS16 PIC support


This patch adds MIPS16 o32 PIC support.  It's work I did under
contract to Specifix, so I'd like to thank them for funding it.

The patch supports both soft-float and hard-float, but it doesn't yet
support -mxgot or TLS.  It relies on a binutils patch that I committed
earlier this week.

A lot of the complication is in supporting hard-float.
The changes are as follows:

- MIPS16 stub calls need to be indirect in some cases.  To avoid
  duplicating logic, I've made the stub calls go through the main
  mips_expand_call function.  (Before the patch, they generated
  insns directly.)
  
- The call to __mips16_ret_* in mips16_copy_fpr_return_value
  is done in the epilogue.  After the change mentioned in the
  previous point, mips_expand_call must know how to handle calls
  in the epilogue, using an epilogue temporary register if need be.

- Calls to hard-float stubs must not be lazily bound.  Such stubs
  take an argument in $2, which the dynamic resolver is not guaranteed
  to preserve.

  The patch adds a new symbol flag, SYMBOL_FLAG_BIND_NOW,
  to control this.

- If a MIPS16 function has a non-MIPS16 hard-float stub, we want
  locally-binding MIPS16 calls to that function to go directly
  to the real function when possible.  This requires a different
  call sequence, so it isn't really something the linker can do.

  The patch therefore creates a directly-callable local alias for
  each MIPS16 function.  We can use this alias for locally-binding
  calls to locally-defined functions.

- The previous point exposed an omission in mips_global_symbol_p;
  SYMBOL_REF_LOCAL_P indicates whether the symbol is locally-binding,
  not whether it is STB_LOCAL.  We need to check SYMBOL_REF_EXTERNAL_P
  as well.

- The patch generalises the function and call stub generators
  so that they handle PIC as well.

- The patch fixes a bug in the code that builds calls that must --
  or might -- go via a hard-float stub.  The calls should _clobber_ $18,
  not use it.

- The patch inlines __mips16_floatsisf into __mips16_floatunsisf.
  The new code has at most two more instructions, is faster,
  and avoids the need to handle PICness in mips16.S.

- mips16.S stubs must copy the target address into $25.
  These instructions can go in the delay slot.

- The MIPS16 gp setup sequence is:

            0: li      $v0,%hi(_gp_disp)
            4: addiupc $v1,%lo(_gp_disp)
            8: sll     $v0,16
           12: addu    $v0,$v1
           14: move    $gp,$v0

  The position of the first two instructions is important; they must
  always come at the start of the function.  (Just like the non-MIPS16
  _gp_disp must.)  However, it isn't always necessary to copy $v0 into
  $gp; it depends on what the following code needs.

  The patch therefore emits the first four instructions at the start
  of the function (in the place where we'd normally emit .cpload).
  It then copies $v0 to $gp in the prologue.

- Indirect MIPS16 calls cannot go via $25 directly.  We instead need
  to call through a MIPS16 register and copy that register to $25
  beforehand.  (This move can be put in the call delay slot.)

- We had a M16_NA_REGS class, which was (only) used for indirect calls.
  This might have been necessary at one time, but these days,
  it's enough to use M16_REGS for indirect calls and to rely on the
  register allocators not to pick a register that is already used
  by the call.  (That's what we do for GR_REGS when !TARGET_MIPS16.)

  The name of M16_NA_REGS ("no argument registers") is rather
  misleading, because $2 can be an argument to MIPS16 stubs.
  The patch therefore removes it.

  (This is related to the "c" constraint change I had to make as
  part of the previous point.)

- We don't need to worry about setting up $25 when calling a MIPS16 stub.

- We shouldn't force symbolic constants into the text constant pool if
  those constants need dynamic relocation.  (Or, in code terms, we
  shouldn't force symbols into the constant pool if we would normally
  use the GOT to access them.)

  This has a knock-on effect on what is and isn't a legitimate
  symbolic constant.  If we need a temporary register when
  computing a constant inline, that constant is only valid
  if we can force it to memory during and after reload.

- $gp isn't a "d" register in MIPS16 mode.  If we want to access
  the $gp register, we must either use the gp pseudo register
  (before reload) or copy $gp into a temporary register
  (during and after reload).  mips_pic_base_register abstracts
  away this difference, but there are various knock-on effects
  throughout the code.

- We want to expose MIPS16 uses of the gp register as soon as possible,
  so that we can make more use of the gp pseudo register.  We therefore
  lower GOT accesses earlier than in non-MIPS16 mode.  This includes the
  high part of SYMBOL_GOT_PAGE_OFST (i.e. the GOT page access).

  Lowering HIGHs before reload is new functionality.  It needs a new
  array (mips_split_hi_p) and changes to mips_split_symbol.

- We now want TARGET_EXPLICIT_RELOCS to hold for MIPS16 code,
  whereas we previously treated the two as mutually exclusive.
  This has various knock-on effects.

There are some mips64-linux-gnu failures that don't occur for -mno-mips16:

- Two tests fail on all MIPS16 targets due to the lack of long branch
  support:

    gcc.c-torture/compile/20001226-1.c  -O0
    g++.dg/opt/longbranch1.C

  (Yes, I should open a PR and XFAIL these.)

- A bunch of libgomp.fortran tests that require TLS.  I'm not sure what
  to do here.  I could XFAIL them, but I'm not sure whether Jakub
  would be happy having a load of "require TLS" directives in
  something that relies so heavily on threads.  Yet the results
  are good enough for check-libgomp to be worthwhile:

         # of expected passes		2001
         # of unexpected failures	18
         # of unsupported tests		128

- libjava Divide_* tests.  Linux doesn't yet raise SIGFPE for MIPS16
  divide-by-zero breaks.

- libjava Throw_2.  This is due to the lack of CFI info in the MIPS16
  call wrappers.  Like long-branch support, this is a general MIPS16
  failure; it just so happens that nothing in the C++ testsuite
  triggers it, so it doesn't show up on *-elf* test results.
  I'm treating this as a separate issue.

I'll write the changes.html entry once we've got the non-PIC stuff in.

Tested on mips64-linux-gnu and mipsisa64-elf.  Applied.

Richard


	* configure.ac (mips*-*-*linux*, mips*-*-gnu*): Use mt-mips-gnu.
	* configure: Regenerate.

config/
	* mt-mips16-compat: New file, taken from mt-mips-elfoabi.
	* mt-mips-elfoabi: Include mt-mips16-compat.
	* mt-mips-gnu: New file.

gcc/
	* config.gcc (mips*-*-linux*, mips64*-*-linux*): Add
	mips/t-libgcc-mips16 to tmake_file.
	* config/mips/mips-protos.h (mips_call_type): New enum.
	(mips_pic_base_register, mips_got_load): Declare.
	(mips_restore_gp): Take an rtx argument.
	(mips_use_pic_fn_addr_reg_p): Declare.
	(mips_expand_call): Replace the sibcall_p argument with
	a mips_call_type argument.  Add a lazy_p parameter.
	(mips_split_call): Declare.
	* config/mips/mips.h (MIPS16_PIC_TEMP_REGNUM): New macro.
	(MIPS16_PIC_TEMP): Likewise.
	(reg_class): Delete M16_NA_REGS.
	(REG_CLASS_NAMES, REG_CLASS_CONTENTS): Update accordingly.
	(SYMBOL_FLAG_BIND_NOW, SYMBOL_REF_BIND_NOW_P): New macros.
	(mips_split_hi_p): Declare.
	* config/mips/mips.c (mips_split_hi_p): New array.
	(mips_regno_to_class): Change M16_NA_REGS entries to M16_REGS.
	(mips_got_symbol_type_p): New function.
	(mips_global_symbol_p): Check SYMBOL_REF_EXTERNAL_P.
	(mips16_stub_function_p): New function.
	(mips16_local_function_p): Likewise.
	(mips_use_pic_fn_addr_reg_p): Likewise.
	(mips_cannot_force_const_mem): Return false for HIGHs.
	Extend CONST_INT and symbolic handling to MIPS16, using
	mips_symbol_insns to check that the base symbol type is a
	legitimate constant.  Reject GOT-based constants if
	TARGET_MIPS16_PCREL_LOADS.
	(mips_const_insns): Check targetm.cannot_force_const_mem when
	decomposing a symbolic base and a large offset.
	(mips_emit_call_insn): Add ORIG_ADDR and ADDR parameters.
	When calling a function that needs $25 from MIPS16 code,
	move the target address into $25 separately and add a USE
	to the call insn.
	(mips16_gp_pseudo_reg): Insert the initializer immediately
	before the first real insn.
	(mips_pic_base_register, mips_got_load): New functions.
	(mips_split_symbol): Generalize the name of the LO_SUM_OUT
	parameter to LOW_OUT.  Say that it can be any valid SET_SRC
	when splitting a load-address operation.  Split SYMBOL_GOT_DISP
	constants and highs of SYMBOL_GOT_PAGE_OFST constants.
	(mips_call_tls_get_addr): Update the call to mips_expand_call,
	also passing NULL_RTX rather than const0_rtx as the aux argument.
	(mips_rewrite_small_data_p): Check mips_lo_relocs and mips_split_p
	instead of TARGET_EXPLICIT_RELOCS.
	(mips_ok_for_lazy_binding_p): Check SYMBOL_REF_BIND_NOW_P.
	(mips_load_call_address): Replace the sibcall_p argument with
	a mips_call_type argument.  Use mips_got_load.
	(mips16_local_alias): New structure.
	(mips16_local_aliases): New variable.
	(mips16_local_aliases_hash): New function.
	(mips16_local_aliases_eq): Likewise.
	(mips16_local_alias): Likewise.
	(mips16_stub_function): Likewise.
	(mips16_build_function_stub): Create a local alias for the target
	function.  Handle TARGET_ABICALLS.  For PIC abicalls, emit a
	.cpload directive and an R_MIPS_NONE relocation for the target
	function, then load the alias rather than the function itself.
	Wrap the non-PIC abicalls version in ".option pic0/.option pic2".
	(mips16_copy_fpr_return_value): Use mips16_stub_function and
	mips_expand_call.  Set SYMBOL_REF_BIND_NOW on the symbol.
	(mips16_build_call_stub): Replace the FN parameter with an
	FN_PTR parameter.  Force the address into a register if it
	isn't a call_insn_operand; don't rely on the caller to do this.
	If a call to a locally-defined and locally-binding MIPS16
	function must be made indirectly, redirect the call to the
	function's local alias.  Use mips16_stub_function_p,
	mips16_stub_function, mips_expand_call and use_reg.
	Set SYMBOL_FLAG_BIND_NOW on __mips_call_* symbols.
	Use explicit %hi and %lo accesses where possible.
	Use MIPS_CALL to generate the correct code form of a
	jal instruction.  Add clobbers of $18 instead of uses.
	Update the call to mips_emit_call_insn.
	(mips_expand_call): Replace the SIBCALL_P argument with a
	mips_call_type argument and handle the new MIPS_CALL_EPILOGUE value.
	Take a LAZY_P parameter.  Call mips16_build_call_stub first,
	allowing it to modify the call address.  Update the calls to
	mips_load_call_address and mips_emit_call_insn.
	(mips_split_call): New function.
	(mips_init_relocs): Clear mips_split_hi_p.  Only use %gp_rel if
	!TARGET_MIPS16.  Split SYMBOL_GOT_DISP, and the high parts of
	SYMBOL_GOT_PAGE_OFST, for MIPS16 code.
	(mips_global_pointer): Check mips16_cfun_returns_in_fpr_p.
	(mips_extra_live_on_entry): Include MIPS16_PIC_TEMP_REGNUM
	if TARGET_MIPS16.
	(mips_cprestore_slot): New function.
	(mips_restore_gp): Take a TEMP parameter.  Handle TARGET_MIPS16
	and use mips_cprestore_slot.
	(mips_output_function_prologue): Handle TARGET_MIPS16 for
	LOADGP_OLDABI.
	(mips_emit_loadgp): Move into MIPS16_PIC_TEMP for MIPS16,
	then use a copygp_mips16 instruction to set up $28.
	(mips_expand_prologue): Initialize the cprestore slot for MIPS16 too.
	(mips16_lay_out_constants): Call split_all_insns_noflow.
	(mips_reorg_process_insns): Explicitly set all_noreorder_p to
	false if TARGET_MIPS16.
	(mips_reorg): Don't call vr4130_align_insns if TARGET_MIPS16.
	(mips_output_mi_thunk): Use mips_got_symbol_type_p.  Use the
	mips_dangerous_for_la25_p approach for MIPS16 PIC calls too.
	(mips_set_mips16_mode): Always set MASK_EXPLICIT_RELOCS for
	MIPS16 code.  Allow MIPS16 o32 PIC.
	(mips_override_options): Allow MIPS16 o32 PIC.
	* config/mips/mips.md: Lower CONST_GP_P moves into register moves
	after reload if TARGET_USE_GOT.
	(UNSPEC_COPYGP): New constant.
	(length): Use a default length of 8 for MIPS16 GOT loads.
	(*got_disp<mode>): Check mips_split_p instead of TARGET_XGOT.
	(*got_page<mode>): Check mips_split_hi_p.
	(*got_disp<mode>, *got_page<mode>): Use mips_got_load.
	(unspec_got<mode>, unspec_call<mode>): New expanders.
	(load_got<mode>, load_call<mode>): Remove the length attributes.
	Use a got attribute instead of a type attribute.
	(copygp_mips16): New insn.
	(restore_gp): Add a scratch clobber and pass it to mips_restore_gp.
	(load_call<mode>): Use a "d" constraint instead of an "r" constraint.
	(sibcall, sibcall_value, call, call_value): Update the calls
	to mips_expand_call.
	(call_internal, call_value_internal): Use mips_split_call.
	(call_value_multiple_internal): Likewise.
	(call_split): Move after call_internal (the insn it is split from).
	(call_internal_direct, call_value_internal_direct): Turn into
	define_insn_and_splits.  Split if TARGET_SPLIT_CALLS.
	(call_direct_split, call_value_direct_split): New patterns.
	* config/mips/constraints.md (c): Handle TARGET_MIPS16 first
	and use M16_REGS instead of M16_NA_REGS.
	* config/mips/predicates.md (const_call_insn_operand): Replace
	the TARGET_ABSOLUTE_ABICALLS-based check with a more general
	mips_use_pic_fn_addr_reg_p check.
	(move_operand): Reject HIGHs if mips_split_hi_p.
	* config/mips/mips16.S: Assembly as empty if the ABI is not suitable.
	(__mips16_floatunsisf): Inline __mips16_floatsisf.
	(CALL_STUB_NO_RET, CALL_STUB_REG): Copy the target register to $25.
	* config/mips/libgcc-mips16.ver: New file.
	* config/mips/t-libgcc-mips16 (SHLIB_MAPFILES): Add
	$(srcdir)/config/mips/libgcc-mips16.ver.

gcc/testsuite/
	* lib/target-supports.exp (check_profiling_available): Return false
	for -p and -pg on MIPS16 targets.

Index: configure.ac
===================================================================
--- configure.ac	2008-08-09 16:50:40.000000000 +0100
+++ configure.ac	2008-08-09 19:22:20.000000000 +0100
@@ -1904,6 +1904,9 @@ case "${target}" in
   mipsisa*-*-elfoabi*)
     target_makefile_frag="config/mt-mips-elfoabi"
     ;;
+  mips*-*-*linux* | mips*-*-gnu*)
+    target_makefile_frag="config/mt-mips-gnu"
+    ;;
   *-*-netware*)
     target_makefile_frag="config/mt-netware"
     ;;
Index: configure
===================================================================
--- configure	2008-08-09 16:50:40.000000000 +0100
+++ configure	2008-08-09 19:22:20.000000000 +0100
@@ -5448,6 +5448,9 @@ case "${target}" in
   mipsisa*-*-elfoabi*)
     target_makefile_frag="config/mt-mips-elfoabi"
     ;;
+  mips*-*-*linux* | mips*-*-gnu*)
+    target_makefile_frag="config/mt-mips-gnu"
+    ;;
   *-*-netware*)
     target_makefile_frag="config/mt-netware"
     ;;
Index: config/mt-mips16-compat
===================================================================
--- /dev/null	2008-08-09 09:51:34.868054250 +0100
+++ config/mt-mips16-compat	2008-08-09 19:22:20.000000000 +0100
@@ -0,0 +1,5 @@
+# Configurations use this fragment if they support MIPS16 and non-MIPS16 code,
+# but if the libraries are all non-MIPS16.  Add -minterlink-mips16 so
+# that the libraries can be used with both ISA modes.
+CFLAGS_FOR_TARGET += -minterlink-mips16
+CXXFLAGS_FOR_TARGET += -minterlink-mips16
Index: config/mt-mips-elfoabi
===================================================================
--- config/mt-mips-elfoabi	2008-08-09 16:50:40.000000000 +0100
+++ config/mt-mips-elfoabi	2008-08-09 19:22:20.000000000 +0100
@@ -1,6 +1,1 @@
-# The *-elfoabi configurations are intended to be usable for both
-# MIPS16 and non-MIPS16 code, but the libraries are all non-MIPS16.
-# Add -minterlink-mips16 so that the libraries can be used with both
-# ISA modes.
-CFLAGS_FOR_TARGET += -minterlink-mips16
-CXXFLAGS_FOR_TARGET += -minterlink-mips16
+include $(srcdir)/config/mt-mips16-compat
Index: config/mt-mips-gnu
===================================================================
--- /dev/null	2008-08-09 09:51:34.868054250 +0100
+++ config/mt-mips-gnu	2008-08-09 19:22:20.000000000 +0100
@@ -0,0 +1,2 @@
+include $(srcdir)/config/mt-gnu
+include $(srcdir)/config/mt-mips16-compat
Index: gcc/config.gcc
===================================================================
--- gcc/config.gcc	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config.gcc	2008-08-09 19:22:20.000000000 +0100
@@ -1544,7 +1544,7 @@ mips*-*-netbsd*)			# NetBSD/mips, either
 	;;
 mips64*-*-linux* | mipsisa64*-*-linux*)
 	tm_file="dbxelf.h elfos.h svr4.h linux.h ${tm_file} mips/linux.h mips/linux64.h"
-	tmake_file="${tmake_file} mips/t-linux64"
+	tmake_file="${tmake_file} mips/t-linux64 mips/t-libgcc-mips16"
 	tm_defines="${tm_defines} MIPS_ABI_DEFAULT=ABI_N32"
 	case ${target} in
 		mips64el-st-linux-gnu)
@@ -1561,6 +1561,7 @@ mips64*-*-linux* | mipsisa64*-*-linux*)
 	;;
 mips*-*-linux*)				# Linux MIPS, either endian.
         tm_file="dbxelf.h elfos.h svr4.h linux.h ${tm_file} mips/linux.h"
+	tmake_file="${tmake_file} mips/t-libgcc-mips16"
 	case ${target} in
         mipsisa32r2*)
 		tm_defines="${tm_defines} MIPS_ISA_DEFAULT=33"
Index: gcc/config/mips/mips-protos.h
===================================================================
--- gcc/config/mips/mips-protos.h	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config/mips/mips-protos.h	2008-08-09 19:22:20.000000000 +0100
@@ -164,6 +164,22 @@ enum mips_loadgp_style {
 
 struct mips16e_save_restore_info;
 
+/* Classifies a type of call.
+
+   MIPS_CALL_NORMAL
+	A normal call or call_value pattern.
+
+   MIPS_CALL_SIBCALL
+	A sibcall or sibcall_value pattern.
+
+   MIPS_CALL_EPILOGUE
+	A call inserted in the epilogue.  */
+enum mips_call_type {
+  MIPS_CALL_NORMAL,
+  MIPS_CALL_SIBCALL,
+  MIPS_CALL_EPILOGUE
+};
+
 extern bool mips_symbolic_constant_p (rtx, enum mips_symbol_context,
 				      enum mips_symbol_type *);
 extern int mips_regno_mode_ok_for_base_p (int, enum machine_mode, bool);
@@ -175,6 +191,8 @@ extern int mips_split_const_insns (rtx);
 extern int mips_load_store_insns (rtx, rtx);
 extern int mips_idiv_insns (void);
 extern rtx mips_emit_move (rtx, rtx);
+extern rtx mips_pic_base_register (rtx);
+extern rtx mips_got_load (rtx, rtx, enum mips_symbol_type);
 extern bool mips_split_symbol (rtx, rtx, enum machine_mode, rtx *);
 extern rtx mips_unspec_address (rtx, enum mips_symbol_type);
 extern bool mips_legitimize_address (rtx *, enum machine_mode);
@@ -202,7 +220,7 @@ extern rtx mips_subword (rtx, bool);
 extern bool mips_split_64bit_move_p (rtx, rtx);
 extern void mips_split_doubleword_move (rtx, rtx);
 extern const char *mips_output_move (rtx, rtx);
-extern void mips_restore_gp (void);
+extern void mips_restore_gp (rtx);
 #ifdef RTX_CODE
 extern bool mips_expand_scc (enum rtx_code, rtx);
 extern void mips_expand_conditional_branch (rtx *, enum rtx_code);
@@ -210,7 +228,9 @@ extern void mips_expand_vcondv2sf (rtx, 
 extern void mips_expand_conditional_move (rtx *);
 extern void mips_expand_conditional_trap (enum rtx_code);
 #endif
-extern rtx mips_expand_call (rtx, rtx, rtx, rtx, bool);
+extern bool mips_use_pic_fn_addr_reg_p (const_rtx);
+extern rtx mips_expand_call (enum mips_call_type, rtx, rtx, rtx, rtx, bool);
+extern void mips_split_call (rtx, rtx);
 extern void mips_expand_fcc_reload (rtx, rtx, rtx);
 extern void mips_set_return_address (rtx, rtx);
 extern bool mips_expand_block_move (rtx, rtx, rtx);
Index: gcc/config/mips/mips.h
===================================================================
--- gcc/config/mips/mips.h	2008-08-09 19:19:28.000000000 +0100
+++ gcc/config/mips/mips.h	2008-08-09 19:22:20.000000000 +0100
@@ -1659,16 +1659,29 @@ #define FRAME_POINTER_REQUIRED (mips_fra
 /* Register in which static-chain is passed to a function.  */
 #define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 15)
 
-/* Registers used as temporaries in prologue/epilogue code.  If we're
-   generating mips16 code, these registers must come from the core set
-   of 8.  The prologue register mustn't conflict with any incoming
-   arguments, the static chain pointer, or the frame pointer.  The
-   epilogue temporary mustn't conflict with the return registers, the
-   frame pointer, the EH stack adjustment, or the EH data registers.  */
+/* Registers used as temporaries in prologue/epilogue code:
 
+   - If a MIPS16 PIC function needs access to _gp, it first loads
+     the value into MIPS16_PIC_TEMP and then copies it to $gp.
+
+   - The prologue can use MIPS_PROLOGUE_TEMP as a general temporary
+     register.  The register must not conflict with MIPS16_PIC_TEMP.
+
+   - The epilogue can use MIPS_EPILOGUE_TEMP as a general temporary
+     register.
+
+   If we're generating MIPS16 code, these registers must come from the
+   core set of 8.  The prologue registers mustn't conflict with any
+   incoming arguments, the static chain pointer, or the frame pointer.
+   The epilogue temporary mustn't conflict with the return registers,
+   the PIC call register ($25), the frame pointer, the EH stack adjustment,
+   or the EH data registers.  */
+
+#define MIPS16_PIC_TEMP_REGNUM (GP_REG_FIRST + 2)
 #define MIPS_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 3)
 #define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8))
 
+#define MIPS16_PIC_TEMP gen_rtx_REG (Pmode, MIPS16_PIC_TEMP_REGNUM)
 #define MIPS_PROLOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP_REGNUM)
 #define MIPS_EPILOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_EPILOGUE_TEMP_REGNUM)
 
@@ -1716,7 +1729,6 @@ #define PIC_FUNCTION_ADDR_REGNUM (GP_REG
 enum reg_class
 {
   NO_REGS,			/* no registers in set */
-  M16_NA_REGS,			/* mips16 regs not used to pass args */
   M16_REGS,			/* mips16 directly accessible registers */
   T_REG,			/* mips16 T register ($24) */
   M16_T_REGS,			/* mips16 registers plus T register */
@@ -1757,7 +1769,6 @@ #define GENERAL_REGS GR_REGS
 #define REG_CLASS_NAMES							\
 {									\
   "NO_REGS",								\
-  "M16_NA_REGS",							\
   "M16_REGS",								\
   "T_REG",								\
   "M16_T_REGS",								\
@@ -1801,7 +1812,6 @@ #define REG_CLASS_NAMES							\
 #define REG_CLASS_CONTENTS						                                \
 {									                                \
   { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },	/* no registers */	\
-  { 0x0003000c, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },	/* mips16 nonarg regs */\
   { 0x000300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },	/* mips16 registers */	\
   { 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },	/* mips16 T register */	\
   { 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 },	/* mips16 and T regs */ \
@@ -2419,6 +2429,11 @@ #define SYMBOL_FLAG_LONG_CALL	(SYMBOL_FL
 #define SYMBOL_REF_LONG_CALL_P(X)					\
   ((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_LONG_CALL) != 0)
 
+/* This flag marks functions that cannot be lazily bound.  */
+#define SYMBOL_FLAG_BIND_NOW (SYMBOL_FLAG_MACH_DEP << 1)
+#define SYMBOL_REF_BIND_NOW_P(RTX) \
+  ((SYMBOL_REF_FLAGS (RTX) & SYMBOL_FLAG_BIND_NOW) != 0)
+
 /* True if we're generating a form of MIPS16 code in which jump tables
    are stored in the text section and encoded as 16-bit PC-relative
    offsets.  This is only possible when general text loads are allowed,
@@ -3280,6 +3295,7 @@ #define MIPS_SYNC_EXCHANGE_12_NONZERO_OP
 extern int mips_dbx_regno[];
 extern int mips_dwarf_regno[];
 extern bool mips_split_p[];
+extern bool mips_split_hi_p[];
 extern GTY(()) rtx cmp_operands[2];
 extern enum processor_type mips_arch;   /* which cpu to codegen for */
 extern enum processor_type mips_tune;   /* which cpu to schedule for */
Index: gcc/config/mips/mips.c
===================================================================
--- gcc/config/mips/mips.c	2008-08-09 19:21:45.000000000 +0100
+++ gcc/config/mips/mips.c	2008-08-09 19:53:27.000000000 +0100
@@ -469,6 +469,10 @@ static GTY (()) int mips_output_filename
    mips_split_symbol.  */
 bool mips_split_p[NUM_SYMBOL_TYPES];
 
+/* mips_split_hi_p[X] is true if the high parts of symbols of type X
+   can be split by mips_split_symbol.  */
+bool mips_split_hi_p[NUM_SYMBOL_TYPES];
+
 /* mips_lo_relocs[X] is the relocation to use when a symbol of type X
    appears in a LO_SUM.  It can be null if such LO_SUMs aren't valid or
    if they are matched by a special .md file pattern.  */
@@ -479,11 +483,11 @@ static GTY (()) int mips_output_filename
 
 /* Index R is the smallest register class that contains register R.  */
 const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
-  LEA_REGS,	LEA_REGS,	M16_NA_REGS,	V1_REG,
+  LEA_REGS,	LEA_REGS,	M16_REGS,	V1_REG,
   M16_REGS,	M16_REGS,	M16_REGS,	M16_REGS,
   LEA_REGS,	LEA_REGS,	LEA_REGS,	LEA_REGS,
   LEA_REGS,	LEA_REGS,	LEA_REGS,	LEA_REGS,
-  M16_NA_REGS,	M16_NA_REGS,	LEA_REGS,	LEA_REGS,
+  M16_REGS,	M16_REGS,	LEA_REGS,	LEA_REGS,
   LEA_REGS,	LEA_REGS,	LEA_REGS,	LEA_REGS,
   T_REG,	PIC_FN_ADDR_REG, LEA_REGS,	LEA_REGS,
   LEA_REGS,	LEA_REGS,	LEA_REGS,	LEA_REGS,
@@ -1342,6 +1346,22 @@ mips_build_integer (struct mips_integer_
     }
 }
 
+/* Return true if symbols of type TYPE require a GOT access.  */
+
+static bool
+mips_got_symbol_type_p (enum mips_symbol_type type)
+{
+  switch (type)
+    {
+    case SYMBOL_GOT_PAGE_OFST:
+    case SYMBOL_GOT_DISP:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
 /* Return true if X is a thread-local symbol.  */
 
 static bool
@@ -1359,7 +1379,7 @@ mips_global_symbol_p (const_rtx x)
   const_tree decl = SYMBOL_REF_DECL (x);
 
   if (!decl)
-    return !SYMBOL_REF_LOCAL_P (x);
+    return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x);
 
   /* Weakref symbols are not TREE_PUBLIC, but their targets are global
      or weak symbols.  Relocations in the object file will be against
@@ -1367,6 +1387,27 @@ mips_global_symbol_p (const_rtx x)
   return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl));
 }
 
+/* Return true if function X is a libgcc MIPS16 stub function.  */
+
+static bool
+mips16_stub_function_p (const_rtx x)
+{
+  return (GET_CODE (x) == SYMBOL_REF
+	  && strncmp (XSTR (x, 0), "__mips16_", 9) == 0);
+}
+
+/* Return true if function X is a locally-defined and locally-binding
+   MIPS16 function.  */
+
+static bool
+mips16_local_function_p (const_rtx x)
+{
+  return (GET_CODE (x) == SYMBOL_REF
+	  && SYMBOL_REF_LOCAL_P (x)
+	  && !SYMBOL_REF_EXTERNAL_P (x)
+	  && mips_use_mips16_mode_p (SYMBOL_REF_DECL (x)));
+}
+
 /* Return true if SYMBOL_REF X binds locally.  */
 
 static bool
@@ -1401,6 +1442,29 @@ mips_dangerous_for_la25_p (rtx x)
 	  && mips_global_symbol_p (x));
 }
 
+/* Return true if calls to X might need $25 to be valid on entry.  */
+
+bool
+mips_use_pic_fn_addr_reg_p (const_rtx x)
+{
+  if (!TARGET_USE_PIC_FN_ADDR_REG)
+    return false;
+
+  /* MIPS16 stub functions are guaranteed not to use $25.  */
+  if (mips16_stub_function_p (x))
+    return false;
+
+  /* When TARGET_ABSOLUTE_ABICALLS is true, locally-defined functions
+     use absolute accesses to set up the global pointer.  */
+  if (TARGET_ABSOLUTE_ABICALLS
+      && GET_CODE (x) == SYMBOL_REF
+      && mips_symbol_binds_local_p (x)
+      && !SYMBOL_REF_EXTERNAL_P (x))
+    return false;
+
+  return true;
+}
+
 /* Return the method that should be used to access SYMBOL_REF or
    LABEL_REF X in context CONTEXT.  */
 
@@ -1736,24 +1800,37 @@ mips_tls_symbol_ref_1 (rtx *x, void *dat
 static bool
 mips_cannot_force_const_mem (rtx x)
 {
+  enum mips_symbol_type type;
   rtx base, offset;
 
-  if (!TARGET_MIPS16)
-    {
-      /* As an optimization, reject constants that mips_legitimize_move
-	 can expand inline.
+  /* There is no assembler syntax for expressing an address-sized
+     high part.  */
+  if (GET_CODE (x) == HIGH)
+    return true;
+
+  /* As an optimization, reject constants that mips_legitimize_move
+     can expand inline.
 
-	 Suppose we have a multi-instruction sequence that loads constant C
-	 into register R.  If R does not get allocated a hard register, and
-	 R is used in an operand that allows both registers and memory
-	 references, reload will consider forcing C into memory and using
-	 one of the instruction's memory alternatives.  Returning false
-	 here will force it to use an input reload instead.  */
-      if (GET_CODE (x) == CONST_INT)
+     Suppose we have a multi-instruction sequence that loads constant C
+     into register R.  If R does not get allocated a hard register, and
+     R is used in an operand that allows both registers and memory
+     references, reload will consider forcing C into memory and using
+     one of the instruction's memory alternatives.  Returning false
+     here will force it to use an input reload instead.  */
+  if (GET_CODE (x) == CONST_INT && LEGITIMATE_CONSTANT_P (x))
+    return true;
+
+  split_const (x, &base, &offset);
+  if (mips_symbolic_constant_p (base, SYMBOL_CONTEXT_LEA, &type)
+      && type != SYMBOL_FORCE_TO_MEM)
+    {
+      /* The same optimization as for CONST_INT.  */
+      if (SMALL_INT (offset) && mips_symbol_insns (type, MAX_MACHINE_MODE) > 0)
 	return true;
 
-      split_const (x, &base, &offset);
-      if (symbolic_operand (base, VOIDmode) && SMALL_INT (offset))
+      /* If MIPS16 constant pools live in the text section, they should
+	 not refer to anything that might need run-time relocation.  */
+      if (TARGET_MIPS16_PCREL_LOADS && mips_got_symbol_type_p (type))
 	return true;
     }
 
@@ -2097,8 +2174,13 @@ mips_const_insns (rtx x)
 	return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE);
 
       /* Otherwise try splitting the constant into a base and offset.
-	 16-bit offsets can be added using an extra ADDIU.  Larger offsets
-	 must be calculated separately and then added to the base.  */
+	 If the offset is a 16-bit value, we can load the base address
+	 into a register and then use (D)ADDIU to add in the offset.
+	 If the offset is larger, we can load the base and offset
+	 into separate registers and add them together with (D)ADDU.
+	 However, the latter is only possible before reload; during
+	 and after reload, we must have the option of forcing the
+	 constant into the pool instead.  */
       split_const (x, &x, &offset);
       if (offset != 0)
 	{
@@ -2107,7 +2189,7 @@ mips_const_insns (rtx x)
 	    {
 	      if (SMALL_INT (offset))
 		return n + 1;
-	      else
+	      else if (!targetm.cannot_force_const_mem (x))
 		return n + 1 + mips_build_integer (codes, INTVAL (offset));
 	    }
 	}
@@ -2238,17 +2320,30 @@ mips_force_temporary (rtx dest, rtx valu
 
 /* Emit a call sequence with call pattern PATTERN and return the call
    instruction itself (which is not necessarily the last instruction
-   emitted).  LAZY_P is true if the call address is lazily-bound.  */
+   emitted).  ORIG_ADDR is the original, unlegitimized address,
+   ADDR is the legitimized form, and LAZY_P is true if the call
+   address is lazily-bound.  */
 
 static rtx
-mips_emit_call_insn (rtx pattern, bool lazy_p)
+mips_emit_call_insn (rtx pattern, rtx orig_addr, rtx addr, bool lazy_p)
 {
-  rtx insn;
+  rtx insn, reg;
 
   insn = emit_call_insn (pattern);
 
-  /* Lazy-binding stubs require $gp to be valid on entry.  */
+  if (TARGET_MIPS16 && mips_use_pic_fn_addr_reg_p (orig_addr))
+    {
+      /* MIPS16 JALRs only take MIPS16 registers.  If the target
+	 function requires $25 to be valid on entry, we must copy it
+	 there separately.  The move instruction can be put in the
+	 call's delay slot.  */
+      reg = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+      emit_insn_before (gen_move_insn (reg, addr), insn);
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+    }
+
   if (lazy_p)
+    /* Lazy-binding stubs require $gp to be valid on entry.  */
     use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
 
   if (TARGET_USE_GOT)
@@ -2334,21 +2429,17 @@ mips16_gp_pseudo_reg (void)
   if (!cfun->machine->initialized_mips16_gp_pseudo_p
       && (current_ir_type () != IR_GIMPLE || currently_expanding_to_rtl))
     {
-      rtx insn, scan, after;
+      rtx insn, scan;
+
+      push_topmost_sequence ();
+
+      scan = get_insns ();
+      while (NEXT_INSN (scan) && !INSN_P (NEXT_INSN (scan)))
+	scan = NEXT_INSN (scan);
 
       insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
+      emit_insn_after (insn, scan);
 
-      push_topmost_sequence ();
-      /* We need to emit the initialization after the FUNCTION_BEG
-         note, so that it will be integrated.  */
-      after = get_insns ();
-      for (scan = after; scan != NULL_RTX; scan = NEXT_INSN (scan))
-	if (NOTE_P (scan) && NOTE_KIND (scan) == NOTE_INSN_FUNCTION_BEG)
-	  {
-	    after = scan;
-	    break;
-	  }
-      insn = emit_insn_after (insn, after);
       pop_topmost_sequence ();
 
       cfun->machine->initialized_mips16_gp_pseudo_p = true;
@@ -2357,17 +2448,74 @@ mips16_gp_pseudo_reg (void)
   return cfun->machine->mips16_gp_pseudo_rtx;
 }
 
+/* Return a base register that holds pic_offset_table_rtx.
+   TEMP, if nonnull, is a scratch Pmode base register.  */
+
+rtx
+mips_pic_base_register (rtx temp)
+{
+  if (!TARGET_MIPS16)
+    return pic_offset_table_rtx;
+
+  if (can_create_pseudo_p ())
+    return mips16_gp_pseudo_reg ();
+
+  if (TARGET_USE_GOT)
+    /* The first post-reload split exposes all references to $gp
+       (both uses and definitions).  All references must remain
+       explicit after that point.
+
+       It is safe to introduce uses of $gp at any time, so for
+       simplicity, we do that before the split too.  */
+    mips_emit_move (temp, pic_offset_table_rtx);
+  else
+    emit_insn (gen_load_const_gp (temp));
+  return temp;
+}
+
+/* Create and return a GOT reference of type TYPE for address ADDR.
+   TEMP, if nonnull, is a scratch Pmode base register.  */
+
+rtx
+mips_got_load (rtx temp, rtx addr, enum mips_symbol_type type)
+{
+  rtx base, high, lo_sum_symbol;
+
+  base = mips_pic_base_register (temp);
+
+  /* If we used the temporary register to load $gp, we can't use
+     it for the high part as well.  */
+  if (temp != NULL && reg_overlap_mentioned_p (base, temp))
+    temp = NULL;
+
+  high = mips_unspec_offset_high (temp, base, addr, type);
+  lo_sum_symbol = mips_unspec_address (addr, type);
+
+  if (type == SYMBOL_GOTOFF_CALL)
+    return (Pmode == SImode
+	    ? gen_unspec_callsi (high, lo_sum_symbol)
+	    : gen_unspec_calldi (high, lo_sum_symbol));
+  else
+    return (Pmode == SImode
+	    ? gen_unspec_gotsi (high, lo_sum_symbol)
+	    : gen_unspec_gotdi (high, lo_sum_symbol));
+}
+
 /* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise
    it appears in a MEM of that mode.  Return true if ADDR is a legitimate
-   constant in that context and can be split into a high part and a LO_SUM.
-   If so, and if LO_SUM_OUT is nonnull, emit the high part and return
-   the LO_SUM in *LO_SUM_OUT.  Leave *LO_SUM_OUT unchanged otherwise.
+   constant in that context and can be split into high and low parts.
+   If so, and if LOW_OUT is nonnull, emit the high part and store the
+   low part in *LOW_OUT.  Leave *LOW_OUT unchanged otherwise.
 
    TEMP is as for mips_force_temporary and is used to load the high
-   part into a register.  */
+   part into a register.
+
+   When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be
+   a legitimize SET_SRC for an .md pattern, otherwise the low part
+   is guaranteed to be a legitimate address for mode MODE.  */
 
 bool
-mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *lo_sum_out)
+mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *low_out)
 {
   enum mips_symbol_context context;
   enum mips_symbol_type symbol_type;
@@ -2376,31 +2524,56 @@ mips_split_symbol (rtx temp, rtx addr, e
   context = (mode == MAX_MACHINE_MODE
 	     ? SYMBOL_CONTEXT_LEA
 	     : SYMBOL_CONTEXT_MEM);
-  if (!mips_symbolic_constant_p (addr, context, &symbol_type)
-      || mips_symbol_insns (symbol_type, mode) == 0
-      || !mips_split_p[symbol_type])
-    return false;
-
-  if (lo_sum_out)
+  if (GET_CODE (addr) == HIGH && context == SYMBOL_CONTEXT_LEA)
     {
-      if (symbol_type == SYMBOL_GP_RELATIVE)
+      addr = XEXP (addr, 0);
+      if (mips_symbolic_constant_p (addr, context, &symbol_type)
+	  && mips_symbol_insns (symbol_type, mode) > 0
+	  && mips_split_hi_p[symbol_type])
 	{
-	  if (!can_create_pseudo_p ())
-	    {
-	      emit_insn (gen_load_const_gp (temp));
-	      high = temp;
-	    }
-	  else
-	    high = mips16_gp_pseudo_reg ();
+	  if (low_out)
+	    switch (symbol_type)
+	      {
+	      case SYMBOL_GOT_PAGE_OFST:
+		/* The high part of a page/ofst pair is loaded from the GOT.  */
+		*low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_PAGE);
+		break;
+
+	      default:
+		gcc_unreachable ();
+	      }
+	  return true;
 	}
-      else
+    }
+  else
+    {
+      if (mips_symbolic_constant_p (addr, context, &symbol_type)
+	  && mips_symbol_insns (symbol_type, mode) > 0
+	  && mips_split_p[symbol_type])
 	{
-	  high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
-	  high = mips_force_temporary (temp, high);
+	  if (low_out)
+	    switch (symbol_type)
+	      {
+	      case SYMBOL_GOT_DISP:
+		/* SYMBOL_GOT_DISP symbols are loaded from the GOT.  */
+		*low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_DISP);
+		break;
+
+	      case SYMBOL_GP_RELATIVE:
+		high = mips_pic_base_register (temp);
+		*low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+		break;
+
+	      default:
+		high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+		high = mips_force_temporary (temp, high);
+		*low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+		break;
+	      }
+	  return true;
 	}
-      *lo_sum_out = gen_rtx_LO_SUM (Pmode, high, addr);
     }
-  return true;
+  return false;
 }
 
 /* Return a legitimate address for REG + OFFSET.  TEMP is as for
@@ -2457,7 +2630,8 @@ mips_call_tls_get_addr (rtx sym, enum mi
 
   emit_insn (gen_rtx_SET (Pmode, a0,
 			  gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
-  insn = mips_expand_call (v0, mips_tls_symbol, const0_rtx, const0_rtx, false);
+  insn = mips_expand_call (MIPS_CALL_NORMAL, v0, mips_tls_symbol,
+			   const0_rtx, NULL_RTX, false);
   RTL_CONST_CALL_P (insn) = 1;
   use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
   insn = get_insns ();
@@ -2716,7 +2890,8 @@ mips_rewrite_small_data_p (rtx x, enum m
 {
   enum mips_symbol_type symbol_type;
 
-  return (TARGET_EXPLICIT_RELOCS
+  return (mips_lo_relocs[SYMBOL_GP_RELATIVE]
+	  && !mips_split_p[SYMBOL_GP_RELATIVE]
 	  && mips_symbolic_constant_p (x, context, &symbol_type)
 	  && symbol_type == SYMBOL_GP_RELATIVE);
 }
@@ -5215,33 +5390,27 @@ mips_ok_for_lazy_binding_p (rtx x)
 {
   return (TARGET_USE_GOT
 	  && GET_CODE (x) == SYMBOL_REF
+	  && !SYMBOL_REF_BIND_NOW_P (x)
 	  && !mips_symbol_binds_local_p (x));
 }
 
-/* Load function address ADDR into register DEST.  SIBCALL_P is true
-   if the address is needed for a sibling call.  Return true if we
-   used an explicit lazy-binding sequence.  */
+/* Load function address ADDR into register DEST.  TYPE is as for
+   mips_expand_call.  Return true if we used an explicit lazy-binding
+   sequence.  */
 
 static bool
-mips_load_call_address (rtx dest, rtx addr, bool sibcall_p)
+mips_load_call_address (enum mips_call_type type, rtx dest, rtx addr)
 {
   /* If we're generating PIC, and this call is to a global function,
      try to allow its address to be resolved lazily.  This isn't
      possible for sibcalls when $gp is call-saved because the value
      of $gp on entry to the stub would be our caller's gp, not ours.  */
   if (TARGET_EXPLICIT_RELOCS
-      && !(sibcall_p && TARGET_CALL_SAVED_GP)
+      && !(type == MIPS_CALL_SIBCALL && TARGET_CALL_SAVED_GP)
       && mips_ok_for_lazy_binding_p (addr))
     {
-      rtx high, lo_sum_symbol;
-
-      high = mips_unspec_offset_high (dest, pic_offset_table_rtx,
-				      addr, SYMBOL_GOTOFF_CALL);
-      lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
-      if (Pmode == SImode)
-	emit_insn (gen_load_callsi (dest, high, lo_sum_symbol));
-      else
-	emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
+      addr = mips_got_load (dest, addr, SYMBOL_GOTOFF_CALL);
+      emit_insn (gen_rtx_SET (VOIDmode, dest, addr));
       return true;
     }
   else
@@ -5251,6 +5420,78 @@ mips_load_call_address (rtx dest, rtx ad
     }
 }
 
+/* Each locally-defined hard-float MIPS16 function has a local symbol
+   associated with it.  This hash table maps the function symbol (FUNC)
+   to the local symbol (LOCAL). */
+struct mips16_local_alias GTY(()) {
+  rtx func;
+  rtx local;
+};
+static GTY ((param_is (struct mips16_local_alias))) htab_t mips16_local_aliases;
+
+/* Hash table callbacks for mips16_local_aliases.  */
+
+static hashval_t
+mips16_local_aliases_hash (const void *entry)
+{
+  const struct mips16_local_alias *alias;
+
+  alias = (const struct mips16_local_alias *) entry;
+  return htab_hash_string (XSTR (alias->func, 0));
+}
+
+static int
+mips16_local_aliases_eq (const void *entry1, const void *entry2)
+{
+  const struct mips16_local_alias *alias1, *alias2;
+
+  alias1 = (const struct mips16_local_alias *) entry1;
+  alias2 = (const struct mips16_local_alias *) entry2;
+  return rtx_equal_p (alias1->func, alias2->func);
+}
+
+/* FUNC is the symbol for a locally-defined hard-float MIPS16 function.
+   Return a local alias for it, creating a new one if necessary.  */
+
+static rtx
+mips16_local_alias (rtx func)
+{
+  struct mips16_local_alias *alias, tmp_alias;
+  void **slot;
+
+  /* Create the hash table if this is the first call.  */
+  if (mips16_local_aliases == NULL)
+    mips16_local_aliases = htab_create_ggc (37, mips16_local_aliases_hash,
+					    mips16_local_aliases_eq, NULL);
+
+  /* Look up the function symbol, creating a new entry if need be.  */
+  tmp_alias.func = func;
+  slot = htab_find_slot (mips16_local_aliases, &tmp_alias, INSERT);
+  gcc_assert (slot != NULL);
+
+  alias = (struct mips16_local_alias *) *slot;
+  if (alias == NULL)
+    {
+      const char *func_name, *local_name;
+      rtx local;
+
+      /* Create a new SYMBOL_REF for the local symbol.  The choice of
+	 __fn_local_* is based on the __fn_stub_* names that we've
+	 traditionally used for the non-MIPS16 stub.  */
+      func_name = targetm.strip_name_encoding (XSTR (func, 0));
+      local_name = ACONCAT (("__fn_local_", func_name, NULL));
+      local = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (local_name));
+      SYMBOL_REF_FLAGS (local) = SYMBOL_REF_FLAGS (func) | SYMBOL_FLAG_LOCAL;
+
+      /* Create a new structure to represent the mapping.  */
+      alias = GGC_NEW (struct mips16_local_alias);
+      alias->func = func;
+      alias->local = local;
+      *slot = alias;
+    }
+  return alias->local;
+}
+
 /* A chained list of functions for which mips16_build_call_stub has already
    generated a stub.  NAME is the name of the function and FP_RET_P is true
    if the function returns a value in floating-point registers.  */
@@ -5261,6 +5502,18 @@ struct mips16_stub {
 };
 static struct mips16_stub *mips16_stubs;
 
+/* Return a SYMBOL_REF for a MIPS16 function called NAME.  */
+
+static rtx
+mips16_stub_function (const char *name)
+{
+  rtx x;
+
+  x = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+  SYMBOL_REF_FLAGS (x) |= (SYMBOL_FLAG_EXTERNAL | SYMBOL_FLAG_FUNCTION);
+  return x;
+}
+
 /* Return the two-character string that identifies floating-point
    return mode MODE in the name of a MIPS16 function stub.  */
 
@@ -5367,14 +5620,18 @@ mips_output_args_xfer (int fp_code, char
 static void
 mips16_build_function_stub (void)
 {
-  const char *fnname, *separator;
+  const char *fnname, *alias_name, *separator;
   char *secname, *stubname;
   tree stubdecl;
   unsigned int f;
+  rtx symbol, alias;
 
   /* Create the name of the stub, and its unique section.  */
-  fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
-  fnname = targetm.strip_name_encoding (fnname);
+  symbol = XEXP (DECL_RTL (current_function_decl), 0);
+  alias = mips16_local_alias (symbol);
+
+  fnname = targetm.strip_name_encoding (XSTR (symbol, 0));
+  alias_name = targetm.strip_name_encoding (XSTR (alias, 0));
   secname = ACONCAT ((".mips16.fn.", fnname, NULL));
   stubname = ACONCAT (("__fn_stub_", fnname, NULL));
 
@@ -5400,23 +5657,48 @@ mips16_build_function_stub (void)
   assemble_start_function (stubdecl, stubname);
   mips_start_function_definition (stubname, false);
 
+  /* If generating abicalls code, either set up the global pointer or
+     switch to absolute mode.  */
+  if (TARGET_ABSOLUTE_ABICALLS)
+    fprintf (asm_out_file, "\t.option\tpic0\n");
+  else if (TARGET_ABICALLS)
+    {
+      output_asm_insn ("%(.cpload\t%^%)", NULL);
+      /* Emit an R_MIPS_NONE relocation to tell the linker what the
+	 target function is.  Use a local GOT access when loading the
+	 symbol, to cut down on the number of unnecessary GOT entries
+	 for stubs that aren't needed.  */
+      output_asm_insn (".reloc\t0,R_MIPS_NONE,%0", &symbol);
+      symbol = alias;
+    }
+
   /* Load the address of the MIPS16 function into $at.  Do this first so
      that targets with coprocessor interlocks can use an MFC1 to fill the
      delay slot.  */
   fprintf (asm_out_file, "\t.set\tnoat\n");
-  fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]);
-  assemble_name (asm_out_file, fnname);
-  fprintf (asm_out_file, "\n");
+  output_asm_insn ("la\t%@,%0", &symbol);
 
   /* Move the arguments from floating-point registers to general registers.  */
   mips_output_args_xfer (crtl->args.info.fp_code, 'f');
 
   /* Jump to the MIPS16 function.  */
-  fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
+  output_asm_insn ("jr\t%@", NULL);
   fprintf (asm_out_file, "\t.set\tat\n");
 
+  if (TARGET_ABSOLUTE_ABICALLS)
+    fprintf (asm_out_file, "\t.option\tpic2\n");
+
   mips_end_function_definition (stubname);
 
+  /* If the linker needs to create a dynamic symbol for the target
+     function, it will associate the symbol with the stub (which,
+     unlike the target function, follows the proper calling conventions).
+     It is therefore useful to have a local alias for the target function,
+     so that it can still be identified as MIPS16 code.  As an optimization,
+     this symbol can also be used for indirect MIPS16 references from
+     within this file.  */
+  ASM_OUTPUT_DEF (asm_out_file, alias_name, fnname);
+
   switch_to_section (function_section (current_function_decl));
 }
 
@@ -5427,31 +5709,46 @@ mips16_build_function_stub (void)
 static void
 mips16_copy_fpr_return_value (void)
 {
-  rtx fn, insn, arg, call;
-  tree id, return_type;
+  rtx fn, insn, retval;
+  tree return_type;
   enum machine_mode return_mode;
+  const char *name;
 
   return_type = DECL_RESULT (current_function_decl);
   return_mode = DECL_MODE (return_type);
 
-  id = get_identifier (ACONCAT (("__mips16_ret_",
-				 mips16_call_stub_mode_suffix (return_mode),
-				 NULL)));
-  fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
-  arg = gen_rtx_REG (return_mode, GP_RETURN);
-  call = gen_call_value_internal (arg, fn, const0_rtx);
-  insn = mips_emit_call_insn (call, false);
-  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), arg);
+  name = ACONCAT (("__mips16_ret_",
+		   mips16_call_stub_mode_suffix (return_mode),
+		   NULL));
+  fn = mips16_stub_function (name);
+
+  /* The function takes arguments in $2 (and possibly $3), so calls
+     to it cannot be lazily bound.  */
+  SYMBOL_REF_FLAGS (fn) |= SYMBOL_FLAG_BIND_NOW;
+
+  /* Model the call as something that takes the GPR return value as
+     argument and returns an "updated" value.  */
+  retval = gen_rtx_REG (return_mode, GP_RETURN);
+  insn = mips_expand_call (MIPS_CALL_EPILOGUE, retval, fn,
+			   const0_rtx, NULL_RTX, false);
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
 }
 
-/* Consider building a stub for a MIPS16 call to function FN.
+/* Consider building a stub for a MIPS16 call to function *FN_PTR.
    RETVAL is the location of the return value, or null if this is
    a "call" rather than a "call_value".  ARGS_SIZE is the size of the
    arguments and FP_CODE is the code built by mips_function_arg;
    see the comment above CUMULATIVE_ARGS for details.
 
-   If a stub was needed, emit the call and return the call insn itself.
-   Return null otherwise.
+   There are three alternatives:
+
+   - If a stub was needed, emit the call and return the call insn itself.
+
+   - If we can avoid using a stub by redirecting the call, set *FN_PTR
+     to the new target and return null.
+
+   - If *FN_PTR doesn't need a stub, return null and leave *FN_PTR
+     unmodified.
 
    A stub is needed for calls to functions that, in normal mode,
    receive arguments in FPRs or return values in FPRs.  The stub
@@ -5460,17 +5757,18 @@ mips16_copy_fpr_return_value (void)
    return value from its hard-float position to its soft-float
    position.
 
-   We emit a JAL to FN even when FN might need a stub.  If FN turns out
-   to be to a non-MIPS16 function, the linker automatically redirects
-   the JAL to the stub, otherwise the JAL continues to call FN directly.  */
+   We can emit a JAL to *FN_PTR even when *FN_PTR might need a stub.
+   If *FN_PTR turns out to be to a non-MIPS16 function, the linker
+   automatically redirects the JAL to the stub, otherwise the JAL
+   continues to call FN directly.  */
 
 static rtx
-mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
+mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
 {
   const char *fnname;
   bool fp_ret_p;
   struct mips16_stub *l;
-  rtx insn;
+  rtx insn, fn;
 
   /* We don't need to do anything if we aren't in MIPS16 mode, or if
      we were invoked with the -msoft-float option.  */
@@ -5489,8 +5787,8 @@ mips16_build_call_stub (rtx retval, rtx 
 
   /* We don't need to do anything if this is a call to a special
      MIPS16 support function.  */
-  if (GET_CODE (fn) == SYMBOL_REF
-      && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
+  fn = *fn_ptr;
+  if (mips16_stub_function_p (fn))
     return NULL_RTX;
 
   /* This code will only work for o32 and o64 abis.  The other ABI's
@@ -5500,11 +5798,20 @@ mips16_build_call_stub (rtx retval, rtx 
   /* If we're calling via a function pointer, use one of the magic
      libgcc.a stubs provided for each (FP_CODE, FP_RET_P) combination.
      Each stub expects the function address to arrive in register $2.  */
-  if (GET_CODE (fn) != SYMBOL_REF)
+  if (GET_CODE (fn) != SYMBOL_REF
+      || !call_insn_operand (fn, VOIDmode))
     {
       char buf[30];
-      tree id;
-      rtx stub_fn, insn;
+      rtx stub_fn, insn, addr;
+      bool lazy_p;
+
+      /* If this is a locally-defined and locally-binding function,
+	 avoid the stub by calling the local alias directly.  */
+      if (mips16_local_function_p (fn))
+	{
+	  *fn_ptr = mips16_local_alias (fn);
+	  return NULL_RTX;
+	}
 
       /* Create a SYMBOL_REF for the libgcc.a function.  */
       if (fp_ret_p)
@@ -5513,24 +5820,22 @@ mips16_build_call_stub (rtx retval, rtx 
 		 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));
+      stub_fn = mips16_stub_function (buf);
+
+      /* The function uses $2 as an argument, so calls to it
+	 cannot be lazily bound.  */
+      SYMBOL_REF_FLAGS (stub_fn) |= SYMBOL_FLAG_BIND_NOW;
 
       /* Load the target function into $2.  */
-      mips_emit_move (gen_rtx_REG (Pmode, 2), fn);
+      addr = gen_rtx_REG (Pmode, GP_REG_FIRST + 2);
+      lazy_p = mips_load_call_address (MIPS_CALL_NORMAL, addr, fn);
 
       /* Emit the call.  */
-      if (retval == NULL_RTX)
-	insn = gen_call_internal (stub_fn, args_size);
-      else
-	insn = gen_call_value_internal (retval, stub_fn, args_size);
-      insn = mips_emit_call_insn (insn, false);
+      insn = mips_expand_call (MIPS_CALL_NORMAL, retval, stub_fn,
+			       args_size, NULL_RTX, lazy_p);
 
       /* Tell GCC that this call does indeed use the value of $2.  */
-      CALL_INSN_FUNCTION_USAGE (insn) =
-	gen_rtx_EXPR_LIST (VOIDmode,
-			   gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 2)),
-			   CALL_INSN_FUNCTION_USAGE (insn));
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), addr);
 
       /* If we are handling a floating-point return value, we need to
          save $18 in the function prologue.  Putting a note on the
@@ -5540,8 +5845,8 @@ mips16_build_call_stub (rtx retval, rtx 
       if (fp_ret_p)
 	CALL_INSN_FUNCTION_USAGE (insn) =
 	  gen_rtx_EXPR_LIST (VOIDmode,
-			     gen_rtx_USE (VOIDmode,
-					  gen_rtx_REG (word_mode, 18)),
+			     gen_rtx_CLOBBER (VOIDmode,
+					      gen_rtx_REG (word_mode, 18)),
 			     CALL_INSN_FUNCTION_USAGE (insn));
 
       return insn;
@@ -5605,8 +5910,13 @@ mips16_build_call_stub (rtx retval, rtx 
 	     first so that targets with coprocessor interlocks can use
 	     an MFC1 to fill the delay slot.  */
 	  fprintf (asm_out_file, "\t.set\tnoat\n");
-	  fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1],
-		   fnname);
+	  if (TARGET_EXPLICIT_RELOCS)
+	    {
+	      output_asm_insn ("lui\t%^,%%hi(%0)", &fn);
+	      output_asm_insn ("addiu\t%^,%^,%%lo(%0)", &fn);
+	    }
+	  else
+	    output_asm_insn ("la\t%^,%0", &fn);
 	}
 
       /* Move the arguments from general registers to floating-point
@@ -5616,7 +5926,7 @@ mips16_build_call_stub (rtx retval, rtx 
       if (!fp_ret_p)
 	{
 	  /* Jump to the previously-loaded address.  */
-	  fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
+	  output_asm_insn ("jr\t%^", NULL);
 	  fprintf (asm_out_file, "\t.set\tat\n");
 	}
       else
@@ -5626,7 +5936,7 @@ mips16_build_call_stub (rtx retval, rtx 
 	     $18 is usually a call-saved register.  */
 	  fprintf (asm_out_file, "\tmove\t%s,%s\n",
 		   reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
-	  fprintf (asm_out_file, "\tjal\t%s\n", fnname);
+	  output_asm_insn (MIPS_CALL ("jal", &fn, 0), &fn);
 
 	  /* Move the result from floating-point registers to
 	     general registers.  */
@@ -5696,7 +6006,7 @@ mips16_build_call_stub (rtx retval, rtx 
     insn = gen_call_internal_direct (fn, args_size);
   else
     insn = gen_call_value_internal_direct (retval, fn, args_size);
-  insn = mips_emit_call_insn (insn, false);
+  insn = mips_emit_call_insn (insn, fn, fn, false);
 
   /* If we are calling a stub which handles a floating-point return
      value, we need to arrange to save $18 in the prologue.  We do this
@@ -5705,70 +6015,114 @@ mips16_build_call_stub (rtx retval, rtx 
   if (fp_ret_p)
     CALL_INSN_FUNCTION_USAGE (insn) =
       gen_rtx_EXPR_LIST (VOIDmode,
-			 gen_rtx_USE (VOIDmode, gen_rtx_REG (word_mode, 18)),
+			 gen_rtx_CLOBBER (VOIDmode,
+					  gen_rtx_REG (word_mode, 18)),
 			 CALL_INSN_FUNCTION_USAGE (insn));
 
   return insn;
 }
 
-/* Expand a "call", "sibcall", "call_value" or "sibcall_value" instruction.
-   RESULT is where the result will go (null for "call"s and "sibcall"s),
-   ADDR is the address of the function, ARGS_SIZE is the size of the
-   arguments and AUX is the value passed to us by mips_function_arg.
-   SIBCALL_P is true if we are expanding a sibling call, false if we're
-   expanding a normal call.
+/* Expand a call of type TYPE.  RESULT is where the result will go (null
+   for "call"s and "sibcall"s), ADDR is the address of the function,
+   ARGS_SIZE is the size of the arguments and AUX is the value passed
+   to us by mips_function_arg.  LAZY_P is true if this call already
+   involves a lazily-bound function address (such as when calling
+   functions through a MIPS16 hard-float stub).
 
    Return the call itself.  */
 
 rtx
-mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, bool sibcall_p)
+mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
+		  rtx args_size, rtx aux, bool lazy_p)
 {
   rtx orig_addr, pattern, insn;
-  bool lazy_p;
+  int fp_code;
 
+  fp_code = aux == 0 ? 0 : (int) GET_MODE (aux);
+  insn = mips16_build_call_stub (result, &addr, args_size, fp_code);
+  if (insn)
+    {
+      gcc_assert (!lazy_p && type == MIPS_CALL_NORMAL);
+      return insn;
+    }
+				 ;
   orig_addr = addr;
-  lazy_p = false;
   if (!call_insn_operand (addr, VOIDmode))
     {
-      addr = gen_reg_rtx (Pmode);
-      lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p);
+      if (type == MIPS_CALL_EPILOGUE)
+	addr = MIPS_EPILOGUE_TEMP (Pmode);
+      else
+	addr = gen_reg_rtx (Pmode);
+      lazy_p |= mips_load_call_address (type, addr, orig_addr);
     }
 
-  insn = mips16_build_call_stub (result, addr, args_size,
-				 aux == 0 ? 0 : (int) GET_MODE (aux));
-  if (insn)
+  if (result == 0)
     {
-      gcc_assert (!sibcall_p && !lazy_p);
-      return insn;
-    }
+      rtx (*fn) (rtx, rtx);
 
-  if (result == 0)
-    pattern = (sibcall_p
-	       ? gen_sibcall_internal (addr, args_size)
-	       : gen_call_internal (addr, args_size));
+      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+	fn = gen_call_split;
+      else if (type == MIPS_CALL_SIBCALL)
+	fn = gen_sibcall_internal;
+      else
+	fn = gen_call_internal;
+
+      pattern = fn (addr, args_size);
+    }
   else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
     {
       /* Handle return values created by mips_return_fpr_pair.  */
+      rtx (*fn) (rtx, rtx, rtx, rtx);
       rtx reg1, reg2;
 
+      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+	fn = gen_call_value_multiple_split;
+      else if (type == MIPS_CALL_SIBCALL)
+	fn = gen_sibcall_value_multiple_internal;
+      else
+	fn = gen_call_value_multiple_internal;
+
       reg1 = XEXP (XVECEXP (result, 0, 0), 0);
       reg2 = XEXP (XVECEXP (result, 0, 1), 0);
-      pattern =
-	(sibcall_p
-	 ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2)
-	 : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
+      pattern = fn (reg1, addr, args_size, reg2);
     }
   else
     {
+      rtx (*fn) (rtx, rtx, rtx);
+
+      if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+	fn = gen_call_value_split;
+      else if (type == MIPS_CALL_SIBCALL)
+	fn = gen_sibcall_value_internal;
+      else
+	fn = gen_call_value_internal;
+
       /* Handle return values created by mips_return_fpr_single.  */
       if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
 	result = XEXP (XVECEXP (result, 0, 0), 0);
-      pattern = (sibcall_p
-		 ? gen_sibcall_value_internal (result, addr, args_size)
-		 : gen_call_value_internal (result, addr, args_size));
+      pattern = fn (result, addr, args_size);
     }
 
-  return mips_emit_call_insn (pattern, lazy_p);
+  return mips_emit_call_insn (pattern, orig_addr, addr, lazy_p);
+}
+
+/* Split call instruction INSN into a $gp-clobbering call and
+   (where necessary) an instruction to restore $gp from its save slot.
+   CALL_PATTERN is the pattern of the new call.  */
+
+void
+mips_split_call (rtx insn, rtx call_pattern)
+{
+  rtx new_insn;
+
+  new_insn = emit_call_insn (call_pattern);
+  CALL_INSN_FUNCTION_USAGE (new_insn)
+    = copy_rtx (CALL_INSN_FUNCTION_USAGE (insn));
+  if (!find_reg_note (insn, REG_NORETURN, 0))
+    /* Pick a temporary register that is suitable for both MIPS16 and
+       non-MIPS16 code.  $4 and $5 are used for returning complex double
+       values in soft-float code, so $6 is the first suitable candidate.  */
+    mips_restore_gp (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
 }
 
 /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL.  */
@@ -6315,6 +6669,7 @@ mips_split_addresses_p (void)
 mips_init_relocs (void)
 {
   memset (mips_split_p, '\0', sizeof (mips_split_p));
+  memset (mips_split_hi_p, '\0', sizeof (mips_split_hi_p));
   memset (mips_hi_relocs, '\0', sizeof (mips_hi_relocs));
   memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
 
@@ -6356,13 +6711,13 @@ mips_init_relocs (void)
       mips_split_p[SYMBOL_GP_RELATIVE] = true;
       mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
     }
+  else if (TARGET_EXPLICIT_RELOCS)
+    /* Small data constants are kept whole until after reload,
+       then lowered by mips_rewrite_small_data.  */
+    mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
 
   if (TARGET_EXPLICIT_RELOCS)
     {
-      /* Small data constants are kept whole until after reload,
-	 then lowered by mips_rewrite_small_data.  */
-      mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
-
       mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
       if (TARGET_NEWABI)
 	{
@@ -6374,6 +6729,9 @@ mips_init_relocs (void)
 	  mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
 	  mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
 	}
+      if (TARGET_MIPS16)
+	/* Expose the use of $28 as soon as possible.  */
+	mips_split_hi_p[SYMBOL_GOT_PAGE_OFST] = true;
 
       if (TARGET_XGOT)
 	{
@@ -6395,6 +6753,9 @@ mips_init_relocs (void)
 	  else
 	    mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
 	  mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+	  if (TARGET_MIPS16)
+	    /* Expose the use of $28 as soon as possible.  */
+	    mips_split_p[SYMBOL_GOT_DISP] = true;
 	}
     }
 
@@ -7939,6 +8300,7 @@ mips_global_pointer (void)
      but no instruction will yet refer to it.  */
   if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
       && !crtl->uses_const_pool
+      && !mips16_cfun_returns_in_fpr_p ()
       && !mips_function_has_gp_insn ())
     return 0;
 
@@ -8261,6 +8623,11 @@ mips_extra_live_on_entry (bitmap regs)
       if (!TARGET_ABSOLUTE_ABICALLS)
 	bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
 
+      /* The prologue may set MIPS16_PIC_TEMP_REGNUM to the value of
+	 the global pointer.  */
+      if (TARGET_MIPS16)
+	bitmap_set_bit (regs, MIPS16_PIC_TEMP_REGNUM);
+
       /* See the comment above load_call<mode> for details.  */
       bitmap_set_bit (regs, GOT_VERSION_REGNUM);
     }
@@ -8293,20 +8660,45 @@ mips_set_return_address (rtx address, rt
   mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
 }
 
-/* Restore $gp from its save slot.  Valid only when using o32 or
-   o64 abicalls.  */
+/* Return a MEM rtx for the cprestore slot, using TEMP as a temporary base
+   register if need be.  */
 
-void
-mips_restore_gp (void)
+static rtx
+mips_cprestore_slot (rtx temp)
 {
-  rtx base, address;
+  const struct mips_frame_info *frame;
+  rtx base;
+  HOST_WIDE_INT offset;
+
+  frame = &cfun->machine->frame;
+  if (frame_pointer_needed)
+    {
+      base = hard_frame_pointer_rtx;
+      offset = frame->args_size - frame->hard_frame_pointer_offset;
+    }
+  else
+    {
+      base = stack_pointer_rtx;
+      offset = frame->args_size;
+    }
+  return gen_frame_mem (Pmode, mips_add_offset (temp, base, offset));
+}
+
+/* Restore $gp from its save slot, using TEMP as a temporary base register
+   if need be.  This function is for o32 and o64 abicalls only.  */
 
+void
+mips_restore_gp (rtx temp)
+{
   gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
 
-  base = frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx;
-  address = mips_add_offset (pic_offset_table_rtx, base,
-			     crtl->outgoing_args_size);
-  mips_emit_move (pic_offset_table_rtx, gen_frame_mem (Pmode, address));
+  if (TARGET_MIPS16)
+    {
+      mips_emit_move (temp, mips_cprestore_slot (temp));
+      mips_emit_move (pic_offset_table_rtx, temp);
+    }
+  else
+    mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp));
   if (!TARGET_EXPLICIT_RELOCS)
     emit_insn (gen_blockage ());
 }
@@ -8448,8 +8840,18 @@ mips_output_function_prologue (FILE *fil
      that need it.  */
   if (mips_current_loadgp_style () == LOADGP_OLDABI)
     {
+      if (TARGET_MIPS16)
+	{
+	  /* This is a fixed-form sequence.  The position of the
+	     first two instructions is important because of the
+	     way _gp_disp is defined.  */
+	  output_asm_insn ("li\t$2,%%hi(_gp_disp)", 0);
+	  output_asm_insn ("addiu\t$3,$pc,%%lo(_gp_disp)", 0);
+	  output_asm_insn ("sll\t$2,16", 0);
+	  output_asm_insn ("addu\t$2,$3", 0);
+	}
       /* .cpload must be in a .set noreorder but not a .set nomacro block.  */
-      if (!cfun->machine->all_noreorder_p)
+      else if (!cfun->machine->all_noreorder_p)
 	output_asm_insn ("%(.cpload\t%^%)", 0);
       else
 	output_asm_insn ("%(.cpload\t%^\n\t%<", 0);
@@ -8541,7 +8943,7 @@ mips_emit_loadgp (void)
 {
   rtx addr, offset, incoming_address, base, index, pic_reg;
 
-  pic_reg = pic_offset_table_rtx;
+  pic_reg = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
   switch (mips_current_loadgp_style ())
     {
     case LOADGP_ABSOLUTE:
@@ -8555,6 +8957,10 @@ mips_emit_loadgp (void)
 		 : gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp));
       break;
 
+    case LOADGP_OLDABI:
+      /* Added by mips_output_function_prologue.  */
+      break;
+
     case LOADGP_NEWABI:
       addr = XEXP (DECL_RTL (current_function_decl), 0);
       offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
@@ -8575,6 +8981,10 @@ mips_emit_loadgp (void)
     default:
       return;
     }
+
+  if (TARGET_MIPS16)
+    emit_insn (gen_copygp_mips16 (pic_offset_table_rtx, pic_reg));
+
   /* Emit a blockage if there are implicit uses of the GP register.
      This includes profiled functions, because FUNCTION_PROFILE uses
      a jal macro.  */
@@ -8710,7 +9120,13 @@ mips_expand_prologue (void)
 
   /* Initialize the $gp save slot.  */
   if (frame->cprestore_size > 0)
-    emit_insn (gen_cprestore (GEN_INT (crtl->outgoing_args_size)));
+    {
+      if (TARGET_MIPS16)
+	mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+			MIPS16_PIC_TEMP);
+      else
+	emit_insn (gen_cprestore (GEN_INT (frame->args_size)));
+    }
 
   /* If we are profiling, make sure no instructions are scheduled before
      the call to mcount.  */
@@ -11462,6 +11878,7 @@ mips16_lay_out_constants (void)
   if (!TARGET_MIPS16_PCREL_LOADS)
     return;
 
+  split_all_insns_noflow ();
   barrier = 0;
   memset (&pool, 0, sizeof (pool));
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
@@ -12096,6 +12513,11 @@ mips_reorg_process_insns (void)
 
   cfun->machine->all_noreorder_p = true;
 
+  /* We don't track MIPS16 PC-relative offsets closely enough to make
+     a good job of "set .noreorder" code in MIPS16 mode.  */
+  if (TARGET_MIPS16)
+    cfun->machine->all_noreorder_p = false;
+
   /* Code that doesn't use explicit relocs can't be ".set nomacro".  */
   if (!TARGET_EXPLICIT_RELOCS)
     cfun->machine->all_noreorder_p = false;
@@ -12185,7 +12607,10 @@ mips_reorg (void)
   if (mips_base_delayed_branch)
     dbr_schedule (get_insns ());
   mips_reorg_process_insns ();
-  if (TARGET_EXPLICIT_RELOCS && TUNE_MIPS4130 && TARGET_VR4130_ALIGN)
+  if (!TARGET_MIPS16
+      && TARGET_EXPLICIT_RELOCS
+      && TUNE_MIPS4130
+      && TARGET_VR4130_ALIGN)
     vr4130_align_insns ();
 }
 
@@ -12212,24 +12637,19 @@ mips_output_mi_thunk (FILE *file, tree t
 		   && const_call_insn_operand (fnaddr, Pmode));
 
   /* Determine if we need to load FNADDR from the GOT.  */
-  if (!use_sibcall_p)
-    switch (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))
-      {
-      case SYMBOL_GOT_PAGE_OFST:
-      case SYMBOL_GOT_DISP:
-	/* Pick a global pointer.  Use a call-clobbered register if
-	   TARGET_CALL_SAVED_GP.  */
-	cfun->machine->global_pointer =
-	  TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
-	SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+  if (!use_sibcall_p
+      && (mips_got_symbol_type_p
+	  (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))))
+    {
+      /* Pick a global pointer.  Use a call-clobbered register if
+	 TARGET_CALL_SAVED_GP.  */
+      cfun->machine->global_pointer
+	= TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+      SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
 
-	/* Set up the global pointer for n32 or n64 abicalls.  */
-	mips_emit_loadgp ();
-	break;
-
-      default:
-	break;
-      }
+      /* Set up the global pointer for n32 or n64 abicalls.  */
+      mips_emit_loadgp ();
+    }
 
   /* We need two temporary registers in some cases.  */
   temp1 = gen_rtx_REG (Pmode, 2);
@@ -12288,12 +12708,17 @@ mips_output_mi_thunk (FILE *file, tree t
 	 We must therefore load the address via a temporary
 	 register if mips_dangerous_for_la25_p.
 
-	 If we jump to the temporary register rather than $25, the assembler
-	 can use the move insn to fill the jump's delay slot.  */
+	 If we jump to the temporary register rather than $25,
+	 the assembler can use the move insn to fill the jump's
+	 delay slot.
+
+	 We can use the same technique for MIPS16 code, where $25
+	 is not a valid JR register.  */
       if (TARGET_USE_PIC_FN_ADDR_REG
+	  && !TARGET_MIPS16
 	  && !mips_dangerous_for_la25_p (fnaddr))
 	temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
-      mips_load_call_address (temp1, fnaddr, true);
+      mips_load_call_address (MIPS_CALL_SIBCALL, temp1, fnaddr);
 
       if (TARGET_USE_PIC_FN_ADDR_REG
 	  && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
@@ -12367,11 +12792,7 @@ mips_set_mips16_mode (int mips16_p)
 	 call.  */
       flag_move_loop_invariants = 0;
 
-      /* Silently disable -mexplicit-relocs since it doesn't apply
-	 to MIPS16 code.  Even so, it would overly pedantic to warn
-	 about "-mips16 -mexplicit-relocs", especially given that
-	 we use a %gprel() operator.  */
-      target_flags &= ~MASK_EXPLICIT_RELOCS;
+      target_flags |= MASK_EXPLICIT_RELOCS;
 
       /* Experiments suggest we get the best overall section-anchor
 	 results from using the range of an unextended LW or SW.  Code
@@ -12381,8 +12802,11 @@ mips_set_mips16_mode (int mips16_p)
       targetm.min_anchor_offset = 0;
       targetm.max_anchor_offset = 127;
 
-      if (flag_pic || TARGET_ABICALLS)
-	sorry ("MIPS16 PIC");
+      if (flag_pic && !TARGET_OLDABI)
+	sorry ("MIPS16 PIC for ABIs other than o32 and o64");
+
+      if (TARGET_XGOT)
+	sorry ("MIPS16 -mxgot code");
 
       if (TARGET_HARD_FLOAT_ABI && !TARGET_OLDABI)
 	sorry ("hard-float MIPS16 code for ABIs other than o32 and o64");
Index: gcc/config/mips/mips.md
===================================================================
--- gcc/config/mips/mips.md	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config/mips/mips.md	2008-08-09 19:22:20.000000000 +0100
@@ -66,6 +66,7 @@ (define_constants
    (UNSPEC_MEMORY_BARRIER	45)
    (UNSPEC_SET_GOT_VERSION	46)
    (UNSPEC_UPDATE_GOT_VERSION	47)
+   (UNSPEC_COPYGP		48)
    
    (UNSPEC_ADDRESS_FIRST	100)
 
@@ -478,7 +479,9 @@ (define_attr "length" ""
 	  (const_int 0)
 
 	  (eq_attr "got" "load")
-	  (const_int 4)
+	  (if_then_else (ne (symbol_ref "TARGET_MIPS16") (const_int 0))
+			(const_int 8)
+			(const_int 4))
 	  (eq_attr "got" "xgot_high")
 	  (const_int 8)
 
@@ -3590,15 +3593,11 @@ (define_insn_and_split "*xgot_lo<mode>"
 (define_insn_and_split "*got_disp<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
 	(match_operand:P 1 "got_disp_operand" ""))]
-  "TARGET_EXPLICIT_RELOCS && !TARGET_XGOT"
+  "TARGET_EXPLICIT_RELOCS && !mips_split_p[SYMBOL_GOT_DISP]"
   "#"
   "&& reload_completed"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 2) (match_dup 3)] UNSPEC_LOAD_GOT))]
-{
-  operands[2] = pic_offset_table_rtx;
-  operands[3] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_DISP);
-}
+  [(set (match_dup 0) (match_dup 2))]
+  { operands[2] = mips_got_load (NULL, operands[1], SYMBOL_GOTOFF_DISP); }
   [(set_attr "got" "load")
    (set_attr "mode" "<MODE>")])
 
@@ -3607,18 +3606,19 @@ (define_insn_and_split "*got_disp<mode>"
 (define_insn_and_split "*got_page<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
 	(high:P (match_operand:P 1 "got_page_ofst_operand" "")))]
-  "TARGET_EXPLICIT_RELOCS"
+  "TARGET_EXPLICIT_RELOCS && !mips_split_hi_p[SYMBOL_GOT_PAGE_OFST]"
   "#"
   "&& reload_completed"
-  [(set (match_dup 0)
-	(unspec:P [(match_dup 2) (match_dup 3)] UNSPEC_LOAD_GOT))]
-{
-  operands[2] = pic_offset_table_rtx;
-  operands[3] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_PAGE);
-}
+  [(set (match_dup 0) (match_dup 2))]
+  { operands[2] = mips_got_load (NULL, operands[1], SYMBOL_GOTOFF_PAGE); }
   [(set_attr "got" "load")
    (set_attr "mode" "<MODE>")])
 
+;; Convenience expander that generates the rhs of a load_got<mode> insn.
+(define_expand "unspec_got<mode>"
+  [(unspec:P [(match_operand:P 0)
+	      (match_operand:P 1)] UNSPEC_LOAD_GOT)])
+
 ;; Lower-level instructions for loading an address from the GOT.
 ;; We could use MEMs, but an unspec gives more optimization
 ;; opportunities.
@@ -3630,9 +3630,8 @@ (define_insn "load_got<mode>"
 		  UNSPEC_LOAD_GOT))]
   ""
   "<load>\t%0,%R2(%1)"
-  [(set_attr "type" "load")
-   (set_attr "mode" "<MODE>")
-   (set_attr "length" "4")])
+  [(set_attr "got" "load")
+   (set_attr "mode" "<MODE>")])
 
 ;; Instructions for adding the low 16 bits of an address to a register.
 ;; Operand 2 is the address: mips_print_operand works out which relocation
@@ -3657,6 +3656,15 @@ (define_insn "*low<mode>_mips16"
    (set_attr "mode" "<MODE>")
    (set_attr "extended_mips16" "yes")])
 
+;; Expose MIPS16 uses of the global pointer after reload if the function
+;; is responsible for setting up the register itself.
+(define_split
+  [(set (match_operand:GPR 0 "d_operand")
+	(const:GPR (unspec:GPR [(const_int 0)] UNSPEC_GP)))]
+  "TARGET_MIPS16 && TARGET_USE_GOT && reload_completed"
+  [(set (match_dup 0) (match_dup 1))]
+  { operands[1] = pic_offset_table_rtx; })
+
 ;; Allow combine to split complex const_int load sequences, using operand 2
 ;; to store the intermediate results.  See move_operand for details.
 (define_split
@@ -4521,6 +4529,18 @@ (define_insn_and_split "loadgp_rtp_<mode
 }
   [(set_attr "length" "12")])
 
+;; Initialize the global pointer for MIPS16 code.  Operand 0 is the
+;; global pointer and operand 1 is the MIPS16 register that holds
+;; the required value.
+(define_insn_and_split "copygp_mips16"
+  [(set (match_operand:SI 0 "register_operand" "=y")
+	(unspec_volatile:SI [(match_operand:SI 1 "register_operand" "d")]
+			    UNSPEC_COPYGP))]
+  "TARGET_MIPS16"
+  "#"
+  "&& reload_completed"
+  [(set (match_dup 0) (match_dup 1))])
+
 ;; Emit a .cprestore directive, which normally expands to a single store
 ;; instruction.  Note that we continue to use .cprestore for explicit reloc
 ;; code so that jals inside inline asms will work correctly.
@@ -5981,16 +6001,17 @@ (define_expand "nonlocal_goto_receiver"
 ;; volatile until all uses of $28 are exposed.
 (define_insn_and_split "restore_gp"
   [(set (reg:SI 28)
-	(unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))]
+	(unspec_volatile:SI [(const_int 0)] UNSPEC_RESTORE_GP))
+   (clobber (match_scratch:SI 0 "=&d"))]
   "TARGET_CALL_CLOBBERED_GP"
   "#"
   "&& reload_completed"
   [(const_int 0)]
 {
-  mips_restore_gp ();
+  mips_restore_gp (operands[0]);
   DONE;
 }
-  [(set_attr "type"   "load")
+  [(set_attr "type" "load")
    (set_attr "length" "12")])
 
 ;;
@@ -6043,16 +6064,22 @@ (define_insn_and_split "restore_gp"
 ;;    - Leave GOT_VERSION_REGNUM out of all register classes.
 ;;	The register is therefore not a valid register_operand
 ;;	and cannot be moved to or from other registers.
+
+;; Convenience expander that generates the rhs of a load_call<mode> insn.
+(define_expand "unspec_call<mode>"
+  [(unspec:P [(match_operand:P 0)
+	      (match_operand:P 1)
+	      (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL)])
+
 (define_insn "load_call<mode>"
   [(set (match_operand:P 0 "register_operand" "=d")
-	(unspec:P [(match_operand:P 1 "register_operand" "r")
+	(unspec:P [(match_operand:P 1 "register_operand" "d")
 		   (match_operand:P 2 "immediate_operand" "")
 		   (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
   "TARGET_USE_GOT"
   "<load>\t%0,%R2(%1)"
-  [(set_attr "type" "load")
-   (set_attr "mode" "<MODE>")
-   (set_attr "length" "4")])
+  [(set_attr "got" "load")
+   (set_attr "mode" "<MODE>")])
 
 (define_insn "set_got_version"
   [(set (reg:SI GOT_VERSION_REGNUM)
@@ -6088,7 +6115,8 @@ (define_expand "sibcall"
 	      (use (match_operand 3 ""))])]	;; struct_value_size_rtx
   "TARGET_SIBCALLS"
 {
-  mips_expand_call (0, XEXP (operands[0], 0), operands[1], operands[2], true);
+  mips_expand_call (MIPS_CALL_SIBCALL, NULL_RTX, XEXP (operands[0], 0),
+		    operands[1], operands[2], false);
   DONE;
 })
 
@@ -6106,8 +6134,8 @@ (define_expand "sibcall_value"
 	      (use (match_operand 3 ""))])]		;; next_arg_reg
   "TARGET_SIBCALLS"
 {
-  mips_expand_call (operands[0], XEXP (operands[1], 0),
-		    operands[2], operands[3], true);
+  mips_expand_call (MIPS_CALL_SIBCALL, operands[0], XEXP (operands[1], 0),
+		    operands[2], operands[3], false);
   DONE;
 })
 
@@ -6137,7 +6165,8 @@ (define_expand "call"
 	      (use (match_operand 3 ""))])]	;; struct_value_size_rtx
   ""
 {
-  mips_expand_call (0, XEXP (operands[0], 0), operands[1], operands[2], false);
+  mips_expand_call (MIPS_CALL_NORMAL, NULL_RTX, XEXP (operands[0], 0),
+		    operands[1], operands[2], false);
   DONE;
 })
 
@@ -6187,28 +6216,44 @@ (define_insn_and_split "call_internal"
   "reload_completed && TARGET_SPLIT_CALLS && (operands[2] = insn)"
   [(const_int 0)]
 {
-  emit_call_insn (gen_call_split (operands[0], operands[1]));
-  if (!find_reg_note (operands[2], REG_NORETURN, 0))
-    mips_restore_gp ();
+  mips_split_call (operands[2], gen_call_split (operands[0], operands[1]));
   DONE;
 }
   [(set_attr "jal" "indirect,direct")])
 
+(define_insn "call_split"
+  [(call (mem:SI (match_operand 0 "call_insn_operand" "cS"))
+	 (match_operand 1 "" ""))
+   (clobber (reg:SI 31))
+   (clobber (reg:SI 28))]
+  "TARGET_SPLIT_CALLS"
+  { return MIPS_CALL ("jal", operands, 0); }
+  [(set_attr "type" "call")])
+
 ;; A pattern for calls that must be made directly.  It is used for
 ;; MIPS16 calls that the linker may need to redirect to a hard-float
 ;; stub; the linker relies on the call relocation type to detect when
 ;; such redirection is needed.
-(define_insn "call_internal_direct"
+(define_insn_and_split "call_internal_direct"
   [(call (mem:SI (match_operand 0 "const_call_insn_operand"))
 	 (match_operand 1))
    (const_int 1)
    (clobber (reg:SI 31))]
   ""
-  { return MIPS_CALL ("jal", operands, 0); })
+  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 0); }
+  "reload_completed && TARGET_SPLIT_CALLS && (operands[2] = insn)"
+  [(const_int 0)]
+{
+  mips_split_call (operands[2],
+		   gen_call_direct_split (operands[0], operands[1]));
+  DONE;
+}
+  [(set_attr "type" "call")])
 
-(define_insn "call_split"
-  [(call (mem:SI (match_operand 0 "call_insn_operand" "cS"))
-	 (match_operand 1 "" ""))
+(define_insn "call_direct_split"
+  [(call (mem:SI (match_operand 0 "const_call_insn_operand"))
+	 (match_operand 1))
+   (const_int 1)
    (clobber (reg:SI 31))
    (clobber (reg:SI 28))]
   "TARGET_SPLIT_CALLS"
@@ -6222,7 +6267,7 @@ (define_expand "call_value"
 	      (use (match_operand 3 ""))])]		;; next_arg_reg
   ""
 {
-  mips_expand_call (operands[0], XEXP (operands[1], 0),
+  mips_expand_call (MIPS_CALL_NORMAL, operands[0], XEXP (operands[1], 0),
 		    operands[2], operands[3], false);
   DONE;
 })
@@ -6238,10 +6283,9 @@ (define_insn_and_split "call_value_inter
   "reload_completed && TARGET_SPLIT_CALLS && (operands[3] = insn)"
   [(const_int 0)]
 {
-  emit_call_insn (gen_call_value_split (operands[0], operands[1],
-					operands[2]));
-  if (!find_reg_note (operands[3], REG_NORETURN, 0))
-    mips_restore_gp ();
+  mips_split_call (operands[3],
+		   gen_call_value_split (operands[0], operands[1],
+					 operands[2]));
   DONE;
 }
   [(set_attr "jal" "indirect,direct")])
@@ -6257,14 +6301,34 @@ (define_insn "call_value_split"
   [(set_attr "type" "call")])
 
 ;; See call_internal_direct.
-(define_insn "call_value_internal_direct"
+(define_insn_and_split "call_value_internal_direct"
   [(set (match_operand 0 "register_operand")
         (call (mem:SI (match_operand 1 "const_call_insn_operand"))
               (match_operand 2)))
    (const_int 1)
    (clobber (reg:SI 31))]
   ""
-  { return MIPS_CALL ("jal", operands, 1); })
+  { return TARGET_SPLIT_CALLS ? "#" : MIPS_CALL ("jal", operands, 1); }
+  "reload_completed && TARGET_SPLIT_CALLS && (operands[3] = insn)"
+  [(const_int 0)]
+{
+  mips_split_call (operands[3],
+		   gen_call_value_direct_split (operands[0], operands[1],
+						operands[2]));
+  DONE;
+}
+  [(set_attr "type" "call")])
+
+(define_insn "call_value_direct_split"
+  [(set (match_operand 0 "register_operand")
+        (call (mem:SI (match_operand 1 "const_call_insn_operand"))
+              (match_operand 2)))
+   (const_int 1)
+   (clobber (reg:SI 31))
+   (clobber (reg:SI 28))]
+  "TARGET_SPLIT_CALLS"
+  { return MIPS_CALL ("jal", operands, 1); }
+  [(set_attr "type" "call")])
 
 ;; See comment for call_internal.
 (define_insn_and_split "call_value_multiple_internal"
@@ -6280,10 +6344,9 @@ (define_insn_and_split "call_value_multi
   "reload_completed && TARGET_SPLIT_CALLS && (operands[4] = insn)"
   [(const_int 0)]
 {
-  emit_call_insn (gen_call_value_multiple_split (operands[0], operands[1],
-						 operands[2], operands[3]));
-  if (!find_reg_note (operands[4], REG_NORETURN, 0))
-    mips_restore_gp ();
+  mips_split_call (operands[4],
+		   gen_call_value_multiple_split (operands[0], operands[1],
+						  operands[2], operands[3]));
   DONE;
 }
   [(set_attr "jal" "indirect,direct")])
Index: gcc/config/mips/constraints.md
===================================================================
--- gcc/config/mips/constraints.md	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config/mips/constraints.md	2008-08-09 19:22:20.000000000 +0100
@@ -43,8 +43,10 @@ (define_register_constraint "x" "MD_REGS
 (define_register_constraint "b" "ALL_REGS"
   "@internal")
 
-(define_register_constraint "c" "TARGET_USE_PIC_FN_ADDR_REG ? PIC_FN_ADDR_REG
-				 : TARGET_MIPS16 ? M16_NA_REGS
+;; MIPS16 code always calls through a MIPS16 register; see mips_emit_call_insn
+;; for details.
+(define_register_constraint "c" "TARGET_MIPS16 ? M16_REGS
+				 : TARGET_USE_PIC_FN_ADDR_REG ? PIC_FN_ADDR_REG
 				 : GR_REGS"
   "A register suitable for use in an indirect jump.  This will always be
    @code{$25} for @option{-mabicalls}.")
Index: gcc/config/mips/predicates.md
===================================================================
--- gcc/config/mips/predicates.md	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config/mips/predicates.md	2008-08-09 19:22:20.000000000 +0100
@@ -104,14 +104,9 @@ (define_predicate "const_call_insn_opera
   switch (symbol_type)
     {
     case SYMBOL_ABSOLUTE:
-      /* We can only use direct calls for TARGET_ABSOLUTE_ABICALLS if we
-	 are sure that the target function does not need $25 to be live
-	 on entry.  This is true for any locally-defined function because
-	 any such function will use %hi/%lo accesses to set up $gp.  */
-      if (TARGET_ABSOLUTE_ABICALLS
-          && !(GET_CODE (op) == SYMBOL_REF
-	       && SYMBOL_REF_DECL (op)
-	       && !DECL_EXTERNAL (SYMBOL_REF_DECL (op))))
+      /* We can only use direct calls if we're sure that the target
+	 function does not need $25 to be valid on entry.  */
+      if (mips_use_pic_fn_addr_reg_p (op))
 	return false;
 
       /* If -mlong-calls or if this function has an explicit long_call
@@ -206,6 +201,11 @@ (define_predicate "move_operand"
       return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type)
 	      && !mips_split_p[symbol_type]);
 
+    case HIGH:
+      op = XEXP (op, 0);
+      return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type)
+	      && !mips_split_hi_p[symbol_type]);
+
     default:
       return true;
     }
Index: gcc/config/mips/mips16.S
===================================================================
--- gcc/config/mips/mips16.S	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config/mips/mips16.S	2008-08-09 19:22:20.000000000 +0100
@@ -38,6 +38,8 @@ Boston, MA 02110-1301, USA.  */
    values using the soft-float calling convention, but do the actual
    operation using the hard floating point instructions.  */
 
+#if defined _MIPS_SIM && (_MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIO64)
+
 /* This file contains 32-bit assembly code.  */
 	.set nomips16
 
@@ -303,8 +305,12 @@ STARTFN (__mips16_floatsisf)
 
 #ifdef L_m16fltunsisf
 STARTFN (__mips16_floatunsisf)
+	.set	noreorder
 	bltz	$4,1f
-	j	__mips16_floatsisf
+	MOVE_SF_BYTE0 (t)
+	.set	reorder
+	cvt.s.w	RET,ARG1
+	MOVE_SF_RET (f, $31)
 1:		
 	and	$2,$4,1
 	srl	$3,$4,1
@@ -522,7 +528,10 @@ RET_FUNCTION (__mips16_ret_dc, DC)
 #define CALL_STUB_NO_RET(NAME, CODE)	\
 STARTFN (NAME);				\
 	STUB_ARGS_##CODE;		\
+	.set	noreorder;		\
 	jr	$2;			\
+	move	$25,$2;			\
+	.set	reorder;		\
 	ENDFN (NAME)
 
 #ifdef L_m16stub1
@@ -569,7 +578,10 @@ CALL_STUB_NO_RET (__mips16_call_stub_10,
 STARTFN (NAME);				\
 	move	$18,$31;		\
 	STUB_ARGS_##CODE;		\
+	.set	noreorder;		\
 	jalr	$2;			\
+	move	$25,$2;			\
+	.set	reorder;		\
 	MOVE_##MODE##_RET (f, $18);	\
 	ENDFN (NAME)
 
@@ -705,3 +717,4 @@ CALL_STUB_RET (__mips16_call_stub_dc_9, 
 CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
 #endif
 #endif /* !__mips_single_float */
+#endif
Index: gcc/config/mips/libgcc-mips16.ver
===================================================================
--- /dev/null	2008-08-09 09:51:34.868054250 +0100
+++ gcc/config/mips/libgcc-mips16.ver	2008-08-09 19:22:20.000000000 +0100
@@ -0,0 +1,68 @@
+GCC_4.4.0 {
+  __mips16_addsf3
+  __mips16_subsf3
+  __mips16_mulsf3
+  __mips16_divsf3
+  __mips16_eqsf2
+  __mips16_nesf2
+  __mips16_gtsf2
+  __mips16_gesf2
+  __mips16_lesf2
+  __mips16_ltsf2
+  __mips16_floatsisf
+  __mips16_floatunsisf
+  __mips16_fix_truncsfsi
+  __mips16_adddf3
+  __mips16_subdf3
+  __mips16_muldf3
+  __mips16_divdf3
+  __mips16_extendsfdf2
+  __mips16_truncdfsf2
+  __mips16_eqdf2
+  __mips16_nedf2
+  __mips16_gtdf2
+  __mips16_gedf2
+  __mips16_ledf2
+  __mips16_ltdf2
+  __mips16_floatsidf
+  __mips16_floatunsidf
+  __mips16_fix_truncdfsi
+  __mips16_ret_sf
+  __mips16_ret_sc
+  __mips16_ret_df
+  __mips16_ret_dc
+  __mips16_call_stub_1
+  __mips16_call_stub_5
+  __mips16_call_stub_2
+  __mips16_call_stub_6
+  __mips16_call_stub_9
+  __mips16_call_stub_10
+  __mips16_call_stub_sf_0
+  __mips16_call_stub_sf_1
+  __mips16_call_stub_sf_5
+  __mips16_call_stub_sf_2
+  __mips16_call_stub_sf_6
+  __mips16_call_stub_sf_9
+  __mips16_call_stub_sf_10
+  __mips16_call_stub_sc_0
+  __mips16_call_stub_sc_1
+  __mips16_call_stub_sc_5
+  __mips16_call_stub_sc_2
+  __mips16_call_stub_sc_6
+  __mips16_call_stub_sc_9
+  __mips16_call_stub_sc_10
+  __mips16_call_stub_df_0
+  __mips16_call_stub_df_1
+  __mips16_call_stub_df_5
+  __mips16_call_stub_df_2
+  __mips16_call_stub_df_6
+  __mips16_call_stub_df_9
+  __mips16_call_stub_df_10
+  __mips16_call_stub_dc_0
+  __mips16_call_stub_dc_1
+  __mips16_call_stub_dc_5
+  __mips16_call_stub_dc_2
+  __mips16_call_stub_dc_6
+  __mips16_call_stub_dc_9
+  __mips16_call_stub_dc_10
+}
Index: gcc/config/mips/t-libgcc-mips16
===================================================================
--- gcc/config/mips/t-libgcc-mips16	2008-08-09 16:50:40.000000000 +0100
+++ gcc/config/mips/t-libgcc-mips16	2008-08-09 19:22:20.000000000 +0100
@@ -22,3 +22,6 @@ LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m1
 
 LIBGCC_SYNC = yes
 LIBGCC_SYNC_CFLAGS = -mno-mips16
+
+# Version these symbols if building libgcc.so.
+SHLIB_MAPFILES += $(srcdir)/config/mips/libgcc-mips16.ver
Index: gcc/testsuite/lib/target-supports.exp
===================================================================
--- gcc/testsuite/lib/target-supports.exp	2008-08-09 16:50:40.000000000 +0100
+++ gcc/testsuite/lib/target-supports.exp	2008-08-09 19:22:20.000000000 +0100
@@ -439,6 +439,14 @@ proc check_profiling_available { test_wh
 	return 0
     }
 
+    # We don't yet support profiling for MIPS16.
+    if { [istarget mips*-*-*]
+	 && ![check_effective_target_nomips16]
+	 && ([lindex $test_what 1] == "-p"
+	     || [lindex $test_what 1] == "-pg") } {
+	return 0
+    }
+
     # MinGW does not support -p.
     if { [istarget *-*-mingw*] && [lindex $test_what 1] == "-p" } {
 	return 0


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