aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/mips/mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/mips/mips.c')
-rw-r--r--gcc/config/mips/mips.c297
1 files changed, 199 insertions, 98 deletions
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
index f67c5454c0c..0de3b691904 100644
--- a/gcc/config/mips/mips.c
+++ b/gcc/config/mips/mips.c
@@ -84,6 +84,8 @@ enum internal_test {
struct constant;
struct mips_arg_info;
static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code));
+static void get_float_compare_codes PARAMS ((enum rtx_code, enum rtx_code *,
+ enum rtx_code *));
static int mips16_simple_memory_operand PARAMS ((rtx, rtx,
enum machine_mode));
static int m16_check_op PARAMS ((rtx, int, int, int));
@@ -216,7 +218,7 @@ struct mips_arg_info
unsigned int stack_words;
/* The offset from the start of the stack overflow area of the argument's
- first stack word. Only meaningful when STACK_WORDS is non-zero. */
+ first stack word. Only meaningful when STACK_WORDS is nonzero. */
unsigned int stack_offset;
};
@@ -238,7 +240,7 @@ int sdb_label_count = 0;
/* Next label # for each statement for Silicon Graphics IRIS systems. */
int sym_lineno = 0;
-/* Non-zero if inside of a function, because the stupid MIPS asm can't
+/* Nonzero if inside of a function, because the stupid MIPS asm can't
handle .files inside of functions. */
int inside_function = 0;
@@ -352,11 +354,6 @@ int mips_split_addresses;
/* Generating calls to position independent functions? */
enum mips_abicalls_type mips_abicalls;
-/* High and low marks for floating point values which we will accept
- as legitimate constants for LEGITIMATE_CONSTANT_P. These are
- initialized in override_options. */
-REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow;
-
/* Mode used for saving/restoring general purpose registers. */
static enum machine_mode gpr_mode;
@@ -370,6 +367,11 @@ char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
constant pool is output. */
int mips_string_length;
+/* When generating mips16 code, a list of all strings that are to be
+ output after the current function. */
+
+static GTY(()) rtx mips16_strings;
+
/* In mips16 mode, we build a list of all the string constants we see
in a particular function. */
@@ -594,9 +596,8 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
{ "r4000", PROCESSOR_R4000, 3 },
{ "vr4100", PROCESSOR_R4100, 3 },
{ "vr4111", PROCESSOR_R4111, 3 },
- { "vr4121", PROCESSOR_R4121, 3 },
+ { "vr4120", PROCESSOR_R4120, 3 },
{ "vr4300", PROCESSOR_R4300, 3 },
- { "vr4320", PROCESSOR_R4320, 3 },
{ "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */
{ "r4600", PROCESSOR_R4600, 3 },
{ "orion", PROCESSOR_R4600, 3 }, /* = r4600 */
@@ -625,6 +626,11 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
{ 0, 0, 0 }
};
+/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT. */
+#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
+#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0
+#endif
+
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
@@ -659,6 +665,9 @@ const struct mips_cpu_info mips_cpu_info_table[] = {
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
+
struct gcc_target targetm = TARGET_INITIALIZER;
/* Return truth value of whether OP can be used as an operands
@@ -813,45 +822,22 @@ mips_const_double_ok (op, mode)
rtx op;
enum machine_mode mode;
{
- REAL_VALUE_TYPE d;
-
if (GET_CODE (op) != CONST_DOUBLE)
return 0;
if (mode == VOIDmode)
return 1;
+ /* We've no zero register in mips16 mode. */
+ if (TARGET_MIPS16)
+ return 0;
+
if (mode != SFmode && mode != DFmode)
return 0;
if (op == CONST0_RTX (mode))
return 1;
- /* ??? li.s does not work right with SGI's Irix 6 assembler. */
- if (mips_abi != ABI_32 && mips_abi != ABI_O64 && mips_abi != ABI_EABI)
- return 0;
-
- REAL_VALUE_FROM_CONST_DOUBLE (d, op);
-
- if (REAL_VALUE_ISNAN (d))
- return FALSE;
-
- if (REAL_VALUE_NEGATIVE (d))
- d = REAL_VALUE_NEGATE (d);
-
- if (mode == DFmode)
- {
- if (REAL_VALUES_LESS (d, dfhigh)
- && REAL_VALUES_LESS (dflow, d))
- return 1;
- }
- else
- {
- if (REAL_VALUES_LESS (d, sfhigh)
- && REAL_VALUES_LESS (sflow, d))
- return 1;
- }
-
return 0;
}
@@ -863,9 +849,6 @@ const_float_1_operand (op, mode)
enum machine_mode mode;
{
REAL_VALUE_TYPE d;
- static REAL_VALUE_TYPE onedf;
- static REAL_VALUE_TYPE onesf;
- static int one_initialized;
if (GET_CODE (op) != CONST_DOUBLE
|| mode != GET_MODE (op)
@@ -874,19 +857,7 @@ const_float_1_operand (op, mode)
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
- /* We only initialize these values if we need them, since we will
- never get called unless mips_isa >= 4. */
- if (! one_initialized)
- {
- onedf = REAL_VALUE_ATOF ("1.0", DFmode);
- onesf = REAL_VALUE_ATOF ("1.0", SFmode);
- one_initialized = 1;
- }
-
- if (mode == DFmode)
- return REAL_VALUES_EQUAL (d, onedf);
- else
- return REAL_VALUES_EQUAL (d, onesf);
+ return REAL_VALUES_EQUAL (d, dconst1);
}
/* Return true if a memory load or store of REG plus OFFSET in MODE
@@ -1496,7 +1467,7 @@ mips_reg_mode_ok_for_base_p (reg, mode, strict)
/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It
returns a nonzero value if XINSN is a legitimate address for a
- memory operand of the indicated MODE. STRICT is non-zero if this
+ memory operand of the indicated MODE. STRICT is nonzero if this
function is called during reload. */
int
@@ -3144,7 +3115,7 @@ map_test_to_internal_test (test_code)
??? This is called with result nonzero by the Scond patterns in
mips.md. These patterns are called with a target in the mode of
the Scond instruction pattern. Since this must be a constant, we
- must use SImode. This means that if RESULT is non-zero, it will
+ must use SImode. This means that if RESULT is nonzero, it will
always be an SImode register, even if TARGET_64BIT is true. We
cope with this by calling convert_move rather than emit_move_insn.
This will sometimes lead to an unnecessary extension of the result;
@@ -3354,6 +3325,34 @@ gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
return result;
}
+/* Work out how to check a floating-point condition. We need a
+ separate comparison instruction (C.cond.fmt), followed by a
+ branch or conditional move. Given that IN_CODE is the
+ required condition, set *CMP_CODE to the C.cond.fmt code
+ and *action_code to the branch or move code. */
+
+static void
+get_float_compare_codes (in_code, cmp_code, action_code)
+ enum rtx_code in_code, *cmp_code, *action_code;
+{
+ switch (in_code)
+ {
+ case NE:
+ case UNGE:
+ case UNGT:
+ case LTGT:
+ case ORDERED:
+ *cmp_code = reverse_condition_maybe_unordered (in_code);
+ *action_code = EQ;
+ break;
+
+ default:
+ *cmp_code = in_code;
+ *action_code = NE;
+ break;
+ }
+}
+
/* Emit the common code for doing conditional branches.
operand[0] is the label to jump to.
The comparison operands are saved away by cmp{si,di,sf,df}. */
@@ -3367,6 +3366,7 @@ gen_conditional_branch (operands, test_code)
rtx cmp0 = branch_cmp[0];
rtx cmp1 = branch_cmp[1];
enum machine_mode mode;
+ enum rtx_code cmp_code;
rtx reg;
int invert;
rtx label1, label2;
@@ -3386,7 +3386,7 @@ gen_conditional_branch (operands, test_code)
test_code = NE;
}
else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
- /* We don't want to build a comparison against a non-zero
+ /* We don't want to build a comparison against a nonzero
constant. */
cmp1 = force_reg (mode, cmp1);
@@ -3399,15 +3399,10 @@ gen_conditional_branch (operands, test_code)
else
reg = gen_reg_rtx (CCmode);
- /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result ==
- 0 in the instruction built below. The MIPS FPU handles
- inequality testing by testing for equality and looking for a
- false result. */
+ get_float_compare_codes (test_code, &cmp_code, &test_code);
emit_insn (gen_rtx_SET (VOIDmode, reg,
- gen_rtx (test_code == NE ? EQ : test_code,
- CCmode, cmp0, cmp1)));
+ gen_rtx (cmp_code, CCmode, cmp0, cmp1)));
- test_code = test_code == NE ? EQ : NE;
mode = CCmode;
cmp0 = reg;
cmp1 = const0_rtx;
@@ -3501,8 +3496,8 @@ gen_conditional_move (operands)
abort ();
}
}
- else if (cmp_code == NE)
- cmp_code = EQ, move_code = EQ;
+ else
+ get_float_compare_codes (cmp_code, &cmp_code, &move_code);
if (mode == SImode || mode == DImode)
cmp_mode = mode;
@@ -3563,6 +3558,51 @@ mips_gen_conditional_trap (operands)
operands[1]));
}
+/* Return true if operand OP is a condition code register.
+ Only for use during or after reload. */
+
+int
+fcc_register_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return ((mode == VOIDmode || mode == GET_MODE (op))
+ && (reload_in_progress || reload_completed)
+ && (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
+ && ST_REG_P (true_regnum (op)));
+}
+
+/* Emit code to move general operand SRC into condition-code
+ register DEST. SCRATCH is a scratch TFmode float register.
+ The sequence is:
+
+ FP1 = SRC
+ FP2 = 0.0f
+ DEST = FP2 < FP1
+
+ where FP1 and FP2 are single-precision float registers
+ taken from SCRATCH. */
+
+void
+mips_emit_fcc_reload (dest, src, scratch)
+ rtx dest, src, scratch;
+{
+ rtx fp1, fp2;
+
+ /* Change the source to SFmode. */
+ if (GET_CODE (src) == MEM)
+ src = adjust_address (src, SFmode, 0);
+ else if (GET_CODE (src) == REG || GET_CODE (src) == SUBREG)
+ src = gen_rtx_REG (SFmode, true_regnum (src));
+
+ fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
+ fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + FP_INC);
+
+ emit_move_insn (copy_rtx (fp1), src);
+ emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
+ emit_insn (gen_slt_sf (dest, fp2, fp1));
+}
+
/* Emit code to change the current function's return address to
ADDRESS. SCRATCH is available as a scratch register, if needed.
ADDRESS and SCRATCH are both word-mode GPRs. */
@@ -5188,6 +5228,24 @@ override_options ()
target_flags &= ~MASK_LONG64;
}
+ if (MIPS_MARCH_CONTROLS_SOFT_FLOAT
+ && (target_flags_explicit & MASK_SOFT_FLOAT) == 0)
+ {
+ /* For some configurations, it is useful to have -march control
+ the default setting of MASK_SOFT_FLOAT. */
+ switch ((int) mips_arch)
+ {
+ case PROCESSOR_R4100:
+ case PROCESSOR_R4120:
+ target_flags |= MASK_SOFT_FLOAT;
+ break;
+
+ default:
+ target_flags &= ~MASK_SOFT_FLOAT;
+ break;
+ }
+ }
+
if (mips_abi != ABI_32 && mips_abi != ABI_O64)
flag_pcc_struct_return = 0;
@@ -5310,14 +5368,6 @@ override_options ()
else
mips16 = 0;
- /* Initialize the high and low values for legitimate floating point
- constants. Rather than trying to get the accuracy down to the
- last bit, just use approximate ranges. */
- dfhigh = REAL_VALUE_ATOF ("1.0e300", DFmode);
- dflow = REAL_VALUE_ATOF ("1.0e-300", DFmode);
- sfhigh = REAL_VALUE_ATOF ("1.0e38", SFmode);
- sflow = REAL_VALUE_ATOF ("1.0e-38", SFmode);
-
mips_print_operand_punct['?'] = 1;
mips_print_operand_punct['#'] = 1;
mips_print_operand_punct['&'] = 1;
@@ -5411,7 +5461,9 @@ override_options ()
/* Allow integer modes that fit into a single
register. We need to put integers into FPRs
when using instructions like cvt and trunc. */
- || (class == MODE_INT && size <= UNITS_PER_FPREG)));
+ || (class == MODE_INT && size <= UNITS_PER_FPREG)
+ /* Allow TFmode for CCmode reloads. */
+ || (ISA_HAS_8CC && mode == TFmode)));
else if (MD_REG_P (regno))
temp = (class == MODE_INT
@@ -5884,12 +5936,10 @@ print_operand (file, op, letter)
else if (code == CONST_DOUBLE
&& GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
{
- REAL_VALUE_TYPE d;
- char s[30];
+ char s[60];
- REAL_VALUE_FROM_CONST_DOUBLE (d, op);
- REAL_VALUE_TO_DECIMAL (d, "%.20e", s);
- fprintf (file, s);
+ real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
+ fputs (s, file);
}
else if (letter == 'x' && GET_CODE (op) == CONST_INT)
@@ -6062,7 +6112,7 @@ mips_assemble_integer (x, size, aligned_p)
If we have -G 0, or the extern size is unknown, or the object is in a user
specified section that is not .sbss/.sdata, don't bother emitting the
- .externs. In the case of user specified sections this behaviour is
+ .externs. In the case of user specified sections this behavior is
required as otherwise GAS will think the object lives in .sbss/.sdata. */
int
@@ -6897,7 +6947,6 @@ save_restore_insns (store_p, large_reg, large_offset)
HOST_WIDE_INT gp_offset;
HOST_WIDE_INT fp_offset;
HOST_WIDE_INT end_offset;
- rtx insn;
if (frame_pointer_needed
&& ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
@@ -6951,11 +7000,9 @@ save_restore_insns (store_p, large_reg, large_offset)
base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
base_offset = large_offset;
if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
+ emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
else
- insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
+ emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
}
else
{
@@ -7060,11 +7107,9 @@ save_restore_insns (store_p, large_reg, large_offset)
base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
base_offset = large_offset;
if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
+ emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
else
- insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
+ emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
}
else
{
@@ -7687,6 +7732,7 @@ mips_output_function_epilogue (file, size)
HOST_WIDE_INT size ATTRIBUTE_UNUSED;
{
const char *fnname = ""; /* FIXME: Correct initialisation? */
+ rtx string;
#ifndef FUNCTION_NAME_ALREADY_DECLARED
/* Get the function name the same way that toplev.c does before calling
@@ -7751,6 +7797,17 @@ mips_output_function_epilogue (file, size)
string_constants = next;
}
+ /* If any following function uses the same strings as this one, force
+ them to refer those strings indirectly. Nearby functions could
+ refer them using pc-relative addressing, but it isn't safe in
+ general. For instance, some functions may be placed in sections
+ other than .text, and we don't know whether they be close enough
+ to this one. In large files, even other .text functions can be
+ too far away. */
+ for (string = mips16_strings; string != 0; string = XEXP (string, 1))
+ SYMBOL_REF_FLAG (XEXP (string, 0)) = 0;
+ free_EXPR_LIST_list (&mips16_strings);
+
/* Restore the output file if optimizing the GP (optimizing the GP causes
the text to be diverted to a tempfile, so that data decls come before
references to the data). */
@@ -7938,7 +7995,7 @@ mips_can_use_return_insn ()
return compute_frame_size (get_frame_size ()) == 0;
}
-/* Returns non-zero if X contains a SYMBOL_REF. */
+/* Returns nonzero if X contains a SYMBOL_REF. */
static int
symbolic_expression_p (x)
@@ -8092,7 +8149,7 @@ mips_select_section (decl, reloc, align)
precisely correct.
When not mips16 code nor embedded PIC, if a symbol is in a
- gp addresable section, SYMBOL_REF_FLAG is set prevent gcc from
+ gp addressable section, SYMBOL_REF_FLAG is set prevent gcc from
splitting the reference so that gas can generate a gp relative
reference.
@@ -8129,7 +8186,11 @@ mips_encode_section_info (decl, first)
&& (! current_function_decl
|| ! DECL_ONE_ONLY (current_function_decl)))
{
- SYMBOL_REF_FLAG (XEXP (TREE_CST_RTL (decl), 0)) = 1;
+ rtx symref;
+
+ symref = XEXP (TREE_CST_RTL (decl), 0);
+ mips16_strings = alloc_EXPR_LIST (0, symref, mips16_strings);
+ SYMBOL_REF_FLAG (symref) = 1;
mips_string_length += TREE_STRING_LENGTH (decl);
}
}
@@ -8336,6 +8397,38 @@ function_arg_pass_by_reference (cum, mode, type, named)
return size == -1 || size > UNITS_PER_WORD;
}
+/* Return the class of registers for which a mode change from FROM to TO
+ is invalid.
+
+ In little-endian mode, the hi-lo registers are numbered backwards,
+ so (subreg:SI (reg:DI hi) 0) gets the high word instead of the low
+ word as intended.
+
+ Similarly, when using paired floating-point registers, the first
+ register holds the low word, regardless of endianness. So in big
+ endian mode, (subreg:SI (reg:DF $f0) 0) does not get the high word
+ as intended.
+
+ Also, loading a 32-bit value into a 64-bit floating-point register
+ will not sign-extend the value, despite what LOAD_EXTEND_OP says.
+ We can't allow 64-bit float registers to change from a 32-bit
+ mode to a 64-bit mode. */
+
+enum reg_class
+mips_cannot_change_mode_class (from, to)
+ enum machine_mode from, to;
+{
+ if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
+ {
+ if (TARGET_BIG_ENDIAN)
+ return FP_REGS;
+ if (TARGET_FLOAT64)
+ return HI_AND_FP_REGS;
+ return HI_REG;
+ }
+ return NO_REGS;
+}
+
/* This function returns the register class required for a secondary
register when copying between one of the registers in CLASS, and X,
using MODE. If IN_P is nonzero, the copy is going from X to the
@@ -8526,6 +8619,14 @@ mips_class_max_nregs (class, mode)
else
return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
+
+bool
+mips_valid_pointer_mode (mode)
+ enum machine_mode mode;
+{
+ return (mode == SImode || (TARGET_64BIT && mode == DImode));
+}
+
/* For each mips16 function which refers to GP relative symbols, we
use a pseudo register, initialized at the start of the function, to
@@ -8771,7 +8872,7 @@ mips16_constant (x, mode, addr, addend)
/* Write out code to move floating point arguments in or out of
general registers. Output the instructions to FILE. FP_CODE is
the code describing which arguments are present (see the comment at
- the definition of CUMULATIVE_ARGS in mips.h). FROM_FP_P is non-zero if
+ the definition of CUMULATIVE_ARGS in mips.h). FROM_FP_P is nonzero if
we are copying from the floating point registers. */
static void
@@ -9968,11 +10069,11 @@ mips_adjust_insn_length (insn, length)
INSN is the branch instruction. OPERANDS[0] is the condition.
OPERANDS[1] is the target of the branch. OPERANDS[2] is the target
of the first operand to the condition. If TWO_OPERANDS_P is
- non-zero the comparison takes two operands; OPERANDS[3] will be the
+ nonzero the comparison takes two operands; OPERANDS[3] will be the
second operand.
- If INVERTED_P is non-zero we are to branch if the condition does
- not hold. If FLOAT_P is non-zero this is a floating-point comparison.
+ If INVERTED_P is nonzero we are to branch if the condition does
+ not hold. If FLOAT_P is nonzero this is a floating-point comparison.
LENGTH is the length (in bytes) of the sequence we are to generate.
That tells us whether to generate a simple conditional branch, or a
@@ -9994,7 +10095,7 @@ mips_output_conditional_branch (insn,
static char buffer[200];
/* The kind of comparison we are doing. */
enum rtx_code code = GET_CODE (operands[0]);
- /* Non-zero if the opcode for the comparison needs a `z' indicating
+ /* Nonzero if the opcode for the comparison needs a `z' indicating
that it is a comparision against zero. */
int need_z_p;
/* A string to use in the assembly output to represent the first
@@ -10021,7 +10122,7 @@ mips_output_conditional_branch (insn,
subtract B from A and then look at the sign bit. But, if we
are doing an unsigned comparison, and B is zero, we don't
have to do the subtraction. Instead, we can just check to
- see if A is non-zero. Thus, we change the CODE here to
+ see if A is nonzero. Thus, we change the CODE here to
reflect the simpler comparison operation. */
switch (code)
{
@@ -10130,7 +10231,7 @@ mips_output_conditional_branch (insn,
INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
}
output_asm_insn ("%>%)", 0);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+ (*targetm.asm_out.internal_label) (asm_out_file, "L",
CODE_LABEL_NUMBER (target));
return "";
}