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

Subversion Repositories openrisc_2011-10-31

[/] [openrisc/] [tags/] [gnu-src/] [gcc-4.5.1/] [gcc-4.5.1-or32-1.0rc1/] [gcc/] [config/] [iq2000/] [iq2000.c] - Diff between revs 282 and 338

Only display areas with differences | Details | Blame | View Log

Rev 282 Rev 338
/* Subroutines used for code generation on Vitesse IQ2000 processors
/* Subroutines used for code generation on Vitesse IQ2000 processors
   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009
   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/>.  */
 
 
#include "config.h"
#include "config.h"
#include "system.h"
#include "system.h"
#include "coretypes.h"
#include "coretypes.h"
#include <signal.h>
#include <signal.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 "optabs.h"
#include "optabs.h"
#include "libfuncs.h"
#include "libfuncs.h"
#include "recog.h"
#include "recog.h"
#include "toplev.h"
#include "toplev.h"
#include "reload.h"
#include "reload.h"
#include "ggc.h"
#include "ggc.h"
#include "tm_p.h"
#include "tm_p.h"
#include "debug.h"
#include "debug.h"
#include "target.h"
#include "target.h"
#include "target-def.h"
#include "target-def.h"
#include "langhooks.h"
#include "langhooks.h"
 
 
/* Enumeration for all of the relational tests, so that we can build
/* Enumeration for all of the relational tests, so that we can build
   arrays indexed by the test type, and not worry about the order
   arrays indexed by the test type, and not worry about the order
   of EQ, NE, etc.  */
   of EQ, NE, etc.  */
 
 
enum internal_test
enum internal_test
  {
  {
    ITEST_EQ,
    ITEST_EQ,
    ITEST_NE,
    ITEST_NE,
    ITEST_GT,
    ITEST_GT,
    ITEST_GE,
    ITEST_GE,
    ITEST_LT,
    ITEST_LT,
    ITEST_LE,
    ITEST_LE,
    ITEST_GTU,
    ITEST_GTU,
    ITEST_GEU,
    ITEST_GEU,
    ITEST_LTU,
    ITEST_LTU,
    ITEST_LEU,
    ITEST_LEU,
    ITEST_MAX
    ITEST_MAX
  };
  };
 
 
struct constant;
struct constant;
 
 


/* Structure to be filled in by compute_frame_size with register
/* Structure to be filled in by compute_frame_size with register
   save masks, and offsets for the current function.  */
   save masks, and offsets for the current function.  */
 
 
struct iq2000_frame_info
struct iq2000_frame_info
{
{
  long total_size;              /* # bytes that the entire frame takes up.  */
  long total_size;              /* # bytes that the entire frame takes up.  */
  long var_size;                /* # bytes that variables take up.  */
  long var_size;                /* # bytes that variables take up.  */
  long args_size;               /* # bytes that outgoing arguments take up.  */
  long args_size;               /* # bytes that outgoing arguments take up.  */
  long extra_size;              /* # bytes of extra gunk.  */
  long extra_size;              /* # bytes of extra gunk.  */
  int  gp_reg_size;             /* # bytes needed to store gp regs.  */
  int  gp_reg_size;             /* # bytes needed to store gp regs.  */
  int  fp_reg_size;             /* # bytes needed to store fp regs.  */
  int  fp_reg_size;             /* # bytes needed to store fp regs.  */
  long mask;                    /* Mask of saved gp registers.  */
  long mask;                    /* Mask of saved gp registers.  */
  long gp_save_offset;          /* Offset from vfp to store gp registers.  */
  long gp_save_offset;          /* Offset from vfp to store gp registers.  */
  long fp_save_offset;          /* Offset from vfp to store fp registers.  */
  long fp_save_offset;          /* Offset from vfp to store fp registers.  */
  long gp_sp_offset;            /* Offset from new sp to store gp registers.  */
  long gp_sp_offset;            /* Offset from new sp to store gp registers.  */
  long fp_sp_offset;            /* Offset from new sp to store fp registers.  */
  long fp_sp_offset;            /* Offset from new sp to store fp registers.  */
  int  initialized;             /* != 0 if frame size already calculated.  */
  int  initialized;             /* != 0 if frame size already calculated.  */
  int  num_gp;                  /* Number of gp registers saved.  */
  int  num_gp;                  /* Number of gp registers saved.  */
} iq2000_frame_info;
} iq2000_frame_info;
 
 
struct GTY(()) machine_function
struct GTY(()) machine_function
{
{
  /* Current frame information, calculated by compute_frame_size.  */
  /* Current frame information, calculated by compute_frame_size.  */
  long total_size;              /* # bytes that the entire frame takes up.  */
  long total_size;              /* # bytes that the entire frame takes up.  */
  long var_size;                /* # bytes that variables take up.  */
  long var_size;                /* # bytes that variables take up.  */
  long args_size;               /* # bytes that outgoing arguments take up.  */
  long args_size;               /* # bytes that outgoing arguments take up.  */
  long extra_size;              /* # bytes of extra gunk.  */
  long extra_size;              /* # bytes of extra gunk.  */
  int  gp_reg_size;             /* # bytes needed to store gp regs.  */
  int  gp_reg_size;             /* # bytes needed to store gp regs.  */
  int  fp_reg_size;             /* # bytes needed to store fp regs.  */
  int  fp_reg_size;             /* # bytes needed to store fp regs.  */
  long mask;                    /* Mask of saved gp registers.  */
  long mask;                    /* Mask of saved gp registers.  */
  long gp_save_offset;          /* Offset from vfp to store gp registers.  */
  long gp_save_offset;          /* Offset from vfp to store gp registers.  */
  long fp_save_offset;          /* Offset from vfp to store fp registers.  */
  long fp_save_offset;          /* Offset from vfp to store fp registers.  */
  long gp_sp_offset;            /* Offset from new sp to store gp registers.  */
  long gp_sp_offset;            /* Offset from new sp to store gp registers.  */
  long fp_sp_offset;            /* Offset from new sp to store fp registers.  */
  long fp_sp_offset;            /* Offset from new sp to store fp registers.  */
  int  initialized;             /* != 0 if frame size already calculated.  */
  int  initialized;             /* != 0 if frame size already calculated.  */
  int  num_gp;                  /* Number of gp registers saved.  */
  int  num_gp;                  /* Number of gp registers saved.  */
};
};
 
 
/* Global variables for machine-dependent things.  */
/* Global variables for machine-dependent things.  */
 
 
/* List of all IQ2000 punctuation characters used by print_operand.  */
/* List of all IQ2000 punctuation characters used by print_operand.  */
char iq2000_print_operand_punct[256];
char iq2000_print_operand_punct[256];
 
 
/* The target cpu for optimization and scheduling.  */
/* The target cpu for optimization and scheduling.  */
enum processor_type iq2000_tune;
enum processor_type iq2000_tune;
 
 
/* Which instruction set architecture to use.  */
/* Which instruction set architecture to use.  */
int iq2000_isa;
int iq2000_isa;
 
 
/* Local variables.  */
/* Local variables.  */
 
 
/* The next branch instruction is a branch likely, not branch normal.  */
/* The next branch instruction is a branch likely, not branch normal.  */
static int iq2000_branch_likely;
static int iq2000_branch_likely;
 
 
/* Count of delay slots and how many are filled.  */
/* Count of delay slots and how many are filled.  */
static int dslots_load_total;
static int dslots_load_total;
static int dslots_load_filled;
static int dslots_load_filled;
static int dslots_jump_total;
static int dslots_jump_total;
 
 
/* # of nops needed by previous insn.  */
/* # of nops needed by previous insn.  */
static int dslots_number_nops;
static int dslots_number_nops;
 
 
/* Number of 1/2/3 word references to data items (i.e., not jal's).  */
/* Number of 1/2/3 word references to data items (i.e., not jal's).  */
static int num_refs[3];
static int num_refs[3];
 
 
/* Registers to check for load delay.  */
/* Registers to check for load delay.  */
static rtx iq2000_load_reg;
static rtx iq2000_load_reg;
static rtx iq2000_load_reg2;
static rtx iq2000_load_reg2;
static rtx iq2000_load_reg3;
static rtx iq2000_load_reg3;
static rtx iq2000_load_reg4;
static rtx iq2000_load_reg4;
 
 
/* Mode used for saving/restoring general purpose registers.  */
/* Mode used for saving/restoring general purpose registers.  */
static enum machine_mode gpr_mode;
static enum machine_mode gpr_mode;
 
 


/* Initialize the GCC target structure.  */
/* Initialize the GCC target structure.  */
static struct machine_function* iq2000_init_machine_status (void);
static struct machine_function* iq2000_init_machine_status (void);
static bool iq2000_handle_option      (size_t, const char *, int);
static bool iq2000_handle_option      (size_t, const char *, int);
static section *iq2000_select_rtx_section (enum machine_mode, rtx,
static section *iq2000_select_rtx_section (enum machine_mode, rtx,
                                           unsigned HOST_WIDE_INT);
                                           unsigned HOST_WIDE_INT);
static void iq2000_init_builtins      (void);
static void iq2000_init_builtins      (void);
static rtx  iq2000_expand_builtin     (tree, rtx, rtx, enum machine_mode, int);
static rtx  iq2000_expand_builtin     (tree, rtx, rtx, enum machine_mode, int);
static bool iq2000_return_in_memory   (const_tree, const_tree);
static bool iq2000_return_in_memory   (const_tree, const_tree);
static void iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *,
static void iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *,
                                           enum machine_mode, tree, int *,
                                           enum machine_mode, tree, int *,
                                           int);
                                           int);
static bool iq2000_rtx_costs          (rtx, int, int, int *, bool);
static bool iq2000_rtx_costs          (rtx, int, int, int *, bool);
static int  iq2000_address_cost       (rtx, bool);
static int  iq2000_address_cost       (rtx, bool);
static section *iq2000_select_section (tree, int, unsigned HOST_WIDE_INT);
static section *iq2000_select_section (tree, int, unsigned HOST_WIDE_INT);
static rtx  iq2000_legitimize_address (rtx, rtx, enum machine_mode);
static rtx  iq2000_legitimize_address (rtx, rtx, enum machine_mode);
static bool iq2000_pass_by_reference  (CUMULATIVE_ARGS *, enum machine_mode,
static bool iq2000_pass_by_reference  (CUMULATIVE_ARGS *, enum machine_mode,
                                       const_tree, bool);
                                       const_tree, bool);
static int  iq2000_arg_partial_bytes  (CUMULATIVE_ARGS *, enum machine_mode,
static int  iq2000_arg_partial_bytes  (CUMULATIVE_ARGS *, enum machine_mode,
                                       tree, bool);
                                       tree, bool);
static void iq2000_va_start           (tree, rtx);
static void iq2000_va_start           (tree, rtx);
static bool iq2000_legitimate_address_p (enum machine_mode, rtx, bool);
static bool iq2000_legitimate_address_p (enum machine_mode, rtx, bool);
static bool iq2000_can_eliminate      (const int, const int);
static bool iq2000_can_eliminate      (const int, const int);
static void iq2000_asm_trampoline_template (FILE *);
static void iq2000_asm_trampoline_template (FILE *);
static void iq2000_trampoline_init    (rtx, tree, rtx);
static void iq2000_trampoline_init    (rtx, tree, rtx);
static rtx iq2000_function_value      (const_tree, const_tree, bool);
static rtx iq2000_function_value      (const_tree, const_tree, bool);
static rtx iq2000_libcall_value       (enum machine_mode, const_rtx);
static rtx iq2000_libcall_value       (enum machine_mode, const_rtx);
 
 
#undef  TARGET_INIT_BUILTINS
#undef  TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS            iq2000_init_builtins
#define TARGET_INIT_BUILTINS            iq2000_init_builtins
#undef  TARGET_EXPAND_BUILTIN
#undef  TARGET_EXPAND_BUILTIN
#define TARGET_EXPAND_BUILTIN           iq2000_expand_builtin
#define TARGET_EXPAND_BUILTIN           iq2000_expand_builtin
#undef  TARGET_ASM_SELECT_RTX_SECTION
#undef  TARGET_ASM_SELECT_RTX_SECTION
#define TARGET_ASM_SELECT_RTX_SECTION   iq2000_select_rtx_section
#define TARGET_ASM_SELECT_RTX_SECTION   iq2000_select_rtx_section
#undef  TARGET_HANDLE_OPTION
#undef  TARGET_HANDLE_OPTION
#define TARGET_HANDLE_OPTION            iq2000_handle_option
#define TARGET_HANDLE_OPTION            iq2000_handle_option
#undef  TARGET_RTX_COSTS
#undef  TARGET_RTX_COSTS
#define TARGET_RTX_COSTS                iq2000_rtx_costs
#define TARGET_RTX_COSTS                iq2000_rtx_costs
#undef  TARGET_ADDRESS_COST
#undef  TARGET_ADDRESS_COST
#define TARGET_ADDRESS_COST             iq2000_address_cost
#define TARGET_ADDRESS_COST             iq2000_address_cost
#undef  TARGET_ASM_SELECT_SECTION
#undef  TARGET_ASM_SELECT_SECTION
#define TARGET_ASM_SELECT_SECTION       iq2000_select_section
#define TARGET_ASM_SELECT_SECTION       iq2000_select_section
 
 
#undef TARGET_LEGITIMIZE_ADDRESS
#undef TARGET_LEGITIMIZE_ADDRESS
#define TARGET_LEGITIMIZE_ADDRESS       iq2000_legitimize_address
#define TARGET_LEGITIMIZE_ADDRESS       iq2000_legitimize_address
 
 
/* The assembler supports switchable .bss sections, but
/* The assembler supports switchable .bss sections, but
   iq2000_select_section doesn't yet make use of them.  */
   iq2000_select_section doesn't yet make use of them.  */
#undef  TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
#undef  TARGET_HAVE_SWITCHABLE_BSS_SECTIONS
#define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
#define TARGET_HAVE_SWITCHABLE_BSS_SECTIONS false
 
 
#undef  TARGET_PROMOTE_FUNCTION_MODE
#undef  TARGET_PROMOTE_FUNCTION_MODE
#define TARGET_PROMOTE_FUNCTION_MODE    default_promote_function_mode_always_promote
#define TARGET_PROMOTE_FUNCTION_MODE    default_promote_function_mode_always_promote
#undef  TARGET_PROMOTE_PROTOTYPES
#undef  TARGET_PROMOTE_PROTOTYPES
#define TARGET_PROMOTE_PROTOTYPES       hook_bool_const_tree_true
#define TARGET_PROMOTE_PROTOTYPES       hook_bool_const_tree_true
 
 
#undef TARGET_FUNCTION_VALUE
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE           iq2000_function_value
#define TARGET_FUNCTION_VALUE           iq2000_function_value
#undef TARGET_LIBCALL_VALUE
#undef TARGET_LIBCALL_VALUE
#define TARGET_LIBCALL_VALUE            iq2000_libcall_value
#define TARGET_LIBCALL_VALUE            iq2000_libcall_value
#undef  TARGET_RETURN_IN_MEMORY
#undef  TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY         iq2000_return_in_memory
#define TARGET_RETURN_IN_MEMORY         iq2000_return_in_memory
#undef  TARGET_PASS_BY_REFERENCE
#undef  TARGET_PASS_BY_REFERENCE
#define TARGET_PASS_BY_REFERENCE        iq2000_pass_by_reference
#define TARGET_PASS_BY_REFERENCE        iq2000_pass_by_reference
#undef  TARGET_CALLEE_COPIES
#undef  TARGET_CALLEE_COPIES
#define TARGET_CALLEE_COPIES            hook_callee_copies_named
#define TARGET_CALLEE_COPIES            hook_callee_copies_named
#undef  TARGET_ARG_PARTIAL_BYTES
#undef  TARGET_ARG_PARTIAL_BYTES
#define TARGET_ARG_PARTIAL_BYTES        iq2000_arg_partial_bytes
#define TARGET_ARG_PARTIAL_BYTES        iq2000_arg_partial_bytes
 
 
#undef  TARGET_SETUP_INCOMING_VARARGS
#undef  TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS   iq2000_setup_incoming_varargs
#define TARGET_SETUP_INCOMING_VARARGS   iq2000_setup_incoming_varargs
#undef  TARGET_STRICT_ARGUMENT_NAMING
#undef  TARGET_STRICT_ARGUMENT_NAMING
#define TARGET_STRICT_ARGUMENT_NAMING   hook_bool_CUMULATIVE_ARGS_true
#define TARGET_STRICT_ARGUMENT_NAMING   hook_bool_CUMULATIVE_ARGS_true
 
 
#undef  TARGET_EXPAND_BUILTIN_VA_START
#undef  TARGET_EXPAND_BUILTIN_VA_START
#define TARGET_EXPAND_BUILTIN_VA_START  iq2000_va_start
#define TARGET_EXPAND_BUILTIN_VA_START  iq2000_va_start
 
 
#undef TARGET_LEGITIMATE_ADDRESS_P
#undef TARGET_LEGITIMATE_ADDRESS_P
#define TARGET_LEGITIMATE_ADDRESS_P     iq2000_legitimate_address_p
#define TARGET_LEGITIMATE_ADDRESS_P     iq2000_legitimate_address_p
 
 
#undef TARGET_CAN_ELIMINATE
#undef TARGET_CAN_ELIMINATE
#define TARGET_CAN_ELIMINATE            iq2000_can_eliminate
#define TARGET_CAN_ELIMINATE            iq2000_can_eliminate
 
 
#undef  TARGET_ASM_TRAMPOLINE_TEMPLATE
#undef  TARGET_ASM_TRAMPOLINE_TEMPLATE
#define TARGET_ASM_TRAMPOLINE_TEMPLATE  iq2000_asm_trampoline_template
#define TARGET_ASM_TRAMPOLINE_TEMPLATE  iq2000_asm_trampoline_template
#undef  TARGET_TRAMPOLINE_INIT
#undef  TARGET_TRAMPOLINE_INIT
#define TARGET_TRAMPOLINE_INIT          iq2000_trampoline_init
#define TARGET_TRAMPOLINE_INIT          iq2000_trampoline_init
 
 
struct gcc_target targetm = TARGET_INITIALIZER;
struct gcc_target targetm = TARGET_INITIALIZER;


/* Return nonzero if we split the address into high and low parts.  */
/* Return nonzero if we split the address into high and low parts.  */
 
 
int
int
iq2000_check_split (rtx address, enum machine_mode mode)
iq2000_check_split (rtx address, enum machine_mode mode)
{
{
  /* This is the same check used in simple_memory_operand.
  /* This is the same check used in simple_memory_operand.
     We use it here because LO_SUM is not offsettable.  */
     We use it here because LO_SUM is not offsettable.  */
  if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
  if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
    return 0;
    return 0;
 
 
  if ((GET_CODE (address) == SYMBOL_REF)
  if ((GET_CODE (address) == SYMBOL_REF)
      || (GET_CODE (address) == CONST
      || (GET_CODE (address) == CONST
          && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF)
          && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF)
      || GET_CODE (address) == LABEL_REF)
      || GET_CODE (address) == LABEL_REF)
    return 1;
    return 1;
 
 
  return 0;
  return 0;
}
}
 
 
/* Return nonzero if REG is valid for MODE.  */
/* Return nonzero if REG is valid for MODE.  */
 
 
int
int
iq2000_reg_mode_ok_for_base_p (rtx reg,
iq2000_reg_mode_ok_for_base_p (rtx reg,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               int strict)
                               int strict)
{
{
  return (strict
  return (strict
          ? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode)
          ? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode)
          : GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));
          : GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));
}
}
 
 
/* Return a nonzero value if XINSN is a legitimate address for a
/* Return a nonzero value if XINSN is a legitimate address for a
   memory operand of the indicated MODE.  STRICT is nonzero if this
   memory operand of the indicated MODE.  STRICT is nonzero if this
   function is called during reload.  */
   function is called during reload.  */
 
 
bool
bool
iq2000_legitimate_address_p (enum machine_mode mode, rtx xinsn, bool strict)
iq2000_legitimate_address_p (enum machine_mode mode, rtx xinsn, bool strict)
{
{
  if (TARGET_DEBUG_A_MODE)
  if (TARGET_DEBUG_A_MODE)
    {
    {
      GO_PRINTF2 ("\n========== legitimate_address_p, %sstrict\n",
      GO_PRINTF2 ("\n========== legitimate_address_p, %sstrict\n",
                  strict ? "" : "not ");
                  strict ? "" : "not ");
      GO_DEBUG_RTX (xinsn);
      GO_DEBUG_RTX (xinsn);
    }
    }
 
 
  /* Check for constant before stripping off SUBREG, so that we don't
  /* Check for constant before stripping off SUBREG, so that we don't
     accept (subreg (const_int)) which will fail to reload.  */
     accept (subreg (const_int)) which will fail to reload.  */
  if (CONSTANT_ADDRESS_P (xinsn)
  if (CONSTANT_ADDRESS_P (xinsn)
      && ! (iq2000_check_split (xinsn, mode))
      && ! (iq2000_check_split (xinsn, mode))
      && ! (GET_CODE (xinsn) == CONST_INT && ! SMALL_INT (xinsn)))
      && ! (GET_CODE (xinsn) == CONST_INT && ! SMALL_INT (xinsn)))
    return 1;
    return 1;
 
 
  while (GET_CODE (xinsn) == SUBREG)
  while (GET_CODE (xinsn) == SUBREG)
    xinsn = SUBREG_REG (xinsn);
    xinsn = SUBREG_REG (xinsn);
 
 
  if (GET_CODE (xinsn) == REG
  if (GET_CODE (xinsn) == REG
      && iq2000_reg_mode_ok_for_base_p (xinsn, mode, strict))
      && iq2000_reg_mode_ok_for_base_p (xinsn, mode, strict))
    return 1;
    return 1;
 
 
  if (GET_CODE (xinsn) == LO_SUM)
  if (GET_CODE (xinsn) == LO_SUM)
    {
    {
      rtx xlow0 = XEXP (xinsn, 0);
      rtx xlow0 = XEXP (xinsn, 0);
      rtx xlow1 = XEXP (xinsn, 1);
      rtx xlow1 = XEXP (xinsn, 1);
 
 
      while (GET_CODE (xlow0) == SUBREG)
      while (GET_CODE (xlow0) == SUBREG)
        xlow0 = SUBREG_REG (xlow0);
        xlow0 = SUBREG_REG (xlow0);
      if (GET_CODE (xlow0) == REG
      if (GET_CODE (xlow0) == REG
          && iq2000_reg_mode_ok_for_base_p (xlow0, mode, strict)
          && iq2000_reg_mode_ok_for_base_p (xlow0, mode, strict)
          && iq2000_check_split (xlow1, mode))
          && iq2000_check_split (xlow1, mode))
        return 1;
        return 1;
    }
    }
 
 
  if (GET_CODE (xinsn) == PLUS)
  if (GET_CODE (xinsn) == PLUS)
    {
    {
      rtx xplus0 = XEXP (xinsn, 0);
      rtx xplus0 = XEXP (xinsn, 0);
      rtx xplus1 = XEXP (xinsn, 1);
      rtx xplus1 = XEXP (xinsn, 1);
      enum rtx_code code0;
      enum rtx_code code0;
      enum rtx_code code1;
      enum rtx_code code1;
 
 
      while (GET_CODE (xplus0) == SUBREG)
      while (GET_CODE (xplus0) == SUBREG)
        xplus0 = SUBREG_REG (xplus0);
        xplus0 = SUBREG_REG (xplus0);
      code0 = GET_CODE (xplus0);
      code0 = GET_CODE (xplus0);
 
 
      while (GET_CODE (xplus1) == SUBREG)
      while (GET_CODE (xplus1) == SUBREG)
        xplus1 = SUBREG_REG (xplus1);
        xplus1 = SUBREG_REG (xplus1);
      code1 = GET_CODE (xplus1);
      code1 = GET_CODE (xplus1);
 
 
      if (code0 == REG
      if (code0 == REG
          && iq2000_reg_mode_ok_for_base_p (xplus0, mode, strict))
          && iq2000_reg_mode_ok_for_base_p (xplus0, mode, strict))
        {
        {
          if (code1 == CONST_INT && SMALL_INT (xplus1)
          if (code1 == CONST_INT && SMALL_INT (xplus1)
              && SMALL_INT_UNSIGNED (xplus1) /* No negative offsets */)
              && SMALL_INT_UNSIGNED (xplus1) /* No negative offsets */)
            return 1;
            return 1;
        }
        }
    }
    }
 
 
  if (TARGET_DEBUG_A_MODE)
  if (TARGET_DEBUG_A_MODE)
    GO_PRINTF ("Not a enum machine_mode mode, legitimate address\n");
    GO_PRINTF ("Not a enum machine_mode mode, legitimate address\n");
 
 
  /* The address was not legitimate.  */
  /* The address was not legitimate.  */
  return 0;
  return 0;
}
}


/* Returns an operand string for the given instruction's delay slot,
/* Returns an operand string for the given instruction's delay slot,
   after updating filled delay slot statistics.
   after updating filled delay slot statistics.
 
 
   We assume that operands[0] is the target register that is set.
   We assume that operands[0] is the target register that is set.
 
 
   In order to check the next insn, most of this functionality is moved
   In order to check the next insn, most of this functionality is moved
   to FINAL_PRESCAN_INSN, and we just set the global variables that
   to FINAL_PRESCAN_INSN, and we just set the global variables that
   it needs.  */
   it needs.  */
 
 
const char *
const char *
iq2000_fill_delay_slot (const char *ret, enum delay_type type, rtx operands[],
iq2000_fill_delay_slot (const char *ret, enum delay_type type, rtx operands[],
                        rtx cur_insn)
                        rtx cur_insn)
{
{
  rtx set_reg;
  rtx set_reg;
  enum machine_mode mode;
  enum machine_mode mode;
  rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
  rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
  int num_nops;
  int num_nops;
 
 
  if (type == DELAY_LOAD || type == DELAY_FCMP)
  if (type == DELAY_LOAD || type == DELAY_FCMP)
    num_nops = 1;
    num_nops = 1;
 
 
  else
  else
    num_nops = 0;
    num_nops = 0;
 
 
  /* Make sure that we don't put nop's after labels.  */
  /* Make sure that we don't put nop's after labels.  */
  next_insn = NEXT_INSN (cur_insn);
  next_insn = NEXT_INSN (cur_insn);
  while (next_insn != 0
  while (next_insn != 0
         && (GET_CODE (next_insn) == NOTE
         && (GET_CODE (next_insn) == NOTE
             || GET_CODE (next_insn) == CODE_LABEL))
             || GET_CODE (next_insn) == CODE_LABEL))
    next_insn = NEXT_INSN (next_insn);
    next_insn = NEXT_INSN (next_insn);
 
 
  dslots_load_total += num_nops;
  dslots_load_total += num_nops;
  if (TARGET_DEBUG_C_MODE
  if (TARGET_DEBUG_C_MODE
      || type == DELAY_NONE
      || type == DELAY_NONE
      || operands == 0
      || operands == 0
      || cur_insn == 0
      || cur_insn == 0
      || next_insn == 0
      || next_insn == 0
      || GET_CODE (next_insn) == CODE_LABEL
      || GET_CODE (next_insn) == CODE_LABEL
      || (set_reg = operands[0]) == 0)
      || (set_reg = operands[0]) == 0)
    {
    {
      dslots_number_nops = 0;
      dslots_number_nops = 0;
      iq2000_load_reg  = 0;
      iq2000_load_reg  = 0;
      iq2000_load_reg2 = 0;
      iq2000_load_reg2 = 0;
      iq2000_load_reg3 = 0;
      iq2000_load_reg3 = 0;
      iq2000_load_reg4 = 0;
      iq2000_load_reg4 = 0;
 
 
      return ret;
      return ret;
    }
    }
 
 
  set_reg = operands[0];
  set_reg = operands[0];
  if (set_reg == 0)
  if (set_reg == 0)
    return ret;
    return ret;
 
 
  while (GET_CODE (set_reg) == SUBREG)
  while (GET_CODE (set_reg) == SUBREG)
    set_reg = SUBREG_REG (set_reg);
    set_reg = SUBREG_REG (set_reg);
 
 
  mode = GET_MODE (set_reg);
  mode = GET_MODE (set_reg);
  dslots_number_nops = num_nops;
  dslots_number_nops = num_nops;
  iq2000_load_reg = set_reg;
  iq2000_load_reg = set_reg;
  if (GET_MODE_SIZE (mode)
  if (GET_MODE_SIZE (mode)
      > (unsigned) (UNITS_PER_WORD))
      > (unsigned) (UNITS_PER_WORD))
    iq2000_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
    iq2000_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
  else
  else
    iq2000_load_reg2 = 0;
    iq2000_load_reg2 = 0;
 
 
  return ret;
  return ret;
}
}


/* Determine whether a memory reference takes one (based off of the GP
/* Determine whether a memory reference takes one (based off of the GP
   pointer), two (normal), or three (label + reg) instructions, and bump the
   pointer), two (normal), or three (label + reg) instructions, and bump the
   appropriate counter for -mstats.  */
   appropriate counter for -mstats.  */
 
 
static void
static void
iq2000_count_memory_refs (rtx op, int num)
iq2000_count_memory_refs (rtx op, int num)
{
{
  int additional = 0;
  int additional = 0;
  int n_words = 0;
  int n_words = 0;
  rtx addr, plus0, plus1;
  rtx addr, plus0, plus1;
  enum rtx_code code0, code1;
  enum rtx_code code0, code1;
  int looping;
  int looping;
 
 
  if (TARGET_DEBUG_B_MODE)
  if (TARGET_DEBUG_B_MODE)
    {
    {
      fprintf (stderr, "\n========== iq2000_count_memory_refs:\n");
      fprintf (stderr, "\n========== iq2000_count_memory_refs:\n");
      debug_rtx (op);
      debug_rtx (op);
    }
    }
 
 
  /* Skip MEM if passed, otherwise handle movsi of address.  */
  /* Skip MEM if passed, otherwise handle movsi of address.  */
  addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
  addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
 
 
  /* Loop, going through the address RTL.  */
  /* Loop, going through the address RTL.  */
  do
  do
    {
    {
      looping = FALSE;
      looping = FALSE;
      switch (GET_CODE (addr))
      switch (GET_CODE (addr))
        {
        {
        case REG:
        case REG:
        case CONST_INT:
        case CONST_INT:
        case LO_SUM:
        case LO_SUM:
          break;
          break;
 
 
        case PLUS:
        case PLUS:
          plus0 = XEXP (addr, 0);
          plus0 = XEXP (addr, 0);
          plus1 = XEXP (addr, 1);
          plus1 = XEXP (addr, 1);
          code0 = GET_CODE (plus0);
          code0 = GET_CODE (plus0);
          code1 = GET_CODE (plus1);
          code1 = GET_CODE (plus1);
 
 
          if (code0 == REG)
          if (code0 == REG)
            {
            {
              additional++;
              additional++;
              addr = plus1;
              addr = plus1;
              looping = 1;
              looping = 1;
              continue;
              continue;
            }
            }
 
 
          if (code0 == CONST_INT)
          if (code0 == CONST_INT)
            {
            {
              addr = plus1;
              addr = plus1;
              looping = 1;
              looping = 1;
              continue;
              continue;
            }
            }
 
 
          if (code1 == REG)
          if (code1 == REG)
            {
            {
              additional++;
              additional++;
              addr = plus0;
              addr = plus0;
              looping = 1;
              looping = 1;
              continue;
              continue;
            }
            }
 
 
          if (code1 == CONST_INT)
          if (code1 == CONST_INT)
            {
            {
              addr = plus0;
              addr = plus0;
              looping = 1;
              looping = 1;
              continue;
              continue;
            }
            }
 
 
          if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
          if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
            {
            {
              addr = plus0;
              addr = plus0;
              looping = 1;
              looping = 1;
              continue;
              continue;
            }
            }
 
 
          if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
          if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
            {
            {
              addr = plus1;
              addr = plus1;
              looping = 1;
              looping = 1;
              continue;
              continue;
            }
            }
 
 
          break;
          break;
 
 
        case LABEL_REF:
        case LABEL_REF:
          n_words = 2;          /* Always 2 words.  */
          n_words = 2;          /* Always 2 words.  */
          break;
          break;
 
 
        case CONST:
        case CONST:
          addr = XEXP (addr, 0);
          addr = XEXP (addr, 0);
          looping = 1;
          looping = 1;
          continue;
          continue;
 
 
        case SYMBOL_REF:
        case SYMBOL_REF:
          n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
          n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
          break;
          break;
 
 
        default:
        default:
          break;
          break;
        }
        }
    }
    }
  while (looping);
  while (looping);
 
 
  if (n_words == 0)
  if (n_words == 0)
    return;
    return;
 
 
  n_words += additional;
  n_words += additional;
  if (n_words > 3)
  if (n_words > 3)
    n_words = 3;
    n_words = 3;
 
 
  num_refs[n_words-1] += num;
  num_refs[n_words-1] += num;
}
}


/* Abort after printing out a specific insn.  */
/* Abort after printing out a specific insn.  */
 
 
static void
static void
abort_with_insn (rtx insn, const char * reason)
abort_with_insn (rtx insn, const char * reason)
{
{
  error (reason);
  error (reason);
  debug_rtx (insn);
  debug_rtx (insn);
  fancy_abort (__FILE__, __LINE__, __FUNCTION__);
  fancy_abort (__FILE__, __LINE__, __FUNCTION__);
}
}


/* Return the appropriate instructions to move one operand to another.  */
/* Return the appropriate instructions to move one operand to another.  */
 
 
const char *
const char *
iq2000_move_1word (rtx operands[], rtx insn, int unsignedp)
iq2000_move_1word (rtx operands[], rtx insn, int unsignedp)
{
{
  const char *ret = 0;
  const char *ret = 0;
  rtx op0 = operands[0];
  rtx op0 = operands[0];
  rtx op1 = operands[1];
  rtx op1 = operands[1];
  enum rtx_code code0 = GET_CODE (op0);
  enum rtx_code code0 = GET_CODE (op0);
  enum rtx_code code1 = GET_CODE (op1);
  enum rtx_code code1 = GET_CODE (op1);
  enum machine_mode mode = GET_MODE (op0);
  enum machine_mode mode = GET_MODE (op0);
  int subreg_offset0 = 0;
  int subreg_offset0 = 0;
  int subreg_offset1 = 0;
  int subreg_offset1 = 0;
  enum delay_type delay = DELAY_NONE;
  enum delay_type delay = DELAY_NONE;
 
 
  while (code0 == SUBREG)
  while (code0 == SUBREG)
    {
    {
      subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
      subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
                                             GET_MODE (SUBREG_REG (op0)),
                                             GET_MODE (SUBREG_REG (op0)),
                                             SUBREG_BYTE (op0),
                                             SUBREG_BYTE (op0),
                                             GET_MODE (op0));
                                             GET_MODE (op0));
      op0 = SUBREG_REG (op0);
      op0 = SUBREG_REG (op0);
      code0 = GET_CODE (op0);
      code0 = GET_CODE (op0);
    }
    }
 
 
  while (code1 == SUBREG)
  while (code1 == SUBREG)
    {
    {
      subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
      subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
                                             GET_MODE (SUBREG_REG (op1)),
                                             GET_MODE (SUBREG_REG (op1)),
                                             SUBREG_BYTE (op1),
                                             SUBREG_BYTE (op1),
                                             GET_MODE (op1));
                                             GET_MODE (op1));
      op1 = SUBREG_REG (op1);
      op1 = SUBREG_REG (op1);
      code1 = GET_CODE (op1);
      code1 = GET_CODE (op1);
    }
    }
 
 
  /* For our purposes, a condition code mode is the same as SImode.  */
  /* For our purposes, a condition code mode is the same as SImode.  */
  if (mode == CCmode)
  if (mode == CCmode)
    mode = SImode;
    mode = SImode;
 
 
  if (code0 == REG)
  if (code0 == REG)
    {
    {
      int regno0 = REGNO (op0) + subreg_offset0;
      int regno0 = REGNO (op0) + subreg_offset0;
 
 
      if (code1 == REG)
      if (code1 == REG)
        {
        {
          int regno1 = REGNO (op1) + subreg_offset1;
          int regno1 = REGNO (op1) + subreg_offset1;
 
 
          /* Do not do anything for assigning a register to itself */
          /* Do not do anything for assigning a register to itself */
          if (regno0 == regno1)
          if (regno0 == regno1)
            ret = "";
            ret = "";
 
 
          else if (GP_REG_P (regno0))
          else if (GP_REG_P (regno0))
            {
            {
              if (GP_REG_P (regno1))
              if (GP_REG_P (regno1))
                ret = "or\t%0,%%0,%1";
                ret = "or\t%0,%%0,%1";
            }
            }
 
 
        }
        }
 
 
      else if (code1 == MEM)
      else if (code1 == MEM)
        {
        {
          delay = DELAY_LOAD;
          delay = DELAY_LOAD;
 
 
          if (TARGET_STATS)
          if (TARGET_STATS)
            iq2000_count_memory_refs (op1, 1);
            iq2000_count_memory_refs (op1, 1);
 
 
          if (GP_REG_P (regno0))
          if (GP_REG_P (regno0))
            {
            {
              /* For loads, use the mode of the memory item, instead of the
              /* For loads, use the mode of the memory item, instead of the
                 target, so zero/sign extend can use this code as well.  */
                 target, so zero/sign extend can use this code as well.  */
              switch (GET_MODE (op1))
              switch (GET_MODE (op1))
                {
                {
                default:
                default:
                  break;
                  break;
                case SFmode:
                case SFmode:
                  ret = "lw\t%0,%1";
                  ret = "lw\t%0,%1";
                  break;
                  break;
                case SImode:
                case SImode:
                case CCmode:
                case CCmode:
                  ret = "lw\t%0,%1";
                  ret = "lw\t%0,%1";
                  break;
                  break;
                case HImode:
                case HImode:
                  ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
                  ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
                  break;
                  break;
                case QImode:
                case QImode:
                  ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
                  ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
                  break;
                  break;
                }
                }
            }
            }
        }
        }
 
 
      else if (code1 == CONST_INT
      else if (code1 == CONST_INT
               || (code1 == CONST_DOUBLE
               || (code1 == CONST_DOUBLE
                   && GET_MODE (op1) == VOIDmode))
                   && GET_MODE (op1) == VOIDmode))
        {
        {
          if (code1 == CONST_DOUBLE)
          if (code1 == CONST_DOUBLE)
            {
            {
              /* This can happen when storing constants into long long
              /* This can happen when storing constants into long long
                 bitfields.  Just store the least significant word of
                 bitfields.  Just store the least significant word of
                 the value.  */
                 the value.  */
              operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
              operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
            }
            }
 
 
          if (INTVAL (op1) == 0)
          if (INTVAL (op1) == 0)
            {
            {
              if (GP_REG_P (regno0))
              if (GP_REG_P (regno0))
                ret = "or\t%0,%%0,%z1";
                ret = "or\t%0,%%0,%z1";
            }
            }
         else if (GP_REG_P (regno0))
         else if (GP_REG_P (regno0))
            {
            {
              if (SMALL_INT_UNSIGNED (op1))
              if (SMALL_INT_UNSIGNED (op1))
                ret = "ori\t%0,%%0,%x1\t\t\t# %1";
                ret = "ori\t%0,%%0,%x1\t\t\t# %1";
              else if (SMALL_INT (op1))
              else if (SMALL_INT (op1))
                ret = "addiu\t%0,%%0,%1\t\t\t# %1";
                ret = "addiu\t%0,%%0,%1\t\t\t# %1";
              else
              else
                ret = "lui\t%0,%X1\t\t\t# %1\n\tori\t%0,%0,%x1";
                ret = "lui\t%0,%X1\t\t\t# %1\n\tori\t%0,%0,%x1";
            }
            }
        }
        }
 
 
      else if (code1 == CONST_DOUBLE && mode == SFmode)
      else if (code1 == CONST_DOUBLE && mode == SFmode)
        {
        {
          if (op1 == CONST0_RTX (SFmode))
          if (op1 == CONST0_RTX (SFmode))
            {
            {
              if (GP_REG_P (regno0))
              if (GP_REG_P (regno0))
                ret = "or\t%0,%%0,%.";
                ret = "or\t%0,%%0,%.";
            }
            }
 
 
          else
          else
            {
            {
              delay = DELAY_LOAD;
              delay = DELAY_LOAD;
              ret = "li.s\t%0,%1";
              ret = "li.s\t%0,%1";
            }
            }
        }
        }
 
 
      else if (code1 == LABEL_REF)
      else if (code1 == LABEL_REF)
        {
        {
          if (TARGET_STATS)
          if (TARGET_STATS)
            iq2000_count_memory_refs (op1, 1);
            iq2000_count_memory_refs (op1, 1);
 
 
          ret = "la\t%0,%a1";
          ret = "la\t%0,%a1";
        }
        }
 
 
      else if (code1 == SYMBOL_REF || code1 == CONST)
      else if (code1 == SYMBOL_REF || code1 == CONST)
        {
        {
          if (TARGET_STATS)
          if (TARGET_STATS)
            iq2000_count_memory_refs (op1, 1);
            iq2000_count_memory_refs (op1, 1);
 
 
          ret = "la\t%0,%a1";
          ret = "la\t%0,%a1";
        }
        }
 
 
      else if (code1 == PLUS)
      else if (code1 == PLUS)
        {
        {
          rtx add_op0 = XEXP (op1, 0);
          rtx add_op0 = XEXP (op1, 0);
          rtx add_op1 = XEXP (op1, 1);
          rtx add_op1 = XEXP (op1, 1);
 
 
          if (GET_CODE (XEXP (op1, 1)) == REG
          if (GET_CODE (XEXP (op1, 1)) == REG
              && GET_CODE (XEXP (op1, 0)) == CONST_INT)
              && GET_CODE (XEXP (op1, 0)) == CONST_INT)
            add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
            add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
 
 
          operands[2] = add_op0;
          operands[2] = add_op0;
          operands[3] = add_op1;
          operands[3] = add_op1;
          ret = "add%:\t%0,%2,%3";
          ret = "add%:\t%0,%2,%3";
        }
        }
 
 
      else if (code1 == HIGH)
      else if (code1 == HIGH)
        {
        {
          operands[1] = XEXP (op1, 0);
          operands[1] = XEXP (op1, 0);
          ret = "lui\t%0,%%hi(%1)";
          ret = "lui\t%0,%%hi(%1)";
        }
        }
    }
    }
 
 
  else if (code0 == MEM)
  else if (code0 == MEM)
    {
    {
      if (TARGET_STATS)
      if (TARGET_STATS)
        iq2000_count_memory_refs (op0, 1);
        iq2000_count_memory_refs (op0, 1);
 
 
      if (code1 == REG)
      if (code1 == REG)
        {
        {
          int regno1 = REGNO (op1) + subreg_offset1;
          int regno1 = REGNO (op1) + subreg_offset1;
 
 
          if (GP_REG_P (regno1))
          if (GP_REG_P (regno1))
            {
            {
              switch (mode)
              switch (mode)
                {
                {
                case SFmode: ret = "sw\t%1,%0"; break;
                case SFmode: ret = "sw\t%1,%0"; break;
                case SImode: ret = "sw\t%1,%0"; break;
                case SImode: ret = "sw\t%1,%0"; break;
                case HImode: ret = "sh\t%1,%0"; break;
                case HImode: ret = "sh\t%1,%0"; break;
                case QImode: ret = "sb\t%1,%0"; break;
                case QImode: ret = "sb\t%1,%0"; break;
                default: break;
                default: break;
                }
                }
            }
            }
        }
        }
 
 
      else if (code1 == CONST_INT && INTVAL (op1) == 0)
      else if (code1 == CONST_INT && INTVAL (op1) == 0)
        {
        {
          switch (mode)
          switch (mode)
            {
            {
            case SFmode: ret = "sw\t%z1,%0"; break;
            case SFmode: ret = "sw\t%z1,%0"; break;
            case SImode: ret = "sw\t%z1,%0"; break;
            case SImode: ret = "sw\t%z1,%0"; break;
            case HImode: ret = "sh\t%z1,%0"; break;
            case HImode: ret = "sh\t%z1,%0"; break;
            case QImode: ret = "sb\t%z1,%0"; break;
            case QImode: ret = "sb\t%z1,%0"; break;
            default: break;
            default: break;
            }
            }
        }
        }
 
 
      else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
      else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
        {
        {
          switch (mode)
          switch (mode)
            {
            {
            case SFmode: ret = "sw\t%.,%0"; break;
            case SFmode: ret = "sw\t%.,%0"; break;
            case SImode: ret = "sw\t%.,%0"; break;
            case SImode: ret = "sw\t%.,%0"; break;
            case HImode: ret = "sh\t%.,%0"; break;
            case HImode: ret = "sh\t%.,%0"; break;
            case QImode: ret = "sb\t%.,%0"; break;
            case QImode: ret = "sb\t%.,%0"; break;
            default: break;
            default: break;
            }
            }
        }
        }
    }
    }
 
 
  if (ret == 0)
  if (ret == 0)
    {
    {
      abort_with_insn (insn, "Bad move");
      abort_with_insn (insn, "Bad move");
      return 0;
      return 0;
    }
    }
 
 
  if (delay != DELAY_NONE)
  if (delay != DELAY_NONE)
    return iq2000_fill_delay_slot (ret, delay, operands, insn);
    return iq2000_fill_delay_slot (ret, delay, operands, insn);
 
 
  return ret;
  return ret;
}
}


/* Provide the costs of an addressing mode that contains ADDR.  */
/* Provide the costs of an addressing mode that contains ADDR.  */
 
 
static int
static int
iq2000_address_cost (rtx addr, bool speed)
iq2000_address_cost (rtx addr, bool speed)
{
{
  switch (GET_CODE (addr))
  switch (GET_CODE (addr))
    {
    {
    case LO_SUM:
    case LO_SUM:
      return 1;
      return 1;
 
 
    case LABEL_REF:
    case LABEL_REF:
      return 2;
      return 2;
 
 
    case CONST:
    case CONST:
      {
      {
        rtx offset = const0_rtx;
        rtx offset = const0_rtx;
 
 
        addr = eliminate_constant_term (XEXP (addr, 0), & offset);
        addr = eliminate_constant_term (XEXP (addr, 0), & offset);
        if (GET_CODE (addr) == LABEL_REF)
        if (GET_CODE (addr) == LABEL_REF)
          return 2;
          return 2;
 
 
        if (GET_CODE (addr) != SYMBOL_REF)
        if (GET_CODE (addr) != SYMBOL_REF)
          return 4;
          return 4;
 
 
        if (! SMALL_INT (offset))
        if (! SMALL_INT (offset))
          return 2;
          return 2;
      }
      }
 
 
      /* Fall through.  */
      /* Fall through.  */
 
 
    case SYMBOL_REF:
    case SYMBOL_REF:
      return SYMBOL_REF_FLAG (addr) ? 1 : 2;
      return SYMBOL_REF_FLAG (addr) ? 1 : 2;
 
 
    case PLUS:
    case PLUS:
      {
      {
        rtx plus0 = XEXP (addr, 0);
        rtx plus0 = XEXP (addr, 0);
        rtx plus1 = XEXP (addr, 1);
        rtx plus1 = XEXP (addr, 1);
 
 
        if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
        if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
          plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
          plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
 
 
        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:
          case HIGH:
          case HIGH:
          case LO_SUM:
          case LO_SUM:
            return iq2000_address_cost (plus1, speed) + 1;
            return iq2000_address_cost (plus1, speed) + 1;
 
 
          default:
          default:
            break;
            break;
          }
          }
      }
      }
 
 
    default:
    default:
      break;
      break;
    }
    }
 
 
  return 4;
  return 4;
}
}


/* Make normal rtx_code into something we can index from an array.  */
/* Make normal rtx_code into something we can index from an array.  */
 
 
static enum internal_test
static enum internal_test
map_test_to_internal_test (enum rtx_code test_code)
map_test_to_internal_test (enum rtx_code test_code)
{
{
  enum internal_test test = ITEST_MAX;
  enum internal_test test = ITEST_MAX;
 
 
  switch (test_code)
  switch (test_code)
    {
    {
    case EQ:  test = ITEST_EQ;  break;
    case EQ:  test = ITEST_EQ;  break;
    case NE:  test = ITEST_NE;  break;
    case NE:  test = ITEST_NE;  break;
    case GT:  test = ITEST_GT;  break;
    case GT:  test = ITEST_GT;  break;
    case GE:  test = ITEST_GE;  break;
    case GE:  test = ITEST_GE;  break;
    case LT:  test = ITEST_LT;  break;
    case LT:  test = ITEST_LT;  break;
    case LE:  test = ITEST_LE;  break;
    case LE:  test = ITEST_LE;  break;
    case GTU: test = ITEST_GTU; break;
    case GTU: test = ITEST_GTU; break;
    case GEU: test = ITEST_GEU; break;
    case GEU: test = ITEST_GEU; break;
    case LTU: test = ITEST_LTU; break;
    case LTU: test = ITEST_LTU; break;
    case LEU: test = ITEST_LEU; break;
    case LEU: test = ITEST_LEU; break;
    default:                    break;
    default:                    break;
    }
    }
 
 
  return test;
  return test;
}
}


/* Generate the code to do a TEST_CODE comparison on two integer values CMP0
/* Generate the code to do a TEST_CODE comparison on two integer values CMP0
   and CMP1.  P_INVERT is NULL or ptr if branch needs to reverse its test.
   and CMP1.  P_INVERT is NULL or ptr if branch needs to reverse its test.
   The return value RESULT is:
   The return value RESULT is:
   (reg:SI xx)          The pseudo register the comparison is in
   (reg:SI xx)          The pseudo register the comparison is in
   0                    No register, generate a simple branch.  */
   0                    No register, generate a simple branch.  */
 
 
rtx
rtx
gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0, rtx cmp1,
gen_int_relational (enum rtx_code test_code, rtx result, rtx cmp0, rtx cmp1,
                    int *p_invert)
                    int *p_invert)
{
{
  struct cmp_info
  struct cmp_info
  {
  {
    enum rtx_code test_code;    /* Code to use in instruction (LT vs. LTU).  */
    enum rtx_code test_code;    /* Code to use in instruction (LT vs. LTU).  */
    int const_low;              /* Low bound of constant we can accept.  */
    int const_low;              /* Low bound of constant we can accept.  */
    int const_high;             /* High bound of constant we can accept.  */
    int const_high;             /* High bound of constant we can accept.  */
    int const_add;              /* Constant to add (convert LE -> LT).  */
    int const_add;              /* Constant to add (convert LE -> LT).  */
    int reverse_regs;           /* Reverse registers in test.  */
    int reverse_regs;           /* Reverse registers in test.  */
    int invert_const;           /* != 0 if invert value if cmp1 is constant.  */
    int invert_const;           /* != 0 if invert value if cmp1 is constant.  */
    int invert_reg;             /* != 0 if invert value if cmp1 is register.  */
    int invert_reg;             /* != 0 if invert value if cmp1 is register.  */
    int unsignedp;              /* != 0 for unsigned comparisons.  */
    int unsignedp;              /* != 0 for unsigned comparisons.  */
  };
  };
 
 
  static struct cmp_info info[ (int)ITEST_MAX ] =
  static struct cmp_info info[ (int)ITEST_MAX ] =
  {
  {
    { XOR,       0,  65535,  0,    0,  0,    0, 0 },  /* EQ  */
    { XOR,       0,  65535,  0,    0,  0,    0, 0 },  /* EQ  */
    { XOR,       0,  65535,  0,    0,  1,   1, 0 }, /* NE  */
    { XOR,       0,  65535,  0,    0,  1,   1, 0 }, /* NE  */
    { LT,   -32769,  32766,  1,  1,  1,  0, 0 },  /* GT  */
    { LT,   -32769,  32766,  1,  1,  1,  0, 0 },  /* GT  */
    { LT,   -32768,  32767,  0,   0,  1,   1, 0 }, /* GE  */
    { LT,   -32768,  32767,  0,   0,  1,   1, 0 }, /* GE  */
    { LT,   -32768,  32767,  0,   0,  0,    0, 0 },  /* LT  */
    { LT,   -32768,  32767,  0,   0,  0,    0, 0 },  /* LT  */
    { LT,   -32769,  32766,  1,  1,  0,   1, 0 }, /* LE  */
    { LT,   -32769,  32766,  1,  1,  0,   1, 0 }, /* LE  */
    { LTU,  -32769,  32766,  1,  1,  1,  0, 1 }, /* GTU */
    { LTU,  -32769,  32766,  1,  1,  1,  0, 1 }, /* GTU */
    { LTU,  -32768,  32767,  0,   0,  1,   1, 1 },        /* GEU */
    { LTU,  -32768,  32767,  0,   0,  1,   1, 1 },        /* GEU */
    { LTU,  -32768,  32767,  0,   0,  0,    0, 1 }, /* LTU */
    { LTU,  -32768,  32767,  0,   0,  0,    0, 1 }, /* LTU */
    { LTU,  -32769,  32766,  1,  1,  0,   1, 1 },        /* LEU */
    { LTU,  -32769,  32766,  1,  1,  0,   1, 1 },        /* LEU */
  };
  };
 
 
  enum internal_test test;
  enum internal_test test;
  enum machine_mode mode;
  enum machine_mode mode;
  struct cmp_info *p_info;
  struct cmp_info *p_info;
  int branch_p;
  int branch_p;
  int eqne_p;
  int eqne_p;
  int invert;
  int invert;
  rtx reg;
  rtx reg;
  rtx reg2;
  rtx reg2;
 
 
  test = map_test_to_internal_test (test_code);
  test = map_test_to_internal_test (test_code);
  gcc_assert (test != ITEST_MAX);
  gcc_assert (test != ITEST_MAX);
 
 
  p_info = &info[(int) test];
  p_info = &info[(int) test];
  eqne_p = (p_info->test_code == XOR);
  eqne_p = (p_info->test_code == XOR);
 
 
  mode = GET_MODE (cmp0);
  mode = GET_MODE (cmp0);
  if (mode == VOIDmode)
  if (mode == VOIDmode)
    mode = GET_MODE (cmp1);
    mode = GET_MODE (cmp1);
 
 
  /* Eliminate simple branches.  */
  /* Eliminate simple branches.  */
  branch_p = (result == 0);
  branch_p = (result == 0);
  if (branch_p)
  if (branch_p)
    {
    {
      if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
      if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
        {
        {
          /* Comparisons against zero are simple branches.  */
          /* Comparisons against zero are simple branches.  */
          if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
          if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
            return 0;
            return 0;
 
 
          /* Test for beq/bne.  */
          /* Test for beq/bne.  */
          if (eqne_p)
          if (eqne_p)
            return 0;
            return 0;
        }
        }
 
 
      /* Allocate a pseudo to calculate the value in.  */
      /* Allocate a pseudo to calculate the value in.  */
      result = gen_reg_rtx (mode);
      result = gen_reg_rtx (mode);
    }
    }
 
 
  /* Make sure we can handle any constants given to us.  */
  /* Make sure we can handle any constants given to us.  */
  if (GET_CODE (cmp0) == CONST_INT)
  if (GET_CODE (cmp0) == CONST_INT)
    cmp0 = force_reg (mode, cmp0);
    cmp0 = force_reg (mode, cmp0);
 
 
  if (GET_CODE (cmp1) == CONST_INT)
  if (GET_CODE (cmp1) == CONST_INT)
    {
    {
      HOST_WIDE_INT value = INTVAL (cmp1);
      HOST_WIDE_INT value = INTVAL (cmp1);
 
 
      if (value < p_info->const_low
      if (value < p_info->const_low
          || value > p_info->const_high)
          || value > p_info->const_high)
        cmp1 = force_reg (mode, cmp1);
        cmp1 = force_reg (mode, cmp1);
    }
    }
 
 
  /* See if we need to invert the result.  */
  /* See if we need to invert the result.  */
  invert = (GET_CODE (cmp1) == CONST_INT
  invert = (GET_CODE (cmp1) == CONST_INT
            ? p_info->invert_const : p_info->invert_reg);
            ? p_info->invert_const : p_info->invert_reg);
 
 
  if (p_invert != (int *)0)
  if (p_invert != (int *)0)
    {
    {
      *p_invert = invert;
      *p_invert = invert;
      invert = 0;
      invert = 0;
    }
    }
 
 
  /* Comparison to constants, may involve adding 1 to change a LT into LE.
  /* Comparison to constants, may involve adding 1 to change a LT into LE.
     Comparison between two registers, may involve switching operands.  */
     Comparison between two registers, may involve switching operands.  */
  if (GET_CODE (cmp1) == CONST_INT)
  if (GET_CODE (cmp1) == CONST_INT)
    {
    {
      if (p_info->const_add != 0)
      if (p_info->const_add != 0)
        {
        {
          HOST_WIDE_INT new_const = INTVAL (cmp1) + p_info->const_add;
          HOST_WIDE_INT new_const = INTVAL (cmp1) + p_info->const_add;
 
 
          /* If modification of cmp1 caused overflow,
          /* If modification of cmp1 caused overflow,
             we would get the wrong answer if we follow the usual path;
             we would get the wrong answer if we follow the usual path;
             thus, x > 0xffffffffU would turn into x > 0U.  */
             thus, x > 0xffffffffU would turn into x > 0U.  */
          if ((p_info->unsignedp
          if ((p_info->unsignedp
               ? (unsigned HOST_WIDE_INT) new_const >
               ? (unsigned HOST_WIDE_INT) new_const >
               (unsigned HOST_WIDE_INT) INTVAL (cmp1)
               (unsigned HOST_WIDE_INT) INTVAL (cmp1)
               : new_const > INTVAL (cmp1))
               : new_const > INTVAL (cmp1))
              != (p_info->const_add > 0))
              != (p_info->const_add > 0))
            {
            {
              /* This test is always true, but if INVERT is true then
              /* This test is always true, but if INVERT is true then
                 the result of the test needs to be inverted so 0 should
                 the result of the test needs to be inverted so 0 should
                 be returned instead.  */
                 be returned instead.  */
              emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
              emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
              return result;
              return result;
            }
            }
          else
          else
            cmp1 = GEN_INT (new_const);
            cmp1 = GEN_INT (new_const);
        }
        }
    }
    }
 
 
  else if (p_info->reverse_regs)
  else if (p_info->reverse_regs)
    {
    {
      rtx temp = cmp0;
      rtx temp = cmp0;
      cmp0 = cmp1;
      cmp0 = cmp1;
      cmp1 = temp;
      cmp1 = temp;
    }
    }
 
 
  if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
  if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
    reg = cmp0;
    reg = cmp0;
  else
  else
    {
    {
      reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
      reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
      convert_move (reg, gen_rtx_fmt_ee (p_info->test_code, mode, cmp0, cmp1), 0);
      convert_move (reg, gen_rtx_fmt_ee (p_info->test_code, mode, cmp0, cmp1), 0);
    }
    }
 
 
  if (test == ITEST_NE)
  if (test == ITEST_NE)
    {
    {
      convert_move (result, gen_rtx_GTU (mode, reg, const0_rtx), 0);
      convert_move (result, gen_rtx_GTU (mode, reg, const0_rtx), 0);
      if (p_invert != NULL)
      if (p_invert != NULL)
        *p_invert = 0;
        *p_invert = 0;
      invert = 0;
      invert = 0;
    }
    }
 
 
  else if (test == ITEST_EQ)
  else if (test == ITEST_EQ)
    {
    {
      reg2 = invert ? gen_reg_rtx (mode) : result;
      reg2 = invert ? gen_reg_rtx (mode) : result;
      convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
      convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
      reg = reg2;
      reg = reg2;
    }
    }
 
 
  if (invert)
  if (invert)
    {
    {
      rtx one;
      rtx one;
 
 
      one = const1_rtx;
      one = const1_rtx;
      convert_move (result, gen_rtx_XOR (mode, reg, one), 0);
      convert_move (result, gen_rtx_XOR (mode, reg, one), 0);
    }
    }
 
 
  return result;
  return result;
}
}


/* Emit the common code for doing conditional branches.
/* Emit the common code for doing conditional branches.
   operand[0] is the label to jump to.
   operand[0] is the label to jump to.
   The comparison operands are saved away by cmp{si,di,sf,df}.  */
   The comparison operands are saved away by cmp{si,di,sf,df}.  */
 
 
void
void
gen_conditional_branch (rtx operands[], enum machine_mode mode)
gen_conditional_branch (rtx operands[], enum machine_mode mode)
{
{
  enum rtx_code test_code = GET_CODE (operands[0]);
  enum rtx_code test_code = GET_CODE (operands[0]);
  rtx cmp0 = operands[1];
  rtx cmp0 = operands[1];
  rtx cmp1 = operands[2];
  rtx cmp1 = operands[2];
  rtx reg;
  rtx reg;
  int invert;
  int invert;
  rtx label1, label2;
  rtx label1, label2;
 
 
  invert = 0;
  invert = 0;
  reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
  reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
 
 
  if (reg)
  if (reg)
    {
    {
      cmp0 = reg;
      cmp0 = reg;
      cmp1 = const0_rtx;
      cmp1 = const0_rtx;
      test_code = NE;
      test_code = NE;
    }
    }
  else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
  else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
    /* We don't want to build a comparison against a nonzero
    /* We don't want to build a comparison against a nonzero
       constant.  */
       constant.  */
    cmp1 = force_reg (mode, cmp1);
    cmp1 = force_reg (mode, cmp1);
 
 
  /* Generate the branch.  */
  /* Generate the branch.  */
  label1 = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
  label1 = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
  label2 = pc_rtx;
  label2 = pc_rtx;
 
 
  if (invert)
  if (invert)
    {
    {
      label2 = label1;
      label2 = label1;
      label1 = pc_rtx;
      label1 = pc_rtx;
    }
    }
 
 
  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
                               gen_rtx_IF_THEN_ELSE (VOIDmode,
                               gen_rtx_IF_THEN_ELSE (VOIDmode,
                                                     gen_rtx_fmt_ee (test_code,
                                                     gen_rtx_fmt_ee (test_code,
                                                                     mode,
                                                                     mode,
                                                                     cmp0, cmp1),
                                                                     cmp0, cmp1),
                                                     label1, label2)));
                                                     label1, label2)));
}
}


/* Initialize CUM for a function FNTYPE.  */
/* Initialize CUM for a function FNTYPE.  */
 
 
void
void
init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
                      rtx libname ATTRIBUTE_UNUSED)
                      rtx libname ATTRIBUTE_UNUSED)
{
{
  static CUMULATIVE_ARGS zero_cum;
  static CUMULATIVE_ARGS zero_cum;
  tree param;
  tree param;
  tree next_param;
  tree next_param;
 
 
  if (TARGET_DEBUG_D_MODE)
  if (TARGET_DEBUG_D_MODE)
    {
    {
      fprintf (stderr,
      fprintf (stderr,
               "\ninit_cumulative_args, fntype = 0x%.8lx", (long) fntype);
               "\ninit_cumulative_args, fntype = 0x%.8lx", (long) fntype);
 
 
      if (!fntype)
      if (!fntype)
        fputc ('\n', stderr);
        fputc ('\n', stderr);
 
 
      else
      else
        {
        {
          tree ret_type = TREE_TYPE (fntype);
          tree ret_type = TREE_TYPE (fntype);
 
 
          fprintf (stderr, ", fntype code = %s, ret code = %s\n",
          fprintf (stderr, ", fntype code = %s, ret code = %s\n",
                   tree_code_name[(int)TREE_CODE (fntype)],
                   tree_code_name[(int)TREE_CODE (fntype)],
                   tree_code_name[(int)TREE_CODE (ret_type)]);
                   tree_code_name[(int)TREE_CODE (ret_type)]);
        }
        }
    }
    }
 
 
  *cum = zero_cum;
  *cum = zero_cum;
 
 
  /* Determine if this function has variable arguments.  This is
  /* Determine if this function has variable arguments.  This is
     indicated by the last argument being 'void_type_mode' if there
     indicated by the last argument being 'void_type_mode' if there
     are no variable arguments.  The standard IQ2000 calling sequence
     are no variable arguments.  The standard IQ2000 calling sequence
     passes all arguments in the general purpose registers in this case.  */
     passes all arguments in the general purpose registers in this case.  */
 
 
  for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
  for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
       param != 0; param = next_param)
       param != 0; param = next_param)
    {
    {
      next_param = TREE_CHAIN (param);
      next_param = TREE_CHAIN (param);
      if (next_param == 0 && TREE_VALUE (param) != void_type_node)
      if (next_param == 0 && TREE_VALUE (param) != void_type_node)
        cum->gp_reg_found = 1;
        cum->gp_reg_found = 1;
    }
    }
}
}
 
 
/* Advance the argument of type TYPE and mode MODE to the next argument
/* Advance the argument of type TYPE and mode MODE to the next argument
   position in CUM.  */
   position in CUM.  */
 
 
void
void
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
                      int named)
                      int named)
{
{
  if (TARGET_DEBUG_D_MODE)
  if (TARGET_DEBUG_D_MODE)
    {
    {
      fprintf (stderr,
      fprintf (stderr,
               "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
               "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
               cum->gp_reg_found, cum->arg_number, cum->arg_words,
               cum->gp_reg_found, cum->arg_number, cum->arg_words,
               GET_MODE_NAME (mode));
               GET_MODE_NAME (mode));
      fprintf (stderr, "%p", (void *) type);
      fprintf (stderr, "%p", (void *) type);
      fprintf (stderr, ", %d )\n\n", named);
      fprintf (stderr, ", %d )\n\n", named);
    }
    }
 
 
  cum->arg_number++;
  cum->arg_number++;
  switch (mode)
  switch (mode)
    {
    {
    case VOIDmode:
    case VOIDmode:
      break;
      break;
 
 
    default:
    default:
      gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
      gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
                  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
                  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
 
 
      cum->gp_reg_found = 1;
      cum->gp_reg_found = 1;
      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
                         / UNITS_PER_WORD);
                         / UNITS_PER_WORD);
      break;
      break;
 
 
    case BLKmode:
    case BLKmode:
      cum->gp_reg_found = 1;
      cum->gp_reg_found = 1;
      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
                         / UNITS_PER_WORD);
                         / UNITS_PER_WORD);
      break;
      break;
 
 
    case SFmode:
    case SFmode:
      cum->arg_words ++;
      cum->arg_words ++;
      if (! cum->gp_reg_found && cum->arg_number <= 2)
      if (! cum->gp_reg_found && cum->arg_number <= 2)
        cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
        cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
      break;
      break;
 
 
    case DFmode:
    case DFmode:
      cum->arg_words += 2;
      cum->arg_words += 2;
      if (! cum->gp_reg_found && cum->arg_number <= 2)
      if (! cum->gp_reg_found && cum->arg_number <= 2)
        cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
        cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
      break;
      break;
 
 
    case DImode:
    case DImode:
      cum->gp_reg_found = 1;
      cum->gp_reg_found = 1;
      cum->arg_words += 2;
      cum->arg_words += 2;
      break;
      break;
 
 
    case TImode:
    case TImode:
      cum->gp_reg_found = 1;
      cum->gp_reg_found = 1;
      cum->arg_words += 4;
      cum->arg_words += 4;
      break;
      break;
 
 
    case QImode:
    case QImode:
    case HImode:
    case HImode:
    case SImode:
    case SImode:
      cum->gp_reg_found = 1;
      cum->gp_reg_found = 1;
      cum->arg_words ++;
      cum->arg_words ++;
      break;
      break;
    }
    }
}
}
 
 
/* Return an RTL expression containing the register for the given mode MODE
/* Return an RTL expression containing the register for the given mode MODE
   and type TYPE in CUM, or 0 if the argument is to be passed on the stack.  */
   and type TYPE in CUM, or 0 if the argument is to be passed on the stack.  */
 
 
struct rtx_def *
struct rtx_def *
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, const_tree type,
function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, const_tree type,
              int named)
              int named)
{
{
  rtx ret;
  rtx ret;
  int regbase = -1;
  int regbase = -1;
  int bias = 0;
  int bias = 0;
  unsigned int *arg_words = &cum->arg_words;
  unsigned int *arg_words = &cum->arg_words;
  int struct_p = (type != 0
  int struct_p = (type != 0
                  && (TREE_CODE (type) == RECORD_TYPE
                  && (TREE_CODE (type) == RECORD_TYPE
                      || TREE_CODE (type) == UNION_TYPE
                      || TREE_CODE (type) == UNION_TYPE
                      || TREE_CODE (type) == QUAL_UNION_TYPE));
                      || TREE_CODE (type) == QUAL_UNION_TYPE));
 
 
  if (TARGET_DEBUG_D_MODE)
  if (TARGET_DEBUG_D_MODE)
    {
    {
      fprintf (stderr,
      fprintf (stderr,
               "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
               "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
               cum->gp_reg_found, cum->arg_number, cum->arg_words,
               cum->gp_reg_found, cum->arg_number, cum->arg_words,
               GET_MODE_NAME (mode));
               GET_MODE_NAME (mode));
      fprintf (stderr, "%p", (const void *) type);
      fprintf (stderr, "%p", (const void *) type);
      fprintf (stderr, ", %d ) = ", named);
      fprintf (stderr, ", %d ) = ", named);
    }
    }
 
 
 
 
  cum->last_arg_fp = 0;
  cum->last_arg_fp = 0;
  switch (mode)
  switch (mode)
    {
    {
    case SFmode:
    case SFmode:
      regbase = GP_ARG_FIRST;
      regbase = GP_ARG_FIRST;
      break;
      break;
 
 
    case DFmode:
    case DFmode:
      cum->arg_words += cum->arg_words & 1;
      cum->arg_words += cum->arg_words & 1;
 
 
      regbase = GP_ARG_FIRST;
      regbase = GP_ARG_FIRST;
      break;
      break;
 
 
    default:
    default:
      gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
      gcc_assert (GET_MODE_CLASS (mode) == MODE_COMPLEX_INT
                  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
                  || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT);
 
 
      /* Drops through.  */
      /* Drops through.  */
    case BLKmode:
    case BLKmode:
      if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
      if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
        cum->arg_words += (cum->arg_words & 1);
        cum->arg_words += (cum->arg_words & 1);
      regbase = GP_ARG_FIRST;
      regbase = GP_ARG_FIRST;
      break;
      break;
 
 
    case VOIDmode:
    case VOIDmode:
    case QImode:
    case QImode:
    case HImode:
    case HImode:
    case SImode:
    case SImode:
      regbase = GP_ARG_FIRST;
      regbase = GP_ARG_FIRST;
      break;
      break;
 
 
    case DImode:
    case DImode:
      cum->arg_words += (cum->arg_words & 1);
      cum->arg_words += (cum->arg_words & 1);
      regbase = GP_ARG_FIRST;
      regbase = GP_ARG_FIRST;
      break;
      break;
 
 
    case TImode:
    case TImode:
      cum->arg_words += (cum->arg_words & 3);
      cum->arg_words += (cum->arg_words & 3);
      regbase = GP_ARG_FIRST;
      regbase = GP_ARG_FIRST;
      break;
      break;
    }
    }
 
 
  if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
  if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
    {
    {
      if (TARGET_DEBUG_D_MODE)
      if (TARGET_DEBUG_D_MODE)
        fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
        fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
 
 
      ret = 0;
      ret = 0;
    }
    }
  else
  else
    {
    {
      gcc_assert (regbase != -1);
      gcc_assert (regbase != -1);
 
 
      if (! type || TREE_CODE (type) != RECORD_TYPE
      if (! type || TREE_CODE (type) != RECORD_TYPE
          || ! named  || ! TYPE_SIZE_UNIT (type)
          || ! named  || ! TYPE_SIZE_UNIT (type)
          || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
          || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
        ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
        ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
      else
      else
        {
        {
          tree field;
          tree field;
 
 
          for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
          for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
            if (TREE_CODE (field) == FIELD_DECL
            if (TREE_CODE (field) == FIELD_DECL
                && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
                && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
                && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
                && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
                && host_integerp (bit_position (field), 0)
                && host_integerp (bit_position (field), 0)
                && int_bit_position (field) % BITS_PER_WORD == 0)
                && int_bit_position (field) % BITS_PER_WORD == 0)
              break;
              break;
 
 
          /* If the whole struct fits a DFmode register,
          /* If the whole struct fits a DFmode register,
             we don't need the PARALLEL.  */
             we don't need the PARALLEL.  */
          if (! field || mode == DFmode)
          if (! field || mode == DFmode)
            ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
            ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
          else
          else
            {
            {
              unsigned int chunks;
              unsigned int chunks;
              HOST_WIDE_INT bitpos;
              HOST_WIDE_INT bitpos;
              unsigned int regno;
              unsigned int regno;
              unsigned int i;
              unsigned int i;
 
 
              /* ??? If this is a packed structure, then the last hunk won't
              /* ??? If this is a packed structure, then the last hunk won't
                 be 64 bits.  */
                 be 64 bits.  */
              chunks
              chunks
                = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
                = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
              if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
              if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
                chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
                chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
 
 
              /* Assign_parms checks the mode of ENTRY_PARM, so we must
              /* Assign_parms checks the mode of ENTRY_PARM, so we must
                 use the actual mode here.  */
                 use the actual mode here.  */
              ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
              ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
 
 
              bitpos = 0;
              bitpos = 0;
              regno = regbase + *arg_words + bias;
              regno = regbase + *arg_words + bias;
              field = TYPE_FIELDS (type);
              field = TYPE_FIELDS (type);
              for (i = 0; i < chunks; i++)
              for (i = 0; i < chunks; i++)
                {
                {
                  rtx reg;
                  rtx reg;
 
 
                  for (; field; field = TREE_CHAIN (field))
                  for (; field; field = TREE_CHAIN (field))
                    if (TREE_CODE (field) == FIELD_DECL
                    if (TREE_CODE (field) == FIELD_DECL
                        && int_bit_position (field) >= bitpos)
                        && int_bit_position (field) >= bitpos)
                      break;
                      break;
 
 
                  if (field
                  if (field
                      && int_bit_position (field) == bitpos
                      && int_bit_position (field) == bitpos
                      && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
                      && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
                      && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
                      && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
                    reg = gen_rtx_REG (DFmode, regno++);
                    reg = gen_rtx_REG (DFmode, regno++);
                  else
                  else
                    reg = gen_rtx_REG (word_mode, regno);
                    reg = gen_rtx_REG (word_mode, regno);
 
 
                  XVECEXP (ret, 0, i)
                  XVECEXP (ret, 0, i)
                    = gen_rtx_EXPR_LIST (VOIDmode, reg,
                    = gen_rtx_EXPR_LIST (VOIDmode, reg,
                                         GEN_INT (bitpos / BITS_PER_UNIT));
                                         GEN_INT (bitpos / BITS_PER_UNIT));
 
 
                  bitpos += 64;
                  bitpos += 64;
                  regno++;
                  regno++;
                }
                }
            }
            }
        }
        }
 
 
      if (TARGET_DEBUG_D_MODE)
      if (TARGET_DEBUG_D_MODE)
        fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
        fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
                 struct_p ? ", [struct]" : "");
                 struct_p ? ", [struct]" : "");
    }
    }
 
 
  /* We will be called with a mode of VOIDmode after the last argument
  /* We will be called with a mode of VOIDmode after the last argument
     has been seen.  Whatever we return will be passed to the call
     has been seen.  Whatever we return will be passed to the call
     insn.  If we need any shifts for small structures, return them in
     insn.  If we need any shifts for small structures, return them in
     a PARALLEL.  */
     a PARALLEL.  */
  if (mode == VOIDmode)
  if (mode == VOIDmode)
    {
    {
      if (cum->num_adjusts > 0)
      if (cum->num_adjusts > 0)
        ret = gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
        ret = gen_rtx_PARALLEL ((enum machine_mode) cum->fp_code,
                       gen_rtvec_v (cum->num_adjusts, cum->adjust));
                       gen_rtvec_v (cum->num_adjusts, cum->adjust));
    }
    }
 
 
  return ret;
  return ret;
}
}
 
 
static int
static int
iq2000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
iq2000_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                          tree type ATTRIBUTE_UNUSED,
                          tree type ATTRIBUTE_UNUSED,
                          bool named ATTRIBUTE_UNUSED)
                          bool named ATTRIBUTE_UNUSED)
{
{
  if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS - 1)
  if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS - 1)
    {
    {
      if (TARGET_DEBUG_D_MODE)
      if (TARGET_DEBUG_D_MODE)
        fprintf (stderr, "iq2000_arg_partial_bytes=%d\n", UNITS_PER_WORD);
        fprintf (stderr, "iq2000_arg_partial_bytes=%d\n", UNITS_PER_WORD);
      return UNITS_PER_WORD;
      return UNITS_PER_WORD;
    }
    }
 
 
  return 0;
  return 0;
}
}


/* Implement va_start.  */
/* Implement va_start.  */
 
 
static void
static void
iq2000_va_start (tree valist, rtx nextarg)
iq2000_va_start (tree valist, rtx nextarg)
{
{
  int int_arg_words;
  int int_arg_words;
  /* Find out how many non-float named formals.  */
  /* Find out how many non-float named formals.  */
  int gpr_save_area_size;
  int gpr_save_area_size;
  /* Note UNITS_PER_WORD is 4 bytes.  */
  /* Note UNITS_PER_WORD is 4 bytes.  */
  int_arg_words = crtl->args.info.arg_words;
  int_arg_words = crtl->args.info.arg_words;
 
 
  if (int_arg_words < 8 )
  if (int_arg_words < 8 )
    /* Adjust for the prologue's economy measure.  */
    /* Adjust for the prologue's economy measure.  */
    gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
    gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
  else
  else
    gpr_save_area_size = 0;
    gpr_save_area_size = 0;
 
 
  /* Everything is in the GPR save area, or in the overflow
  /* Everything is in the GPR save area, or in the overflow
     area which is contiguous with it.  */
     area which is contiguous with it.  */
  nextarg = plus_constant (nextarg, - gpr_save_area_size);
  nextarg = plus_constant (nextarg, - gpr_save_area_size);
  std_expand_builtin_va_start (valist, nextarg);
  std_expand_builtin_va_start (valist, nextarg);
}
}


/* Allocate a chunk of memory for per-function machine-dependent data.  */
/* Allocate a chunk of memory for per-function machine-dependent data.  */
 
 
static struct machine_function *
static struct machine_function *
iq2000_init_machine_status (void)
iq2000_init_machine_status (void)
{
{
  struct machine_function *f;
  struct machine_function *f;
 
 
  f = GGC_CNEW (struct machine_function);
  f = GGC_CNEW (struct machine_function);
 
 
  return f;
  return f;
}
}
 
 
/* Implement TARGET_HANDLE_OPTION.  */
/* Implement TARGET_HANDLE_OPTION.  */
 
 
static bool
static bool
iq2000_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
iq2000_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
{
{
  switch (code)
  switch (code)
    {
    {
    case OPT_mcpu_:
    case OPT_mcpu_:
      if (strcmp (arg, "iq10") == 0)
      if (strcmp (arg, "iq10") == 0)
        iq2000_tune = PROCESSOR_IQ10;
        iq2000_tune = PROCESSOR_IQ10;
      else if (strcmp (arg, "iq2000") == 0)
      else if (strcmp (arg, "iq2000") == 0)
        iq2000_tune = PROCESSOR_IQ2000;
        iq2000_tune = PROCESSOR_IQ2000;
      else
      else
        return false;
        return false;
      return true;
      return true;
 
 
    case OPT_march_:
    case OPT_march_:
      /* This option has no effect at the moment.  */
      /* This option has no effect at the moment.  */
      return (strcmp (arg, "default") == 0
      return (strcmp (arg, "default") == 0
              || strcmp (arg, "DEFAULT") == 0
              || strcmp (arg, "DEFAULT") == 0
              || strcmp (arg, "iq2000") == 0);
              || strcmp (arg, "iq2000") == 0);
 
 
    default:
    default:
      return true;
      return true;
    }
    }
}
}
 
 
/* Detect any conflicts in the switches.  */
/* Detect any conflicts in the switches.  */
 
 
void
void
override_options (void)
override_options (void)
{
{
  target_flags &= ~MASK_GPOPT;
  target_flags &= ~MASK_GPOPT;
 
 
  iq2000_isa = IQ2000_ISA_DEFAULT;
  iq2000_isa = IQ2000_ISA_DEFAULT;
 
 
  /* Identify the processor type.  */
  /* Identify the processor type.  */
 
 
  iq2000_print_operand_punct['?'] = 1;
  iq2000_print_operand_punct['?'] = 1;
  iq2000_print_operand_punct['#'] = 1;
  iq2000_print_operand_punct['#'] = 1;
  iq2000_print_operand_punct['&'] = 1;
  iq2000_print_operand_punct['&'] = 1;
  iq2000_print_operand_punct['!'] = 1;
  iq2000_print_operand_punct['!'] = 1;
  iq2000_print_operand_punct['*'] = 1;
  iq2000_print_operand_punct['*'] = 1;
  iq2000_print_operand_punct['@'] = 1;
  iq2000_print_operand_punct['@'] = 1;
  iq2000_print_operand_punct['.'] = 1;
  iq2000_print_operand_punct['.'] = 1;
  iq2000_print_operand_punct['('] = 1;
  iq2000_print_operand_punct['('] = 1;
  iq2000_print_operand_punct[')'] = 1;
  iq2000_print_operand_punct[')'] = 1;
  iq2000_print_operand_punct['['] = 1;
  iq2000_print_operand_punct['['] = 1;
  iq2000_print_operand_punct[']'] = 1;
  iq2000_print_operand_punct[']'] = 1;
  iq2000_print_operand_punct['<'] = 1;
  iq2000_print_operand_punct['<'] = 1;
  iq2000_print_operand_punct['>'] = 1;
  iq2000_print_operand_punct['>'] = 1;
  iq2000_print_operand_punct['{'] = 1;
  iq2000_print_operand_punct['{'] = 1;
  iq2000_print_operand_punct['}'] = 1;
  iq2000_print_operand_punct['}'] = 1;
  iq2000_print_operand_punct['^'] = 1;
  iq2000_print_operand_punct['^'] = 1;
  iq2000_print_operand_punct['$'] = 1;
  iq2000_print_operand_punct['$'] = 1;
  iq2000_print_operand_punct['+'] = 1;
  iq2000_print_operand_punct['+'] = 1;
  iq2000_print_operand_punct['~'] = 1;
  iq2000_print_operand_punct['~'] = 1;
 
 
  /* Save GPR registers in word_mode sized hunks.  word_mode hasn't been
  /* Save GPR registers in word_mode sized hunks.  word_mode hasn't been
     initialized yet, so we can't use that here.  */
     initialized yet, so we can't use that here.  */
  gpr_mode = SImode;
  gpr_mode = SImode;
 
 
  /* Function to allocate machine-dependent function status.  */
  /* Function to allocate machine-dependent function status.  */
  init_machine_status = iq2000_init_machine_status;
  init_machine_status = iq2000_init_machine_status;
}
}


/* The arg pointer (which is eliminated) points to the virtual frame pointer,
/* The arg pointer (which is eliminated) points to the virtual frame pointer,
   while the frame pointer (which may be eliminated) points to the stack
   while the frame pointer (which may be eliminated) points to the stack
   pointer after the initial adjustments.  */
   pointer after the initial adjustments.  */
 
 
HOST_WIDE_INT
HOST_WIDE_INT
iq2000_debugger_offset (rtx addr, HOST_WIDE_INT offset)
iq2000_debugger_offset (rtx addr, HOST_WIDE_INT offset)
{
{
  rtx offset2 = const0_rtx;
  rtx offset2 = const0_rtx;
  rtx reg = eliminate_constant_term (addr, & offset2);
  rtx reg = eliminate_constant_term (addr, & offset2);
 
 
  if (offset == 0)
  if (offset == 0)
    offset = INTVAL (offset2);
    offset = INTVAL (offset2);
 
 
  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
      || reg == hard_frame_pointer_rtx)
      || reg == hard_frame_pointer_rtx)
    {
    {
      HOST_WIDE_INT frame_size = (!cfun->machine->initialized)
      HOST_WIDE_INT frame_size = (!cfun->machine->initialized)
                                  ? compute_frame_size (get_frame_size ())
                                  ? compute_frame_size (get_frame_size ())
                                  : cfun->machine->total_size;
                                  : cfun->machine->total_size;
 
 
      offset = offset - frame_size;
      offset = offset - frame_size;
    }
    }
 
 
  return offset;
  return offset;
}
}


/* If defined, a C statement to be executed just prior to the output of
/* If defined, a C statement to be executed just prior to the output of
   assembler code for INSN, to modify the extracted operands so they will be
   assembler code for INSN, to modify the extracted operands so they will be
   output differently.
   output differently.
 
 
   Here the argument OPVEC is the vector containing the operands extracted
   Here the argument OPVEC is the vector containing the operands extracted
   from INSN, and NOPERANDS is the number of elements of the vector which
   from INSN, and NOPERANDS is the number of elements of the vector which
   contain meaningful data for this insn.  The contents of this vector are
   contain meaningful data for this insn.  The contents of this vector are
   what will be used to convert the insn template into assembler code, so you
   what will be used to convert the insn template into assembler code, so you
   can change the assembler output by changing the contents of the vector.
   can change the assembler output by changing the contents of the vector.
 
 
   We use it to check if the current insn needs a nop in front of it because
   We use it to check if the current insn needs a nop in front of it because
   of load delays, and also to update the delay slot statistics.  */
   of load delays, and also to update the delay slot statistics.  */
 
 
void
void
final_prescan_insn (rtx insn, rtx opvec[] ATTRIBUTE_UNUSED,
final_prescan_insn (rtx insn, rtx opvec[] ATTRIBUTE_UNUSED,
                    int noperands ATTRIBUTE_UNUSED)
                    int noperands ATTRIBUTE_UNUSED)
{
{
  if (dslots_number_nops > 0)
  if (dslots_number_nops > 0)
    {
    {
      rtx pattern = PATTERN (insn);
      rtx pattern = PATTERN (insn);
      int length = get_attr_length (insn);
      int length = get_attr_length (insn);
 
 
      /* Do we need to emit a NOP?  */
      /* Do we need to emit a NOP?  */
      if (length == 0
      if (length == 0
          || (iq2000_load_reg != 0 && reg_mentioned_p (iq2000_load_reg,  pattern))
          || (iq2000_load_reg != 0 && reg_mentioned_p (iq2000_load_reg,  pattern))
          || (iq2000_load_reg2 != 0 && reg_mentioned_p (iq2000_load_reg2, pattern))
          || (iq2000_load_reg2 != 0 && reg_mentioned_p (iq2000_load_reg2, pattern))
          || (iq2000_load_reg3 != 0 && reg_mentioned_p (iq2000_load_reg3, pattern))
          || (iq2000_load_reg3 != 0 && reg_mentioned_p (iq2000_load_reg3, pattern))
          || (iq2000_load_reg4 != 0
          || (iq2000_load_reg4 != 0
              && reg_mentioned_p (iq2000_load_reg4, pattern)))
              && reg_mentioned_p (iq2000_load_reg4, pattern)))
        fputs ("\tnop\n", asm_out_file);
        fputs ("\tnop\n", asm_out_file);
 
 
      else
      else
        dslots_load_filled ++;
        dslots_load_filled ++;
 
 
      while (--dslots_number_nops > 0)
      while (--dslots_number_nops > 0)
        fputs ("\tnop\n", asm_out_file);
        fputs ("\tnop\n", asm_out_file);
 
 
      iq2000_load_reg = 0;
      iq2000_load_reg = 0;
      iq2000_load_reg2 = 0;
      iq2000_load_reg2 = 0;
      iq2000_load_reg3 = 0;
      iq2000_load_reg3 = 0;
      iq2000_load_reg4 = 0;
      iq2000_load_reg4 = 0;
    }
    }
 
 
  if (   (GET_CODE (insn) == JUMP_INSN
  if (   (GET_CODE (insn) == JUMP_INSN
       || GET_CODE (insn) == CALL_INSN
       || GET_CODE (insn) == CALL_INSN
       || (GET_CODE (PATTERN (insn)) == RETURN))
       || (GET_CODE (PATTERN (insn)) == RETURN))
           && NEXT_INSN (PREV_INSN (insn)) == insn)
           && NEXT_INSN (PREV_INSN (insn)) == insn)
    {
    {
      rtx nop_insn = emit_insn_after (gen_nop (), insn);
      rtx nop_insn = emit_insn_after (gen_nop (), insn);
 
 
      INSN_ADDRESSES_NEW (nop_insn, -1);
      INSN_ADDRESSES_NEW (nop_insn, -1);
    }
    }
 
 
  if (TARGET_STATS
  if (TARGET_STATS
      && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
      && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
    dslots_jump_total ++;
    dslots_jump_total ++;
}
}


/* 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 where SIZE is the # of var. bytes allocated.
   stack pointer where SIZE is the # of var. bytes allocated.
 
 
   IQ2000 stack frames look like:
   IQ2000 stack frames look like:
 
 
             Before call                        After call
             Before call                        After call
        +-----------------------+       +-----------------------+
        +-----------------------+       +-----------------------+
   high |                       |       |                       |
   high |                       |       |                       |
   mem. |                       |       |                       |
   mem. |                       |       |                       |
        |  caller's temps.      |       |  caller's temps.      |
        |  caller's temps.      |       |  caller's temps.      |
        |                       |       |                       |
        |                       |       |                       |
        +-----------------------+       +-----------------------+
        +-----------------------+       +-----------------------+
        |                       |       |                       |
        |                       |       |                       |
        |  arguments on stack.  |       |  arguments on stack.  |
        |  arguments on stack.  |       |  arguments on stack.  |
        |                       |       |                       |
        |                       |       |                       |
        +-----------------------+       +-----------------------+
        +-----------------------+       +-----------------------+
        |  4 words to save      |       |  4 words to save      |
        |  4 words to save      |       |  4 words to save      |
        |  arguments passed     |       |  arguments passed     |
        |  arguments passed     |       |  arguments passed     |
        |  in registers, even   |       |  in registers, even   |
        |  in registers, even   |       |  in registers, even   |
    SP->|  if not passed.       |  VFP->|  if not passed.       |
    SP->|  if not passed.       |  VFP->|  if not passed.       |
        +-----------------------+       +-----------------------+
        +-----------------------+       +-----------------------+
                                        |                       |
                                        |                       |
                                        |  fp register save     |
                                        |  fp register save     |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  gp register save     |
                                        |  gp register save     |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  local variables      |
                                        |  local variables      |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  alloca allocations   |
                                        |  alloca allocations   |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  GP save for V.4 abi  |
                                        |  GP save for V.4 abi  |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |                       |
                                        |                       |
                                        |  arguments on stack   |
                                        |  arguments on stack   |
                                        |                       |
                                        |                       |
                                        +-----------------------+
                                        +-----------------------+
                                        |  4 words to save      |
                                        |  4 words to save      |
                                        |  arguments passed     |
                                        |  arguments passed     |
                                        |  in registers, even   |
                                        |  in registers, even   |
   low                              SP->|  if not passed.       |
   low                              SP->|  if not passed.       |
   memory                               +-----------------------+  */
   memory                               +-----------------------+  */
 
 
HOST_WIDE_INT
HOST_WIDE_INT
compute_frame_size (HOST_WIDE_INT size)
compute_frame_size (HOST_WIDE_INT size)
{
{
  int regno;
  int regno;
  HOST_WIDE_INT total_size;     /* # bytes that the entire frame takes up.  */
  HOST_WIDE_INT total_size;     /* # bytes that the entire frame takes up.  */
  HOST_WIDE_INT var_size;       /* # bytes that variables take up.  */
  HOST_WIDE_INT var_size;       /* # bytes that variables take up.  */
  HOST_WIDE_INT args_size;      /* # bytes that outgoing arguments take up.  */
  HOST_WIDE_INT args_size;      /* # bytes that outgoing arguments take up.  */
  HOST_WIDE_INT extra_size;     /* # extra bytes.  */
  HOST_WIDE_INT extra_size;     /* # extra bytes.  */
  HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding.  */
  HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding.  */
  HOST_WIDE_INT gp_reg_size;    /* # bytes needed to store gp regs.  */
  HOST_WIDE_INT gp_reg_size;    /* # bytes needed to store gp regs.  */
  HOST_WIDE_INT fp_reg_size;    /* # bytes needed to store fp regs.  */
  HOST_WIDE_INT fp_reg_size;    /* # bytes needed to store fp regs.  */
  long mask;                    /* mask of saved gp registers.  */
  long mask;                    /* mask of saved gp registers.  */
  int  fp_inc;                  /* 1 or 2 depending on the size of fp regs.  */
  int  fp_inc;                  /* 1 or 2 depending on the size of fp regs.  */
  long fp_bits;                 /* bitmask to use for each fp register.  */
  long fp_bits;                 /* bitmask to use for each fp register.  */
 
 
  gp_reg_size = 0;
  gp_reg_size = 0;
  fp_reg_size = 0;
  fp_reg_size = 0;
  mask = 0;
  mask = 0;
  extra_size = IQ2000_STACK_ALIGN ((0));
  extra_size = IQ2000_STACK_ALIGN ((0));
  var_size = IQ2000_STACK_ALIGN (size);
  var_size = IQ2000_STACK_ALIGN (size);
  args_size = IQ2000_STACK_ALIGN (crtl->outgoing_args_size);
  args_size = IQ2000_STACK_ALIGN (crtl->outgoing_args_size);
 
 
  /* If a function dynamically allocates the stack and
  /* If a function dynamically allocates the stack and
     has 0 for STACK_DYNAMIC_OFFSET then allocate some stack space.  */
     has 0 for STACK_DYNAMIC_OFFSET then allocate some stack space.  */
  if (args_size == 0 && cfun->calls_alloca)
  if (args_size == 0 && cfun->calls_alloca)
    args_size = 4 * UNITS_PER_WORD;
    args_size = 4 * UNITS_PER_WORD;
 
 
  total_size = var_size + args_size + extra_size;
  total_size = var_size + args_size + extra_size;
 
 
  /* Calculate space needed for gp registers.  */
  /* Calculate space needed for gp registers.  */
  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
    {
    {
      if (MUST_SAVE_REGISTER (regno))
      if (MUST_SAVE_REGISTER (regno))
        {
        {
          gp_reg_size += GET_MODE_SIZE (gpr_mode);
          gp_reg_size += GET_MODE_SIZE (gpr_mode);
          mask |= 1L << (regno - GP_REG_FIRST);
          mask |= 1L << (regno - GP_REG_FIRST);
        }
        }
    }
    }
 
 
  /* We need to restore these for the handler.  */
  /* We need to restore these for the handler.  */
  if (crtl->calls_eh_return)
  if (crtl->calls_eh_return)
    {
    {
      unsigned int i;
      unsigned int i;
 
 
      for (i = 0; ; ++i)
      for (i = 0; ; ++i)
        {
        {
          regno = EH_RETURN_DATA_REGNO (i);
          regno = EH_RETURN_DATA_REGNO (i);
          if (regno == (int) INVALID_REGNUM)
          if (regno == (int) INVALID_REGNUM)
            break;
            break;
          gp_reg_size += GET_MODE_SIZE (gpr_mode);
          gp_reg_size += GET_MODE_SIZE (gpr_mode);
          mask |= 1L << (regno - GP_REG_FIRST);
          mask |= 1L << (regno - GP_REG_FIRST);
        }
        }
    }
    }
 
 
  fp_inc = 2;
  fp_inc = 2;
  fp_bits = 3;
  fp_bits = 3;
  gp_reg_rounded = IQ2000_STACK_ALIGN (gp_reg_size);
  gp_reg_rounded = IQ2000_STACK_ALIGN (gp_reg_size);
  total_size += gp_reg_rounded + IQ2000_STACK_ALIGN (fp_reg_size);
  total_size += gp_reg_rounded + IQ2000_STACK_ALIGN (fp_reg_size);
 
 
  /* The gp reg is caller saved, so there is no need for leaf routines
  /* The gp reg is caller saved, so there is no need for leaf routines
     (total_size == extra_size) to save the gp reg.  */
     (total_size == extra_size) to save the gp reg.  */
  if (total_size == extra_size
  if (total_size == extra_size
      && ! profile_flag)
      && ! profile_flag)
    total_size = extra_size = 0;
    total_size = extra_size = 0;
 
 
  total_size += IQ2000_STACK_ALIGN (crtl->args.pretend_args_size);
  total_size += IQ2000_STACK_ALIGN (crtl->args.pretend_args_size);
 
 
  /* Save other computed information.  */
  /* Save other computed information.  */
  cfun->machine->total_size = total_size;
  cfun->machine->total_size = total_size;
  cfun->machine->var_size = var_size;
  cfun->machine->var_size = var_size;
  cfun->machine->args_size = args_size;
  cfun->machine->args_size = args_size;
  cfun->machine->extra_size = extra_size;
  cfun->machine->extra_size = extra_size;
  cfun->machine->gp_reg_size = gp_reg_size;
  cfun->machine->gp_reg_size = gp_reg_size;
  cfun->machine->fp_reg_size = fp_reg_size;
  cfun->machine->fp_reg_size = fp_reg_size;
  cfun->machine->mask = mask;
  cfun->machine->mask = mask;
  cfun->machine->initialized = reload_completed;
  cfun->machine->initialized = reload_completed;
  cfun->machine->num_gp = gp_reg_size / UNITS_PER_WORD;
  cfun->machine->num_gp = gp_reg_size / UNITS_PER_WORD;
 
 
  if (mask)
  if (mask)
    {
    {
      unsigned long offset;
      unsigned long offset;
 
 
      offset = (args_size + extra_size + var_size
      offset = (args_size + extra_size + var_size
                + gp_reg_size - GET_MODE_SIZE (gpr_mode));
                + gp_reg_size - GET_MODE_SIZE (gpr_mode));
 
 
      cfun->machine->gp_sp_offset = offset;
      cfun->machine->gp_sp_offset = offset;
      cfun->machine->gp_save_offset = offset - total_size;
      cfun->machine->gp_save_offset = offset - total_size;
    }
    }
  else
  else
    {
    {
      cfun->machine->gp_sp_offset = 0;
      cfun->machine->gp_sp_offset = 0;
      cfun->machine->gp_save_offset = 0;
      cfun->machine->gp_save_offset = 0;
    }
    }
 
 
  cfun->machine->fp_sp_offset = 0;
  cfun->machine->fp_sp_offset = 0;
  cfun->machine->fp_save_offset = 0;
  cfun->machine->fp_save_offset = 0;
 
 
  /* Ok, we're done.  */
  /* Ok, we're done.  */
  return total_size;
  return total_size;
}
}


 
 
/* We can always eliminate to the frame pointer.  We can eliminate to the
/* We can always eliminate to the frame pointer.  We can eliminate to the
   stack pointer unless a frame pointer is needed.  */
   stack pointer unless a frame pointer is needed.  */
 
 
bool
bool
iq2000_can_eliminate (const int from, const int to)
iq2000_can_eliminate (const int from, const int to)
{
{
  return (from == RETURN_ADDRESS_POINTER_REGNUM
  return (from == RETURN_ADDRESS_POINTER_REGNUM
          && (! leaf_function_p ()
          && (! leaf_function_p ()
              || (to == GP_REG_FIRST + 31 && leaf_function_p)))
              || (to == GP_REG_FIRST + 31 && leaf_function_p)))
          || (from != RETURN_ADDRESS_POINTER_REGNUM
          || (from != RETURN_ADDRESS_POINTER_REGNUM
              && (to == HARD_FRAME_POINTER_REGNUM
              && (to == HARD_FRAME_POINTER_REGNUM
                  || (to == STACK_POINTER_REGNUM
                  || (to == STACK_POINTER_REGNUM
                      && ! frame_pointer_needed)));
                      && ! frame_pointer_needed)));
}
}
 
 
/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
   pointer, argument pointer, or return address pointer.  TO is either
   pointer, argument pointer, or return address pointer.  TO is either
   the stack pointer or hard frame pointer.  */
   the stack pointer or hard frame pointer.  */
 
 
int
int
iq2000_initial_elimination_offset (int from, int to ATTRIBUTE_UNUSED)
iq2000_initial_elimination_offset (int from, int to ATTRIBUTE_UNUSED)
{
{
  int offset;
  int offset;
 
 
  compute_frame_size (get_frame_size ());
  compute_frame_size (get_frame_size ());
  if ((from) == FRAME_POINTER_REGNUM)
  if ((from) == FRAME_POINTER_REGNUM)
    (offset) = 0;
    (offset) = 0;
  else if ((from) == ARG_POINTER_REGNUM)
  else if ((from) == ARG_POINTER_REGNUM)
    (offset) = (cfun->machine->total_size);
    (offset) = (cfun->machine->total_size);
  else if ((from) == RETURN_ADDRESS_POINTER_REGNUM)
  else if ((from) == RETURN_ADDRESS_POINTER_REGNUM)
    {
    {
      if (leaf_function_p ())
      if (leaf_function_p ())
        (offset) = 0;
        (offset) = 0;
      else (offset) = cfun->machine->gp_sp_offset
      else (offset) = cfun->machine->gp_sp_offset
             + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT))
             + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT))
                * (BYTES_BIG_ENDIAN != 0));
                * (BYTES_BIG_ENDIAN != 0));
    }
    }
 
 
  return offset;
  return offset;
}
}


/* Common code to emit the insns (or to write the instructions to a file)
/* Common code to emit the insns (or to write the instructions to a file)
   to save/restore registers.
   to save/restore registers.
   Other parts of the code assume that IQ2000_TEMP1_REGNUM (aka large_reg)
   Other parts of the code assume that IQ2000_TEMP1_REGNUM (aka large_reg)
   is not modified within save_restore_insns.  */
   is not modified within save_restore_insns.  */
 
 
#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
 
 
/* Emit instructions to load the value (SP + OFFSET) into IQ2000_TEMP2_REGNUM
/* Emit instructions to load the value (SP + OFFSET) into IQ2000_TEMP2_REGNUM
   and return an rtl expression for the register.  Write the assembly
   and return an rtl expression for the register.  Write the assembly
   instructions directly to FILE if it is not null, otherwise emit them as
   instructions directly to FILE if it is not null, otherwise emit them as
   rtl.
   rtl.
 
 
   This function is a subroutine of save_restore_insns.  It is used when
   This function is a subroutine of save_restore_insns.  It is used when
   OFFSET is too large to add in a single instruction.  */
   OFFSET is too large to add in a single instruction.  */
 
 
static rtx
static rtx
iq2000_add_large_offset_to_sp (HOST_WIDE_INT offset)
iq2000_add_large_offset_to_sp (HOST_WIDE_INT offset)
{
{
  rtx reg = gen_rtx_REG (Pmode, IQ2000_TEMP2_REGNUM);
  rtx reg = gen_rtx_REG (Pmode, IQ2000_TEMP2_REGNUM);
  rtx offset_rtx = GEN_INT (offset);
  rtx offset_rtx = GEN_INT (offset);
 
 
  emit_move_insn (reg, offset_rtx);
  emit_move_insn (reg, offset_rtx);
  emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
  emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
  return reg;
  return reg;
}
}
 
 
/* Make INSN frame related and note that it performs the frame-related
/* Make INSN frame related and note that it performs the frame-related
   operation DWARF_PATTERN.  */
   operation DWARF_PATTERN.  */
 
 
static void
static void
iq2000_annotate_frame_insn (rtx insn, rtx dwarf_pattern)
iq2000_annotate_frame_insn (rtx insn, rtx dwarf_pattern)
{
{
  RTX_FRAME_RELATED_P (insn) = 1;
  RTX_FRAME_RELATED_P (insn) = 1;
  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
                                      dwarf_pattern,
                                      dwarf_pattern,
                                      REG_NOTES (insn));
                                      REG_NOTES (insn));
}
}
 
 
/* Emit a move instruction that stores REG in MEM.  Make the instruction
/* Emit a move instruction that stores REG in MEM.  Make the instruction
   frame related and note that it stores REG at (SP + OFFSET).  */
   frame related and note that it stores REG at (SP + OFFSET).  */
 
 
static void
static void
iq2000_emit_frame_related_store (rtx mem, rtx reg, HOST_WIDE_INT offset)
iq2000_emit_frame_related_store (rtx mem, rtx reg, HOST_WIDE_INT offset)
{
{
  rtx dwarf_address = plus_constant (stack_pointer_rtx, offset);
  rtx dwarf_address = plus_constant (stack_pointer_rtx, offset);
  rtx dwarf_mem = gen_rtx_MEM (GET_MODE (reg), dwarf_address);
  rtx dwarf_mem = gen_rtx_MEM (GET_MODE (reg), dwarf_address);
 
 
  iq2000_annotate_frame_insn (emit_move_insn (mem, reg),
  iq2000_annotate_frame_insn (emit_move_insn (mem, reg),
                            gen_rtx_SET (GET_MODE (reg), dwarf_mem, reg));
                            gen_rtx_SET (GET_MODE (reg), dwarf_mem, reg));
}
}
 
 
/* Emit instructions to save/restore registers, as determined by STORE_P.  */
/* Emit instructions to save/restore registers, as determined by STORE_P.  */
 
 
static void
static void
save_restore_insns (int store_p)
save_restore_insns (int store_p)
{
{
  long mask = cfun->machine->mask;
  long mask = cfun->machine->mask;
  int regno;
  int regno;
  rtx base_reg_rtx;
  rtx base_reg_rtx;
  HOST_WIDE_INT base_offset;
  HOST_WIDE_INT base_offset;
  HOST_WIDE_INT gp_offset;
  HOST_WIDE_INT gp_offset;
  HOST_WIDE_INT end_offset;
  HOST_WIDE_INT end_offset;
 
 
  gcc_assert (!frame_pointer_needed
  gcc_assert (!frame_pointer_needed
              || BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST));
              || BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST));
 
 
  if (mask == 0)
  if (mask == 0)
    {
    {
      base_reg_rtx = 0, base_offset  = 0;
      base_reg_rtx = 0, base_offset  = 0;
      return;
      return;
    }
    }
 
 
  /* Save registers starting from high to low.  The debuggers prefer at least
  /* Save registers starting from high to low.  The debuggers prefer at least
     the return register be stored at func+4, and also it allows us not to
     the return register be stored at func+4, and also it allows us not to
     need a nop in the epilog if at least one register is reloaded in
     need a nop in the epilog if at least one register is reloaded in
     addition to return address.  */
     addition to return address.  */
 
 
  /* Save GP registers if needed.  */
  /* Save GP registers if needed.  */
  /* Pick which pointer to use as a base register.  For small frames, just
  /* Pick which pointer to use as a base register.  For small frames, just
     use the stack pointer.  Otherwise, use a temporary register.  Save 2
     use the stack pointer.  Otherwise, use a temporary register.  Save 2
     cycles if the save area is near the end of a large frame, by reusing
     cycles if the save area is near the end of a large frame, by reusing
     the constant created in the prologue/epilogue to adjust the stack
     the constant created in the prologue/epilogue to adjust the stack
     frame.  */
     frame.  */
 
 
  gp_offset = cfun->machine->gp_sp_offset;
  gp_offset = cfun->machine->gp_sp_offset;
  end_offset
  end_offset
    = gp_offset - (cfun->machine->gp_reg_size
    = gp_offset - (cfun->machine->gp_reg_size
                   - GET_MODE_SIZE (gpr_mode));
                   - GET_MODE_SIZE (gpr_mode));
 
 
  if (gp_offset < 0 || end_offset < 0)
  if (gp_offset < 0 || end_offset < 0)
    internal_error
    internal_error
      ("gp_offset (%ld) or end_offset (%ld) is less than zero",
      ("gp_offset (%ld) or end_offset (%ld) is less than zero",
       (long) gp_offset, (long) end_offset);
       (long) gp_offset, (long) end_offset);
 
 
  else if (gp_offset < 32768)
  else if (gp_offset < 32768)
    base_reg_rtx = stack_pointer_rtx, base_offset  = 0;
    base_reg_rtx = stack_pointer_rtx, base_offset  = 0;
  else
  else
    {
    {
      int regno;
      int regno;
      int reg_save_count = 0;
      int reg_save_count = 0;
 
 
      for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
      for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
        if (BITSET_P (mask, regno - GP_REG_FIRST)) reg_save_count += 1;
        if (BITSET_P (mask, regno - GP_REG_FIRST)) reg_save_count += 1;
      base_offset = gp_offset - ((reg_save_count - 1) * 4);
      base_offset = gp_offset - ((reg_save_count - 1) * 4);
      base_reg_rtx = iq2000_add_large_offset_to_sp (base_offset);
      base_reg_rtx = iq2000_add_large_offset_to_sp (base_offset);
    }
    }
 
 
  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
    {
    {
      if (BITSET_P (mask, regno - GP_REG_FIRST))
      if (BITSET_P (mask, regno - GP_REG_FIRST))
        {
        {
          rtx reg_rtx;
          rtx reg_rtx;
          rtx mem_rtx
          rtx mem_rtx
            = gen_rtx_MEM (gpr_mode,
            = gen_rtx_MEM (gpr_mode,
                       gen_rtx_PLUS (Pmode, base_reg_rtx,
                       gen_rtx_PLUS (Pmode, base_reg_rtx,
                                GEN_INT (gp_offset - base_offset)));
                                GEN_INT (gp_offset - base_offset)));
 
 
          reg_rtx = gen_rtx_REG (gpr_mode, regno);
          reg_rtx = gen_rtx_REG (gpr_mode, regno);
 
 
          if (store_p)
          if (store_p)
            iq2000_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
            iq2000_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
          else
          else
            {
            {
              emit_move_insn (reg_rtx, mem_rtx);
              emit_move_insn (reg_rtx, mem_rtx);
            }
            }
          gp_offset -= GET_MODE_SIZE (gpr_mode);
          gp_offset -= GET_MODE_SIZE (gpr_mode);
        }
        }
    }
    }
}
}


/* Expand the prologue into a bunch of separate insns.  */
/* Expand the prologue into a bunch of separate insns.  */
 
 
void
void
iq2000_expand_prologue (void)
iq2000_expand_prologue (void)
{
{
  int regno;
  int regno;
  HOST_WIDE_INT tsize;
  HOST_WIDE_INT tsize;
  int last_arg_is_vararg_marker = 0;
  int last_arg_is_vararg_marker = 0;
  tree fndecl = current_function_decl;
  tree fndecl = current_function_decl;
  tree fntype = TREE_TYPE (fndecl);
  tree fntype = TREE_TYPE (fndecl);
  tree fnargs = DECL_ARGUMENTS (fndecl);
  tree fnargs = DECL_ARGUMENTS (fndecl);
  rtx next_arg_reg;
  rtx next_arg_reg;
  int i;
  int i;
  tree next_arg;
  tree next_arg;
  tree cur_arg;
  tree cur_arg;
  CUMULATIVE_ARGS args_so_far;
  CUMULATIVE_ARGS args_so_far;
  int store_args_on_stack = (iq2000_can_use_return_insn ());
  int store_args_on_stack = (iq2000_can_use_return_insn ());
 
 
  /* If struct value address is treated as the first argument.  */
  /* If struct value address is treated as the first argument.  */
  if (aggregate_value_p (DECL_RESULT (fndecl), fndecl)
  if (aggregate_value_p (DECL_RESULT (fndecl), fndecl)
      && !cfun->returns_pcc_struct
      && !cfun->returns_pcc_struct
      && targetm.calls.struct_value_rtx (TREE_TYPE (fndecl), 1) == 0)
      && targetm.calls.struct_value_rtx (TREE_TYPE (fndecl), 1) == 0)
    {
    {
      tree type = build_pointer_type (fntype);
      tree type = build_pointer_type (fntype);
      tree function_result_decl = build_decl (BUILTINS_LOCATION,
      tree function_result_decl = build_decl (BUILTINS_LOCATION,
                                              PARM_DECL, NULL_TREE, type);
                                              PARM_DECL, NULL_TREE, type);
 
 
      DECL_ARG_TYPE (function_result_decl) = type;
      DECL_ARG_TYPE (function_result_decl) = type;
      TREE_CHAIN (function_result_decl) = fnargs;
      TREE_CHAIN (function_result_decl) = fnargs;
      fnargs = function_result_decl;
      fnargs = function_result_decl;
    }
    }
 
 
  /* For arguments passed in registers, find the register number
  /* For arguments passed in registers, find the register number
     of the first argument in the variable part of the argument list,
     of the first argument in the variable part of the argument list,
     otherwise GP_ARG_LAST+1.  Note also if the last argument is
     otherwise GP_ARG_LAST+1.  Note also if the last argument is
     the varargs special argument, and treat it as part of the
     the varargs special argument, and treat it as part of the
     variable arguments.
     variable arguments.
 
 
     This is only needed if store_args_on_stack is true.  */
     This is only needed if store_args_on_stack is true.  */
  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0, 0);
  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0, 0);
  regno = GP_ARG_FIRST;
  regno = GP_ARG_FIRST;
 
 
  for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
  for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
    {
    {
      tree passed_type = DECL_ARG_TYPE (cur_arg);
      tree passed_type = DECL_ARG_TYPE (cur_arg);
      enum machine_mode passed_mode = TYPE_MODE (passed_type);
      enum machine_mode passed_mode = TYPE_MODE (passed_type);
      rtx entry_parm;
      rtx entry_parm;
 
 
      if (TREE_ADDRESSABLE (passed_type))
      if (TREE_ADDRESSABLE (passed_type))
        {
        {
          passed_type = build_pointer_type (passed_type);
          passed_type = build_pointer_type (passed_type);
          passed_mode = Pmode;
          passed_mode = Pmode;
        }
        }
 
 
      entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
      entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
 
 
      FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
      FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
      next_arg = TREE_CHAIN (cur_arg);
      next_arg = TREE_CHAIN (cur_arg);
 
 
      if (entry_parm && store_args_on_stack)
      if (entry_parm && store_args_on_stack)
        {
        {
          if (next_arg == 0
          if (next_arg == 0
              && DECL_NAME (cur_arg)
              && DECL_NAME (cur_arg)
              && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
              && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
                                "__builtin_va_alist"))
                                "__builtin_va_alist"))
                  || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
                  || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
                                   "va_alist"))))
                                   "va_alist"))))
            {
            {
              last_arg_is_vararg_marker = 1;
              last_arg_is_vararg_marker = 1;
              break;
              break;
            }
            }
          else
          else
            {
            {
              int words;
              int words;
 
 
              gcc_assert (GET_CODE (entry_parm) == REG);
              gcc_assert (GET_CODE (entry_parm) == REG);
 
 
              /* Passed in a register, so will get homed automatically.  */
              /* Passed in a register, so will get homed automatically.  */
              if (GET_MODE (entry_parm) == BLKmode)
              if (GET_MODE (entry_parm) == BLKmode)
                words = (int_size_in_bytes (passed_type) + 3) / 4;
                words = (int_size_in_bytes (passed_type) + 3) / 4;
              else
              else
                words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
                words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
 
 
              regno = REGNO (entry_parm) + words - 1;
              regno = REGNO (entry_parm) + words - 1;
            }
            }
        }
        }
      else
      else
        {
        {
          regno = GP_ARG_LAST+1;
          regno = GP_ARG_LAST+1;
          break;
          break;
        }
        }
    }
    }
 
 
  /* In order to pass small structures by value in registers we need to
  /* In order to pass small structures by value in registers we need to
     shift the value into the high part of the register.
     shift the value into the high part of the register.
     Function_arg has encoded a PARALLEL rtx, holding a vector of
     Function_arg has encoded a PARALLEL rtx, holding a vector of
     adjustments to be made as the next_arg_reg variable, so we split up the
     adjustments to be made as the next_arg_reg variable, so we split up the
     insns, and emit them separately.  */
     insns, and emit them separately.  */
  next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
  next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
  if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
  if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
    {
    {
      rtvec adjust = XVEC (next_arg_reg, 0);
      rtvec adjust = XVEC (next_arg_reg, 0);
      int num = GET_NUM_ELEM (adjust);
      int num = GET_NUM_ELEM (adjust);
 
 
      for (i = 0; i < num; i++)
      for (i = 0; i < num; i++)
        {
        {
          rtx insn, pattern;
          rtx insn, pattern;
 
 
          pattern = RTVEC_ELT (adjust, i);
          pattern = RTVEC_ELT (adjust, i);
          if (GET_CODE (pattern) != SET
          if (GET_CODE (pattern) != SET
              || GET_CODE (SET_SRC (pattern)) != ASHIFT)
              || GET_CODE (SET_SRC (pattern)) != ASHIFT)
            abort_with_insn (pattern, "Insn is not a shift");
            abort_with_insn (pattern, "Insn is not a shift");
          PUT_CODE (SET_SRC (pattern), ASHIFTRT);
          PUT_CODE (SET_SRC (pattern), ASHIFTRT);
 
 
          insn = emit_insn (pattern);
          insn = emit_insn (pattern);
        }
        }
    }
    }
 
 
  tsize = compute_frame_size (get_frame_size ());
  tsize = compute_frame_size (get_frame_size ());
 
 
  /* If this function is a varargs function, store any registers that
  /* If this function is a varargs function, store any registers that
     would normally hold arguments ($4 - $7) on the stack.  */
     would normally hold arguments ($4 - $7) on the stack.  */
  if (store_args_on_stack
  if (store_args_on_stack
      && ((TYPE_ARG_TYPES (fntype) != 0
      && ((TYPE_ARG_TYPES (fntype) != 0
           && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
           && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
               != void_type_node))
               != void_type_node))
          || last_arg_is_vararg_marker))
          || last_arg_is_vararg_marker))
    {
    {
      int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
      int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
      rtx ptr = stack_pointer_rtx;
      rtx ptr = stack_pointer_rtx;
 
 
      for (; regno <= GP_ARG_LAST; regno++)
      for (; regno <= GP_ARG_LAST; regno++)
        {
        {
          if (offset != 0)
          if (offset != 0)
            ptr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
            ptr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset));
          emit_move_insn (gen_rtx_MEM (gpr_mode, ptr),
          emit_move_insn (gen_rtx_MEM (gpr_mode, ptr),
                          gen_rtx_REG (gpr_mode, regno));
                          gen_rtx_REG (gpr_mode, regno));
 
 
          offset += GET_MODE_SIZE (gpr_mode);
          offset += GET_MODE_SIZE (gpr_mode);
        }
        }
    }
    }
 
 
  if (tsize > 0)
  if (tsize > 0)
    {
    {
      rtx tsize_rtx = GEN_INT (tsize);
      rtx tsize_rtx = GEN_INT (tsize);
      rtx adjustment_rtx, insn, dwarf_pattern;
      rtx adjustment_rtx, insn, dwarf_pattern;
 
 
      if (tsize > 32767)
      if (tsize > 32767)
        {
        {
          adjustment_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
          adjustment_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
          emit_move_insn (adjustment_rtx, tsize_rtx);
          emit_move_insn (adjustment_rtx, tsize_rtx);
        }
        }
      else
      else
        adjustment_rtx = tsize_rtx;
        adjustment_rtx = tsize_rtx;
 
 
      insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
      insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
                                    adjustment_rtx));
                                    adjustment_rtx));
 
 
      dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
      dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
                                   plus_constant (stack_pointer_rtx, -tsize));
                                   plus_constant (stack_pointer_rtx, -tsize));
 
 
      iq2000_annotate_frame_insn (insn, dwarf_pattern);
      iq2000_annotate_frame_insn (insn, dwarf_pattern);
 
 
      save_restore_insns (1);
      save_restore_insns (1);
 
 
      if (frame_pointer_needed)
      if (frame_pointer_needed)
        {
        {
          rtx insn = 0;
          rtx insn = 0;
 
 
          insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
          insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
                                       stack_pointer_rtx));
                                       stack_pointer_rtx));
 
 
          if (insn)
          if (insn)
            RTX_FRAME_RELATED_P (insn) = 1;
            RTX_FRAME_RELATED_P (insn) = 1;
        }
        }
    }
    }
 
 
  emit_insn (gen_blockage ());
  emit_insn (gen_blockage ());
}
}


/* Expand the epilogue into a bunch of separate insns.  */
/* Expand the epilogue into a bunch of separate insns.  */
 
 
void
void
iq2000_expand_epilogue (void)
iq2000_expand_epilogue (void)
{
{
  HOST_WIDE_INT tsize = cfun->machine->total_size;
  HOST_WIDE_INT tsize = cfun->machine->total_size;
  rtx tsize_rtx = GEN_INT (tsize);
  rtx tsize_rtx = GEN_INT (tsize);
  rtx tmp_rtx = (rtx)0;
  rtx tmp_rtx = (rtx)0;
 
 
  if (iq2000_can_use_return_insn ())
  if (iq2000_can_use_return_insn ())
    {
    {
      emit_jump_insn (gen_return ());
      emit_jump_insn (gen_return ());
      return;
      return;
    }
    }
 
 
  if (tsize > 32767)
  if (tsize > 32767)
    {
    {
      tmp_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
      tmp_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
      emit_move_insn (tmp_rtx, tsize_rtx);
      emit_move_insn (tmp_rtx, tsize_rtx);
      tsize_rtx = tmp_rtx;
      tsize_rtx = tmp_rtx;
    }
    }
 
 
  if (tsize > 0)
  if (tsize > 0)
    {
    {
      if (frame_pointer_needed)
      if (frame_pointer_needed)
        {
        {
          emit_insn (gen_blockage ());
          emit_insn (gen_blockage ());
 
 
          emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
          emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
        }
        }
 
 
      save_restore_insns (0);
      save_restore_insns (0);
 
 
      if (crtl->calls_eh_return)
      if (crtl->calls_eh_return)
        {
        {
          rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
          rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
          emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
          emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
          tsize_rtx = eh_ofs;
          tsize_rtx = eh_ofs;
        }
        }
 
 
      emit_insn (gen_blockage ());
      emit_insn (gen_blockage ());
 
 
      if (tsize != 0 || crtl->calls_eh_return)
      if (tsize != 0 || crtl->calls_eh_return)
        {
        {
          emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
          emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
                                 tsize_rtx));
                                 tsize_rtx));
        }
        }
    }
    }
 
 
  if (crtl->calls_eh_return)
  if (crtl->calls_eh_return)
    {
    {
      /* Perform the additional bump for __throw.  */
      /* Perform the additional bump for __throw.  */
      emit_move_insn (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM),
      emit_move_insn (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM),
                      stack_pointer_rtx);
                      stack_pointer_rtx);
      emit_use (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM));
      emit_use (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM));
      emit_jump_insn (gen_eh_return_internal ());
      emit_jump_insn (gen_eh_return_internal ());
    }
    }
  else
  else
      emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
      emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode,
                                                  GP_REG_FIRST + 31)));
                                                  GP_REG_FIRST + 31)));
}
}
 
 
void
void
iq2000_expand_eh_return (rtx address)
iq2000_expand_eh_return (rtx address)
{
{
  HOST_WIDE_INT gp_offset = cfun->machine->gp_sp_offset;
  HOST_WIDE_INT gp_offset = cfun->machine->gp_sp_offset;
  rtx scratch;
  rtx scratch;
 
 
  scratch = plus_constant (stack_pointer_rtx, gp_offset);
  scratch = plus_constant (stack_pointer_rtx, gp_offset);
  emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);
  emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);
}
}


/* Return nonzero if this function is known to have a null epilogue.
/* Return nonzero if this function is known to have a null epilogue.
   This allows the optimizer to omit jumps to jumps if no stack
   This allows the optimizer to omit jumps to jumps if no stack
   was created.  */
   was created.  */
 
 
int
int
iq2000_can_use_return_insn (void)
iq2000_can_use_return_insn (void)
{
{
  if (! reload_completed)
  if (! reload_completed)
    return 0;
    return 0;
 
 
  if (df_regs_ever_live_p (31) || profile_flag)
  if (df_regs_ever_live_p (31) || profile_flag)
    return 0;
    return 0;
 
 
  if (cfun->machine->initialized)
  if (cfun->machine->initialized)
    return cfun->machine->total_size == 0;
    return cfun->machine->total_size == 0;
 
 
  return compute_frame_size (get_frame_size ()) == 0;
  return compute_frame_size (get_frame_size ()) == 0;
}
}


/* Returns nonzero if X contains a SYMBOL_REF.  */
/* Returns nonzero if X contains a SYMBOL_REF.  */
 
 
static int
static int
symbolic_expression_p (rtx x)
symbolic_expression_p (rtx x)
{
{
  if (GET_CODE (x) == SYMBOL_REF)
  if (GET_CODE (x) == SYMBOL_REF)
    return 1;
    return 1;
 
 
  if (GET_CODE (x) == CONST)
  if (GET_CODE (x) == CONST)
    return symbolic_expression_p (XEXP (x, 0));
    return symbolic_expression_p (XEXP (x, 0));
 
 
  if (UNARY_P (x))
  if (UNARY_P (x))
    return symbolic_expression_p (XEXP (x, 0));
    return symbolic_expression_p (XEXP (x, 0));
 
 
  if (ARITHMETIC_P (x))
  if (ARITHMETIC_P (x))
    return (symbolic_expression_p (XEXP (x, 0))
    return (symbolic_expression_p (XEXP (x, 0))
            || symbolic_expression_p (XEXP (x, 1)));
            || symbolic_expression_p (XEXP (x, 1)));
 
 
  return 0;
  return 0;
}
}
 
 
/* Choose the section to use for the constant rtx expression X that has
/* Choose the section to use for the constant rtx expression X that has
   mode MODE.  */
   mode MODE.  */
 
 
static section *
static section *
iq2000_select_rtx_section (enum machine_mode mode, rtx x ATTRIBUTE_UNUSED,
iq2000_select_rtx_section (enum machine_mode mode, rtx x ATTRIBUTE_UNUSED,
                           unsigned HOST_WIDE_INT align)
                           unsigned HOST_WIDE_INT align)
{
{
  /* For embedded applications, always put constants in read-only data,
  /* For embedded applications, always put constants in read-only data,
     in order to reduce RAM usage.  */
     in order to reduce RAM usage.  */
  return mergeable_constant_section (mode, align, 0);
  return mergeable_constant_section (mode, align, 0);
}
}
 
 
/* Choose the section to use for DECL.  RELOC is true if its value contains
/* Choose the section to use for DECL.  RELOC is true if its value contains
   any relocatable expression.
   any relocatable expression.
 
 
   Some of the logic used here needs to be replicated in
   Some of the logic used here needs to be replicated in
   ENCODE_SECTION_INFO in iq2000.h so that references to these symbols
   ENCODE_SECTION_INFO in iq2000.h so that references to these symbols
   are done correctly.  */
   are done correctly.  */
 
 
static section *
static section *
iq2000_select_section (tree decl, int reloc ATTRIBUTE_UNUSED,
iq2000_select_section (tree decl, int reloc ATTRIBUTE_UNUSED,
                       unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
                       unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
{
{
  if (TARGET_EMBEDDED_DATA)
  if (TARGET_EMBEDDED_DATA)
    {
    {
      /* For embedded applications, always put an object in read-only data
      /* For embedded applications, always put an object in read-only data
         if possible, in order to reduce RAM usage.  */
         if possible, in order to reduce RAM usage.  */
      if ((TREE_CODE (decl) == VAR_DECL
      if ((TREE_CODE (decl) == VAR_DECL
           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
           && DECL_INITIAL (decl)
           && DECL_INITIAL (decl)
           && (DECL_INITIAL (decl) == error_mark_node
           && (DECL_INITIAL (decl) == error_mark_node
               || TREE_CONSTANT (DECL_INITIAL (decl))))
               || TREE_CONSTANT (DECL_INITIAL (decl))))
          /* Deal with calls from output_constant_def_contents.  */
          /* Deal with calls from output_constant_def_contents.  */
          || TREE_CODE (decl) != VAR_DECL)
          || TREE_CODE (decl) != VAR_DECL)
        return readonly_data_section;
        return readonly_data_section;
      else
      else
        return data_section;
        return data_section;
    }
    }
  else
  else
    {
    {
      /* For hosted applications, always put an object in small data if
      /* For hosted applications, always put an object in small data if
         possible, as this gives the best performance.  */
         possible, as this gives the best performance.  */
      if ((TREE_CODE (decl) == VAR_DECL
      if ((TREE_CODE (decl) == VAR_DECL
           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
           && DECL_INITIAL (decl)
           && DECL_INITIAL (decl)
           && (DECL_INITIAL (decl) == error_mark_node
           && (DECL_INITIAL (decl) == error_mark_node
               || TREE_CONSTANT (DECL_INITIAL (decl))))
               || TREE_CONSTANT (DECL_INITIAL (decl))))
          /* Deal with calls from output_constant_def_contents.  */
          /* Deal with calls from output_constant_def_contents.  */
          || TREE_CODE (decl) != VAR_DECL)
          || TREE_CODE (decl) != VAR_DECL)
        return readonly_data_section;
        return readonly_data_section;
      else
      else
        return data_section;
        return data_section;
    }
    }
}
}
/* Return register to use for a function return value with VALTYPE for function
/* Return register to use for a function return value with VALTYPE for function
   FUNC.  */
   FUNC.  */
 
 
static rtx
static rtx
iq2000_function_value (const_tree valtype,
iq2000_function_value (const_tree valtype,
                       const_tree fn_decl_or_type,
                       const_tree fn_decl_or_type,
                       bool outgoing ATTRIBUTE_UNUSED)
                       bool outgoing ATTRIBUTE_UNUSED)
{
{
  int reg = GP_RETURN;
  int reg = GP_RETURN;
  enum machine_mode mode = TYPE_MODE (valtype);
  enum machine_mode mode = TYPE_MODE (valtype);
  int unsignedp = TYPE_UNSIGNED (valtype);
  int unsignedp = TYPE_UNSIGNED (valtype);
  tree func = fn_decl_or_type;
  tree func = fn_decl_or_type;
 
 
  if (fn_decl_or_type
  if (fn_decl_or_type
      && !DECL_P (fn_decl_or_type))
      && !DECL_P (fn_decl_or_type))
    fn_decl_or_type = NULL;
    fn_decl_or_type = NULL;
 
 
  /* Since we promote return types, we must promote the mode here too.  */
  /* Since we promote return types, we must promote the mode here too.  */
  mode = promote_function_mode (valtype, mode, &unsignedp, func, 1);
  mode = promote_function_mode (valtype, mode, &unsignedp, func, 1);
 
 
  return gen_rtx_REG (mode, reg);
  return gen_rtx_REG (mode, reg);
}
}
 
 
/* Worker function for TARGET_LIBCALL_VALUE.  */
/* Worker function for TARGET_LIBCALL_VALUE.  */
 
 
static rtx
static rtx
iq2000_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
iq2000_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
{
{
  return gen_rtx_REG (((GET_MODE_CLASS (mode) != MODE_INT
  return gen_rtx_REG (((GET_MODE_CLASS (mode) != MODE_INT
                        || GET_MODE_SIZE (mode) >= 4)
                        || GET_MODE_SIZE (mode) >= 4)
                       ? mode : SImode),
                       ? mode : SImode),
                      GP_RETURN);
                      GP_RETURN);
}
}
 
 
/* Worker function for FUNCTION_VALUE_REGNO_P.
/* Worker function for FUNCTION_VALUE_REGNO_P.
 
 
   On the IQ2000, R2 and R3 are the only register thus used.  */
   On the IQ2000, R2 and R3 are the only register thus used.  */
 
 
bool
bool
iq2000_function_value_regno_p (const unsigned int regno)
iq2000_function_value_regno_p (const unsigned int regno)
{
{
  return (regno == GP_RETURN);
  return (regno == GP_RETURN);
}
}
 
 


/* Return true when an argument must be passed by reference.  */
/* Return true when an argument must be passed by reference.  */
 
 
static bool
static bool
iq2000_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
iq2000_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode,
                          const_tree type, bool named ATTRIBUTE_UNUSED)
                          const_tree type, bool named ATTRIBUTE_UNUSED)
{
{
  int size;
  int size;
 
 
  /* We must pass by reference if we would be both passing in registers
  /* We must pass by reference if we would be both passing in registers
     and the stack.  This is because any subsequent partial arg would be
     and the stack.  This is because any subsequent partial arg would be
     handled incorrectly in this case.  */
     handled incorrectly in this case.  */
  if (cum && targetm.calls.must_pass_in_stack (mode, type))
  if (cum && targetm.calls.must_pass_in_stack (mode, type))
     {
     {
       /* Don't pass the actual CUM to FUNCTION_ARG, because we would
       /* Don't pass the actual CUM to FUNCTION_ARG, because we would
          get double copies of any offsets generated for small structs
          get double copies of any offsets generated for small structs
          passed in registers.  */
          passed in registers.  */
       CUMULATIVE_ARGS temp;
       CUMULATIVE_ARGS temp;
 
 
       temp = *cum;
       temp = *cum;
       if (FUNCTION_ARG (temp, mode, type, named) != 0)
       if (FUNCTION_ARG (temp, mode, type, named) != 0)
         return 1;
         return 1;
     }
     }
 
 
  if (type == NULL_TREE || mode == DImode || mode == DFmode)
  if (type == NULL_TREE || mode == DImode || mode == DFmode)
    return 0;
    return 0;
 
 
  size = int_size_in_bytes (type);
  size = int_size_in_bytes (type);
  return size == -1 || size > UNITS_PER_WORD;
  return size == -1 || size > UNITS_PER_WORD;
}
}
 
 
/* Return the length of INSN.  LENGTH is the initial length computed by
/* Return the length of INSN.  LENGTH is the initial length computed by
   attributes in the machine-description file.  */
   attributes in the machine-description file.  */
 
 
int
int
iq2000_adjust_insn_length (rtx insn, int length)
iq2000_adjust_insn_length (rtx insn, int length)
{
{
  /* A unconditional jump has an unfilled delay slot if it is not part
  /* A unconditional jump has an unfilled delay slot if it is not part
     of a sequence.  A conditional jump normally has a delay slot.  */
     of a sequence.  A conditional jump normally has a delay slot.  */
  if (simplejump_p (insn)
  if (simplejump_p (insn)
      || (   (GET_CODE (insn) == JUMP_INSN
      || (   (GET_CODE (insn) == JUMP_INSN
           || GET_CODE (insn) == CALL_INSN)))
           || GET_CODE (insn) == CALL_INSN)))
    length += 4;
    length += 4;
 
 
  return length;
  return length;
}
}
 
 
/* Output assembly instructions to perform a conditional branch.
/* Output assembly instructions to perform a conditional branch.
 
 
   INSN is the branch instruction.  OPERANDS[0] is the condition.
   INSN is the branch instruction.  OPERANDS[0] is the condition.
   OPERANDS[1] is the target of the branch.  OPERANDS[2] is the target
   OPERANDS[1] is the target of the branch.  OPERANDS[2] is the target
   of the first operand to the condition.  If TWO_OPERANDS_P is
   of the first operand to the condition.  If TWO_OPERANDS_P is
   nonzero the comparison takes two operands; OPERANDS[3] will be the
   nonzero the comparison takes two operands; OPERANDS[3] will be the
   second operand.
   second operand.
 
 
   If INVERTED_P is nonzero we are to branch if the condition does
   If INVERTED_P is nonzero we are to branch if the condition does
   not hold.  If FLOAT_P is nonzero this is a floating-point comparison.
   not hold.  If FLOAT_P is nonzero this is a floating-point comparison.
 
 
   LENGTH is the length (in bytes) of the sequence we are to generate.
   LENGTH is the length (in bytes) of the sequence we are to generate.
   That tells us whether to generate a simple conditional branch, or a
   That tells us whether to generate a simple conditional branch, or a
   reversed conditional branch around a `jr' instruction.  */
   reversed conditional branch around a `jr' instruction.  */
 
 
char *
char *
iq2000_output_conditional_branch (rtx insn, rtx * operands, int two_operands_p,
iq2000_output_conditional_branch (rtx insn, rtx * operands, int two_operands_p,
                                  int float_p, int inverted_p, int length)
                                  int float_p, int inverted_p, int length)
{
{
  static char buffer[200];
  static char buffer[200];
  /* The kind of comparison we are doing.  */
  /* The kind of comparison we are doing.  */
  enum rtx_code code = GET_CODE (operands[0]);
  enum rtx_code code = GET_CODE (operands[0]);
  /* Nonzero if the opcode for the comparison needs a `z' indicating
  /* Nonzero if the opcode for the comparison needs a `z' indicating
     that it is a comparison against zero.  */
     that it is a comparison against zero.  */
  int need_z_p;
  int need_z_p;
  /* A string to use in the assembly output to represent the first
  /* A string to use in the assembly output to represent the first
     operand.  */
     operand.  */
  const char *op1 = "%z2";
  const char *op1 = "%z2";
  /* A string to use in the assembly output to represent the second
  /* A string to use in the assembly output to represent the second
     operand.  Use the hard-wired zero register if there's no second
     operand.  Use the hard-wired zero register if there's no second
     operand.  */
     operand.  */
  const char *op2 = (two_operands_p ? ",%z3" : ",%.");
  const char *op2 = (two_operands_p ? ",%z3" : ",%.");
  /* The operand-printing string for the comparison.  */
  /* The operand-printing string for the comparison.  */
  const char *comp = (float_p ? "%F0" : "%C0");
  const char *comp = (float_p ? "%F0" : "%C0");
  /* The operand-printing string for the inverted comparison.  */
  /* The operand-printing string for the inverted comparison.  */
  const char *inverted_comp = (float_p ? "%W0" : "%N0");
  const char *inverted_comp = (float_p ? "%W0" : "%N0");
 
 
  /* Likely variants of each branch instruction annul the instruction
  /* Likely variants of each branch instruction annul the instruction
     in the delay slot if the branch is not taken.  */
     in the delay slot if the branch is not taken.  */
  iq2000_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
  iq2000_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
 
 
  if (!two_operands_p)
  if (!two_operands_p)
    {
    {
      /* To compute whether than A > B, for example, we normally
      /* To compute whether than A > B, for example, we normally
         subtract B from A and then look at the sign bit.  But, if we
         subtract B from A and then look at the sign bit.  But, if we
         are doing an unsigned comparison, and B is zero, we don't
         are doing an unsigned comparison, and B is zero, we don't
         have to do the subtraction.  Instead, we can just check to
         have to do the subtraction.  Instead, we can just check to
         see if A is nonzero.  Thus, we change the CODE here to
         see if A is nonzero.  Thus, we change the CODE here to
         reflect the simpler comparison operation.  */
         reflect the simpler comparison operation.  */
      switch (code)
      switch (code)
        {
        {
        case GTU:
        case GTU:
          code = NE;
          code = NE;
          break;
          break;
 
 
        case LEU:
        case LEU:
          code = EQ;
          code = EQ;
          break;
          break;
 
 
        case GEU:
        case GEU:
          /* A condition which will always be true.  */
          /* A condition which will always be true.  */
          code = EQ;
          code = EQ;
          op1 = "%.";
          op1 = "%.";
          break;
          break;
 
 
        case LTU:
        case LTU:
          /* A condition which will always be false.  */
          /* A condition which will always be false.  */
          code = NE;
          code = NE;
          op1 = "%.";
          op1 = "%.";
          break;
          break;
 
 
        default:
        default:
          /* Not a special case.  */
          /* Not a special case.  */
          break;
          break;
        }
        }
    }
    }
 
 
  /* Relative comparisons are always done against zero.  But
  /* Relative comparisons are always done against zero.  But
     equality comparisons are done between two operands, and therefore
     equality comparisons are done between two operands, and therefore
     do not require a `z' in the assembly language output.  */
     do not require a `z' in the assembly language output.  */
  need_z_p = (!float_p && code != EQ && code != NE);
  need_z_p = (!float_p && code != EQ && code != NE);
  /* For comparisons against zero, the zero is not provided
  /* For comparisons against zero, the zero is not provided
     explicitly.  */
     explicitly.  */
  if (need_z_p)
  if (need_z_p)
    op2 = "";
    op2 = "";
 
 
  /* Begin by terminating the buffer.  That way we can always use
  /* Begin by terminating the buffer.  That way we can always use
     strcat to add to it.  */
     strcat to add to it.  */
  buffer[0] = '\0';
  buffer[0] = '\0';
 
 
  switch (length)
  switch (length)
    {
    {
    case 4:
    case 4:
    case 8:
    case 8:
      /* Just a simple conditional branch.  */
      /* Just a simple conditional branch.  */
      if (float_p)
      if (float_p)
        sprintf (buffer, "b%s%%?\t%%Z2%%1",
        sprintf (buffer, "b%s%%?\t%%Z2%%1",
                 inverted_p ? inverted_comp : comp);
                 inverted_p ? inverted_comp : comp);
      else
      else
        sprintf (buffer, "b%s%s%%?\t%s%s,%%1",
        sprintf (buffer, "b%s%s%%?\t%s%s,%%1",
                 inverted_p ? inverted_comp : comp,
                 inverted_p ? inverted_comp : comp,
                 need_z_p ? "z" : "",
                 need_z_p ? "z" : "",
                 op1,
                 op1,
                 op2);
                 op2);
      return buffer;
      return buffer;
 
 
    case 12:
    case 12:
    case 16:
    case 16:
      {
      {
        /* Generate a reversed conditional branch around ` j'
        /* Generate a reversed conditional branch around ` j'
           instruction:
           instruction:
 
 
                .set noreorder
                .set noreorder
                .set nomacro
                .set nomacro
                bc    l
                bc    l
                nop
                nop
                j     target
                j     target
                .set macro
                .set macro
                .set reorder
                .set reorder
             l:
             l:
 
 
           Because we have to jump four bytes *past* the following
           Because we have to jump four bytes *past* the following
           instruction if this branch was annulled, we can't just use
           instruction if this branch was annulled, we can't just use
           a label, as in the picture above; there's no way to put the
           a label, as in the picture above; there's no way to put the
           label after the next instruction, as the assembler does not
           label after the next instruction, as the assembler does not
           accept `.L+4' as the target of a branch.  (We can't just
           accept `.L+4' as the target of a branch.  (We can't just
           wait until the next instruction is output; it might be a
           wait until the next instruction is output; it might be a
           macro and take up more than four bytes.  Once again, we see
           macro and take up more than four bytes.  Once again, we see
           why we want to eliminate macros.)
           why we want to eliminate macros.)
 
 
           If the branch is annulled, we jump four more bytes that we
           If the branch is annulled, we jump four more bytes that we
           would otherwise; that way we skip the annulled instruction
           would otherwise; that way we skip the annulled instruction
           in the delay slot.  */
           in the delay slot.  */
 
 
        const char *target
        const char *target
          = ((iq2000_branch_likely || length == 16) ? ".+16" : ".+12");
          = ((iq2000_branch_likely || length == 16) ? ".+16" : ".+12");
        char *c;
        char *c;
 
 
        c = strchr (buffer, '\0');
        c = strchr (buffer, '\0');
        /* Generate the reversed comparison.  This takes four
        /* Generate the reversed comparison.  This takes four
           bytes.  */
           bytes.  */
        if (float_p)
        if (float_p)
          sprintf (c, "b%s\t%%Z2%s",
          sprintf (c, "b%s\t%%Z2%s",
                   inverted_p ? comp : inverted_comp,
                   inverted_p ? comp : inverted_comp,
                   target);
                   target);
        else
        else
          sprintf (c, "b%s%s\t%s%s,%s",
          sprintf (c, "b%s%s\t%s%s,%s",
                   inverted_p ? comp : inverted_comp,
                   inverted_p ? comp : inverted_comp,
                   need_z_p ? "z" : "",
                   need_z_p ? "z" : "",
                   op1,
                   op1,
                   op2,
                   op2,
                   target);
                   target);
        strcat (c, "\n\tnop\n\tj\t%1");
        strcat (c, "\n\tnop\n\tj\t%1");
        if (length == 16)
        if (length == 16)
          /* The delay slot was unfilled.  Since we're inside
          /* The delay slot was unfilled.  Since we're inside
             .noreorder, the assembler will not fill in the NOP for
             .noreorder, the assembler will not fill in the NOP for
             us, so we must do it ourselves.  */
             us, so we must do it ourselves.  */
          strcat (buffer, "\n\tnop");
          strcat (buffer, "\n\tnop");
        return buffer;
        return buffer;
      }
      }
 
 
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  /* NOTREACHED */
  /* NOTREACHED */
  return 0;
  return 0;
}
}
 
 
#define def_builtin(NAME, TYPE, CODE)                                   \
#define def_builtin(NAME, TYPE, CODE)                                   \
  add_builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,    \
  add_builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD,    \
                       NULL, NULL_TREE)
                       NULL, NULL_TREE)
 
 
static void
static void
iq2000_init_builtins (void)
iq2000_init_builtins (void)
{
{
  tree endlink = void_list_node;
  tree endlink = void_list_node;
  tree void_ftype, void_ftype_int, void_ftype_int_int;
  tree void_ftype, void_ftype_int, void_ftype_int_int;
  tree void_ftype_int_int_int;
  tree void_ftype_int_int_int;
  tree int_ftype_int, int_ftype_int_int, int_ftype_int_int_int;
  tree int_ftype_int, int_ftype_int_int, int_ftype_int_int_int;
  tree int_ftype_int_int_int_int;
  tree int_ftype_int_int_int_int;
 
 
  /* func () */
  /* func () */
  void_ftype
  void_ftype
    = build_function_type (void_type_node,
    = build_function_type (void_type_node,
                           tree_cons (NULL_TREE, void_type_node, endlink));
                           tree_cons (NULL_TREE, void_type_node, endlink));
 
 
  /* func (int) */
  /* func (int) */
  void_ftype_int
  void_ftype_int
    = build_function_type (void_type_node,
    = build_function_type (void_type_node,
                           tree_cons (NULL_TREE, integer_type_node, endlink));
                           tree_cons (NULL_TREE, integer_type_node, endlink));
 
 
  /* void func (int, int) */
  /* void func (int, int) */
  void_ftype_int_int
  void_ftype_int_int
    = build_function_type (void_type_node,
    = build_function_type (void_type_node,
                           tree_cons (NULL_TREE, integer_type_node,
                           tree_cons (NULL_TREE, integer_type_node,
                                      tree_cons (NULL_TREE, integer_type_node,
                                      tree_cons (NULL_TREE, integer_type_node,
                                                 endlink)));
                                                 endlink)));
 
 
  /* int func (int) */
  /* int func (int) */
  int_ftype_int
  int_ftype_int
    = build_function_type (integer_type_node,
    = build_function_type (integer_type_node,
                           tree_cons (NULL_TREE, integer_type_node, endlink));
                           tree_cons (NULL_TREE, integer_type_node, endlink));
 
 
  /* int func (int, int) */
  /* int func (int, int) */
  int_ftype_int_int
  int_ftype_int_int
    = build_function_type (integer_type_node,
    = build_function_type (integer_type_node,
                           tree_cons (NULL_TREE, integer_type_node,
                           tree_cons (NULL_TREE, integer_type_node,
                                      tree_cons (NULL_TREE, integer_type_node,
                                      tree_cons (NULL_TREE, integer_type_node,
                                                 endlink)));
                                                 endlink)));
 
 
  /* void func (int, int, int) */
  /* void func (int, int, int) */
void_ftype_int_int_int
void_ftype_int_int_int
    = build_function_type
    = build_function_type
    (void_type_node,
    (void_type_node,
     tree_cons (NULL_TREE, integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                           tree_cons (NULL_TREE,
                           tree_cons (NULL_TREE,
                                      integer_type_node,
                                      integer_type_node,
                                      endlink))));
                                      endlink))));
 
 
  /* int func (int, int, int, int) */
  /* int func (int, int, int, int) */
  int_ftype_int_int_int_int
  int_ftype_int_int_int_int
    = build_function_type
    = build_function_type
    (integer_type_node,
    (integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                           tree_cons (NULL_TREE,
                           tree_cons (NULL_TREE,
                                      integer_type_node,
                                      integer_type_node,
                                      tree_cons (NULL_TREE,
                                      tree_cons (NULL_TREE,
                                                 integer_type_node,
                                                 integer_type_node,
                                                 endlink)))));
                                                 endlink)))));
 
 
  /* int func (int, int, int) */
  /* int func (int, int, int) */
  int_ftype_int_int_int
  int_ftype_int_int_int
    = build_function_type
    = build_function_type
    (integer_type_node,
    (integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                           tree_cons (NULL_TREE,
                           tree_cons (NULL_TREE,
                                      integer_type_node,
                                      integer_type_node,
                                      endlink))));
                                      endlink))));
 
 
  /* int func (int, int, int, int) */
  /* int func (int, int, int, int) */
  int_ftype_int_int_int_int
  int_ftype_int_int_int_int
    = build_function_type
    = build_function_type
    (integer_type_node,
    (integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
     tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                tree_cons (NULL_TREE, integer_type_node,
                           tree_cons (NULL_TREE,
                           tree_cons (NULL_TREE,
                                      integer_type_node,
                                      integer_type_node,
                                      tree_cons (NULL_TREE,
                                      tree_cons (NULL_TREE,
                                                 integer_type_node,
                                                 integer_type_node,
                                                 endlink)))));
                                                 endlink)))));
 
 
  def_builtin ("__builtin_ado16", int_ftype_int_int, IQ2000_BUILTIN_ADO16);
  def_builtin ("__builtin_ado16", int_ftype_int_int, IQ2000_BUILTIN_ADO16);
  def_builtin ("__builtin_ram", int_ftype_int_int_int_int, IQ2000_BUILTIN_RAM);
  def_builtin ("__builtin_ram", int_ftype_int_int_int_int, IQ2000_BUILTIN_RAM);
  def_builtin ("__builtin_chkhdr", void_ftype_int_int, IQ2000_BUILTIN_CHKHDR);
  def_builtin ("__builtin_chkhdr", void_ftype_int_int, IQ2000_BUILTIN_CHKHDR);
  def_builtin ("__builtin_pkrl", void_ftype_int_int, IQ2000_BUILTIN_PKRL);
  def_builtin ("__builtin_pkrl", void_ftype_int_int, IQ2000_BUILTIN_PKRL);
  def_builtin ("__builtin_cfc0", int_ftype_int, IQ2000_BUILTIN_CFC0);
  def_builtin ("__builtin_cfc0", int_ftype_int, IQ2000_BUILTIN_CFC0);
  def_builtin ("__builtin_cfc1", int_ftype_int, IQ2000_BUILTIN_CFC1);
  def_builtin ("__builtin_cfc1", int_ftype_int, IQ2000_BUILTIN_CFC1);
  def_builtin ("__builtin_cfc2", int_ftype_int, IQ2000_BUILTIN_CFC2);
  def_builtin ("__builtin_cfc2", int_ftype_int, IQ2000_BUILTIN_CFC2);
  def_builtin ("__builtin_cfc3", int_ftype_int, IQ2000_BUILTIN_CFC3);
  def_builtin ("__builtin_cfc3", int_ftype_int, IQ2000_BUILTIN_CFC3);
  def_builtin ("__builtin_ctc0", void_ftype_int_int, IQ2000_BUILTIN_CTC0);
  def_builtin ("__builtin_ctc0", void_ftype_int_int, IQ2000_BUILTIN_CTC0);
  def_builtin ("__builtin_ctc1", void_ftype_int_int, IQ2000_BUILTIN_CTC1);
  def_builtin ("__builtin_ctc1", void_ftype_int_int, IQ2000_BUILTIN_CTC1);
  def_builtin ("__builtin_ctc2", void_ftype_int_int, IQ2000_BUILTIN_CTC2);
  def_builtin ("__builtin_ctc2", void_ftype_int_int, IQ2000_BUILTIN_CTC2);
  def_builtin ("__builtin_ctc3", void_ftype_int_int, IQ2000_BUILTIN_CTC3);
  def_builtin ("__builtin_ctc3", void_ftype_int_int, IQ2000_BUILTIN_CTC3);
  def_builtin ("__builtin_mfc0", int_ftype_int, IQ2000_BUILTIN_MFC0);
  def_builtin ("__builtin_mfc0", int_ftype_int, IQ2000_BUILTIN_MFC0);
  def_builtin ("__builtin_mfc1", int_ftype_int, IQ2000_BUILTIN_MFC1);
  def_builtin ("__builtin_mfc1", int_ftype_int, IQ2000_BUILTIN_MFC1);
  def_builtin ("__builtin_mfc2", int_ftype_int, IQ2000_BUILTIN_MFC2);
  def_builtin ("__builtin_mfc2", int_ftype_int, IQ2000_BUILTIN_MFC2);
  def_builtin ("__builtin_mfc3", int_ftype_int, IQ2000_BUILTIN_MFC3);
  def_builtin ("__builtin_mfc3", int_ftype_int, IQ2000_BUILTIN_MFC3);
  def_builtin ("__builtin_mtc0", void_ftype_int_int, IQ2000_BUILTIN_MTC0);
  def_builtin ("__builtin_mtc0", void_ftype_int_int, IQ2000_BUILTIN_MTC0);
  def_builtin ("__builtin_mtc1", void_ftype_int_int, IQ2000_BUILTIN_MTC1);
  def_builtin ("__builtin_mtc1", void_ftype_int_int, IQ2000_BUILTIN_MTC1);
  def_builtin ("__builtin_mtc2", void_ftype_int_int, IQ2000_BUILTIN_MTC2);
  def_builtin ("__builtin_mtc2", void_ftype_int_int, IQ2000_BUILTIN_MTC2);
  def_builtin ("__builtin_mtc3", void_ftype_int_int, IQ2000_BUILTIN_MTC3);
  def_builtin ("__builtin_mtc3", void_ftype_int_int, IQ2000_BUILTIN_MTC3);
  def_builtin ("__builtin_lur", void_ftype_int_int, IQ2000_BUILTIN_LUR);
  def_builtin ("__builtin_lur", void_ftype_int_int, IQ2000_BUILTIN_LUR);
  def_builtin ("__builtin_rb", void_ftype_int_int, IQ2000_BUILTIN_RB);
  def_builtin ("__builtin_rb", void_ftype_int_int, IQ2000_BUILTIN_RB);
  def_builtin ("__builtin_rx", void_ftype_int_int, IQ2000_BUILTIN_RX);
  def_builtin ("__builtin_rx", void_ftype_int_int, IQ2000_BUILTIN_RX);
  def_builtin ("__builtin_srrd", void_ftype_int, IQ2000_BUILTIN_SRRD);
  def_builtin ("__builtin_srrd", void_ftype_int, IQ2000_BUILTIN_SRRD);
  def_builtin ("__builtin_srwr", void_ftype_int_int, IQ2000_BUILTIN_SRWR);
  def_builtin ("__builtin_srwr", void_ftype_int_int, IQ2000_BUILTIN_SRWR);
  def_builtin ("__builtin_wb", void_ftype_int_int, IQ2000_BUILTIN_WB);
  def_builtin ("__builtin_wb", void_ftype_int_int, IQ2000_BUILTIN_WB);
  def_builtin ("__builtin_wx", void_ftype_int_int, IQ2000_BUILTIN_WX);
  def_builtin ("__builtin_wx", void_ftype_int_int, IQ2000_BUILTIN_WX);
  def_builtin ("__builtin_luc32l", void_ftype_int_int, IQ2000_BUILTIN_LUC32L);
  def_builtin ("__builtin_luc32l", void_ftype_int_int, IQ2000_BUILTIN_LUC32L);
  def_builtin ("__builtin_luc64", void_ftype_int_int, IQ2000_BUILTIN_LUC64);
  def_builtin ("__builtin_luc64", void_ftype_int_int, IQ2000_BUILTIN_LUC64);
  def_builtin ("__builtin_luc64l", void_ftype_int_int, IQ2000_BUILTIN_LUC64L);
  def_builtin ("__builtin_luc64l", void_ftype_int_int, IQ2000_BUILTIN_LUC64L);
  def_builtin ("__builtin_luk", void_ftype_int_int, IQ2000_BUILTIN_LUK);
  def_builtin ("__builtin_luk", void_ftype_int_int, IQ2000_BUILTIN_LUK);
  def_builtin ("__builtin_lulck", void_ftype_int, IQ2000_BUILTIN_LULCK);
  def_builtin ("__builtin_lulck", void_ftype_int, IQ2000_BUILTIN_LULCK);
  def_builtin ("__builtin_lum32", void_ftype_int_int, IQ2000_BUILTIN_LUM32);
  def_builtin ("__builtin_lum32", void_ftype_int_int, IQ2000_BUILTIN_LUM32);
  def_builtin ("__builtin_lum32l", void_ftype_int_int, IQ2000_BUILTIN_LUM32L);
  def_builtin ("__builtin_lum32l", void_ftype_int_int, IQ2000_BUILTIN_LUM32L);
  def_builtin ("__builtin_lum64", void_ftype_int_int, IQ2000_BUILTIN_LUM64);
  def_builtin ("__builtin_lum64", void_ftype_int_int, IQ2000_BUILTIN_LUM64);
  def_builtin ("__builtin_lum64l", void_ftype_int_int, IQ2000_BUILTIN_LUM64L);
  def_builtin ("__builtin_lum64l", void_ftype_int_int, IQ2000_BUILTIN_LUM64L);
  def_builtin ("__builtin_lurl", void_ftype_int_int, IQ2000_BUILTIN_LURL);
  def_builtin ("__builtin_lurl", void_ftype_int_int, IQ2000_BUILTIN_LURL);
  def_builtin ("__builtin_mrgb", int_ftype_int_int_int, IQ2000_BUILTIN_MRGB);
  def_builtin ("__builtin_mrgb", int_ftype_int_int_int, IQ2000_BUILTIN_MRGB);
  def_builtin ("__builtin_srrdl", void_ftype_int, IQ2000_BUILTIN_SRRDL);
  def_builtin ("__builtin_srrdl", void_ftype_int, IQ2000_BUILTIN_SRRDL);
  def_builtin ("__builtin_srulck", void_ftype_int, IQ2000_BUILTIN_SRULCK);
  def_builtin ("__builtin_srulck", void_ftype_int, IQ2000_BUILTIN_SRULCK);
  def_builtin ("__builtin_srwru", void_ftype_int_int, IQ2000_BUILTIN_SRWRU);
  def_builtin ("__builtin_srwru", void_ftype_int_int, IQ2000_BUILTIN_SRWRU);
  def_builtin ("__builtin_trapqfl", void_ftype, IQ2000_BUILTIN_TRAPQFL);
  def_builtin ("__builtin_trapqfl", void_ftype, IQ2000_BUILTIN_TRAPQFL);
  def_builtin ("__builtin_trapqne", void_ftype, IQ2000_BUILTIN_TRAPQNE);
  def_builtin ("__builtin_trapqne", void_ftype, IQ2000_BUILTIN_TRAPQNE);
  def_builtin ("__builtin_traprel", void_ftype_int, IQ2000_BUILTIN_TRAPREL);
  def_builtin ("__builtin_traprel", void_ftype_int, IQ2000_BUILTIN_TRAPREL);
  def_builtin ("__builtin_wbu", void_ftype_int_int_int, IQ2000_BUILTIN_WBU);
  def_builtin ("__builtin_wbu", void_ftype_int_int_int, IQ2000_BUILTIN_WBU);
  def_builtin ("__builtin_syscall", void_ftype, IQ2000_BUILTIN_SYSCALL);
  def_builtin ("__builtin_syscall", void_ftype, IQ2000_BUILTIN_SYSCALL);
}
}
 
 
/* Builtin for ICODE having ARGCOUNT args in EXP where each arg
/* Builtin for ICODE having ARGCOUNT args in EXP where each arg
   has an rtx CODE.  */
   has an rtx CODE.  */
 
 
static rtx
static rtx
expand_one_builtin (enum insn_code icode, rtx target, tree exp,
expand_one_builtin (enum insn_code icode, rtx target, tree exp,
                    enum rtx_code *code, int argcount)
                    enum rtx_code *code, int argcount)
{
{
  rtx pat;
  rtx pat;
  tree arg [5];
  tree arg [5];
  rtx op [5];
  rtx op [5];
  enum machine_mode mode [5];
  enum machine_mode mode [5];
  int i;
  int i;
 
 
  mode[0] = insn_data[icode].operand[0].mode;
  mode[0] = insn_data[icode].operand[0].mode;
  for (i = 0; i < argcount; i++)
  for (i = 0; i < argcount; i++)
    {
    {
      arg[i] = CALL_EXPR_ARG (exp, i);
      arg[i] = CALL_EXPR_ARG (exp, i);
      op[i] = expand_expr (arg[i], NULL_RTX, VOIDmode, 0);
      op[i] = expand_expr (arg[i], NULL_RTX, VOIDmode, 0);
      mode[i] = insn_data[icode].operand[i].mode;
      mode[i] = insn_data[icode].operand[i].mode;
      if (code[i] == CONST_INT && GET_CODE (op[i]) != CONST_INT)
      if (code[i] == CONST_INT && GET_CODE (op[i]) != CONST_INT)
        error ("argument %qd is not a constant", i + 1);
        error ("argument %qd is not a constant", i + 1);
      if (code[i] == REG
      if (code[i] == REG
          && ! (*insn_data[icode].operand[i].predicate) (op[i], mode[i]))
          && ! (*insn_data[icode].operand[i].predicate) (op[i], mode[i]))
        op[i] = copy_to_mode_reg (mode[i], op[i]);
        op[i] = copy_to_mode_reg (mode[i], op[i]);
    }
    }
 
 
  if (insn_data[icode].operand[0].constraint[0] == '=')
  if (insn_data[icode].operand[0].constraint[0] == '=')
    {
    {
      if (target == 0
      if (target == 0
          || GET_MODE (target) != mode[0]
          || GET_MODE (target) != mode[0]
          || ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
          || ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
        target = gen_reg_rtx (mode[0]);
        target = gen_reg_rtx (mode[0]);
    }
    }
  else
  else
    target = 0;
    target = 0;
 
 
  switch (argcount)
  switch (argcount)
    {
    {
    case 0:
    case 0:
        pat = GEN_FCN (icode) (target);
        pat = GEN_FCN (icode) (target);
    case 1:
    case 1:
      if (target)
      if (target)
        pat = GEN_FCN (icode) (target, op[0]);
        pat = GEN_FCN (icode) (target, op[0]);
      else
      else
        pat = GEN_FCN (icode) (op[0]);
        pat = GEN_FCN (icode) (op[0]);
      break;
      break;
    case 2:
    case 2:
      if (target)
      if (target)
        pat = GEN_FCN (icode) (target, op[0], op[1]);
        pat = GEN_FCN (icode) (target, op[0], op[1]);
      else
      else
        pat = GEN_FCN (icode) (op[0], op[1]);
        pat = GEN_FCN (icode) (op[0], op[1]);
      break;
      break;
    case 3:
    case 3:
      if (target)
      if (target)
        pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
        pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
      else
      else
        pat = GEN_FCN (icode) (op[0], op[1], op[2]);
        pat = GEN_FCN (icode) (op[0], op[1], op[2]);
      break;
      break;
    case 4:
    case 4:
      if (target)
      if (target)
        pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
        pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
      else
      else
        pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
        pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
      break;
      break;
    default:
    default:
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  if (! pat)
  if (! pat)
    return 0;
    return 0;
  emit_insn (pat);
  emit_insn (pat);
  return target;
  return target;
}
}
 
 
/* Expand an expression EXP that calls a built-in function,
/* Expand an expression EXP that calls a built-in function,
   with result going to TARGET if that's convenient
   with result going to TARGET if that's convenient
   (and in mode MODE if that's convenient).
   (and in mode MODE if that's convenient).
   SUBTARGET may be used as the target for computing one of EXP's operands.
   SUBTARGET may be used as the target for computing one of EXP's operands.
   IGNORE is nonzero if the value is to be ignored.  */
   IGNORE is nonzero if the value is to be ignored.  */
 
 
static rtx
static rtx
iq2000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
iq2000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
                       enum machine_mode mode ATTRIBUTE_UNUSED,
                       enum machine_mode mode ATTRIBUTE_UNUSED,
                       int ignore ATTRIBUTE_UNUSED)
                       int ignore ATTRIBUTE_UNUSED)
{
{
  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
  int fcode = DECL_FUNCTION_CODE (fndecl);
  int fcode = DECL_FUNCTION_CODE (fndecl);
  enum rtx_code code [5];
  enum rtx_code code [5];
 
 
  code[0] = REG;
  code[0] = REG;
  code[1] = REG;
  code[1] = REG;
  code[2] = REG;
  code[2] = REG;
  code[3] = REG;
  code[3] = REG;
  code[4] = REG;
  code[4] = REG;
  switch (fcode)
  switch (fcode)
    {
    {
    default:
    default:
      break;
      break;
 
 
    case IQ2000_BUILTIN_ADO16:
    case IQ2000_BUILTIN_ADO16:
      return expand_one_builtin (CODE_FOR_ado16, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_ado16, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_RAM:
    case IQ2000_BUILTIN_RAM:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      code[2] = CONST_INT;
      code[2] = CONST_INT;
      code[3] = CONST_INT;
      code[3] = CONST_INT;
      return expand_one_builtin (CODE_FOR_ram, target, exp, code, 4);
      return expand_one_builtin (CODE_FOR_ram, target, exp, code, 4);
 
 
    case IQ2000_BUILTIN_CHKHDR:
    case IQ2000_BUILTIN_CHKHDR:
      return expand_one_builtin (CODE_FOR_chkhdr, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_chkhdr, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_PKRL:
    case IQ2000_BUILTIN_PKRL:
      return expand_one_builtin (CODE_FOR_pkrl, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_pkrl, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_CFC0:
    case IQ2000_BUILTIN_CFC0:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_cfc0, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_cfc0, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_CFC1:
    case IQ2000_BUILTIN_CFC1:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_cfc1, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_cfc1, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_CFC2:
    case IQ2000_BUILTIN_CFC2:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_cfc2, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_cfc2, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_CFC3:
    case IQ2000_BUILTIN_CFC3:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_cfc3, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_cfc3, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_CTC0:
    case IQ2000_BUILTIN_CTC0:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_ctc0, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_ctc0, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_CTC1:
    case IQ2000_BUILTIN_CTC1:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_ctc1, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_ctc1, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_CTC2:
    case IQ2000_BUILTIN_CTC2:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_ctc2, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_ctc2, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_CTC3:
    case IQ2000_BUILTIN_CTC3:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_ctc3, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_ctc3, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_MFC0:
    case IQ2000_BUILTIN_MFC0:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mfc0, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_mfc0, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_MFC1:
    case IQ2000_BUILTIN_MFC1:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mfc1, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_mfc1, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_MFC2:
    case IQ2000_BUILTIN_MFC2:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mfc2, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_mfc2, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_MFC3:
    case IQ2000_BUILTIN_MFC3:
      code[0] = CONST_INT;
      code[0] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mfc3, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_mfc3, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_MTC0:
    case IQ2000_BUILTIN_MTC0:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mtc0, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_mtc0, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_MTC1:
    case IQ2000_BUILTIN_MTC1:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mtc1, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_mtc1, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_MTC2:
    case IQ2000_BUILTIN_MTC2:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mtc2, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_mtc2, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_MTC3:
    case IQ2000_BUILTIN_MTC3:
      code[1] = CONST_INT;
      code[1] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mtc3, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_mtc3, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUR:
    case IQ2000_BUILTIN_LUR:
      return expand_one_builtin (CODE_FOR_lur, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_lur, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_RB:
    case IQ2000_BUILTIN_RB:
      return expand_one_builtin (CODE_FOR_rb, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_rb, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_RX:
    case IQ2000_BUILTIN_RX:
      return expand_one_builtin (CODE_FOR_rx, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_rx, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_SRRD:
    case IQ2000_BUILTIN_SRRD:
      return expand_one_builtin (CODE_FOR_srrd, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_srrd, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_SRWR:
    case IQ2000_BUILTIN_SRWR:
      return expand_one_builtin (CODE_FOR_srwr, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_srwr, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_WB:
    case IQ2000_BUILTIN_WB:
      return expand_one_builtin (CODE_FOR_wb, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_wb, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_WX:
    case IQ2000_BUILTIN_WX:
      return expand_one_builtin (CODE_FOR_wx, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_wx, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUC32L:
    case IQ2000_BUILTIN_LUC32L:
      return expand_one_builtin (CODE_FOR_luc32l, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_luc32l, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUC64:
    case IQ2000_BUILTIN_LUC64:
      return expand_one_builtin (CODE_FOR_luc64, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_luc64, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUC64L:
    case IQ2000_BUILTIN_LUC64L:
      return expand_one_builtin (CODE_FOR_luc64l, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_luc64l, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUK:
    case IQ2000_BUILTIN_LUK:
      return expand_one_builtin (CODE_FOR_luk, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_luk, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LULCK:
    case IQ2000_BUILTIN_LULCK:
      return expand_one_builtin (CODE_FOR_lulck, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_lulck, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_LUM32:
    case IQ2000_BUILTIN_LUM32:
      return expand_one_builtin (CODE_FOR_lum32, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_lum32, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUM32L:
    case IQ2000_BUILTIN_LUM32L:
      return expand_one_builtin (CODE_FOR_lum32l, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_lum32l, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUM64:
    case IQ2000_BUILTIN_LUM64:
      return expand_one_builtin (CODE_FOR_lum64, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_lum64, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LUM64L:
    case IQ2000_BUILTIN_LUM64L:
      return expand_one_builtin (CODE_FOR_lum64l, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_lum64l, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_LURL:
    case IQ2000_BUILTIN_LURL:
      return expand_one_builtin (CODE_FOR_lurl, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_lurl, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_MRGB:
    case IQ2000_BUILTIN_MRGB:
      code[2] = CONST_INT;
      code[2] = CONST_INT;
      return expand_one_builtin (CODE_FOR_mrgb, target, exp, code, 3);
      return expand_one_builtin (CODE_FOR_mrgb, target, exp, code, 3);
 
 
    case IQ2000_BUILTIN_SRRDL:
    case IQ2000_BUILTIN_SRRDL:
      return expand_one_builtin (CODE_FOR_srrdl, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_srrdl, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_SRULCK:
    case IQ2000_BUILTIN_SRULCK:
      return expand_one_builtin (CODE_FOR_srulck, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_srulck, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_SRWRU:
    case IQ2000_BUILTIN_SRWRU:
      return expand_one_builtin (CODE_FOR_srwru, target, exp, code, 2);
      return expand_one_builtin (CODE_FOR_srwru, target, exp, code, 2);
 
 
    case IQ2000_BUILTIN_TRAPQFL:
    case IQ2000_BUILTIN_TRAPQFL:
      return expand_one_builtin (CODE_FOR_trapqfl, target, exp, code, 0);
      return expand_one_builtin (CODE_FOR_trapqfl, target, exp, code, 0);
 
 
    case IQ2000_BUILTIN_TRAPQNE:
    case IQ2000_BUILTIN_TRAPQNE:
      return expand_one_builtin (CODE_FOR_trapqne, target, exp, code, 0);
      return expand_one_builtin (CODE_FOR_trapqne, target, exp, code, 0);
 
 
    case IQ2000_BUILTIN_TRAPREL:
    case IQ2000_BUILTIN_TRAPREL:
      return expand_one_builtin (CODE_FOR_traprel, target, exp, code, 1);
      return expand_one_builtin (CODE_FOR_traprel, target, exp, code, 1);
 
 
    case IQ2000_BUILTIN_WBU:
    case IQ2000_BUILTIN_WBU:
      return expand_one_builtin (CODE_FOR_wbu, target, exp, code, 3);
      return expand_one_builtin (CODE_FOR_wbu, target, exp, code, 3);
 
 
    case IQ2000_BUILTIN_SYSCALL:
    case IQ2000_BUILTIN_SYSCALL:
      return expand_one_builtin (CODE_FOR_syscall, target, exp, code, 0);
      return expand_one_builtin (CODE_FOR_syscall, target, exp, code, 0);
    }
    }
 
 
  return NULL_RTX;
  return NULL_RTX;
}
}


/* Worker function for TARGET_RETURN_IN_MEMORY.  */
/* Worker function for TARGET_RETURN_IN_MEMORY.  */
 
 
static bool
static bool
iq2000_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
iq2000_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
{
{
  return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
  return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
          || (int_size_in_bytes (type) == -1));
          || (int_size_in_bytes (type) == -1));
}
}
 
 
/* Worker function for TARGET_SETUP_INCOMING_VARARGS.  */
/* Worker function for TARGET_SETUP_INCOMING_VARARGS.  */
 
 
static void
static void
iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
iq2000_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               enum machine_mode mode ATTRIBUTE_UNUSED,
                               tree type ATTRIBUTE_UNUSED, int * pretend_size,
                               tree type ATTRIBUTE_UNUSED, int * pretend_size,
                               int no_rtl)
                               int no_rtl)
{
{
  unsigned int iq2000_off = ! cum->last_arg_fp;
  unsigned int iq2000_off = ! cum->last_arg_fp;
  unsigned int iq2000_fp_off = cum->last_arg_fp;
  unsigned int iq2000_fp_off = cum->last_arg_fp;
 
 
  if ((cum->arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off))
  if ((cum->arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off))
    {
    {
      int iq2000_save_gp_regs
      int iq2000_save_gp_regs
        = MAX_ARGS_IN_REGISTERS - cum->arg_words - iq2000_off;
        = MAX_ARGS_IN_REGISTERS - cum->arg_words - iq2000_off;
      int iq2000_save_fp_regs
      int iq2000_save_fp_regs
        = (MAX_ARGS_IN_REGISTERS - cum->fp_arg_words - iq2000_fp_off);
        = (MAX_ARGS_IN_REGISTERS - cum->fp_arg_words - iq2000_fp_off);
 
 
      if (iq2000_save_gp_regs < 0)
      if (iq2000_save_gp_regs < 0)
        iq2000_save_gp_regs = 0;
        iq2000_save_gp_regs = 0;
      if (iq2000_save_fp_regs < 0)
      if (iq2000_save_fp_regs < 0)
        iq2000_save_fp_regs = 0;
        iq2000_save_fp_regs = 0;
 
 
      *pretend_size = ((iq2000_save_gp_regs * UNITS_PER_WORD)
      *pretend_size = ((iq2000_save_gp_regs * UNITS_PER_WORD)
                      + (iq2000_save_fp_regs * UNITS_PER_FPREG));
                      + (iq2000_save_fp_regs * UNITS_PER_FPREG));
 
 
      if (! (no_rtl))
      if (! (no_rtl))
        {
        {
          if (cum->arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off)
          if (cum->arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off)
            {
            {
              rtx ptr, mem;
              rtx ptr, mem;
              ptr = plus_constant (virtual_incoming_args_rtx,
              ptr = plus_constant (virtual_incoming_args_rtx,
                                   - (iq2000_save_gp_regs
                                   - (iq2000_save_gp_regs
                                      * UNITS_PER_WORD));
                                      * UNITS_PER_WORD));
              mem = gen_rtx_MEM (BLKmode, ptr);
              mem = gen_rtx_MEM (BLKmode, ptr);
              move_block_from_reg
              move_block_from_reg
                (cum->arg_words + GP_ARG_FIRST + iq2000_off,
                (cum->arg_words + GP_ARG_FIRST + iq2000_off,
                 mem,
                 mem,
                 iq2000_save_gp_regs);
                 iq2000_save_gp_regs);
            }
            }
        }
        }
    }
    }
}
}


/* A C compound statement to output to stdio stream STREAM the
/* A C compound statement to output to stdio stream STREAM the
   assembler syntax for an instruction operand that is a memory
   assembler syntax for an instruction operand that is a memory
   reference whose address is ADDR.  ADDR is an RTL expression.  */
   reference whose address is ADDR.  ADDR is an RTL expression.  */
 
 
void
void
print_operand_address (FILE * file, rtx addr)
print_operand_address (FILE * file, rtx addr)
{
{
  if (!addr)
  if (!addr)
    error ("PRINT_OPERAND_ADDRESS, null pointer");
    error ("PRINT_OPERAND_ADDRESS, null pointer");
 
 
  else
  else
    switch (GET_CODE (addr))
    switch (GET_CODE (addr))
      {
      {
      case REG:
      case REG:
        if (REGNO (addr) == ARG_POINTER_REGNUM)
        if (REGNO (addr) == ARG_POINTER_REGNUM)
          abort_with_insn (addr, "Arg pointer not eliminated.");
          abort_with_insn (addr, "Arg pointer not eliminated.");
 
 
        fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
        fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
        break;
        break;
 
 
      case LO_SUM:
      case LO_SUM:
        {
        {
          rtx arg0 = XEXP (addr, 0);
          rtx arg0 = XEXP (addr, 0);
          rtx arg1 = XEXP (addr, 1);
          rtx arg1 = XEXP (addr, 1);
 
 
          if (GET_CODE (arg0) != REG)
          if (GET_CODE (arg0) != REG)
            abort_with_insn (addr,
            abort_with_insn (addr,
                             "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
                             "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
 
 
          fprintf (file, "%%lo(");
          fprintf (file, "%%lo(");
          print_operand_address (file, arg1);
          print_operand_address (file, arg1);
          fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
          fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
        }
        }
        break;
        break;
 
 
      case PLUS:
      case PLUS:
        {
        {
          rtx reg = 0;
          rtx reg = 0;
          rtx offset = 0;
          rtx offset = 0;
          rtx arg0 = XEXP (addr, 0);
          rtx arg0 = XEXP (addr, 0);
          rtx arg1 = XEXP (addr, 1);
          rtx arg1 = XEXP (addr, 1);
 
 
          if (GET_CODE (arg0) == REG)
          if (GET_CODE (arg0) == REG)
            {
            {
              reg = arg0;
              reg = arg0;
              offset = arg1;
              offset = arg1;
              if (GET_CODE (offset) == REG)
              if (GET_CODE (offset) == REG)
                abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
                abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
            }
            }
 
 
          else if (GET_CODE (arg1) == REG)
          else if (GET_CODE (arg1) == REG)
              reg = arg1, offset = arg0;
              reg = arg1, offset = arg0;
          else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
          else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
            {
            {
              output_addr_const (file, addr);
              output_addr_const (file, addr);
              break;
              break;
            }
            }
          else
          else
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
 
 
          if (! CONSTANT_P (offset))
          if (! CONSTANT_P (offset))
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
            abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
 
 
          if (REGNO (reg) == ARG_POINTER_REGNUM)
          if (REGNO (reg) == ARG_POINTER_REGNUM)
            abort_with_insn (addr, "Arg pointer not eliminated.");
            abort_with_insn (addr, "Arg pointer not eliminated.");
 
 
          output_addr_const (file, offset);
          output_addr_const (file, offset);
          fprintf (file, "(%s)", reg_names [REGNO (reg)]);
          fprintf (file, "(%s)", reg_names [REGNO (reg)]);
        }
        }
        break;
        break;
 
 
      case LABEL_REF:
      case LABEL_REF:
      case SYMBOL_REF:
      case SYMBOL_REF:
      case CONST_INT:
      case CONST_INT:
      case CONST:
      case CONST:
        output_addr_const (file, addr);
        output_addr_const (file, addr);
        if (GET_CODE (addr) == CONST_INT)
        if (GET_CODE (addr) == CONST_INT)
          fprintf (file, "(%s)", reg_names [0]);
          fprintf (file, "(%s)", reg_names [0]);
        break;
        break;
 
 
      default:
      default:
        abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
        abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
        break;
        break;
    }
    }
}
}


/* A C compound statement to output to stdio stream FILE the
/* A C compound statement to output to stdio stream FILE the
   assembler syntax for an instruction operand OP.
   assembler syntax for an instruction operand OP.
 
 
   LETTER is a value that can be used to specify one of several ways
   LETTER is a value that can be used to specify one of several ways
   of printing the operand.  It is used when identical operands
   of printing the operand.  It is used when identical operands
   must be printed differently depending on the context.  LETTER
   must be printed differently depending on the context.  LETTER
   comes from the `%' specification that was used to request
   comes from the `%' specification that was used to request
   printing of the operand.  If the specification was just `%DIGIT'
   printing of the operand.  If the specification was just `%DIGIT'
   then LETTER is 0; if the specification was `%LTR DIGIT' then LETTER
   then LETTER is 0; if the specification was `%LTR DIGIT' then LETTER
   is the ASCII code for LTR.
   is the ASCII code for LTR.
 
 
   If OP is a register, this macro should print the register's name.
   If OP is a register, this macro should print the register's name.
   The names can be found in an array `reg_names' whose type is
   The names can be found in an array `reg_names' whose type is
   `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
   `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
 
 
   When the machine description has a specification `%PUNCT' (a `%'
   When the machine description has a specification `%PUNCT' (a `%'
   followed by a punctuation character), this macro is called with
   followed by a punctuation character), this macro is called with
   a null pointer for X and the punctuation character for LETTER.
   a null pointer for X and the punctuation character for LETTER.
 
 
   The IQ2000 specific codes are:
   The IQ2000 specific codes are:
 
 
   'X'  X is CONST_INT, prints upper 16 bits in hexadecimal format = "0x%04x",
   'X'  X is CONST_INT, prints upper 16 bits in hexadecimal format = "0x%04x",
   'x'  X is CONST_INT, prints lower 16 bits in hexadecimal format = "0x%04x",
   'x'  X is CONST_INT, prints lower 16 bits in hexadecimal format = "0x%04x",
   'd'  output integer constant in decimal,
   'd'  output integer constant in decimal,
   'z'  if the operand is 0, use $0 instead of normal operand.
   'z'  if the operand is 0, use $0 instead of normal operand.
   'D'  print second part of double-word register or memory operand.
   'D'  print second part of double-word register or memory operand.
   'L'  print low-order register of double-word register operand.
   'L'  print low-order register of double-word register operand.
   'M'  print high-order register of double-word register operand.
   'M'  print high-order register of double-word register operand.
   'C'  print part of opcode for a branch condition.
   'C'  print part of opcode for a branch condition.
   'F'  print part of opcode for a floating-point branch condition.
   'F'  print part of opcode for a floating-point branch condition.
   'N'  print part of opcode for a branch condition, inverted.
   'N'  print part of opcode for a branch condition, inverted.
   'W'  print part of opcode for a floating-point branch condition, inverted.
   'W'  print part of opcode for a floating-point branch condition, inverted.
   'A'  Print part of opcode for a bit test condition.
   'A'  Print part of opcode for a bit test condition.
   'P'  Print label for a bit test.
   'P'  Print label for a bit test.
   'p'  Print log for a bit test.
   'p'  Print log for a bit test.
   'B'  print 'z' for EQ, 'n' for NE
   'B'  print 'z' for EQ, 'n' for NE
   'b'  print 'n' for EQ, 'z' for NE
   'b'  print 'n' for EQ, 'z' for NE
   'T'  print 'f' for EQ, 't' for NE
   'T'  print 'f' for EQ, 't' for NE
   't'  print 't' for EQ, 'f' for NE
   't'  print 't' for EQ, 'f' for NE
   'Z'  print register and a comma, but print nothing for $fcc0
   'Z'  print register and a comma, but print nothing for $fcc0
   '?'  Print 'l' if we are to use a branch likely instead of normal branch.
   '?'  Print 'l' if we are to use a branch likely instead of normal branch.
   '@'  Print the name of the assembler temporary register (at or $1).
   '@'  Print the name of the assembler temporary register (at or $1).
   '.'  Print the name of the register with a hard-wired zero (zero or $0).
   '.'  Print the name of the register with a hard-wired zero (zero or $0).
   '$'  Print the name of the stack pointer register (sp or $29).
   '$'  Print the name of the stack pointer register (sp or $29).
   '+'  Print the name of the gp register (gp or $28).  */
   '+'  Print the name of the gp register (gp or $28).  */
 
 
void
void
print_operand (FILE *file, rtx op, int letter)
print_operand (FILE *file, rtx op, int letter)
{
{
  enum rtx_code code;
  enum rtx_code code;
 
 
  if (PRINT_OPERAND_PUNCT_VALID_P (letter))
  if (PRINT_OPERAND_PUNCT_VALID_P (letter))
    {
    {
      switch (letter)
      switch (letter)
        {
        {
        case '?':
        case '?':
          if (iq2000_branch_likely)
          if (iq2000_branch_likely)
            putc ('l', file);
            putc ('l', file);
          break;
          break;
 
 
        case '@':
        case '@':
          fputs (reg_names [GP_REG_FIRST + 1], file);
          fputs (reg_names [GP_REG_FIRST + 1], file);
          break;
          break;
 
 
        case '.':
        case '.':
          fputs (reg_names [GP_REG_FIRST + 0], file);
          fputs (reg_names [GP_REG_FIRST + 0], file);
          break;
          break;
 
 
        case '$':
        case '$':
          fputs (reg_names[STACK_POINTER_REGNUM], file);
          fputs (reg_names[STACK_POINTER_REGNUM], file);
          break;
          break;
 
 
        case '+':
        case '+':
          fputs (reg_names[GP_REG_FIRST + 28], file);
          fputs (reg_names[GP_REG_FIRST + 28], file);
          break;
          break;
 
 
        default:
        default:
          error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
          error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
          break;
          break;
        }
        }
 
 
      return;
      return;
    }
    }
 
 
  if (! op)
  if (! op)
    {
    {
      error ("PRINT_OPERAND null pointer");
      error ("PRINT_OPERAND null pointer");
      return;
      return;
    }
    }
 
 
  code = GET_CODE (op);
  code = GET_CODE (op);
 
 
  if (code == SIGN_EXTEND)
  if (code == SIGN_EXTEND)
    op = XEXP (op, 0), code = GET_CODE (op);
    op = XEXP (op, 0), code = GET_CODE (op);
 
 
  if (letter == 'C')
  if (letter == 'C')
    switch (code)
    switch (code)
      {
      {
      case EQ:  fputs ("eq",  file); break;
      case EQ:  fputs ("eq",  file); break;
      case NE:  fputs ("ne",  file); break;
      case NE:  fputs ("ne",  file); break;
      case GT:  fputs ("gt",  file); break;
      case GT:  fputs ("gt",  file); break;
      case GE:  fputs ("ge",  file); break;
      case GE:  fputs ("ge",  file); break;
      case LT:  fputs ("lt",  file); break;
      case LT:  fputs ("lt",  file); break;
      case LE:  fputs ("le",  file); break;
      case LE:  fputs ("le",  file); break;
      case GTU: fputs ("ne", file); break;
      case GTU: fputs ("ne", file); break;
      case GEU: fputs ("geu", file); break;
      case GEU: fputs ("geu", file); break;
      case LTU: fputs ("ltu", file); break;
      case LTU: fputs ("ltu", file); break;
      case LEU: fputs ("eq", file); break;
      case LEU: fputs ("eq", file); break;
      default:
      default:
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
      }
      }
 
 
  else if (letter == 'N')
  else if (letter == 'N')
    switch (code)
    switch (code)
      {
      {
      case EQ:  fputs ("ne",  file); break;
      case EQ:  fputs ("ne",  file); break;
      case NE:  fputs ("eq",  file); break;
      case NE:  fputs ("eq",  file); break;
      case GT:  fputs ("le",  file); break;
      case GT:  fputs ("le",  file); break;
      case GE:  fputs ("lt",  file); break;
      case GE:  fputs ("lt",  file); break;
      case LT:  fputs ("ge",  file); break;
      case LT:  fputs ("ge",  file); break;
      case LE:  fputs ("gt",  file); break;
      case LE:  fputs ("gt",  file); break;
      case GTU: fputs ("leu", file); break;
      case GTU: fputs ("leu", file); break;
      case GEU: fputs ("ltu", file); break;
      case GEU: fputs ("ltu", file); break;
      case LTU: fputs ("geu", file); break;
      case LTU: fputs ("geu", file); break;
      case LEU: fputs ("gtu", file); break;
      case LEU: fputs ("gtu", file); break;
      default:
      default:
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
      }
      }
 
 
  else if (letter == 'F')
  else if (letter == 'F')
    switch (code)
    switch (code)
      {
      {
      case EQ: fputs ("c1f", file); break;
      case EQ: fputs ("c1f", file); break;
      case NE: fputs ("c1t", file); break;
      case NE: fputs ("c1t", file); break;
      default:
      default:
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
      }
      }
 
 
  else if (letter == 'W')
  else if (letter == 'W')
    switch (code)
    switch (code)
      {
      {
      case EQ: fputs ("c1t", file); break;
      case EQ: fputs ("c1t", file); break;
      case NE: fputs ("c1f", file); break;
      case NE: fputs ("c1f", file); break;
      default:
      default:
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
        abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
      }
      }
 
 
  else if (letter == 'A')
  else if (letter == 'A')
    fputs (code == LABEL_REF ? "i" : "in", file);
    fputs (code == LABEL_REF ? "i" : "in", file);
 
 
  else if (letter == 'P')
  else if (letter == 'P')
    {
    {
      if (code == LABEL_REF)
      if (code == LABEL_REF)
        output_addr_const (file, op);
        output_addr_const (file, op);
      else if (code != PC)
      else if (code != PC)
        output_operand_lossage ("invalid %%P operand");
        output_operand_lossage ("invalid %%P operand");
    }
    }
 
 
  else if (letter == 'p')
  else if (letter == 'p')
    {
    {
      int value;
      int value;
      if (code != CONST_INT
      if (code != CONST_INT
          || (value = exact_log2 (INTVAL (op))) < 0)
          || (value = exact_log2 (INTVAL (op))) < 0)
        output_operand_lossage ("invalid %%p value");
        output_operand_lossage ("invalid %%p value");
      fprintf (file, "%d", value);
      fprintf (file, "%d", value);
    }
    }
 
 
  else if (letter == 'Z')
  else if (letter == 'Z')
    {
    {
      gcc_unreachable ();
      gcc_unreachable ();
    }
    }
 
 
  else if (code == REG || code == SUBREG)
  else if (code == REG || code == SUBREG)
    {
    {
      int regnum;
      int regnum;
 
 
      if (code == REG)
      if (code == REG)
        regnum = REGNO (op);
        regnum = REGNO (op);
      else
      else
        regnum = true_regnum (op);
        regnum = true_regnum (op);
 
 
      if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
      if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
          || (letter == 'L' && WORDS_BIG_ENDIAN)
          || (letter == 'L' && WORDS_BIG_ENDIAN)
          || letter == 'D')
          || letter == 'D')
        regnum++;
        regnum++;
 
 
      fprintf (file, "%s", reg_names[regnum]);
      fprintf (file, "%s", reg_names[regnum]);
    }
    }
 
 
  else if (code == MEM)
  else if (code == MEM)
    {
    {
      if (letter == 'D')
      if (letter == 'D')
        output_address (plus_constant (XEXP (op, 0), 4));
        output_address (plus_constant (XEXP (op, 0), 4));
      else
      else
        output_address (XEXP (op, 0));
        output_address (XEXP (op, 0));
    }
    }
 
 
  else if (code == CONST_DOUBLE
  else if (code == CONST_DOUBLE
           && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
           && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
    {
    {
      char s[60];
      char s[60];
 
 
      real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
      real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
      fputs (s, file);
      fputs (s, file);
    }
    }
 
 
  else if (letter == 'x' && GET_CODE (op) == CONST_INT)
  else if (letter == 'x' && GET_CODE (op) == CONST_INT)
    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
 
 
  else if (letter == 'X' && GET_CODE(op) == CONST_INT)
  else if (letter == 'X' && GET_CODE(op) == CONST_INT)
    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & (INTVAL (op) >> 16));
    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & (INTVAL (op) >> 16));
 
 
  else if (letter == 'd' && GET_CODE(op) == CONST_INT)
  else if (letter == 'd' && GET_CODE(op) == CONST_INT)
    fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
    fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
 
 
  else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
  else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
    fputs (reg_names[GP_REG_FIRST], file);
    fputs (reg_names[GP_REG_FIRST], file);
 
 
  else if (letter == 'd' || letter == 'x' || letter == 'X')
  else if (letter == 'd' || letter == 'x' || letter == 'X')
    output_operand_lossage ("invalid use of %%d, %%x, or %%X");
    output_operand_lossage ("invalid use of %%d, %%x, or %%X");
 
 
  else if (letter == 'B')
  else if (letter == 'B')
    fputs (code == EQ ? "z" : "n", file);
    fputs (code == EQ ? "z" : "n", file);
  else if (letter == 'b')
  else if (letter == 'b')
    fputs (code == EQ ? "n" : "z", file);
    fputs (code == EQ ? "n" : "z", file);
  else if (letter == 'T')
  else if (letter == 'T')
    fputs (code == EQ ? "f" : "t", file);
    fputs (code == EQ ? "f" : "t", file);
  else if (letter == 't')
  else if (letter == 't')
    fputs (code == EQ ? "t" : "f", file);
    fputs (code == EQ ? "t" : "f", file);
 
 
  else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
  else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
    {
    {
      print_operand (file, XEXP (op, 0), letter);
      print_operand (file, XEXP (op, 0), letter);
    }
    }
 
 
  else
  else
    output_addr_const (file, op);
    output_addr_const (file, op);
}
}
 
 
 
 
/* For the IQ2000, transform:
/* For the IQ2000, transform:
 
 
        memory(X + <large int>)
        memory(X + <large int>)
   into:
   into:
        Y = <large int> & ~0x7fff;
        Y = <large int> & ~0x7fff;
        Z = X + Y
        Z = X + Y
        memory (Z + (<large int> & 0x7fff));
        memory (Z + (<large int> & 0x7fff));
*/
*/
 
 
rtx
rtx
iq2000_legitimize_address (rtx xinsn, rtx old_x ATTRIBUTE_UNUSED,
iq2000_legitimize_address (rtx xinsn, rtx old_x ATTRIBUTE_UNUSED,
                           enum machine_mode mode)
                           enum machine_mode mode)
{
{
  if (TARGET_DEBUG_B_MODE)
  if (TARGET_DEBUG_B_MODE)
    {
    {
      GO_PRINTF ("\n========== LEGITIMIZE_ADDRESS\n");
      GO_PRINTF ("\n========== LEGITIMIZE_ADDRESS\n");
      GO_DEBUG_RTX (xinsn);
      GO_DEBUG_RTX (xinsn);
    }
    }
 
 
  if (iq2000_check_split (xinsn, mode))
  if (iq2000_check_split (xinsn, mode))
    {
    {
      return gen_rtx_LO_SUM (Pmode,
      return gen_rtx_LO_SUM (Pmode,
                             copy_to_mode_reg (Pmode,
                             copy_to_mode_reg (Pmode,
                                               gen_rtx_HIGH (Pmode, xinsn)),
                                               gen_rtx_HIGH (Pmode, xinsn)),
                             xinsn);
                             xinsn);
    }
    }
 
 
  if (GET_CODE (xinsn) == PLUS)
  if (GET_CODE (xinsn) == PLUS)
    {
    {
      rtx xplus0 = XEXP (xinsn, 0);
      rtx xplus0 = XEXP (xinsn, 0);
      rtx xplus1 = XEXP (xinsn, 1);
      rtx xplus1 = XEXP (xinsn, 1);
      enum rtx_code code0 = GET_CODE (xplus0);
      enum rtx_code code0 = GET_CODE (xplus0);
      enum rtx_code code1 = GET_CODE (xplus1);
      enum rtx_code code1 = GET_CODE (xplus1);
 
 
      if (code0 != REG && code1 == REG)
      if (code0 != REG && code1 == REG)
        {
        {
          xplus0 = XEXP (xinsn, 1);
          xplus0 = XEXP (xinsn, 1);
          xplus1 = XEXP (xinsn, 0);
          xplus1 = XEXP (xinsn, 0);
          code0 = GET_CODE (xplus0);
          code0 = GET_CODE (xplus0);
          code1 = GET_CODE (xplus1);
          code1 = GET_CODE (xplus1);
        }
        }
 
 
      if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, mode)
      if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, mode)
          && code1 == CONST_INT && !SMALL_INT (xplus1))
          && code1 == CONST_INT && !SMALL_INT (xplus1))
        {
        {
          rtx int_reg = gen_reg_rtx (Pmode);
          rtx int_reg = gen_reg_rtx (Pmode);
          rtx ptr_reg = gen_reg_rtx (Pmode);
          rtx ptr_reg = gen_reg_rtx (Pmode);
 
 
          emit_move_insn (int_reg,
          emit_move_insn (int_reg,
                          GEN_INT (INTVAL (xplus1) & ~ 0x7fff));
                          GEN_INT (INTVAL (xplus1) & ~ 0x7fff));
 
 
          emit_insn (gen_rtx_SET (VOIDmode,
          emit_insn (gen_rtx_SET (VOIDmode,
                                  ptr_reg,
                                  ptr_reg,
                                  gen_rtx_PLUS (Pmode, xplus0, int_reg)));
                                  gen_rtx_PLUS (Pmode, xplus0, int_reg)));
 
 
          return plus_constant (ptr_reg, INTVAL (xplus1) & 0x7fff);
          return plus_constant (ptr_reg, INTVAL (xplus1) & 0x7fff);
        }
        }
    }
    }
 
 
  if (TARGET_DEBUG_B_MODE)
  if (TARGET_DEBUG_B_MODE)
    GO_PRINTF ("LEGITIMIZE_ADDRESS could not fix.\n");
    GO_PRINTF ("LEGITIMIZE_ADDRESS could not fix.\n");
 
 
  return xinsn;
  return xinsn;
}
}
 
 
 
 
static bool
static bool
iq2000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int * total,
iq2000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED, int * total,
                  bool speed ATTRIBUTE_UNUSED)
                  bool speed ATTRIBUTE_UNUSED)
{
{
  enum machine_mode mode = GET_MODE (x);
  enum machine_mode mode = GET_MODE (x);
 
 
  switch (code)
  switch (code)
    {
    {
    case MEM:
    case MEM:
      {
      {
        int num_words = (GET_MODE_SIZE (mode) > UNITS_PER_WORD) ? 2 : 1;
        int num_words = (GET_MODE_SIZE (mode) > UNITS_PER_WORD) ? 2 : 1;
 
 
        if (simple_memory_operand (x, mode))
        if (simple_memory_operand (x, mode))
          return COSTS_N_INSNS (num_words);
          return COSTS_N_INSNS (num_words);
 
 
        * total = COSTS_N_INSNS (2 * num_words);
        * total = COSTS_N_INSNS (2 * num_words);
        break;
        break;
      }
      }
 
 
    case FFS:
    case FFS:
      * total = COSTS_N_INSNS (6);
      * total = COSTS_N_INSNS (6);
      break;
      break;
 
 
    case AND:
    case AND:
    case IOR:
    case IOR:
    case XOR:
    case XOR:
    case NOT:
    case NOT:
      * total = COSTS_N_INSNS (mode == DImode ? 2 : 1);
      * total = COSTS_N_INSNS (mode == DImode ? 2 : 1);
      break;
      break;
 
 
    case ASHIFT:
    case ASHIFT:
    case ASHIFTRT:
    case ASHIFTRT:
    case LSHIFTRT:
    case LSHIFTRT:
      if (mode == DImode)
      if (mode == DImode)
        * total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) ? 4 : 12);
        * total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT) ? 4 : 12);
      else
      else
        * total = COSTS_N_INSNS (1);
        * total = COSTS_N_INSNS (1);
    break;
    break;
 
 
    case ABS:
    case ABS:
      if (mode == SFmode || mode == DFmode)
      if (mode == SFmode || mode == DFmode)
        * total = COSTS_N_INSNS (1);
        * total = COSTS_N_INSNS (1);
      else
      else
        * total = COSTS_N_INSNS (4);
        * total = COSTS_N_INSNS (4);
      break;
      break;
 
 
    case PLUS:
    case PLUS:
    case MINUS:
    case MINUS:
      if (mode == SFmode || mode == DFmode)
      if (mode == SFmode || mode == DFmode)
        * total = COSTS_N_INSNS (6);
        * total = COSTS_N_INSNS (6);
      else if (mode == DImode)
      else if (mode == DImode)
        * total = COSTS_N_INSNS (4);
        * total = COSTS_N_INSNS (4);
      else
      else
        * total = COSTS_N_INSNS (1);
        * total = COSTS_N_INSNS (1);
      break;
      break;
 
 
    case NEG:
    case NEG:
      * total = (mode == DImode) ? 4 : 1;
      * total = (mode == DImode) ? 4 : 1;
      break;
      break;
 
 
    case MULT:
    case MULT:
      if (mode == SFmode)
      if (mode == SFmode)
        * total = COSTS_N_INSNS (7);
        * total = COSTS_N_INSNS (7);
      else if (mode == DFmode)
      else if (mode == DFmode)
        * total = COSTS_N_INSNS (8);
        * total = COSTS_N_INSNS (8);
      else
      else
        * total = COSTS_N_INSNS (10);
        * total = COSTS_N_INSNS (10);
      break;
      break;
 
 
    case DIV:
    case DIV:
    case MOD:
    case MOD:
      if (mode == SFmode)
      if (mode == SFmode)
        * total = COSTS_N_INSNS (23);
        * total = COSTS_N_INSNS (23);
      else if (mode == DFmode)
      else if (mode == DFmode)
        * total = COSTS_N_INSNS (36);
        * total = COSTS_N_INSNS (36);
      else
      else
        * total = COSTS_N_INSNS (69);
        * total = COSTS_N_INSNS (69);
      break;
      break;
 
 
    case UDIV:
    case UDIV:
    case UMOD:
    case UMOD:
      * total = COSTS_N_INSNS (69);
      * total = COSTS_N_INSNS (69);
      break;
      break;
 
 
    case SIGN_EXTEND:
    case SIGN_EXTEND:
      * total = COSTS_N_INSNS (2);
      * total = COSTS_N_INSNS (2);
      break;
      break;
 
 
    case ZERO_EXTEND:
    case ZERO_EXTEND:
      * total = COSTS_N_INSNS (1);
      * total = COSTS_N_INSNS (1);
      break;
      break;
 
 
    case CONST_INT:
    case CONST_INT:
      * total = 0;
      * total = 0;
      break;
      break;
 
 
    case LABEL_REF:
    case LABEL_REF:
      * total = COSTS_N_INSNS (2);
      * total = COSTS_N_INSNS (2);
      break;
      break;
 
 
    case CONST:
    case CONST:
      {
      {
        rtx offset = const0_rtx;
        rtx offset = const0_rtx;
        rtx symref = eliminate_constant_term (XEXP (x, 0), & offset);
        rtx symref = eliminate_constant_term (XEXP (x, 0), & offset);
 
 
        if (GET_CODE (symref) == LABEL_REF)
        if (GET_CODE (symref) == LABEL_REF)
          * total = COSTS_N_INSNS (2);
          * total = COSTS_N_INSNS (2);
        else if (GET_CODE (symref) != SYMBOL_REF)
        else if (GET_CODE (symref) != SYMBOL_REF)
          * total = COSTS_N_INSNS (4);
          * total = COSTS_N_INSNS (4);
        /* Let's be paranoid....  */
        /* Let's be paranoid....  */
        else if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)
        else if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)
          * total = COSTS_N_INSNS (2);
          * total = COSTS_N_INSNS (2);
        else
        else
          * total = COSTS_N_INSNS (SYMBOL_REF_FLAG (symref) ? 1 : 2);
          * total = COSTS_N_INSNS (SYMBOL_REF_FLAG (symref) ? 1 : 2);
        break;
        break;
      }
      }
 
 
    case SYMBOL_REF:
    case SYMBOL_REF:
      * total = COSTS_N_INSNS (SYMBOL_REF_FLAG (x) ? 1 : 2);
      * total = COSTS_N_INSNS (SYMBOL_REF_FLAG (x) ? 1 : 2);
      break;
      break;
 
 
    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 (  (high == CONST0_RTX (GET_MODE (high))
        * total = COSTS_N_INSNS (  (high == CONST0_RTX (GET_MODE (high))
                                  || low == CONST0_RTX (GET_MODE (low)))
                                  || low == CONST0_RTX (GET_MODE (low)))
                                   ? 2 : 4);
                                   ? 2 : 4);
        break;
        break;
      }
      }
 
 
    default:
    default:
      return false;
      return false;
    }
    }
  return true;
  return true;
}
}
 
 
/* Worker for TARGET_ASM_TRAMPOLINE_TEMPLATE.  */
/* Worker for TARGET_ASM_TRAMPOLINE_TEMPLATE.  */
 
 
static void
static void
iq2000_asm_trampoline_template (FILE *f)
iq2000_asm_trampoline_template (FILE *f)
{
{
  fprintf (f, "\t.word\t0x03e00821\t\t# move   $1,$31\n");
  fprintf (f, "\t.word\t0x03e00821\t\t# move   $1,$31\n");
  fprintf (f, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n");
  fprintf (f, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n");
  fprintf (f, "\t.word\t0x00000000\t\t# nop\n");
  fprintf (f, "\t.word\t0x00000000\t\t# nop\n");
  if (Pmode == DImode)
  if (Pmode == DImode)
    {
    {
      fprintf (f, "\t.word\t0xdfe30014\t\t# ld     $3,20($31)\n");
      fprintf (f, "\t.word\t0xdfe30014\t\t# ld     $3,20($31)\n");
      fprintf (f, "\t.word\t0xdfe2001c\t\t# ld     $2,28($31)\n");
      fprintf (f, "\t.word\t0xdfe2001c\t\t# ld     $2,28($31)\n");
    }
    }
  else
  else
    {
    {
      fprintf (f, "\t.word\t0x8fe30014\t\t# lw     $3,20($31)\n");
      fprintf (f, "\t.word\t0x8fe30014\t\t# lw     $3,20($31)\n");
      fprintf (f, "\t.word\t0x8fe20018\t\t# lw     $2,24($31)\n");
      fprintf (f, "\t.word\t0x8fe20018\t\t# lw     $2,24($31)\n");
    }
    }
  fprintf (f, "\t.word\t0x0060c821\t\t# move   $25,$3 (abicalls)\n");
  fprintf (f, "\t.word\t0x0060c821\t\t# move   $25,$3 (abicalls)\n");
  fprintf (f, "\t.word\t0x00600008\t\t# jr     $3\n");
  fprintf (f, "\t.word\t0x00600008\t\t# jr     $3\n");
  fprintf (f, "\t.word\t0x0020f821\t\t# move   $31,$1\n");
  fprintf (f, "\t.word\t0x0020f821\t\t# move   $31,$1\n");
  fprintf (f, "\t.word\t0x00000000\t\t# <function address>\n");
  fprintf (f, "\t.word\t0x00000000\t\t# <function address>\n");
  fprintf (f, "\t.word\t0x00000000\t\t# <static chain value>\n");
  fprintf (f, "\t.word\t0x00000000\t\t# <static chain value>\n");
}
}
 
 
/* Worker for TARGET_TRAMPOLINE_INIT.  */
/* Worker for TARGET_TRAMPOLINE_INIT.  */
 
 
static void
static void
iq2000_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
iq2000_trampoline_init (rtx m_tramp, tree fndecl, rtx chain_value)
{
{
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
  rtx mem;
  rtx mem;
 
 
  emit_block_move (m_tramp, assemble_trampoline_template (),
  emit_block_move (m_tramp, assemble_trampoline_template (),
                   GEN_INT (TRAMPOLINE_CODE_SIZE), BLOCK_OP_NORMAL);
                   GEN_INT (TRAMPOLINE_CODE_SIZE), BLOCK_OP_NORMAL);
 
 
  mem = adjust_address (m_tramp, Pmode, TRAMPOLINE_CODE_SIZE);
  mem = adjust_address (m_tramp, Pmode, TRAMPOLINE_CODE_SIZE);
  emit_move_insn (mem, fnaddr);
  emit_move_insn (mem, fnaddr);
  mem = adjust_address (m_tramp, Pmode,
  mem = adjust_address (m_tramp, Pmode,
                        TRAMPOLINE_CODE_SIZE + GET_MODE_SIZE (Pmode));
                        TRAMPOLINE_CODE_SIZE + GET_MODE_SIZE (Pmode));
  emit_move_insn (mem, chain_value);
  emit_move_insn (mem, chain_value);
}
}
 
 
#include "gt-iq2000.h"
#include "gt-iq2000.h"
 
 

powered by: WebSVN 2.1.0

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