From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- gcc/config/mn10300/constraints.md | 107 ++ gcc/config/mn10300/linux.h | 90 + gcc/config/mn10300/mn10300-modes.def | 24 + gcc/config/mn10300/mn10300-protos.h | 57 + gcc/config/mn10300/mn10300.c | 3254 ++++++++++++++++++++++++++++++++++ gcc/config/mn10300/mn10300.h | 766 ++++++++ gcc/config/mn10300/mn10300.md | 2154 ++++++++++++++++++++++ gcc/config/mn10300/mn10300.opt | 56 + gcc/config/mn10300/predicates.md | 69 + gcc/config/mn10300/t-linux | 29 + gcc/config/mn10300/t-mn10300 | 36 + 11 files changed, 6642 insertions(+) create mode 100644 gcc/config/mn10300/constraints.md create mode 100644 gcc/config/mn10300/linux.h create mode 100644 gcc/config/mn10300/mn10300-modes.def create mode 100644 gcc/config/mn10300/mn10300-protos.h create mode 100644 gcc/config/mn10300/mn10300.c create mode 100644 gcc/config/mn10300/mn10300.h create mode 100644 gcc/config/mn10300/mn10300.md create mode 100644 gcc/config/mn10300/mn10300.opt create mode 100644 gcc/config/mn10300/predicates.md create mode 100644 gcc/config/mn10300/t-linux create mode 100644 gcc/config/mn10300/t-mn10300 (limited to 'gcc/config/mn10300') diff --git a/gcc/config/mn10300/constraints.md b/gcc/config/mn10300/constraints.md new file mode 100644 index 000000000..c8ee2d4e2 --- /dev/null +++ b/gcc/config/mn10300/constraints.md @@ -0,0 +1,107 @@ +;; Constraint definitions for the MN10300. +;; Copyright (C) 2007, 2008 Free Software Foundation, Inc. +;; +;; 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 +;; . + +(define_register_constraint "d" "DATA_REGS" + "A data register.") + +(define_register_constraint "a" "ADDRESS_REGS" + "An address register.") + +;; This can be used for QI/HImode memory operations, and most arithmetic. +;; AM33 supports these on all registers, where MN103 needs DATA_REGS. +(define_register_constraint "D" "TARGET_AM33 ? GENERAL_REGS : DATA_REGS" + "A general register for AM33, and a data register otherwise.") + +;; Similarly for ADDRESS_REGS vs GENERAL_REGS. +(define_register_constraint "A" "TARGET_AM33 ? GENERAL_REGS : ADDRESS_REGS" + "A general register for AM33, and an address register otherwise.") + +(define_register_constraint "y" "SP_REGS" + "An SP register (if available).") + +(define_register_constraint "z" "MDR_REGS" + "The MDR register.") + +(define_register_constraint "x" "TARGET_AM33 ? EXTENDED_REGS : NO_REGS" + "An extended register.") + +(define_register_constraint "f" "TARGET_AM33_2 ? FP_REGS : NO_REGS" + "A floating point register.") + +(define_register_constraint "c" "TARGET_AM33_2 ? FP_ACC_REGS : NO_REGS" + "A floating point accumulator register.") + +(define_memory_constraint "Q" + "@internal" + (and (match_code "mem") + (match_test "!CONSTANT_ADDRESS_P (XEXP (op, 0))"))) + +(define_constraint "S" + "@internal" + (if_then_else (match_test "flag_pic") + (and (match_test "GET_CODE (op) == UNSPEC") + (ior (match_test "XINT (op, 1) == UNSPEC_PLT") + (match_test "XINT (op, 1) == UNSPEC_PIC") + (match_test "XINT (op, 1) == UNSPEC_GOTSYM_OFF"))) + (match_test "GET_CODE (op) == SYMBOL_REF"))) + +;; Integer constraints + +(define_constraint "I" + "An integer zero." + (and (match_code "const_int") + (match_test "ival == 0"))) + +(define_constraint "J" + "An integer one." + (and (match_code "const_int") + (match_test "ival == 1"))) + +(define_constraint "K" + "An integer two." + (and (match_code "const_int") + (match_test "ival == 2"))) + +(define_constraint "L" + "An integer four." + (and (match_code "const_int") + (match_test "ival == 4"))) + +(define_constraint "M" + "An integer three." + (and (match_code "const_int") + (match_test "ival == 3"))) + +(define_constraint "N" + "An integer of either 255 or 65535." + (and (match_code "const_int") + (ior (match_test "ival == 255") + (match_test "ival == 65535")))) + +(define_constraint "O" + "An integer between -8 and +7 inclusive." + (and (match_code "const_int") + (and (match_test "ival >= -8") + (match_test "ival <= 7")))) + +;; Floating-point constraints +(define_constraint "G" + "Floating-point zero." + (and (match_code "const_double") + (match_test "op == CONST0_RTX (mode)"))) diff --git a/gcc/config/mn10300/linux.h b/gcc/config/mn10300/linux.h new file mode 100644 index 000000000..ca0e10a65 --- /dev/null +++ b/gcc/config/mn10300/linux.h @@ -0,0 +1,90 @@ +/* Definitions of taret machine for GNU compiler. + Matsushita AM33/2.0 + Copyright 2001, 2002, 2005, 2006, 2007, 2010 Free Software Foundation, Inc. + Contributed by Alexandre Oliva + + 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 + . */ + +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG + +#define TARGET_OS_CPP_BUILTINS() LINUX_TARGET_OS_CPP_BUILTINS() + +#undef CPP_SPEC +#define CPP_SPEC "%{mam33:-D__AM33__} %{!mam33:-D__AM33__=2 -D__AM33_2__} \ + %{posix:-D_POSIX_SOURCE} \ + %{pthread:-D_REENTRANT -D_PTHREADS}" + +#undef ASM_SPEC +#define ASM_SPEC "" + +#define GLIBC_DYNAMIC_LINKER "/lib/ld.so.1" + +#undef LINK_SPEC +#define LINK_SPEC "%{mrelax:--relax} %{shared:-shared} \ + %{!static: \ + %{rdynamic:-export-dynamic} \ + -dynamic-linker " LINUX_DYNAMIC_LINKER "} \ + %{static:-static}" + +#undef PROCESSOR_DEFAULT +#define PROCESSOR_DEFAULT PROCESSOR_AM33_2 + +#undef TARGET_VERSION +#define TARGET_VERSION fprintf (stderr, " (AM33/2.0 GNU/Linux)"); + +#define DBX_REGISTER_NUMBER(REGNO) (REGNO) + +extern int mn10300_protect_label; + +#undef PRINT_OPERAND +#define PRINT_OPERAND(FILE, X, CODE) \ + do \ + { \ + mn10300_protect_label = 1; \ + mn10300_print_operand ((FILE), (X), (CODE)); \ + mn10300_protect_label = 0; \ + } \ + while (0) + +#undef PRINT_OPERAND_ADDRESS +#define PRINT_OPERAND_ADDRESS(FILE, X) \ + do \ + { \ + mn10300_protect_label = 1; \ + mn10300_print_operand_address ((FILE), (X)); \ + mn10300_protect_label = 0; \ + } \ + while (0) + +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(FILE, NAME) \ + do \ + { \ + const char * real_name; \ + \ + real_name = (*targetm.strip_name_encoding) (NAME); \ + if (mn10300_protect_label) \ + asm_fprintf (FILE, "+"); \ + asm_fprintf (FILE, "%U%s", real_name); \ + } \ + while (0) + +#undef SIZE_TYPE +#undef PTRDIFF_TYPE +#undef WCHAR_TYPE +#undef WCHAR_TYPE_SIZE diff --git a/gcc/config/mn10300/mn10300-modes.def b/gcc/config/mn10300/mn10300-modes.def new file mode 100644 index 000000000..832663edb --- /dev/null +++ b/gcc/config/mn10300/mn10300-modes.def @@ -0,0 +1,24 @@ +/* Definitions of target machine for GNU compiler, for MN10300. + Copyright (C) 2006 Free Software Foundation, Inc. + Contributed by Red Hat Inc. + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) 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 COPYING. If not, write to + the Free Software Foundation, , Inc., 51 Franklin Street - Fifth + Floor, Boston, MA 02110-1301, USA. */ + +CC_MODE (CCZN); +CC_MODE (CCZNC); +CC_MODE (CC_FLOAT); diff --git a/gcc/config/mn10300/mn10300-protos.h b/gcc/config/mn10300/mn10300-protos.h new file mode 100644 index 000000000..058f5df87 --- /dev/null +++ b/gcc/config/mn10300/mn10300-protos.h @@ -0,0 +1,57 @@ +/* Definitions of target machine for GNU compiler. Matsushita MN10300 series + Copyright (C) 2000, 2003, 2004, 2005, 2007, 2009, 2010 + Free Software Foundation, Inc. + Contributed by Jeff Law (law@cygnus.com). + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 + . */ + +#define Mmode enum machine_mode +#define Cstar const char * +#define Rclas enum reg_class + +#ifdef RTX_CODE +extern rtx mn10300_legitimize_pic_address (rtx, rtx); +extern int mn10300_legitimate_pic_operand_p (rtx); +extern rtx mn10300_legitimize_reload_address (rtx, Mmode, int, int, int); +extern bool mn10300_function_value_regno_p (const unsigned int); +extern int mn10300_get_live_callee_saved_regs (void); +extern bool mn10300_hard_regno_mode_ok (unsigned int, Mmode); +extern bool mn10300_legitimate_constant_p (rtx); +extern bool mn10300_modes_tieable (Mmode, Mmode); +extern Cstar mn10300_output_add (rtx[3], bool); +extern void mn10300_print_operand (FILE *, rtx, int); +extern void mn10300_print_operand_address (FILE *, rtx); +extern void mn10300_print_reg_list (FILE *, int); +extern Mmode mn10300_select_cc_mode (enum rtx_code, rtx, rtx); +extern int mn10300_store_multiple_operation (rtx, Mmode); +extern int mn10300_symbolic_operand (rtx, Mmode); +extern void mn10300_split_cbranch (Mmode, rtx, rtx); +extern int mn10300_split_and_operand_count (rtx); +extern bool mn10300_match_ccmode (rtx, Mmode); +#endif /* RTX_CODE */ + +extern bool mn10300_regno_in_class_p (unsigned, int, bool); +extern bool mn10300_can_use_rets_insn (void); +extern bool mn10300_can_use_retf_insn (void); +extern void mn10300_expand_prologue (void); +extern void mn10300_expand_epilogue (void); +extern int mn10300_initial_offset (int, int); +extern int mn10300_frame_size (void); + +#undef Mmode +#undef Cstar +#undef Rclas diff --git a/gcc/config/mn10300/mn10300.c b/gcc/config/mn10300/mn10300.c new file mode 100644 index 000000000..e3c417e46 --- /dev/null +++ b/gcc/config/mn10300/mn10300.c @@ -0,0 +1,3254 @@ +/* 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 + . */ + +#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) +{ + rtx symbol; + + default_encode_section_info (decl, rtl, first); + + 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 and wind up at aligned addresses. + + 0x28 0x00 add 0,d0 + 0xfc 0xdd mov chain,a1 + + 0xf8 0xed 0x00 btst 0,d1 + 0xdc jmp fnaddr + + + 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; diff --git a/gcc/config/mn10300/mn10300.h b/gcc/config/mn10300/mn10300.h new file mode 100644 index 000000000..2e97ca4ff --- /dev/null +++ b/gcc/config/mn10300/mn10300.h @@ -0,0 +1,766 @@ +/* Definitions of target machine for GNU compiler. + Matsushita MN10300 series + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + Contributed by Jeff Law (law@cygnus.com). + + This file is part of GCC. + + GCC is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 + . */ + +#undef ASM_SPEC +#undef LIB_SPEC +#undef ENDFILE_SPEC +#undef LINK_SPEC +#define LINK_SPEC "%{mrelax:--relax}" +#undef STARTFILE_SPEC +#define STARTFILE_SPEC "%{!mno-crt0:%{!shared:%{pg:gcrt0%O%s}%{!pg:%{p:mcrt0%O%s}%{!p:crt0%O%s}}}}" + +/* Names to predefine in the preprocessor for this target machine. */ + +#define TARGET_CPU_CPP_BUILTINS() \ + do \ + { \ + builtin_define ("__mn10300__"); \ + builtin_define ("__MN10300__"); \ + builtin_assert ("cpu=mn10300"); \ + builtin_assert ("machine=mn10300"); \ + \ + if (TARGET_AM34) \ + { \ + builtin_define ("__AM33__=4"); \ + builtin_define ("__AM34__"); \ + } \ + else if (TARGET_AM33_2) \ + { \ + builtin_define ("__AM33__=2"); \ + builtin_define ("__AM33_2__"); \ + } \ + else if (TARGET_AM33) \ + builtin_define ("__AM33__=1"); \ + \ + builtin_define (TARGET_ALLOW_LIW ? \ + "__LIW__" : "__NO_LIW__");\ + \ + } \ + while (0) + +enum processor_type +{ + PROCESSOR_MN10300, + PROCESSOR_AM33, + PROCESSOR_AM33_2, + PROCESSOR_AM34 +}; + +extern enum processor_type mn10300_processor; +extern enum processor_type mn10300_tune_cpu; + +#define TARGET_AM33 (mn10300_processor >= PROCESSOR_AM33) +#define TARGET_AM33_2 (mn10300_processor >= PROCESSOR_AM33_2) +#define TARGET_AM34 (mn10300_processor >= PROCESSOR_AM34) + +#ifndef PROCESSOR_DEFAULT +#define PROCESSOR_DEFAULT PROCESSOR_MN10300 +#endif + +/* Print subsidiary information on the compiler version in use. */ + +#define TARGET_VERSION fprintf (stderr, " (MN10300)"); + + +/* Target machine storage layout */ + +/* Define this if most significant bit is lowest numbered + in instructions that operate on numbered bit-fields. + This is not true on the Matsushita MN1003. */ +#define BITS_BIG_ENDIAN 0 + +/* Define this if most significant byte of a word is the lowest numbered. */ +/* This is not true on the Matsushita MN10300. */ +#define BYTES_BIG_ENDIAN 0 + +/* Define this if most significant word of a multiword number is lowest + numbered. + This is not true on the Matsushita MN10300. */ +#define WORDS_BIG_ENDIAN 0 + +/* Width of a word, in units (bytes). */ +#define UNITS_PER_WORD 4 + +/* Allocation boundary (in *bits*) for storing arguments in argument list. */ +#define PARM_BOUNDARY 32 + +/* The stack goes in 32-bit lumps. */ +#define STACK_BOUNDARY 32 + +/* Allocation boundary (in *bits*) for the code of a function. + 8 is the minimum boundary; it's unclear if bigger alignments + would improve performance. */ +#define FUNCTION_BOUNDARY 8 + +/* No data type wants to be aligned rounder than this. */ +#define BIGGEST_ALIGNMENT 32 + +/* Alignment of field after `int : 0' in a structure. */ +#define EMPTY_FIELD_BOUNDARY 32 + +/* Define this if move instructions will actually fail to work + when given unaligned data. */ +#define STRICT_ALIGNMENT 1 + +/* Define this as 1 if `char' should by default be signed; else as 0. */ +#define DEFAULT_SIGNED_CHAR 0 + +#undef SIZE_TYPE +#define SIZE_TYPE "unsigned int" + +#undef PTRDIFF_TYPE +#define PTRDIFF_TYPE "int" + +#undef WCHAR_TYPE +#define WCHAR_TYPE "long int" + +#undef WCHAR_TYPE_SIZE +#define WCHAR_TYPE_SIZE BITS_PER_WORD + +/* Standard register usage. */ + +/* Number of actual hardware registers. + The hardware registers are assigned numbers for the compiler + from 0 to just below FIRST_PSEUDO_REGISTER. + + All registers that the compiler knows about must be given numbers, + even those that are not normally considered general registers. */ + +#define FIRST_PSEUDO_REGISTER 52 + +/* Specify machine-specific register numbers. The commented out entries + are defined in mn10300.md. */ +#define FIRST_DATA_REGNUM 0 +#define LAST_DATA_REGNUM 3 +#define FIRST_ADDRESS_REGNUM 4 +/* #define PIC_REG 6 */ +#define LAST_ADDRESS_REGNUM 8 +/* #define SP_REG 9 */ +#define FIRST_EXTENDED_REGNUM 10 +#define LAST_EXTENDED_REGNUM 17 +#define FIRST_FP_REGNUM 18 +#define LAST_FP_REGNUM 49 +/* #define MDR_REG 50 */ +/* #define CC_REG 51 */ +#define FIRST_ARGUMENT_REGNUM 0 + +/* Specify the registers used for certain standard purposes. + The values of these macros are register numbers. */ + +/* Register to use for pushing function arguments. */ +#define STACK_POINTER_REGNUM (LAST_ADDRESS_REGNUM + 1) + +/* Base register for access to local variables of the function. */ +#define FRAME_POINTER_REGNUM (LAST_ADDRESS_REGNUM - 1) + +/* Base register for access to arguments of the function. This + is a fake register and will be eliminated into either the frame + pointer or stack pointer. */ +#define ARG_POINTER_REGNUM LAST_ADDRESS_REGNUM + +/* Register in which static-chain is passed to a function. */ +#define STATIC_CHAIN_REGNUM (FIRST_ADDRESS_REGNUM + 1) + +/* 1 for registers that have pervasive standard uses + and are not available for the register allocator. */ + +#define FIXED_REGISTERS \ + { 0, 0, 0, 0, /* data regs */ \ + 0, 0, 0, 0, /* addr regs */ \ + 1, /* arg reg */ \ + 1, /* sp reg */ \ + 0, 0, 0, 0, 0, 0, 0, 0, /* extended regs */ \ + 0, 0, /* fp regs (18-19) */ \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* fp regs (20-29) */ \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* fp regs (30-39) */ \ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* fp regs (40-49) */ \ + 0, /* mdr reg */ \ + 1 /* cc reg */ \ + } + +/* 1 for registers not available across function calls. + These must include the FIXED_REGISTERS and also any + registers that can be used without being saved. + The latter must include the registers where values are returned + and the register where structure-value addresses are passed. + Aside from that, you can include as many other registers as you + like. */ + +#define CALL_USED_REGISTERS \ + { 1, 1, 0, 0, /* data regs */ \ + 1, 1, 0, 0, /* addr regs */ \ + 1, /* arg reg */ \ + 1, /* sp reg */ \ + 1, 1, 1, 1, 0, 0, 0, 0, /* extended regs */ \ + 1, 1, /* fp regs (18-19) */ \ + 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, /* fp regs (20-29) */ \ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, /* fp regs (30-39) */ \ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* fp regs (40-49) */ \ + 1, /* mdr reg */ \ + 1 /* cc reg */ \ + } + +/* Note: The definition of CALL_REALLY_USED_REGISTERS is not + redundant. It is needed when compiling in PIC mode because + the a2 register becomes fixed (and hence must be marked as + call_used) but in order to preserve the ABI it is not marked + as call_really_used. */ +#define CALL_REALLY_USED_REGISTERS CALL_USED_REGISTERS + +#define REG_ALLOC_ORDER \ + { 0, 1, 4, 5, 2, 3, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 8, 9 \ + , 42, 43, 44, 45, 46, 47, 48, 49, 34, 35, 36, 37, 38, 39, 40, 41 \ + , 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 50, 51 \ + } + +/* Return number of consecutive hard regs needed starting at reg REGNO + to hold something of mode MODE. + + This is ordinarily the length in words of a value of mode MODE + but can be less for certain modes in special long registers. */ + +#define HARD_REGNO_NREGS(REGNO, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* Value is 1 if hard register REGNO can hold a value of machine-mode + MODE. */ +#define HARD_REGNO_MODE_OK(REGNO, MODE) \ + mn10300_hard_regno_mode_ok ((REGNO), (MODE)) + +/* Value is 1 if it is a good idea to tie two pseudo registers + when one has mode MODE1 and one has mode MODE2. + If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2, + for any hard reg, then this must be 0 for correct output. */ +#define MODES_TIEABLE_P(MODE1, MODE2) \ + mn10300_modes_tieable ((MODE1), (MODE2)) + +/* 4 data, and effectively 3 address registers is small as far as I'm + concerned. */ +#define TARGET_SMALL_REGISTER_CLASSES_FOR_MODE_P hook_bool_mode_true + +/* Define the classes of registers for register constraints in the + machine description. Also define ranges of constants. + + One of the classes must always be named ALL_REGS and include all hard regs. + If there is more than one class, another class must be named NO_REGS + and contain no registers. + + The name GENERAL_REGS must be the name of a class (or an alias for + another name such as ALL_REGS). This is the class of registers + that is allowed by "g" or "r" in a register constraint. + Also, registers outside this class are allocated only when + instructions express preferences for them. + + The classes must be numbered in nondecreasing order; that is, + a larger-numbered class must never be contained completely + in a smaller-numbered class. + + For any two classes, it is very desirable that there be another + class that represents their union. */ + +enum reg_class +{ + NO_REGS, DATA_REGS, ADDRESS_REGS, SP_REGS, SP_OR_ADDRESS_REGS, + EXTENDED_REGS, FP_REGS, FP_ACC_REGS, CC_REGS, MDR_REGS, + GENERAL_REGS, SP_OR_GENERAL_REGS, ALL_REGS, LIM_REG_CLASSES +}; + +#define N_REG_CLASSES (int) LIM_REG_CLASSES + +/* Give names of register classes as strings for dump file. */ + +#define REG_CLASS_NAMES \ +{ "NO_REGS", "DATA_REGS", "ADDRESS_REGS", "SP_REGS", "SP_OR_ADDRESS_REGS", \ + "EXTENDED_REGS", "FP_REGS", "FP_ACC_REGS", "CC_REGS", "MDR_REGS", \ + "GENERAL_REGS", "SP_OR_GENERAL_REGS", "ALL_REGS", "LIM_REGS" \ +} + +/* Define which registers fit in which classes. + This is an initializer for a vector of HARD_REG_SET + of length N_REG_CLASSES. */ + +#define REG_CLASS_CONTENTS \ +{ { 0, 0 }, /* No regs */ \ + { 0x0000000f, 0 }, /* DATA_REGS */ \ + { 0x000001f0, 0 }, /* ADDRESS_REGS */ \ + { 0x00000200, 0 }, /* SP_REGS */ \ + { 0x000003f0, 0 }, /* SP_OR_ADDRESS_REGS */ \ + { 0x0003fc00, 0 }, /* EXTENDED_REGS */ \ + { 0xfffc0000, 0x3ffff },/* FP_REGS */ \ + { 0x03fc0000, 0 }, /* FP_ACC_REGS */ \ + { 0x00000000, 0x80000 },/* CC_REGS */ \ + { 0x00000000, 0x40000 },/* MDR_REGS */ \ + { 0x0003fdff, 0 }, /* GENERAL_REGS */ \ + { 0x0003ffff, 0 }, /* SP_OR_GENERAL_REGS */ \ + { 0xffffffff, 0xfffff } /* ALL_REGS */ \ +} + +/* The following macro defines cover classes for Integrated Register + Allocator. Cover classes is a set of non-intersected register + classes covering all hard registers used for register allocation + purpose. Any move between two registers of a cover class should be + cheaper than load or store of the registers. The macro value is + array of register classes with LIM_REG_CLASSES used as the end + marker. */ + +#define IRA_COVER_CLASSES \ +{ \ + GENERAL_REGS, FP_REGS, MDR_REGS, LIM_REG_CLASSES \ +} + +/* The same information, inverted: + Return the class number of the smallest class containing + reg number REGNO. This could be a conditional expression + or could index an array. */ + +#define REGNO_REG_CLASS(REGNO) \ + ((REGNO) <= LAST_DATA_REGNUM ? DATA_REGS : \ + (REGNO) <= LAST_ADDRESS_REGNUM ? ADDRESS_REGS : \ + (REGNO) == STACK_POINTER_REGNUM ? SP_REGS : \ + (REGNO) <= LAST_EXTENDED_REGNUM ? EXTENDED_REGS : \ + (REGNO) <= LAST_FP_REGNUM ? FP_REGS : \ + (REGNO) == MDR_REG ? MDR_REGS : \ + (REGNO) == CC_REG ? CC_REGS : \ + NO_REGS) + +/* The class value for index registers, and the one for base regs. */ +#define INDEX_REG_CLASS \ + (TARGET_AM33 ? GENERAL_REGS : DATA_REGS) +#define BASE_REG_CLASS \ + (TARGET_AM33 ? SP_OR_GENERAL_REGS : SP_OR_ADDRESS_REGS) + +/* Macros to check register numbers against specific register classes. */ + +/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx + and check its validity for a certain class. + We have two alternate definitions for each of them. + The usual definition accepts all pseudo regs; the other rejects + them unless they have been allocated suitable hard regs. + The symbol REG_OK_STRICT causes the latter definition to be used. + + Most source files want to accept pseudo regs in the hope that + they will get allocated to the class that the insn wants them to be in. + Source files for reload pass need to be strict. + After reload, it makes no difference, since pseudo regs have + been eliminated by then. */ + +/* These assume that REGNO is a hard or pseudo reg number. + They give nonzero only if REGNO is a hard reg of the suitable class + or a pseudo reg currently allocated to a suitable hard reg. + Since they use reg_renumber, they are safe only once reg_renumber + has been allocated, which happens in local-alloc.c. */ + +#ifndef REG_OK_STRICT +# define REG_STRICT 0 +#else +# define REG_STRICT 1 +#endif + +#define REGNO_DATA_P(regno, strict) \ + mn10300_regno_in_class_p (regno, DATA_REGS, strict) +#define REGNO_ADDRESS_P(regno, strict) \ + mn10300_regno_in_class_p (regno, ADDRESS_REGS, strict) +#define REGNO_EXTENDED_P(regno, strict) \ + mn10300_regno_in_class_p (regno, EXTENDED_REGS, strict) +#define REGNO_GENERAL_P(regno, strict) \ + mn10300_regno_in_class_p (regno, GENERAL_REGS, strict) + +#define REGNO_STRICT_OK_FOR_BASE_P(regno, strict) \ + mn10300_regno_in_class_p (regno, BASE_REG_CLASS, strict) +#define REGNO_OK_FOR_BASE_P(regno) \ + (REGNO_STRICT_OK_FOR_BASE_P ((regno), REG_STRICT)) +#define REG_OK_FOR_BASE_P(X) \ + (REGNO_OK_FOR_BASE_P (REGNO (X))) + +#define REGNO_STRICT_OK_FOR_BIT_BASE_P(regno, strict) \ + mn10300_regno_in_class_p (regno, ADDRESS_REGS, strict) +#define REGNO_OK_FOR_BIT_BASE_P(regno) \ + (REGNO_STRICT_OK_FOR_BIT_BASE_P ((regno), REG_STRICT)) +#define REG_OK_FOR_BIT_BASE_P(X) \ + (REGNO_OK_FOR_BIT_BASE_P (REGNO (X))) + +#define REGNO_STRICT_OK_FOR_INDEX_P(regno, strict) \ + mn10300_regno_in_class_p (regno, INDEX_REG_CLASS, strict) +#define REGNO_OK_FOR_INDEX_P(regno) \ + (REGNO_STRICT_OK_FOR_INDEX_P ((regno), REG_STRICT)) +#define REG_OK_FOR_INDEX_P(X) \ + (REGNO_OK_FOR_INDEX_P (REGNO (X))) + +#define LIMIT_RELOAD_CLASS(MODE, CLASS) \ + (!TARGET_AM33 && (MODE == QImode || MODE == HImode) ? DATA_REGS : CLASS) + +/* Return the maximum number of consecutive registers + needed to represent mode MODE in a register of class CLASS. */ + +#define CLASS_MAX_NREGS(CLASS, MODE) \ + ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD) + +/* A class that contains registers which the compiler must always + access in a mode that is the same size as the mode in which it + loaded the register. */ +#define CLASS_CANNOT_CHANGE_SIZE FP_REGS + +/* Return 1 if VALUE is in the range specified. */ + +#define INT_8_BITS(VALUE) ((unsigned) (VALUE) + 0x80 < 0x100) +#define INT_16_BITS(VALUE) ((unsigned) (VALUE) + 0x8000 < 0x10000) + + +/* Stack layout; function entry, exit and calling. */ + +/* Define this if pushing a word on the stack + makes the stack pointer a smaller address. */ + +#define STACK_GROWS_DOWNWARD + +/* Define this to nonzero if the nominal address of the stack frame + is at the high-address end of the local variables; + that is, each additional local variable allocated + goes at a more negative offset in the frame. */ + +#define FRAME_GROWS_DOWNWARD 1 + +/* Offset within stack frame to start allocating local variables at. + If FRAME_GROWS_DOWNWARD, this is the offset to the END of the + first local allocated. Otherwise, it is the offset to the BEGINNING + of the first local allocated. */ + +#define STARTING_FRAME_OFFSET 0 + +/* Offset of first parameter from the argument pointer register value. */ +/* Is equal to the size of the saved fp + pc, even if an fp isn't + saved since the value is used before we know. */ + +#define FIRST_PARM_OFFSET(FNDECL) 4 + +/* But the CFA is at the arg pointer directly, not at the first argument. */ +#define ARG_POINTER_CFA_OFFSET(FNDECL) 0 + +#define ELIMINABLE_REGS \ +{{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM}, \ + { ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM}, \ + { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM}} + +#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \ + OFFSET = mn10300_initial_offset (FROM, TO) + +/* We use d0/d1 for passing parameters, so allocate 8 bytes of space + for a register flushback area. */ +#define REG_PARM_STACK_SPACE(DECL) 8 +#define OUTGOING_REG_PARM_STACK_SPACE(FNTYPE) 1 +#define ACCUMULATE_OUTGOING_ARGS 1 + +/* So we can allocate space for return pointers once for the function + instead of around every call. */ +#define STACK_POINTER_OFFSET 4 + +/* 1 if N is a possible register number for function argument passing. + On the MN10300, d0 and d1 are used in this way. */ + +#define FUNCTION_ARG_REGNO_P(N) ((N) <= 1) + + +/* Define a data type for recording info about an argument list + during the scan of that argument list. This data type should + hold all necessary information about the function itself + and about the args processed so far, enough to enable macros + such as FUNCTION_ARG to determine where the next arg should go. + + On the MN10300, this is a single integer, which is a number of bytes + of arguments scanned so far. */ + +#define CUMULATIVE_ARGS struct cum_arg + +struct cum_arg +{ + int nbytes; +}; + +/* Initialize a variable CUM of type CUMULATIVE_ARGS + for a call to a function whose data type is FNTYPE. + For a library call, FNTYPE is 0. + + On the MN10300, the offset starts at 0. */ + +#define INIT_CUMULATIVE_ARGS(CUM, FNTYPE, LIBNAME, INDIRECT, N_NAMED_ARGS) \ + ((CUM).nbytes = 0) + +#define FUNCTION_VALUE_REGNO_P(N) mn10300_function_value_regno_p (N) + +#define DEFAULT_PCC_STRUCT_RETURN 0 + +/* EXIT_IGNORE_STACK should be nonzero if, when returning from a function, + the stack pointer does not matter. The value is tested only in + functions that have frame pointers. + No definition is equivalent to always zero. */ + +#define EXIT_IGNORE_STACK 1 + +/* Output assembler code to FILE to increment profiler label # LABELNO + for profiling a function entry. */ + +#define FUNCTION_PROFILER(FILE, LABELNO) ; + +/* Length in units of the trampoline for entering a nested function. */ + +#define TRAMPOLINE_SIZE 16 +#define TRAMPOLINE_ALIGNMENT 32 + +/* A C expression whose value is RTL representing the value of the return + address for the frame COUNT steps up from the current frame. + + On the mn10300, the return address is not at a constant location + due to the frame layout. Luckily, it is at a constant offset from + the argument pointer, so we define RETURN_ADDR_RTX to return a + MEM using arg_pointer_rtx. Reload will replace arg_pointer_rtx + with a reference to the stack/frame pointer + an appropriate offset. */ + +#define RETURN_ADDR_RTX(COUNT, FRAME) \ + ((COUNT == 0) \ + ? gen_rtx_MEM (Pmode, arg_pointer_rtx) \ + : (rtx) 0) + +/* The return address is saved both in the stack and in MDR. Using + the stack location is handiest for what unwinding needs. */ +#define INCOMING_RETURN_ADDR_RTX \ + gen_rtx_MEM (VOIDmode, gen_rtx_REG (VOIDmode, STACK_POINTER_REGNUM)) + +/* Maximum number of registers that can appear in a valid memory address. */ + +#define MAX_REGS_PER_ADDRESS 2 + + +/* We have post-increments. */ +#define HAVE_POST_INCREMENT TARGET_AM33 +#define HAVE_POST_MODIFY_DISP TARGET_AM33 + +/* ... But we don't want to use them for block moves. Small offsets are + just as effective, at least for inline block move sizes, and appears + to produce cleaner code. */ +#define USE_LOAD_POST_INCREMENT(M) 0 +#define USE_STORE_POST_INCREMENT(M) 0 + +/* Accept either REG or SUBREG where a register is valid. */ + +#define RTX_OK_FOR_BASE_P(X, strict) \ + ((REG_P (X) && REGNO_STRICT_OK_FOR_BASE_P (REGNO (X), \ + (strict))) \ + || (GET_CODE (X) == SUBREG && REG_P (SUBREG_REG (X)) \ + && REGNO_STRICT_OK_FOR_BASE_P (REGNO (SUBREG_REG (X)), \ + (strict)))) + +#define LEGITIMIZE_RELOAD_ADDRESS(X,MODE,OPNUM,TYPE,IND_L,WIN) \ +do { \ + rtx new_x = mn10300_legitimize_reload_address (X, MODE, OPNUM, TYPE, IND_L); \ + if (new_x) \ + { \ + X = new_x; \ + goto WIN; \ + } \ +} while (0) + + +/* Nonzero if the constant value X is a legitimate general operand. + It is given that X satisfies CONSTANT_P or is a CONST_DOUBLE. */ +#define LEGITIMATE_CONSTANT_P(X) mn10300_legitimate_constant_p (X) + +/* Zero if this needs fixing up to become PIC. */ + +#define LEGITIMATE_PIC_OPERAND_P(X) \ + mn10300_legitimate_pic_operand_p (X) + +/* Register to hold the addressing base for + position independent code access to data items. */ +#define PIC_OFFSET_TABLE_REGNUM PIC_REG + +/* The name of the pseudo-symbol representing the Global Offset Table. */ +#define GOT_SYMBOL_NAME "*_GLOBAL_OFFSET_TABLE_" + +#define SYMBOLIC_CONST_P(X) \ +((GET_CODE (X) == SYMBOL_REF || GET_CODE (X) == LABEL_REF) \ + && ! LEGITIMATE_PIC_OPERAND_P (X)) + +/* Non-global SYMBOL_REFs have SYMBOL_REF_FLAG enabled. */ +#define MN10300_GLOBAL_P(X) (! SYMBOL_REF_FLAG (X)) + +#define SELECT_CC_MODE(OP, X, Y) mn10300_select_cc_mode (OP, X, Y) +#define REVERSIBLE_CC_MODE(MODE) 0 + +/* Nonzero if access to memory by bytes or half words is no faster + than accessing full words. */ +#define SLOW_BYTE_ACCESS 1 + +#define NO_FUNCTION_CSE + +/* According expr.c, a value of around 6 should minimize code size, and + for the MN10300 series, that's our primary concern. */ +#define MOVE_RATIO(speed) 6 + +#define TEXT_SECTION_ASM_OP "\t.section .text" +#define DATA_SECTION_ASM_OP "\t.section .data" +#define BSS_SECTION_ASM_OP "\t.section .bss" + +#define ASM_COMMENT_START "#" + +/* Output to assembler file text saying following lines + may contain character constants, extra white space, comments, etc. */ + +#define ASM_APP_ON "#APP\n" + +/* Output to assembler file text saying following lines + no longer contain unusual constructs. */ + +#define ASM_APP_OFF "#NO_APP\n" + +#undef USER_LABEL_PREFIX +#define USER_LABEL_PREFIX "_" + +/* This says how to output the assembler to define a global + uninitialized but not common symbol. + Try to use asm_output_bss to implement this macro. */ + +#define ASM_OUTPUT_ALIGNED_BSS(FILE, DECL, NAME, SIZE, ALIGN) \ + asm_output_aligned_bss ((FILE), (DECL), (NAME), (SIZE), (ALIGN)) + +/* Globalizing directive for a label. */ +#define GLOBAL_ASM_OP "\t.global " + +/* This is how to output a reference to a user-level label named NAME. + `assemble_name' uses this. */ + +#undef ASM_OUTPUT_LABELREF +#define ASM_OUTPUT_LABELREF(FILE, NAME) \ + asm_fprintf (FILE, "%U%s", (*targetm.strip_name_encoding) (NAME)) + +/* This is how we tell the assembler that two symbols have the same value. */ + +#define ASM_OUTPUT_DEF(FILE,NAME1,NAME2) \ + do \ + { \ + assemble_name (FILE, NAME1); \ + fputs (" = ", FILE); \ + assemble_name (FILE, NAME2); \ + fputc ('\n', FILE); \ + } \ + while (0) + +/* How to refer to registers in assembler output. + This sequence is indexed by compiler's hard-register-number (see above). */ + +#define REGISTER_NAMES \ +{ "d0", "d1", "d2", "d3", "a0", "a1", "a2", "a3", "ap", "sp", \ + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" \ +, "fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7" \ +, "fs8", "fs9", "fs10", "fs11", "fs12", "fs13", "fs14", "fs15" \ +, "fs16", "fs17", "fs18", "fs19", "fs20", "fs21", "fs22", "fs23" \ +, "fs24", "fs25", "fs26", "fs27", "fs28", "fs29", "fs30", "fs31" \ +, "mdr", "EPSW" \ +} + +#define ADDITIONAL_REGISTER_NAMES \ +{ {"r8", 4}, {"r9", 5}, {"r10", 6}, {"r11", 7}, \ + {"r12", 0}, {"r13", 1}, {"r14", 2}, {"r15", 3}, \ + {"e0", 10}, {"e1", 11}, {"e2", 12}, {"e3", 13}, \ + {"e4", 14}, {"e5", 15}, {"e6", 16}, {"e7", 17} \ +, {"fd0", 18}, {"fd2", 20}, {"fd4", 22}, {"fd6", 24} \ +, {"fd8", 26}, {"fd10", 28}, {"fd12", 30}, {"fd14", 32} \ +, {"fd16", 34}, {"fd18", 36}, {"fd20", 38}, {"fd22", 40} \ +, {"fd24", 42}, {"fd26", 44}, {"fd28", 46}, {"fd30", 48} \ +, {"cc", CC_REG} \ +} + +/* Print an instruction operand X on file FILE. + look in mn10300.c for details */ + +#define PRINT_OPERAND(FILE, X, CODE) \ + mn10300_print_operand (FILE, X, CODE) + +/* Print a memory operand whose address is X, on file FILE. + This uses a function in output-vax.c. */ + +#define PRINT_OPERAND_ADDRESS(FILE, ADDR) \ + mn10300_print_operand_address (FILE, ADDR) + +/* This is how to output an element of a case-vector that is absolute. */ + +#define ASM_OUTPUT_ADDR_VEC_ELT(FILE, VALUE) \ + fprintf (FILE, "\t%s .L%d\n", ".long", VALUE) + +/* This is how to output an element of a case-vector that is relative. */ + +#define ASM_OUTPUT_ADDR_DIFF_ELT(FILE, BODY, VALUE, REL) \ + fprintf (FILE, "\t%s .L%d-.L%d\n", ".long", VALUE, REL) + +#define ASM_OUTPUT_ALIGN(FILE,LOG) \ + if ((LOG) != 0) \ + fprintf (FILE, "\t.align %d\n", (LOG)) + +/* We don't have to worry about dbx compatibility for the mn10300. */ +#define DEFAULT_GDB_EXTENSIONS 1 + +/* Use dwarf2 debugging info by default. */ +#undef PREFERRED_DEBUGGING_TYPE +#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG +#define DWARF2_DEBUGGING_INFO 1 +#define DWARF2_ASM_LINE_DEBUG_INFO 1 + +/* Specify the machine mode that this machine uses + for the index in the tablejump instruction. */ +#define CASE_VECTOR_MODE Pmode + +/* Define if operations between registers always perform the operation + on the full register even if a narrower mode is specified. */ +#define WORD_REGISTER_OPERATIONS + +#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND + +/* This flag, if defined, says the same insns that convert to a signed fixnum + also convert validly to an unsigned one. */ +#define FIXUNS_TRUNC_LIKE_FIX_TRUNC + +/* Max number of bytes we can move from memory to memory + in one reasonably fast instruction. */ +#define MOVE_MAX 4 + +/* Define if shifts truncate the shift count + which implies one can omit a sign-extension or zero-extension + of a shift count. */ +#define SHIFT_COUNT_TRUNCATED 1 + +/* Value is 1 if truncating an integer of INPREC bits to OUTPREC bits + is done just by pretending it is already truncated. */ +#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1 + +/* Specify the machine mode that pointers have. + After generation of rtl, the compiler makes no further distinction + between pointers and any other objects of this machine mode. */ +#define Pmode SImode + +/* A function address in a call instruction + is a byte address (for indexing purposes) + so give the MEM rtx a byte's mode. */ +#define FUNCTION_MODE QImode + +/* The assembler op to get a word. */ + +#define FILE_ASM_OP "\t.file\n" + diff --git a/gcc/config/mn10300/mn10300.md b/gcc/config/mn10300/mn10300.md new file mode 100644 index 000000000..3d8e91470 --- /dev/null +++ b/gcc/config/mn10300/mn10300.md @@ -0,0 +1,2154 @@ +;; GCC machine description for Matsushita MN10300 +;; 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 +;; . + +;; The original PO technology requires these to be ordered by speed, +;; so that assigner will pick the fastest. + +;; See file "rtl.def" for documentation on define_insn, match_*, et. al. + +(define_constants [ + (PIC_REG 6) + (SP_REG 9) + (MDR_REG 50) + (CC_REG 51) + + (UNSPEC_PIC 1) + (UNSPEC_GOT 2) + (UNSPEC_GOTOFF 3) + (UNSPEC_PLT 4) + (UNSPEC_GOTSYM_OFF 5) + + (UNSPEC_EXT 6) + (UNSPEC_BSCH 7) + + ;; This is used to encode LIW patterns. + (UNSPEC_LIW 8) +]) + +(include "predicates.md") +(include "constraints.md") + +;; Processor type. This attribute must exactly match the processor_type +;; enumeration in mn10300.h. +(define_attr "cpu" "mn10300,am33,am33_2,am34" + (const (symbol_ref "(enum attr_cpu) mn10300_tune_cpu"))) + +;; Used to control the "enabled" attribute on a per-instruction basis. +(define_attr "isa" "base,am33,am33_2,am34" + (const_string "base")) + +(define_attr "enabled" "" + (cond [(eq_attr "isa" "base") + (const_int 1) + + (and (eq_attr "isa" "am33") + (ne (symbol_ref "TARGET_AM33") (const_int 0))) + (const_int 1) + + (and (eq_attr "isa" "am33_2") + (ne (symbol_ref "TARGET_AM33_2") (const_int 0))) + (const_int 1) + + (and (eq_attr "isa" "am34") + (ne (symbol_ref "TARGET_AM34") (const_int 0))) + (const_int 1) + ] + (const_int 0)) +) + +(define_mode_iterator INT [QI HI SI]) + + +;; Bundling of smaller insns into a long instruction word (LIW) +(define_automaton "liw_bundling") +(automata_option "ndfa") + +(define_cpu_unit "liw_op1_u,liw_op2_u" "liw_bundling") + +(define_attr "liw" "op1,op2,both,either" + (const_string "both")) +;; Note: this list must match the one defined for liw_op_names[]. +(define_attr "liw_op" "add,cmp,sub,mov,and,or,xor,asr,lsr,asl,none,max" + (const_string "none")) + +(define_insn_reservation "liw_op1" 1 + (and (ior (eq_attr "cpu" "am33") + (eq_attr "cpu" "am33_2") + (eq_attr "cpu" "am34")) + (eq_attr "liw" "op1")) + "liw_op1_u"); +(define_insn_reservation "liw_op2" 1 + (and (ior (eq_attr "cpu" "am33") + (eq_attr "cpu" "am33_2") + (eq_attr "cpu" "am34")) + (eq_attr "liw" "op2")) + "liw_op2_u"); +(define_insn_reservation "liw_both" 1 + (and (ior (eq_attr "cpu" "am33") + (eq_attr "cpu" "am33_2") + (eq_attr "cpu" "am34")) + (eq_attr "liw" "both")) + "liw_op1_u + liw_op2_u"); +(define_insn_reservation "liw_either" 1 + (and (ior (eq_attr "cpu" "am33") + (eq_attr "cpu" "am33_2") + (eq_attr "cpu" "am34")) + (eq_attr "liw" "either")) + "liw_op1_u | liw_op2_u"); + +;; ---------------------------------------------------------------------- +;; Pipeline description. +;; ---------------------------------------------------------------------- + +;; The AM33 only has a single pipeline. It has five stages (fetch, +;; decode, execute, memory access, writeback) each of which normally +;; takes a single CPU clock cycle. + +;; The timings attribute consists of two numbers, the first is the +;; throughput, which is the number of cycles the instruction takes +;; to execute and generate a result. The second is the latency +;; which is the effective number of cycles the instruction takes to +;; execute if its result is used by the following instruction. The +;; latency is always greater than or equal to the throughput. +;; These values were taken from the Appendix of the "MN103E Series +;; Instruction Manual" and the timings for the AM34. + +;; Note - it would be nice to use strings rather than integers for +;; the possible values of this attribute, so that we can have the +;; gcc build mechanism check for values that are not supported by +;; the reservations below. But this will not work because the code +;; in mn10300_adjust_sched_cost() needs integers not strings. + +(define_attr "timings" "" (const_int 11)) + +(define_automaton "pipelining") +(define_cpu_unit "throughput" "pipelining") + +(define_insn_reservation "throughput__1_latency__1" 1 + (eq_attr "timings" "11") "throughput") +(define_insn_reservation "throughput__1_latency__2" 2 + (eq_attr "timings" "12") "throughput,nothing") +(define_insn_reservation "throughput__1_latency__3" 3 + (eq_attr "timings" "13") "throughput,nothing*2") +(define_insn_reservation "throughput__1_latency__4" 4 + (eq_attr "timings" "14") "throughput,nothing*3") +(define_insn_reservation "throughput__2_latency__2" 2 + (eq_attr "timings" "22") "throughput*2") +(define_insn_reservation "throughput__2_latency__3" 3 + (eq_attr "timings" "23") "throughput*2,nothing") +(define_insn_reservation "throughput__2_latency__4" 4 + (eq_attr "timings" "24") "throughput*2,nothing*2") +(define_insn_reservation "throughput__2_latency__5" 5 + (eq_attr "timings" "25") "throughput*2,nothing*3") +(define_insn_reservation "throughput__3_latency__3" 3 + (eq_attr "timings" "33") "throughput*3") +(define_insn_reservation "throughput__3_latency__7" 7 + (eq_attr "timings" "37") "throughput*3,nothing*4") +(define_insn_reservation "throughput__4_latency__4" 4 + (eq_attr "timings" "44") "throughput*4") +(define_insn_reservation "throughput__4_latency__7" 7 + (eq_attr "timings" "47") "throughput*4,nothing*3") +(define_insn_reservation "throughput__4_latency__8" 8 + (eq_attr "timings" "48") "throughput*4,nothing*4") +(define_insn_reservation "throughput__5_latency__5" 5 + (eq_attr "timings" "55") "throughput*5") +(define_insn_reservation "throughput__6_latency__6" 6 + (eq_attr "timings" "66") "throughput*6") +(define_insn_reservation "throughput__7_latency__7" 7 + (eq_attr "timings" "77") "throughput*7") +(define_insn_reservation "throughput__7_latency__8" 8 + (eq_attr "timings" "78") "throughput*7,nothing") +(define_insn_reservation "throughput__8_latency__8" 8 + (eq_attr "timings" "88") "throughput*8") +(define_insn_reservation "throughput__9_latency__9" 9 + (eq_attr "timings" "99") "throughput*9") +(define_insn_reservation "throughput__8_latency_14" 14 + (eq_attr "timings" "814") "throughput*8,nothing*6") +(define_insn_reservation "throughput__9_latency_10" 10 + (eq_attr "timings" "910") "throughput*9,nothing") +(define_insn_reservation "throughput_10_latency_10" 10 + (eq_attr "timings" "1010") "throughput*10") +(define_insn_reservation "throughput_12_latency_16" 16 + (eq_attr "timings" "1216") "throughput*12,nothing*4") +(define_insn_reservation "throughput_13_latency_13" 13 + (eq_attr "timings" "1313") "throughput*13") +(define_insn_reservation "throughput_14_latency_14" 14 + (eq_attr "timings" "1414") "throughput*14") +(define_insn_reservation "throughput_13_latency_17" 17 + (eq_attr "timings" "1317") "throughput*13,nothing*4") +(define_insn_reservation "throughput_23_latency_27" 27 + (eq_attr "timings" "2327") "throughput*23,nothing*4") +(define_insn_reservation "throughput_25_latency_31" 31 + (eq_attr "timings" "2531") "throughput*25,nothing*6") +(define_insn_reservation "throughput_38_latency_39" 39 + (eq_attr "timings" "3839") "throughput*38,nothing") +(define_insn_reservation "throughput_39_latency_40" 40 + (eq_attr "timings" "3940") "throughput*39,nothing") +(define_insn_reservation "throughput_40_latency_40" 40 + (eq_attr "timings" "4040") "throughput*40") +(define_insn_reservation "throughput_41_latency_42" 42 + (eq_attr "timings" "4142") "throughput*41,nothing") +(define_insn_reservation "throughput_42_latency_43" 44 + (eq_attr "timings" "4243") "throughput*42,nothing") +(define_insn_reservation "throughput_43_latency_44" 44 + (eq_attr "timings" "4344") "throughput*43,nothing") +(define_insn_reservation "throughput_45_latency_46" 46 + (eq_attr "timings" "4546") "throughput*45,nothing") +(define_insn_reservation "throughput_47_latency_53" 53 + (eq_attr "timings" "4753") "throughput*47,nothing*6") + +;; Note - the conflict between memory load/store instructions +;; and floating point instructions described in section 1-7-4 +;; of Chapter 3 of the MN103E Series Instruction Manual is +;; handled by the mn10300_adjust_sched_cost function. + +;; ---------------------------------------------------------------------- +;; MOVE INSTRUCTIONS +;; ---------------------------------------------------------------------- + +;; movqi + +(define_expand "movqi" + [(set (match_operand:QI 0 "nonimmediate_operand") + (match_operand:QI 1 "general_operand"))] + "" +{ + /* One of the ops has to be in a register. */ + if (!register_operand (operand0, QImode) + && !register_operand (operand1, QImode)) + operands[1] = force_reg (QImode, operand1); +}) + +(define_insn "*movqi_internal" + [(set (match_operand:QI 0 "nonimmediate_operand" "=*r,D*r,D*r,D,m") + (match_operand:QI 1 "general_operand" " 0,D*r, i,m,D"))] + "(register_operand (operands[0], QImode) + || register_operand (operands[1], QImode))" +{ + switch (which_alternative) + { + case 0: + return ""; + case 1: + case 2: + return "mov %1,%0"; + case 3: + case 4: + return "movbu %1,%0"; + default: + gcc_unreachable (); + } +} + [(set_attr_alternative "timings" + [(const_int 11) + (const_int 11) + (const_int 11) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 22)) + ])] +) + +;; movhi + +(define_expand "movhi" + [(set (match_operand:HI 0 "nonimmediate_operand") + (match_operand:HI 1 "general_operand"))] + "" +{ + /* One of the ops has to be in a register. */ + if (!register_operand (operand1, HImode) + && !register_operand (operand0, HImode)) + operands[1] = force_reg (HImode, operand1); +}) + +(define_insn "*movhi_internal" + [(set (match_operand:HI 0 "nonimmediate_operand" "=*r,D*r,D*r,D,m") + (match_operand:HI 1 "general_operand" " 0, i,D*r,m,D"))] + "(register_operand (operands[0], HImode) + || register_operand (operands[1], HImode))" +{ + switch (which_alternative) + { + case 0: + return ""; + case 1: + /* Note that "MOV imm8,An" is already zero-extending, and is 2 bytes. + We have "MOV imm16,Dn" at 3 bytes. The only win for the 4 byte + movu is for an 8-bit unsigned move into Rn. */ + if (TARGET_AM33 + && CONST_INT_P (operands[1]) + && IN_RANGE (INTVAL (operands[1]), 0x80, 0xff) + && REGNO_EXTENDED_P (REGNO (operands[0]), 1)) + return "movu %1,%0"; + /* FALLTHRU */ + case 2: + return "mov %1,%0"; + case 3: + case 4: + return "movhu %1,%0"; + default: + gcc_unreachable (); + } +} + [(set_attr_alternative "timings" + [(const_int 11) + (const_int 11) + (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 22)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 22)) + ])] +) + +;; movsi and helpers + +;; We use this to handle addition of two values when one operand is the +;; stack pointer and the other is a memory reference of some kind. Reload +;; does not handle them correctly without this expander. +(define_expand "reload_plus_sp_const" + [(set (match_operand:SI 0 "register_operand" "=r") + (match_operand:SI 1 "impossible_plus_operand" "")) + (clobber (match_operand:SI 2 "register_operand" "=&A"))] + "" +{ + rtx dest, scratch, other; + + dest = operands[0]; + scratch = operands[2]; + + other = XEXP (operands[1], 1); + if (other == stack_pointer_rtx) + other = XEXP (operands[1], 0); + + if (true_regnum (other) == true_regnum (dest)) + { + gcc_assert (true_regnum (scratch) != true_regnum (dest)); + emit_move_insn (scratch, stack_pointer_rtx); + emit_insn (gen_addsi3 (dest, dest, scratch)); + } + else if (TARGET_AM33 || REGNO_REG_CLASS (true_regnum (dest)) == ADDRESS_REGS) + { + emit_move_insn (dest, stack_pointer_rtx); + if (other == stack_pointer_rtx) + emit_insn (gen_addsi3 (dest, dest, dest)); + else if (other != const0_rtx) + emit_insn (gen_addsi3 (dest, dest, other)); + } + else + { + emit_move_insn (scratch, stack_pointer_rtx); + if (other == stack_pointer_rtx) + { + emit_move_insn (dest, scratch); + emit_insn (gen_addsi3 (dest, dest, dest)); + } + else if (other != const0_rtx) + { + emit_move_insn (dest, other); + emit_insn (gen_addsi3 (dest, dest, scratch)); + } + else + emit_move_insn (dest, scratch); + } + DONE; +}) + +(define_expand "movsi" + [(set (match_operand:SI 0 "nonimmediate_operand") + (match_operand:SI 1 "general_operand"))] + "" +{ + /* One of the ops has to be in a register. */ + if (!register_operand (operand1, SImode) + && !register_operand (operand0, SImode)) + operands[1] = force_reg (SImode, operand1); + if (flag_pic) + { + rtx temp; + if (SYMBOLIC_CONST_P (operands[1])) + { + if (MEM_P (operands[0])) + operands[1] = force_reg (Pmode, operands[1]); + else + { + temp = (!can_create_pseudo_p () + ? operands[0] + : gen_reg_rtx (Pmode)); + operands[1] = mn10300_legitimize_pic_address (operands[1], temp); + } + } + else if (GET_CODE (operands[1]) == CONST + && GET_CODE (XEXP (operands[1], 0)) == PLUS + && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0))) + { + temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode); + temp = mn10300_legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0), + temp); + operands[1] = expand_binop (SImode, add_optab, temp, + XEXP (XEXP (operands[1], 0), 1), + (!can_create_pseudo_p () + ? temp + : gen_reg_rtx (Pmode)), + 0, OPTAB_LIB_WIDEN); + } + } +}) + +(define_insn "*movsi_internal" + [(set (match_operand:SI 0 "nonimmediate_operand" + "=r,r,r,r,m,r, A,*y,*y,*z,*d") + (match_operand:SI 1 "general_operand" + " 0,O,i,r,r,m,*y, A, i,*d,*z"))] + "register_operand (operands[0], SImode) + || register_operand (operands[1], SImode)" +{ + switch (which_alternative) + { + case 0: + return ""; + case 1: /* imm-reg. */ + case 2: + /* See movhi for a discussion of sizes for 8-bit movu. Note that the + 24-bit movu is 6 bytes, which is the same size as the full 32-bit + mov form for An and Dn. So again movu is only a win for Rn. */ + if (TARGET_AM33 + && CONST_INT_P (operands[1]) + && REGNO_EXTENDED_P (REGNO (operands[0]), 1)) + { + HOST_WIDE_INT val = INTVAL (operands[1]); + if (IN_RANGE (val, 0x80, 0xff) + || IN_RANGE (val, 0x800000, 0xffffff)) + return "movu %1,%0"; + } + /* FALLTHRU */ + case 3: /* reg-reg */ + case 4: /* reg-mem */ + case 5: /* mem-reg */ + case 6: /* sp-reg */ + case 7: /* reg-sp */ + case 8: /* imm-sp */ + case 9: /* reg-mdr */ + case 10: /* mdr-reg */ + return "mov %1,%0"; + default: + gcc_unreachable (); + } +} + [(set_attr "isa" "*,*,*,*,*,*,*,*,am33,*,*") + (set_attr "liw" "*,either,*,either,*,*,*,*,*,*,*") + (set_attr "liw_op" "mov") + (set_attr_alternative "timings" + [(const_int 11) + (const_int 22) + (const_int 22) + (const_int 11) + (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 22)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 22)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (const_int 11) + (const_int 11) + (const_int 11) + ])] +) + +(define_expand "movsf" + [(set (match_operand:SF 0 "nonimmediate_operand") + (match_operand:SF 1 "general_operand"))] + "TARGET_AM33_2" +{ + /* One of the ops has to be in a register. */ + if (!register_operand (operand1, SFmode) + && !register_operand (operand0, SFmode)) + operands[1] = force_reg (SFmode, operand1); +}) + +(define_insn "*movsf_internal" + [(set (match_operand:SF 0 "nonimmediate_operand" "=rf,r,f,r,f,r,f,r,m,f,Q") + (match_operand:SF 1 "general_operand" " 0,F,F,r,f,f,r,m,r,Q,f"))] + "TARGET_AM33_2 + && (register_operand (operands[0], SFmode) + || register_operand (operands[1], SFmode))" +{ + switch (which_alternative) + { + case 0: + return ""; + case 1: + case 3: + case 7: + case 8: + return "mov %1,%0"; + case 2: + case 4: + case 5: + case 6: + case 9: + case 10: + return "fmov %1,%0"; + default: + gcc_unreachable (); + } +} + [(set_attr_alternative "timings" + [(const_int 11) + (const_int 22) + (if_then_else (eq_attr "cpu" "am34") + (const_int 47) (const_int 25)) + (const_int 11) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 14)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 12)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 14)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + ])] +) + +;; If the flags register is not live, generate CLR instead of MOV 0. +;; For MN103, this is only legal for DATA_REGS; for AM33 this is legal +;; but not a win for ADDRESS_REGS. +(define_peephole2 + [(set (match_operand:INT 0 "register_operand" "") (const_int 0))] + "peep2_regno_dead_p (0, CC_REG) + && (REGNO_DATA_P (REGNO (operands[0]), 1) + || REGNO_EXTENDED_P (REGNO (operands[0]), 1))" + [(parallel [(set (match_dup 0) (const_int 0)) + (clobber (reg:CC CC_REG))])] +) + +(define_insn "*mov_clr" + [(set (match_operand:INT 0 "register_operand" "=D") + (const_int 0)) + (clobber (reg:CC CC_REG))] + "" + "clr %0" +) + +;; ---------------------------------------------------------------------- +;; ADD INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "addsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,!*y,!r") + (plus:SI (match_operand:SI 1 "register_operand" "%0,0,0, 0, r") + (match_operand:SI 2 "nonmemory_operand" "r,O,i, i, r"))) + (clobber (reg:CC CC_REG))] + "" + { return mn10300_output_add (operands, false); } + [(set_attr "timings" "11,11,11,11,22") + (set_attr "liw" "either,either,*,*,*") + (set_attr "liw_op" "add")] +) + +;; Note that ADD IMM,SP does not set the flags, so omit that here. +(define_insn "*addsi3_flags" + [(set (match_operand:SI 0 "register_operand" "=r,!r") + (plus:SI (match_operand:SI 1 "register_operand" "%0, r") + (match_operand:SI 2 "nonmemory_operand" "ri, r"))) + (set (reg CC_REG) + (compare (plus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "reload_completed && mn10300_match_ccmode (insn, CCZNCmode)" + { return mn10300_output_add (operands, true); } + [(set_attr "timings" "11,22")] +) + +;; A helper to expand the above, with the CC_MODE filled in. +(define_expand "addsi3_flags" + [(parallel [(set (match_operand:SI 0 "register_operand") + (plus:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "nonmemory_operand"))) + (set (reg:CCZNC CC_REG) + (compare:CCZNC (plus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))])] + "" +) + +(define_insn "addc_internal" + [(set (match_operand:SI 0 "register_operand" "=D,r,r") + (plus:SI + (plus:SI + (ltu:SI (reg:CC CC_REG) (const_int 0)) + (match_operand:SI 1 "register_operand" "%0,0,r")) + (match_operand:SI 2 "reg_or_am33_const_operand" " D,i,r"))) + (clobber (reg:CC CC_REG))] + "reload_completed" + "@ + addc %2,%0 + addc %2,%0 + addc %2,%1,%0" + [(set_attr "isa" "*,am33,am33")] +) + +(define_expand "adddi3" + [(set (match_operand:DI 0 "register_operand" "") + (plus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "nonmemory_operand" "")))] + "" +{ + rtx op0l, op0h, op1l, op1h, op2l, op2h; + + op0l = gen_lowpart (SImode, operands[0]); + op1l = gen_lowpart (SImode, operands[1]); + op2l = gen_lowpart (SImode, operands[2]); + op0h = gen_highpart (SImode, operands[0]); + op1h = gen_highpart (SImode, operands[1]); + op2h = gen_highpart_mode (SImode, DImode, operands[2]); + + if (!reg_or_am33_const_operand (op2h, SImode)) + op2h = force_reg (SImode, op2h); + + emit_insn (gen_adddi3_internal (op0l, op0h, op1l, op2l, op1h, op2h)); + DONE; +}) + +;; Note that reload only supports one commutative operand. Thus we cannot +;; auto-swap both the high and low outputs with their matching constraints. +;; For MN103, we're strapped for registers but thankfully the alternatives +;; are few. For AM33, it becomes much easier to not represent the early +;; clobber and 6 permutations of immediate and three-operand adds, but +;; instead allocate a scratch register and do the expansion by hand. + +(define_insn_and_split "adddi3_internal" + [(set (match_operand:SI 0 "register_operand" "=r, r, r") + (plus:SI (match_operand:SI 2 "register_operand" "%0, 0, r") + (match_operand:SI 3 "nonmemory_operand" "ri,ri,ri"))) + (set (match_operand:SI 1 "register_operand" "=D, D, r") + (plus:SI + (plus:SI + (ltu:SI (plus:SI (match_dup 2) (match_dup 3)) (match_dup 2)) + (match_operand:SI 4 "register_operand" " 1, D, r")) + (match_operand:SI 5 "reg_or_am33_const_operand" " D, 1,ri"))) + (clobber (match_scratch:SI 6 "=X, X,&r")) + (clobber (reg:CC CC_REG))] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + rtx op0l = operands[0]; + rtx op0h = operands[1]; + rtx op1l = operands[2]; + rtx op2l = operands[3]; + rtx op1h = operands[4]; + rtx op2h = operands[5]; + rtx scratch = operands[6]; + rtx x; + + if (reg_overlap_mentioned_p (op0l, op1h)) + { + emit_move_insn (scratch, op0l); + op1h = scratch; + if (reg_overlap_mentioned_p (op0l, op2h)) + op2h = scratch; + } + else if (reg_overlap_mentioned_p (op0l, op2h)) + { + emit_move_insn (scratch, op0l); + op2h = scratch; + } + + if (rtx_equal_p (op0l, op1l)) + ; + else if (rtx_equal_p (op0l, op2l)) + x = op1l, op1l = op2l, op2l = x; + else + { + gcc_assert (TARGET_AM33); + if (!REG_P (op2l)) + { + emit_move_insn (op0l, op2l); + op2l = op1l; + op1l = op0l; + } + } + emit_insn (gen_addsi3_flags (op0l, op1l, op2l)); + + if (rtx_equal_p (op0h, op1h)) + ; + else if (rtx_equal_p (op0h, op2h)) + x = op1h, op1h = op2h, op2h = x; + else + { + gcc_assert (TARGET_AM33); + if (!REG_P (op2h)) + { + emit_move_insn (op0h, op2h); + op2h = op1h; + op1h = op0h; + } + } + emit_insn (gen_addc_internal (op0h, op1h, op2h)); + DONE; +} + [(set_attr "isa" "*,*,am33")] +) + +;; The following pattern is generated by combine when it proves that one +;; of the inputs to the low-part of the double-word add is zero, and thus +;; no carry is generated into the high-part. + +(define_insn_and_split "*adddi3_degenerate" + [(set (match_operand:SI 0 "register_operand" "=&r,&r") + (match_operand:SI 2 "nonmemory_operand" " 0, 0")) + (set (match_operand:SI 1 "register_operand" "=r , r") + (plus:SI (match_operand:SI 3 "register_operand" "%1 , r") + (match_operand:SI 4 "nonmemory_operand" "ri, r"))) + (clobber (reg:CC CC_REG))] + "" + "#" + "" + [(const_int 0)] +{ + rtx scratch = NULL_RTX; + if (!rtx_equal_p (operands[0], operands[2])) + { + gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[1])); + if (reg_overlap_mentioned_p (operands[0], operands[3]) + || reg_overlap_mentioned_p (operands[0], operands[4])) + { + scratch = gen_reg_rtx (SImode); + emit_move_insn (scratch, operands[2]); + } + else + emit_move_insn (operands[0], operands[2]); + } + emit_insn (gen_addsi3 (operands[1], operands[3], operands[4])); + if (scratch) + emit_move_insn (operands[0], scratch); + DONE; +}) + +;; ---------------------------------------------------------------------- +;; SUBTRACT INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "subsi3" + [(set (match_operand:SI 0 "register_operand" "=r,r,r,r") + (minus:SI (match_operand:SI 1 "register_operand" "0,0,0,r") + (match_operand:SI 2 "nonmemory_operand" "r,O,i,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + sub %2,%0 + sub %2,%0 + sub %2,%0 + sub %2,%1,%0" + [(set_attr "isa" "*,*,*,am33") + (set_attr "liw" "either,either,*,*") + (set_attr "liw_op" "sub") + (set_attr "timings" "11,11,11,22")] +) + +(define_insn "*subsi3_flags" + [(set (match_operand:SI 0 "register_operand" "=r, r") + (minus:SI (match_operand:SI 1 "register_operand" "0, r") + (match_operand:SI 2 "nonmemory_operand" "ri,r"))) + (set (reg CC_REG) + (compare (minus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "reload_completed && mn10300_match_ccmode (insn, CCZNCmode)" + "@ + sub %2,%0 + sub %2,%1,%0" + [(set_attr "isa" "*,am33") + (set_attr "timings" "11,22")] +) + +;; A helper to expand the above, with the CC_MODE filled in. +(define_expand "subsi3_flags" + [(parallel [(set (match_operand:SI 0 "register_operand") + (minus:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "nonmemory_operand"))) + (set (reg:CCZNC CC_REG) + (compare:CCZNC (minus:SI (match_dup 1) (match_dup 2)) + (const_int 0)))])] + "" +) + +(define_insn "subc_internal" + [(set (match_operand:SI 0 "register_operand" "=D,r,r") + (minus:SI + (minus:SI (match_operand:SI 1 "register_operand" " 0,0,r") + (match_operand:SI 2 "reg_or_am33_const_operand" " D,i,r")) + (geu:SI (reg:CC CC_REG) (const_int 0)))) + (clobber (reg:CC CC_REG))] + "reload_completed" + "@ + subc %2,%0 + subc %2,%0 + subc %2,%1,%0" + [(set_attr "isa" "*,am33,am33")] +) + +(define_expand "subdi3" + [(set (match_operand:DI 0 "register_operand" "") + (minus:DI (match_operand:DI 1 "register_operand" "") + (match_operand:DI 2 "nonmemory_operand" "")))] + "" +{ + rtx op0l, op0h, op1l, op1h, op2l, op2h; + + op0l = gen_lowpart (SImode, operands[0]); + op1l = gen_lowpart (SImode, operands[1]); + op2l = gen_lowpart (SImode, operands[2]); + op0h = gen_highpart (SImode, operands[0]); + op1h = gen_highpart (SImode, operands[1]); + op2h = gen_highpart_mode (SImode, DImode, operands[2]); + + if (!reg_or_am33_const_operand (op2h, SImode)) + op2h = force_reg (SImode, op2h); + + emit_insn (gen_subdi3_internal (op0l, op0h, op1l, op1h, op2l, op2h)); + DONE; +}) + +;; As with adddi3, the use of the scratch register helps reduce the +;; number of permutations for AM33. +;; ??? The early clobber on op0 avoids a reload bug wherein both output +;; registers are set the same. Consider negate, where both op2 and op3 +;; are 0, are csed to the same input register, and reload fails to undo +;; the cse when satisfying the matching constraints. + +(define_insn_and_split "subdi3_internal" + [(set (match_operand:SI 0 "register_operand" "=&r, r") + (minus:SI + (match_operand:SI 2 "register_operand" " 0, r") + (match_operand:SI 4 "nonmemory_operand" " ri,ri"))) + (set (match_operand:SI 1 "register_operand" "=D , r") + (minus:SI + (minus:SI + (match_operand:SI 3 "register_operand" " 1, r") + (match_operand:SI 5 "reg_or_am33_const_operand" " D,ri")) + (ltu:SI (match_dup 2) (match_dup 4)))) + (clobber (match_scratch:SI 6 "=X ,&r")) + (clobber (reg:CC CC_REG))] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + rtx op0l = operands[0]; + rtx op0h = operands[1]; + rtx op1l = operands[2]; + rtx op1h = operands[3]; + rtx op2l = operands[4]; + rtx op2h = operands[5]; + rtx scratch = operands[6]; + + if (reg_overlap_mentioned_p (op0l, op1h)) + { + emit_move_insn (scratch, op0l); + op1h = scratch; + if (reg_overlap_mentioned_p (op0l, op2h)) + op2h = scratch; + } + else if (reg_overlap_mentioned_p (op0l, op2h)) + { + emit_move_insn (scratch, op0l); + op2h = scratch; + } + + if (!rtx_equal_p (op0l, op1l)) + { + gcc_assert (TARGET_AM33); + if (!REG_P (op2l)) + { + emit_move_insn (op0l, op1l); + op1l = op0l; + } + } + emit_insn (gen_subsi3_flags (op0l, op1l, op2l)); + + if (!rtx_equal_p (op0h, op1h)) + { + gcc_assert (TARGET_AM33); + if (!REG_P (op2h)) + { + emit_move_insn (op0h, op1h); + op1h = op0h; + } + } + emit_insn (gen_subc_internal (op0h, op1h, op2h)); + DONE; +} + [(set_attr "isa" "*,am33")] +) + +;; The following pattern is generated by combine when it proves that one +;; of the inputs to the low-part of the double-word sub is zero, and thus +;; no carry is generated into the high-part. + +(define_insn_and_split "*subdi3_degenerate" + [(set (match_operand:SI 0 "register_operand" "=&r,&r") + (match_operand:SI 2 "nonmemory_operand" " 0, 0")) + (set (match_operand:SI 1 "register_operand" "=r , r") + (minus:SI (match_operand:SI 3 "register_operand" " 1, r") + (match_operand:SI 4 "nonmemory_operand" " ri, r"))) + (clobber (reg:CC CC_REG))] + "" + "#" + "" + [(const_int 0)] +{ + rtx scratch = NULL_RTX; + if (!rtx_equal_p (operands[0], operands[2])) + { + gcc_assert (!reg_overlap_mentioned_p (operands[0], operands[1])); + if (reg_overlap_mentioned_p (operands[0], operands[3]) + || reg_overlap_mentioned_p (operands[0], operands[4])) + { + scratch = gen_reg_rtx (SImode); + emit_move_insn (scratch, operands[2]); + } + else + emit_move_insn (operands[0], operands[2]); + } + emit_insn (gen_subsi3 (operands[1], operands[3], operands[4])); + if (scratch) + emit_move_insn (operands[0], scratch); + DONE; +}) + +(define_insn_and_split "negsi2" + [(set (match_operand:SI 0 "register_operand" "=D,&r") + (neg:SI (match_operand:SI 1 "register_operand" " 0, r"))) + (clobber (reg:CC CC_REG))] + "" + "#" + "&& reload_completed" + [(const_int 0)] +{ + /* Recall that twos-compliment is ones-compliment plus one. When + allocated in DATA_REGS this is 2+1 bytes; otherwise (for am33) + this is 3+3 bytes. + + For AM33, it would have been possible to load zero and use the + three-address subtract to have a total size of 3+4*N bytes for + multiple negations, plus increased throughput. Not attempted here. */ + + if (true_regnum (operands[0]) == true_regnum (operands[1])) + { + emit_insn (gen_one_cmplsi2 (operands[0], operands[0])); + emit_insn (gen_addsi3 (operands[0], operands[0], const1_rtx)); + } + else + { + emit_move_insn (operands[0], const0_rtx); + emit_insn (gen_subsi3 (operands[0], operands[0], operands[1])); + } + DONE; +}) + +;; ---------------------------------------------------------------------- +;; MULTIPLY INSTRUCTIONS +;; ---------------------------------------------------------------------- + +;; ??? Note that AM33 has a third multiply variant that puts the high part +;; into the MDRQ register, however this variant also constrains the inputs +;; to be in DATA_REGS and thus isn't as helpful as it might be considering +;; the existance of the 4-operand multiply. Nor is there a set of divide +;; insns that use MDRQ. Given that there is an IMM->MDRQ insn, this would +;; have been very handy for starting udivmodsi4... + +(define_expand "mulsidi3" + [(set (match_operand:DI 0 "register_operand" "") + (mult:DI (sign_extend:DI (match_operand:SI 1 "register_operand" "")) + (sign_extend:DI (match_operand:SI 2 "register_operand" ""))))] + "" +{ + emit_insn (gen_mulsidi3_internal (gen_lowpart (SImode, operands[0]), + gen_highpart (SImode, operands[0]), + operands[1], operands[2])); + DONE; +}) + +(define_insn "mulsidi3_internal" + [(set (match_operand:SI 0 "register_operand" "=D,r") + (mult:SI (match_operand:SI 2 "register_operand" "%0,r") + (match_operand:SI 3 "register_operand" " D,r"))) + (set (match_operand:SI 1 "register_operand" "=z,r") + (truncate:SI + (ashiftrt:DI + (mult:DI (sign_extend:DI (match_dup 2)) + (sign_extend:DI (match_dup 3))) + (const_int 32)))) + (clobber (reg:CC CC_REG))] + "" +{ + if (which_alternative == 1) + return "mul %2,%3,%1,%0"; + else if (TARGET_MULT_BUG) + return "nop\;nop\;mul %3,%0"; + else + return "mul %3,%0"; +} + [(set_attr "isa" "*,am33") + (set (attr "timings") + (if_then_else (eq_attr "cpu" "am34") (const_int 24) (const_int 23)))] +) + +(define_expand "umulsidi3" + [(set (match_operand:DI 0 "register_operand" "") + (mult:DI (zero_extend:DI (match_operand:SI 1 "register_operand" "")) + (zero_extend:DI (match_operand:SI 2 "register_operand" "")))) + (clobber (reg:CC CC_REG))] + "" +{ + emit_insn (gen_umulsidi3_internal (gen_lowpart (SImode, operands[0]), + gen_highpart (SImode, operands[0]), + operands[1], operands[2])); + DONE; +}) + +(define_insn "umulsidi3_internal" + [(set (match_operand:SI 0 "register_operand" "=D,r") + (mult:SI (match_operand:SI 2 "register_operand" "%0,r") + (match_operand:SI 3 "register_operand" " D,r"))) + (set (match_operand:SI 1 "register_operand" "=z,r") + (truncate:SI + (lshiftrt:DI + (mult:DI (zero_extend:DI (match_dup 2)) + (zero_extend:DI (match_dup 3))) + (const_int 32)))) + (clobber (reg:CC CC_REG))] + "" +{ + if (which_alternative == 1) + return "mulu %2,%3,%1,%0"; + else if (TARGET_MULT_BUG) + return "nop\;nop\;mulu %3,%0"; + else + return "mulu %3,%0"; +} + [(set_attr "isa" "*,am33") + (set (attr "timings") + (if_then_else (eq_attr "cpu" "am34") (const_int 24) (const_int 23)))] +) + +(define_expand "mulsi3" + [(parallel [(set (match_operand:SI 0 "register_operand") + (mult:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "reg_or_am33_const_operand"))) + (clobber (match_scratch:SI 3)) + (clobber (reg:CC CC_REG))])] + "" +) + +(define_insn "*mulsi3" + [(set (match_operand:SI 0 "register_operand" "=D, r,r") + (mult:SI (match_operand:SI 2 "register_operand" "%0, 0,r") + (match_operand:SI 3 "reg_or_am33_const_operand" " D,ri,r"))) + (clobber (match_scratch:SI 1 "=z, z,r")) + (clobber (reg:CC CC_REG))] + "" +{ + if (which_alternative == 2) + return "mul %2,%3,%1,%0"; + else if (TARGET_MULT_BUG) + return "nop\;nop\;mul %3,%0"; + else + return "mul %3,%0"; +} + [(set_attr "isa" "*,am33,am33") + (set (attr "timings") + (if_then_else (eq_attr "cpu" "am34") (const_int 24) (const_int 23)))] +) + +(define_expand "udivmodsi4" + [(parallel [(set (match_operand:SI 0 "register_operand") + (udiv:SI (match_operand:SI 1 "register_operand") + (match_operand:SI 2 "register_operand"))) + (set (match_operand:SI 3 "register_operand") + (umod:SI (match_dup 1) (match_dup 2))) + (use (const_int 0)) + (clobber (reg:CC CC_REG))])] + "" +) + +;; Note the trick to get reload to put the zero into the MDR register, +;; rather than exposing the load early and letting CSE or someone try +;; to share the zeros between division insns. Which tends to result +;; in sequences like 0->r0->d0->mdr. + +(define_insn "*udivmodsi4" + [(set (match_operand:SI 0 "register_operand" "=D") + (udiv:SI (match_operand:SI 2 "register_operand" " 0") + (match_operand:SI 3 "register_operand" " D"))) + (set (match_operand:SI 1 "register_operand" "=z") + (umod:SI (match_dup 2) (match_dup 3))) + (use (match_operand:SI 4 "nonmemory_operand" " 1")) + (clobber (reg:CC CC_REG))] + "" + "divu %3,%0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 3839) (const_int 4243)))] +) + +(define_expand "divmodsi4" + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (div:SI (match_operand:SI 1 "register_operand" "") + (match_operand:SI 2 "register_operand" ""))) + (set (match_operand:SI 3 "register_operand" "") + (mod:SI (match_dup 1) (match_dup 2))) + (use (match_dup 4)) + (clobber (reg:CC CC_REG))])] + "" +{ + operands[4] = gen_reg_rtx (SImode); + emit_insn (gen_ext_internal (operands[4], operands[1])); +}) + +;; ??? Ideally we'd represent this via shift, but it seems like adding a +;; special-case pattern for (ashiftrt x 31) is just as likely to result +;; in poor register allocation choices. +(define_insn "ext_internal" + [(set (match_operand:SI 0 "register_operand" "=z") + (unspec:SI [(match_operand:SI 1 "register_operand" "D")] UNSPEC_EXT))] + "" + "ext %1" +) + +(define_insn "*divmodsi4" + [(set (match_operand:SI 0 "register_operand" "=D") + (div:SI (match_operand:SI 2 "register_operand" " 0") + (match_operand:SI 3 "register_operand" " D"))) + (set (match_operand:SI 1 "register_operand" "=z") + (mod:SI (match_dup 2) (match_dup 3))) + (use (match_operand:SI 4 "register_operand" " 1")) + (clobber (reg:CC CC_REG))] + "" + "div %3,%0"; + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 3839) (const_int 4243)))] +) + + +;; ---------------------------------------------------------------------- +;; AND INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "andsi3" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (and:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" " i,D,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + and %2,%0 + and %2,%0 + and %2,%1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr "liw" "*,op1,*") + (set_attr "liw_op" "and") + (set_attr "timings" "22,11,11")] +) + +(define_insn "*andsi3_flags" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (and:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" " i,D,r"))) + (set (reg CC_REG) + (compare (and:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "reload_completed && mn10300_match_ccmode (insn, CCZNmode)" + "@ + and %2,%0 + and %2,%0 + and %2,%1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr "timings" "22,11,11")] +) + +;; Make sure we generate extensions instead of ANDs. + +(define_split + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_operand:SI 1 "register_operand" "") + (const_int 255))) + (clobber (reg:CC CC_REG))])] + "" + [(set (match_dup 0) (zero_extend:SI (match_dup 1)))] + { operands[1] = gen_lowpart (QImode, operands[1]); } +) + +(define_split + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_operand:SI 1 "register_operand" "") + (const_int 65535))) + (clobber (reg:CC CC_REG))])] + "" + [(set (match_dup 0) (zero_extend:SI (match_dup 1)))] + { operands[1] = gen_lowpart (HImode, operands[1]); } +) + +;; Split AND by an appropriate constant into two shifts. Recall that +;; operations with a full 32-bit immediate require an extra cycle, so +;; this is a size optimization with no speed penalty. This only applies +;; do DATA_REGS; the shift insns that AM33 adds are too large for a win. + +(define_split + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (and:SI (match_dup 0) + (match_operand:SI 1 "const_int_operand" ""))) + (clobber (reg:CC CC_REG))])] + "reload_completed + && REGNO_DATA_P (true_regnum (operands[0]), 1) + && mn10300_split_and_operand_count (operands[1]) != 0" + [(const_int 0)] +{ + int count = mn10300_split_and_operand_count (operands[1]); + if (count > 0) + { + emit_insn (gen_lshrsi3 (operands[0], operands[0], GEN_INT (count))); + emit_insn (gen_ashlsi3 (operands[0], operands[0], GEN_INT (count))); + } + else + { + emit_insn (gen_ashlsi3 (operands[0], operands[0], GEN_INT (-count))); + emit_insn (gen_lshrsi3 (operands[0], operands[0], GEN_INT (-count))); + } + DONE; +}) + +;; ---------------------------------------------------------------------- +;; OR INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "iorsi3" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" " i,D,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + or %2,%0 + or %2,%0 + or %2,%1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr "liw" "*,op1,*") + (set_attr "liw_op" "or") + (set_attr "timings" "22,11,11")] +) + +(define_insn "*iorsi3_flags" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (ior:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" " i,D,r"))) + (set (reg CC_REG) + (compare (ior:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "reload_completed && mn10300_match_ccmode (insn, CCZNmode)" + "@ + or %2,%0 + or %2,%0 + or %2,%1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr "timings" "22,11,11")] +) + +;; ---------------------------------------------------------------------- +;; XOR INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "xorsi3" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" " i,D,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + xor %2,%0 + xor %2,%0 + xor %2,%1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr "liw" "*,op1,*") + (set_attr "liw_op" "xor") + (set_attr "timings" "22,11,11")] +) + +(define_insn "*xorsi3_flags" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (xor:SI (match_operand:SI 1 "register_operand" "%0,0,r") + (match_operand:SI 2 "nonmemory_operand" " i,D,r"))) + (set (reg CC_REG) + (compare (xor:SI (match_dup 1) (match_dup 2)) + (const_int 0)))] + "reload_completed && mn10300_match_ccmode (insn, CCZNmode)" + "@ + xor %2,%0 + xor %2,%0 + xor %2,%1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr "timings" "22,11,11")] +) + +;; ---------------------------------------------------------------------- +;; NOT INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "one_cmplsi2" + [(set (match_operand:SI 0 "register_operand" "=D") + (not:SI (match_operand:SI 1 "register_operand" " 0"))) + (clobber (reg:CC CC_REG))] + "" + "not %0" +) + +(define_insn "*one_cmplsi2_flags" + [(set (match_operand:SI 0 "register_operand" "=D") + (not:SI (match_operand:SI 1 "register_operand" " 0"))) + (set (reg CC_REG) + (compare (not:SI (match_dup 1)) + (const_int 0)))] + "reload_completed && mn10300_match_ccmode (insn, CCZNmode)" + "not %0" +) + +;; ---------------------------------------------------------------------- +;; COMPARE AND BRANCH INSTRUCTIONS +;; ---------------------------------------------------------------------- + +;; We expand the comparison into a single insn so that it will not be split +;; up by reload. +(define_expand "cbranchsi4" + [(set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" + [(match_operand:SI 1 "register_operand") + (match_operand:SI 2 "nonmemory_operand")]) + (label_ref (match_operand 3 "")) + (pc)))] + "" + "" +) + +(define_insn_and_split "*cbranchsi4_cmp" + [(set (pc) + (if_then_else (match_operator 3 "ordered_comparison_operator" + [(match_operand:SI 0 "register_operand" "r") + (match_operand:SI 1 "nonmemory_operand" "ri")]) + (match_operand 2 "label_ref_operand" "") + (pc)))] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + mn10300_split_cbranch (CCmode, operands[3], operands[2]); + DONE; +}) + +(define_insn "*cmpsi" + [(set (reg CC_REG) + (compare (match_operand:SI 0 "register_operand" "r,r,r") + (match_operand:SI 1 "nonmemory_operand" "r,O,i")))] + "reload_completed" +{ + /* The operands of CMP must be distinct registers. In the case where + we've failed to optimize the comparison of a register to itself, we + must use another method to set the Z flag. We can achieve this + effect with a BTST 0,D0. This will not alter the contents of D0; + the use of d0 is arbitrary; any data register would work. */ + if (rtx_equal_p (operands[0], operands[1])) + return "btst 0,d0"; + else + return "cmp %1,%0"; +} + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") (const_int 11) (const_int 22)) + (if_then_else (eq_attr "cpu" "am34") (const_int 11) (const_int 22)) + (if_then_else (eq_attr "cpu" "am34") (const_int 11) (const_int 22))]) + (set_attr "liw" "either,either,*") + (set_attr "liw_op" "cmp")] +) + +(define_insn "*integer_conditional_branch" + [(set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(match_operand 2 "int_mode_flags" "") + (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "reload_completed" + "b%b0 %1" +) + +(define_insn_and_split "*cbranchsi4_btst" + [(set (pc) + (if_then_else + (match_operator 3 "CCZN_comparison_operator" + [(and:SI (match_operand:SI 0 "register_operand" "D") + (match_operand:SI 1 "immediate_operand" "i")) + (const_int 0)]) + (match_operand 2 "label_ref_operand" "") + (pc)))] + "" + "#" + "reload_completed" + [(const_int 0)] +{ + mn10300_split_cbranch (CCZNmode, operands[3], operands[2]); + DONE; +}) + +(define_insn "*btstsi" + [(set (reg:CCZN CC_REG) + (compare:CCZN + (and:SI (match_operand:SI 0 "register_operand" "D") + (match_operand:SI 1 "immediate_operand" "i")) + (const_int 0)))] + "reload_completed" + "btst %1,%0" +) + +(define_expand "cbranchsf4" + [(set (pc) + (if_then_else + (match_operator 0 "ordered_comparison_operator" + [(match_operand:SF 1 "register_operand") + (match_operand:SF 2 "nonmemory_operand")]) + (label_ref (match_operand 3 "")) + (pc)))] + "TARGET_AM33_2" + "" +) + +(define_insn_and_split "*cbranchsf4_cmp" + [(set (pc) + (if_then_else (match_operator 3 "ordered_comparison_operator" + [(match_operand:SF 0 "register_operand" "f") + (match_operand:SF 1 "nonmemory_operand" "fF")]) + (match_operand 2 "label_ref_operand" "") + (pc))) + ] + "TARGET_AM33_2" + "#" + "&& reload_completed" + [(const_int 0)] +{ + mn10300_split_cbranch (CC_FLOATmode, operands[3], operands[2]); + DONE; +}) + +(define_insn "*am33_cmpsf" + [(set (reg:CC_FLOAT CC_REG) + (compare:CC_FLOAT (match_operand:SF 0 "register_operand" "f") + (match_operand:SF 1 "nonmemory_operand" "fF")))] + "TARGET_AM33_2 && reload_completed" + "fcmp %1, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 25)))] +) + +(define_insn "*float_conditional_branch" + [(set (pc) + (if_then_else (match_operator 0 "comparison_operator" + [(reg:CC_FLOAT CC_REG) (const_int 0)]) + (label_ref (match_operand 1 "" "")) + (pc)))] + "TARGET_AM33_2 && reload_completed" + "fb%b0 %1" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 44) (const_int 33)))] +) + +;; Unconditional and other jump instructions. + +(define_insn "jump" + [(set (pc) + (label_ref (match_operand 0 "" "")))] + "" + "jmp %l0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 44)))] +) + +(define_insn "indirect_jump" + [(set (pc) (match_operand:SI 0 "register_operand" "a"))] + "" + "jmp (%0)" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 33)))] +) + +(define_expand "builtin_setjmp_receiver" + [(match_operand 0 "" "")] + "flag_pic" +{ + emit_insn (gen_load_pic ()); + DONE; +}) + +(define_expand "casesi" + [(match_operand:SI 0 "register_operand") + (match_operand:SI 1 "immediate_operand") + (match_operand:SI 2 "immediate_operand") + (match_operand 3 "" "") (match_operand 4 "")] + "" +{ + rtx table = gen_reg_rtx (SImode); + rtx index = gen_reg_rtx (SImode); + rtx addr = gen_reg_rtx (Pmode); + rtx test; + + emit_move_insn (table, gen_rtx_LABEL_REF (VOIDmode, operands[3])); + emit_insn (gen_addsi3 (index, operands[0], GEN_INT (- INTVAL (operands[1])))); + test = gen_rtx_fmt_ee (GTU, VOIDmode, index, operands[2]); + emit_jump_insn (gen_cbranchsi4 (test, index, operands[2], operands[4])); + + emit_insn (gen_ashlsi3 (index, index, const2_rtx)); + emit_move_insn (addr, gen_rtx_MEM (SImode, + gen_rtx_PLUS (SImode, table, index))); + if (flag_pic) + emit_insn (gen_addsi3 (addr, addr, table)); + + emit_jump_insn (gen_tablejump (addr, operands[3])); + DONE; +}) + +(define_insn "tablejump" + [(set (pc) (match_operand:SI 0 "register_operand" "a")) + (use (label_ref (match_operand 1 "" "")))] + "" + "jmp (%0)" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 11) (const_int 33)))] +) + +;; Call subroutine with no return value. + +(define_expand "call" + [(call (match_operand:QI 0 "general_operand") + (match_operand:SI 1 "general_operand"))] + "" +{ + rtx fn = XEXP (operands[0], 0); + + if (flag_pic && GET_CODE (fn) == SYMBOL_REF) + { + if (MN10300_GLOBAL_P (fn)) + { + /* The PLT code won't run on AM30, but then, there's no + shared library support for AM30 either, so we just assume + the linker is going to adjust all @PLT relocs to the + actual symbols. */ + emit_use (pic_offset_table_rtx); + fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PLT); + } + else + fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PIC); + } + if (! call_address_operand (fn, VOIDmode)) + fn = force_reg (SImode, fn); + + XEXP (operands[0], 0) = fn; +}) + +(define_insn "*call_internal" + [(call (mem:QI (match_operand:SI 0 "call_address_operand" "a,S")) + (match_operand:SI 1 "" ""))] + "" + "@ + calls %C0 + call %C0,[],0" + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") + (const_int 33) (const_int 44)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 55) (const_int 33)) + ]) + ] +) + +;; Call subroutine, returning value in operand 0 +;; (which must be a hard register). + +(define_expand "call_value" + [(set (match_operand 0 "") + (call (match_operand:QI 1 "general_operand") + (match_operand:SI 2 "general_operand")))] + "" +{ + rtx fn = XEXP (operands[1], 0); + + if (flag_pic && GET_CODE (fn) == SYMBOL_REF) + { + if (MN10300_GLOBAL_P (fn)) + { + /* The PLT code won't run on AM30, but then, there's no + shared library support for AM30 either, so we just assume + the linker is going to adjust all @PLT relocs to the + actual symbols. */ + emit_use (pic_offset_table_rtx); + fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PLT); + } + else + fn = gen_rtx_UNSPEC (SImode, gen_rtvec (1, fn), UNSPEC_PIC); + } + if (! call_address_operand (fn, VOIDmode)) + fn = force_reg (SImode, fn); + + XEXP (operands[1], 0) = fn; +}) + +(define_insn "call_value_internal" + [(set (match_operand 0 "" "") + (call (mem:QI (match_operand:SI 1 "call_address_operand" "a,S")) + (match_operand:SI 2 "" "")))] + "" + "@ + calls %C1 + call %C1,[],0" + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") + (const_int 33) (const_int 44)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 55) (const_int 33)) + ]) + ] +) + +(define_expand "untyped_call" + [(parallel [(call (match_operand 0 "") + (const_int 0)) + (match_operand 1 "") + (match_operand 2 "")])] + "" +{ + int i; + + emit_call_insn (gen_call (operands[0], const0_rtx)); + + for (i = 0; i < XVECLEN (operands[2], 0); i++) + { + rtx set = XVECEXP (operands[2], 0, i); + emit_move_insn (SET_DEST (set), SET_SRC (set)); + } + DONE; +}) + +(define_insn "nop" + [(const_int 0)] + "" + "nop" +) + +;; ---------------------------------------------------------------------- +;; EXTEND INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "zero_extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (zero_extend:SI + (match_operand:QI 1 "nonimmediate_operand" " 0,m,r")))] + "" + "@ + extbu %0 + movbu %1,%0 + extbu %1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr_alternative "timings" + [(const_int 11) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (const_int 11) + ])] +) + +(define_insn "zero_extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=D,D,r") + (zero_extend:SI + (match_operand:HI 1 "nonimmediate_operand" " 0,m,r")))] + "" + "@ + exthu %0 + movhu %1,%0 + exthu %1,%0" + [(set_attr "isa" "*,*,am33") + (set_attr_alternative "timings" + [(const_int 11) + (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 24)) + (const_int 11)])] +) + +(define_insn "extendqisi2" + [(set (match_operand:SI 0 "register_operand" "=D,r") + (sign_extend:SI + (match_operand:QI 1 "register_operand" "0,r")))] + "" + "@ + extb %0 + extb %1,%0" + [(set_attr "isa" "*,am33")] +) + +(define_insn "extendhisi2" + [(set (match_operand:SI 0 "register_operand" "=D,r") + (sign_extend:SI + (match_operand:HI 1 "register_operand" "0,r")))] + "" + "@ + exth %0 + exth %1,%0" + [(set_attr "isa" "*,am33")] +) + +;; ---------------------------------------------------------------------- +;; SHIFTS +;; ---------------------------------------------------------------------- + +(define_insn "ashlsi3" + [(set (match_operand:SI 0 "register_operand" "=r,D,d,d,D,D,D,r") + (ashift:SI + (match_operand:SI 1 "register_operand" " 0,0,0,0,0,0,0,r") + (match_operand:QI 2 "nonmemory_operand" " J,K,M,L,D,O,i,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + add %0,%0 + asl2 %0 + asl2 %0\;add %0,%0 + asl2 %0\;asl2 %0 + asl %S2,%0 + asl %S2,%0 + asl %S2,%0 + asl %2,%1,%0" + [(set_attr "isa" "*,*,*,*,*,*,*,am33") + (set_attr "liw" "op2,op2,op2,op2,op2,op2,*,*") + (set_attr "liw_op" "asl") + (set_attr "timings" "11,11,22,22,11,11,11,11")] +) + +(define_insn "lshrsi3" + [(set (match_operand:SI 0 "register_operand" "=D,D,D,r") + (lshiftrt:SI + (match_operand:SI 1 "register_operand" "0,0,0,r") + (match_operand:QI 2 "nonmemory_operand" "D,O,i,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + lsr %S2,%0 + lsr %S2,%0 + lsr %S2,%0 + lsr %2,%1,%0" + [(set_attr "isa" "*,*,*,am33") + (set_attr "liw" "op2,op2,*,*") + (set_attr "liw_op" "lsr")] +) + +(define_insn "ashrsi3" + [(set (match_operand:SI 0 "register_operand" "=D,D,D,r") + (ashiftrt:SI + (match_operand:SI 1 "register_operand" "0,0,0,r") + (match_operand:QI 2 "nonmemory_operand" "D,O,i,r"))) + (clobber (reg:CC CC_REG))] + "" + "@ + asr %S2,%0 + asr %S2,%0 + asr %S2,%0 + asr %2,%1,%0" + [(set_attr "isa" "*,*,*,am33") + (set_attr "liw" "op2,op2,*,*") + (set_attr "liw_op" "asr")] +) + +;; ---------------------------------------------------------------------- +;; MISCELANEOUS +;; ---------------------------------------------------------------------- + +(define_expand "clzsi2" + [(parallel [(set (match_operand:SI 0 "register_operand" "") + (unspec:SI [(match_operand:SI 1 "register_operand" "") + (const_int 0)] UNSPEC_BSCH)) + (clobber (reg:CC CC_REG))])] + "TARGET_AM33" +) + +(define_insn "*bsch" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_operand:SI 1 "register_operand" "r") + (match_operand:SI 2 "nonmemory_operand" "0")] + UNSPEC_BSCH)) + (clobber (reg:CC CC_REG))] + "TARGET_AM33" + "bsch %1,%0" +) + +;; ---------------------------------------------------------------------- +;; FP INSTRUCTIONS +;; ---------------------------------------------------------------------- + +(define_insn "abssf2" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (abs:SF (match_operand:SF 1 "register_operand" "0,?f")))] + "TARGET_AM33_2" + "@ + fabs %0 + fabs %1, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 14)))] +) + +(define_insn "negsf2" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (neg:SF (match_operand:SF 1 "register_operand" "0,?f")))] + "TARGET_AM33_2" + "@ + fneg %0 + fneg %1, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 14)))] +) + +(define_expand "sqrtsf2" + [(set (match_operand:SF 0 "register_operand" "") + (sqrt:SF (match_operand:SF 1 "register_operand" "")))] + "TARGET_AM33_2 && flag_unsafe_math_optimizations" +{ + rtx scratch = gen_reg_rtx (SFmode); + emit_insn (gen_rsqrtsf2 (scratch, operands[1], CONST1_RTX (SFmode))); + emit_insn (gen_divsf3 (operands[0], force_reg (SFmode, CONST1_RTX (SFmode)), + scratch)); + DONE; +}) + +(define_insn "rsqrtsf2" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (div:SF (match_operand:SF 2 "const_1f_operand" "F,F") + (sqrt:SF (match_operand:SF 1 "register_operand" "0,?f")))) + (clobber (reg:CC_FLOAT CC_REG))] + "TARGET_AM33_2" + "@ + frsqrt %0 + frsqrt %1, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 4753) (const_int 2327)))] +) + +(define_insn "addsf3" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (plus:SF (match_operand:SF 1 "register_operand" "%0,f") + (match_operand:SF 2 "nonmemory_operand" "f,?fF"))) + (clobber (reg:CC_FLOAT CC_REG))] + "TARGET_AM33_2" + "@ + fadd %2, %0 + fadd %2, %1, %0" + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 14)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 25)) + ])] +) + +(define_insn "subsf3" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (minus:SF (match_operand:SF 1 "register_operand" "0,f") + (match_operand:SF 2 "nonmemory_operand" "f,?fF"))) + (clobber (reg:CC_FLOAT CC_REG))] + "TARGET_AM33_2" + "@ + fsub %2, %0 + fsub %2, %1, %0" + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 14)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 25)) + ])] +) + +(define_insn "mulsf3" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (mult:SF (match_operand:SF 1 "register_operand" "%0,f") + (match_operand:SF 2 "nonmemory_operand" "f,?fF"))) + (clobber (reg:CC_FLOAT CC_REG)) + ] + "TARGET_AM33_2" + "@ + fmul %2, %0 + fmul %2, %1, %0" + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 14)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 25)) + ])] +) + +(define_insn "divsf3" + [(set (match_operand:SF 0 "register_operand" "=f,f") + (div:SF (match_operand:SF 1 "register_operand" "0,f") + (match_operand:SF 2 "nonmemory_operand" "f,?fF"))) + (clobber (reg:CC_FLOAT CC_REG))] + "TARGET_AM33_2" + "@ + fdiv %2, %0 + fdiv %2, %1, %0" + [(set_attr_alternative "timings" + [(if_then_else (eq_attr "cpu" "am34") + (const_int 2531) (const_int 1216)) + (if_then_else (eq_attr "cpu" "am34") + (const_int 2531) (const_int 1317)) + ])] +) + +(define_insn "fmasf4" + [(set (match_operand:SF 0 "register_operand" "=c") + (fma:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f") + (match_operand:SF 3 "register_operand" "f"))) + (clobber (reg:CC_FLOAT CC_REG)) + ] + "TARGET_AM33_2" + "fmadd %1, %2, %3, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 24)))] +) + +(define_insn "fmssf4" + [(set (match_operand:SF 0 "register_operand" "=c") + (fma:SF (match_operand:SF 1 "register_operand" "f") + (match_operand:SF 2 "register_operand" "f") + (neg:SF (match_operand:SF 3 "register_operand" "f")))) + (clobber (reg:CC_FLOAT CC_REG)) + ] + "TARGET_AM33_2" + "fmsub %1, %2, %3, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 24)))] +) + +(define_insn "fnmasf4" + [(set (match_operand:SF 0 "register_operand" "=c") + (fma:SF (neg:SF (match_operand:SF 1 "register_operand" "f")) + (match_operand:SF 2 "register_operand" "f") + (match_operand:SF 3 "register_operand" "f"))) + (clobber (reg:CC_FLOAT CC_REG)) + ] + "TARGET_AM33_2" + "fnmadd %1, %2, %3, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 24)))] +) + +(define_insn "fnmssf4" + [(set (match_operand:SF 0 "register_operand" "=c") + (fma:SF (neg:SF (match_operand:SF 1 "register_operand" "f")) + (match_operand:SF 2 "register_operand" "f") + (neg:SF (match_operand:SF 3 "register_operand" "f")))) + (clobber (reg:CC_FLOAT CC_REG)) + ] + "TARGET_AM33_2" + "fnmsub %1, %2, %3, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 17) (const_int 24)))] +) + +;; ---------------------------------------------------------------------- +;; PROLOGUE/EPILOGUE +;; ---------------------------------------------------------------------- +(define_expand "prologue" + [(const_int 0)] + "" + { mn10300_expand_prologue (); DONE; } +) + +(define_expand "epilogue" + [(return)] + "" + { mn10300_expand_epilogue (); DONE; } +) + +(define_insn "return" + [(return)] + "mn10300_can_use_rets_insn ()" +{ + /* The RETF insn is 4 cycles faster than RETS, though 1 byte larger. */ + if (optimize_insn_for_speed_p () && mn10300_can_use_retf_insn ()) + return "retf [],0"; + else + return "rets"; +}) + +(define_insn "return_ret" + [(return) + (use (match_operand:SI 0 "const_int_operand" ""))] + "" +{ + /* The RETF insn is up to 3 cycles faster than RET. */ + fputs ((mn10300_can_use_retf_insn () ? "\tretf " : "\tret "), asm_out_file); + mn10300_print_reg_list (asm_out_file, mn10300_get_live_callee_saved_regs ()); + fprintf (asm_out_file, ",%d\n", (int) INTVAL (operands[0])); + return ""; +}) + +;; This instruction matches one generated by mn10300_gen_multiple_store() +(define_insn "store_movm" + [(match_parallel 0 "mn10300_store_multiple_operation" + [(set (reg:SI SP_REG) (plus:SI (reg:SI SP_REG) (match_operand 1 "" "")))])] + "" +{ + fputs ("\tmovm ", asm_out_file); + mn10300_print_reg_list (asm_out_file, + mn10300_store_multiple_operation (operands[0], + VOIDmode)); + fprintf (asm_out_file, ",(sp)\n"); + return ""; +} + ;; Assume that no more than 8 registers will be pushed. + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 99) (const_int 88)))] +) + +(define_expand "load_pic" + [(const_int 0)] + "flag_pic" +{ + if (TARGET_AM33) + emit_insn (gen_am33_load_pic (pic_offset_table_rtx)); + else if (mn10300_frame_size () == 0) + emit_insn (gen_mn10300_load_pic0 (pic_offset_table_rtx)); + else + emit_insn (gen_mn10300_load_pic1 (pic_offset_table_rtx)); + DONE; +}) + +(define_insn "am33_load_pic" + [(set (match_operand:SI 0 "register_operand" "=a") + (unspec:SI [(const_int 0)] UNSPEC_GOT)) + (clobber (reg:CC CC_REG))] + "TARGET_AM33" +{ + operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME); + return ".LPIC%=:\;mov pc,%0\;add %1-(.LPIC%=-.),%0"; +} + [(set_attr "timings" "33")] +) + +;; Load pic register with push/pop of stack. +(define_insn "mn10300_load_pic0" + [(set (match_operand:SI 0 "register_operand" "=a") + (unspec:SI [(const_int 0)] UNSPEC_GOT)) + (clobber (reg:SI MDR_REG)) + (clobber (reg:CC CC_REG))] + "" +{ + operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME); + return ("add -4,sp\;" + "calls .LPIC%=\n" + ".LPIC%=:\;" + "movm (sp),[%0]\;" + "add %1-(.LPIC%=-.),%0"); +} + [(set_attr "timings" "88")] +) + +;; Load pic register re-using existing stack space. +(define_insn "mn10300_load_pic1" + [(set (match_operand:SI 0 "register_operand" "=a") + (unspec:SI [(const_int 0)] UNSPEC_GOT)) + (clobber (mem:SI (reg:SI SP_REG))) + (clobber (reg:SI MDR_REG)) + (clobber (reg:CC CC_REG))] + "" +{ + operands[1] = gen_rtx_SYMBOL_REF (VOIDmode, GOT_SYMBOL_NAME); + return ("calls .LPIC%=\n" + ".LPIC%=:\;" + "mov (sp),%0\;" + "add %1-(.LPIC%=-.),%0"); +} + [(set_attr "timings" "66")] +) + +;; The mode on operand 3 has been deliberately omitted because it +;; can be either SI (for arithmetic operations) or QI (for shifts). +(define_insn "liw" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_dup 0) + (match_operand 2 "liw_operand" "rO") + (match_operand:SI 4 "const_int_operand" "")] + UNSPEC_LIW)) + (set (match_operand:SI 1 "register_operand" "=r") + (unspec:SI [(match_dup 1) + (match_operand 3 "liw_operand" "rO") + (match_operand:SI 5 "const_int_operand" "")] + UNSPEC_LIW))] + "TARGET_ALLOW_LIW" + "%W4_%W5 %2, %0, %3, %1" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 12)))] +) + +;; The mode on operand 1 has been deliberately omitted because it +;; can be either SI (for arithmetic operations) or QI (for shifts). +(define_insn "cmp_liw" + [(set (reg:CC CC_REG) + (compare:CC (match_operand:SI 2 "register_operand" "r") + (match_operand 3 "liw_operand" "rO"))) + (set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_dup 0) + (match_operand 1 "liw_operand" "rO") + (match_operand:SI 4 "const_int_operand" "")] + UNSPEC_LIW))] + "TARGET_ALLOW_LIW" + "cmp_%W4 %3, %2, %1, %0" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 12)))] +) + +(define_insn "liw_cmp" + [(set (match_operand:SI 0 "register_operand" "=r") + (unspec:SI [(match_dup 0) + (match_operand 1 "liw_operand" "rO") + (match_operand:SI 4 "const_int_operand" "")] + UNSPEC_LIW)) + (set (reg:CC CC_REG) + (compare:CC (match_operand:SI 2 "register_operand" "r") + (match_operand 3 "liw_operand" "rO")))] + "TARGET_ALLOW_LIW" + "%W4_cmp %1, %0, %3, %2" + [(set (attr "timings") (if_then_else (eq_attr "cpu" "am34") + (const_int 13) (const_int 12)))] +) diff --git a/gcc/config/mn10300/mn10300.opt b/gcc/config/mn10300/mn10300.opt new file mode 100644 index 000000000..8909d8bd1 --- /dev/null +++ b/gcc/config/mn10300/mn10300.opt @@ -0,0 +1,56 @@ +; Options for the Matsushita MN10300 port of the compiler. + +; Copyright (C) 2005, 2007, 2010, 2011 Free Software Foundation, Inc. +; +; 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 +; . + +mam33 +Target +Target the AM33 processor + +mam33-2 +Target +Target the AM33/2.0 processor + +mam34 +Target Report +Target the AM34 processor + +mtune= +Target RejectNegative Joined +Tune code for the given processor + +mmult-bug +Target Report Mask(MULT_BUG) +Work around hardware multiply bug + +; Ignored by the compiler +mno-crt0 +Target RejectNegative + +; Ignored by the compiler +mrelax +Target RejectNegative +Enable linker relaxations + +mreturn-pointer-on-d0 +Target Report Mask(PTR_A0D0) +Return pointers in both a0 and d0 + +mliw +Target Report Mask(ALLOW_LIW) +Allow gcc to generate LIW instructions diff --git a/gcc/config/mn10300/predicates.md b/gcc/config/mn10300/predicates.md new file mode 100644 index 000000000..4c78c51e4 --- /dev/null +++ b/gcc/config/mn10300/predicates.md @@ -0,0 +1,69 @@ +;; Predicate definitions for Matsushita MN10300. +;; Copyright (C) 2005, 2007, 2010 Free Software Foundation, Inc. +;; +;; 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 +;; . + +;; Return true if the operand is the 1.0f constant. + +(define_predicate "const_1f_operand" + (match_code "const_int,const_double") +{ + return (op == CONST1_RTX (SFmode)); +}) + +;; Return true if OP is a valid call operand. + +(define_predicate "call_address_operand" + (match_code "symbol_ref,reg,unspec") +{ + if (flag_pic) + return (satisfies_constraint_S (op) || GET_CODE (op) == REG); + + return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == REG); +}) + +(define_predicate "impossible_plus_operand" + (match_code "plus") +{ + return XEXP (op, 0) == stack_pointer_rtx + || XEXP (op, 1) == stack_pointer_rtx; +}) + +(define_predicate "reg_or_am33_const_operand" + (ior (match_operand 0 "register_operand") + (and (match_test "TARGET_AM33") + (match_operand 0 "immediate_operand")))) + +(define_predicate "label_ref_operand" + (match_code "label_ref")) + +(define_special_predicate "int_mode_flags" + (match_code "reg") +{ + if (REGNO (op) != CC_REG) + return false; + if (GET_MODE (op) == CC_FLOATmode) + return false; + return GET_MODE_CLASS (GET_MODE (op)) == MODE_CC; +}) + +(define_predicate "CCZN_comparison_operator" + (match_code "eq,ne,lt,ge")) + +(define_predicate "liw_operand" + (ior (match_operand 0 "register_operand") + (match_test "satisfies_constraint_O (op)"))) diff --git a/gcc/config/mn10300/t-linux b/gcc/config/mn10300/t-linux new file mode 100644 index 000000000..61ed88e66 --- /dev/null +++ b/gcc/config/mn10300/t-linux @@ -0,0 +1,29 @@ +# Copyright (C) 2003 Free Software Foundation, Inc. +# +# 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 +# . + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + cat $(srcdir)/config/fp-bit.c > dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c diff --git a/gcc/config/mn10300/t-mn10300 b/gcc/config/mn10300/t-mn10300 new file mode 100644 index 000000000..eeefeb602 --- /dev/null +++ b/gcc/config/mn10300/t-mn10300 @@ -0,0 +1,36 @@ +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, +# 2003, 2010 Free Software Foundation, Inc. +# +# 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 +# . + +# We want fine grained libraries, so use the new code to build the +# floating point emulation libraries. +FPBIT = fp-bit.c +DPBIT = dp-bit.c + +dp-bit.c: $(srcdir)/config/fp-bit.c + cat $(srcdir)/config/fp-bit.c > dp-bit.c + +fp-bit.c: $(srcdir)/config/fp-bit.c + echo '#define FLOAT' > fp-bit.c + cat $(srcdir)/config/fp-bit.c >> fp-bit.c + +MULTILIB_OPTIONS = mam33/mam33-2/mam34 +MULTILIB_DIRNAMES = am33 am33-2 am34 + +LIBGCC = stmp-multilib +INSTALL_LIBGCC = install-multilib -- cgit v1.2.3