#include "langhooks.h"
#include "cfglayout.h"
#include "sched-int.h"
-#include "tree-gimple.h"
+#include "gimple.h"
#include "bitmap.h"
#include "diagnostic.h"
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. */
/* 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, M16_NA_REGS,
+ 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,
{ "mips32", PROCESSOR_4KC, 32, PTF_AVOID_BRANCHLIKELY },
{ "mips32r2", PROCESSOR_M4K, 33, PTF_AVOID_BRANCHLIKELY },
{ "mips64", PROCESSOR_5KC, 64, PTF_AVOID_BRANCHLIKELY },
+ /* ??? For now just tune the generic MIPS64r2 for 5KC as well. */
+ { "mips64r2", PROCESSOR_5KC, 65, PTF_AVOID_BRANCHLIKELY },
/* MIPS I processors. */
{ "r3000", PROCESSOR_R3000, 1, 0 },
}
}
\f
+/* 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
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
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
&& 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;
+
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ /* If PLTs and copy relocations are available, the static linker
+ will make sure that $25 is valid on entry to the target function. */
+ if (TARGET_ABICALLS_PIC0)
+ return false;
+
+ /* Locally-defined functions use absolute accesses to set up
+ the global pointer. */
+ if (TARGET_ABSOLUTE_ABICALLS
+ && 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. */
/* Don't use GOT accesses for locally-binding symbols when -mno-shared
is in effect. */
- if (TARGET_ABICALLS
+ if (TARGET_ABICALLS_PIC2
&& !(TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x)))
{
/* There are three cases to consider:
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;
- 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)
+ /* 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;
+
+ 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;
}
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)
{
{
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));
}
}
/* 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)
return insn;
}
\f
+/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE,
+ then add CONST_INT OFFSET to the result. */
+
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+ enum mips_symbol_type symbol_type)
+{
+ base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+ UNSPEC_ADDRESS_FIRST + symbol_type);
+ if (offset != const0_rtx)
+ base = gen_rtx_PLUS (Pmode, base, offset);
+ return gen_rtx_CONST (Pmode, base);
+}
+
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+ type SYMBOL_TYPE. */
+
+rtx
+mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
+{
+ rtx base, offset;
+
+ split_const (address, &base, &offset);
+ return mips_unspec_address_offset (base, offset, 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 as for mips_force_temporary.
+
+ The returned expression can be used as the first operand to a LO_SUM. */
+
+static rtx
+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));
+ addr = mips_force_temporary (temp, addr);
+ base = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
+ }
+ return base;
+}
+\f
/* Return an instruction that copies $gp into register REG. We want
GCC to treat the register's value as constant, so that its value
can be rematerialized on demand. */
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;
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;
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 ();
- }
- else
- {
- high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
- high = mips_force_temporary (temp, high);
+ 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;
}
- *lo_sum_out = gen_rtx_LO_SUM (Pmode, high, addr);
}
- return true;
-}
-
-/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE,
- then add CONST_INT OFFSET to the result. */
-
-static rtx
-mips_unspec_address_offset (rtx base, rtx offset,
- enum mips_symbol_type symbol_type)
-{
- base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
- UNSPEC_ADDRESS_FIRST + symbol_type);
- if (offset != const0_rtx)
- base = gen_rtx_PLUS (Pmode, base, offset);
- return gen_rtx_CONST (Pmode, base);
-}
-
-/* Return an UNSPEC address with underlying address ADDRESS and symbol
- type SYMBOL_TYPE. */
-
-rtx
-mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
-{
- rtx base, offset;
-
- split_const (address, &base, &offset);
- return mips_unspec_address_offset (base, offset, 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 as for mips_force_temporary.
+ else
+ {
+ if (mips_symbolic_constant_p (addr, context, &symbol_type)
+ && mips_symbol_insns (symbol_type, mode) > 0
+ && mips_split_p[symbol_type])
+ {
+ if (low_out)
+ switch (symbol_type)
+ {
+ case SYMBOL_GOT_DISP:
+ /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */
+ *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_DISP);
+ break;
- The returned expression can be used as the first operand to a LO_SUM. */
+ case SYMBOL_GP_RELATIVE:
+ high = mips_pic_base_register (temp);
+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+ break;
-static rtx
-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));
- addr = mips_force_temporary (temp, addr);
- base = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
+ 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;
+ }
}
- return base;
+ return false;
}
/* Return a legitimate address for REG + OFFSET. TEMP is as for
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 ();
{
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);
}
if (cum->stack_words > 0)
t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl), t,
size_int (cum->stack_words * UNITS_PER_WORD));
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovfl), ovfl, t);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
/* Emit code to initialize GTOP, the top of the GPR save area. */
t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (gtop), gtop, t);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
/* Emit code to initialize FTOP, the top of the FPR save area.
if (fpr_offset)
t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ftop), t,
size_int (-fpr_offset));
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ftop), ftop, t);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
/* Emit code to initialize GOFF, the offset from GTOP of the
next GPR argument. */
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (goff), goff,
+ t = build2 (MODIFY_EXPR, TREE_TYPE (goff), goff,
build_int_cst (TREE_TYPE (goff), gpr_save_area_size));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
/* Likewise emit code to initialize FOFF, the offset from FTOP
of the next FPR argument. */
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (foff), foff,
+ t = build2 (MODIFY_EXPR, TREE_TYPE (foff), foff,
build_int_cst (TREE_TYPE (foff), fpr_save_area_size));
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
/* Implement TARGET_GIMPLIFY_VA_ARG_EXPR. */
static tree
-mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
+mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
{
tree addr;
bool indirect_p;
/* [1] Emit code for: off &= -rsize. */
t = build2 (BIT_AND_EXPR, TREE_TYPE (off), off,
build_int_cst (NULL_TREE, -rsize));
- t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (off), off, t);
- gimplify_and_add (t, pre_p);
+ gimplify_assign (off, t, pre_p);
}
osize = rsize;
}
u = size_int (-osize);
t = build2 (BIT_AND_EXPR, sizetype, t, u);
t = fold_convert (TREE_TYPE (ovfl), t);
- align = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovfl), ovfl, t);
+ align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
}
else
align = NULL;
return addr;
}
\f
+/* Start a definition of function NAME. MIPS16_P indicates whether the
+ function contains MIPS16 code. */
+
+static void
+mips_start_function_definition (const char *name, bool mips16_p)
+{
+ if (mips16_p)
+ fprintf (asm_out_file, "\t.set\tmips16\n");
+ else
+ fprintf (asm_out_file, "\t.set\tnomips16\n");
+
+ if (!flag_inhibit_size_directive)
+ {
+ fputs ("\t.ent\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n", asm_out_file);
+ }
+
+ ASM_OUTPUT_TYPE_DIRECTIVE (asm_out_file, name, "function");
+
+ /* Start the definition proper. */
+ assemble_name (asm_out_file, name);
+ fputs (":\n", asm_out_file);
+}
+
+/* End a function definition started by mips_start_function_definition. */
+
+static void
+mips_end_function_definition (const char *name)
+{
+ if (!flag_inhibit_size_directive)
+ {
+ fputs ("\t.end\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n", asm_out_file);
+ }
+}
+\f
+/* Return true if calls to X can use R_MIPS_CALL* relocations. */
+
+static bool
+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. TYPE is as for
+ mips_expand_call. Return true if we used an explicit lazy-binding
+ sequence. */
+
+static bool
+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
+ && !(type == MIPS_CALL_SIBCALL && TARGET_CALL_SAVED_GP)
+ && mips_ok_for_lazy_binding_p (addr))
+ {
+ addr = mips_got_load (dest, addr, SYMBOL_GOTOFF_CALL);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, addr));
+ return true;
+ }
+ else
+ {
+ mips_emit_move (dest, addr);
+ return false;
+ }
+}
+\f
+/* 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;
+}
+\f
/* 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. */
};
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. */
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));
}
fprintf (asm_out_file, ")\n");
- /* Write the preamble leading up to the function declaration. */
- fprintf (asm_out_file, "\t.set\tnomips16\n");
- switch_to_section (function_section (stubdecl));
- ASM_OUTPUT_ALIGN (asm_out_file,
- floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT));
+ /* Start the function definition. */
+ assemble_start_function (stubdecl, stubname);
+ mips_start_function_definition (stubname, false);
- /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are
- within a .ent, and we cannot emit another .ent. */
- if (!FUNCTION_NAME_ALREADY_DECLARED)
+ /* If generating pic2 code, either set up the global pointer or
+ switch to pic0. */
+ if (TARGET_ABICALLS_PIC2)
{
- fputs ("\t.ent\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
+ if (TARGET_ABSOLUTE_ABICALLS)
+ fprintf (asm_out_file, "\t.option\tpic0\n");
+ else
+ {
+ 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;
+ }
}
- /* Start the definition proper. */
- assemble_name (asm_out_file, stubname);
- fputs (":\n", asm_out_file);
-
- /* Load the address of the MIPS16 function into $at. Do this first so
+ /* Load the address of the MIPS16 function into $25. 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]);
- fprintf (asm_out_file, "\t.set\tat\n");
+ output_asm_insn ("jr\t%^", NULL);
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.end\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
- }
+ if (TARGET_ABICALLS_PIC2 && 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));
}
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
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. */
/* 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
/* 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)
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
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;
}
fprintf (asm_out_file, ")\n");
- /* Write the preamble leading up to the function declaration. */
- fprintf (asm_out_file, "\t.set\tnomips16\n");
+ /* Start the function definition. */
assemble_start_function (stubdecl, stubname);
-
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.ent\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
-
- assemble_name (asm_out_file, stubname);
- fputs (":\n", asm_out_file);
- }
+ mips_start_function_definition (stubname, false);
if (!fp_ret_p)
{
- /* Load the address of the MIPS16 function into $at. Do this
+ /* Load the address of the MIPS16 function into $25. 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,%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
if (!fp_ret_p)
{
/* Jump to the previously-loaded address. */
- fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
- fprintf (asm_out_file, "\t.set\tat\n");
+ output_asm_insn ("jr\t%^", NULL);
}
else
{
$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. */
ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
#endif
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.end\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
- }
+ mips_end_function_definition (stubname);
/* Record this stub. */
l = XNEW (struct mips16_stub);
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
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;
}
\f
-/* Return true if calls to X can use R_MIPS_CALL* relocations. */
-
-static bool
-mips_ok_for_lazy_binding_p (rtx x)
-{
- return (TARGET_USE_GOT
- && GET_CODE (x) == SYMBOL_REF
- && !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. */
-
-static bool
-mips_load_call_address (rtx dest, rtx addr, bool 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 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)
- && 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));
- return true;
- }
- else
- {
- mips_emit_move (dest, addr);
- return false;
- }
-}
-
-/* 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. */
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));
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)
{
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)
{
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;
}
}
/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
The complication here is that, with the combination TARGET_ABICALLS
- && !TARGET_GPWORD, jump tables will use absolute addresses, and should
- therefore not be included in the read-only part of a DSO. Handle such
- cases by selecting a normal data section instead of a read-only one.
- The logic apes that in default_function_rodata_section. */
+ && !TARGET_ABSOLUTE_ABICALLS && !TARGET_GPWORD, jump tables will use
+ absolute addresses, and should therefore not be included in the
+ read-only part of a DSO. Handle such cases by selecting a normal
+ data section instead of a read-only one. The logic apes that in
+ default_function_rodata_section. */
static section *
mips_function_rodata_section (tree decl)
{
- if (!TARGET_ABICALLS || TARGET_GPWORD)
+ if (!TARGET_ABICALLS || TARGET_ABSOLUTE_ABICALLS || TARGET_GPWORD)
return default_function_rodata_section (decl);
if (decl && DECL_SECTION_NAME (decl))
/* If TARGET_ABICALLS, tell GAS to generate -KPIC code. */
if (TARGET_ABICALLS)
- fprintf (asm_out_file, "\t.abicalls\n");
+ {
+ fprintf (asm_out_file, "\t.abicalls\n");
+ if (TARGET_ABICALLS_PIC0)
+ fprintf (asm_out_file, "\t.option\tpic0\n");
+ }
if (flag_verbose_asm)
fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
return cfun->machine->has_gp_insn_p;
}
+/* Return true if the current function returns its value in a floating-point
+ register in MIPS16 mode. */
+
+static bool
+mips16_cfun_returns_in_fpr_p (void)
+{
+ tree return_type = DECL_RESULT (current_function_decl);
+ return (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
+ && !aggregate_value_p (return_type, current_function_decl)
+ && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
+}
+
/* Return the register that should be used as the global pointer
within this function. Return 0 if the function doesn't need
a global pointer. */
if (crtl->has_nonlocal_goto)
return GLOBAL_POINTER_REGNUM;
- /* If the gp is never referenced, there's no need to initialize it.
- Note that reload can sometimes introduce constant pool references
- into a function that otherwise didn't need them. For example,
- suppose we have an instruction like:
+ /* There's no need to initialize $gp if it isn't referenced now,
+ and if we can be sure that no new references will be added during
+ or after reload. */
+ if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
+ && !mips_function_has_gp_insn ())
+ {
+ /* The function doesn't use $gp at the moment. If we're generating
+ -call_nonpic code, no new uses will be introduced during or after
+ reload. */
+ if (TARGET_ABICALLS_PIC0)
+ return 0;
+
+ /* We need to handle the following implicit gp references:
- (set (reg:DF R1) (float:DF (reg:SI R2)))
+ - Reload can sometimes introduce constant pool references
+ into a function that otherwise didn't need them. For example,
+ suppose we have an instruction like:
- If R2 turns out to be constant such as 1, the instruction may have a
- REG_EQUAL note saying that R1 == 1.0. Reload then has the option of
- using this constant if R2 doesn't get allocated to a register.
+ (set (reg:DF R1) (float:DF (reg:SI R2)))
- In cases like these, reload will have added the constant to the pool
- but no instruction will yet refer to it. */
- if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
- && !crtl->uses_const_pool
- && !mips_function_has_gp_insn ())
- return 0;
+ If R2 turns out to be constant such as 1, the instruction may
+ have a REG_EQUAL note saying that R1 == 1.0. Reload then has
+ the option of using this constant if R2 doesn't get allocated
+ to a register.
+
+ In cases like these, reload will have added the constant to the
+ pool but no instruction will yet refer to it.
+
+ - MIPS16 functions that return in FPRs need to call an
+ external libgcc routine. */
+ if (!crtl->uses_const_pool
+ && !mips16_cfun_returns_in_fpr_p ())
+ return 0;
+ }
/* We need a global pointer, but perhaps we can use a call-clobbered
register instead of $gp. */
return GLOBAL_POINTER_REGNUM;
}
-/* Return true if the current function returns its value in a floating-point
- register in MIPS16 mode. */
-
-static bool
-mips16_cfun_returns_in_fpr_p (void)
-{
- tree return_type = DECL_RESULT (current_function_decl);
- return (TARGET_MIPS16
- && TARGET_HARD_FLOAT_ABI
- && !aggregate_value_p (return_type, current_function_decl)
- && mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
-}
-
/* Return true if the current function must save register REGNO. */
static bool
mips_save_reg_p (unsigned int regno)
{
- /* We only need to save $gp if TARGET_CALL_SAVED_GP and only then
- if we have not chosen a call-clobbered substitute. */
- if (regno == GLOBAL_POINTER_REGNUM)
- return TARGET_CALL_SAVED_GP && cfun->machine->global_pointer == regno;
+ /* We need to save $gp if TARGET_CALL_SAVED_GP and if we have not
+ chosen a call-clobbered substitute. */
+ if (TARGET_CALL_SAVED_GP
+ && regno == GLOBAL_POINTER_REGNUM
+ && cfun->machine->global_pointer == regno)
+ return true;
/* Check call-saved registers. */
if ((crtl->saves_all_registers || df_regs_ever_live_p (regno))
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);
}
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 (cfun->machine->global_pointer == 0)
+ return;
+
+ 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 ());
}
&& crtl->args.info.fp_code != 0)
mips16_build_function_stub ();
- /* Select the MIPS16 mode for this function. */
- if (TARGET_MIPS16)
- fprintf (file, "\t.set\tmips16\n");
- else
- fprintf (file, "\t.set\tnomips16\n");
-
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- /* Get the function name the same way that toplev.c does before calling
- assemble_start_function. This is needed so that the name used here
- exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
- fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
-
- if (!flag_inhibit_size_directive)
- {
- fputs ("\t.ent\t", file);
- assemble_name (file, fnname);
- fputs ("\n", file);
- }
-
- assemble_name (file, fnname);
- fputs (":\n", file);
- }
+ /* Get the function name the same way that toplev.c does before calling
+ assemble_start_function. This is needed so that the name used here
+ exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
+ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ mips_start_function_definition (fnname, TARGET_MIPS16);
/* Stop mips_file_end from treating this function as external. */
if (TARGET_IRIX && mips_abi == ABI_32)
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);
mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
+ const char *fnname;
+
/* Reinstate the normal $gp. */
SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM);
mips_output_cplocal ();
set_noreorder = set_nomacro = 0;
}
- if (!FUNCTION_NAME_ALREADY_DECLARED && !flag_inhibit_size_directive)
- {
- const char *fnname;
-
- /* Get the function name the same way that toplev.c does before calling
- assemble_start_function. This is needed so that the name used here
- exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
- fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
- fputs ("\t.end\t", file);
- assemble_name (file, fnname);
- fputs ("\n", file);
- }
+ /* Get the function name the same way that toplev.c does before calling
+ assemble_start_function. This is needed so that the name used here
+ exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
+ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ mips_end_function_definition (fnname);
}
\f
/* Save register REG to MEM. Make the instruction frame-related. */
{
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:
: 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);
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. */
mips_emit_loadgp ();
/* Initialize the $gp save slot. */
- if (frame->cprestore_size > 0)
- emit_insn (gen_cprestore (GEN_INT (crtl->outgoing_args_size)));
+ if (frame->cprestore_size > 0
+ && cfun->machine->global_pointer != 0)
+ {
+ if (TARGET_MIPS16)
+ mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+ MIPS16_PIC_TEMP);
+ else if (TARGET_ABICALLS_PIC2)
+ emit_insn (gen_cprestore (GEN_INT (frame->args_size)));
+ else
+ emit_move_insn (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+ pic_offset_table_rtx);
+ }
/* If we are profiling, make sure no instructions are scheduled before
the call to mcount. */
mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
{
unsigned int size;
- enum mode_class class;
+ enum mode_class mclass;
if (mode == CCV2mode)
return (ISA_HAS_8CC
}
size = GET_MODE_SIZE (mode);
- class = GET_MODE_CLASS (mode);
+ mclass = GET_MODE_CLASS (mode);
if (GP_REG_P (regno))
return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD;
|| mode == DImode))
return true;
- if (class == MODE_FLOAT
- || class == MODE_COMPLEX_FLOAT
- || class == MODE_VECTOR_FLOAT)
+ if (mclass == MODE_FLOAT
+ || mclass == MODE_COMPLEX_FLOAT
+ || mclass == MODE_VECTOR_FLOAT)
return size <= UNITS_PER_FPVALUE;
/* Allow integer modes that fit into a single register. We need
to put integers into FPRs when using instructions like CVT
and TRUNC. There's no point allowing sizes smaller than a word,
because the FPU has no appropriate load/store instructions. */
- if (class == MODE_INT)
+ if (mclass == MODE_INT)
return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG;
}
}
if (ALL_COP_REG_P (regno))
- return class == MODE_INT && size <= UNITS_PER_WORD;
+ return mclass == MODE_INT && size <= UNITS_PER_WORD;
if (regno == GOT_VERSION_REGNUM)
return mode == SImode;
in mips_hard_regno_nregs. */
int
-mips_class_max_nregs (enum reg_class class, enum machine_mode mode)
+mips_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
{
int size;
HARD_REG_SET left;
size = 0x8000;
- COPY_HARD_REG_SET (left, reg_class_contents[(int) class]);
+ COPY_HARD_REG_SET (left, reg_class_contents[(int) rclass]);
if (hard_reg_set_intersect_p (left, reg_class_contents[(int) ST_REGS]))
{
size = MIN (size, 4);
bool
mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
enum machine_mode to ATTRIBUTE_UNUSED,
- enum reg_class class)
+ enum reg_class rclass)
{
/* There are several problems with changing the modes of values
in floating-point registers:
not ask it to treat the value as having a different format.
We therefore disallow all mode changes involving FPRs. */
- return reg_classes_intersect_p (FP_REGS, class);
+ return reg_classes_intersect_p (FP_REGS, rclass);
}
/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction. */
/* Implement PREFERRED_RELOAD_CLASS. */
enum reg_class
-mips_preferred_reload_class (rtx x, enum reg_class class)
+mips_preferred_reload_class (rtx x, enum reg_class rclass)
{
- if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
+ if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, rclass))
return LEA_REGS;
- if (reg_class_subset_p (FP_REGS, class)
+ if (reg_class_subset_p (FP_REGS, rclass)
&& mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
return FP_REGS;
- if (reg_class_subset_p (GR_REGS, class))
- class = GR_REGS;
+ if (reg_class_subset_p (GR_REGS, rclass))
+ rclass = GR_REGS;
- if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class))
- class = M16_REGS;
+ if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, rclass))
+ rclass = M16_REGS;
- return class;
+ return rclass;
}
/* Implement REGISTER_MOVE_COST. */
}
/* Return the register class required for a secondary register when
- copying between one of the registers in CLASS and value X, which
+ copying between one of the registers in RCLASS and value X, which
has mode MODE. X is the source of the move if IN_P, otherwise it
is the destination. Return NO_REGS if no secondary register is
needed. */
enum reg_class
-mips_secondary_reload_class (enum reg_class class,
+mips_secondary_reload_class (enum reg_class rclass,
enum machine_mode mode, rtx x, bool in_p)
{
int regno;
/* If X is a constant that cannot be loaded into $25, it must be loaded
into some other GPR. No other register class allows a direct move. */
if (mips_dangerous_for_la25_p (x))
- return reg_class_subset_p (class, LEA_REGS) ? NO_REGS : LEA_REGS;
+ return reg_class_subset_p (rclass, LEA_REGS) ? NO_REGS : LEA_REGS;
regno = true_regnum (x);
if (TARGET_MIPS16)
{
/* In MIPS16 mode, every move must involve a member of M16_REGS. */
- if (!reg_class_subset_p (class, M16_REGS) && !M16_REG_P (regno))
+ if (!reg_class_subset_p (rclass, M16_REGS) && !M16_REG_P (regno))
return M16_REGS;
/* We can't really copy to HI or LO at all in MIPS16 mode. */
- if (in_p ? reg_classes_intersect_p (class, ACC_REGS) : ACC_REG_P (regno))
+ if (in_p ? reg_classes_intersect_p (rclass, ACC_REGS) : ACC_REG_P (regno))
return M16_REGS;
return NO_REGS;
/* Copying from accumulator registers to anywhere other than a general
register requires a temporary general register. */
- if (reg_class_subset_p (class, ACC_REGS))
+ if (reg_class_subset_p (rclass, ACC_REGS))
return GP_REG_P (regno) ? NO_REGS : GR_REGS;
if (ACC_REG_P (regno))
- return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
/* We can only copy a value to a condition code register from a
floating-point register, and even then we require a scratch
floating-point register. We can only copy a value out of a
condition-code register into a general register. */
- if (reg_class_subset_p (class, ST_REGS))
+ if (reg_class_subset_p (rclass, ST_REGS))
{
if (in_p)
return FP_REGS;
{
if (!in_p)
return FP_REGS;
- return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
}
- if (reg_class_subset_p (class, FP_REGS))
+ if (reg_class_subset_p (rclass, FP_REGS))
{
if (MEM_P (x)
&& (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
return GR_REGS;
}
if (FP_REG_P (regno))
- return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
return NO_REGS;
}
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))
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;
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 ();
}
\f
HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
tree function)
{
- rtx this, temp1, temp2, insn, fnaddr;
+ rtx this_rtx, temp1, temp2, insn, fnaddr;
bool use_sibcall_p;
/* Pretend to be a post-reload pass while generating rtl. */
&& 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);
/* 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);
+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
else
- this = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST);
- /* Add DELTA to THIS. */
+ /* Add DELTA to THIS_RTX. */
if (delta != 0)
{
rtx offset = GEN_INT (delta);
mips_emit_move (temp1, offset);
offset = temp1;
}
- emit_insn (gen_add3_insn (this, this, offset));
+ emit_insn (gen_add3_insn (this_rtx, this_rtx, offset));
}
- /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */
+ /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */
if (vcall_offset != 0)
{
rtx addr;
- /* Set TEMP1 to *THIS. */
- mips_emit_move (temp1, gen_rtx_MEM (Pmode, this));
+ /* Set TEMP1 to *THIS_RTX. */
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx));
- /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */
+ /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */
addr = mips_add_offset (temp2, temp1, vcall_offset);
- /* Load the offset and add it to THIS. */
+ /* Load the offset and add it to THIS_RTX. */
mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
- emit_insn (gen_add3_insn (this, this, temp1));
+ emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1));
}
/* Jump to the target function. Use a sibcall if direct jumps are
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)
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
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");
target_flags &= ~MASK_ABICALLS;
}
- if (TARGET_ABICALLS)
+ if (TARGET_ABICALLS_PIC2)
/* We need to set flag_pic for executables as well as DSOs
because we may reference symbols that are not defined in
the final executable. (MIPS does not use things like
copy relocs, for example.)
- Also, there is a body of code that uses __PIC__ to distinguish
- between -mabicalls and -mno-abicalls code. */
+ There is a body of code that uses __PIC__ to distinguish
+ between -mabicalls and -mno-abicalls code. The non-__PIC__
+ variant is usually appropriate for TARGET_ABICALLS_PIC0, as
+ long as any indirect jumps use $25. */
flag_pic = 1;
/* -mvr4130-align is a "speed over size" optimization: it usually produces