diff options
Diffstat (limited to 'gcc/config/mips/mips.c')
-rw-r--r-- | gcc/config/mips/mips.c | 297 |
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 ""; } |