--- gcc/config/mips/mips.c 2018/04/24 18:10:25 1.1.1.1 +++ gcc/config/mips/mips.c 2018/04/24 18:29:29 1.1.1.4 @@ -1,7 +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. - Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc. + Changes by Michael Meissner, meissner@osf.org. + 64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and + Brendan Eich, brendan@microunity.com. This file is part of GNU CC. @@ -17,7 +19,12 @@ 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 + be replaced with something better designed. */ #include "config.h" #include "rtl.h" @@ -75,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 @@ -111,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 @@ -188,13 +194,15 @@ 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; -/* Array to RTX class classification. At present, we care about - whether the operator is an add-type operator, or a divide/modulus, - and if divide/modulus, whether it is unsigned. This is for the - peephole code. */ -char mips_rtx_classify[NUM_RTX_CODE]; +/* 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; /* Array giving truth value on whether or not a given hard register can support a given mode. */ @@ -210,6 +218,11 @@ struct mips_frame_info zero_frame_info; for -mgpopt. */ static char *temp_filename; +/* Pseudo-reg holding the address of the current function when + generating embedded PIC code. Created by LEGITIMIZE_ADDRESS, used + by mips_finalize_pic if it was created. */ +rtx embedded_pic_fnaddr_rtx; + /* List of all MIPS punctuation characters used by print_operand. */ char mips_print_operand_punct[256]; @@ -233,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 @@ -241,15 +254,15 @@ char mips_reg_names[][8] = char mips_sw_reg_names[][8] = { - "$0", "at", "v0", "v1", "a0", "a1", "a2", "a3", - "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", - "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", - "t8", "t9", "k0", "k1", "gp", "sp", "$fp", "ra", + "$zero","$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3", + "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7", + "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", + "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra", "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$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 */ @@ -271,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. */ @@ -396,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 @@ -416,7 +429,7 @@ large_int (op, mode) if (((unsigned long)(value + 32768)) <= 32767) /* subu reg,$r0,value */ return FALSE; - if ((value & 0xffff0000) == value) /* lui reg,value>>16 */ + if ((value & 0x0000ffff) == 0) /* lui reg,value>>16 */ return FALSE; return TRUE; @@ -438,7 +451,7 @@ reg_or_0_operand (op, mode) return (INTVAL (op) == 0); case CONST_DOUBLE: - if (CONST_DOUBLE_HIGH (op) != 0 || CONST_DOUBLE_LOW (op) != 0) + if (op != CONST0_RTX (mode)) return FALSE; return TRUE; @@ -451,29 +464,6 @@ reg_or_0_operand (op, mode) return FALSE; } -/* Return truth value of whether OP is one of the special multiply/divide - registers (hi, lo). */ - -int -md_register_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (GET_MODE_CLASS (mode) == MODE_INT - && GET_CODE (op) == REG - && MD_REG_P (REGNO (op))); -} - -/* Return truth value of whether OP is the FP status register. */ - -int -fpsw_register_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - return (GET_CODE (op) == REG && ST_REG_P (REGNO (op))); -} - /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */ int @@ -481,44 +471,44 @@ mips_const_double_ok (op, mode) rtx op; enum machine_mode mode; { + REAL_VALUE_TYPE d; + if (GET_CODE (op) != CONST_DOUBLE) return FALSE; - if (mode == DImode) + if (mode == VOIDmode) return TRUE; if (mode != SFmode && mode != DFmode) return FALSE; - if (CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0) + if (op == CONST0_RTX (mode)) return TRUE; -#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - if (TARGET_MIPS_AS) /* gas doesn't like li.d/li.s yet */ - { - union { double d; int i[2]; } u; - double d; - - u.i[0] = CONST_DOUBLE_LOW (op); - u.i[1] = CONST_DOUBLE_HIGH (op); - d = u.d; + /* ??? li.s does not work right with SGI's Irix 6 assembler. */ + if (ABI_64BIT) + return FALSE; - if (d != d) - return FALSE; /* NAN */ + REAL_VALUE_FROM_CONST_DOUBLE (d, op); - if (d < 0.0) - d = - d; + if (REAL_VALUE_ISNAN (d)) + return FALSE; - /* Rather than trying to get the accuracy down to the last bit, - just use approximate ranges. */ + if (REAL_VALUE_NEGATIVE (d)) + d = REAL_VALUE_NEGATE (d); - if (mode == DFmode && d > 1.0e-300 && d < 1.0e300) + if (mode == DFmode) + { + if (REAL_VALUES_LESS (d, dfhigh) + && REAL_VALUES_LESS (dflow, d)) return TRUE; - - if (mode == SFmode && d > 1.0e-38 && d < 1.0e+38) + } + else + { + if (REAL_VALUES_LESS (d, sfhigh) + && REAL_VALUES_LESS (sflow, d)) return TRUE; } -#endif return FALSE; } @@ -538,7 +528,7 @@ simple_memory_operand (op, mode) return FALSE; /* dword operations really put out 2 instructions, so eliminate them. */ - if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4)) + if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD) return FALSE; /* Decode the address now. */ @@ -582,17 +572,17 @@ simple_memory_operand (op, mode) case CONST: /* If -G 0, we can never have a GP relative memory operation. Also, save some time if not optimizing. */ - if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT) + if (!TARGET_GP_OPT) return FALSE; { rtx offset = const0_rtx; - addr = eliminate_constant_term (addr, &offset); + addr = eliminate_constant_term (XEXP (addr, 0), &offset); if (GET_CODE (op) != SYMBOL_REF) return FALSE; /* let's be paranoid.... */ - if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff) + if (! SMALL_INT (offset)) return FALSE; } /* fall through */ @@ -615,7 +605,7 @@ equality_op (op, mode) if (mode != GET_MODE (op)) return FALSE; - return (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0; + return (GET_CODE (op) == EQ || GET_CODE (op) == NE); } /* Return true if the code is a relational operations (EQ, LE, etc.) */ @@ -628,52 +618,9 @@ cmp_op (op, mode) if (mode != GET_MODE (op)) return FALSE; - return (classify_op (op, mode) & CLASS_CMP_OP) != 0; + return (GET_RTX_CLASS (GET_CODE (op)) == '<'); } - -/* Genrecog does not take the type of match_operator into consideration, - and would complain about two patterns being the same if the same - function is used, so make it believe they are different. */ - -int -cmp2_op (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != GET_MODE (op)) - return FALSE; - - return (classify_op (op, mode) & CLASS_CMP_OP) != 0; -} - -/* Return true if the code is an unsigned relational operations (LEU, etc.) */ - -int -uns_cmp_op (op,mode) - rtx op; - enum machine_mode mode; -{ - if (mode != GET_MODE (op)) - return FALSE; - - return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP; -} - -/* Return true if the code is a relational operation FP can use. */ - -int -fcmp_op (op, mode) - rtx op; - enum machine_mode mode; -{ - if (mode != GET_MODE (op)) - return FALSE; - - return (classify_op (op, mode) & CLASS_FCMP_OP) != 0; -} - - /* Return true if the operand is either the PC or a label_ref. */ int @@ -710,11 +657,8 @@ call_insn_operand (op, mode) return 0; } -/* Return an operand string if the given instruction's delay slot or - wrap it in a .set noreorder section. This is for filling delay - slots on load type instructions under GAS, which does no reordering - on its own. For the MIPS assembler, all we do is update the filled - delay slot statistics. +/* Returns an operand string for the given instruction's delay slot, + after updating filled delay slot statistics. We assume that operands[0] is the target register that is set. @@ -722,6 +666,9 @@ call_insn_operand (op, mode) to FINAL_PRESCAN_INSN, and we just set the global variables that it needs. */ +/* ??? This function no longer does anything useful, because final_prescan_insn + now will never emit a nop. */ + char * mips_fill_delay_slot (ret, type, operands, cur_insn) char *ret; /* normal string to return */ @@ -775,10 +722,12 @@ mips_fill_delay_slot (ret, type, operand mode = GET_MODE (set_reg); dslots_number_nops = num_nops; - mips_load_reg = set_reg; - mips_load_reg2 = (mode == DImode || mode == DFmode) - ? gen_rtx (REG, SImode, REGNO (set_reg) + 1) - : (rtx)0; + mips_load_reg = set_reg; + if (GET_MODE_SIZE (mode) + > (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; if (type == DELAY_HILO) { @@ -791,9 +740,6 @@ mips_fill_delay_slot (ret, type, operand mips_load_reg4 = 0; } - if (TARGET_GAS && set_noreorder++ == 0) - fputs ("\t.set\tnoreorder\n", asm_out_file); - return ret; } @@ -914,6 +860,42 @@ mips_count_memory_refs (op, num) } +/* Return RTL for the offset from the current function to the + argument. */ + +rtx +embedded_pic_offset (x) + rtx x; +{ + if (embedded_pic_fnaddr_rtx == NULL) + { + 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))); +} + /* Return the appropriate instructions to move one operand to another. */ char * @@ -967,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 @@ -998,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"; } } @@ -1025,11 +1011,22 @@ mips_move_1word (operands, insn, unsigne target, so zero/sign extend can use this code as well. */ switch (GET_MODE (op1)) { - default: break; - case SFmode: ret = "lw\t%0,%1"; break; - case SImode: ret = "lw\t%0,%1"; break; - case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break; - case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break; + default: + break; + case SFmode: + ret = "lw\t%0,%1"; + break; + case SImode: + ret = ((unsignedp && TARGET_64BIT) + ? "lwu\t%0,%1" + : "lw\t%0,%1"); + break; + case HImode: + ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; + break; + case QImode: + ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; + break; } } @@ -1047,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)) @@ -1059,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)) @@ -1067,7 +1080,7 @@ mips_move_1word (operands, insn, unsigne else if (code1 == CONST_DOUBLE && mode == SFmode) { - if (CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0) + if (op1 == CONST0_RTX (SFmode)) { if (GP_REG_P (regno0)) ret = "move\t%0,%."; @@ -1113,15 +1126,22 @@ mips_move_1word (operands, insn, unsigne if (INTVAL (offset) == 0) { delay = DELAY_LOAD; - ret = "lw\t%0,%2"; + ret = (unsignedp && TARGET_64BIT + ? "lwu\t%0,%2" + : "lw\t%0,%2"); } else { dslots_load_total++; operands[3] = offset; - ret = (SMALL_INT (offset)) - ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3" - : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"; + if (unsignedp && TARGET_64BIT) + ret = (SMALL_INT (offset)) + ? "lwu\t%0,%2%#\n\tadd\t%0,%0,%3" + : "lwu\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"; + else + ret = (SMALL_INT (offset)) + ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3" + : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"; } } } @@ -1188,7 +1208,7 @@ mips_move_1word (operands, insn, unsigne } } - else if (code1 == CONST_DOUBLE && CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0) + else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode)) { switch (mode) { @@ -1275,32 +1295,69 @@ mips_move_2words (operands, insn) else { delay = DELAY_LOAD; - ret = (TARGET_FLOAT64) - ? "dmtc1\t%1,%0" - : "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0"; + if (TARGET_FLOAT64) + { + if (!TARGET_64BIT) + abort_with_insn (insn, "Bad move"); +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno1)) + ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tdmtc1\t%1,%0"; + else +#endif + ret = "dmtc1\t%1,%0"; + } + else + ret = "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0"; } } else if (FP_REG_P (regno1)) { delay = DELAY_LOAD; - ret = (TARGET_FLOAT64) - ? "dmfc1\t%0,%1" - : "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1"; + if (TARGET_FLOAT64) + { + if (!TARGET_64BIT) + abort_with_insn (insn, "Bad move"); +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno0)) + ret = "dmfc1\t%0,%1\n\tmfc1\t%D0,%1\n\tdsrl\t%0,32"; + else +#endif + ret = "dmfc1\t%0,%1"; + } + else + ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1"; } else if (MD_REG_P (regno0) && GP_REG_P (regno1)) { delay = DELAY_HILO; - ret = "mthi\t%M1\n\tmtlo\t%L1"; + if (TARGET_64BIT) + { + 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"; } else if (GP_REG_P (regno0) && MD_REG_P (regno1)) { delay = DELAY_HILO; - ret = "mfhi\t%M0\n\tmflo\t%L0"; + if (TARGET_64BIT) + { + if (regno1 != HILO_REGNUM) + ret = "mf%1\t%0"; + } + else + ret = "mfhi\t%M0\n\tmflo\t%L0"; } + else if (TARGET_64BIT) + ret = "move\t%0,%1"; + else if (regno0 != (regno1+1)) ret = "move\t%0,%1\n\tmove\t%D0,%D1"; @@ -1310,31 +1367,55 @@ mips_move_2words (operands, insn) else if (code1 == CONST_DOUBLE) { - if (CONST_DOUBLE_HIGH (op1) != 0 || CONST_DOUBLE_LOW (op1) != 0) + /* Move zero from $0 unless !TARGET_64BIT and recipient + is 64-bit fp reg, in which case generate a constant. */ + if (op1 != CONST0_RTX (GET_MODE (op1)) + || (TARGET_FLOAT64 && !TARGET_64BIT && FP_REG_P (regno0))) { if (GET_MODE (op1) == DFmode) { delay = DELAY_LOAD; - ret = "li.d\t%0,%1"; +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno0)) + { + if (TARGET_FLOAT64 && !TARGET_64BIT) + { + 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"; + } + else +#endif + ret = "li.d\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"; } } else { if (GP_REG_P (regno0)) - ret = "move\t%0,%.\n\tmove\t%D0,%."; + ret = (TARGET_64BIT +#ifdef TARGET_FP_CALL_32 + && ! FP_CALL_GP_REG_P (regno0) +#endif + ) + ? "move\t%0,%." + : "move\t%0,%.\n\tmove\t%D0,%."; else if (FP_REG_P (regno0)) { delay = DELAY_LOAD; - ret = (TARGET_FLOAT64) + ret = (TARGET_64BIT) ? "dmtc1\t%.,%0" : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"; } @@ -1344,21 +1425,48 @@ mips_move_2words (operands, insn) else if (code1 == CONST_INT && INTVAL (op1) == 0) { if (GP_REG_P (regno0)) - ret = "move\t%0,%.\n\tmove\t%D0,%."; + ret = (TARGET_64BIT) + ? "move\t%0,%." + : "move\t%0,%.\n\tmove\t%D0,%."; else if (FP_REG_P (regno0)) { delay = DELAY_LOAD; - ret = (TARGET_FLOAT64) + ret = (TARGET_64BIT) ? "dmtc1\t%.,%0" - : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"; + : (TARGET_FLOAT64 + ? "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)) { - operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1); - ret = "li\t%M0,%2\n\tli\t%L0,%1"; + if (TARGET_64BIT) + { + 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); + ret = "li\t%M0,%2\n\tli\t%L0,%1"; + } } else if (code1 == MEM) @@ -1371,6 +1479,21 @@ mips_move_2words (operands, insn) if (FP_REG_P (regno0)) ret = "l.d\t%0,%1"; + else if (TARGET_64BIT) + { +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno0)) + { + if (offsettable_address_p (FALSE, SImode, op1)) + ret = "lwu\t%0,%1\n\tlwu\t%D0,4+%1"; + else + ret = "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32"; + } + else +#endif + ret = "ld\t%0,%1"; + } + else if (offsettable_address_p (1, DFmode, XEXP (op1, 0))) { operands[2] = adj_offsettable_operand (op1, 4); @@ -1390,6 +1513,16 @@ mips_move_2words (operands, insn) ret = volatile_buffer; } } + + else if (code1 == LABEL_REF + || code1 == SYMBOL_REF + || code1 == CONST) + { + if (TARGET_STATS) + mips_count_memory_refs (op1, 2); + + ret = "dla\t%0,%a1"; + } } else if (code0 == MEM) @@ -1401,6 +1534,16 @@ mips_move_2words (operands, insn) if (FP_REG_P (regno1)) ret = "s.d\t%1,%0"; + else if (TARGET_64BIT) + { +#ifdef TARGET_FP_CALL_32 + if (FP_CALL_GP_REG_P (regno1)) + ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tsd\t%1,%0"; + else +#endif + ret = "sd\t%1,%0"; + } + else if (offsettable_address_p (1, DFmode, XEXP (op0, 0))) { operands[2] = adj_offsettable_operand (op0, 4); @@ -1408,12 +1551,13 @@ mips_move_2words (operands, insn) } } - else if (code1 == CONST_DOUBLE - && CONST_DOUBLE_HIGH (op1) == 0 - && CONST_DOUBLE_LOW (op1) == 0 - && offsettable_address_p (1, DFmode, XEXP (op0, 0))) + else if (((code1 == CONST_INT && INTVAL (op1) == 0) + || (code1 == CONST_DOUBLE + && op1 == CONST0_RTX (GET_MODE (op1)))) + && (TARGET_64BIT + || offsettable_address_p (1, DFmode, XEXP (op0, 0)))) { - if (TARGET_FLOAT64) + if (TARGET_64BIT) ret = "sd\t%.,%0"; else { @@ -1471,14 +1615,14 @@ mips_address_cost (addr) case CONST: { rtx offset = const0_rtx; - addr = eliminate_constant_term (addr, &offset); + addr = eliminate_constant_term (XEXP (addr, 0), &offset); if (GET_CODE (addr) == LABEL_REF) return 2; if (GET_CODE (addr) != SYMBOL_REF) return 4; - if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767) + if (! SMALL_INT (offset)) return 2; } /* fall through */ @@ -1506,10 +1650,7 @@ mips_address_cost (addr) break; case CONST_INT: - { - int value = INTVAL (plus1); - return (value < -32768 || value > 32767) ? 2 : 1; - } + return (SMALL_INT (plus1) ? 1 : 2); case CONST: case SYMBOL_REF: @@ -1524,6 +1665,22 @@ mips_address_cost (addr) return 4; } +/* Return true if X is an address which needs a temporary register when + reloaded while generating PIC code. */ + +int +pic_address_needs_scratch (x) + rtx x; +{ + /* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */ + if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && ! SMALL_INT (XEXP (XEXP (x, 0), 1))) + return 1; + + return 0; +} /* Make normal rtx_code into something we can index from an array */ @@ -1554,7 +1711,24 @@ map_test_to_internal_test (test_code) /* Generate the code to compare two integer values. The return value is: (reg:SI xx) The pseudo register the comparison is in - (rtx)0 No register, generate a simple branch. */ + (rtx)0 No register, generate a simple branch. + + ??? 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 + 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; + for example: + + long long + foo (long long i) + { + return i < 5; + } + + */ rtx gen_int_relational (test_code, result, cmp0, cmp1, p_invert) @@ -1591,6 +1765,7 @@ gen_int_relational (test_code, result, c }; enum internal_test test; + enum machine_mode mode; struct cmp_info *p_info; int branch_p; int eqne_p; @@ -1605,6 +1780,10 @@ gen_int_relational (test_code, result, c p_info = &info[ (int)test ]; eqne_p = (p_info->test_code == XOR); + mode = GET_MODE (cmp0); + if (mode == VOIDmode) + mode = GET_MODE (cmp1); + /* Eliminate simple branches */ branch_p = (result == (rtx)0); if (branch_p) @@ -1621,18 +1800,28 @@ gen_int_relational (test_code, result, c } /* allocate a pseudo to calculate the value in. */ - result = gen_reg_rtx (SImode); + result = gen_reg_rtx (mode); } /* Make sure we can handle any constants given to us. */ if (GET_CODE (cmp0) == CONST_INT) - cmp0 = force_reg (SImode, cmp0); + cmp0 = force_reg (mode, cmp0); if (GET_CODE (cmp1) == CONST_INT) { HOST_WIDE_INT value = INTVAL (cmp1); - if (value < p_info->const_low || value > p_info->const_high) - cmp1 = force_reg (SImode, cmp1); + if (value < p_info->const_low + || value > p_info->const_high + /* ??? Why? And why wasn't the similar code below modified too? */ + || (TARGET_64BIT + && HOST_BITS_PER_WIDE_INT < 64 + && p_info->const_add != 0 + && ((p_info->unsignedp + ? ((unsigned HOST_WIDE_INT) (value + p_info->const_add) + > INTVAL (cmp1)) + : (value + p_info->const_add) > INTVAL (cmp1)) + != (p_info->const_add > 0)))) + cmp1 = force_reg (mode, cmp1); } /* See if we need to invert the result. */ @@ -1655,15 +1844,18 @@ 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)) != (p_info->const_add > 0)) - /* 1 is the right value in the LE and LEU case. - In the GT and GTU case, *p_invert is already set, - so this is effectively 0. */ - return force_reg (SImode, const1_rtx); + { + /* This test is always true, but if INVERT is true then + the result of the test needs to be inverted so 0 should + be returned instead. */ + emit_move_insn (result, invert ? const0_rtx : const_true_rtx); + return result; + } else cmp1 = GEN_INT (new); } @@ -1679,25 +1871,25 @@ gen_int_relational (test_code, result, c reg = cmp0; else { - reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result; - emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1)); + reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result; + convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0); } if (test == ITEST_NE) { - emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx)); + convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0); invert = FALSE; } else if (test == ITEST_EQ) { - reg2 = (invert) ? gen_reg_rtx (SImode) : result; - emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx)); + reg2 = (invert) ? gen_reg_rtx (mode) : result; + convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0); reg = reg2; } if (invert) - emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx)); + convert_move (result, gen_rtx (XOR, mode, reg, const1_rtx), 0); return result; } @@ -1705,7 +1897,7 @@ gen_int_relational (test_code, result, c /* 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,sf,df}. */ + The comparison operands are saved away by cmp{si,di,sf,df}. */ void gen_conditional_branch (operands, test_code) @@ -1725,6 +1917,18 @@ gen_conditional_branch (operands, test_c SImode, /* ltu */ SImode, /* leu */ }, + { /* CMP_DI */ + DImode, /* eq */ + DImode, /* ne */ + DImode, /* gt */ + DImode, /* ge */ + DImode, /* lt */ + DImode, /* le */ + DImode, /* gtu */ + DImode, /* geu */ + DImode, /* ltu */ + DImode, /* leu */ + }, { /* CMP_SF */ CC_FPmode, /* eq */ CC_REV_FPmode, /* ne */ @@ -1763,7 +1967,7 @@ gen_conditional_branch (operands, test_c if (test == ITEST_MAX) { - mode = SImode; + mode = word_mode; goto fail; } @@ -1772,12 +1976,13 @@ gen_conditional_branch (operands, test_c if (mode == VOIDmode) goto fail; - switch (branch_type) + switch (type) { default: goto fail; case CMP_SI: + case CMP_DI: reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert); if (reg != (rtx)0) { @@ -1788,7 +1993,7 @@ gen_conditional_branch (operands, test_c /* Make sure not non-zero constant if ==/!= */ else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) - cmp1 = force_reg (SImode, cmp1); + cmp1 = force_reg (mode, cmp1); break; @@ -1825,12 +2030,14 @@ fail: } -#define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT) - +#if 0 /* Internal code to generate the load and store of one word/short/byte. The load is emitted directly, and the store insn is returned. */ -#if 0 +#define UNITS_PER_MIPS_DWORD 8 +#define UNITS_PER_MIPS_WORD 4 +#define UNITS_PER_MIPS_HWORD 2 + static rtx block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src) rtx src_reg; /* register holding source memory address */ @@ -1856,30 +2063,38 @@ block_move_load_store (dest_reg, src_reg if (bytes <= 0 || align <= 0) abort (); - if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD) + if (bytes >= UNITS_PER_MIPS_DWORD && align >= UNIS_PER_MIPS_DWORD) + { + mode = DImode; + size = UNITS_PER_MIPS_DWORD; + load_func = gen_movdi; + store_func = gen_movdi; + } + else if (bytes >= UNITS_PER_MIPS_WORD && align >= UNITS_PER_MIPS_WORD) { mode = SImode; - size = UNITS_PER_WORD; + size = UNITS_PER_MIPS_WORD; load_func = gen_movsi; store_func = gen_movsi; } #if 0 /* Don't generate unaligned moves here, rather defer those to the - general movestrsi_internal pattern. */ - else if (bytes >= UNITS_PER_WORD) + general movestrsi_internal pattern. + If this gets commented back in, then should add the dword equivalent. */ + else if (bytes >= UNITS_PER_MIPS_WORD) { mode = SImode; - size = UNITS_PER_WORD; + size = UNITS_PER_MIPS_WORD; load_func = gen_movsi_ulw; store_func = gen_movsi_usw; } #endif - else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT) + else if (bytes >= UNITS_PER_MIPS_SHORT && align >= UNITS_PER_MIPS_SHORT) { mode = HImode; - size = UNITS_PER_SHORT; + size = UNITS_PER_MIPS_SHORT; load_func = gen_movhi; store_func = gen_movhi; } @@ -2001,6 +2216,8 @@ block_move_sequence (dest_reg, src_reg, #define MAX_MOVE_REGS 4 #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD) +/* ??? Should add code to use DWORD load/stores. */ + static void block_move_loop (dest_reg, src_reg, bytes, align, orig_src) rtx dest_reg; /* register holding destination address */ @@ -2029,19 +2246,41 @@ block_move_loop (dest_reg, src_reg, byte if (bytes > 0x7fff) { - emit_insn (gen_movsi (final_src, bytes_rtx)); - emit_insn (gen_addsi3 (final_src, final_src, src_reg)); + if (TARGET_LONG64) + { + emit_insn (gen_movdi (final_src, bytes_rtx)); + emit_insn (gen_adddi3 (final_src, final_src, src_reg)); + } + else + { + emit_insn (gen_movsi (final_src, bytes_rtx)); + emit_insn (gen_addsi3 (final_src, final_src, src_reg)); + } } else - emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx)); + { + if (TARGET_LONG64) + emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx)); + else + emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx)); + } emit_label (label); bytes_rtx = GEN_INT (MAX_MOVE_BYTES); emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx)); - emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx)); - emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx)); - emit_insn (gen_cmpsi (src_reg, final_src)); + if (TARGET_LONG64) + { + emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx)); + emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx)); + emit_insn (gen_cmpdi (src_reg, final_src)); + } + else + { + emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx)); + emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx)); + emit_insn (gen_cmpsi (src_reg, final_src)); + } emit_jump_insn (gen_bne (label)); if (leftover) @@ -2059,18 +2298,24 @@ block_move_call (dest_reg, src_reg, byte rtx src_reg; rtx bytes_rtx; { + /* We want to pass the size as Pmode, which will normally be SImode + but will be DImode if we are using 64 bit longs and pointers. */ + if (GET_MODE (bytes_rtx) != VOIDmode + && GET_MODE (bytes_rtx) != Pmode) + bytes_rtx = convert_to_mode (Pmode, bytes_rtx, TRUE); + #ifdef TARGET_MEM_FUNCTIONS emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0, VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode, - bytes_rtx, SImode); + bytes_rtx, Pmode); #else emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0, VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode, - bytes_rtx, SImode); + bytes_rtx, Pmode); #endif } @@ -2134,9 +2379,18 @@ expand_block_move (operands) bytes -= leftover; - emit_insn (gen_iorsi3 (temp, src_reg, dest_reg)); - emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); - emit_insn (gen_cmpsi (temp, const0_rtx)); + if (TARGET_LONG64) + { + emit_insn (gen_iordi3 (temp, src_reg, dest_reg)); + emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); + emit_insn (gen_cmpdi (temp, const0_rtx)); + } + else + { + emit_insn (gen_iorsi3 (temp, src_reg, dest_reg)); + emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); + emit_insn (gen_cmpsi (temp, const0_rtx)); + } emit_jump_insn (gen_beq (aligned_label)); /* Unaligned loop. */ @@ -2252,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); } } @@ -2266,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); } } } @@ -2277,14 +2537,48 @@ output_block_move (insn, operands, num_r else if (num_regs < 1) abort_with_insn (insn, "Cannot do block move, not enough scratch registers"); - if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0) - output_asm_insn (".set\tnoreorder", operands); - while (bytes > 0) { load_store[num].offset = offset; - if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD) + if (TARGET_64BIT && bytes >= 8 && align >= 8) + { + load_store[num].load = "ld\t%0,%1"; + load_store[num].load_nop = "ld\t%0,%1%#"; + load_store[num].store = "sd\t%0,%1"; + load_store[num].last_store = "sd\t%0,%1"; + load_store[num].final = (char *)0; + load_store[num].mode = DImode; + offset += 8; + bytes -= 8; + } + + /* ??? 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"; + } + load_store[num].mode = DImode; + offset += 8; + bytes -= 8; + use_lwl_lwr = TRUE; + } + + else if (bytes >= 4 && align >= 4) { load_store[num].load = "lw\t%0,%1"; load_store[num].load_nop = "lw\t%0,%1%#"; @@ -2292,42 +2586,44 @@ output_block_move (insn, operands, num_r load_store[num].last_store = "sw\t%0,%1"; load_store[num].final = (char *)0; load_store[num].mode = SImode; - offset += UNITS_PER_WORD; - bytes -= UNITS_PER_WORD; + offset += 4; + bytes -= 4; } - else if (bytes >= UNITS_PER_WORD) + 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 += UNITS_PER_WORD; - bytes -= UNITS_PER_WORD; + offset += 4; + bytes -= 4; use_lwl_lwr = TRUE; } - else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT) + else if (bytes >= 2 && align >= 2) { load_store[num].load = "lh\t%0,%1"; load_store[num].load_nop = "lh\t%0,%1%#"; load_store[num].store = "sh\t%0,%1"; load_store[num].last_store = "sh\t%0,%1"; load_store[num].final = (char *)0; - load_store[num].offset = offset; load_store[num].mode = HImode; - offset += UNITS_PER_SHORT; - bytes -= UNITS_PER_SHORT; + offset += 2; + bytes -= 2; } else @@ -2385,8 +2681,14 @@ output_block_move (insn, operands, num_r plus_constant (src_reg, offset)); if (use_lwl_lwr) - xoperands[2] = gen_rtx (MEM, load_store[i].mode, - plus_constant (src_reg, UNITS_PER_WORD-1+offset)); + { + int extra_offset; + extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1; + xoperands[2] = gen_rtx (MEM, load_store[i].mode, + plus_constant (src_reg, + extra_offset + + offset)); + } output_asm_insn (load_store[i].load, xoperands); } @@ -2403,8 +2705,14 @@ output_block_move (insn, operands, num_r if (use_lwl_lwr) - xoperands[2] = gen_rtx (MEM, load_store[i].mode, - plus_constant (dest_reg, UNITS_PER_WORD-1+offset)); + { + int extra_offset; + extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1; + xoperands[2] = gen_rtx (MEM, load_store[i].mode, + plus_constant (dest_reg, + extra_offset + + offset)); + } if (move_type == BLOCK_MOVE_NORMAL) output_asm_insn (load_store[i].store, xoperands); @@ -2423,13 +2731,10 @@ output_block_move (insn, operands, num_r } num = 0; /* reset load_store */ - use_lwl_lwr = FALSE; /* reset whether or not we used lwl/lwr */ + use_lwl_lwr = FALSE; } } - if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0) - output_asm_insn (".set\treorder", operands); - return ""; } @@ -2498,16 +2803,22 @@ function_arg_advance (cum, mode, type, n cum->arg_number++; switch (mode) { - default: - error ("Illegal mode given to function_arg_advance"); + case VOIDmode: break; - case VOIDmode: + default: + if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + abort (); + cum->gp_reg_found = 1; + cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); break; case BLKmode: cum->gp_reg_found = 1; - cum->arg_words += (int_size_in_bytes (type) + 3) / 4; + cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); break; case SFmode: @@ -2515,12 +2826,12 @@ function_arg_advance (cum, mode, type, n break; case DFmode: - cum->arg_words += 2; + cum->arg_words += (TARGET_64BIT ? 1 : 2); break; case DImode: cum->gp_reg_found = 1; - cum->arg_words += 2; + cum->arg_words += (TARGET_64BIT ? 1 : 2); break; case QImode: @@ -2532,8 +2843,8 @@ function_arg_advance (cum, mode, type, n } } -/* Return a RTL expression containing the register for the given mode, - or 0 if the argument is too be passed on the stack. */ +/* Return an RTL expression containing the register for the given mode, + or 0 if the argument is to be passed on the stack. */ struct rtx_def * function_arg (cum, mode, type, named) @@ -2557,30 +2868,48 @@ function_arg (cum, mode, type, named) switch (mode) { - default: - error ("Illegal mode given to function_arg"); - break; - case SFmode: - if (cum->gp_reg_found || cum->arg_number >= 2) - regbase = GP_ARG_FIRST; - else { - regbase = (TARGET_SOFT_FLOAT) ? GP_ARG_FIRST : FP_ARG_FIRST; - if (cum->arg_words == 1) /* first arg was float */ - bias = 1; /* use correct reg */ - } - + if (! ABI_64BIT || mips_isa < 3) + { + 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: - cum->arg_words += (cum->arg_words & 1); - regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT) - ? GP_ARG_FIRST - : FP_ARG_FIRST; + if (! TARGET_64BIT) + cum->arg_words += (cum->arg_words & 1); + 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: + if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + abort (); + + /* Drops through. */ case BLKmode: - if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD) + if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD + && ! TARGET_64BIT) cum->arg_words += (cum->arg_words & 1); regbase = GP_ARG_FIRST; @@ -2594,7 +2923,8 @@ function_arg (cum, mode, type, named) break; case DImode: - cum->arg_words += (cum->arg_words & 1); + if (! TARGET_64BIT) + cum->arg_words += (cum->arg_words & 1); regbase = GP_ARG_FIRST; } @@ -2622,14 +2952,29 @@ function_arg (cum, mode, type, named) This also makes varargs work. If we have such a structure, we save the adjustment RTL, and the call define expands will emit them. For the VOIDmode argument (argument after the - last real argument, pass back a parallel vector holding each + last real argument), pass back a parallel vector holding each of the adjustments. */ - if (struct_p && (mode == QImode || mode == HImode)) - { - rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode)); - rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias); - cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount); + /* ??? function_arg can be called more than once for each argument. + As a result, we compute more adjustments than we need here. + See the CUMULATIVE_ARGS definition in mips.h. */ + + /* ??? This scheme requires everything smaller than the word size to + shifted to the left, but when TARGET_64BIT and ! TARGET_INT64, + that would mean every int needs to be shifted left, which is very + inefficient. Let's not carry this compatibility to the 64 bit + calling convention for now. */ + + if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD + && ! TARGET_64BIT) + { + rtx amount = GEN_INT (BITS_PER_WORD + - int_size_in_bytes (type) * BITS_PER_UNIT); + rtx reg = gen_rtx (REG, word_mode, regbase + cum->arg_words + bias); + if (TARGET_64BIT) + cum->adjust[ cum->num_adjusts++ ] = gen_ashldi3 (reg, reg, amount); + else + cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount); } } @@ -2647,9 +2992,17 @@ function_arg_partial_nregs (cum, mode, t tree type; /* type of the argument or 0 if lib support */ int named; /* != 0 for normal args, == 0 for ... args */ { - if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS) - { - int words = (int_size_in_bytes (type) + 3) / 4; + if ((mode == BLKmode + || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT + || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT) + && cum->arg_words < MAX_ARGS_IN_REGISTERS) + { + int words; + if (mode == BLKmode) + words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1) + / UNITS_PER_WORD); + else + words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD; if (words + cum->arg_words <= MAX_ARGS_IN_REGISTERS) return 0; /* structure fits in registers */ @@ -2661,7 +3014,8 @@ function_arg_partial_nregs (cum, mode, t return MAX_ARGS_IN_REGISTERS - cum->arg_words; } - else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1) + else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1 + && ! TARGET_64BIT) { if (TARGET_DEBUG_E_MODE) fprintf (stderr, "function_arg_partial_nregs = 1\n"); @@ -2797,13 +3151,70 @@ override_options () mips_section_threshold = (g_switch_set) ? g_switch_value : MIPS_DEFAULT_GVALUE; + if (mips_section_threshold <= 0) + target_flags &= ~MASK_GPOPT; + else if (optimize) + target_flags |= MASK_GPOPT; + + /* Get the architectural level. */ + if (mips_isa_string == (char *)0) + { +#ifdef MIPS_ISA_DEFAULT + mips_isa = MIPS_ISA_DEFAULT; +#else + mips_isa = 1; +#endif + } + + else if (isdigit (*mips_isa_string)) + { + mips_isa = atoi (mips_isa_string); + if (mips_isa < 1 || mips_isa > 4) + { + error ("-mips%d not supported", mips_isa); + mips_isa = 1; + } + } + + else + { + error ("bad value (%s) for -mips switch", mips_isa_string); + 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") || !strcmp (mips_cpu_string, "DEFAULT")) { - mips_cpu_string = "default"; - mips_cpu = PROCESSOR_DEFAULT; + switch (mips_isa) + { + default: + mips_cpu_string = "3000"; + mips_cpu = PROCESSOR_R3000; + break; + case 2: + mips_cpu_string = "6000"; + mips_cpu = PROCESSOR_R6000; + break; + case 3: + mips_cpu_string = "4000"; + mips_cpu = PROCESSOR_R4000; + break; + case 4: + mips_cpu_string = "8000"; + mips_cpu = PROCESSOR_R8000; + break; + } } else @@ -2832,12 +3243,30 @@ override_options () case '4': if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K")) mips_cpu = PROCESSOR_R4000; + /* The r4400 is exactly the same as the r4000 from the compiler's + viewpoint. */ + else if (!strcmp (p, "4400")) + 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': if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K")) 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; + break; } if (mips_cpu == PROCESSOR_DEFAULT) @@ -2847,66 +3276,80 @@ override_options () } } - /* Now get the architectural level. */ - if (mips_isa_string == (char *)0) - mips_isa = 1; - - else if (isdigit (*mips_isa_string)) - mips_isa = atoi (mips_isa_string); - - else - { - error ("bad value (%s) for -mips switch", mips_isa_string); - mips_isa = 1; - } - - if (mips_isa < 0 || mips_isa > 3) - error ("-mips%d not supported", mips_isa); - - else if (mips_isa > 1 - && (mips_cpu == PROCESSOR_DEFAULT || mips_cpu == PROCESSOR_R3000)) - error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa); - - else if (mips_cpu == PROCESSOR_R6000 && mips_isa > 2) + if ((mips_cpu == PROCESSOR_R3000 && mips_isa > 1) + || (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 the r4000 can support 64 bit ints"); + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit ints"); else if (TARGET_LONG64) - fatal ("Only the r4000 can support 64 bit longs"); - - else if (TARGET_LLONG128) - fatal ("Only the r4000 can support 128 bit long longs"); + fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit longs"); else if (TARGET_FLOAT64) - fatal ("Only the r4000 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 or MIPS-IV CPUs can support 64 bit gp registers"); } - else if (TARGET_INT64 || TARGET_LONG64 || TARGET_LLONG128 || TARGET_FLOAT64) - warning ("r4000 64/128 bit types not yet supported"); + + 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 (); + /* -fpic (-KPIC) is the default when TARGET_ABICALLS is defined. We need + to set flag_pic so that the LEGITIMATE_PIC_OPERAND_P macro will work. */ + /* ??? -non_shared turns off pic code generation, but this is not + implemented. */ + if (TARGET_ABICALLS) + { + mips_abicalls = MIPS_ABICALLS_YES; + flag_pic = 1; + if (mips_section_threshold > 0) + warning ("-G is incompatible with PIC code which is the default"); + } + else + mips_abicalls = MIPS_ABICALLS_NO; + + /* -membedded-pic is a form of PIC code suitable for embedded + systems. All calls are made using PC relative addressing, and + all data is addressed using the $gp register. This requires gas, + which does most of the work, and GNU ld, which automatically + expands PC relative calls which are out of range into a longer + instruction sequence. All gcc really does differently is + generate a different sequence for a switch. */ + if (TARGET_EMBEDDED_PIC) + { + flag_pic = 1; + if (TARGET_ABICALLS) + warning ("-membedded-pic and -mabicalls are incompatible"); + if (g_switch_set) + warning ("-G and -membedded-pic are incompatible"); + /* Setting mips_section_threshold is not required, because gas + will force everything to be GP addressable anyhow, but + setting it will cause gcc to make better estimates of the + number of instructions required to access a particular data + item. */ + mips_section_threshold = 0x7fffffff; + } + /* -mrnames says to use the MIPS software convention for register - names instead of the hardware names (ie, a0 instead of $4). + names instead of the hardware names (ie, $a0 instead of $4). We do this by switching the names in mips_reg_names, which the reg_names points into via the REGISTER_NAMES macro. */ if (TARGET_NAME_REGS) - { - if (TARGET_GAS) - { - target_flags &= ~ MASK_NAME_REGS; - error ("Gas does not support the MIPS software register name convention."); - } - else - bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names)); - } + bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names)); /* If this is OSF/1, set up a SIGINFO handler so we can see what function is currently being compiled. */ @@ -2929,23 +3372,13 @@ override_options () #endif #endif - /* Set up the classification arrays now. */ - mips_rtx_classify[(int)PLUS] = CLASS_ADD_OP; - mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP; - mips_rtx_classify[(int)DIV] = CLASS_DIVMOD_OP; - mips_rtx_classify[(int)MOD] = CLASS_DIVMOD_OP; - mips_rtx_classify[(int)UDIV] = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP; - mips_rtx_classify[(int)UMOD] = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP; - mips_rtx_classify[(int)EQ] = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP; - mips_rtx_classify[(int)NE] = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP; - mips_rtx_classify[(int)GT] = CLASS_CMP_OP | CLASS_FCMP_OP; - mips_rtx_classify[(int)GE] = CLASS_CMP_OP | CLASS_FCMP_OP; - mips_rtx_classify[(int)LT] = CLASS_CMP_OP | CLASS_FCMP_OP; - mips_rtx_classify[(int)LE] = CLASS_CMP_OP | CLASS_FCMP_OP; - mips_rtx_classify[(int)GTU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; - mips_rtx_classify[(int)GEU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; - mips_rtx_classify[(int)LTU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; - mips_rtx_classify[(int)LEU] = CLASS_CMP_OP | CLASS_UNSIGNED_OP; + /* 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['?'] = TRUE; mips_print_operand_punct['#'] = TRUE; @@ -2962,12 +3395,15 @@ override_options () mips_print_operand_punct['>'] = TRUE; mips_print_operand_punct['{'] = TRUE; mips_print_operand_punct['}'] = TRUE; + mips_print_operand_punct['^'] = TRUE; mips_char_to_class['d'] = GR_REGS; 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; @@ -3013,10 +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)) - 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; @@ -3098,6 +3537,11 @@ mips_debugger_offset (addr, offset) 'M' print high-order register of double-word register operand. '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 @@ -3112,7 +3556,8 @@ mips_debugger_offset (addr, offset) '#' Print nop if in a .set noreorder section. '?' Print 'l' if we are to use a branch likely instead of normal branch. '@' Print the name of the assembler temporary register (at or $1). - '.' Print the name of the register with a hard-wired zero (zero or $0). */ + '.' Print the name of the register with a hard-wired zero (zero or $0). + '^' Print the name of the pic call-through register (t9 or $25). */ void print_operand (file, op, letter) @@ -3139,6 +3584,10 @@ print_operand (file, op, letter) fputs (reg_names [GP_REG_FIRST + 1], file); break; + case '^': + fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file); + break; + case '.': fputs (reg_names [GP_REG_FIRST + 0], file); break; @@ -3168,7 +3617,7 @@ print_operand (file, op, letter) if (set_noreorder != 0) fputs ("\n\tnop", file); - else if (TARGET_GAS || TARGET_STATS) + else if (TARGET_STATS) fputs ("\n\t#nop", file); break; @@ -3254,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') @@ -3272,20 +3721,24 @@ 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 (code == REG) + else if (letter == 'S') { - register int regnum = REGNO (op); + char buffer[100]; - if (letter == 'M') - regnum += MOST_SIGNIFICANT_WORD; + ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op)); + assemble_name (file, buffer); + } - else if (letter == 'L') - regnum += LEAST_SIGNIFICANT_WORD; + else if (code == REG) + { + register int regnum = REGNO (op); - else if (letter == 'D') + if ((letter == 'M' && ! WORDS_BIG_ENDIAN) + || (letter == 'L' && WORDS_BIG_ENDIAN) + || letter == 'D') regnum++; fprintf (file, "%s", reg_names[regnum]); @@ -3294,30 +3747,28 @@ 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) { -#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT - union { double d; int i[2]; } u; - u.i[0] = CONST_DOUBLE_LOW (op); - u.i[1] = CONST_DOUBLE_HIGH (op); - if (GET_MODE (op) == SFmode) - { - float f; - f = u.d; - u.d = f; - } - fprintf (file, "%.20e", u.d); -#else - fatal ("CONST_DOUBLE found in cross compilation"); -#endif + REAL_VALUE_TYPE d; + char s[30]; + + REAL_VALUE_FROM_CONST_DOUBLE (d, op); + REAL_VALUE_TO_DECIMAL (d, "%.20e", s); + fprintf (file, s); } 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))); @@ -3329,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); } @@ -3355,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: @@ -3393,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."); @@ -3433,7 +3893,6 @@ mips_output_external (file, decl, name) int len; if (TARGET_GP_OPT - && mips_section_threshold != 0 && ((TREE_CODE (decl)) != FUNCTION_DECL) && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0)) { @@ -3443,12 +3902,56 @@ mips_output_external (file, decl, name) p->size = len; extern_head = p; } + +#ifdef ASM_OUTPUT_UNDEF_FUNCTION + if (TREE_CODE (decl) == FUNCTION_DECL + /* ??? Don't include alloca, since gcc will always expand it + inline. If we don't do this, libg++ fails to build. */ + && strcmp (name, "alloca") + /* ??? Don't include __builtin_next_arg, because then gcc will not + bootstrap under Irix 5.1. */ + && strcmp (name, "__builtin_next_arg")) + { + p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = -1; + extern_head = p; + } +#endif + return 0; } +#ifdef ASM_OUTPUT_UNDEF_FUNCTION +int +mips_output_external_libcall (file, name) + FILE *file; + char *name; +{ + register struct extern_list *p; + + p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list)); + p->next = extern_head; + p->name = name; + p->size = -1; + extern_head = p; + + return 0; +} +#endif + /* Compute a string to use as a temporary file name. */ +/* On MSDOS, write temp files in current dir + because there's no place else we can expect to use. */ +#if __MSDOS__ +#ifndef P_tmpdir +#define P_tmpdir "./" +#endif +#endif + static FILE * make_temp_file () { @@ -3470,19 +3973,25 @@ make_temp_file () } len = strlen (base); - temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX")); + /* temp_filename is global, so we must use malloc, not alloca. */ + temp_filename = (char *) xmalloc (len + sizeof("/ctXXXXXX")); strcpy (temp_filename, base); if (len > 0 && temp_filename[len-1] != '/') temp_filename[len++] = '/'; - strcpy (temp_filename + len, "ccXXXXXX"); + strcpy (temp_filename + len, "ctXXXXXX"); mktemp (temp_filename); stream = fopen (temp_filename, "w+"); if (!stream) pfatal_with_name (temp_filename); +#ifndef __MSDOS__ + /* In MSDOS, we cannot unlink the temporary file until we are finished using + it. Otherwise, we delete it now, so that it will be gone even if the + compiler happens to crash. */ unlink (temp_filename); +#endif return stream; } @@ -3504,16 +4013,18 @@ mips_output_filename (stream, name) first_time = FALSE; SET_FILE_NUMBER (); current_function_file = name; - fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name); + 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"); } - else if (!TARGET_GAS && write_symbols == DBX_DEBUG) + else if (write_symbols == DBX_DEBUG) { ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0); - fprintf (stream, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP, - name, N_SOL, <ext_label_name[1]); + fprintf (stream, "%s ", ASM_STABS_OP); + output_quoted_string (stream, name); + fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]); } else if (name != current_function_file @@ -3527,15 +4038,12 @@ 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 \"%s\"\n", num_source_filenames, name); } - else { SET_FILE_NUMBER (); current_function_file = name; - fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name); + ASM_OUTPUT_FILENAME (stream, num_source_filenames, name); } } } @@ -3551,11 +4059,12 @@ mips_output_lineno (stream, line) FILE *stream; int line; { - if (!TARGET_GAS && write_symbols == DBX_DEBUG) + 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 @@ -3584,6 +4093,9 @@ mips_output_lineno (stream, line) because of load delays, and also to update the delay slot statistics. */ +/* ??? There is no real need for this function, because it never actually + emits a NOP anymore. */ + void final_prescan_insn (insn, opvec, noperands) rtx insn; @@ -3601,21 +4113,18 @@ final_prescan_insn (insn, opvec, noperan || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern)) || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern)) || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern))) - fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); + fputs ("\t#nop\n", asm_out_file); else dslots_load_filled++; while (--dslots_number_nops > 0) - fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); + fputs ("\t#nop\n", asm_out_file); mips_load_reg = (rtx)0; mips_load_reg2 = (rtx)0; mips_load_reg3 = (rtx)0; mips_load_reg4 = (rtx)0; - - if (set_noreorder && --set_noreorder == 0) - fputs ("\t.set\treorder\n", asm_out_file); } if (TARGET_STATS) @@ -3649,10 +4158,26 @@ mips_asm_file_start (stream) if (TARGET_MIPS_AS && optimize && flag_delayed_branch) fprintf (stream, "\t.set\tnobopt\n"); - /* Generate the pseudo ops that the Pyramid based System V.4 wants. */ + /* Generate the pseudo ops that System V.4 wants. */ +#ifndef ABICALLS_ASM_OP +#define ABICALLS_ASM_OP ".abicalls" +#endif if (TARGET_ABICALLS) - fprintf (stream, "\t.abicalls\n"); + /* ??? 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. */ + /* ??? Current versions of gas do not require that externs occur before + symbol references. This means that this code is unnecessary when + gas is being used. This gas feature hasn't been well tested as yet + though. */ if (TARGET_GP_OPT) { asm_out_data_file = stream; @@ -3661,9 +4186,6 @@ mips_asm_file_start (stream) else asm_out_data_file = asm_out_text_file = stream; - if (TARGET_NAME_REGS) - fprintf (asm_out_file, "#include \n"); - print_options (stream); } @@ -3686,10 +4208,9 @@ mips_asm_file_end (file) if (HALF_PIC_P ()) HALF_PIC_FINISH (file); - if (TARGET_GP_OPT) + if (extern_head) { - if (extern_head) - fputs ("\n", file); + fputs ("\n", file); for (p = extern_head; p != 0; p = p->next) { @@ -3699,12 +4220,22 @@ mips_asm_file_end (file) if (! TREE_ASM_WRITTEN (name_tree)) { TREE_ASM_WRITTEN (name_tree) = 1; - fputs ("\t.extern\t", file); - assemble_name (file, p->name); - fprintf (file, ", %d\n", p->size); +#ifdef ASM_OUTPUT_UNDEF_FUNCTION + if (p->size == -1) + ASM_OUTPUT_UNDEF_FUNCTION (file, p->name); + else +#endif + { + fputs ("\t.extern\t", file); + assemble_name (file, p->name); + fprintf (file, ", %d\n", p->size); + } } } - + } + + if (TARGET_GP_OPT) + { fprintf (file, "\n\t.text\n"); rewind (asm_out_text_file); if (ferror (asm_out_text_file)) @@ -3719,6 +4250,10 @@ mips_asm_file_end (file) if (fclose (asm_out_text_file) != 0) pfatal_with_name (temp_filename); + +#ifdef __MSDOS__ + unlink (temp_filename); +#endif } } @@ -3739,7 +4274,7 @@ mips_declare_object (stream, name, init_ assemble_name (stream, name); fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */ - if (TARGET_GP_OPT && mips_section_threshold != 0) + if (TARGET_GP_OPT) { tree name_tree = get_identifier (name); TREE_ASM_WRITTEN (name_tree) = 1; @@ -3850,7 +4385,6 @@ epilogue_reg_mentioned_p (insn) return 0; } - /* Return the bytes needed to compute the frame pointer from the current stack pointer. @@ -3871,12 +4405,8 @@ epilogue_reg_mentioned_p (insn) | 4 words to save | | 4 words to save | | arguments passed | | arguments passed | | in registers, even | | in registers, even | - SP->| if not passed. | FP->| if not passed. | + SP->| if not passed. | VFP->| if not passed. | +-----------------------+ +-----------------------+ - | | - | GP save for V.4 abi | - | | - +-----------------------+ | | | fp register save | | | @@ -3894,6 +4424,10 @@ epilogue_reg_mentioned_p (insn) | | +-----------------------+ | | + | GP save for V.4 abi | + | | + +-----------------------+ + | | | arguments on stack | | | +-----------------------+ @@ -3951,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; @@ -3966,16 +4500,34 @@ compute_frame_size (size) { if (regs_ever_live[regno] && !call_used_regs[regno]) { - fp_reg_size += 2*UNITS_PER_WORD; + fp_reg_size += fp_inc * UNITS_PER_FPREG; fmask |= fp_bits << (regno - FP_REG_FIRST); } } 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) + { + /* Add the context-pointer to the saved registers. */ + gp_reg_size += UNITS_PER_WORD; + mask |= 1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST); + total_size -= gp_reg_rounded; + gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_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; @@ -3988,11 +4540,12 @@ compute_frame_size (size) current_frame_info.fmask = fmask; current_frame_info.initialized = reload_completed; current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD; - current_frame_info.num_fp = fp_reg_size / (2*UNITS_PER_WORD); + current_frame_info.num_fp = fp_reg_size / (fp_inc * UNITS_PER_FPREG); if (mask) { - unsigned long offset = args_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; } @@ -4005,7 +4558,9 @@ compute_frame_size (size) if (fmask) { - unsigned long offset = args_size + var_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD; + unsigned long offset = (args_size + extra_size + var_size + + gp_reg_rounded + fp_reg_size + - fp_inc * UNITS_PER_FPREG); current_frame_info.fp_sp_offset = offset; current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD; } @@ -4084,9 +4639,15 @@ save_restore_insns (store_p, large_reg, base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); base_offset = large_offset; if (file == (FILE *)0) - emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + { + if (TARGET_LONG64) + emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + else + emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + } else - fprintf (file, "\taddu\t%s,%s,%s\n", + fprintf (file, "\t%s\t%s,%s,%s\n", + TARGET_LONG64 ? "daddu" : "addu", reg_names[MIPS_TEMP2_REGNUM], reg_names[REGNO (large_reg)], reg_names[STACK_POINTER_REGNUM]); @@ -4099,13 +4660,17 @@ save_restore_insns (store_p, large_reg, if (file == (FILE *)0) { emit_move_insn (base_reg_rtx, GEN_INT (gp_offset)); - emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); + if (TARGET_LONG64) + emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); + else + emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); } else - fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n", + fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\t%s\t%s,%s,%s\n", reg_names[MIPS_TEMP2_REGNUM], (long)base_offset, (long)base_offset, + TARGET_LONG64 ? "daddu" : "addu", reg_names[MIPS_TEMP2_REGNUM], reg_names[MIPS_TEMP2_REGNUM], reg_names[STACK_POINTER_REGNUM]); @@ -4117,41 +4682,49 @@ save_restore_insns (store_p, large_reg, { if (file == (FILE *)0) { - rtx reg_rtx = gen_rtx (REG, Pmode, regno); - rtx mem_rtx = gen_rtx (MEM, Pmode, + rtx reg_rtx = gen_rtx (REG, word_mode, regno); + rtx mem_rtx = gen_rtx (MEM, word_mode, gen_rtx (PLUS, Pmode, base_reg_rtx, GEN_INT (gp_offset - base_offset))); if (store_p) emit_move_insn (mem_rtx, reg_rtx); - else + 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 - fprintf (file, "\t%s\t%s,%ld(%s)\n", - (store_p) ? "sw" : "lw", - reg_names[regno], - gp_offset - base_offset, - reg_names[REGNO(base_reg_rtx)]); + { + 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 + ? (store_p) ? "sd" : "ld" + : (store_p) ? "sw" : "lw"), + reg_names[regno], + gp_offset - base_offset, + reg_names[REGNO(base_reg_rtx)]); + } gp_offset -= UNITS_PER_WORD; } } } 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. */ fp_offset = current_frame_info.fp_sp_offset; - end_offset = fp_offset - (current_frame_info.fp_reg_size - 2*UNITS_PER_WORD); + end_offset = fp_offset - (current_frame_info.fp_reg_size - fp_size); if (fp_offset < 0 || end_offset < 0) fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.", @@ -4177,9 +4750,15 @@ save_restore_insns (store_p, large_reg, base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); base_offset = large_offset; if (file == (FILE *)0) - emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + { + if (TARGET_LONG64) + emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + else + emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + } else - fprintf (file, "\taddu\t%s,%s,%s\n", + fprintf (file, "\t%s\t%s,%s,%s\n", + TARGET_LONG64 ? "daddu" : "addu", reg_names[MIPS_TEMP2_REGNUM], reg_names[REGNO (large_reg)], reg_names[STACK_POINTER_REGNUM]); @@ -4192,13 +4771,17 @@ save_restore_insns (store_p, large_reg, if (file == (FILE *)0) { emit_move_insn (base_reg_rtx, GEN_INT (fp_offset)); - emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); + if (TARGET_LONG64) + emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); + else + emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); } else - fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n", + fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\t%s\t%s,%s,%s\n", reg_names[MIPS_TEMP2_REGNUM], (long)base_offset, (long)base_offset, + TARGET_LONG64 ? "daddu" : "addu", reg_names[MIPS_TEMP2_REGNUM], reg_names[MIPS_TEMP2_REGNUM], reg_names[STACK_POINTER_REGNUM]); @@ -4210,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))); @@ -4222,13 +4807,15 @@ 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)]); - fp_offset -= 2*UNITS_PER_WORD; + fp_offset -= fp_size; } } } @@ -4242,31 +4829,30 @@ 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)); - if (debug_info_level != DINFO_LEVEL_TERSE) +#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); - if (TARGET_ABICALLS) - fprintf (file, - "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n", - reg_names[ GP_REG_FIRST + 25 ]); - - tsize = current_frame_info.total_size; - if (tsize > 0 && TARGET_ABICALLS) - fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET); - - fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n", + fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args= %d, extra= %d\n", reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ], tsize, reg_names[31 + GP_REG_FIRST], @@ -4281,6 +4867,21 @@ function_prologue (file, size) current_frame_info.gp_save_offset, current_frame_info.fmask, current_frame_info.fp_save_offset); + + if (TARGET_ABICALLS && ! (ABI_64BIT && mips_isa >= 3)) + { + char *sp_str = reg_names[STACK_POINTER_REGNUM]; + + fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n", + reg_names[PIC_FUNCTION_ADDR_REGNUM]); + if (tsize > 0) + { + fprintf (file, "\t%s\t%s,%s,%d\n", + (TARGET_LONG64 ? "dsubu" : "subu"), + sp_str, sp_str, tsize); + fprintf (file, "\t.cprestore %d\n", current_frame_info.args_size); + } + } } @@ -4304,6 +4905,18 @@ mips_expand_prologue () tree cur_arg; CUMULATIVE_ARGS args_so_far; + /* If struct value address is treated as the first argument, make it so. */ + if (aggregate_value_p (DECL_RESULT (fndecl)) + && ! current_function_returns_pcc_struct + && struct_value_incoming_rtx == 0) + { + tree type = build_pointer_type (fntype); + tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); + DECL_ARG_TYPE (function_result_decl) = type; + TREE_CHAIN (function_result_decl) = fnargs; + fnargs = function_result_decl; + } + /* Determine the last argument, and get its name. */ INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0); @@ -4311,12 +4924,17 @@ mips_expand_prologue () for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg) { - tree type = DECL_ARG_TYPE (cur_arg); - enum machine_mode passed_mode = TYPE_MODE (type); - rtx entry_parm = FUNCTION_ARG (args_so_far, - passed_mode, - DECL_ARG_TYPE (cur_arg), - 1); + tree passed_type = DECL_ARG_TYPE (cur_arg); + enum machine_mode passed_mode = TYPE_MODE (passed_type); + rtx entry_parm; + + if (TYPE_NEEDS_CONSTRUCTING (passed_type)) + { + passed_type = build_pointer_type (passed_type); + passed_mode = Pmode; + } + + entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1); if (entry_parm) { @@ -4324,7 +4942,7 @@ mips_expand_prologue () /* passed in a register, so will get homed automatically */ if (GET_MODE (entry_parm) == BLKmode) - words = (int_size_in_bytes (type) + 3) / 4; + words = (int_size_in_bytes (passed_type) + 3) / 4; else words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; @@ -4336,10 +4954,7 @@ mips_expand_prologue () break; } - FUNCTION_ARG_ADVANCE (args_so_far, - passed_mode, - DECL_ARG_TYPE (cur_arg), - 1); + FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1); next_arg = TREE_CHAIN (cur_arg); if (next_arg == (tree)0) @@ -4376,43 +4991,68 @@ mips_expand_prologue () } } + tsize = compute_frame_size (get_frame_size ()); + /* 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 && ! (ABI_64BIT && mips_isa >= 3)) + offset += tsize; + for (; regno <= GP_ARG_LAST; regno++) { - rtx ptr = stack_pointer_rtx; - if (regno != GP_ARG_FIRST) - ptr = gen_rtx (PLUS, Pmode, ptr, - GEN_INT ((regno - GP_ARG_FIRST) * UNITS_PER_WORD)); - - emit_move_insn (gen_rtx (MEM, Pmode, ptr), gen_rtx (REG, Pmode, regno)); + if (offset != 0) + ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset)); + emit_move_insn (gen_rtx (MEM, word_mode, ptr), + gen_rtx (REG, word_mode, regno)); + offset += UNITS_PER_WORD; } } - tsize = compute_frame_size (get_frame_size ()); if (tsize > 0) { rtx tsize_rtx = GEN_INT (tsize); - if (tsize > 32767) + /* If we are doing svr4-abi, sp move is done by function_prologue. */ + if (!TARGET_ABICALLS || (ABI_64BIT && mips_isa >= 3)) { - tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM); - emit_move_insn (tmp_rtx, tsize_rtx); - tsize_rtx = tmp_rtx; - } + if (tsize > 32767) + { + tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM); + emit_move_insn (tmp_rtx, tsize_rtx); + tsize_rtx = tmp_rtx; + } - emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); + if (TARGET_LONG64) + emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + else + emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + } save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0); if (frame_pointer_needed) - emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); + { + if (TARGET_64BIT) + emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx)); + 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 @@ -4426,17 +5066,19 @@ mips_expand_prologue () /* Do any necessary cleanup after a function to restore stack, frame, and regs. */ #define RA_MASK ((long) 0x80000000) /* 1 << 31 */ +#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST)) void 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]; rtx epilogue_delay = current_function_epilogue_delay_list; - int noreorder = !TARGET_MIPS_AS || (epilogue_delay != 0); + int noreorder = (epilogue_delay != 0); int noepilogue = FALSE; int load_nop = FALSE; int load_only_r31; @@ -4459,11 +5101,8 @@ function_epilogue (file, size) else { while (--dslots_number_nops > 0) - fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); + fputs ("\t#nop\n", asm_out_file); } - - if (set_noreorder > 0 && --set_noreorder == 0) - fputs ("\t.set\treorder\n", file); } if (set_noat != 0) @@ -4490,7 +5129,7 @@ function_epilogue (file, size) if (set_volatile != 0) { set_volatile = 0; - fprintf (file, "\t#.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#"); + fprintf (file, "\t%s.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#"); error ("internal gcc error: .set volatile left on in epilogue"); } @@ -4534,7 +5173,10 @@ function_epilogue (file, size) save_restore_insns (FALSE, tmp_rtx, tsize, file); - load_only_r31 = (current_frame_info.mask == RA_MASK + load_only_r31 = (((current_frame_info.mask + & ~ (TARGET_ABICALLS && ! (ABI_64BIT && mips_isa >= 3) + ? PIC_OFFSET_TABLE_MASK : 0)) + == RA_MASK) && current_frame_info.fmask == 0); if (noreorder) @@ -4562,10 +5204,14 @@ function_epilogue (file, size) fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]); if (tsize > 32767) - fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str); + fprintf (file, "\t%s\t%s,%s,%s\n", + TARGET_LONG64 ? "daddu" : "addu", + sp_str, sp_str, t1_str); else if (tsize > 0) - fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize); + fprintf (file, "\t%s\t%s,%s,%d\n", + TARGET_LONG64 ? "daddu" : "addu", + sp_str, sp_str, tsize); else if (!load_only_r31 && epilogue_delay != 0) final_scan_insn (XEXP (epilogue_delay, 0), @@ -4580,17 +5226,26 @@ function_epilogue (file, size) else { if (tsize > 32767) - fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str); + fprintf (file, "\t%s\t%s,%s,%s\n", + TARGET_LONG64 ? "daddu" : "addu", + sp_str, sp_str, t1_str); else if (tsize > 0) - fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize); + fprintf (file, "\t%s\t%s,%s,%d\n", + TARGET_LONG64 ? "daddu" : "addu", + sp_str, sp_str, tsize); fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]); } } + /* 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) @@ -4598,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++; @@ -4685,7 +5340,7 @@ mips_expand_epilogue () if (tsize > 32767) { - tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM); + tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM); emit_move_insn (tmp_rtx, tsize_rtx); tsize_rtx = tmp_rtx; } @@ -4693,11 +5348,21 @@ mips_expand_epilogue () if (tsize > 0) { if (frame_pointer_needed) - emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); + { + if (TARGET_LONG64) + emit_insn (gen_movdi (stack_pointer_rtx, frame_pointer_rtx)); + else + emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); + } save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0); - emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); + if (TARGET_LONG64) + emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); + else + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, + tsize_rtx)); } emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31))); @@ -4740,3 +5405,225 @@ simple_epilogue_p () return (compute_frame_size (get_frame_size ())) == 0; } + +/* Choose the section to use for the constant rtx expression X that has + mode MODE. */ + +mips_select_rtx_section (mode, x) + enum machine_mode mode; + rtx x; +{ + if (TARGET_EMBEDDED_DATA) + { + /* For embedded applications, always put constants in read-only data, + in order to reduce RAM usage. */ + READONLY_DATA_SECTION (); + } + else + { + /* For hosted applications, always put constants in small data if + possible, as this gives the best performance. */ + + if (GET_MODE_SIZE (mode) <= mips_section_threshold + && mips_section_threshold > 0) + SMALL_DATA_SECTION (); + else + READONLY_DATA_SECTION (); + } +} + +/* Choose the section to use for DECL. RELOC is true if its value contains + any relocatable expression. */ + +mips_select_section (decl, reloc) + tree decl; + int reloc; +{ + int size = int_size_in_bytes (TREE_TYPE (decl)); + + if (TARGET_EMBEDDED_PIC + && TREE_CODE (decl) == STRING_CST + && !flag_writable_strings) + { + /* For embedded position independent code, put constant strings + in the text section, because the data section is limited to + 64K in size. */ + + text_section (); + } + else if (TARGET_EMBEDDED_DATA) + { + /* For embedded applications, always put an object in read-only data + if possible, in order to reduce RAM usage. */ + + 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)))) + /* Deal with calls from output_constant_def_contents. */ + || (TREE_CODE (decl) != VAR_DECL + && (TREE_CODE (decl) != STRING_CST + || !flag_writable_strings))) + && ! (flag_pic && reloc)) + READONLY_DATA_SECTION (); + else if (size > 0 && size <= mips_section_threshold) + SMALL_DATA_SECTION (); + else + data_section (); + } + else + { + /* For hosted applications, always put an object in small data if + possible, as this gives the best performance. */ + + if (size > 0 && size <= mips_section_threshold) + 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)))) + /* Deal with calls from output_constant_def_contents. */ + || (TREE_CODE (decl) != VAR_DECL + && (TREE_CODE (decl) != STRING_CST + || !flag_writable_strings))) + && ! (flag_pic && reloc)) + 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; +}