/* 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.
+ Changes by Michael Meissner, meissner@osf.org.
64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
Brendan Eich, brendan@microunity.com.
- Copyright (C) 1989, 1990, 1991, 1993 Free Software Foundation, Inc.
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
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;
"$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
"$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 & 0x0000ffff) == 0 /* lui reg,value>>16 */
- && ((value & ~2147483647) == 0 /* signed value */
- || (value & ~2147483647) == ~2147483647))
+ if ((value & 0x0000ffff) == 0) /* lui reg,value>>16 */
return FALSE;
return TRUE;
if (GET_CODE (op) != CONST_DOUBLE)
return FALSE;
- if (mode == DImode)
+ if (mode == VOIDmode)
return TRUE;
if (mode != SFmode && mode != DFmode)
if (op == CONST0_RTX (mode))
return TRUE;
+ /* ??? li.s does not work right with SGI's Irix 6 assembler. */
+ if (mips_abi != ABI_32 && mips_abi != ABI_EABI)
+ return FALSE;
+
REAL_VALUE_FROM_CONST_DOUBLE (d, op);
if (REAL_VALUE_ISNAN (d))
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. */
+ /* ??? 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;
break;
case REG:
+ case LO_SUM:
return TRUE;
case CONST_INT:
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 (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)));
+}
+
+/* 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
+movdi_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
+ || (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 general_operand (op, mode);
+}
+
+/* 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
+se_register_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 register_operand (op, mode);
+}
+
+/* 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
+se_reg_or_0_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 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. */
+
+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
+se_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 arith_operand (op, mode);
+}
+
+/* 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);
+}
+
+/* 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
+se_nonimmediate_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 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
/* Returns an operand string for the given instruction's delay slot,
after updating filled delay slot statistics.
dslots_number_nops = num_nops;
mips_load_reg = set_reg;
if (GET_MODE_SIZE (mode)
- > (FP_REG_P (set_reg) ? UNITS_PER_FPREG : UNITS_PER_WORD))
+ > (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD))
mips_load_reg2 = gen_rtx (REG, SImode, REGNO (set_reg) + 1);
else
mips_load_reg2 = 0;
case REG:
case CONST_INT:
+ case LO_SUM:
break;
case PLUS:
rtx x;
{
if (embedded_pic_fnaddr_rtx == NULL)
- embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
+ {
+ rtx seq;
+
+ embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
+
+ /* Output code at function start to initialize the pseudo-reg. */
+ /* ??? We used to do this in FINALIZE_PIC, but that does not work for
+ inline functions, because it is called after RTL for the function
+ has been copied. The pseudo-reg in embedded_pic_fnaddr_rtx however
+ does not get copied, and ends up not matching the rest of the RTL.
+ This solution works, but means that we get unnecessary code to
+ initialize this value every time a function is inlined into another
+ function. */
+ start_sequence ();
+ emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx,
+ XEXP (DECL_RTL (current_function_decl), 0)));
+ seq = gen_sequence ();
+ end_sequence ();
+ push_topmost_sequence ();
+ emit_insn_after (seq, get_insns ());
+ pop_topmost_sequence ();
+ }
+
return gen_rtx (CONST, Pmode,
gen_rtx (MINUS, Pmode, x,
XEXP (DECL_RTL (current_function_decl), 0)));
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))
{
ret = "lw\t%0,%1";
break;
case SImode:
+ case CCmode:
ret = ((unsignedp && TARGET_64BIT)
? "lwu\t%0,%1"
: "lw\t%0,%1");
}
}
- 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)
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)
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)
+ abort ();
+
if (code0 == REG)
{
int regno0 = REGNO (op0) + subreg_word0;
{
delay = DELAY_HILO;
if (TARGET_64BIT)
- ret = "mt%0\t%1";
+ {
+ if (regno0 != HILO_REGNUM)
+ ret = "mt%0\t%1";
+ else if (regno1 == 0)
+ ret = "mtlo\t%.\n\tmthi\t%.";
+ }
else
ret = "mthi\t%M1\n\tmtlo\t%L1";
}
{
delay = DELAY_HILO;
if (TARGET_64BIT)
- ret = "mf%1\t%0";
+ {
+ if (regno1 != HILO_REGNUM)
+ ret = "mf%1\t%0";
+ }
else
ret = "mfhi\t%M0\n\tmflo\t%L0";
}
{
if (TARGET_FLOAT64 && !TARGET_64BIT)
{
- operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
- operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
- ret = "li\t%M0,%3\n\tli\t%L0,%2";
+ split_double (op1, operands + 2, operands + 3);
+ ret = "li\t%0,%2\n\tli\t%D0,%3";
}
else
ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
ret = "li.d\t%0,%1";
}
- else if (TARGET_FLOAT64)
- ret = "li\t%0,%1";
+ else if (TARGET_64BIT)
+ ret = "dli\t%0,%1";
else
{
- operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
- operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
- ret = "li\t%M0,%3\n\tli\t%L0,%2";
+ split_double (op1, operands + 2, operands + 3);
+ ret = "li\t%0,%2\n\tli\t%D0,%3";
}
}
? "li.d\t%0,%1"
: "mtc1\t%.,%0\n\tmtc1\t%.,%D0");
}
+ else if (MD_REG_P (regno0))
+ {
+ delay = DELAY_HILO;
+ if (regno0 != HILO_REGNUM)
+ ret = "mt%0\t%.\n";
+ else
+ ret = "mtlo\t%.\n\tmthi\t%.";
+ }
}
else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0))
{
if (TARGET_64BIT)
- ret = "li\t%0,%1";
- else
+ {
+ 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)
|| code1 == SYMBOL_REF
|| code1 == CONST)
{
- if (! TARGET_64BIT)
- abort ();
- return mips_move_1word (operands, insn, 0);
+ if (TARGET_STATS)
+ mips_count_memory_refs (op1, 2);
+
+ ret = "dla\t%0,%a1";
}
}
break;
case LO_SUM:
- case HIGH:
return 1;
case LABEL_REF:
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))
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_DI */
- DImode, /* eq */
- DImode, /* ne */
- DImode, /* gt */
- DImode, /* ge */
- DImode, /* lt */
- DImode, /* le */
- DImode, /* gtu */
- DImode, /* geu */
- DImode, /* ltu */
- DImode, /* leu */
- },
- { /* CMP_SF */
- CC_FPmode, /* eq */
- CC_REV_FPmode, /* ne */
- 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 = word_mode;
- 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 (type)
{
default:
- goto fail;
+ abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
case CMP_SI:
case CMP_DI:
- reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
- if (reg != (rtx)0)
+ 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 (mode, 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
-#if 0
-/* Internal code to generate the load and store of one word/short/byte.
- The load is emitted directly, and the store insn is returned. */
-
-#define UNITS_PER_MIPS_DWORD 8
-#define UNITS_PER_MIPS_WORD 4
-#define UNITS_PER_MIPS_HWORD 2
-
-static rtx
-block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src)
- rtx src_reg; /* register holding source memory address */
- 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 */
-{
- 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 ();
+/* Emit the common code for conditional moves. OPERANDS is the array
+ of operands passed to the conditional move defined_expand. */
- if (bytes >= UNITS_PER_MIPS_DWORD && align >= UNIS_PER_MIPS_DWORD)
- {
- mode = DImode;
- size = UNITS_PER_MIPS_DWORD;
- load_func = gen_movdi;
- store_func = gen_movdi;
- }
- else if (bytes >= UNITS_PER_MIPS_WORD && align >= UNITS_PER_MIPS_WORD)
- {
- mode = SImode;
- size = UNITS_PER_MIPS_WORD;
- load_func = gen_movsi;
- store_func = gen_movsi;
- }
-
-#if 0
- /* Don't generate unaligned moves here, rather defer those to the
- general movestrsi_internal pattern.
- If this gets commented back in, then should add the dword equivalent. */
- else if (bytes >= UNITS_PER_MIPS_WORD)
- {
- mode = SImode;
- size = UNITS_PER_MIPS_WORD;
- load_func = gen_movsi_ulw;
- store_func = gen_movsi_usw;
- }
-#endif
-
- else if (bytes >= UNITS_PER_MIPS_SHORT && align >= UNITS_PER_MIPS_SHORT)
+void
+gen_conditional_move (operands)
+ rtx *operands;
+{
+ 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_MIPS_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_REGS 4
#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
-/* ??? Should add code to use DWORD load/stores. */
-
static void
-block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
+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;
GEN_INT (leftover),
align_rtx));
}
-
\f
/* Use a library function to move some bytes. */
VOIDmode, 3,
dest_reg, Pmode,
src_reg, Pmode,
- bytes_rtx, Pmode);
+ 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, Pmode);
+ 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)
{
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]));
/* ??? Fails because of a MIPS assembler bug? */
else if (TARGET_64BIT && bytes >= 8)
{
-#if BYTES_BIG_ENDIAN
- load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2";
- load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#";
- load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2";
- load_store[num].last_store = "sdr\t%0,%2";
- load_store[num].final = "sdl\t%0,%1";
-#else
- load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1";
- load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#";
- load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1";
- load_store[num].last_store = "sdr\t%0,%1";
- load_store[num].final = "sdl\t%0,%2";
-#endif
+ if (BYTES_BIG_ENDIAN)
+ {
+ load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2";
+ load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#";
+ load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2";
+ load_store[num].last_store = "sdr\t%0,%2";
+ load_store[num].final = "sdl\t%0,%1";
+ }
+ else
+ {
+ load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1";
+ load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#";
+ load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1";
+ load_store[num].last_store = "sdr\t%0,%1";
+ load_store[num].final = "sdl\t%0,%2";
+ }
load_store[num].mode = DImode;
offset += 8;
bytes -= 8;
else if (bytes >= 4)
{
-#if BYTES_BIG_ENDIAN
- load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
- load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
- load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
- load_store[num].last_store = "swr\t%0,%2";
- load_store[num].final = "swl\t%0,%1";
-#else
- load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
- load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
- load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
- load_store[num].last_store = "swr\t%0,%1";
- load_store[num].final = "swl\t%0,%2";
-#endif
+ if (BYTES_BIG_ENDIAN)
+ {
+ load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
+ load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
+ load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
+ load_store[num].last_store = "swr\t%0,%2";
+ load_store[num].final = "swl\t%0,%1";
+ }
+ else
+ {
+ load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
+ load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
+ load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
+ load_store[num].last_store = "swr\t%0,%1";
+ load_store[num].final = "swl\t%0,%2";
+ }
load_store[num].mode = SImode;
offset += 4;
bytes -= 4;
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 += (TARGET_64BIT ? 1 : 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:
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)
{
case SFmode:
- if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
- regbase = GP_ARG_FIRST;
- else
+ 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;
- /* If the first arg was a float in a floating point register,
- then set bias to align this float arg properly. */
- if (cum->arg_words == 1)
- bias = 1;
}
-
+ else
+ regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
break;
case DFmode:
if (! TARGET_64BIT)
- cum->arg_words += (cum->arg_words & 1);
- regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT || cum->arg_number >= 2
- ? GP_ARG_FIRST
- : FP_ARG_FIRST);
+ {
+ if (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:
/* Drops through. */
case BLKmode:
if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD
- && ! TARGET_64BIT)
+ && ! TARGET_64BIT && mips_abi != ABI_EABI)
cum->arg_words += (cum->arg_words & 1);
-
regbase = GP_ARG_FIRST;
break;
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
calling convention for now. */
if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
- && ! TARGET_64BIT)
+ && ! 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, word_mode, regbase + cum->arg_words + bias);
+ 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
if ((mode == BLKmode
|| GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
|| GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
- && cum->arg_words < MAX_ARGS_IN_REGISTERS)
+ && cum->arg_words < MAX_ARGS_IN_REGISTERS
+ && mips_abi != ABI_EABI)
{
int words;
if (mode == BLKmode)
}
else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1
- && ! TARGET_64BIT)
+ && ! 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. */
else if (isdigit (*mips_isa_string))
{
mips_isa = atoi (mips_isa_string);
- if (mips_isa < 1 || mips_isa > 3)
+ if (mips_isa < 1 || mips_isa > 4)
{
error ("-mips%d not supported", mips_isa);
mips_isa = 1;
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")
mips_cpu_string = "4000";
mips_cpu = PROCESSOR_R4000;
break;
+ case 4:
+ mips_cpu_string = "8000";
+ mips_cpu = PROCESSOR_R8000;
+ break;
}
-
-#ifdef MIPS_CPU_DEFAULT
- if (mips_isa_string == (char *)0)
- {
- mips_cpu_string = MIPS_CPU_STRING_DEFAULT;
- mips_cpu = MIPS_CPU_DEFAULT;
- }
-#endif
}
else
{
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':
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);
}
if ((mips_cpu == PROCESSOR_R3000 && mips_isa > 1)
- || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2))
+ || (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)
+ || ((mips_cpu == PROCESSOR_R4000
+ || mips_cpu == PROCESSOR_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 MIPS-III CPUs can support 64 bit ints");
+ fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit ints");
else if (TARGET_LONG64)
- fatal ("Only MIPS-III CPUs can support 64 bit longs");
+ fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit longs");
else if (TARGET_FLOAT64)
- fatal ("Only MIPS-III CPUs can support 64 bit fp registers");
+ fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit fp registers");
else if (TARGET_64BIT)
- fatal ("Only MIPS-III CPUs can support 64 bit gp registers");
+ fatal ("Only MIPS-III or MIPS-IV CPUs can support 64 bit gp registers");
}
+ if (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 ();
mips_section_threshold = 0x7fffffff;
}
+ /* ??? This does not work when target addresses are DImode.
+ This is because we are missing DImode high/lo_sum patterns. */
+
+ if (TARGET_GAS && 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).
We do this by switching the names in mips_reg_names, which the
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))
- {
- if (TARGET_64BIT)
- temp = (mode == DImode
- || (regno == MD_REG_FIRST && mode == TImode));
- else
- temp = (mode == SImode
- || (regno == MD_REG_FIRST && mode == DImode));
- }
+ temp = (class == MODE_INT
+ && (size <= UNITS_PER_WORD
+ || (regno == MD_REG_FIRST && size == 2 * UNITS_PER_WORD)));
else
temp = FALSE;
'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
}
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 (letter == 'S')
assemble_name (file, buffer);
}
- else if (code == REG)
+ 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)
{
REAL_VALUE_TYPE d;
char s[30];
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)]);
}
#ifdef ASM_OUTPUT_UNDEF_FUNCTION
- if (TREE_CODE (decl) == FUNCTION_DECL)
+ 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;
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
/* ??? 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. */
else
asm_out_data_file = asm_out_text_file = stream;
- 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
return 0;
}
-
-\f
-/* When generating embedded PIC code we may need to get the address of
- the current function. We will need it if we take the address of
- any symbol in the .text section. */
-
-void
-mips_finalize_pic ()
-{
- rtx seq;
-
- if (! TARGET_EMBEDDED_PIC)
- return;
- if (embedded_pic_fnaddr_rtx == NULL)
- return;
-
- start_sequence ();
-
- emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx,
- XEXP (DECL_RTL (current_function_decl), 0)));
-
- seq = gen_sequence ();
- end_sequence ();
- emit_insn_after (seq, get_insns ());
-
- embedded_pic_fnaddr_rtx = NULL;
-}
-
\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;
}
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;
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;
}
+ 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
{
GEN_INT (gp_offset - base_offset)));
if (store_p)
- emit_move_insn (mem_rtx, reg_rtx);
- else if (!TARGET_ABICALLS
+ {
+ rtx 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",
(TARGET_64BIT
}
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. */
{
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);
+ {
+ 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)]);
FILE *file;
int size;
{
+ char *fnname;
long tsize = current_frame_info.total_size;
ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
+#ifdef SDB_DEBUGGING_INFO
if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+#endif
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];
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)
enum machine_mode passed_mode = TYPE_MODE (passed_type);
rtx entry_parm;
- if (TYPE_NEEDS_CONSTRUCTING (passed_type))
+ if (TREE_ADDRESSABLE (passed_type))
{
passed_type = build_pointer_type (passed_type);
passed_mode = Pmode;
/* 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;
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, Pmode, MIPS_TEMP1_REGNUM);
}
if (TARGET_LONG64)
- emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
- tsize_rtx));
+ insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
+ tsize_rtx));
else
- emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
- tsize_rtx));
+ 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)
{
+ rtx insn;
+
if (TARGET_64BIT)
- emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
+ insn= emit_insn (gen_movdi (frame_pointer_rtx, stack_pointer_rtx));
else
- emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+ 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];
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);
}
}
+#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++;
{
/* For embedded applications, always put constants in read-only data,
in order to reduce RAM usage. */
- rdata_section ();
+ READONLY_DATA_SECTION ();
}
else
{
if (GET_MODE_SIZE (mode) <= mips_section_threshold
&& mips_section_threshold > 0)
- sdata_section ();
+ SMALL_DATA_SECTION ();
else
- rdata_section ();
+ READONLY_DATA_SECTION ();
}
}
/* For embedded applications, always put an object in read-only data
if possible, in order to reduce RAM usage. */
- if (((TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
+ if (((TREE_CODE (decl) == VAR_DECL
+ && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
&& DECL_INITIAL (decl)
&& (DECL_INITIAL (decl) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (decl))))
&& (TREE_CODE (decl) != STRING_CST
|| !flag_writable_strings)))
&& ! (flag_pic && reloc))
- rdata_section ();
+ READONLY_DATA_SECTION ();
else if (size > 0 && size <= mips_section_threshold)
- sdata_section ();
+ SMALL_DATA_SECTION ();
else
data_section ();
}
possible, as this gives the best performance. */
if (size > 0 && size <= mips_section_threshold)
- sdata_section ();
- else if (((TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
+ SMALL_DATA_SECTION ();
+ else if (((TREE_CODE (decl) == VAR_DECL
+ && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
&& DECL_INITIAL (decl)
&& (DECL_INITIAL (decl) == error_mark_node
|| TREE_CONSTANT (DECL_INITIAL (decl))))
&& (TREE_CODE (decl) != STRING_CST
|| !flag_writable_strings)))
&& ! (flag_pic && reloc))
- rdata_section ();
+ 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;
+}