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
This commit is contained in:
parent
e5d95b6993
commit
b85aed9ef7
2 changed files with 230 additions and 40 deletions
|
@ -1,3 +1,16 @@
|
|||
2004-01-18 Richard Sandiford <rsandifo@redhat.com>
|
||||
|
||||
* 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 <bernie@develer.com>
|
||||
|
||||
* longlong.h (mc68020, __mc68030__, mc68030, __mc68040__, mc68040,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue