OpenCores
URL https://opencores.org/ocsvn/openrisc_2011-10-31/openrisc_2011-10-31/trunk

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [tags/] [gnu-src/] [gcc-4.5.1/] [gcc-4.5.1-or32-1.0rc2/] [gcc/] [config/] [mn10300/] [mn10300.c] - Diff between revs 282 and 384

Go to most recent revision | Only display areas with differences | Details | Blame | View Log

Rev 282 Rev 384
/* Subroutines for insn-output.c for Matsushita MN10300 series
/* Subroutines for insn-output.c for Matsushita MN10300 series
   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
   Contributed by Jeff Law (law@cygnus.com).
   Contributed by Jeff Law (law@cygnus.com).
 
 
This file is part of GCC.
This file is part of GCC.
 
 
GCC is free software; you can redistribute it and/or modify
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
the Free Software Foundation; either version 3, or (at your option)
any later version.
any later version.
 
 
GCC is distributed in the hope that it will be useful,
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
GNU General Public License for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
<http://www.gnu.org/licenses/>.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include "tm.h"
#include "tm.h"
#include "rtl.h"
#include "rtl.h"
#include "tree.h"
#include "tree.h"
#include "regs.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "hard-reg-set.h"
#include "real.h"
#include "real.h"
#include "insn-config.h"
#include "insn-config.h"
#include "conditions.h"
#include "conditions.h"
#include "output.h"
#include "output.h"
#include "insn-attr.h"
#include "insn-attr.h"
#include "flags.h"
#include "flags.h"
#include "recog.h"
#include "recog.h"
#include "reload.h"
#include "reload.h"
#include "expr.h"
#include "expr.h"
#include "optabs.h"
#include "optabs.h"
#include "function.h"
#include "function.h"
#include "obstack.h"
#include "obstack.h"
#include "toplev.h"
#include "toplev.h"
#include "tm_p.h"
#include "tm_p.h"
#include "target.h"
#include "target.h"
#include "target-def.h"
#include "target-def.h"
 
 
/* This is used by GOTaddr2picreg to uniquely identify
/* This is used by GOTaddr2picreg to uniquely identify
   UNSPEC_INT_LABELs.  */
   UNSPEC_INT_LABELs.  */
int mn10300_unspec_int_label_counter;
int mn10300_unspec_int_label_counter;
 
 
/* This is used in the am33_2.0-linux-gnu port, in which global symbol
/* 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
   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
   label with a plus sign or not, so that the assembler can tell
   symbol names from register names.  */
   symbol names from register names.  */
int mn10300_protect_label;
int mn10300_protect_label;
 
 
/* The selected processor.  */
/* The selected processor.  */
enum processor_type mn10300_processor = PROCESSOR_DEFAULT;
enum processor_type mn10300_processor = PROCESSOR_DEFAULT;
 
 
/* The size of the callee register save area.  Right now we save everything
/* 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
   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.  */
   speed standpoint, so we want to optimize this sooner or later.  */
#define REG_SAVE_BYTES (4 * df_regs_ever_live_p (2)     \
#define REG_SAVE_BYTES (4 * df_regs_ever_live_p (2)     \
                        + 4 * df_regs_ever_live_p (3)   \
                        + 4 * df_regs_ever_live_p (3)   \
                        + 4 * df_regs_ever_live_p (6)   \
                        + 4 * df_regs_ever_live_p (6)   \
                        + 4 * df_regs_ever_live_p (7)                   \
                        + 4 * df_regs_ever_live_p (7)                   \
                        + 16 * (df_regs_ever_live_p (14) || df_regs_ever_live_p (15) \
                        + 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)))
                                || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)))
 
 
 
 
static bool mn10300_handle_option (size_t, const char *, int);
static bool mn10300_handle_option (size_t, const char *, int);
static bool mn10300_legitimate_address_p (enum machine_mode, rtx, bool);
static bool mn10300_legitimate_address_p (enum machine_mode, rtx, bool);
static int mn10300_address_cost_1 (rtx, int *);
static int mn10300_address_cost_1 (rtx, int *);
static int mn10300_address_cost (rtx, bool);
static int mn10300_address_cost (rtx, bool);
static bool mn10300_rtx_costs (rtx, int, int, int *, bool);
static bool mn10300_rtx_costs (rtx, int, int, int *, bool);
static void mn10300_file_start (void);
static void mn10300_file_start (void);
static bool mn10300_return_in_memory (const_tree, const_tree);
static bool mn10300_return_in_memory (const_tree, const_tree);
static rtx mn10300_builtin_saveregs (void);
static rtx mn10300_builtin_saveregs (void);
static void mn10300_va_start (tree, rtx);
static void mn10300_va_start (tree, rtx);
static rtx mn10300_legitimize_address (rtx, rtx, enum machine_mode);
static rtx mn10300_legitimize_address (rtx, rtx, enum machine_mode);
static bool mn10300_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
static bool mn10300_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
                                       const_tree, bool);
                                       const_tree, bool);
static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
static int mn10300_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
                                      tree, bool);
                                      tree, bool);
static unsigned int mn10300_case_values_threshold (void);
static unsigned int mn10300_case_values_threshold (void);
static void mn10300_encode_section_info (tree, rtx, int);
static void mn10300_encode_section_info (tree, rtx, int);
static void mn10300_asm_trampoline_template (FILE *);
static void mn10300_asm_trampoline_template (FILE *);
static void mn10300_trampoline_init (rtx, tree, rtx);
static void mn10300_trampoline_init (rtx, tree, rtx);
static rtx mn10300_function_value (const_tree, const_tree, bool);
static rtx mn10300_function_value (const_tree, const_tree, bool);
static rtx mn10300_libcall_value (enum machine_mode, const_rtx);
static rtx mn10300_libcall_value (enum machine_mode, const_rtx);
static void mn10300_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
static void mn10300_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
static bool mn10300_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT, const_tree);
static bool mn10300_can_output_mi_thunk (const_tree, HOST_WIDE_INT, HOST_WIDE_INT, const_tree);


/* Initialize the GCC target structure.  */
/* Initialize the GCC target structure.  */
#undef TARGET_ASM_ALIGNED_HI_OP
#undef TARGET_ASM_ALIGNED_HI_OP
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
#define TARGET_ASM_ALIGNED_HI_OP "\t.hword\t"
 
 
#undef TARGET_LEGITIMIZE_ADDRESS
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS mn10300_legitimize_address
#define TARGET_LEGITIMIZE_ADDRESS mn10300_legitimize_address
 
 
#undef TARGET_RTX_COSTS
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS mn10300_rtx_costs
#define TARGET_RTX_COSTS mn10300_rtx_costs
#undef TARGET_ADDRESS_COST
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST mn10300_address_cost
#define TARGET_ADDRESS_COST mn10300_address_cost
 
 
#undef TARGET_ASM_FILE_START
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START mn10300_file_start
#define TARGET_ASM_FILE_START mn10300_file_start
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
 
#undef TARGET_DEFAULT_TARGET_FLAGS
#undef TARGET_DEFAULT_TARGET_FLAGS
#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0
#define TARGET_DEFAULT_TARGET_FLAGS MASK_MULT_BUG | MASK_PTR_A0D0
#undef TARGET_HANDLE_OPTION
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION mn10300_handle_option
#define TARGET_HANDLE_OPTION mn10300_handle_option
 
 
#undef  TARGET_ENCODE_SECTION_INFO
#undef  TARGET_ENCODE_SECTION_INFO
#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
#define TARGET_ENCODE_SECTION_INFO mn10300_encode_section_info
 
 
#undef TARGET_PROMOTE_PROTOTYPES
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
#undef TARGET_RETURN_IN_MEMORY
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY mn10300_return_in_memory
#define TARGET_RETURN_IN_MEMORY mn10300_return_in_memory
#undef TARGET_PASS_BY_REFERENCE
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE mn10300_pass_by_reference
#define TARGET_PASS_BY_REFERENCE mn10300_pass_by_reference
#undef TARGET_CALLEE_COPIES
#undef TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
#undef TARGET_ARG_PARTIAL_BYTES
#undef TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES mn10300_arg_partial_bytes
#define TARGET_ARG_PARTIAL_BYTES mn10300_arg_partial_bytes
 
 
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#undef TARGET_EXPAND_BUILTIN_SAVEREGS
#define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs
#define TARGET_EXPAND_BUILTIN_SAVEREGS mn10300_builtin_saveregs
#undef TARGET_EXPAND_BUILTIN_VA_START
#undef TARGET_EXPAND_BUILTIN_VA_START
#define TARGET_EXPAND_BUILTIN_VA_START mn10300_va_start
#define TARGET_EXPAND_BUILTIN_VA_START mn10300_va_start
 
 
#undef TARGET_CASE_VALUES_THRESHOLD
#undef TARGET_CASE_VALUES_THRESHOLD
#define TARGET_CASE_VALUES_THRESHOLD mn10300_case_values_threshold
#define TARGET_CASE_VALUES_THRESHOLD mn10300_case_values_threshold
 
 
#undef TARGET_LEGITIMATE_ADDRESS_P
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P     mn10300_legitimate_address_p
#define TARGET_LEGITIMATE_ADDRESS_P     mn10300_legitimate_address_p
 
 
#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
#define TARGET_ASM_TRAMPOLINE_TEMPLATE mn10300_asm_trampoline_template
#define TARGET_ASM_TRAMPOLINE_TEMPLATE mn10300_asm_trampoline_template
#undef TARGET_TRAMPOLINE_INIT
#undef TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT mn10300_trampoline_init
#define TARGET_TRAMPOLINE_INIT mn10300_trampoline_init
 
 
#undef TARGET_FUNCTION_VALUE
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE mn10300_function_value
#define TARGET_FUNCTION_VALUE mn10300_function_value
#undef TARGET_LIBCALL_VALUE
#undef TARGET_LIBCALL_VALUE
#define TARGET_LIBCALL_VALUE mn10300_libcall_value
#define TARGET_LIBCALL_VALUE mn10300_libcall_value
 
 
#undef  TARGET_ASM_OUTPUT_MI_THUNK
#undef  TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK      mn10300_asm_output_mi_thunk
#define TARGET_ASM_OUTPUT_MI_THUNK      mn10300_asm_output_mi_thunk
#undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
#undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK  mn10300_can_output_mi_thunk
#define TARGET_ASM_CAN_OUTPUT_MI_THUNK  mn10300_can_output_mi_thunk
 
 
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;


/* Implement TARGET_HANDLE_OPTION.  */
/* Implement TARGET_HANDLE_OPTION.  */
 
 
static bool
static bool
mn10300_handle_option (size_t code,
mn10300_handle_option (size_t code,
                       const char *arg ATTRIBUTE_UNUSED,
                       const char *arg ATTRIBUTE_UNUSED,
                       int value)
                       int value)
{
{
  switch (code)
  switch (code)
    {
    {
    case OPT_mam33:
    case OPT_mam33:
      mn10300_processor = value ? PROCESSOR_AM33 : PROCESSOR_MN10300;
      mn10300_processor = value ? PROCESSOR_AM33 : PROCESSOR_MN10300;
      return true;
      return true;
    case OPT_mam33_2:
    case OPT_mam33_2:
      mn10300_processor = (value
      mn10300_processor = (value
                           ? PROCESSOR_AM33_2
                           ? PROCESSOR_AM33_2
                           : MIN (PROCESSOR_AM33, PROCESSOR_DEFAULT));
                           : MIN (PROCESSOR_AM33, PROCESSOR_DEFAULT));
      return true;
      return true;
    default:
    default:
      return true;
      return true;
    }
    }
}
}
 
 
/* Implement OVERRIDE_OPTIONS.  */
/* Implement OVERRIDE_OPTIONS.  */
 
 
void
void
mn10300_override_options (void)
mn10300_override_options (void)
{
{
  if (TARGET_AM33)
  if (TARGET_AM33)
    target_flags &= ~MASK_MULT_BUG;
    target_flags &= ~MASK_MULT_BUG;
}
}
 
 
static void
static void
mn10300_file_start (void)
mn10300_file_start (void)
{
{
  default_file_start ();
  default_file_start ();
 
 
  if (TARGET_AM33_2)
  if (TARGET_AM33_2)
    fprintf (asm_out_file, "\t.am33_2\n");
    fprintf (asm_out_file, "\t.am33_2\n");
  else if (TARGET_AM33)
  else if (TARGET_AM33)
    fprintf (asm_out_file, "\t.am33\n");
    fprintf (asm_out_file, "\t.am33\n");
}
}


 
 
/* Print operand X using operand code CODE to assembly language output file
/* Print operand X using operand code CODE to assembly language output file
   FILE.  */
   FILE.  */
 
 
