OSDN Git Service

* configure.ac (mips*-*-*linux*, mips*-*-gnu*): Use mt-mips-gnu.
authorrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 9 Aug 2008 19:08:15 +0000 (19:08 +0000)
committerrsandifo <rsandifo@138bc75d-0d04-0410-961f-82ee72b054a4>
Sat, 9 Aug 2008 19:08:15 +0000 (19:08 +0000)
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@138912 138bc75d-0d04-0410-961f-82ee72b054a4

20 files changed:
ChangeLog
config/ChangeLog
config/mt-mips-elfoabi
config/mt-mips-gnu [new file with mode: 0644]
config/mt-mips16-compat [new file with mode: 0644]
configure
configure.ac
gcc/ChangeLog
gcc/config.gcc
gcc/config/mips/constraints.md
gcc/config/mips/libgcc-mips16.ver [new file with mode: 0644]
gcc/config/mips/mips-protos.h
gcc/config/mips/mips.c
gcc/config/mips/mips.h
gcc/config/mips/mips.md
gcc/config/mips/mips16.S
gcc/config/mips/predicates.md
gcc/config/mips/t-libgcc-mips16
gcc/testsuite/ChangeLog
gcc/testsuite/lib/target-supports.exp

index c4400d6..156f55d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2008-08-09  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * configure.ac (mips*-*-*linux*, mips*-*-gnu*): Use mt-mips-gnu.
+       * configure: Regenerate.
+
 2008-07-30 Paolo Bonzini  <bonzini@gnu.org>
 
        * configure.ac: Add makefile fragments for hpux.
index 8662c7a..1062d8e 100644 (file)
@@ -1,3 +1,9 @@
+2008-08-09  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * mt-mips16-compat: New file, taken from mt-mips-elfoabi.
+       * mt-mips-elfoabi: Include mt-mips16-compat.
+       * mt-mips-gnu: New file.
+
 2008-08-03  Alan Modra  <amodra@bigpond.net.au>
 
        * mt-spu (all-ld): Update for ld Makefile changes.
index 988ca1e..a9f9cbe 100644 (file)
@@ -1,6 +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
diff --git a/config/mt-mips-gnu b/config/mt-mips-gnu
new file mode 100644 (file)
index 0000000..a8198c0
--- /dev/null
@@ -0,0 +1,2 @@
+include $(srcdir)/config/mt-gnu
+include $(srcdir)/config/mt-mips16-compat
diff --git a/config/mt-mips16-compat b/config/mt-mips16-compat
new file mode 100644 (file)
index 0000000..7e36791
--- /dev/null
@@ -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 797cfcf..b69f90d 100755 (executable)
--- a/configure
+++ b/configure
@@ -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 dc9ffac..1804731 100644 (file)
@@ -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 513e54d..c6a2449 100644 (file)
@@ -1,5 +1,142 @@
 2008-08-09  Richard Sandiford  <rdsandiford@googlemail.com>
 
+       * 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.
+
+2008-08-09  Richard Sandiford  <rdsandiford@googlemail.com>
+
        * config/mips/mips.c (mips_unspec_address_offset): Move earlier in file.
        (mips_unspec_address, mips_unspec_offset_high): Likewise.
        (mips_ok_for_lazy_binding_p, mips_load_call_address): Likewise.
index d1381a4..30339e2 100644 (file)
@@ -1544,7 +1544,7 @@ mips*-*-netbsd*)                  # NetBSD/mips, either endian.
        ;;
 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 88fcbf6..3a03a3c 100644 (file)
 (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}.")
