/* Subroutines used for MIPS code generation.
Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
Free Software Foundation, Inc.
Contributed by A. Lichnewsky, lich@inria.inria.fr.
Changes by Michael Meissner, meissner@osf.org.
#include "rtl.h"
#include "regs.h"
#include "hard-reg-set.h"
-#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-attr.h"
#include "gimple.h"
#include "bitmap.h"
#include "diagnostic.h"
+#include "target-globals.h"
/* True if X is an UNSPEC wrapper around a SYMBOL_REF or LABEL_REF. */
#define UNSPEC_ADDRESS_P(X) \
/* True if bit BIT is set in VALUE. */
#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+/* Return the opcode for a ptr_mode load of the form:
+
+ l[wd] DEST, OFFSET(BASE). */
+#define MIPS_LOAD_PTR(DEST, OFFSET, BASE) \
+ (((ptr_mode == DImode ? 0x37 : 0x23) << 26) \
+ | ((BASE) << 21) \
+ | ((DEST) << 16) \
+ | (OFFSET))
+
+/* Return the opcode to move register SRC into register DEST. */
+#define MIPS_MOVE(DEST, SRC) \
+ ((TARGET_64BIT ? 0x2d : 0x21) \
+ | ((DEST) << 11) \
+ | ((SRC) << 21))
+
+/* Return the opcode for:
+
+ lui DEST, VALUE. */
+#define MIPS_LUI(DEST, VALUE) \
+ ((0xf << 26) | ((DEST) << 16) | (VALUE))
+
+/* Return the opcode to jump to register DEST. */
+#define MIPS_JR(DEST) \
+ (((DEST) << 21) | 0x8)
+
+/* Return the opcode for:
+
+ bal . + (1 + OFFSET) * 4. */
+#define MIPS_BAL(OFFSET) \
+ ((0x1 << 26) | (0x11 << 16) | (OFFSET))
+
+/* Return the usual opcode for a nop. */
+#define MIPS_NOP 0
+
/* Classifies an address.
ADDRESS_REG
HOST_WIDE_INT acc_sp_offset;
HOST_WIDE_INT cop0_sp_offset;
+ /* Similar, but the value passed to _mcount. */
+ HOST_WIDE_INT ra_fp_offset;
+
/* The offset of arg_pointer_rtx from the bottom of the frame. */
HOST_WIDE_INT arg_pointer_offset;
HOST_WIDE_INT size;
};
+/* Costs of various operations on the different architectures. */
+
+struct mips_rtx_cost_data
+{
+ unsigned short fp_add;
+ unsigned short fp_mult_sf;
+ unsigned short fp_mult_df;
+ unsigned short fp_div_sf;
+ unsigned short fp_div_df;
+ unsigned short int_mult_si;
+ unsigned short int_mult_di;
+ unsigned short int_div_si;
+ unsigned short int_div_di;
+ unsigned short branch_cost;
+ unsigned short memory_latency;
+};
+
/* Global variables for machine-dependent things. */
/* The -G setting, or the configuration's default small-data limit if
static bool mips_branch_likely;
/* The current instruction-set architecture. */
-enum processor_type mips_arch;
+enum processor mips_arch;
const struct mips_cpu_info *mips_arch_info;
/* The processor that we should tune the code for. */
-enum processor_type mips_tune;
+enum processor mips_tune;
const struct mips_cpu_info *mips_tune_info;
/* The ISA level associated with mips_arch. */
int mips_abi = MIPS_ABI_DEFAULT;
/* Which cost information to use. */
-const struct mips_rtx_cost_data *mips_cost;
+static const struct mips_rtx_cost_data *mips_cost;
/* The ambient target flags, excluding MASK_MIPS16. */
static int mips_base_target_flags;
/* Index C is true if character C is a valid PRINT_OPERAND punctation
character. */
-bool mips_print_operand_punct[256];
+static bool mips_print_operand_punct[256];
static GTY (()) int mips_output_filename_first_time = 1;
/* Likewise for HIGHs. */
static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
+/* Target state for MIPS16. */
+struct target_globals *mips16_globals;
+
+/* Cached value of can_issue_more. This is cached in mips_variable_issue hook
+ and returned from mips_sched_reorder2. */
+static int cached_can_issue_more;
+
/* 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_REGS, V1_REG,
};
/* Costs to use when optimizing for speed, indexed by processor. */
-static const struct mips_rtx_cost_data mips_rtx_cost_data[PROCESSOR_MAX] = {
+static const struct mips_rtx_cost_data
+ mips_rtx_cost_data[NUM_PROCESSOR_VALUES] = {
{ /* R3000 */
COSTS_N_INSNS (2), /* fp_add */
COSTS_N_INSNS (4), /* fp_mult_sf */
};
\f
static rtx mips_find_pic_call_symbol (rtx, rtx);
+static int mips_register_move_cost (enum machine_mode, reg_class_t,
+ reg_class_t);
\f
/* This hash table keeps track of implicit "mips16" and "nomips16" attributes
for -mflip_mips16. It maps decl names onto a boolean mode setting. */
if (!entry)
{
mips16_flipper = !mips16_flipper;
- entry = GGC_NEW (struct mflip_mips16_entry);
+ entry = ggc_alloc_mflip_mips16_entry ();
entry->name = name;
entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
*slot = entry;
: emit_move_insn_1 (dest, src));
}
+/* Emit an instruction of the form (set TARGET (CODE OP0)). */
+
+static void
+mips_emit_unary (enum rtx_code code, rtx target, rtx op0)
+{
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_fmt_e (code, GET_MODE (op0), op0)));
+}
+
+/* Compute (CODE OP0) and store the result in a new register of mode MODE.
+ Return that new register. */
+
+static rtx
+mips_force_unary (enum machine_mode mode, enum rtx_code code, rtx op0)
+{
+ rtx reg;
+
+ reg = gen_reg_rtx (mode);
+ mips_emit_unary (code, reg, op0);
+ return reg;
+}
+
/* Emit an instruction of the form (set TARGET (CODE OP0 OP1)). */
static void
/* Return the cost of binary operation X, given that the instruction
sequence for a word-sized or smaller operation has cost SINGLE_COST
- and that the sequence of a double-word operation has cost DOUBLE_COST. */
+ and that the sequence of a double-word operation has cost DOUBLE_COST.
+ If SPEED is true, optimize for speed otherwise optimize for size. */
static int
-mips_binary_cost (rtx x, int single_cost, int double_cost)
+mips_binary_cost (rtx x, int single_cost, int double_cost, bool speed)
{
int cost;
else
cost = single_cost;
return (cost
- + rtx_cost (XEXP (x, 0), SET, !optimize_size)
- + rtx_cost (XEXP (x, 1), GET_CODE (x), !optimize_size));
+ + rtx_cost (XEXP (x, 0), SET, speed)
+ + rtx_cost (XEXP (x, 1), GET_CODE (x), speed));
}
/* Return the cost of floating-point multiplications of mode MODE. */
/* Implement TARGET_RTX_COSTS. */
static bool
-mips_rtx_costs (rtx x, int code, int outer_code, int *total,
- bool speed)
+mips_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
{
enum machine_mode mode = GET_MODE (x);
bool float_mode_p = FLOAT_MODE_P (mode);
operand needs to be forced into a register, we will often be
able to hoist the constant load out of the loop, so the load
should not contribute to the cost. */
- if (!optimize_size
- || mips_immediate_operand_p (outer_code, INTVAL (x)))
+ if (speed || mips_immediate_operand_p (outer_code, INTVAL (x)))
{
*total = 0;
return true;
case IOR:
case XOR:
/* Double-word operations use two single-word operations. */
- *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2));
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2),
+ speed);
return true;
case ASHIFT:
case ROTATE:
case ROTATERT:
if (CONSTANT_P (XEXP (x, 1)))
- *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+ speed);
else
- *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12));
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12),
+ speed);
return true;
case ABS:
*total = mips_cost->fp_add;
return false;
}
- *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4),
+ speed);
return true;
case MINUS:
an SLTU. The MIPS16 version then needs to move the result of
the SLTU from $24 to a MIPS16 register. */
*total = mips_binary_cost (x, COSTS_N_INSNS (1),
- COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4));
+ COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4),
+ speed);
return true;
case NEG:
else if (mode == DImode && !TARGET_64BIT)
/* Synthesized from 2 mulsi3s, 1 mulsidi3 and two additions,
where the mulsidi3 always includes an MFHI and an MFLO. */
- *total = (optimize_size
- ? COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9)
- : mips_cost->int_mult_si * 3 + 6);
- else if (optimize_size)
+ *total = (speed
+ ? mips_cost->int_mult_si * 3 + 6
+ : COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9));
+ else if (!speed)
*total = (ISA_HAS_MUL3 ? 1 : 2);
else if (mode == DImode)
*total = mips_cost->int_mult_di;
case UDIV:
case UMOD:
- if (optimize_size)
+ if (!speed)
{
/* It is our responsibility to make division by a power of 2
as cheap as 2 register additions if we want the division
struct mips_multi_member *member;
unsigned int i;
- for (i = 0;
- VEC_iterate (mips_multi_member, mips_multi_members, i, member);
- i++)
+ FOR_EACH_VEC_ELT (mips_multi_member, mips_multi_members, i, member)
if (member->is_label_p)
fprintf (asm_out_file, "%s\n", member->format);
else
static void
mips_get_arg_info (struct mips_arg_info *info, const CUMULATIVE_ARGS *cum,
- enum machine_mode mode, tree type, int named)
+ enum machine_mode mode, const_tree type, bool named)
{
bool doubleword_aligned_p;
unsigned int num_bytes, num_words, max_regs;
return !TARGET_OLDABI;
}
-/* Implement FUNCTION_ARG. */
+/* Implement TARGET_FUNCTION_ARG. */
-rtx
-mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
+static rtx
+mips_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
{
struct mips_arg_info info;
tree field;
/* First check to see if there is any such field. */
- for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
&& TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
{
rtx reg;
- for (; field; field = TREE_CHAIN (field))
+ for (; field; field = DECL_CHAIN (field))
if (TREE_CODE (field) == FIELD_DECL
&& int_bit_position (field) >= bitpos)
break;
return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
}
-/* Implement FUNCTION_ARG_ADVANCE. */
+/* Implement TARGET_FUNCTION_ARG_ADVANCE. */
-void
+static void
mips_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
+ const_tree type, bool named)
{
struct mips_arg_info info;
to STACK_BOUNDARY bits if the type requires it. */
int
-mips_function_arg_boundary (enum machine_mode mode, tree type)
+mips_function_arg_boundary (enum machine_mode mode, const_tree type)
{
unsigned int alignment;
return 0;
i = 0;
- for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
+ for (field = TYPE_FIELDS (valtype); field != 0; field = DECL_CHAIN (field))
{
if (TREE_CODE (field) != FIELD_DECL)
continue;
argument. Advance a local copy of CUM past the last "real" named
argument, to find out how many registers are left over. */
local_cum = *cum;
- FUNCTION_ARG_ADVANCE (local_cum, mode, type, true);
+ mips_function_arg_advance (&local_cum, mode, type, true);
/* Found out how many registers we need to save. */
gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
DECL_FIELD_CONTEXT (f_res) = record;
TYPE_FIELDS (record) = f_ovfl;
- TREE_CHAIN (f_ovfl) = f_gtop;
- TREE_CHAIN (f_gtop) = f_ftop;
- TREE_CHAIN (f_ftop) = f_goff;
- TREE_CHAIN (f_goff) = f_foff;
- TREE_CHAIN (f_foff) = f_res;
+ DECL_CHAIN (f_ovfl) = f_gtop;
+ DECL_CHAIN (f_gtop) = f_ftop;
+ DECL_CHAIN (f_ftop) = f_goff;
+ DECL_CHAIN (f_goff) = f_foff;
+ DECL_CHAIN (f_foff) = f_res;
layout_type (record);
return record;
}
- else if (TARGET_IRIX && TARGET_IRIX6)
+ else if (TARGET_IRIX6)
/* On IRIX 6, this type is 'char *'. */
return build_pointer_type (char_type_node);
else
= (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
f_ovfl = TYPE_FIELDS (va_list_type_node);
- f_gtop = TREE_CHAIN (f_ovfl);
- f_ftop = TREE_CHAIN (f_gtop);
- f_goff = TREE_CHAIN (f_ftop);
- f_foff = TREE_CHAIN (f_goff);
+ f_gtop = DECL_CHAIN (f_ovfl);
+ f_ftop = DECL_CHAIN (f_gtop);
+ f_goff = DECL_CHAIN (f_ftop);
+ f_foff = DECL_CHAIN (f_goff);
ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
NULL_TREE);
tree t, u;
f_ovfl = TYPE_FIELDS (va_list_type_node);
- f_gtop = TREE_CHAIN (f_ovfl);
- f_ftop = TREE_CHAIN (f_gtop);
- f_goff = TREE_CHAIN (f_ftop);
- f_foff = TREE_CHAIN (f_goff);
+ f_gtop = DECL_CHAIN (f_ovfl);
+ f_ftop = DECL_CHAIN (f_gtop);
+ f_goff = DECL_CHAIN (f_ftop);
+ f_foff = DECL_CHAIN (f_goff);
/* Let:
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 = ggc_alloc_mips16_local_alias ();
alias->func = func;
alias->local = local;
*slot = alias;
The stub's caller knows that $18 might be clobbered, even though
$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]);
+ reg_names[GP_REG_FIRST + 18], reg_names[RETURN_ADDR_REGNUM]);
output_asm_insn (MIPS_CALL ("jal", &fn, 0, -1), &fn);
/* Move the result from floating-point registers to
switch (GET_MODE (retval))
{
case SCmode:
- mips_output_32bit_xfer ('f', GP_RETURN + 1,
- FP_REG_FIRST + MAX_FPRS_PER_FMT);
- /* Fall though. */
- case SFmode:
- mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+ mips_output_32bit_xfer ('f', GP_RETURN + TARGET_BIG_ENDIAN,
+ TARGET_BIG_ENDIAN
+ ? FP_REG_FIRST + MAX_FPRS_PER_FMT
+ : FP_REG_FIRST);
+ mips_output_32bit_xfer ('f', GP_RETURN + TARGET_LITTLE_ENDIAN,
+ TARGET_LITTLE_ENDIAN
+ ? FP_REG_FIRST + MAX_FPRS_PER_FMT
+ : FP_REG_FIRST);
if (GET_MODE (retval) == SCmode && TARGET_64BIT)
{
/* On 64-bit targets, complex floats are returned in
a single GPR, such that "sd" on a suitably-aligned
target would store the value correctly. */
fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN],
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN]);
+ fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN],
reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN]);
+ fprintf (asm_out_file, "\tdsrl\t%s,%s,32\n",
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN],
+ reg_names[GP_RETURN + TARGET_BIG_ENDIAN]);
fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
reg_names[GP_RETURN],
reg_names[GP_RETURN],
}
break;
+ case SFmode:
+ mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+ break;
+
case DCmode:
mips_output_64bit_xfer ('f', GP_RETURN + (8 / UNITS_PER_WORD),
FP_REG_FIRST + MAX_FPRS_PER_FMT);
void
mips_expand_synci_loop (rtx begin, rtx end)
{
- rtx inc, label, cmp, cmp_result;
+ rtx inc, label, end_label, cmp_result, mask, length;
+
+ /* Create end_label. */
+ end_label = gen_label_rtx ();
+
+ /* Check if begin equals end. */
+ cmp_result = gen_rtx_EQ (VOIDmode, begin, end);
+ emit_jump_insn (gen_condjump (cmp_result, end_label));
/* Load INC with the cache line size (rdhwr INC,$1). */
inc = gen_reg_rtx (Pmode);
? gen_rdhwr_synci_step_si (inc)
: gen_rdhwr_synci_step_di (inc));
+ /* Check if inc is 0. */
+ cmp_result = gen_rtx_EQ (VOIDmode, inc, const0_rtx);
+ emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+ /* Calculate mask. */
+ mask = mips_force_unary (Pmode, NEG, inc);
+
+ /* Mask out begin by mask. */
+ begin = mips_force_binary (Pmode, AND, begin, mask);
+
+ /* Calculate length. */
+ length = mips_force_binary (Pmode, MINUS, end, begin);
+
/* Loop back to here. */
label = gen_label_rtx ();
emit_label (label);
emit_insn (gen_synci (begin));
- cmp = mips_force_binary (Pmode, GTU, begin, end);
+ /* Update length. */
+ mips_emit_binary (MINUS, length, length, inc);
+ /* Update begin. */
mips_emit_binary (PLUS, begin, begin, inc);
- cmp_result = gen_rtx_EQ (VOIDmode, cmp, const0_rtx);
+ /* Check if length is greater than 0. */
+ cmp_result = gen_rtx_GT (VOIDmode, length, const0_rtx);
emit_jump_insn (gen_condjump (cmp_result, label));
+
+ emit_label (end_label);
}
\f
/* Expand a QI or HI mode atomic memory operation.
break;
case '@':
- fputs (reg_names[GP_REG_FIRST + 1], file);
+ fputs (reg_names[AT_REGNUM], file);
break;
case '^':
}
}
-/* Implement the PRINT_OPERAND macro. The MIPS-specific operand codes are:
+/* Implement TARGET_PRINT_OPERAND_PUNCT_VALID_P. */
+
+static bool
+mips_print_operand_punct_valid_p (unsigned char code)
+{
+ return mips_print_operand_punct[code];
+}
+
+/* Implement TARGET_PRINT_OPERAND. The MIPS-specific operand codes are:
'X' Print CONST_INT OP in hexadecimal format.
'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
'M' Print high-order register in a double-word register operand.
'z' Print $0 if OP is zero, otherwise print OP normally. */
-void
+static void
mips_print_operand (FILE *file, rtx op, int letter)
{
enum rtx_code code;
- if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+ if (mips_print_operand_punct_valid_p (letter))
{
mips_print_operand_punctuation (file, letter);
return;
}
}
-/* Output address operand X to FILE. */
+/* Implement TARGET_PRINT_OPERAND_ADDRESS. */
-void
+static void
mips_print_operand_address (FILE *file, rtx x)
{
struct mips_address_info addr;
/* We don't yet generate small-data references for -mabicalls
or VxWorks RTP code. See the related -G handling in
- mips_override_options. */
+ mips_option_override. */
if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
return false;
fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n",
int_size_in_bytes (TREE_TYPE (decl)));
}
- else if (TARGET_IRIX
- && mips_abi == ABI_32
- && TREE_CODE (decl) == FUNCTION_DECL)
- {
- /* In IRIX 5 or IRIX 6 for the O32 ABI, we must output a
- `.global name .text' directive for every used but
- undefined function. If we don't, the linker may perform
- an optimization (skipping over the insns that set $gp)
- when it is unsafe. */
- fputs ("\t.globl ", file);
- assemble_name (file, name);
- fputs (" .text\n", file);
- }
}
}
-/* Implement ASM_OUTPUT_SOURCE_FILENAME. */
+/* Implement TARGET_ASM_OUTPUT_SOURCE_FILENAME. */
-void
+static void
mips_output_filename (FILE *stream, const char *name)
{
/* If we are emitting DWARF-2, let dwarf2out handle the ".file"
/* Generate a special section to describe the ABI switches used to
produce the resultant binary. This is unnecessary on IRIX and
causes unwanted warnings from the native linker. */
- if (!TARGET_IRIX)
+ if (!TARGET_IRIX6)
{
/* Record the ABI itself. Modern versions of binutils encode
this information in the ELF header flags, but GDB needs the
"\t.previous\n", TARGET_LONG64 ? 64 : 32);
#ifdef HAVE_AS_GNU_ATTRIBUTE
- fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
- (TARGET_HARD_FLOAT_ABI
- ? (TARGET_DOUBLE_FLOAT
- ? ((!TARGET_64BIT && TARGET_FLOAT64) ? 4 : 1) : 2) : 3));
+ {
+ int attr;
+
+ /* No floating-point operations, -mno-float. */
+ if (TARGET_NO_FLOAT)
+ attr = 0;
+ /* Soft-float code, -msoft-float. */
+ else if (!TARGET_HARD_FLOAT_ABI)
+ attr = 3;
+ /* Single-float code, -msingle-float. */
+ else if (!TARGET_DOUBLE_FLOAT)
+ attr = 2;
+ /* 64-bit FP registers on a 32-bit target, -mips32r2 -mfp64. */
+ else if (!TARGET_64BIT && TARGET_FLOAT64)
+ attr = 4;
+ /* Regular FP code, FP regs same size as GP regs, -mdouble-float. */
+ else
+ attr = 1;
+
+ fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n", attr);
+ }
#endif
}
/* If we're saving the return address register and the DWARF return
address column differs from the hard register number, adjust the
note reg to refer to the former. */
- if (REGNO (reg) == GP_REG_FIRST + 31
- && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31)
+ if (REGNO (reg) == RETURN_ADDR_REGNUM
+ && DWARF_FRAME_RETURN_COLUMN != RETURN_ADDR_REGNUM)
reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN);
set = gen_rtx_SET (VOIDmode, mem, reg);
mips16e_a0_a3_regs[end - 1]);
/* Save or restore $31. */
- if (BITSET_P (info.mask, 31))
- s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+ if (BITSET_P (info.mask, RETURN_ADDR_REGNUM))
+ s += sprintf (s, ",%s", reg_names[RETURN_ADDR_REGNUM]);
return buffer;
}
return GLOBAL_POINTER_REGNUM;
}
-/* Return true if current function's prologue must load the global
+/* Return true if the current function's prologue must load the global
pointer value into pic_offset_table_rtx and store the same value in
the function's cprestore slot (if any).
/* If a MIPS16 function returns a value in FPRs, its epilogue
will need to call an external libgcc routine. This yet-to-be
generated call_insn will clobber $31. */
- if (regno == GP_REG_FIRST + 31 && mips16_cfun_returns_in_fpr_p ())
+ if (regno == RETURN_ADDR_REGNUM && mips16_cfun_returns_in_fpr_p ())
return true;
/* If REGNO is ordinarily call-clobbered, we must assume that any
/* We need to save the incoming return address if __builtin_eh_return
is being used to set a different return address. */
- if (regno == GP_REG_FIRST + 31 && crtl->calls_eh_return)
+ if (regno == RETURN_ADDR_REGNUM && crtl->calls_eh_return)
return true;
return false;
if (count != 0)
return const0_rtx;
- return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
+ return get_hard_reg_initial_val (Pmode, RETURN_ADDR_REGNUM);
}
/* Emit code to change the current function's return address to
{
rtx slot_address;
- gcc_assert (BITSET_P (cfun->machine->frame.mask, 31));
+ gcc_assert (BITSET_P (cfun->machine->frame.mask, RETURN_ADDR_REGNUM));
slot_address = mips_add_offset (scratch, stack_pointer_rtx,
cfun->machine->frame.gp_sp_offset);
mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
gcc_assert (TARGET_ABICALLS && TARGET_OLDABI && epilogue_completed);
if (!cfun->machine->must_restore_gp_when_clobbered_p)
- return;
+ {
+ emit_note (NOTE_INSN_DELETED);
+ return;
+ }
if (TARGET_MIPS16)
{
for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
{
+ /* Record the ra offset for use by mips_function_profiler. */
+ if (regno == RETURN_ADDR_REGNUM)
+ cfun->machine->frame.ra_fp_offset = offset + sp_offset;
mips_save_restore_reg (word_mode, regno, offset, fn);
offset -= UNITS_PER_WORD;
}
mips_direct_save_slot_move_p (unsigned int regno, rtx mem, bool load_p)
{
/* There is a specific MIPS16 instruction for saving $31 to the stack. */
- if (TARGET_MIPS16 && !load_p && regno == GP_REG_FIRST + 31)
+ if (TARGET_MIPS16 && !load_p && regno == RETURN_ADDR_REGNUM)
return false;
return mips_secondary_reload_class (REGNO_REG_CLASS (regno),
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)
- TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1;
-
/* Output MIPS-specific frame information. */
if (!flag_inhibit_size_directive)
{
(frame_pointer_needed
? frame->total_size - frame->hard_frame_pointer_offset
: frame->total_size),
- reg_names[GP_REG_FIRST + 31],
+ reg_names[RETURN_ADDR_REGNUM],
frame->var_size,
frame->num_gp, frame->num_fp,
frame->args_size,
frame = &cfun->machine->frame;
size = frame->total_size;
+ if (flag_stack_usage)
+ current_function_static_stack_size = size;
+
/* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP
bytes beforehand; this is enough to cover the register save area
without going out of range. */
{
/* There's no MIPS16 instruction to load $31 directly. Load into
$7 instead and adjust the return insn appropriately. */
- if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
+ if (TARGET_MIPS16 && REGNO (reg) == RETURN_ADDR_REGNUM)
reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
mips_emit_save_slot_move (reg, mem, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
address into $7 rather than $31. */
if (TARGET_MIPS16
&& !GENERATE_MIPS16E_SAVE_RESTORE
- && BITSET_P (frame->mask, 31))
+ && BITSET_P (frame->mask, RETURN_ADDR_REGNUM))
regno = GP_REG_FIRST + 7;
else
- regno = GP_REG_FIRST + 31;
+ regno = RETURN_ADDR_REGNUM;
emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
}
}
return reg_classes_intersect_p (FP_REGS, rclass);
}
+/* Implement target hook small_register_classes_for_mode_p. */
+
+static bool
+mips_small_register_classes_for_mode_p (enum machine_mode mode
+ ATTRIBUTE_UNUSED)
+{
+ return TARGET_MIPS16;
+}
+
/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction. */
static bool
/* RCLASS is a class involved in a REGISTER_MOVE_COST calculation.
Return a "canonical" class to represent it in later calculations. */
-static enum reg_class
-mips_canonicalize_move_class (enum reg_class rclass)
+static reg_class_t
+mips_canonicalize_move_class (reg_class_t rclass)
{
/* All moves involving accumulator registers have the same cost. */
if (reg_class_subset_p (rclass, ACC_REGS))
static int
mips_move_to_gpr_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
- enum reg_class from)
+ reg_class_t from)
{
switch (from)
{
other classes handled by this function. */
static int
-mips_move_from_gpr_cost (enum machine_mode mode, enum reg_class to)
+mips_move_from_gpr_cost (enum machine_mode mode, reg_class_t to)
{
switch (to)
{
}
}
-/* Implement REGISTER_MOVE_COST. Return 0 for classes that are the
+/* Implement TARGET_REGISTER_MOVE_COST. Return 0 for classes that are the
maximum of the move costs for subclasses; regclass will work out
the maximum for us. */
-int
+static int
mips_register_move_cost (enum machine_mode mode,
- enum reg_class from, enum reg_class to)
+ reg_class_t from, reg_class_t to)
{
- enum reg_class dregs;
+ reg_class_t dregs;
int cost1, cost2;
from = mips_canonicalize_move_class (from);
return 0;
}
+/* Implement TARGET_MEMORY_MOVE_COST. */
+
+static int
+mips_memory_move_cost (enum machine_mode mode, reg_class_t rclass, bool in)
+{
+ return (mips_cost->memory_latency
+ + memory_move_secondary_cost (mode, rclass, in));
+}
+
/* Implement TARGET_IRA_COVER_CLASSES. */
-static const enum reg_class *
+static const reg_class_t *
mips_ira_cover_classes (void)
{
- static const enum reg_class acc_classes[] = {
+ static const reg_class_t acc_classes[] = {
GR_AND_ACC_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
ST_REGS, LIM_REG_CLASSES
};
- static const enum reg_class no_acc_classes[] = {
+ static const reg_class_t no_acc_classes[] = {
GR_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
ST_REGS, LIM_REG_CLASSES
};
return default_scalar_mode_supported_p (mode);
}
\f
+/* Implement TARGET_VECTORIZE_PREFERRED_SIMD_MODE. */
+
+static enum machine_mode
+mips_preferred_simd_mode (enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (TARGET_PAIRED_SINGLE_FLOAT
+ && mode == SFmode)
+ return V2SFmode;
+ return word_mode;
+}
+
/* Implement TARGET_INIT_LIBFUNCS. */
#include "config/gofast.h"
/* Output the release side of the memory barrier. */
if (get_attr_sync_release_barrier (insn) == SYNC_RELEASE_BARRIER_YES)
- mips_multi_add_insn ("sync", NULL);
+ {
+ if (required_oldval == 0 && TARGET_OCTEON)
+ {
+ /* Octeon doesn't reorder reads, so a full barrier can be
+ created by using SYNCW to order writes combined with the
+ write from the following SC. When the SC successfully
+ completes, we know that all preceding writes are also
+ committed to the coherent memory system. It is possible
+ for a single SYNCW to fail, but a pair of them will never
+ fail, so we use two. */
+ mips_multi_add_insn ("syncw", NULL);
+ mips_multi_add_insn ("syncw", NULL);
+ }
+ else
+ mips_multi_add_insn ("sync", NULL);
+ }
/* Output the branch-back label. */
mips_multi_add_label ("1:");
s = "bnez\t%2,1f\n\tbreak\t7\n1:";
}
else if (GENERATE_DIVIDE_TRAPS)
- {
- output_asm_insn (s, operands);
- s = "teq\t%2,%.,7";
- }
+ {
+ /* Avoid long replay penalty on load miss by putting the trap before
+ the divide. */
+ if (TUNE_74K)
+ output_asm_insn ("teq\t%2,%.,7", operands);
+ else
+ {
+ output_asm_insn (s, operands);
+ s = "teq\t%2,%.,7";
+ }
+ }
else
{
output_asm_insn ("%(bne\t%2,%.,1f", operands);
mips_ls2.falu1_turn_p = true;
}
-/* Implement TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2. */
+/* Subroutine used by TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2. */
-static int
-mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
- rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+static void
+mips_sched_reorder_1 (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
{
if (!reload_completed
&& TUNE_MACC_CHAINS
if (TUNE_74K)
mips_74k_agen_reorder (ready, *nreadyp);
+}
+/* Implement TARGET_SCHED_REORDER. */
+
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+ mips_sched_reorder_1 (file, verbose, ready, nreadyp, cycle);
return mips_issue_rate ();
}
+/* Implement TARGET_SCHED_REORDER2. */
+
+static int
+mips_sched_reorder2 (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+ mips_sched_reorder_1 (file, verbose, ready, nreadyp, cycle);
+ return cached_can_issue_more;
+}
+
/* Update round-robin counters for ALU1/2 and FALU1/2. */
static void
|| recog_memoized (insn) < 0
|| get_attr_type (insn) != TYPE_MULTI);
+ cached_can_issue_more = more;
return more;
}
\f
#define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3
#define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3
#define CODE_FOR_mips_mul_ph CODE_FOR_mulv2hi3
+#define CODE_FOR_mips_mult CODE_FOR_mulsidi3_32bit
+#define CODE_FOR_mips_multu CODE_FOR_umulsidi3_32bit
#define CODE_FOR_loongson_packsswh CODE_FOR_vec_pack_ssat_v2si
#define CODE_FOR_loongson_packsshb CODE_FOR_vec_pack_ssat_v4hi
#define CODE_FOR_loongson_pminub CODE_FOR_uminv8qi3
#define CODE_FOR_loongson_pmulhuh CODE_FOR_umulv4hi3_highpart
#define CODE_FOR_loongson_pmulhh CODE_FOR_smulv4hi3_highpart
+#define CODE_FOR_loongson_pmullh CODE_FOR_mulv4hi3
+#define CODE_FOR_loongson_psllh CODE_FOR_ashlv4hi3
+#define CODE_FOR_loongson_psllw CODE_FOR_ashlv2si3
+#define CODE_FOR_loongson_psrlh CODE_FOR_lshrv4hi3
+#define CODE_FOR_loongson_psrlw CODE_FOR_lshrv2si3
+#define CODE_FOR_loongson_psrah CODE_FOR_ashrv4hi3
+#define CODE_FOR_loongson_psraw CODE_FOR_ashrv2si3
#define CODE_FOR_loongson_psubw CODE_FOR_subv2si3
#define CODE_FOR_loongson_psubh CODE_FOR_subv4hi3
#define CODE_FOR_loongson_psubb CODE_FOR_subv8qi3
DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, dsp_32),
DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, dsp_32),
DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dsp_32),
+ DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dsp_32),
+ DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dsp_32),
+ DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dsp_32),
/* The following are for the MIPS DSP ASE REV 2 (32-bit only). */
DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
- DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
- DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
- DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
- DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
- DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dspr2_32),
- DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dspr2_32),
DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache)
};
+/* Index I is the function declaration for mips_builtins[I], or null if the
+ function isn't defined on this target. */
+static GTY(()) tree mips_builtin_decls[ARRAY_SIZE (mips_builtins)];
+
/* MODE is a vector mode whose elements have type TYPE. Return the type
of the vector itself. */
{
d = &mips_builtins[i];
if (d->avail ())
- add_builtin_function (d->name,
- mips_build_function_type (d->function_type),
- i, BUILT_IN_MD, NULL, NULL);
+ mips_builtin_decls[i]
+ = add_builtin_function (d->name,
+ mips_build_function_type (d->function_type),
+ i, BUILT_IN_MD, NULL, NULL);
}
}
+/* Implement TARGET_BUILTIN_DECL. */
+
+static tree
+mips_builtin_decl (unsigned int code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+ if (code >= ARRAY_SIZE (mips_builtins))
+ return error_mark_node;
+ return mips_builtin_decls[code];
+}
+
/* Take argument ARGNO from EXP's argument list and convert it into a
form suitable for input operand OPNO of instruction ICODE. Return the
value. */
}
\f
/* If INSN is a call, return the underlying CALL expr. Return NULL_RTX
- otherwise. */
+ otherwise. If INSN has two call rtx, then store the second one in
+ SECOND_CALL. */
static rtx
-mips_call_expr_from_insn (rtx insn)
+mips_call_expr_from_insn (rtx insn, rtx *second_call)
{
rtx x;
+ rtx x2;
if (!CALL_P (insn))
return NULL_RTX;
x = PATTERN (insn);
if (GET_CODE (x) == PARALLEL)
- x = XVECEXP (x, 0, 0);
+ {
+ /* Calls returning complex values have two CALL rtx. Look for the second
+ one here, and return it via the SECOND_CALL arg. */
+ x2 = XVECEXP (x, 0, 1);
+ if (GET_CODE (x2) == SET)
+ x2 = XEXP (x2, 1);
+ if (GET_CODE (x2) == CALL)
+ *second_call = x2;
+
+ x = XVECEXP (x, 0, 0);
+ }
if (GET_CODE (x) == SET)
x = XEXP (x, 1);
-
gcc_assert (GET_CODE (x) == CALL);
+
return x;
}
FOR_EACH_BB (bb)
FOR_BB_INSNS (bb, insn)
{
- rtx call, reg, symbol;
+ rtx call, reg, symbol, second_call;
- call = mips_call_expr_from_insn (insn);
+ second_call = 0;
+ call = mips_call_expr_from_insn (insn, &second_call);
if (!call)
continue;
gcc_assert (MEM_P (XEXP (call, 0)));
symbol = mips_find_pic_call_symbol (insn, reg);
if (symbol)
- mips_annotate_pic_call_expr (call, symbol);
+ {
+ mips_annotate_pic_call_expr (call, symbol);
+ if (second_call)
+ mips_annotate_pic_call_expr (second_call, symbol);
+ }
}
}
\f
/* (Re)initialize MIPS target internals for new ISA. */
mips_init_relocs ();
- if (was_mips16_p >= 0 || was_mips16_pch_p >= 0)
- /* Reinitialize target-dependent state. */
- target_reinit ();
+ if (mips16_p)
+ {
+ if (!mips16_globals)
+ mips16_globals = save_target_globals ();
+ else
+ restore_target_globals (mips16_globals);
+ }
+ else
+ restore_target_globals (&default_target_globals);
was_mips16_p = mips16_p;
was_mips16_pch_p = mips16_p;
static struct machine_function *
mips_init_machine_status (void)
{
- return ((struct machine_function *)
- ggc_alloc_cleared (sizeof (struct machine_function)));
+ return ggc_alloc_cleared_machine_function ();
}
/* Return the processor associated with the given ISA level, or null
}
}
-/* Implement OVERRIDE_OPTIONS. */
+/* Implement TARGET_OPTION_OVERRIDE. */
-void
-mips_override_options (void)
+static void
+mips_option_override (void)
{
int i, start, regno, mode;
SUBTARGET_OVERRIDE_OPTIONS;
#endif
+ /* -mno-float overrides -mhard-float and -msoft-float. */
+ if (TARGET_NO_FLOAT)
+ {
+ target_flags |= MASK_SOFT_FLOAT_ABI;
+ target_flags_explicit |= MASK_SOFT_FLOAT_ABI;
+ }
+
+ if (TARGET_FLIP_MIPS16)
+ TARGET_INTERLINK_MIPS16 = 1;
+
/* Set the small data limit. */
- mips_small_data_threshold = (g_switch_set
+ mips_small_data_threshold = (global_options_set.x_g_switch_value
? g_switch_value
: MIPS_DEFAULT_GVALUE);
mips_set_mips16_mode (false);
}
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
+static const struct default_options mips_option_optimization_table[] =
+ {
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
+
/* Swap the register information for registers I and I + 1, which
currently have the wrong endianness. Note that the registers'
fixedness and call-clobberedness might have been set on the
/* Say that the epilogue uses the return address register. Note that
in the case of sibcalls, the values "used by the epilogue" are
considered live at the start of the called function. */
- if (regno == 31)
+ if (regno == RETURN_ADDR_REGNUM)
return true;
/* If using a GOT, say that the epilogue also uses GOT_VERSION_REGNUM.
if (mips_need_noat_wrapper_p (insn, opvec, noperands))
mips_pop_asm_switch (&mips_noat);
}
-\f
-/* Implement TARGET_ASM_TRAMPOLINE_TEMPLATE. */
-static void
-mips_asm_trampoline_template (FILE *f)
+/* Return the function that is used to expand the <u>mulsidi3 pattern.
+ EXT_CODE is the code of the extension used. Return NULL if widening
+ multiplication shouldn't be used. */
+
+mulsidi3_gen_fn
+mips_mulsidi3_gen_fn (enum rtx_code ext_code)
{
- if (ptr_mode == DImode)
- fprintf (f, "\t.word\t0x03e0082d\t\t# dmove $1,$31\n");
- else
- fprintf (f, "\t.word\t0x03e00821\t\t# move $1,$31\n");
- fprintf (f, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n");
- fprintf (f, "\t.word\t0x00000000\t\t# nop\n");
- if (ptr_mode == DImode)
+ bool signed_p;
+
+ signed_p = ext_code == SIGN_EXTEND;
+ if (TARGET_64BIT)
{
- fprintf (f, "\t.word\t0xdff90014\t\t# ld $25,20($31)\n");
- fprintf (f, "\t.word\t0xdfef001c\t\t# ld $15,28($31)\n");
+ /* Don't use widening multiplication with MULT when we have DMUL. Even
+ with the extension of its input operands DMUL is faster. Note that
+ the extension is not needed for signed multiplication. In order to
+ ensure that we always remove the redundant sign-extension in this
+ case we still expand mulsidi3 for DMUL. */
+ if (ISA_HAS_DMUL3)
+ return signed_p ? gen_mulsidi3_64bit_dmul : NULL;
+ if (TARGET_FIX_R4000)
+ return NULL;
+ return signed_p ? gen_mulsidi3_64bit : gen_umulsidi3_64bit;
}
else
{
- fprintf (f, "\t.word\t0x8ff90010\t\t# lw $25,16($31)\n");
- fprintf (f, "\t.word\t0x8fef0014\t\t# lw $15,20($31)\n");
- }
- fprintf (f, "\t.word\t0x03200008\t\t# jr $25\n");
- if (ptr_mode == DImode)
- {
- fprintf (f, "\t.word\t0x0020f82d\t\t# dmove $31,$1\n");
- fprintf (f, "\t.word\t0x00000000\t\t# <padding>\n");
- fprintf (f, "\t.dword\t0x00000000\t\t# <function address>\n");
- fprintf (f, "\t.dword\t0x00000000\t\t# <static chain value>\n");
+ if (TARGET_FIX_R4000 && !ISA_HAS_DSP)
+ return signed_p ? gen_mulsidi3_32bit_r4000 : gen_umulsidi3_32bit_r4000;
+ return signed_p ? gen_mulsidi3_32bit : gen_umulsidi3_32bit;
}
+}
+\f
+/* Return the size in bytes of the trampoline code, padded to
+ TRAMPOLINE_ALIGNMENT bits. The static chain pointer and target
+ function address immediately follow. */
+
+int
+mips_trampoline_code_size (void)
+{
+ if (TARGET_USE_PIC_FN_ADDR_REG)
+ return 4 * 4;
+ else if (ptr_mode == DImode)
+ return 8 * 4;
+ else if (ISA_HAS_LOAD_DELAY)
+ return 6 * 4;
else
- {
- fprintf (f, "\t.word\t0x0020f821\t\t# move $31,$1\n");
- fprintf (f, "\t.word\t0x00000000\t\t# <function address>\n");
- fprintf (f, "\t.word\t0x00000000\t\t# <static chain value>\n");
- }
+ return 4 * 4;
}
/* Implement TARGET_TRAMPOLINE_INIT. */
static void
mips_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
{
- rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
- rtx mem, addr, end_addr;
+ rtx addr, end_addr, high, low, opcode, mem;
+ rtx trampoline[8];
+ unsigned int i, j;
+ HOST_WIDE_INT end_addr_offset, static_chain_offset, target_function_offset;
- emit_block_move (m_tramp, assemble_trampoline_template (),
- GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
+ /* Work out the offsets of the pointers from the start of the
+ trampoline code. */
+ end_addr_offset = mips_trampoline_code_size ();
+ static_chain_offset = end_addr_offset;
+ target_function_offset = static_chain_offset + GET_MODE_SIZE (ptr_mode);
- mem = adjust_address (m_tramp, ptr_mode, ptr_mode == DImode ? 32 : 28);
- mips_emit_move (mem, force_reg (ptr_mode, fnaddr));
- mem = adjust_address (mem, ptr_mode, GET_MODE_SIZE (ptr_mode));
- mips_emit_move (mem, force_reg (ptr_mode, chain_value));
+ /* Get pointers to the beginning and end of the code block. */
+ addr = force_reg (Pmode, XEXP (m_tramp, 0));
+ end_addr = mips_force_binary (Pmode, PLUS, addr, GEN_INT (end_addr_offset));
- addr = force_reg (ptr_mode, XEXP (m_tramp, 0));
- end_addr = gen_reg_rtx (ptr_mode);
+#define OP(X) gen_int_mode (X, SImode)
+
+ /* Build up the code in TRAMPOLINE. */
+ i = 0;
+ if (TARGET_USE_PIC_FN_ADDR_REG)
+ {
+ /* $25 contains the address of the trampoline. Emit code of the form:
+
+ l[wd] $1, target_function_offset($25)
+ l[wd] $static_chain, static_chain_offset($25)
+ jr $1
+ move $25,$1. */
+ trampoline[i++] = OP (MIPS_LOAD_PTR (AT_REGNUM,
+ target_function_offset,
+ PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+ static_chain_offset,
+ PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_JR (AT_REGNUM));
+ trampoline[i++] = OP (MIPS_MOVE (PIC_FUNCTION_ADDR_REGNUM, AT_REGNUM));
+ }
+ else if (ptr_mode == DImode)
+ {
+ /* It's too cumbersome to create the full 64-bit address, so let's
+ instead use:
+
+ move $1, $31
+ bal 1f
+ nop
+ 1: l[wd] $25, target_function_offset - 12($31)
+ l[wd] $static_chain, static_chain_offset - 12($31)
+ jr $25
+ move $31, $1
+
+ where 12 is the offset of "1:" from the start of the code block. */
+ trampoline[i++] = OP (MIPS_MOVE (AT_REGNUM, RETURN_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_BAL (1));
+ trampoline[i++] = OP (MIPS_NOP);
+ trampoline[i++] = OP (MIPS_LOAD_PTR (PIC_FUNCTION_ADDR_REGNUM,
+ target_function_offset - 12,
+ RETURN_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+ static_chain_offset - 12,
+ RETURN_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_MOVE (RETURN_ADDR_REGNUM, AT_REGNUM));
+ }
+ else
+ {
+ /* If the target has load delays, emit:
+
+ lui $1, %hi(end_addr)
+ lw $25, %lo(end_addr + ...)($1)
+ lw $static_chain, %lo(end_addr + ...)($1)
+ jr $25
+ nop
+
+ Otherwise emit:
+
+ lui $1, %hi(end_addr)
+ lw $25, %lo(end_addr + ...)($1)
+ jr $25
+ lw $static_chain, %lo(end_addr + ...)($1). */
+
+ /* Split END_ADDR into %hi and %lo values. Trampolines are aligned
+ to 64 bits, so the %lo value will have the bottom 3 bits clear. */
+ high = expand_simple_binop (SImode, PLUS, end_addr, GEN_INT (0x8000),
+ NULL, false, OPTAB_WIDEN);
+ high = expand_simple_binop (SImode, LSHIFTRT, high, GEN_INT (16),
+ NULL, false, OPTAB_WIDEN);
+ low = convert_to_mode (SImode, gen_lowpart (HImode, end_addr), true);
+
+ /* Emit the LUI. */
+ opcode = OP (MIPS_LUI (AT_REGNUM, 0));
+ trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, high,
+ NULL, false, OPTAB_WIDEN);
+
+ /* Emit the load of the target function. */
+ opcode = OP (MIPS_LOAD_PTR (PIC_FUNCTION_ADDR_REGNUM,
+ target_function_offset - end_addr_offset,
+ AT_REGNUM));
+ trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low,
+ NULL, false, OPTAB_WIDEN);
+
+ /* Emit the JR here, if we can. */
+ if (!ISA_HAS_LOAD_DELAY)
+ trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+
+ /* Emit the load of the static chain register. */
+ opcode = OP (MIPS_LOAD_PTR (STATIC_CHAIN_REGNUM,
+ static_chain_offset - end_addr_offset,
+ AT_REGNUM));
+ trampoline[i++] = expand_simple_binop (SImode, IOR, opcode, low,
+ NULL, false, OPTAB_WIDEN);
+
+ /* Emit the JR, if we couldn't above. */
+ if (ISA_HAS_LOAD_DELAY)
+ {
+ trampoline[i++] = OP (MIPS_JR (PIC_FUNCTION_ADDR_REGNUM));
+ trampoline[i++] = OP (MIPS_NOP);
+ }
+ }
+
+#undef OP
+
+ /* Copy the trampoline code. Leave any padding uninitialized. */
+ for (j = 0; j < i; j++)
+ {
+ mem = adjust_address (m_tramp, SImode, j * GET_MODE_SIZE (SImode));
+ mips_emit_move (mem, trampoline[j]);
+ }
+
+ /* Set up the static chain pointer field. */
+ mem = adjust_address (m_tramp, ptr_mode, static_chain_offset);
+ mips_emit_move (mem, chain_value);
+
+ /* Set up the target function field. */
+ mem = adjust_address (m_tramp, ptr_mode, target_function_offset);
+ mips_emit_move (mem, XEXP (DECL_RTL (fndecl), 0));
+
+ /* Flush the code part of the trampoline. */
emit_insn (gen_add3_insn (end_addr, addr, GEN_INT (TRAMPOLINE_SIZE)));
emit_insn (gen_clear_cache (addr, end_addr));
}
+/* Implement FUNCTION_PROFILER. */
+
+void mips_function_profiler (FILE *file)
+{
+ if (TARGET_MIPS16)
+ sorry ("mips16 function profiling");
+ if (TARGET_LONG_CALLS)
+ {
+ /* For TARGET_LONG_CALLS use $3 for the address of _mcount. */
+ if (Pmode == DImode)
+ fprintf (file, "\tdla\t%s,_mcount\n", reg_names[3]);
+ else
+ fprintf (file, "\tla\t%s,_mcount\n", reg_names[3]);
+ }
+ mips_push_asm_switch (&mips_noat);
+ fprintf (file, "\tmove\t%s,%s\t\t# save current return address\n",
+ reg_names[AT_REGNUM], reg_names[RETURN_ADDR_REGNUM]);
+ /* _mcount treats $2 as the static chain register. */
+ if (cfun->static_chain_decl != NULL)
+ fprintf (file, "\tmove\t%s,%s\n", reg_names[2],
+ reg_names[STATIC_CHAIN_REGNUM]);
+ if (TARGET_MCOUNT_RA_ADDRESS)
+ {
+ /* If TARGET_MCOUNT_RA_ADDRESS load $12 with the address of the
+ ra save location. */
+ if (cfun->machine->frame.ra_fp_offset == 0)
+ /* ra not saved, pass zero. */
+ fprintf (file, "\tmove\t%s,%s\n", reg_names[12], reg_names[0]);
+ else
+ fprintf (file, "\t%s\t%s," HOST_WIDE_INT_PRINT_DEC "(%s)\n",
+ Pmode == DImode ? "dla" : "la", reg_names[12],
+ cfun->machine->frame.ra_fp_offset,
+ reg_names[STACK_POINTER_REGNUM]);
+ }
+ if (!TARGET_NEWABI)
+ fprintf (file,
+ "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from stack\n",
+ TARGET_64BIT ? "dsubu" : "subu",
+ reg_names[STACK_POINTER_REGNUM],
+ reg_names[STACK_POINTER_REGNUM],
+ Pmode == DImode ? 16 : 8);
+
+ if (TARGET_LONG_CALLS)
+ fprintf (file, "\tjalr\t%s\n", reg_names[3]);
+ else
+ fprintf (file, "\tjal\t_mcount\n");
+ mips_pop_asm_switch (&mips_noat);
+ /* _mcount treats $2 as the static chain register. */
+ if (cfun->static_chain_decl != NULL)
+ fprintf (file, "\tmove\t%s,%s\n", reg_names[STATIC_CHAIN_REGNUM],
+ reg_names[2]);
+}
+
+/* Implement TARGET_SHIFT_TRUNCATION_MASK. We want to keep the default
+ behaviour of TARGET_SHIFT_TRUNCATION_MASK for non-vector modes even
+ when TARGET_LOONGSON_2EF is true. */
+
+static unsigned HOST_WIDE_INT
+mips_shift_truncation_mask (enum machine_mode mode)
+{
+ if (TARGET_LOONGSON_2EF && VECTOR_MODE_P (mode))
+ return 0;
+
+ return GET_MODE_BITSIZE (mode) - 1;
+}
+
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#undef TARGET_ASM_ALIGNED_DI_OP
#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE mips_option_override
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE mips_option_optimization_table
+
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS mips_legitimize_address
#undef TARGET_SCHED_REORDER
#define TARGET_SCHED_REORDER mips_sched_reorder
#undef TARGET_SCHED_REORDER2
-#define TARGET_SCHED_REORDER2 mips_sched_reorder
+#define TARGET_SCHED_REORDER2 mips_sched_reorder2
#undef TARGET_SCHED_VARIABLE_ISSUE
#define TARGET_SCHED_VARIABLE_ISSUE mips_variable_issue
#undef TARGET_SCHED_ADJUST_COST
#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
mips_multipass_dfa_lookahead
+#undef TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P
+#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P \
+ mips_small_register_classes_for_mode_p
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS \
#undef TARGET_VALID_POINTER_MODE
#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST mips_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST mips_memory_move_cost
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS mips_rtx_costs
#undef TARGET_ADDRESS_COST
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
+#undef TARGET_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND mips_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS mips_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P mips_print_operand_punct_valid_p
+
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
#undef TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_CALLEE_COPIES mips_callee_copies
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG mips_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE mips_function_arg_advance
#undef TARGET_MODE_REP_EXTENDED
#define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended
#undef TARGET_SCALAR_MODE_SUPPORTED_P
#define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
+#undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
+#define TARGET_VECTORIZE_PREFERRED_SIMD_MODE mips_preferred_simd_mode
+
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS mips_init_builtins
+#undef TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL mips_builtin_decl
#undef TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN mips_expand_builtin
#undef TARGET_CAN_ELIMINATE
#define TARGET_CAN_ELIMINATE mips_can_eliminate
-#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
-#define TARGET_ASM_TRAMPOLINE_TEMPLATE mips_asm_trampoline_template
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT mips_trampoline_init
+#undef TARGET_ASM_OUTPUT_SOURCE_FILENAME
+#define TARGET_ASM_OUTPUT_SOURCE_FILENAME mips_output_filename
+
+#undef TARGET_SHIFT_TRUNCATION_MASK
+#define TARGET_SHIFT_TRUNCATION_MASK mips_shift_truncation_mask
+
struct gcc_target targetm = TARGET_INITIALIZER;
\f
#include "gt-mips.h"