void
void
print_operand (FILE *file, rtx x, int code)
print_operand (FILE *file, rtx x, int code)
{
{
  switch (code)
  switch (code)
    {
    {
      case 'b':
      case 'b':
      case 'B':
      case 'B':
        if (cc_status.mdep.fpCC)
        if (cc_status.mdep.fpCC)
          {
          {
            switch (code == 'b' ? GET_CODE (x)
            switch (code == 'b' ? GET_CODE (x)
                    : reverse_condition_maybe_unordered (GET_CODE (x)))
                    : reverse_condition_maybe_unordered (GET_CODE (x)))
              {
              {
              case NE:
              case NE:
                fprintf (file, "ne");
                fprintf (file, "ne");
                break;
                break;
              case EQ:
              case EQ:
                fprintf (file, "eq");
                fprintf (file, "eq");
                break;
                break;
              case GE:
              case GE:
                fprintf (file, "ge");
                fprintf (file, "ge");
                break;
                break;
              case GT:
              case GT:
                fprintf (file, "gt");
                fprintf (file, "gt");
                break;
                break;
              case LE:
              case LE:
                fprintf (file, "le");
                fprintf (file, "le");
                break;
                break;
              case LT:
              case LT:
                fprintf (file, "lt");
                fprintf (file, "lt");
                break;
                break;
              case ORDERED:
              case ORDERED:
                fprintf (file, "lge");
                fprintf (file, "lge");
                break;
                break;
              case UNORDERED:
              case UNORDERED:
                fprintf (file, "uo");
                fprintf (file, "uo");
                break;
                break;
              case LTGT:
              case LTGT:
                fprintf (file, "lg");
                fprintf (file, "lg");
                break;
                break;
              case UNEQ:
              case UNEQ:
                fprintf (file, "ue");
                fprintf (file, "ue");
                break;
                break;
              case UNGE:
              case UNGE:
                fprintf (file, "uge");
                fprintf (file, "uge");
                break;
                break;
              case UNGT:
              case UNGT:
                fprintf (file, "ug");
                fprintf (file, "ug");
                break;
                break;
              case UNLE:
              case UNLE:
                fprintf (file, "ule");
                fprintf (file, "ule");
                break;
                break;
              case UNLT:
              case UNLT:
                fprintf (file, "ul");
                fprintf (file, "ul");
                break;
                break;
              default:
              default:
                gcc_unreachable ();
                gcc_unreachable ();
              }
              }
            break;
            break;
          }
          }
        /* These are normal and reversed branches.  */
        /* These are normal and reversed branches.  */
        switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
        switch (code == 'b' ? GET_CODE (x) : reverse_condition (GET_CODE (x)))
          {
          {
          case NE:
          case NE:
            fprintf (file, "ne");
            fprintf (file, "ne");
            break;
            break;
          case EQ:
          case EQ:
            fprintf (file, "eq");
            fprintf (file, "eq");
            break;
            break;
          case GE:
          case GE:
            fprintf (file, "ge");
            fprintf (file, "ge");
            break;
            break;
          case GT:
          case GT:
            fprintf (file, "gt");
            fprintf (file, "gt");
            break;
            break;
          case LE:
          case LE:
            fprintf (file, "le");
            fprintf (file, "le");
            break;
            break;
          case LT:
          case LT:
            fprintf (file, "lt");
            fprintf (file, "lt");
            break;
            break;
          case GEU:
          case GEU:
            fprintf (file, "cc");
            fprintf (file, "cc");
            break;
            break;
          case GTU:
          case GTU:
            fprintf (file, "hi");
            fprintf (file, "hi");
            break;
            break;
          case LEU:
          case LEU:
            fprintf (file, "ls");
            fprintf (file, "ls");
            break;
            break;
          case LTU:
          case LTU:
            fprintf (file, "cs");
            fprintf (file, "cs");
            break;
            break;
          default:
          default:
            gcc_unreachable ();
            gcc_unreachable ();
          }
          }
        break;
        break;
      case 'C':
      case 'C':
        /* This is used for the operand to a call instruction;
        /* This is used for the operand to a call instruction;
           if it's a REG, enclose it in parens, else output
           if it's a REG, enclose it in parens, else output
           the operand normally.  */
           the operand normally.  */
        if (GET_CODE (x) == REG)
        if (GET_CODE (x) == REG)
          {
          {
            fputc ('(', file);
            fputc ('(', file);
            print_operand (file, x, 0);
            print_operand (file, x, 0);
            fputc (')', file);
            fputc (')', file);
          }
          }
        else
        else
          print_operand (file, x, 0);
          print_operand (file, x, 0);
        break;
        break;
 
 
      case 'D':
      case 'D':
        switch (GET_CODE (x))
        switch (GET_CODE (x))
          {
          {
          case MEM:
          case MEM:
            fputc ('(', file);
            fputc ('(', file);
            output_address (XEXP (x, 0));
            output_address (XEXP (x, 0));
            fputc (')', file);
            fputc (')', file);
            break;
            break;
 
 
          case REG:
          case REG:
            fprintf (file, "fd%d", REGNO (x) - 18);
            fprintf (file, "fd%d", REGNO (x) - 18);
            break;
            break;
 
 
          default:
          default:
            gcc_unreachable ();
            gcc_unreachable ();
          }
          }
        break;
        break;
 
 
      /* These are the least significant word in a 64bit value.  */
      /* These are the least significant word in a 64bit value.  */
      case 'L':
      case 'L':
        switch (GET_CODE (x))
        switch (GET_CODE (x))
          {
          {
          case MEM:
          case MEM:
            fputc ('(', file);
            fputc ('(', file);
            output_address (XEXP (x, 0));
            output_address (XEXP (x, 0));
            fputc (')', file);
            fputc (')', file);
            break;
            break;
 
 
          case REG:
          case REG:
            fprintf (file, "%s", reg_names[REGNO (x)]);
            fprintf (file, "%s", reg_names[REGNO (x)]);
            break;
            break;
 
 
          case SUBREG:
          case SUBREG:
            fprintf (file, "%s", reg_names[subreg_regno (x)]);
            fprintf (file, "%s", reg_names[subreg_regno (x)]);
            break;
            break;
 
 
          case CONST_DOUBLE:
          case CONST_DOUBLE:
              {
              {
                long val[2];
                long val[2];
                REAL_VALUE_TYPE rv;
                REAL_VALUE_TYPE rv;
 
 
                switch (GET_MODE (x))
                switch (GET_MODE (x))
                  {
                  {
                    case DFmode:
                    case DFmode:
                      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
                      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
                      REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
                      REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
                      fprintf (file, "0x%lx", val[0]);
                      fprintf (file, "0x%lx", val[0]);
                      break;;
                      break;;
                    case SFmode:
                    case SFmode:
                      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
                      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
                      REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]);
                      REAL_VALUE_TO_TARGET_SINGLE (rv, val[0]);
                      fprintf (file, "0x%lx", val[0]);
                      fprintf (file, "0x%lx", val[0]);
                      break;;
                      break;;
                    case VOIDmode:
                    case VOIDmode:
                    case DImode:
                    case DImode:
                      print_operand_address (file,
                      print_operand_address (file,
                                             GEN_INT (CONST_DOUBLE_LOW (x)));
                                             GEN_INT (CONST_DOUBLE_LOW (x)));
                      break;
                      break;
                    default:
                    default:
                      break;
                      break;
                  }
                  }
                break;
                break;
              }
              }
 
 
          case CONST_INT:
          case CONST_INT:
            {
            {
              rtx low, high;
              rtx low, high;
              split_double (x, &low, &high);
              split_double (x, &low, &high);
              fprintf (file, "%ld", (long)INTVAL (low));
              fprintf (file, "%ld", (long)INTVAL (low));
              break;
              break;
            }
            }
 
 
          default:
          default:
            gcc_unreachable ();
            gcc_unreachable ();
          }
          }
        break;
        break;
 
 
      /* Similarly, but for the most significant word.  */
      /* Similarly, but for the most significant word.  */
      case 'H':
      case 'H':
        switch (GET_CODE (x))
        switch (GET_CODE (x))
          {
          {
          case MEM:
          case MEM:
            fputc ('(', file);
            fputc ('(', file);
            x = adjust_address (x, SImode, 4);
            x = adjust_address (x, SImode, 4);
            output_address (XEXP (x, 0));
            output_address (XEXP (x, 0));
            fputc (')', file);
            fputc (')', file);
            break;
            break;
 
 
          case REG:
          case REG:
            fprintf (file, "%s", reg_names[REGNO (x) + 1]);
            fprintf (file, "%s", reg_names[REGNO (x) + 1]);
            break;
            break;
 
 
          case SUBREG:
          case SUBREG:
            fprintf (file, "%s", reg_names[subreg_regno (x) + 1]);
            fprintf (file, "%s", reg_names[subreg_regno (x) + 1]);
            break;
            break;
 
 
          case CONST_DOUBLE:
          case CONST_DOUBLE:
              {
              {
                long val[2];
                long val[2];
                REAL_VALUE_TYPE rv;
                REAL_VALUE_TYPE rv;
 
 
                switch (GET_MODE (x))
                switch (GET_MODE (x))
                  {
                  {
                    case DFmode:
                    case DFmode:
                      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
                      REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
                      REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
                      REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
                      fprintf (file, "0x%lx", val[1]);
                      fprintf (file, "0x%lx", val[1]);
                      break;;
                      break;;
                    case SFmode:
                    case SFmode:
                      gcc_unreachable ();
                      gcc_unreachable ();
                    case VOIDmode:
                    case VOIDmode:
                    case DImode:
                    case DImode:
                      print_operand_address (file,
                      print_operand_address (file,
                                             GEN_INT (CONST_DOUBLE_HIGH (x)));
                                             GEN_INT (CONST_DOUBLE_HIGH (x)));
                      break;
                      break;
                    default:
                    default:
                      break;
                      break;
                  }
                  }
                break;
                break;
              }
              }
 
 
          case CONST_INT:
          case CONST_INT:
            {
            {
              rtx low, high;
              rtx low, high;
              split_double (x, &low, &high);
              split_double (x, &low, &high);
              fprintf (file, "%ld", (long)INTVAL (high));
              fprintf (file, "%ld", (long)INTVAL (high));
              break;
              break;
            }
            }
 
 
          default:
          default:
            gcc_unreachable ();
            gcc_unreachable ();
          }
          }
        break;
        break;
 
 
      case 'A':
      case 'A':
        fputc ('(', file);
        fputc ('(', file);
        if (GET_CODE (XEXP (x, 0)) == REG)
        if (GET_CODE (XEXP (x, 0)) == REG)
          output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), const0_rtx));
          output_address (gen_rtx_PLUS (SImode, XEXP (x, 0), const0_rtx));
        else
        else
          output_address (XEXP (x, 0));
          output_address (XEXP (x, 0));
        fputc (')', file);
        fputc (')', file);
        break;
        break;
 
 
      case 'N':
      case 'N':
        gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
        gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
        fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff));
        fprintf (file, "%d", (int)((~INTVAL (x)) & 0xff));
        break;
        break;
 
 
      case 'U':
      case 'U':
        gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
        gcc_assert (INTVAL (x) >= -128 && INTVAL (x) <= 255);
        fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
        fprintf (file, "%d", (int)(INTVAL (x) & 0xff));
        break;
        break;
 
 
      /* For shift counts.  The hardware ignores the upper bits of
      /* For shift counts.  The hardware ignores the upper bits of
         any immediate, but the assembler will flag an out of range
         any immediate, but the assembler will flag an out of range
         shift count as an error.  So we mask off the high bits
         shift count as an error.  So we mask off the high bits
         of the immediate here.  */
         of the immediate here.  */
      case 'S':
      case 'S':
        if (GET_CODE (x) == CONST_INT)
        if (GET_CODE (x) == CONST_INT)
          {
          {
            fprintf (file, "%d", (int)(INTVAL (x) & 0x1f));
            fprintf (file, "%d", (int)(INTVAL (x) & 0x1f));
            break;
            break;
          }
          }
        /* FALL THROUGH */
        /* FALL THROUGH */
 
 
      default:
      default:
        switch (GET_CODE (x))
        switch (GET_CODE (x))
          {
          {
          case MEM:
          case MEM:
            fputc ('(', file);
            fputc ('(', file);
            output_address (XEXP (x, 0));
            output_address (XEXP (x, 0));
            fputc (')', file);
            fputc (')', file);
            break;
            break;
 
 
          case PLUS:
          case PLUS:
            output_address (x);
            output_address (x);
            break;
            break;
 
 
          case REG:
          case REG:
            fprintf (file, "%s", reg_names[REGNO (x)]);
            fprintf (file, "%s", reg_names[REGNO (x)]);
            break;
            break;
 
 
          case SUBREG:
          case SUBREG:
            fprintf (file, "%s", reg_names[subreg_regno (x)]);
            fprintf (file, "%s", reg_names[subreg_regno (x)]);
            break;
            break;
 
 
          /* This will only be single precision....  */
          /* This will only be single precision....  */
          case CONST_DOUBLE:
          case CONST_DOUBLE:
            {
            {
              unsigned long val;
              unsigned long val;
              REAL_VALUE_TYPE rv;
              REAL_VALUE_TYPE rv;
 
 
              REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
              REAL_VALUE_FROM_CONST_DOUBLE (rv, x);
              REAL_VALUE_TO_TARGET_SINGLE (rv, val);
              REAL_VALUE_TO_TARGET_SINGLE (rv, val);
              fprintf (file, "0x%lx", val);
              fprintf (file, "0x%lx", val);
              break;
              break;
            }
            }
 
 
          case CONST_INT:
          case CONST_INT:
          case SYMBOL_REF:
          case SYMBOL_REF:
          case CONST:
          case CONST:
          case LABEL_REF:
          case LABEL_REF:
          case CODE_LABEL:
          case CODE_LABEL:
          case UNSPEC:
          case UNSPEC:
            print_operand_address (file, x);
            print_operand_address (file, x);
            break;
            break;
          default:
          default:
            gcc_unreachable ();
            gcc_unreachable ();
          }
          }
        break;
        break;
   }
   }
}
}
 
 
/* Output assembly language output for the address ADDR to FILE.  */
/* Output assembly language output for the address ADDR to FILE.  */
 
 
void
void
print_operand_address (FILE *file, rtx addr)
print_operand_address (FILE *file, rtx addr)
{
{
  switch (GET_CODE (addr))
  switch (GET_CODE (addr))
    {
    {
    case POST_INC:
    case POST_INC:
      print_operand_address (file, XEXP (addr, 0));
      print_operand_address (file, XEXP (addr, 0));
      fputc ('+', file);
      fputc ('+', file);
      break;
      break;
    case REG:
    case REG:
      print_operand (file, addr, 0);
      print_operand (file, addr, 0);
      break;
      break;
    case PLUS:
    case PLUS:
      {
      {
        rtx base, index;
        rtx base, index;
        if (REG_P (XEXP (addr, 0))
        if (REG_P (XEXP (addr, 0))
            && REG_OK_FOR_BASE_P (XEXP (addr, 0)))
            && REG_OK_FOR_BASE_P (XEXP (addr, 0)))
          base = XEXP (addr, 0), index = XEXP (addr, 1);
          base = XEXP (addr, 0), index = XEXP (addr, 1);
        else if (REG_P (XEXP (addr, 1))
        else if (REG_P (XEXP (addr, 1))
            && REG_OK_FOR_BASE_P (XEXP (addr, 1)))
            && REG_OK_FOR_BASE_P (XEXP (addr, 1)))
          base = XEXP (addr, 1), index = XEXP (addr, 0);
          base = XEXP (addr, 1), index = XEXP (addr, 0);
        else
        else
          gcc_unreachable ();
          gcc_unreachable ();
        print_operand (file, index, 0);
        print_operand (file, index, 0);
        fputc (',', file);
        fputc (',', file);
        print_operand (file, base, 0);;
        print_operand (file, base, 0);;
        break;
        break;
      }
      }
    case SYMBOL_REF:
    case SYMBOL_REF:
      output_addr_const (file, addr);
      output_addr_const (file, addr);
      break;
      break;
    default:
    default:
      output_addr_const (file, addr);
      output_addr_const (file, addr);
      break;
      break;
    }
    }
}
}
 
 
/* Count the number of FP registers that have to be saved.  */
/* Count the number of FP registers that have to be saved.  */
static int
static int
fp_regs_to_save (void)
fp_regs_to_save (void)
{
{
  int i, n = 0;
  int i, n = 0;
 
 
  if (! TARGET_AM33_2)
  if (! TARGET_AM33_2)
    return 0;
    return 0;
 
 
  for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
  for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
    if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
    if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
      ++n;
      ++n;
 
 
  return n;
  return n;
}
}
 
 
/* Print a set of registers in the format required by "movm" and "ret".
/* 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
   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.
   registers can be stored individually, but the extended registers cannot.
   We assume that the mask already takes that into account.  For instance,
   We assume that the mask already takes that into account.  For instance,
   bits 14 to 17 must have the same value.  */
   bits 14 to 17 must have the same value.  */
 
 
void
void
mn10300_print_reg_list (FILE *file, int mask)
mn10300_print_reg_list (FILE *file, int mask)
{
{
  int need_comma;
  int need_comma;
  int i;
  int i;
 
 
  need_comma = 0;
  need_comma = 0;
  fputc ('[', file);
  fputc ('[', file);
 
 
  for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
  for (i = 0; i < FIRST_EXTENDED_REGNUM; i++)
    if ((mask & (1 << i)) != 0)
    if ((mask & (1 << i)) != 0)
      {
      {
        if (need_comma)
        if (need_comma)
          fputc (',', file);
          fputc (',', file);
        fputs (reg_names [i], file);
        fputs (reg_names [i], file);
        need_comma = 1;
        need_comma = 1;
      }
      }
 
 
  if ((mask & 0x3c000) != 0)
  if ((mask & 0x3c000) != 0)
    {
    {
      gcc_assert ((mask & 0x3c000) == 0x3c000);
      gcc_assert ((mask & 0x3c000) == 0x3c000);
      if (need_comma)
      if (need_comma)
        fputc (',', file);
        fputc (',', file);
      fputs ("exreg1", file);
      fputs ("exreg1", file);
      need_comma = 1;
      need_comma = 1;
    }
    }
 
 
  fputc (']', file);
  fputc (']', file);
}
}
 
 
int
int
can_use_return_insn (void)
can_use_return_insn (void)
{
{
  /* size includes the fixed stack space needed for function calls.  */
  /* size includes the fixed stack space needed for function calls.  */
  int size = get_frame_size () + crtl->outgoing_args_size;
  int size = get_frame_size () + crtl->outgoing_args_size;
 
 
  /* And space for the return pointer.  */
  /* And space for the return pointer.  */
  size += crtl->outgoing_args_size ? 4 : 0;
  size += crtl->outgoing_args_size ? 4 : 0;
 
 
  return (reload_completed
  return (reload_completed
          && size == 0
          && size == 0
          && !df_regs_ever_live_p (2)
          && !df_regs_ever_live_p (2)
          && !df_regs_ever_live_p (3)
          && !df_regs_ever_live_p (3)
          && !df_regs_ever_live_p (6)
          && !df_regs_ever_live_p (6)
          && !df_regs_ever_live_p (7)
          && !df_regs_ever_live_p (7)
          && !df_regs_ever_live_p (14)
          && !df_regs_ever_live_p (14)
          && !df_regs_ever_live_p (15)
          && !df_regs_ever_live_p (15)
          && !df_regs_ever_live_p (16)
          && !df_regs_ever_live_p (16)
          && !df_regs_ever_live_p (17)
          && !df_regs_ever_live_p (17)
          && fp_regs_to_save () == 0
          && fp_regs_to_save () == 0
          && !frame_pointer_needed);
          && !frame_pointer_needed);
}
}
 
 
/* Returns the set of live, callee-saved registers as a bitmask.  The
/* Returns the set of live, callee-saved registers as a bitmask.  The
   callee-saved extended registers cannot be stored individually, so
   callee-saved extended registers cannot be stored individually, so
   all of them will be included in the mask if any one of them is used.  */
   all of them will be included in the mask if any one of them is used.  */
 
 
