/* Subroutines for insn-output.c for Matsushita MN10300 series
- Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
- Free Software Foundation, Inc.
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
Contributed by Jeff Law (law@cygnus.com).
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
+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.
-GNU CC is distributed in the hope that it will be useful,
+GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
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, 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 "coretypes.h"
+#include "tm.h"
#include "rtl.h"
#include "tree.h"
#include "regs.h"
#include "flags.h"
#include "recog.h"
#include "expr.h"
+#include "optabs.h"
#include "function.h"
#include "obstack.h"
#include "toplev.h"
#include "target.h"
#include "target-def.h"
+/* This is used by GOTaddr2picreg to uniquely identify
+ UNSPEC_INT_LABELs. */
+int mn10300_unspec_int_label_counter;
+
+/* This is used in the am33_2.0-linux-gnu port, in which global symbol
+ names are not prefixed by underscores, to tell whether to prefix a
+ label with a plus sign or not, so that the assembler can tell
+ 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 int mn10300_address_cost_1 (rtx, int *);
+static int mn10300_address_cost (rtx);
+static bool mn10300_rtx_costs (rtx, int, int, int *);
+static void mn10300_file_start (void);
+static bool mn10300_return_in_memory (const_tree, const_tree);
+static rtx mn10300_builtin_saveregs (void);
+static void mn10300_va_start (tree, rtx);
+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);
\f
/* Initialize the GCC target structure. */
-
-struct gcc_target target = TARGET_INITIALIZER;
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS mn10300_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST mn10300_address_cost
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START mn10300_file_start
+#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_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
+
+static void mn10300_encode_section_info (tree, rtx, int);
+struct gcc_target targetm = TARGET_INITIALIZER;
\f
-void
-asm_file_start (file)
- FILE *file;
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+mn10300_handle_option (size_t code,
+ const char *arg ATTRIBUTE_UNUSED,
+ int value)
{
- fprintf (file, "#\tGCC For the Matsushita MN10300\n");
- if (optimize)
- fprintf (file, "# -O%d\n", optimize);
- else
- fprintf (file, "\n\n");
+ 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)
- fprintf (file, "\t.am33\n");
- output_file_directive (file, main_input_filename);
+ target_flags &= ~MASK_MULT_BUG;
+}
+
+static void
+mn10300_file_start (void)
+{
+ default_file_start ();
+
+ if (TARGET_AM33_2)
+ fprintf (asm_out_file, "\t.am33_2\n");
+ else if (TARGET_AM33)
+ fprintf (asm_out_file, "\t.am33\n");
}
\f
FILE. */
void
-print_operand (file, x, code)
- FILE *file;
- rtx x;
- int code;
+print_operand (FILE *file, rtx x, int code)
{
switch (code)
{
case 'b':
case 'B':
+ if (cc_status.mdep.fpCC)
+ {
+ switch (code == 'b' ? GET_CODE (x)
+ : reverse_condition_maybe_unordered (GET_CODE (x)))
+ {
+ case NE:
+ fprintf (file, "ne");
+ break;
+ case EQ:
+ fprintf (file, "eq");
+ break;
+ case GE:
+ fprintf (file, "ge");
+ break;
+ case GT:
+ fprintf (file, "gt");
+ break;
+ case LE:
+ fprintf (file, "le");
+ break;
+ case LT:
+ fprintf (file, "lt");
+ break;
+ case ORDERED:
+ fprintf (file, "lge");
+ break;
+ case UNORDERED:
+ fprintf (file, "uo");
+ break;
+ case LTGT:
+ fprintf (file, "lg");
+ break;
+ case UNEQ:
+ fprintf (file, "ue");
+ break;
+ case UNGE:
+ fprintf (file, "uge");
+ break;
+ case UNGT:
+ fprintf (file, "ug");
+ break;
+ case UNLE:
+ fprintf (file, "ule");
+ break;
+ case UNLT:
+ fprintf (file, "ul");
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ }
/* These are normal and reversed branches. */
switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
{
fprintf (file, "cs");
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
case 'C':
else
print_operand (file, x, 0);
break;
-
+
+ case 'D':
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "fd%d", REGNO (x) - 18);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
/* These are the least significant word in a 64bit value. */
case 'L':
switch (GET_CODE (x))
}
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;
case 'A':
fputc ('(', file);
if (GET_CODE (XEXP (x, 0)) == REG)
- output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), GEN_INT (0)));
+ output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), const0_rtx));
else
output_address (XEXP (x, 0));
fputc (')', file);
break;
case 'N':
- output_address (GEN_INT ((~INTVAL (x)) & 0xff));
+ gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
+ fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff));
+ break;
+
+ case 'U':
+ gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
+ fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
break;
/* For shift counts. The hardware ignores the upper bits of
case 'S':
if (GET_CODE (x) == CONST_INT)
{
- fprintf (file, "%d", INTVAL (x) & 0x1f);
+ fprintf (file, "%d", (int)(INTVAL (x) & 0x1f));
break;
}
/* FALL THROUGH */
case CONST:
case LABEL_REF:
case CODE_LABEL:
+ case UNSPEC:
print_operand_address (file, x);
break;
default:
- abort ();
+ gcc_unreachable ();
}
break;
}
/* Output assembly language output for the address ADDR to FILE. */
void
-print_operand_address (file, addr)
- FILE *file;
- rtx addr;
+print_operand_address (FILE *file, rtx addr)
{
switch (GET_CODE (addr))
{
&& 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);;
}
}
+/* Count the number of FP registers that have to be saved. */
+static int
+fp_regs_to_save (void)
+{
+ int i, n = 0;
+
+ if (! TARGET_AM33_2)
+ return 0;
+
+ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
+ if (df_regs_ever_live_p (i) && ! call_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,
- bits 14 to 17 must have the same value. */
+ We assume that the mask already takes that into account. For instance,
+ bits 14 to 17 must have the same value. */
void
-mn10300_print_reg_list (file, mask)
- FILE *file;
- int mask;
+mn10300_print_reg_list (FILE *file, int mask)
{
int need_comma;
int i;
if ((mask & 0x3c000) != 0)
{
- if ((mask & 0x3c000) != 0x3c000)
- abort();
+ gcc_assert ((mask & 0x3c000) == 0x3c000);
if (need_comma)
fputc (',', file);
fputs ("exreg1", file);
}
int
-can_use_return_insn ()
+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;
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);
}
/* Returns the set of live, callee-saved registers as a bitmask. The
callee-saved extended registers cannot be stored individually, so
- all of them will be included in the mask if any one of them is used. */
+ all of them will be included in the mask if any one of them is used. */
int
-mn10300_get_live_callee_saved_regs ()
+mn10300_get_live_callee_saved_regs (void)
{
int mask;
int i;
mask = 0;
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- if (regs_ever_live[i] && ! call_used_regs[i])
+ for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
mask |= (1 << i);
if ((mask & 0x3c000) != 0)
mask |= 0x3c000;
(reg:SI R1))) */
void
-mn10300_gen_multiple_store (mask)
- int mask;
+mn10300_gen_multiple_store (int mask)
{
if (mask != 0)
{
rtx par;
int pari;
- /* Count how many registers need to be saved. */
+ /* Count how many registers need to be saved. */
count = 0;
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
if ((mask & (1 << i)) != 0)
count += 1;
/* We need one PARALLEL element to update the stack pointer and
- an additional element for each register that is stored. */
+ an additional element for each register that is stored. */
par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
- /* Create the instruction that updates the stack pointer. */
+ /* Create the instruction that updates the stack pointer. */
XVECEXP (par, 0, 0)
= gen_rtx_SET (SImode,
stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-count * 4)));
- /* Create each store. */
+ /* Create each store. */
pari = 1;
- for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+ for (i = LAST_EXTENDED_REGNUM; i >= 0; i--)
if ((mask & (1 << i)) != 0)
{
rtx address = gen_rtx_PLUS (SImode,
}
void
-expand_prologue ()
+expand_prologue (void)
{
HOST_WIDE_INT size;
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
- /* If this is an old-style varargs function, then its arguments
- need to be flushed back to the stack. */
- if (current_function_varargs)
+ /* If we use any of the callee-saved registers, save them now. */
+ mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
+
+ if (TARGET_AM33_2 && fp_regs_to_save ())
{
- emit_move_insn (gen_rtx_MEM (SImode,
- plus_constant (stack_pointer_rtx, 4)),
- gen_rtx_REG (SImode, 0));
- emit_move_insn (gen_rtx_MEM (SImode,
- plus_constant (stack_pointer_rtx, 8)),
- gen_rtx_REG (SImode, 1));
- }
+ int num_regs_to_save = fp_regs_to_save (), i;
+ HOST_WIDE_INT xsize;
+ enum { save_sp_merge,
+ save_sp_no_merge,
+ save_sp_partial_merge,
+ save_a0_merge,
+ save_a0_no_merge } strategy;
+ unsigned int strategy_size = (unsigned)-1, this_strategy_size;
+ rtx reg;
+ rtx insn;
+
+ /* We have several different strategies to save FP registers.
+ We can store them using SP offsets, which is beneficial if
+ there are just a few registers to save, or we can use `a0' in
+ post-increment mode (`a0' is the only call-clobbered address
+ register that is never used to pass information to a
+ function). Furthermore, if we don't need a frame pointer, we
+ can merge the two SP adds into a single one, but this isn't
+ always beneficial; sometimes we can just split the two adds
+ so that we don't exceed a 16-bit constant size. The code
+ below will select which strategy to use, so as to generate
+ smallest code. Ties are broken in favor or shorter sequences
+ (in terms of number of instructions). */
+
+#define SIZE_ADD_AX(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
+ : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 2)
+#define SIZE_ADD_SP(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
+ : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 3)
+#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
+ (((S) >= (L)) ? (SIZE1) * (N) \
+ : ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
+ + ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
+ : (ELSE))
+#define SIZE_FMOV_SP_(S,N) \
+ (SIZE_FMOV_LIMIT ((S), (N), (1 << 24), 7, 6, \
+ SIZE_FMOV_LIMIT ((S), (N), (1 << 8), 6, 4, \
+ (S) ? 4 * (N) : 3 + 4 * ((N) - 1))))
+#define SIZE_FMOV_SP(S,N) (SIZE_FMOV_SP_ ((unsigned HOST_WIDE_INT)(S), (N)))
+
+ /* Consider alternative save_sp_merge only if we don't need the
+ frame pointer and size is nonzero. */
+ if (! frame_pointer_needed && size)
+ {
+ /* Insn: add -(size + 4 * num_regs_to_save), sp. */
+ this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
+ /* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
+ this_strategy_size += SIZE_FMOV_SP (size, num_regs_to_save);
- /* If we use any of the callee-saved registers, save them now. */
- mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_sp_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider alternative save_sp_no_merge unconditionally. */
+ /* Insn: add -4 * num_regs_to_save, sp. */
+ this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
+ /* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
+ this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
+ if (size)
+ {
+ /* Insn: add -size, sp. */
+ this_strategy_size += SIZE_ADD_SP (-size);
+ }
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_sp_no_merge;
+ strategy_size = this_strategy_size;
+ }
+
+ /* Consider alternative save_sp_partial_merge only if we don't
+ need a frame pointer and size is reasonably large. */
+ if (! frame_pointer_needed && size + 4 * num_regs_to_save > 128)
+ {
+ /* Insn: add -128, sp. */
+ this_strategy_size = SIZE_ADD_SP (-128);
+ /* Insn: fmov fs#, (##, sp), for each fs# to be saved. */
+ this_strategy_size += SIZE_FMOV_SP (128 - 4 * num_regs_to_save,
+ num_regs_to_save);
+ if (size)
+ {
+ /* Insn: add 128-size, sp. */
+ this_strategy_size += SIZE_ADD_SP (128 - size);
+ }
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_sp_partial_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider alternative save_a0_merge only if we don't need a
+ 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]
+ && ! fixed_regs[FIRST_ADDRESS_REGNUM])
+ {
+ /* Insn: add -(size + 4 * num_regs_to_save), sp. */
+ this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
+ /* Insn: mov sp, a0. */
+ this_strategy_size++;
+ if (size)
+ {
+ /* Insn: add size, a0. */
+ this_strategy_size += SIZE_ADD_AX (size);
+ }
+ /* Insn: fmov fs#, (a0+), for each fs# to be saved. */
+ this_strategy_size += 3 * num_regs_to_save;
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_a0_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Consider alternative save_a0_no_merge if the user hasn't
+ changed the calling conventions of a0. */
+ if (call_used_regs[FIRST_ADDRESS_REGNUM]
+ && ! fixed_regs[FIRST_ADDRESS_REGNUM])
+ {
+ /* Insn: add -4 * num_regs_to_save, sp. */
+ this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
+ /* Insn: mov sp, a0. */
+ this_strategy_size++;
+ /* Insn: fmov fs#, (a0+), for each fs# to be saved. */
+ this_strategy_size += 3 * num_regs_to_save;
+ if (size)
+ {
+ /* Insn: add -size, sp. */
+ this_strategy_size += SIZE_ADD_SP (-size);
+ }
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = save_a0_no_merge;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* Emit the initial SP add, common to all strategies. */
+ switch (strategy)
+ {
+ case save_sp_no_merge:
+ case save_a0_no_merge:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-4 * num_regs_to_save)));
+ xsize = 0;
+ break;
+
+ case save_sp_partial_merge:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-128)));
+ xsize = 128 - 4 * num_regs_to_save;
+ size -= xsize;
+ break;
+
+ case save_sp_merge:
+ case save_a0_merge:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-(size + 4 * num_regs_to_save))));
+ /* We'll have to adjust FP register saves according to the
+ frame size. */
+ xsize = size;
+ /* Since we've already created the stack frame, don't do it
+ again at the end of the function. */
+ size = 0;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Now prepare register a0, if we have decided to use it. */
+ switch (strategy)
+ {
+ case save_sp_merge:
+ case save_sp_no_merge:
+ case save_sp_partial_merge:
+ reg = 0;
+ break;
+
+ case save_a0_merge:
+ case save_a0_no_merge:
+ reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM);
+ emit_insn (gen_movsi (reg, stack_pointer_rtx));
+ if (xsize)
+ emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
+ reg = gen_rtx_POST_INC (SImode, reg);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* Now actually save the FP registers. */
+ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ {
+ rtx addr;
+
+ if (reg)
+ addr = reg;
+ else
+ {
+ /* If we aren't using `a0', use an SP offset. */
+ if (xsize)
+ {
+ addr = gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (xsize));
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ xsize += 4;
+ }
+
+ insn = emit_insn (gen_movsi (gen_rtx_MEM (SImode, addr),
+ gen_rtx_REG (SImode, i)));
+
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
/* Now put the frame pointer into the frame pointer register. */
if (frame_pointer_needed)
emit_insn (gen_addsi3 (stack_pointer_rtx,
stack_pointer_rtx,
GEN_INT (-size)));
+ if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+ emit_insn (gen_GOTaddr2picreg ());
}
void
-expand_epilogue ()
+expand_epilogue (void)
{
HOST_WIDE_INT size;
size = get_frame_size () + current_function_outgoing_args_size;
size += (current_function_outgoing_args_size ? 4 : 0);
+ if (TARGET_AM33_2 && fp_regs_to_save ())
+ {
+ int num_regs_to_save = fp_regs_to_save (), i;
+ rtx reg = 0;
+
+ /* We have several options to restore FP registers. We could
+ load them from SP offsets, but, if there are enough FP
+ registers to restore, we win if we use a post-increment
+ addressing mode. */
+
+ /* If we have a frame pointer, it's the best option, because we
+ already know it has the value we want. */
+ if (frame_pointer_needed)
+ reg = gen_rtx_REG (SImode, FRAME_POINTER_REGNUM);
+ /* Otherwise, we may use `a1', since it's call-clobbered and
+ it's never used for return values. But only do so if it's
+ smaller than using SP offsets. */
+ else
+ {
+ enum { restore_sp_post_adjust,
+ restore_sp_pre_adjust,
+ restore_sp_partial_adjust,
+ restore_a1 } strategy;
+ unsigned int this_strategy_size, strategy_size = (unsigned)-1;
+
+ /* Consider using sp offsets before adjusting sp. */
+ /* Insn: fmov (##,sp),fs#, for each fs# to be restored. */
+ this_strategy_size = SIZE_FMOV_SP (size, num_regs_to_save);
+ /* If size is too large, we'll have to adjust SP with an
+ add. */
+ if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
+ {
+ /* Insn: add size + 4 * num_regs_to_save, sp. */
+ this_strategy_size += SIZE_ADD_SP (size + 4 * num_regs_to_save);
+ }
+ /* If we don't have to restore any non-FP registers,
+ we'll be able to save one byte by using rets. */
+ if (! REG_SAVE_BYTES)
+ this_strategy_size--;
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_sp_post_adjust;
+ strategy_size = this_strategy_size;
+ }
+
+ /* Consider using sp offsets after adjusting sp. */
+ /* Insn: add size, sp. */
+ this_strategy_size = SIZE_ADD_SP (size);
+ /* Insn: fmov (##,sp),fs#, for each fs# to be restored. */
+ this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
+ /* We're going to use ret to release the FP registers
+ save area, so, no savings. */
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_sp_pre_adjust;
+ strategy_size = this_strategy_size;
+ }
+
+ /* Consider using sp offsets after partially adjusting sp.
+ When size is close to 32Kb, we may be able to adjust SP
+ with an imm16 add instruction while still using fmov
+ (d8,sp). */
+ if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
+ {
+ /* Insn: add size + 4 * num_regs_to_save
+ + REG_SAVE_BYTES - 252,sp. */
+ this_strategy_size = SIZE_ADD_SP (size + 4 * num_regs_to_save
+ + REG_SAVE_BYTES - 252);
+ /* Insn: fmov (##,sp),fs#, fo each fs# to be restored. */
+ this_strategy_size += SIZE_FMOV_SP (252 - REG_SAVE_BYTES
+ - 4 * num_regs_to_save,
+ num_regs_to_save);
+ /* We're going to use ret to release the FP registers
+ save area, so, no savings. */
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_sp_partial_adjust;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ /* 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]
+ && ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
+ {
+ /* Insn: mov sp,a1. */
+ this_strategy_size = 1;
+ if (size)
+ {
+ /* Insn: add size,a1. */
+ this_strategy_size += SIZE_ADD_AX (size);
+ }
+ /* Insn: fmov (a1+),fs#, for each fs# to be restored. */
+ this_strategy_size += 3 * num_regs_to_save;
+ /* If size is large enough, we may be able to save a
+ couple of bytes. */
+ if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
+ {
+ /* Insn: mov a1,sp. */
+ this_strategy_size += 2;
+ }
+ /* If we don't have to restore any non-FP registers,
+ we'll be able to save one byte by using rets. */
+ if (! REG_SAVE_BYTES)
+ this_strategy_size--;
+
+ if (this_strategy_size < strategy_size)
+ {
+ strategy = restore_a1;
+ strategy_size = this_strategy_size;
+ }
+ }
+
+ switch (strategy)
+ {
+ case restore_sp_post_adjust:
+ break;
+
+ case restore_sp_pre_adjust:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (size)));
+ size = 0;
+ break;
+
+ case restore_sp_partial_adjust:
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (size + 4 * num_regs_to_save
+ + 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));
+ if (size)
+ emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ /* Adjust the selected register, if any, for post-increment. */
+ if (reg)
+ reg = gen_rtx_POST_INC (SImode, reg);
+
+ for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
+ if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+ {
+ rtx addr;
+
+ if (reg)
+ addr = reg;
+ else if (size)
+ {
+ /* If we aren't using a post-increment register, use an
+ SP offset. */
+ addr = gen_rtx_PLUS (SImode,
+ stack_pointer_rtx,
+ GEN_INT (size));
+ }
+ else
+ addr = stack_pointer_rtx;
+
+ size += 4;
+
+ emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
+ gen_rtx_MEM (SImode, addr)));
+ }
+
+ /* If we were using the restore_a1 strategy and the number of
+ bytes to be released won't fit in the `ret' byte, copy `a1'
+ to `sp', to avoid having to use `add' to adjust it. */
+ if (! frame_pointer_needed && reg && size + REG_SAVE_BYTES > 255)
+ {
+ emit_move_insn (stack_pointer_rtx, XEXP (reg, 0));
+ size = 0;
+ }
+ }
+
/* Maybe cut back the stack, except for the register save area.
If the frame pointer exists, then use the frame pointer to
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.
- Under no circumstanes should the register save area be
+ Under no circumstances should the register save area be
deallocated here, that would leave a window where an interrupt
could occur and trash the register save area. */
if (frame_pointer_needed)
}
/* 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)));
/* Update the condition code from the insn. */
void
-notice_update_cc (body, insn)
- rtx body;
- rtx insn;
+notice_update_cc (rtx body, rtx insn)
{
switch (get_attr_cc (insn))
{
/* The insn is a compare instruction. */
CC_STATUS_INIT;
cc_status.value1 = SET_SRC (body);
- break;
-
- case CC_INVERT:
- /* The insn is a compare instruction. */
- CC_STATUS_INIT;
- cc_status.value1 = SET_SRC (body);
- cc_status.flags |= CC_INVERTED;
+ if (GET_CODE (cc_status.value1) == COMPARE
+ && GET_MODE (XEXP (cc_status.value1, 0)) == SFmode)
+ cc_status.mdep.fpCC = 1;
break;
case CC_CLOBBER:
break;
default:
- abort ();
+ gcc_unreachable ();
}
}
-/* Recognise the PARALLEL rtx generated by mn10300_gen_multiple_store().
+/* Recognize the PARALLEL rtx generated by mn10300_gen_multiple_store().
This function is for MATCH_PARALLEL and so assumes OP is known to be
parallel. If OP is a multiple store, return a mask indicating which
registers it saves. Return 0 otherwise. */
int
-store_multiple_operation (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
int count;
int mask;
/* Check that A is the stack pointer and B is the expected stack size.
For OP to match, each subsequent instruction should push a word onto
the stack. We therefore expect the first instruction to create
- COUNT-1 stack slots. */
+ COUNT-1 stack slots. */
elt = SET_SRC (elt);
if (GET_CODE (XEXP (elt, 0)) != REG
|| REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
store a lower-numbered register to the slot below.
LAST keeps track of the smallest-numbered register stored so far.
- MASK is the set of stored registers. */
- last = FIRST_PSEUDO_REGISTER;
+ MASK is the set of stored registers. */
+ last = LAST_EXTENDED_REGNUM + 1;
mask = 0;
for (i = 1; i < count; i++)
{
- /* Check that element i is a (set (mem M) R) and that R is valid. */
+ /* Check that element i is a (set (mem M) R) and that R is valid. */
elt = XVECEXP (op, 0, i);
if (GET_CODE (elt) != SET
|| GET_CODE (SET_DEST (elt)) != MEM
return 0;
/* R was OK, so provisionally add it to MASK. We return 0 in any
- case if the rest of the instruction has a flaw. */
+ case if the rest of the instruction has a flaw. */
last = REGNO (SET_SRC (elt));
mask |= (1 << last);
return 0;
}
- /* All or none of the callee-saved extended registers must be in the set. */
+ /* All or none of the callee-saved extended registers must be in the set. */
if ((mask & 0x3c000) != 0
&& (mask & 0x3c000) != 0x3c000)
return 0;
return mask;
}
-/* Return true if OP is a valid call operand. */
-
-int
-call_address_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- 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 CLASS.
We might be able to simplify this. */
enum reg_class
-secondary_reload_class (class, mode, in)
- enum reg_class class;
- enum machine_mode mode;
- rtx in;
+mn10300_secondary_reload_class (enum reg_class class, enum machine_mode mode,
+ rtx in)
{
/* Memory loads less than a full word wide can't have an
address or stack pointer destination. They must use
if (GET_CODE (in) == PLUS
&& (XEXP (in, 0) == stack_pointer_rtx
|| XEXP (in, 1) == stack_pointer_rtx))
+ return GENERAL_REGS;
+
+ if (TARGET_AM33_2 && class == FP_REGS
+ && GET_CODE (in) == MEM
+ && ! (GET_CODE (in) == MEM && !CONSTANT_ADDRESS_P (XEXP (in, 0))))
{
if (TARGET_AM33)
return DATA_OR_EXTENDED_REGS;
return DATA_REGS;
}
-
+
/* Otherwise assume no secondary reloads are needed. */
return NO_REGS;
}
int
-initial_offset (from, to)
- int from, to;
+initial_offset (int from, int to)
{
/* The difference between the argument pointer and the frame pointer
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;
+ return REG_SAVE_BYTES
+ + 4 * fp_regs_to_save ();
else
return 0;
}
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));
+ ? current_function_outgoing_args_size + 4 : 0));
else
return (get_frame_size ()
+ (current_function_outgoing_args_size
- ? current_function_outgoing_args_size + 4 : 0));
+ ? current_function_outgoing_args_size + 4 : 0));
}
/* The difference between the frame pointer and stack pointer is the sum
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));
+ ? current_function_outgoing_args_size + 4 : 0));
- abort ();
+ gcc_unreachable ();
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY. */
+
+static bool
+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
+ || int_size_in_bytes (type) == 0
+ || TYPE_MODE (type) == BLKmode);
}
/* Flush the argument registers to the stack for a stdarg function;
return the new argument pointer. */
-rtx
-mn10300_builtin_saveregs ()
+static rtx
+mn10300_builtin_saveregs (void)
{
rtx offset, mem;
tree fntype = TREE_TYPE (current_function_decl);
&& (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
!= void_type_node)))
? 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 = current_function_arg_offset_rtx;
mem = gen_rtx_MEM (SImode, current_function_internal_arg_pointer);
- MEM_ALIAS_SET (mem) = set;
+ 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));
- MEM_ALIAS_SET (mem) = set;
+ set_mem_alias_set (mem, set);
emit_move_insn (mem, gen_rtx_REG (SImode, 1));
return copy_to_reg (expand_binop (Pmode, add_optab,
offset, 0, 0, OPTAB_LIB_WIDEN));
}
-void
-mn10300_va_start (stdarg_p, valist, nextarg)
- int stdarg_p;
- tree valist;
- rtx nextarg;
+static void
+mn10300_va_start (tree valist, rtx nextarg)
{
- if (stdarg_p)
- nextarg = expand_builtin_saveregs ();
-
- std_expand_builtin_va_start (stdarg_p, valist, nextarg);
+ nextarg = expand_builtin_saveregs ();
+ std_expand_builtin_va_start (valist, nextarg);
}
-rtx
-mn10300_va_arg (valist, type)
- tree valist, 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. */
rtx
-function_arg (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type;
- int named ATTRIBUTE_UNUSED;
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int named ATTRIBUTE_UNUSED)
{
rtx result = 0;
int size, align;
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 (cum, mode, type, named)
- 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. */
+
+rtx
+mn10300_function_value (const_tree valtype, const_tree func, int 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
+ || current_function_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;
}
/* Output a tst insn. */
-char *
-output_tst (operand, insn)
- rtx operand, insn;
+const char *
+output_tst (rtx operand, rtx insn)
{
rtx temp;
int past_call = 0;
continue;
}
- /* It must be an insn, see if it is a simple set. */
+ /* It must be an insn, see if it is a simple set. */
set = single_set (temp);
if (!set)
{
}
/* 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
+ && (!past_call
|| !call_used_regs[REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
!= REGNO_REG_CLASS (REGNO (operand)))
&& REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS
&& REGNO (SET_DEST (set)) != REGNO (operand)
- && (!past_call
+ && (!past_call
|| !call_used_regs[REGNO (SET_DEST (set))]))
{
rtx xoperands[2];
}
int
-impossible_plus_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+impossible_plus_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
if (GET_CODE (op) != PLUS)
return 0;
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 (op, mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= 0
- && INTVAL (op) < 256);
-}
-
/* Similarly, but when using a zero_extract pattern for a btst where
the source operand might end up in memory. */
int
-mask_ok_for_mem_btst (len, bit)
- int len;
- int bit;
+mask_ok_for_mem_btst (int len, int bit)
{
- int mask = 0;
+ unsigned int mask = 0;
while (len > 0)
{
expressions will have one of a few well defined forms, so
we need only check those forms. */
int
-symbolic_operand (op, mode)
- register rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+symbolic_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
switch (GET_CODE (op))
{
But on a few ports with segmented architectures and indexed addressing
(mn10300, hppa) it is used to rewrite certain problematical addresses. */
rtx
-legitimize_address (x, oldx, mode)
- rtx x;
- rtx oldx ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+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);
+
/* Uh-oh. We might have an address for x[n-100000]. This needs
special handling to avoid creating an indexed memory address
with x-100000 as the base. */
regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0));
regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0));
regx1 = force_reg (Pmode,
- gen_rtx (GET_CODE (y), Pmode, regx1, regy2));
+ gen_rtx_fmt_ee (GET_CODE (y), Pmode, regx1, regy2));
return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1));
}
}
return x;
}
+/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
+ @GOTOFF in `reg'. */
+rtx
+legitimize_pic_address (rtx orig, rtx reg)
+{
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF
+ && (CONSTANT_POOL_ADDRESS_P (orig)
+ || ! MN10300_GLOBAL_P (orig))))
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOTOFF2reg (reg, orig));
+ return reg;
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == 0)
+ reg = gen_reg_rtx (Pmode);
+
+ emit_insn (gen_symGOT2reg (reg, orig));
+ return reg;
+ }
+ return orig;
+}
+
+/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec; nonzero otherwise. */
int
-mn10300_address_cost (x, unsig)
- rtx x;
- int *unsig;
+legitimate_pic_operand_p (rtx x)
+{
+ register const char *fmt;
+ register int i;
+
+ if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ return 0;
+
+ if (GET_CODE (x) == UNSPEC
+ && (XINT (x, 1) == UNSPEC_PIC
+ || XINT (x, 1) == UNSPEC_GOT
+ || XINT (x, 1) == UNSPEC_GOTOFF
+ || XINT (x, 1) == UNSPEC_PLT))
+ return 1;
+
+ fmt = GET_RTX_FORMAT (GET_CODE (x));
+ for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'E')
+ {
+ register int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
+ return 0;
+ }
+ else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is
+ legitimate, and FALSE otherwise. */
+bool
+legitimate_address_p (enum machine_mode mode, rtx x, int 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)
{
- int _s = 0;
- if (unsig == 0)
- unsig = &_s;
-
switch (GET_CODE (x))
{
case REG:
case DATA_REGS:
case EXTENDED_REGS:
+ case FP_REGS:
return 3;
case NO_REGS:
return 5;
default:
- abort ();
+ gcc_unreachable ();
}
case PLUS:
case ASHIFT:
case AND:
case IOR:
- return (mn10300_address_cost (XEXP (x, 0), unsig)
- + mn10300_address_cost (XEXP (x, 1), unsig));
+ return (mn10300_address_cost_1 (XEXP (x, 0), unsig)
+ + mn10300_address_cost_1 (XEXP (x, 1), unsig));
case EXPR_LIST:
case SUBREG:
case MEM:
- return ADDRESS_COST (XEXP (x, 0));
+ return mn10300_address_cost (XEXP (x, 0));
case ZERO_EXTEND:
*unsig = 1;
- return mn10300_address_cost (XEXP (x, 0), unsig);
+ return mn10300_address_cost_1 (XEXP (x, 0), unsig);
case CONST_INT:
if (INTVAL (x) == 0)
case LABEL_REF:
return 8;
- case ADDRESSOF:
- switch (GET_CODE (XEXP (x, 0)))
- {
- case MEM:
- return ADDRESS_COST (XEXP (x, 0));
+ default:
+ gcc_unreachable ();
- case REG:
- return 1;
+ }
+}
- default:
- abort ();
- }
+static int
+mn10300_address_cost (rtx x)
+{
+ int s = 0;
+ return mn10300_address_cost_1 (x, &s);
+}
+
+static bool
+mn10300_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+ switch (code)
+ {
+ case CONST_INT:
+ /* Zeros are extremely cheap. */
+ if (INTVAL (x) == 0 && outer_code == SET)
+ *total = 0;
+ /* If it fits in 8 bits, then it's still relatively cheap. */
+ else if (INT_8_BITS (INTVAL (x)))
+ *total = 1;
+ /* This is the "base" cost, includes constants where either the
+ upper or lower 16bits are all zeros. */
+ else if (INT_16_BITS (INTVAL (x))
+ || (INTVAL (x) & 0xffff) == 0
+ || (INTVAL (x) & 0xffff0000) == 0)
+ *total = 2;
+ else
+ *total = 4;
+ return true;
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ /* These are more costly than a CONST_INT, but we can relax them,
+ so they're less costly than a CONST_DOUBLE. */
+ *total = 6;
+ return true;
+
+ case CONST_DOUBLE:
+ /* We don't optimize CONST_DOUBLEs well nor do we relax them well,
+ so their cost is very high. */
+ *total = 8;
+ return true;
+
+ /* ??? This probably needs more work. */
+ case MOD:
+ case DIV:
+ case MULT:
+ *total = 8;
+ return true;
default:
- abort ();
+ return false;
+ }
+}
+/* Check whether a constant used to initialize a DImode or DFmode can
+ use a clr instruction. The code here must be kept in sync with
+ movdf and movdi. */
+
+bool
+mn10300_wide_const_load_uses_clr (rtx operands[2])
+{
+ long val[2];
+
+ if (GET_CODE (operands[0]) != REG
+ || REGNO_REG_CLASS (REGNO (operands[0])) != DATA_REGS)
+ return false;
+
+ switch (GET_CODE (operands[1]))
+ {
+ case CONST_INT:
+ {
+ rtx low, high;
+ split_double (operands[1], &low, &high);
+ val[0] = INTVAL (low);
+ val[1] = INTVAL (high);
+ }
+ break;
+
+ case CONST_DOUBLE:
+ if (GET_MODE (operands[1]) == DFmode)
+ {
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
+ }
+ else if (GET_MODE (operands[1]) == VOIDmode
+ || GET_MODE (operands[1]) == DImode)
+ {
+ val[0] = CONST_DOUBLE_LOW (operands[1]);
+ val[1] = CONST_DOUBLE_HIGH (operands[1]);
+ }
+ break;
+
+ default:
+ return false;
}
+
+ return val[0] == 0 || val[1] == 0;
+}
+/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
+ may access it using GOTOFF instead of GOT. */
+
+static void
+mn10300_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
+{
+ rtx symbol;
+
+ if (GET_CODE (rtl) != MEM)
+ return;
+ symbol = XEXP (rtl, 0);
+ if (GET_CODE (symbol) != SYMBOL_REF)
+ return;
+
+ if (flag_pic)
+ SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
}