--- gcc/config/mips/mips.c 2018/04/24 18:21:54 1.1.1.3 +++ gcc/config/mips/mips.c 2018/04/24 18:29:29 1.1.1.4 @@ -1,9 +1,9 @@ /* Subroutines for insn-output.c for MIPS + Copyright (C) 1989, 90, 91, 93, 94, 1995 Free Software Foundation, Inc. Contributed by A. Lichnewsky, lich@inria.inria.fr. - Changes by Michael Meissner, meissner@osf.org. + Changes by Michael Meissner, meissner@osf.org. 64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and Brendan Eich, brendan@microunity.com. - Copyright (C) 1989, 1990, 1991, 1993, 1994 Free Software Foundation, Inc. This file is part of GNU CC. @@ -19,7 +19,8 @@ GNU General Public License for more deta You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ /* ??? The TARGET_FP_CALL_32 macros are intended to simulate a 32 bit calling convention in 64 bit mode. It doesn't work though, and should @@ -81,7 +82,6 @@ extern tree lookup_name (); extern void pfatal_with_name (); extern void warning (); -extern tree current_function_decl; extern FILE *asm_out_file; /* Enumeration for all of the relational tests, so that we can build @@ -117,7 +117,7 @@ int num_source_filenames = 0; start and end boundaries). */ int sdb_label_count = 0; -/* Next label # for each statment for Silicon Graphics IRIS systems. */ +/* 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 @@ -194,7 +194,7 @@ int mips_isa; /* Strings to hold which cpu and instruction set architecture to use. */ char *mips_cpu_string; /* for -mcpu= */ -char *mips_isa_string; /* for -mips{1,2,3} */ +char *mips_isa_string; /* for -mips{1,2,3,4} */ /* Generating calls to position independent functions? */ enum mips_abicalls_type mips_abicalls; @@ -246,7 +246,7 @@ char mips_reg_names[][8] = "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", - "hi", "lo", "$fcr31" + "hi", "lo", "accum","$fcr31" }; /* Mips software names for the registers, used to overwrite the @@ -262,7 +262,7 @@ char mips_sw_reg_names[][8] = "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31", - "hi", "lo", "$fcr31" + "hi", "lo", "accum","$fcr31" }; /* Map hard register number to register class */ @@ -284,7 +284,7 @@ enum reg_class mips_regno_to_class[] = FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, FP_REGS, - HI_REG, LO_REG, ST_REGS + HI_REG, LO_REG, HILO_REG, ST_REGS }; /* Map register constraint character to register class. */ @@ -409,7 +409,7 @@ small_int (op, mode) return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); } -/* Return truth value of whether OP is an integer which is too big to +/* Return truth value of whether OP is a 32 bit integer which is too big to be loaded with one instruction. */ int @@ -429,9 +429,7 @@ large_int (op, mode) if (((unsigned long)(value + 32768)) <= 32767) /* subu reg,$r0,value */ return FALSE; - if ((value & 0x0000ffff) == 0 /* lui reg,value>>16 */ - && ((value & ~2147483647) == 0 /* signed value */ - || (value & ~2147483647) == ~2147483647)) + if ((value & 0x0000ffff) == 0) /* lui reg,value>>16 */ return FALSE; return TRUE; @@ -478,7 +476,7 @@ mips_const_double_ok (op, mode) if (GET_CODE (op) != CONST_DOUBLE) return FALSE; - if (mode == DImode) + if (mode == VOIDmode) return TRUE; if (mode != SFmode && mode != DFmode) @@ -487,6 +485,10 @@ mips_const_double_ok (op, mode) if (op == CONST0_RTX (mode)) return TRUE; + /* ??? li.s does not work right with SGI's Irix 6 assembler. */ + if (ABI_64BIT) + return FALSE; + REAL_VALUE_FROM_CONST_DOUBLE (d, op); if (REAL_VALUE_ISNAN (d)) @@ -722,7 +724,7 @@ mips_fill_delay_slot (ret, type, operand dslots_number_nops = num_nops; mips_load_reg = set_reg; if (GET_MODE_SIZE (mode) - > (FP_REG_P (set_reg) ? UNITS_PER_FPREG : UNITS_PER_WORD)) + > (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD)) mips_load_reg2 = gen_rtx (REG, SImode, REGNO (set_reg) + 1); else mips_load_reg2 = 0; @@ -866,7 +868,29 @@ embedded_pic_offset (x) rtx x; { if (embedded_pic_fnaddr_rtx == NULL) - embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode); + { + rtx seq; + + embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode); + + /* Output code at function start to initialize the pseudo-reg. */ + /* ??? We used to do this in FINALIZE_PIC, but that does not work for + inline functions, because it is called after RTL for the function + has been copied. The pseudo-reg in embedded_pic_fnaddr_rtx however + does not get copied, and ends up not matching the rest of the RTL. + This solution works, but means that we get unnecessary code to + initialize this value every time a function is inlined into another + function. */ + start_sequence (); + emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx, + XEXP (DECL_RTL (current_function_decl), 0))); + seq = gen_sequence (); + end_sequence (); + push_topmost_sequence (); + emit_insn_after (seq, get_insns ()); + pop_topmost_sequence (); + } + return gen_rtx (CONST, Pmode, gen_rtx (MINUS, Pmode, x, XEXP (DECL_RTL (current_function_decl), 0))); @@ -925,7 +949,10 @@ mips_move_1word (operands, insn, unsigne else if (MD_REG_P (regno1)) { delay = DELAY_HILO; - ret = "mf%1\t%0"; + if (regno1 != HILO_REGNUM) + ret = "mf%1\t%0"; + else + ret = "mflo\t%0"; } else @@ -956,7 +983,8 @@ mips_move_1word (operands, insn, unsigne if (GP_REG_P (regno1)) { delay = DELAY_HILO; - ret = "mt%0\t%1"; + if (regno0 != HILO_REGNUM) + ret = "mt%0\t%1"; } } @@ -1016,8 +1044,18 @@ mips_move_1word (operands, insn, unsigne } } - else if (code1 == CONST_INT) - { + else if (code1 == CONST_INT + || (code1 == CONST_DOUBLE + && GET_MODE (op1) == VOIDmode)) + { + if (code1 == CONST_DOUBLE) + { + /* This can happen when storing constants into long long + bitfields. Just store the least significant word of + the value. */ + operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1)); + } + if (INTVAL (op1) == 0) { if (GP_REG_P (regno0)) @@ -1028,6 +1066,12 @@ mips_move_1word (operands, insn, unsigne delay = DELAY_LOAD; ret = "mtc1\t%z1,%0"; } + + else if (MD_REG_P (regno0)) + { + delay = DELAY_HILO; + ret = "mt%0\t%."; + } } else if (GP_REG_P (regno0)) @@ -1289,7 +1333,12 @@ mips_move_2words (operands, insn) { delay = DELAY_HILO; if (TARGET_64BIT) - ret = "mt%0\t%1"; + { + if (regno0 != HILO_REGNUM) + ret = "mt%0\t%1"; + else if (regno1 == 0) + ret = "mtlo\t%.\n\tmthi\t%."; + } else ret = "mthi\t%M1\n\tmtlo\t%L1"; } @@ -1298,7 +1347,10 @@ mips_move_2words (operands, insn) { delay = DELAY_HILO; if (TARGET_64BIT) - ret = "mf%1\t%0"; + { + if (regno1 != HILO_REGNUM) + ret = "mf%1\t%0"; + } else ret = "mfhi\t%M0\n\tmflo\t%L0"; } @@ -1328,9 +1380,8 @@ mips_move_2words (operands, insn) { if (TARGET_FLOAT64 && !TARGET_64BIT) { - operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1)); - operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1)); - ret = "li\t%M0,%3\n\tli\t%L0,%2"; + split_double (op1, operands + 2, operands + 3); + ret = "li\t%0,%2\n\tli\t%D0,%3"; } else ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32"; @@ -1340,14 +1391,13 @@ mips_move_2words (operands, insn) ret = "li.d\t%0,%1"; } - else if (TARGET_FLOAT64) - ret = "li\t%0,%1"; + else if (TARGET_64BIT) + ret = "dli\t%0,%1"; else { - operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1)); - operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1)); - ret = "li\t%M0,%3\n\tli\t%L0,%2"; + split_double (op1, operands + 2, operands + 3); + ret = "li\t%0,%2\n\tli\t%D0,%3"; } } @@ -1388,12 +1438,30 @@ mips_move_2words (operands, insn) ? "li.d\t%0,%1" : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"); } + else if (MD_REG_P (regno0)) + { + delay = DELAY_HILO; + if (regno0 != HILO_REGNUM) + ret = "mt%0\t%.\n"; + else + ret = "mtlo\t%.\n\tmthi\t%."; + } } else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0)) { if (TARGET_64BIT) - ret = "li\t%0,%1"; + { + if (HOST_BITS_PER_WIDE_INT < 64) + /* We can't use 'X' for negative numbers, because then we won't + get the right value for the upper 32 bits. */ + ret = ((INTVAL (op1) < 0) ? "dli\t%0,%1\t\t\t# %X1" + : "dli\t%0,%X1\t\t# %1"); + else + /* We must use 'X', because otherwise LONG_MIN will print as + a number that the assembler won't accept. */ + ret = "dli\t%0,%X1\t\t# %1"; + } else { operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1); @@ -1450,9 +1518,10 @@ mips_move_2words (operands, insn) || code1 == SYMBOL_REF || code1 == CONST) { - if (! TARGET_64BIT) - abort (); - return mips_move_1word (operands, insn, 0); + if (TARGET_STATS) + mips_count_memory_refs (op1, 2); + + ret = "dla\t%0,%a1"; } } @@ -1775,7 +1844,7 @@ gen_int_relational (test_code, result, c HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add; /* If modification of cmp1 caused overflow, we would get the wrong answer if we follow the usual path; - thus, x > 0xffffffffu would turn into x > 0u. */ + thus, x > 0xffffffffU would turn into x > 0U. */ if ((p_info->unsignedp ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1) : new > INTVAL (cmp1)) @@ -2437,7 +2506,10 @@ output_block_move (insn, operands, num_r { xoperands[1] = operands[1]; xoperands[0] = src_reg; - output_asm_insn ("la\t%0,%1", xoperands); + if (Pmode == DImode) + output_asm_insn ("dla\t%0,%1", xoperands); + else + output_asm_insn ("la\t%0,%1", xoperands); } } @@ -2451,7 +2523,10 @@ output_block_move (insn, operands, num_r { xoperands[1] = operands[0]; xoperands[0] = dest_reg; - output_asm_insn ("la\t%0,%1", xoperands); + if (Pmode == DImode) + output_asm_insn ("dla\t%0,%1", xoperands); + else + output_asm_insn ("la\t%0,%1", xoperands); } } } @@ -2481,19 +2556,22 @@ output_block_move (insn, operands, num_r /* ??? Fails because of a MIPS assembler bug? */ else if (TARGET_64BIT && bytes >= 8) { -#if BYTES_BIG_ENDIAN - load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2"; - load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#"; - load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2"; - load_store[num].last_store = "sdr\t%0,%2"; - load_store[num].final = "sdl\t%0,%1"; -#else - load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1"; - load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#"; - load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1"; - load_store[num].last_store = "sdr\t%0,%1"; - load_store[num].final = "sdl\t%0,%2"; -#endif + if (BYTES_BIG_ENDIAN) + { + load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2"; + load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#"; + load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2"; + load_store[num].last_store = "sdr\t%0,%2"; + load_store[num].final = "sdl\t%0,%1"; + } + else + { + load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1"; + load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#"; + load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1"; + load_store[num].last_store = "sdr\t%0,%1"; + load_store[num].final = "sdl\t%0,%2"; + } load_store[num].mode = DImode; offset += 8; bytes -= 8; @@ -2514,19 +2592,22 @@ output_block_move (insn, operands, num_r else if (bytes >= 4) { -#if BYTES_BIG_ENDIAN - load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2"; - load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#"; - load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2"; - load_store[num].last_store = "swr\t%0,%2"; - load_store[num].final = "swl\t%0,%1"; -#else - load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1"; - load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#"; - load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1"; - load_store[num].last_store = "swr\t%0,%1"; - load_store[num].final = "swl\t%0,%2"; -#endif + if (BYTES_BIG_ENDIAN) + { + load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2"; + load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#"; + load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2"; + load_store[num].last_store = "swr\t%0,%2"; + load_store[num].final = "swl\t%0,%1"; + } + else + { + load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1"; + load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#"; + load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1"; + load_store[num].last_store = "swr\t%0,%1"; + load_store[num].final = "swl\t%0,%2"; + } load_store[num].mode = SImode; offset += 4; bytes -= 4; @@ -2788,25 +2869,36 @@ function_arg (cum, mode, type, named) switch (mode) { case SFmode: - if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT) - regbase = GP_ARG_FIRST; - else + if (! ABI_64BIT || mips_isa < 3) { - regbase = FP_ARG_FIRST; - /* If the first arg was a float in a floating point register, - then set bias to align this float arg properly. */ - if (cum->arg_words == 1) - bias = 1; + if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT) + regbase = GP_ARG_FIRST; + else + { + regbase = FP_ARG_FIRST; + /* If the first arg was a float in a floating point register, + then set bias to align this float arg properly. */ + if (cum->arg_words == 1) + bias = 1; + } } - + else + regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST); break; case DFmode: if (! TARGET_64BIT) cum->arg_words += (cum->arg_words & 1); - regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT || cum->arg_number >= 2 - ? GP_ARG_FIRST - : FP_ARG_FIRST); + if (! ABI_64BIT || mips_isa < 3) + regbase = ((cum->gp_reg_found + || TARGET_SOFT_FLOAT + || TARGET_SINGLE_FLOAT + || cum->arg_number >= 2) + ? GP_ARG_FIRST + : FP_ARG_FIRST); + else + regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named + ? GP_ARG_FIRST : FP_ARG_FIRST); break; default: @@ -3077,7 +3169,7 @@ override_options () else if (isdigit (*mips_isa_string)) { mips_isa = atoi (mips_isa_string); - if (mips_isa < 1 || mips_isa > 3) + if (mips_isa < 1 || mips_isa > 4) { error ("-mips%d not supported", mips_isa); mips_isa = 1; @@ -3090,6 +3182,15 @@ override_options () mips_isa = 1; } +#ifdef MIPS_CPU_STRING_DEFAULT + /* ??? There is a minor inconsistency here. If the user specifies an ISA + greater than that supported by the default processor, then the user gets + an error. Normally, the compiler will just default to the base level cpu + for the indicated isa. */ + if (mips_cpu_string == (char *)0) + mips_cpu_string = MIPS_CPU_STRING_DEFAULT; +#endif + /* Identify the processor type */ if (mips_cpu_string == (char *)0 || !strcmp (mips_cpu_string, "default") @@ -3109,15 +3210,11 @@ override_options () mips_cpu_string = "4000"; mips_cpu = PROCESSOR_R4000; break; + case 4: + mips_cpu_string = "8000"; + mips_cpu = PROCESSOR_R8000; + break; } - -#ifdef MIPS_CPU_DEFAULT - if (mips_isa_string == (char *)0) - { - mips_cpu_string = MIPS_CPU_STRING_DEFAULT; - mips_cpu = MIPS_CPU_DEFAULT; - } -#endif } else @@ -3152,6 +3249,8 @@ override_options () mips_cpu = PROCESSOR_R4000; else if (!strcmp (p, "4600")) mips_cpu = PROCESSOR_R4600; + else if (!strcmp (p, "4650")) + mips_cpu = PROCESSOR_R4650; break; case '6': @@ -3159,6 +3258,11 @@ override_options () mips_cpu = PROCESSOR_R6000; break; + case '8': + if (!strcmp (p, "8000")) + mips_cpu = PROCESSOR_R8000; + break; + case 'o': if (!strcmp (p, "orion")) mips_cpu = PROCESSOR_R4600; @@ -3173,25 +3277,32 @@ override_options () } if ((mips_cpu == PROCESSOR_R3000 && mips_isa > 1) - || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)) + || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2) + || ((mips_cpu == PROCESSOR_R4000 + || mips_cpu == PROCESSOR_R4600 + || mips_cpu == PROCESSOR_R4650) + && mips_isa > 3)) error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa); /* make sure sizes of ints/longs/etc. are ok */ if (mips_isa < 3) { if (TARGET_INT64) - fatal ("Only MIPS-III CPUs can support 64 bit ints"); + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit ints"); else if (TARGET_LONG64) - fatal ("Only MIPS-III CPUs can support 64 bit longs"); + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit longs"); else if (TARGET_FLOAT64) - fatal ("Only MIPS-III CPUs can support 64 bit fp registers"); + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit fp registers"); else if (TARGET_64BIT) - fatal ("Only MIPS-III CPUs can support 64 bit gp registers"); + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit gp registers"); } + if (ABI_64BIT && mips_isa >= 3) + flag_pcc_struct_return = 0; + /* Tell halfpic.c that we have half-pic code if we do. */ if (TARGET_HALF_PIC) HALF_PIC_INIT (); @@ -3290,7 +3401,9 @@ override_options () mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS); mips_char_to_class['h'] = HI_REG; mips_char_to_class['l'] = LO_REG; + mips_char_to_class['a'] = HILO_REG; mips_char_to_class['x'] = MD_REGS; + mips_char_to_class['b'] = ALL_REGS; mips_char_to_class['y'] = GR_REGS; mips_char_to_class['z'] = ST_REGS; @@ -3336,17 +3449,13 @@ override_options () temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)) && (class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT - || (TARGET_DEBUG_H_MODE && class == MODE_INT))); + || (TARGET_DEBUG_H_MODE && class == MODE_INT)) + && (! TARGET_SINGLE_FLOAT || size <= 4)); else if (MD_REG_P (regno)) - { - if (TARGET_64BIT) - temp = (mode == DImode - || (regno == MD_REG_FIRST && mode == TImode)); - else - temp = (mode == SImode - || (regno == MD_REG_FIRST && mode == DImode)); - } + temp = (class == MODE_INT + && (size <= UNITS_PER_WORD + || (regno == MD_REG_FIRST && size == 2 * UNITS_PER_WORD))); else temp = FALSE; @@ -3429,6 +3538,10 @@ mips_debugger_offset (addr, offset) 'C' print part of opcode for a branch condition. 'N' print part of opcode for a branch condition, inverted. 'S' X is CODE_LABEL, print with prefix of "LS" (for embedded switch). + 'B' print 'z' for EQ, 'n' for NE + 'b' print 'n' for EQ, 'z' for NE + 'T' print 'f' for EQ, 't' for NE + 't' print 't' for EQ, 'f' for NE '(' Turn on .set noreorder ')' Turn on .set reorder '[' Turn on .set noat @@ -3590,7 +3703,7 @@ print_operand (file, op, letter) case LEU: fputs ("leu", file); break; default: - abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%C"); + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C"); } else if (letter == 'N') @@ -3608,7 +3721,7 @@ print_operand (file, op, letter) case LEU: fputs ("gtu", file); break; default: - abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%N"); + abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N"); } else if (letter == 'S') @@ -3623,13 +3736,9 @@ print_operand (file, op, letter) { register int regnum = REGNO (op); - if (letter == 'M') - regnum += MOST_SIGNIFICANT_WORD; - - else if (letter == 'L') - regnum += LEAST_SIGNIFICANT_WORD; - - else if (letter == 'D') + if ((letter == 'M' && ! WORDS_BIG_ENDIAN) + || (letter == 'L' && WORDS_BIG_ENDIAN) + || letter == 'D') regnum++; fprintf (file, "%s", reg_names[regnum]); @@ -3638,7 +3747,8 @@ print_operand (file, op, letter) else if (code == MEM) output_address (XEXP (op, 0)); - else if (code == CONST_DOUBLE) + else if (code == CONST_DOUBLE + && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT) { REAL_VALUE_TYPE d; char s[30]; @@ -3651,9 +3761,14 @@ print_operand (file, op, letter) else if ((letter == 'x') && (GET_CODE(op) == CONST_INT)) fprintf (file, "0x%04x", 0xffff & (INTVAL(op))); - else if ((letter == 'X') && (GET_CODE(op) == CONST_INT)) + else if ((letter == 'X') && (GET_CODE(op) == CONST_INT) + && HOST_BITS_PER_WIDE_INT == 32) fprintf (file, "0x%08x", INTVAL(op)); + else if ((letter == 'X') && (GET_CODE(op) == CONST_INT) + && HOST_BITS_PER_WIDE_INT == 64) + fprintf (file, "0x%016lx", INTVAL(op)); + else if ((letter == 'd') && (GET_CODE(op) == CONST_INT)) fprintf (file, "%d", (INTVAL(op))); @@ -3665,6 +3780,15 @@ print_operand (file, op, letter) else if (letter == 'd' || letter == 'x' || letter == 'X') fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", letter); + else if (letter == 'B') + fputs (code == EQ ? "z" : "n", file); + else if (letter == 'b') + fputs (code == EQ ? "n" : "z", file); + else if (letter == 'T') + fputs (code == EQ ? "f" : "t", file); + else if (letter == 't') + fputs (code == EQ ? "t" : "f", file); + else output_addr_const (file, op); } @@ -3691,7 +3815,7 @@ print_operand_address (file, addr) switch (GET_CODE (addr)) { default: - abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #1"); + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1"); break; case REG: @@ -3729,7 +3853,7 @@ print_operand_address (file, addr) abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs"); if (!CONSTANT_P (offset)) - abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2"); + abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2"); if (REGNO (reg) == ARG_POINTER_REGNUM) abort_with_insn (addr, "Arg pointer not eliminated."); @@ -3889,9 +4013,7 @@ mips_output_filename (stream, name) first_time = FALSE; SET_FILE_NUMBER (); current_function_file = name; - fprintf (stream, "\t.file\t%d ", num_source_filenames); - output_quoted_string (stream, name); - fprintf (stream, "\n"); + ASM_OUTPUT_FILENAME (stream, num_source_filenames, name); /* This tells mips-tfile that stabs will follow. */ if (!TARGET_GAS && write_symbols == DBX_DEBUG) fprintf (stream, "\t#@stabs\n"); @@ -3916,18 +4038,13 @@ mips_output_filename (stream, name) ignore_line_number = TRUE; warning ("MIPS ECOFF format does not allow changing filenames within functions with #line"); } - - fprintf (stream, "\t#.file\t%d ", num_source_filenames); } - else { SET_FILE_NUMBER (); current_function_file = name; - fprintf (stream, "\t.file\t%d ", num_source_filenames); + ASM_OUTPUT_FILENAME (stream, num_source_filenames, name); } - output_quoted_string (stream, name); - fprintf (stream, "\n"); } } @@ -3945,8 +4062,9 @@ mips_output_lineno (stream, line) if (write_symbols == DBX_DEBUG) { ++sym_lineno; - fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n", - sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno); + fprintf (stream, "%sLM%d:\n\t%s %d,0,%d,%sLM%d\n", + LOCAL_LABEL_PREFIX, sym_lineno, ASM_STABN_OP, N_SLINE, line, + LOCAL_LABEL_PREFIX, sym_lineno); } else @@ -4048,6 +4166,11 @@ mips_asm_file_start (stream) /* ??? but do not want this (or want pic0) if -non-shared? */ fprintf (stream, "\t%s\n", ABICALLS_ASM_OP); + /* Start a section, so that the first .popsection directive is guaranteed + to have a previously defined section to pop back to. */ + if (ABI_64BIT && mips_isa >= 3) + fprintf (stream, "\t.section\t.text\n"); + /* This code exists so that we can put all externs before all symbol references. This is necessary for the assembler's global pointer optimizations to work. */ @@ -4262,34 +4385,6 @@ epilogue_reg_mentioned_p (insn) return 0; } - - -/* When generating embedded PIC code we may need to get the address of - the current function. We will need it if we take the address of - any symbol in the .text section. */ - -void -mips_finalize_pic () -{ - rtx seq; - - if (! TARGET_EMBEDDED_PIC) - return; - if (embedded_pic_fnaddr_rtx == NULL) - return; - - start_sequence (); - - emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx, - XEXP (DECL_RTL (current_function_decl), 0))); - - seq = gen_sequence (); - end_sequence (); - emit_insn_after (seq, get_insns ()); - - embedded_pic_fnaddr_rtx = NULL; -} - /* Return the bytes needed to compute the frame pointer from the current stack pointer. @@ -4390,7 +4485,7 @@ compute_frame_size (size) } /* Calculate space needed for fp registers. */ - if (TARGET_FLOAT64) + if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) { fp_inc = 1; fp_bits = 1; @@ -4411,9 +4506,13 @@ compute_frame_size (size) } gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size); - total_size += gp_reg_rounded + fp_reg_size; + total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size); - if (total_size == extra_size) + /* The gp reg is caller saved in the 32 bit ABI, so there is no need + for leaf routines (total_size == extra_size) to save the gp reg. + The gp reg is callee saved in the 64 bit ABI, so all routines must + save the gp reg. */ + if (total_size == extra_size && ! (ABI_64BIT && mips_isa >= 3)) total_size = extra_size = 0; else if (TARGET_ABICALLS) { @@ -4425,6 +4524,11 @@ compute_frame_size (size) total_size += gp_reg_rounded; } + /* Add in space reserved on the stack by the callee for storing arguments + passed in registers. */ + if (ABI_64BIT && mips_isa >= 3) + total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size); + /* Save other computed information. */ current_frame_info.total_size = total_size; current_frame_info.var_size = var_size; @@ -4440,8 +4544,8 @@ compute_frame_size (size) if (mask) { - unsigned long offset = args_size + extra_size + var_size - + gp_reg_size - UNITS_PER_WORD; + unsigned long offset = (args_size + extra_size + var_size + + gp_reg_size - UNITS_PER_WORD); current_frame_info.gp_sp_offset = offset; current_frame_info.gp_save_offset = offset - total_size; } @@ -4585,13 +4689,13 @@ save_restore_insns (store_p, large_reg, if (store_p) emit_move_insn (mem_rtx, reg_rtx); - else if (!TARGET_ABICALLS + else if (!TARGET_ABICALLS || (ABI_64BIT && mips_isa >= 3) || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)) emit_move_insn (reg_rtx, mem_rtx); } else { - if (store_p || !TARGET_ABICALLS + if (store_p || !TARGET_ABICALLS || (ABI_64BIT && mips_isa >= 3) || regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)) fprintf (file, "\t%s\t%s,%ld(%s)\n", (TARGET_64BIT @@ -4608,14 +4712,14 @@ save_restore_insns (store_p, large_reg, } else { - base_reg_rtx = (rtx)0; /* Make sure these are initialzed */ + base_reg_rtx = (rtx)0; /* Make sure these are initialized */ base_offset = 0; } /* Save floating point registers if needed. */ if (fmask) { - int fp_inc = (TARGET_FLOAT64) ? 1 : 2; + int fp_inc = (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) ? 1 : 2; int fp_size = fp_inc * UNITS_PER_FPREG; /* Pick which pointer to use as a base register. */ @@ -4689,8 +4793,10 @@ save_restore_insns (store_p, large_reg, { if (file == (FILE *)0) { - rtx reg_rtx = gen_rtx (REG, DFmode, regno); - rtx mem_rtx = gen_rtx (MEM, DFmode, + enum machine_mode sz = + TARGET_SINGLE_FLOAT ? SFmode : DFmode; + rtx reg_rtx = gen_rtx (REG, sz, regno); + rtx mem_rtx = gen_rtx (MEM, sz, gen_rtx (PLUS, Pmode, base_reg_rtx, GEN_INT (fp_offset - base_offset))); @@ -4701,7 +4807,9 @@ save_restore_insns (store_p, large_reg, } else fprintf (file, "\t%s\t%s,%ld(%s)\n", - (store_p) ? "s.d" : "l.d", + (TARGET_SINGLE_FLOAT + ? ((store_p) ? "s.s" : "l.s") + : ((store_p) ? "s.d" : "l.d")), reg_names[regno], fp_offset - base_offset, reg_names[REGNO(base_reg_rtx)]); @@ -4721,19 +4829,27 @@ function_prologue (file, size) FILE *file; int size; { + char *fnname; long tsize = current_frame_info.total_size; ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl)); +#ifdef SDB_DEBUGGING_INFO if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG) ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); +#endif + + /* Get the function name the same way that toplev.c does before calling + assemble_start_function. This is needed so that the name used here + exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */ + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); inside_function = 1; fputs ("\t.ent\t", file); - assemble_name (file, current_function_name); + assemble_name (file, fnname); fputs ("\n", file); - assemble_name (file, current_function_name); + assemble_name (file, fnname); fputs (":\n", file); fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args= %d, extra= %d\n", @@ -4752,7 +4868,7 @@ function_prologue (file, size) current_frame_info.fmask, current_frame_info.fp_save_offset); - if (TARGET_ABICALLS) + if (TARGET_ABICALLS && ! (ABI_64BIT && mips_isa >= 3)) { char *sp_str = reg_names[STACK_POINTER_REGNUM]; @@ -4879,17 +4995,18 @@ mips_expand_prologue () /* If this function is a varargs function, store any registers that would normally hold arguments ($4 - $7) on the stack. */ - if ((TYPE_ARG_TYPES (fntype) != 0 - && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)) - || (arg_name != (char *)0 - && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0) - || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0)))) + if ((! ABI_64BIT || mips_isa < 3) + && ((TYPE_ARG_TYPES (fntype) != 0 + && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)) + || (arg_name != (char *)0 + && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0) + || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0))))) { int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD; rtx ptr = stack_pointer_rtx; /* If we are doing svr4-abi, sp has already been decremented by tsize. */ - if (TARGET_ABICALLS) + if (TARGET_ABICALLS && ! (ABI_64BIT && mips_isa >= 3)) offset += tsize; for (; regno <= GP_ARG_LAST; regno++) @@ -4907,7 +5024,7 @@ mips_expand_prologue () rtx tsize_rtx = GEN_INT (tsize); /* If we are doing svr4-abi, sp move is done by function_prologue. */ - if (!TARGET_ABICALLS) + if (!TARGET_ABICALLS || (ABI_64BIT && mips_isa >= 3)) { if (tsize > 32767) { @@ -4933,6 +5050,9 @@ mips_expand_prologue () else emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); } + + if (TARGET_ABICALLS && (ABI_64BIT && mips_isa >= 3)) + emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0))); } /* If we are profiling, make sure no instructions are scheduled before @@ -4953,6 +5073,7 @@ function_epilogue (file, size) FILE *file; int size; { + char *fnname; long tsize; char *sp_str = reg_names[STACK_POINTER_REGNUM]; char *t1_str = reg_names[MIPS_TEMP1_REGNUM]; @@ -5053,7 +5174,8 @@ function_epilogue (file, size) save_restore_insns (FALSE, tmp_rtx, tsize, file); load_only_r31 = (((current_frame_info.mask - & ~ (TARGET_ABICALLS ? PIC_OFFSET_TABLE_MASK : 0)) + & ~ (TARGET_ABICALLS && ! (ABI_64BIT && mips_isa >= 3) + ? PIC_OFFSET_TABLE_MASK : 0)) == RA_MASK) && current_frame_info.fmask == 0); @@ -5117,8 +5239,13 @@ function_epilogue (file, size) } } + /* Get the function name the same way that toplev.c does before calling + assemble_start_function. This is needed so that the name used here + exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */ + fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); + fputs ("\t.end\t", file); - assemble_name (file, current_function_name); + assemble_name (file, fnname); fputs ("\n", file); if (TARGET_STATS) @@ -5126,7 +5253,7 @@ function_epilogue (file, size) int num_gp_regs = current_frame_info.gp_reg_size / 4; int num_fp_regs = current_frame_info.fp_reg_size / 8; int num_regs = num_gp_regs + num_fp_regs; - char *name = current_function_name; + char *name = fnname; if (name[0] == '*') name++; @@ -5290,7 +5417,7 @@ mips_select_rtx_section (mode, x) { /* For embedded applications, always put constants in read-only data, in order to reduce RAM usage. */ - rdata_section (); + READONLY_DATA_SECTION (); } else { @@ -5299,9 +5426,9 @@ mips_select_rtx_section (mode, x) if (GET_MODE_SIZE (mode) <= mips_section_threshold && mips_section_threshold > 0) - sdata_section (); + SMALL_DATA_SECTION (); else - rdata_section (); + READONLY_DATA_SECTION (); } } @@ -5329,7 +5456,8 @@ mips_select_section (decl, reloc) /* For embedded applications, always put an object in read-only data if possible, in order to reduce RAM usage. */ - if (((TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) + if (((TREE_CODE (decl) == VAR_DECL + && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) && DECL_INITIAL (decl) && (DECL_INITIAL (decl) == error_mark_node || TREE_CONSTANT (DECL_INITIAL (decl)))) @@ -5338,9 +5466,9 @@ mips_select_section (decl, reloc) && (TREE_CODE (decl) != STRING_CST || !flag_writable_strings))) && ! (flag_pic && reloc)) - rdata_section (); + READONLY_DATA_SECTION (); else if (size > 0 && size <= mips_section_threshold) - sdata_section (); + SMALL_DATA_SECTION (); else data_section (); } @@ -5350,8 +5478,9 @@ mips_select_section (decl, reloc) possible, as this gives the best performance. */ if (size > 0 && size <= mips_section_threshold) - sdata_section (); - else if (((TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) + SMALL_DATA_SECTION (); + else if (((TREE_CODE (decl) == VAR_DECL + && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) && DECL_INITIAL (decl) && (DECL_INITIAL (decl) == error_mark_node || TREE_CONSTANT (DECL_INITIAL (decl)))) @@ -5360,8 +5489,141 @@ mips_select_section (decl, reloc) && (TREE_CODE (decl) != STRING_CST || !flag_writable_strings))) && ! (flag_pic && reloc)) - rdata_section (); + READONLY_DATA_SECTION (); else data_section (); } } + +#if ABI_64BIT +/* Support functions for the 64 bit ABI. */ + +/* Return the register to be used for word INDEX of a variable with type TYPE + being passed starting at general purpose reg REGNO. + + If the word being passed is a single field of a structure which has type + double, then pass it in a floating point reg instead of a general purpose + reg. Otherwise, we return the default value REGNO + INDEX. */ + +rtx +type_dependent_reg (regno, index, type) + int regno; + int index; + tree type; +{ + tree field; + tree offset; + + /* If type isn't a structure type, return the default value now. */ + if (! type || TREE_CODE (type) != RECORD_TYPE || mips_isa < 3) + return gen_rtx (REG, word_mode, regno + index); + + /* Iterate through the structure fields to find which one corresponds to + this index. */ + offset = size_int (index * BITS_PER_WORD); + for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field)) + { + if (! tree_int_cst_lt (DECL_FIELD_BITPOS (field), offset)) + break; + } + + if (field && tree_int_cst_equal (DECL_FIELD_BITPOS (field), offset) + && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE + && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD) + return gen_rtx (REG, DFmode, + regno + index + FP_ARG_FIRST - GP_ARG_FIRST); + else + return gen_rtx (REG, word_mode, regno + index); +} + +/* Return register to use for a function return value with VALTYPE for function + FUNC. */ + +rtx +mips_function_value (valtype, func) + tree valtype; + tree func; +{ + int reg = GP_RETURN; + enum machine_mode mode = TYPE_MODE (valtype); + enum mode_class mclass = GET_MODE_CLASS (mode); + + if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT) + reg = FP_RETURN; + else if (TREE_CODE (valtype) == RECORD_TYPE && mips_isa >= 3) + { + /* A struct with only one or two floating point fields is returned in + the floating point registers. */ + tree field; + int i; + + for (i = 0, field = TYPE_FIELDS (valtype); field; + field = TREE_CHAIN (field), i++) + { + if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2) + break; + } + + if (! field) + reg = FP_RETURN; + } + + return gen_rtx (REG, mode, reg); +} +#endif + +/* 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 + register, otherwise the register is the source. A return value of + NO_REGS means that no secondary register is required. */ + +enum reg_class +mips_secondary_reload_class (class, mode, x, in_p) + enum reg_class class; + enum machine_mode mode; + rtx x; + int in_p; +{ + int regno = -1; + + if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG) + regno = true_regnum (x); + + /* We always require a general register when copying anything to + HILO_REGNUM, except when copying an SImode value from HILO_REGNUM + to a general register, or when copying from register 0. */ + if (class == HILO_REG && regno != GP_REG_FIRST + 0) + { + if (! in_p + && GP_REG_P (regno) + && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode)) + return NO_REGS; + return GR_REGS; + } + if (regno == HILO_REGNUM) + { + if (in_p + && class == GR_REGS + && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode)) + return NO_REGS; + return GR_REGS; + } + + /* Copying from HI or LO to anywhere other than a general register + requires a general register. */ + if (class == HI_REG || class == LO_REG || class == MD_REGS) + { + if (GP_REG_P (regno)) + return NO_REGS; + return GR_REGS; + } + if (MD_REG_P (regno)) + { + if (class == GR_REGS) + return NO_REGS; + return GR_REGS; + } + + return NO_REGS; +}