int
int
mn10300_get_live_callee_saved_regs (void)
mn10300_get_live_callee_saved_regs (void)
{
{
  int mask;
  int mask;
  int i;
  int i;
 
 
  mask = 0;
  mask = 0;
  for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
  for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
    if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
    if (df_regs_ever_live_p (i) && ! call_really_used_regs[i])
      mask |= (1 << i);
      mask |= (1 << i);
  if ((mask & 0x3c000) != 0)
  if ((mask & 0x3c000) != 0)
    mask |= 0x3c000;
    mask |= 0x3c000;
 
 
  return mask;
  return mask;
}
}
 
 
/* Generate an instruction that pushes several registers onto the stack.
/* 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
   Register K will be saved if bit K in MASK is set.  The function does
   nothing if MASK is zero.
   nothing if MASK is zero.
 
 
   To be compatible with the "movm" instruction, the lowest-numbered
   To be compatible with the "movm" instruction, the lowest-numbered
   register must be stored in the lowest slot.  If MASK is the set
   register must be stored in the lowest slot.  If MASK is the set
   { R1,...,RN }, where R1...RN are ordered least first, the generated
   { R1,...,RN }, where R1...RN are ordered least first, the generated
   instruction will have the form:
   instruction will have the form:
 
 
       (parallel
       (parallel
         (set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4)))
         (set (reg:SI 9) (plus:SI (reg:SI 9) (const_int -N*4)))
         (set (mem:SI (plus:SI (reg:SI 9)
         (set (mem:SI (plus:SI (reg:SI 9)
                               (const_int -1*4)))
                               (const_int -1*4)))
              (reg:SI RN))
              (reg:SI RN))
         ...
         ...
         (set (mem:SI (plus:SI (reg:SI 9)
         (set (mem:SI (plus:SI (reg:SI 9)
                               (const_int -N*4)))
                               (const_int -N*4)))
              (reg:SI R1))) */
              (reg:SI R1))) */
 
 
void
void
mn10300_gen_multiple_store (int mask)
mn10300_gen_multiple_store (int mask)
{
{
  if (mask != 0)
  if (mask != 0)
    {
    {
      int i;
      int i;
      int count;
      int count;
      rtx par;
      rtx par;
      int pari;
      int pari;
 
 
      /* Count how many registers need to be saved.  */
      /* Count how many registers need to be saved.  */
      count = 0;
      count = 0;
      for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
      for (i = 0; i <= LAST_EXTENDED_REGNUM; i++)
        if ((mask & (1 << i)) != 0)
        if ((mask & (1 << i)) != 0)
          count += 1;
          count += 1;
 
 
      /* We need one PARALLEL element to update the stack pointer and
      /* We need one PARALLEL element to update the stack pointer and
         an additional element for each register that is stored.  */
         an additional element for each register that is stored.  */
      par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
      par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count + 1));
 
 
      /* Create the instruction that updates the stack pointer.  */
      /* Create the instruction that updates the stack pointer.  */
      XVECEXP (par, 0, 0)
      XVECEXP (par, 0, 0)
        = gen_rtx_SET (SImode,
        = gen_rtx_SET (SImode,
                       stack_pointer_rtx,
                       stack_pointer_rtx,
                       gen_rtx_PLUS (SImode,
                       gen_rtx_PLUS (SImode,
                                     stack_pointer_rtx,
                                     stack_pointer_rtx,
                                     GEN_INT (-count * 4)));
                                     GEN_INT (-count * 4)));
 
 
      /* Create each store.  */
      /* Create each store.  */
      pari = 1;
      pari = 1;
      for (i = LAST_EXTENDED_REGNUM; i >= 0; i--)
      for (i = LAST_EXTENDED_REGNUM; i >= 0; i--)
        if ((mask & (1 << i)) != 0)
        if ((mask & (1 << i)) != 0)
          {
          {
            rtx address = gen_rtx_PLUS (SImode,
            rtx address = gen_rtx_PLUS (SImode,
                                        stack_pointer_rtx,
                                        stack_pointer_rtx,
                                        GEN_INT (-pari * 4));
                                        GEN_INT (-pari * 4));
            XVECEXP(par, 0, pari)
            XVECEXP(par, 0, pari)
              = gen_rtx_SET (VOIDmode,
              = gen_rtx_SET (VOIDmode,
                             gen_rtx_MEM (SImode, address),
                             gen_rtx_MEM (SImode, address),
                             gen_rtx_REG (SImode, i));
                             gen_rtx_REG (SImode, i));
            pari += 1;
            pari += 1;
          }
          }
 
 
      par = emit_insn (par);
      par = emit_insn (par);
      RTX_FRAME_RELATED_P (par) = 1;
      RTX_FRAME_RELATED_P (par) = 1;
    }
    }
}
}
 
 
void
void
expand_prologue (void)
expand_prologue (void)
{
{
  HOST_WIDE_INT size;
  HOST_WIDE_INT size;
 
 
  /* SIZE includes the fixed stack space needed for function calls.  */
  /* SIZE includes the fixed stack space needed for function calls.  */
  size = get_frame_size () + crtl->outgoing_args_size;
  size = get_frame_size () + crtl->outgoing_args_size;
  size += (crtl->outgoing_args_size ? 4 : 0);
  size += (crtl->outgoing_args_size ? 4 : 0);
 
 
  /* If we use any of the callee-saved registers, save them now.  */
  /* If we use any of the callee-saved registers, save them now.  */
  mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
  mn10300_gen_multiple_store (mn10300_get_live_callee_saved_regs ());
 
 
  if (TARGET_AM33_2 && fp_regs_to_save ())
  if (TARGET_AM33_2 && fp_regs_to_save ())
    {
    {
      int num_regs_to_save = fp_regs_to_save (), i;
      int num_regs_to_save = fp_regs_to_save (), i;
      HOST_WIDE_INT xsize;
      HOST_WIDE_INT xsize;
      enum { save_sp_merge,
      enum { save_sp_merge,
             save_sp_no_merge,
             save_sp_no_merge,
             save_sp_partial_merge,
             save_sp_partial_merge,
             save_a0_merge,
             save_a0_merge,
             save_a0_no_merge } strategy;
             save_a0_no_merge } strategy;
      unsigned int strategy_size = (unsigned)-1, this_strategy_size;
      unsigned int strategy_size = (unsigned)-1, this_strategy_size;
      rtx reg;
      rtx reg;
      rtx insn;
      rtx insn;
 
 
      /* We have several different strategies to save FP registers.
      /* We have several different strategies to save FP registers.
         We can store them using SP offsets, which is beneficial if
         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
         there are just a few registers to save, or we can use `a0' in
         post-increment mode (`a0' is the only call-clobbered address
         post-increment mode (`a0' is the only call-clobbered address
         register that is never used to pass information to a
         register that is never used to pass information to a
         function).  Furthermore, if we don't need a frame pointer, we
         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
         can merge the two SP adds into a single one, but this isn't
         always beneficial; sometimes we can just split the two adds
         always beneficial; sometimes we can just split the two adds
         so that we don't exceed a 16-bit constant size.  The code
         so that we don't exceed a 16-bit constant size.  The code
         below will select which strategy to use, so as to generate
         below will select which strategy to use, so as to generate
         smallest code.  Ties are broken in favor or shorter sequences
         smallest code.  Ties are broken in favor or shorter sequences
         (in terms of number of instructions).  */
         (in terms of number of instructions).  */
 
 
#define SIZE_ADD_AX(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
#define SIZE_ADD_AX(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
                        : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 2)
                        : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 2)
#define SIZE_ADD_SP(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
#define SIZE_ADD_SP(S) ((((S) >= (1 << 15)) || ((S) < -(1 << 15))) ? 6 \
                        : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 3)
                        : (((S) >= (1 << 7)) || ((S) < -(1 << 7))) ? 4 : 3)
#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
#define SIZE_FMOV_LIMIT(S,N,L,SIZE1,SIZE2,ELSE) \
  (((S) >= (L)) ? (SIZE1) * (N) \
  (((S) >= (L)) ? (SIZE1) * (N) \
   : ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
   : ((S) + 4 * (N) >= (L)) ? (((L) - (S)) / 4 * (SIZE2) \
                               + ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
                               + ((S) + 4 * (N) - (L)) / 4 * (SIZE1)) \
   : (ELSE))
   : (ELSE))
#define SIZE_FMOV_SP_(S,N) \
#define SIZE_FMOV_SP_(S,N) \
  (SIZE_FMOV_LIMIT ((S), (N), (1 << 24), 7, 6, \
  (SIZE_FMOV_LIMIT ((S), (N), (1 << 24), 7, 6, \
                   SIZE_FMOV_LIMIT ((S), (N), (1 << 8), 6, 4, \
                   SIZE_FMOV_LIMIT ((S), (N), (1 << 8), 6, 4, \
                                    (S) ? 4 * (N) : 3 + 4 * ((N) - 1))))
                                    (S) ? 4 * (N) : 3 + 4 * ((N) - 1))))
