aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.6/gcc/config/mn10300/mn10300.c
diff options
context:
space:
mode:
authorJing Yu <jingyu@google.com>2011-12-19 16:56:54 -0800
committerJing Yu <jingyu@google.com>2011-12-19 16:56:54 -0800
commit40d7cd0fd78fe2004e2a53c4618c148339b02733 (patch)
tree5874557a6c86a1f564a03e5f28b266e31bc3759c /gcc-4.6/gcc/config/mn10300/mn10300.c
parentfe2afdf3f3701489c05d2a7509752d6f0c7616f7 (diff)
downloadtoolchain_gcc-40d7cd0fd78fe2004e2a53c4618c148339b02733.tar.gz
toolchain_gcc-40d7cd0fd78fe2004e2a53c4618c148339b02733.tar.bz2
toolchain_gcc-40d7cd0fd78fe2004e2a53c4618c148339b02733.zip
Add gcc-4.6. Synced to @180989
Change-Id: Ie3676586e1d8e3c8cd9f07d022f450d05fa08439 svn://gcc.gnu.org/svn/gcc/branches/google/gcc-4_6-mobile
Diffstat (limited to 'gcc-4.6/gcc/config/mn10300/mn10300.c')
-rw-r--r--gcc-4.6/gcc/config/mn10300/mn10300.c3251
1 files changed, 3251 insertions, 0 deletions
diff --git a/gcc-4.6/gcc/config/mn10300/mn10300.c b/gcc-4.6/gcc/config/mn10300/mn10300.c
new file mode 100644
index 000000000..0b68f2d3e
--- /dev/null
+++ b/gcc-4.6/gcc/config/mn10300/mn10300.c
@@ -0,0 +1,3251 @@
+/* Subroutines for insn-output.c for Matsushita MN10300 series
+ Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ 2005, 2006, 2007, 2008, 2009, 2010, 2011 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 3, or (at your option)
+ any later version.
+
+ 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 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 "hard-reg-set.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 "tm_p.h"
+#include "tm-constrs.h"
+#include "target.h"
+#include "target-def.h"
+#include "df.h"
+
+/* 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;
+
+/* Processor type to select for tuning. */
+static const char * mn10300_tune_string = NULL;
+
+/* Selected processor type for tuning. */
+enum processor_type mn10300_tune_cpu = 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 * 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)))
+
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
+static const struct default_options mn10300_option_optimization_table[] =
+ {
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
+
+#define CC_FLAG_Z 1
+#define CC_FLAG_N 2
+#define CC_FLAG_C 4
+#define CC_FLAG_V 8
+
+static int cc_flags_for_mode(enum machine_mode);
+static int cc_flags_for_code(enum rtx_code);
+
+/* 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;
+
+ case OPT_mam34:
+ mn10300_processor = (value ? PROCESSOR_AM34 : PROCESSOR_DEFAULT);
+ return true;
+
+ case OPT_mtune_:
+ mn10300_tune_string = arg;
+ return true;
+
+ default:
+ return true;
+ }
+}
+
+/* Implement TARGET_OPTION_OVERRIDE. */
+
+static void
+mn10300_option_override (void)
+{
+ if (TARGET_AM33)
+ target_flags &= ~MASK_MULT_BUG;
+ else
+ {
+ /* Disable scheduling for the MN10300 as we do
+ not have timing information available for it. */
+ flag_schedule_insns = 0;
+ flag_schedule_insns_after_reload = 0;
+
+ /* Force enable splitting of wide types, as otherwise it is trivial
+ to run out of registers. Indeed, this works so well that register
+ allocation problems are now more common *without* optimization,
+ when this flag is not enabled by default. */
+ flag_split_wide_types = 1;
+ }
+
+ if (mn10300_tune_string)
+ {
+ if (strcasecmp (mn10300_tune_string, "mn10300") == 0)
+ mn10300_tune_cpu = PROCESSOR_MN10300;
+ else if (strcasecmp (mn10300_tune_string, "am33") == 0)
+ mn10300_tune_cpu = PROCESSOR_AM33;
+ else if (strcasecmp (mn10300_tune_string, "am33-2") == 0)
+ mn10300_tune_cpu = PROCESSOR_AM33_2;
+ else if (strcasecmp (mn10300_tune_string, "am34") == 0)
+ mn10300_tune_cpu = PROCESSOR_AM34;
+ else
+ error ("-mtune= expects mn10300, am33, am33-2, or am34");
+ }
+}
+
+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");
+}
+
+/* Note: This list must match the liw_op attribute in mn10300.md. */
+
+static const char *liw_op_names[] =
+{
+ "add", "cmp", "sub", "mov",
+ "and", "or", "xor",
+ "asr", "lsr", "asl",
+ "none", "max"
+};
+
+/* Print operand X using operand code CODE to assembly language output file
+ FILE. */
+
+void
+mn10300_print_operand (FILE *file, rtx x, int code)
+{
+ switch (code)
+ {
+ case 'W':
+ {
+ unsigned int liw_op = UINTVAL (x);
+
+ gcc_assert (TARGET_ALLOW_LIW);
+ gcc_assert (liw_op < LIW_OP_MAX);
+ fputs (liw_op_names[liw_op], file);
+ break;
+ }
+
+ case 'b':
+ case 'B':
+ {
+ enum rtx_code cmp = GET_CODE (x);
+ enum machine_mode mode = GET_MODE (XEXP (x, 0));
+ const char *str;
+ int have_flags;
+
+ if (code == 'B')
+ cmp = reverse_condition (cmp);
+ have_flags = cc_flags_for_mode (mode);
+
+ switch (cmp)
+ {
+ case NE:
+ str = "ne";
+ break;
+ case EQ:
+ str = "eq";
+ break;
+ case GE:
+ /* bge is smaller than bnc. */
+ str = (have_flags & CC_FLAG_V ? "ge" : "nc");
+ break;
+ case LT:
+ str = (have_flags & CC_FLAG_V ? "lt" : "ns");
+ break;
+ case GT:
+ str = "gt";
+ break;
+ case LE:
+ str = "le";
+ break;
+ case GEU:
+ str = "cc";
+ break;
+ case GTU:
+ str = "hi";
+ break;
+ case LEU:
+ str = "ls";
+ break;
+ case LTU:
+ str = "cs";
+ break;
+ case ORDERED:
+ str = "lge";
+ break;
+ case UNORDERED:
+ str = "uo";
+ break;
+ case LTGT:
+ str = "lg";
+ break;
+ case UNEQ:
+ str = "ue";
+ break;
+ case UNGE:
+ str = "uge";
+ break;
+ case UNGT:
+ str = "ug";
+ break;
+ case UNLE:
+ str = "ule";
+ break;
+ case UNLT:
+ str = "ul";
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ gcc_checking_assert ((cc_flags_for_code (cmp) & ~have_flags) == 0);
+ fputs (str, file);
+ }
+ break;
+
+ case 'C':
+ /* This is used for the operand to a call instruction;
+ if it's a REG, enclose it in parens, else output
+ the operand normally. */
+ if (REG_P (x))
+ {
+ fputc ('(', file);
+ mn10300_print_operand (file, x, 0);
+ fputc (')', file);
+ }
+ else
+ mn10300_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))
+ {
+ case MEM:
+ fputc ('(', file);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ break;
+
+ case SUBREG:
+ fprintf (file, "%s", reg_names[subreg_regno (x)]);
+ break;
+
+ case CONST_DOUBLE:
+ {
+ long val[2];
+ REAL_VALUE_TYPE rv;
+
+ switch (GET_MODE (x))
+ {
+ case DFmode:
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
+ fprintf (file, "0x%lx", val[0]);
+ break;;
+ case SFmode:
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]);
+ fprintf (file, "0x%lx", val[0]);
+ break;;
+ case VOIDmode:
+ case DImode:
+ mn10300_print_operand_address (file,
+ GEN_INT (CONST_DOUBLE_LOW (x)));
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case CONST_INT:
+ {
+ rtx low, high;
+ split_double (x, &low, &high);
+ fprintf (file, "%ld", (long)INTVAL (low));
+ break;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ /* Similarly, but for the most significant word. */
+ case 'H':
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ x = adjust_address (x, SImode, 4);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x) + 1]);
+ break;
+
+ case SUBREG:
+ fprintf (file, "%s", reg_names[subreg_regno (x) + 1]);
+ break;
+
+ case CONST_DOUBLE:
+ {
+ long val[2];
+ REAL_VALUE_TYPE rv;
+
+ switch (GET_MODE (x))
+ {
+ case DFmode:
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
+ fprintf (file, "0x%lx", val[1]);
+ break;;
+ case SFmode:
+ gcc_unreachable ();
+ case VOIDmode:
+ case DImode:
+ mn10300_print_operand_address (file,
+ GEN_INT (CONST_DOUBLE_HIGH (x)));
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ case CONST_INT:
+ {
+ rtx low, high;
+ split_double (x, &low, &high);
+ fprintf (file, "%ld", (long)INTVAL (high));
+ break;
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+ break;
+
+ case 'A':
+ fputc ('(', file);
+ if (REG_P (XEXP (x, 0)))
+ output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), const0_rtx));
+ else
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case 'N':
+ 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
+ any immediate, but the assembler will flag an out of range
+ shift count as an error. So we mask off the high bits
+ of the immediate here. */
+ case 'S':
+ if (CONST_INT_P (x))
+ {
+ fprintf (file, "%d", (int)(INTVAL (x) & 0x1f));
+ break;
+ }
+ /* FALL THROUGH */
+
+ default:
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ fputc ('(', file);
+ output_address (XEXP (x, 0));
+ fputc (')', file);
+ break;
+
+ case PLUS:
+ output_address (x);
+ break;
+
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (x)]);
+ break;
+
+ case SUBREG:
+ fprintf (file, "%s", reg_names[subreg_regno (x)]);
+ break;
+
+ /* This will only be single precision.... */
+ case CONST_DOUBLE:
+ {
+ unsigned long val;
+ REAL_VALUE_TYPE rv;
+
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, val);
+ fprintf (file, "0x%lx", val);
+ break;
+ }
+
+ case CONST_INT:
+ case SYMBOL_REF:
+ case CONST:
+ case LABEL_REF:
+ case CODE_LABEL:
+ case UNSPEC:
+ mn10300_print_operand_address (file, x);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ break;
+ }
+}
+
+/* Output assembly language output for the address ADDR to FILE. */
+
+void
+mn10300_print_operand_address (FILE *file, rtx addr)
+{
+ switch (GET_CODE (addr))
+ {
+ case POST_INC:
+ mn10300_print_operand (file, XEXP (addr, 0), 0);
+ fputc ('+', file);
+ break;
+
+ case POST_MODIFY:
+ mn10300_print_operand (file, XEXP (addr, 0), 0);
+ fputc ('+', file);
+ fputc (',', file);
+ mn10300_print_operand (file, XEXP (addr, 1), 0);
+ break;
+
+ case REG:
+ mn10300_print_operand (file, addr, 0);
+ break;
+ case PLUS:
+ {
+ rtx base = XEXP (addr, 0);
+ rtx index = XEXP (addr, 1);
+
+ if (REG_P (index) && !REG_OK_FOR_INDEX_P (index))
+ {
+ rtx x = base;
+ base = index;
+ index = x;
+
+ gcc_assert (REG_P (index) && REG_OK_FOR_INDEX_P (index));
+ }
+ gcc_assert (REG_OK_FOR_BASE_P (base));
+
+ mn10300_print_operand (file, index, 0);
+ fputc (',', file);
+ mn10300_print_operand (file, base, 0);
+ break;
+ }
+ case SYMBOL_REF:
+ output_addr_const (file, addr);
+ break;
+ default:
+ output_addr_const (file, addr);
+ break;
+ }
+}
+
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA.
+
+ Used for PIC-specific UNSPECs. */
+
+static bool
+mn10300_asm_output_addr_const_extra (FILE *file, rtx x)
+{
+ if (GET_CODE (x) == UNSPEC)
+ {
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_PIC:
+ /* GLOBAL_OFFSET_TABLE or local symbols, no suffix. */
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ break;
+ case UNSPEC_GOT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOT", file);
+ break;
+ case UNSPEC_GOTOFF:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@GOTOFF", file);
+ break;
+ case UNSPEC_PLT:
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("@PLT", file);
+ break;
+ case UNSPEC_GOTSYM_OFF:
+ assemble_name (file, GOT_SYMBOL_NAME);
+ fputs ("-(", file);
+ output_addr_const (file, XVECEXP (x, 0, 0));
+ fputs ("-.)", file);
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+/* 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_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 already takes that into account. For instance,
+ bits 14 to 17 must have the same value. */
+
+void
+mn10300_print_reg_list (FILE *file, int mask)
+{
+ int need_comma;
+ int i;
+
+ need_comma = 0;
+ fputc ('[', file);
+
+ for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
+ if ((mask & (1 << i)) != 0)
+ {
+ if (need_comma)
+ fputc (',', file);
+ fputs (reg_names [i], file);
+ need_comma = 1;
+ }
+
+ if ((mask & 0x3c000) != 0)
+ {
+ gcc_assert ((mask & 0x3c000) == 0x3c000);
+ if (need_comma)
+ fputc (',', file);
+ fputs ("exreg1", file);
+ need_comma = 1;
+ }
+
+ fputc (']', file);
+}
+
+/* If the MDR register is never clobbered, we can use the RETF instruction
+ which takes the address from the MDR register. This is 3 cycles faster
+ than having to load the address from the stack. */
+
+bool
+mn10300_can_use_retf_insn (void)
+{
+ /* Don't bother if we're not optimizing. In this case we won't
+ have proper access to df_regs_ever_live_p. */
+ if (!optimize)
+ return false;
+
+ /* EH returns alter the saved return address; MDR is not current. */
+ if (crtl->calls_eh_return)
+ return false;
+
+ /* Obviously not if MDR is ever clobbered. */
+ if (df_regs_ever_live_p (MDR_REG))
+ return false;
+
+ /* ??? Careful not to use this during expand_epilogue etc. */
+ gcc_assert (!in_sequence_p ());
+ return leaf_function_p ();
+}
+
+bool
+mn10300_can_use_rets_insn (void)
+{
+ return !mn10300_initial_offset (ARG_POINTER_REGNUM, STACK_POINTER_REGNUM);
+}
+
+/* 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. */
+
+int
+mn10300_get_live_callee_saved_regs (void)
+{
+ int mask;
+ int i;
+
+ mask = 0;
+ for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
+ if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
+ mask |= (1 << i);
+ if ((mask & 0x3c000) != 0)
+ mask |= 0x3c000;
+
+ return mask;
+}
+
+static rtx
+F (rtx r)
+{
+ RTX_FRAME_RELATED_P (r) = 1;
+ return r;
+}
+
+/* Generate an instruction that pushes several registers onto the stack.
+ Register K will be saved if bit K in MASK is set. The function does
+ nothing if MASK is zero.
+
+ To be compatible with the "movm" instruction, the lowest-numbered
+ register must be stored in the lowest slot. If MASK is the set
+ { R1,...,RN }, where R1...RN are ordered least first, the generated
+ instruction will have the form:
+
+ (parallel
+ (set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4)))
+ (set (mem:SI (plus:SI (reg:SI 9)
+ (const_int -1*4)))
+ (reg:SI RN))
+ ...
+ (set (mem:SI (plus:SI (reg:SI 9)
+ (const_int -N*4)))
+ (reg:SI R1))) */
+
+static void
+mn10300_gen_multiple_store (unsigned int mask)
+{
+ /* The order in which registers are stored, from SP-4 through SP-N*4. */
+ static const unsigned int store_order[8] = {
+ /* e2, e3: never saved */
+ FIRST_EXTENDED_REGNUM + 4,
+ FIRST_EXTENDED_REGNUM + 5,
+ FIRST_EXTENDED_REGNUM + 6,
+ FIRST_EXTENDED_REGNUM + 7,
+ /* e0, e1, mdrq, mcrh, mcrl, mcvf: never saved. */
+ FIRST_DATA_REGNUM + 2,
+ FIRST_DATA_REGNUM + 3,
+ FIRST_ADDRESS_REGNUM + 2,
+ FIRST_ADDRESS_REGNUM + 3,
+ /* d0, d1, a0, a1, mdr, lir, lar: never saved. */
+ };
+
+ rtx x, elts[9];
+ unsigned int i;
+ int count;
+
+ if (mask == 0)
+ return;
+
+ for (i = count = 0; i < ARRAY_SIZE(store_order); ++i)
+ {
+ unsigned regno = store_order[i];
+
+ if (((mask >> regno) & 1) == 0)
+ continue;
+
+ ++count;
+ x = plus_constant (stack_pointer_rtx, count * -4);
+ x = gen_frame_mem (SImode, x);
+ x = gen_rtx_SET (VOIDmode, x, gen_rtx_REG (SImode, regno));
+ elts[count] = F(x);
+
+ /* Remove the register from the mask so that... */
+ mask &= ~(1u << regno);
+ }
+
+ /* ... we can make sure that we didn't try to use a register
+ not listed in the store order. */
+ gcc_assert (mask == 0);
+
+ /* Create the instruction that updates the stack pointer. */
+ x = plus_constant (stack_pointer_rtx, count * -4);
+ x = gen_rtx_SET (VOIDmode, stack_pointer_rtx, x);
+ elts[0] = F(x);
+
+ /* We need one PARALLEL element to update the stack pointer and
+ an additional element for each register that is stored. */
+ x = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (count + 1, elts));
+ F (emit_insn (x));
+}
+
+void
+mn10300_expand_prologue (void)
+{
+ HOST_WIDE_INT size = mn10300_frame_size ();
+
+ /* 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 ())
+ {
+ 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;
+
+ /* 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)
+
+/* We add 0 * (S) in two places to promote to the type of S,
+ so that all arms of the conditional have the same type. */
+#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
+ (((S) >= (L)) ? 0 * (S) + (SIZE1) * (N) \
+ : ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
+ + ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
+ : 0 * (S) + (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 (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_really_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_really_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:
+ F (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:
+ F (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:
+ F (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);
+ F (emit_insn (gen_movsi (reg, stack_pointer_rtx)));
+ if (xsize)
+ F (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_really_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;
+ }
+
+ F (emit_insn (gen_movsf (gen_rtx_MEM (SFmode, addr),
+ gen_rtx_REG (SFmode, i))));
+ }
+ }
+
+ /* Now put the frame pointer into the frame pointer register. */
+ if (frame_pointer_needed)
+ F (emit_move_insn (frame_pointer_rtx, stack_pointer_rtx));
+
+ /* Allocate stack for this frame. */
+ if (size)
+ F (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_load_pic ());
+}
+
+void
+mn10300_expand_epilogue (void)
+{
+ HOST_WIDE_INT size = mn10300_frame_size ();
+ int reg_save_bytes = REG_SAVE_BYTES;
+
+ 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_really_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_really_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_movsf (gen_rtx_REG (SFmode, i),
+ gen_rtx_MEM (SFmode, 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
+ cut back the stack.
+
+ 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.
+
+ 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 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)
+ {
+ emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
+ size = 0;
+ }
+ else if (size + reg_save_bytes > 255)
+ {
+ emit_insn (gen_addsi3 (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (size)));
+ size = 0;
+ }
+
+ /* Adjust the stack and restore callee-saved registers, if any. */
+ if (mn10300_can_use_rets_insn ())
+ emit_jump_insn (gen_rtx_RETURN (VOIDmode));
+ else
+ emit_jump_insn (gen_return_ret (GEN_INT (size + REG_SAVE_BYTES)));
+}
+
+/* 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
+mn10300_store_multiple_operation (rtx op,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ int count;
+ int mask;
+ int i;
+ unsigned int last;
+ rtx elt;
+
+ count = XVECLEN (op, 0);
+ if (count < 2)
+ return 0;
+
+ /* Check that first instruction has the form (set (sp) (plus A B)) */
+ elt = XVECEXP (op, 0, 0);
+ if (GET_CODE (elt) != SET
+ || (! REG_P (SET_DEST (elt)))
+ || REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
+ || GET_CODE (SET_SRC (elt)) != PLUS)
+ return 0;
+
+ /* 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. */
+ elt = SET_SRC (elt);
+ if ((! REG_P (XEXP (elt, 0)))
+ || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
+ || (! CONST_INT_P (XEXP (elt, 1)))
+ || INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
+ return 0;
+
+ mask = 0;
+ for (i = 1; i < count; i++)
+ {
+ /* Check that element i is a (set (mem M) R). */
+ /* ??? Validate the register order a-la mn10300_gen_multiple_store.
+ Remember: the ordering is *not* monotonic. */
+ elt = XVECEXP (op, 0, i);
+ if (GET_CODE (elt) != SET
+ || (! MEM_P (SET_DEST (elt)))
+ || (! REG_P (SET_SRC (elt))))
+ return 0;
+
+ /* Remember which registers are to be saved. */
+ last = REGNO (SET_SRC (elt));
+ mask |= (1 << last);
+
+ /* Check that M has the form (plus (sp) (const_int -I*4)) */
+ elt = XEXP (SET_DEST (elt), 0);
+ if (GET_CODE (elt) != PLUS
+ || (! REG_P (XEXP (elt, 0)))
+ || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
+ || (! CONST_INT_P (XEXP (elt, 1)))
+ || INTVAL (XEXP (elt, 1)) != -i * 4)
+ return 0;
+ }
+
+ /* 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;
+}
+
+/* Implement TARGET_PREFERRED_RELOAD_CLASS. */
+
+static reg_class_t
+mn10300_preferred_reload_class (rtx x, reg_class_t rclass)
+{
+ if (x == stack_pointer_rtx && rclass != SP_REGS)
+ return (TARGET_AM33 ? GENERAL_REGS : ADDRESS_REGS);
+ else if (MEM_P (x)
+ || (REG_P (x)
+ && !HARD_REGISTER_P (x))
+ || (GET_CODE (x) == SUBREG
+ && REG_P (SUBREG_REG (x))
+ && !HARD_REGISTER_P (SUBREG_REG (x))))
+ return LIMIT_RELOAD_CLASS (GET_MODE (x), rclass);
+ else
+ return rclass;
+}
+
+/* Implement TARGET_PREFERRED_OUTPUT_RELOAD_CLASS. */
+
+static reg_class_t
+mn10300_preferred_output_reload_class (rtx x, reg_class_t rclass)
+{
+ if (x == stack_pointer_rtx && rclass != SP_REGS)
+ return (TARGET_AM33 ? GENERAL_REGS : ADDRESS_REGS);
+ return rclass;
+}
+
+/* Implement TARGET_SECONDARY_RELOAD. */
+
+static reg_class_t
+mn10300_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
+ enum machine_mode mode, secondary_reload_info *sri)
+{
+ enum reg_class rclass = (enum reg_class) rclass_i;
+ enum reg_class xclass = NO_REGS;
+ unsigned int xregno = INVALID_REGNUM;
+
+ if (REG_P (x))
+ {
+ xregno = REGNO (x);
+ if (xregno >= FIRST_PSEUDO_REGISTER)
+ xregno = true_regnum (x);
+ if (xregno != INVALID_REGNUM)
+ xclass = REGNO_REG_CLASS (xregno);
+ }
+
+ if (!TARGET_AM33)
+ {
+ /* Memory load/stores 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 (rclass != DATA_REGS
+ && (mode == QImode || mode == HImode)
+ && xclass == NO_REGS)
+ return DATA_REGS;
+
+ /* We can only move SP to/from an address register. */
+ if (in_p
+ && rclass == SP_REGS
+ && xclass != ADDRESS_REGS)
+ return ADDRESS_REGS;
+ if (!in_p
+ && xclass == SP_REGS
+ && rclass != ADDRESS_REGS
+ && rclass != SP_OR_ADDRESS_REGS)
+ return ADDRESS_REGS;
+ }
+
+ /* We can't directly load sp + const_int into a register;
+ we must use an address register as an scratch. */
+ if (in_p
+ && rclass != SP_REGS
+ && rclass != SP_OR_ADDRESS_REGS
+ && rclass != SP_OR_GENERAL_REGS
+ && GET_CODE (x) == PLUS
+ && (XEXP (x, 0) == stack_pointer_rtx
+ || XEXP (x, 1) == stack_pointer_rtx))
+ {
+ sri->icode = CODE_FOR_reload_plus_sp_const;
+ return NO_REGS;
+ }
+
+ /* We can only move MDR to/from a data register. */
+ if (rclass == MDR_REGS && xclass != DATA_REGS)
+ return DATA_REGS;
+ if (xclass == MDR_REGS && rclass != DATA_REGS)
+ return DATA_REGS;
+
+ /* We can't load/store an FP register from a constant address. */
+ if (TARGET_AM33_2
+ && (rclass == FP_REGS || xclass == FP_REGS)
+ && (xclass == NO_REGS || rclass == NO_REGS))
+ {
+ rtx addr = NULL;
+
+ if (xregno >= FIRST_PSEUDO_REGISTER && xregno != INVALID_REGNUM)
+ {
+ addr = reg_equiv_mem [xregno];
+ if (addr)
+ addr = XEXP (addr, 0);
+ }
+ else if (MEM_P (x))
+ addr = XEXP (x, 0);
+
+ if (addr && CONSTANT_ADDRESS_P (addr))
+ return GENERAL_REGS;
+ }
+
+ /* Otherwise assume no secondary reloads are needed. */
+ return NO_REGS;
+}
+
+int
+mn10300_frame_size (void)
+{
+ /* size includes the fixed stack space needed for function calls. */
+ int size = get_frame_size () + crtl->outgoing_args_size;
+
+ /* And space for the return pointer. */
+ size += crtl->outgoing_args_size ? 4 : 0;
+
+ return size;
+}
+
+int
+mn10300_initial_offset (int from, int to)
+{
+ int diff = 0;
+
+ gcc_assert (from == ARG_POINTER_REGNUM || from == FRAME_POINTER_REGNUM);
+ gcc_assert (to == FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM);
+
+ if (to == STACK_POINTER_REGNUM)
+ diff = mn10300_frame_size ();
+
+ /* The difference between the argument pointer and the frame pointer
+ is the size of the callee register save area. */
+ if (from == ARG_POINTER_REGNUM)
+ {
+ diff += REG_SAVE_BYTES;
+ diff += 4 * fp_regs_to_save ();
+ }
+
+ return diff;
+}
+
+/* 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. */
+static rtx
+mn10300_builtin_saveregs (void)
+{
+ rtx offset, mem;
+ tree fntype = TREE_TYPE (current_function_decl);
+ int argadj = ((!stdarg_p (fntype))
+ ? UNITS_PER_WORD : 0);
+ alias_set_type set = get_varargs_alias_set ();
+
+ if (argadj)
+ offset = plus_constant (crtl->args.arg_offset_rtx, argadj);
+ else
+ offset = crtl->args.arg_offset_rtx;
+
+ 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 (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,
+ crtl->args.internal_arg_pointer,
+ offset, 0, 0, OPTAB_LIB_WIDEN));
+}
+
+static void
+mn10300_va_start (tree valist, rtx nextarg)
+{
+ nextarg = expand_builtin_saveregs ();
+ std_expand_builtin_va_start (valist, nextarg);
+}
+
+/* Return true when a parameter should be passed by reference. */
+
+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;
+
+ if (type)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ 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 NULL_RTX, the argument is pushed. */
+
+static rtx
+mn10300_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ rtx result = NULL_RTX;
+ int size;
+
+ /* We only support using 2 data registers as argument registers. */
+ int nregs = 2;
+
+ /* Figure out the size of the object to be passed. */
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ cum->nbytes = (cum->nbytes + 3) & ~3;
+
+ /* Don't pass this arg via a register if all the argument registers
+ are used up. */
+ if (cum->nbytes > nregs * UNITS_PER_WORD)
+ 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 result;
+
+ switch (cum->nbytes / UNITS_PER_WORD)
+ {
+ case 0:
+ result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM);
+ break;
+ case 1:
+ result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM + 1);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+/* Update the data in CUM to advance over an argument
+ of mode MODE and data type TYPE.
+ (TYPE is null for libcalls where that information may not be available.) */
+
+static void
+mn10300_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named ATTRIBUTE_UNUSED)
+{
+ cum->nbytes += (mode != BLKmode
+ ? (GET_MODE_SIZE (mode) + 3) & ~3
+ : (int_size_in_bytes (type) + 3) & ~3);
+}
+
+/* Return the number of bytes of registers to use for an argument passed
+ partially in registers and partially in memory. */
+
+static int
+mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, bool named ATTRIBUTE_UNUSED)
+{
+ int size;
+
+ /* We only support using 2 data registers as argument registers. */
+ int nregs = 2;
+
+ /* Figure out the size of the object to be passed. */
+ if (mode == BLKmode)
+ size = int_size_in_bytes (type);
+ else
+ size = GET_MODE_SIZE (mode);
+
+ cum->nbytes = (cum->nbytes + 3) & ~3;
+
+ /* 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;
+
+ if (cum->nbytes + size <= nregs * UNITS_PER_WORD)
+ return 0;
+
+ /* 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 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 an addition operation. */
+
+const char *
+mn10300_output_add (rtx operands[3], bool need_flags)
+{
+ rtx dest, src1, src2;
+ unsigned int dest_regnum, src1_regnum, src2_regnum;
+ enum reg_class src1_class, src2_class, dest_class;
+
+ dest = operands[0];
+ src1 = operands[1];
+ src2 = operands[2];
+
+ dest_regnum = true_regnum (dest);
+ src1_regnum = true_regnum (src1);
+
+ dest_class = REGNO_REG_CLASS (dest_regnum);
+ src1_class = REGNO_REG_CLASS (src1_regnum);
+
+ if (CONST_INT_P (src2))
+ {
+ gcc_assert (dest_regnum == src1_regnum);
+
+ if (src2 == const1_rtx && !need_flags)
+ return "inc %0";
+ if (INTVAL (src2) == 4 && !need_flags && dest_class != DATA_REGS)
+ return "inc4 %0";
+
+ gcc_assert (!need_flags || dest_class != SP_REGS);
+ return "add %2,%0";
+ }
+ else if (CONSTANT_P (src2))
+ return "add %2,%0";
+
+ src2_regnum = true_regnum (src2);
+ src2_class = REGNO_REG_CLASS (src2_regnum);
+
+ if (dest_regnum == src1_regnum)
+ return "add %2,%0";
+ if (dest_regnum == src2_regnum)
+ return "add %1,%0";
+
+ /* The rest of the cases are reg = reg+reg. For AM33, we can implement
+ this directly, as below, but when optimizing for space we can sometimes
+ do better by using a mov+add. For MN103, we claimed that we could
+ implement a three-operand add because the various move and add insns
+ change sizes across register classes, and we can often do better than
+ reload in choosing which operand to move. */
+ if (TARGET_AM33 && optimize_insn_for_speed_p ())
+ return "add %2,%1,%0";
+
+ /* Catch cases where no extended register was used. */
+ if (src1_class != EXTENDED_REGS
+ && src2_class != EXTENDED_REGS
+ && dest_class != EXTENDED_REGS)
+ {
+ /* We have to copy one of the sources into the destination, then
+ add the other source to the destination.
+
+ Carefully select which source to copy to the destination; a
+ naive implementation will waste a byte when the source classes
+ are different and the destination is an address register.
+ Selecting the lowest cost register copy will optimize this
+ sequence. */
+ if (src1_class == dest_class)
+ return "mov %1,%0\n\tadd %2,%0";
+ else
+ return "mov %2,%0\n\tadd %1,%0";
+ }
+
+ /* At least one register is an extended register. */
+
+ /* The three operand add instruction on the am33 is a win iff the
+ output register is an extended register, or if both source
+ registers are extended registers. */
+ if (dest_class == EXTENDED_REGS || src1_class == src2_class)
+ return "add %2,%1,%0";
+
+ /* It is better to copy one of the sources to the destination, then
+ perform a 2 address add. The destination in this case must be
+ an address or data register and one of the sources must be an
+ extended register and the remaining source must not be an extended
+ register.
+
+ The best code for this case is to copy the extended reg to the
+ destination, then emit a two address add. */
+ if (src1_class == EXTENDED_REGS)
+ return "mov %1,%0\n\tadd %2,%0";
+ else
+ return "mov %2,%0\n\tadd %1,%0";
+}
+
+/* Return 1 if X contains a symbolic expression. We know these
+ expressions will have one of a few well defined forms, so
+ we need only check those forms. */
+
+int
+mn10300_symbolic_operand (rtx op,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ switch (GET_CODE (op))
+ {
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return 1;
+ case CONST:
+ op = XEXP (op, 0);
+ return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+ || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+ && CONST_INT_P (XEXP (op, 1)));
+ default:
+ return 0;
+ }
+}
+
+/* Try machine dependent ways of modifying an illegitimate address
+ to be legitimate. If we find one, return the new valid address.
+ This macro is used in only one place: `memory_address' in explow.c.
+
+ 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.
+
+ 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. */
+
+static rtx
+mn10300_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (flag_pic && ! mn10300_legitimate_pic_operand_p (x))
+ x = mn10300_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. */
+ if (GET_CODE (x) == PLUS
+ && mn10300_symbolic_operand (XEXP (x, 1), VOIDmode))
+ {
+ /* Ugly. We modify things here so that the address offset specified
+ by the index expression is computed first, then added to x to form
+ the entire address. */
+
+ rtx regx1, regy1, regy2, y;
+
+ /* Strip off any CONST. */
+ y = XEXP (x, 1);
+ if (GET_CODE (y) == CONST)
+ y = XEXP (y, 0);
+
+ if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
+ {
+ regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0));
+ 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_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
+mn10300_legitimize_pic_address (rtx orig, rtx reg)
+{
+ rtx x;
+
+ if (GET_CODE (orig) == LABEL_REF
+ || (GET_CODE (orig) == SYMBOL_REF
+ && (CONSTANT_POOL_ADDRESS_P (orig)
+ || ! MN10300_GLOBAL_P (orig))))
+ {
+ if (reg == NULL)
+ reg = gen_reg_rtx (Pmode);
+
+ x = gen_rtx_UNSPEC (SImode, gen_rtvec (1, orig), UNSPEC_GOTOFF);
+ x = gen_rtx_CONST (SImode, x);
+ emit_move_insn (reg, x);
+
+ x = emit_insn (gen_addsi3 (reg, reg, pic_offset_table_rtx));
+ }
+ else if (GET_CODE (orig) == SYMBOL_REF)
+ {
+ if (reg == NULL)
+ reg = gen_reg_rtx (Pmode);
+
+ x = gen_rtx_UNSPEC (SImode, gen_rtvec (1, orig), UNSPEC_GOT);
+ x = gen_rtx_CONST (SImode, x);
+ x = gen_rtx_PLUS (SImode, pic_offset_table_rtx, x);
+ x = gen_const_mem (SImode, x);
+
+ x = emit_move_insn (reg, x);
+ }
+ else
+ return orig;
+
+ set_unique_reg_note (x, REG_EQUAL, orig);
+ return reg;
+}
+
+/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
+ isn't protected by a PIC unspec; nonzero otherwise. */
+
+int
+mn10300_legitimate_pic_operand_p (rtx x)
+{
+ const char *fmt;
+ 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
+ || XINT (x, 1) == UNSPEC_GOTSYM_OFF))
+ 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')
+ {
+ int j;
+
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ if (! mn10300_legitimate_pic_operand_p (XVECEXP (x, i, j)))
+ return 0;
+ }
+ else if (fmt[i] == 'e'
+ && ! mn10300_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.
+
+ 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. */
+
+static bool
+mn10300_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
+{
+ rtx base, index;
+
+ if (CONSTANT_ADDRESS_P (x))
+ return !flag_pic || mn10300_legitimate_pic_operand_p (x);
+
+ if (RTX_OK_FOR_BASE_P (x, strict))
+ return true;
+
+ if (TARGET_AM33 && (mode == SImode || mode == SFmode || mode == HImode))
+ {
+ if (GET_CODE (x) == POST_INC)
+ return RTX_OK_FOR_BASE_P (XEXP (x, 0), strict);
+ if (GET_CODE (x) == POST_MODIFY)
+ return (RTX_OK_FOR_BASE_P (XEXP (x, 0), strict)
+ && CONSTANT_ADDRESS_P (XEXP (x, 1)));
+ }
+
+ if (GET_CODE (x) != PLUS)
+ return false;
+
+ base = XEXP (x, 0);
+ index = XEXP (x, 1);
+
+ if (!REG_P (base))
+ return false;
+ if (REG_P (index))
+ {
+ /* ??? Without AM33 generalized (Ri,Rn) addressing, reg+reg
+ addressing is hard to satisfy. */
+ if (!TARGET_AM33)
+ return false;
+
+ return (REGNO_GENERAL_P (REGNO (base), strict)
+ && REGNO_GENERAL_P (REGNO (index), strict));
+ }
+
+ if (!REGNO_STRICT_OK_FOR_BASE_P (REGNO (base), strict))
+ return false;
+
+ if (CONST_INT_P (index))
+ return IN_RANGE (INTVAL (index), -1 - 0x7fffffff, 0x7fffffff);
+
+ if (CONSTANT_ADDRESS_P (index))
+ return !flag_pic || mn10300_legitimate_pic_operand_p (index);
+
+ return false;
+}
+
+bool
+mn10300_regno_in_class_p (unsigned regno, int rclass, bool strict)
+{
+ if (regno >= FIRST_PSEUDO_REGISTER)
+ {
+ if (!strict)
+ return true;
+ if (!reg_renumber)
+ return false;
+ regno = reg_renumber[regno];
+ if (regno == INVALID_REGNUM)
+ return false;
+ }
+ return TEST_HARD_REG_BIT (reg_class_contents[rclass], regno);
+}
+
+rtx
+mn10300_legitimize_reload_address (rtx x,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int opnum, int type,
+ int ind_levels ATTRIBUTE_UNUSED)
+{
+ bool any_change = false;
+
+ /* See above re disabling reg+reg addressing for MN103. */
+ if (!TARGET_AM33)
+ return NULL_RTX;
+
+ if (GET_CODE (x) != PLUS)
+ return NULL_RTX;
+
+ if (XEXP (x, 0) == stack_pointer_rtx)
+ {
+ push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
+ GENERAL_REGS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, (enum reload_type) type);
+ any_change = true;
+ }
+ if (XEXP (x, 1) == stack_pointer_rtx)
+ {
+ push_reload (XEXP (x, 1), NULL_RTX, &XEXP (x, 1), NULL,
+ GENERAL_REGS, GET_MODE (x), VOIDmode, 0, 0,
+ opnum, (enum reload_type) type);
+ any_change = true;
+ }
+
+ return any_change ? x : NULL_RTX;
+}
+
+/* Used by LEGITIMATE_CONSTANT_P(). Returns TRUE if X is a valid
+ constant. Note that some "constants" aren't valid, such as TLS
+ symbols and unconverted GOT-based references, so we eliminate
+ those here. */
+
+bool
+mn10300_legitimate_constant_p (rtx x)
+{
+ switch (GET_CODE (x))
+ {
+ case CONST:
+ x = XEXP (x, 0);
+
+ if (GET_CODE (x) == PLUS)
+ {
+ if (! CONST_INT_P (XEXP (x, 1)))
+ return false;
+ x = XEXP (x, 0);
+ }
+
+ /* Only some unspecs are valid as "constants". */
+ if (GET_CODE (x) == UNSPEC)
+ {
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_PIC:
+ case UNSPEC_GOT:
+ case UNSPEC_GOTOFF:
+ case UNSPEC_PLT:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /* We must have drilled down to a symbol. */
+ if (! mn10300_symbolic_operand (x, Pmode))
+ return false;
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/* Undo pic address legitimization for the benefit of debug info. */
+
+static rtx
+mn10300_delegitimize_address (rtx orig_x)
+{
+ rtx x = orig_x, ret, addend = NULL;
+ bool need_mem;
+
+ if (MEM_P (x))
+ x = XEXP (x, 0);
+ if (GET_CODE (x) != PLUS || GET_MODE (x) != Pmode)
+ return orig_x;
+
+ if (XEXP (x, 0) == pic_offset_table_rtx)
+ ;
+ /* With the REG+REG addressing of AM33, var-tracking can re-assemble
+ some odd-looking "addresses" that were never valid in the first place.
+ We need to look harder to avoid warnings being emitted. */
+ else if (GET_CODE (XEXP (x, 0)) == PLUS)
+ {
+ rtx x0 = XEXP (x, 0);
+ rtx x00 = XEXP (x0, 0);
+ rtx x01 = XEXP (x0, 1);
+
+ if (x00 == pic_offset_table_rtx)
+ addend = x01;
+ else if (x01 == pic_offset_table_rtx)
+ addend = x00;
+ else
+ return orig_x;
+
+ }
+ else
+ return orig_x;
+ x = XEXP (x, 1);
+
+ if (GET_CODE (x) != CONST)
+ return orig_x;
+ x = XEXP (x, 0);
+ if (GET_CODE (x) != UNSPEC)
+ return orig_x;
+
+ ret = XVECEXP (x, 0, 0);
+ if (XINT (x, 1) == UNSPEC_GOTOFF)
+ need_mem = false;
+ else if (XINT (x, 1) == UNSPEC_GOT)
+ need_mem = true;
+ else
+ return orig_x;
+
+ gcc_assert (GET_CODE (ret) == SYMBOL_REF);
+ if (need_mem != MEM_P (orig_x))
+ return orig_x;
+ if (need_mem && addend)
+ return orig_x;
+ if (addend)
+ ret = gen_rtx_PLUS (Pmode, addend, ret);
+ return ret;
+}
+
+/* For addresses, costs are relative to "MOV (Rm),Rn". For AM33 this is
+ the 3-byte fully general instruction; for MN103 this is the 2-byte form
+ with an address register. */
+
+static int
+mn10300_address_cost (rtx x, bool speed)
+{
+ HOST_WIDE_INT i;
+ rtx base, index;
+
+ switch (GET_CODE (x))
+ {
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ /* We assume all of these require a 32-bit constant, even though
+ some symbol and label references can be relaxed. */
+ return speed ? 1 : 4;
+
+ case REG:
+ case SUBREG:
+ case POST_INC:
+ return 0;
+
+ case POST_MODIFY:
+ /* Assume any symbolic offset is a 32-bit constant. */
+ i = (CONST_INT_P (XEXP (x, 1)) ? INTVAL (XEXP (x, 1)) : 0x12345678);
+ if (IN_RANGE (i, -128, 127))
+ return speed ? 0 : 1;
+ if (speed)
+ return 1;
+ if (IN_RANGE (i, -0x800000, 0x7fffff))
+ return 3;
+ return 4;
+
+ case PLUS:
+ base = XEXP (x, 0);
+ index = XEXP (x, 1);
+ if (register_operand (index, SImode))
+ {
+ /* Attempt to minimize the number of registers in the address.
+ This is similar to what other ports do. */
+ if (register_operand (base, SImode))
+ return 1;
+
+ base = XEXP (x, 1);
+ index = XEXP (x, 0);
+ }
+
+ /* Assume any symbolic offset is a 32-bit constant. */
+ i = (CONST_INT_P (XEXP (x, 1)) ? INTVAL (XEXP (x, 1)) : 0x12345678);
+ if (IN_RANGE (i, -128, 127))
+ return speed ? 0 : 1;
+ if (IN_RANGE (i, -32768, 32767))
+ return speed ? 0 : 2;
+ return speed ? 2 : 6;
+
+ default:
+ return rtx_cost (x, MEM, speed);
+ }
+}
+
+/* Implement the TARGET_REGISTER_MOVE_COST hook.
+
+ Recall that the base value of 2 is required by assumptions elsewhere
+ in the body of the compiler, and that cost 2 is special-cased as an
+ early exit from reload meaning no work is required. */
+
+static int
+mn10300_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t ifrom, reg_class_t ito)
+{
+ enum reg_class from = (enum reg_class) ifrom;
+ enum reg_class to = (enum reg_class) ito;
+ enum reg_class scratch, test;
+
+ /* Simplify the following code by unifying the fp register classes. */
+ if (to == FP_ACC_REGS)
+ to = FP_REGS;
+ if (from == FP_ACC_REGS)
+ from = FP_REGS;
+
+ /* Diagnose invalid moves by costing them as two moves. */
+
+ scratch = NO_REGS;
+ test = from;
+ if (to == SP_REGS)
+ scratch = (TARGET_AM33 ? GENERAL_REGS : ADDRESS_REGS);
+ else if (to == MDR_REGS)
+ scratch = DATA_REGS;
+ else if (to == FP_REGS && to != from)
+ scratch = GENERAL_REGS;
+ else
+ {
+ test = to;
+ if (from == SP_REGS)
+ scratch = (TARGET_AM33 ? GENERAL_REGS : ADDRESS_REGS);
+ else if (from == MDR_REGS)
+ scratch = DATA_REGS;
+ else if (from == FP_REGS && to != from)
+ scratch = GENERAL_REGS;
+ }
+ if (scratch != NO_REGS && !reg_class_subset_p (test, scratch))
+ return (mn10300_register_move_cost (VOIDmode, from, scratch)
+ + mn10300_register_move_cost (VOIDmode, scratch, to));
+
+ /* From here on, all we need consider are legal combinations. */
+
+ if (optimize_size)
+ {
+ /* The scale here is bytes * 2. */
+
+ if (from == to && (to == ADDRESS_REGS || to == DATA_REGS))
+ return 2;
+
+ if (from == SP_REGS)
+ return (to == ADDRESS_REGS ? 2 : 6);
+
+ /* For MN103, all remaining legal moves are two bytes. */
+ if (TARGET_AM33)
+ return 4;
+
+ if (to == SP_REGS)
+ return (from == ADDRESS_REGS ? 4 : 6);
+
+ if ((from == ADDRESS_REGS || from == DATA_REGS)
+ && (to == ADDRESS_REGS || to == DATA_REGS))
+ return 4;
+
+ if (to == EXTENDED_REGS)
+ return (to == from ? 6 : 4);
+
+ /* What's left are SP_REGS, FP_REGS, or combinations of the above. */
+ return 6;
+ }
+ else
+ {
+ /* The scale here is cycles * 2. */
+
+ if (to == FP_REGS)
+ return 8;
+ if (from == FP_REGS)
+ return 4;
+
+ /* All legal moves between integral registers are single cycle. */
+ return 2;
+ }
+}
+
+/* Implement the TARGET_MEMORY_MOVE_COST hook.
+
+ Given lack of the form of the address, this must be speed-relative,
+ though we should never be less expensive than a size-relative register
+ move cost above. This is not a problem. */
+
+static int
+mn10300_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t iclass, bool in ATTRIBUTE_UNUSED)
+{
+ enum reg_class rclass = (enum reg_class) iclass;
+
+ if (rclass == FP_REGS)
+ return 8;
+ return 6;
+}
+
+/* Implement the TARGET_RTX_COSTS hook.
+
+ Speed-relative costs are relative to COSTS_N_INSNS, which is intended
+ to represent cycles. Size-relative costs are in bytes. */
+
+static bool
+mn10300_rtx_costs (rtx x, int code, int outer_code, int *ptotal, bool speed)
+{
+ /* This value is used for SYMBOL_REF etc where we want to pretend
+ we have a full 32-bit constant. */
+ HOST_WIDE_INT i = 0x12345678;
+ int total;
+
+ switch (code)
+ {
+ case CONST_INT:
+ i = INTVAL (x);
+ do_int_costs:
+ if (speed)
+ {
+ if (outer_code == SET)
+ {
+ /* 16-bit integer loads have latency 1, 32-bit loads 2. */
+ if (IN_RANGE (i, -32768, 32767))
+ total = COSTS_N_INSNS (1);
+ else
+ total = COSTS_N_INSNS (2);
+ }
+ else
+ {
+ /* 16-bit integer operands don't affect latency;
+ 24-bit and 32-bit operands add a cycle. */
+ if (IN_RANGE (i, -32768, 32767))
+ total = 0;
+ else
+ total = COSTS_N_INSNS (1);
+ }
+ }
+ else
+ {
+ if (outer_code == SET)
+ {
+ if (i == 0)
+ total = 1;
+ else if (IN_RANGE (i, -128, 127))
+ total = 2;
+ else if (IN_RANGE (i, -32768, 32767))
+ total = 3;
+ else
+ total = 6;
+ }
+ else
+ {
+ /* Reference here is ADD An,Dn, vs ADD imm,Dn. */
+ if (IN_RANGE (i, -128, 127))
+ total = 0;
+ else if (IN_RANGE (i, -32768, 32767))
+ total = 2;
+ else if (TARGET_AM33 && IN_RANGE (i, -0x01000000, 0x00ffffff))
+ total = 3;
+ else
+ total = 4;
+ }
+ }
+ goto alldone;
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ case CONST_DOUBLE:
+ /* We assume all of these require a 32-bit constant, even though
+ some symbol and label references can be relaxed. */
+ goto do_int_costs;
+
+ case UNSPEC:
+ switch (XINT (x, 1))
+ {
+ case UNSPEC_PIC:
+ case UNSPEC_GOT:
+ case UNSPEC_GOTOFF:
+ case UNSPEC_PLT:
+ case UNSPEC_GOTSYM_OFF:
+ /* The PIC unspecs also resolve to a 32-bit constant. */
+ goto do_int_costs;
+
+ default:
+ /* Assume any non-listed unspec is some sort of arithmetic. */
+ goto do_arith_costs;
+ }
+
+ case PLUS:
+ /* Notice the size difference of INC and INC4. */
+ if (!speed && outer_code == SET && CONST_INT_P (XEXP (x, 1)))
+ {
+ i = INTVAL (XEXP (x, 1));
+ if (i == 1 || i == 4)
+ {
+ total = 1 + rtx_cost (XEXP (x, 0), PLUS, speed);
+ goto alldone;
+ }
+ }
+ goto do_arith_costs;
+
+ case MINUS:
+ case AND:
+ case IOR:
+ case XOR:
+ case NOT:
+ case NEG:
+ case ZERO_EXTEND:
+ case SIGN_EXTEND:
+ case COMPARE:
+ case BSWAP:
+ case CLZ:
+ do_arith_costs:
+ total = (speed ? COSTS_N_INSNS (1) : 2);
+ break;
+
+ case ASHIFT:
+ /* Notice the size difference of ASL2 and variants. */
+ if (!speed && CONST_INT_P (XEXP (x, 1)))
+ switch (INTVAL (XEXP (x, 1)))
+ {
+ case 1:
+ case 2:
+ total = 1;
+ goto alldone;
+ case 3:
+ case 4:
+ total = 2;
+ goto alldone;
+ }
+ /* FALLTHRU */
+
+ case ASHIFTRT:
+ case LSHIFTRT:
+ total = (speed ? COSTS_N_INSNS (1) : 3);
+ goto alldone;
+
+ case MULT:
+ total = (speed ? COSTS_N_INSNS (3) : 2);
+ break;
+
+ case DIV:
+ case UDIV:
+ case MOD:
+ case UMOD:
+ total = (speed ? COSTS_N_INSNS (39)
+ /* Include space to load+retrieve MDR. */
+ : code == MOD || code == UMOD ? 6 : 4);
+ break;
+
+ case MEM:
+ total = mn10300_address_cost (XEXP (x, 0), speed);
+ if (speed)
+ total = COSTS_N_INSNS (2 + total);
+ goto alldone;
+
+ default:
+ /* Probably not implemented. Assume external call. */
+ total = (speed ? COSTS_N_INSNS (10) : 7);
+ break;
+ }
+
+ *ptotal = total;
+ return false;
+
+ alldone:
+ *ptotal = total;
+ return true;
+}
+
+/* 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 (! MEM_P (rtl))
+ return;
+ symbol = XEXP (rtl, 0);
+ if (GET_CODE (symbol) != SYMBOL_REF)
+ return;
+
+ 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. */
+
+static unsigned int
+mn10300_case_values_threshold (void)
+{
+ return 6;
+}
+
+/* Worker function for TARGET_TRAMPOLINE_INIT. */
+
+static void
+mn10300_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
+{
+ rtx mem, disp, fnaddr = XEXP (DECL_RTL (fndecl), 0);
+
+ /* This is a strict alignment target, which means that we play
+ some games to make sure that the locations at which we need
+ to store <chain> and <disp> wind up at aligned addresses.
+
+ 0x28 0x00 add 0,d0
+ 0xfc 0xdd mov chain,a1
+ <chain>
+ 0xf8 0xed 0x00 btst 0,d1
+ 0xdc jmp fnaddr
+ <disp>
+
+ Note that the two extra insns are effectively nops; they
+ clobber the flags but do not affect the contents of D0 or D1. */
+
+ disp = expand_binop (SImode, sub_optab, fnaddr,
+ plus_constant (XEXP (m_tramp, 0), 11),
+ NULL_RTX, 1, OPTAB_DIRECT);
+
+ mem = adjust_address (m_tramp, SImode, 0);
+ emit_move_insn (mem, gen_int_mode (0xddfc0028, SImode));
+ mem = adjust_address (m_tramp, SImode, 4);
+ emit_move_insn (mem, chain_value);
+ mem = adjust_address (m_tramp, SImode, 8);
+ emit_move_insn (mem, gen_int_mode (0xdc00edf8, SImode));
+ mem = adjust_address (m_tramp, SImode, 12);
+ emit_move_insn (mem, disp);
+}
+
+/* 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;
+}
+
+bool
+mn10300_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
+{
+ if (REGNO_REG_CLASS (regno) == FP_REGS
+ || REGNO_REG_CLASS (regno) == FP_ACC_REGS)
+ /* Do not store integer values in FP registers. */
+ return GET_MODE_CLASS (mode) == MODE_FLOAT && ((regno & 1) == 0);
+
+ if (((regno) & 1) == 0 || GET_MODE_SIZE (mode) == 4)
+ return true;
+
+ if (REGNO_REG_CLASS (regno) == DATA_REGS
+ || (TARGET_AM33 && REGNO_REG_CLASS (regno) == ADDRESS_REGS)
+ || REGNO_REG_CLASS (regno) == EXTENDED_REGS)
+ return GET_MODE_SIZE (mode) <= 4;
+
+ return false;
+}
+
+bool
+mn10300_modes_tieable (enum machine_mode mode1, enum machine_mode mode2)
+{
+ if (GET_MODE_CLASS (mode1) == MODE_FLOAT
+ && GET_MODE_CLASS (mode2) != MODE_FLOAT)
+ return false;
+
+ if (GET_MODE_CLASS (mode2) == MODE_FLOAT
+ && GET_MODE_CLASS (mode1) != MODE_FLOAT)
+ return false;
+
+ if (TARGET_AM33
+ || mode1 == mode2
+ || (GET_MODE_SIZE (mode1) <= 4 && GET_MODE_SIZE (mode2) <= 4))
+ return true;
+
+ return false;
+}
+
+static int
+cc_flags_for_mode (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case CCmode:
+ return CC_FLAG_Z | CC_FLAG_N | CC_FLAG_C | CC_FLAG_V;
+ case CCZNCmode:
+ return CC_FLAG_Z | CC_FLAG_N | CC_FLAG_C;
+ case CCZNmode:
+ return CC_FLAG_Z | CC_FLAG_N;
+ case CC_FLOATmode:
+ return -1;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+static int
+cc_flags_for_code (enum rtx_code code)
+{
+ switch (code)
+ {
+ case EQ: /* Z */
+ case NE: /* ~Z */
+ return CC_FLAG_Z;
+
+ case LT: /* N */
+ case GE: /* ~N */
+ return CC_FLAG_N;
+ break;
+
+ case GT: /* ~(Z|(N^V)) */
+ case LE: /* Z|(N^V) */
+ return CC_FLAG_Z | CC_FLAG_N | CC_FLAG_V;
+
+ case GEU: /* ~C */
+ case LTU: /* C */
+ return CC_FLAG_C;
+
+ case GTU: /* ~(C | Z) */
+ case LEU: /* C | Z */
+ return CC_FLAG_Z | CC_FLAG_C;
+
+ case ORDERED:
+ case UNORDERED:
+ case LTGT:
+ case UNEQ:
+ case UNGE:
+ case UNGT:
+ case UNLE:
+ case UNLT:
+ return -1;
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+enum machine_mode
+mn10300_select_cc_mode (enum rtx_code code, rtx x, rtx y ATTRIBUTE_UNUSED)
+{
+ int req;
+
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ return CC_FLOATmode;
+
+ req = cc_flags_for_code (code);
+
+ if (req & CC_FLAG_V)
+ return CCmode;
+ if (req & CC_FLAG_C)
+ return CCZNCmode;
+ return CCZNmode;
+}
+
+static inline bool
+is_load_insn (rtx insn)
+{
+ if (GET_CODE (PATTERN (insn)) != SET)
+ return false;
+
+ return MEM_P (SET_SRC (PATTERN (insn)));
+}
+
+static inline bool
+is_store_insn (rtx insn)
+{
+ if (GET_CODE (PATTERN (insn)) != SET)
+ return false;
+
+ return MEM_P (SET_DEST (PATTERN (insn)));
+}
+
+/* Update scheduling costs for situations that cannot be
+ described using the attributes and DFA machinery.
+ DEP is the insn being scheduled.
+ INSN is the previous insn.
+ COST is the current cycle cost for DEP. */
+
+static int
+mn10300_adjust_sched_cost (rtx insn, rtx link, rtx dep, int cost)
+{
+ int timings = get_attr_timings (insn);
+
+ if (!TARGET_AM33)
+ return 1;
+
+ if (GET_CODE (insn) == PARALLEL)
+ insn = XVECEXP (insn, 0, 0);
+
+ if (GET_CODE (dep) == PARALLEL)
+ dep = XVECEXP (dep, 0, 0);
+
+ /* For the AM34 a load instruction that follows a
+ store instruction incurs an extra cycle of delay. */
+ if (mn10300_tune_cpu == PROCESSOR_AM34
+ && is_load_insn (dep)
+ && is_store_insn (insn))
+ cost += 1;
+
+ /* For the AM34 a non-store, non-branch FPU insn that follows
+ another FPU insn incurs a one cycle throughput increase. */
+ else if (mn10300_tune_cpu == PROCESSOR_AM34
+ && ! is_store_insn (insn)
+ && ! JUMP_P (insn)
+ && GET_CODE (PATTERN (dep)) == SET
+ && GET_CODE (PATTERN (insn)) == SET
+ && GET_MODE_CLASS (GET_MODE (SET_SRC (PATTERN (dep)))) == MODE_FLOAT
+ && GET_MODE_CLASS (GET_MODE (SET_SRC (PATTERN (insn)))) == MODE_FLOAT)
+ cost += 1;
+
+ /* Resolve the conflict described in section 1-7-4 of
+ Chapter 3 of the MN103E Series Instruction Manual
+ where it says:
+
+ "When the preceeding instruction is a CPU load or
+ store instruction, a following FPU instruction
+ cannot be executed until the CPU completes the
+ latency period even though there are no register
+ or flag dependencies between them." */
+
+ /* Only the AM33-2 (and later) CPUs have FPU instructions. */
+ if (! TARGET_AM33_2)
+ return cost;
+
+ /* If a data dependence already exists then the cost is correct. */
+ if (REG_NOTE_KIND (link) == 0)
+ return cost;
+
+ /* Check that the instruction about to scheduled is an FPU instruction. */
+ if (GET_CODE (PATTERN (dep)) != SET)
+ return cost;
+
+ if (GET_MODE_CLASS (GET_MODE (SET_SRC (PATTERN (dep)))) != MODE_FLOAT)
+ return cost;
+
+ /* Now check to see if the previous instruction is a load or store. */
+ if (! is_load_insn (insn) && ! is_store_insn (insn))
+ return cost;
+
+ /* XXX: Verify: The text of 1-7-4 implies that the restriction
+ only applies when an INTEGER load/store preceeds an FPU
+ instruction, but is this true ? For now we assume that it is. */
+ if (GET_MODE_CLASS (GET_MODE (SET_SRC (PATTERN (insn)))) != MODE_INT)
+ return cost;
+
+ /* Extract the latency value from the timings attribute. */
+ return timings < 100 ? (timings % 10) : (timings % 100);
+}
+
+static void
+mn10300_conditional_register_usage (void)
+{
+ unsigned int i;
+
+ if (!TARGET_AM33)
+ {
+ for (i = FIRST_EXTENDED_REGNUM;
+ i <= LAST_EXTENDED_REGNUM; i++)
+ fixed_regs[i] = call_used_regs[i] = 1;
+ }
+ if (!TARGET_AM33_2)
+ {
+ for (i = FIRST_FP_REGNUM;
+ i <= LAST_FP_REGNUM; i++)
+ fixed_regs[i] = call_used_regs[i] = 1;
+ }
+ if (flag_pic)
+ fixed_regs[PIC_OFFSET_TABLE_REGNUM] =
+ call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+}
+
+/* Worker function for TARGET_MD_ASM_CLOBBERS.
+ We do this in the mn10300 backend to maintain source compatibility
+ with the old cc0-based compiler. */
+
+static tree
+mn10300_md_asm_clobbers (tree outputs ATTRIBUTE_UNUSED,
+ tree inputs ATTRIBUTE_UNUSED,
+ tree clobbers)
+{
+ clobbers = tree_cons (NULL_TREE, build_string (5, "EPSW"),
+ clobbers);
+ return clobbers;
+}
+
+/* A helper function for splitting cbranch patterns after reload. */
+
+void
+mn10300_split_cbranch (enum machine_mode cmp_mode, rtx cmp_op, rtx label_ref)
+{
+ rtx flags, x;
+
+ flags = gen_rtx_REG (cmp_mode, CC_REG);
+ x = gen_rtx_COMPARE (cmp_mode, XEXP (cmp_op, 0), XEXP (cmp_op, 1));
+ x = gen_rtx_SET (VOIDmode, flags, x);
+ emit_insn (x);
+
+ x = gen_rtx_fmt_ee (GET_CODE (cmp_op), VOIDmode, flags, const0_rtx);
+ x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label_ref, pc_rtx);
+ x = gen_rtx_SET (VOIDmode, pc_rtx, x);
+ emit_jump_insn (x);
+}
+
+/* A helper function for matching parallels that set the flags. */
+
+bool
+mn10300_match_ccmode (rtx insn, enum machine_mode cc_mode)
+{
+ rtx op1, flags;
+ enum machine_mode flags_mode;
+
+ gcc_checking_assert (XVECLEN (PATTERN (insn), 0) == 2);
+
+ op1 = XVECEXP (PATTERN (insn), 0, 1);
+ gcc_checking_assert (GET_CODE (SET_SRC (op1)) == COMPARE);
+
+ flags = SET_DEST (op1);
+ flags_mode = GET_MODE (flags);
+
+ if (GET_MODE (SET_SRC (op1)) != flags_mode)
+ return false;
+ if (GET_MODE_CLASS (flags_mode) != MODE_CC)
+ return false;
+
+ /* Ensure that the mode of FLAGS is compatible with CC_MODE. */
+ if (cc_flags_for_mode (flags_mode) & ~cc_flags_for_mode (cc_mode))
+ return false;
+
+ return true;
+}
+
+int
+mn10300_split_and_operand_count (rtx op)
+{
+ HOST_WIDE_INT val = INTVAL (op);
+ int count;
+
+ if (val < 0)
+ {
+ /* High bit is set, look for bits clear at the bottom. */
+ count = exact_log2 (-val);
+ if (count < 0)
+ return 0;
+ /* This is only size win if we can use the asl2 insn. Otherwise we
+ would be replacing 1 6-byte insn with 2 3-byte insns. */
+ if (count > (optimize_insn_for_speed_p () ? 2 : 4))
+ return 0;
+ return -count;
+ }
+ else
+ {
+ /* High bit is clear, look for bits set at the bottom. */
+ count = exact_log2 (val + 1);
+ count = 32 - count;
+ /* Again, this is only a size win with asl2. */
+ if (count > (optimize_insn_for_speed_p () ? 2 : 4))
+ return 0;
+ return -count;
+ }
+}
+
+struct liw_data
+{
+ enum attr_liw slot;
+ enum attr_liw_op op;
+ rtx dest;
+ rtx src;
+};
+
+/* Decide if the given insn is a candidate for LIW bundling. If it is then
+ extract the operands and LIW attributes from the insn and use them to fill
+ in the liw_data structure. Return true upon success or false if the insn
+ cannot be bundled. */
+
+static bool
+extract_bundle (rtx insn, struct liw_data * pdata)
+{
+ bool allow_consts = true;
+ rtx p,s;
+
+ gcc_assert (pdata != NULL);
+
+ if (insn == NULL_RTX)
+ return false;
+ /* Make sure that we are dealing with a simple SET insn. */
+ p = single_set (insn);
+ if (p == NULL_RTX)
+ return false;
+
+ /* Make sure that it could go into one of the LIW pipelines. */
+ pdata->slot = get_attr_liw (insn);
+ if (pdata->slot == LIW_BOTH)
+ return false;
+
+ pdata->op = get_attr_liw_op (insn);
+
+ s = SET_SRC (p);
+
+ switch (pdata->op)
+ {
+ case LIW_OP_MOV:
+ pdata->dest = SET_DEST (p);
+ pdata->src = SET_SRC (p);
+ break;
+ case LIW_OP_CMP:
+ pdata->dest = XEXP (SET_SRC (p), 0);
+ pdata->src = XEXP (SET_SRC (p), 1);
+ break;
+ case LIW_OP_NONE:
+ return false;
+ case LIW_OP_AND:
+ case LIW_OP_OR:
+ case LIW_OP_XOR:
+ /* The AND, OR and XOR long instruction words only accept register arguments. */
+ allow_consts = false;
+ /* Fall through. */
+ default:
+ pdata->dest = SET_DEST (p);
+ pdata->src = XEXP (SET_SRC (p), 1);
+ break;
+ }
+
+ if (! REG_P (pdata->dest))
+ return false;
+
+ if (REG_P (pdata->src))
+ return true;
+
+ return allow_consts && satisfies_constraint_O (pdata->src);
+}
+
+/* Make sure that it is OK to execute LIW1 and LIW2 in parallel. GCC generated
+ the instructions with the assumption that LIW1 would be executed before LIW2
+ so we must check for overlaps between their sources and destinations. */
+
+static bool
+check_liw_constraints (struct liw_data * pliw1, struct liw_data * pliw2)
+{
+ /* Check for slot conflicts. */
+ if (pliw2->slot == pliw1->slot && pliw1->slot != LIW_EITHER)
+ return false;
+
+ /* If either operation is a compare, then "dest" is really an input; the real
+ destination is CC_REG. So these instructions need different checks. */
+
+ /* Changing "CMP ; OP" into "CMP | OP" is OK because the comparison will
+ check its values prior to any changes made by OP. */
+ if (pliw1->op == LIW_OP_CMP)
+ {
+ /* Two sequential comparisons means dead code, which ought to
+ have been eliminated given that bundling only happens with
+ optimization. We cannot bundle them in any case. */
+ gcc_assert (pliw1->op != pliw2->op);
+ return true;
+ }
+
+ /* Changing "OP ; CMP" into "OP | CMP" does not work if the value being compared
+ is the destination of OP, as the CMP will look at the old value, not the new
+ one. */
+ if (pliw2->op == LIW_OP_CMP)
+ {
+ if (REGNO (pliw2->dest) == REGNO (pliw1->dest))
+ return false;
+
+ if (REG_P (pliw2->src))
+ return REGNO (pliw2->src) != REGNO (pliw1->dest);
+
+ return true;
+ }
+
+ /* Changing "OP1 ; OP2" into "OP1 | OP2" does not work if they both write to the
+ same destination register. */
+ if (REGNO (pliw2->dest) == REGNO (pliw1->dest))
+ return false;
+
+ /* Changing "OP1 ; OP2" into "OP1 | OP2" generally does not work if the destination
+ of OP1 is the source of OP2. The exception is when OP1 is a MOVE instruction when
+ we can replace the source in OP2 with the source of OP1. */
+ if (REG_P (pliw2->src) && REGNO (pliw2->src) == REGNO (pliw1->dest))
+ {
+ if (pliw1->op == LIW_OP_MOV && REG_P (pliw1->src))
+ {
+ if (! REG_P (pliw1->src)
+ && (pliw2->op == LIW_OP_AND
+ || pliw2->op == LIW_OP_OR
+ || pliw2->op == LIW_OP_XOR))
+ return false;
+
+ pliw2->src = pliw1->src;
+ return true;
+ }
+ return false;
+ }
+
+ /* Everything else is OK. */
+ return true;
+}
+
+/* Combine pairs of insns into LIW bundles. */
+
+static void
+mn10300_bundle_liw (void)
+{
+ rtx r;
+
+ for (r = get_insns (); r != NULL_RTX; r = next_nonnote_nondebug_insn (r))
+ {
+ rtx insn1, insn2;
+ struct liw_data liw1, liw2;
+
+ insn1 = r;
+ if (! extract_bundle (insn1, & liw1))
+ continue;
+
+ insn2 = next_nonnote_nondebug_insn (insn1);
+ if (! extract_bundle (insn2, & liw2))
+ continue;
+
+ /* Check for source/destination overlap. */
+ if (! check_liw_constraints (& liw1, & liw2))
+ continue;
+
+ if (liw1.slot == LIW_OP2 || liw2.slot == LIW_OP1)
+ {
+ struct liw_data temp;
+
+ temp = liw1;
+ liw1 = liw2;
+ liw2 = temp;
+ }
+
+ delete_insn (insn2);
+
+ if (liw1.op == LIW_OP_CMP)
+ insn2 = gen_cmp_liw (liw2.dest, liw2.src, liw1.dest, liw1.src,
+ GEN_INT (liw2.op));
+ else if (liw2.op == LIW_OP_CMP)
+ insn2 = gen_liw_cmp (liw1.dest, liw1.src, liw2.dest, liw2.src,
+ GEN_INT (liw1.op));
+ else
+ insn2 = gen_liw (liw1.dest, liw2.dest, liw1.src, liw2.src,
+ GEN_INT (liw1.op), GEN_INT (liw2.op));
+
+ insn2 = emit_insn_after (insn2, insn1);
+ delete_insn (insn1);
+ r = insn2;
+ }
+}
+
+static void
+mn10300_reorg (void)
+{
+ if (TARGET_AM33)
+ {
+ if (TARGET_ALLOW_LIW)
+ mn10300_bundle_liw ();
+ }
+}
+
+/* Initialize the GCC target structure. */
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG mn10300_reorg
+
+#undef TARGET_EXCEPT_UNWIND_INFO
+#define TARGET_EXCEPT_UNWIND_INFO sjlj_except_unwind_info
+
+#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_ADDRESS_COST
+#define TARGET_ADDRESS_COST mn10300_address_cost
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST mn10300_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST mn10300_memory_move_cost
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS mn10300_rtx_costs
+
+#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_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA mn10300_asm_output_addr_const_extra
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0 | MASK_ALLOW_LIW
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION mn10300_handle_option
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE mn10300_option_override
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE mn10300_option_optimization_table
+
+#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_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG mn10300_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE mn10300_function_arg_advance
+
+#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_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS mn10300_delegitimize_address
+
+#undef TARGET_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS mn10300_preferred_reload_class
+#undef TARGET_PREFERRED_OUTPUT_RELOAD_CLASS
+#define TARGET_PREFERRED_OUTPUT_RELOAD_CLASS \
+ mn10300_preferred_output_reload_class
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD mn10300_secondary_reload
+
+#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
+
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST mn10300_adjust_sched_cost
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE mn10300_conditional_register_usage
+
+#undef TARGET_MD_ASM_CLOBBERS
+#define TARGET_MD_ASM_CLOBBERS mn10300_md_asm_clobbers
+
+#undef TARGET_FLAGS_REGNUM
+#define TARGET_FLAGS_REGNUM CC_REG
+
+struct gcc_target targetm = TARGET_INITIALIZER;