/* 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"
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;
: 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
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
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.
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);
- }
}
}
/* 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
}
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;
}
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)
{
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
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);
}
\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
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;
+ }
+
/* Set the small data limit. */
mips_small_data_threshold = (g_switch_set
? g_switch_value
if (mips_need_noat_wrapper_p (insn, opvec, noperands))
mips_pop_asm_switch (&mips_noat);
}
+
+/* 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)
+{
+ bool signed_p;
+
+ signed_p = ext_code == SIGN_EXTEND;
+ if (TARGET_64BIT)
+ {
+ /* 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
+ {
+ if (TARGET_FIX_R4000)
+ return signed_p ? gen_mulsidi3_32bit_r4000 : gen_umulsidi3_32bit_r4000;
+ if (ISA_HAS_DSPR2)
+ return signed_p ? gen_mips_mult : gen_mips_multu;
+ 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
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]);
+}
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#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 \