#define SIZE_FMOV_SP(S,N) (SIZE_FMOV_SP_ ((unsigned HOST_WIDE_INT)(S), (N)))
#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
      /* Consider alternative save_sp_merge only if we don't need the
         frame pointer and size is nonzero.  */
         frame pointer and size is nonzero.  */
      if (! frame_pointer_needed && size)
      if (! frame_pointer_needed && size)
        {
        {
          /* Insn: add -(size + 4 * num_regs_to_save), sp.  */
          /* Insn: add -(size + 4 * num_regs_to_save), sp.  */
          this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
          this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
          /* Insn: fmov fs#, (##, sp), for each fs# to be saved.  */
          /* Insn: fmov fs#, (##, sp), for each fs# to be saved.  */
          this_strategy_size += SIZE_FMOV_SP (size, num_regs_to_save);
          this_strategy_size += SIZE_FMOV_SP (size, num_regs_to_save);
 
 
          if (this_strategy_size < strategy_size)
          if (this_strategy_size < strategy_size)
            {
            {
              strategy = save_sp_merge;
              strategy = save_sp_merge;
              strategy_size = this_strategy_size;
              strategy_size = this_strategy_size;
            }
            }
        }
        }
 
 
      /* Consider alternative save_sp_no_merge unconditionally.  */
      /* Consider alternative save_sp_no_merge unconditionally.  */
      /* Insn: add -4 * num_regs_to_save, sp.  */
      /* Insn: add -4 * num_regs_to_save, sp.  */
      this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
      this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
      /* Insn: fmov fs#, (##, sp), for each fs# to be saved.  */
      /* Insn: fmov fs#, (##, sp), for each fs# to be saved.  */
      this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
      this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
      if (size)
      if (size)
        {
        {
          /* Insn: add -size, sp.  */
          /* Insn: add -size, sp.  */
          this_strategy_size += SIZE_ADD_SP (-size);
          this_strategy_size += SIZE_ADD_SP (-size);
        }
        }
 
 
      if (this_strategy_size < strategy_size)
      if (this_strategy_size < strategy_size)
        {
        {
          strategy = save_sp_no_merge;
          strategy = save_sp_no_merge;
          strategy_size = this_strategy_size;
          strategy_size = this_strategy_size;
        }
        }
 
 
      /* Consider alternative save_sp_partial_merge only if we don't
      /* Consider alternative save_sp_partial_merge only if we don't
         need a frame pointer and size is reasonably large.  */
         need a frame pointer and size is reasonably large.  */
      if (! frame_pointer_needed && size + 4 * num_regs_to_save > 128)
      if (! frame_pointer_needed && size + 4 * num_regs_to_save > 128)
        {
        {
          /* Insn: add -128, sp.  */
          /* Insn: add -128, sp.  */
          this_strategy_size = SIZE_ADD_SP (-128);
          this_strategy_size = SIZE_ADD_SP (-128);
          /* Insn: fmov fs#, (##, sp), for each fs# to be saved.  */
          /* Insn: fmov fs#, (##, sp), for each fs# to be saved.  */
          this_strategy_size += SIZE_FMOV_SP (128 - 4 * num_regs_to_save,
          this_strategy_size += SIZE_FMOV_SP (128 - 4 * num_regs_to_save,
                                              num_regs_to_save);
                                              num_regs_to_save);
          if (size)
          if (size)
            {
            {
              /* Insn: add 128-size, sp.  */
              /* Insn: add 128-size, sp.  */
              this_strategy_size += SIZE_ADD_SP (128 - size);
              this_strategy_size += SIZE_ADD_SP (128 - size);
            }
            }
 
 
          if (this_strategy_size < strategy_size)
          if (this_strategy_size < strategy_size)
            {
            {
              strategy = save_sp_partial_merge;
              strategy = save_sp_partial_merge;
              strategy_size = this_strategy_size;
              strategy_size = this_strategy_size;
            }
            }
        }
        }
 
 
      /* Consider alternative save_a0_merge only if we don't need a
      /* Consider alternative save_a0_merge only if we don't need a
         frame pointer, size is nonzero and the user hasn't
         frame pointer, size is nonzero and the user hasn't
         changed the calling conventions of a0.  */
         changed the calling conventions of a0.  */
      if (! frame_pointer_needed && size
      if (! frame_pointer_needed && size
          && call_really_used_regs [FIRST_ADDRESS_REGNUM]
          && call_really_used_regs [FIRST_ADDRESS_REGNUM]
          && ! fixed_regs[FIRST_ADDRESS_REGNUM])
          && ! fixed_regs[FIRST_ADDRESS_REGNUM])
        {
        {
          /* Insn: add -(size + 4 * num_regs_to_save), sp.  */
          /* Insn: add -(size + 4 * num_regs_to_save), sp.  */
          this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
          this_strategy_size = SIZE_ADD_SP (-(size + 4 * num_regs_to_save));
          /* Insn: mov sp, a0.  */
          /* Insn: mov sp, a0.  */
          this_strategy_size++;
          this_strategy_size++;
          if (size)
          if (size)
            {
            {
              /* Insn: add size, a0.  */
              /* Insn: add size, a0.  */
              this_strategy_size += SIZE_ADD_AX (size);
              this_strategy_size += SIZE_ADD_AX (size);
            }
            }
          /* Insn: fmov fs#, (a0+), for each fs# to be saved.  */
          /* Insn: fmov fs#, (a0+), for each fs# to be saved.  */
          this_strategy_size += 3 * num_regs_to_save;
          this_strategy_size += 3 * num_regs_to_save;
 
 
          if (this_strategy_size < strategy_size)
          if (this_strategy_size < strategy_size)
            {
            {
              strategy = save_a0_merge;
              strategy = save_a0_merge;
              strategy_size = this_strategy_size;
              strategy_size = this_strategy_size;
            }
            }
        }
        }
 
 
      /* Consider alternative save_a0_no_merge if the user hasn't
      /* Consider alternative save_a0_no_merge if the user hasn't
         changed the calling conventions of a0.  */
         changed the calling conventions of a0.  */
      if (call_really_used_regs [FIRST_ADDRESS_REGNUM]
      if (call_really_used_regs [FIRST_ADDRESS_REGNUM]
          && ! fixed_regs[FIRST_ADDRESS_REGNUM])
          && ! fixed_regs[FIRST_ADDRESS_REGNUM])
        {
        {
          /* Insn: add -4 * num_regs_to_save, sp.  */
          /* Insn: add -4 * num_regs_to_save, sp.  */
          this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
          this_strategy_size = SIZE_ADD_SP (-4 * num_regs_to_save);
          /* Insn: mov sp, a0.  */
          /* Insn: mov sp, a0.  */
          this_strategy_size++;
          this_strategy_size++;
          /* Insn: fmov fs#, (a0+), for each fs# to be saved.  */
          /* Insn: fmov fs#, (a0+), for each fs# to be saved.  */
          this_strategy_size += 3 * num_regs_to_save;
          this_strategy_size += 3 * num_regs_to_save;
          if (size)
          if (size)
            {
            {
              /* Insn: add -size, sp.  */
              /* Insn: add -size, sp.  */
              this_strategy_size += SIZE_ADD_SP (-size);
              this_strategy_size += SIZE_ADD_SP (-size);
            }
            }
 
 
          if (this_strategy_size < strategy_size)
          if (this_strategy_size < strategy_size)
            {
            {
              strategy = save_a0_no_merge;
              strategy = save_a0_no_merge;
              strategy_size = this_strategy_size;
              strategy_size = this_strategy_size;
            }
            }
        }
        }
 
 
      /* Emit the initial SP add, common to all strategies.  */
      /* Emit the initial SP add, common to all strategies.  */
      switch (strategy)
      switch (strategy)
        {
        {
        case save_sp_no_merge:
        case save_sp_no_merge:
        case save_a0_no_merge:
        case save_a0_no_merge:
          emit_insn (gen_addsi3 (stack_pointer_rtx,
          emit_insn (gen_addsi3 (stack_pointer_rtx,
                                 stack_pointer_rtx,
                                 stack_pointer_rtx,
                                 GEN_INT (-4 * num_regs_to_save)));
                                 GEN_INT (-4 * num_regs_to_save)));
          xsize = 0;
          xsize = 0;
          break;
          break;
 
 
        case save_sp_partial_merge:
        case save_sp_partial_merge:
          emit_insn (gen_addsi3 (stack_pointer_rtx,
          emit_insn (gen_addsi3 (stack_pointer_rtx,
                                 stack_pointer_rtx,
                                 stack_pointer_rtx,
                                 GEN_INT (-128)));
                                 GEN_INT (-128)));
          xsize = 128 - 4 * num_regs_to_save;
          xsize = 128 - 4 * num_regs_to_save;
          size -= xsize;
          size -= xsize;
          break;
          break;
 
 
        case save_sp_merge:
        case save_sp_merge:
        case save_a0_merge:
        case save_a0_merge:
          emit_insn (gen_addsi3 (stack_pointer_rtx,
          emit_insn (gen_addsi3 (stack_pointer_rtx,
                                 stack_pointer_rtx,
                                 stack_pointer_rtx,
                                 GEN_INT (-(size + 4 * num_regs_to_save))));
                                 GEN_INT (-(size + 4 * num_regs_to_save))));
          /* We'll have to adjust FP register saves according to the
          /* We'll have to adjust FP register saves according to the
             frame size.  */
             frame size.  */
          xsize = size;
          xsize = size;
          /* Since we've already created the stack frame, don't do it
          /* Since we've already created the stack frame, don't do it
             again at the end of the function.  */
             again at the end of the function.  */
          size = 0;
          size = 0;
          break;
          break;
 
 
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
      /* Now prepare register a0, if we have decided to use it.  */
      /* Now prepare register a0, if we have decided to use it.  */
      switch (strategy)
      switch (strategy)
        {
        {
        case save_sp_merge:
        case save_sp_merge:
        case save_sp_no_merge:
        case save_sp_no_merge:
        case save_sp_partial_merge:
        case save_sp_partial_merge:
          reg = 0;
          reg = 0;
          break;
          break;
 
 
        case save_a0_merge:
        case save_a0_merge:
        case save_a0_no_merge:
        case save_a0_no_merge:
          reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM);
          reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM);
          emit_insn (gen_movsi (reg, stack_pointer_rtx));
          emit_insn (gen_movsi (reg, stack_pointer_rtx));
          if (xsize)
          if (xsize)
            emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
            emit_insn (gen_addsi3 (reg, reg, GEN_INT (xsize)));
          reg = gen_rtx_POST_INC (SImode, reg);
          reg = gen_rtx_POST_INC (SImode, reg);
          break;
          break;
 
 
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
      /* Now actually save the FP registers.  */
      /* Now actually save the FP registers.  */
      for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
      for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
        if (df_regs_ever_live_p (i) && ! call_really_used_regs [i])
        if (df_regs_ever_live_p (i) && ! call_really_used_regs [i])
          {
          {
            rtx addr;
            rtx addr;
 
 
            if (reg)
            if (reg)
              addr = reg;
              addr = reg;
            else
            else
              {
              {
                /* If we aren't using `a0', use an SP offset.  */
                /* If we aren't using `a0', use an SP offset.  */
                if (xsize)
                if (xsize)
                  {
                  {
                    addr = gen_rtx_PLUS (SImode,
                    addr = gen_rtx_PLUS (SImode,
                                         stack_pointer_rtx,
                                         stack_pointer_rtx,
                                         GEN_INT (xsize));
                                         GEN_INT (xsize));
                  }
                  }
                else
                else
                  addr = stack_pointer_rtx;
                  addr = stack_pointer_rtx;
 
 
                xsize += 4;
                xsize += 4;
              }
              }
 
 
            insn = emit_insn (gen_movsi (gen_rtx_MEM (SImode, addr),
            insn = emit_insn (gen_movsi (gen_rtx_MEM (SImode, addr),
                                         gen_rtx_REG (SImode, i)));
                                         gen_rtx_REG (SImode, i)));
 
 
            RTX_FRAME_RELATED_P (insn) = 1;
            RTX_FRAME_RELATED_P (insn) = 1;
          }
          }
    }
    }
 
 
  /* Now put the frame pointer into the frame pointer register.  */
  /* Now put the frame pointer into the frame pointer register.  */
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
    emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
 
 
  /* Allocate stack for this frame.  */
  /* Allocate stack for this frame.  */
  if (size)
  if (size)
    emit_insn (gen_addsi3 (stack_pointer_rtx,
    emit_insn (gen_addsi3 (stack_pointer_rtx,
                           stack_pointer_rtx,
                           stack_pointer_rtx,
                           GEN_INT (-size)));
                           GEN_INT (-size)));
  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
    emit_insn (gen_GOTaddr2picreg ());
    emit_insn (gen_GOTaddr2picreg ());
}
}
 
 
void
void
expand_epilogue (void)
expand_epilogue (void)
{
{
  HOST_WIDE_INT size;
  HOST_WIDE_INT size;
 
 
  /* SIZE includes the fixed stack space needed for function calls.  */
  /* SIZE includes the fixed stack space needed for function calls.  */
  size = get_frame_size () + crtl->outgoing_args_size;
  size = get_frame_size () + crtl->outgoing_args_size;
  size += (crtl->outgoing_args_size ? 4 : 0);
  size += (crtl->outgoing_args_size ? 4 : 0);
 
 
  if (TARGET_AM33_2 && fp_regs_to_save ())
  if (TARGET_AM33_2 && fp_regs_to_save ())
    {
    {
      int num_regs_to_save = fp_regs_to_save (), i;
      int num_regs_to_save = fp_regs_to_save (), i;
      rtx reg = 0;
      rtx reg = 0;
 
 
      /* We have several options to restore FP registers.  We could
      /* We have several options to restore FP registers.  We could
         load them from SP offsets, but, if there are enough FP
         load them from SP offsets, but, if there are enough FP
         registers to restore, we win if we use a post-increment
         registers to restore, we win if we use a post-increment
         addressing mode.  */
         addressing mode.  */
 
 
      /* If we have a frame pointer, it's the best option, because we
      /* If we have a frame pointer, it's the best option, because we
         already know it has the value we want.  */
         already know it has the value we want.  */
      if (frame_pointer_needed)
      if (frame_pointer_needed)
        reg = gen_rtx_REG (SImode, FRAME_POINTER_REGNUM);
        reg = gen_rtx_REG (SImode, FRAME_POINTER_REGNUM);
      /* Otherwise, we may use `a1', since it's call-clobbered and
      /* 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
         it's never used for return values.  But only do so if it's
         smaller than using SP offsets.  */
         smaller than using SP offsets.  */
      else
      else
        {
        {
          enum { restore_sp_post_adjust,
          enum { restore_sp_post_adjust,
                 restore_sp_pre_adjust,
                 restore_sp_pre_adjust,
                 restore_sp_partial_adjust,
                 restore_sp_partial_adjust,
                 restore_a1 } strategy;
                 restore_a1 } strategy;
          unsigned int this_strategy_size, strategy_size = (unsigned)-1;
          unsigned int this_strategy_size, strategy_size = (unsigned)-1;
 
 
          /* Consider using sp offsets before adjusting sp.  */
          /* Consider using sp offsets before adjusting sp.  */
          /* Insn: fmov (##,sp),fs#, for each fs# to be restored.  */
          /* Insn: fmov (##,sp),fs#, for each fs# to be restored.  */
          this_strategy_size = SIZE_FMOV_SP (size, num_regs_to_save);
          this_strategy_size = SIZE_FMOV_SP (size, num_regs_to_save);
          /* If size is too large, we'll have to adjust SP with an
          /* If size is too large, we'll have to adjust SP with an
                 add.  */
                 add.  */
          if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
          if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
            {
            {
              /* Insn: add size + 4 * num_regs_to_save, sp.  */
              /* Insn: add size + 4 * num_regs_to_save, sp.  */
              this_strategy_size += SIZE_ADD_SP (size + 4 * num_regs_to_save);
              this_strategy_size += SIZE_ADD_SP (size + 4 * num_regs_to_save);
            }
            }
          /* If we don't have to restore any non-FP registers,
          /* If we don't have to restore any non-FP registers,
                 we'll be able to save one byte by using rets.  */
                 we'll be able to save one byte by using rets.  */
          if (! REG_SAVE_BYTES)
          if (! REG_SAVE_BYTES)
            this_strategy_size--;
            this_strategy_size--;
 
 
          if (this_strategy_size < strategy_size)
          if (this_strategy_size < strategy_size)
            {
            {
              strategy = restore_sp_post_adjust;
              strategy = restore_sp_post_adjust;
              strategy_size = this_strategy_size;
              strategy_size = this_strategy_size;
            }
            }
 
 
          /* Consider using sp offsets after adjusting sp.  */
          /* Consider using sp offsets after adjusting sp.  */
          /* Insn: add size, sp.  */
          /* Insn: add size, sp.  */
          this_strategy_size = SIZE_ADD_SP (size);
          this_strategy_size = SIZE_ADD_SP (size);
          /* Insn: fmov (##,sp),fs#, for each fs# to be restored.  */
          /* Insn: fmov (##,sp),fs#, for each fs# to be restored.  */
          this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
          this_strategy_size += SIZE_FMOV_SP (0, num_regs_to_save);
          /* We're going to use ret to release the FP registers
          /* We're going to use ret to release the FP registers
                 save area, so, no savings.  */
                 save area, so, no savings.  */
 
 
          if (this_strategy_size < strategy_size)
          if (this_strategy_size < strategy_size)
            {
            {
              strategy = restore_sp_pre_adjust;
              strategy = restore_sp_pre_adjust;
              strategy_size = this_strategy_size;
              strategy_size = this_strategy_size;
            }
            }
 
 
          /* Consider using sp offsets after partially adjusting sp.
          /* Consider using sp offsets after partially adjusting sp.
             When size is close to 32Kb, we may be able to adjust SP
             When size is close to 32Kb, we may be able to adjust SP
             with an imm16 add instruction while still using fmov
             with an imm16 add instruction while still using fmov
             (d8,sp).  */
             (d8,sp).  */
          if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
          if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
            {
            {
              /* Insn: add size + 4 * num_regs_to_save
              /* Insn: add size + 4 * num_regs_to_save
                                + REG_SAVE_BYTES - 252,sp.  */
                                + REG_SAVE_BYTES - 252,sp.  */
              this_strategy_size = SIZE_ADD_SP (size + 4 * num_regs_to_save
              this_strategy_size = SIZE_ADD_SP (size + 4 * num_regs_to_save
                                                + REG_SAVE_BYTES - 252);
                                                + REG_SAVE_BYTES - 252);
              /* Insn: fmov (##,sp),fs#, fo each fs# to be restored.  */
              /* Insn: fmov (##,sp),fs#, fo each fs# to be restored.  */
              this_strategy_size += SIZE_FMOV_SP (252 - REG_SAVE_BYTES
              this_strategy_size += SIZE_FMOV_SP (252 - REG_SAVE_BYTES
                                                  - 4 * num_regs_to_save,
                                                  - 4 * num_regs_to_save,
                                                  num_regs_to_save);
                                                  num_regs_to_save);
              /* We're going to use ret to release the FP registers
              /* We're going to use ret to release the FP registers
                 save area, so, no savings.  */
                 save area, so, no savings.  */
 
 
              if (this_strategy_size < strategy_size)
              if (this_strategy_size < strategy_size)
                {
                {
                  strategy = restore_sp_partial_adjust;
                  strategy = restore_sp_partial_adjust;
                  strategy_size = this_strategy_size;
                  strategy_size = this_strategy_size;
                }
                }
            }
            }
 
 
          /* Consider using a1 in post-increment mode, as long as the
          /* Consider using a1 in post-increment mode, as long as the
             user hasn't changed the calling conventions of a1.  */
             user hasn't changed the calling conventions of a1.  */
          if (call_really_used_regs [FIRST_ADDRESS_REGNUM + 1]
          if (call_really_used_regs [FIRST_ADDRESS_REGNUM + 1]
              && ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
              && ! fixed_regs[FIRST_ADDRESS_REGNUM+1])
            {
            {
              /* Insn: mov sp,a1.  */
              /* Insn: mov sp,a1.  */
              this_strategy_size = 1;
              this_strategy_size = 1;
              if (size)
              if (size)
                {
                {
                  /* Insn: add size,a1.  */
                  /* Insn: add size,a1.  */
                  this_strategy_size += SIZE_ADD_AX (size);
                  this_strategy_size += SIZE_ADD_AX (size);
                }
                }
              /* Insn: fmov (a1+),fs#, for each fs# to be restored.  */
              /* Insn: fmov (a1+),fs#, for each fs# to be restored.  */
              this_strategy_size += 3 * num_regs_to_save;
              this_strategy_size += 3 * num_regs_to_save;
              /* If size is large enough, we may be able to save a
              /* If size is large enough, we may be able to save a
                 couple of bytes.  */
                 couple of bytes.  */
              if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
              if (size + 4 * num_regs_to_save + REG_SAVE_BYTES > 255)
                {
                {
                  /* Insn: mov a1,sp.  */
                  /* Insn: mov a1,sp.  */
                  this_strategy_size += 2;
                  this_strategy_size += 2;
                }
                }
              /* If we don't have to restore any non-FP registers,
              /* If we don't have to restore any non-FP registers,
                 we'll be able to save one byte by using rets.  */
                 we'll be able to save one byte by using rets.  */
              if (! REG_SAVE_BYTES)
              if (! REG_SAVE_BYTES)
                this_strategy_size--;
                this_strategy_size--;
 
 
              if (this_strategy_size < strategy_size)
              if (this_strategy_size < strategy_size)
                {
                {
                  strategy = restore_a1;
                  strategy = restore_a1;
                  strategy_size = this_strategy_size;
                  strategy_size = this_strategy_size;
                }
                }
            }
            }
 
 
          switch (strategy)
          switch (strategy)
            {
            {
            case restore_sp_post_adjust:
            case restore_sp_post_adjust:
              break;
              break;
 
 
            case restore_sp_pre_adjust:
            case restore_sp_pre_adjust:
              emit_insn (gen_addsi3 (stack_pointer_rtx,
              emit_insn (gen_addsi3 (stack_pointer_rtx,
                                     stack_pointer_rtx,
                                     stack_pointer_rtx,
                                     GEN_INT (size)));
                                     GEN_INT (size)));
              size = 0;
              size = 0;
              break;
              break;
 
 
            case restore_sp_partial_adjust:
            case restore_sp_partial_adjust:
              emit_insn (gen_addsi3 (stack_pointer_rtx,
              emit_insn (gen_addsi3 (stack_pointer_rtx,
                                     stack_pointer_rtx,
                                     stack_pointer_rtx,
                                     GEN_INT (size + 4 * num_regs_to_save
                                     GEN_INT (size + 4 * num_regs_to_save
                                              + REG_SAVE_BYTES - 252)));
                                              + REG_SAVE_BYTES - 252)));
              size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save;
              size = 252 - REG_SAVE_BYTES - 4 * num_regs_to_save;
              break;
              break;
 
 
            case restore_a1:
            case restore_a1:
              reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1);
              reg = gen_rtx_REG (SImode, FIRST_ADDRESS_REGNUM + 1);
              emit_insn (gen_movsi (reg, stack_pointer_rtx));
              emit_insn (gen_movsi (reg, stack_pointer_rtx));
              if (size)
              if (size)
                emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
                emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
              break;
              break;
 
 
            default:
            default:
              gcc_unreachable ();
              gcc_unreachable ();
            }
            }
        }
        }
 
 
      /* Adjust the selected register, if any, for post-increment.  */
      /* Adjust the selected register, if any, for post-increment.  */
      if (reg)
      if (reg)
        reg = gen_rtx_POST_INC (SImode, reg);
        reg = gen_rtx_POST_INC (SImode, reg);
 
 
      for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
      for (i = FIRST_FP_REGNUM; i <= LAST_FP_REGNUM; ++i)
        if (df_regs_ever_live_p (i) && ! call_really_used_regs [i])
        if (df_regs_ever_live_p (i) && ! call_really_used_regs [i])
          {
          {
            rtx addr;
            rtx addr;
 
 
            if (reg)
            if (reg)
              addr = reg;
              addr = reg;
            else if (size)
            else if (size)
              {
              {
                /* If we aren't using a post-increment register, use an
                /* If we aren't using a post-increment register, use an
                   SP offset.  */
                   SP offset.  */
                addr = gen_rtx_PLUS (SImode,
                addr = gen_rtx_PLUS (SImode,
                                     stack_pointer_rtx,
                                     stack_pointer_rtx,
                                     GEN_INT (size));
                                     GEN_INT (size));
              }
              }
            else
            else
              addr = stack_pointer_rtx;
              addr = stack_pointer_rtx;
 
 
            size += 4;
            size += 4;
 
 
            emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
            emit_insn (gen_movsi (gen_rtx_REG (SImode, i),
                                  gen_rtx_MEM (SImode, addr)));
                                  gen_rtx_MEM (SImode, addr)));
          }
          }
 
 
      /* If we were using the restore_a1 strategy and the number of
      /* 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'
         bytes to be released won't fit in the `ret' byte, copy `a1'
         to `sp', to avoid having to use `add' to adjust it.  */
         to `sp', to avoid having to use `add' to adjust it.  */
      if (! frame_pointer_needed && reg && size + REG_SAVE_BYTES > 255)
      if (! frame_pointer_needed && reg && size + REG_SAVE_BYTES > 255)
        {
        {
          emit_move_insn (stack_pointer_rtx, XEXP (reg, 0));
          emit_move_insn (stack_pointer_rtx, XEXP (reg, 0));
          size = 0;
          size = 0;
        }
        }
    }
    }
 
 
  /* Maybe cut back the stack, except for the register save area.
  /* Maybe cut back the stack, except for the register save area.
 
 
     If the frame pointer exists, then use the frame pointer to
     If the frame pointer exists, then use the frame pointer to
     cut back the stack.
     cut back the stack.
 
 
     If the stack size + register save area is more than 255 bytes,
     If the stack size + register save area is more than 255 bytes,
     then the stack must be cut back here since the size + register
     then the stack must be cut back here since the size + register
     save size is too big for a ret/retf instruction.
     save size is too big for a ret/retf instruction.
 
 
     Else leave it alone, it will be cut back as part of the
     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.
     ret/retf instruction, or there wasn't any stack to begin with.
 
 
     Under no circumstances should the register save area be
     Under no circumstances should the register save area be
     deallocated here, that would leave a window where an interrupt
     deallocated here, that would leave a window where an interrupt
     could occur and trash the register save area.  */
     could occur and trash the register save area.  */
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    {
    {
      emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
      emit_move_insn (stack_pointer_rtx, frame_pointer_rtx);
      size = 0;
      size = 0;
    }
    }
  else if (size + REG_SAVE_BYTES > 255)
  else if (size + REG_SAVE_BYTES > 255)
    {
    {
      emit_insn (gen_addsi3 (stack_pointer_rtx,
      emit_insn (gen_addsi3 (stack_pointer_rtx,
                             stack_pointer_rtx,
                             stack_pointer_rtx,
                             GEN_INT (size)));
                             GEN_INT (size)));
      size = 0;
      size = 0;
    }
    }
 
 
  /* Adjust the stack and restore callee-saved registers, if any.  */
  /* Adjust the stack and restore callee-saved registers, if any.  */
  if (size || df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
  if (size || df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
      || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
      || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
      || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
      || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
      || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
      || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
      || frame_pointer_needed)
      || frame_pointer_needed)
    emit_jump_insn (gen_return_internal_regs
    emit_jump_insn (gen_return_internal_regs
                    (GEN_INT (size + REG_SAVE_BYTES)));
                    (GEN_INT (size + REG_SAVE_BYTES)));
  else
  else
    emit_jump_insn (gen_return_internal ());
    emit_jump_insn (gen_return_internal ());
}
}
 
 
/* Update the condition code from the insn.  */
/* Update the condition code from the insn.  */
 
 
void
void
notice_update_cc (rtx body, rtx insn)
notice_update_cc (rtx body, rtx insn)
{
{
  switch (get_attr_cc (insn))
  switch (get_attr_cc (insn))
    {
    {
    case CC_NONE:
    case CC_NONE:
      /* Insn does not affect CC at all.  */
      /* Insn does not affect CC at all.  */
      break;
      break;
 
 
    case CC_NONE_0HIT:
    case CC_NONE_0HIT:
      /* Insn does not change CC, but the 0'th operand has been changed.  */
      /* Insn does not change CC, but the 0'th operand has been changed.  */
      if (cc_status.value1 != 0
      if (cc_status.value1 != 0
          && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
          && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
        cc_status.value1 = 0;
        cc_status.value1 = 0;
      break;
      break;
 
 
    case CC_SET_ZN:
    case CC_SET_ZN:
      /* Insn sets the Z,N flags of CC to recog_data.operand[0].
      /* Insn sets the Z,N flags of CC to recog_data.operand[0].
         V,C are unusable.  */
         V,C are unusable.  */
      CC_STATUS_INIT;
      CC_STATUS_INIT;
      cc_status.flags |= CC_NO_CARRY | CC_OVERFLOW_UNUSABLE;
      cc_status.flags |= CC_NO_CARRY | CC_OVERFLOW_UNUSABLE;
      cc_status.value1 = recog_data.operand[0];
      cc_status.value1 = recog_data.operand[0];
      break;
      break;
 
 
    case CC_SET_ZNV:
    case CC_SET_ZNV:
      /* Insn sets the Z,N,V flags of CC to recog_data.operand[0].
      /* Insn sets the Z,N,V flags of CC to recog_data.operand[0].
         C is unusable.  */
         C is unusable.  */
      CC_STATUS_INIT;
      CC_STATUS_INIT;
      cc_status.flags |= CC_NO_CARRY;
      cc_status.flags |= CC_NO_CARRY;
      cc_status.value1 = recog_data.operand[0];
      cc_status.value1 = recog_data.operand[0];
      break;
      break;
 
 
    case CC_COMPARE:
    case CC_COMPARE:
      /* The insn is a compare instruction.  */
      /* The insn is a compare instruction.  */
      CC_STATUS_INIT;
      CC_STATUS_INIT;
      cc_status.value1 = SET_SRC (body);
      cc_status.value1 = SET_SRC (body);
      if (GET_CODE (cc_status.value1) == COMPARE
      if (GET_CODE (cc_status.value1) == COMPARE
          && GET_MODE (XEXP (cc_status.value1, 0)) == SFmode)
          && GET_MODE (XEXP (cc_status.value1, 0)) == SFmode)
        cc_status.mdep.fpCC = 1;
        cc_status.mdep.fpCC = 1;
      break;
      break;
 
 
    case CC_CLOBBER:
    case CC_CLOBBER:
      /* Insn doesn't leave CC in a usable state.  */
      /* Insn doesn't leave CC in a usable state.  */
      CC_STATUS_INIT;
      CC_STATUS_INIT;
      break;
      break;
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
}
}
 
 
/* Recognize the PARALLEL rtx generated by mn10300_gen_multiple_store().
/* Recognize the PARALLEL rtx generated by mn10300_gen_multiple_store().
   This function is for MATCH_PARALLEL and so assumes OP is known to be
   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
   parallel.  If OP is a multiple store, return a mask indicating which
   registers it saves.  Return 0 otherwise.  */
   registers it saves.  Return 0 otherwise.  */
 
 
int
int
store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  int count;
  int count;
  int mask;
  int mask;
  int i;
  int i;
  unsigned int last;
  unsigned int last;
  rtx elt;
  rtx elt;
 
 
  count = XVECLEN (op, 0);
  count = XVECLEN (op, 0);
  if (count < 2)
  if (count < 2)
    return 0;
    return 0;
 
 
  /* Check that first instruction has the form (set (sp) (plus A B)) */
  /* Check that first instruction has the form (set (sp) (plus A B)) */
  elt = XVECEXP (op, 0, 0);
  elt = XVECEXP (op, 0, 0);
  if (GET_CODE (elt) != SET
  if (GET_CODE (elt) != SET
      || GET_CODE (SET_DEST (elt)) != REG
      || GET_CODE (SET_DEST (elt)) != REG
      || REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
      || REGNO (SET_DEST (elt)) != STACK_POINTER_REGNUM
      || GET_CODE (SET_SRC (elt)) != PLUS)
      || GET_CODE (SET_SRC (elt)) != PLUS)
    return 0;
    return 0;
 
 
  /* Check that A is the stack pointer and B is the expected stack size.
  /* 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
     For OP to match, each subsequent instruction should push a word onto
     the stack.  We therefore expect the first instruction to create
     the stack.  We therefore expect the first instruction to create
     COUNT-1 stack slots.  */
     COUNT-1 stack slots.  */
  elt = SET_SRC (elt);
  elt = SET_SRC (elt);
  if (GET_CODE (XEXP (elt, 0)) != REG
  if (GET_CODE (XEXP (elt, 0)) != REG
      || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
      || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
      || GET_CODE (XEXP (elt, 1)) != CONST_INT
      || GET_CODE (XEXP (elt, 1)) != CONST_INT
      || INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
      || INTVAL (XEXP (elt, 1)) != -(count - 1) * 4)
    return 0;
    return 0;
 
 
  /* Now go through the rest of the vector elements.  They must be
  /* Now go through the rest of the vector elements.  They must be
     ordered so that the first instruction stores the highest-numbered
     ordered so that the first instruction stores the highest-numbered
     register to the highest stack slot and that subsequent instructions
     register to the highest stack slot and that subsequent instructions
     store a lower-numbered register to the slot below.
     store a lower-numbered register to the slot below.
 
 
     LAST keeps track of the smallest-numbered register stored so far.
     LAST keeps track of the smallest-numbered register stored so far.
     MASK is the set of stored registers.  */
     MASK is the set of stored registers.  */
  last = LAST_EXTENDED_REGNUM + 1;
  last = LAST_EXTENDED_REGNUM + 1;
  mask = 0;
  mask = 0;
  for (i = 1; i < count; i++)
  for (i = 1; i < count; i++)
    {
    {
      /* Check that element i is a (set (mem M) R) and that R is valid.  */
      /* Check that element i is a (set (mem M) R) and that R is valid.  */
      elt = XVECEXP (op, 0, i);
      elt = XVECEXP (op, 0, i);
      if (GET_CODE (elt) != SET
      if (GET_CODE (elt) != SET
          || GET_CODE (SET_DEST (elt)) != MEM
          || GET_CODE (SET_DEST (elt)) != MEM
          || GET_CODE (SET_SRC (elt)) != REG
          || GET_CODE (SET_SRC (elt)) != REG
          || REGNO (SET_SRC (elt)) >= last)
          || REGNO (SET_SRC (elt)) >= last)
        return 0;
        return 0;
 
 
      /* R was OK, so provisionally add it to MASK.  We return 0 in any
      /* R was OK, so provisionally add it to MASK.  We return 0 in any
         case if the rest of the instruction has a flaw.  */
         case if the rest of the instruction has a flaw.  */
      last = REGNO (SET_SRC (elt));
      last = REGNO (SET_SRC (elt));
      mask |= (1 << last);
      mask |= (1 << last);
 
 
      /* Check that M has the form (plus (sp) (const_int -I*4)) */
      /* Check that M has the form (plus (sp) (const_int -I*4)) */
      elt = XEXP (SET_DEST (elt), 0);
      elt = XEXP (SET_DEST (elt), 0);
      if (GET_CODE (elt) != PLUS
      if (GET_CODE (elt) != PLUS
          || GET_CODE (XEXP (elt, 0)) != REG
          || GET_CODE (XEXP (elt, 0)) != REG
          || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
          || REGNO (XEXP (elt, 0)) != STACK_POINTER_REGNUM
          || GET_CODE (XEXP (elt, 1)) != CONST_INT
          || GET_CODE (XEXP (elt, 1)) != CONST_INT
          || INTVAL (XEXP (elt, 1)) != -i * 4)
          || INTVAL (XEXP (elt, 1)) != -i * 4)
        return 0;
        return 0;
    }
    }
 
 
  /* All or none of the callee-saved extended registers must be in the set.  */
  /* All or none of the callee-saved extended registers must be in the set.  */
  if ((mask & 0x3c000) != 0
  if ((mask & 0x3c000) != 0
      && (mask & 0x3c000) != 0x3c000)
      && (mask & 0x3c000) != 0x3c000)
    return 0;
    return 0;
 
 
  return mask;
  return mask;
}
}
 
 
/* What (if any) secondary registers are needed to move IN with mode
/* What (if any) secondary registers are needed to move IN with mode
   MODE into a register in register class RCLASS.
   MODE into a register in register class RCLASS.
 
 
   We might be able to simplify this.  */
   We might be able to simplify this.  */
enum reg_class
enum reg_class
mn10300_secondary_reload_class (enum reg_class rclass, enum machine_mode mode,
mn10300_secondary_reload_class (enum reg_class rclass, enum machine_mode mode,
                                rtx in)
                                rtx in)
{
{
  rtx inner = in;
  rtx inner = in;
 
 
  /* Strip off any SUBREG expressions from IN.  Basically we want
  /* Strip off any SUBREG expressions from IN.  Basically we want
     to know if IN is a pseudo or (subreg (pseudo)) as those can
     to know if IN is a pseudo or (subreg (pseudo)) as those can
     turn into MEMs during reload.  */
     turn into MEMs during reload.  */
  while (GET_CODE (inner) == SUBREG)
  while (GET_CODE (inner) == SUBREG)
    inner = SUBREG_REG (inner);
    inner = SUBREG_REG (inner);
 
 
  /* Memory loads less than a full word wide can't have an
  /* Memory loads less than a full word wide can't have an
     address or stack pointer destination.  They must use
     address or stack pointer destination.  They must use
     a data register as an intermediate register.  */
     a data register as an intermediate register.  */
  if ((GET_CODE (in) == MEM
  if ((GET_CODE (in) == MEM
       || (GET_CODE (inner) == REG
       || (GET_CODE (inner) == REG
           && REGNO (inner) >= FIRST_PSEUDO_REGISTER))
           && REGNO (inner) >= FIRST_PSEUDO_REGISTER))
      && (mode == QImode || mode == HImode)
      && (mode == QImode || mode == HImode)
      && (rclass == ADDRESS_REGS || rclass == SP_REGS
      && (rclass == ADDRESS_REGS || rclass == SP_REGS
          || rclass == SP_OR_ADDRESS_REGS))
          || rclass == SP_OR_ADDRESS_REGS))
    {
    {
      if (TARGET_AM33)
      if (TARGET_AM33)
        return DATA_OR_EXTENDED_REGS;
        return DATA_OR_EXTENDED_REGS;
      return DATA_REGS;
      return DATA_REGS;
    }
    }
 
 
  /* We can't directly load sp + const_int into a data register;
  /* We can't directly load sp + const_int into a data register;
     we must use an address register as an intermediate.  */
     we must use an address register as an intermediate.  */
  if (rclass != SP_REGS
  if (rclass != SP_REGS
      && rclass != ADDRESS_REGS
      && rclass != ADDRESS_REGS
      && rclass != SP_OR_ADDRESS_REGS
      && rclass != SP_OR_ADDRESS_REGS
      && rclass != SP_OR_EXTENDED_REGS
      && rclass != SP_OR_EXTENDED_REGS
      && rclass != ADDRESS_OR_EXTENDED_REGS
      && rclass != ADDRESS_OR_EXTENDED_REGS
      && rclass != SP_OR_ADDRESS_OR_EXTENDED_REGS
      && rclass != SP_OR_ADDRESS_OR_EXTENDED_REGS
      && (in == stack_pointer_rtx
      && (in == stack_pointer_rtx
          || (GET_CODE (in) == PLUS
          || (GET_CODE (in) == PLUS
              && (XEXP (in, 0) == stack_pointer_rtx
              && (XEXP (in, 0) == stack_pointer_rtx
                  || XEXP (in, 1) == stack_pointer_rtx))))
                  || XEXP (in, 1) == stack_pointer_rtx))))
    return ADDRESS_REGS;
    return ADDRESS_REGS;
 
 
  if (GET_CODE (in) == PLUS
  if (GET_CODE (in) == PLUS
      && (XEXP (in, 0) == stack_pointer_rtx
      && (XEXP (in, 0) == stack_pointer_rtx
          || XEXP (in, 1) == stack_pointer_rtx))
          || XEXP (in, 1) == stack_pointer_rtx))
    return GENERAL_REGS;
    return GENERAL_REGS;
 
 
  if (TARGET_AM33_2
  if (TARGET_AM33_2
      && rclass == FP_REGS)
      && rclass == FP_REGS)
    {
    {
      /* We can't load directly into an FP register from a
      /* We can't load directly into an FP register from a
         constant address.  */
         constant address.  */
      if (GET_CODE (in) == MEM
      if (GET_CODE (in) == MEM
          && CONSTANT_ADDRESS_P (XEXP (in, 0)))
          && CONSTANT_ADDRESS_P (XEXP (in, 0)))
        return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS);
        return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS);
 
 
      /* Handle case were a pseudo may not get a hard register
      /* Handle case were a pseudo may not get a hard register
         but has an equivalent memory location defined.  */
         but has an equivalent memory location defined.  */
      if (GET_CODE (inner) == REG
      if (GET_CODE (inner) == REG
          && REGNO (inner) >= FIRST_PSEUDO_REGISTER
          && REGNO (inner) >= FIRST_PSEUDO_REGISTER
          && reg_equiv_mem [REGNO (inner)]
          && reg_equiv_mem [REGNO (inner)]
          && CONSTANT_ADDRESS_P (XEXP (reg_equiv_mem [REGNO (inner)], 0)))
          && CONSTANT_ADDRESS_P (XEXP (reg_equiv_mem [REGNO (inner)], 0)))
        return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS);
        return (TARGET_AM33 ? DATA_OR_EXTENDED_REGS : DATA_REGS);
    }
    }
 
 
  /* Otherwise assume no secondary reloads are needed.  */
  /* Otherwise assume no secondary reloads are needed.  */
  return NO_REGS;
  return NO_REGS;
}
}
 
 
int
int
initial_offset (int from, int to)
initial_offset (int from, int to)
{
{
  /* The difference between the argument pointer and the frame pointer
  /* The difference between the argument pointer and the frame pointer
     is the size of the callee register save area.  */
     is the size of the callee register save area.  */
  if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
  if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
    {
    {
      if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
      if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
          || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
          || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
          || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
          || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
          || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
          || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
          || fp_regs_to_save ()
          || fp_regs_to_save ()
          || frame_pointer_needed)
          || frame_pointer_needed)
        return REG_SAVE_BYTES
        return REG_SAVE_BYTES
          + 4 * fp_regs_to_save ();
          + 4 * fp_regs_to_save ();
      else
      else
        return 0;
        return 0;
    }
    }
 
 
  /* The difference between the argument pointer and the stack pointer is
  /* The difference between the argument pointer and the stack pointer is
     the sum of the size of this function's frame, the callee register save
     the sum of the size of this function's frame, the callee register save
     area, and the fixed stack space needed for function calls (if any).  */
     area, and the fixed stack space needed for function calls (if any).  */
  if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    {
    {
      if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
      if (df_regs_ever_live_p (2) || df_regs_ever_live_p (3)
          || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
          || df_regs_ever_live_p (6) || df_regs_ever_live_p (7)
          || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
          || df_regs_ever_live_p (14) || df_regs_ever_live_p (15)
          || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
          || df_regs_ever_live_p (16) || df_regs_ever_live_p (17)
          || fp_regs_to_save ()
          || fp_regs_to_save ()
          || frame_pointer_needed)
          || frame_pointer_needed)
        return (get_frame_size () + REG_SAVE_BYTES
        return (get_frame_size () + REG_SAVE_BYTES
                + 4 * fp_regs_to_save ()
                + 4 * fp_regs_to_save ()
                + (crtl->outgoing_args_size
                + (crtl->outgoing_args_size
                   ? crtl->outgoing_args_size + 4 : 0));
                   ? crtl->outgoing_args_size + 4 : 0));
      else
      else
        return (get_frame_size ()
        return (get_frame_size ()
                + (crtl->outgoing_args_size
                + (crtl->outgoing_args_size
                   ? crtl->outgoing_args_size + 4 : 0));
                   ? crtl->outgoing_args_size + 4 : 0));
    }
    }
 
 
  /* The difference between the frame pointer and stack pointer is the sum
  /* The difference between the frame pointer and stack pointer is the sum
     of the size of this function's frame and the fixed stack space needed
     of the size of this function's frame and the fixed stack space needed
     for function calls (if any).  */
     for function calls (if any).  */
  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
    return (get_frame_size ()
    return (get_frame_size ()
            + (crtl->outgoing_args_size
            + (crtl->outgoing_args_size
               ? crtl->outgoing_args_size + 4 : 0));
               ? crtl->outgoing_args_size + 4 : 0));
 
 
  gcc_unreachable ();
  gcc_unreachable ();
}
}
 
 
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
 
 
static bool
static bool
mn10300_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
mn10300_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
{
  /* Return values > 8 bytes in length in memory.  */
  /* Return values > 8 bytes in length in memory.  */
  return (int_size_in_bytes (type) > 8
  return (int_size_in_bytes (type) > 8
          || int_size_in_bytes (type) == 0
          || int_size_in_bytes (type) == 0
          || TYPE_MODE (type) == BLKmode);
          || TYPE_MODE (type) == BLKmode);
}
}
 
 
/* Flush the argument registers to the stack for a stdarg function;
/* Flush the argument registers to the stack for a stdarg function;
   return the new argument pointer.  */
   return the new argument pointer.  */
