/* Subroutines for insn-output.c for Matsushita MN10300 series
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
- Free Software Foundation, Inc.
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
Contributed by Jeff Law (law@cygnus.com).
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
-#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "output.h"
#include "insn-attr.h"
#include "flags.h"
#include "recog.h"
+#include "reload.h"
#include "expr.h"
#include "optabs.h"
#include "function.h"
#include "obstack.h"
+#include "diagnostic-core.h"
#include "toplev.h"
#include "tm_p.h"
#include "target.h"
symbol names from register names. */
int mn10300_protect_label;
+/* The selected processor. */
+enum processor_type mn10300_processor = PROCESSOR_DEFAULT;
+
/* The size of the callee register save area. Right now we save everything
on entry since it costs us nothing in code size. It does cost us from a
speed standpoint, so we want to optimize this sooner or later. */
-#define REG_SAVE_BYTES (4 * regs_ever_live[2] \
- + 4 * regs_ever_live[3] \
- + 4 * regs_ever_live[6] \
- + 4 * regs_ever_live[7] \
- + 16 * (regs_ever_live[14] || regs_ever_live[15] \
- || regs_ever_live[16] || regs_ever_live[17]))
+#define REG_SAVE_BYTES (4 * df_regs_ever_live_p (2) \
+ + 4 * df_regs_ever_live_p (3) \
+ + 4 * df_regs_ever_live_p (6) \
+ + 4 * df_regs_ever_live_p (7) \
+ + 16 * (df_regs_ever_live_p (14) || df_regs_ever_live_p (15) \
+ || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)))
+static bool mn10300_handle_option (size_t, const char *, int);
+static bool mn10300_legitimate_address_p (enum machine_mode, rtx, bool);
static int mn10300_address_cost_1 (rtx, int *);
-static int mn10300_address_cost (rtx);
-static bool mn10300_rtx_costs (rtx, int, int, int *);
+static int mn10300_address_cost (rtx, bool);
+static bool mn10300_rtx_costs (rtx, int, int, int *, bool);
static void mn10300_file_start (void);
-static bool mn10300_return_in_memory (tree, tree);
+static bool mn10300_return_in_memory (const_tree, const_tree);
static rtx mn10300_builtin_saveregs (void);
-
+static void mn10300_va_start (tree, rtx);
+static rtx mn10300_legitimize_address (rtx, rtx, enum machine_mode);
+static bool mn10300_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static unsigned int mn10300_case_values_threshold (void);
+static void mn10300_encode_section_info (tree, rtx, int);
+static void mn10300_asm_trampoline_template (FILE *);
+static void mn10300_trampoline_init (rtx, tree, rtx);
+static rtx mn10300_function_value (const_tree, const_tree, bool);
+static rtx mn10300_libcall_value (enum machine_mode, const_rtx);
+static void mn10300_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
+static bool mn10300_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT, const_tree);
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS mn10300_legitimize_address
+
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS mn10300_rtx_costs
#undef TARGET_ADDRESS_COST
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION mn10300_handle_option
+
#undef TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
#undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
-
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY mn10300_return_in_memory
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE mn10300_pass_by_reference
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES mn10300_arg_partial_bytes
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START mn10300_va_start
+
+#undef TARGET_CASE_VALUES_THRESHOLD
+#define TARGET_CASE_VALUES_THRESHOLD mn10300_case_values_threshold
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P mn10300_legitimate_address_p
+
+#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
+#define TARGET_ASM_TRAMPOLINE_TEMPLATE mn10300_asm_trampoline_template
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT mn10300_trampoline_init
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE mn10300_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE mn10300_libcall_value
+
+#undef TARGET_ASM_OUTPUT_MI_THUNK
+#define TARGET_ASM_OUTPUT_MI_THUNK mn10300_asm_output_mi_thunk
+#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK mn10300_can_output_mi_thunk
-static void mn10300_encode_section_info (tree, rtx, int);
struct gcc_target targetm = TARGET_INITIALIZER;
\f
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+mn10300_handle_option (size_t code,
+ const char *arg ATTRIBUTE_UNUSED,
+ int value)
+{
+ switch (code)
+ {
+ case OPT_mam33:
+ mn10300_processor = value ? PROCESSOR_AM33 : PROCESSOR_MN10300;
+ return true;
+ case OPT_mam33_2:
+ mn10300_processor = (value
+ ? PROCESSOR_AM33_2
+ : MIN (PROCESSOR_AM33, PROCESSOR_DEFAULT));
+ return true;
+ default:
+ return true;
+ }
+}
+
+/* Implement OVERRIDE_OPTIONS. */
+
+void
+mn10300_override_options (void)
+{
+ if (TARGET_AM33)
+ target_flags &= ~MASK_MULT_BUG;
+}
+
static void
mn10300_file_start (void)
{
fprintf (file, "ul");
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
}
fprintf (file, "cs");
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
case 'C':
else
print_operand (file, x, 0);
break;
-
+
case 'D':
switch (GET_CODE (x))
{
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
}
default:
- abort ();
+ gcc_unreachable ();
}
break;
fprintf (file, "0x%lx", val[1]);
break;;
case SFmode:
- abort ();
+ gcc_unreachable ();
case VOIDmode:
case DImode:
- print_operand_address (file,
+ print_operand_address (file,
GEN_INT (CONST_DOUBLE_HIGH (x)));
break;
default:
}
default:
- abort ();
+ gcc_unreachable ();
}
break;
break;
case 'N':
- if (INTVAL (x) < -128 || INTVAL (x) > 255)
- abort ();
+ gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff));
break;
case 'U':
- if (INTVAL (x) < -128 || INTVAL (x) > 255)
- abort ();
+ gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
break;
print_operand_address (file, x);
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
}
&& REG_OK_FOR_BASE_P (XEXP (addr, 1)))
base = XEXP (addr, 1), index = XEXP (addr, 0);
else
- abort ();
+ gcc_unreachable ();
print_operand (file, index, 0);
fputc (',', file);
print_operand (file, base, 0);;
return 0;
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
++n;
return n;
/* Print a set of registers in the format required by "movm" and "ret".
Register K is saved if bit K of MASK is set. The data and address
registers can be stored individually, but the extended registers cannot.
- We assume that the mask alread takes that into account. For instance,
+ We assume that the mask already takes that into account. For instance,
bits 14 to 17 must have the same value. */
void
if ((mask & 0x3c000) != 0)
{
- if ((mask & 0x3c000) != 0x3c000)
- abort();
+ gcc_assert ((mask & 0x3c000) == 0x3c000);
if (need_comma)
fputc (',', file);
fputs ("exreg1", file);
can_use_return_insn (void)
{
/* size includes the fixed stack space needed for function calls. */
- int size = get_frame_size () + current_function_outgoing_args_size;
+ int size = get_frame_size () + crtl->outgoing_args_size;
/* And space for the return pointer. */
- size += current_function_outgoing_args_size ? 4 : 0;
+ size += crtl->outgoing_args_size ? 4 : 0;
return (reload_completed
&& size == 0
- && !regs_ever_live[2]
- && !regs_ever_live[3]
- && !regs_ever_live[6]
- && !regs_ever_live[7]
- && !regs_ever_live[14]
- && !regs_ever_live[15]
- && !regs_ever_live[16]
- && !regs_ever_live[17]
+ && !df_regs_ever_live_p (2)
+ && !df_regs_ever_live_p (3)
+ && !df_regs_ever_live_p (6)
+ && !df_regs_ever_live_p (7)
+ && !df_regs_ever_live_p (14)
+ && !df_regs_ever_live_p (15)
+ && !df_regs_ever_live_p (16)
+ && !df_regs_ever_live_p (17)
&& fp_regs_to_save () == 0
&& !frame_pointer_needed);
}
mask = 0;
for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
mask |= (1 << i);
if ((mask & 0x3c000) != 0)
mask |= 0x3c000;
HOST_WIDE_INT size;
/* SIZE includes the fixed stack space needed for function calls. */
- size = get_frame_size () + current_function_outgoing_args_size;
- size += (current_function_outgoing_args_size ? 4 : 0);
+ size = get_frame_size () + crtl->outgoing_args_size;
+ size += (crtl->outgoing_args_size ? 4 : 0);
/* If we use any of the callee-saved registers, save them now. */
mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
frame pointer, size is nonzero and the user hasn't
changed the calling conventions of a0. */
if (! frame_pointer_needed && size
- && call_used_regs[FIRST_ADDRESS_REGNUM]
+ && call_really_used_regs [FIRST_ADDRESS_REGNUM]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM])
{
/* Insn: add -(size + 4 * num_regs_to_save), sp. */
/* Consider alternative save_a0_no_merge if the user hasn't
changed the calling conventions of a0. */
- if (call_used_regs[FIRST_ADDRESS_REGNUM]
+ if (call_really_used_regs [FIRST_ADDRESS_REGNUM]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM])
{
/* Insn: add -4 * num_regs_to_save, sp. */
break;
default:
- abort ();
+ gcc_unreachable ();
}
-
+
/* Now prepare register a0, if we have decided to use it. */
switch (strategy)
{
emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
reg = gen_rtx_POST_INC (SImode, reg);
break;
-
+
default:
- abort ();
+ gcc_unreachable ();
}
-
+
/* Now actually save the FP registers. */
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_really_used_regs [i])
{
rtx addr;
}
else
addr = stack_pointer_rtx;
-
+
xsize += 4;
}
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-size)));
- if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
- {
- rtx insn = get_last_insn ();
- rtx last = emit_insn (gen_GOTaddr2picreg ());
-
- /* Mark these insns as possibly dead. Sometimes, flow2 may
- delete all uses of the PIC register. In this case, let it
- delete the initialization too. */
- do
- {
- insn = NEXT_INSN (insn);
-
- REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
- const0_rtx,
- REG_NOTES (insn));
- }
- while (insn != last);
- }
+ if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+ emit_insn (gen_GOTaddr2picreg ());
}
void
HOST_WIDE_INT size;
/* SIZE includes the fixed stack space needed for function calls. */
- size = get_frame_size () + current_function_outgoing_args_size;
- size += (current_function_outgoing_args_size ? 4 : 0);
+ size = get_frame_size () + crtl->outgoing_args_size;
+ size += (crtl->outgoing_args_size ? 4 : 0);
if (TARGET_AM33_2 && fp_regs_to_save ())
{
/* Consider using a1 in post-increment mode, as long as the
user hasn't changed the calling conventions of a1. */
- if (call_used_regs[FIRST_ADDRESS_REGNUM+1]
+ if (call_really_used_regs [FIRST_ADDRESS_REGNUM + 1]
&& ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
{
/* Insn: mov sp,a1. */
+ REG_SAVE_BYTES - 252)));
size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save;
break;
-
+
case restore_a1:
reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1);
emit_insn (gen_movsi (reg, stack_pointer_rtx));
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
reg = gen_rtx_POST_INC (SImode, reg);
for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ if (df_regs_ever_live_p (i) && ! call_really_used_regs [i])
{
rtx addr;
-
+
if (reg)
addr = reg;
else if (size)
If the stack size + register save area is more than 255 bytes,
then the stack must be cut back here since the size + register
- save size is too big for a ret/retf instruction.
+ save size is too big for a ret/retf instruction.
Else leave it alone, it will be cut back as part of the
ret/retf instruction, or there wasn't any stack to begin with.
}
/* Adjust the stack and restore callee-saved registers, if any. */
- if (size || regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
+ if (size || df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
+ || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
+ || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
+ || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
|| frame_pointer_needed)
emit_jump_insn (gen_return_internal_regs
(GEN_INT (size + REG_SAVE_BYTES)));
cc_status.mdep.fpCC = 1;
break;
- case CC_INVERT:
- /* The insn is a compare instruction. */
- CC_STATUS_INIT;
- cc_status.value1 = SET_SRC (body);
- cc_status.flags |= CC_INVERTED;
- break;
-
case CC_CLOBBER:
/* Insn doesn't leave CC in a usable state. */
CC_STATUS_INIT;
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
return mask;
}
-/* Return true if OP is a valid call operand. */
-
-int
-call_address_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (flag_pic)
- return (EXTRA_CONSTRAINT (op, 'S') || GET_CODE (op) == REG);
-
- return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG);
-}
-
/* What (if any) secondary registers are needed to move IN with mode
- MODE into a register in register class CLASS.
+ MODE into a register in register class RCLASS.
We might be able to simplify this. */
enum reg_class
-secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in)
+mn10300_secondary_reload_class (enum reg_class rclass, enum machine_mode mode,
+ rtx in)
{
+ rtx inner = in;
+
+ /* Strip off any SUBREG expressions from IN. Basically we want
+ to know if IN is a pseudo or (subreg (pseudo)) as those can
+ turn into MEMs during reload. */
+ while (GET_CODE (inner) == SUBREG)
+ inner = SUBREG_REG (inner);
+
/* Memory loads less than a full word wide can't have an
address or stack pointer destination. They must use
a data register as an intermediate register. */
if ((GET_CODE (in) == MEM
- || (GET_CODE (in) == REG
- && REGNO (in) >= FIRST_PSEUDO_REGISTER)
- || (GET_CODE (in) == SUBREG
- && GET_CODE (SUBREG_REG (in)) == REG
- && REGNO (SUBREG_REG (in)) >= FIRST_PSEUDO_REGISTER))
+ || (GET_CODE (inner) == REG
+ && REGNO (inner) >= FIRST_PSEUDO_REGISTER))
&& (mode == QImode || mode == HImode)
- && (class == ADDRESS_REGS || class == SP_REGS
- || class == SP_OR_ADDRESS_REGS))
+ && (rclass == ADDRESS_REGS || rclass == SP_REGS
+ || rclass == SP_OR_ADDRESS_REGS))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
/* We can't directly load sp + const_int into a data register;
we must use an address register as an intermediate. */
- if (class != SP_REGS
- && class != ADDRESS_REGS
- && class != SP_OR_ADDRESS_REGS
- && class != SP_OR_EXTENDED_REGS
- && class != ADDRESS_OR_EXTENDED_REGS
- && class != SP_OR_ADDRESS_OR_EXTENDED_REGS
+ if (rclass != SP_REGS
+ && rclass != ADDRESS_REGS
+ && rclass != SP_OR_ADDRESS_REGS
+ && rclass != SP_OR_EXTENDED_REGS
+ && rclass != ADDRESS_OR_EXTENDED_REGS
+ && rclass != SP_OR_ADDRESS_OR_EXTENDED_REGS
&& (in == stack_pointer_rtx
|| (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
if (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
|| XEXP (in, 1) == stack_pointer_rtx))
+ return GENERAL_REGS;
+
+ if (TARGET_AM33_2
+ && rclass == FP_REGS)
{
- if (TARGET_AM33)
- return DATA_OR_EXTENDED_REGS;
- return DATA_REGS;
- }
-
- if (TARGET_AM33_2 && class == FP_REGS
- && GET_CODE (in) == MEM && ! OK_FOR_Q (in))
- {
- if (TARGET_AM33)
- return DATA_OR_EXTENDED_REGS;
- return DATA_REGS;
+ /* We can't load directly into an FP register from a
+ constant address. */
+ if (GET_CODE (in) == MEM
+ && CONSTANT_ADDRESS_P (XEXP (in, 0)))
+ return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS);
+
+ /* Handle case were a pseudo may not get a hard register
+ but has an equivalent memory location defined. */
+ if (GET_CODE (inner) == REG
+ && REGNO (inner) >= FIRST_PSEUDO_REGISTER
+ && reg_equiv_mem [REGNO (inner)]
+ && CONSTANT_ADDRESS_P (XEXP (reg_equiv_mem [REGNO (inner)], 0)))
+ return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS);
}
/* Otherwise assume no secondary reloads are needed. */
is the size of the callee register save area. */
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
{
- if (regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
+ if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
+ || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
+ || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
+ || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
|| fp_regs_to_save ()
|| frame_pointer_needed)
return REG_SAVE_BYTES
area, and the fixed stack space needed for function calls (if any). */
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
{
- if (regs_ever_live[2] || regs_ever_live[3]
- || regs_ever_live[6] || regs_ever_live[7]
- || regs_ever_live[14] || regs_ever_live[15]
- || regs_ever_live[16] || regs_ever_live[17]
+ if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
+ || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
+ || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
+ || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
|| fp_regs_to_save ()
|| frame_pointer_needed)
return (get_frame_size () + REG_SAVE_BYTES
+ 4 * fp_regs_to_save ()
- + (current_function_outgoing_args_size
- ? current_function_outgoing_args_size + 4 : 0));
+ + (crtl->outgoing_args_size
+ ? crtl->outgoing_args_size + 4 : 0));
else
return (get_frame_size ()
- + (current_function_outgoing_args_size
- ? current_function_outgoing_args_size + 4 : 0));
+ + (crtl->outgoing_args_size
+ ? crtl->outgoing_args_size + 4 : 0));
}
/* The difference between the frame pointer and stack pointer is the sum
for function calls (if any). */
if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
return (get_frame_size ()
- + (current_function_outgoing_args_size
- ? current_function_outgoing_args_size + 4 : 0));
+ + (crtl->outgoing_args_size
+ ? crtl->outgoing_args_size + 4 : 0));
- abort ();
+ gcc_unreachable ();
}
/* Worker function for TARGET_RETURN_IN_MEMORY. */
static bool
-mn10300_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
+mn10300_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
/* Return values > 8 bytes in length in memory. */
- return int_size_in_bytes (type) > 8 || TYPE_MODE (type) == BLKmode;
+ return (int_size_in_bytes (type) > 8
+ || int_size_in_bytes (type) == 0
+ || TYPE_MODE (type) == BLKmode);
}
/* Flush the argument registers to the stack for a stdarg function;
{
rtx offset, mem;
tree fntype = TREE_TYPE (current_function_decl);
- int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node)))
+ int argadj = ((!stdarg_p (fntype))
? UNITS_PER_WORD : 0);
- int set = get_varargs_alias_set ();
+ alias_set_type set = get_varargs_alias_set ();
if (argadj)
- offset = plus_constant (current_function_arg_offset_rtx, argadj);
+ offset = plus_constant (crtl->args.arg_offset_rtx, argadj);
else
- offset = current_function_arg_offset_rtx;
+ offset = crtl->args.arg_offset_rtx;
- mem = gen_rtx_MEM (SImode, current_function_internal_arg_pointer);
+ mem = gen_rtx_MEM (SImode, crtl->args.internal_arg_pointer);
set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 0));
mem = gen_rtx_MEM (SImode,
- plus_constant (current_function_internal_arg_pointer, 4));
+ plus_constant (crtl->args.internal_arg_pointer, 4));
set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 1));
return copy_to_reg (expand_binop (Pmode, add_optab,
- current_function_internal_arg_pointer,
+ crtl->args.internal_arg_pointer,
offset, 0, 0, OPTAB_LIB_WIDEN));
}
-void
+static void
mn10300_va_start (tree valist, rtx nextarg)
{
nextarg = expand_builtin_saveregs ();
std_expand_builtin_va_start (valist, nextarg);
}
-rtx
-mn10300_va_arg (tree valist, tree type)
-{
- HOST_WIDE_INT align, rsize;
- tree t, ptr, pptr;
-
- /* Compute the rounded size of the type. */
- align = PARM_BOUNDARY / BITS_PER_UNIT;
- rsize = (((int_size_in_bytes (type) + align - 1) / align) * align);
-
- t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist,
- build_int_2 ((rsize > 8 ? 4 : rsize), 0));
- TREE_SIDE_EFFECTS (t) = 1;
+/* Return true when a parameter should be passed by reference. */
- ptr = build_pointer_type (type);
-
- /* "Large" types are passed by reference. */
- if (rsize > 8)
- {
- pptr = build_pointer_type (ptr);
- t = build1 (NOP_EXPR, pptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
+static bool
+mn10300_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode, const_tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ unsigned HOST_WIDE_INT size;
- t = build1 (INDIRECT_REF, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
- }
+ if (type)
+ size = int_size_in_bytes (type);
else
- {
- t = build1 (NOP_EXPR, ptr, t);
- TREE_SIDE_EFFECTS (t) = 1;
- }
+ size = GET_MODE_SIZE (mode);
- /* Calculate! */
- return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
+ return (size > 8 || size == 0);
}
/* Return an RTX to represent where a value with mode MODE will be returned
- from a function. If the result is 0, the argument is pushed. */
+ from a function. If the result is NULL_RTX, the argument is pushed. */
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
tree type, int named ATTRIBUTE_UNUSED)
{
- rtx result = 0;
+ rtx result = NULL_RTX;
int size, align;
/* We only support using 2 data registers as argument registers. */
/* Don't pass this arg via a register if all the argument registers
are used up. */
if (cum->nbytes > nregs * UNITS_PER_WORD)
- return 0;
+ return result;
/* Don't pass this arg via a register if it would be split between
registers and memory. */
if (type == NULL_TREE
&& cum->nbytes + size > nregs * UNITS_PER_WORD)
- return 0;
+ return result;
switch (cum->nbytes / UNITS_PER_WORD)
{
case 0:
- result = gen_rtx_REG (mode, 0);
+ result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM);
break;
case 1:
- result = gen_rtx_REG (mode, 1);
+ result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM + 1);
break;
default:
- result = 0;
+ break;
}
return result;
}
-/* Return the number of registers to use for an argument passed partially
- in registers and partially in memory. */
+/* Return the number of bytes of registers to use for an argument passed
+ partially in registers and partially in memory. */
-int
-function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named ATTRIBUTE_UNUSED)
+static int
+mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, bool named ATTRIBUTE_UNUSED)
{
int size, align;
&& cum->nbytes + size > nregs * UNITS_PER_WORD)
return 0;
- return (nregs * UNITS_PER_WORD - cum->nbytes) / UNITS_PER_WORD;
+ return nregs * UNITS_PER_WORD - cum->nbytes;
+}
+
+/* Return the location of the function's value. This will be either
+ $d0 for integer functions, $a0 for pointers, or a PARALLEL of both
+ $d0 and $a0 if the -mreturn-pointer-on-do flag is set. Note that
+ we only return the PARALLEL for outgoing values; we do not want
+ callers relying on this extra copy. */
+
+static rtx
+mn10300_function_value (const_tree valtype,
+ const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+ bool outgoing)
+{
+ rtx rv;
+ enum machine_mode mode = TYPE_MODE (valtype);
+
+ if (! POINTER_TYPE_P (valtype))
+ return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
+ else if (! TARGET_PTR_A0D0 || ! outgoing
+ || cfun->returns_struct)
+ return gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM);
+
+ rv = gen_rtx_PARALLEL (mode, rtvec_alloc (2));
+ XVECEXP (rv, 0, 0)
+ = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM),
+ GEN_INT (0));
+
+ XVECEXP (rv, 0, 1)
+ = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode, FIRST_DATA_REGNUM),
+ GEN_INT (0));
+ return rv;
+}
+
+/* Implements TARGET_LIBCALL_VALUE. */
+
+static rtx
+mn10300_libcall_value (enum machine_mode mode,
+ const_rtx fun ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
+}
+
+/* Implements FUNCTION_VALUE_REGNO_P. */
+
+bool
+mn10300_function_value_regno_p (const unsigned int regno)
+{
+ return (regno == FIRST_DATA_REGNUM || regno == FIRST_ADDRESS_REGNUM);
}
/* Output a tst insn. */
}
/* Are we setting a data register to zero (this does not win for
- address registers)?
+ address registers)?
If it's a call clobbered register, have we past a call?
== REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
- && (!past_call
- || !call_used_regs[REGNO (SET_DEST (set))]))
+ && (!past_call
+ || ! call_really_used_regs [REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
xoperands[0] = operand;
!= REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
- && (!past_call
- || !call_used_regs[REGNO (SET_DEST (set))]))
+ && (!past_call
+ || ! call_really_used_regs [REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
xoperands[0] = operand;
return 0;
}
-/* Return 1 if X is a CONST_INT that is only 8 bits wide. This is used
- for the btst insn which may examine memory or a register (the memory
- variant only allows an unsigned 8 bit integer). */
-int
-const_8bit_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= 0
- && INTVAL (op) < 256);
-}
-
-/* Return true if the operand is the 1.0f constant. */
-int
-const_1f_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return (op == CONST1_RTX (SFmode));
-}
-
/* Similarly, but when using a zero_extract pattern for a btst where
the source operand might end up in memory. */
int
OLDX is the address as it was before break_out_memory_refs was called.
In some cases it is useful to look at this to decide what needs to be done.
- MODE and WIN are passed so that this macro can use
- GO_IF_LEGITIMATE_ADDRESS.
-
Normally it is always safe for this macro to do nothing. It exists to
recognize opportunities to optimize the output.
But on a few ports with segmented architectures and indexed addressing
(mn10300, hppa) it is used to rewrite certain problematical addresses. */
rtx
-legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED)
+mn10300_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (flag_pic && ! legitimate_pic_operand_p (x))
x = legitimize_pic_address (oldx, NULL_RTX);
&& (XINT (x, 1) == UNSPEC_PIC
|| XINT (x, 1) == UNSPEC_GOT
|| XINT (x, 1) == UNSPEC_GOTOFF
- || XINT (x, 1) == UNSPEC_PLT))
+ || XINT (x, 1) == UNSPEC_PLT
+ || XINT (x, 1) == UNSPEC_GOTSYM_OFF))
return 1;
- if (GET_CODE (x) == QUEUED)
- return legitimate_pic_operand_p (QUEUED_VAR (x));
-
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
return 1;
}
+/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is
+ legitimate, and FALSE otherwise.
+
+ On the mn10300, the value in the address register must be
+ in the same memory space/segment as the effective address.
+
+ This is problematical for reload since it does not understand
+ that base+index != index+base in a memory reference.
+
+ Note it is still possible to use reg+reg addressing modes,
+ it's just much more difficult. For a discussion of a possible
+ workaround and solution, see the comments in pa.c before the
+ function record_unscaled_index_insn_codes. */
+
+bool
+mn10300_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
+{
+ if (CONSTANT_ADDRESS_P (x)
+ && (! flag_pic || legitimate_pic_operand_p (x)))
+ return TRUE;
+
+ if (RTX_OK_FOR_BASE_P (x, strict))
+ return TRUE;
+
+ if (TARGET_AM33
+ && GET_CODE (x) == POST_INC
+ && RTX_OK_FOR_BASE_P (XEXP (x, 0), strict)
+ && (mode == SImode || mode == SFmode || mode == HImode))
+ return TRUE;
+
+ if (GET_CODE (x) == PLUS)
+ {
+ rtx base = 0, index = 0;
+
+ if (REG_P (XEXP (x, 0))
+ && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 0)), strict))
+ {
+ base = XEXP (x, 0);
+ index = XEXP (x, 1);
+ }
+
+ if (REG_P (XEXP (x, 1))
+ && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 1)), strict))
+ {
+ base = XEXP (x, 1);
+ index = XEXP (x, 0);
+ }
+
+ if (base != 0 && index != 0)
+ {
+ if (GET_CODE (index) == CONST_INT)
+ return TRUE;
+ if (GET_CODE (index) == CONST
+ && GET_CODE (XEXP (index, 0)) != PLUS
+ && (! flag_pic
+ || legitimate_pic_operand_p (index)))
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
static int
mn10300_address_cost_1 (rtx x, int *unsig)
{
return 5;
default:
- abort ();
+ gcc_unreachable ();
}
case PLUS:
case EXPR_LIST:
case SUBREG:
case MEM:
- return mn10300_address_cost (XEXP (x, 0));
+ return mn10300_address_cost (XEXP (x, 0), !optimize_size);
case ZERO_EXTEND:
*unsig = 1;
case LABEL_REF:
return 8;
- case ADDRESSOF:
- switch (GET_CODE (XEXP (x, 0)))
- {
- case MEM:
- return mn10300_address_cost (XEXP (x, 0));
-
- case REG:
- return 1;
-
- default:
- abort ();
- }
-
default:
- abort ();
+ gcc_unreachable ();
}
}
static int
-mn10300_address_cost (rtx x)
+mn10300_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
{
int s = 0;
return mn10300_address_cost_1 (x, &s);
}
static bool
-mn10300_rtx_costs (rtx x, int code, int outer_code, int *total)
+mn10300_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed ATTRIBUTE_UNUSED)
{
switch (code)
{
case CONST_INT:
/* Zeros are extremely cheap. */
- if (INTVAL (x) == 0 && outer_code == SET)
+ if (INTVAL (x) == 0 && (outer_code == SET || outer_code == COMPARE))
*total = 0;
/* If it fits in 8 bits, then it's still relatively cheap. */
else if (INT_8_BITS (INTVAL (x)))
*total = 8;
return true;
+ case ZERO_EXTRACT:
+ /* This is cheap, we can use btst. */
+ if (outer_code == COMPARE)
+ *total = 0;
+ return false;
+
/* ??? This probably needs more work. */
case MOD:
case DIV:
val[1] = INTVAL (high);
}
break;
-
+
case CONST_DOUBLE:
if (GET_MODE (operands[1]) == DFmode)
{
val[1] = CONST_DOUBLE_HIGH (operands[1]);
}
break;
-
+
default:
return false;
}
if (flag_pic)
SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
}
+
+/* Dispatch tables on the mn10300 are extremely expensive in terms of code
+ and readonly data size. So we crank up the case threshold value to
+ encourage a series of if/else comparisons to implement many small switch
+ statements. In theory, this value could be increased much more if we
+ were solely optimizing for space, but we keep it "reasonable" to avoid
+ serious code efficiency lossage. */
+
+unsigned int mn10300_case_values_threshold (void)
+{
+ return 6;
+}
+
+/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE. */
+
+static void
+mn10300_asm_trampoline_template (FILE *f)
+{
+ fprintf (f, "\tadd -4,sp\n");
+ fprintf (f, "\t.long 0x0004fffa\n");
+ fprintf (f, "\tmov (0,sp),a0\n");
+ fprintf (f, "\tadd 4,sp\n");
+ fprintf (f, "\tmov (13,a0),a1\n");
+ fprintf (f, "\tmov (17,a0),a0\n");
+ fprintf (f, "\tjmp (a0)\n");
+ fprintf (f, "\t.long 0\n");
+ fprintf (f, "\t.long 0\n");
+}
+
+/* Worker function for TARGET_TRAMPOLINE_INIT. */
+
+static void
+mn10300_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx mem;
+
+ emit_block_move (m_tramp, assemble_trampoline_template (),
+ GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
+
+ mem = adjust_address (m_tramp, SImode, 0x14);
+ emit_move_insn (mem, chain_value);
+ mem = adjust_address (m_tramp, SImode, 0x18);
+ emit_move_insn (mem, fnaddr);
+}
+
+/* Output the assembler code for a C++ thunk function.
+ THUNK_DECL is the declaration for the thunk function itself, FUNCTION
+ is the decl for the target function. DELTA is an immediate constant
+ offset to be added to the THIS parameter. If VCALL_OFFSET is nonzero
+ the word at the adjusted address *(*THIS' + VCALL_OFFSET) should be
+ additionally added to THIS. Finally jump to the entry point of
+ FUNCTION. */
+
+static void
+mn10300_asm_output_mi_thunk (FILE * file,
+ tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta,
+ HOST_WIDE_INT vcall_offset,
+ tree function)
+{
+ const char * _this;
+
+ /* Get the register holding the THIS parameter. Handle the case
+ where there is a hidden first argument for a returned structure. */
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+ _this = reg_names [FIRST_ARGUMENT_REGNUM + 1];
+ else
+ _this = reg_names [FIRST_ARGUMENT_REGNUM];
+
+ fprintf (file, "\t%s Thunk Entry Point:\n", ASM_COMMENT_START);
+
+ if (delta)
+ fprintf (file, "\tadd %d, %s\n", (int) delta, _this);
+
+ if (vcall_offset)
+ {
+ const char * scratch = reg_names [FIRST_ADDRESS_REGNUM + 1];
+
+ fprintf (file, "\tmov %s, %s\n", _this, scratch);
+ fprintf (file, "\tmov (%s), %s\n", scratch, scratch);
+ fprintf (file, "\tadd %d, %s\n", (int) vcall_offset, scratch);
+ fprintf (file, "\tmov (%s), %s\n", scratch, scratch);
+ fprintf (file, "\tadd %s, %s\n", scratch, _this);
+ }
+
+ fputs ("\tjmp ", file);
+ assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
+ putc ('\n', file);
+}
+
+/* Return true if mn10300_output_mi_thunk would be able to output the
+ assembler code for the thunk function specified by the arguments
+ it is passed, and false otherwise. */
+
+static bool
+mn10300_can_output_mi_thunk (const_tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
+ const_tree function ATTRIBUTE_UNUSED)
+{
+ return true;
+}