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