static rtx
static rtx
mn10300_builtin_saveregs (void)
mn10300_builtin_saveregs (void)
{
{
  rtx offset, mem;
  rtx offset, mem;
  tree fntype = TREE_TYPE (current_function_decl);
  tree fntype = TREE_TYPE (current_function_decl);
  int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
  int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0
                   && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
                   && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
                       != void_type_node)))
                       != void_type_node)))
                ? UNITS_PER_WORD : 0);
                ? UNITS_PER_WORD : 0);
  alias_set_type set = get_varargs_alias_set ();
  alias_set_type set = get_varargs_alias_set ();
 
 
  if (argadj)
  if (argadj)
    offset = plus_constant (crtl->args.arg_offset_rtx, argadj);
    offset = plus_constant (crtl->args.arg_offset_rtx, argadj);
  else
  else
    offset = crtl->args.arg_offset_rtx;
    offset = crtl->args.arg_offset_rtx;
 
 
  mem = gen_rtx_MEM (SImode, crtl->args.internal_arg_pointer);
  mem = gen_rtx_MEM (SImode, crtl->args.internal_arg_pointer);
  set_mem_alias_set (mem, set);
  set_mem_alias_set (mem, set);
  emit_move_insn (mem, gen_rtx_REG (SImode, 0));
  emit_move_insn (mem, gen_rtx_REG (SImode, 0));
 
 
  mem = gen_rtx_MEM (SImode,
  mem = gen_rtx_MEM (SImode,
                     plus_constant (crtl->args.internal_arg_pointer, 4));
                     plus_constant (crtl->args.internal_arg_pointer, 4));
  set_mem_alias_set (mem, set);
  set_mem_alias_set (mem, set);
  emit_move_insn (mem, gen_rtx_REG (SImode, 1));
  emit_move_insn (mem, gen_rtx_REG (SImode, 1));
 
 
  return copy_to_reg (expand_binop (Pmode, add_optab,
  return copy_to_reg (expand_binop (Pmode, add_optab,
                                    crtl->args.internal_arg_pointer,
                                    crtl->args.internal_arg_pointer,
                                    offset, 0, 0, OPTAB_LIB_WIDEN));
                                    offset, 0, 0, OPTAB_LIB_WIDEN));
}
}
 
 
static void
static void
mn10300_va_start (tree valist, rtx nextarg)
mn10300_va_start (tree valist, rtx nextarg)
{
{
  nextarg = expand_builtin_saveregs ();
  nextarg = expand_builtin_saveregs ();
  std_expand_builtin_va_start (valist, nextarg);
  std_expand_builtin_va_start (valist, nextarg);
}
}
 
 
/* Return true when a parameter should be passed by reference.  */
/* Return true when a parameter should be passed by reference.  */
 
 
static bool
static bool
mn10300_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
mn10300_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
                           enum machine_mode mode, const_tree type,
                           enum machine_mode mode, const_tree type,
                           bool named ATTRIBUTE_UNUSED)
                           bool named ATTRIBUTE_UNUSED)
{
{
  unsigned HOST_WIDE_INT size;
  unsigned HOST_WIDE_INT size;
 
 
  if (type)
  if (type)
    size = int_size_in_bytes (type);
    size = int_size_in_bytes (type);
  else
  else
    size = GET_MODE_SIZE (mode);
    size = GET_MODE_SIZE (mode);
 
 
  return (size > 8 || size == 0);
  return (size > 8 || size == 0);
}
}
 
 
/* Return an RTX to represent where a value with mode MODE will be returned
/* 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.  */
   from a function.  If the result is NULL_RTX, the argument is pushed.  */
 
 
