Commit 5f5fe6d9 by Richard Sandiford Committed by Richard Sandiford

mips.c (mips16_copy_fpr_return_value): New function, split out from...

gcc/
	* config/mips/mips.c (mips16_copy_fpr_return_value): New function,
	split out from...
	(mips_expand_epilogue): ...here.
	(mips_mdebug_abi_name): New function, split out from...
	(mips_file_start): ...here.
	(mips_hard_regno_mode_ok_p): New function, split out from...
	(override_options): ...here.
	(mips_expand_builtin_1): New function, split out from...
	(mips_expand_buitin): ...here.  Abort if we don't recognize
	the function.

From-SVN: r129477
parent 6a68a258
2007-10-19 Richard Sandiford <rsandifo@nildram.co.uk> 2007-10-19 Richard Sandiford <rsandifo@nildram.co.uk>
* config/mips/mips.c (mips16_copy_fpr_return_value): New function,
split out from...
(mips_expand_epilogue): ...here.
(mips_mdebug_abi_name): New function, split out from...
(mips_file_start): ...here.
(mips_hard_regno_mode_ok_p): New function, split out from...
(override_options): ...here.
(mips_expand_builtin_1): New function, split out from...
(mips_expand_buitin): ...here. Abort if we don't recognize
the function.
2007-10-19 Richard Sandiford <rsandifo@nildram.co.uk>
* config/mips/mips.c (build_mips16_function_stub): Load the * config/mips/mips.c (build_mips16_function_stub): Load the
target address into $1 before transfering the arguments. target address into $1 before transfering the arguments.
Don't use ".set noreorder". Don't use ".set noreorder".
...@@ -5223,6 +5223,30 @@ build_mips16_function_stub (void) ...@@ -5223,6 +5223,30 @@ build_mips16_function_stub (void)
switch_to_section (function_section (current_function_decl)); switch_to_section (function_section (current_function_decl));
} }
/* The current function is a MIPS16 function that returns a value in an FPR.
Copy the return value from its soft-float to its hard-float location.
libgcc2 has special non-MIPS16 helper functions for each case. */
static void
mips16_copy_fpr_return_value (void)
{
rtx fn, insn, arg, call;
tree id, return_type;
enum machine_mode return_mode;
return_type = DECL_RESULT (current_function_decl);
return_mode = DECL_MODE (return_type);
id = get_identifier (ACONCAT (("__mips16_ret_",
mips16_call_stub_mode_suffix (return_mode),
NULL)));
fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
arg = gen_rtx_REG (return_mode, GP_RETURN);
call = gen_call_value_internal (arg, fn, const0_rtx);
insn = emit_call_insn (call);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), arg);
}
/* Build a call stub for a mips16 call. A stub is needed if we are /* Build a call stub for a mips16 call. A stub is needed if we are
passing any floating point values which should go into the floating passing any floating point values which should go into the floating
point registers. If we are, and the call turns out to be to a point registers. If we are, and the call turns out to be to a
...@@ -7087,6 +7111,29 @@ mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end) ...@@ -7087,6 +7111,29 @@ mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
} }
#endif #endif
/* Return the FOO in the name of the ".mdebug.FOO" section associated
with the current ABI. */
static const char *
mips_mdebug_abi_name (void)
{
switch (mips_abi)
{
case ABI_32:
return "abi32";
case ABI_O64:
return "abiO64";
case ABI_N32:
return "abiN32";
case ABI_64:
return "abiN64";
case ABI_EABI:
return TARGET_64BIT ? "eabi64" : "eabi32";
default:
gcc_unreachable ();
}
}
/* Implement TARGET_ASM_FILE_START. */ /* Implement TARGET_ASM_FILE_START. */
static void static void
...@@ -7103,24 +7150,12 @@ mips_file_start (void) ...@@ -7103,24 +7150,12 @@ mips_file_start (void)
debug these binaries. See the function mips_gdbarch_init() in debug these binaries. See the function mips_gdbarch_init() in
gdb/mips-tdep.c. This is unnecessary for the IRIX 5/6 ABIs and gdb/mips-tdep.c. This is unnecessary for the IRIX 5/6 ABIs and
causes unnecessary IRIX 6 ld warnings. */ causes unnecessary IRIX 6 ld warnings. */
const char * abi_string = NULL;
switch (mips_abi)
{
case ABI_32: abi_string = "abi32"; break;
case ABI_N32: abi_string = "abiN32"; break;
case ABI_64: abi_string = "abi64"; break;
case ABI_O64: abi_string = "abiO64"; break;
case ABI_EABI: abi_string = TARGET_64BIT ? "eabi64" : "eabi32"; break;
default:
gcc_unreachable ();
}
/* Note - we use fprintf directly rather than calling switch_to_section /* Note - we use fprintf directly rather than calling switch_to_section
because in this way we can avoid creating an allocated section. We because in this way we can avoid creating an allocated section. We
do not want this section to take up any space in the running do not want this section to take up any space in the running
executable. */ executable. */
fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n", fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
abi_string); mips_mdebug_abi_name ());
/* There is no ELF header flag to distinguish long32 forms of the /* There is no ELF header flag to distinguish long32 forms of the
EABI from long64 forms. Emit a special section to help tools EABI from long64 forms. Emit a special section to help tools
...@@ -8548,29 +8583,7 @@ mips_expand_epilogue (int sibcall_p) ...@@ -8548,29 +8583,7 @@ mips_expand_epilogue (int sibcall_p)
/* In mips16 mode, if the return value should go into a floating-point /* In mips16 mode, if the return value should go into a floating-point
register, we need to call a helper routine to copy it over. */ register, we need to call a helper routine to copy it over. */
if (mips16_cfun_returns_in_fpr_p ()) if (mips16_cfun_returns_in_fpr_p ())
{ mips16_copy_fpr_return_value ();
char *name;
rtx func;
rtx insn;
rtx retval;
rtx call;
tree id;
tree return_type;
enum machine_mode return_mode;
return_type = DECL_RESULT (current_function_decl);
return_mode = DECL_MODE (return_type);
name = ACONCAT (("__mips16_ret_",
mips16_call_stub_mode_suffix (return_mode),
NULL));
id = get_identifier (name);
func = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
retval = gen_rtx_REG (return_mode, GP_RETURN);
call = gen_call_value_internal (retval, func, const0_rtx);
insn = emit_call_insn (call);
use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
}
/* Split the frame into two. STEP1 is the amount of stack we should /* Split the frame into two. STEP1 is the amount of stack we should
deallocate before restoring the registers. STEP2 is the amount we deallocate before restoring the registers. STEP2 is the amount we
...@@ -8718,6 +8731,80 @@ mips_can_use_return_insn (void) ...@@ -8718,6 +8731,80 @@ mips_can_use_return_insn (void)
return cfun->machine->frame.total_size == 0; return cfun->machine->frame.total_size == 0;
} }
/* Return true if register REGNO can store a value of mode MODE.
The result of this function is cached in mips_hard_regno_mode_ok. */
static bool
mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
{
unsigned int size;
enum mode_class class;
if (mode == CCV2mode)
return (ISA_HAS_8CC
&& ST_REG_P (regno)
&& (regno - ST_REG_FIRST) % 2 == 0);
if (mode == CCV4mode)
return (ISA_HAS_8CC
&& ST_REG_P (regno)
&& (regno - ST_REG_FIRST) % 4 == 0);
if (mode == CCmode)
{
if (!ISA_HAS_8CC)
return regno == FPSW_REGNUM;
return (ST_REG_P (regno)
|| GP_REG_P (regno)
|| FP_REG_P (regno));
}
size = GET_MODE_SIZE (mode);
class = GET_MODE_CLASS (mode);
if (GP_REG_P (regno))
return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD;
if (FP_REG_P (regno)
&& (((regno - FP_REG_FIRST) % MAX_FPRS_PER_FMT) == 0
|| (MIN_FPRS_PER_FMT == 1 && size <= UNITS_PER_FPREG)))
{
/* Allow TFmode for CCmode reloads. */
if (mode == TFmode && ISA_HAS_8CC)
return true;
if (class == MODE_FLOAT
|| class == MODE_COMPLEX_FLOAT
|| class == MODE_VECTOR_FLOAT)
return size <= UNITS_PER_FPVALUE;
/* Allow integer modes that fit into a single register. We need
to put integers into FPRs when using instructions like CVT
and TRUNC. There's no point allowing sizes smaller than a word,
because the FPU has no appropriate load/store instructions. */
if (class == MODE_INT)
return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG;
}
if (ACC_REG_P (regno)
&& (INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode)))
{
if (size <= UNITS_PER_WORD)
return true;
if (size <= UNITS_PER_WORD * 2)
return (DSP_ACC_REG_P (regno)
? ((regno - DSP_ACC_REG_FIRST) & 1) == 0
: regno == MD_REG_FIRST);
}
if (ALL_COP_REG_P (regno))
return class == MODE_INT && size <= UNITS_PER_WORD;
return false;
}
/* Implement HARD_REGNO_NREGS. The size of FP registers is controlled /* Implement HARD_REGNO_NREGS. The size of FP registers is controlled
by UNITS_PER_FPREG. The size of FP status registers is always 4, because by UNITS_PER_FPREG. The size of FP status registers is always 4, because
they only hold condition code modes, and CCmode is always considered to they only hold condition code modes, and CCmode is always considered to
...@@ -10525,6 +10612,41 @@ mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target) ...@@ -10525,6 +10612,41 @@ mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
const1_rtx, const0_rtx); const1_rtx, const0_rtx);
} }
/* EXP is a CALL_EXPR that calls the function described by BDESC.
Expand the call and return an rtx for its return value.
TARGET, if nonnull, suggests a good place to put this value. */
static rtx
mips_expand_builtin_1 (const struct builtin_description *bdesc,
tree exp, rtx target)
{
switch (bdesc->builtin_type)
{
case MIPS_BUILTIN_DIRECT:
return mips_expand_builtin_direct (bdesc->icode, target, exp, true);
case MIPS_BUILTIN_DIRECT_NO_TARGET:
return mips_expand_builtin_direct (bdesc->icode, target, exp, false);
case MIPS_BUILTIN_MOVT:
case MIPS_BUILTIN_MOVF:
return mips_expand_builtin_movtf (bdesc->builtin_type, bdesc->icode,
bdesc->cond, target, exp);
case MIPS_BUILTIN_CMP_ANY:
case MIPS_BUILTIN_CMP_ALL:
case MIPS_BUILTIN_CMP_UPPER:
case MIPS_BUILTIN_CMP_LOWER:
case MIPS_BUILTIN_CMP_SINGLE:
return mips_expand_builtin_compare (bdesc->builtin_type, bdesc->icode,
bdesc->cond, target, exp);
case MIPS_BUILTIN_BPOSGE32:
return mips_expand_builtin_bposge (bdesc->builtin_type, target);
}
gcc_unreachable ();
}
/* Expand builtin functions. This is called from TARGET_EXPAND_BUILTIN. */ /* Expand builtin functions. This is called from TARGET_EXPAND_BUILTIN. */
static rtx static rtx
...@@ -10532,11 +10654,8 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, ...@@ -10532,11 +10654,8 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED, enum machine_mode mode ATTRIBUTE_UNUSED,
int ignore ATTRIBUTE_UNUSED) int ignore ATTRIBUTE_UNUSED)
{ {
enum insn_code icode;
enum mips_builtin_type type;
tree fndecl; tree fndecl;
unsigned int fcode; unsigned int fcode;
const struct builtin_description *bdesc;
const struct bdesc_map *m; const struct bdesc_map *m;
fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0); fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
...@@ -10549,48 +10668,13 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED, ...@@ -10549,48 +10668,13 @@ mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
return const0_rtx; return const0_rtx;
} }
bdesc = NULL;
for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++) for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
{ {
if (fcode < m->size) if (fcode < m->size)
{ return mips_expand_builtin_1 (m->bdesc + fcode, exp, target);
bdesc = m->bdesc;
icode = bdesc[fcode].icode;
type = bdesc[fcode].builtin_type;
break;
}
fcode -= m->size; fcode -= m->size;
} }
if (bdesc == NULL) gcc_unreachable ();
return 0;
switch (type)
{
case MIPS_BUILTIN_DIRECT:
return mips_expand_builtin_direct (icode, target, exp, true);
case MIPS_BUILTIN_DIRECT_NO_TARGET:
return mips_expand_builtin_direct (icode, target, exp, false);
case MIPS_BUILTIN_MOVT:
case MIPS_BUILTIN_MOVF:
return mips_expand_builtin_movtf (type, icode, bdesc[fcode].cond,
target, exp);
case MIPS_BUILTIN_CMP_ANY:
case MIPS_BUILTIN_CMP_ALL:
case MIPS_BUILTIN_CMP_UPPER:
case MIPS_BUILTIN_CMP_LOWER:
case MIPS_BUILTIN_CMP_SINGLE:
return mips_expand_builtin_compare (type, icode, bdesc[fcode].cond,
target, exp);
case MIPS_BUILTIN_BPOSGE32:
return mips_expand_builtin_bposge (type, target);
default:
return 0;
}
} }
/* An entry in the mips16 constant pool. VALUE is the pool constant, /* An entry in the mips16 constant pool. VALUE is the pool constant,
...@@ -12071,76 +12155,11 @@ override_options (void) ...@@ -12071,76 +12155,11 @@ override_options (void)
mips_dwarf_regno[i + TARGET_BIG_ENDIAN] = i + 1; mips_dwarf_regno[i + TARGET_BIG_ENDIAN] = i + 1;
} }
/* Set up array giving whether a given register can hold a given mode. */ /* Set up mips_hard_regno_mode_ok. */
for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
for (mode = VOIDmode; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
mode != MAX_MACHINE_MODE; mips_hard_regno_mode_ok[(int)mode][regno]
mode = (enum machine_mode) ((int)mode + 1)) = mips_hard_regno_mode_ok_p (regno, mode);
{
register int size = GET_MODE_SIZE (mode);
register enum mode_class class = GET_MODE_CLASS (mode);
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
register int temp;
if (mode == CCV2mode)
temp = (ISA_HAS_8CC
&& ST_REG_P (regno)
&& (regno - ST_REG_FIRST) % 2 == 0);
else if (mode == CCV4mode)
temp = (ISA_HAS_8CC
&& ST_REG_P (regno)
&& (regno - ST_REG_FIRST) % 4 == 0);
else if (mode == CCmode)
{
if (! ISA_HAS_8CC)
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);
else if (FP_REG_P (regno))
temp = ((((regno % MAX_FPRS_PER_FMT) == 0)
|| (MIN_FPRS_PER_FMT == 1
&& size <= UNITS_PER_FPREG))
&& (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT
|| class == MODE_VECTOR_FLOAT)
&& size <= UNITS_PER_FPVALUE)
/* Allow integer modes that fit into a single
register. We need to put integers into FPRs
when using instructions like cvt and trunc.
We can't allow sizes smaller than a word,
the FPU has no appropriate load/store
instructions for those. */
|| (class == MODE_INT
&& size >= MIN_UNITS_PER_WORD
&& size <= UNITS_PER_FPREG)
/* Allow TFmode for CCmode reloads. */
|| (ISA_HAS_8CC && mode == TFmode)));
else if (ACC_REG_P (regno))
temp = ((INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode))
&& size <= UNITS_PER_WORD * 2
&& (size <= UNITS_PER_WORD
|| regno == MD_REG_FIRST
|| (DSP_ACC_REG_P (regno)
&& ((regno - DSP_ACC_REG_FIRST) & 1) == 0)));
else if (ALL_COP_REG_P (regno))
temp = (class == MODE_INT && size <= UNITS_PER_WORD);
else
temp = 0;
mips_hard_regno_mode_ok[(int)mode][regno] = temp;
}
}
/* Save GPR registers in word_mode sized hunks. word_mode hasn't been /* Save GPR registers in word_mode sized hunks. word_mode hasn't been
initialized yet, so we can't use that here. */ initialized yet, so we can't use that here. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment