OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gcc-4.2.2/] [gcc/] [config/] [arc/] [arc.c] - Diff between revs 154 and 816

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

Rev 154 Rev 816
/* Subroutines used for code generation on the Argonaut ARC cpu.
/* Subroutines used for code generation on the Argonaut ARC cpu.
   Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
   Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
   2005, 2007
   2005, 2007
   Free Software Foundation, Inc.
   Free Software Foundation, Inc.
 
 
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/>.  */
 
 
/* ??? This is an old port, and is undoubtedly suffering from bit rot.  */
/* ??? This is an old port, and is undoubtedly suffering from bit rot.  */
 
 
#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 "tree.h"
#include "tree.h"
#include "rtl.h"
#include "rtl.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 "function.h"
#include "function.h"
#include "expr.h"
#include "expr.h"
#include "recog.h"
#include "recog.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"
 
 
/* Which cpu we're compiling for.  */
/* Which cpu we're compiling for.  */
int arc_cpu_type;
int arc_cpu_type;
 
 
/* Name of mangle string to add to symbols to separate code compiled for each
/* Name of mangle string to add to symbols to separate code compiled for each
   cpu (or NULL).  */
   cpu (or NULL).  */
const char *arc_mangle_cpu;
const char *arc_mangle_cpu;
 
 
/* Save the operands last given to a compare for use when we
/* Save the operands last given to a compare for use when we
   generate a scc or bcc insn.  */
   generate a scc or bcc insn.  */
rtx arc_compare_op0, arc_compare_op1;
rtx arc_compare_op0, arc_compare_op1;
 
 
/* Name of text, data, and rodata sections used in varasm.c.  */
/* Name of text, data, and rodata sections used in varasm.c.  */
const char *arc_text_section;
const char *arc_text_section;
const char *arc_data_section;
const char *arc_data_section;
const char *arc_rodata_section;
const char *arc_rodata_section;
 
 
/* Array of valid operand punctuation characters.  */
/* Array of valid operand punctuation characters.  */
char arc_punct_chars[256];
char arc_punct_chars[256];
 
 
/* Variables used by arc_final_prescan_insn to implement conditional
/* Variables used by arc_final_prescan_insn to implement conditional
   execution.  */
   execution.  */
static int arc_ccfsm_state;
static int arc_ccfsm_state;
static int arc_ccfsm_current_cc;
static int arc_ccfsm_current_cc;
static rtx arc_ccfsm_target_insn;
static rtx arc_ccfsm_target_insn;
static int arc_ccfsm_target_label;
static int arc_ccfsm_target_label;
 
 
/* The maximum number of insns skipped which will be conditionalised if
/* The maximum number of insns skipped which will be conditionalised if
   possible.  */
   possible.  */
#define MAX_INSNS_SKIPPED 3
#define MAX_INSNS_SKIPPED 3
 
 
/* A nop is needed between a 4 byte insn that sets the condition codes and
/* A nop is needed between a 4 byte insn that sets the condition codes and
   a branch that uses them (the same isn't true for an 8 byte insn that sets
   a branch that uses them (the same isn't true for an 8 byte insn that sets
   the condition codes).  Set by arc_final_prescan_insn.  Used by
   the condition codes).  Set by arc_final_prescan_insn.  Used by
   arc_print_operand.  */
   arc_print_operand.  */
static int last_insn_set_cc_p;
static int last_insn_set_cc_p;
static int current_insn_set_cc_p;
static int current_insn_set_cc_p;
static bool arc_handle_option (size_t, const char *, int);
static bool arc_handle_option (size_t, const char *, int);
static void record_cc_ref (rtx);
static void record_cc_ref (rtx);
static void arc_init_reg_tables (void);
static void arc_init_reg_tables (void);
static int get_arc_condition_code (rtx);
static int get_arc_condition_code (rtx);
const struct attribute_spec arc_attribute_table[];
const struct attribute_spec arc_attribute_table[];
static tree arc_handle_interrupt_attribute (tree *, tree, tree, int, bool *);
static tree arc_handle_interrupt_attribute (tree *, tree, tree, int, bool *);
static bool arc_assemble_integer (rtx, unsigned int, int);
static bool arc_assemble_integer (rtx, unsigned int, int);
static void arc_output_function_prologue (FILE *, HOST_WIDE_INT);
static void arc_output_function_prologue (FILE *, HOST_WIDE_INT);
static void arc_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void arc_output_function_epilogue (FILE *, HOST_WIDE_INT);
static void arc_file_start (void);
static void arc_file_start (void);
static void arc_internal_label (FILE *, const char *, unsigned long);
static void arc_internal_label (FILE *, const char *, unsigned long);
static void arc_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
static void arc_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
                                        tree, int *, int);
                                        tree, int *, int);
static bool arc_rtx_costs (rtx, int, int, int *);
static bool arc_rtx_costs (rtx, int, int, int *);
static int arc_address_cost (rtx);
static int arc_address_cost (rtx);
static void arc_external_libcall (rtx);
static void arc_external_libcall (rtx);
static bool arc_return_in_memory (tree, tree);
static bool arc_return_in_memory (tree, tree);
static bool arc_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
static bool arc_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
                                   tree, bool);
                                   tree, bool);


/* 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_ASM_ALIGNED_SI_OP
#undef TARGET_ASM_ALIGNED_SI_OP
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
#undef TARGET_ASM_INTEGER
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER arc_assemble_integer
#define TARGET_ASM_INTEGER arc_assemble_integer
 
 
#undef TARGET_ASM_FUNCTION_PROLOGUE
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE arc_output_function_prologue
#define TARGET_ASM_FUNCTION_PROLOGUE arc_output_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#undef TARGET_ASM_FUNCTION_EPILOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE arc_output_function_epilogue
#define TARGET_ASM_FUNCTION_EPILOGUE arc_output_function_epilogue
#undef TARGET_ASM_FILE_START
#undef TARGET_ASM_FILE_START
#define TARGET_ASM_FILE_START arc_file_start
#define TARGET_ASM_FILE_START arc_file_start
#undef TARGET_ATTRIBUTE_TABLE
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE arc_attribute_table
#define TARGET_ATTRIBUTE_TABLE arc_attribute_table
#undef TARGET_ASM_INTERNAL_LABEL
#undef TARGET_ASM_INTERNAL_LABEL
#define TARGET_ASM_INTERNAL_LABEL arc_internal_label
#define TARGET_ASM_INTERNAL_LABEL arc_internal_label
#undef TARGET_ASM_EXTERNAL_LIBCALL
#undef TARGET_ASM_EXTERNAL_LIBCALL
#define TARGET_ASM_EXTERNAL_LIBCALL arc_external_libcall
#define TARGET_ASM_EXTERNAL_LIBCALL arc_external_libcall
 
 
#undef TARGET_HANDLE_OPTION
#undef TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION arc_handle_option
#define TARGET_HANDLE_OPTION arc_handle_option
 
 
#undef TARGET_RTX_COSTS
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS arc_rtx_costs
#define TARGET_RTX_COSTS arc_rtx_costs
#undef TARGET_ADDRESS_COST
#undef TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST arc_address_cost
#define TARGET_ADDRESS_COST arc_address_cost
 
 
#undef TARGET_PROMOTE_FUNCTION_ARGS
#undef TARGET_PROMOTE_FUNCTION_ARGS
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
#undef TARGET_PROMOTE_FUNCTION_RETURN
#undef TARGET_PROMOTE_FUNCTION_RETURN
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
#undef TARGET_PROMOTE_PROTOTYPES
#undef TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
 
 
#undef TARGET_RETURN_IN_MEMORY
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY arc_return_in_memory
#define TARGET_RETURN_IN_MEMORY arc_return_in_memory
#undef TARGET_PASS_BY_REFERENCE
#undef TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE arc_pass_by_reference
#define TARGET_PASS_BY_REFERENCE arc_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_SETUP_INCOMING_VARARGS
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS arc_setup_incoming_varargs
#define TARGET_SETUP_INCOMING_VARARGS arc_setup_incoming_varargs
 
 
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;


/* Implement TARGET_HANDLE_OPTION.  */
/* Implement TARGET_HANDLE_OPTION.  */
 
 
static bool
static bool
arc_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
arc_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
{
{
  switch (code)
  switch (code)
    {
    {
    case OPT_mcpu_:
    case OPT_mcpu_:
      return strcmp (arg, "base") == 0 || ARC_EXTENSION_CPU (arg);
      return strcmp (arg, "base") == 0 || ARC_EXTENSION_CPU (arg);
 
 
    default:
    default:
      return true;
      return true;
    }
    }
}
}
 
 
/* Called by OVERRIDE_OPTIONS to initialize various things.  */
/* Called by OVERRIDE_OPTIONS to initialize various things.  */
 
 
void
void
arc_init (void)
arc_init (void)
{
{
  char *tmp;
  char *tmp;
 
 
  /* Set the pseudo-ops for the various standard sections.  */
  /* Set the pseudo-ops for the various standard sections.  */
  arc_text_section = tmp = xmalloc (strlen (arc_text_string) + sizeof (ARC_SECTION_FORMAT) + 1);
  arc_text_section = tmp = xmalloc (strlen (arc_text_string) + sizeof (ARC_SECTION_FORMAT) + 1);
  sprintf (tmp, ARC_SECTION_FORMAT, arc_text_string);
  sprintf (tmp, ARC_SECTION_FORMAT, arc_text_string);
  arc_data_section = tmp = xmalloc (strlen (arc_data_string) + sizeof (ARC_SECTION_FORMAT) + 1);
  arc_data_section = tmp = xmalloc (strlen (arc_data_string) + sizeof (ARC_SECTION_FORMAT) + 1);
  sprintf (tmp, ARC_SECTION_FORMAT, arc_data_string);
  sprintf (tmp, ARC_SECTION_FORMAT, arc_data_string);
  arc_rodata_section = tmp = xmalloc (strlen (arc_rodata_string) + sizeof (ARC_SECTION_FORMAT) + 1);
  arc_rodata_section = tmp = xmalloc (strlen (arc_rodata_string) + sizeof (ARC_SECTION_FORMAT) + 1);
  sprintf (tmp, ARC_SECTION_FORMAT, arc_rodata_string);
  sprintf (tmp, ARC_SECTION_FORMAT, arc_rodata_string);
 
 
  arc_init_reg_tables ();
  arc_init_reg_tables ();
 
 
  /* Initialize array for PRINT_OPERAND_PUNCT_VALID_P.  */
  /* Initialize array for PRINT_OPERAND_PUNCT_VALID_P.  */
  memset (arc_punct_chars, 0, sizeof (arc_punct_chars));
  memset (arc_punct_chars, 0, sizeof (arc_punct_chars));
  arc_punct_chars['#'] = 1;
  arc_punct_chars['#'] = 1;
  arc_punct_chars['*'] = 1;
  arc_punct_chars['*'] = 1;
  arc_punct_chars['?'] = 1;
  arc_punct_chars['?'] = 1;
  arc_punct_chars['!'] = 1;
  arc_punct_chars['!'] = 1;
  arc_punct_chars['~'] = 1;
  arc_punct_chars['~'] = 1;
}
}


/* The condition codes of the ARC, and the inverse function.  */
/* The condition codes of the ARC, and the inverse function.  */
static const char *const arc_condition_codes[] =
static const char *const arc_condition_codes[] =
{
{
  "al", 0, "eq", "ne", "p", "n", "c", "nc", "v", "nv",
  "al", 0, "eq", "ne", "p", "n", "c", "nc", "v", "nv",
  "gt", "le", "ge", "lt", "hi", "ls", "pnz", 0
  "gt", "le", "ge", "lt", "hi", "ls", "pnz", 0
};
};
 
 
#define ARC_INVERSE_CONDITION_CODE(X)  ((X) ^ 1)
#define ARC_INVERSE_CONDITION_CODE(X)  ((X) ^ 1)
 
 
/* Returns the index of the ARC condition code string in
/* Returns the index of the ARC condition code string in
   `arc_condition_codes'.  COMPARISON should be an rtx like
   `arc_condition_codes'.  COMPARISON should be an rtx like
   `(eq (...) (...))'.  */
   `(eq (...) (...))'.  */
 
 
static int
static int
get_arc_condition_code (rtx comparison)
get_arc_condition_code (rtx comparison)
{
{
  switch (GET_CODE (comparison))
  switch (GET_CODE (comparison))
    {
    {
    case EQ : return 2;
    case EQ : return 2;
    case NE : return 3;
    case NE : return 3;
    case GT : return 10;
    case GT : return 10;
    case LE : return 11;
    case LE : return 11;
    case GE : return 12;
    case GE : return 12;
    case LT : return 13;
    case LT : return 13;
    case GTU : return 14;
    case GTU : return 14;
    case LEU : return 15;
    case LEU : return 15;
    case LTU : return 6;
    case LTU : return 6;
    case GEU : return 7;
    case GEU : return 7;
    default : gcc_unreachable ();
    default : gcc_unreachable ();
    }
    }
  /*NOTREACHED*/
  /*NOTREACHED*/
  return (42);
  return (42);
}
}
 
 
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
/* Given a comparison code (EQ, NE, etc.) and the first operand of a COMPARE,
   return the mode to be used for the comparison.  */
   return the mode to be used for the comparison.  */
 
 
enum machine_mode
enum machine_mode
arc_select_cc_mode (enum rtx_code op,
arc_select_cc_mode (enum rtx_code op,
                    rtx x ATTRIBUTE_UNUSED,
                    rtx x ATTRIBUTE_UNUSED,
                    rtx y ATTRIBUTE_UNUSED)
                    rtx y ATTRIBUTE_UNUSED)
{
{
  switch (op)
  switch (op)
    {
    {
    case EQ :
    case EQ :
    case NE :
    case NE :
      return CCZNmode;
      return CCZNmode;
    default :
    default :
      switch (GET_CODE (x))
      switch (GET_CODE (x))
        {
        {
        case AND :
        case AND :
        case IOR :
        case IOR :
        case XOR :
        case XOR :
        case SIGN_EXTEND :
        case SIGN_EXTEND :
        case ZERO_EXTEND :
        case ZERO_EXTEND :
          return CCZNmode;
          return CCZNmode;
        case ASHIFT :
        case ASHIFT :
        case ASHIFTRT :
        case ASHIFTRT :
        case LSHIFTRT :
        case LSHIFTRT :
          return CCZNCmode;
          return CCZNCmode;
        default:
        default:
          break;
          break;
        }
        }
    }
    }
  return CCmode;
  return CCmode;
}
}


/* Vectors to keep interesting information about registers where it can easily
/* Vectors to keep interesting information about registers where it can easily
   be got.  We use to use the actual mode value as the bit number, but there
   be got.  We use to use the actual mode value as the bit number, but there
   is (or may be) more than 32 modes now.  Instead we use two tables: one
   is (or may be) more than 32 modes now.  Instead we use two tables: one
   indexed by hard register number, and one indexed by mode.  */
   indexed by hard register number, and one indexed by mode.  */
 
 
/* The purpose of arc_mode_class is to shrink the range of modes so that
/* The purpose of arc_mode_class is to shrink the range of modes so that
   they all fit (as bit numbers) in a 32 bit word (again).  Each real mode is
   they all fit (as bit numbers) in a 32 bit word (again).  Each real mode is
   mapped into one arc_mode_class mode.  */
   mapped into one arc_mode_class mode.  */
 
 
enum arc_mode_class {
enum arc_mode_class {
  C_MODE,
  C_MODE,
  S_MODE, D_MODE, T_MODE, O_MODE,
  S_MODE, D_MODE, T_MODE, O_MODE,
  SF_MODE, DF_MODE, TF_MODE, OF_MODE
  SF_MODE, DF_MODE, TF_MODE, OF_MODE
};
};
 
 
/* Modes for condition codes.  */
/* Modes for condition codes.  */
#define C_MODES (1 << (int) C_MODE)
#define C_MODES (1 << (int) C_MODE)
 
 
/* Modes for single-word and smaller quantities.  */
/* Modes for single-word and smaller quantities.  */
#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE))
 
 
/* Modes for double-word and smaller quantities.  */
/* Modes for double-word and smaller quantities.  */
#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE))
 
 
/* Modes for quad-word and smaller quantities.  */
/* Modes for quad-word and smaller quantities.  */
#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE))
 
 
/* Value is 1 if register/mode pair is acceptable on arc.  */
/* Value is 1 if register/mode pair is acceptable on arc.  */
 
 
const unsigned int arc_hard_regno_mode_ok[] = {
const unsigned int arc_hard_regno_mode_ok[] = {
  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES,
  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES,
  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES,
  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES,
  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, D_MODES,
  T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, T_MODES, D_MODES,
  D_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  D_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
 
 
  /* ??? Leave these as S_MODES for now.  */
  /* ??? Leave these as S_MODES for now.  */
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, S_MODES,
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, C_MODES
  S_MODES, S_MODES, S_MODES, S_MODES, S_MODES, C_MODES
};
};
 
 
unsigned int arc_mode_class [NUM_MACHINE_MODES];
unsigned int arc_mode_class [NUM_MACHINE_MODES];
 
 
enum reg_class arc_regno_reg_class[FIRST_PSEUDO_REGISTER];
enum reg_class arc_regno_reg_class[FIRST_PSEUDO_REGISTER];
 
 
static void
static void
arc_init_reg_tables (void)
arc_init_reg_tables (void)
{
{
  int i;
  int i;
 
 
  for (i = 0; i < NUM_MACHINE_MODES; i++)
  for (i = 0; i < NUM_MACHINE_MODES; i++)
    {
    {
      switch (GET_MODE_CLASS (i))
      switch (GET_MODE_CLASS (i))
        {
        {
        case MODE_INT:
        case MODE_INT:
        case MODE_PARTIAL_INT:
        case MODE_PARTIAL_INT:
        case MODE_COMPLEX_INT:
        case MODE_COMPLEX_INT:
          if (GET_MODE_SIZE (i) <= 4)
          if (GET_MODE_SIZE (i) <= 4)
            arc_mode_class[i] = 1 << (int) S_MODE;
            arc_mode_class[i] = 1 << (int) S_MODE;
          else if (GET_MODE_SIZE (i) == 8)
          else if (GET_MODE_SIZE (i) == 8)
            arc_mode_class[i] = 1 << (int) D_MODE;
            arc_mode_class[i] = 1 << (int) D_MODE;
          else if (GET_MODE_SIZE (i) == 16)
          else if (GET_MODE_SIZE (i) == 16)
            arc_mode_class[i] = 1 << (int) T_MODE;
            arc_mode_class[i] = 1 << (int) T_MODE;
          else if (GET_MODE_SIZE (i) == 32)
          else if (GET_MODE_SIZE (i) == 32)
            arc_mode_class[i] = 1 << (int) O_MODE;
            arc_mode_class[i] = 1 << (int) O_MODE;
          else
          else
            arc_mode_class[i] = 0;
            arc_mode_class[i] = 0;
          break;
          break;
        case MODE_FLOAT:
        case MODE_FLOAT:
        case MODE_COMPLEX_FLOAT:
        case MODE_COMPLEX_FLOAT:
          if (GET_MODE_SIZE (i) <= 4)
          if (GET_MODE_SIZE (i) <= 4)
            arc_mode_class[i] = 1 << (int) SF_MODE;
            arc_mode_class[i] = 1 << (int) SF_MODE;
          else if (GET_MODE_SIZE (i) == 8)
          else if (GET_MODE_SIZE (i) == 8)
            arc_mode_class[i] = 1 << (int) DF_MODE;
            arc_mode_class[i] = 1 << (int) DF_MODE;
          else if (GET_MODE_SIZE (i) == 16)
          else if (GET_MODE_SIZE (i) == 16)
            arc_mode_class[i] = 1 << (int) TF_MODE;
            arc_mode_class[i] = 1 << (int) TF_MODE;
          else if (GET_MODE_SIZE (i) == 32)
          else if (GET_MODE_SIZE (i) == 32)
            arc_mode_class[i] = 1 << (int) OF_MODE;
            arc_mode_class[i] = 1 << (int) OF_MODE;
          else
          else
            arc_mode_class[i] = 0;
            arc_mode_class[i] = 0;
          break;
          break;
        case MODE_CC:
        case MODE_CC:
          arc_mode_class[i] = 1 << (int) C_MODE;
          arc_mode_class[i] = 1 << (int) C_MODE;
          break;
          break;
        default:
        default:
          arc_mode_class[i] = 0;
          arc_mode_class[i] = 0;
          break;
          break;
        }
        }
    }
    }
 
 
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
    {
    {
      if (i < 60)
      if (i < 60)
        arc_regno_reg_class[i] = GENERAL_REGS;
        arc_regno_reg_class[i] = GENERAL_REGS;
      else if (i == 60)
      else if (i == 60)
        arc_regno_reg_class[i] = LPCOUNT_REG;
        arc_regno_reg_class[i] = LPCOUNT_REG;
      else if (i == 61)
      else if (i == 61)
        arc_regno_reg_class[i] = NO_REGS /* CC_REG: must be NO_REGS */;
        arc_regno_reg_class[i] = NO_REGS /* CC_REG: must be NO_REGS */;
      else
      else
        arc_regno_reg_class[i] = NO_REGS;
        arc_regno_reg_class[i] = NO_REGS;
    }
    }
}
}


/* ARC specific attribute support.
/* ARC specific attribute support.
 
 
   The ARC has these attributes:
   The ARC has these attributes:
   interrupt - for interrupt functions
   interrupt - for interrupt functions
*/
*/
 
 
const struct attribute_spec arc_attribute_table[] =
const struct attribute_spec arc_attribute_table[] =
{
{
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
  { "interrupt", 1, 1, true,  false, false, arc_handle_interrupt_attribute },
  { "interrupt", 1, 1, true,  false, false, arc_handle_interrupt_attribute },
  { NULL,        0, 0, false, false, false, NULL }
  { NULL,        0, 0, false, false, false, NULL }
};
};
 
 
/* Handle an "interrupt" attribute; arguments as in
/* Handle an "interrupt" attribute; arguments as in
   struct attribute_spec.handler.  */
   struct attribute_spec.handler.  */
static tree
static tree
arc_handle_interrupt_attribute (tree *node ATTRIBUTE_UNUSED,
arc_handle_interrupt_attribute (tree *node ATTRIBUTE_UNUSED,
                                tree name,
                                tree name,
                                tree args,
                                tree args,
                                int flags ATTRIBUTE_UNUSED,
                                int flags ATTRIBUTE_UNUSED,
                                bool *no_add_attrs)
                                bool *no_add_attrs)
{
{
  tree value = TREE_VALUE (args);
  tree value = TREE_VALUE (args);
 
 
  if (TREE_CODE (value) != STRING_CST)
  if (TREE_CODE (value) != STRING_CST)
    {
    {
      warning (OPT_Wattributes,
      warning (OPT_Wattributes,
               "argument of %qs attribute is not a string constant",
               "argument of %qs attribute is not a string constant",
               IDENTIFIER_POINTER (name));
               IDENTIFIER_POINTER (name));
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
  else if (strcmp (TREE_STRING_POINTER (value), "ilink1")
  else if (strcmp (TREE_STRING_POINTER (value), "ilink1")
           && strcmp (TREE_STRING_POINTER (value), "ilink2"))
           && strcmp (TREE_STRING_POINTER (value), "ilink2"))
    {
    {
      warning (OPT_Wattributes,
      warning (OPT_Wattributes,
               "argument of %qs attribute is not \"ilink1\" or \"ilink2\"",
               "argument of %qs attribute is not \"ilink1\" or \"ilink2\"",
               IDENTIFIER_POINTER (name));
               IDENTIFIER_POINTER (name));
      *no_add_attrs = true;
      *no_add_attrs = true;
    }
    }
 
 
  return NULL_TREE;
  return NULL_TREE;
}
}
 
 


/* Acceptable arguments to the call insn.  */
/* Acceptable arguments to the call insn.  */
 
 
int
int
call_address_operand (rtx op, enum machine_mode mode)
call_address_operand (rtx op, enum machine_mode mode)
{
{
  return (symbolic_operand (op, mode)
  return (symbolic_operand (op, mode)
          || (GET_CODE (op) == CONST_INT && LEGITIMATE_CONSTANT_P (op))
          || (GET_CODE (op) == CONST_INT && LEGITIMATE_CONSTANT_P (op))
          || (GET_CODE (op) == REG));
          || (GET_CODE (op) == REG));
}
}
 
 
int
int
call_operand (rtx op, enum machine_mode mode)
call_operand (rtx op, enum machine_mode mode)
{
{
  if (GET_CODE (op) != MEM)
  if (GET_CODE (op) != MEM)
    return 0;
    return 0;
  op = XEXP (op, 0);
  op = XEXP (op, 0);
  return call_address_operand (op, mode);
  return call_address_operand (op, mode);
}
}
 
 
/* Returns 1 if OP is a symbol reference.  */
/* Returns 1 if OP is a symbol reference.  */
 
 
int
int
symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
symbolic_operand (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:
    case CONST :
    case CONST :
      return 1;
      return 1;
    default:
    default:
      return 0;
      return 0;
    }
    }
}
}
 
 
/* Return truth value of statement that OP is a symbolic memory
/* Return truth value of statement that OP is a symbolic memory
   operand of mode MODE.  */
   operand of mode MODE.  */
 
 
int
int
symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
symbolic_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  if (GET_CODE (op) == SUBREG)
  if (GET_CODE (op) == SUBREG)
    op = SUBREG_REG (op);
    op = SUBREG_REG (op);
  if (GET_CODE (op) != MEM)
  if (GET_CODE (op) != MEM)
    return 0;
    return 0;
  op = XEXP (op, 0);
  op = XEXP (op, 0);
  return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST
  return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST
          || GET_CODE (op) == LABEL_REF);
          || GET_CODE (op) == LABEL_REF);
}
}
 
 
/* Return true if OP is a short immediate (shimm) value.  */
/* Return true if OP is a short immediate (shimm) value.  */
 
 
int
int
short_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
short_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  if (GET_CODE (op) != CONST_INT)
  if (GET_CODE (op) != CONST_INT)
    return 0;
    return 0;
  return SMALL_INT (INTVAL (op));
  return SMALL_INT (INTVAL (op));
}
}
 
 
/* Return true if OP will require a long immediate (limm) value.
/* Return true if OP will require a long immediate (limm) value.
   This is currently only used when calculating length attributes.  */
   This is currently only used when calculating length attributes.  */
 
 
int
int
long_immediate_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
long_immediate_operand (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 :
    case CONST :
    case CONST :
      return 1;
      return 1;
    case CONST_INT :
    case CONST_INT :
      return !SMALL_INT (INTVAL (op));
      return !SMALL_INT (INTVAL (op));
    case CONST_DOUBLE :
    case CONST_DOUBLE :
      /* These can happen because large unsigned 32 bit constants are
      /* These can happen because large unsigned 32 bit constants are
         represented this way (the multiplication patterns can cause these
         represented this way (the multiplication patterns can cause these
         to be generated).  They also occur for SFmode values.  */
         to be generated).  They also occur for SFmode values.  */
      return 1;
      return 1;
    default:
    default:
      break;
      break;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Return true if OP is a MEM that when used as a load or store address will
/* Return true if OP is a MEM that when used as a load or store address will
   require an 8 byte insn.
   require an 8 byte insn.
   Load and store instructions don't allow the same possibilities but they're
   Load and store instructions don't allow the same possibilities but they're
   similar enough that this one function will do.
   similar enough that this one function will do.
   This is currently only used when calculating length attributes.  */
   This is currently only used when calculating length attributes.  */
 
 
int
int
long_immediate_loadstore_operand (rtx op,
long_immediate_loadstore_operand (rtx op,
                                  enum machine_mode mode ATTRIBUTE_UNUSED)
                                  enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  if (GET_CODE (op) != MEM)
  if (GET_CODE (op) != MEM)
    return 0;
    return 0;
 
 
  op = XEXP (op, 0);
  op = XEXP (op, 0);
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case SYMBOL_REF :
    case SYMBOL_REF :
    case LABEL_REF :
    case LABEL_REF :
    case CONST :
    case CONST :
      return 1;
      return 1;
    case CONST_INT :
    case CONST_INT :
      /* This must be handled as "st c,[limm]".  Ditto for load.
      /* This must be handled as "st c,[limm]".  Ditto for load.
         Technically, the assembler could translate some possibilities to
         Technically, the assembler could translate some possibilities to
         "st c,[limm/2 + limm/2]" if limm/2 will fit in a shimm, but we don't
         "st c,[limm/2 + limm/2]" if limm/2 will fit in a shimm, but we don't
         assume that it does.  */
         assume that it does.  */
      return 1;
      return 1;
    case CONST_DOUBLE :
    case CONST_DOUBLE :
      /* These can happen because large unsigned 32 bit constants are
      /* These can happen because large unsigned 32 bit constants are
         represented this way (the multiplication patterns can cause these
         represented this way (the multiplication patterns can cause these
         to be generated).  They also occur for SFmode values.  */
         to be generated).  They also occur for SFmode values.  */
      return 1;
      return 1;
    case REG :
    case REG :
      return 0;
      return 0;
    case PLUS :
    case PLUS :
      if (GET_CODE (XEXP (op, 1)) == CONST_INT
      if (GET_CODE (XEXP (op, 1)) == CONST_INT
          && !SMALL_INT (INTVAL (XEXP (op, 1))))
          && !SMALL_INT (INTVAL (XEXP (op, 1))))
        return 1;
        return 1;
      return 0;
      return 0;
    default:
    default:
      break;
      break;
    }
    }
  return 0;
  return 0;
}
}
 
 
/* Return true if OP is an acceptable argument for a single word
/* Return true if OP is an acceptable argument for a single word
   move source.  */
   move source.  */
 
 
int
int
move_src_operand (rtx op, enum machine_mode mode)
move_src_operand (rtx op, enum machine_mode mode)
{
{
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case SYMBOL_REF :
    case SYMBOL_REF :
    case LABEL_REF :
    case LABEL_REF :
    case CONST :
    case CONST :
      return 1;
      return 1;
    case CONST_INT :
    case CONST_INT :
      return (LARGE_INT (INTVAL (op)));
      return (LARGE_INT (INTVAL (op)));
    case CONST_DOUBLE :
    case CONST_DOUBLE :
      /* We can handle DImode integer constants in SImode if the value
      /* We can handle DImode integer constants in SImode if the value
         (signed or unsigned) will fit in 32 bits.  This is needed because
         (signed or unsigned) will fit in 32 bits.  This is needed because
         large unsigned 32 bit constants are represented as CONST_DOUBLEs.  */
         large unsigned 32 bit constants are represented as CONST_DOUBLEs.  */
      if (mode == SImode)
      if (mode == SImode)
        return arc_double_limm_p (op);
        return arc_double_limm_p (op);
      /* We can handle 32 bit floating point constants.  */
      /* We can handle 32 bit floating point constants.  */
      if (mode == SFmode)
      if (mode == SFmode)
        return GET_MODE (op) == SFmode;
        return GET_MODE (op) == SFmode;
      return 0;
      return 0;
    case REG :
    case REG :
      return register_operand (op, mode);
      return register_operand (op, mode);
    case SUBREG :
    case SUBREG :
      /* (subreg (mem ...) ...) can occur here if the inner part was once a
      /* (subreg (mem ...) ...) can occur here if the inner part was once a
         pseudo-reg and is now a stack slot.  */
         pseudo-reg and is now a stack slot.  */
      if (GET_CODE (SUBREG_REG (op)) == MEM)
      if (GET_CODE (SUBREG_REG (op)) == MEM)
        return address_operand (XEXP (SUBREG_REG (op), 0), mode);
        return address_operand (XEXP (SUBREG_REG (op), 0), mode);
      else
      else
        return register_operand (op, mode);
        return register_operand (op, mode);
    case MEM :
    case MEM :
      return address_operand (XEXP (op, 0), mode);
      return address_operand (XEXP (op, 0), mode);
    default :
    default :
      return 0;
      return 0;
    }
    }
}
}
 
 
/* Return true if OP is an acceptable argument for a double word
/* Return true if OP is an acceptable argument for a double word
   move source.  */
   move source.  */
 
 
int
int
move_double_src_operand (rtx op, enum machine_mode mode)
move_double_src_operand (rtx op, enum machine_mode mode)
{
{
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case REG :
    case REG :
      return register_operand (op, mode);
      return register_operand (op, mode);
    case SUBREG :
    case SUBREG :
      /* (subreg (mem ...) ...) can occur here if the inner part was once a
      /* (subreg (mem ...) ...) can occur here if the inner part was once a
         pseudo-reg and is now a stack slot.  */
         pseudo-reg and is now a stack slot.  */
      if (GET_CODE (SUBREG_REG (op)) == MEM)
      if (GET_CODE (SUBREG_REG (op)) == MEM)
        return move_double_src_operand (SUBREG_REG (op), mode);
        return move_double_src_operand (SUBREG_REG (op), mode);
      else
      else
        return register_operand (op, mode);
        return register_operand (op, mode);
    case MEM :
    case MEM :
      /* Disallow auto inc/dec for now.  */
      /* Disallow auto inc/dec for now.  */
      if (GET_CODE (XEXP (op, 0)) == PRE_DEC
      if (GET_CODE (XEXP (op, 0)) == PRE_DEC
          || GET_CODE (XEXP (op, 0)) == PRE_INC)
          || GET_CODE (XEXP (op, 0)) == PRE_INC)
        return 0;
        return 0;
      return address_operand (XEXP (op, 0), mode);
      return address_operand (XEXP (op, 0), mode);
    case CONST_INT :
    case CONST_INT :
    case CONST_DOUBLE :
    case CONST_DOUBLE :
      return 1;
      return 1;
    default :
    default :
      return 0;
      return 0;
    }
    }
}
}
 
 
/* Return true if OP is an acceptable argument for a move destination.  */
/* Return true if OP is an acceptable argument for a move destination.  */
 
 
int
int
move_dest_operand (rtx op, enum machine_mode mode)
move_dest_operand (rtx op, enum machine_mode mode)
{
{
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case REG :
    case REG :
      return register_operand (op, mode);
      return register_operand (op, mode);
    case SUBREG :
    case SUBREG :
      /* (subreg (mem ...) ...) can occur here if the inner part was once a
      /* (subreg (mem ...) ...) can occur here if the inner part was once a
         pseudo-reg and is now a stack slot.  */
         pseudo-reg and is now a stack slot.  */
      if (GET_CODE (SUBREG_REG (op)) == MEM)
      if (GET_CODE (SUBREG_REG (op)) == MEM)
        return address_operand (XEXP (SUBREG_REG (op), 0), mode);
        return address_operand (XEXP (SUBREG_REG (op), 0), mode);
      else
      else
        return register_operand (op, mode);
        return register_operand (op, mode);
    case MEM :
    case MEM :
      return address_operand (XEXP (op, 0), mode);
      return address_operand (XEXP (op, 0), mode);
    default :
    default :
      return 0;
      return 0;
    }
    }
}
}
 
 
/* Return true if OP is valid load with update operand.  */
/* Return true if OP is valid load with update operand.  */
 
 
int
int
load_update_operand (rtx op, enum machine_mode mode)
load_update_operand (rtx op, enum machine_mode mode)
{
{
  if (GET_CODE (op) != MEM
  if (GET_CODE (op) != MEM
      || GET_MODE (op) != mode)
      || GET_MODE (op) != mode)
    return 0;
    return 0;
  op = XEXP (op, 0);
  op = XEXP (op, 0);
  if (GET_CODE (op) != PLUS
  if (GET_CODE (op) != PLUS
      || GET_MODE (op) != Pmode
      || GET_MODE (op) != Pmode
      || !register_operand (XEXP (op, 0), Pmode)
      || !register_operand (XEXP (op, 0), Pmode)
      || !nonmemory_operand (XEXP (op, 1), Pmode))
      || !nonmemory_operand (XEXP (op, 1), Pmode))
    return 0;
    return 0;
  return 1;
  return 1;
}
}
 
 
/* Return true if OP is valid store with update operand.  */
/* Return true if OP is valid store with update operand.  */
 
 
int
int
store_update_operand (rtx op, enum machine_mode mode)
store_update_operand (rtx op, enum machine_mode mode)
{
{
  if (GET_CODE (op) != MEM
  if (GET_CODE (op) != MEM
      || GET_MODE (op) != mode)
      || GET_MODE (op) != mode)
    return 0;
    return 0;
  op = XEXP (op, 0);
  op = XEXP (op, 0);
  if (GET_CODE (op) != PLUS
  if (GET_CODE (op) != PLUS
      || GET_MODE (op) != Pmode
      || GET_MODE (op) != Pmode
      || !register_operand (XEXP (op, 0), Pmode)
      || !register_operand (XEXP (op, 0), Pmode)
      || !(GET_CODE (XEXP (op, 1)) == CONST_INT
      || !(GET_CODE (XEXP (op, 1)) == CONST_INT
           && SMALL_INT (INTVAL (XEXP (op, 1)))))
           && SMALL_INT (INTVAL (XEXP (op, 1)))))
    return 0;
    return 0;
  return 1;
  return 1;
}
}
 
 
/* Return true if OP is a non-volatile non-immediate operand.
/* Return true if OP is a non-volatile non-immediate operand.
   Volatile memory refs require a special "cache-bypass" instruction
   Volatile memory refs require a special "cache-bypass" instruction
   and only the standard movXX patterns are set up to handle them.  */
   and only the standard movXX patterns are set up to handle them.  */
 
 
int
int
nonvol_nonimm_operand (rtx op, enum machine_mode mode)
nonvol_nonimm_operand (rtx op, enum machine_mode mode)
{
{
  if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op))
  if (GET_CODE (op) == MEM && MEM_VOLATILE_P (op))
    return 0;
    return 0;
  return nonimmediate_operand (op, mode);
  return nonimmediate_operand (op, mode);
}
}
 
 
/* Accept integer operands in the range -0x80000000..0x7fffffff.  We have
/* Accept integer operands in the range -0x80000000..0x7fffffff.  We have
   to check the range carefully since this predicate is used in DImode
   to check the range carefully since this predicate is used in DImode
   contexts.  */
   contexts.  */
 
 
int
int
const_sint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
const_sint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  /* All allowed constants will fit a CONST_INT.  */
  /* All allowed constants will fit a CONST_INT.  */
  return (GET_CODE (op) == CONST_INT
  return (GET_CODE (op) == CONST_INT
          && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff));
          && (INTVAL (op) >= (-0x7fffffff - 1) && INTVAL (op) <= 0x7fffffff));
}
}
 
 
/* Accept integer operands in the range 0..0xffffffff.  We have to check the
/* Accept integer operands in the range 0..0xffffffff.  We have to check the
   range carefully since this predicate is used in DImode contexts.  Also, we
   range carefully since this predicate is used in DImode contexts.  Also, we
   need some extra crud to make it work when hosted on 64-bit machines.  */
   need some extra crud to make it work when hosted on 64-bit machines.  */
 
 
int
int
const_uint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
const_uint32_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
#if HOST_BITS_PER_WIDE_INT > 32
#if HOST_BITS_PER_WIDE_INT > 32
  /* All allowed constants will fit a CONST_INT.  */
  /* All allowed constants will fit a CONST_INT.  */
  return (GET_CODE (op) == CONST_INT
  return (GET_CODE (op) == CONST_INT
          && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL));
          && (INTVAL (op) >= 0 && INTVAL (op) <= 0xffffffffL));
#else
#else
  return ((GET_CODE (op) == CONST_INT && INTVAL (op) >= 0)
  return ((GET_CODE (op) == CONST_INT && INTVAL (op) >= 0)
          || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0));
          || (GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0));
#endif
#endif
}
}
 
 
/* Return 1 if OP is a comparison operator valid for the mode of CC.
/* Return 1 if OP is a comparison operator valid for the mode of CC.
   This allows the use of MATCH_OPERATOR to recognize all the branch insns.
   This allows the use of MATCH_OPERATOR to recognize all the branch insns.
 
 
   Some insns only set a few bits in the condition code.  So only allow those
   Some insns only set a few bits in the condition code.  So only allow those
   comparisons that use the bits that are valid.  */
   comparisons that use the bits that are valid.  */
 
 
int
int
proper_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
proper_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  enum rtx_code code;
  enum rtx_code code;
  if (!COMPARISON_P (op))
  if (!COMPARISON_P (op))
    return 0;
    return 0;
 
 
  code = GET_CODE (op);
  code = GET_CODE (op);
  if (GET_MODE (XEXP (op, 0)) == CCZNmode)
  if (GET_MODE (XEXP (op, 0)) == CCZNmode)
    return (code == EQ || code == NE);
    return (code == EQ || code == NE);
  if (GET_MODE (XEXP (op, 0)) == CCZNCmode)
  if (GET_MODE (XEXP (op, 0)) == CCZNCmode)
    return (code == EQ || code == NE
    return (code == EQ || code == NE
            || code == LTU || code == GEU || code == GTU || code == LEU);
            || code == LTU || code == GEU || code == GTU || code == LEU);
  return 1;
  return 1;
}
}


/* Misc. utilities.  */
/* Misc. utilities.  */
 
 
/* X and Y are two things to compare using CODE.  Emit the compare insn and
/* X and Y are two things to compare using CODE.  Emit the compare insn and
   return the rtx for the cc reg in the proper mode.  */
   return the rtx for the cc reg in the proper mode.  */
 
 
rtx
rtx
gen_compare_reg (enum rtx_code code, rtx x, rtx y)
gen_compare_reg (enum rtx_code code, rtx x, rtx y)
{
{
  enum machine_mode mode = SELECT_CC_MODE (code, x, y);
  enum machine_mode mode = SELECT_CC_MODE (code, x, y);
  rtx cc_reg;
  rtx cc_reg;
 
 
  cc_reg = gen_rtx_REG (mode, 61);
  cc_reg = gen_rtx_REG (mode, 61);
 
 
  emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
  emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
                          gen_rtx_COMPARE (mode, x, y)));
                          gen_rtx_COMPARE (mode, x, y)));
 
 
  return cc_reg;
  return cc_reg;
}
}
 
 
/* Return 1 if VALUE, a const_double, will fit in a limm (4 byte number).
/* Return 1 if VALUE, a const_double, will fit in a limm (4 byte number).
   We assume the value can be either signed or unsigned.  */
   We assume the value can be either signed or unsigned.  */
 
 
int
int
arc_double_limm_p (rtx value)
arc_double_limm_p (rtx value)
{
{
  HOST_WIDE_INT low, high;
  HOST_WIDE_INT low, high;
 
 
  gcc_assert (GET_CODE (value) == CONST_DOUBLE);
  gcc_assert (GET_CODE (value) == CONST_DOUBLE);
 
 
  low = CONST_DOUBLE_LOW (value);
  low = CONST_DOUBLE_LOW (value);
  high = CONST_DOUBLE_HIGH (value);
  high = CONST_DOUBLE_HIGH (value);
 
 
  if (low & 0x80000000)
  if (low & 0x80000000)
    {
    {
      return (((unsigned HOST_WIDE_INT) low <= 0xffffffff && high == 0)
      return (((unsigned HOST_WIDE_INT) low <= 0xffffffff && high == 0)
              || (((low & - (unsigned HOST_WIDE_INT) 0x80000000)
              || (((low & - (unsigned HOST_WIDE_INT) 0x80000000)
                   == - (unsigned HOST_WIDE_INT) 0x80000000)
                   == - (unsigned HOST_WIDE_INT) 0x80000000)
                  && high == -1));
                  && high == -1));
    }
    }
  else
  else
    {
    {
      return (unsigned HOST_WIDE_INT) low <= 0x7fffffff && high == 0;
      return (unsigned HOST_WIDE_INT) low <= 0x7fffffff && high == 0;
    }
    }
}
}


/* Do any needed setup for a variadic function.  For the ARC, we must
/* Do any needed setup for a variadic function.  For the ARC, we must
   create a register parameter block, and then copy any anonymous arguments
   create a register parameter block, and then copy any anonymous arguments
   in registers to memory.
   in registers to memory.
 
 
   CUM has not been updated for the last named argument which has type TYPE
   CUM has not been updated for the last named argument which has type TYPE
   and mode MODE, and we rely on this fact.
   and mode MODE, and we rely on this fact.
 
 
   We do things a little weird here.  We're supposed to only allocate space
   We do things a little weird here.  We're supposed to only allocate space
   for the anonymous arguments.  However we need to keep the stack eight byte
   for the anonymous arguments.  However we need to keep the stack eight byte
   aligned.  So we round the space up if necessary, and leave it to va_start
   aligned.  So we round the space up if necessary, and leave it to va_start
   to compensate.  */
   to compensate.  */
 
 
static void
static void
arc_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
arc_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
                            enum machine_mode mode,
                            enum machine_mode mode,
                            tree type ATTRIBUTE_UNUSED,
                            tree type ATTRIBUTE_UNUSED,
                            int *pretend_size,
                            int *pretend_size,
                            int no_rtl)
                            int no_rtl)
{
{
  int first_anon_arg;
  int first_anon_arg;
 
 
  /* All BLKmode values are passed by reference.  */
  /* All BLKmode values are passed by reference.  */
  gcc_assert (mode != BLKmode);
  gcc_assert (mode != BLKmode);
 
 
  first_anon_arg = *cum + ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
  first_anon_arg = *cum + ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
                           / UNITS_PER_WORD);
                           / UNITS_PER_WORD);
 
 
  if (first_anon_arg < MAX_ARC_PARM_REGS && !no_rtl)
  if (first_anon_arg < MAX_ARC_PARM_REGS && !no_rtl)
    {
    {
      /* Note that first_reg_offset < MAX_ARC_PARM_REGS.  */
      /* Note that first_reg_offset < MAX_ARC_PARM_REGS.  */
      int first_reg_offset = first_anon_arg;
      int first_reg_offset = first_anon_arg;
      /* Size in words to "pretend" allocate.  */
      /* Size in words to "pretend" allocate.  */
      int size = MAX_ARC_PARM_REGS - first_reg_offset;
      int size = MAX_ARC_PARM_REGS - first_reg_offset;
      /* Extra slop to keep stack eight byte aligned.  */
      /* Extra slop to keep stack eight byte aligned.  */
      int align_slop = size & 1;
      int align_slop = size & 1;
      rtx regblock;
      rtx regblock;
 
 
      regblock = gen_rtx_MEM (BLKmode,
      regblock = gen_rtx_MEM (BLKmode,
                              plus_constant (arg_pointer_rtx,
                              plus_constant (arg_pointer_rtx,
                                             FIRST_PARM_OFFSET (0)
                                             FIRST_PARM_OFFSET (0)
                                             + align_slop * UNITS_PER_WORD));
                                             + align_slop * UNITS_PER_WORD));
      set_mem_alias_set (regblock, get_varargs_alias_set ());
      set_mem_alias_set (regblock, get_varargs_alias_set ());
      set_mem_align (regblock, BITS_PER_WORD);
      set_mem_align (regblock, BITS_PER_WORD);
      move_block_from_reg (first_reg_offset, regblock,
      move_block_from_reg (first_reg_offset, regblock,
                           MAX_ARC_PARM_REGS - first_reg_offset);
                           MAX_ARC_PARM_REGS - first_reg_offset);
 
 
      *pretend_size = ((MAX_ARC_PARM_REGS - first_reg_offset + align_slop)
      *pretend_size = ((MAX_ARC_PARM_REGS - first_reg_offset + align_slop)
                       * UNITS_PER_WORD);
                       * UNITS_PER_WORD);
    }
    }
}
}


/* Cost functions.  */
/* Cost functions.  */
 
 
/* Compute a (partial) cost for rtx X.  Return true if the complete
/* Compute a (partial) cost for rtx X.  Return true if the complete
   cost has been computed, and false if subexpressions should be
   cost has been computed, and false if subexpressions should be
   scanned.  In either case, *TOTAL contains the cost result.  */
   scanned.  In either case, *TOTAL contains the cost result.  */
 
 
static bool
static bool
arc_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
arc_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int *total)
{
{
  switch (code)
  switch (code)
    {
    {
      /* Small integers are as cheap as registers.  4 byte values can
      /* Small integers are as cheap as registers.  4 byte values can
         be fetched as immediate constants - let's give that the cost
         be fetched as immediate constants - let's give that the cost
         of an extra insn.  */
         of an extra insn.  */
    case CONST_INT:
    case CONST_INT:
      if (SMALL_INT (INTVAL (x)))
      if (SMALL_INT (INTVAL (x)))
        {
        {
          *total = 0;
          *total = 0;
          return true;
          return true;
        }
        }
      /* FALLTHRU */
      /* FALLTHRU */
 
 
    case CONST:
    case CONST:
    case LABEL_REF:
    case LABEL_REF:
    case SYMBOL_REF:
    case SYMBOL_REF:
      *total = COSTS_N_INSNS (1);
      *total = COSTS_N_INSNS (1);
      return true;
      return true;
 
 
    case CONST_DOUBLE:
    case CONST_DOUBLE:
      {
      {
        rtx high, low;
        rtx high, low;
        split_double (x, &high, &low);
        split_double (x, &high, &low);
        *total = COSTS_N_INSNS (!SMALL_INT (INTVAL (high))
        *total = COSTS_N_INSNS (!SMALL_INT (INTVAL (high))
                                + !SMALL_INT (INTVAL (low)));
                                + !SMALL_INT (INTVAL (low)));
        return true;
        return true;
      }
      }
 
 
    /* Encourage synth_mult to find a synthetic multiply when reasonable.
    /* Encourage synth_mult to find a synthetic multiply when reasonable.
       If we need more than 12 insns to do a multiply, then go out-of-line,
       If we need more than 12 insns to do a multiply, then go out-of-line,
       since the call overhead will be < 10% of the cost of the multiply.  */
       since the call overhead will be < 10% of the cost of the multiply.  */
    case ASHIFT:
    case ASHIFT:
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
      if (TARGET_SHIFTER)
      if (TARGET_SHIFTER)
        *total = COSTS_N_INSNS (1);
        *total = COSTS_N_INSNS (1);
      else if (GET_CODE (XEXP (x, 1)) != CONST_INT)
      else if (GET_CODE (XEXP (x, 1)) != CONST_INT)
        *total = COSTS_N_INSNS (16);
        *total = COSTS_N_INSNS (16);
      else
      else
        *total = COSTS_N_INSNS (INTVAL (XEXP ((x), 1)));
        *total = COSTS_N_INSNS (INTVAL (XEXP ((x), 1)));
      return false;
      return false;
 
 
    default:
    default:
      return false;
      return false;
    }
    }
}
}
 
 
 
 
/* Provide the costs of an addressing mode that contains ADDR.
/* Provide the costs of an addressing mode that contains ADDR.
   If ADDR is not a valid address, its cost is irrelevant.  */
   If ADDR is not a valid address, its cost is irrelevant.  */
 
 
static int
static int
arc_address_cost (rtx addr)
arc_address_cost (rtx addr)
{
{
  switch (GET_CODE (addr))
  switch (GET_CODE (addr))
    {
    {
    case REG :
    case REG :
      return 1;
      return 1;
 
 
    case LABEL_REF :
    case LABEL_REF :
    case SYMBOL_REF :
    case SYMBOL_REF :
    case CONST :
    case CONST :
      return 2;
      return 2;
 
 
    case PLUS :
    case PLUS :
      {
      {
        register rtx plus0 = XEXP (addr, 0);
        register rtx plus0 = XEXP (addr, 0);
        register rtx plus1 = XEXP (addr, 1);
        register rtx plus1 = XEXP (addr, 1);
 
 
        if (GET_CODE (plus0) != REG)
        if (GET_CODE (plus0) != REG)
          break;
          break;
 
 
        switch (GET_CODE (plus1))
        switch (GET_CODE (plus1))
          {
          {
          case CONST_INT :
          case CONST_INT :
            return SMALL_INT (plus1) ? 1 : 2;
            return SMALL_INT (plus1) ? 1 : 2;
          case CONST :
          case CONST :
          case SYMBOL_REF :
          case SYMBOL_REF :
          case LABEL_REF :
          case LABEL_REF :
            return 2;
            return 2;
          default:
          default:
            break;
            break;
          }
          }
        break;
        break;
      }
      }
    default:
    default:
      break;
      break;
    }
    }
 
 
  return 4;
  return 4;
}
}


/* Function prologue/epilogue handlers.  */
/* Function prologue/epilogue handlers.  */
 
 
/* ARC stack frames look like:
/* ARC stack frames look like:
 
 
             Before call                       After call
             Before call                       After call
        +-----------------------+       +-----------------------+
        +-----------------------+       +-----------------------+
        |                       |       |                       |
        |                       |       |                       |
   high |  local variables,     |       |  local variables,     |
   high |  local variables,     |       |  local variables,     |
   mem  |  reg save area, etc.  |       |  reg save area, etc.  |
   mem  |  reg save area, etc.  |       |  reg save area, etc.  |
        |                       |       |                       |
        |                       |       |                       |
        +-----------------------+       +-----------------------+
        +-----------------------+       +-----------------------+
        |                       |       |                       |
        |                       |       |                       |
        |  arguments on stack.  |       |  arguments on stack.  |
        |  arguments on stack.  |       |  arguments on stack.  |
        |                       |       |                       |
        |                       |       |                       |
 SP+16->+-----------------------+FP+48->+-----------------------+
 SP+16->+-----------------------+FP+48->+-----------------------+
        | 4 word save area for  |       |  reg parm save area,  |
        | 4 word save area for  |       |  reg parm save area,  |
        | return addr, prev %fp |       |  only created for     |
        | return addr, prev %fp |       |  only created for     |
  SP+0->+-----------------------+       |  variable argument    |
  SP+0->+-----------------------+       |  variable argument    |
                                        |  functions            |
                                        |  functions            |
                                 FP+16->+-----------------------+
                                 FP+16->+-----------------------+
                                        | 4 word save area for  |
                                        | 4 word save area for  |
                                        | return addr, prev %fp |
                                        | return addr, prev %fp |
                                  FP+0->+-----------------------+
                                  FP+0->+-----------------------+
                                        |                       |
                                        |                       |
                                        |  local variables      |
                                        |  local variables      |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  register save area   |
                                        |  register save area   |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  alloca allocations   |
                                        |  alloca allocations   |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  arguments on stack   |
                                        |  arguments on stack   |
                                        |                       |
                                        |                       |
                                 SP+16->+-----------------------+
                                 SP+16->+-----------------------+
   low                                  | 4 word save area for  |
   low                                  | 4 word save area for  |
   memory                               | return addr, prev %fp |
   memory                               | return addr, prev %fp |
                                  SP+0->+-----------------------+
                                  SP+0->+-----------------------+
 
 
Notes:
Notes:
1) The "reg parm save area" does not exist for non variable argument fns.
1) The "reg parm save area" does not exist for non variable argument fns.
   The "reg parm save area" can be eliminated completely if we created our
   The "reg parm save area" can be eliminated completely if we created our
   own va-arc.h, but that has tradeoffs as well (so it's not done).  */
   own va-arc.h, but that has tradeoffs as well (so it's not done).  */
 
 
/* Structure to be filled in by arc_compute_frame_size with register
/* Structure to be filled in by arc_compute_frame_size with register
   save masks, and offsets for the current function.  */
   save masks, and offsets for the current function.  */
struct arc_frame_info
struct arc_frame_info
{
{
  unsigned int total_size;      /* # bytes that the entire frame takes up.  */
  unsigned int total_size;      /* # bytes that the entire frame takes up.  */
  unsigned int extra_size;      /* # bytes of extra stuff.  */
  unsigned int extra_size;      /* # bytes of extra stuff.  */
  unsigned int pretend_size;    /* # bytes we push and pretend caller did.  */
  unsigned int pretend_size;    /* # bytes we push and pretend caller did.  */
  unsigned int args_size;       /* # bytes that outgoing arguments take up.  */
  unsigned int args_size;       /* # bytes that outgoing arguments take up.  */
  unsigned int reg_size;        /* # bytes needed to store regs.  */
  unsigned int reg_size;        /* # bytes needed to store regs.  */
  unsigned int var_size;        /* # bytes that variables take up.  */
  unsigned int var_size;        /* # bytes that variables take up.  */
  unsigned int reg_offset;      /* Offset from new sp to store regs.  */
  unsigned int reg_offset;      /* Offset from new sp to store regs.  */
  unsigned int gmask;           /* Mask of saved gp registers.  */
  unsigned int gmask;           /* Mask of saved gp registers.  */
  int          initialized;     /* Nonzero if frame size already calculated.  */
  int          initialized;     /* Nonzero if frame size already calculated.  */
};
};
 
 
/* Current frame information calculated by arc_compute_frame_size.  */
/* Current frame information calculated by arc_compute_frame_size.  */
static struct arc_frame_info current_frame_info;
static struct arc_frame_info current_frame_info;
 
 
/* Zero structure to initialize current_frame_info.  */
/* Zero structure to initialize current_frame_info.  */
static struct arc_frame_info zero_frame_info;
static struct arc_frame_info zero_frame_info;
 
 
/* Type of function DECL.
/* Type of function DECL.
 
 
   The result is cached.  To reset the cache at the end of a function,
   The result is cached.  To reset the cache at the end of a function,
   call with DECL = NULL_TREE.  */
   call with DECL = NULL_TREE.  */
 
 
enum arc_function_type
enum arc_function_type
arc_compute_function_type (tree decl)
arc_compute_function_type (tree decl)
{
{
  tree a;
  tree a;
  /* Cached value.  */
  /* Cached value.  */
  static enum arc_function_type fn_type = ARC_FUNCTION_UNKNOWN;
  static enum arc_function_type fn_type = ARC_FUNCTION_UNKNOWN;
  /* Last function we were called for.  */
  /* Last function we were called for.  */
  static tree last_fn = NULL_TREE;
  static tree last_fn = NULL_TREE;
 
 
  /* Resetting the cached value?  */
  /* Resetting the cached value?  */
  if (decl == NULL_TREE)
  if (decl == NULL_TREE)
    {
    {
      fn_type = ARC_FUNCTION_UNKNOWN;
      fn_type = ARC_FUNCTION_UNKNOWN;
      last_fn = NULL_TREE;
      last_fn = NULL_TREE;
      return fn_type;
      return fn_type;
    }
    }
 
 
  if (decl == last_fn && fn_type != ARC_FUNCTION_UNKNOWN)
  if (decl == last_fn && fn_type != ARC_FUNCTION_UNKNOWN)
    return fn_type;
    return fn_type;
 
 
  /* Assume we have a normal function (not an interrupt handler).  */
  /* Assume we have a normal function (not an interrupt handler).  */
  fn_type = ARC_FUNCTION_NORMAL;
  fn_type = ARC_FUNCTION_NORMAL;
 
 
  /* Now see if this is an interrupt handler.  */
  /* Now see if this is an interrupt handler.  */
  for (a = DECL_ATTRIBUTES (current_function_decl);
  for (a = DECL_ATTRIBUTES (current_function_decl);
       a;
       a;
       a = TREE_CHAIN (a))
       a = TREE_CHAIN (a))
    {
    {
      tree name = TREE_PURPOSE (a), args = TREE_VALUE (a);
      tree name = TREE_PURPOSE (a), args = TREE_VALUE (a);
 
 
      if (name == get_identifier ("__interrupt__")
      if (name == get_identifier ("__interrupt__")
          && list_length (args) == 1
          && list_length (args) == 1
          && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
          && TREE_CODE (TREE_VALUE (args)) == STRING_CST)
        {
        {
          tree value = TREE_VALUE (args);
          tree value = TREE_VALUE (args);
 
 
          if (!strcmp (TREE_STRING_POINTER (value), "ilink1"))
          if (!strcmp (TREE_STRING_POINTER (value), "ilink1"))
            fn_type = ARC_FUNCTION_ILINK1;
            fn_type = ARC_FUNCTION_ILINK1;
          else if (!strcmp (TREE_STRING_POINTER (value), "ilink2"))
          else if (!strcmp (TREE_STRING_POINTER (value), "ilink2"))
            fn_type = ARC_FUNCTION_ILINK2;
            fn_type = ARC_FUNCTION_ILINK2;
          else
          else
            gcc_unreachable ();
            gcc_unreachable ();
          break;
          break;
        }
        }
    }
    }
 
 
  last_fn = decl;
  last_fn = decl;
  return fn_type;
  return fn_type;
}
}
 
 
#define ILINK1_REGNUM 29
#define ILINK1_REGNUM 29
#define ILINK2_REGNUM 30
#define ILINK2_REGNUM 30
#define RETURN_ADDR_REGNUM 31
#define RETURN_ADDR_REGNUM 31
#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM))
#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM))
#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM))
#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM))
 
 
/* Tell prologue and epilogue if register REGNO should be saved / restored.
/* Tell prologue and epilogue if register REGNO should be saved / restored.
   The return address and frame pointer are treated separately.
   The return address and frame pointer are treated separately.
   Don't consider them here.  */
   Don't consider them here.  */
#define MUST_SAVE_REGISTER(regno, interrupt_p) \
#define MUST_SAVE_REGISTER(regno, interrupt_p) \
((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \
((regno) != RETURN_ADDR_REGNUM && (regno) != FRAME_POINTER_REGNUM \
 && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p)))
 && (regs_ever_live[regno] && (!call_used_regs[regno] || interrupt_p)))
 
 
#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM])
#define MUST_SAVE_RETURN_ADDR (regs_ever_live[RETURN_ADDR_REGNUM])
 
 
/* Return the bytes needed to compute the frame pointer from the current
/* Return the bytes needed to compute the frame pointer from the current
   stack pointer.
   stack pointer.
 
 
   SIZE is the size needed for local variables.  */
   SIZE is the size needed for local variables.  */
 
 
unsigned int
unsigned int
arc_compute_frame_size (int size /* # of var. bytes allocated.  */)
arc_compute_frame_size (int size /* # of var. bytes allocated.  */)
{
{
  int regno;
  int regno;
  unsigned int total_size, var_size, args_size, pretend_size, extra_size;
  unsigned int total_size, var_size, args_size, pretend_size, extra_size;
  unsigned int reg_size, reg_offset;
  unsigned int reg_size, reg_offset;
  unsigned int gmask;
  unsigned int gmask;
  enum arc_function_type fn_type;
  enum arc_function_type fn_type;
  int interrupt_p;
  int interrupt_p;
 
 
  var_size      = size;
  var_size      = size;
  args_size     = current_function_outgoing_args_size;
  args_size     = current_function_outgoing_args_size;
  pretend_size  = current_function_pretend_args_size;
  pretend_size  = current_function_pretend_args_size;
  extra_size    = FIRST_PARM_OFFSET (0);
  extra_size    = FIRST_PARM_OFFSET (0);
  total_size    = extra_size + pretend_size + args_size + var_size;
  total_size    = extra_size + pretend_size + args_size + var_size;
  reg_offset    = FIRST_PARM_OFFSET(0) + current_function_outgoing_args_size;
  reg_offset    = FIRST_PARM_OFFSET(0) + current_function_outgoing_args_size;
  reg_size      = 0;
  reg_size      = 0;
  gmask         = 0;
  gmask         = 0;
 
 
  /* See if this is an interrupt handler.  Call used registers must be saved
  /* See if this is an interrupt handler.  Call used registers must be saved
     for them too.  */
     for them too.  */
  fn_type = arc_compute_function_type (current_function_decl);
  fn_type = arc_compute_function_type (current_function_decl);
  interrupt_p = ARC_INTERRUPT_P (fn_type);
  interrupt_p = ARC_INTERRUPT_P (fn_type);
 
 
  /* Calculate space needed for registers.
  /* Calculate space needed for registers.
     ??? We ignore the extension registers for now.  */
     ??? We ignore the extension registers for now.  */
 
 
  for (regno = 0; regno <= 31; regno++)
  for (regno = 0; regno <= 31; regno++)
    {
    {
      if (MUST_SAVE_REGISTER (regno, interrupt_p))
      if (MUST_SAVE_REGISTER (regno, interrupt_p))
        {
        {
          reg_size += UNITS_PER_WORD;
          reg_size += UNITS_PER_WORD;
          gmask |= 1 << regno;
          gmask |= 1 << regno;
        }
        }
    }
    }
 
 
  total_size += reg_size;
  total_size += reg_size;
 
 
  /* If the only space to allocate is the fp/blink save area this is an
  /* If the only space to allocate is the fp/blink save area this is an
     empty frame.  However, if we'll be making a function call we need to
     empty frame.  However, if we'll be making a function call we need to
     allocate a stack frame for our callee's fp/blink save area.  */
     allocate a stack frame for our callee's fp/blink save area.  */
  if (total_size == extra_size
  if (total_size == extra_size
      && !MUST_SAVE_RETURN_ADDR)
      && !MUST_SAVE_RETURN_ADDR)
    total_size = extra_size = 0;
    total_size = extra_size = 0;
 
 
  total_size = ARC_STACK_ALIGN (total_size);
  total_size = ARC_STACK_ALIGN (total_size);
 
 
  /* Save computed information.  */
  /* Save computed information.  */
  current_frame_info.total_size   = total_size;
  current_frame_info.total_size   = total_size;
  current_frame_info.extra_size   = extra_size;
  current_frame_info.extra_size   = extra_size;
  current_frame_info.pretend_size = pretend_size;
  current_frame_info.pretend_size = pretend_size;
  current_frame_info.var_size     = var_size;
  current_frame_info.var_size     = var_size;
  current_frame_info.args_size    = args_size;
  current_frame_info.args_size    = args_size;
  current_frame_info.reg_size     = reg_size;
  current_frame_info.reg_size     = reg_size;
  current_frame_info.reg_offset   = reg_offset;
  current_frame_info.reg_offset   = reg_offset;
  current_frame_info.gmask        = gmask;
  current_frame_info.gmask        = gmask;
  current_frame_info.initialized  = reload_completed;
  current_frame_info.initialized  = reload_completed;
 
 
  /* Ok, we're done.  */
  /* Ok, we're done.  */
  return total_size;
  return total_size;
}
}


/* Common code to save/restore registers.  */
/* Common code to save/restore registers.  */
 
 
void
void
arc_save_restore (FILE *file,
arc_save_restore (FILE *file,
                  const char *base_reg,
                  const char *base_reg,
                  unsigned int offset,
                  unsigned int offset,
                  unsigned int gmask,
                  unsigned int gmask,
                  const char *op)
                  const char *op)
{
{
  int regno;
  int regno;
 
 
  if (gmask == 0)
  if (gmask == 0)
    return;
    return;
 
 
  for (regno = 0; regno <= 31; regno++)
  for (regno = 0; regno <= 31; regno++)
    {
    {
      if ((gmask & (1L << regno)) != 0)
      if ((gmask & (1L << regno)) != 0)
        {
        {
          fprintf (file, "\t%s %s,[%s,%d]\n",
          fprintf (file, "\t%s %s,[%s,%d]\n",
                     op, reg_names[regno], base_reg, offset);
                     op, reg_names[regno], base_reg, offset);
          offset += UNITS_PER_WORD;
          offset += UNITS_PER_WORD;
        }
        }
    }
    }
}
}


/* Target hook to assemble an integer object.  The ARC version needs to
/* Target hook to assemble an integer object.  The ARC version needs to
   emit a special directive for references to labels and function
   emit a special directive for references to labels and function
   symbols.  */
   symbols.  */
 
 
static bool
static bool
arc_assemble_integer (rtx x, unsigned int size, int aligned_p)
arc_assemble_integer (rtx x, unsigned int size, int aligned_p)
{
{
  if (size == UNITS_PER_WORD && aligned_p
  if (size == UNITS_PER_WORD && aligned_p
      && ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
      && ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
          || GET_CODE (x) == LABEL_REF))
          || GET_CODE (x) == LABEL_REF))
    {
    {
      fputs ("\t.word\t%st(", asm_out_file);
      fputs ("\t.word\t%st(", asm_out_file);
      output_addr_const (asm_out_file, x);
      output_addr_const (asm_out_file, x);
      fputs (")\n", asm_out_file);
      fputs (")\n", asm_out_file);
      return true;
      return true;
    }
    }
  return default_assemble_integer (x, size, aligned_p);
  return default_assemble_integer (x, size, aligned_p);
}
}


/* Set up the stack and frame pointer (if desired) for the function.  */
/* Set up the stack and frame pointer (if desired) for the function.  */
 
 
static void
static void
arc_output_function_prologue (FILE *file, HOST_WIDE_INT size)
arc_output_function_prologue (FILE *file, HOST_WIDE_INT size)
{
{
  const char *sp_str = reg_names[STACK_POINTER_REGNUM];
  const char *sp_str = reg_names[STACK_POINTER_REGNUM];
  const char *fp_str = reg_names[FRAME_POINTER_REGNUM];
  const char *fp_str = reg_names[FRAME_POINTER_REGNUM];
  unsigned int gmask = current_frame_info.gmask;
  unsigned int gmask = current_frame_info.gmask;
  enum arc_function_type fn_type = arc_compute_function_type (current_function_decl);
  enum arc_function_type fn_type = arc_compute_function_type (current_function_decl);
 
 
  /* If this is an interrupt handler, set up our stack frame.
  /* If this is an interrupt handler, set up our stack frame.
     ??? Optimize later.  */
     ??? Optimize later.  */
  if (ARC_INTERRUPT_P (fn_type))
  if (ARC_INTERRUPT_P (fn_type))
    {
    {
      fprintf (file, "\t%s interrupt handler\n",
      fprintf (file, "\t%s interrupt handler\n",
               ASM_COMMENT_START);
               ASM_COMMENT_START);
      fprintf (file, "\tsub %s,%s,16\n", sp_str, sp_str);
      fprintf (file, "\tsub %s,%s,16\n", sp_str, sp_str);
    }
    }
 
 
  /* This is only for the human reader.  */
  /* This is only for the human reader.  */
  fprintf (file, "\t%s BEGIN PROLOGUE %s vars= %d, regs= %d, args= %d, extra= %d\n",
  fprintf (file, "\t%s BEGIN PROLOGUE %s vars= %d, regs= %d, args= %d, extra= %d\n",
           ASM_COMMENT_START, ASM_COMMENT_START,
           ASM_COMMENT_START, ASM_COMMENT_START,
           current_frame_info.var_size,
           current_frame_info.var_size,
           current_frame_info.reg_size / 4,
           current_frame_info.reg_size / 4,
           current_frame_info.args_size,
           current_frame_info.args_size,
           current_frame_info.extra_size);
           current_frame_info.extra_size);
 
 
  size = ARC_STACK_ALIGN (size);
  size = ARC_STACK_ALIGN (size);
  size = (! current_frame_info.initialized
  size = (! current_frame_info.initialized
           ? arc_compute_frame_size (size)
           ? arc_compute_frame_size (size)
           : current_frame_info.total_size);
           : current_frame_info.total_size);
 
 
  /* These cases shouldn't happen.  Catch them now.  */
  /* These cases shouldn't happen.  Catch them now.  */
  gcc_assert (size || !gmask);
  gcc_assert (size || !gmask);
 
 
  /* Allocate space for register arguments if this is a variadic function.  */
  /* Allocate space for register arguments if this is a variadic function.  */
  if (current_frame_info.pretend_size != 0)
  if (current_frame_info.pretend_size != 0)
    fprintf (file, "\tsub %s,%s,%d\n",
    fprintf (file, "\tsub %s,%s,%d\n",
             sp_str, sp_str, current_frame_info.pretend_size);
             sp_str, sp_str, current_frame_info.pretend_size);
 
 
  /* The home-grown ABI says link register is saved first.  */
  /* The home-grown ABI says link register is saved first.  */
  if (MUST_SAVE_RETURN_ADDR)
  if (MUST_SAVE_RETURN_ADDR)
    fprintf (file, "\tst %s,[%s,%d]\n",
    fprintf (file, "\tst %s,[%s,%d]\n",
             reg_names[RETURN_ADDR_REGNUM], sp_str, UNITS_PER_WORD);
             reg_names[RETURN_ADDR_REGNUM], sp_str, UNITS_PER_WORD);
 
 
  /* Set up the previous frame pointer next (if we need to).  */
  /* Set up the previous frame pointer next (if we need to).  */
  if (frame_pointer_needed)
  if (frame_pointer_needed)
    {
    {
      fprintf (file, "\tst %s,[%s]\n", fp_str, sp_str);
      fprintf (file, "\tst %s,[%s]\n", fp_str, sp_str);
      fprintf (file, "\tmov %s,%s\n", fp_str, sp_str);
      fprintf (file, "\tmov %s,%s\n", fp_str, sp_str);
    }
    }
 
 
  /* ??? We don't handle the case where the saved regs are more than 252
  /* ??? We don't handle the case where the saved regs are more than 252
     bytes away from sp.  This can be handled by decrementing sp once, saving
     bytes away from sp.  This can be handled by decrementing sp once, saving
     the regs, and then decrementing it again.  The epilogue doesn't have this
     the regs, and then decrementing it again.  The epilogue doesn't have this
     problem as the `ld' insn takes reg+limm values (though it would be more
     problem as the `ld' insn takes reg+limm values (though it would be more
     efficient to avoid reg+limm).  */
     efficient to avoid reg+limm).  */
 
 
  /* Allocate the stack frame.  */
  /* Allocate the stack frame.  */
  if (size - current_frame_info.pretend_size > 0)
  if (size - current_frame_info.pretend_size > 0)
    fprintf (file, "\tsub %s,%s," HOST_WIDE_INT_PRINT_DEC "\n",
    fprintf (file, "\tsub %s,%s," HOST_WIDE_INT_PRINT_DEC "\n",
             sp_str, sp_str, size - current_frame_info.pretend_size);
             sp_str, sp_str, size - current_frame_info.pretend_size);
 
 
  /* Save any needed call-saved regs (and call-used if this is an
  /* Save any needed call-saved regs (and call-used if this is an
     interrupt handler).  */
     interrupt handler).  */
  arc_save_restore (file, sp_str, current_frame_info.reg_offset,
  arc_save_restore (file, sp_str, current_frame_info.reg_offset,
                    /* The zeroing of these two bits is unnecessary,
                    /* The zeroing of these two bits is unnecessary,
                       but leave this in for clarity.  */
                       but leave this in for clarity.  */
                    gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
                    gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
                    "st");
                    "st");
 
 
  fprintf (file, "\t%s END PROLOGUE\n", ASM_COMMENT_START);
  fprintf (file, "\t%s END PROLOGUE\n", ASM_COMMENT_START);
}
}


/* Do any necessary cleanup after a function to restore stack, frame,
/* Do any necessary cleanup after a function to restore stack, frame,
   and regs.  */
   and regs.  */
 
 
static void
static void
arc_output_function_epilogue (FILE *file, HOST_WIDE_INT size)
arc_output_function_epilogue (FILE *file, HOST_WIDE_INT size)
{
{
  rtx epilogue_delay = current_function_epilogue_delay_list;
  rtx epilogue_delay = current_function_epilogue_delay_list;
  int noepilogue = FALSE;
  int noepilogue = FALSE;
  enum arc_function_type fn_type = arc_compute_function_type (current_function_decl);
  enum arc_function_type fn_type = arc_compute_function_type (current_function_decl);
 
 
  /* This is only for the human reader.  */
  /* This is only for the human reader.  */
  fprintf (file, "\t%s EPILOGUE\n", ASM_COMMENT_START);
  fprintf (file, "\t%s EPILOGUE\n", ASM_COMMENT_START);
 
 
  size = ARC_STACK_ALIGN (size);
  size = ARC_STACK_ALIGN (size);
  size = (!current_frame_info.initialized
  size = (!current_frame_info.initialized
           ? arc_compute_frame_size (size)
           ? arc_compute_frame_size (size)
           : current_frame_info.total_size);
           : current_frame_info.total_size);
 
 
  if (size == 0 && epilogue_delay == 0)
  if (size == 0 && epilogue_delay == 0)
    {
    {
      rtx insn = get_last_insn ();
      rtx insn = get_last_insn ();
 
 
      /* If the last insn was a BARRIER, we don't have to write any code
      /* If the last insn was a BARRIER, we don't have to write any code
         because a jump (aka return) was put there.  */
         because a jump (aka return) was put there.  */
      if (GET_CODE (insn) == NOTE)
      if (GET_CODE (insn) == NOTE)
        insn = prev_nonnote_insn (insn);
        insn = prev_nonnote_insn (insn);
      if (insn && GET_CODE (insn) == BARRIER)
      if (insn && GET_CODE (insn) == BARRIER)
        noepilogue = TRUE;
        noepilogue = TRUE;
    }
    }
 
 
  if (!noepilogue)
  if (!noepilogue)
    {
    {
      unsigned int pretend_size = current_frame_info.pretend_size;
      unsigned int pretend_size = current_frame_info.pretend_size;
      unsigned int frame_size = size - pretend_size;
      unsigned int frame_size = size - pretend_size;
      int restored, fp_restored_p;
      int restored, fp_restored_p;
      int can_trust_sp_p = !current_function_calls_alloca;
      int can_trust_sp_p = !current_function_calls_alloca;
      const char *sp_str = reg_names[STACK_POINTER_REGNUM];
      const char *sp_str = reg_names[STACK_POINTER_REGNUM];
      const char *fp_str = reg_names[FRAME_POINTER_REGNUM];
      const char *fp_str = reg_names[FRAME_POINTER_REGNUM];
 
 
      /* ??? There are lots of optimizations that can be done here.
      /* ??? There are lots of optimizations that can be done here.
         EG: Use fp to restore regs if it's closer.
         EG: Use fp to restore regs if it's closer.
         Maybe in time we'll do them all.  For now, always restore regs from
         Maybe in time we'll do them all.  For now, always restore regs from
         sp, but don't restore sp if we don't have to.  */
         sp, but don't restore sp if we don't have to.  */
 
 
      if (!can_trust_sp_p)
      if (!can_trust_sp_p)
        {
        {
          gcc_assert (frame_pointer_needed);
          gcc_assert (frame_pointer_needed);
          fprintf (file,"\tsub %s,%s,%d\t\t%s sp not trusted here\n",
          fprintf (file,"\tsub %s,%s,%d\t\t%s sp not trusted here\n",
                   sp_str, fp_str, frame_size, ASM_COMMENT_START);
                   sp_str, fp_str, frame_size, ASM_COMMENT_START);
        }
        }
 
 
      /* Restore any saved registers.  */
      /* Restore any saved registers.  */
      arc_save_restore (file, sp_str, current_frame_info.reg_offset,
      arc_save_restore (file, sp_str, current_frame_info.reg_offset,
                        /* The zeroing of these two bits is unnecessary,
                        /* The zeroing of these two bits is unnecessary,
                           but leave this in for clarity.  */
                           but leave this in for clarity.  */
                        current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
                        current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK),
                        "ld");
                        "ld");
 
 
      if (MUST_SAVE_RETURN_ADDR)
      if (MUST_SAVE_RETURN_ADDR)
        fprintf (file, "\tld %s,[%s,%d]\n",
        fprintf (file, "\tld %s,[%s,%d]\n",
                 reg_names[RETURN_ADDR_REGNUM],
                 reg_names[RETURN_ADDR_REGNUM],
                 frame_pointer_needed ? fp_str : sp_str,
                 frame_pointer_needed ? fp_str : sp_str,
                 UNITS_PER_WORD + (frame_pointer_needed ? 0 : frame_size));
                 UNITS_PER_WORD + (frame_pointer_needed ? 0 : frame_size));
 
 
      /* Keep track of how much of the stack pointer we've restored.
      /* Keep track of how much of the stack pointer we've restored.
         It makes the following a lot more readable.  */
         It makes the following a lot more readable.  */
      restored = 0;
      restored = 0;
      fp_restored_p = 0;
      fp_restored_p = 0;
 
 
      /* We try to emit the epilogue delay slot insn right after the load
      /* We try to emit the epilogue delay slot insn right after the load
         of the return address register so that it can execute with the
         of the return address register so that it can execute with the
         stack intact.  Secondly, loads are delayed.  */
         stack intact.  Secondly, loads are delayed.  */
      /* ??? If stack intactness is important, always emit now.  */
      /* ??? If stack intactness is important, always emit now.  */
      if (MUST_SAVE_RETURN_ADDR && epilogue_delay != NULL_RTX)
      if (MUST_SAVE_RETURN_ADDR && epilogue_delay != NULL_RTX)
        {
        {
          final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL);
          final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL);
          epilogue_delay = NULL_RTX;
          epilogue_delay = NULL_RTX;
        }
        }
 
 
      if (frame_pointer_needed)
      if (frame_pointer_needed)
        {
        {
          /* Try to restore the frame pointer in the delay slot.  We can't,
          /* Try to restore the frame pointer in the delay slot.  We can't,
             however, if any of these is true.  */
             however, if any of these is true.  */
          if (epilogue_delay != NULL_RTX
          if (epilogue_delay != NULL_RTX
              || !SMALL_INT (frame_size)
              || !SMALL_INT (frame_size)
              || pretend_size
              || pretend_size
              || ARC_INTERRUPT_P (fn_type))
              || ARC_INTERRUPT_P (fn_type))
            {
            {
              /* Note that we restore fp and sp here!  */
              /* Note that we restore fp and sp here!  */
              fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size);
              fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size);
              restored += frame_size;
              restored += frame_size;
              fp_restored_p = 1;
              fp_restored_p = 1;
            }
            }
        }
        }
      else if (!SMALL_INT (size /* frame_size + pretend_size */)
      else if (!SMALL_INT (size /* frame_size + pretend_size */)
               || ARC_INTERRUPT_P (fn_type))
               || ARC_INTERRUPT_P (fn_type))
        {
        {
          fprintf (file, "\tadd %s,%s,%d\n", sp_str, sp_str, frame_size);
          fprintf (file, "\tadd %s,%s,%d\n", sp_str, sp_str, frame_size);
          restored += frame_size;
          restored += frame_size;
        }
        }
 
 
      /* These must be done before the return insn because the delay slot
      /* These must be done before the return insn because the delay slot
         does the final stack restore.  */
         does the final stack restore.  */
      if (ARC_INTERRUPT_P (fn_type))
      if (ARC_INTERRUPT_P (fn_type))
        {
        {
          if (epilogue_delay)
          if (epilogue_delay)
            {
            {
              final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL);
              final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL);
            }
            }
        }
        }
 
 
      /* Emit the return instruction.  */
      /* Emit the return instruction.  */
      {
      {
        static const int regs[4] = {
        static const int regs[4] = {
          0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM
          0, RETURN_ADDR_REGNUM, ILINK1_REGNUM, ILINK2_REGNUM
        };
        };
 
 
        /* Update the flags, if returning from an interrupt handler. */
        /* Update the flags, if returning from an interrupt handler. */
        if (ARC_INTERRUPT_P (fn_type))
        if (ARC_INTERRUPT_P (fn_type))
          fprintf (file, "\tj.d.f %s\n", reg_names[regs[fn_type]]);
          fprintf (file, "\tj.d.f %s\n", reg_names[regs[fn_type]]);
        else
        else
          fprintf (file, "\tj.d %s\n", reg_names[regs[fn_type]]);
          fprintf (file, "\tj.d %s\n", reg_names[regs[fn_type]]);
        }
        }
 
 
      /* If the only register saved is the return address, we need a
      /* If the only register saved is the return address, we need a
         nop, unless we have an instruction to put into it.  Otherwise
         nop, unless we have an instruction to put into it.  Otherwise
         we don't since reloading multiple registers doesn't reference
         we don't since reloading multiple registers doesn't reference
         the register being loaded.  */
         the register being loaded.  */
 
 
      if (ARC_INTERRUPT_P (fn_type))
      if (ARC_INTERRUPT_P (fn_type))
        fprintf (file, "\tadd %s,%s,16\n", sp_str, sp_str);
        fprintf (file, "\tadd %s,%s,16\n", sp_str, sp_str);
      else if (epilogue_delay != NULL_RTX)
      else if (epilogue_delay != NULL_RTX)
        {
        {
          gcc_assert (!frame_pointer_needed || fp_restored_p);
          gcc_assert (!frame_pointer_needed || fp_restored_p);
          gcc_assert (restored >= size);
          gcc_assert (restored >= size);
          final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL);
          final_scan_insn (XEXP (epilogue_delay, 0), file, 1, 1, NULL);
        }
        }
      else if (frame_pointer_needed && !fp_restored_p)
      else if (frame_pointer_needed && !fp_restored_p)
        {
        {
          gcc_assert (SMALL_INT (frame_size));
          gcc_assert (SMALL_INT (frame_size));
          /* Note that we restore fp and sp here!  */
          /* Note that we restore fp and sp here!  */
          fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size);
          fprintf (file, "\tld.a %s,[%s,%d]\n", fp_str, sp_str, frame_size);
        }
        }
      else if (restored < size)
      else if (restored < size)
        {
        {
          gcc_assert (SMALL_INT (size - restored));
          gcc_assert (SMALL_INT (size - restored));
          fprintf (file, "\tadd %s,%s," HOST_WIDE_INT_PRINT_DEC "\n",
          fprintf (file, "\tadd %s,%s," HOST_WIDE_INT_PRINT_DEC "\n",
                   sp_str, sp_str, size - restored);
                   sp_str, sp_str, size - restored);
        }
        }
      else
      else
        fprintf (file, "\tnop\n");
        fprintf (file, "\tnop\n");
    }
    }
 
 
  /* Reset state info for each function.  */
  /* Reset state info for each function.  */
  current_frame_info = zero_frame_info;
  current_frame_info = zero_frame_info;
  arc_compute_function_type (NULL_TREE);
  arc_compute_function_type (NULL_TREE);
}
}


/* Define the number of delay slots needed for the function epilogue.
/* Define the number of delay slots needed for the function epilogue.
 
 
   Interrupt handlers can't have any epilogue delay slots (it's always needed
   Interrupt handlers can't have any epilogue delay slots (it's always needed
   for something else, I think).  For normal functions, we have to worry about
   for something else, I think).  For normal functions, we have to worry about
   using call-saved regs as they'll be restored before the delay slot insn.
   using call-saved regs as they'll be restored before the delay slot insn.
   Functions with non-empty frames already have enough choices for the epilogue
   Functions with non-empty frames already have enough choices for the epilogue
   delay slot so for now we only consider functions with empty frames.  */
   delay slot so for now we only consider functions with empty frames.  */
 
 
int
int
arc_delay_slots_for_epilogue (void)
arc_delay_slots_for_epilogue (void)
{
{
  if (arc_compute_function_type (current_function_decl) != ARC_FUNCTION_NORMAL)
  if (arc_compute_function_type (current_function_decl) != ARC_FUNCTION_NORMAL)
    return 0;
    return 0;
  if (!current_frame_info.initialized)
  if (!current_frame_info.initialized)
    (void) arc_compute_frame_size (get_frame_size ());
    (void) arc_compute_frame_size (get_frame_size ());
  if (current_frame_info.total_size == 0)
  if (current_frame_info.total_size == 0)
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
/* Return true if TRIAL is a valid insn for the epilogue delay slot.
/* Return true if TRIAL is a valid insn for the epilogue delay slot.
   Any single length instruction which doesn't reference the stack or frame
   Any single length instruction which doesn't reference the stack or frame
   pointer or any call-saved register is OK.  SLOT will always be 0.  */
   pointer or any call-saved register is OK.  SLOT will always be 0.  */
 
 
int
int
arc_eligible_for_epilogue_delay (rtx trial, int slot)
arc_eligible_for_epilogue_delay (rtx trial, int slot)
{
{
  gcc_assert (!slot);
  gcc_assert (!slot);
 
 
  if (get_attr_length (trial) == 1
  if (get_attr_length (trial) == 1
      /* If registers where saved, presumably there's more than enough
      /* If registers where saved, presumably there's more than enough
         possibilities for the delay slot.  The alternative is something
         possibilities for the delay slot.  The alternative is something
         more complicated (of course, if we expanded the epilogue as rtl
         more complicated (of course, if we expanded the epilogue as rtl
         this problem would go away).  */
         this problem would go away).  */
      /* ??? Note that this will always be true since only functions with
      /* ??? Note that this will always be true since only functions with
         empty frames have epilogue delay slots.  See
         empty frames have epilogue delay slots.  See
         arc_delay_slots_for_epilogue.  */
         arc_delay_slots_for_epilogue.  */
      && current_frame_info.gmask == 0
      && current_frame_info.gmask == 0
      && ! reg_mentioned_p (stack_pointer_rtx, PATTERN (trial))
      && ! reg_mentioned_p (stack_pointer_rtx, PATTERN (trial))
      && ! reg_mentioned_p (frame_pointer_rtx, PATTERN (trial)))
      && ! reg_mentioned_p (frame_pointer_rtx, PATTERN (trial)))
    return 1;
    return 1;
  return 0;
  return 0;
}
}


/* Return true if OP is a shift operator.  */
/* Return true if OP is a shift operator.  */
 
 
int
int
shift_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
shift_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
{
{
  switch (GET_CODE (op))
  switch (GET_CODE (op))
    {
    {
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
    case ASHIFT:
    case ASHIFT:
      return 1;
      return 1;
    default:
    default:
      return 0;
      return 0;
    }
    }
}
}
 
 
/* Output the assembler code for doing a shift.
/* Output the assembler code for doing a shift.
   We go to a bit of trouble to generate efficient code as the ARC only has
   We go to a bit of trouble to generate efficient code as the ARC only has
   single bit shifts.  This is taken from the h8300 port.  We only have one
   single bit shifts.  This is taken from the h8300 port.  We only have one
   mode of shifting and can't access individual bytes like the h8300 can, so
   mode of shifting and can't access individual bytes like the h8300 can, so
   this is greatly simplified (at the expense of not generating hyper-
   this is greatly simplified (at the expense of not generating hyper-
   efficient code).
   efficient code).
 
 
   This function is not used if the variable shift insns are present.  */
   This function is not used if the variable shift insns are present.  */
 
 
/* ??? We assume the output operand is the same as operand 1.
/* ??? We assume the output operand is the same as operand 1.
   This can be optimized (deleted) in the case of 1 bit shifts.  */
   This can be optimized (deleted) in the case of 1 bit shifts.  */
/* ??? We use the loop register here.  We don't use it elsewhere (yet) and
/* ??? We use the loop register here.  We don't use it elsewhere (yet) and
   using it here will give us a chance to play with it.  */
   using it here will give us a chance to play with it.  */
 
 
const char *
const char *
output_shift (rtx *operands)
output_shift (rtx *operands)
{
{
  rtx shift = operands[3];
  rtx shift = operands[3];
  enum machine_mode mode = GET_MODE (shift);
  enum machine_mode mode = GET_MODE (shift);
  enum rtx_code code = GET_CODE (shift);
  enum rtx_code code = GET_CODE (shift);
  const char *shift_one;
  const char *shift_one;
 
 
  gcc_assert (mode == SImode);
  gcc_assert (mode == SImode);
 
 
  switch (code)
  switch (code)
    {
    {
    case ASHIFT:   shift_one = "asl %0,%0"; break;
    case ASHIFT:   shift_one = "asl %0,%0"; break;
    case ASHIFTRT: shift_one = "asr %0,%0"; break;
    case ASHIFTRT: shift_one = "asr %0,%0"; break;
    case LSHIFTRT: shift_one = "lsr %0,%0"; break;
    case LSHIFTRT: shift_one = "lsr %0,%0"; break;
    default:       gcc_unreachable ();
    default:       gcc_unreachable ();
    }
    }
 
 
  if (GET_CODE (operands[2]) != CONST_INT)
  if (GET_CODE (operands[2]) != CONST_INT)
    {
    {
      if (optimize)
      if (optimize)
        {
        {
          output_asm_insn ("sub.f 0,%2,0", operands);
          output_asm_insn ("sub.f 0,%2,0", operands);
          output_asm_insn ("mov lp_count,%2", operands);
          output_asm_insn ("mov lp_count,%2", operands);
          output_asm_insn ("bz 2f", operands);
          output_asm_insn ("bz 2f", operands);
        }
        }
      else
      else
        output_asm_insn ("mov %4,%2", operands);
        output_asm_insn ("mov %4,%2", operands);
      goto shiftloop;
      goto shiftloop;
    }
    }
  else
  else
    {
    {
      int n = INTVAL (operands[2]);
      int n = INTVAL (operands[2]);
 
 
      /* If the count is negative, make it 0.  */
      /* If the count is negative, make it 0.  */
      if (n < 0)
      if (n < 0)
        n = 0;
        n = 0;
      /* If the count is too big, truncate it.
      /* If the count is too big, truncate it.
         ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
         ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
         do the intuitive thing.  */
         do the intuitive thing.  */
      else if (n > GET_MODE_BITSIZE (mode))
      else if (n > GET_MODE_BITSIZE (mode))
        n = GET_MODE_BITSIZE (mode);
        n = GET_MODE_BITSIZE (mode);
 
 
      /* First see if we can do them inline.  */
      /* First see if we can do them inline.  */
      if (n <= 8)
      if (n <= 8)
        {
        {
          while (--n >= 0)
          while (--n >= 0)
            output_asm_insn (shift_one, operands);
            output_asm_insn (shift_one, operands);
        }
        }
      /* See if we can use a rotate/and.  */
      /* See if we can use a rotate/and.  */
      else if (n == BITS_PER_WORD - 1)
      else if (n == BITS_PER_WORD - 1)
        {
        {
          switch (code)
          switch (code)
            {
            {
            case ASHIFT :
            case ASHIFT :
              output_asm_insn ("and %0,%0,1\n\tror %0,%0", operands);
              output_asm_insn ("and %0,%0,1\n\tror %0,%0", operands);
              break;
              break;
            case ASHIFTRT :
            case ASHIFTRT :
              /* The ARC doesn't have a rol insn.  Use something else.  */
              /* The ARC doesn't have a rol insn.  Use something else.  */
              output_asm_insn ("asl.f 0,%0\n\tsbc %0,0,0", operands);
              output_asm_insn ("asl.f 0,%0\n\tsbc %0,0,0", operands);
              break;
              break;
            case LSHIFTRT :
            case LSHIFTRT :
              /* The ARC doesn't have a rol insn.  Use something else.  */
              /* The ARC doesn't have a rol insn.  Use something else.  */
              output_asm_insn ("asl.f 0,%0\n\tadc %0,0,0", operands);
              output_asm_insn ("asl.f 0,%0\n\tadc %0,0,0", operands);
              break;
              break;
            default:
            default:
              break;
              break;
            }
            }
        }
        }
      /* Must loop.  */
      /* Must loop.  */
      else
      else
        {
        {
          char buf[100];
          char buf[100];
 
 
          if (optimize)
          if (optimize)
            output_asm_insn ("mov lp_count,%c2", operands);
            output_asm_insn ("mov lp_count,%c2", operands);
          else
          else
            output_asm_insn ("mov %4,%c2", operands);
            output_asm_insn ("mov %4,%c2", operands);
        shiftloop:
        shiftloop:
          if (optimize)
          if (optimize)
            {
            {
              if (flag_pic)
              if (flag_pic)
                sprintf (buf, "lr %%4,[status]\n\tadd %%4,%%4,6\t%s single insn loop start",
                sprintf (buf, "lr %%4,[status]\n\tadd %%4,%%4,6\t%s single insn loop start",
                         ASM_COMMENT_START);
                         ASM_COMMENT_START);
              else
              else
                sprintf (buf, "mov %%4,%%%%st(1f)\t%s (single insn loop start) >> 2",
                sprintf (buf, "mov %%4,%%%%st(1f)\t%s (single insn loop start) >> 2",
                         ASM_COMMENT_START);
                         ASM_COMMENT_START);
              output_asm_insn (buf, operands);
              output_asm_insn (buf, operands);
              output_asm_insn ("sr %4,[lp_start]", operands);
              output_asm_insn ("sr %4,[lp_start]", operands);
              output_asm_insn ("add %4,%4,1", operands);
              output_asm_insn ("add %4,%4,1", operands);
              output_asm_insn ("sr %4,[lp_end]", operands);
              output_asm_insn ("sr %4,[lp_end]", operands);
              output_asm_insn ("nop\n\tnop", operands);
              output_asm_insn ("nop\n\tnop", operands);
              if (flag_pic)
              if (flag_pic)
                fprintf (asm_out_file, "\t%s single insn loop\n",
                fprintf (asm_out_file, "\t%s single insn loop\n",
                         ASM_COMMENT_START);
                         ASM_COMMENT_START);
              else
              else
                fprintf (asm_out_file, "1:\t%s single insn loop\n",
                fprintf (asm_out_file, "1:\t%s single insn loop\n",
                         ASM_COMMENT_START);
                         ASM_COMMENT_START);
              output_asm_insn (shift_one, operands);
              output_asm_insn (shift_one, operands);
              fprintf (asm_out_file, "2:\t%s end single insn loop\n",
              fprintf (asm_out_file, "2:\t%s end single insn loop\n",
                       ASM_COMMENT_START);
                       ASM_COMMENT_START);
            }
            }
          else
          else
            {
            {
              fprintf (asm_out_file, "1:\t%s begin shift loop\n",
              fprintf (asm_out_file, "1:\t%s begin shift loop\n",
                       ASM_COMMENT_START);
                       ASM_COMMENT_START);
              output_asm_insn ("sub.f %4,%4,1", operands);
              output_asm_insn ("sub.f %4,%4,1", operands);
              output_asm_insn ("nop", operands);
              output_asm_insn ("nop", operands);
              output_asm_insn ("bn.nd 2f", operands);
              output_asm_insn ("bn.nd 2f", operands);
              output_asm_insn (shift_one, operands);
              output_asm_insn (shift_one, operands);
              output_asm_insn ("b.nd 1b", operands);
              output_asm_insn ("b.nd 1b", operands);
              fprintf (asm_out_file, "2:\t%s end shift loop\n",
              fprintf (asm_out_file, "2:\t%s end shift loop\n",
                       ASM_COMMENT_START);
                       ASM_COMMENT_START);
            }
            }
        }
        }
    }
    }
 
 
  return "";
  return "";
}
}


/* Nested function support.  */
/* Nested function support.  */
 
 
/* Emit RTL insns to initialize the variable parts of a trampoline.
/* Emit RTL insns to initialize the variable parts of a trampoline.
   FNADDR is an RTX for the address of the function's pure code.
   FNADDR is an RTX for the address of the function's pure code.
   CXT is an RTX for the static chain value for the function.  */
   CXT is an RTX for the static chain value for the function.  */
 
 
void
void
arc_initialize_trampoline (rtx tramp ATTRIBUTE_UNUSED,
arc_initialize_trampoline (rtx tramp ATTRIBUTE_UNUSED,
                           rtx fnaddr ATTRIBUTE_UNUSED,
                           rtx fnaddr ATTRIBUTE_UNUSED,
                           rtx cxt ATTRIBUTE_UNUSED)
                           rtx cxt ATTRIBUTE_UNUSED)
{
{
}
}


/* Set the cpu type and print out other fancy things,
/* Set the cpu type and print out other fancy things,
   at the top of the file.  */
   at the top of the file.  */
 
 
static void
static void
arc_file_start (void)
arc_file_start (void)
{
{
  default_file_start ();
  default_file_start ();
  fprintf (asm_out_file, "\t.cpu %s\n", arc_cpu_string);
  fprintf (asm_out_file, "\t.cpu %s\n", arc_cpu_string);
}
}


/* Print operand X (an rtx) in assembler syntax to file FILE.
/* Print operand X (an rtx) in assembler syntax to file FILE.
   CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
   CODE is a letter or dot (`z' in `%z0') or 0 if no letter was specified.
   For `%' followed by punctuation, CODE is the punctuation and X is null.  */
   For `%' followed by punctuation, CODE is the punctuation and X is null.  */
 
 
void
void
arc_print_operand (FILE *file, rtx x, int code)
arc_print_operand (FILE *file, rtx x, int code)
{
{
  switch (code)
  switch (code)
    {
    {
    case '#' :
    case '#' :
      /* Conditional branches.  For now these are equivalent.  */
      /* Conditional branches.  For now these are equivalent.  */
    case '*' :
    case '*' :
      /* Unconditional branches.  Output the appropriate delay slot suffix.  */
      /* Unconditional branches.  Output the appropriate delay slot suffix.  */
      if (!final_sequence || XVECLEN (final_sequence, 0) == 1)
      if (!final_sequence || XVECLEN (final_sequence, 0) == 1)
        {
        {
          /* There's nothing in the delay slot.  */
          /* There's nothing in the delay slot.  */
          fputs (".nd", file);
          fputs (".nd", file);
        }
        }
      else
      else
        {
        {
          rtx jump = XVECEXP (final_sequence, 0, 0);
          rtx jump = XVECEXP (final_sequence, 0, 0);
          rtx delay = XVECEXP (final_sequence, 0, 1);
          rtx delay = XVECEXP (final_sequence, 0, 1);
          if (INSN_ANNULLED_BRANCH_P (jump))
          if (INSN_ANNULLED_BRANCH_P (jump))
            fputs (INSN_FROM_TARGET_P (delay) ? ".jd" : ".nd", file);
            fputs (INSN_FROM_TARGET_P (delay) ? ".jd" : ".nd", file);
          else
          else
            fputs (".d", file);
            fputs (".d", file);
        }
        }
      return;
      return;
    case '?' : /* with leading "." */
    case '?' : /* with leading "." */
    case '!' : /* without leading "." */
    case '!' : /* without leading "." */
      /* This insn can be conditionally executed.  See if the ccfsm machinery
      /* This insn can be conditionally executed.  See if the ccfsm machinery
         says it should be conditionalized.  */
         says it should be conditionalized.  */
      if (arc_ccfsm_state == 3 || arc_ccfsm_state == 4)
      if (arc_ccfsm_state == 3 || arc_ccfsm_state == 4)
        {
        {
          /* Is this insn in a delay slot?  */
          /* Is this insn in a delay slot?  */
          if (final_sequence && XVECLEN (final_sequence, 0) == 2)
          if (final_sequence && XVECLEN (final_sequence, 0) == 2)
            {
            {
              rtx insn = XVECEXP (final_sequence, 0, 1);
              rtx insn = XVECEXP (final_sequence, 0, 1);
 
 
              /* If the insn is annulled and is from the target path, we need
              /* If the insn is annulled and is from the target path, we need
                 to inverse the condition test.  */
                 to inverse the condition test.  */
              if (INSN_ANNULLED_BRANCH_P (insn))
              if (INSN_ANNULLED_BRANCH_P (insn))
                {
                {
                  if (INSN_FROM_TARGET_P (insn))
                  if (INSN_FROM_TARGET_P (insn))
                    fprintf (file, "%s%s",
                    fprintf (file, "%s%s",
                             code == '?' ? "." : "",
                             code == '?' ? "." : "",
                             arc_condition_codes[ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc)]);
                             arc_condition_codes[ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc)]);
                  else
                  else
                    fprintf (file, "%s%s",
                    fprintf (file, "%s%s",
                             code == '?' ? "." : "",
                             code == '?' ? "." : "",
                             arc_condition_codes[arc_ccfsm_current_cc]);
                             arc_condition_codes[arc_ccfsm_current_cc]);
                }
                }
              else
              else
                {
                {
                  /* This insn is executed for either path, so don't
                  /* This insn is executed for either path, so don't
                     conditionalize it at all.  */
                     conditionalize it at all.  */
                  ; /* nothing to do */
                  ; /* nothing to do */
                }
                }
            }
            }
          else
          else
            {
            {
              /* This insn isn't in a delay slot.  */
              /* This insn isn't in a delay slot.  */
              fprintf (file, "%s%s",
              fprintf (file, "%s%s",
                       code == '?' ? "." : "",
                       code == '?' ? "." : "",
                       arc_condition_codes[arc_ccfsm_current_cc]);
                       arc_condition_codes[arc_ccfsm_current_cc]);
            }
            }
        }
        }
      return;
      return;
    case '~' :
    case '~' :
      /* Output a nop if we're between a set of the condition codes,
      /* Output a nop if we're between a set of the condition codes,
         and a conditional branch.  */
         and a conditional branch.  */
      if (last_insn_set_cc_p)
      if (last_insn_set_cc_p)
        fputs ("nop\n\t", file);
        fputs ("nop\n\t", file);
      return;
      return;
    case 'd' :
    case 'd' :
      fputs (arc_condition_codes[get_arc_condition_code (x)], file);
      fputs (arc_condition_codes[get_arc_condition_code (x)], file);
      return;
      return;
    case 'D' :
    case 'D' :
      fputs (arc_condition_codes[ARC_INVERSE_CONDITION_CODE
      fputs (arc_condition_codes[ARC_INVERSE_CONDITION_CODE
                                 (get_arc_condition_code (x))],
                                 (get_arc_condition_code (x))],
             file);
             file);
      return;
      return;
    case 'R' :
    case 'R' :
      /* Write second word of DImode or DFmode reference,
      /* Write second word of DImode or DFmode reference,
         register or memory.  */
         register or memory.  */
      if (GET_CODE (x) == REG)
      if (GET_CODE (x) == REG)
        fputs (reg_names[REGNO (x)+1], file);
        fputs (reg_names[REGNO (x)+1], file);
      else if (GET_CODE (x) == MEM)
      else if (GET_CODE (x) == MEM)
        {
        {
          fputc ('[', file);
          fputc ('[', file);
          /* Handle possible auto-increment.  Since it is pre-increment and
          /* Handle possible auto-increment.  Since it is pre-increment and
             we have already done it, we can just use an offset of four.  */
             we have already done it, we can just use an offset of four.  */
          /* ??? This is taken from rs6000.c I think.  I don't think it is
          /* ??? This is taken from rs6000.c I think.  I don't think it is
             currently necessary, but keep it around.  */
             currently necessary, but keep it around.  */
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
            output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4));
            output_address (plus_constant (XEXP (XEXP (x, 0), 0), 4));
          else
          else
            output_address (plus_constant (XEXP (x, 0), 4));
            output_address (plus_constant (XEXP (x, 0), 4));
          fputc (']', file);
          fputc (']', file);
        }
        }
      else
      else
        output_operand_lossage ("invalid operand to %%R code");
        output_operand_lossage ("invalid operand to %%R code");
      return;
      return;
    case 'S' :
    case 'S' :
      if ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
      if ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FUNCTION_P (x))
          || GET_CODE (x) == LABEL_REF)
          || GET_CODE (x) == LABEL_REF)
        {
        {
          fprintf (file, "%%st(");
          fprintf (file, "%%st(");
          output_addr_const (file, x);
          output_addr_const (file, x);
          fprintf (file, ")");
          fprintf (file, ")");
          return;
          return;
        }
        }
      break;
      break;
    case 'H' :
    case 'H' :
    case 'L' :
    case 'L' :
      if (GET_CODE (x) == REG)
      if (GET_CODE (x) == REG)
        {
        {
          /* L = least significant word, H = most significant word */
          /* L = least significant word, H = most significant word */
          if ((TARGET_BIG_ENDIAN != 0) ^ (code == 'L'))
          if ((TARGET_BIG_ENDIAN != 0) ^ (code == 'L'))
            fputs (reg_names[REGNO (x)], file);
            fputs (reg_names[REGNO (x)], file);
          else
          else
            fputs (reg_names[REGNO (x)+1], file);
            fputs (reg_names[REGNO (x)+1], file);
        }
        }
      else if (GET_CODE (x) == CONST_INT
      else if (GET_CODE (x) == CONST_INT
               || GET_CODE (x) == CONST_DOUBLE)
               || GET_CODE (x) == CONST_DOUBLE)
        {
        {
          rtx first, second;
          rtx first, second;
 
 
          split_double (x, &first, &second);
          split_double (x, &first, &second);
          fprintf (file, "0x%08lx",
          fprintf (file, "0x%08lx",
                   (long)(code == 'L' ? INTVAL (first) : INTVAL (second)));
                   (long)(code == 'L' ? INTVAL (first) : INTVAL (second)));
        }
        }
      else
      else
        output_operand_lossage ("invalid operand to %%H/%%L code");
        output_operand_lossage ("invalid operand to %%H/%%L code");
      return;
      return;
    case 'A' :
    case 'A' :
      {
      {
        char str[30];
        char str[30];
 
 
        gcc_assert (GET_CODE (x) == CONST_DOUBLE
        gcc_assert (GET_CODE (x) == CONST_DOUBLE
                    && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT);
                    && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT);
 
 
        real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (x), sizeof (str), 0, 1);
        real_to_decimal (str, CONST_DOUBLE_REAL_VALUE (x), sizeof (str), 0, 1);
        fprintf (file, "%s", str);
        fprintf (file, "%s", str);
        return;
        return;
      }
      }
    case 'U' :
    case 'U' :
      /* Output a load/store with update indicator if appropriate.  */
      /* Output a load/store with update indicator if appropriate.  */
      if (GET_CODE (x) == MEM)
      if (GET_CODE (x) == MEM)
        {
        {
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
            fputs (".a", file);
            fputs (".a", file);
        }
        }
      else
      else
        output_operand_lossage ("invalid operand to %%U code");
        output_operand_lossage ("invalid operand to %%U code");
      return;
      return;
    case 'V' :
    case 'V' :
      /* Output cache bypass indicator for a load/store insn.  Volatile memory
      /* Output cache bypass indicator for a load/store insn.  Volatile memory
         refs are defined to use the cache bypass mechanism.  */
         refs are defined to use the cache bypass mechanism.  */
      if (GET_CODE (x) == MEM)
      if (GET_CODE (x) == MEM)
        {
        {
          if (MEM_VOLATILE_P (x))
          if (MEM_VOLATILE_P (x))
            fputs (".di", file);
            fputs (".di", file);
        }
        }
      else
      else
        output_operand_lossage ("invalid operand to %%V code");
        output_operand_lossage ("invalid operand to %%V code");
      return;
      return;
    case 0 :
    case 0 :
      /* Do nothing special.  */
      /* Do nothing special.  */
      break;
      break;
    default :
    default :
      /* Unknown flag.  */
      /* Unknown flag.  */
      output_operand_lossage ("invalid operand output code");
      output_operand_lossage ("invalid operand output code");
    }
    }
 
 
  switch (GET_CODE (x))
  switch (GET_CODE (x))
    {
    {
    case REG :
    case REG :
      fputs (reg_names[REGNO (x)], file);
      fputs (reg_names[REGNO (x)], file);
      break;
      break;
    case MEM :
    case MEM :
      fputc ('[', file);
      fputc ('[', file);
      if (GET_CODE (XEXP (x, 0)) == PRE_INC)
      if (GET_CODE (XEXP (x, 0)) == PRE_INC)
        output_address (plus_constant (XEXP (XEXP (x, 0), 0),
        output_address (plus_constant (XEXP (XEXP (x, 0), 0),
                                       GET_MODE_SIZE (GET_MODE (x))));
                                       GET_MODE_SIZE (GET_MODE (x))));
      else if (GET_CODE (XEXP (x, 0)) == PRE_DEC)
      else if (GET_CODE (XEXP (x, 0)) == PRE_DEC)
        output_address (plus_constant (XEXP (XEXP (x, 0), 0),
        output_address (plus_constant (XEXP (XEXP (x, 0), 0),
                                       - GET_MODE_SIZE (GET_MODE (x))));
                                       - GET_MODE_SIZE (GET_MODE (x))));
      else
      else
        output_address (XEXP (x, 0));
        output_address (XEXP (x, 0));
      fputc (']', file);
      fputc (']', file);
      break;
      break;
    case CONST_DOUBLE :
    case CONST_DOUBLE :
      /* We handle SFmode constants here as output_addr_const doesn't.  */
      /* We handle SFmode constants here as output_addr_const doesn't.  */
      if (GET_MODE (x) == SFmode)
      if (GET_MODE (x) == SFmode)
        {
        {
          REAL_VALUE_TYPE d;
          REAL_VALUE_TYPE d;
          long l;
          long l;
 
 
          REAL_VALUE_FROM_CONST_DOUBLE (d, x);
          REAL_VALUE_FROM_CONST_DOUBLE (d, x);
          REAL_VALUE_TO_TARGET_SINGLE (d, l);
          REAL_VALUE_TO_TARGET_SINGLE (d, l);
          fprintf (file, "0x%08lx", l);
          fprintf (file, "0x%08lx", l);
          break;
          break;
        }
        }
      /* Fall through.  Let output_addr_const deal with it.  */
      /* Fall through.  Let output_addr_const deal with it.  */
    default :
    default :
      output_addr_const (file, x);
      output_addr_const (file, x);
      break;
      break;
    }
    }
}
}
 
 
/* Print a memory address as an operand to reference that memory location.  */
/* Print a memory address as an operand to reference that memory location.  */
 
 
void
void
arc_print_operand_address (FILE *file, rtx addr)
arc_print_operand_address (FILE *file, rtx addr)
{
{
  register rtx base, index = 0;
  register rtx base, index = 0;
  int offset = 0;
  int offset = 0;
 
 
  switch (GET_CODE (addr))
  switch (GET_CODE (addr))
    {
    {
    case REG :
    case REG :
      fputs (reg_names[REGNO (addr)], file);
      fputs (reg_names[REGNO (addr)], file);
      break;
      break;
    case SYMBOL_REF :
    case SYMBOL_REF :
      if (/*???*/ 0 && SYMBOL_REF_FUNCTION_P (addr))
      if (/*???*/ 0 && SYMBOL_REF_FUNCTION_P (addr))
        {
        {
          fprintf (file, "%%st(");
          fprintf (file, "%%st(");
          output_addr_const (file, addr);
          output_addr_const (file, addr);
          fprintf (file, ")");
          fprintf (file, ")");
        }
        }
      else
      else
        output_addr_const (file, addr);
        output_addr_const (file, addr);
      break;
      break;
    case PLUS :
    case PLUS :
      if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
      if (GET_CODE (XEXP (addr, 0)) == CONST_INT)
        offset = INTVAL (XEXP (addr, 0)), base = XEXP (addr, 1);
        offset = INTVAL (XEXP (addr, 0)), base = XEXP (addr, 1);
      else if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
      else if (GET_CODE (XEXP (addr, 1)) == CONST_INT)
        offset = INTVAL (XEXP (addr, 1)), base = XEXP (addr, 0);
        offset = INTVAL (XEXP (addr, 1)), base = XEXP (addr, 0);
      else
      else
        base = XEXP (addr, 0), index = XEXP (addr, 1);
        base = XEXP (addr, 0), index = XEXP (addr, 1);
      gcc_assert (GET_CODE (base) == REG);
      gcc_assert (GET_CODE (base) == REG);
      fputs (reg_names[REGNO (base)], file);
      fputs (reg_names[REGNO (base)], file);
      if (index == 0)
      if (index == 0)
        {
        {
          if (offset != 0)
          if (offset != 0)
            fprintf (file, ",%d", offset);
            fprintf (file, ",%d", offset);
        }
        }
      else
      else
        {
        {
          switch (GET_CODE (index))
          switch (GET_CODE (index))
            {
            {
            case REG:
            case REG:
              fprintf (file, ",%s", reg_names[REGNO (index)]);
              fprintf (file, ",%s", reg_names[REGNO (index)]);
              break;
              break;
            case SYMBOL_REF:
            case SYMBOL_REF:
              fputc (',', file), output_addr_const (file, index);
              fputc (',', file), output_addr_const (file, index);
              break;
              break;
            default:
            default:
              gcc_unreachable ();
              gcc_unreachable ();
            }
            }
        }
        }
      break;
      break;
    case PRE_INC :
    case PRE_INC :
    case PRE_DEC :
    case PRE_DEC :
      /* We shouldn't get here as we've lost the mode of the memory object
      /* We shouldn't get here as we've lost the mode of the memory object
         (which says how much to inc/dec by.  */
         (which says how much to inc/dec by.  */
      gcc_unreachable ();
      gcc_unreachable ();
      break;
      break;
    default :
    default :
      output_addr_const (file, addr);
      output_addr_const (file, addr);
      break;
      break;
    }
    }
}
}
 
 
/* Update compare/branch separation marker.  */
/* Update compare/branch separation marker.  */
 
 
static void
static void
record_cc_ref (rtx insn)
record_cc_ref (rtx insn)
{
{
  last_insn_set_cc_p = current_insn_set_cc_p;
  last_insn_set_cc_p = current_insn_set_cc_p;
 
 
  switch (get_attr_cond (insn))
  switch (get_attr_cond (insn))
    {
    {
    case COND_SET :
    case COND_SET :
    case COND_SET_ZN :
    case COND_SET_ZN :
    case COND_SET_ZNC :
    case COND_SET_ZNC :
      if (get_attr_length (insn) == 1)
      if (get_attr_length (insn) == 1)
        current_insn_set_cc_p = 1;
        current_insn_set_cc_p = 1;
      else
      else
        current_insn_set_cc_p = 0;
        current_insn_set_cc_p = 0;
      break;
      break;
    default :
    default :
      current_insn_set_cc_p = 0;
      current_insn_set_cc_p = 0;
      break;
      break;
    }
    }
}
}


/* Conditional execution support.
/* Conditional execution support.
 
 
   This is based on the ARM port but for now is much simpler.
   This is based on the ARM port but for now is much simpler.
 
 
   A finite state machine takes care of noticing whether or not instructions
   A finite state machine takes care of noticing whether or not instructions
   can be conditionally executed, and thus decrease execution time and code
   can be conditionally executed, and thus decrease execution time and code
   size by deleting branch instructions.  The fsm is controlled by
   size by deleting branch instructions.  The fsm is controlled by
   final_prescan_insn, and controls the actions of PRINT_OPERAND.  The patterns
   final_prescan_insn, and controls the actions of PRINT_OPERAND.  The patterns
   in the .md file for the branch insns also have a hand in this.  */
   in the .md file for the branch insns also have a hand in this.  */
 
 
/* The state of the fsm controlling condition codes are:
/* The state of the fsm controlling condition codes are:
   0: normal, do nothing special
   0: normal, do nothing special
   1: don't output this insn
   1: don't output this insn
   2: don't output this insn
   2: don't output this insn
   3: make insns conditional
   3: make insns conditional
   4: make insns conditional
   4: make insns conditional
 
 
   State transitions (state->state by whom, under what condition):
   State transitions (state->state by whom, under what condition):
   0 -> 1 final_prescan_insn, if insn is conditional branch
   0 -> 1 final_prescan_insn, if insn is conditional branch
   0 -> 2 final_prescan_insn, if the `target' is an unconditional branch
   0 -> 2 final_prescan_insn, if the `target' is an unconditional branch
   1 -> 3 branch patterns, after having not output the conditional branch
   1 -> 3 branch patterns, after having not output the conditional branch
   2 -> 4 branch patterns, after having not output the conditional branch
   2 -> 4 branch patterns, after having not output the conditional branch
   3 -> 0 (*targetm.asm_out.internal_label), if the `target' label is reached
   3 -> 0 (*targetm.asm_out.internal_label), if the `target' label is reached
          (the target label has CODE_LABEL_NUMBER equal to
          (the target label has CODE_LABEL_NUMBER equal to
          arc_ccfsm_target_label).
          arc_ccfsm_target_label).
   4 -> 0 final_prescan_insn, if `target' unconditional branch is reached
   4 -> 0 final_prescan_insn, if `target' unconditional branch is reached
 
 
   If the jump clobbers the conditions then we use states 2 and 4.
   If the jump clobbers the conditions then we use states 2 and 4.
 
 
   A similar thing can be done with conditional return insns.
   A similar thing can be done with conditional return insns.
 
 
   We also handle separating branches from sets of the condition code.
   We also handle separating branches from sets of the condition code.
   This is done here because knowledge of the ccfsm state is required,
   This is done here because knowledge of the ccfsm state is required,
   we may not be outputting the branch.  */
   we may not be outputting the branch.  */
 
 
void
void
arc_final_prescan_insn (rtx insn,
arc_final_prescan_insn (rtx insn,
                        rtx *opvec ATTRIBUTE_UNUSED,
                        rtx *opvec ATTRIBUTE_UNUSED,
                        int noperands ATTRIBUTE_UNUSED)
                        int noperands ATTRIBUTE_UNUSED)
{
{
  /* BODY will hold the body of INSN.  */
  /* BODY will hold the body of INSN.  */
  register rtx body = PATTERN (insn);
  register rtx body = PATTERN (insn);
 
 
  /* This will be 1 if trying to repeat the trick (i.e.: do the `else' part of
  /* This will be 1 if trying to repeat the trick (i.e.: do the `else' part of
     an if/then/else), and things need to be reversed.  */
     an if/then/else), and things need to be reversed.  */
  int reverse = 0;
  int reverse = 0;
 
 
  /* If we start with a return insn, we only succeed if we find another one.  */
  /* If we start with a return insn, we only succeed if we find another one.  */
  int seeking_return = 0;
  int seeking_return = 0;
 
 
  /* START_INSN will hold the insn from where we start looking.  This is the
  /* START_INSN will hold the insn from where we start looking.  This is the
     first insn after the following code_label if REVERSE is true.  */
     first insn after the following code_label if REVERSE is true.  */
  rtx start_insn = insn;
  rtx start_insn = insn;
 
 
  /* Update compare/branch separation marker.  */
  /* Update compare/branch separation marker.  */
  record_cc_ref (insn);
  record_cc_ref (insn);
 
 
  /* Allow -mdebug-ccfsm to turn this off so we can see how well it does.
  /* Allow -mdebug-ccfsm to turn this off so we can see how well it does.
     We can't do this in macro FINAL_PRESCAN_INSN because its called from
     We can't do this in macro FINAL_PRESCAN_INSN because its called from
     final_scan_insn which has `optimize' as a local.  */
     final_scan_insn which has `optimize' as a local.  */
  if (optimize < 2 || TARGET_NO_COND_EXEC)
  if (optimize < 2 || TARGET_NO_COND_EXEC)
    return;
    return;
 
 
  /* If in state 4, check if the target branch is reached, in order to
  /* If in state 4, check if the target branch is reached, in order to
     change back to state 0.  */
     change back to state 0.  */
  if (arc_ccfsm_state == 4)
  if (arc_ccfsm_state == 4)
    {
    {
      if (insn == arc_ccfsm_target_insn)
      if (insn == arc_ccfsm_target_insn)
        {
        {
          arc_ccfsm_target_insn = NULL;
          arc_ccfsm_target_insn = NULL;
          arc_ccfsm_state = 0;
          arc_ccfsm_state = 0;
        }
        }
      return;
      return;
    }
    }
 
 
  /* If in state 3, it is possible to repeat the trick, if this insn is an
  /* If in state 3, it is possible to repeat the trick, if this insn is an
     unconditional branch to a label, and immediately following this branch
     unconditional branch to a label, and immediately following this branch
     is the previous target label which is only used once, and the label this
     is the previous target label which is only used once, and the label this
     branch jumps to is not too far off.  Or in other words "we've done the
     branch jumps to is not too far off.  Or in other words "we've done the
     `then' part, see if we can do the `else' part."  */
     `then' part, see if we can do the `else' part."  */
  if (arc_ccfsm_state == 3)
  if (arc_ccfsm_state == 3)
    {
    {
      if (simplejump_p (insn))
      if (simplejump_p (insn))
        {
        {
          start_insn = next_nonnote_insn (start_insn);
          start_insn = next_nonnote_insn (start_insn);
          if (GET_CODE (start_insn) == BARRIER)
          if (GET_CODE (start_insn) == BARRIER)
            {
            {
              /* ??? Isn't this always a barrier?  */
              /* ??? Isn't this always a barrier?  */
              start_insn = next_nonnote_insn (start_insn);
              start_insn = next_nonnote_insn (start_insn);
            }
            }
          if (GET_CODE (start_insn) == CODE_LABEL
          if (GET_CODE (start_insn) == CODE_LABEL
              && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label
              && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label
              && LABEL_NUSES (start_insn) == 1)
              && LABEL_NUSES (start_insn) == 1)
            reverse = TRUE;
            reverse = TRUE;
          else
          else
            return;
            return;
        }
        }
      else if (GET_CODE (body) == RETURN)
      else if (GET_CODE (body) == RETURN)
        {
        {
          start_insn = next_nonnote_insn (start_insn);
          start_insn = next_nonnote_insn (start_insn);
          if (GET_CODE (start_insn) == BARRIER)
          if (GET_CODE (start_insn) == BARRIER)
            start_insn = next_nonnote_insn (start_insn);
            start_insn = next_nonnote_insn (start_insn);
          if (GET_CODE (start_insn) == CODE_LABEL
          if (GET_CODE (start_insn) == CODE_LABEL
              && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label
              && CODE_LABEL_NUMBER (start_insn) == arc_ccfsm_target_label
              && LABEL_NUSES (start_insn) == 1)
              && LABEL_NUSES (start_insn) == 1)
            {
            {
              reverse = TRUE;
              reverse = TRUE;
              seeking_return = 1;
              seeking_return = 1;
            }
            }
          else
          else
            return;
            return;
        }
        }
      else
      else
        return;
        return;
    }
    }
 
 
  if (GET_CODE (insn) != JUMP_INSN)
  if (GET_CODE (insn) != JUMP_INSN)
    return;
    return;
 
 
  /* This jump might be paralleled with a clobber of the condition codes,
  /* This jump might be paralleled with a clobber of the condition codes,
     the jump should always come first.  */
     the jump should always come first.  */
  if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0)
  if (GET_CODE (body) == PARALLEL && XVECLEN (body, 0) > 0)
    body = XVECEXP (body, 0, 0);
    body = XVECEXP (body, 0, 0);
 
 
  if (reverse
  if (reverse
      || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
      || (GET_CODE (body) == SET && GET_CODE (SET_DEST (body)) == PC
          && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
          && GET_CODE (SET_SRC (body)) == IF_THEN_ELSE))
    {
    {
      int insns_skipped = 0, fail = FALSE, succeed = FALSE;
      int insns_skipped = 0, fail = FALSE, succeed = FALSE;
      /* Flag which part of the IF_THEN_ELSE is the LABEL_REF.  */
      /* Flag which part of the IF_THEN_ELSE is the LABEL_REF.  */
      int then_not_else = TRUE;
      int then_not_else = TRUE;
      /* Nonzero if next insn must be the target label.  */
      /* Nonzero if next insn must be the target label.  */
      int next_must_be_target_label_p;
      int next_must_be_target_label_p;
      rtx this_insn = start_insn, label = 0;
      rtx this_insn = start_insn, label = 0;
 
 
      /* Register the insn jumped to.  */
      /* Register the insn jumped to.  */
      if (reverse)
      if (reverse)
        {
        {
          if (!seeking_return)
          if (!seeking_return)
            label = XEXP (SET_SRC (body), 0);
            label = XEXP (SET_SRC (body), 0);
        }
        }
      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == LABEL_REF)
        label = XEXP (XEXP (SET_SRC (body), 1), 0);
        label = XEXP (XEXP (SET_SRC (body), 1), 0);
      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == LABEL_REF)
        {
        {
          label = XEXP (XEXP (SET_SRC (body), 2), 0);
          label = XEXP (XEXP (SET_SRC (body), 2), 0);
          then_not_else = FALSE;
          then_not_else = FALSE;
        }
        }
      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
        seeking_return = 1;
        seeking_return = 1;
      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
        {
        {
          seeking_return = 1;
          seeking_return = 1;
          then_not_else = FALSE;
          then_not_else = FALSE;
        }
        }
      else
      else
        gcc_unreachable ();
        gcc_unreachable ();
 
 
      /* See how many insns this branch skips, and what kind of insns.  If all
      /* See how many insns this branch skips, and what kind of insns.  If all
         insns are okay, and the label or unconditional branch to the same
         insns are okay, and the label or unconditional branch to the same
         label is not too far away, succeed.  */
         label is not too far away, succeed.  */
      for (insns_skipped = 0, next_must_be_target_label_p = FALSE;
      for (insns_skipped = 0, next_must_be_target_label_p = FALSE;
           !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
           !fail && !succeed && insns_skipped < MAX_INSNS_SKIPPED;
           insns_skipped++)
           insns_skipped++)
        {
        {
          rtx scanbody;
          rtx scanbody;
 
 
          this_insn = next_nonnote_insn (this_insn);
          this_insn = next_nonnote_insn (this_insn);
          if (!this_insn)
          if (!this_insn)
            break;
            break;
 
 
          if (next_must_be_target_label_p)
          if (next_must_be_target_label_p)
            {
            {
              if (GET_CODE (this_insn) == BARRIER)
              if (GET_CODE (this_insn) == BARRIER)
                continue;
                continue;
              if (GET_CODE (this_insn) == CODE_LABEL
              if (GET_CODE (this_insn) == CODE_LABEL
                  && this_insn == label)
                  && this_insn == label)
                {
                {
                  arc_ccfsm_state = 1;
                  arc_ccfsm_state = 1;
                  succeed = TRUE;
                  succeed = TRUE;
                }
                }
              else
              else
                fail = TRUE;
                fail = TRUE;
              break;
              break;
            }
            }
 
 
          scanbody = PATTERN (this_insn);
          scanbody = PATTERN (this_insn);
 
 
          switch (GET_CODE (this_insn))
          switch (GET_CODE (this_insn))
            {
            {
            case CODE_LABEL:
            case CODE_LABEL:
              /* Succeed if it is the target label, otherwise fail since
              /* Succeed if it is the target label, otherwise fail since
                 control falls in from somewhere else.  */
                 control falls in from somewhere else.  */
              if (this_insn == label)
              if (this_insn == label)
                {
                {
                  arc_ccfsm_state = 1;
                  arc_ccfsm_state = 1;
                  succeed = TRUE;
                  succeed = TRUE;
                }
                }
              else
              else
                fail = TRUE;
                fail = TRUE;
              break;
              break;
 
 
            case BARRIER:
            case BARRIER:
              /* Succeed if the following insn is the target label.
              /* Succeed if the following insn is the target label.
                 Otherwise fail.
                 Otherwise fail.
                 If return insns are used then the last insn in a function
                 If return insns are used then the last insn in a function
                 will be a barrier.  */
                 will be a barrier.  */
              next_must_be_target_label_p = TRUE;
              next_must_be_target_label_p = TRUE;
              break;
              break;
 
 
            case CALL_INSN:
            case CALL_INSN:
              /* Can handle a call insn if there are no insns after it.
              /* Can handle a call insn if there are no insns after it.
                 IE: The next "insn" is the target label.  We don't have to
                 IE: The next "insn" is the target label.  We don't have to
                 worry about delay slots as such insns are SEQUENCE's inside
                 worry about delay slots as such insns are SEQUENCE's inside
                 INSN's.  ??? It is possible to handle such insns though.  */
                 INSN's.  ??? It is possible to handle such insns though.  */
              if (get_attr_cond (this_insn) == COND_CANUSE)
              if (get_attr_cond (this_insn) == COND_CANUSE)
                next_must_be_target_label_p = TRUE;
                next_must_be_target_label_p = TRUE;
              else
              else
                fail = TRUE;
                fail = TRUE;
              break;
              break;
 
 
            case JUMP_INSN:
            case JUMP_INSN:
              /* If this is an unconditional branch to the same label, succeed.
              /* If this is an unconditional branch to the same label, succeed.
                 If it is to another label, do nothing.  If it is conditional,
                 If it is to another label, do nothing.  If it is conditional,
                 fail.  */
                 fail.  */
              /* ??? Probably, the test for the SET and the PC are unnecessary.  */
              /* ??? Probably, the test for the SET and the PC are unnecessary.  */
 
 
              if (GET_CODE (scanbody) == SET
              if (GET_CODE (scanbody) == SET
                  && GET_CODE (SET_DEST (scanbody)) == PC)
                  && GET_CODE (SET_DEST (scanbody)) == PC)
                {
                {
                  if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
                  if (GET_CODE (SET_SRC (scanbody)) == LABEL_REF
                      && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
                      && XEXP (SET_SRC (scanbody), 0) == label && !reverse)
                    {
                    {
                      arc_ccfsm_state = 2;
                      arc_ccfsm_state = 2;
                      succeed = TRUE;
                      succeed = TRUE;
                    }
                    }
                  else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
                  else if (GET_CODE (SET_SRC (scanbody)) == IF_THEN_ELSE)
                    fail = TRUE;
                    fail = TRUE;
                }
                }
              else if (GET_CODE (scanbody) == RETURN
              else if (GET_CODE (scanbody) == RETURN
                       && seeking_return)
                       && seeking_return)
                {
                {
                  arc_ccfsm_state = 2;
                  arc_ccfsm_state = 2;
                  succeed = TRUE;
                  succeed = TRUE;
                }
                }
              else if (GET_CODE (scanbody) == PARALLEL)
              else if (GET_CODE (scanbody) == PARALLEL)
                {
                {
                  if (get_attr_cond (this_insn) != COND_CANUSE)
                  if (get_attr_cond (this_insn) != COND_CANUSE)
                    fail = TRUE;
                    fail = TRUE;
                }
                }
              break;
              break;
 
 
            case INSN:
            case INSN:
              /* We can only do this with insns that can use the condition
              /* We can only do this with insns that can use the condition
                 codes (and don't set them).  */
                 codes (and don't set them).  */
              if (GET_CODE (scanbody) == SET
              if (GET_CODE (scanbody) == SET
                  || GET_CODE (scanbody) == PARALLEL)
                  || GET_CODE (scanbody) == PARALLEL)
                {
                {
                  if (get_attr_cond (this_insn) != COND_CANUSE)
                  if (get_attr_cond (this_insn) != COND_CANUSE)
                    fail = TRUE;
                    fail = TRUE;
                }
                }
              /* We can't handle other insns like sequences.  */
              /* We can't handle other insns like sequences.  */
              else
              else
                fail = TRUE;
                fail = TRUE;
              break;
              break;
 
 
            default:
            default:
              break;
              break;
            }
            }
        }
        }
 
 
      if (succeed)
      if (succeed)
        {
        {
          if ((!seeking_return) && (arc_ccfsm_state == 1 || reverse))
          if ((!seeking_return) && (arc_ccfsm_state == 1 || reverse))
            arc_ccfsm_target_label = CODE_LABEL_NUMBER (label);
            arc_ccfsm_target_label = CODE_LABEL_NUMBER (label);
          else
          else
            {
            {
              gcc_assert (seeking_return || arc_ccfsm_state == 2);
              gcc_assert (seeking_return || arc_ccfsm_state == 2);
              while (this_insn && GET_CODE (PATTERN (this_insn)) == USE)
              while (this_insn && GET_CODE (PATTERN (this_insn)) == USE)
                {
                {
                  this_insn = next_nonnote_insn (this_insn);
                  this_insn = next_nonnote_insn (this_insn);
                  gcc_assert (!this_insn
                  gcc_assert (!this_insn
                              || (GET_CODE (this_insn) != BARRIER
                              || (GET_CODE (this_insn) != BARRIER
                                  && GET_CODE (this_insn) != CODE_LABEL));
                                  && GET_CODE (this_insn) != CODE_LABEL));
                }
                }
              if (!this_insn)
              if (!this_insn)
                {
                {
                  /* Oh dear! we ran off the end, give up.  */
                  /* Oh dear! we ran off the end, give up.  */
                  extract_insn_cached (insn);
                  extract_insn_cached (insn);
                  arc_ccfsm_state = 0;
                  arc_ccfsm_state = 0;
                  arc_ccfsm_target_insn = NULL;
                  arc_ccfsm_target_insn = NULL;
                  return;
                  return;
                }
                }
              arc_ccfsm_target_insn = this_insn;
              arc_ccfsm_target_insn = this_insn;
            }
            }
 
 
          /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
          /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
             what it was.  */
             what it was.  */
          if (!reverse)
          if (!reverse)
            arc_ccfsm_current_cc = get_arc_condition_code (XEXP (SET_SRC (body),
            arc_ccfsm_current_cc = get_arc_condition_code (XEXP (SET_SRC (body),
                                                                 0));
                                                                 0));
 
 
          if (reverse || then_not_else)
          if (reverse || then_not_else)
            arc_ccfsm_current_cc = ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc);
            arc_ccfsm_current_cc = ARC_INVERSE_CONDITION_CODE (arc_ccfsm_current_cc);
        }
        }
 
 
      /* Restore recog_data.  Getting the attributes of other insns can
      /* Restore recog_data.  Getting the attributes of other insns can
         destroy this array, but final.c assumes that it remains intact
         destroy this array, but final.c assumes that it remains intact
         across this call.  */
         across this call.  */
      extract_insn_cached (insn);
      extract_insn_cached (insn);
    }
    }
}
}
 
 
/* Record that we are currently outputting label NUM with prefix PREFIX.
/* Record that we are currently outputting label NUM with prefix PREFIX.
   It it's the label we're looking for, reset the ccfsm machinery.
   It it's the label we're looking for, reset the ccfsm machinery.
 
 
   Called from (*targetm.asm_out.internal_label).  */
   Called from (*targetm.asm_out.internal_label).  */
 
 
void
void
arc_ccfsm_at_label (const char *prefix, int num)
arc_ccfsm_at_label (const char *prefix, int num)
{
{
  if (arc_ccfsm_state == 3 && arc_ccfsm_target_label == num
  if (arc_ccfsm_state == 3 && arc_ccfsm_target_label == num
      && !strcmp (prefix, "L"))
      && !strcmp (prefix, "L"))
    {
    {
      arc_ccfsm_state = 0;
      arc_ccfsm_state = 0;
      arc_ccfsm_target_insn = NULL_RTX;
      arc_ccfsm_target_insn = NULL_RTX;
    }
    }
}
}
 
 
/* See if the current insn, which is a conditional branch, is to be
/* See if the current insn, which is a conditional branch, is to be
   deleted.  */
   deleted.  */
 
 
int
int
arc_ccfsm_branch_deleted_p (void)
arc_ccfsm_branch_deleted_p (void)
{
{
  if (arc_ccfsm_state == 1 || arc_ccfsm_state == 2)
  if (arc_ccfsm_state == 1 || arc_ccfsm_state == 2)
    return 1;
    return 1;
  return 0;
  return 0;
}
}
 
 
/* Record a branch isn't output because subsequent insns can be
/* Record a branch isn't output because subsequent insns can be
   conditionalized.  */
   conditionalized.  */
 
 
void
void
arc_ccfsm_record_branch_deleted (void)
arc_ccfsm_record_branch_deleted (void)
{
{
  /* Indicate we're conditionalizing insns now.  */
  /* Indicate we're conditionalizing insns now.  */
  arc_ccfsm_state += 2;
  arc_ccfsm_state += 2;
 
 
  /* If the next insn is a subroutine call, we still need a nop between the
  /* If the next insn is a subroutine call, we still need a nop between the
     cc setter and user.  We need to undo the effect of calling record_cc_ref
     cc setter and user.  We need to undo the effect of calling record_cc_ref
     for the just deleted branch.  */
     for the just deleted branch.  */
  current_insn_set_cc_p = last_insn_set_cc_p;
  current_insn_set_cc_p = last_insn_set_cc_p;
}
}


void
void
arc_va_start (tree valist, rtx nextarg)
arc_va_start (tree valist, rtx nextarg)
{
{
  /* See arc_setup_incoming_varargs for reasons for this oddity.  */
  /* See arc_setup_incoming_varargs for reasons for this oddity.  */
  if (current_function_args_info < 8
  if (current_function_args_info < 8
      && (current_function_args_info & 1))
      && (current_function_args_info & 1))
    nextarg = plus_constant (nextarg, UNITS_PER_WORD);
    nextarg = plus_constant (nextarg, UNITS_PER_WORD);
 
 
  std_expand_builtin_va_start (valist, nextarg);
  std_expand_builtin_va_start (valist, nextarg);
}
}
 
 
/* This is how to output a definition of an internal numbered label where
/* This is how to output a definition of an internal numbered label where
   PREFIX is the class of label and NUM is the number within the class.  */
   PREFIX is the class of label and NUM is the number within the class.  */
 
 
static void
static void
arc_internal_label (FILE *stream, const char *prefix, unsigned long labelno)
arc_internal_label (FILE *stream, const char *prefix, unsigned long labelno)
{
{
  arc_ccfsm_at_label (prefix, labelno);
  arc_ccfsm_at_label (prefix, labelno);
  default_internal_label (stream, prefix, labelno);
  default_internal_label (stream, prefix, labelno);
}
}
 
 
/* Worker function for TARGET_ASM_EXTERNAL_LIBCALL.  */
/* Worker function for TARGET_ASM_EXTERNAL_LIBCALL.  */
 
 
static void
static void
arc_external_libcall (rtx fun ATTRIBUTE_UNUSED)
arc_external_libcall (rtx fun ATTRIBUTE_UNUSED)
{
{
#if 0
#if 0
/* On the ARC we want to have libgcc's for multiple cpus in one binary.
/* On the ARC we want to have libgcc's for multiple cpus in one binary.
   We can't use `assemble_name' here as that will call ASM_OUTPUT_LABELREF
   We can't use `assemble_name' here as that will call ASM_OUTPUT_LABELREF
   and we'll get another suffix added on if -mmangle-cpu.  */
   and we'll get another suffix added on if -mmangle-cpu.  */
  if (TARGET_MANGLE_CPU_LIBGCC)
  if (TARGET_MANGLE_CPU_LIBGCC)
    {
    {
      fprintf (FILE, "\t.rename\t_%s, _%s%s\n",
      fprintf (FILE, "\t.rename\t_%s, _%s%s\n",
               XSTR (SYMREF, 0), XSTR (SYMREF, 0),
               XSTR (SYMREF, 0), XSTR (SYMREF, 0),
               arc_mangle_suffix);
               arc_mangle_suffix);
    }
    }
#endif
#endif
}
}
 
 
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
 
 
static bool
static bool
arc_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
arc_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
{
{
  if (AGGREGATE_TYPE_P (type))
  if (AGGREGATE_TYPE_P (type))
    return true;
    return true;
  else
  else
    {
    {
      HOST_WIDE_INT size = int_size_in_bytes (type);
      HOST_WIDE_INT size = int_size_in_bytes (type);
      return (size == -1 || size > 8);
      return (size == -1 || size > 8);
    }
    }
}
}
 
 
/* For ARC, All aggregates and arguments greater than 8 bytes are
/* For ARC, All aggregates and arguments greater than 8 bytes are
   passed by reference.  */
   passed by reference.  */
 
 
static bool
static bool
arc_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
arc_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
                       enum machine_mode mode, tree type,
                       enum machine_mode mode, tree type,
                       bool named ATTRIBUTE_UNUSED)
                       bool named ATTRIBUTE_UNUSED)
{
{
  unsigned HOST_WIDE_INT size;
  unsigned HOST_WIDE_INT size;
 
 
  if (type)
  if (type)
    {
    {
      if (AGGREGATE_TYPE_P (type))
      if (AGGREGATE_TYPE_P (type))
        return true;
        return true;
      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;
  return size > 8;
}
}
 
 

powered by: WebSVN 2.1.0

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