rtx
rtx
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
              tree type, int named ATTRIBUTE_UNUSED)
              tree type, int named ATTRIBUTE_UNUSED)
{
{
  rtx result = NULL_RTX;
  rtx result = NULL_RTX;
  int size, align;
  int size, align;
 
 
  /* We only support using 2 data registers as argument registers.  */
  /* We only support using 2 data registers as argument registers.  */
  int nregs = 2;
  int nregs = 2;
 
 
  /* Figure out the size of the object to be passed.  */
  /* Figure out the size of the object to be passed.  */
  if (mode == BLKmode)
  if (mode == BLKmode)
    size = int_size_in_bytes (type);
    size = int_size_in_bytes (type);
  else
  else
    size = GET_MODE_SIZE (mode);
    size = GET_MODE_SIZE (mode);
 
 
  /* Figure out the alignment of the object to be passed.  */
  /* Figure out the alignment of the object to be passed.  */
  align = size;
  align = size;
 
 
  cum->nbytes = (cum->nbytes + 3) & ~3;
  cum->nbytes = (cum->nbytes + 3) & ~3;
 
 
  /* Don't pass this arg via a register if all the argument registers
  /* Don't pass this arg via a register if all the argument registers
     are used up.  */
     are used up.  */
  if (cum->nbytes > nregs * UNITS_PER_WORD)
  if (cum->nbytes > nregs * UNITS_PER_WORD)
    return result;
    return result;
 
 
  /* Don't pass this arg via a register if it would be split between
  /* Don't pass this arg via a register if it would be split between
     registers and memory.  */
     registers and memory.  */
  if (type == NULL_TREE
  if (type == NULL_TREE
      && cum->nbytes + size > nregs * UNITS_PER_WORD)
      && cum->nbytes + size > nregs * UNITS_PER_WORD)
    return result;
    return result;
 
 
  switch (cum->nbytes / UNITS_PER_WORD)
  switch (cum->nbytes / UNITS_PER_WORD)
    {
    {
    case 0:
    case 0:
      result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM);
      result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM);
      break;
      break;
    case 1:
    case 1:
      result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM + 1);
      result = gen_rtx_REG (mode, FIRST_ARGUMENT_REGNUM + 1);
      break;
      break;
    default:
    default:
      break;
      break;
    }
    }
 
 
  return result;
  return result;
}
}
 
 
/* Return the number of bytes of registers to use for an argument passed
/* Return the number of bytes of registers to use for an argument passed
   partially in registers and partially in memory.  */
   partially in registers and partially in memory.  */
 
 
static int
static int
mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
mn10300_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                           tree type, bool named ATTRIBUTE_UNUSED)
                           tree type, bool named ATTRIBUTE_UNUSED)
{
{
  int size, align;
  int size, align;
 
 
  /* We only support using 2 data registers as argument registers.  */
  /* We only support using 2 data registers as argument registers.  */
  int nregs = 2;
  int nregs = 2;
 
 
  /* Figure out the size of the object to be passed.  */
  /* Figure out the size of the object to be passed.  */
  if (mode == BLKmode)
  if (mode == BLKmode)
    size = int_size_in_bytes (type);
    size = int_size_in_bytes (type);
  else
  else
    size = GET_MODE_SIZE (mode);
    size = GET_MODE_SIZE (mode);
 
 
  /* Figure out the alignment of the object to be passed.  */
  /* Figure out the alignment of the object to be passed.  */
  align = size;
  align = size;
 
 
  cum->nbytes = (cum->nbytes + 3) & ~3;
  cum->nbytes = (cum->nbytes + 3) & ~3;
 
 
  /* Don't pass this arg via a register if all the argument registers
  /* Don't pass this arg via a register if all the argument registers
     are used up.  */
     are used up.  */
  if (cum->nbytes > nregs * UNITS_PER_WORD)
  if (cum->nbytes > nregs * UNITS_PER_WORD)
    return 0;
    return 0;
 
 
  if (cum->nbytes + size <= nregs * UNITS_PER_WORD)
  if (cum->nbytes + size <= nregs * UNITS_PER_WORD)
    return 0;
    return 0;
 
 
  /* Don't pass this arg via a register if it would be split between
  /* Don't pass this arg via a register if it would be split between
     registers and memory.  */
     registers and memory.  */
  if (type == NULL_TREE
  if (type == NULL_TREE
      && cum->nbytes + size > nregs * UNITS_PER_WORD)
      && cum->nbytes + size > nregs * UNITS_PER_WORD)
    return 0;
    return 0;
 
 
  return nregs * UNITS_PER_WORD - cum->nbytes;
  return nregs * UNITS_PER_WORD - cum->nbytes;
}
}
 
 
/* Return the location of the function's value.  This will be either
/* 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 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
   $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
   we only return the PARALLEL for outgoing values; we do not want
   callers relying on this extra copy.  */
   callers relying on this extra copy.  */
 
 
static rtx
static rtx
mn10300_function_value (const_tree valtype,
mn10300_function_value (const_tree valtype,
                        const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
                        const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
                        bool outgoing)
                        bool outgoing)
{
{
  rtx rv;
  rtx rv;
  enum machine_mode mode = TYPE_MODE (valtype);
  enum machine_mode mode = TYPE_MODE (valtype);
 
 
  if (! POINTER_TYPE_P (valtype))
  if (! POINTER_TYPE_P (valtype))
    return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
    return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
  else if (! TARGET_PTR_A0D0 || ! outgoing
  else if (! TARGET_PTR_A0D0 || ! outgoing
           || cfun->returns_struct)
           || cfun->returns_struct)
    return gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM);
    return gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM);
 
 
  rv = gen_rtx_PARALLEL (mode, rtvec_alloc (2));
  rv = gen_rtx_PARALLEL (mode, rtvec_alloc (2));
  XVECEXP (rv, 0, 0)
  XVECEXP (rv, 0, 0)
    = gen_rtx_EXPR_LIST (VOIDmode,
    = gen_rtx_EXPR_LIST (VOIDmode,
                         gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM),
                         gen_rtx_REG (mode, FIRST_ADDRESS_REGNUM),
                         GEN_INT (0));
                         GEN_INT (0));
 
 
  XVECEXP (rv, 0, 1)
  XVECEXP (rv, 0, 1)
    = gen_rtx_EXPR_LIST (VOIDmode,
    = gen_rtx_EXPR_LIST (VOIDmode,
                         gen_rtx_REG (mode, FIRST_DATA_REGNUM),
                         gen_rtx_REG (mode, FIRST_DATA_REGNUM),
                         GEN_INT (0));
                         GEN_INT (0));
  return rv;
  return rv;
}
}
 
 
/* Implements TARGET_LIBCALL_VALUE.  */
/* Implements TARGET_LIBCALL_VALUE.  */
 
 
static rtx
static rtx
mn10300_libcall_value (enum machine_mode mode,
mn10300_libcall_value (enum machine_mode mode,
                       const_rtx fun ATTRIBUTE_UNUSED)
                       const_rtx fun ATTRIBUTE_UNUSED)
{
{
  return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
  return gen_rtx_REG (mode, FIRST_DATA_REGNUM);
}
}
 
 
/* Implements FUNCTION_VALUE_REGNO_P.  */
/* Implements FUNCTION_VALUE_REGNO_P.  */
 
 
bool
bool
mn10300_function_value_regno_p (const unsigned int regno)
mn10300_function_value_regno_p (const unsigned int regno)
{
{
 return (regno == FIRST_DATA_REGNUM || regno == FIRST_ADDRESS_REGNUM);
 return (regno == FIRST_DATA_REGNUM || regno == FIRST_ADDRESS_REGNUM);
}
}
 
 
/* Output a tst insn.  */
/* Output a tst insn.  */
const char *
const char *
output_tst (rtx operand, rtx insn)
output_tst (rtx operand, rtx insn)
{
{
  rtx temp;
  rtx temp;
  int past_call = 0;
  int past_call = 0;
 
 
  /* We can save a byte if we can find a register which has the value
  /* We can save a byte if we can find a register which has the value
     zero in it.  */
     zero in it.  */
  temp = PREV_INSN (insn);
  temp = PREV_INSN (insn);
  while (optimize && temp)
  while (optimize && temp)
    {
    {
      rtx set;
      rtx set;
 
 
      /* We allow the search to go through call insns.  We record
      /* We allow the search to go through call insns.  We record
         the fact that we've past a CALL_INSN and reject matches which
         the fact that we've past a CALL_INSN and reject matches which
         use call clobbered registers.  */
         use call clobbered registers.  */
      if (GET_CODE (temp) == CODE_LABEL
      if (GET_CODE (temp) == CODE_LABEL
          || GET_CODE (temp) == JUMP_INSN
          || GET_CODE (temp) == JUMP_INSN
          || GET_CODE (temp) == BARRIER)
          || GET_CODE (temp) == BARRIER)
        break;
        break;
 
 
      if (GET_CODE (temp) == CALL_INSN)
      if (GET_CODE (temp) == CALL_INSN)
        past_call = 1;
        past_call = 1;
 
 
      if (GET_CODE (temp) == NOTE)
      if (GET_CODE (temp) == NOTE)
        {
        {
          temp = PREV_INSN (temp);
          temp = PREV_INSN (temp);
          continue;
          continue;
        }
        }
 
 
      /* It must be an insn, see if it is a simple set.  */
      /* It must be an insn, see if it is a simple set.  */
      set = single_set (temp);
      set = single_set (temp);
      if (!set)
      if (!set)
        {
        {
          temp = PREV_INSN (temp);
          temp = PREV_INSN (temp);
          continue;
          continue;
        }
        }
 
 
      /* Are we setting a data register to zero (this does not win for
      /* Are we setting a data register to zero (this does not win for
         address registers)?
         address registers)?
 
 
         If it's a call clobbered register, have we past a call?
         If it's a call clobbered register, have we past a call?
 
 
         Make sure the register we find isn't the same as ourself;
         Make sure the register we find isn't the same as ourself;
         the mn10300 can't encode that.
         the mn10300 can't encode that.
 
 
         ??? reg_set_between_p return nonzero anytime we pass a CALL_INSN
         ??? reg_set_between_p return nonzero anytime we pass a CALL_INSN
         so the code to detect calls here isn't doing anything useful.  */
         so the code to detect calls here isn't doing anything useful.  */
      if (REG_P (SET_DEST (set))
      if (REG_P (SET_DEST (set))
          && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
          && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
          && !reg_set_between_p (SET_DEST (set), temp, insn)
          && !reg_set_between_p (SET_DEST (set), temp, insn)
          && (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
          && (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
              == REGNO_REG_CLASS (REGNO (operand)))
              == REGNO_REG_CLASS (REGNO (operand)))
          && REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS
          && REGNO_REG_CLASS (REGNO (SET_DEST (set))) != EXTENDED_REGS
          && REGNO (SET_DEST (set)) != REGNO (operand)
          && REGNO (SET_DEST (set)) != REGNO (operand)
          && (!past_call
          && (!past_call
              || ! call_really_used_regs [REGNO (SET_DEST (set))]))
              || ! call_really_used_regs [REGNO (SET_DEST (set))]))
        {
        {
          rtx xoperands[2];
          rtx xoperands[2];
          xoperands[0] = operand;
          xoperands[0] = operand;
          xoperands[1] = SET_DEST (set);
          xoperands[1] = SET_DEST (set);
 
 
          output_asm_insn ("cmp %1,%0", xoperands);
          output_asm_insn ("cmp %1,%0", xoperands);
          return "";
          return "";
        }
        }
 
 
      if (REGNO_REG_CLASS (REGNO (operand)) == EXTENDED_REGS
      if (REGNO_REG_CLASS (REGNO (operand)) == EXTENDED_REGS
          && REG_P (SET_DEST (set))
          && REG_P (SET_DEST (set))
          && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
          && SET_SRC (set) == CONST0_RTX (GET_MODE (SET_DEST (set)))
          && !reg_set_between_p (SET_DEST (set), temp, insn)
          && !reg_set_between_p (SET_DEST (set), temp, insn)
          && (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
          && (REGNO_REG_CLASS (REGNO (SET_DEST (set)))
              != REGNO_REG_CLASS (REGNO (operand)))
              != REGNO_REG_CLASS (REGNO (operand)))
          && REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS
          && REGNO_REG_CLASS (REGNO (SET_DEST (set))) == EXTENDED_REGS
          && REGNO (SET_DEST (set)) != REGNO (operand)
          && REGNO (SET_DEST (set)) != REGNO (operand)
          && (!past_call
          && (!past_call
              || ! call_really_used_regs [REGNO (SET_DEST (set))]))
              || ! call_really_used_regs [REGNO (SET_DEST (set))]))
        {
        {
          rtx xoperands[2];
          rtx xoperands[2];
          xoperands[0] = operand;
          xoperands[0] = operand;
          xoperands[1] = SET_DEST (set);
          xoperands[1] = SET_DEST (set);
 
 
          output_asm_insn ("cmp %1,%0", xoperands);
          output_asm_insn ("cmp %1,%0", xoperands);
          return "";
          return "";
        }
        }
      temp = PREV_INSN (temp);
      temp = PREV_INSN (temp);
    }
    }
  return "cmp 0,%0";
  return "cmp 0,%0";
}
}
 
 
int
int
impossible_plus_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
impossible_plus_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  if (GET_CODE (op) != PLUS)
  if (GET_CODE (op) != PLUS)
    return 0;
    return 0;
 
 
  if (XEXP (op, 0) == stack_pointer_rtx
  if (XEXP (op, 0) == stack_pointer_rtx
      || XEXP (op, 1) == stack_pointer_rtx)
      || XEXP (op, 1) == stack_pointer_rtx)
    return 1;
    return 1;
 
 
  return 0;
  return 0;
}
}
 
 
/* Similarly, but when using a zero_extract pattern for a btst where
/* Similarly, but when using a zero_extract pattern for a btst where
   the source operand might end up in memory.  */
   the source operand might end up in memory.  */
int
int
mask_ok_for_mem_btst (int len, int bit)
mask_ok_for_mem_btst (int len, int bit)
{
{
  unsigned int mask = 0;
  unsigned int mask = 0;
 
 
  while (len > 0)
  while (len > 0)
    {
    {
      mask |= (1 << bit);
      mask |= (1 << bit);
      bit++;
      bit++;
      len--;
      len--;
    }
    }
 
 
  /* MASK must bit into an 8bit value.  */
  /* MASK must bit into an 8bit value.  */
  return (((mask & 0xff) == mask)
  return (((mask & 0xff) == mask)
          || ((mask & 0xff00) == mask)
          || ((mask & 0xff00) == mask)
          || ((mask & 0xff0000) == mask)
          || ((mask & 0xff0000) == mask)
          || ((mask & 0xff000000) == mask));
          || ((mask & 0xff000000) == mask));
}
}
 
 
/* Return 1 if X contains a symbolic expression.  We know these
/* Return 1 if X contains a symbolic expression.  We know these
   expressions will have one of a few well defined forms, so
   expressions will have one of a few well defined forms, so
   we need only check those forms.  */
   we need only check those forms.  */
int
int
symbolic_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
symbolic_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case SYMBOL_REF:
    case SYMBOL_REF:
    case LABEL_REF:
    case LABEL_REF:
      return 1;
      return 1;
    case CONST:
    case CONST:
      op = XEXP (op, 0);
      op = XEXP (op, 0);
      return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
      return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
               || GET_CODE (XEXP (op, 0)) == LABEL_REF)
               || GET_CODE (XEXP (op, 0)) == LABEL_REF)
              && GET_CODE (XEXP (op, 1)) == CONST_INT);
              && GET_CODE (XEXP (op, 1)) == CONST_INT);
    default:
    default:
      return 0;
      return 0;
    }
    }
}
}
 
 
/* Try machine dependent ways of modifying an illegitimate address
/* Try machine dependent ways of modifying an illegitimate address
   to be legitimate.  If we find one, return the new valid 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.
   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.
   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.
   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
   Normally it is always safe for this macro to do nothing.  It exists to
   recognize opportunities to optimize the output.
   recognize opportunities to optimize the output.
 
 
   But on a few ports with segmented architectures and indexed addressing
   But on a few ports with segmented architectures and indexed addressing
   (mn10300, hppa) it is used to rewrite certain problematical addresses.  */
   (mn10300, hppa) it is used to rewrite certain problematical addresses.  */
rtx
rtx
mn10300_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
mn10300_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
                            enum machine_mode mode ATTRIBUTE_UNUSED)
                            enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  if (flag_pic && ! legitimate_pic_operand_p (x))
  if (flag_pic && ! legitimate_pic_operand_p (x))
    x = legitimize_pic_address (oldx, NULL_RTX);
    x = legitimize_pic_address (oldx, NULL_RTX);
 
 
  /* Uh-oh.  We might have an address for x[n-100000].  This needs
  /* Uh-oh.  We might have an address for x[n-100000].  This needs
     special handling to avoid creating an indexed memory address
     special handling to avoid creating an indexed memory address
     with x-100000 as the base.  */
     with x-100000 as the base.  */
  if (GET_CODE (x) == PLUS
  if (GET_CODE (x) == PLUS
      && symbolic_operand (XEXP (x, 1), VOIDmode))
      && symbolic_operand (XEXP (x, 1), VOIDmode))
    {
    {
      /* Ugly.  We modify things here so that the address offset specified
      /* Ugly.  We modify things here so that the address offset specified
         by the index expression is computed first, then added to x to form
         by the index expression is computed first, then added to x to form
         the entire address.  */
         the entire address.  */
 
 
      rtx regx1, regy1, regy2, y;
      rtx regx1, regy1, regy2, y;
 
 
      /* Strip off any CONST.  */
      /* Strip off any CONST.  */
      y = XEXP (x, 1);
      y = XEXP (x, 1);
      if (GET_CODE (y) == CONST)
      if (GET_CODE (y) == CONST)
        y = XEXP (y, 0);
        y = XEXP (y, 0);
 
 
      if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
      if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS)
        {
        {
          regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0));
          regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0));
          regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0));
          regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0));
          regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0));
          regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0));
          regx1 = force_reg (Pmode,
          regx1 = force_reg (Pmode,
                             gen_rtx_fmt_ee (GET_CODE (y), Pmode, regx1, regy2));
                             gen_rtx_fmt_ee (GET_CODE (y), Pmode, regx1, regy2));
          return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1));
          return force_reg (Pmode, gen_rtx_PLUS (Pmode, regx1, regy1));
        }
        }
    }
    }
  return x;
  return x;
}
}
 
 
/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
/* Convert a non-PIC address in `orig' to a PIC address using @GOT or
   @GOTOFF in `reg'.  */
   @GOTOFF in `reg'.  */
rtx
rtx
legitimize_pic_address (rtx orig, rtx reg)
legitimize_pic_address (rtx orig, rtx reg)
{
{
  if (GET_CODE (orig) == LABEL_REF
  if (GET_CODE (orig) == LABEL_REF
      || (GET_CODE (orig) == SYMBOL_REF
      || (GET_CODE (orig) == SYMBOL_REF
          && (CONSTANT_POOL_ADDRESS_P (orig)
          && (CONSTANT_POOL_ADDRESS_P (orig)
              || ! MN10300_GLOBAL_P (orig))))
              || ! MN10300_GLOBAL_P (orig))))
    {
    {
      if (reg == 0)
      if (reg == 0)
        reg = gen_reg_rtx (Pmode);
        reg = gen_reg_rtx (Pmode);
 
 
      emit_insn (gen_symGOTOFF2reg (reg, orig));
      emit_insn (gen_symGOTOFF2reg (reg, orig));
      return reg;
      return reg;
    }
    }
  else if (GET_CODE (orig) == SYMBOL_REF)
  else if (GET_CODE (orig) == SYMBOL_REF)
    {
    {
      if (reg == 0)
      if (reg == 0)
        reg = gen_reg_rtx (Pmode);
        reg = gen_reg_rtx (Pmode);
 
 
      emit_insn (gen_symGOT2reg (reg, orig));
      emit_insn (gen_symGOT2reg (reg, orig));
      return reg;
      return reg;
    }
    }
  return orig;
  return orig;
}
}
 
 
/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
/* Return zero if X references a SYMBOL_REF or LABEL_REF whose symbol
   isn't protected by a PIC unspec; nonzero otherwise.  */
   isn't protected by a PIC unspec; nonzero otherwise.  */
int
int
legitimate_pic_operand_p (rtx x)
legitimate_pic_operand_p (rtx x)
{
{
  register const char *fmt;
  register const char *fmt;
  register int i;
  register int i;
 
 
  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
    return 0;
    return 0;
 
 
  if (GET_CODE (x) == UNSPEC
  if (GET_CODE (x) == UNSPEC
      && (XINT (x, 1) == UNSPEC_PIC
      && (XINT (x, 1) == UNSPEC_PIC
          || XINT (x, 1) == UNSPEC_GOT
          || XINT (x, 1) == UNSPEC_GOT
          || XINT (x, 1) == UNSPEC_GOTOFF
          || XINT (x, 1) == UNSPEC_GOTOFF
          || XINT (x, 1) == UNSPEC_PLT
          || XINT (x, 1) == UNSPEC_PLT
          || XINT (x, 1) == UNSPEC_GOTSYM_OFF))
          || XINT (x, 1) == UNSPEC_GOTSYM_OFF))
      return 1;
      return 1;
 
 
  fmt = GET_RTX_FORMAT (GET_CODE (x));
  fmt = GET_RTX_FORMAT (GET_CODE (x));
  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
    {
    {
      if (fmt[i] == 'E')
      if (fmt[i] == 'E')
        {
        {
          register int j;
          register int j;
 
 
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
          for (j = XVECLEN (x, i) - 1; j >= 0; j--)
            if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
            if (! legitimate_pic_operand_p (XVECEXP (x, i, j)))
              return 0;
              return 0;
        }
        }
      else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
      else if (fmt[i] == 'e' && ! legitimate_pic_operand_p (XEXP (x, i)))
        return 0;
        return 0;
    }
    }
 
 
  return 1;
  return 1;
}
}
 
 
/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is
/* Return TRUE if the address X, taken from a (MEM:MODE X) rtx, is
   legitimate, and FALSE otherwise.
   legitimate, and FALSE otherwise.
 
 
   On the mn10300, the value in the address register must be
   On the mn10300, the value in the address register must be
   in the same memory space/segment as the effective address.
   in the same memory space/segment as the effective address.
 
 
   This is problematical for reload since it does not understand
   This is problematical for reload since it does not understand
   that base+index != index+base in a memory reference.
   that base+index != index+base in a memory reference.
 
 
   Note it is still possible to use reg+reg addressing modes,
   Note it is still possible to use reg+reg addressing modes,
   it's just much more difficult.  For a discussion of a possible
   it's just much more difficult.  For a discussion of a possible
   workaround and solution, see the comments in pa.c before the
   workaround and solution, see the comments in pa.c before the
   function record_unscaled_index_insn_codes.  */
   function record_unscaled_index_insn_codes.  */
 
 
bool
bool
mn10300_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
mn10300_legitimate_address_p (enum machine_mode mode, rtx x, bool strict)
{
{
  if (CONSTANT_ADDRESS_P (x)
  if (CONSTANT_ADDRESS_P (x)
      && (! flag_pic || legitimate_pic_operand_p (x)))
      && (! flag_pic || legitimate_pic_operand_p (x)))
    return TRUE;
    return TRUE;
 
 
  if (RTX_OK_FOR_BASE_P (x, strict))
  if (RTX_OK_FOR_BASE_P (x, strict))
    return TRUE;
    return TRUE;
 
 
  if (TARGET_AM33
  if (TARGET_AM33
      && GET_CODE (x) == POST_INC
      && GET_CODE (x) == POST_INC
      && RTX_OK_FOR_BASE_P (XEXP (x, 0), strict)
      && RTX_OK_FOR_BASE_P (XEXP (x, 0), strict)
      && (mode == SImode || mode == SFmode || mode == HImode))
      && (mode == SImode || mode == SFmode || mode == HImode))
    return TRUE;
    return TRUE;
 
 
  if (GET_CODE (x) == PLUS)
  if (GET_CODE (x) == PLUS)
    {
    {
      rtx base = 0, index = 0;
      rtx base = 0, index = 0;
 
 
      if (REG_P (XEXP (x, 0))
      if (REG_P (XEXP (x, 0))
          && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 0)), strict))
          && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 0)), strict))
        {
        {
          base = XEXP (x, 0);
          base = XEXP (x, 0);
          index = XEXP (x, 1);
          index = XEXP (x, 1);
        }
        }
 
 
      if (REG_P (XEXP (x, 1))
      if (REG_P (XEXP (x, 1))
          && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 1)), strict))
          && REGNO_STRICT_OK_FOR_BASE_P (REGNO (XEXP (x, 1)), strict))
        {
        {
          base = XEXP (x, 1);
          base = XEXP (x, 1);
          index = XEXP (x, 0);
          index = XEXP (x, 0);
        }
        }
 
 
      if (base != 0 && index != 0)
      if (base != 0 && index != 0)
        {
        {
          if (GET_CODE (index) == CONST_INT)
          if (GET_CODE (index) == CONST_INT)
            return TRUE;
            return TRUE;
          if (GET_CODE (index) == CONST
          if (GET_CODE (index) == CONST
              && GET_CODE (XEXP (index, 0)) != PLUS
              && GET_CODE (XEXP (index, 0)) != PLUS
              && (! flag_pic
              && (! flag_pic
                  || legitimate_pic_operand_p (index)))
                  || legitimate_pic_operand_p (index)))
            return TRUE;
            return TRUE;
        }
        }
    }
    }
 
 
  return FALSE;
  return FALSE;
}
}
 
 
static int
static int
mn10300_address_cost_1 (rtx x, int *unsig)
mn10300_address_cost_1 (rtx x, int *unsig)
{
{
  switch (GET_CODE (x))
  switch (GET_CODE (x))
    {
    {
    case REG:
    case REG:
      switch (REGNO_REG_CLASS (REGNO (x)))
      switch (REGNO_REG_CLASS (REGNO (x)))
        {
        {
        case SP_REGS:
        case SP_REGS:
          *unsig = 1;
          *unsig = 1;
          return 0;
          return 0;
 
 
        case ADDRESS_REGS:
        case ADDRESS_REGS:
          return 1;
          return 1;
 
 
        case DATA_REGS:
        case DATA_REGS:
        case EXTENDED_REGS:
        case EXTENDED_REGS:
        case FP_REGS:
        case FP_REGS:
          return 3;
          return 3;
 
 
        case NO_REGS:
        case NO_REGS:
          return 5;
          return 5;
 
 
        default:
        default:
          gcc_unreachable ();
          gcc_unreachable ();
        }
        }
 
 
    case PLUS:
    case PLUS:
    case MINUS:
    case MINUS:
    case ASHIFT:
    case ASHIFT:
    case AND:
    case AND:
    case IOR:
    case IOR:
      return (mn10300_address_cost_1 (XEXP (x, 0), unsig)
      return (mn10300_address_cost_1 (XEXP (x, 0), unsig)
              + mn10300_address_cost_1 (XEXP (x, 1), unsig));
              + mn10300_address_cost_1 (XEXP (x, 1), unsig));
 
 
    case EXPR_LIST:
    case EXPR_LIST:
    case SUBREG:
    case SUBREG:
    case MEM:
    case MEM:
      return mn10300_address_cost (XEXP (x, 0), !optimize_size);
      return mn10300_address_cost (XEXP (x, 0), !optimize_size);
 
 
    case ZERO_EXTEND:
    case ZERO_EXTEND:
      *unsig = 1;
      *unsig = 1;
      return mn10300_address_cost_1 (XEXP (x, 0), unsig);
      return mn10300_address_cost_1 (XEXP (x, 0), unsig);
 
 
    case CONST_INT:
    case CONST_INT:
      if (INTVAL (x) == 0)
      if (INTVAL (x) == 0)
        return 0;
        return 0;
      if (INTVAL (x) + (*unsig ? 0 : 0x80) < 0x100)
      if (INTVAL (x) + (*unsig ? 0 : 0x80) < 0x100)
        return 1;
        return 1;
      if (INTVAL (x) + (*unsig ? 0 : 0x8000) < 0x10000)
      if (INTVAL (x) + (*unsig ? 0 : 0x8000) < 0x10000)
        return 3;
        return 3;
      if (INTVAL (x) + (*unsig ? 0 : 0x800000) < 0x1000000)
      if (INTVAL (x) + (*unsig ? 0 : 0x800000) < 0x1000000)
        return 5;
        return 5;
      return 7;
      return 7;
 
 
    case CONST:
    case CONST:
    case SYMBOL_REF:
    case SYMBOL_REF:
    case LABEL_REF:
    case LABEL_REF:
      return 8;
      return 8;
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
 
 
    }
    }
}
}
 
 
static int
static int
mn10300_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
mn10300_address_cost (rtx x, bool speed ATTRIBUTE_UNUSED)
{
{
  int s = 0;
  int s = 0;
  return mn10300_address_cost_1 (x, &s);
  return mn10300_address_cost_1 (x, &s);
}
}
 
 
static bool
static bool
mn10300_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed ATTRIBUTE_UNUSED)
mn10300_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed ATTRIBUTE_UNUSED)
{
{
  switch (code)
  switch (code)
    {
    {
    case CONST_INT:
    case CONST_INT:
      /* Zeros are extremely cheap.  */
      /* Zeros are extremely cheap.  */
      if (INTVAL (x) == 0 && (outer_code == SET || outer_code == COMPARE))
      if (INTVAL (x) == 0 && (outer_code == SET || outer_code == COMPARE))
        *total = 0;
        *total = 0;
      /* If it fits in 8 bits, then it's still relatively cheap.  */
      /* If it fits in 8 bits, then it's still relatively cheap.  */
      else if (INT_8_BITS (INTVAL (x)))
      else if (INT_8_BITS (INTVAL (x)))
        *total = 1;
        *total = 1;
      /* This is the "base" cost, includes constants where either the
      /* This is the "base" cost, includes constants where either the
         upper or lower 16bits are all zeros.  */
         upper or lower 16bits are all zeros.  */
      else if (INT_16_BITS (INTVAL (x))
      else if (INT_16_BITS (INTVAL (x))
               || (INTVAL (x) & 0xffff) == 0
               || (INTVAL (x) & 0xffff) == 0
               || (INTVAL (x) & 0xffff0000) == 0)
               || (INTVAL (x) & 0xffff0000) == 0)
        *total = 2;
        *total = 2;
      else
      else
        *total = 4;
        *total = 4;
      return true;
      return true;
 
 
    case CONST:
    case CONST:
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
      /* These are more costly than a CONST_INT, but we can relax them,
      /* These are more costly than a CONST_INT, but we can relax them,
         so they're less costly than a CONST_DOUBLE.  */
         so they're less costly than a CONST_DOUBLE.  */
      *total = 6;
      *total = 6;
      return true;
      return true;
 
 
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      /* We don't optimize CONST_DOUBLEs well nor do we relax them well,
      /* We don't optimize CONST_DOUBLEs well nor do we relax them well,
         so their cost is very high.  */
         so their cost is very high.  */
      *total = 8;
      *total = 8;
      return true;
      return true;
 
 
    case ZERO_EXTRACT:
    case ZERO_EXTRACT:
      /* This is cheap, we can use btst.  */
      /* This is cheap, we can use btst.  */
      if (outer_code == COMPARE)
      if (outer_code == COMPARE)
        *total = 0;
        *total = 0;
      return false;
      return false;
 
 
   /* ??? This probably needs more work.  */
   /* ??? This probably needs more work.  */
    case MOD:
    case MOD:
    case DIV:
    case DIV:
    case MULT:
    case MULT:
      *total = 8;
      *total = 8;
      return true;
      return true;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
}
}
 
 
/* Check whether a constant used to initialize a DImode or DFmode can
/* Check whether a constant used to initialize a DImode or DFmode can
   use a clr instruction.  The code here must be kept in sync with
   use a clr instruction.  The code here must be kept in sync with
   movdf and movdi.  */
   movdf and movdi.  */
 
 
bool
bool
mn10300_wide_const_load_uses_clr (rtx operands[2])
mn10300_wide_const_load_uses_clr (rtx operands[2])
{
{
  long val[2];
  long val[2];
 
 
  if (GET_CODE (operands[0]) != REG
  if (GET_CODE (operands[0]) != REG
      || REGNO_REG_CLASS (REGNO (operands[0])) != DATA_REGS)
      || REGNO_REG_CLASS (REGNO (operands[0])) != DATA_REGS)
    return false;
    return false;
 
 
  switch (GET_CODE (operands[1]))
  switch (GET_CODE (operands[1]))
    {
    {
    case CONST_INT:
    case CONST_INT:
      {
      {
        rtx low, high;
        rtx low, high;
        split_double (operands[1], &low, &high);
        split_double (operands[1], &low, &high);
        val[0] = INTVAL (low);
        val[0] = INTVAL (low);
        val[1] = INTVAL (high);
        val[1] = INTVAL (high);
      }
      }
      break;
      break;
 
 
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      if (GET_MODE (operands[1]) == DFmode)
      if (GET_MODE (operands[1]) == DFmode)
        {
        {
          REAL_VALUE_TYPE rv;
          REAL_VALUE_TYPE rv;
 
 
          REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
          REAL_VALUE_FROM_CONST_DOUBLE (rv, operands[1]);
          REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
          REAL_VALUE_TO_TARGET_DOUBLE (rv, val);
        }
        }
      else if (GET_MODE (operands[1]) == VOIDmode
      else if (GET_MODE (operands[1]) == VOIDmode
               || GET_MODE (operands[1]) == DImode)
               || GET_MODE (operands[1]) == DImode)
        {
        {
          val[0] = CONST_DOUBLE_LOW (operands[1]);
          val[0] = CONST_DOUBLE_LOW (operands[1]);
          val[1] = CONST_DOUBLE_HIGH (operands[1]);
          val[1] = CONST_DOUBLE_HIGH (operands[1]);
        }
        }
      break;
      break;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
 
 
  return val[0] == 0 || val[1] == 0;
  return val[0] == 0 || val[1] == 0;
}
}
/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
   may access it using GOTOFF instead of GOT.  */
   may access it using GOTOFF instead of GOT.  */
 
 
static void
static void
mn10300_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
mn10300_encode_section_info (tree decl, rtx rtl, int first ATTRIBUTE_UNUSED)
{
{
  rtx symbol;
  rtx symbol;
 
 
  if (GET_CODE (rtl) != MEM)
  if (GET_CODE (rtl) != MEM)
    return;
    return;
  symbol = XEXP (rtl, 0);
  symbol = XEXP (rtl, 0);
  if (GET_CODE (symbol) != SYMBOL_REF)
  if (GET_CODE (symbol) != SYMBOL_REF)
    return;
    return;
 
 
  if (flag_pic)
  if (flag_pic)
    SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
    SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
}
}
 
 
/* Dispatch tables on the mn10300 are extremely expensive in terms of code
/* 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
   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
   encourage a series of if/else comparisons to implement many small switch
   statements.  In theory, this value could be increased much more if we
   statements.  In theory, this value could be increased much more if we
   were solely optimizing for space, but we keep it "reasonable" to avoid
   were solely optimizing for space, but we keep it "reasonable" to avoid
   serious code efficiency lossage.  */
   serious code efficiency lossage.  */
 
 
unsigned int mn10300_case_values_threshold (void)
unsigned int mn10300_case_values_threshold (void)
{
{
  return 6;
  return 6;
}
}
 
 
/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE.  */
/* Worker function for TARGET_ASM_TRAMPOLINE_TEMPLATE.  */
 
 
static void
static void
mn10300_asm_trampoline_template (FILE *f)
mn10300_asm_trampoline_template (FILE *f)
{
{
  fprintf (f, "\tadd -4,sp\n");
  fprintf (f, "\tadd -4,sp\n");
  fprintf (f, "\t.long 0x0004fffa\n");
  fprintf (f, "\t.long 0x0004fffa\n");
  fprintf (f, "\tmov (0,sp),a0\n");
  fprintf (f, "\tmov (0,sp),a0\n");
  fprintf (f, "\tadd 4,sp\n");
  fprintf (f, "\tadd 4,sp\n");
  fprintf (f, "\tmov (13,a0),a1\n");
  fprintf (f, "\tmov (13,a0),a1\n");
  fprintf (f, "\tmov (17,a0),a0\n");
  fprintf (f, "\tmov (17,a0),a0\n");
  fprintf (f, "\tjmp (a0)\n");
  fprintf (f, "\tjmp (a0)\n");
  fprintf (f, "\t.long 0\n");
  fprintf (f, "\t.long 0\n");
  fprintf (f, "\t.long 0\n");
  fprintf (f, "\t.long 0\n");
}
}
 
 
/* Worker function for TARGET_TRAMPOLINE_INIT.  */
/* Worker function for TARGET_TRAMPOLINE_INIT.  */
 
 
static void
static void
mn10300_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
mn10300_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
{
{
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx mem;
  rtx mem;
 
 
  emit_block_move (m_tramp, assemble_trampoline_template (),
  emit_block_move (m_tramp, assemble_trampoline_template (),
                   GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
                   GEN_INT (TRAMPOLINE_SIZE), BLOCK_OP_NORMAL);
 
 
  mem = adjust_address (m_tramp, SImode, 0x14);
  mem = adjust_address (m_tramp, SImode, 0x14);
  emit_move_insn (mem, chain_value);
  emit_move_insn (mem, chain_value);
  mem = adjust_address (m_tramp, SImode, 0x18);
  mem = adjust_address (m_tramp, SImode, 0x18);
  emit_move_insn (mem, fnaddr);
  emit_move_insn (mem, fnaddr);
}
}
 
 
/* Output the assembler code for a C++ thunk function.
/* Output the assembler code for a C++ thunk function.
   THUNK_DECL is the declaration for the thunk function itself, FUNCTION
   THUNK_DECL is the declaration for the thunk function itself, FUNCTION
   is the decl for the target function.  DELTA is an immediate constant
   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
   offset to be added to the THIS parameter.  If VCALL_OFFSET is nonzero
   the word at the adjusted address *(*THIS' + VCALL_OFFSET) should be
   the word at the adjusted address *(*THIS' + VCALL_OFFSET) should be
   additionally added to THIS.  Finally jump to the entry point of
   additionally added to THIS.  Finally jump to the entry point of
   FUNCTION.  */
   FUNCTION.  */
 
 
static void
static void
mn10300_asm_output_mi_thunk (FILE *        file,
mn10300_asm_output_mi_thunk (FILE *        file,
                             tree          thunk_fndecl ATTRIBUTE_UNUSED,
                             tree          thunk_fndecl ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT delta,
                             HOST_WIDE_INT delta,
                             HOST_WIDE_INT vcall_offset,
                             HOST_WIDE_INT vcall_offset,
                             tree          function)
                             tree          function)
{
{
  const char * _this;
  const char * _this;
 
 
  /* Get the register holding the THIS parameter.  Handle the case
  /* Get the register holding the THIS parameter.  Handle the case
     where there is a hidden first argument for a returned structure.  */
     where there is a hidden first argument for a returned structure.  */
  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
    _this = reg_names [FIRST_ARGUMENT_REGNUM + 1];
    _this = reg_names [FIRST_ARGUMENT_REGNUM + 1];
  else
  else
    _this = reg_names [FIRST_ARGUMENT_REGNUM];
    _this = reg_names [FIRST_ARGUMENT_REGNUM];
 
 
  fprintf (file, "\t%s Thunk Entry Point:\n", ASM_COMMENT_START);
  fprintf (file, "\t%s Thunk Entry Point:\n", ASM_COMMENT_START);
 
 
  if (delta)
  if (delta)
    fprintf (file, "\tadd %d, %s\n", (int) delta, _this);
    fprintf (file, "\tadd %d, %s\n", (int) delta, _this);
 
 
  if (vcall_offset)
  if (vcall_offset)
    {
    {
      const char * scratch = reg_names [FIRST_ADDRESS_REGNUM + 1];
      const char * scratch = reg_names [FIRST_ADDRESS_REGNUM + 1];
 
 
      fprintf (file, "\tmov %s, %s\n", _this, scratch);
      fprintf (file, "\tmov %s, %s\n", _this, scratch);
      fprintf (file, "\tmov (%s), %s\n", scratch, scratch);
      fprintf (file, "\tmov (%s), %s\n", scratch, scratch);
      fprintf (file, "\tadd %d, %s\n", (int) vcall_offset, scratch);
      fprintf (file, "\tadd %d, %s\n", (int) vcall_offset, scratch);
      fprintf (file, "\tmov (%s), %s\n", scratch, scratch);
      fprintf (file, "\tmov (%s), %s\n", scratch, scratch);
      fprintf (file, "\tadd %s, %s\n", scratch, _this);
      fprintf (file, "\tadd %s, %s\n", scratch, _this);
    }
    }
 
 
  fputs ("\tjmp ", file);
  fputs ("\tjmp ", file);
  assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
  assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
  putc ('\n', file);
  putc ('\n', file);
}
}
 
 
/* Return true if mn10300_output_mi_thunk would be able to output the
/* Return true if mn10300_output_mi_thunk would be able to output the
   assembler code for the thunk function specified by the arguments
   assembler code for the thunk function specified by the arguments
   it is passed, and false otherwise.  */
   it is passed, and false otherwise.  */
 
 
static bool
static bool
mn10300_can_output_mi_thunk (const_tree    thunk_fndecl ATTRIBUTE_UNUSED,
mn10300_can_output_mi_thunk (const_tree    thunk_fndecl ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT delta        ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT delta        ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
                             const_tree    function     ATTRIBUTE_UNUSED)
                             const_tree    function     ATTRIBUTE_UNUSED)
{
{
  return true;
  return true;
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.