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:
Richard Sandiford 2004-01-18 09:42:11 +00:00 committed by Richard Sandiford
parent e5d95b6993
commit b85aed9ef7
2 changed files with 230 additions and 40 deletions

View file

@ -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,

View file

@ -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