diff --git a/gcc/config/mips/libgcc-mips16.ver b/gcc/config/mips/libgcc-mips16.ver
new file mode 100644 (file)
index 0000000..d74bf49
--- /dev/null
@@ -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 d092cb6..01645a1 100644 (file)
@@ -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, rtx, rtx, enum rtx_code, rtx, 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 d8336bb..48daec4 100644 (file)
@@ -469,6 +469,10 @@ static GTY (()) int mips_output_filename_first_time = 1;
    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 const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
 
 /* 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_op *codes,
     }
 }
 \f
+/* 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 *data ATTRIBUTE_UNUSED)
 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 && LEGITIMATE_CONSTANT_P (x))
+    return true;
 
-        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)
+  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 value)
 
 /* 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, enum machine_mode mode, rtx *lo_sum_out)
   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 mips_symbol_type type, rtx v0)
 
   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 mips_symbol_context context)
 {
   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 addr, bool sibcall_p)
     }
 }
 \f
+/* 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;
+}
+\f
 /* 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 direction)
 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 fn, rtx args_size, int fp_code)
 
   /* 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 fn, rtx args_size, int fp_code)
   /* 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 fn, rtx args_size, int fp_code)
                 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 fn, rtx args_size, int fp_code)
       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 fn, rtx args_size, int fp_code)
             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 fn, rtx args_size, int fp_code)
       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 fn, rtx args_size, int fp_code)
             $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 fn, rtx args_size, int fp_code)
     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 fn, rtx args_size, int fp_code)
   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;
 }
 \f
-/* 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 @@ static 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, rtx scratch)
   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 *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
      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 ();
 }
 \f
@@ -12212,24 +12637,19 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
                   && 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);
-
-       /* Set up the global pointer for n32 or n64 abicalls.  */
-       mips_emit_loadgp ();
-       break;
+  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);
 
-      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 thunk_fndecl ATTRIBUTE_UNUSED,
         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 006c73e..ffeef0a 100644 (file)
@@ -1659,16 +1659,29 @@ enum mips_code_readable_setting {
 /* 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 @@ enum mips_code_readable_setting {
 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 @@ enum reg_class
 #define REG_CLASS_NAMES                                                        \
 {                                                                      \
   "NO_REGS",                                                           \
-  "M16_NA_REGS",                                                       \
   "M16_REGS",                                                          \
   "T_REG",                                                             \
   "M16_T_REGS",                                                                \
@@ -1801,7 +1812,6 @@ enum reg_class
 #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 @@ typedef struct mips_args {
 #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 @@ extern int set_nomacro;                   /* # of nested .set nomacro's  */
 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 43c47e5..4d09085 100644 (file)
@@ -66,6 +66,7 @@
    (UNSPEC_MEMORY_BARRIER      45)
    (UNSPEC_SET_GOT_VERSION     46)
    (UNSPEC_UPDATE_GOT_VERSION  47)
+   (UNSPEC_COPYGP              48)
    
    (UNSPEC_ADDRESS_FIRST       100)
 
          (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)
 
 (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>")])
 
 (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.
                  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
    (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
 }
   [(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.
 ;; 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")])
 \f
 ;;
 ;;    - 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)
              (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;
 })
 
              (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;
 })
 
              (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;
 })
 
   "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"
              (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;
 })
   "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")])
   [(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"
   "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 90651b1..edc84de 100644 (file)
@@ -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, 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, 9, DC)
 CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
 #endif
 #endif /* !__mips_single_float */
+#endif
index 73db027..0e8c85b 100644 (file)
   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
       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 d37b6ee..b1a547d 100644 (file)
@@ -22,3 +22,6 @@ LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \
 
 LIBGCC_SYNC = yes
 LIBGCC_SYNC_CFLAGS = -mno-mips16
+
+# Version these symbols if building libgcc.so.
+SHLIB_MAPFILES += $(srcdir)/config/mips/libgcc-mips16.ver
index 4c98fca..de65eae 100644 (file)
@@ -1,3 +1,8 @@
+2008-08-09  Richard Sandiford  <rdsandiford@googlemail.com>
+
+       * lib/target-supports.exp (check_profiling_available): Return false
+       for -p and -pg on MIPS16 targets.
+
 2008-08-09  Richard Guenther  <rguenther@suse.de>
 
        * gcc.dg/tree-ssa/inline-2.c: New testcase.
index 33c8c71..2c9165c 100644 (file)
@@ -439,6 +439,14 @@ proc check_profiling_available { test_what } {
        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