From b85aed9ef750a096fcaf48f433644817a04faf35 Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Sun, 18 Jan 2004 09:42:11 +0000 Subject: [PATCH] mips.c: Include cfglayout.h. * config/mips/mips.c: Include cfglayout.h. (TARGET_ASM_OUTPUT_MI_THUNK, TARGET_ASM_CAN_OUTPUT_MI_THUNK): Define. (mips_unspec_offset_high): Add temporary register argument. (mips_load_call_address): New function, split out from... (mips_expand_call): ...here. (mips_output_cplocal): New function. (mips_output_function_prologue, mips_output_function_epilogue): Use it. (mips_emit_loadgp): New function, split out from... (mips_expand_prologue): ...here. (mips_output_mi_thunk): New function. From-SVN: r76088 --- gcc/ChangeLog | 13 +++ gcc/config/mips/mips.c | 257 ++++++++++++++++++++++++++++++++++------- 2 files changed, 230 insertions(+), 40 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1bdd7ea46c4..04568ec9163 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2004-01-18 Richard Sandiford + + * config/mips/mips.c: Include cfglayout.h. + (TARGET_ASM_OUTPUT_MI_THUNK, TARGET_ASM_CAN_OUTPUT_MI_THUNK): Define. + (mips_unspec_offset_high): Add temporary register argument. + (mips_load_call_address): New function, split out from... + (mips_expand_call): ...here. + (mips_output_cplocal): New function. + (mips_output_function_prologue, mips_output_function_epilogue): Use it. + (mips_emit_loadgp): New function, split out from... + (mips_expand_prologue): ...here. + (mips_output_mi_thunk): New function. + 2004-01-17 Bernardo Innocenti * longlong.h (mc68020, __mc68030__, mc68030, __mc68040__, mc68040, diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index 477f98964aa..131c5594015 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -53,6 +53,7 @@ Boston, MA 02111-1307, USA. */ #include "target-def.h" #include "integrate.h" #include "langhooks.h" +#include "cfglayout.h" /* Enumeration for all of the relational tests, so that we can build arrays indexed by the test type, and not worry about the order @@ -200,7 +201,7 @@ static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx); static rtx mips_force_temporary (rtx, rtx); static rtx mips_split_symbol (rtx, rtx); static rtx mips_unspec_address (rtx, enum mips_symbol_type); -static rtx mips_unspec_offset_high (rtx, rtx, enum mips_symbol_type); +static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type); static rtx mips_load_got (rtx, rtx, enum mips_symbol_type); static rtx mips_add_offset (rtx, HOST_WIDE_INT); static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT); @@ -216,6 +217,7 @@ static int mips_address_cost (rtx); static enum internal_test map_test_to_internal_test (enum rtx_code); static void get_float_compare_codes (enum rtx_code, enum rtx_code *, enum rtx_code *); +static void mips_load_call_address (rtx, rtx, int); static bool mips_function_ok_for_sibcall (tree, tree); static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT); static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *); @@ -239,12 +241,16 @@ static bool mips_save_reg_p (unsigned int); static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT, mips_save_restore_fn); static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn); +static void mips_output_cplocal (void); +static void mips_emit_loadgp (void); static void mips_output_function_prologue (FILE *, HOST_WIDE_INT); static void mips_set_frame_expr (rtx); static rtx mips_frame_set (rtx, rtx); static void mips_save_reg (rtx, rtx); static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT); static void mips_restore_reg (rtx, rtx); +static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, + HOST_WIDE_INT, tree); static int symbolic_expression_p (rtx); static void mips_select_rtx_section (enum machine_mode, rtx, unsigned HOST_WIDE_INT); @@ -792,6 +798,11 @@ const struct mips_cpu_info mips_cpu_info_table[] = { #undef TARGET_RETURN_IN_MSB #define TARGET_RETURN_IN_MSB mips_return_in_msb +#undef TARGET_ASM_OUTPUT_MI_THUNK +#define TARGET_ASM_OUTPUT_MI_THUNK mips_output_mi_thunk +#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK +#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true + struct gcc_target targetm = TARGET_INITIALIZER; /* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */ @@ -1648,17 +1659,19 @@ mips_unspec_address (rtx address, enum mips_symbol_type symbol_type) /* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the high part to BASE and return the result. Just return BASE otherwise. + TEMP is available as a temporary register if needed. The returned expression can be used as the first operand to a LO_SUM. */ static rtx -mips_unspec_offset_high (rtx base, rtx addr, enum mips_symbol_type symbol_type) +mips_unspec_offset_high (rtx temp, rtx base, rtx addr, + enum mips_symbol_type symbol_type) { if (mips_split_p[symbol_type]) { addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type)); - base = force_reg (Pmode, expand_simple_binop (Pmode, PLUS, base, addr, - NULL, 0, OPTAB_WIDEN)); + addr = mips_force_temporary (temp, addr); + return mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base)); } return base; } @@ -3192,6 +3205,35 @@ mips_gen_conditional_trap (rtx *operands) operands[1])); } +/* Load function address ADDR into register DEST. SIBCALL_P is true + if the address is needed for a sibling call. */ + +static void +mips_load_call_address (rtx dest, rtx addr, int sibcall_p) +{ + /* 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 NewABI sibcalls since the value of $gp on entry + to the stub would be our caller's gp, not ours. */ + if (TARGET_EXPLICIT_RELOCS + && !(sibcall_p && TARGET_NEWABI) + && global_got_operand (addr, VOIDmode)) + { + 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)); + } + else + emit_move_insn (dest, addr); +} + + /* Expand a call or call_value instruction. RESULT is where the result will go (null for calls), ADDR is the address of the function, ARGS_SIZE is the size of the arguments and AUX is @@ -3204,27 +3246,9 @@ mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p) { if (!call_insn_operand (addr, VOIDmode)) { - /* 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 NewABI sibcalls since the value of $gp on entry - to the stub would be our caller's gp, not ours. */ - if (TARGET_EXPLICIT_RELOCS - && !(sibcall_p && TARGET_NEWABI) - && global_got_operand (addr, VOIDmode)) - { - rtx high, lo_sum_symbol; - - high = mips_unspec_offset_high (pic_offset_table_rtx, - addr, SYMBOL_GOTOFF_CALL); - lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL); - addr = gen_reg_rtx (Pmode); - if (Pmode == SImode) - emit_insn (gen_load_callsi (addr, high, lo_sum_symbol)); - else - emit_insn (gen_load_calldi (addr, high, lo_sum_symbol)); - } - else - addr = force_reg (Pmode, addr); + rtx dest = gen_reg_rtx (Pmode); + mips_load_call_address (dest, addr, sibcall_p); + addr = dest; } if (TARGET_MIPS16 @@ -6466,6 +6490,38 @@ mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn) #undef BITSET_P } +/* If we're generating n32 or n64 abicalls, and the current function + does not use $28 as its global pointer, emit a cplocal directive. + Use pic_offset_table_rtx as the argument to the directive. */ + +static void +mips_output_cplocal (void) +{ + if (!TARGET_EXPLICIT_RELOCS + && cfun->machine->global_pointer > 0 + && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM) + output_asm_insn (".cplocal %+", 0); +} + +/* If we're generating n32 or n64 abicalls, emit instructions + to set up the global pointer. */ + +static void +mips_emit_loadgp (void) +{ + if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0) + { + rtx addr, offset, incoming_address; + + addr = XEXP (DECL_RTL (current_function_decl), 0); + offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP); + incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); + emit_insn (gen_loadgp (offset, incoming_address)); + if (!TARGET_EXPLICIT_RELOCS) + emit_insn (gen_loadgp_blockage ()); + } +} + /* Set up the stack and frame (if desired) for the function. */ static void @@ -6554,6 +6610,11 @@ mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED) } else if (cfun->machine->all_noreorder_p) output_asm_insn ("%(%<", 0); + + /* Tell the assembler which register we're using as the global + pointer. This is needed for thunks, since they can use either + explicit relocs or assembler macros. */ + mips_output_cplocal (); } /* Make the last instruction frame related and note that it performs @@ -6709,18 +6770,7 @@ mips_expand_prologue (void) if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf) emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size))); - /* If generating n32/n64 abicalls, emit the instructions to load $gp. */ - if (TARGET_ABICALLS && TARGET_NEWABI && cfun->machine->global_pointer > 0) - { - rtx addr, offset, incoming_address; - - addr = XEXP (DECL_RTL (current_function_decl), 0); - offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP); - incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); - emit_insn (gen_loadgp (offset, incoming_address)); - if (!TARGET_EXPLICIT_RELOCS) - emit_insn (gen_loadgp_blockage ()); - } + mips_emit_loadgp (); /* If we are profiling, make sure no instructions are scheduled before the call to mcount. */ @@ -6741,6 +6791,10 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, { rtx string; + /* Reinstate the normal $gp. */ + REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM; + mips_output_cplocal (); + if (cfun->machine->all_noreorder_p) { /* Avoid using %>%) since it adds excess whitespace. */ @@ -6781,9 +6835,6 @@ mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, for (string = mips16_strings; string != 0; string = XEXP (string, 1)) SYMBOL_REF_FLAG (XEXP (string, 0)) = 0; free_EXPR_LIST_list (&mips16_strings); - - /* Reinstate the normal $gp. */ - REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM; } /* Emit instructions to restore register REG from slot MEM. */ @@ -6954,6 +7005,132 @@ mips_can_use_return_insn (void) return compute_frame_size (get_frame_size ()) == 0; } +/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text + in order to avoid duplicating too much logic from elsewhere. */ + +static void +mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED, + HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset, + tree function) +{ + rtx this, temp1, temp2, insn, fnaddr; + + /* Pretend to be a post-reload pass while generating rtl. */ + no_new_pseudos = 1; + reload_completed = 1; + + /* Pick a global pointer for -mabicalls. Use $15 rather than $28 + for TARGET_NEWABI since the latter is a call-saved register. */ + if (TARGET_ABICALLS) + cfun->machine->global_pointer + = REGNO (pic_offset_table_rtx) + = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM; + + /* 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); + temp2 = gen_rtx_REG (Pmode, 3); + + /* Find out which register contains the "this" pointer. */ + if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)) + this = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1); + else + this = gen_rtx_REG (Pmode, GP_ARG_FIRST); + + /* Add DELTA to THIS. */ + if (delta != 0) + { + rtx offset = GEN_INT (delta); + if (!SMALL_OPERAND (delta)) + { + emit_move_insn (temp1, offset); + offset = temp1; + } + emit_insn (gen_add3_insn (this, this, offset)); + } + + /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */ + if (vcall_offset != 0) + { + rtx addr; + + /* Set TEMP1 to *THIS. */ + emit_move_insn (temp1, gen_rtx_MEM (Pmode, this)); + + /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */ + if (SMALL_OPERAND (vcall_offset)) + addr = gen_rtx_PLUS (Pmode, temp1, GEN_INT (vcall_offset)); + else if (TARGET_MIPS16) + { + /* Load the full offset into a register so that we can use + an unextended instruction for the load itself. */ + emit_move_insn (temp2, GEN_INT (vcall_offset)); + emit_insn (gen_add3_insn (temp1, temp1, temp2)); + addr = temp1; + } + else + { + /* Load the high part of the offset into a register and + leave the low part for the address. */ + emit_move_insn (temp2, GEN_INT (CONST_HIGH_PART (vcall_offset))); + emit_insn (gen_add3_insn (temp1, temp1, temp2)); + addr = gen_rtx_PLUS (Pmode, temp1, + GEN_INT (CONST_LOW_PART (vcall_offset))); + } + + /* Load the offset and add it to THIS. */ + emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr)); + emit_insn (gen_add3_insn (this, this, temp1)); + } + + /* Jump to the target function. Use a sibcall if direct jumps are + allowed, otherwise load the address into a register first. */ + fnaddr = XEXP (DECL_RTL (function), 0); + if (TARGET_MIPS16 || TARGET_ABICALLS || TARGET_LONG_CALLS) + { + /* This is messy. gas treats "la $25,foo" as part of a call + sequence and may allow a global "foo" to be lazily bound. + The general move patterns therefore reject this combination. + + In this context, lazy binding would actually be OK for o32 and o64, + but it's still wrong for n32 and n64; see mips_load_call_address. + 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 (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr)) + temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM); + mips_load_call_address (temp1, fnaddr, true); + + if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM) + emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1); + emit_jump_insn (gen_indirect_jump (temp1)); + } + else + { + insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx)); + SIBLING_CALL_P (insn) = 1; + } + + /* Run just enough of rest_of_compilation. This sequence was + "borrowed" from alpha.c. */ + insn = get_insns (); + insn_locators_initialize (); + split_all_insns_noflow (); + shorten_branches (insn); + final_start_function (insn, file, 1); + final (insn, file, 1, 0); + final_end_function (); + + /* Clean up the vars set above. Note that final_end_function resets + the global pointer for us. */ + reload_completed = 0; + no_new_pseudos = 0; +} + /* Returns nonzero if X contains a SYMBOL_REF. */ static int