Commit 08d0963a by Richard Sandiford Committed by Richard Sandiford

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

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

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

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

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

From-SVN: r138912
parent 7462a715
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> 2008-07-30 Paolo Bonzini <bonzini@gnu.org>
* configure.ac: Add makefile fragments for hpux. * configure.ac: Add makefile fragments for hpux.
......
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> 2008-08-03 Alan Modra <amodra@bigpond.net.au>
* mt-spu (all-ld): Update for ld Makefile changes. * mt-spu (all-ld): Update for ld Makefile changes.
......
# The *-elfoabi configurations are intended to be usable for both include $(srcdir)/config/mt-mips16-compat
# 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-gnu
include $(srcdir)/config/mt-mips16-compat
# 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
...@@ -5448,6 +5448,9 @@ case "${target}" in ...@@ -5448,6 +5448,9 @@ case "${target}" in
mipsisa*-*-elfoabi*) mipsisa*-*-elfoabi*)
target_makefile_frag="config/mt-mips-elfoabi" target_makefile_frag="config/mt-mips-elfoabi"
;; ;;
mips*-*-*linux* | mips*-*-gnu*)
target_makefile_frag="config/mt-mips-gnu"
;;
*-*-netware*) *-*-netware*)
target_makefile_frag="config/mt-netware" target_makefile_frag="config/mt-netware"
;; ;;
......
...@@ -1904,6 +1904,9 @@ case "${target}" in ...@@ -1904,6 +1904,9 @@ case "${target}" in
mipsisa*-*-elfoabi*) mipsisa*-*-elfoabi*)
target_makefile_frag="config/mt-mips-elfoabi" target_makefile_frag="config/mt-mips-elfoabi"
;; ;;
mips*-*-*linux* | mips*-*-gnu*)
target_makefile_frag="config/mt-mips-gnu"
;;
*-*-netware*) *-*-netware*)
target_makefile_frag="config/mt-netware" target_makefile_frag="config/mt-netware"
;; ;;
......
2008-08-09 Richard Sandiford <rdsandiford@googlemail.com> 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. * config/mips/mips.c (mips_unspec_address_offset): Move earlier in file.
(mips_unspec_address, mips_unspec_offset_high): Likewise. (mips_unspec_address, mips_unspec_offset_high): Likewise.
(mips_ok_for_lazy_binding_p, mips_load_call_address): Likewise. (mips_ok_for_lazy_binding_p, mips_load_call_address): Likewise.
......
...@@ -1544,7 +1544,7 @@ mips*-*-netbsd*) # NetBSD/mips, either endian. ...@@ -1544,7 +1544,7 @@ mips*-*-netbsd*) # NetBSD/mips, either endian.
;; ;;
mips64*-*-linux* | mipsisa64*-*-linux*) mips64*-*-linux* | mipsisa64*-*-linux*)
tm_file="dbxelf.h elfos.h svr4.h linux.h ${tm_file} mips/linux.h mips/linux64.h" 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" tm_defines="${tm_defines} MIPS_ABI_DEFAULT=ABI_N32"
case ${target} in case ${target} in
mips64el-st-linux-gnu) mips64el-st-linux-gnu)
...@@ -1561,6 +1561,7 @@ mips64*-*-linux* | mipsisa64*-*-linux*) ...@@ -1561,6 +1561,7 @@ mips64*-*-linux* | mipsisa64*-*-linux*)
;; ;;
mips*-*-linux*) # Linux MIPS, either endian. mips*-*-linux*) # Linux MIPS, either endian.
tm_file="dbxelf.h elfos.h svr4.h linux.h ${tm_file} mips/linux.h" 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 case ${target} in
mipsisa32r2*) mipsisa32r2*)
tm_defines="${tm_defines} MIPS_ISA_DEFAULT=33" tm_defines="${tm_defines} MIPS_ISA_DEFAULT=33"
......
...@@ -43,8 +43,10 @@ ...@@ -43,8 +43,10 @@
(define_register_constraint "b" "ALL_REGS" (define_register_constraint "b" "ALL_REGS"
"@internal") "@internal")
(define_register_constraint "c" "TARGET_USE_PIC_FN_ADDR_REG ? PIC_FN_ADDR_REG ;; MIPS16 code always calls through a MIPS16 register; see mips_emit_call_insn
: TARGET_MIPS16 ? M16_NA_REGS ;; for details.
(define_register_constraint "c" "TARGET_MIPS16 ? M16_REGS
: TARGET_USE_PIC_FN_ADDR_REG ? PIC_FN_ADDR_REG
: GR_REGS" : GR_REGS"
"A register suitable for use in an indirect jump. This will always be "A register suitable for use in an indirect jump. This will always be
@code{$25} for @option{-mabicalls}.") @code{$25} for @option{-mabicalls}.")
......
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
}
...@@ -164,6 +164,22 @@ enum mips_loadgp_style { ...@@ -164,6 +164,22 @@ enum mips_loadgp_style {
struct mips16e_save_restore_info; 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, extern bool mips_symbolic_constant_p (rtx, enum mips_symbol_context,
enum mips_symbol_type *); enum mips_symbol_type *);
extern int mips_regno_mode_ok_for_base_p (int, enum machine_mode, bool); 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); ...@@ -175,6 +191,8 @@ extern int mips_split_const_insns (rtx);
extern int mips_load_store_insns (rtx, rtx); extern int mips_load_store_insns (rtx, rtx);
extern int mips_idiv_insns (void); extern int mips_idiv_insns (void);
extern rtx mips_emit_move (rtx, rtx); 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 bool mips_split_symbol (rtx, rtx, enum machine_mode, rtx *);
extern rtx mips_unspec_address (rtx, enum mips_symbol_type); extern rtx mips_unspec_address (rtx, enum mips_symbol_type);
extern bool mips_legitimize_address (rtx *, enum machine_mode); extern bool mips_legitimize_address (rtx *, enum machine_mode);
...@@ -202,7 +220,7 @@ extern rtx mips_subword (rtx, bool); ...@@ -202,7 +220,7 @@ extern rtx mips_subword (rtx, bool);
extern bool mips_split_64bit_move_p (rtx, rtx); extern bool mips_split_64bit_move_p (rtx, rtx);
extern void mips_split_doubleword_move (rtx, rtx); extern void mips_split_doubleword_move (rtx, rtx);
extern const char *mips_output_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 #ifdef RTX_CODE
extern bool mips_expand_scc (enum rtx_code, rtx); extern bool mips_expand_scc (enum rtx_code, rtx);
extern void mips_expand_conditional_branch (rtx *, enum rtx_code); 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); ...@@ -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_move (rtx *);
extern void mips_expand_conditional_trap (enum rtx_code); extern void mips_expand_conditional_trap (enum rtx_code);
#endif #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_expand_fcc_reload (rtx, rtx, rtx);
extern void mips_set_return_address (rtx, rtx); extern void mips_set_return_address (rtx, rtx);
extern bool mips_expand_block_move (rtx, rtx, rtx); extern bool mips_expand_block_move (rtx, rtx, rtx);
......
...@@ -469,6 +469,10 @@ static GTY (()) int mips_output_filename_first_time = 1; ...@@ -469,6 +469,10 @@ static GTY (()) int mips_output_filename_first_time = 1;
mips_split_symbol. */ mips_split_symbol. */
bool mips_split_p[NUM_SYMBOL_TYPES]; 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 /* 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 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. */ if they are matched by a special .md file pattern. */
...@@ -479,11 +483,11 @@ static const char *mips_hi_relocs[NUM_SYMBOL_TYPES]; ...@@ -479,11 +483,11 @@ static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
/* Index R is the smallest register class that contains register R. */ /* Index R is the smallest register class that contains register R. */
const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = { 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, M16_REGS, M16_REGS, M16_REGS, M16_REGS,
LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_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, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
T_REG, PIC_FN_ADDR_REG, LEA_REGS, LEA_REGS, T_REG, PIC_FN_ADDR_REG, LEA_REGS, LEA_REGS,
LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
...@@ -1342,6 +1346,22 @@ mips_build_integer (struct mips_integer_op *codes, ...@@ -1342,6 +1346,22 @@ mips_build_integer (struct mips_integer_op *codes,
} }
} }
/* 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. */ /* Return true if X is a thread-local symbol. */
static bool static bool
...@@ -1359,7 +1379,7 @@ mips_global_symbol_p (const_rtx x) ...@@ -1359,7 +1379,7 @@ mips_global_symbol_p (const_rtx x)
const_tree decl = SYMBOL_REF_DECL (x); const_tree decl = SYMBOL_REF_DECL (x);
if (!decl) 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 /* Weakref symbols are not TREE_PUBLIC, but their targets are global
or weak symbols. Relocations in the object file will be against or weak symbols. Relocations in the object file will be against
...@@ -1367,6 +1387,27 @@ mips_global_symbol_p (const_rtx x) ...@@ -1367,6 +1387,27 @@ mips_global_symbol_p (const_rtx x)
return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl)); 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. */ /* Return true if SYMBOL_REF X binds locally. */
static bool static bool
...@@ -1401,6 +1442,29 @@ mips_dangerous_for_la25_p (rtx x) ...@@ -1401,6 +1442,29 @@ mips_dangerous_for_la25_p (rtx x)
&& mips_global_symbol_p (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 /* Return the method that should be used to access SYMBOL_REF or
LABEL_REF X in context CONTEXT. */ LABEL_REF X in context CONTEXT. */
...@@ -1736,10 +1800,14 @@ mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED) ...@@ -1736,10 +1800,14 @@ mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
static bool static bool
mips_cannot_force_const_mem (rtx x) mips_cannot_force_const_mem (rtx x)
{ {
enum mips_symbol_type type;
rtx base, offset; rtx base, offset;
if (!TARGET_MIPS16) /* 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 /* As an optimization, reject constants that mips_legitimize_move
can expand inline. can expand inline.
...@@ -1749,11 +1817,20 @@ mips_cannot_force_const_mem (rtx x) ...@@ -1749,11 +1817,20 @@ mips_cannot_force_const_mem (rtx x)
references, reload will consider forcing C into memory and using references, reload will consider forcing C into memory and using
one of the instruction's memory alternatives. Returning false one of the instruction's memory alternatives. Returning false
here will force it to use an input reload instead. */ here will force it to use an input reload instead. */
if (GET_CODE (x) == CONST_INT) if (GET_CODE (x) == CONST_INT && LEGITIMATE_CONSTANT_P (x))
return true; return true;
split_const (x, &base, &offset); split_const (x, &base, &offset);
if (symbolic_operand (base, VOIDmode) && SMALL_INT (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;
/* 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; return true;
} }
...@@ -2097,8 +2174,13 @@ mips_const_insns (rtx x) ...@@ -2097,8 +2174,13 @@ mips_const_insns (rtx x)
return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE); return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE);
/* Otherwise try splitting the constant into a base and offset. /* Otherwise try splitting the constant into a base and offset.
16-bit offsets can be added using an extra ADDIU. Larger offsets If the offset is a 16-bit value, we can load the base address
must be calculated separately and then added to the base. */ 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); split_const (x, &x, &offset);
if (offset != 0) if (offset != 0)
{ {
...@@ -2107,7 +2189,7 @@ mips_const_insns (rtx x) ...@@ -2107,7 +2189,7 @@ mips_const_insns (rtx x)
{ {
if (SMALL_INT (offset)) if (SMALL_INT (offset))
return n + 1; return n + 1;
else else if (!targetm.cannot_force_const_mem (x))
return n + 1 + mips_build_integer (codes, INTVAL (offset)); return n + 1 + mips_build_integer (codes, INTVAL (offset));
} }
} }
...@@ -2238,17 +2320,30 @@ mips_force_temporary (rtx dest, rtx value) ...@@ -2238,17 +2320,30 @@ mips_force_temporary (rtx dest, rtx value)
/* Emit a call sequence with call pattern PATTERN and return the call /* Emit a call sequence with call pattern PATTERN and return the call
instruction itself (which is not necessarily the last instruction 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 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); 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) if (lazy_p)
/* Lazy-binding stubs require $gp to be valid on entry. */
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
if (TARGET_USE_GOT) if (TARGET_USE_GOT)
...@@ -2334,21 +2429,17 @@ mips16_gp_pseudo_reg (void) ...@@ -2334,21 +2429,17 @@ mips16_gp_pseudo_reg (void)
if (!cfun->machine->initialized_mips16_gp_pseudo_p if (!cfun->machine->initialized_mips16_gp_pseudo_p
&& (current_ir_type () != IR_GIMPLE || currently_expanding_to_rtl)) && (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); 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 (); pop_topmost_sequence ();
cfun->machine->initialized_mips16_gp_pseudo_p = true; cfun->machine->initialized_mips16_gp_pseudo_p = true;
...@@ -2357,17 +2448,74 @@ mips16_gp_pseudo_reg (void) ...@@ -2357,17 +2448,74 @@ mips16_gp_pseudo_reg (void)
return cfun->machine->mips16_gp_pseudo_rtx; 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 /* 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 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. constant in that context and can be split into high and low parts.
If so, and if LO_SUM_OUT is nonnull, emit the high part and return If so, and if LOW_OUT is nonnull, emit the high part and store the
the LO_SUM in *LO_SUM_OUT. Leave *LO_SUM_OUT unchanged otherwise. low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise.
TEMP is as for mips_force_temporary and is used to load the high 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 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_context context;
enum mips_symbol_type symbol_type; 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) ...@@ -2376,31 +2524,56 @@ mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *lo_sum_out)
context = (mode == MAX_MACHINE_MODE context = (mode == MAX_MACHINE_MODE
? SYMBOL_CONTEXT_LEA ? SYMBOL_CONTEXT_LEA
: SYMBOL_CONTEXT_MEM); : SYMBOL_CONTEXT_MEM);
if (!mips_symbolic_constant_p (addr, context, &symbol_type) if (GET_CODE (addr) == HIGH && context == SYMBOL_CONTEXT_LEA)
|| mips_symbol_insns (symbol_type, mode) == 0
|| !mips_split_p[symbol_type])
return false;
if (lo_sum_out)
{ {
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 ()) if (low_out)
switch (symbol_type)
{ {
emit_insn (gen_load_const_gp (temp)); case SYMBOL_GOT_PAGE_OFST:
high = temp; /* 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
high = mips16_gp_pseudo_reg ();
} }
else else
{ {
if (mips_symbolic_constant_p (addr, context, &symbol_type)
&& mips_symbol_insns (symbol_type, mode) > 0
&& mips_split_p[symbol_type])
{
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 = gen_rtx_HIGH (Pmode, copy_rtx (addr));
high = mips_force_temporary (temp, high); high = mips_force_temporary (temp, high);
} *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
*lo_sum_out = gen_rtx_LO_SUM (Pmode, high, addr); break;
} }
return true; return true;
}
}
return false;
} }
/* Return a legitimate address for REG + OFFSET. TEMP is as for /* 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) ...@@ -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, emit_insn (gen_rtx_SET (Pmode, a0,
gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc))); 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; RTL_CONST_CALL_P (insn) = 1;
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0); use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
insn = get_insns (); insn = get_insns ();
...@@ -2716,7 +2890,8 @@ mips_rewrite_small_data_p (rtx x, enum mips_symbol_context context) ...@@ -2716,7 +2890,8 @@ mips_rewrite_small_data_p (rtx x, enum mips_symbol_context context)
{ {
enum mips_symbol_type symbol_type; 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) && mips_symbolic_constant_p (x, context, &symbol_type)
&& symbol_type == SYMBOL_GP_RELATIVE); && symbol_type == SYMBOL_GP_RELATIVE);
} }
...@@ -5215,33 +5390,27 @@ mips_ok_for_lazy_binding_p (rtx x) ...@@ -5215,33 +5390,27 @@ mips_ok_for_lazy_binding_p (rtx x)
{ {
return (TARGET_USE_GOT return (TARGET_USE_GOT
&& GET_CODE (x) == SYMBOL_REF && GET_CODE (x) == SYMBOL_REF
&& !SYMBOL_REF_BIND_NOW_P (x)
&& !mips_symbol_binds_local_p (x)); && !mips_symbol_binds_local_p (x));
} }
/* Load function address ADDR into register DEST. SIBCALL_P is true /* Load function address ADDR into register DEST. TYPE is as for
if the address is needed for a sibling call. Return true if we mips_expand_call. Return true if we used an explicit lazy-binding
used an explicit lazy-binding sequence. */ sequence. */
static bool 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, /* 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 try to allow its address to be resolved lazily. This isn't
possible for sibcalls when $gp is call-saved because the value 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. */ of $gp on entry to the stub would be our caller's gp, not ours. */
if (TARGET_EXPLICIT_RELOCS 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)) && mips_ok_for_lazy_binding_p (addr))
{ {
rtx high, lo_sum_symbol; addr = mips_got_load (dest, addr, SYMBOL_GOTOFF_CALL);
emit_insn (gen_rtx_SET (VOIDmode, dest, addr));
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));
return true; return true;
} }
else else
...@@ -5251,6 +5420,78 @@ mips_load_call_address (rtx dest, rtx addr, bool sibcall_p) ...@@ -5251,6 +5420,78 @@ mips_load_call_address (rtx dest, rtx addr, bool sibcall_p)
} }
} }
/* Each locally-defined hard-float MIPS16 function has a local symbol
associated with it. This hash table maps the function symbol (FUNC)
to the local symbol (LOCAL). */
struct mips16_local_alias GTY(()) {
rtx func;
rtx local;
};
static GTY ((param_is (struct mips16_local_alias))) htab_t mips16_local_aliases;
/* Hash table callbacks for mips16_local_aliases. */
static hashval_t
mips16_local_aliases_hash (const void *entry)
{
const struct mips16_local_alias *alias;
alias = (const struct mips16_local_alias *) entry;
return htab_hash_string (XSTR (alias->func, 0));
}
static int
mips16_local_aliases_eq (const void *entry1, const void *entry2)
{
const struct mips16_local_alias *alias1, *alias2;
alias1 = (const struct mips16_local_alias *) entry1;
alias2 = (const struct mips16_local_alias *) entry2;
return rtx_equal_p (alias1->func, alias2->func);
}
/* FUNC is the symbol for a locally-defined hard-float MIPS16 function.
Return a local alias for it, creating a new one if necessary. */
static rtx
mips16_local_alias (rtx func)
{
struct mips16_local_alias *alias, tmp_alias;
void **slot;
/* Create the hash table if this is the first call. */
if (mips16_local_aliases == NULL)
mips16_local_aliases = htab_create_ggc (37, mips16_local_aliases_hash,
mips16_local_aliases_eq, NULL);
/* Look up the function symbol, creating a new entry if need be. */
tmp_alias.func = func;
slot = htab_find_slot (mips16_local_aliases, &tmp_alias, INSERT);
gcc_assert (slot != NULL);
alias = (struct mips16_local_alias *) *slot;
if (alias == NULL)
{
const char *func_name, *local_name;
rtx local;
/* Create a new SYMBOL_REF for the local symbol. The choice of
__fn_local_* is based on the __fn_stub_* names that we've
traditionally used for the non-MIPS16 stub. */
func_name = targetm.strip_name_encoding (XSTR (func, 0));
local_name = ACONCAT (("__fn_local_", func_name, NULL));
local = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (local_name));
SYMBOL_REF_FLAGS (local) = SYMBOL_REF_FLAGS (func) | SYMBOL_FLAG_LOCAL;
/* Create a new structure to represent the mapping. */
alias = GGC_NEW (struct mips16_local_alias);
alias->func = func;
alias->local = local;
*slot = alias;
}
return alias->local;
}
/* A chained list of functions for which mips16_build_call_stub has already /* 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 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. */ if the function returns a value in floating-point registers. */
...@@ -5261,6 +5502,18 @@ struct mips16_stub { ...@@ -5261,6 +5502,18 @@ struct mips16_stub {
}; };
static struct mips16_stub *mips16_stubs; 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 the two-character string that identifies floating-point
return mode MODE in the name of a MIPS16 function stub. */ return mode MODE in the name of a MIPS16 function stub. */
...@@ -5367,14 +5620,18 @@ mips_output_args_xfer (int fp_code, char direction) ...@@ -5367,14 +5620,18 @@ mips_output_args_xfer (int fp_code, char direction)
static void static void
mips16_build_function_stub (void) mips16_build_function_stub (void)
{ {
const char *fnname, *separator; const char *fnname, *alias_name, *separator;
char *secname, *stubname; char *secname, *stubname;
tree stubdecl; tree stubdecl;
unsigned int f; unsigned int f;
rtx symbol, alias;
/* Create the name of the stub, and its unique section. */ /* Create the name of the stub, and its unique section. */
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); symbol = XEXP (DECL_RTL (current_function_decl), 0);
fnname = targetm.strip_name_encoding (fnname); 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)); secname = ACONCAT ((".mips16.fn.", fnname, NULL));
stubname = ACONCAT (("__fn_stub_", fnname, NULL)); stubname = ACONCAT (("__fn_stub_", fnname, NULL));
...@@ -5400,23 +5657,48 @@ mips16_build_function_stub (void) ...@@ -5400,23 +5657,48 @@ mips16_build_function_stub (void)
assemble_start_function (stubdecl, stubname); assemble_start_function (stubdecl, stubname);
mips_start_function_definition (stubname, false); 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 /* 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 that targets with coprocessor interlocks can use an MFC1 to fill the
delay slot. */ delay slot. */
fprintf (asm_out_file, "\t.set\tnoat\n"); fprintf (asm_out_file, "\t.set\tnoat\n");
fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]); output_asm_insn ("la\t%@,%0", &symbol);
assemble_name (asm_out_file, fnname);
fprintf (asm_out_file, "\n");
/* Move the arguments from floating-point registers to general registers. */ /* Move the arguments from floating-point registers to general registers. */
mips_output_args_xfer (crtl->args.info.fp_code, 'f'); mips_output_args_xfer (crtl->args.info.fp_code, 'f');
/* Jump to the MIPS16 function. */ /* 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"); 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); 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)); switch_to_section (function_section (current_function_decl));
} }
...@@ -5427,31 +5709,46 @@ mips16_build_function_stub (void) ...@@ -5427,31 +5709,46 @@ mips16_build_function_stub (void)
static void static void
mips16_copy_fpr_return_value (void) mips16_copy_fpr_return_value (void)
{ {
rtx fn, insn, arg, call; rtx fn, insn, retval;
tree id, return_type; tree return_type;
enum machine_mode return_mode; enum machine_mode return_mode;
const char *name;
return_type = DECL_RESULT (current_function_decl); return_type = DECL_RESULT (current_function_decl);
return_mode = DECL_MODE (return_type); return_mode = DECL_MODE (return_type);
id = get_identifier (ACONCAT (("__mips16_ret_", name = ACONCAT (("__mips16_ret_",
mips16_call_stub_mode_suffix (return_mode), mips16_call_stub_mode_suffix (return_mode),
NULL))); NULL));
fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id)); fn = mips16_stub_function (name);
arg = gen_rtx_REG (return_mode, GP_RETURN);
call = gen_call_value_internal (arg, fn, const0_rtx); /* The function takes arguments in $2 (and possibly $3), so calls
insn = mips_emit_call_insn (call, false); to it cannot be lazily bound. */
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), arg); 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 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 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; arguments and FP_CODE is the code built by mips_function_arg;
see the comment above CUMULATIVE_ARGS for details. see the comment above CUMULATIVE_ARGS for details.
If a stub was needed, emit the call and return the call insn itself. There are three alternatives:
Return null otherwise.
- 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, A stub is needed for calls to functions that, in normal mode,
receive arguments in FPRs or return values in FPRs. The stub receive arguments in FPRs or return values in FPRs. The stub
...@@ -5460,17 +5757,18 @@ mips16_copy_fpr_return_value (void) ...@@ -5460,17 +5757,18 @@ mips16_copy_fpr_return_value (void)
return value from its hard-float position to its soft-float return value from its hard-float position to its soft-float
position. position.
We emit a JAL to FN even when FN might need a stub. If FN turns out We can emit a JAL to *FN_PTR even when *FN_PTR might need a stub.
to be to a non-MIPS16 function, the linker automatically redirects If *FN_PTR turns out to be to a non-MIPS16 function, the linker
the JAL to the stub, otherwise the JAL continues to call FN directly. */ automatically redirects the JAL to the stub, otherwise the JAL
continues to call FN directly. */
static rtx 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; const char *fnname;
bool fp_ret_p; bool fp_ret_p;
struct mips16_stub *l; 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 don't need to do anything if we aren't in MIPS16 mode, or if
we were invoked with the -msoft-float option. */ 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) ...@@ -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 /* We don't need to do anything if this is a call to a special
MIPS16 support function. */ MIPS16 support function. */
if (GET_CODE (fn) == SYMBOL_REF fn = *fn_ptr;
&& strncmp (XSTR (fn, 0), "__mips16_", 9) == 0) if (mips16_stub_function_p (fn))
return NULL_RTX; return NULL_RTX;
/* This code will only work for o32 and o64 abis. The other ABI's /* 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) ...@@ -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 /* 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. libgcc.a stubs provided for each (FP_CODE, FP_RET_P) combination.
Each stub expects the function address to arrive in register $2. */ 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]; char buf[30];
tree id; rtx stub_fn, insn, addr;
rtx stub_fn, insn; 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. */ /* Create a SYMBOL_REF for the libgcc.a function. */
if (fp_ret_p) if (fp_ret_p)
...@@ -5513,24 +5820,22 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5513,24 +5820,22 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
fp_code); fp_code);
else else
sprintf (buf, "__mips16_call_stub_%d", fp_code); sprintf (buf, "__mips16_call_stub_%d", fp_code);
id = get_identifier (buf); stub_fn = mips16_stub_function (buf);
stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
/* 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. */ /* 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. */ /* Emit the call. */
if (retval == NULL_RTX) insn = mips_expand_call (MIPS_CALL_NORMAL, retval, stub_fn,
insn = gen_call_internal (stub_fn, args_size); args_size, NULL_RTX, lazy_p);
else
insn = gen_call_value_internal (retval, stub_fn, args_size);
insn = mips_emit_call_insn (insn, false);
/* Tell GCC that this call does indeed use the value of $2. */ /* Tell GCC that this call does indeed use the value of $2. */
CALL_INSN_FUNCTION_USAGE (insn) = use_reg (&CALL_INSN_FUNCTION_USAGE (insn), addr);
gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 2)),
CALL_INSN_FUNCTION_USAGE (insn));
/* If we are handling a floating-point return value, we need to /* If we are handling a floating-point return value, we need to
save $18 in the function prologue. Putting a note on the save $18 in the function prologue. Putting a note on the
...@@ -5540,7 +5845,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -5540,7 +5845,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
if (fp_ret_p) if (fp_ret_p)
CALL_INSN_FUNCTION_USAGE (insn) = CALL_INSN_FUNCTION_USAGE (insn) =
gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_EXPR_LIST (VOIDmode,
gen_rtx_USE (VOIDmode, gen_rtx_CLOBBER (VOIDmode,
gen_rtx_REG (word_mode, 18)), gen_rtx_REG (word_mode, 18)),
CALL_INSN_FUNCTION_USAGE (insn)); CALL_INSN_FUNCTION_USAGE (insn));
...@@ -5605,8 +5910,13 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -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 first so that targets with coprocessor interlocks can use
an MFC1 to fill the delay slot. */ an MFC1 to fill the delay slot. */
fprintf (asm_out_file, "\t.set\tnoat\n"); fprintf (asm_out_file, "\t.set\tnoat\n");
fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1], if (TARGET_EXPLICIT_RELOCS)
fnname); {
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 /* 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) ...@@ -5616,7 +5926,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
if (!fp_ret_p) if (!fp_ret_p)
{ {
/* Jump to the previously-loaded address. */ /* 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"); fprintf (asm_out_file, "\t.set\tat\n");
} }
else else
...@@ -5626,7 +5936,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -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. */ $18 is usually a call-saved register. */
fprintf (asm_out_file, "\tmove\t%s,%s\n", fprintf (asm_out_file, "\tmove\t%s,%s\n",
reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]); 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 /* Move the result from floating-point registers to
general registers. */ general registers. */
...@@ -5696,7 +6006,7 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code) ...@@ -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); insn = gen_call_internal_direct (fn, args_size);
else else
insn = gen_call_value_internal_direct (retval, fn, args_size); 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 /* 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 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) ...@@ -5705,70 +6015,114 @@ mips16_build_call_stub (rtx retval, rtx fn, rtx args_size, int fp_code)
if (fp_ret_p) if (fp_ret_p)
CALL_INSN_FUNCTION_USAGE (insn) = CALL_INSN_FUNCTION_USAGE (insn) =
gen_rtx_EXPR_LIST (VOIDmode, 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)); CALL_INSN_FUNCTION_USAGE (insn));
return insn; return insn;
} }
/* Expand a "call", "sibcall", "call_value" or "sibcall_value" instruction. /* Expand a call of type TYPE. RESULT is where the result will go (null
RESULT is where the result will go (null for "call"s and "sibcall"s), for "call"s and "sibcall"s), ADDR is the address of the function,
ADDR is the address of the function, ARGS_SIZE is the size of the ARGS_SIZE is the size of the arguments and AUX is the value passed
arguments and AUX is the value passed to us by mips_function_arg. to us by mips_function_arg. LAZY_P is true if this call already
SIBCALL_P is true if we are expanding a sibling call, false if we're involves a lazily-bound function address (such as when calling
expanding a normal call. functions through a MIPS16 hard-float stub).
Return the call itself. */ Return the call itself. */
rtx 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; 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; orig_addr = addr;
lazy_p = false;
if (!call_insn_operand (addr, VOIDmode)) if (!call_insn_operand (addr, VOIDmode))
{ {
if (type == MIPS_CALL_EPILOGUE)
addr = MIPS_EPILOGUE_TEMP (Pmode);
else
addr = gen_reg_rtx (Pmode); addr = gen_reg_rtx (Pmode);
lazy_p = mips_load_call_address (addr, orig_addr, sibcall_p); lazy_p |= mips_load_call_address (type, addr, orig_addr);
} }
insn = mips16_build_call_stub (result, addr, args_size, if (result == 0)
aux == 0 ? 0 : (int) GET_MODE (aux));
if (insn)
{ {
gcc_assert (!sibcall_p && !lazy_p); rtx (*fn) (rtx, rtx);
return insn;
}
if (result == 0) if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
pattern = (sibcall_p fn = gen_call_split;
? gen_sibcall_internal (addr, args_size) else if (type == MIPS_CALL_SIBCALL)
: gen_call_internal (addr, args_size)); fn = gen_sibcall_internal;
else
fn = gen_call_internal;
pattern = fn (addr, args_size);
}
else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2) else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
{ {
/* Handle return values created by mips_return_fpr_pair. */ /* Handle return values created by mips_return_fpr_pair. */
rtx (*fn) (rtx, rtx, rtx, rtx);
rtx reg1, reg2; 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); reg1 = XEXP (XVECEXP (result, 0, 0), 0);
reg2 = XEXP (XVECEXP (result, 0, 1), 0); reg2 = XEXP (XVECEXP (result, 0, 1), 0);
pattern = pattern = fn (reg1, addr, args_size, reg2);
(sibcall_p
? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2)
: gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
} }
else 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. */ /* Handle return values created by mips_return_fpr_single. */
if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1) if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
result = XEXP (XVECEXP (result, 0, 0), 0); result = XEXP (XVECEXP (result, 0, 0), 0);
pattern = (sibcall_p pattern = fn (result, addr, args_size);
? gen_sibcall_value_internal (result, addr, args_size)
: gen_call_value_internal (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. */ /* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
...@@ -6315,6 +6669,7 @@ static void ...@@ -6315,6 +6669,7 @@ static void
mips_init_relocs (void) mips_init_relocs (void)
{ {
memset (mips_split_p, '\0', sizeof (mips_split_p)); 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_hi_relocs, '\0', sizeof (mips_hi_relocs));
memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs)); memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
...@@ -6356,13 +6711,13 @@ mips_init_relocs (void) ...@@ -6356,13 +6711,13 @@ mips_init_relocs (void)
mips_split_p[SYMBOL_GP_RELATIVE] = true; mips_split_p[SYMBOL_GP_RELATIVE] = true;
mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel("; mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
} }
else if (TARGET_EXPLICIT_RELOCS)
if (TARGET_EXPLICIT_RELOCS)
{
/* Small data constants are kept whole until after reload, /* Small data constants are kept whole until after reload,
then lowered by mips_rewrite_small_data. */ then lowered by mips_rewrite_small_data. */
mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel("; mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
if (TARGET_EXPLICIT_RELOCS)
{
mips_split_p[SYMBOL_GOT_PAGE_OFST] = true; mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
if (TARGET_NEWABI) if (TARGET_NEWABI)
{ {
...@@ -6374,6 +6729,9 @@ mips_init_relocs (void) ...@@ -6374,6 +6729,9 @@ mips_init_relocs (void)
mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got("; mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo("; 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) if (TARGET_XGOT)
{ {
...@@ -6395,6 +6753,9 @@ mips_init_relocs (void) ...@@ -6395,6 +6753,9 @@ mips_init_relocs (void)
else else
mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got("; mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16("; 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) ...@@ -7939,6 +8300,7 @@ mips_global_pointer (void)
but no instruction will yet refer to it. */ but no instruction will yet refer to it. */
if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM) if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
&& !crtl->uses_const_pool && !crtl->uses_const_pool
&& !mips16_cfun_returns_in_fpr_p ()
&& !mips_function_has_gp_insn ()) && !mips_function_has_gp_insn ())
return 0; return 0;
...@@ -8261,6 +8623,11 @@ mips_extra_live_on_entry (bitmap regs) ...@@ -8261,6 +8623,11 @@ mips_extra_live_on_entry (bitmap regs)
if (!TARGET_ABSOLUTE_ABICALLS) if (!TARGET_ABSOLUTE_ABICALLS)
bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM); 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. */ /* See the comment above load_call<mode> for details. */
bitmap_set_bit (regs, GOT_VERSION_REGNUM); bitmap_set_bit (regs, GOT_VERSION_REGNUM);
} }
...@@ -8293,20 +8660,45 @@ mips_set_return_address (rtx address, rtx scratch) ...@@ -8293,20 +8660,45 @@ mips_set_return_address (rtx address, rtx scratch)
mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address); mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
} }
/* Restore $gp from its save slot. Valid only when using o32 or /* Return a MEM rtx for the cprestore slot, using TEMP as a temporary base
o64 abicalls. */ register if need be. */
void static rtx
mips_restore_gp (void) 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); gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
base = frame_pointer_needed ? hard_frame_pointer_rtx : stack_pointer_rtx; if (TARGET_MIPS16)
address = mips_add_offset (pic_offset_table_rtx, base, {
crtl->outgoing_args_size); mips_emit_move (temp, mips_cprestore_slot (temp));
mips_emit_move (pic_offset_table_rtx, gen_frame_mem (Pmode, address)); mips_emit_move (pic_offset_table_rtx, temp);
}
else
mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp));
if (!TARGET_EXPLICIT_RELOCS) if (!TARGET_EXPLICIT_RELOCS)
emit_insn (gen_blockage ()); emit_insn (gen_blockage ());
} }
...@@ -8448,8 +8840,18 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) ...@@ -8448,8 +8840,18 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
that need it. */ that need it. */
if (mips_current_loadgp_style () == LOADGP_OLDABI) 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. */ /* .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); output_asm_insn ("%(.cpload\t%^%)", 0);
else else
output_asm_insn ("%(.cpload\t%^\n\t%<", 0); output_asm_insn ("%(.cpload\t%^\n\t%<", 0);
...@@ -8541,7 +8943,7 @@ mips_emit_loadgp (void) ...@@ -8541,7 +8943,7 @@ mips_emit_loadgp (void)
{ {
rtx addr, offset, incoming_address, base, index, pic_reg; 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 ()) switch (mips_current_loadgp_style ())
{ {
case LOADGP_ABSOLUTE: case LOADGP_ABSOLUTE:
...@@ -8555,6 +8957,10 @@ mips_emit_loadgp (void) ...@@ -8555,6 +8957,10 @@ mips_emit_loadgp (void)
: gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp)); : gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp));
break; break;
case LOADGP_OLDABI:
/* Added by mips_output_function_prologue. */
break;
case LOADGP_NEWABI: case LOADGP_NEWABI:
addr = XEXP (DECL_RTL (current_function_decl), 0); addr = XEXP (DECL_RTL (current_function_decl), 0);
offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP); offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
...@@ -8575,6 +8981,10 @@ mips_emit_loadgp (void) ...@@ -8575,6 +8981,10 @@ mips_emit_loadgp (void)
default: default:
return; 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. /* Emit a blockage if there are implicit uses of the GP register.
This includes profiled functions, because FUNCTION_PROFILE uses This includes profiled functions, because FUNCTION_PROFILE uses
a jal macro. */ a jal macro. */
...@@ -8710,7 +9120,13 @@ mips_expand_prologue (void) ...@@ -8710,7 +9120,13 @@ mips_expand_prologue (void)
/* Initialize the $gp save slot. */ /* Initialize the $gp save slot. */
if (frame->cprestore_size > 0) 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 /* If we are profiling, make sure no instructions are scheduled before
the call to mcount. */ the call to mcount. */
...@@ -11462,6 +11878,7 @@ mips16_lay_out_constants (void) ...@@ -11462,6 +11878,7 @@ mips16_lay_out_constants (void)
if (!TARGET_MIPS16_PCREL_LOADS) if (!TARGET_MIPS16_PCREL_LOADS)
return; return;
split_all_insns_noflow ();
barrier = 0; barrier = 0;
memset (&pool, 0, sizeof (pool)); memset (&pool, 0, sizeof (pool));
for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
...@@ -12096,6 +12513,11 @@ mips_reorg_process_insns (void) ...@@ -12096,6 +12513,11 @@ mips_reorg_process_insns (void)
cfun->machine->all_noreorder_p = true; 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". */ /* Code that doesn't use explicit relocs can't be ".set nomacro". */
if (!TARGET_EXPLICIT_RELOCS) if (!TARGET_EXPLICIT_RELOCS)
cfun->machine->all_noreorder_p = false; cfun->machine->all_noreorder_p = false;
...@@ -12185,7 +12607,10 @@ mips_reorg (void) ...@@ -12185,7 +12607,10 @@ mips_reorg (void)
if (mips_base_delayed_branch) if (mips_base_delayed_branch)
dbr_schedule (get_insns ()); dbr_schedule (get_insns ());
mips_reorg_process_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 (); vr4130_align_insns ();
} }
...@@ -12212,23 +12637,18 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, ...@@ -12212,23 +12637,18 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
&& const_call_insn_operand (fnaddr, Pmode)); && const_call_insn_operand (fnaddr, Pmode));
/* Determine if we need to load FNADDR from the GOT. */ /* Determine if we need to load FNADDR from the GOT. */
if (!use_sibcall_p) if (!use_sibcall_p
switch (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA)) && (mips_got_symbol_type_p
(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 /* Pick a global pointer. Use a call-clobbered register if
TARGET_CALL_SAVED_GP. */ TARGET_CALL_SAVED_GP. */
cfun->machine->global_pointer = cfun->machine->global_pointer
TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM; = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer); SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
/* Set up the global pointer for n32 or n64 abicalls. */ /* Set up the global pointer for n32 or n64 abicalls. */
mips_emit_loadgp (); mips_emit_loadgp ();
break;
default:
break;
} }
/* We need two temporary registers in some cases. */ /* We need two temporary registers in some cases. */
...@@ -12288,12 +12708,17 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, ...@@ -12288,12 +12708,17 @@ mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
We must therefore load the address via a temporary We must therefore load the address via a temporary
register if mips_dangerous_for_la25_p. register if mips_dangerous_for_la25_p.
If we jump to the temporary register rather than $25, the assembler If we jump to the temporary register rather than $25,
can use the move insn to fill the jump's delay slot. */ 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 if (TARGET_USE_PIC_FN_ADDR_REG
&& !TARGET_MIPS16
&& !mips_dangerous_for_la25_p (fnaddr)) && !mips_dangerous_for_la25_p (fnaddr))
temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); 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 if (TARGET_USE_PIC_FN_ADDR_REG
&& REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM) && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
...@@ -12367,11 +12792,7 @@ mips_set_mips16_mode (int mips16_p) ...@@ -12367,11 +12792,7 @@ mips_set_mips16_mode (int mips16_p)
call. */ call. */
flag_move_loop_invariants = 0; flag_move_loop_invariants = 0;
/* Silently disable -mexplicit-relocs since it doesn't apply target_flags |= MASK_EXPLICIT_RELOCS;
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;
/* Experiments suggest we get the best overall section-anchor /* Experiments suggest we get the best overall section-anchor
results from using the range of an unextended LW or SW. Code results from using the range of an unextended LW or SW. Code
...@@ -12381,8 +12802,11 @@ mips_set_mips16_mode (int mips16_p) ...@@ -12381,8 +12802,11 @@ mips_set_mips16_mode (int mips16_p)
targetm.min_anchor_offset = 0; targetm.min_anchor_offset = 0;
targetm.max_anchor_offset = 127; targetm.max_anchor_offset = 127;
if (flag_pic || TARGET_ABICALLS) if (flag_pic && !TARGET_OLDABI)
sorry ("MIPS16 PIC"); sorry ("MIPS16 PIC for ABIs other than o32 and o64");
if (TARGET_XGOT)
sorry ("MIPS16 -mxgot code");
if (TARGET_HARD_FLOAT_ABI && !TARGET_OLDABI) if (TARGET_HARD_FLOAT_ABI && !TARGET_OLDABI)
sorry ("hard-float MIPS16 code for ABIs other than o32 and o64"); sorry ("hard-float MIPS16 code for ABIs other than o32 and o64");
......
...@@ -1659,16 +1659,29 @@ enum mips_code_readable_setting { ...@@ -1659,16 +1659,29 @@ enum mips_code_readable_setting {
/* Register in which static-chain is passed to a function. */ /* Register in which static-chain is passed to a function. */
#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 15) #define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 15)
/* Registers used as temporaries in prologue/epilogue code. If we're /* Registers used as temporaries in prologue/epilogue code:
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. */
- 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_PROLOGUE_TEMP_REGNUM (GP_REG_FIRST + 3)
#define MIPS_EPILOGUE_TEMP_REGNUM (GP_REG_FIRST + (TARGET_MIPS16 ? 6 : 8)) #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_PROLOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_PROLOGUE_TEMP_REGNUM)
#define MIPS_EPILOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_EPILOGUE_TEMP_REGNUM) #define MIPS_EPILOGUE_TEMP(MODE) gen_rtx_REG (MODE, MIPS_EPILOGUE_TEMP_REGNUM)
...@@ -1716,7 +1729,6 @@ enum mips_code_readable_setting { ...@@ -1716,7 +1729,6 @@ enum mips_code_readable_setting {
enum reg_class enum reg_class
{ {
NO_REGS, /* no registers in set */ NO_REGS, /* no registers in set */
M16_NA_REGS, /* mips16 regs not used to pass args */
M16_REGS, /* mips16 directly accessible registers */ M16_REGS, /* mips16 directly accessible registers */
T_REG, /* mips16 T register ($24) */ T_REG, /* mips16 T register ($24) */
M16_T_REGS, /* mips16 registers plus T register */ M16_T_REGS, /* mips16 registers plus T register */
...@@ -1757,7 +1769,6 @@ enum reg_class ...@@ -1757,7 +1769,6 @@ enum reg_class
#define REG_CLASS_NAMES \ #define REG_CLASS_NAMES \
{ \ { \
"NO_REGS", \ "NO_REGS", \
"M16_NA_REGS", \
"M16_REGS", \ "M16_REGS", \
"T_REG", \ "T_REG", \
"M16_T_REGS", \ "M16_T_REGS", \
...@@ -1801,7 +1812,6 @@ enum reg_class ...@@ -1801,7 +1812,6 @@ enum reg_class
#define REG_CLASS_CONTENTS \ #define REG_CLASS_CONTENTS \
{ \ { \
{ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* no registers */ \ { 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 */ \ { 0x000300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 registers */ \
{ 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 T register */ \ { 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 T register */ \
{ 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 and T regs */ \ { 0x010300fc, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, /* mips16 and T regs */ \
...@@ -2419,6 +2429,11 @@ typedef struct mips_args { ...@@ -2419,6 +2429,11 @@ typedef struct mips_args {
#define SYMBOL_REF_LONG_CALL_P(X) \ #define SYMBOL_REF_LONG_CALL_P(X) \
((SYMBOL_REF_FLAGS (X) & SYMBOL_FLAG_LONG_CALL) != 0) ((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 /* 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 are stored in the text section and encoded as 16-bit PC-relative
offsets. This is only possible when general text loads are allowed, offsets. This is only possible when general text loads are allowed,
...@@ -3280,6 +3295,7 @@ extern int set_nomacro; /* # of nested .set nomacro's */ ...@@ -3280,6 +3295,7 @@ extern int set_nomacro; /* # of nested .set nomacro's */
extern int mips_dbx_regno[]; extern int mips_dbx_regno[];
extern int mips_dwarf_regno[]; extern int mips_dwarf_regno[];
extern bool mips_split_p[]; extern bool mips_split_p[];
extern bool mips_split_hi_p[];
extern GTY(()) rtx cmp_operands[2]; extern GTY(()) rtx cmp_operands[2];
extern enum processor_type mips_arch; /* which cpu to codegen for */ extern enum processor_type mips_arch; /* which cpu to codegen for */
extern enum processor_type mips_tune; /* which cpu to schedule for */ extern enum processor_type mips_tune; /* which cpu to schedule for */
......
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
(UNSPEC_MEMORY_BARRIER 45) (UNSPEC_MEMORY_BARRIER 45)
(UNSPEC_SET_GOT_VERSION 46) (UNSPEC_SET_GOT_VERSION 46)
(UNSPEC_UPDATE_GOT_VERSION 47) (UNSPEC_UPDATE_GOT_VERSION 47)
(UNSPEC_COPYGP 48)
(UNSPEC_ADDRESS_FIRST 100) (UNSPEC_ADDRESS_FIRST 100)
...@@ -478,7 +479,9 @@ ...@@ -478,7 +479,9 @@
(const_int 0) (const_int 0)
(eq_attr "got" "load") (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") (eq_attr "got" "xgot_high")
(const_int 8) (const_int 8)
...@@ -3590,15 +3593,11 @@ ...@@ -3590,15 +3593,11 @@
(define_insn_and_split "*got_disp<mode>" (define_insn_and_split "*got_disp<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(set (match_operand:P 0 "register_operand" "=d")
(match_operand:P 1 "got_disp_operand" ""))] (match_operand:P 1 "got_disp_operand" ""))]
"TARGET_EXPLICIT_RELOCS && !TARGET_XGOT" "TARGET_EXPLICIT_RELOCS && !mips_split_p[SYMBOL_GOT_DISP]"
"#" "#"
"&& reload_completed" "&& reload_completed"
[(set (match_dup 0) [(set (match_dup 0) (match_dup 2))]
(unspec:P [(match_dup 2) (match_dup 3)] UNSPEC_LOAD_GOT))] { operands[2] = mips_got_load (NULL, operands[1], SYMBOL_GOTOFF_DISP); }
{
operands[2] = pic_offset_table_rtx;
operands[3] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_DISP);
}
[(set_attr "got" "load") [(set_attr "got" "load")
(set_attr "mode" "<MODE>")]) (set_attr "mode" "<MODE>")])
...@@ -3607,18 +3606,19 @@ ...@@ -3607,18 +3606,19 @@
(define_insn_and_split "*got_page<mode>" (define_insn_and_split "*got_page<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(set (match_operand:P 0 "register_operand" "=d")
(high:P (match_operand:P 1 "got_page_ofst_operand" "")))] (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" "&& reload_completed"
[(set (match_dup 0) [(set (match_dup 0) (match_dup 2))]
(unspec:P [(match_dup 2) (match_dup 3)] UNSPEC_LOAD_GOT))] { operands[2] = mips_got_load (NULL, operands[1], SYMBOL_GOTOFF_PAGE); }
{
operands[2] = pic_offset_table_rtx;
operands[3] = mips_unspec_address (operands[1], SYMBOL_GOTOFF_PAGE);
}
[(set_attr "got" "load") [(set_attr "got" "load")
(set_attr "mode" "<MODE>")]) (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. ;; Lower-level instructions for loading an address from the GOT.
;; We could use MEMs, but an unspec gives more optimization ;; We could use MEMs, but an unspec gives more optimization
;; opportunities. ;; opportunities.
...@@ -3630,9 +3630,8 @@ ...@@ -3630,9 +3630,8 @@
UNSPEC_LOAD_GOT))] UNSPEC_LOAD_GOT))]
"" ""
"<load>\t%0,%R2(%1)" "<load>\t%0,%R2(%1)"
[(set_attr "type" "load") [(set_attr "got" "load")
(set_attr "mode" "<MODE>") (set_attr "mode" "<MODE>")])
(set_attr "length" "4")])
;; Instructions for adding the low 16 bits of an address to a register. ;; 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 ;; Operand 2 is the address: mips_print_operand works out which relocation
...@@ -3657,6 +3656,15 @@ ...@@ -3657,6 +3656,15 @@
(set_attr "mode" "<MODE>") (set_attr "mode" "<MODE>")
(set_attr "extended_mips16" "yes")]) (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 ;; Allow combine to split complex const_int load sequences, using operand 2
;; to store the intermediate results. See move_operand for details. ;; to store the intermediate results. See move_operand for details.
(define_split (define_split
...@@ -4521,6 +4529,18 @@ ...@@ -4521,6 +4529,18 @@
} }
[(set_attr "length" "12")]) [(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 ;; Emit a .cprestore directive, which normally expands to a single store
;; instruction. Note that we continue to use .cprestore for explicit reloc ;; instruction. Note that we continue to use .cprestore for explicit reloc
;; code so that jals inside inline asms will work correctly. ;; code so that jals inside inline asms will work correctly.
...@@ -5981,13 +6001,14 @@ ...@@ -5981,13 +6001,14 @@
;; volatile until all uses of $28 are exposed. ;; volatile until all uses of $28 are exposed.
(define_insn_and_split "restore_gp" (define_insn_and_split "restore_gp"
[(set (reg:SI 28) [(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" "TARGET_CALL_CLOBBERED_GP"
"#" "#"
"&& reload_completed" "&& reload_completed"
[(const_int 0)] [(const_int 0)]
{ {
mips_restore_gp (); mips_restore_gp (operands[0]);
DONE; DONE;
} }
[(set_attr "type" "load") [(set_attr "type" "load")
...@@ -6043,16 +6064,22 @@ ...@@ -6043,16 +6064,22 @@
;; - Leave GOT_VERSION_REGNUM out of all register classes. ;; - Leave GOT_VERSION_REGNUM out of all register classes.
;; The register is therefore not a valid register_operand ;; The register is therefore not a valid register_operand
;; and cannot be moved to or from other registers. ;; 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>" (define_insn "load_call<mode>"
[(set (match_operand:P 0 "register_operand" "=d") [(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" "") (match_operand:P 2 "immediate_operand" "")
(reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))] (reg:SI GOT_VERSION_REGNUM)] UNSPEC_LOAD_CALL))]
"TARGET_USE_GOT" "TARGET_USE_GOT"
"<load>\t%0,%R2(%1)" "<load>\t%0,%R2(%1)"
[(set_attr "type" "load") [(set_attr "got" "load")
(set_attr "mode" "<MODE>") (set_attr "mode" "<MODE>")])
(set_attr "length" "4")])
(define_insn "set_got_version" (define_insn "set_got_version"
[(set (reg:SI GOT_VERSION_REGNUM) [(set (reg:SI GOT_VERSION_REGNUM)
...@@ -6088,7 +6115,8 @@ ...@@ -6088,7 +6115,8 @@
(use (match_operand 3 ""))])] ;; struct_value_size_rtx (use (match_operand 3 ""))])] ;; struct_value_size_rtx
"TARGET_SIBCALLS" "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; DONE;
}) })
...@@ -6106,8 +6134,8 @@ ...@@ -6106,8 +6134,8 @@
(use (match_operand 3 ""))])] ;; next_arg_reg (use (match_operand 3 ""))])] ;; next_arg_reg
"TARGET_SIBCALLS" "TARGET_SIBCALLS"
{ {
mips_expand_call (operands[0], XEXP (operands[1], 0), mips_expand_call (MIPS_CALL_SIBCALL, operands[0], XEXP (operands[1], 0),
operands[2], operands[3], true); operands[2], operands[3], false);
DONE; DONE;
}) })
...@@ -6137,7 +6165,8 @@ ...@@ -6137,7 +6165,8 @@
(use (match_operand 3 ""))])] ;; struct_value_size_rtx (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; DONE;
}) })
...@@ -6187,28 +6216,44 @@ ...@@ -6187,28 +6216,44 @@
"reload_completed && TARGET_SPLIT_CALLS && (operands[2] = insn)" "reload_completed && TARGET_SPLIT_CALLS && (operands[2] = insn)"
[(const_int 0)] [(const_int 0)]
{ {
emit_call_insn (gen_call_split (operands[0], operands[1])); mips_split_call (operands[2], gen_call_split (operands[0], operands[1]));
if (!find_reg_note (operands[2], REG_NORETURN, 0))
mips_restore_gp ();
DONE; DONE;
} }
[(set_attr "jal" "indirect,direct")]) [(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 ;; 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 ;; 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 ;; stub; the linker relies on the call relocation type to detect when
;; such redirection is needed. ;; 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")) [(call (mem:SI (match_operand 0 "const_call_insn_operand"))
(match_operand 1)) (match_operand 1))
(const_int 1) (const_int 1)
(clobber (reg:SI 31))] (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" (define_insn "call_direct_split"
[(call (mem:SI (match_operand 0 "call_insn_operand" "cS")) [(call (mem:SI (match_operand 0 "const_call_insn_operand"))
(match_operand 1 "" "")) (match_operand 1))
(const_int 1)
(clobber (reg:SI 31)) (clobber (reg:SI 31))
(clobber (reg:SI 28))] (clobber (reg:SI 28))]
"TARGET_SPLIT_CALLS" "TARGET_SPLIT_CALLS"
...@@ -6222,7 +6267,7 @@ ...@@ -6222,7 +6267,7 @@
(use (match_operand 3 ""))])] ;; next_arg_reg (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); operands[2], operands[3], false);
DONE; DONE;
}) })
...@@ -6238,10 +6283,9 @@ ...@@ -6238,10 +6283,9 @@
"reload_completed && TARGET_SPLIT_CALLS && (operands[3] = insn)" "reload_completed && TARGET_SPLIT_CALLS && (operands[3] = insn)"
[(const_int 0)] [(const_int 0)]
{ {
emit_call_insn (gen_call_value_split (operands[0], operands[1], mips_split_call (operands[3],
gen_call_value_split (operands[0], operands[1],
operands[2])); operands[2]));
if (!find_reg_note (operands[3], REG_NORETURN, 0))
mips_restore_gp ();
DONE; DONE;
} }
[(set_attr "jal" "indirect,direct")]) [(set_attr "jal" "indirect,direct")])
...@@ -6257,14 +6301,34 @@ ...@@ -6257,14 +6301,34 @@
[(set_attr "type" "call")]) [(set_attr "type" "call")])
;; See call_internal_direct. ;; See call_internal_direct.
(define_insn "call_value_internal_direct" (define_insn_and_split "call_value_internal_direct"
[(set (match_operand 0 "register_operand") [(set (match_operand 0 "register_operand")
(call (mem:SI (match_operand 1 "const_call_insn_operand")) (call (mem:SI (match_operand 1 "const_call_insn_operand"))
(match_operand 2))) (match_operand 2)))
(const_int 1) (const_int 1)
(clobber (reg:SI 31))] (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. ;; See comment for call_internal.
(define_insn_and_split "call_value_multiple_internal" (define_insn_and_split "call_value_multiple_internal"
...@@ -6280,10 +6344,9 @@ ...@@ -6280,10 +6344,9 @@
"reload_completed && TARGET_SPLIT_CALLS && (operands[4] = insn)" "reload_completed && TARGET_SPLIT_CALLS && (operands[4] = insn)"
[(const_int 0)] [(const_int 0)]
{ {
emit_call_insn (gen_call_value_multiple_split (operands[0], operands[1], mips_split_call (operands[4],
gen_call_value_multiple_split (operands[0], operands[1],
operands[2], operands[3])); operands[2], operands[3]));
if (!find_reg_note (operands[4], REG_NORETURN, 0))
mips_restore_gp ();
DONE; DONE;
} }
[(set_attr "jal" "indirect,direct")]) [(set_attr "jal" "indirect,direct")])
......
...@@ -38,6 +38,8 @@ Boston, MA 02110-1301, USA. */ ...@@ -38,6 +38,8 @@ Boston, MA 02110-1301, USA. */
values using the soft-float calling convention, but do the actual values using the soft-float calling convention, but do the actual
operation using the hard floating point instructions. */ operation using the hard floating point instructions. */
#if defined _MIPS_SIM && (_MIPS_SIM == _ABIO32 || _MIPS_SIM == _ABIO64)
/* This file contains 32-bit assembly code. */ /* This file contains 32-bit assembly code. */
.set nomips16 .set nomips16
...@@ -303,8 +305,12 @@ STARTFN (__mips16_floatsisf) ...@@ -303,8 +305,12 @@ STARTFN (__mips16_floatsisf)
#ifdef L_m16fltunsisf #ifdef L_m16fltunsisf
STARTFN (__mips16_floatunsisf) STARTFN (__mips16_floatunsisf)
.set noreorder
bltz $4,1f bltz $4,1f
j __mips16_floatsisf MOVE_SF_BYTE0 (t)
.set reorder
cvt.s.w RET,ARG1
MOVE_SF_RET (f, $31)
1: 1:
and $2,$4,1 and $2,$4,1
srl $3,$4,1 srl $3,$4,1
...@@ -522,7 +528,10 @@ RET_FUNCTION (__mips16_ret_dc, DC) ...@@ -522,7 +528,10 @@ RET_FUNCTION (__mips16_ret_dc, DC)
#define CALL_STUB_NO_RET(NAME, CODE) \ #define CALL_STUB_NO_RET(NAME, CODE) \
STARTFN (NAME); \ STARTFN (NAME); \
STUB_ARGS_##CODE; \ STUB_ARGS_##CODE; \
.set noreorder; \
jr $2; \ jr $2; \
move $25,$2; \
.set reorder; \
ENDFN (NAME) ENDFN (NAME)
#ifdef L_m16stub1 #ifdef L_m16stub1
...@@ -569,7 +578,10 @@ CALL_STUB_NO_RET (__mips16_call_stub_10, 10) ...@@ -569,7 +578,10 @@ CALL_STUB_NO_RET (__mips16_call_stub_10, 10)
STARTFN (NAME); \ STARTFN (NAME); \
move $18,$31; \ move $18,$31; \
STUB_ARGS_##CODE; \ STUB_ARGS_##CODE; \
.set noreorder; \
jalr $2; \ jalr $2; \
move $25,$2; \
.set reorder; \
MOVE_##MODE##_RET (f, $18); \ MOVE_##MODE##_RET (f, $18); \
ENDFN (NAME) ENDFN (NAME)
...@@ -705,3 +717,4 @@ CALL_STUB_RET (__mips16_call_stub_dc_9, 9, DC) ...@@ -705,3 +717,4 @@ CALL_STUB_RET (__mips16_call_stub_dc_9, 9, DC)
CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC) CALL_STUB_RET (__mips16_call_stub_dc_10, 10, DC)
#endif #endif
#endif /* !__mips_single_float */ #endif /* !__mips_single_float */
#endif
...@@ -104,14 +104,9 @@ ...@@ -104,14 +104,9 @@
switch (symbol_type) switch (symbol_type)
{ {
case SYMBOL_ABSOLUTE: case SYMBOL_ABSOLUTE:
/* We can only use direct calls for TARGET_ABSOLUTE_ABICALLS if we /* We can only use direct calls if we're sure that the target
are sure that the target function does not need $25 to be live function does not need $25 to be valid on entry. */
on entry. This is true for any locally-defined function because if (mips_use_pic_fn_addr_reg_p (op))
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))))
return false; return false;
/* If -mlong-calls or if this function has an explicit long_call /* If -mlong-calls or if this function has an explicit long_call
...@@ -206,6 +201,11 @@ ...@@ -206,6 +201,11 @@
return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type) return (mips_symbolic_constant_p (op, SYMBOL_CONTEXT_LEA, &symbol_type)
&& !mips_split_p[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: default:
return true; return true;
} }
......
...@@ -22,3 +22,6 @@ LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \ ...@@ -22,3 +22,6 @@ LIB1ASMFUNCS = _m16addsf3 _m16subsf3 _m16mulsf3 _m16divsf3 \
LIBGCC_SYNC = yes LIBGCC_SYNC = yes
LIBGCC_SYNC_CFLAGS = -mno-mips16 LIBGCC_SYNC_CFLAGS = -mno-mips16
# Version these symbols if building libgcc.so.
SHLIB_MAPFILES += $(srcdir)/config/mips/libgcc-mips16.ver
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> 2008-08-09 Richard Guenther <rguenther@suse.de>
* gcc.dg/tree-ssa/inline-2.c: New testcase. * gcc.dg/tree-ssa/inline-2.c: New testcase.
......
...@@ -439,6 +439,14 @@ proc check_profiling_available { test_what } { ...@@ -439,6 +439,14 @@ proc check_profiling_available { test_what } {
return 0 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. # MinGW does not support -p.
if { [istarget *-*-mingw*] && [lindex $test_what 1] == "-p" } { if { [istarget *-*-mingw*] && [lindex $test_what 1] == "-p" } {
return 0 return 0
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment