/* Subroutines for insn-output.c for MIPS
+ Copyright (C) 1989, 90, 91, 93-95, 1996 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.
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"
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
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
/* which instruction set architecture to use. */
int mips_isa;
+#ifdef MIPS_ABI_DEFAULT
+/* which ABI to use. This is defined to a constant in mips.h if the target
+ doesn't support multiple ABIs. */
+enum mips_abi_type mips_abi;
+#endif
+
/* Strings to hold which cpu and instruction set architecture to use. */
char *mips_cpu_string; /* for -mcpu=<xxx> */
-char *mips_isa_string; /* for -mips{1,2,3} */
+char *mips_isa_string; /* for -mips{1,2,3,4} */
+char *mips_abi_string; /* for -mabi={o32,32,n32,n64,64,eabi} */
+
+/* If TRUE, we split addresses into their high and low parts in the RTL. */
+int mips_split_addresses;
/* 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. */
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];
"$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","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
+ "$fcc5","$fcc6","$fcc7","$rap"
};
/* Mips software names for the registers, used to overwrite the
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","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
+ "$fcc5","$fcc6","$fcc7","$rap"
};
/* Map hard register number to register 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,
+ ST_REGS, ST_REGS, ST_REGS, ST_REGS,
+ ST_REGS, ST_REGS, ST_REGS, GR_REGS
};
/* Map register constraint character to register class. */
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
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;
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;
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
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 (mips_abi != ABI_32 && mips_abi != ABI_EABI)
+ 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;
}
+/* Accept the floating point constant 1 in the appropriate mode. */
+
+int
+const_float_1_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ REAL_VALUE_TYPE d;
+ static REAL_VALUE_TYPE onedf;
+ static REAL_VALUE_TYPE onesf;
+ static int one_initialized;
+
+ if (GET_CODE (op) != CONST_DOUBLE
+ || mode != GET_MODE (op)
+ || (mode != DFmode && mode != SFmode))
+ return FALSE;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+
+ /* We only initialize these values if we need them, since we will
+ never get called unless mips_isa >= 4. */
+ if (! one_initialized)
+ {
+ onedf = REAL_VALUE_ATOF ("1.0", DFmode);
+ onesf = REAL_VALUE_ATOF ("1.0", SFmode);
+ one_initialized = TRUE;
+ }
+
+ if (mode == DFmode)
+ return REAL_VALUES_EQUAL (d, onedf);
+ else
+ return REAL_VALUES_EQUAL (d, onesf);
+}
+
/* Return truth value if a memory operand fits in a single instruction
(ie, register + small offset). */
return FALSE;
/* dword operations really put out 2 instructions, so eliminate them. */
- if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
+ /* ??? This isn't strictly correct. It is OK to accept multiword modes
+ here, since the length attributes are being set correctly, but only
+ if the address is offsettable. LO_SUM is not offsettable. */
+ if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD)
return FALSE;
/* Decode the address now. */
break;
case REG:
+ case LO_SUM:
return TRUE;
case CONST_INT:
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 */
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.) */
if (mode != GET_MODE (op))
return FALSE;
- return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+ return (GET_RTX_CLASS (GET_CODE (op)) == '<');
+}
+
+/* Return true if the operand is either the PC or a label_ref. */
+
+int
+pc_or_label_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (op == pc_rtx)
+ return TRUE;
+
+ if (GET_CODE (op) == LABEL_REF)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Test for a valid operand for a call instruction.
+ Don't allow the arg pointer register or virtual regs
+ since they may change into reg + const, which the patterns
+ can't handle yet. */
+
+int
+call_insn_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (CONSTANT_ADDRESS_P (op)
+ || (GET_CODE (op) == REG && op != arg_pointer_rtx
+ && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
+ && REGNO (op) <= LAST_VIRTUAL_REGISTER)))
+ return 1;
+ return 0;
}
+/* Return true if OPERAND is valid as a source operand for a move
+ instruction. */
+
+int
+move_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (general_operand (op, mode)
+ && ! (mips_split_addresses && mips_check_split (op, mode)));
+}
-/* 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. */
+/* Return true if OPERAND is valid as a source operand for movdi.
+ This accepts not only general_operand, but also sign extended
+ constants and registers. We need to accept sign extended constants
+ in case a sign extended register which is used in an expression,
+ and is equivalent to a constant, is spilled. */
int
-cmp2_op (op, mode)
+movdi_operand (op, mode)
rtx op;
enum machine_mode mode;
{
- if (mode != GET_MODE (op))
- return FALSE;
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && (GET_MODE (XEXP (op, 0)) == SImode
+ || (GET_CODE (XEXP (op, 0)) == CONST_INT
+ && GET_MODE (XEXP (op, 0)) == VOIDmode))
+ && (register_operand (XEXP (op, 0), SImode)
+ || immediate_operand (XEXP (op, 0), SImode)))
+ return 1;
- return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+ return general_operand (op, mode);
}
-/* Return true if the code is an unsigned relational operations (LEU, etc.) */
+/* Like register_operand, but when in 64 bit mode also accept a sign
+ extend of a 32 bit register, since the value is known to be already
+ sign extended. */
int
-uns_cmp_op (op,mode)
+se_register_operand (op, mode)
rtx op;
enum machine_mode mode;
{
- if (mode != GET_MODE (op))
- return FALSE;
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && register_operand (XEXP (op, 0), SImode))
+ return 1;
- return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP;
+ return register_operand (op, mode);
}
-/* Return true if the code is a relational operation FP can use. */
+/* Like reg_or_0_operand, but when in 64 bit mode also accept a sign
+ extend of a 32 bit register, since the value is known to be already
+ sign extended. */
int
-fcmp_op (op, mode)
+se_reg_or_0_operand (op, mode)
rtx op;
enum machine_mode mode;
{
- if (mode != GET_MODE (op))
- return FALSE;
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && register_operand (XEXP (op, 0), SImode))
+ return 1;
- return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;
+ return reg_or_0_operand (op, mode);
}
+/* Like uns_arith_operand, but when in 64 bit mode also accept a sign
+ extend of a 32 bit register, since the value is known to be already
+ sign extended. */
-/* Return true if the operand is either the PC or a label_ref. */
+int
+se_uns_arith_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && register_operand (XEXP (op, 0), SImode))
+ return 1;
+
+ return uns_arith_operand (op, mode);
+}
+
+/* Like arith_operand, but when in 64 bit mode also accept a sign
+ extend of a 32 bit register, since the value is known to be already
+ sign extended. */
int
-pc_or_label_operand (op, mode)
+se_arith_operand (op, mode)
rtx op;
enum machine_mode mode;
{
- if (op == pc_rtx)
- return TRUE;
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && register_operand (XEXP (op, 0), SImode))
+ return 1;
- if (GET_CODE (op) == LABEL_REF)
- return TRUE;
+ return arith_operand (op, mode);
+}
- return FALSE;
+/* Like nonmemory_operand, but when in 64 bit mode also accept a sign
+ extend of a 32 bit register, since the value is known to be already
+ sign extended. */
+
+int
+se_nonmemory_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && register_operand (XEXP (op, 0), SImode))
+ return 1;
+
+ return nonmemory_operand (op, mode);
}
-/* Test for a valid operand for a call instruction.
- Don't allow the arg pointer register or virtual regs
- since they may change into reg + const, which the patterns
- can't handle yet. */
+/* Like nonimmediate_operand, but when in 64 bit mode also accept a
+ sign extend of a 32 bit register, since the value is known to be
+ already sign extended. */
int
-call_insn_operand (op, mode)
+se_nonimmediate_operand (op, mode)
rtx op;
enum machine_mode mode;
{
- if (GET_CODE (op) == MEM
- && (CONSTANT_ADDRESS_P (XEXP (op, 0))
- || (GET_CODE (XEXP (op, 0)) == REG
- && XEXP (op, 0) != arg_pointer_rtx
- && !(REGNO (XEXP (op, 0)) >= FIRST_PSEUDO_REGISTER
- && REGNO (XEXP (op, 0)) <= LAST_VIRTUAL_REGISTER))))
+ if (TARGET_64BIT
+ && mode == DImode
+ && GET_CODE (op) == SIGN_EXTEND
+ && GET_MODE (op) == DImode
+ && GET_MODE (XEXP (op, 0)) == SImode
+ && register_operand (XEXP (op, 0), SImode))
+ return 1;
+
+ return nonimmediate_operand (op, mode);
+}
+
+/* Return true if we split the address into high and low parts. */
+
+/* ??? We should also handle reg+array somewhere. We get four
+ instructions currently, lui %hi/addui %lo/addui reg/lw. Better is
+ lui %hi/addui reg/lw %lo. Fixing GO_IF_LEGITIMATE_ADDRESS to accept
+ (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken
+ out of the address, then we have 4 instructions to combine. Perhaps
+ add a 3->2 define_split for combine. */
+
+/* ??? We could also split a CONST_INT here if it is a large_int().
+ However, it doesn't seem to be very useful to have %hi(constant).
+ We would be better off by doing the masking ourselves and then putting
+ the explicit high part of the constant in the RTL. This will give better
+ optimization. Also, %hi(constant) needs assembler changes to work.
+ There is already a define_split that does this. */
+
+int
+mips_check_split (address, mode)
+ rtx address;
+ enum machine_mode mode;
+{
+ /* ??? This is the same check used in simple_memory_operand.
+ We use it here because LO_SUM is not offsettable. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+ return 0;
+
+ if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address))
+ || (GET_CODE (address) == CONST
+ && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF
+ && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0)))
+ || GET_CODE (address) == LABEL_REF)
return 1;
+
return 0;
}
\f
-/* 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.
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 */
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)
{
mips_load_reg4 = 0;
}
- if (TARGET_GAS && set_noreorder++ == 0)
- fputs ("\t.set\tnoreorder\n", asm_out_file);
-
return ret;
}
case REG:
case CONST_INT:
+ case LO_SUM:
break;
case PLUS:
}
\f
+/* 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 *
code1 = GET_CODE (op1);
}
+ /* For our purposes, a condition code mode is the same as SImode. */
+ if (mode == CCmode)
+ mode = SImode;
+
if (code0 == REG)
{
int regno0 = REGNO (op0) + subreg_word0;
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 if (ST_REG_P (regno1) && mips_isa >= 4)
+ ret = "li\t%0,1\n\tmovf\t%0,%.,%1";
+
else
{
delay = DELAY_LOAD;
if (FP_REG_P (regno1))
ret = "mfc1\t%0,%1";
- else if (regno1 == FPSW_REGNUM)
+ else if (regno1 == FPSW_REGNUM && mips_isa < 4)
ret = "cfc1\t%0,$31";
}
}
if (GP_REG_P (regno1))
{
delay = DELAY_HILO;
- ret = "mt%0\t%1";
+ if (regno0 != HILO_REGNUM)
+ ret = "mt%0\t%1";
}
}
- else if (regno0 == FPSW_REGNUM)
+ else if (regno0 == FPSW_REGNUM && mips_isa < 4)
{
if (GP_REG_P (regno1))
{
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:
+ case CCmode:
+ 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;
}
}
}
}
- 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))
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))
- ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1";
+ /* Don't use X format, because that will give out of range
+ numbers for 64 bit host and 32 bit target. */
+ ret = "li\t%0,%1\t\t\t# %X1";
}
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,%.";
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,%@%]";
}
}
}
operands[3] = add_op1;
ret = "add%:\t%0,%2,%3";
}
+
+ else if (code1 == HIGH)
+ {
+ operands[1] = XEXP (op1, 0);
+ ret = "lui\t%0,%%hi(%1)";
+ }
}
else if (code0 == MEM)
}
}
- 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)
{
code0 = GET_CODE (op0);
}
+ if (code1 == SIGN_EXTEND)
+ {
+ op1 = XEXP (op1, 0);
+ code1 = GET_CODE (op1);
+ }
+
while (code1 == SUBREG)
{
subreg_word1 += SUBREG_WORD (op1);
code1 = GET_CODE (op1);
}
+ /* Sanity check. */
+ if (GET_CODE (operands[1]) == SIGN_EXTEND
+ && code1 != REG
+ && code1 != CONST_INT
+ /* The following three can happen as the result of a questionable
+ cast. */
+ && code1 != LABEL_REF
+ && code1 != SYMBOL_REF
+ && code1 != CONST)
+ abort ();
+
if (code0 == REG)
{
int regno0 = REGNO (op0) + subreg_word0;
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";
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";
}
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 (GET_CODE (operands[1]) == SIGN_EXTEND)
+ ret = "li\t%0,%1\t\t# %X1";
+ else 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 if (HOST_BITS_PER_WIDE_INT < 64)
+ {
+ operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
+ ret = "li\t%M0,%2\n\tli\t%L0,%1";
+ }
+ else
+ {
+ /* We use multiple shifts here, to avoid warnings about out
+ of range shifts on 32 bit hosts. */
+ operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16);
+ operands[1] = GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16);
+ ret = "li\t%M0,%2\n\tli\t%L0,%1";
+ }
}
else if (code1 == MEM)
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);
ret = volatile_buffer;
}
}
+
+ else if (code1 == LABEL_REF
+ || code1 == SYMBOL_REF
+ || code1 == CONST)
+ {
+ if (TARGET_STATS)
+ mips_count_memory_refs (op1, 2);
+
+ if (GET_CODE (operands[1]) == SIGN_EXTEND)
+ /* We deliberately remove the 'a' from '%1', so that we don't
+ have to add SIGN_EXTEND support to print_operand_address.
+ print_operand will just call print_operand_address in this
+ case, so there is no problem. */
+ ret = "la\t%0,%1";
+ else
+ ret = "dla\t%0,%a1";
+ }
}
else if (code0 == MEM)
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);
}
}
- 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
{
break;
case LO_SUM:
- case HIGH:
return 1;
case LABEL_REF:
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 */
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:
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;
+}
\f
/* Make normal rtx_code into something we can index from an array */
\f
/* 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)
};
enum internal_test test;
+ enum machine_mode mode;
struct cmp_info *p_info;
int branch_p;
int eqne_p;
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)
}
/* 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. */
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))
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;
}
\f
/* 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)
rtx operands[];
enum rtx_code test_code;
{
- static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = {
- { /* CMP_SI */
- SImode, /* eq */
- SImode, /* ne */
- SImode, /* gt */
- SImode, /* ge */
- SImode, /* lt */
- SImode, /* le */
- SImode, /* gtu */
- SImode, /* geu */
- SImode, /* ltu */
- SImode, /* leu */
- },
- { /* CMP_SF */
- CC_FPmode, /* eq */
- CC_REV_FPmode, /* ne */
- CC_FPmode, /* gt */
- CC_FPmode, /* ge */
- CC_FPmode, /* lt */
- CC_FPmode, /* le */
- VOIDmode, /* gtu */
- VOIDmode, /* geu */
- VOIDmode, /* ltu */
- VOIDmode, /* leu */
- },
- { /* CMP_DF */
- CC_FPmode, /* eq */
- CC_REV_FPmode, /* ne */
- CC_FPmode, /* gt */
- CC_FPmode, /* ge */
- CC_FPmode, /* lt */
- CC_FPmode, /* le */
- VOIDmode, /* gtu */
- VOIDmode, /* geu */
- VOIDmode, /* ltu */
- VOIDmode, /* leu */
- },
- };
-
+ enum cmp_type type = branch_type;
+ rtx cmp0 = branch_cmp[0];
+ rtx cmp1 = branch_cmp[1];
enum machine_mode mode;
- enum cmp_type type = branch_type;
- rtx cmp0 = branch_cmp[0];
- rtx cmp1 = branch_cmp[1];
- rtx label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
- rtx label2 = pc_rtx;
- rtx reg = (rtx)0;
- int invert = 0;
- enum internal_test test = map_test_to_internal_test (test_code);
-
- if (test == ITEST_MAX)
- {
- mode = SImode;
- goto fail;
- }
-
- /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode). */
- mode = mode_map[(int)type][(int)test];
- if (mode == VOIDmode)
- goto fail;
+ rtx reg;
+ int invert;
+ rtx label1, label2;
- switch (branch_type)
+ switch (type)
{
default:
- goto fail;
+ abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
case CMP_SI:
- reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
- if (reg != (rtx)0)
+ case CMP_DI:
+ mode = type == CMP_SI ? SImode : DImode;
+ invert = FALSE;
+ reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
+ if (reg)
{
cmp0 = reg;
cmp1 = const0_rtx;
test_code = NE;
}
-
- /* Make sure not non-zero constant if ==/!= */
else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
- cmp1 = force_reg (SImode, cmp1);
-
+ {
+ /* We don't want to build a comparison against a non-zero
+ constant. */
+ cmp1 = force_reg (mode, cmp1);
+ }
break;
- case CMP_DF:
case CMP_SF:
- {
- rtx reg = gen_rtx (REG, mode, FPSW_REGNUM);
- emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1)));
- cmp0 = reg;
- cmp1 = const0_rtx;
- test_code = NE;
- }
+ case CMP_DF:
+ if (mips_isa < 4)
+ reg = gen_rtx (REG, CCmode, FPSW_REGNUM);
+ else
+ reg = gen_reg_rtx (CCmode);
+
+ /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result ==
+ 0 in the instruction built below. The MIPS FPU handles
+ inequality testing by testing for equality and looking for a
+ false result. */
+ emit_insn (gen_rtx (SET, VOIDmode,
+ reg,
+ gen_rtx (test_code == NE ? EQ : test_code,
+ CCmode, cmp0, cmp1)));
+
+ test_code = test_code == NE ? EQ : NE;
+ mode = CCmode;
+ cmp0 = reg;
+ cmp1 = const0_rtx;
+ invert = FALSE;
break;
}
- /* Generate the jump */
+ /* Generate the branch. */
+
+ label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
+ label2 = pc_rtx;
+
if (invert)
{
label2 = label1;
gen_rtx (test_code, mode, cmp0, cmp1),
label1,
label2)));
-
- return;
-
-fail:
- abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test");
}
-\f
-#define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT)
+/* Emit the common code for conditional moves. OPERANDS is the array
+ of operands passed to the conditional move defined_expand. */
-/* 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
-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 */
- rtx dest_reg; /* register holding dest. memory address */
- int *p_bytes; /* pointer to # bytes remaining */
- int *p_offset; /* pointer to current offset */
- int align; /* alignment */
- rtx orig_src; /* original source for making a reg note */
+void
+gen_conditional_move (operands)
+ rtx *operands;
{
- int bytes; /* # bytes remaining */
- int offset; /* offset to use */
- int size; /* size in bytes of load/store */
- enum machine_mode mode; /* mode to use for load/store */
- rtx reg; /* temporary register */
- rtx src_addr; /* source address */
- rtx dest_addr; /* destination address */
- rtx insn; /* insn of the load */
- rtx orig_src_addr; /* original source address */
- rtx (*load_func)(); /* function to generate load insn */
- rtx (*store_func)(); /* function to generate destination insn */
-
- bytes = *p_bytes;
- if (bytes <= 0 || align <= 0)
- abort ();
-
- if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
- {
- mode = SImode;
- size = UNITS_PER_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)
- {
- mode = SImode;
- size = UNITS_PER_WORD;
- load_func = gen_movsi_ulw;
- store_func = gen_movsi_usw;
- }
-#endif
-
- else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
+ rtx op0 = branch_cmp[0];
+ rtx op1 = branch_cmp[1];
+ enum machine_mode mode = GET_MODE (branch_cmp[0]);
+ enum rtx_code cmp_code = GET_CODE (operands[1]);
+ enum rtx_code move_code = NE;
+ enum machine_mode op_mode = GET_MODE (operands[0]);
+ enum machine_mode cmp_mode;
+ rtx cmp_reg;
+
+ if (GET_MODE_CLASS (mode) != MODE_FLOAT)
{
- mode = HImode;
- size = UNITS_PER_SHORT;
- load_func = gen_movhi;
- store_func = gen_movhi;
+ switch (cmp_code)
+ {
+ case EQ:
+ cmp_code = XOR;
+ move_code = EQ;
+ break;
+ case NE:
+ cmp_code = XOR;
+ break;
+ case LT:
+ break;
+ case GE:
+ cmp_code = LT;
+ move_code = EQ;
+ break;
+ case GT:
+ cmp_code = LT;
+ op0 = force_reg (mode, branch_cmp[1]);
+ op1 = branch_cmp[0];
+ break;
+ case LE:
+ cmp_code = LT;
+ op0 = force_reg (mode, branch_cmp[1]);
+ op1 = branch_cmp[0];
+ move_code = EQ;
+ break;
+ case LTU:
+ break;
+ case GEU:
+ cmp_code = LTU;
+ move_code = EQ;
+ break;
+ case GTU:
+ cmp_code = LTU;
+ op0 = force_reg (mode, branch_cmp[1]);
+ op1 = branch_cmp[0];
+ break;
+ case LEU:
+ cmp_code = LTU;
+ op0 = force_reg (mode, branch_cmp[1]);
+ op1 = branch_cmp[0];
+ move_code = EQ;
+ break;
+ default:
+ abort ();
+ }
}
-
else
{
- mode = QImode;
- size = 1;
- load_func = gen_movqi;
- store_func = gen_movqi;
- }
-
- offset = *p_offset;
- *p_offset = offset + size;
- *p_bytes = bytes - size;
-
- if (offset == 0)
- {
- src_addr = src_reg;
- dest_addr = dest_reg;
+ if (cmp_code == NE)
+ {
+ cmp_code = EQ;
+ move_code = EQ;
+ }
}
+
+ if (mode == SImode || mode == DImode)
+ cmp_mode = mode;
+ else if (mode == SFmode || mode == DFmode)
+ cmp_mode = CCmode;
else
- {
- src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset));
- dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
- }
-
- reg = gen_reg_rtx (mode);
- insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
- orig_src_addr = XEXP (orig_src, 0);
- if (CONSTANT_P (orig_src_addr))
- REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV,
- plus_constant (orig_src_addr, offset),
- REG_NOTES (insn));
-
- return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);
-}
-#endif
-
-\f
-/* Write a series of loads/stores to move some bytes. Generate load/stores as follows:
-
- load 1
- load 2
- load 3
- store 1
- load 4
- store 2
- load 5
- store 3
- ...
-
- This way, no NOP's are needed, except at the end, and only
- two temp registers are needed. Two delay slots are used
- in deference to the R4000. */
-
-#if 0
-static void
-block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
- rtx dest_reg; /* register holding destination address */
- rtx src_reg; /* register holding source address */
- int bytes; /* # bytes to move */
- int align; /* max alignment to assume */
- rtx orig_src; /* original source for making a reg note */
-{
- int offset = 0;
- rtx prev2_store = (rtx)0;
- rtx prev_store = (rtx)0;
- rtx cur_store = (rtx)0;
-
- while (bytes > 0)
- {
- /* Is there a store to do? */
- if (prev2_store)
- emit_insn (prev2_store);
-
- prev2_store = prev_store;
- prev_store = cur_store;
- cur_store = block_move_load_store (dest_reg, src_reg,
- &bytes, &offset,
- align, orig_src);
- }
-
- /* Finish up last three stores. */
- if (prev2_store)
- emit_insn (prev2_store);
-
- if (prev_store)
- emit_insn (prev_store);
+ abort ();
- if (cur_store)
- emit_insn (cur_store);
+ cmp_reg = gen_reg_rtx (cmp_mode);
+ emit_insn (gen_rtx (SET, cmp_mode,
+ cmp_reg,
+ gen_rtx (cmp_code, cmp_mode, op0, op1)));
+ emit_insn (gen_rtx (SET, op_mode,
+ operands[0],
+ gen_rtx (IF_THEN_ELSE, op_mode,
+ gen_rtx (move_code, VOIDmode,
+ cmp_reg,
+ CONST0_RTX (SImode)),
+ operands[2],
+ operands[3])));
}
-#endif
-
\f
/* Write a loop to move a constant number of bytes. Generate load/stores as follows:
#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
static void
-block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
+block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src)
rtx dest_reg; /* register holding destination address */
rtx src_reg; /* register holding source address */
int bytes; /* # bytes to move */
int align; /* alignment */
+ rtx orig_dest; /* original dest for change_address */
rtx orig_src; /* original source for making a reg note */
{
- rtx dest_mem = gen_rtx (MEM, BLKmode, dest_reg);
- rtx src_mem = gen_rtx (MEM, BLKmode, src_reg);
+ rtx dest_mem = change_address (orig_dest, BLKmode, dest_reg);
+ rtx src_mem = change_address (orig_src, BLKmode, src_reg);
rtx align_rtx = GEN_INT (align);
rtx label;
rtx final_src;
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)
GEN_INT (leftover),
align_rtx));
}
-
\f
/* Use a library function to move some bytes. */
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);
+ convert_to_mode (TYPE_MODE (sizetype), bytes_rtx,
+ TREE_UNSIGNED (sizetype)),
+ TYPE_MODE (sizetype));
#else
emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0,
VOIDmode, 3,
src_reg, Pmode,
dest_reg, Pmode,
- bytes_rtx, SImode);
+ convert_to_mode (TYPE_MODE (integer_type_node),
+ bytes_rtx,
+ TREE_UNSIGNED (integer_type_node)),
+ TYPE_MODE (integer_type_node));
#endif
}
int bytes = (constp ? INTVAL (bytes_rtx) : 0);
int align = INTVAL (align_rtx);
rtx orig_src = operands[1];
+ rtx orig_dest = operands[0];
rtx src_reg;
rtx dest_reg;
align = UNITS_PER_WORD;
/* Move the address into scratch registers. */
- dest_reg = copy_addr_to_reg (XEXP (operands[0], 0));
+ dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
if (TARGET_MEMCPY)
block_move_call (dest_reg, src_reg, bytes_rtx);
-#if 0
- else if (constp && bytes <= 3*align)
- block_move_sequence (dest_reg, src_reg, bytes, align, orig_src);
-#endif
-
else if (constp && bytes <= 2*MAX_MOVE_BYTES)
- emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
- gen_rtx (MEM, BLKmode, src_reg),
+ emit_insn (gen_movstrsi_internal (change_address (orig_dest, BLKmode,
+ dest_reg),
+ change_address (orig_src, BLKmode,
+ src_reg),
bytes_rtx, align_rtx));
else if (constp && align >= UNITS_PER_WORD && optimize)
- block_move_loop (dest_reg, src_reg, bytes, align, orig_src);
+ block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src);
else if (constp && optimize)
{
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. */
- block_move_loop (dest_reg, src_reg, bytes, 1, orig_src);
+ block_move_loop (dest_reg, src_reg, bytes, 1, orig_dest, orig_src);
emit_jump_insn (gen_jump (join_label));
emit_barrier ();
/* Aligned loop. */
emit_label (aligned_label);
- block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src);
+ block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_dest,
+ orig_src);
emit_label (join_label);
/* Bytes at the end of the loop. */
if (leftover)
- {
-#if 0
- if (leftover <= 3*align)
- block_move_sequence (dest_reg, src_reg, leftover, align, orig_src);
-
- else
-#endif
- emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
- gen_rtx (MEM, BLKmode, src_reg),
- GEN_INT (leftover),
- GEN_INT (align)));
- }
+ emit_insn (gen_movstrsi_internal (change_address (orig_dest, BLKmode,
+ dest_reg),
+ change_address (orig_src, BLKmode,
+ src_reg),
+ GEN_INT (leftover),
+ GEN_INT (align)));
}
else
{
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);
}
}
{
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);
}
}
}
+ /* ??? We really shouldn't get any LO_SUM addresses here, because they
+ are not offsettable, however, offsettable_address_p says they are
+ offsettable. I think this is a bug in offsettable_address_p.
+ For expediency, we fix this by just loading the address into a register
+ if we happen to get one. */
+
+ if (GET_CODE (src_reg) == LO_SUM)
+ {
+ src_reg = operands[ 3 + num_regs-- ];
+ if (move_type != BLOCK_MOVE_LAST)
+ {
+ xoperands[2] = XEXP (XEXP (operands[1], 0), 1);
+ xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
+ xoperands[0] = src_reg;
+ if (Pmode == DImode)
+ output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
+ else
+ output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
+ }
+ }
+
+ if (GET_CODE (dest_reg) == LO_SUM)
+ {
+ dest_reg = operands[ 3 + num_regs-- ];
+ if (move_type != BLOCK_MOVE_LAST)
+ {
+ xoperands[2] = XEXP (XEXP (operands[0], 0), 1);
+ xoperands[1] = XEXP (XEXP (operands[0], 0), 0);
+ xoperands[0] = dest_reg;
+ if (Pmode == DImode)
+ output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
+ else
+ output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
+ }
+ }
+
if (num_regs > (sizeof (load_store) / sizeof (load_store[0])))
num_regs = (sizeof (load_store) / sizeof (load_store[0]));
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%#";
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
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);
}
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);
}
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 "";
}
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:
- cum->arg_words++;
+ if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
+ cum->fp_arg_words++;
+ else
+ cum->arg_words++;
break;
case DFmode:
- cum->arg_words += 2;
+ if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
+ cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
+ else
+ 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:
}
}
-/* 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)
rtx ret;
int regbase = -1;
int bias = 0;
+ int *arg_words = &cum->arg_words;
int struct_p = ((type != (tree)0)
&& (TREE_CODE (type) == RECORD_TYPE
|| TREE_CODE (type) == UNION_TYPE));
cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
type, named);
+ cum->last_arg_fp = 0;
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 (mips_abi == ABI_32)
+ {
+ 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 if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
+ {
+ if (! TARGET_64BIT)
+ cum->fp_arg_words += cum->fp_arg_words & 1;
+ cum->last_arg_fp = 1;
+ arg_words = &cum->fp_arg_words;
+ regbase = FP_ARG_FIRST;
+ }
+ 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 || cum->arg_number >= 2
- ? GP_ARG_FIRST
- : FP_ARG_FIRST);
+ if (! TARGET_64BIT)
+ {
+ if (mips_abi == ABI_EABI
+ && ! TARGET_SOFT_FLOAT
+ && ! TARGET_SINGLE_FLOAT)
+ cum->fp_arg_words += cum->fp_arg_words & 1;
+ else
+ cum->arg_words += cum->arg_words & 1;
+ }
+ if (mips_abi == ABI_32)
+ regbase = ((cum->gp_reg_found
+ || TARGET_SOFT_FLOAT
+ || TARGET_SINGLE_FLOAT
+ || cum->arg_number >= 2)
+ ? GP_ARG_FIRST
+ : FP_ARG_FIRST);
+ else if (mips_abi == ABI_EABI
+ && ! TARGET_SOFT_FLOAT
+ && ! TARGET_SINGLE_FLOAT)
+ {
+ cum->last_arg_fp = 1;
+ arg_words = &cum->fp_arg_words;
+ regbase = 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 && mips_abi != ABI_EABI)
cum->arg_words += (cum->arg_words & 1);
-
regbase = GP_ARG_FIRST;
break;
break;
case DImode:
- cum->arg_words += (cum->arg_words & 1);
+ if (! TARGET_64BIT)
+ cum->arg_words += (cum->arg_words & 1);
regbase = GP_ARG_FIRST;
}
- if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
+ if (*arg_words >= MAX_ARGS_IN_REGISTERS)
{
if (TARGET_DEBUG_E_MODE)
fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
if (regbase == -1)
abort ();
- ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias);
+ if (! type || TREE_CODE (type) != RECORD_TYPE || mips_abi == ABI_32
+ || mips_abi == ABI_EABI || ! named)
+ ret = gen_rtx (REG, mode, regbase + *arg_words + bias);
+ else
+ {
+ /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
+ structure contains a double in its entirety, then that 64 bit
+ chunk is passed in a floating point register. */
+ tree field;
+
+ /* First check to see if there is any such field. */
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+ && (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
+ % BITS_PER_WORD == 0))
+ break;
+
+ if (! field)
+ ret = gen_rtx (REG, mode, regbase + *arg_words + bias);
+ else
+ {
+ /* Now handle the special case by returning a PARALLEL
+ indicating where each 64 bit chunk goes. */
+ int chunks;
+ int bitpos;
+ int regno;
+ int i;
+
+ /* ??? If this is a packed structure, then the last hunk won't
+ be 64 bits. */
+
+ /* ??? If this is a structure with a single double field,
+ it would be more convenient to return (REG:DI %fX) than
+ a parallel. However, we would have to modify the mips
+ backend to allow DImode values in fp registers. */
+
+ chunks = TREE_INT_CST_LOW (TYPE_SIZE (type)) / BITS_PER_WORD;
+ if (chunks + *arg_words + bias > MAX_ARGS_IN_REGISTERS)
+ chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
+
+ /* assign_parms checks the mode of ENTRY_PARM, so we must
+ use the actual mode here. */
+ ret = gen_rtx (PARALLEL, mode, rtvec_alloc (chunks));
+
+ bitpos = 0;
+ regno = regbase + *arg_words + bias;
+ field = TYPE_FIELDS (type);
+ for (i = 0; i < chunks; i++)
+ {
+ rtx reg;
+
+ for (; field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && (TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field))
+ >= bitpos))
+ break;
+
+ if (field
+ && TREE_INT_CST_LOW (DECL_FIELD_BITPOS (field)) == bitpos
+ && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+ reg = gen_rtx (REG, DFmode,
+ regno + FP_ARG_FIRST - GP_ARG_FIRST);
+ else
+ reg = gen_rtx (REG, word_mode, regno);
+
+ XVECEXP (ret, 0, i) = gen_rtx (EXPR_LIST, VOIDmode, reg,
+ GEN_INT (bitpos / BITS_PER_UNIT));
+
+ bitpos += 64;
+ regno++;
+ }
+ }
+ }
if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias],
+ fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
struct_p ? ", [struct]" : "");
/* The following is a hack in order to pass 1 byte structures
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. */
/* ??? 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. */
- if (struct_p && int_size_in_bytes (type) < 4)
+ /* ??? 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 && mips_abi != ABI_EABI)
{
rtx amount = GEN_INT (BITS_PER_WORD
- int_size_in_bytes (type) * BITS_PER_UNIT);
- rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias);
- cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
+ rtx reg = gen_rtx (REG, word_mode, regbase + *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);
}
}
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)
+ if ((mode == BLKmode
+ || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
+ || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
+ && cum->arg_words < MAX_ARGS_IN_REGISTERS
+ && mips_abi != ABI_EABI)
{
- int words = (int_size_in_bytes (type) + 3) / 4;
+ 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 */
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
+ && mips_abi != ABI_EABI)
{
if (TARGET_DEBUG_E_MODE)
fprintf (stderr, "function_arg_partial_nregs = 1\n");
return 0;
}
-
-\f
-/* Print the options used in the assembly file. */
-
-static struct {char *name; int value;} target_switches []
- = TARGET_SWITCHES;
-
-void
-print_options (out)
- FILE *out;
-{
- int line_len;
- int len;
- int j;
- char **p;
- int mask = TARGET_DEFAULT;
-
- /* Allow assembly language comparisons with -mdebug eliminating the
- compiler version number and switch lists. */
-
- if (TARGET_DEBUG_MODE)
- return;
-
- fprintf (out, "\n # %s %s", language_string, version_string);
-#ifdef TARGET_VERSION_INTERNAL
- TARGET_VERSION_INTERNAL (out);
-#endif
-#ifdef __GNUC__
- fprintf (out, " compiled by GNU C\n\n");
-#else
- fprintf (out, " compiled by CC\n\n");
-#endif
-
- fprintf (out, " # Cc1 defaults:");
- line_len = 32767;
- for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
- {
- if (target_switches[j].name[0] != '\0'
- && target_switches[j].value > 0
- && (target_switches[j].value & mask) == target_switches[j].value)
- {
- mask &= ~ target_switches[j].value;
- len = strlen (target_switches[j].name) + 1;
- if (len + line_len > 79)
- {
- line_len = 2;
- fputs ("\n #", out);
- }
- fprintf (out, " -m%s", target_switches[j].name);
- line_len += len;
- }
- }
-
- fprintf (out, "\n\n # Cc1 arguments (-G value = %d, Cpu = %s, ISA = %d):",
- mips_section_threshold, mips_cpu_string, mips_isa);
-
- line_len = 32767;
- for (p = &save_argv[1]; *p != (char *)0; p++)
- {
- char *arg = *p;
- if (*arg == '-')
- {
- len = strlen (arg) + 1;
- if (len + line_len > 79)
- {
- line_len = 2;
- fputs ("\n #", out);
- }
- fprintf (out, " %s", *p);
- line_len += len;
- }
- }
-
- fputs ("\n\n", out);
-}
-
\f
/* Abort after printing out a specific insn. */
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_ABI_DEFAULT
+ /* Get the ABI to use. Currently this code is only used for Irix 6. */
+ if (mips_abi_string == (char *) 0)
+ mips_abi = MIPS_ABI_DEFAULT;
+ else if (! strcmp (mips_abi_string, "32")
+ || ! strcmp (mips_abi_string, "o32"))
+ mips_abi = ABI_32;
+ else if (! strcmp (mips_abi_string, "n32"))
+ mips_abi = ABI_N32;
+ else if (! strcmp (mips_abi_string, "64")
+ || ! strcmp (mips_abi_string, "n64"))
+ mips_abi = ABI_64;
+ else if (! strcmp (mips_abi_string, "eabi"))
+ mips_abi = ABI_EABI;
+ else
+ error ("bad value (%s) for -mabi= switch", mips_abi_string);
+
+ /* A specified ISA defaults the ABI if it was not specified. */
+ if (mips_abi_string == 0 && mips_isa_string && mips_abi != ABI_EABI)
+ {
+ if (mips_isa <= 2)
+ mips_abi = ABI_32;
+ else
+ mips_abi = ABI_64;
+ }
+ /* A specified ABI defaults the ISA if it was not specified. */
+ else if (mips_isa_string == 0 && mips_abi_string && mips_abi != ABI_EABI)
+ {
+ if (mips_abi == ABI_32)
+ mips_isa = 1;
+ else if (mips_abi == ABI_N32)
+ mips_isa = 3;
+ else
+ mips_isa = 4;
+ }
+ /* If both ABI and ISA were specified, check for conflicts. */
+ else if (mips_isa_string && mips_abi_string)
+ {
+ if ((mips_isa <= 2 && (mips_abi == ABI_N32 || mips_abi == ABI_64))
+ || (mips_isa >= 3 && mips_abi == ABI_32))
+ error ("-mabi=%s does not support -mips%d", mips_abi_string, mips_isa);
+ }
+
+ /* Override TARGET_DEFAULT if necessary. */
+ if (mips_abi == ABI_32)
+ target_flags &= ~ (MASK_FLOAT64|MASK_64BIT);
+
+ /* In the EABI in 64 bit mode, longs and pointers are 64 bits. */
+ if (mips_abi == ABI_EABI && TARGET_64BIT)
+ target_flags |= MASK_LONG64;
+
+ /* ??? This doesn't work yet, so don't let people try to use it. */
+ if (mips_abi == ABI_32)
+ error ("The -mabi=32 support does not work yet.");
+#else
+ if (mips_abi_string)
+ error ("This target does not support the -mabi switch.");
+#endif
+
+#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
{
char *p = mips_cpu_string;
+ int seen_v = FALSE;
+ /* We need to cope with the various "vr" prefixes for the NEC 4300
+ and 4100 processors. */
+ if (*p == 'v' || *p == 'V')
+ {
+ seen_v = TRUE;
+ p++;
+ }
if (*p == 'r' || *p == 'R')
p++;
case '4':
if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
mips_cpu = PROCESSOR_R4000;
+ /* The vr4100 is a non-FP ISA III processor with some extra
+ instructions. */
+ else if (!strcmp (p, "4100")) {
+ mips_cpu = PROCESSOR_R4100;
+ target_flags |= MASK_SOFT_FLOAT ;
+ }
+ /* The vr4300 is a standard ISA III processor, but with a different
+ pipeline. */
+ else if (!strcmp (p, "4300"))
+ mips_cpu = PROCESSOR_R4300;
+ /* 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 '5':
+ if (!strcmp (p, "5000") || !strcmp (p, "5k") || !strcmp (p, "5K"))
+ mips_cpu = PROCESSOR_R5000;
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 (seen_v
+ && mips_cpu != PROCESSOR_R4300
+ && mips_cpu != PROCESSOR_R4100
+ && mips_cpu != PROCESSOR_R5000)
+ mips_cpu = PROCESSOR_DEFAULT;
+
if (mips_cpu == PROCESSOR_DEFAULT)
{
error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
}
}
- /* 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_R4100
+ || mips_cpu == PROCESSOR_R4300
+ || 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 (mips_abi != ABI_32)
+ 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;
+ {
+ 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;
+ }
+
+ /* This optimization requires a linker that can support a R_MIPS_LO16
+ relocation which is not immediately preceeded by a R_MIPS_HI16 relocation.
+ GNU ld has this support, but not all other MIPS linkers do, so we enable
+ this optimization only if the user requests it, or if GNU ld is the
+ standard linker for this configuration. */
+ /* ??? This does not work when target addresses are DImode.
+ This is because we are missing DImode high/lo_sum patterns. */
+ if (TARGET_GAS && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic
+ && Pmode == SImode)
+ mips_split_addresses = 1;
+ else
+ mips_split_addresses = 0;
+
/* -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. */
#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;
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;
{
register int temp;
- if (mode == CC_FPmode || mode == CC_REV_FPmode)
- temp = (regno == FPSW_REGNUM);
+ if (mode == CCmode)
+ {
+ if (mips_isa < 4)
+ temp = (regno == FPSW_REGNUM);
+ else
+ temp = (ST_REG_P (regno)
+ || GP_REG_P (regno)
+ || FP_REG_P (regno));
+ }
else if (GP_REG_P (regno))
temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
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;
'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
+ 'Z' print register and a comma, but print nothing for $fcc0
'(' Turn on .set noreorder
')' Turn on .set reorder
'[' Turn on .set noat
if (set_noreorder != 0)
fputs ("\n\tnop", file);
- else if (TARGET_GAS || TARGET_STATS)
+ else if (TARGET_STATS)
fputs ("\n\t#nop", file);
break;
}
code = GET_CODE (op);
+
+ if (code == SIGN_EXTEND)
+ {
+ op = XEXP (op, 0);
+ code = GET_CODE (op);
+ }
+
if (letter == 'C')
switch (code)
{
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')
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')
+ {
+ char buffer[100];
+
+ ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
+ assemble_name (file, buffer);
+ }
+
+ else if (letter == 'Z')
{
- register int regnum = REGNO (op);
+ register int regnum;
+
+ if (code != REG)
+ abort ();
+ regnum = REGNO (op);
+ if (! ST_REG_P (regnum))
+ abort ();
+ if (regnum != ST_REG_FIRST)
+ fprintf (file, "%s,", reg_names[regnum]);
+ }
- if (letter == 'M')
- regnum += MOST_SIGNIFICANT_WORD;
+ else if (code == REG || code == SUBREG)
+ {
+ register int regnum;
- else if (letter == 'L')
- regnum += LEAST_SIGNIFICANT_WORD;
+ if (code == REG)
+ regnum = REGNO (op);
+ else
+ regnum = true_regnum (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]);
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)));
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);
}
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:
fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
break;
+ case LO_SUM:
+ {
+ register rtx arg0 = XEXP (addr, 0);
+ register rtx arg1 = XEXP (addr, 1);
+
+ if (! mips_split_addresses)
+ abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM.");
+
+ if (GET_CODE (arg0) != REG)
+ abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
+
+ fprintf (file, "%%lo(");
+ print_operand_address (file, arg1);
+ fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
+ }
+ break;
+
case PLUS:
{
register rtx reg = (rtx)0;
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.");
+ if (REGNO (reg) == ARG_POINTER_REGNUM)
+ abort_with_insn (addr, "Arg pointer not eliminated.");
output_addr_const (file, offset);
fprintf (file, "(%s)", reg_names [REGNO (reg)]);
int len;
if (TARGET_GP_OPT
- && mips_section_threshold != 0
&& ((TREE_CODE (decl)) != FUNCTION_DECL)
&& ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0))
{
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
\f
/* 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 ()
{
}
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;
}
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");
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");
}
}
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
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;
|| (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)
/* ??? 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 (mips_abi != ABI_32 && mips_abi != ABI_EABI)
+ 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;
else
asm_out_data_file = asm_out_text_file = stream;
- if (TARGET_NAME_REGS)
- fprintf (asm_out_file, "#include <regdef.h>\n");
-
- print_options (stream);
+ if (flag_verbose_asm)
+ fprintf (stream, "\n%s -G value = %d, Cpu = %s, ISA = %d\n",
+ ASM_COMMENT_START,
+ mips_section_threshold, mips_cpu_string, mips_isa);
}
\f
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)
{
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))
if (fclose (asm_out_text_file) != 0)
pfatal_with_name (temp_filename);
+
+#ifdef __MSDOS__
+ unlink (temp_filename);
+#endif
}
}
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;
return 0;
}
-
\f
/* Return the bytes needed to compute the frame pointer from the current
stack pointer.
}
/* Calculate space needed for fp registers. */
- if (TARGET_FLOAT64)
+ if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)
{
fp_inc = 1;
fp_bits = 1;
{
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 && (mips_abi == ABI_32 || mips_abi == ABI_EABI))
total_size = extra_size = 0;
else if (TARGET_ABICALLS)
{
total_size += gp_reg_rounded;
}
+ /* Add in space reserved on the stack by the callee for storing arguments
+ passed in registers. */
+ if (mips_abi != ABI_32)
+ 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;
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 + 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;
}
if (fmask)
{
- unsigned long offset = args_size + extra_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;
+ current_frame_info.fp_save_offset = offset - total_size;
}
else
{
long gp_offset;
long fp_offset;
long end_offset;
+ rtx insn;
if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST))
abort ();
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)
+ insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+ else
+ insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
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]);
base_offset = gp_offset;
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));
+ insn = emit_move_insn (base_reg_rtx, GEN_INT (gp_offset));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ if (TARGET_LONG64)
+ insn = emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+ else
+ insn = emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+ RTX_FRAME_RELATED_P (insn) = 1;
}
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]);
{
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 if (!TARGET_ABICALLS
+ {
+ insn = emit_move_insn (mem_rtx, reg_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else if (!TARGET_ABICALLS || mips_abi != ABI_32
|| 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 || mips_abi != ABI_32
|| regno != (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
fprintf (file, "\t%s\t%s,%ld(%s)\n",
- (store_p) ? "sw" : "lw",
+ (TARGET_64BIT
+ ? (store_p) ? "sd" : "ld"
+ : (store_p) ? "sw" : "lw"),
reg_names[regno],
gp_offset - base_offset,
reg_names[REGNO(base_reg_rtx)]);
}
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.",
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)
+ insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+ else
+ insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
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]);
base_offset = fp_offset;
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));
+ insn = emit_move_insn (base_reg_rtx, GEN_INT (fp_offset));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ if (TARGET_LONG64)
+ insn = emit_insn (gen_adddi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+ else
+ insn = emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
+ RTX_FRAME_RELATED_P (insn) = 1;
}
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]);
{
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)));
if (store_p)
- emit_move_insn (mem_rtx, reg_rtx);
+ {
+ insn = emit_move_insn (mem_rtx, reg_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
else
emit_move_insn (reg_rtx, mem_rtx);
}
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;
}
}
}
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
inside_function = 1;
+
+#ifndef FUNCTION_NAME_ALREADY_DECLARED
+ /* 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.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);
+#endif
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 ],
current_frame_info.fmask,
current_frame_info.fp_save_offset);
- if (TARGET_ABICALLS)
+ if (TARGET_ABICALLS && mips_abi == ABI_32)
{
char *sp_str = reg_names[STACK_POINTER_REGNUM];
reg_names[PIC_FUNCTION_ADDR_REGNUM]);
if (tsize > 0)
{
- fprintf (file, "\tsubu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+ 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);
}
}
char *arg_name = (char *)0;
tree fndecl = current_function_decl;
tree fntype = TREE_TYPE (fndecl);
- tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
- ? DECL_ARGUMENTS (fndecl)
- : 0;
+ tree fnargs = DECL_ARGUMENTS (fndecl);
rtx next_arg_reg;
int i;
tree next_arg;
/* Determine the last argument, and get its name. */
- INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+ INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0, 0);
regno = GP_ARG_FIRST;
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 (TREE_ADDRESSABLE (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)
{
/* 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;
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)
/* 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 (mips_abi == ABI_32
+ && ((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 (offset != 0)
ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset));
- emit_move_insn (gen_rtx (MEM, Pmode, ptr),
- gen_rtx (REG, Pmode, regno));
+ emit_move_insn (gen_rtx (MEM, word_mode, ptr),
+ gen_rtx (REG, word_mode, regno));
offset += UNITS_PER_WORD;
}
}
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 || mips_abi != ABI_32)
{
+ rtx insn;
+
if (tsize > 32767)
{
- tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
- emit_move_insn (tmp_rtx, tsize_rtx);
+ tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
+ insn = emit_move_insn (tmp_rtx, tsize_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
tsize_rtx = tmp_rtx;
}
- emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
- tsize_rtx));
+ if (TARGET_LONG64)
+ insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
+ tsize_rtx));
+ else
+ insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
+ tsize_rtx));
+
+ RTX_FRAME_RELATED_P (insn) = 1;
}
save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
if (frame_pointer_needed)
- emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+ {
+ rtx insn;
+
+ if (TARGET_64BIT)
+ insn= emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
+ else
+ insn= emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+
+ if (TARGET_ABICALLS && mips_abi != ABI_32)
+ emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0)));
}
/* If we are profiling, make sure no instructions are scheduled before
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;
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)
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");
}
save_restore_insns (FALSE, tmp_rtx, tsize, file);
load_only_r31 = (((current_frame_info.mask
- & ~ (TARGET_ABICALLS ? PIC_OFFSET_TABLE_MASK : 0))
+ & ~ (TARGET_ABICALLS && mips_abi == ABI_32
+ ? PIC_OFFSET_TABLE_MASK : 0))
== RA_MASK)
&& current_frame_info.fmask == 0);
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),
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]);
}
}
+#ifndef FUNCTION_NAME_ALREADY_DECLARED
+ /* 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);
+#endif
if (TARGET_STATS)
{
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++;
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;
}
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)));
return (compute_frame_size (get_frame_size ())) == 0;
}
+\f
+/* 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 ();
+ }
+}
+\f
+#ifdef MIPS_ABI_DEFAULT
+/* Support functions for the 64 bit ABI. */
+
+/* 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);
+
+ /* ??? How should we return complex float? */
+ if (mclass == MODE_FLOAT || mclass == MODE_COMPLEX_FLOAT)
+ {
+ if (TARGET_SINGLE_FLOAT
+ && (mclass == MODE_FLOAT
+ ? GET_MODE_SIZE (mode) > 4
+ : GET_MODE_SIZE (mode) / 2 > 4))
+ reg = GP_RETURN;
+ else
+ reg = FP_RETURN;
+ }
+ else if (TREE_CODE (valtype) == RECORD_TYPE
+ && mips_abi != ABI_32 && mips_abi != ABI_EABI)
+ {
+ /* A struct with only one or two floating point fields is returned in
+ the floating point registers. */
+ tree field, fields[2];
+ int i;
+
+ for (i = 0, field = TYPE_FIELDS (valtype); field;
+ field = TREE_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+ if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2)
+ break;
+
+ fields[i++] = field;
+ }
+
+ /* Must check i, so that we reject structures with no elements. */
+ if (! field)
+ {
+ if (i == 1)
+ {
+ /* The structure has DImode, but we don't allow DImode values
+ in FP registers, so we use a PARALLEL even though it isn't
+ strictly necessary. */
+ enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0]));
+
+ return gen_rtx (PARALLEL, mode,
+ gen_rtvec (1,
+ gen_rtx (EXPR_LIST, VOIDmode,
+ gen_rtx (REG, field_mode, FP_RETURN),
+ const0_rtx)));
+ }
+ else if (i == 2)
+ {
+ enum machine_mode first_mode
+ = TYPE_MODE (TREE_TYPE (fields[0]));
+ enum machine_mode second_mode
+ = TYPE_MODE (TREE_TYPE (fields[1]));
+ int first_offset
+ = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (fields[0]));
+ int second_offset
+ = TREE_INT_CST_LOW (DECL_FIELD_BITPOS (fields[1]));
+
+ return gen_rtx (PARALLEL, mode,
+ gen_rtvec (2,
+ gen_rtx (EXPR_LIST, VOIDmode,
+ gen_rtx (REG, first_mode, FP_RETURN),
+ GEN_INT (first_offset / BITS_PER_UNIT)),
+ gen_rtx (EXPR_LIST, VOIDmode,
+ gen_rtx (REG, second_mode, FP_RETURN + 2),
+ GEN_INT (second_offset / BITS_PER_UNIT))));
+ }
+ }
+ }
+
+ return gen_rtx (REG, mode, reg);
+}
+
+/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE. Return
+ nonzero when an argument must be passed by reference. */
+
+int
+function_arg_pass_by_reference (cum, mode, type, named)
+ CUMULATIVE_ARGS *cum;
+ enum machine_mode mode;
+ tree type;
+ int named;
+{
+ int size;
+
+ if (mips_abi != ABI_EABI)
+ return 0;
+
+ /* ??? How should SCmode be handled? */
+ if (type == NULL_TREE || mode == DImode || mode == DFmode)
+ return 0;
+
+ size = int_size_in_bytes (type);
+ return size == -1 || size > UNITS_PER_WORD;
+}
+
+#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) == SIGN_EXTEND)
+ {
+ int off = 0;
+
+ x = XEXP (x, 0);
+
+ /* We may be called with reg_renumber NULL from regclass.
+ ??? This is probably a bug. */
+ if (reg_renumber)
+ regno = true_regnum (x);
+ else
+ {
+ while (GET_CODE (x) == SUBREG)
+ {
+ off += SUBREG_WORD (x);
+ x = SUBREG_REG (x);
+ }
+ if (GET_CODE (x) == REG)
+ regno = REGNO (x) + off;
+ }
+ }
+ else 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;
+ }
+
+ /* We can only copy a value to a condition code register from a
+ floating point register, and even then we require a scratch
+ floating point register. We can only copy a value out of a
+ condition code register into a general register. */
+ if (class == ST_REGS)
+ {
+ if (in_p)
+ return FP_REGS;
+ if (GP_REG_P (regno))
+ return NO_REGS;
+ return GR_REGS;
+ }
+ if (ST_REG_P (regno))
+ {
+ if (! in_p)
+ return FP_REGS;
+ if (class == GR_REGS)
+ return NO_REGS;
+ return GR_REGS;
+ }
+
+ return NO_REGS;
+}