]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
iq2000: New port.
authorStan Cox <scox@redhat.com>
Fri, 8 Aug 2003 11:30:25 +0000 (11:30 +0000)
committerStan Cox <scox@gcc.gnu.org>
Fri, 8 Aug 2003 11:30:25 +0000 (11:30 +0000)
* config/iq2000: New port.
* config.gcc (iq2000-*-elf): Added.
* doc/install.texi (Specific):  Add iq2000 description.

From-SVN: r70245

gcc/ChangeLog
gcc/config.gcc
gcc/config/iq2000/abi [new file with mode: 0644]
gcc/config/iq2000/iq2000-protos.h [new file with mode: 0644]
gcc/config/iq2000/iq2000.c [new file with mode: 0644]
gcc/config/iq2000/iq2000.h [new file with mode: 0644]
gcc/config/iq2000/iq2000.md [new file with mode: 0644]
gcc/config/iq2000/lib2extra-funcs.c [new file with mode: 0644]
gcc/config/iq2000/t-iq2000 [new file with mode: 0644]
gcc/config/iq2000/xm-iq2000.h [new file with mode: 0644]
gcc/doc/install.texi

index a8f1f91c5a557d849b38929db7e29e4506de44f8..0645031e413216a71a84fa303b5ed0491cbe0132 100644 (file)
@@ -1,3 +1,9 @@
+2003-08-08  Stan Cox  <scox@redhat.com>
+
+       * config/iq2000: New port.
+       * config.gcc (iq2000-*-elf): Added.
+       * doc/install.texi (Specific):  Add iq2000 description.
+
 2003-08-08  Andreas Schwab  <schwab@suse.de>
 
        * configure.in (gcc_cv_as_ia64_ltoffx_ldxmov_relocs): Fix quoting
index 15ff2ce6923e0358a9b90643bdced4548d0da18f..0cb89384ce83faa21274bf81ae34e6dfe27a2a63 100644 (file)
@@ -1296,6 +1296,13 @@ ia64*-*-hpux*)
 ip2k-*-elf)
        tm_file="elfos.h ${tm_file}"
        ;;
+iq2000*-*-elf*)
+        tm_file="iq2000/iq2000.h"
+        tmake_file=iq2000/t-iq2000
+        out_file=iq2000/iq2000.c
+        xm_file=iq2000/xm-iq2000.h
+        md_file=iq2000/iq2000.md
+        ;;
 m32r-*-elf*)
        tm_file="dbxelf.h elfos.h svr4.h ${tm_file}"
        extra_parts="crtinit.o crtfini.o"
diff --git a/gcc/config/iq2000/abi b/gcc/config/iq2000/abi
new file mode 100644 (file)
index 0000000..fa49a82
--- /dev/null
@@ -0,0 +1,232 @@
+                       IQ2000 ABI
+                       =========
+
+Sizes and alignments
+--------------------
+
+       Type            Size (bytes)    Alignment (bytes)
+
+       char            1               1
+       short           2               2
+       int             4               4
+       unsigned        4               4
+       long            4               4 
+       long long       8               8
+       float           4               4
+       double          8               8
+       pointers        4               4 
+
+* alignment within aggregates (structs and unions) is as above, with
+  padding added if needed
+* aggregates have alignment equal to that of their most aligned
+  member
+* aggregates have sizes which are a multiple of their alignment
+
+
+Floating point
+--------------
+
+All emulated using IEEE floating point conventions.
+
+Registers
+----------------
+
+%0             always zero
+%1             call clobbered
+%2             return value
+%3             return value
+%4             argument register 1
+%5             argument register 2
+%6             argument register 3
+%7             argument register 4
+%8             argument register 5
+%9             argument register 6
+%10            argument register 7
+%11            argument register 8
+%12            call clobbered
+%13            call clobbered
+%14            call clobbered
+%15            call clobbered
+%16            call saved
+%17            call saved
+%18            call saved
+%19            call saved
+%20            call saved
+%21            call saved
+%22            call saved
+%23            call saved
+%24            call clobbered
+%25            call clobbered
+%26            reserved
+%27            frame ptr
+%28            global ptr
+%29            stack ptr
+%30            reserved
+%31            return address
+
+Stack alignment                8 bytes
+
+Structures passed      <= 32 bits as values, else as pointers
+
+The IQ2000 Stack
+---------------
+
+Space is allocated as needed in the stack frame for the following at compile
+time:
+
+* Outgoing parameters beyond the eighth
+
+* All automatic arrays, automatic data aggregates, automatic
+  scalars which must be addressable, and automatic scalars for
+  which there is no room in registers 
+
+* Compiler-generated temporary values (typically when there are
+  too many for the compiler to keep them all in registers) 
+
+Space can be allocated dynamically (at runtime) in the stack frame for the
+following:
+
+* Memory allocated using the alloca() function of the C library
+
+Addressable automatic variables on the stack are addressed with positive
+offsets relative to %27; dynamically allocated space is addressed with positive
+offsets from the pointer returned by alloca().
+
+Stack Frame
+-----------
+
+        +-----------------------+
+       |    Caller memory args |
+        +-----------------------+ <-sp
+       |    Return address     |
+       +-----------------------+
+       |    Previous FP        |
+       +-----------------------+
+       |    Saved Registers    |
+       +-----------------------+
+       |        ...            |
+       +-----------------------+
+       |    Local Variables    |
+       +-----------------------+ <-fp
+       |    Alloca             |
+       +-----------------------+
+       |        ...            |
+       +-----------------------+
+       |   Parameter Word 2    |
+       +-----------------------+
+       |   Parameter Word 1    |
+       +-----------------------+ <-sp
+
+
+Parameter Assignment to Registers
+---------------------------------
+
+Consider the parameters in a function call as ordered from left (first
+parameter) to right.  GR contains the number of the next available
+general-purpose register.  STARG is the address of the next available stack
+parameter word.
+
+INITIALIZE:
+       Set GR=r4 and STARG to point to parameter word 1.
+
+SCAN:
+       If there are no more parameters, terminate.
+       Otherwise, select one of the following depending on the type
+       of the next parameter:
+
+    SIMPLE ARG:
+
+       A SIMPLE ARG is one of the following:
+
+       * One of the simple integer types which will fit into a
+         general-purpose register,
+       * A pointer to an object of any type,
+       * A struct or union small enough to fit in a register (<= 32 bits)
+       * A larger struct or union, which shall be treated as a
+         pointer to the object or to a copy of the object.
+         (See below for when copies are made.)
+
+       If GR > r11, go to STACK.  Otherwise, load the parameter value into
+       general-purpose register GR and advance GR to the next general-purpose
+       register.  Values shorter than the register size are sign-extended or
+       zero-extended depending on whether they are signed or unsigned.  Then
+       go to SCAN.
+
+    DOUBLE or LONG LONG
+
+       If GR > r10, go to STACK.  Otherwise, if GR is odd, advance GR to the
+       next register.  Load the 64-bit long long or double value into register
+       pair GR and GR+1.  Advance GR to GR+2 and go to SCAN.
+
+    STACK:
+
+       Parameters not otherwise handled above are passed in the parameter
+       words of the caller's stack frame.  SIMPLE ARGs, as defined above, are
+       considered to have size and alignment equal to the size of a
+       general-purpose register, with simple argument types shorter than this
+       sign- or zero-extended to this width.  Round STARG up to a multiple of
+       the alignment requirement of the parameter and copy the argument
+       byte-for-byte into STARG, STARG+1, ...  STARG+size-1.  Set STARG to
+       STARG+size and go to SCAN.
+
+
+Structure passing
+-----------------
+
+As noted above, code which passes structures and unions by value is implemented
+specially.  (In this section, "struct" will refer to structs and unions
+inclusively.)  Structs small enough to fit in a register are passed by value in
+a single register or in a stack frame slot the size of a register.  Structs
+containing a single double or long long component are passed by value in two
+registers or in a stack frame slot the size of two registers.  Other structs
+are handled by passing the address of the structure.  In this case, a copy of
+the structure will be made if necessary in order to preserve the pass-by-value
+semantics.
+
+Copies of large structs are made under the following rules:
+
+                       ANSI mode                       K&R Mode
+                       ---------                       --------
+Normal param           Callee copies if needed         Caller copies
+Varargs (...) param    Caller copies                   Caller copies
+
+In the case of normal (non-varargs) large-struct parameters in ANSI mode, the
+callee is responsible for producing the same effect as if a copy of the
+structure were passed, preserving the pass-by-value semantics.  This may be
+accomplished by having the callee make a copy, but in some cases the callee may
+be able to determine that a copy is not necessary in order to produce the same
+results.  In such cases, the callee may choose to avoid making a copy of the
+parameter.
+
+
+Varargs handling
+----------------
+
+No special changes are needed for handling varargs parameters other than the
+caller knowing that a copy is needed on struct parameters larger than a
+register (see above).
+
+The varargs macros set up a register save area for the general-purpose
+registers to be saved.  Because the save area lies between the caller and
+callee stack frames, the saved register parameters are contiguous with
+parameters passed on the stack.  A pointer advances from the register save area
+into the caller's stack frame.
+
+
+Function return values
+----------------------
+
+       Type            Register
+       ----            --------
+       int             r2
+       short           r2
+       long            r2
+       long long       r2-r3
+       float           r2
+       double          r2-r3
+       struct/union    see below
+
+Structs/unions which will fit into two general-purpose registers are returned
+in r2, or in r2-r3 if necessary.  Larger structs/unions are handled by the
+caller passing as a "hidden" first argument a pointer to space allocated to
+receive the return value.
diff --git a/gcc/config/iq2000/iq2000-protos.h b/gcc/config/iq2000/iq2000-protos.h
new file mode 100644 (file)
index 0000000..6a28074
--- /dev/null
@@ -0,0 +1,64 @@
+/* Definitions of target machine for GNU compiler for iq2000.
+   Copyright (C) 2003 Free Software Foundation, Inc.
+
+   This file is part of GNU CC.
+
+   GNU CC is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   GNU CC is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GNU CC; see the file COPYING.  If not, write to
+   the Free Software Foundation, 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA. */
+
+#ifndef GCC_IQ2000_PROTOS_H
+#define GCC_IQ2000_PROTOS_H
+
+extern int iq2000_check_split PARAMS ((rtx, enum machine_mode));
+extern int iq2000_reg_mode_ok_for_base_p PARAMS ((rtx, enum machine_mode, int));
+extern int iq2000_legitimate_address_p PARAMS ((enum machine_mode, rtx, int));
+extern const char* iq2000_fill_delay_slot PARAMS ((const char*, enum delay_type, rtx*, rtx));
+extern const char *iq2000_move_1word PARAMS ((rtx *, rtx, int));
+extern int iq2000_address_cost PARAMS ((rtx));
+extern void override_options PARAMS ((void));
+extern HOST_WIDE_INT iq2000_debugger_offset PARAMS ((rtx, HOST_WIDE_INT));
+extern void final_prescan_insn PARAMS ((rtx, rtx*, int));
+extern HOST_WIDE_INT compute_frame_size PARAMS ((HOST_WIDE_INT));
+extern int iq2000_initial_elimination_offset (int, int);
+extern void iq2000_expand_prologue PARAMS ((void));
+extern void iq2000_expand_epilogue PARAMS ((void));
+extern void iq2000_expand_eh_return PARAMS ((rtx));
+extern int iq2000_can_use_return_insn PARAMS ((void));
+int function_arg_pass_by_reference PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int));
+int iq2000_adjust_insn_length PARAMS ((rtx, int));
+char *iq2000_output_conditional_branch PARAMS ((rtx, rtx*, int, int, int, int));
+extern void iq2000_init_builtins PARAMS ((void));
+extern void iq2000_setup_incoming_varargs PARAMS ((CUMULATIVE_ARGS, int, tree, int*, int));
+extern void print_operand_address PARAMS ((FILE*, rtx));
+extern void print_operand PARAMS ((FILE*, rtx, int));
+
+#ifdef RTX_CODE
+extern rtx gen_int_relational PARAMS ((enum rtx_code, rtx, rtx, rtx, int*));
+extern void gen_conditional_branch PARAMS ((rtx *, enum rtx_code));
+#endif
+
+#ifdef TREE_CODE
+extern void init_cumulative_args PARAMS ((CUMULATIVE_ARGS*, tree, rtx));
+extern void function_arg_advance PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int));
+extern struct rtx_def* function_arg PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int));
+extern int function_arg_partial_nregs PARAMS ((CUMULATIVE_ARGS*, enum machine_mode, tree, int));
+extern void iq2000_va_start PARAMS ((tree, rtx));
+extern rtx iq2000_va_arg PARAMS ((tree, tree));
+extern rtx iq2000_function_value PARAMS ((tree, tree));
+extern rtx iq2000_expand_builtin PARAMS ((tree, rtx, rtx,
+                                         enum machine_mode, int));
+#endif
+
+#endif /* ! GCC_IQ2000_PROTOS_H */
diff --git a/gcc/config/iq2000/iq2000.c b/gcc/config/iq2000/iq2000.c
new file mode 100644 (file)
index 0000000..f9deb8a
--- /dev/null
@@ -0,0 +1,3745 @@
+/* Subroutines used for code generation on Vitesse IQ2000 processors
+   Copyright (C) 2003 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include <signal.h>
+#include "tm.h"
+#include "tree.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "output.h"
+#include "insn-attr.h"
+#include "flags.h"
+#include "function.h"
+#include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
+#include "recog.h"
+#include "toplev.h"
+#include "reload.h"
+#include "ggc.h"
+#include "tm_p.h"
+#include "debug.h"
+#include "target.h"
+#include "target-def.h"
+
+/* Enumeration for all of the relational tests, so that we can build
+   arrays indexed by the test type, and not worry about the order
+   of EQ, NE, etc.  */
+
+enum internal_test {
+    ITEST_EQ,
+    ITEST_NE,
+    ITEST_GT,
+    ITEST_GE,
+    ITEST_LT,
+    ITEST_LE,
+    ITEST_GTU,
+    ITEST_GEU,
+    ITEST_LTU,
+    ITEST_LEU,
+    ITEST_MAX
+  };
+
+struct constant;
+
+static void iq2000_count_memory_refs PARAMS ((rtx, int));
+static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code));
+static rtx iq2000_add_large_offset_to_sp PARAMS ((HOST_WIDE_INT));
+static void iq2000_annotate_frame_insn PARAMS ((rtx, rtx));
+static void iq2000_emit_frame_related_store PARAMS ((rtx, rtx,
+                                                    HOST_WIDE_INT));
+static struct machine_function * iq2000_init_machine_status PARAMS ((void));
+static void save_restore_insns PARAMS ((int));
+static void abort_with_insn PARAMS ((rtx, const char *))
+     ATTRIBUTE_NORETURN;
+static int symbolic_expression_p PARAMS ((rtx));
+static enum processor_type iq2000_parse_cpu PARAMS ((const char *));
+static void iq2000_select_rtx_section PARAMS ((enum machine_mode, rtx,
+                                              unsigned HOST_WIDE_INT));
+static void iq2000_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT));
+static rtx expand_one_builtin PARAMS ((enum insn_code, rtx, tree, enum rtx_code*,
+                                      int));
+\f
+/* Structure to be filled in by compute_frame_size with register
+   save masks, and offsets for the current function.  */
+
+struct iq2000_frame_info
+{
+  long total_size;             /* # bytes that the entire frame takes up */
+  long var_size;               /* # bytes that variables take up */
+  long args_size;              /* # bytes that outgoing arguments take up */
+  long extra_size;             /* # bytes of extra gunk */
+  int  gp_reg_size;            /* # bytes needed to store gp regs */
+  int  fp_reg_size;            /* # bytes needed to store fp regs */
+  long mask;                   /* mask of saved 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 gp_sp_offset;           /* offset from new sp to store gp registers */
+  long fp_sp_offset;           /* offset from new sp to store fp registers */
+  int  initialized;            /* != 0 if frame size already calculated */
+  int  num_gp;                 /* number of gp registers saved */
+};
+
+struct machine_function
+{
+  /* Current frame information, calculated by compute_frame_size.  */
+  struct iq2000_frame_info frame;
+};
+
+/* Global variables for machine-dependent things.  */
+
+/* Count the number of .file directives, so that .loc is up to date.  */
+int num_source_filenames = 0;
+
+/* Files to separate the text and the data output, so that all of the data
+   can be emitted before the text, which will mean that the assembler will
+   generate smaller code, based on the global pointer.  */
+FILE *asm_out_data_file;
+FILE *asm_out_text_file;
+
+/* The next branch instruction is a branch likely, not branch normal.  */
+int iq2000_branch_likely;
+
+/* Count of delay slots and how many are filled.  */
+int dslots_load_total;
+int dslots_load_filled;
+int dslots_jump_total;
+int dslots_jump_filled;
+
+/* # of nops needed by previous insn */
+int dslots_number_nops;
+
+/* Number of 1/2/3 word references to data items (ie, not jal's).  */
+int num_refs[3];
+
+/* registers to check for load delay */
+rtx iq2000_load_reg, iq2000_load_reg2, iq2000_load_reg3, iq2000_load_reg4;
+
+/* Cached operands, and operator to compare for use in set/branch/trap
+   on condition codes.  */
+rtx branch_cmp[2];
+
+/* what type of branch to use */
+enum cmp_type branch_type;
+
+/* The target cpu for code generation.  */
+enum processor_type iq2000_arch;
+
+/* The target cpu for optimization and scheduling.  */
+enum processor_type iq2000_tune;
+
+/* which instruction set architecture to use.  */
+int iq2000_isa;
+
+/* Strings to hold which cpu and instruction set architecture to use.  */
+const char *iq2000_cpu_string; /* for -mcpu=<xxx> */
+const char *iq2000_arch_string;   /* for -march=<xxx> */
+
+/* Mode used for saving/restoring general purpose registers.  */
+static enum machine_mode gpr_mode;
+
+/* List of all IQ2000 punctuation characters used by print_operand.  */
+char iq2000_print_operand_punct[256];
+\f
+/* Initialize the GCC target structure.  */
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS iq2000_init_builtins
+
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN iq2000_expand_builtin
+
+#undef TARGET_ASM_SELECT_RTX_SECTION
+#define TARGET_ASM_SELECT_RTX_SECTION iq2000_select_rtx_section
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+/* Return 1 if OP can be used as an operand where a register or 16 bit unsigned
+   integer is needed.  */
+
+int
+uns_arith_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
+    return 1;
+
+  return register_operand (op, mode);
+}
+
+/* Return 1 if OP can be used as an operand where a 16 bit integer is needed. */
+
+int
+arith_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
+    return 1;
+
+  return register_operand (op, mode);
+}
+
+/* Return 1 if OP is a integer which fits in 16 bits  */
+
+int
+small_int (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
+}
+
+/* Return 1 if OP is a 32 bit integer which is too big to be loaded with one
+   instruction.  */
+
+int
+large_int (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  HOST_WIDE_INT value;
+
+  if (GET_CODE (op) != CONST_INT)
+    return 0;
+
+  value = INTVAL (op);
+
+  /* ior reg,$r0,value */
+  if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0)
+    return 0;
+
+  /* subu reg,$r0,value */
+  if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767)
+    return 0;
+
+  /* lui reg,value>>16 */
+  if ((value & 0x0000ffff) == 0)
+    return 0;
+
+  return 1;
+}
+
+/* Return 1 if OP is a register or the constant 0.  */
+
+int
+reg_or_0_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  switch (GET_CODE (op))
+    {
+    case CONST_INT:
+      return INTVAL (op) == 0;
+
+    case CONST_DOUBLE:
+      return op == CONST0_RTX (mode);
+
+    case REG:
+    case SUBREG:
+      return register_operand (op, mode);
+
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+/* Return 1 if OP is a memory operand that fits in a single instruction
+   (ie, register + small offset).  */
+
+int
+simple_memory_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  rtx addr, plus0, plus1;
+
+  /* Eliminate non-memory operations */
+  if (GET_CODE (op) != MEM)
+    return 0;
+
+  /* dword operations really put out 2 instructions, so eliminate them.  */
+  if (GET_MODE_SIZE (GET_MODE (op)) > (unsigned) UNITS_PER_WORD)
+    return 0;
+
+  /* Decode the address now.  */
+  addr = XEXP (op, 0);
+  switch (GET_CODE (addr))
+    {
+    case REG:
+    case LO_SUM:
+      return 1;
+
+    case CONST_INT:
+      return SMALL_INT (addr);
+
+    case PLUS:
+      plus0 = XEXP (addr, 0);
+      plus1 = XEXP (addr, 1);
+      if (GET_CODE (plus0) == REG
+         && GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1)
+         && SMALL_INT_UNSIGNED (plus1) /* No negative offsets */)
+       return 1;
+
+      else if (GET_CODE (plus1) == REG
+              && GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0)
+              && SMALL_INT_UNSIGNED (plus1) /* No negative offsets */)
+       return 1;
+
+      else
+       return 0;
+
+    case SYMBOL_REF:
+      return 0;
+
+    default:
+      break;
+    }
+
+  return 0;
+}
+
+/* Return nonzero if the code of this rtx pattern is EQ or NE.  */
+
+int
+equality_op (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (mode != GET_MODE (op))
+    return 0;
+
+  return GET_CODE (op) == EQ || GET_CODE (op) == NE;
+}
+
+/* Return nonzero if the code is a relational operations (EQ, LE, etc.) */
+
+int
+cmp_op (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (mode != GET_MODE (op))
+    return 0;
+
+  return GET_RTX_CLASS (GET_CODE (op)) == '<';
+}
+
+/* Return nonzero if the operand is either the PC or a label_ref.  */
+
+int
+pc_or_label_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  if (op == pc_rtx)
+    return 1;
+
+  if (GET_CODE (op) == LABEL_REF)
+    return 1;
+
+  return 0;
+}
+
+/* Return nonzero if OP is a valid operand for a call instruction.  */
+
+int
+call_insn_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  return (CONSTANT_ADDRESS_P (op)
+         || (GET_CODE (op) == REG && op != arg_pointer_rtx
+             && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
+                   && REGNO (op) <= LAST_VIRTUAL_REGISTER)));
+}
+
+/* Return nonzero if OP is valid as a source operand for a move instruction.  */
+
+int
+move_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  /* Accept any general operand after reload has started; doing so
+     avoids losing if reload does an in-place replacement of a register
+     with a SYMBOL_REF or CONST.  */
+  return (general_operand (op, mode)
+         && (! (iq2000_check_split (op, mode))
+             || reload_in_progress || reload_completed));
+}
+
+/* Return nonzero if OP is a constant power of 2.  */
+
+int
+power_of_2_operand (op, mode)
+     rtx op;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+{
+  int intval;
+
+  if (GET_CODE (op) != CONST_INT)
+    return 0;
+  else
+    intval = INTVAL (op);
+
+  return ((intval & ((unsigned)(intval) - 1)) == 0);
+}
+
+/* Return nonzero if we split the address into high and low parts.  */
+
+int
+iq2000_check_split (address, mode)
+     rtx address;
+     enum machine_mode mode;
+{
+  /* This is the same check used in simple_memory_operand.
+     We use it here because LO_SUM is not offsettable.  */
+  if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
+    return 0;
+
+  if ((GET_CODE (address) == SYMBOL_REF)
+      || (GET_CODE (address) == CONST
+         && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF)
+      || GET_CODE (address) == LABEL_REF)
+    return 1;
+
+  return 0;
+}
+
+/* Return nonzero if REG is valid for MODE.  */
+
+int
+iq2000_reg_mode_ok_for_base_p (reg, mode, strict)
+     rtx reg;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     int strict;
+{
+  return (strict
+         ? REGNO_MODE_OK_FOR_BASE_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
+   memory operand of the indicated MODE.  STRICT is non-zero if this
+   function is called during reload.  */
+
+int
+iq2000_legitimate_address_p (mode, xinsn, strict)
+     enum machine_mode mode;
+     rtx xinsn;
+     int strict;
+{
+  if (TARGET_DEBUG_A_MODE)
+    {
+      GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
+                 strict ? "" : "not ");
+      GO_DEBUG_RTX (xinsn);
+    }
+
+  /* Check for constant before stripping off SUBREG, so that we don't
+     accept (subreg (const_int)) which will fail to reload.  */
+  if (CONSTANT_ADDRESS_P (xinsn)
+      && ! (iq2000_check_split (xinsn, mode))
+      && ! (GET_CODE (xinsn) == CONST_INT && ! SMALL_INT (xinsn)))
+    return 1;
+
+  while (GET_CODE (xinsn) == SUBREG)
+    xinsn = SUBREG_REG (xinsn);
+
+  if (GET_CODE (xinsn) == REG
+      && iq2000_reg_mode_ok_for_base_p (xinsn, mode, strict))
+    return 1;
+
+  if (GET_CODE (xinsn) == LO_SUM)
+    {
+      register rtx xlow0 = XEXP (xinsn, 0);
+      register rtx xlow1 = XEXP (xinsn, 1);
+
+      while (GET_CODE (xlow0) == SUBREG)
+       xlow0 = SUBREG_REG (xlow0);
+      if (GET_CODE (xlow0) == REG
+         && iq2000_reg_mode_ok_for_base_p (xlow0, mode, strict)
+         && iq2000_check_split (xlow1, mode))
+       return 1;
+    }
+
+  if (GET_CODE (xinsn) == PLUS)
+    {
+      register rtx xplus0 = XEXP (xinsn, 0);
+      register rtx xplus1 = XEXP (xinsn, 1);
+      register enum rtx_code code0;
+      register enum rtx_code code1;
+
+      while (GET_CODE (xplus0) == SUBREG)
+       xplus0 = SUBREG_REG (xplus0);
+      code0 = GET_CODE (xplus0);
+
+      while (GET_CODE (xplus1) == SUBREG)
+       xplus1 = SUBREG_REG (xplus1);
+      code1 = GET_CODE (xplus1);
+
+      if (code0 == REG
+         && iq2000_reg_mode_ok_for_base_p (xplus0, mode, strict))
+       {
+         if (code1 == CONST_INT && SMALL_INT (xplus1)
+             && SMALL_INT_UNSIGNED (xplus1) /* No negative offsets */)
+           return 1;
+       }
+    }
+
+  if (TARGET_DEBUG_A_MODE)
+    GO_PRINTF ("Not a legitimate address\n");
+
+  /* The address was not legitimate.  */
+  return 0;
+}
+\f
+/* Returns an operand string for the given instruction's delay slot,
+   after updating filled delay slot statistics.
+
+   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
+   to FINAL_PRESCAN_INSN, and we just set the global variables that
+   it needs.  */
+
+const char *
+iq2000_fill_delay_slot (ret, type, operands, cur_insn)
+     const char *ret;          /* normal string to return */
+     enum delay_type type;     /* type of delay */
+     rtx operands[];           /* operands to use */
+     rtx cur_insn;             /* current insn */
+{
+  register rtx set_reg;
+  register enum machine_mode mode;
+  register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
+  register int num_nops;
+
+  if (type == DELAY_LOAD || type == DELAY_FCMP)
+    num_nops = 1;
+
+  else
+    num_nops = 0;
+
+  /* Make sure that we don't put nop's after labels.  */
+  next_insn = NEXT_INSN (cur_insn);
+  while (next_insn != 0
+        && (GET_CODE (next_insn) == NOTE
+            || GET_CODE (next_insn) == CODE_LABEL))
+    next_insn = NEXT_INSN (next_insn);
+
+  dslots_load_total += num_nops;
+  if (TARGET_DEBUG_C_MODE
+      || type == DELAY_NONE
+      || operands == 0
+      || cur_insn == 0
+      || next_insn == 0
+      || GET_CODE (next_insn) == CODE_LABEL
+      || (set_reg = operands[0]) == 0)
+    {
+      dslots_number_nops = 0;
+      iq2000_load_reg  = 0;
+      iq2000_load_reg2 = 0;
+      iq2000_load_reg3 = 0;
+      iq2000_load_reg4 = 0;
+      return ret;
+    }
+
+  set_reg = operands[0];
+  if (set_reg == 0)
+    return ret;
+
+  while (GET_CODE (set_reg) == SUBREG)
+    set_reg = SUBREG_REG (set_reg);
+
+  mode = GET_MODE (set_reg);
+  dslots_number_nops = num_nops;
+  iq2000_load_reg = set_reg;
+  if (GET_MODE_SIZE (mode)
+      > (unsigned) (UNITS_PER_WORD))
+    iq2000_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
+  else
+    iq2000_load_reg2 = 0;
+
+  return ret;
+}
+\f
+/* Determine whether a memory reference takes one (based off of the GP
+   pointer), two (normal), or three (label + reg) instructions, and bump the
+   appropriate counter for -mstats.  */
+
+static void
+iq2000_count_memory_refs (op, num)
+     rtx op;
+     int num;
+{
+  int additional = 0;
+  int n_words = 0;
+  rtx addr, plus0, plus1;
+  enum rtx_code code0, code1;
+  int looping;
+
+  if (TARGET_DEBUG_B_MODE)
+    {
+      fprintf (stderr, "\n========== iq2000_count_memory_refs:\n");
+      debug_rtx (op);
+    }
+
+  /* Skip MEM if passed, otherwise handle movsi of address.  */
+  addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
+
+  /* Loop, going through the address RTL.  */
+  do
+    {
+      looping = FALSE;
+      switch (GET_CODE (addr))
+       {
+       case REG:
+       case CONST_INT:
+       case LO_SUM:
+         break;
+
+       case PLUS:
+         plus0 = XEXP (addr, 0);
+         plus1 = XEXP (addr, 1);
+         code0 = GET_CODE (plus0);
+         code1 = GET_CODE (plus1);
+
+         if (code0 == REG)
+           {
+             additional++;
+             addr = plus1;
+             looping = 1;
+             continue;
+           }
+
+         if (code0 == CONST_INT)
+           {
+             addr = plus1;
+             looping = 1;
+             continue;
+           }
+
+         if (code1 == REG)
+           {
+             additional++;
+             addr = plus0;
+             looping = 1;
+             continue;
+           }
+
+         if (code1 == CONST_INT)
+           {
+             addr = plus0;
+             looping = 1;
+             continue;
+           }
+
+         if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
+           {
+             addr = plus0;
+             looping = 1;
+             continue;
+           }
+
+         if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
+           {
+             addr = plus1;
+             looping = 1;
+             continue;
+           }
+
+         break;
+
+       case LABEL_REF:
+         n_words = 2;          /* always 2 words */
+         break;
+
+       case CONST:
+         addr = XEXP (addr, 0);
+         looping = 1;
+         continue;
+
+       case SYMBOL_REF:
+         n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
+         break;
+
+       default:
+         break;
+       }
+    }
+  while (looping);
+
+  if (n_words == 0)
+    return;
+
+  n_words += additional;
+  if (n_words > 3)
+    n_words = 3;
+
+  num_refs[n_words-1] += num;
+}
+\f
+/* Return the appropriate instructions to move one operand to another.  */
+
+const char *
+iq2000_move_1word (operands, insn, unsignedp)
+     rtx operands[];
+     rtx insn;
+     int unsignedp;
+{
+  const char *ret = 0;
+  rtx op0 = operands[0];
+  rtx op1 = operands[1];
+  enum rtx_code code0 = GET_CODE (op0);
+  enum rtx_code code1 = GET_CODE (op1);
+  enum machine_mode mode = GET_MODE (op0);
+  int subreg_offset0 = 0;
+  int subreg_offset1 = 0;
+  enum delay_type delay = DELAY_NONE;
+
+  while (code0 == SUBREG)
+    {
+      subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
+                                            GET_MODE (SUBREG_REG (op0)),
+                                            SUBREG_BYTE (op0),
+                                            GET_MODE (op0));
+      op0 = SUBREG_REG (op0);
+      code0 = GET_CODE (op0);
+    }
+
+  while (code1 == SUBREG)
+    {
+      subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
+                                            GET_MODE (SUBREG_REG (op1)),
+                                            SUBREG_BYTE (op1),
+                                            GET_MODE (op1));
+      op1 = SUBREG_REG (op1);
+      code1 = GET_CODE (op1);
+    }
+
+  /* For our purposes, a condition code mode is the same as SImode.  */
+  if (mode == CCmode)
+    mode = SImode;
+
+  if (code0 == REG)
+    {
+      int regno0 = REGNO (op0) + subreg_offset0;
+
+      if (code1 == REG)
+       {
+         int regno1 = REGNO (op1) + subreg_offset1;
+
+         /* Do not do anything for assigning a register to itself */
+         if (regno0 == regno1)
+           ret = "";
+
+         else if (GP_REG_P (regno0))
+           {
+             if (GP_REG_P (regno1))
+               ret = "or\t%0,%%0,%1";
+           }
+
+       }
+
+      else if (code1 == MEM)
+       {
+         delay = DELAY_LOAD;
+
+         if (TARGET_STATS)
+           iq2000_count_memory_refs (op1, 1);
+
+         if (GP_REG_P (regno0))
+           {
+             /* For loads, use the mode of the memory item, instead of the
+                target, so zero/sign extend can use this code as well.  */
+             switch (GET_MODE (op1))
+               {
+               default:
+                 break;
+               case SFmode:
+                 ret = "lw\t%0,%1";
+                 break;
+               case SImode:
+               case CCmode:
+                 ret = "lw\t%0,%1";
+                 break;
+               case HImode:
+                 ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
+                 break;
+               case QImode:
+                 ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
+                 break;
+               }
+           }
+       }
+
+      else if (code1 == CONST_INT
+              || (code1 == CONST_DOUBLE
+                  && GET_MODE (op1) == VOIDmode))
+       {
+         if (code1 == CONST_DOUBLE)
+           {
+             /* This can happen when storing constants into long long
+                 bitfields.  Just store the least significant word of
+                 the value.  */
+             operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
+           }
+
+         if (INTVAL (op1) == 0)
+           {
+             if (GP_REG_P (regno0))
+               ret = "or\t%0,%%0,%z1";
+           }
+        else if (GP_REG_P (regno0))
+           {
+             if (SMALL_INT_UNSIGNED (op1))
+               ret = "ori\t%0,%%0,%x1\t\t\t# %1";
+             else if (SMALL_INT (op1))
+               ret = "addiu\t%0,%%0,%1\t\t\t# %1";
+             else
+               ret = "lui\t%0,%X1\t\t\t# %1\n\tori\t%0,%0,%x1";
+           }
+       }
+
+      else if (code1 == CONST_DOUBLE && mode == SFmode)
+       {
+         if (op1 == CONST0_RTX (SFmode))
+           {
+             if (GP_REG_P (regno0))
+               ret = "or\t%0,%%0,%.";
+           }
+
+         else
+           {
+             delay = DELAY_LOAD;
+             ret = "li.s\t%0,%1";
+           }
+       }
+
+      else if (code1 == LABEL_REF)
+       {
+         if (TARGET_STATS)
+           iq2000_count_memory_refs (op1, 1);
+
+         ret = "la\t%0,%a1";
+       }
+
+      else if (code1 == SYMBOL_REF || code1 == CONST)
+       {
+         if (TARGET_STATS)
+           iq2000_count_memory_refs (op1, 1);
+
+         ret = "la\t%0,%a1";
+       }
+
+      else if (code1 == PLUS)
+       {
+         rtx add_op0 = XEXP (op1, 0);
+         rtx add_op1 = XEXP (op1, 1);
+
+         if (GET_CODE (XEXP (op1, 1)) == REG
+             && GET_CODE (XEXP (op1, 0)) == CONST_INT)
+           add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
+
+         operands[2] = add_op0;
+         operands[3] = add_op1;
+         ret = "add%:\t%0,%2,%3";
+       }
+
+      else if (code1 == HIGH)
+       {
+         operands[1] = XEXP (op1, 0);
+         ret = "lui\t%0,%%hi(%1)";
+       }
+    }
+
+  else if (code0 == MEM)
+    {
+      if (TARGET_STATS)
+       iq2000_count_memory_refs (op0, 1);
+
+      if (code1 == REG)
+       {
+         int regno1 = REGNO (op1) + subreg_offset1;
+
+         if (GP_REG_P (regno1))
+           {
+             switch (mode)
+               {
+               case SFmode: ret = "sw\t%1,%0"; break;
+               case SImode: ret = "sw\t%1,%0"; break;
+               case HImode: ret = "sh\t%1,%0"; break;
+               case QImode: ret = "sb\t%1,%0"; break;
+               default: break;
+               }
+           }
+       }
+
+      else if (code1 == CONST_INT && INTVAL (op1) == 0)
+       {
+         switch (mode)
+           {
+           case SFmode: ret = "sw\t%z1,%0"; break;
+           case SImode: ret = "sw\t%z1,%0"; break;
+           case HImode: ret = "sh\t%z1,%0"; break;
+           case QImode: ret = "sb\t%z1,%0"; break;
+           default: break;
+           }
+       }
+
+      else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
+       {
+         switch (mode)
+           {
+           case SFmode: ret = "sw\t%.,%0"; break;
+           case SImode: ret = "sw\t%.,%0"; break;
+           case HImode: ret = "sh\t%.,%0"; break;
+           case QImode: ret = "sb\t%.,%0"; break;
+           default: break;
+           }
+       }
+    }
+
+  if (ret == 0)
+    {
+      abort_with_insn (insn, "Bad move");
+      return 0;
+    }
+
+  if (delay != DELAY_NONE)
+    return iq2000_fill_delay_slot (ret, delay, operands, insn);
+
+  return ret;
+}
+\f
+/* Provide the costs of an addressing mode that contains ADDR.  */
+
+int
+iq2000_address_cost (addr)
+     rtx addr;
+{
+  switch (GET_CODE (addr))
+    {
+    case LO_SUM:
+      return 1;
+
+    case LABEL_REF:
+      return 2;
+
+    case CONST:
+      {
+       rtx offset = const0_rtx;
+       addr = eliminate_constant_term (XEXP (addr, 0), &offset);
+       if (GET_CODE (addr) == LABEL_REF)
+         return 2;
+
+       if (GET_CODE (addr) != SYMBOL_REF)
+         return 4;
+
+       if (! SMALL_INT (offset))
+         return 2;
+      }
+
+      /* ... fall through ... */
+
+    case SYMBOL_REF:
+      return SYMBOL_REF_FLAG (addr) ? 1 : 2;
+
+    case PLUS:
+      {
+       register rtx plus0 = XEXP (addr, 0);
+       register rtx plus1 = XEXP (addr, 1);
+
+       if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
+         plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
+
+       if (GET_CODE (plus0) != REG)
+         break;
+
+       switch (GET_CODE (plus1))
+         {
+         case CONST_INT:
+           return SMALL_INT (plus1) ? 1 : 2;
+
+         case CONST:
+         case SYMBOL_REF:
+         case LABEL_REF:
+         case HIGH:
+         case LO_SUM:
+           return iq2000_address_cost (plus1) + 1;
+
+         default:
+           break;
+         }
+      }
+
+    default:
+      break;
+    }
+
+  return 4;
+}
+\f
+/* Make normal rtx_code into something we can index from an array.  */
+
+static enum internal_test
+map_test_to_internal_test (test_code)
+     enum rtx_code test_code;
+{
+  enum internal_test test = ITEST_MAX;
+
+  switch (test_code)
+    {
+    case EQ:  test = ITEST_EQ;  break;
+    case NE:  test = ITEST_NE;  break;
+    case GT:  test = ITEST_GT;  break;
+    case GE:  test = ITEST_GE;  break;
+    case LT:  test = ITEST_LT;  break;
+    case LE:  test = ITEST_LE;  break;
+    case GTU: test = ITEST_GTU; break;
+    case GEU: test = ITEST_GEU; break;
+    case LTU: test = ITEST_LTU; break;
+    case LEU: test = ITEST_LEU; break;
+    default:                   break;
+    }
+
+  return test;
+}
+\f
+/* Generate the code to compare two integer values.  The return value is:
+   (reg:SI xx)         The pseudo register the comparison is in
+   0                   No register, generate a simple branch.
+*/
+
+rtx
+gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
+     enum rtx_code test_code;  /* relational test (EQ, etc) */
+     rtx result;               /* result to store comp. or 0 if branch */
+     rtx cmp0;                 /* first operand to compare */
+     rtx cmp1;                 /* second operand to compare */
+     int *p_invert;            /* NULL or ptr to hold whether branch needs */
+                               /* to reverse its test */
+{
+  struct cmp_info
+  {
+    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_high;            /* high bound of constant we can accept */
+    int const_add;             /* constant to add (convert LE -> LT) */
+    int reverse_regs;          /* reverse registers in test */
+    int invert_const;          /* != 0 if invert value if cmp1 is constant */
+    int invert_reg;            /* != 0 if invert value if cmp1 is register */
+    int unsignedp;             /* != 0 for unsigned comparisons.  */
+  };
+
+  static struct cmp_info info[ (int)ITEST_MAX ] = {
+
+    { XOR,      0,  65535,  0,  0,  0,  0, 0 },        /* EQ  */
+    { XOR,      0,  65535,  0,  0,  1,  1, 0 },        /* NE  */
+    { LT,   -32769,  32766,  1,         1,  1,  0, 0 },        /* GT  */
+    { LT,   -32768,  32767,  0,         0,  1,  1, 0 },        /* GE  */
+    { LT,   -32768,  32767,  0,         0,  0,  0, 0 },        /* LT  */
+    { LT,   -32769,  32766,  1,         1,  0,  1, 0 },        /* LE  */
+    { LTU,  -32769,  32766,  1,         1,  1,  0, 1 },        /* GTU */
+    { LTU,  -32768,  32767,  0,         0,  1,  1, 1 },        /* GEU */
+    { LTU,  -32768,  32767,  0,         0,  0,  0, 1 },        /* LTU */
+    { LTU,  -32769,  32766,  1,         1,  0,  1, 1 },        /* LEU */
+  };
+
+  enum internal_test test;
+  enum machine_mode mode;
+  struct cmp_info *p_info;
+  int branch_p;
+  int eqne_p;
+  int invert;
+  rtx reg;
+  rtx reg2;
+
+  test = map_test_to_internal_test (test_code);
+  if (test == ITEST_MAX)
+    abort ();
+
+  p_info = &info[(int) test];
+  eqne_p = (p_info->test_code == XOR);
+
+  mode = GET_MODE (cmp0);
+  if (mode == VOIDmode)
+    mode = GET_MODE (cmp1);
+
+  /* Eliminate simple branches */
+  branch_p = (result == 0);
+  if (branch_p)
+    {
+      if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
+       {
+         /* Comparisons against zero are simple branches */
+         if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
+           return 0;
+
+         /* Test for beq/bne.  */
+         if (eqne_p)
+           return 0;
+       }
+
+      /* allocate a pseudo to calculate the value in.  */
+      result = gen_reg_rtx (mode);
+    }
+
+  /* Make sure we can handle any constants given to us.  */
+  if (GET_CODE (cmp0) == CONST_INT)
+    cmp0 = force_reg (mode, cmp0);
+
+  if (GET_CODE (cmp1) == CONST_INT)
+    {
+      HOST_WIDE_INT value = INTVAL (cmp1);
+
+      if (value < p_info->const_low
+         || value > p_info->const_high)
+       cmp1 = force_reg (mode, cmp1);
+    }
+
+  /* See if we need to invert the result.  */
+  invert = (GET_CODE (cmp1) == CONST_INT
+           ? p_info->invert_const : p_info->invert_reg);
+
+  if (p_invert != (int *)0)
+    {
+      *p_invert = invert;
+      invert = 0;
+    }
+
+  /* Comparison to constants, may involve adding 1 to change a LT into LE.
+     Comparison between two registers, may involve switching operands.  */
+  if (GET_CODE (cmp1) == CONST_INT)
+    {
+      if (p_info->const_add != 0)
+       {
+         HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
+
+         /* If modification of cmp1 caused overflow,
+            we would get the wrong answer if we follow the usual path;
+            thus, x > 0xffffffffU would turn into x > 0U.  */
+         if ((p_info->unsignedp
+              ? (unsigned HOST_WIDE_INT) new >
+              (unsigned HOST_WIDE_INT) INTVAL (cmp1)
+              : new > INTVAL (cmp1))
+             != (p_info->const_add > 0))
+           {
+             /* This test is always true, but if INVERT is true then
+                the result of the test needs to be inverted so 0 should
+                be returned instead.  */
+             emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
+             return result;
+           }
+         else
+           cmp1 = GEN_INT (new);
+       }
+    }
+
+  else if (p_info->reverse_regs)
+    {
+      rtx temp = cmp0;
+      cmp0 = cmp1;
+      cmp1 = temp;
+    }
+
+  if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
+    reg = cmp0;
+  else
+    {
+      reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
+      convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0);
+    }
+
+  if (test == ITEST_NE)
+    {
+      convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
+      if (p_invert != NULL)
+       *p_invert = 0;
+      invert = 0;
+    }
+
+  else if (test == ITEST_EQ)
+    {
+      reg2 = invert ? gen_reg_rtx (mode) : result;
+      convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
+      reg = reg2;
+    }
+
+  if (invert)
+    {
+      rtx one;
+
+      one = const1_rtx;
+      convert_move (result, gen_rtx (XOR, mode, reg, one), 0);
+    }
+
+  return result;
+}
+\f
+/* Emit the common code for doing conditional branches.
+   operand[0] is the label to jump to.
+   The comparison operands are saved away by cmp{si,di,sf,df}.  */
+
+void
+gen_conditional_branch (operands, test_code)
+     rtx operands[];
+     enum rtx_code test_code;
+{
+  enum cmp_type type = branch_type;
+  rtx cmp0 = branch_cmp[0];
+  rtx cmp1 = branch_cmp[1];
+  enum machine_mode mode;
+  rtx reg;
+  int invert;
+  rtx label1, label2;
+
+  switch (type)
+    {
+    case CMP_SI:
+    case CMP_DI:
+      mode = type == CMP_SI ? SImode : DImode;
+      invert = 0;
+      reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
+
+      if (reg)
+       {
+         cmp0 = reg;
+         cmp1 = const0_rtx;
+         test_code = NE;
+       }
+      else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
+       /* We don't want to build a comparison against a non-zero
+          constant.  */
+       cmp1 = force_reg (mode, cmp1);
+
+      break;
+
+    case CMP_SF:
+    case CMP_DF:
+      reg = gen_reg_rtx (CCmode);
+
+      /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result == 0 */
+      emit_insn (gen_rtx_SET (VOIDmode, reg,
+                             gen_rtx (test_code == NE ? EQ : test_code,
+                                      CCmode, cmp0, cmp1)));
+
+      test_code = test_code == NE ? EQ : NE;
+      mode = CCmode;
+      cmp0 = reg;
+      cmp1 = const0_rtx;
+      invert = 0;
+      break;
+
+    default:
+      abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
+    }
+
+  /* Generate the branch.  */
+
+  label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
+  label2 = pc_rtx;
+
+  if (invert)
+    {
+      label2 = label1;
+      label1 = pc_rtx;
+    }
+
+  emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
+                              gen_rtx_IF_THEN_ELSE (VOIDmode,
+                                                    gen_rtx (test_code, mode,
+                                                             cmp0, cmp1),
+                                                    label1, label2)));
+}
+\f
+/* Initialize CUMULATIVE_ARGS for a function.  */
+
+void
+init_cumulative_args (cum, fntype, libname)
+     CUMULATIVE_ARGS *cum;             /* argument info to initialize */
+     tree fntype;                      /* tree ptr for function decl */
+     rtx libname ATTRIBUTE_UNUSED;     /* SYMBOL_REF of library name or 0 */
+{
+  static CUMULATIVE_ARGS zero_cum;
+  tree param, next_param;
+
+  if (TARGET_DEBUG_D_MODE)
+    {
+      fprintf (stderr,
+              "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
+
+      if (!fntype)
+       fputc ('\n', stderr);
+
+      else
+       {
+         tree ret_type = TREE_TYPE (fntype);
+         fprintf (stderr, ", fntype code = %s, ret code = %s\n",
+                  tree_code_name[(int)TREE_CODE (fntype)],
+                  tree_code_name[(int)TREE_CODE (ret_type)]);
+       }
+    }
+
+  *cum = zero_cum;
+
+  /* Determine if this function has variable arguments.  This is
+     indicated by the last argument being 'void_type_mode' if there
+     are no variable arguments.  The standard IQ2000 calling sequence
+     passes all arguments in the general purpose registers in this case.  */
+
+  for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
+       param != 0; param = next_param)
+    {
+      next_param = TREE_CHAIN (param);
+      if (next_param == 0 && TREE_VALUE (param) != void_type_node)
+       cum->gp_reg_found = 1;
+    }
+}
+
+/* Advance the argument to the next argument position.  */
+
+void
+function_arg_advance (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;     /* current arg information */
+     enum machine_mode mode;   /* current arg mode */
+     tree type;                        /* type of the argument or 0 if lib support */
+     int named;                        /* whether or not the argument was named */
+{
+  if (TARGET_DEBUG_D_MODE)
+    {
+      fprintf (stderr,
+              "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
+              cum->gp_reg_found, cum->arg_number, cum->arg_words,
+              GET_MODE_NAME (mode));
+      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
+      fprintf (stderr, ", %d )\n\n", named);
+    }
+
+  cum->arg_number++;
+  switch (mode)
+    {
+    case VOIDmode:
+      break;
+
+    default:
+      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
+         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
+       abort ();
+
+      cum->gp_reg_found = 1;
+      cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
+                        / UNITS_PER_WORD);
+      break;
+
+    case BLKmode:
+      cum->gp_reg_found = 1;
+      cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
+                        / UNITS_PER_WORD);
+      break;
+
+    case SFmode:
+      cum->arg_words++;
+      if (! cum->gp_reg_found && cum->arg_number <= 2)
+       cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
+      break;
+
+    case DFmode:
+      cum->arg_words += 2;
+      if (! cum->gp_reg_found && cum->arg_number <= 2)
+       cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
+      break;
+
+    case DImode:
+      cum->gp_reg_found = 1;
+      cum->arg_words += 2;
+      break;
+
+    case QImode:
+    case HImode:
+    case SImode:
+      cum->gp_reg_found = 1;
+      cum->arg_words++;
+      break;
+    }
+}
+
+/* Return an RTL expression containing the register for the given mode,
+   or 0 if the argument is to be passed on the stack.  */
+
+struct rtx_def *
+function_arg (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;     /* current arg information */
+     enum machine_mode mode;   /* current arg mode */
+     tree type;                        /* type of the argument or 0 if lib support */
+     int named;                        /* != 0 for normal args, == 0 for ... args */
+{
+  rtx ret;
+  int regbase = -1;
+  int bias = 0;
+  unsigned int *arg_words = &cum->arg_words;
+  int struct_p = (type != 0
+                 && (TREE_CODE (type) == RECORD_TYPE
+                     || TREE_CODE (type) == UNION_TYPE
+                     || TREE_CODE (type) == QUAL_UNION_TYPE));
+
+  if (TARGET_DEBUG_D_MODE)
+    {
+      fprintf (stderr,
+              "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
+              cum->gp_reg_found, cum->arg_number, cum->arg_words,
+              GET_MODE_NAME (mode));
+      fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
+      fprintf (stderr, ", %d ) = ", named);
+    }
+
+
+  cum->last_arg_fp = 0;
+  switch (mode)
+    {
+    case SFmode:
+      regbase = GP_ARG_FIRST;
+      break;
+
+    case DFmode:
+      cum->arg_words += cum->arg_words & 1;
+
+      regbase = GP_ARG_FIRST;
+      break;
+
+    default:
+      if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
+         && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
+       abort ();
+
+      /* Drops through.  */
+    case BLKmode:
+      if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
+       cum->arg_words += (cum->arg_words & 1);
+      regbase = GP_ARG_FIRST;
+      break;
+
+    case VOIDmode:
+    case QImode:
+    case HImode:
+    case SImode:
+      regbase = GP_ARG_FIRST;
+      break;
+
+    case DImode:
+      cum->arg_words += (cum->arg_words & 1);
+      regbase = GP_ARG_FIRST;
+    }
+
+  if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
+    {
+      if (TARGET_DEBUG_D_MODE)
+       fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
+
+      ret = 0;
+    }
+  else
+    {
+      if (regbase == -1)
+       abort ();
+
+      if (! type || TREE_CODE (type) != RECORD_TYPE
+         || ! named  || ! TYPE_SIZE_UNIT (type)
+         || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
+       ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
+      else
+       {
+         tree field;
+
+         for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+           if (TREE_CODE (field) == FIELD_DECL
+               && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+               && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+               && host_integerp (bit_position (field), 0)
+               && int_bit_position (field) % BITS_PER_WORD == 0)
+             break;
+
+         /* If the whole struct fits a DFmode register,
+            we don't need the PARALLEL.  */
+         if (! field || mode == DFmode)
+           ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
+         else
+           {
+             unsigned int chunks;
+             HOST_WIDE_INT bitpos;
+             unsigned int regno;
+             unsigned int i;
+
+             /* ??? If this is a packed structure, then the last hunk won't
+                be 64 bits.  */
+
+             chunks
+               = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
+             if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
+               chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
+
+             /* assign_parms checks the mode of ENTRY_PARM, so we must
+                use the actual mode here.  */
+             ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
+
+             bitpos = 0;
+             regno = regbase + *arg_words + bias;
+             field = TYPE_FIELDS (type);
+             for (i = 0; i < chunks; i++)
+               {
+                 rtx reg;
+
+                 for (; field; field = TREE_CHAIN (field))
+                   if (TREE_CODE (field) == FIELD_DECL
+                       && int_bit_position (field) >= bitpos)
+                     break;
+
+                 if (field
+                     && int_bit_position (field) == bitpos
+                     && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
+                     && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+                   reg = gen_rtx_REG (DFmode, regno++);
+                 else
+                   reg = gen_rtx_REG (word_mode, regno);
+
+                 XVECEXP (ret, 0, i)
+                   = gen_rtx_EXPR_LIST (VOIDmode, reg,
+                                        GEN_INT (bitpos / BITS_PER_UNIT));
+
+                 bitpos += 64;
+                 regno++;
+               }
+           }
+       }
+
+      if (TARGET_DEBUG_D_MODE)
+       fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
+                struct_p ? ", [struct]" : "");
+    }
+
+  /* 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
+     insn.  If we need any shifts for small structures, return them in
+     a PARALLEL.  */
+  if (mode == VOIDmode)
+    {
+      if (cum->num_adjusts > 0)
+       ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
+                      gen_rtvec_v (cum->num_adjusts, cum->adjust));
+    }
+
+  return ret;
+}
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum;     /* current arg information */
+     enum machine_mode mode;   /* current arg mode */
+     tree type ATTRIBUTE_UNUSED;/* type of the argument or 0 if lib support */
+     int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */
+{
+  if (mode == DImode
+          && cum->arg_words == MAX_ARGS_IN_REGISTERS - (unsigned)1)
+    {
+      if (TARGET_DEBUG_D_MODE)
+       fprintf (stderr, "function_arg_partial_nregs = 1\n");
+
+      return 1;
+    }
+
+  return 0;
+}
+\f
+/* Implement va_start.  */
+
+void
+iq2000_va_start (valist, nextarg)
+     tree valist;
+     rtx nextarg;
+{
+  int int_arg_words;
+
+  /* Find out how many non-float named formals */
+  int gpr_save_area_size;
+  /* Note UNITS_PER_WORD is 4 bytes */
+  int_arg_words = current_function_args_info.arg_words;
+  if (int_arg_words < 8 )
+    /* Adjust for the prologue's economy measure */
+    gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
+  else
+    gpr_save_area_size = 0;
+
+  /* Everything is in the GPR save area, or in the overflow
+     area which is contiguous with it.  */
+
+  nextarg = plus_constant (nextarg, -gpr_save_area_size);
+  std_expand_builtin_va_start (valist, nextarg);
+}
+
+/* Implement va_arg.  */
+
+rtx
+iq2000_va_arg (valist, type)
+     tree valist, type;
+{
+  HOST_WIDE_INT size, rsize;
+  rtx addr_rtx;
+  tree t;
+
+  int indirect;
+  rtx r, lab_over = NULL_RTX, lab_false;
+  tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+  tree ovfl, gtop, ftop, goff, foff;
+
+  size = int_size_in_bytes (type);
+  rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+  indirect
+    = function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+  if (indirect)
+    {
+      size = POINTER_SIZE / BITS_PER_UNIT;
+      rsize = UNITS_PER_WORD;
+    }
+
+  addr_rtx = gen_reg_rtx (Pmode);
+
+  {
+    /* Case of all args in a merged stack. No need to check bounds,
+       just advance valist along the stack.  */
+
+    tree gpr = valist;
+    if (! indirect
+       && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
+      {
+       t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
+                  build_int_2 (2*UNITS_PER_WORD - 1, 0));
+       t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
+                  build_int_2 (-2*UNITS_PER_WORD, -1));
+       t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
+       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      }
+
+    t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
+              size_int (rsize));
+    r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+    if (r != addr_rtx)
+      emit_move_insn (addr_rtx, r);
+
+    /* flush the POSTINCREMENT */
+    emit_queue();
+
+    if (indirect)
+      {
+       r = gen_rtx_MEM (Pmode, addr_rtx);
+       set_mem_alias_set (r, get_varargs_alias_set ());
+       emit_move_insn (addr_rtx, r);
+      }
+    else
+      {
+       if (BYTES_BIG_ENDIAN && rsize != size)
+         addr_rtx = plus_constant (addr_rtx, rsize - size);
+      }
+    return addr_rtx;
+  }
+
+  /* Not a simple merged stack.  Need ptrs and indexes left by va_start.  */
+
+  f_ovfl  = TYPE_FIELDS (va_list_type_node);
+  f_gtop = TREE_CHAIN (f_ovfl);
+  f_ftop = TREE_CHAIN (f_gtop);
+  f_goff = TREE_CHAIN (f_ftop);
+  f_foff = TREE_CHAIN (f_goff);
+
+  ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
+  gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
+  ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
+  goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
+  foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+
+  lab_false = gen_label_rtx ();
+  lab_over = gen_label_rtx ();
+
+  if (TREE_CODE (type) == REAL_TYPE)
+    {
+
+      /* Emit code to branch if foff == 0.  */
+      r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)),
+                      EXPAND_NORMAL);
+      emit_cmp_and_jump_insns (r, const0_rtx, EQ,
+                              const1_rtx, GET_MODE (r), 1, lab_false);
+
+      /* Emit code for addr_rtx = ftop - foff */
+      t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff );
+      r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+      if (r != addr_rtx)
+       emit_move_insn (addr_rtx, r);
+
+      /* Emit code for foff-=8.
+        Advances the offset up FPR save area by one double */
+      t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0));
+      t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t);
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      emit_queue();
+      emit_jump (lab_over);
+      emit_barrier ();
+      emit_label (lab_false);
+
+      /* If a 4-byte int is followed by an 8-byte float, then
+        natural alignment causes a 4 byte gap.
+        So, dynamically adjust ovfl up to a multiple of 8.  */
+      t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl,
+                build_int_2 (7, 0));
+      t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t);
+      t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+      /* Emit code for addr_rtx = the ovfl pointer into overflow area.
+        Postincrement the ovfl pointer by 8.  */
+      t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
+                size_int (8));
+      r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+      if (r != addr_rtx)
+       emit_move_insn (addr_rtx, r);
+
+      emit_queue();
+      emit_label (lab_over);
+      return addr_rtx;
+    }
+  else
+    {
+      /* not REAL_TYPE */
+      int step_size;
+
+      if (TREE_CODE (type) == INTEGER_TYPE
+         && TYPE_PRECISION (type) == 64)
+       {
+         /* int takes 32 bits of the GPR save area, but
+            longlong takes an aligned 64 bits.  So, emit code
+            to zero the low order bits of goff, thus aligning
+            the later calculation of (gtop-goff) upwards.  */
+         t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff,
+                    build_int_2 (-8, -1));
+         t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
+         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+       }
+
+      /* Emit code to branch if goff == 0.  */
+      r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)),
+                      EXPAND_NORMAL);
+      emit_cmp_and_jump_insns (r, const0_rtx, EQ,
+                              const1_rtx, GET_MODE (r), 1, lab_false);
+
+      /* Emit code for addr_rtx = gtop - goff.  */
+      t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff);
+      r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+      if (r != addr_rtx)
+       emit_move_insn (addr_rtx, r);
+      
+      if (TYPE_PRECISION (type) == 64)
+       step_size = 8;
+      else
+       step_size = UNITS_PER_WORD;
+
+      /* Emit code for goff = goff - step_size.
+        Advances the offset up GPR save area over the item.  */
+      t = build (MINUS_EXPR, TREE_TYPE (goff), goff,
+                build_int_2 (step_size, 0));
+      t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      
+      emit_queue();
+      emit_jump (lab_over);
+      emit_barrier ();
+      emit_label (lab_false);
+
+      /* Emit code for addr_rtx -> overflow area, postinc by step_size */
+      t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
+                size_int (step_size));
+      r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
+      if (r != addr_rtx)
+       emit_move_insn (addr_rtx, r);
+
+      emit_queue();
+      emit_label (lab_over);
+
+      if (indirect)
+       {
+         r = gen_rtx_MEM (Pmode, addr_rtx);
+         set_mem_alias_set (r, get_varargs_alias_set ());
+         emit_move_insn (addr_rtx, r);
+       }
+      else
+       {
+         if (BYTES_BIG_ENDIAN && rsize != size)
+           addr_rtx = plus_constant (addr_rtx, rsize - size);
+       }
+      return addr_rtx;
+    }
+}
+\f
+/* Abort after printing out a specific insn.  */
+
+static void
+abort_with_insn (insn, reason)
+     rtx insn;
+     const char *reason;
+{
+  error (reason);
+  debug_rtx (insn);
+  abort ();
+}
+\f
+/* Detect any conflicts in the switches.  */
+
+void
+override_options ()
+{
+  register enum processor_type iq2000_cpu;
+
+  target_flags &= ~MASK_GPOPT;
+
+  iq2000_isa = IQ2000_ISA_DEFAULT;
+
+  /* Identify the processor type.  */
+
+  if (iq2000_cpu_string != 0)
+    {
+      iq2000_cpu = iq2000_parse_cpu (iq2000_cpu_string);
+      if (iq2000_cpu == PROCESSOR_DEFAULT)
+       {
+         error ("bad value (%s) for -mcpu= switch", iq2000_arch_string);
+         iq2000_cpu_string = "default";
+       }
+      iq2000_arch = iq2000_cpu;
+      iq2000_tune = iq2000_cpu;
+    }
+
+  if (iq2000_arch_string == 0
+      || ! strcmp (iq2000_arch_string, "default")
+      || ! strcmp (iq2000_arch_string, "DEFAULT"))
+    {
+      switch (iq2000_isa)
+       {
+       default:
+         iq2000_arch_string = "iq2000";
+         iq2000_arch = PROCESSOR_IQ2000;
+         break;
+       }
+    }
+  else
+    {
+      iq2000_arch = iq2000_parse_cpu (iq2000_arch_string);
+      if (iq2000_arch == PROCESSOR_DEFAULT)
+       {
+         error ("bad value (%s) for -march= switch", iq2000_arch_string);
+         iq2000_arch_string = "default";
+       }
+      if (iq2000_arch == PROCESSOR_IQ10)
+       {
+         error ("The compiler does not support -march=%s.", iq2000_arch_string);
+         iq2000_arch_string = "default";
+       }
+    }
+
+  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
+     initialized yet, so we can't use that here.  */
+  gpr_mode = SImode;
+
+  /* Function to allocate machine-dependent function status.  */
+  init_machine_status = &iq2000_init_machine_status;
+}
+
+/* Allocate a chunk of memory for per-function machine-dependent data.  */
+
+static struct machine_function *
+iq2000_init_machine_status ()
+{
+  return ((struct machine_function *)
+         ggc_alloc_cleared (sizeof (struct machine_function)));
+}
+\f
+/* The arg pointer (which is eliminated) points to the virtual frame pointer,
+   while the frame pointer (which may be eliminated) points to the stack
+   pointer after the initial adjustments.  */
+
+HOST_WIDE_INT
+iq2000_debugger_offset (addr, offset)
+     rtx addr;
+     HOST_WIDE_INT offset;
+{
+  rtx offset2 = const0_rtx;
+  rtx reg = eliminate_constant_term (addr, &offset2);
+
+  if (offset == 0)
+    offset = INTVAL (offset2);
+
+  if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
+      || reg == hard_frame_pointer_rtx)
+    {
+      HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized)
+                                 ? compute_frame_size (get_frame_size ())
+                                 : cfun->machine->frame.total_size;
+
+      offset = offset - frame_size;
+    }
+
+  return offset;
+}
+\f
+/* 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
+   output differently.
+
+   Here the argument OPVEC is the vector containing the operands extracted
+   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
+   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.
+
+   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.  */
+
+void
+final_prescan_insn (insn, opvec, noperands)
+     rtx insn;
+     rtx opvec[] ATTRIBUTE_UNUSED;
+     int noperands ATTRIBUTE_UNUSED;
+{
+  if (dslots_number_nops > 0)
+    {
+      rtx pattern = PATTERN (insn);
+      int length = get_attr_length (insn);
+
+      /* Do we need to emit a NOP? */
+      if (length == 0
+         || (iq2000_load_reg != 0 && reg_mentioned_p (iq2000_load_reg,  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_reg4 != 0
+             && reg_mentioned_p (iq2000_load_reg4, pattern)))
+       fputs ("\tnop\n", asm_out_file);
+
+      else
+       dslots_load_filled++;
+
+      while (--dslots_number_nops > 0)
+       fputs ("\tnop\n", asm_out_file);
+
+      iq2000_load_reg = 0;
+      iq2000_load_reg2 = 0;
+      iq2000_load_reg3 = 0;
+      iq2000_load_reg4 = 0;
+    }
+
+  if ((GET_CODE (insn) == JUMP_INSN
+       || GET_CODE (insn) == CALL_INSN
+       || (GET_CODE (PATTERN (insn)) == RETURN))
+          && NEXT_INSN (PREV_INSN (insn)) == insn)
+    {
+      rtx nop_insn = emit_insn_after (gen_nop (), insn);
+      INSN_ADDRESSES_NEW (nop_insn, -1);
+    }
+  
+  if (TARGET_STATS
+      && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
+    dslots_jump_total++;
+}
+\f
+/* Return the bytes needed to compute the frame pointer from the current
+   stack pointer.
+
+   IQ2000 stack frames look like:
+
+             Before call                       After call
+        +-----------------------+      +-----------------------+
+   high |                      |       |                       |
+   mem. |                      |       |                       |
+        |  caller's temps.     |       |  caller's temps.      |
+       |                       |       |                       |
+        +-----------------------+      +-----------------------+
+       |                       |       |                       |
+        |  arguments on stack.  |      |  arguments on stack.  |
+       |                       |       |                       |
+        +-----------------------+      +-----------------------+
+       |  4 words to save      |       |  4 words to save      |
+       |  arguments passed     |       |  arguments passed     |
+       |  in registers, even   |       |  in registers, even   |
+    SP->|  if not passed.       |  VFP->|  if not passed.      |
+       +-----------------------+       +-----------------------+
+                                       |                       |
+                                        |  fp register save     |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                        |  gp register save     |
+                                        |                      |
+                                       +-----------------------+
+                                       |                       |
+                                       |  local variables      |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                        |  alloca allocations   |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                       |  GP save for V.4 abi  |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                        |  arguments on stack   |
+                                       |                       |
+                                       +-----------------------+
+                                        |  4 words to save      |
+                                       |  arguments passed     |
+                                        |  in registers, even   |
+   low                              SP->|  if not passed.       |
+   memory                              +-----------------------+
+
+*/
+
+HOST_WIDE_INT
+compute_frame_size (size)
+     HOST_WIDE_INT size;       /* # of var. bytes allocated */
+{
+  int regno;
+  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 args_size;     /* # bytes that outgoing arguments take up */
+  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_size;   /* # bytes needed to store gp regs */
+  HOST_WIDE_INT fp_reg_size;   /* # bytes needed to store fp regs */
+  long mask;                   /* mask of saved gp registers */
+  int  fp_inc;                 /* 1 or 2 depending on the size of fp regs */
+  long fp_bits;                        /* bitmask to use for each fp register */
+
+  gp_reg_size = 0;
+  fp_reg_size = 0;
+  mask = 0;
+  extra_size = IQ2000_STACK_ALIGN ((0));
+  var_size = IQ2000_STACK_ALIGN (size);
+  args_size = IQ2000_STACK_ALIGN (current_function_outgoing_args_size);
+
+  /* If a function dynamically allocates the stack and
+     has 0 for STACK_DYNAMIC_OFFSET then allocate some stack space */
+
+  if (args_size == 0 && current_function_calls_alloca)
+    args_size = 4 * UNITS_PER_WORD;
+
+  total_size = var_size + args_size + extra_size;
+
+  /* Calculate space needed for gp registers.  */
+  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    {
+      if (MUST_SAVE_REGISTER (regno))
+       {
+         gp_reg_size += GET_MODE_SIZE (gpr_mode);
+         mask |= 1L << (regno - GP_REG_FIRST);
+       }
+    }
+
+  /* We need to restore these for the handler.  */
+  if (current_function_calls_eh_return)
+    {
+      int i;
+      for (i = 0; ; ++i)
+       {
+         regno = EH_RETURN_DATA_REGNO (i);
+         if (regno == (signed int) INVALID_REGNUM)
+           break;
+         gp_reg_size += GET_MODE_SIZE (gpr_mode);
+         mask |= 1L << (regno - GP_REG_FIRST);
+       }
+    }
+
+  fp_inc = 2;
+  fp_bits = 3;
+  gp_reg_rounded = IQ2000_STACK_ALIGN (gp_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 
+     (total_size == extra_size) to save the gp reg.  */
+  if (total_size == extra_size
+      && ! profile_flag)
+    total_size = extra_size = 0;
+
+  total_size += IQ2000_STACK_ALIGN (current_function_pretend_args_size);
+
+  /* Save other computed information.  */
+  cfun->machine->frame.total_size = total_size;
+  cfun->machine->frame.var_size = var_size;
+  cfun->machine->frame.args_size = args_size;
+  cfun->machine->frame.extra_size = extra_size;
+  cfun->machine->frame.gp_reg_size = gp_reg_size;
+  cfun->machine->frame.fp_reg_size = fp_reg_size;
+  cfun->machine->frame.mask = mask;
+  cfun->machine->frame.initialized = reload_completed;
+  cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD;
+
+  if (mask)
+    {
+      unsigned long offset;
+
+      offset = (args_size + extra_size + var_size
+               + gp_reg_size - GET_MODE_SIZE (gpr_mode));
+
+      cfun->machine->frame.gp_sp_offset = offset;
+      cfun->machine->frame.gp_save_offset = offset - total_size;
+    }
+  else
+    {
+      cfun->machine->frame.gp_sp_offset = 0;
+      cfun->machine->frame.gp_save_offset = 0;
+    }
+
+  cfun->machine->frame.fp_sp_offset = 0;
+  cfun->machine->frame.fp_save_offset = 0;
+
+  /* Ok, we're done.  */
+  return total_size;
+}
+\f
+/* Implement INITIAL_ELIMINATION_OFFSET.  FROM is either the frame
+   pointer, argument pointer, or return address pointer.  TO is either
+   the stack pointer or hard frame pointer.  */
+
+int
+iq2000_initial_elimination_offset (from, to)
+     int from;
+     int to ATTRIBUTE_UNUSED;
+{
+  int offset;
+
+  compute_frame_size (get_frame_size ());                               
+  if ((from) == FRAME_POINTER_REGNUM) 
+    (offset) = 0; 
+  else if ((from) == ARG_POINTER_REGNUM) 
+    (offset) = (cfun->machine->frame.total_size); 
+  else if ((from) == RETURN_ADDRESS_POINTER_REGNUM) 
+  {
+   if (leaf_function_p ()) 
+      (offset) = 0; 
+   else (offset) = cfun->machine->frame.gp_sp_offset 
+              + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT)) 
+                 * (BYTES_BIG_ENDIAN != 0)); 
+  } 
+
+  return offset;
+}
+\f
+/* Common code to emit the insns (or to write the instructions to a file)
+   to save/restore registers.  
+   Other parts of the code assume that IQ2000_TEMP1_REGNUM (aka large_reg)
+   is not modified within save_restore_insns.  */
+
+#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
+
+/* Emit instructions to load the value (SP + OFFSET) into IQ2000_TEMP2_REGNUM
+   and return an rtl expression for the register.  Write the assembly
+   instructions directly to FILE if it is not null, otherwise emit them as
+   rtl.
+
+   This function is a subroutine of save_restore_insns.  It is used when
+   OFFSET is too large to add in a single instruction.  */
+
+static rtx
+iq2000_add_large_offset_to_sp (offset)
+     HOST_WIDE_INT offset;
+{
+  rtx reg = gen_rtx_REG (Pmode, IQ2000_TEMP2_REGNUM);
+  rtx offset_rtx = GEN_INT (offset);
+
+  emit_move_insn (reg, offset_rtx);
+  emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
+  return reg;
+}
+
+/* Make INSN frame related and note that it performs the frame-related
+   operation DWARF_PATTERN.  */
+
+static void
+iq2000_annotate_frame_insn (insn, dwarf_pattern)
+     rtx insn, dwarf_pattern;
+{
+  RTX_FRAME_RELATED_P (insn) = 1;
+  REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                     dwarf_pattern,
+                                     REG_NOTES (insn));
+}
+
+/* Emit a move instruction that stores REG in MEM.  Make the instruction
+   frame related and note that it stores REG at (SP + OFFSET).  */
+
+static void
+iq2000_emit_frame_related_store (mem, reg, offset)
+     rtx mem;
+     rtx reg;
+     HOST_WIDE_INT offset;
+{
+  rtx dwarf_address = plus_constant (stack_pointer_rtx, offset);
+  rtx dwarf_mem = gen_rtx_MEM (GET_MODE (reg), dwarf_address);
+
+  iq2000_annotate_frame_insn (emit_move_insn (mem, reg),
+                           gen_rtx_SET (GET_MODE (reg), dwarf_mem, reg));
+}
+
+static void
+save_restore_insns (store_p)
+     int store_p;      /* true if this is prologue */
+{
+  long mask = cfun->machine->frame.mask;
+  int regno;
+  rtx base_reg_rtx;
+  HOST_WIDE_INT base_offset;
+  HOST_WIDE_INT gp_offset;
+  HOST_WIDE_INT end_offset;
+
+  if (frame_pointer_needed
+      && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
+    abort ();
+
+  if (mask == 0)
+    {
+      base_reg_rtx = 0, base_offset  = 0;
+      return;
+    }
+
+  /* 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
+     need a nop in the epilog if at least one register is reloaded in
+     addition to return address.  */
+
+  /* Save GP registers if needed.  */
+  /* Pick which pointer to use as a base register.  For small frames, just
+     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
+     the constant created in the prologue/epilogue to adjust the stack
+     frame.  */
+
+  gp_offset = cfun->machine->frame.gp_sp_offset;
+  end_offset
+    = gp_offset - (cfun->machine->frame.gp_reg_size
+                  - GET_MODE_SIZE (gpr_mode));
+
+  if (gp_offset < 0 || end_offset < 0)
+    internal_error
+      ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
+       (long) gp_offset, (long) end_offset);
+
+  else if (gp_offset < 32768)
+    base_reg_rtx = stack_pointer_rtx, base_offset  = 0;
+  else
+    {
+      int regno;
+      int reg_save_count = 0;
+      for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+       if (BITSET_P (mask, regno - GP_REG_FIRST)) reg_save_count += 1;
+      base_offset = gp_offset - ((reg_save_count - 1) * 4);
+      base_reg_rtx = iq2000_add_large_offset_to_sp (base_offset);
+    }
+
+  for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+    {
+      if (BITSET_P (mask, regno - GP_REG_FIRST))
+       {
+         rtx reg_rtx;
+         rtx mem_rtx
+           = gen_rtx (MEM, gpr_mode,
+                      gen_rtx (PLUS, Pmode, base_reg_rtx,
+                               GEN_INT (gp_offset - base_offset)));
+
+         if (! current_function_calls_eh_return)
+           RTX_UNCHANGING_P (mem_rtx) = 1;
+
+         reg_rtx = gen_rtx (REG, gpr_mode, regno);
+
+         if (store_p)
+           iq2000_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
+         else 
+           {
+             emit_move_insn (reg_rtx, mem_rtx);
+           }
+         gp_offset -= GET_MODE_SIZE (gpr_mode);
+       }
+    }
+}
+\f
+/* Expand the prologue into a bunch of separate insns.  */
+
+void
+iq2000_expand_prologue ()
+{
+  int regno;
+  HOST_WIDE_INT tsize;
+  int last_arg_is_vararg_marker = 0;
+  tree fndecl = current_function_decl;
+  tree fntype = TREE_TYPE (fndecl);
+  tree fnargs = DECL_ARGUMENTS (fndecl);
+  rtx next_arg_reg;
+  int i;
+  tree next_arg;
+  tree cur_arg;
+  CUMULATIVE_ARGS args_so_far;
+  int store_args_on_stack = (iq2000_can_use_return_insn ());
+
+  /* If struct value address is treated as the first argument.  */
+  if (aggregate_value_p (DECL_RESULT (fndecl))
+      && ! current_function_returns_pcc_struct
+      && struct_value_incoming_rtx == 0)
+    {
+      tree type = build_pointer_type (fntype);
+      tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
+
+      DECL_ARG_TYPE (function_result_decl) = type;
+      TREE_CHAIN (function_result_decl) = fnargs;
+      fnargs = function_result_decl;
+    }
+
+  /* For arguments passed in registers, find the register number
+     of the first argument in the variable part of the argument list,
+     otherwise GP_ARG_LAST+1.  Note also if the last argument is
+     the varargs special argument, and treat it as part of the
+     variable arguments.
+
+     This is only needed if store_args_on_stack is true.  */
+
+  INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
+  regno = GP_ARG_FIRST;
+
+  for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
+    {
+      tree passed_type = DECL_ARG_TYPE (cur_arg);
+      enum machine_mode passed_mode = TYPE_MODE (passed_type);
+      rtx entry_parm;
+
+      if (TREE_ADDRESSABLE (passed_type))
+       {
+         passed_type = build_pointer_type (passed_type);
+         passed_mode = Pmode;
+       }
+
+      entry_parm = FUNCTION_ARG (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);
+
+      if (entry_parm && store_args_on_stack)
+       {
+         if (next_arg == 0
+             && DECL_NAME (cur_arg)
+             && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
+                               "__builtin_va_alist"))
+                 || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
+                                  "va_alist"))))
+           {
+             last_arg_is_vararg_marker = 1;
+             break;
+           }
+         else
+           {
+             int words;
+
+             if (GET_CODE (entry_parm) != REG)
+               abort ();
+
+             /* passed in a register, so will get homed automatically */
+             if (GET_MODE (entry_parm) == BLKmode)
+               words = (int_size_in_bytes (passed_type) + 3) / 4;
+             else
+               words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
+
+             regno = REGNO (entry_parm) + words - 1;
+           }
+       }
+      else
+       {
+         regno = GP_ARG_LAST+1;
+         break;
+       }
+    }
+
+  /* In order to pass small structures by value in registers we need to
+     shift the value into the high part of the register.
+     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
+     insns, and emit them separately.  */
+
+  next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
+  if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
+    {
+      rtvec adjust = XVEC (next_arg_reg, 0);
+      int num = GET_NUM_ELEM (adjust);
+
+      for (i = 0; i < num; i++)
+       {
+         rtx insn, pattern;
+
+         pattern = RTVEC_ELT (adjust, i);
+         if (GET_CODE (pattern) != SET
+             || GET_CODE (SET_SRC (pattern)) != ASHIFT)
+           abort_with_insn (pattern, "Insn is not a shift");
+         PUT_CODE (SET_SRC (pattern), ASHIFTRT);
+
+         insn = emit_insn (pattern);
+
+         /* Global life information isn't valid at this point, so we
+            can't check whether these shifts are actually used.  Mark
+            them MAYBE_DEAD so that flow2 will remove them, and not
+            complain about dead code in the prologue.  */
+         REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
+                                              REG_NOTES (insn));
+       }
+    }
+
+  tsize = compute_frame_size (get_frame_size ());
+
+  /* If this function is a varargs function, store any registers that
+     would normally hold arguments ($4 - $7) on the stack.  */
+  if (store_args_on_stack
+      && ((TYPE_ARG_TYPES (fntype) != 0
+          && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
+              != void_type_node))
+         || last_arg_is_vararg_marker))
+    {
+      int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
+      rtx ptr = stack_pointer_rtx;
+
+      for (; regno <= GP_ARG_LAST; regno++)
+       {
+         if (offset != 0)
+           ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset));
+         emit_move_insn (gen_rtx (MEM, gpr_mode, ptr),
+                         gen_rtx (REG, gpr_mode, regno));
+
+         offset += GET_MODE_SIZE (gpr_mode);
+       }
+    }
+
+  if (tsize > 0)
+    {
+      rtx tsize_rtx = GEN_INT (tsize);
+      rtx adjustment_rtx, insn, dwarf_pattern;
+
+      if (tsize > 32767)
+       {
+         adjustment_rtx = gen_rtx (REG, Pmode, IQ2000_TEMP1_REGNUM);
+         emit_move_insn (adjustment_rtx, tsize_rtx);
+       }
+      else
+       adjustment_rtx = tsize_rtx;
+
+      insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                   adjustment_rtx));
+
+      dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
+                                  plus_constant (stack_pointer_rtx, -tsize));
+
+      iq2000_annotate_frame_insn (insn, dwarf_pattern);
+
+      save_restore_insns (1);
+
+      if (frame_pointer_needed)
+       {
+         rtx insn = 0;
+
+         insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
+                                      stack_pointer_rtx));
+
+         if (insn)
+           RTX_FRAME_RELATED_P (insn) = 1;
+       }
+    }
+
+  emit_insn (gen_blockage ());
+}
+\f
+/* Expand the epilogue into a bunch of separate insns.  */
+
+void
+iq2000_expand_epilogue ()
+{
+  HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
+  rtx tsize_rtx = GEN_INT (tsize);
+  rtx tmp_rtx = (rtx)0;
+
+  if (iq2000_can_use_return_insn ())
+    {
+      emit_insn (gen_return ());
+      return;
+    }
+
+  if (tsize > 32767)
+    {
+      tmp_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM);
+      emit_move_insn (tmp_rtx, tsize_rtx);
+      tsize_rtx = tmp_rtx;
+    }
+
+  if (tsize > 0)
+    {
+      if (frame_pointer_needed)
+       {
+         emit_insn (gen_blockage ());
+
+         emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
+       }
+
+      save_restore_insns (0);
+
+      if (current_function_calls_eh_return)
+       {
+         rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
+         emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
+         tsize_rtx = eh_ofs;
+       }
+
+      emit_insn (gen_blockage ());
+
+      if (tsize != 0 || current_function_calls_eh_return)
+       {
+         emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                tsize_rtx));
+       }
+    }
+
+  if (current_function_calls_eh_return)
+    {
+      /* Perform the additional bump for __throw.  */
+      emit_move_insn (gen_rtx (REG, Pmode, HARD_FRAME_POINTER_REGNUM),
+                     stack_pointer_rtx);
+      emit_insn (gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode,
+                                                 HARD_FRAME_POINTER_REGNUM)));
+      emit_jump_insn (gen_eh_return_internal ());
+    }
+  else
+      emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
+                                                 GP_REG_FIRST + 31)));
+}
+
+void
+iq2000_expand_eh_return (address)
+     rtx address;
+{
+  HOST_WIDE_INT gp_offset = cfun->machine->frame.gp_sp_offset;
+  rtx scratch;
+
+  scratch = plus_constant (stack_pointer_rtx, gp_offset);
+  emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);
+}
+\f
+/* Return nonzero if this function is known to have a null epilogue.
+   This allows the optimizer to omit jumps to jumps if no stack
+   was created.  */
+
+int
+iq2000_can_use_return_insn ()
+{
+  if (! reload_completed)
+    return 0;
+
+  if (regs_ever_live[31] || profile_flag)
+    return 0;
+
+  if (cfun->machine->frame.initialized)
+    return cfun->machine->frame.total_size == 0;
+
+  return compute_frame_size (get_frame_size ()) == 0;
+}
+\f
+/* Returns non-zero if X contains a SYMBOL_REF.  */
+
+static int
+symbolic_expression_p (x)
+     rtx x;
+{
+  if (GET_CODE (x) == SYMBOL_REF)
+    return 1;
+
+  if (GET_CODE (x) == CONST)
+    return symbolic_expression_p (XEXP (x, 0));
+
+  if (GET_RTX_CLASS (GET_CODE (x)) == '1')
+    return symbolic_expression_p (XEXP (x, 0));
+
+  if (GET_RTX_CLASS (GET_CODE (x)) == 'c'
+      || GET_RTX_CLASS (GET_CODE (x)) == '2')
+    return (symbolic_expression_p (XEXP (x, 0))
+           || symbolic_expression_p (XEXP (x, 1)));
+
+  return 0;
+}
+
+/* Choose the section to use for the constant rtx expression X that has
+   mode MODE.  */
+
+static void
+iq2000_select_rtx_section (mode, x, align)
+     enum machine_mode mode;
+     rtx x ATTRIBUTE_UNUSED;
+     unsigned HOST_WIDE_INT align;
+{
+  /* For embedded applications, always put constants in read-only data,
+     in order to reduce RAM usage.  */
+      /* For embedded applications, always put constants in read-only data,
+         in order to reduce RAM usage.  */
+  mergeable_constant_section (mode, align, 0);
+}
+
+/* Choose the section to use for DECL.  RELOC is true if its value contains
+   any relocatable expression.
+
+   Some of the logic used here needs to be replicated in
+   ENCODE_SECTION_INFO in iq2000.h so that references to these symbols
+   are done correctly.  */
+
+static void
+iq2000_select_section (decl, reloc, align)
+     tree decl;
+     int reloc ATTRIBUTE_UNUSED;
+     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+{
+  if (TARGET_EMBEDDED_DATA)
+    {
+      /* For embedded applications, always put an object in read-only data
+        if possible, in order to reduce RAM usage.  */
+
+      if (((TREE_CODE (decl) == VAR_DECL
+           && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
+           && DECL_INITIAL (decl)
+           && (DECL_INITIAL (decl) == error_mark_node
+               || TREE_CONSTANT (DECL_INITIAL (decl))))
+          /* Deal with calls from output_constant_def_contents.  */
+          || (TREE_CODE (decl) != VAR_DECL
+              && (TREE_CODE (decl) != STRING_CST
+                  || !flag_writable_strings))))
+       readonly_data_section ();
+      else
+       data_section ();
+    }
+  else
+    {
+      /* For hosted applications, always put an object in small data if
+        possible, as this gives the best performance.  */
+
+      if (((TREE_CODE (decl) == VAR_DECL
+                && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
+                && DECL_INITIAL (decl)
+                && (DECL_INITIAL (decl) == error_mark_node
+                    || TREE_CONSTANT (DECL_INITIAL (decl))))
+               /* Deal with calls from output_constant_def_contents.  */
+               || (TREE_CODE (decl) != VAR_DECL
+                   && (TREE_CODE (decl) != STRING_CST
+                       || !flag_writable_strings))))
+       readonly_data_section ();
+      else
+       data_section ();
+    }
+}
+/* Return register to use for a function return value with VALTYPE for function
+   FUNC.  */
+
+rtx
+iq2000_function_value (valtype, func)
+     tree valtype;
+     tree func ATTRIBUTE_UNUSED;
+{
+  int reg = GP_RETURN;
+  enum machine_mode mode = TYPE_MODE (valtype);
+  int unsignedp = TREE_UNSIGNED (valtype);
+
+  /* Since we define PROMOTE_FUNCTION_RETURN, we must promote the mode
+     just as PROMOTE_MODE does.  */
+  mode = promote_mode (valtype, mode, &unsignedp, 1);
+
+  return gen_rtx_REG (mode, reg);
+}
+\f
+/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE.  Return
+   nonzero when an argument must be passed by reference.  */
+
+int
+function_arg_pass_by_reference (cum, mode, type, named)
+     CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
+     enum machine_mode mode;
+     tree type;
+     int named ATTRIBUTE_UNUSED;
+{
+  int size;
+
+  /* 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
+     handled incorrectly in this case.  */
+
+  if (cum && MUST_PASS_IN_STACK (mode, type))
+     {
+       /* Don't pass the actual CUM to FUNCTION_ARG, because we would
+         get double copies of any offsets generated for small structs
+         passed in registers.  */
+       CUMULATIVE_ARGS temp;
+       temp = *cum;
+       if (FUNCTION_ARG (temp, mode, type, named) != 0)
+        return 1;
+     }
+
+  if (type == NULL_TREE || mode == DImode || mode == DFmode)
+    return 0;
+
+  size = int_size_in_bytes (type);
+  return size == -1 || size > UNITS_PER_WORD;
+}
+
+/* Return the length of INSN.  LENGTH is the initial length computed by
+   attributes in the machine-description file.  */
+
+int
+iq2000_adjust_insn_length (insn, length)
+     rtx insn;
+     int length;
+{
+  /* A unconditional jump has an unfilled delay slot if it is not part
+     of a sequence.  A conditional jump normally has a delay slot */
+  if (simplejump_p (insn)
+      || ((GET_CODE (insn) == JUMP_INSN
+                             || GET_CODE (insn) == CALL_INSN)))
+    length += 4;
+
+  return length;
+}
+
+/* Output assembly instructions to perform a conditional branch.
+
+   INSN is the branch instruction.  OPERANDS[0] is the condition.
+   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
+   non-zero the comparison takes two operands; OPERANDS[3] will be the
+   second operand.
+
+   If INVERTED_P is non-zero we are to branch if the condition does
+   not hold.  If FLOAT_P is non-zero this is a floating-point comparison.
+
+   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
+   reversed conditional branch around a `jr' instruction.  */
+
+char *
+iq2000_output_conditional_branch (insn,
+                               operands,
+                               two_operands_p,
+                               float_p,
+                               inverted_p,
+                               length)
+     rtx insn;
+     rtx *operands;
+     int two_operands_p;
+     int float_p;
+     int inverted_p;
+     int length;
+{
+  static char buffer[200];
+  /* The kind of comparison we are doing.  */
+  enum rtx_code code = GET_CODE (operands[0]);
+  /* Non-zero if the opcode for the comparison needs a `z' indicating
+     that it is a comparision against zero.  */
+  int need_z_p;
+  /* A string to use in the assembly output to represent the first
+     operand.  */
+  const char *op1 = "%z2";
+  /* 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.  */
+  const char *op2 = (two_operands_p ? ",%z3" : ",%.");
+  /* The operand-printing string for the comparison.  */
+  const char *comp = (float_p ? "%F0" : "%C0");
+  /* The operand-printing string for the inverted comparison.  */
+  const char *inverted_comp = (float_p ? "%W0" : "%N0");
+
+  /* likely variants of each branch instruction annul the instruction
+     in the delay slot if the branch is not taken.  */
+  iq2000_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+
+  if (!two_operands_p)
+    {
+      /* To compute whether than A > B, for example, we normally
+        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
+        have to do the subtraction.  Instead, we can just check to
+        see if A is non-zero.  Thus, we change the CODE here to
+        reflect the simpler comparison operation.  */
+      switch (code)
+       {
+       case GTU:
+         code = NE;
+         break;
+
+       case LEU:
+         code = EQ;
+         break;
+
+       case GEU:
+         /* A condition which will always be true.  */
+         code = EQ;
+         op1 = "%.";
+         break;
+
+       case LTU:
+         /* A condition which will always be false.  */
+         code = NE;
+         op1 = "%.";
+         break;
+
+       default:
+         /* Not a special case.  */
+         break;
+       }
+    }
+
+  /* Relative comparisons are always done against zero.  But
+     equality comparisons are done between two operands, and therefore
+     do not require a `z' in the assembly language output.  */
+  need_z_p = (!float_p && code != EQ && code != NE);
+  /* For comparisons against zero, the zero is not provided
+     explicitly.  */
+  if (need_z_p)
+    op2 = "";
+
+  /* Begin by terminating the buffer.  That way we can always use
+     strcat to add to it.  */
+  buffer[0] = '\0';
+
+  switch (length)
+    {
+    case 4:
+    case 8:
+      /* Just a simple conditional branch.  */
+      if (float_p)
+       sprintf (buffer, "b%s%%?\t%%Z2%%1",
+                inverted_p ? inverted_comp : comp);
+      else
+       sprintf (buffer, "b%s%s%%?\t%s%s,%%1",
+                inverted_p ? inverted_comp : comp,
+                need_z_p ? "z" : "",
+                op1,
+                op2);
+      return buffer;
+
+    case 12:
+    case 16:
+      {
+       /* Generate a reversed conditional branch around ` j'
+          instruction:
+
+               .set noreorder
+               .set nomacro
+               bc    l
+               nop
+               j     target
+               .set macro
+               .set reorder
+            l:
+
+          Because we have to jump four bytes *past* the following
+          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
+          label after the next instruction, as the assembler does not
+          accept `.L+4' as the target of a branch.  (We can't just
+          wait until the next instruction is output; it might be a
+          macro and take up more than four bytes.  Once again, we see
+          why we want to eliminate macros.)
+
+          If the branch is annulled, we jump four more bytes that we
+          would otherwise; that way we skip the annulled instruction
+          in the delay slot.  */
+
+       const char *target
+         = ((iq2000_branch_likely || length == 16) ? ".+16" : ".+12");
+       char *c;
+
+       c = strchr (buffer, '\0');
+       /* Generate the reversed comparision.  This takes four
+          bytes.  */
+       if (float_p)
+         sprintf (c, "b%s\t%%Z2%s",
+                  inverted_p ? comp : inverted_comp,
+                  target);
+       else
+         sprintf (c, "b%s%s\t%s%s,%s",
+                  inverted_p ? comp : inverted_comp,
+                  need_z_p ? "z" : "",
+                  op1,
+                  op2,
+                  target);
+       strcat (c, "\n\tnop\n\tj\t%1");
+       if (length == 16)
+         /* The delay slot was unfilled.  Since we're inside
+            .noreorder, the assembler will not fill in the NOP for
+            us, so we must do it ourselves.  */
+         strcat (buffer, "\n\tnop");
+       return buffer;
+      }
+
+    default:
+      abort ();
+    }
+
+  /* NOTREACHED */
+  return 0;
+}
+
+static enum processor_type
+iq2000_parse_cpu (cpu_string)
+     const char *cpu_string;
+{
+  const char *p = cpu_string;
+  enum processor_type cpu;
+
+  cpu = PROCESSOR_DEFAULT;
+  switch (p[2])
+    {
+    case '1':
+      if (!strcmp (p, "iq10"))
+       cpu = PROCESSOR_IQ10;
+      break;
+    case '2':
+      if (!strcmp (p, "iq2000"))
+       cpu = PROCESSOR_IQ2000;
+      break;
+    }
+
+  return cpu;
+}
+
+#define def_builtin(NAME, TYPE, CODE) \
+  builtin_function ((NAME), (TYPE), (CODE), BUILT_IN_MD, NULL, NULL_TREE)
+
+void
+iq2000_init_builtins ()
+{
+  tree endlink = void_list_node;
+  tree void_ftype, void_ftype_int, void_ftype_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_int_int;
+
+  /* func () */
+  void_ftype
+    = build_function_type (void_type_node,
+                          tree_cons (NULL_TREE, void_type_node, endlink));
+
+  /* func (int) */
+  void_ftype_int
+    = build_function_type (void_type_node,
+                          tree_cons (NULL_TREE, integer_type_node, endlink));
+
+  /* void func (int, int) */
+  void_ftype_int_int
+    = build_function_type (void_type_node,
+                           tree_cons (NULL_TREE, integer_type_node,
+                                      tree_cons (NULL_TREE, integer_type_node,
+                                                 endlink)));
+
+  /* int func (int) */
+  int_ftype_int
+    = build_function_type (integer_type_node,
+                           tree_cons (NULL_TREE, integer_type_node, endlink));
+
+  /* int func (int, int) */
+  int_ftype_int_int
+    = build_function_type (integer_type_node,
+                           tree_cons (NULL_TREE, integer_type_node,
+                                      tree_cons (NULL_TREE, integer_type_node,
+                                                 endlink)));
+
+  /* void func (int, int, int) */
+void_ftype_int_int_int
+    = 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,
+                                     endlink))));
+
+  /* int func (int, int, int, int) */
+  int_ftype_int_int_int_int
+    = 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)))));
+
+  /* int func (int, int, int) */
+  int_ftype_int_int_int
+    = 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,
+                                     endlink))));
+
+  /* int func (int, int, int, int) */
+  int_ftype_int_int_int_int
+    = 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)))));
+
+  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_chkhdr", void_ftype_int_int, IQ2000_BUILTIN_CHKHDR);
+  def_builtin ("__builtin_pkrl", void_ftype_int_int, IQ2000_BUILTIN_PKRL);
+  def_builtin ("__builtin_cfc0", int_ftype_int, IQ2000_BUILTIN_CFC0);
+  def_builtin ("__builtin_cfc1", int_ftype_int, IQ2000_BUILTIN_CFC1);
+  def_builtin ("__builtin_cfc2", int_ftype_int, IQ2000_BUILTIN_CFC2);
+  def_builtin ("__builtin_cfc3", int_ftype_int, IQ2000_BUILTIN_CFC3);
+  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_ctc2", void_ftype_int_int, IQ2000_BUILTIN_CTC2);
+  def_builtin ("__builtin_ctc3", void_ftype_int_int, IQ2000_BUILTIN_CTC3);
+  def_builtin ("__builtin_mfc0", int_ftype_int, IQ2000_BUILTIN_MFC0);
+  def_builtin ("__builtin_mfc1", int_ftype_int, IQ2000_BUILTIN_MFC1);
+  def_builtin ("__builtin_mfc2", int_ftype_int, IQ2000_BUILTIN_MFC2);
+  def_builtin ("__builtin_mfc3", int_ftype_int, IQ2000_BUILTIN_MFC3);
+  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_mtc2", void_ftype_int_int, IQ2000_BUILTIN_MTC2);
+  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_rb", void_ftype_int_int, IQ2000_BUILTIN_RB);
+  def_builtin ("__builtin_rx", void_ftype_int_int, IQ2000_BUILTIN_RX);
+  def_builtin ("__builtin_srrd", void_ftype_int, IQ2000_BUILTIN_SRRD);
+  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_wx", void_ftype_int_int, IQ2000_BUILTIN_WX);
+  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_luc64l", void_ftype_int_int, IQ2000_BUILTIN_LUC64L);
+  def_builtin ("__builtin_luk", void_ftype_int_int, IQ2000_BUILTIN_LUK);
+  def_builtin ("__builtin_lulck", void_ftype_int, IQ2000_BUILTIN_LULCK);
+  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_lum64", void_ftype_int_int, IQ2000_BUILTIN_LUM64);
+  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_mrgb", int_ftype_int_int_int, IQ2000_BUILTIN_MRGB);
+  def_builtin ("__builtin_srrdl", void_ftype_int, IQ2000_BUILTIN_SRRDL);
+  def_builtin ("__builtin_srulck", void_ftype_int, IQ2000_BUILTIN_SRULCK);
+  def_builtin ("__builtin_srwru", void_ftype_int_int, IQ2000_BUILTIN_SRWRU);
+  def_builtin ("__builtin_trapqfl", void_ftype, IQ2000_BUILTIN_TRAPQFL);
+  def_builtin ("__builtin_trapqne", void_ftype, IQ2000_BUILTIN_TRAPQNE);
+  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_syscall", void_ftype, IQ2000_BUILTIN_SYSCALL);
+}
+
+/* Builtin for ICODE having ARGCOUNT args in ARGLIST where each arg
+   has an rtx CODE */
+
+static rtx
+expand_one_builtin (icode, target, arglist, code, argcount)
+  enum insn_code icode;
+  rtx target;
+  tree arglist;
+  enum rtx_code *code;
+  int argcount;
+{
+  rtx pat;
+  tree arg [5];
+  rtx op [5];
+  enum machine_mode mode [5];
+  int i;
+
+  mode[0] = insn_data[icode].operand[0].mode;
+  for (i = 0; i < argcount; i++)
+    {
+      arg[i] = TREE_VALUE (arglist);
+      arglist = TREE_CHAIN (arglist);
+      op[i] = expand_expr (arg[i], NULL_RTX, VOIDmode, 0);
+      mode[i] = insn_data[icode].operand[i].mode;
+      if (code[i] == CONST_INT && GET_CODE (op[i]) != CONST_INT)
+       error ("argument `%d' is not a constant", i + 1);
+      if (code[i] == REG
+         && ! (*insn_data[icode].operand[i].predicate) (op[i], mode[i]))
+       op[i] = copy_to_mode_reg (mode[i], op[i]);
+    }
+
+  if (insn_data[icode].operand[0].constraint[0] == '=')
+    {
+      if (target == 0
+         || GET_MODE (target) != mode[0]
+         || ! (*insn_data[icode].operand[0].predicate) (target, mode[0]))
+       target = gen_reg_rtx (mode[0]);
+    }
+  else
+    target = 0;
+
+  switch (argcount)
+    {
+    case 0:
+       pat = GEN_FCN (icode) (target);
+    case 1:
+      if (target)
+       pat = GEN_FCN (icode) (target, op[0]);
+      else
+       pat = GEN_FCN (icode) (op[0]);
+      break;
+    case 2:
+      if (target)
+       pat = GEN_FCN (icode) (target, op[0], op[1]);
+      else
+       pat = GEN_FCN (icode) (op[0], op[1]);
+      break;
+    case 3:
+      if (target)
+       pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
+      else
+       pat = GEN_FCN (icode) (op[0], op[1], op[2]);
+      break;
+    case 4:
+      if (target)
+       pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
+      else
+       pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
+      break;
+    default:
+      abort ();
+    }
+  
+  if (! pat)
+    return 0;
+  emit_insn (pat);
+  return target;
+}
+
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET 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.
+   IGNORE is nonzero if the value is to be ignored.  */
+
+rtx
+iq2000_expand_builtin (exp, target, subtarget, mode, ignore)
+     tree exp;
+     rtx target;
+     rtx subtarget ATTRIBUTE_UNUSED;
+     enum machine_mode mode ATTRIBUTE_UNUSED;
+     int ignore ATTRIBUTE_UNUSED;
+{
+  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  tree arglist = TREE_OPERAND (exp, 1);
+  int fcode = DECL_FUNCTION_CODE (fndecl);
+  enum rtx_code code [5];
+
+  code[0] = REG;
+  code[1] = REG;
+  code[2] = REG;
+  code[3] = REG;
+  code[4] = REG;
+  switch (fcode)
+    {
+    default:
+      break;
+      
+    case IQ2000_BUILTIN_ADO16:
+      return expand_one_builtin (CODE_FOR_ado16, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_RAM:
+      code[1] = CONST_INT;
+      code[2] = CONST_INT;
+      code[3] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_ram, target, arglist, code, 4);
+      
+    case IQ2000_BUILTIN_CHKHDR:
+      return expand_one_builtin (CODE_FOR_chkhdr, target, arglist, code, 2);
+      
+    case IQ2000_BUILTIN_PKRL:
+      return expand_one_builtin (CODE_FOR_pkrl, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_CFC0:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_cfc0, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_CFC1:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_cfc1, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_CFC2:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_cfc2, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_CFC3:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_cfc3, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_CTC0:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_ctc0, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_CTC1:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_ctc1, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_CTC2:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_ctc2, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_CTC3:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_ctc3, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_MFC0:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mfc0, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_MFC1:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mfc1, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_MFC2:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mfc2, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_MFC3:
+      code[0] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mfc3, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_MTC0:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mtc0, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_MTC1:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mtc1, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_MTC2:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mtc2, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_MTC3:
+      code[1] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mtc3, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUR:
+      return expand_one_builtin (CODE_FOR_lur, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_RB:
+      return expand_one_builtin (CODE_FOR_rb, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_RX:
+      return expand_one_builtin (CODE_FOR_rx, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_SRRD:
+      return expand_one_builtin (CODE_FOR_srrd, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_SRWR:
+      return expand_one_builtin (CODE_FOR_srwr, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_WB:
+      return expand_one_builtin (CODE_FOR_wb, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_WX:
+      return expand_one_builtin (CODE_FOR_wx, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUC32L:
+      return expand_one_builtin (CODE_FOR_luc32l, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUC64:
+      return expand_one_builtin (CODE_FOR_luc64, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUC64L:
+      return expand_one_builtin (CODE_FOR_luc64l, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUK:
+      return expand_one_builtin (CODE_FOR_luk, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LULCK:
+      return expand_one_builtin (CODE_FOR_lulck, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_LUM32:
+      return expand_one_builtin (CODE_FOR_lum32, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUM32L:
+      return expand_one_builtin (CODE_FOR_lum32l, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUM64:
+      return expand_one_builtin (CODE_FOR_lum64, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LUM64L:
+      return expand_one_builtin (CODE_FOR_lum64l, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_LURL:
+      return expand_one_builtin (CODE_FOR_lurl, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_MRGB:
+      code[2] = CONST_INT;
+      return expand_one_builtin (CODE_FOR_mrgb, target, arglist, code, 3);
+
+    case IQ2000_BUILTIN_SRRDL:
+      return expand_one_builtin (CODE_FOR_srrdl, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_SRULCK:
+      return expand_one_builtin (CODE_FOR_srulck, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_SRWRU:
+      return expand_one_builtin (CODE_FOR_srwru, target, arglist, code, 2);
+
+    case IQ2000_BUILTIN_TRAPQFL:
+      return expand_one_builtin (CODE_FOR_trapqfl, target, arglist, code, 0);
+
+    case IQ2000_BUILTIN_TRAPQNE:
+      return expand_one_builtin (CODE_FOR_trapqne, target, arglist, code, 0);
+
+    case IQ2000_BUILTIN_TRAPREL:
+      return expand_one_builtin (CODE_FOR_traprel, target, arglist, code, 1);
+
+    case IQ2000_BUILTIN_WBU:
+      return expand_one_builtin (CODE_FOR_wbu, target, arglist, code, 3);
+
+    case IQ2000_BUILTIN_SYSCALL:
+      return expand_one_builtin (CODE_FOR_syscall, target, arglist, code, 0);
+    }
+  
+  return NULL_RTX;
+}
+\f
+void
+iq2000_setup_incoming_varargs (cum, mode, type, pretend_size, no_rtl) 
+     CUMULATIVE_ARGS cum;
+     int             mode ATTRIBUTE_UNUSED;
+     tree            type ATTRIBUTE_UNUSED;
+     int *           pretend_size;
+     int             no_rtl;
+{
+  unsigned int iq2000_off = (! (cum).last_arg_fp); 
+  unsigned int iq2000_fp_off = ((cum).last_arg_fp); 
+  if (((cum).arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off))
+    {
+      int iq2000_save_gp_regs 
+       = MAX_ARGS_IN_REGISTERS - (cum).arg_words - iq2000_off; 
+      int iq2000_save_fp_regs 
+        = (MAX_ARGS_IN_REGISTERS - (cum).fp_arg_words - iq2000_fp_off); 
+
+      if (iq2000_save_gp_regs < 0) 
+       iq2000_save_gp_regs = 0; 
+      if (iq2000_save_fp_regs < 0) 
+       iq2000_save_fp_regs = 0; 
+
+      *pretend_size = ((iq2000_save_gp_regs * UNITS_PER_WORD) 
+                      + (iq2000_save_fp_regs * UNITS_PER_FPREG)); 
+
+      if (! (no_rtl)) 
+       {
+         if ((cum).arg_words < MAX_ARGS_IN_REGISTERS - iq2000_off) 
+           {
+             rtx ptr, mem; 
+             ptr = plus_constant (virtual_incoming_args_rtx, 
+                                  - (iq2000_save_gp_regs 
+                                     * UNITS_PER_WORD)); 
+             mem = gen_rtx_MEM (BLKmode, ptr); 
+             move_block_from_reg 
+               ((cum).arg_words + GP_ARG_FIRST + iq2000_off, 
+                mem, 
+                iq2000_save_gp_regs);
+           } 
+       } 
+    }
+}
+\f
+/* A C compound statement to output to stdio stream STREAM the
+   assembler syntax for an instruction operand that is a memory
+   reference whose address is ADDR.  ADDR is an RTL expression.
+*/
+
+void
+print_operand_address (file, addr)
+     FILE *file;
+     rtx addr;
+{
+  if (!addr)
+    error ("PRINT_OPERAND_ADDRESS, null pointer");
+
+  else
+    switch (GET_CODE (addr))
+      {
+      case REG:
+       if (REGNO (addr) == ARG_POINTER_REGNUM)
+         abort_with_insn (addr, "Arg pointer not eliminated.");
+
+       fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
+       break;
+
+      case LO_SUM:
+       {
+         register rtx arg0 = XEXP (addr, 0);
+         register rtx arg1 = XEXP (addr, 1);
+
+         if (GET_CODE (arg0) != REG)
+           abort_with_insn (addr,
+                            "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG.");
+
+         fprintf (file, "%%lo(");
+         print_operand_address (file, arg1);
+         fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
+       }
+       break;
+
+      case PLUS:
+       {
+         register rtx reg = 0;
+         register rtx offset = 0;
+         register rtx arg0 = XEXP (addr, 0);
+         register rtx arg1 = XEXP (addr, 1);
+
+         if (GET_CODE (arg0) == REG)
+           {
+             reg = arg0;
+             offset = arg1;
+             if (GET_CODE (offset) == REG)
+               abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
+           }
+
+         else if (GET_CODE (arg1) == REG)
+             reg = arg1, offset = arg0;
+         else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
+           {
+             output_addr_const (file, addr);
+             break;
+           }
+         else
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
+
+         if (! CONSTANT_P (offset))
+           abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
+
+         if (REGNO (reg) == ARG_POINTER_REGNUM)
+           abort_with_insn (addr, "Arg pointer not eliminated.");
+
+         output_addr_const (file, offset);
+         fprintf (file, "(%s)", reg_names [REGNO (reg)]);
+       }
+       break;
+
+      case LABEL_REF:
+      case SYMBOL_REF:
+      case CONST_INT:
+      case CONST:
+       output_addr_const (file, addr);
+       if (GET_CODE (addr) == CONST_INT)
+         fprintf (file, "(%s)", reg_names [0]);
+       break;
+
+      default:
+       abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
+       break;
+    }
+}
+\f
+/* A C compound statement to output to stdio stream STREAM the
+   assembler syntax for an instruction operand X.  X is an RTL
+   expression.
+
+   CODE is a value that can be used to specify one of several ways
+   of printing the operand.  It is used when identical operands
+   must be printed differently depending on the context.  CODE
+   comes from the `%' specification that was used to request
+   printing of the operand.  If the specification was just `%DIGIT'
+   then CODE is 0; if the specification was `%LTR DIGIT' then CODE
+   is the ASCII code for LTR.
+
+   If X is a register, this macro should print the register's name.
+   The names can be found in an array `reg_names' whose type is
+   `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
+
+   When the machine description has a specification `%PUNCT' (a `%'
+   followed by a punctuation character), this macro is called with
+   a null pointer for X and the punctuation character for CODE.
+
+   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 lower 16 bits in hexadecimal format = "0x%04x",
+   'd'  output integer constant in decimal,
+   'z' if the operand is 0, use $0 instead of normal operand.
+   'D'  print second part of double-word register or memory operand.
+   'L'  print low-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.
+   'F'  print part of opcode for a floating-point branch condition.
+   'N'  print part of opcode for a 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.
+   'P'  Print label for a bit test.
+   'p'  Print log for a bit test.
+   'B'  print 'z' for EQ, 'n' for NE
+   'b'  print 'n' for EQ, 'z' for NE
+   'T'  print 'f' for EQ, 't' for NE
+   't'  print 't' for EQ, 'f' for NE
+   '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 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 stack pointer register (sp or $29).
+   '+' Print the name of the gp register (gp or $28).  */
+
+void
+print_operand (file, op, letter)
+     FILE *file;               /* file to write to */
+     rtx op;                   /* operand to print */
+     int letter;               /* %<letter> or 0 */
+{
+  register enum rtx_code code;
+
+  if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+    {
+      switch (letter)
+       {
+       case '?':
+         if (iq2000_branch_likely)
+           putc ('l', file);
+         break;
+
+       case '@':
+         fputs (reg_names [GP_REG_FIRST + 1], file);
+         break;
+
+       case '.':
+         fputs (reg_names [GP_REG_FIRST + 0], file);
+         break;
+
+       case '$':
+         fputs (reg_names[STACK_POINTER_REGNUM], file);
+         break;
+
+       case '+':
+         fputs (reg_names[GP_REG_FIRST + 28], file);
+         break;
+
+       default:
+         error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
+         break;
+       }
+
+      return;
+    }
+
+  if (! op)
+    {
+      error ("PRINT_OPERAND null pointer");
+      return;
+    }
+
+  code = GET_CODE (op);
+
+  if (code == SIGN_EXTEND)
+    op = XEXP (op, 0), code = GET_CODE (op);
+
+  if (letter == 'C')
+    switch (code)
+      {
+      case EQ: fputs ("eq",  file); break;
+      case NE: fputs ("ne",  file); break;
+      case GT: fputs ("gt",  file); break;
+      case GE: fputs ("ge",  file); break;
+      case LT: fputs ("lt",  file); break;
+      case LE: fputs ("le",  file); break;
+      case GTU: fputs ("ne", file); break;
+      case GEU: fputs ("geu", file); break;
+      case LTU: fputs ("ltu", file); break;
+      case LEU: fputs ("eq", file); break;
+      default:
+       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
+      }
+
+  else if (letter == 'N')
+    switch (code)
+      {
+      case EQ: fputs ("ne",  file); break;
+      case NE: fputs ("eq",  file); break;
+      case GT: fputs ("le",  file); break;
+      case GE: fputs ("lt",  file); break;
+      case LT: fputs ("ge",  file); break;
+      case LE: fputs ("gt",  file); break;
+      case GTU: fputs ("leu", file); break;
+      case GEU: fputs ("ltu", file); break;
+      case LTU: fputs ("geu", file); break;
+      case LEU: fputs ("gtu", file); break;
+      default:
+       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
+      }
+
+  else if (letter == 'F')
+    switch (code)
+      {
+      case EQ: fputs ("c1f", file); break;
+      case NE: fputs ("c1t", file); break;
+      default:
+       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
+      }
+
+  else if (letter == 'W')
+    switch (code)
+      {
+      case EQ: fputs ("c1t", file); break;
+      case NE: fputs ("c1f", file); break;
+      default:
+       abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
+      }
+
+  else if (letter == 'A')
+    fputs (code == LABEL_REF ? "i" : "in", file);
+
+  else if (letter == 'P')
+    {
+      if (code == LABEL_REF)
+       output_addr_const (file, op);
+      else if (code != PC)
+       output_operand_lossage ("invalid %%P operand");
+    }
+
+  else if (letter == 'p')
+    {
+      int value;
+      if (code != CONST_INT
+         || (value = exact_log2 (INTVAL (op))) < 0)
+       output_operand_lossage ("invalid %%p value");
+      fprintf (file, "%d", value);
+    }
+
+  else if (letter == 'Z')
+    {
+      register int regnum;
+
+      if (code != REG)
+       abort ();
+
+      regnum = REGNO (op);
+      abort ();
+
+      fprintf (file, "%s,", reg_names[regnum]);
+    }
+
+  else if (code == REG || code == SUBREG)
+    {
+      register int regnum;
+
+      if (code == REG)
+       regnum = REGNO (op);
+      else
+       regnum = true_regnum (op);
+
+      if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
+         || (letter == 'L' && WORDS_BIG_ENDIAN)
+         || letter == 'D')
+       regnum++;
+
+      fprintf (file, "%s", reg_names[regnum]);
+    }
+
+  else if (code == MEM)
+    {
+      if (letter == 'D')
+       output_address (plus_constant (XEXP (op, 0), 4));
+      else
+       output_address (XEXP (op, 0));
+    }
+
+  else if (code == CONST_DOUBLE
+          && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
+    {
+      char s[60];
+
+      real_to_decimal (s, CONST_DOUBLE_REAL_VALUE (op), sizeof (s), 0, 1);
+      fputs (s, file);
+    }
+
+  else if (letter == 'x' && GET_CODE (op) == CONST_INT)
+    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
+
+  else if (letter == 'X' && GET_CODE(op) == CONST_INT)
+    fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & (INTVAL (op) >> 16));
+
+  else if (letter == 'd' && GET_CODE(op) == CONST_INT)
+    fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
+
+  else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
+    fputs (reg_names[GP_REG_FIRST], file);
+
+  else if (letter == 'd' || letter == 'x' || letter == 'X')
+    output_operand_lossage ("invalid use of %%d, %%x, or %%X");
+
+  else if (letter == 'B')
+    fputs (code == EQ ? "z" : "n", file);
+  else if (letter == 'b')
+    fputs (code == EQ ? "n" : "z", file);
+  else if (letter == 'T')
+    fputs (code == EQ ? "f" : "t", file);
+  else if (letter == 't')
+    fputs (code == EQ ? "t" : "f", file);
+
+  else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
+    {
+      print_operand (file, XEXP (op, 0), letter);
+    }
+
+  else
+    output_addr_const (file, op);
+}
diff --git a/gcc/config/iq2000/iq2000.h b/gcc/config/iq2000/iq2000.h
new file mode 100644 (file)
index 0000000..4f4bf39
--- /dev/null
@@ -0,0 +1,1486 @@
+/* Definitions of target machine for GNU compiler.  
+   Vitesse IQ2000 processors
+   Copyright (C) 2003 Free Software Foundation, Inc.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it
+   under the terms of the GNU General Public License as published
+   by the Free Software Foundation; either version 2, or (at your
+   option) any later version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT
+   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
+   License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING.  If not, write to the Free
+   Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA.  */
+
+/* Set up System V.4 (aka ELF) defaults.  */
+#include "svr4.h"
+#include "elfos.h"
+
+\f
+/* Driver configuration.  */
+
+#undef SWITCH_TAKES_ARG
+#define SWITCH_TAKES_ARG(CHAR)                                         \
+  (DEFAULT_SWITCH_TAKES_ARG (CHAR) || (CHAR) == 'G')
+
+/* The svr4.h LIB_SPEC with -leval and --*group tacked on */
+#undef  LIB_SPEC
+#define LIB_SPEC "%{!shared:%{!symbolic:--start-group -lc -leval -lgcc --end-group}}"
+
+#undef STARTFILE_SPEC
+#undef ENDFILE_SPEC
+
+\f
+/* Run-time target specifications.  */
+
+#define TARGET_CPU_CPP_BUILTINS()               \
+  do                                            \
+    {                                           \
+     builtin_define ("__iq2000__"); \
+     builtin_assert ("cpu=iq2000"); \
+     builtin_assert ("machine=iq2000"); \
+    }                                           \
+  while (0)
+
+
+extern int     target_flags;
+
+#define MASK_GPOPT         0x00000008   /* Optimize for global pointer */
+#define MASK_EMBEDDED_DATA 0x00008000   /* Reduce RAM usage, not fast code */
+#define MASK_UNINIT_CONST_IN_RODATA \
+                           0x00800000   /* Store uninitialized
+                                           consts in rodata */
+
+/* Macros used in the machine description to test the flags.  */
+
+#define TARGET_STATS           0
+
+                                       /* for embedded systems, optimize for
+                                          reduced RAM space instead of for
+                                          fastest code.  */
+#define TARGET_EMBEDDED_DATA   (target_flags & MASK_EMBEDDED_DATA)
+
+#define TARGET_DEBUG_MODE      (target_flags & 0)
+#define TARGET_DEBUG_A_MODE    (target_flags & 0)
+#define TARGET_DEBUG_B_MODE    (target_flags & 0)
+#define TARGET_DEBUG_C_MODE    (target_flags & 0)
+#define TARGET_DEBUG_D_MODE    (target_flags & 0)
+
+#define TARGET_SWITCHES                                                        \
+{                                                                      \
+  {"no-crt0",          0,                                               \
+     N_("No default crt0.o") },                                                \
+  {"gpopt",              MASK_GPOPT,                                   \
+     N_("Use GP relative sdata/sbss sections")},                       \
+  {"no-gpopt",          -MASK_GPOPT,                                   \
+     N_("Don't use GP relative sdata/sbss sections")},                 \
+  {"embedded-data",      MASK_EMBEDDED_DATA,                           \
+     N_("Use ROM instead of RAM")},                                    \
+  {"no-embedded-data",  -MASK_EMBEDDED_DATA,                           \
+     N_("Don't use ROM instead of RAM")},                              \
+  {"uninit-const-in-rodata", MASK_UNINIT_CONST_IN_RODATA,              \
+     N_("Put uninitialized constants in ROM (needs -membedded-data)")},        \
+  {"no-uninit-const-in-rodata", -MASK_UNINIT_CONST_IN_RODATA,          \
+     N_("Don't put uninitialized constants in ROM")},                  \
+  {"",                   (TARGET_DEFAULT                               \
+                          | TARGET_CPU_DEFAULT),                       \
+     NULL},                                                            \
+}
+
+/* Default target_flags if no switches are specified.  */
+
+#define TARGET_DEFAULT 0
+
+#ifndef TARGET_CPU_DEFAULT
+#define TARGET_CPU_DEFAULT 0
+#endif
+
+#ifndef IQ2000_ISA_DEFAULT
+#define IQ2000_ISA_DEFAULT 1
+#endif
+
+#define TARGET_OPTIONS                                                 \
+{                                                                      \
+  SUBTARGET_TARGET_OPTIONS                                             \
+  { "cpu=",    & iq2000_cpu_string,                                    \
+      N_("Specify CPU for scheduling purposes")},                      \
+  { "arch=",    & iq2000_arch_string,                                   \
+      N_("Specify CPU for code generation purposes")},                  \
+}
+
+/* This is meant to be redefined in the host dependent files.  */
+#define SUBTARGET_TARGET_OPTIONS
+
+#define IQ2000_VERSION "[1.0]"
+
+#ifndef MACHINE_TYPE
+#define MACHINE_TYPE "IQ2000"
+#endif
+
+#ifndef TARGET_VERSION_INTERNAL
+#define TARGET_VERSION_INTERNAL(STREAM)                                        \
+  fprintf (STREAM, " %s %s", IQ2000_VERSION, MACHINE_TYPE)
+#endif
+
+#ifndef TARGET_VERSION
+#define TARGET_VERSION TARGET_VERSION_INTERNAL (stderr)
+#endif
+
+#define OVERRIDE_OPTIONS override_options ()
+
+#define CAN_DEBUG_WITHOUT_FP
+\f
+/* Storage Layout.  */
+
+#define BITS_BIG_ENDIAN 0
+
+#define BYTES_BIG_ENDIAN 1 
+
+#define WORDS_BIG_ENDIAN 1
+
+#define LIBGCC2_WORDS_BIG_ENDIAN 1
+
+#define BITS_PER_UNIT 8
+
+#define BITS_PER_WORD 32
+
+#define MAX_BITS_PER_WORD 64
+
+#define UNITS_PER_WORD 4
+
+#define MIN_UNITS_PER_WORD 4
+
+#define POINTER_SIZE 32
+
+/* Define this macro if it is advisable to hold scalars in registers
+   in a wider mode than that declared by the program.  In such cases,
+   the value is constrained to be within the bounds of the declared
+   type, but kept valid in the wider mode.  The signedness of the
+   extension may differ from that of the type.
+
+   We promote any value smaller than SImode up to SImode.  */
+
+#define PROMOTE_MODE(MODE, UNSIGNEDP, TYPE)    \
+  if (GET_MODE_CLASS (MODE) == MODE_INT                \
+      && GET_MODE_SIZE (MODE) < 4)             \
+    (MODE) = SImode;
+
+#define PROMOTE_FUNCTION_ARGS
+
+#define PROMOTE_FUNCTION_RETURN
+
+#define PARM_BOUNDARY 32
+
+#define STACK_BOUNDARY 64
+
+#define FUNCTION_BOUNDARY 32
+
+#define BIGGEST_ALIGNMENT 64
+
+#undef DATA_ALIGNMENT
+#define DATA_ALIGNMENT(TYPE, ALIGN)                                    \
+  ((((ALIGN) < BITS_PER_WORD)                                          \
+    && (TREE_CODE (TYPE) == ARRAY_TYPE                                 \
+       || TREE_CODE (TYPE) == UNION_TYPE                               \
+       || TREE_CODE (TYPE) == RECORD_TYPE)) ? BITS_PER_WORD : (ALIGN))
+
+#define CONSTANT_ALIGNMENT(EXP, ALIGN)                                 \
+  ((TREE_CODE (EXP) == STRING_CST  || TREE_CODE (EXP) == CONSTRUCTOR)  \
+   && (ALIGN) < BITS_PER_WORD ? BITS_PER_WORD : (ALIGN))
+
+#define EMPTY_FIELD_BOUNDARY 32
+
+#define STRUCTURE_SIZE_BOUNDARY 8
+
+#define STRICT_ALIGNMENT 1
+
+#define PCC_BITFIELD_TYPE_MATTERS 1
+
+#define TARGET_FLOAT_FORMAT IEEE_FLOAT_FORMAT
+
+\f
+/* Layout of Source Language Data Types.  */
+
+#define INT_TYPE_SIZE 32
+
+#define MAX_INT_TYPE_SIZE 32
+
+#define SHORT_TYPE_SIZE 16
+
+#define LONG_TYPE_SIZE 32
+
+#define LONG_LONG_TYPE_SIZE 64
+
+#define CHAR_TYPE_SIZE BITS_PER_UNIT
+
+#define FLOAT_TYPE_SIZE 32
+
+#define DOUBLE_TYPE_SIZE 64
+
+#define LONG_DOUBLE_TYPE_SIZE 64
+
+#define DEFAULT_SIGNED_CHAR 1
+
+#define MAX_WCHAR_TYPE_SIZE MAX_INT_TYPE_SIZE
+
+\f
+/* Register Basics.  */
+
+/* On the IQ2000, we have 32 integer registers.  */
+#define FIRST_PSEUDO_REGISTER 33
+
+#define FIXED_REGISTERS                                                        \
+{                                                                      \
+  1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                      \
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1                    \
+}
+
+#define CALL_USED_REGISTERS                                            \
+{                                                                      \
+  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,                      \
+  0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1                    \
+}
+
+\f
+/* Order of allocation of registers.  */
+
+#define REG_ALLOC_ORDER                                                        \
+{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,      \
+  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31       \
+}
+
+\f
+/* How Values Fit in Registers.  */
+
+#define HARD_REGNO_NREGS(REGNO, MODE)   \
+  ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+#define HARD_REGNO_MODE_OK(REGNO, MODE)                                \
+ ((REGNO_REG_CLASS (REGNO) == GR_REGS)                                 \
+  ? ((REGNO) & 1) == 0 || GET_MODE_SIZE (MODE) <= 4                    \
+  : ((REGNO) & 1) == 0 || GET_MODE_SIZE (MODE) == 4)
+
+#define MODES_TIEABLE_P(MODE1, MODE2)                                  \
+  ((GET_MODE_CLASS (MODE1) == MODE_FLOAT ||                            \
+    GET_MODE_CLASS (MODE1) == MODE_COMPLEX_FLOAT)                      \
+   == (GET_MODE_CLASS (MODE2) == MODE_FLOAT ||                         \
+       GET_MODE_CLASS (MODE2) == MODE_COMPLEX_FLOAT))
+
+#define AVOID_CCMODE_COPIES
+
+\f
+/* Register Classes.  */
+
+enum reg_class
+{
+  NO_REGS,                     /* no registers in set */
+  GR_REGS,                     /* integer registers */
+  ALL_REGS,                    /* all registers */
+  LIM_REG_CLASSES              /* max value + 1 */
+};
+
+#define GENERAL_REGS GR_REGS
+
+#define N_REG_CLASSES (int) LIM_REG_CLASSES
+
+#define REG_CLASS_NAMES                                                        \
+{                                                                      \
+  "NO_REGS",                                                           \
+  "GR_REGS",                                                           \
+  "ALL_REGS"                                                           \
+}
+
+#define REG_CLASS_CONTENTS                                             \
+{                                                                      \
+  { 0x00000000, 0x00000000 },  /* no registers */      \
+  { 0xffffffff, 0x00000000 },  /* integer registers */ \
+  { 0xffffffff, 0x00000001 }   /* all registers */     \
+}
+
+#define REGNO_REG_CLASS(REGNO) \
+((REGNO) <= GP_REG_LAST + 1 ? GR_REGS : NO_REGS)
+
+#define BASE_REG_CLASS  (GR_REGS)
+
+#define INDEX_REG_CLASS NO_REGS
+
+#define REG_CLASS_FROM_LETTER(C) \
+  ((C) == 'd' ? GR_REGS : \
+   (C) == 'b' ? ALL_REGS : \
+   (C) == 'y' ? GR_REGS : \
+   NO_REGS)
+
+#define REGNO_OK_FOR_INDEX_P(regno)    0
+
+#define PREFERRED_RELOAD_CLASS(X,CLASS)                                        \
+  ((CLASS) != ALL_REGS                                                 \
+   ? (CLASS)                                                           \
+   : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_FLOAT                     \
+       || GET_MODE_CLASS (GET_MODE (X)) == MODE_COMPLEX_FLOAT)         \
+      ? (GR_REGS)                              \
+      : ((GET_MODE_CLASS (GET_MODE (X)) == MODE_INT                    \
+         || GET_MODE (X) == VOIDmode)                                  \
+        ? (GR_REGS)                            \
+        : (CLASS))))
+
+#define SMALL_REGISTER_CLASSES 0
+
+#define CLASS_MAX_NREGS(CLASS, MODE)    \
+  ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) / UNITS_PER_WORD)
+
+/* For IQ2000:
+
+   `I' is used for the range of constants an arithmetic insn can
+       actually contain (16 bits signed integers).
+
+   `J' is used for the range which is just zero (ie, $r0).
+
+   `K' is used for the range of constants a logical insn can actually
+       contain (16 bit zero-extended integers).
+
+   `L' is used for the range of constants that be loaded with lui
+       (ie, the bottom 16 bits are zero).
+
+   `M' is used for the range of constants that take two words to load
+       (ie, not matched by `I', `K', and `L').
+
+   `N' is used for constants 0xffffnnnn or 0xnnnnffff
+
+   `O' is a 5 bit zero-extended integer.
+*/
+
+#define CONST_OK_FOR_LETTER_P(VALUE, C)                                        \
+  ((C) == 'I' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x8000) < 0x10000)        \
+   : (C) == 'J' ? ((VALUE) == 0)                                       \
+   : (C) == 'K' ? ((unsigned HOST_WIDE_INT) (VALUE) < 0x10000)         \
+   : (C) == 'L' ? (((VALUE) & 0x0000ffff) == 0                         \
+                  && (((VALUE) & ~2147483647) == 0                     \
+                      || ((VALUE) & ~2147483647) == ~2147483647))      \
+   : (C) == 'M' ? ((((VALUE) & ~0x0000ffff) != 0)                      \
+                  && (((VALUE) & ~0x0000ffff) != ~0x0000ffff)          \
+                  && (((VALUE) & 0x0000ffff) != 0                      \
+                      || (((VALUE) & ~2147483647) != 0                 \
+                          && ((VALUE) & ~2147483647) != ~2147483647))) \
+   : (C) == 'N' ? ((((VALUE) & 0xffff) == 0xffff)                      \
+                  || (((VALUE) & 0xffff0000) == 0xffff0000))           \
+   : (C) == 'O' ? ((unsigned HOST_WIDE_INT) ((VALUE) + 0x20) < 0x40)   \
+   : 0)
+
+#define CONST_DOUBLE_OK_FOR_LETTER_P(VALUE, C)                         \
+  ((C) == 'G'                                                          \
+   && (VALUE) == CONST0_RTX (GET_MODE (VALUE)))
+
+/* `R' is for memory references which take 1 word for the instruction.  */
+
+#define EXTRA_CONSTRAINT(OP,CODE)                                      \
+  (((CODE) == 'R')       ? simple_memory_operand (OP, GET_MODE (OP))   \
+   : FALSE)
+
+\f
+/* Basic Stack Layout.  */
+
+#define STACK_GROWS_DOWNWARD
+
+/* #define FRAME_GROWS_DOWNWARD */
+
+#define STARTING_FRAME_OFFSET                                          \
+  (current_function_outgoing_args_size)
+
+/* Use the default value zero.  */
+/* #define STACK_POINTER_OFFSET 0 */
+
+#define FIRST_PARM_OFFSET(FNDECL) 0
+
+/* The return address for the current frame is in r31 if this is a leaf
+   function.  Otherwise, it is on the stack.  It is at a variable offset
+   from sp/fp/ap, so we define a fake hard register rap which is a
+   pointer to the return address on the stack.  This always gets eliminated
+   during reload to be either the frame pointer or the stack pointer plus
+   an offset.  */
+
+#define RETURN_ADDR_RTX(count, frame)                                   \
+  (((count) == 0)                                                       \
+   ? (leaf_function_p ()                                                \
+      ? gen_rtx_REG (Pmode, GP_REG_FIRST + 31)                          \
+      : gen_rtx_MEM (Pmode, gen_rtx_REG (Pmode,                         \
+                                         RETURN_ADDRESS_POINTER_REGNUM))) \
+    : (rtx) 0)
+
+/* Before the prologue, RA lives in r31.  */
+#define INCOMING_RETURN_ADDR_RTX  gen_rtx_REG (VOIDmode, GP_REG_FIRST + 31)
+
+\f
+/* Register That Address the Stack Frame.  */
+
+#define STACK_POINTER_REGNUM (GP_REG_FIRST + 29)
+
+#define FRAME_POINTER_REGNUM (GP_REG_FIRST + 1)
+
+#define HARD_FRAME_POINTER_REGNUM \
+  (GP_REG_FIRST + 27)
+
+#define ARG_POINTER_REGNUM GP_REG_FIRST
+
+#define RETURN_ADDRESS_POINTER_REGNUM RAP_REG_NUM
+
+#define STATIC_CHAIN_REGNUM (GP_REG_FIRST + 2)
+
+\f
+/* Eliminating the Frame Pointer and the Arg Pointer.  */
+
+#define FRAME_POINTER_REQUIRED 0
+
+#define ELIMINABLE_REGS                                                        \
+{{ ARG_POINTER_REGNUM,   STACK_POINTER_REGNUM},                                \
+ { ARG_POINTER_REGNUM,   HARD_FRAME_POINTER_REGNUM},                   \
+ { RETURN_ADDRESS_POINTER_REGNUM, STACK_POINTER_REGNUM},               \
+ { RETURN_ADDRESS_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM},          \
+ { RETURN_ADDRESS_POINTER_REGNUM, GP_REG_FIRST + 31},                  \
+ { FRAME_POINTER_REGNUM, STACK_POINTER_REGNUM},                                \
+ { FRAME_POINTER_REGNUM, HARD_FRAME_POINTER_REGNUM}}
+
+
+/* We can always eliminate to the frame pointer.  We can eliminate to the 
+   stack pointer unless a frame pointer is needed.  */
+
+#define CAN_ELIMINATE(FROM, TO)                                                \
+  (((FROM) == RETURN_ADDRESS_POINTER_REGNUM && (! leaf_function_p ()   \
+   || (TO == GP_REG_FIRST + 31 && leaf_function_p)))                           \
+  || ((FROM) != RETURN_ADDRESS_POINTER_REGNUM                          \
+   && ((TO) == HARD_FRAME_POINTER_REGNUM                               \
+   || ((TO) == STACK_POINTER_REGNUM && ! frame_pointer_needed))))
+
+#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET)                    \
+        (OFFSET) = iq2000_initial_elimination_offset ((FROM), (TO))
+\f
+/* Passing Function Arguments on the Stack.  */
+
+#define PROMOTE_PROTOTYPES 1
+
+/* #define PUSH_ROUNDING(BYTES) 0 */
+
+#define ACCUMULATE_OUTGOING_ARGS 1
+
+#define REG_PARM_STACK_SPACE(FNDECL) 0
+
+#define OUTGOING_REG_PARM_STACK_SPACE
+
+#define RETURN_POPS_ARGS(FUNDECL,FUNTYPE,SIZE) 0
+
+\f
+/* Function Arguments in Registers.  */
+
+#define FUNCTION_ARG(CUM, MODE, TYPE, NAMED) \
+  function_arg( &CUM, MODE, TYPE, NAMED)
+
+#define FUNCTION_ARG_PARTIAL_NREGS(CUM, MODE, TYPE, NAMED) \
+  function_arg_partial_nregs (&CUM, MODE, TYPE, NAMED)
+
+#define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED)         \
+  function_arg_pass_by_reference (&CUM, MODE, TYPE, NAMED)
+
+#define FUNCTION_ARG_CALLEE_COPIES(CUM, MODE, TYPE, NAMED)             \
+  ((NAMED) && FUNCTION_ARG_PASS_BY_REFERENCE (CUM, MODE, TYPE, NAMED))
+
+#define MAX_ARGS_IN_REGISTERS 8
+
+typedef struct iq2000_args {
+  int gp_reg_found;            /* whether a gp register was found yet */
+  unsigned int arg_number;     /* argument number */
+  unsigned int arg_words;      /* # total words the arguments take */
+  unsigned int fp_arg_words;   /* # words for FP args (IQ2000_EABI only) */
+  int last_arg_fp;             /* nonzero if last arg was FP (EABI only) */
+  int fp_code;                 /* Mode of FP arguments */
+  unsigned int num_adjusts;    /* number of adjustments made */
+                               /* Adjustments made to args pass in regs.  */
+  struct rtx_def *adjust[MAX_ARGS_IN_REGISTERS*2];
+} CUMULATIVE_ARGS;
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is 0.  */
+#define INIT_CUMULATIVE_ARGS(CUM,FNTYPE,LIBNAME,INDIRECT)              \
+  init_cumulative_args (&CUM, FNTYPE, LIBNAME)                         \
+
+#define FUNCTION_ARG_ADVANCE(CUM, MODE, TYPE, NAMED)                   \
+  function_arg_advance (&CUM, MODE, TYPE, NAMED)
+
+#define FUNCTION_ARG_PADDING(MODE, TYPE)                               \
+  (! BYTES_BIG_ENDIAN                                                  \
+   ? upward                                                            \
+   : (((MODE) == BLKmode                                               \
+       ? ((TYPE) && TREE_CODE (TYPE_SIZE (TYPE)) == INTEGER_CST                \
+         && int_size_in_bytes (TYPE) < (PARM_BOUNDARY / BITS_PER_UNIT))\
+       : (GET_MODE_BITSIZE (MODE) < PARM_BOUNDARY                      \
+         && (GET_MODE_CLASS (MODE) == MODE_INT)))                      \
+      ? downward : upward))
+
+#define FUNCTION_ARG_BOUNDARY(MODE, TYPE)                              \
+  (((TYPE) != 0)                                                       \
+       ? ((TYPE_ALIGN(TYPE) <= PARM_BOUNDARY)                          \
+               ? PARM_BOUNDARY                                         \
+               : TYPE_ALIGN(TYPE))                                     \
+       : ((GET_MODE_ALIGNMENT(MODE) <= PARM_BOUNDARY)                  \
+               ? PARM_BOUNDARY                                         \
+               : GET_MODE_ALIGNMENT(MODE)))
+
+#define FUNCTION_ARG_REGNO_P(N)                                                \
+  (((N) >= GP_ARG_FIRST && (N) <= GP_ARG_LAST))                        
+
+\f
+/* How Scalar Function Values are Returned.  */
+
+#define FUNCTION_VALUE(VALTYPE, FUNC)  iq2000_function_value (VALTYPE, FUNC)
+
+#define LIBCALL_VALUE(MODE)                                            \
+  gen_rtx (REG,                                                                \
+          ((GET_MODE_CLASS (MODE) != MODE_INT                          \
+            || GET_MODE_SIZE (MODE) >= 4)                              \
+           ? (MODE)                                                    \
+           : SImode),                                                  \
+          GP_RETURN)
+
+/* On the IQ2000, R2 and R3 are the only register thus used.  */
+
+#define FUNCTION_VALUE_REGNO_P(N) ((N) == GP_RETURN)
+
+\f
+/* How Large Values are Returned.  */
+
+#define RETURN_IN_MEMORY(TYPE)                                          \
+  (((int_size_in_bytes (TYPE)                                           \
+       > (2 * UNITS_PER_WORD))                                                  \
+      || (int_size_in_bytes (TYPE) == -1)))
+
+#define DEFAULT_PCC_STRUCT_RETURN 0
+
+#define STRUCT_VALUE 0
+
+\f
+/* Function Entry and Exit.  */
+
+#define EXIT_IGNORE_STACK 1
+
+\f
+/* Generating Code for Profiling.  */
+
+#define FUNCTION_PROFILER(FILE, LABELNO)                               \
+{                                                                      \
+  fprintf (FILE, "\t.set\tnoreorder\n");                               \
+  fprintf (FILE, "\t.set\tnoat\n");                                    \
+  fprintf (FILE, "\tmove\t%s,%s\t\t# save current return address\n",   \
+          reg_names[GP_REG_FIRST + 1], reg_names[GP_REG_FIRST + 31]);  \
+  fprintf (FILE, "\tjal\t_mcount\n");                                  \
+  fprintf (FILE,                                                       \
+          "\t%s\t%s,%s,%d\t\t# _mcount pops 2 words from  stack\n",    \
+          "subu",                                                      \
+          reg_names[STACK_POINTER_REGNUM],                             \
+          reg_names[STACK_POINTER_REGNUM],                             \
+          Pmode == DImode ? 16 : 8);                                   \
+  fprintf (FILE, "\t.set\treorder\n");                                 \
+  fprintf (FILE, "\t.set\tat\n");                                      \
+}
+
+\f
+/* Implementing the Varargs Macros.  */
+
+#define SETUP_INCOMING_VARARGS(CUM,MODE,TYPE,PRETEND_SIZE,NO_RTL)      \
+  iq2000_setup_incoming_varargs (CUM,MODE,TYPE,&PRETEND_SIZE,NO_RTL);
+
+#define STRICT_ARGUMENT_NAMING  1
+
+#define BUILD_VA_LIST_TYPE(VALIST) \
+  (VALIST) = ptr_type_node
+
+#define EXPAND_BUILTIN_VA_START(valist, nextarg) \
+  iq2000_va_start (valist, nextarg)
+
+/* Implement `va_arg'.  */
+#define EXPAND_BUILTIN_VA_ARG(valist, type) \
+  iq2000_va_arg (valist, type)
+
+\f
+/* Trampolines for Nested Functions.  */
+
+/* A C statement to output, on the stream FILE, assembler code for a
+   block of data that contains the constant parts of a trampoline.
+   This code should not include a label--the label is taken care of
+   automatically.  */
+
+#define TRAMPOLINE_TEMPLATE(STREAM)                                     \
+{                                                                       \
+  fprintf (STREAM, "\t.word\t0x03e00821\t\t# move   $1,$31\n");                \
+  fprintf (STREAM, "\t.word\t0x04110001\t\t# bgezal $0,.+8\n");                \
+  fprintf (STREAM, "\t.word\t0x00000000\t\t# nop\n");                  \
+  if (Pmode == DImode)                                                 \
+    {                                                                  \
+      fprintf (STREAM, "\t.word\t0xdfe30014\t\t# ld     $3,20($31)\n");        \
+      fprintf (STREAM, "\t.word\t0xdfe2001c\t\t# ld     $2,28($31)\n");        \
+    }                                                                  \
+  else                                                                 \
+    {                                                                  \
+      fprintf (STREAM, "\t.word\t0x8fe30014\t\t# lw     $3,20($31)\n");        \
+      fprintf (STREAM, "\t.word\t0x8fe20018\t\t# lw     $2,24($31)\n");        \
+    }                                                                  \
+  fprintf (STREAM, "\t.word\t0x0060c821\t\t# move   $25,$3 (abicalls)\n"); \
+  fprintf (STREAM, "\t.word\t0x00600008\t\t# jr     $3\n");            \
+  fprintf (STREAM, "\t.word\t0x0020f821\t\t# move   $31,$1\n");                \
+  fprintf (STREAM, "\t.word\t0x00000000\t\t# <function address>\n"); \
+  fprintf (STREAM, "\t.word\t0x00000000\t\t# <static chain value>\n"); \
+}
+
+#define TRAMPOLINE_SIZE (40)
+
+#define TRAMPOLINE_ALIGNMENT 32
+
+#define INITIALIZE_TRAMPOLINE(ADDR, FUNC, CHAIN)                           \
+{                                                                          \
+  rtx addr = ADDR;                                                         \
+    emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 32)), FUNC); \
+    emit_move_insn (gen_rtx_MEM (SImode, plus_constant (addr, 36)), CHAIN);\
+}
+
+\f
+/* Implicit Calls to Library Routines.  */
+
+#define INIT_TARGET_OPTABS                                             \
+do                                                                     \
+  {                                                                    \
+    INIT_SUBTARGET_OPTABS;                                             \
+  }                                                                    \
+while (0)
+
+\f
+/* Addressing Modes.  */
+
+#define CONSTANT_ADDRESS_P(X)                                          \
+  ((GET_CODE (X) == LABEL_REF || GET_CODE (X) == SYMBOL_REF            \
+    || GET_CODE (X) == CONST_INT || GET_CODE (X) == HIGH               \
+    || (GET_CODE (X) == CONST)))
+
+#define MAX_REGS_PER_ADDRESS 1
+
+#ifdef REG_OK_STRICT
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR)        \
+{                                              \
+  if (iq2000_legitimate_address_p (MODE, X, 1))        \
+    goto ADDR;                                 \
+}
+#else
+#define GO_IF_LEGITIMATE_ADDRESS(MODE, X, ADDR)        \
+{                                              \
+  if (iq2000_legitimate_address_p (MODE, X, 0))        \
+    goto ADDR;                                 \
+}
+#endif
+
+#define REG_OK_FOR_INDEX_P(X) 0
+
+
+/* For the IQ2000, transform:
+
+       memory(X + <large int>)
+   into:
+       Y = <large int> & ~0x7fff;
+       Z = X + Y
+       memory (Z + (<large int> & 0x7fff));
+*/
+
+#define LEGITIMIZE_ADDRESS(X,OLDX,MODE,WIN)                            \
+{                                                                      \
+  register rtx xinsn = (X);                                            \
+                                                                       \
+  if (TARGET_DEBUG_B_MODE)                                             \
+    {                                                                  \
+      GO_PRINTF ("\n========== LEGITIMIZE_ADDRESS\n");                 \
+      GO_DEBUG_RTX (xinsn);                                            \
+    }                                                                  \
+                                                                       \
+  if (iq2000_check_split (X, MODE))            \
+    {                                                                  \
+      X = gen_rtx_LO_SUM (Pmode,                                       \
+                         copy_to_mode_reg (Pmode,                      \
+                                           gen_rtx (HIGH, Pmode, X)),  \
+                         X);                                           \
+      goto WIN;                                                                \
+    }                                                                  \
+                                                                       \
+  if (GET_CODE (xinsn) == PLUS)                                                \
+    {                                                                  \
+      register rtx xplus0 = XEXP (xinsn, 0);                           \
+      register rtx xplus1 = XEXP (xinsn, 1);                           \
+      register enum rtx_code code0 = GET_CODE (xplus0);                        \
+      register enum rtx_code code1 = GET_CODE (xplus1);                        \
+                                                                       \
+      if (code0 != REG && code1 == REG)                                        \
+       {                                                               \
+         xplus0 = XEXP (xinsn, 1);                                     \
+         xplus1 = XEXP (xinsn, 0);                                     \
+         code0 = GET_CODE (xplus0);                                    \
+         code1 = GET_CODE (xplus1);                                    \
+       }                                                               \
+                                                                       \
+      if (code0 == REG && REG_MODE_OK_FOR_BASE_P (xplus0, MODE)                \
+         && code1 == CONST_INT && !SMALL_INT (xplus1))                 \
+       {                                                               \
+         rtx int_reg = gen_reg_rtx (Pmode);                            \
+         rtx ptr_reg = gen_reg_rtx (Pmode);                            \
+                                                                       \
+         emit_move_insn (int_reg,                                      \
+                         GEN_INT (INTVAL (xplus1) & ~ 0x7fff));        \
+                                                                       \
+         emit_insn (gen_rtx_SET (VOIDmode,                             \
+                                 ptr_reg,                              \
+                                 gen_rtx_PLUS (Pmode, xplus0, int_reg))); \
+                                                                       \
+         X = plus_constant (ptr_reg, INTVAL (xplus1) & 0x7fff);        \
+         goto WIN;                                                     \
+       }                                                               \
+    }                                                                  \
+                                                                       \
+  if (TARGET_DEBUG_B_MODE)                                             \
+    GO_PRINTF ("LEGITIMIZE_ADDRESS could not fix.\n");                 \
+}
+
+#define GO_IF_MODE_DEPENDENT_ADDRESS(ADDR,LABEL) {}
+
+#define LEGITIMATE_CONSTANT_P(X) (1)
+
+\f
+/* Describing Relative Costs of Operations.  */
+
+#define CONST_COSTS(X,CODE,OUTER_CODE)                                 \
+  case CONST_INT:                                                      \
+    return 0;                                                          \
+                                                                       \
+  case LABEL_REF:                                                      \
+    return COSTS_N_INSNS (2);                                          \
+                                                                       \
+  case CONST:                                                          \
+    {                                                                  \
+      rtx offset = const0_rtx;                                         \
+      rtx symref = eliminate_constant_term (XEXP (X, 0), &offset);     \
+                                                                       \
+      if (GET_CODE (symref) == LABEL_REF)                              \
+       return COSTS_N_INSNS (2);                                       \
+                                                                       \
+      if (GET_CODE (symref) != SYMBOL_REF)                             \
+       return COSTS_N_INSNS (4);                                       \
+                                                                       \
+      /* let's be paranoid....  */                                     \
+      if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)         \
+       return COSTS_N_INSNS (2);                                       \
+                                                                       \
+      return COSTS_N_INSNS (SYMBOL_REF_FLAG (symref) ? 1 : 2);         \
+    }                                                                  \
+                                                                       \
+  case SYMBOL_REF:                                                     \
+    return COSTS_N_INSNS (SYMBOL_REF_FLAG (X) ? 1 : 2);                        \
+                                                                       \
+  case CONST_DOUBLE:                                                   \
+    {                                                                  \
+      rtx high, low;                                                   \
+      split_double (X, &high, &low);                                   \
+      return COSTS_N_INSNS ((high == CONST0_RTX (GET_MODE (high))      \
+                            || low == CONST0_RTX (GET_MODE (low)))     \
+                           ? 2 : 4);                                   \
+    }
+
+#define RTX_COSTS(X,CODE,OUTER_CODE)                                   \
+  case MEM:                                                            \
+    {                                                                  \
+      int num_words = (GET_MODE_SIZE (GET_MODE (X)) > UNITS_PER_WORD) ? 2 : 1; \
+      if (simple_memory_operand (X, GET_MODE (X)))                     \
+       return COSTS_N_INSNS (num_words);                               \
+                                                                       \
+      return COSTS_N_INSNS (2*num_words);                              \
+    }                                                                  \
+                                                                       \
+  case FFS:                                                            \
+    return COSTS_N_INSNS (6);                                          \
+                                                                       \
+  case NOT:                                                            \
+    return COSTS_N_INSNS (GET_MODE (X) == DImode && 2);                \
+                                                                       \
+  case AND:                                                            \
+  case IOR:                                                            \
+  case XOR:                                                            \
+    if (GET_MODE (X) == DImode)                                                \
+      return COSTS_N_INSNS (2);                                                \
+                                                                       \
+    break;                                                             \
+                                                                       \
+  case ASHIFT:                                                         \
+  case ASHIFTRT:                                                       \
+  case LSHIFTRT:                                                       \
+    if (GET_MODE (X) == DImode)                                                \
+      return COSTS_N_INSNS ((GET_CODE (XEXP (X, 1)) == CONST_INT) ? 4 : 12); \
+                                                                       \
+    break;                                                             \
+                                                                       \
+  case ABS:                                                            \
+    {                                                                  \
+      enum machine_mode xmode = GET_MODE (X);                          \
+      if (xmode == SFmode || xmode == DFmode)                          \
+       return COSTS_N_INSNS (1);                                       \
+                                                                       \
+      return COSTS_N_INSNS (4);                                                \
+    }                                                                  \
+                                                                       \
+  case PLUS:                                                           \
+  case MINUS:                                                          \
+    {                                                                  \
+      enum machine_mode xmode = GET_MODE (X);                          \
+      if (xmode == SFmode || xmode == DFmode)                          \
+       {                                                               \
+          return COSTS_N_INSNS (6);                                    \
+       }                                                               \
+                                                                       \
+      if (xmode == DImode)                                             \
+       return COSTS_N_INSNS (4);                                       \
+                                                                       \
+      break;                                                           \
+    }                                                                  \
+                                                                       \
+  case NEG:                                                            \
+    if (GET_MODE (X) == DImode)                                                \
+      return 4;                                                                \
+                                                                       \
+    break;                                                             \
+                                                                       \
+  case MULT:                                                           \
+    {                                                                  \
+      enum machine_mode xmode = GET_MODE (X);                          \
+      if (xmode == SFmode)                                             \
+       {                                                               \
+           return COSTS_N_INSNS (7);                                   \
+       }                                                               \
+                                                                       \
+      if (xmode == DFmode)                                             \
+       {                                                               \
+           return COSTS_N_INSNS (8);                                   \
+       }                                                               \
+                                                                       \
+       return COSTS_N_INSNS (10);                                      \
+    }                                                                  \
+                                                                       \
+  case DIV:                                                            \
+  case MOD:                                                            \
+    {                                                                  \
+      enum machine_mode xmode = GET_MODE (X);                          \
+      if (xmode == SFmode)                                             \
+       {                                                               \
+           return COSTS_N_INSNS (23);                                  \
+       }                                                               \
+                                                                       \
+      if (xmode == DFmode)                                             \
+       {                                                               \
+           return COSTS_N_INSNS (36);                                  \
+       }                                                               \
+    }                                                                  \
+    /* fall through */                                                 \
+                                                                       \
+  case UDIV:                                                           \
+  case UMOD:                                                           \
+      return COSTS_N_INSNS (69);                                       \
+                                                                       \
+  case SIGN_EXTEND:                                                    \
+    return COSTS_N_INSNS (2);                                          \
+                                                                       \
+  case ZERO_EXTEND:                                                    \
+    return COSTS_N_INSNS (1);
+
+#define ADDRESS_COST(ADDR) (REG_P (ADDR) ? 1 : iq2000_address_cost (ADDR))
+
+#define REGISTER_MOVE_COST(MODE, FROM, TO)     2
+
+#define MEMORY_MOVE_COST(MODE,CLASS,TO_P)      \
+  (TO_P ? 2 : 16)
+
+#define BRANCH_COST 2
+
+#define SLOW_BYTE_ACCESS 1
+
+#define NO_FUNCTION_CSE 1
+
+#define NO_RECURSIVE_FUNCTION_CSE 1
+
+#define ADJUST_COST(INSN,LINK,DEP_INSN,COST)                           \
+  if (REG_NOTE_KIND (LINK) != 0)                                       \
+    (COST) = 0; /* Anti or output dependence.  */
+
+\f
+/* Dividing the output into sections.  */
+
+#define TEXT_SECTION_ASM_OP    "\t.text"       /* instructions */
+
+#define DATA_SECTION_ASM_OP    "\t.data"       /* large data */
+
+\f
+/* The Overall Framework of an Assembler File.  */
+
+#define ASM_COMMENT_START " #"
+
+#define ASM_APP_ON "#APP\n"
+
+#define ASM_APP_OFF "#NO_APP\n"
+
+\f
+/* Output and Generation of Labels.  */
+
+#undef ASM_OUTPUT_INTERNAL_LABEL
+#define ASM_OUTPUT_INTERNAL_LABEL(STREAM,PREFIX,NUM)                   \
+  fprintf (STREAM, "%s%s%d:\n", LOCAL_LABEL_PREFIX, PREFIX, NUM)
+
+#undef ASM_GENERATE_INTERNAL_LABEL
+#define ASM_GENERATE_INTERNAL_LABEL(LABEL,PREFIX,NUM)                  \
+  sprintf ((LABEL), "*%s%s%ld", (LOCAL_LABEL_PREFIX), (PREFIX), (long)(NUM))
+
+#define GLOBAL_ASM_OP "\t.globl\t"
+
+\f
+/* Output of Assembler Instructions.  */
+
+#define REGISTER_NAMES                                                 \
+{                                                                      \
+ "%0",   "%1",   "%2",   "%3",   "%4",   "%5",   "%6",   "%7",         \
+ "%8",   "%9",   "%10",  "%11",  "%12",  "%13",  "%14",  "%15",                \
+ "%16",  "%17",  "%18",  "%19",  "%20",  "%21",  "%22",  "%23",                \
+ "%24",  "%25",  "%26",  "%27",  "%28",  "%29",  "%30",  "%31",  "%rap"        \
+};
+
+#define ADDITIONAL_REGISTER_NAMES                                      \
+{                                                                      \
+  { "%0",       0 + GP_REG_FIRST },                                    \
+  { "%1",       1 + GP_REG_FIRST },                                    \
+  { "%2",       2 + GP_REG_FIRST },                                    \
+  { "%3",       3 + GP_REG_FIRST },                                    \
+  { "%4",       4 + GP_REG_FIRST },                                    \
+  { "%5",       5 + GP_REG_FIRST },                                    \
+  { "%6",       6 + GP_REG_FIRST },                                    \
+  { "%7",       7 + GP_REG_FIRST },                                    \
+  { "%8",       8 + GP_REG_FIRST },                                    \
+  { "%9",       9 + GP_REG_FIRST },                                    \
+  { "%10",     10 + GP_REG_FIRST },                                    \
+  { "%11",     11 + GP_REG_FIRST },                                    \
+  { "%12",     12 + GP_REG_FIRST },                                    \
+  { "%13",     13 + GP_REG_FIRST },                                    \
+  { "%14",     14 + GP_REG_FIRST },                                    \
+  { "%15",     15 + GP_REG_FIRST },                                    \
+  { "%16",     16 + GP_REG_FIRST },                                    \
+  { "%17",     17 + GP_REG_FIRST },                                    \
+  { "%18",     18 + GP_REG_FIRST },                                    \
+  { "%19",     19 + GP_REG_FIRST },                                    \
+  { "%20",     20 + GP_REG_FIRST },                                    \
+  { "%21",     21 + GP_REG_FIRST },                                    \
+  { "%22",     22 + GP_REG_FIRST },                                    \
+  { "%23",     23 + GP_REG_FIRST },                                    \
+  { "%24",     24 + GP_REG_FIRST },                                    \
+  { "%25",     25 + GP_REG_FIRST },                                    \
+  { "%26",     26 + GP_REG_FIRST },                                    \
+  { "%27",     27 + GP_REG_FIRST },                                    \
+  { "%28",     28 + GP_REG_FIRST },                                    \
+  { "%29",     29 + GP_REG_FIRST },                                    \
+  { "%30",     27 + GP_REG_FIRST },                                    \
+  { "%31",     31 + GP_REG_FIRST },                                    \
+  { "%rap",    32 + GP_REG_FIRST },                                    \
+}
+
+/* Check if the current insn needs a nop in front of it
+   because of load delays, and also update the delay slot statistics.  */
+
+#define FINAL_PRESCAN_INSN(INSN, OPVEC, NOPERANDS)                     \
+  final_prescan_insn (INSN, OPVEC, NOPERANDS)
+
+/* See iq2000.c for the IQ2000 specific codes.  */
+#define PRINT_OPERAND(FILE, X, CODE) print_operand (FILE, X, CODE)
+
+#define PRINT_OPERAND_PUNCT_VALID_P(CODE) iq2000_print_operand_punct[CODE]
+
+#define PRINT_OPERAND_ADDRESS(FILE, ADDR) print_operand_address (FILE, ADDR)
+
+#define DBR_OUTPUT_SEQEND(STREAM)                                      \
+do                                                                     \
+  {                                                                    \
+    dslots_jump_filled++;                                              \
+    fputs ("\n", STREAM);                                              \
+  }                                                                    \
+while (0)
+
+#define LOCAL_LABEL_PREFIX     "$"
+
+#define USER_LABEL_PREFIX      ""
+
+\f
+/* Output of dispatch tables.  */
+
+#define ASM_OUTPUT_ADDR_DIFF_ELT(STREAM, BODY, VALUE, REL)             \
+do {                                                                   \
+  fprintf (STREAM, "\t%s\t%sL%d\n",                                    \
+          Pmode == DImode ? ".dword" : ".word",                        \
+          LOCAL_LABEL_PREFIX, VALUE);                                  \
+} while (0)
+
+#define ASM_OUTPUT_ADDR_VEC_ELT(STREAM, VALUE)                         \
+  fprintf (STREAM, "\t%s\t%sL%d\n",                                    \
+          Pmode == DImode ? ".dword" : ".word",                        \
+          LOCAL_LABEL_PREFIX,                                          \
+          VALUE)
+
+\f
+/* Assembler Commands for Alignment.  */
+
+#undef ASM_OUTPUT_SKIP
+#define ASM_OUTPUT_SKIP(STREAM,SIZE)                                   \
+  fprintf (STREAM, "\t.space\t%u\n", (SIZE))
+
+#define ASM_OUTPUT_ALIGN(STREAM,LOG)                                   \
+  if ((LOG) != 0)                                                      \
+    fprintf (STREAM, "\t.balign %d\n", 1<<(LOG))
+
+\f
+/* Macros Affecting all Debug Formats.  */
+
+#define DEBUGGER_AUTO_OFFSET(X)  \
+  iq2000_debugger_offset (X, (HOST_WIDE_INT) 0)
+
+#define DEBUGGER_ARG_OFFSET(OFFSET, X)  \
+  iq2000_debugger_offset (X, (HOST_WIDE_INT) OFFSET)
+
+#define PREFERRED_DEBUGGING_TYPE DWARF2_DEBUG
+
+#define DWARF2_DEBUGGING_INFO 1
+
+\f
+/* Miscellaneous Parameters.  */
+
+#define PREDICATE_CODES                                                        \
+  {"uns_arith_operand",                { REG, CONST_INT, SUBREG }},            \
+  {"arith_operand",            { REG, CONST_INT, SUBREG }},            \
+  {"small_int",                        { CONST_INT }},                         \
+  {"large_int",                        { CONST_INT }},                         \
+  {"reg_or_0_operand",         { REG, CONST_INT, CONST_DOUBLE, SUBREG }}, \
+  {"simple_memory_operand",    { MEM, SUBREG }},                       \
+  {"equality_op",              { EQ, NE }},                            \
+  {"cmp_op",                   { EQ, NE, GT, GE, GTU, GEU, LT, LE,     \
+                                 LTU, LEU }},                          \
+  {"pc_or_label_operand",      { PC, LABEL_REF }},                     \
+  {"call_insn_operand",                { CONST_INT, CONST, SYMBOL_REF, REG}},  \
+  {"move_operand",             { CONST_INT, CONST_DOUBLE, CONST,       \
+                                 SYMBOL_REF, LABEL_REF, SUBREG,        \
+                                 REG, MEM}},                           \
+  {"power_of_2_operand",       { CONST_INT }},
+
+#define CASE_VECTOR_MODE SImode
+
+#define CASE_VECTOR_PC_RELATIVE 0
+
+#define WORD_REGISTER_OPERATIONS
+
+#define LOAD_EXTEND_OP(MODE) ZERO_EXTEND
+
+#define MOVE_MAX 4
+
+#define MAX_MOVE_MAX 8
+
+#define SHIFT_COUNT_TRUNCATED 1
+
+#define TRULY_NOOP_TRUNCATION(OUTPREC, INPREC) 1
+
+#define STORE_FLAG_VALUE 1
+
+#define Pmode SImode
+
+#define FUNCTION_MODE SImode
+
+/* Standard GCC variables that we reference.  */
+
+extern char    call_used_regs[];
+
+/* IQ2000 external variables defined in iq2000.c.  */
+
+/* Comparison type.  */
+enum cmp_type
+{
+  CMP_SI,                              /* compare four byte integers */
+  CMP_DI,                              /* compare eight byte integers */
+  CMP_SF,                              /* compare single precision floats */
+  CMP_DF,                              /* compare double precision floats */
+  CMP_MAX                              /* max comparison type */
+};
+
+/* Types of delay slot.  */
+enum delay_type
+{
+  DELAY_NONE,                          /* no delay slot */
+  DELAY_LOAD,                          /* load from memory delay */
+  DELAY_FCMP                           /* delay after doing c.<xx>.{d,s} */
+};
+
+/* Which processor to schedule for.  */
+
+enum processor_type
+{
+  PROCESSOR_DEFAULT,
+  PROCESSOR_IQ2000,
+  PROCESSOR_IQ10
+};
+
+/* Recast the cpu class to be the cpu attribute.  */
+#define iq2000_cpu_attr ((enum attr_cpu)iq2000_tune)
+
+extern char iq2000_print_operand_punct[];      /* print_operand punctuation chars */
+extern int num_source_filenames;       /* current .file # */
+extern int iq2000_branch_likely;               /* emit 'l' after br (branch likely) */
+extern struct rtx_def *branch_cmp[2];  /* operands for compare */
+extern enum cmp_type branch_type;      /* what type of branch to use */
+extern enum processor_type iq2000_arch;   /* which cpu to codegen for */
+extern enum processor_type iq2000_tune;   /* which cpu to schedule for */
+extern int iq2000_isa;                 /* architectural level */
+extern const char *iq2000_cpu_string;  /* for -mcpu=<xxx> */
+extern const char *iq2000_arch_string;    /* for -march=<xxx> */
+extern int dslots_load_total;          /* total # load related delay slots */
+extern int dslots_load_filled;         /* # filled load delay slots */
+extern int dslots_jump_total;          /* total # jump related delay slots */
+extern int dslots_jump_filled;         /* # filled jump delay slots */
+extern int dslots_number_nops;         /* # of nops needed by previous insn */
+extern int num_refs[3];                        /* # 1/2/3 word references */
+extern struct rtx_def *iq2000_load_reg;        /* register to check for load delay */
+extern struct rtx_def *iq2000_load_reg2;       /* 2nd reg to check for load delay */
+extern struct rtx_def *iq2000_load_reg3;       /* 3rd reg to check for load delay */
+extern struct rtx_def *iq2000_load_reg4;       /* 4th reg to check for load delay */
+
+/* Functions to change what output section we are using.  */
+extern void            rdata_section PARAMS ((void));
+extern void            sdata_section PARAMS ((void));
+extern void            sbss_section PARAMS ((void));
+
+#define BITMASK_UPPER16        ((unsigned long)0xffff << 16)   /* 0xffff0000 */
+#define BITMASK_LOWER16        ((unsigned long)0xffff)         /* 0x0000ffff */
+
+\f
+#define GENERATE_BRANCHLIKELY  (ISA_HAS_BRANCHLIKELY)
+
+/* Macros to decide whether certain features are available or not,
+   depending on the instruction set architecture level.  */
+
+#define BRANCH_LIKELY_P()      GENERATE_BRANCHLIKELY
+
+/* ISA has branch likely instructions.  */
+#define ISA_HAS_BRANCHLIKELY   (iq2000_isa == 1)
+
+\f
+#undef ASM_SPEC
+#define ASM_SPEC "%{march=iq2000: -m2000} %{march=iq10: -m10} %{!march=*: -m2000}"
+
+\f
+/* The mapping from gcc register number to DWARF 2 CFA column number.
+   This mapping does not allow for tracking register 0, since
+   register 0 is fixed.  */
+#define DWARF_FRAME_REGNUM(REG)                                \
+  (REG == GP_REG_FIRST + 31 ? DWARF_FRAME_RETURN_COLUMN : REG)
+
+/* The DWARF 2 CFA column which tracks the return address.  */
+#define DWARF_FRAME_RETURN_COLUMN ( GP_REG_FIRST + 26)
+
+/* Describe how we implement __builtin_eh_return.  */
+#define EH_RETURN_DATA_REGNO(N) ((N) < 4 ? (N) + GP_ARG_FIRST : INVALID_REGNUM)
+
+/* The EH_RETURN_STACKADJ_RTX macro returns RTL which describes the
+   location used to store the amount to adjust the stack.  This is
+   usually a register that is available from end of the function's body
+   to the end of the epilogue. Thus, this cannot be a register used as a
+   temporary by the epilogue.
+
+   This must be an integer register.  */
+#define EH_RETURN_STACKADJ_REGNO        3
+#define EH_RETURN_STACKADJ_RTX  gen_rtx_REG (Pmode, EH_RETURN_STACKADJ_REGNO)
+
+/* The EH_RETURN_HANDLER_RTX macro returns RTL which describes the
+   location used to store the address the processor should jump to
+   catch exception.  This is usually a registers that is available from
+   end of the function's body to the end of the epilogue. Thus, this
+   cannot be a register used as a temporary by the epilogue.
+
+   This must be an address register.  */
+#define EH_RETURN_HANDLER_REGNO         26
+#define EH_RETURN_HANDLER_RTX           \
+        gen_rtx_REG (Pmode, EH_RETURN_HANDLER_REGNO)
+
+/* Offsets recorded in opcodes are a multiple of this alignment factor.  */
+#define DWARF_CIE_DATA_ALIGNMENT 4
+
+/* For IQ2000, width of a floating point register.  */
+#define UNITS_PER_FPREG 4
+
+/* Force right-alignment for small varargs in 32 bit little_endian mode */
+
+#define PAD_VARARGS_DOWN !BYTES_BIG_ENDIAN
+
+/* Internal macros to classify a register number as to whether it's a
+   general purpose register, a floating point register, a
+   multiply/divide register, or a status register.  */
+
+#define GP_REG_FIRST 0
+#define GP_REG_LAST  31
+#define GP_REG_NUM   (GP_REG_LAST - GP_REG_FIRST + 1)
+
+#define RAP_REG_NUM   32
+#define AT_REGNUM      (GP_REG_FIRST + 1)
+
+#define GP_REG_P(REGNO)        \
+  ((unsigned int) ((int) (REGNO) - GP_REG_FIRST) < GP_REG_NUM)
+
+/* IQ2000 registers used in prologue/epilogue code when the stack frame
+   is larger than 32K bytes.  These registers must come from the
+   scratch register set, and not used for passing and returning
+   arguments and any other information used in the calling sequence.  */
+
+#define IQ2000_TEMP1_REGNUM (GP_REG_FIRST + 12)
+#define IQ2000_TEMP2_REGNUM (GP_REG_FIRST + 13)
+
+/* This macro is used later on in the file.  */
+#define GR_REG_CLASS_P(CLASS)                                          \
+  ((CLASS) == GR_REGS)
+
+#define SMALL_INT(X) ((unsigned HOST_WIDE_INT) (INTVAL (X) + 0x8000) < 0x10000)
+#define SMALL_INT_UNSIGNED(X) ((unsigned HOST_WIDE_INT) (INTVAL (X)) < 0x10000)
+
+/* Certain machines have the property that some registers cannot be
+   copied to some other registers without using memory.  Define this
+   macro on those machines to be a C expression that is non-zero if
+   objects of mode MODE in registers of CLASS1 can only be copied to
+   registers of class CLASS2 by storing a register of CLASS1 into
+   memory and loading that memory location into a register of CLASS2.
+
+   Do not define this macro if its value would always be zero.  */
+
+/* Return the maximum number of consecutive registers
+   needed to represent mode MODE in a register of class CLASS.  */
+
+#define CLASS_UNITS(mode, size)                                                \
+  ((GET_MODE_SIZE (mode) + (size) - 1) / (size))
+
+/* If defined, gives a class of registers that cannot be used as the
+   operand of a SUBREG that changes the mode of the object illegally.  */
+
+#define CLASS_CANNOT_CHANGE_MODE 0
+
+/* Defines illegal mode changes for CLASS_CANNOT_CHANGE_MODE.  */
+
+#define CLASS_CANNOT_CHANGE_MODE_P(FROM,TO) \
+  (GET_MODE_SIZE (FROM) != GET_MODE_SIZE (TO))
+
+/* Make sure 4 words are always allocated on the stack.  */
+
+#ifndef STACK_ARGS_ADJUST
+#define STACK_ARGS_ADJUST(SIZE)                                                \
+{                                                                      \
+  if (SIZE.constant < 4 * UNITS_PER_WORD)                              \
+    SIZE.constant = 4 * UNITS_PER_WORD;                                        \
+}
+#endif
+
+\f
+/* Symbolic macros for the registers used to return integer and floating
+   point values.  */
+
+#define GP_RETURN (GP_REG_FIRST + 2)
+
+/* Symbolic macros for the first/last argument registers.  */
+
+#define GP_ARG_FIRST (GP_REG_FIRST + 4)
+#define GP_ARG_LAST  (GP_REG_FIRST + 11)
+
+#define MAX_ARGS_IN_REGISTERS  8
+
+\f
+/* Tell prologue and epilogue if register REGNO should be saved / restored.  */
+
+#define MUST_SAVE_REGISTER(regno) \
+ ((regs_ever_live[regno] && !call_used_regs[regno])                    \
+  || (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)      \
+  || (regno == (GP_REG_FIRST + 31) && regs_ever_live[GP_REG_FIRST + 31]))
+
+/* ALIGN FRAMES on double word boundaries */
+#ifndef IQ2000_STACK_ALIGN
+#define IQ2000_STACK_ALIGN(LOC) (((LOC) + 7) & ~7)
+#endif
+
+\f
+/* These assume that REGNO is a hard or pseudo reg number.
+   They give nonzero only if REGNO is a hard reg of the suitable class
+   or a pseudo reg currently allocated to a suitable hard reg.
+   These definitions are NOT overridden anywhere.  */
+
+#define BASE_REG_P(regno, mode)                                        \
+  (GP_REG_P (regno))
+
+#define GP_REG_OR_PSEUDO_STRICT_P(regno, mode)                             \
+  BASE_REG_P((regno < FIRST_PSEUDO_REGISTER) ? regno : reg_renumber[regno], \
+            (mode))
+
+#define GP_REG_OR_PSEUDO_NONSTRICT_P(regno, mode) \
+  (((regno) >= FIRST_PSEUDO_REGISTER) || (BASE_REG_P ((regno), (mode))))
+
+#define REGNO_MODE_OK_FOR_BASE_P(regno, mode) \
+  GP_REG_OR_PSEUDO_STRICT_P ((regno), (mode))
+
+/* The macros REG_OK_FOR..._P assume that the arg is a REG rtx
+   and check its validity for a certain class.
+   We have two alternate definitions for each of them.
+   The usual definition accepts all pseudo regs; the other rejects them all.
+   The symbol REG_OK_STRICT causes the latter definition to be used.
+
+   Most source files want to accept pseudo regs in the hope that
+   they will get allocated to the class that the insn wants them to be in.
+   Some source files that are used after register allocation
+   need to be strict.  */
+
+#ifndef REG_OK_STRICT
+#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
+  iq2000_reg_mode_ok_for_base_p (X, MODE, 0)
+#else
+#define REG_MODE_OK_FOR_BASE_P(X, MODE) \
+  iq2000_reg_mode_ok_for_base_p (X, MODE, 1)
+#endif
+
+#if 1
+#define GO_PRINTF(x)   fprintf(stderr, (x))
+#define GO_PRINTF2(x,y)        fprintf(stderr, (x), (y))
+#define GO_DEBUG_RTX(x) debug_rtx(x)
+
+#else
+#define GO_PRINTF(x)
+#define GO_PRINTF2(x,y)
+#define GO_DEBUG_RTX(x)
+#endif
+
+/* Specify the tree operation to be used to convert reals to integers.  */
+#define IMPLICIT_FIX_EXPR FIX_ROUND_EXPR
+
+/* This is the kind of divide that is easiest to do in the general case.  */
+#define EASY_DIV_EXPR TRUNC_DIV_EXPR
+
+/* Define this if zero-extension is slow (more than one real instruction).  */
+#define SLOW_ZERO_EXTEND
+
+/* If defined, modifies the length assigned to instruction INSN as a
+   function of the context in which it is used.  LENGTH is an lvalue
+   that contains the initially computed length of the insn and should
+   be updated with the correct length of the insn.  */
+#define ADJUST_INSN_LENGTH(INSN, LENGTH) \
+  ((LENGTH) = iq2000_adjust_insn_length ((INSN), (LENGTH)))
+
+\f
+/* A list of predicates that do special things with modes, and so
+   should not elicit warnings for VOIDmode match_operand.  */
+
+#define SPECIAL_MODE_PREDICATES \
+  "pc_or_label_operand",
+
+\f
+
+
+/* How to tell the debugger about changes of source files.  */
+
+#ifndef SET_FILE_NUMBER
+#define SET_FILE_NUMBER() ++num_source_filenames
+#endif
+
+/* This is how to output a note the debugger telling it the line number
+   to which the following sequence of instructions corresponds.  */
+
+#ifndef LABEL_AFTER_LOC
+#define LABEL_AFTER_LOC(STREAM)
+#endif
+
+/* Handle certain cpp directives used in header files on sysV.  */
+#define SCCS_DIRECTIVE
+
+\f
+/* Default to -G 8 */
+#ifndef IQ2000_DEFAULT_GVALUE
+#define IQ2000_DEFAULT_GVALUE 8
+#endif
+
+#define SDATA_SECTION_ASM_OP   "\t.sdata"      /* small data */
+
+/* Given a decl node or constant node, choose the section to output it in
+   and select that section.  */
+
+#undef  TARGET_ASM_SELECT_SECTION
+#define TARGET_ASM_SELECT_SECTION  iq2000_select_section
+\f
+/* See iq2000_expand_prologue's use of loadgp for when this should be
+   true.  */
+
+#define DONT_ACCESS_GBLS_AFTER_EPILOGUE 0
+\f
+
+#ifndef INIT_SUBTARGET_OPTABS
+#define INIT_SUBTARGET_OPTABS
+#endif
+
+enum iq2000_builtins
+{
+  IQ2000_BUILTIN_ADO16,
+  IQ2000_BUILTIN_CFC0,
+  IQ2000_BUILTIN_CFC1,
+  IQ2000_BUILTIN_CFC2,
+  IQ2000_BUILTIN_CFC3,
+  IQ2000_BUILTIN_CHKHDR,
+  IQ2000_BUILTIN_CTC0,
+  IQ2000_BUILTIN_CTC1,
+  IQ2000_BUILTIN_CTC2,
+  IQ2000_BUILTIN_CTC3,
+  IQ2000_BUILTIN_LU,
+  IQ2000_BUILTIN_LUC32L,
+  IQ2000_BUILTIN_LUC64,
+  IQ2000_BUILTIN_LUC64L,
+  IQ2000_BUILTIN_LUK,
+  IQ2000_BUILTIN_LULCK,
+  IQ2000_BUILTIN_LUM32,
+  IQ2000_BUILTIN_LUM32L,
+  IQ2000_BUILTIN_LUM64,
+  IQ2000_BUILTIN_LUM64L,
+  IQ2000_BUILTIN_LUR,
+  IQ2000_BUILTIN_LURL,
+  IQ2000_BUILTIN_MFC0,
+  IQ2000_BUILTIN_MFC1,
+  IQ2000_BUILTIN_MFC2,
+  IQ2000_BUILTIN_MFC3,
+  IQ2000_BUILTIN_MRGB,
+  IQ2000_BUILTIN_MTC0,
+  IQ2000_BUILTIN_MTC1,
+  IQ2000_BUILTIN_MTC2,
+  IQ2000_BUILTIN_MTC3,
+  IQ2000_BUILTIN_PKRL,
+  IQ2000_BUILTIN_RAM,
+  IQ2000_BUILTIN_RB,
+  IQ2000_BUILTIN_RX,
+  IQ2000_BUILTIN_SRRD,
+  IQ2000_BUILTIN_SRRDL,
+  IQ2000_BUILTIN_SRULC,
+  IQ2000_BUILTIN_SRULCK,
+  IQ2000_BUILTIN_SRWR,
+  IQ2000_BUILTIN_SRWRU,
+  IQ2000_BUILTIN_TRAPQF,
+  IQ2000_BUILTIN_TRAPQFL,
+  IQ2000_BUILTIN_TRAPQN,
+  IQ2000_BUILTIN_TRAPQNE,
+  IQ2000_BUILTIN_TRAPRE,
+  IQ2000_BUILTIN_TRAPREL,
+  IQ2000_BUILTIN_WB,
+  IQ2000_BUILTIN_WBR,
+  IQ2000_BUILTIN_WBU,
+  IQ2000_BUILTIN_WX,
+  IQ2000_BUILTIN_SYSCALL
+};
diff --git a/gcc/config/iq2000/iq2000.md b/gcc/config/iq2000/iq2000.md
new file mode 100644 (file)
index 0000000..de493b3
--- /dev/null
@@ -0,0 +1,2553 @@
+;;  iq2000.md       Machine Description for Vitesse IQ2000 processors
+;;  Copyright (C) 2003 Free Software Foundation, Inc.
+
+;; This file is part of GNU CC.
+
+;; GNU CC is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU CC is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU CC; see the file COPYING.  If not, write to
+;; the Free Software Foundation, 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;; ??? Currently does not have define_function_unit support for the R8000.
+;; Must include new entries for fmadd in addition to existing entries.
+
+(define_constants
+  [(UNSPEC_ADO16 0)
+   (UNSPEC_RAM 1)
+   (UNSPEC_CHKHDR 2)
+   (UNSPEC_PKRL        3)
+   (UNSPEC_CFC0        4)
+   (UNSPEC_CFC1        5)
+   (UNSPEC_CFC2        6)
+   (UNSPEC_CFC3        7)
+   (UNSPEC_CTC0        8)
+   (UNSPEC_CTC1        9)
+   (UNSPEC_CTC2        10)
+   (UNSPEC_CTC3        11)
+   (UNSPEC_MFC0        12)
+   (UNSPEC_MFC1        13)
+   (UNSPEC_MFC2        14)
+   (UNSPEC_MFC3        15)
+   (UNSPEC_MTC0        16)
+   (UNSPEC_MTC1        17)
+   (UNSPEC_MTC2        18)
+   (UNSPEC_MTC3        19)
+   (UNSPEC_LUR 20)
+   (UNSPEC_RB  21)
+   (UNSPEC_RX  22)
+   (UNSPEC_SRRD        23)
+   (UNSPEC_SRWR        24)
+   (UNSPEC_WB  25)
+   (UNSPEC_WX  26)
+   (UNSPEC_LUC32 49)
+   (UNSPEC_LUC32L 27)
+   (UNSPEC_LUC64 28)
+   (UNSPEC_LUC64L 29)
+   (UNSPEC_LUK 30)
+   (UNSPEC_LULCK 31)
+   (UNSPEC_LUM32 32)
+   (UNSPEC_LUM32L 33)
+   (UNSPEC_LUM64 34)
+   (UNSPEC_LUM64L 35)
+   (UNSPEC_LURL 36)
+   (UNSPEC_MRGB 37)
+   (UNSPEC_SRRDL 38)
+   (UNSPEC_SRULCK 39)
+   (UNSPEC_SRWRU 40)
+   (UNSPEC_TRAPQFL 41)
+   (UNSPEC_TRAPQNE 42)
+   (UNSPEC_TRAPREL 43)
+   (UNSPEC_WBU 44)
+   (UNSPEC_SYSCALL 45)]
+)
+;; UNSPEC values used in iq2000.md
+;; Number      USE
+;; 0           movsi_ul
+;; 1           movsi_us, get_fnaddr
+;; 3           eh_set_return
+;; 20          builtin_setjmp_setup
+;;
+;; UNSPEC_VOLATILE values
+;; 0           blockage
+;; 2           loadgp
+;; 3           builtin_longjmp
+;; 4           exception_receiver
+;; 10          consttable_qi
+;; 11          consttable_hi
+;; 12          consttable_si
+;; 13          consttable_di
+;; 14          consttable_sf
+;; 15          consttable_df
+;; 16          align_2
+;; 17          align_4
+;; 18          align_8
+\f
+
+;; ....................
+;;
+;;     Attributes
+;;
+;; ....................
+
+;; Classification of each insn.
+;; branch      conditional branch
+;; jump                unconditional jump
+;; call                unconditional call
+;; load                load instruction(s)
+;; store       store instruction(s)
+;; move                data movement within same register set
+;; xfer                transfer to/from coprocessor
+;; arith       integer arithmetic instruction
+;; darith      double precision integer arithmetic instructions
+;; imul                integer multiply
+;; idiv                integer divide
+;; icmp                integer compare
+;; fadd                floating point add/subtract
+;; fmul                floating point multiply
+;; fmadd       floating point multiply-add
+;; fdiv                floating point divide
+;; fabs                floating point absolute value
+;; fneg                floating point negation
+;; fcmp                floating point compare
+;; fcvt                floating point convert
+;; fsqrt       floating point square root
+;; multi       multiword sequence (or user asm statements)
+;; nop         no operation
+
+(define_attr "type"
+  "unknown,branch,jump,call,load,store,move,xfer,arith,darith,imul,idiv,icmp,fadd,fmul,fmadd,fdiv,fabs,fneg,fcmp,fcvt,fsqrt,multi,nop"
+  (const_string "unknown"))
+
+;; Main data type used by the insn
+(define_attr "mode" "unknown,none,QI,HI,SI,DI,SF,DF,FPSW" (const_string "unknown"))
+
+;; Length (in # of bytes).  A conditional branch is allowed only to a
+;; location within a signed 18-bit offset of the delay slot.  If that
+;; provides too small a range, we use the `j' instruction.  This
+;; instruction takes a 28-bit value, but that value is not an offset.
+;; Instead, it's bitwise-ored with the high-order four bits of the
+;; instruction in the delay slot, which means it cannot be used to
+;; cross a 256MB boundary.  We could fall back back on the jr,
+;; instruction which allows full access to the entire address space,
+;; but we do not do so at present.
+
+(define_attr "length" ""
+   (cond [(eq_attr "type" "branch")
+          (cond [(lt (abs (minus (match_dup 1) (plus (pc) (const_int 4))))
+                     (const_int 131072))
+                 (const_int 4)]
+                (const_int 12))]
+          (const_int 4)))
+
+(define_attr "cpu"
+  "default,iq2000"
+  (const (symbol_ref "iq2000_cpu_attr")))
+
+;; Does the instruction have a mandatory delay slot? has_dslot
+;; Can the instruction be in a delay slot? ok_in_dslot
+;; Can the instruction not be in a delay slot? not_in_dslot
+(define_attr "dslot" "has_dslot,ok_in_dslot,not_in_dslot"
+  (if_then_else (eq_attr "type" "branch,jump,call,xfer,fcmp")
+               (const_string "has_dslot")
+               (const_string "ok_in_dslot")))
+
+;; Attribute defining whether or not we can use the branch-likely instructions
+
+(define_attr "branch_likely" "no,yes"
+  (const
+   (if_then_else (ne (symbol_ref "GENERATE_BRANCHLIKELY") (const_int 0))
+                (const_string "yes")
+                (const_string "no"))))
+
+
+;; Describe a user's asm statement.
+(define_asm_attributes
+  [(set_attr "type" "multi")])
+
+\f
+
+;; .........................
+;;
+;;     Delay slots, can't describe load/fcmp/xfer delay slots here
+;;
+;; .........................
+
+(define_delay (eq_attr "type" "jump")
+  [(and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4"))
+   (nil)
+   (nil)])
+
+(define_delay (eq_attr "type" "branch")
+  [(and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4"))
+   (nil)
+   (and (eq_attr "branch_likely" "yes") (and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4")))])
+
+(define_delay (eq_attr "type" "call")
+  [(and (eq_attr "dslot" "ok_in_dslot") (eq_attr "length" "4"))
+   (nil)
+   (nil)])
+
+\f
+
+;; .........................
+;;
+;;     Functional units
+;;
+;; .........................
+
+; (define_function_unit NAME MULTIPLICITY SIMULTANEITY
+;                      TEST READY-DELAY ISSUE-DELAY [CONFLICT-LIST])
+
+;; Make the default case (PROCESSOR_DEFAULT) handle the worst case
+
+(define_function_unit "memory" 1 0
+  (and (eq_attr "type" "load")
+       (eq_attr "cpu" "iq2000"))
+  3 0)
+
+(define_function_unit "memory" 1 0
+  (and (eq_attr "type" "move")
+       (eq_attr "cpu" "iq2000"))
+  3 0)
+
+(define_function_unit "memory" 1 0
+  (and (eq_attr "type" "arith")
+       (eq_attr "cpu" "iq2000"))
+  3 0)
+
+(define_function_unit "memory"   1 0 (eq_attr "type" "store") 1 0)
+
+(define_function_unit "memory"   1 0 (eq_attr "type" "xfer") 2 0)
+\f
+;;
+;;  ....................
+;;
+;;     CONDITIONAL TRAPS
+;;
+;;  ....................
+;;
+
+(define_insn "trap"
+  [(trap_if (const_int 1) (const_int 0))]
+  ""
+  "*
+{
+  return \"break\";
+}")
+\f
+;;
+;;  ....................
+;;
+;;     ADDITION
+;;
+;;  ....................
+;;
+
+(define_expand "addsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+                (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "")
+
+(define_insn "addsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d,=d")
+       (plus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ,dJ")
+                (match_operand:SI 2 "arith_operand" "d,I")))]
+  ""
+  "@
+   addu\\t%0,%z1,%2
+   addiu\\t%0,%z1,%2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+\f
+;;
+;;  ....................
+;;
+;;     SUBTRACTION
+;;
+;;  ....................
+;;
+
+(define_expand "subsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ")
+                 (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "")
+
+(define_insn "subsi3_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d,=d")
+       (minus:SI (match_operand:SI 1 "reg_or_0_operand" "dJ,dJ")
+                 (match_operand:SI 2 "arith_operand" "d,I")))]
+  ""
+  "@
+   subu\\t%0,%z1,%2
+   addiu\\t%0,%z1,%n2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+\f
+;;
+;;  ....................
+;;
+;;     NEGATION and ONE'S COMPLEMENT
+;;
+;;  ....................
+
+(define_insn "negsi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (neg:SI (match_operand:SI 1 "register_operand" "d")))]
+  ""
+  "*
+{
+  operands[2] = const0_rtx;
+  return \"subu\\t%0,%z2,%1\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_insn "one_cmplsi2"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (not:SI (match_operand:SI 1 "register_operand" "d")))]
+  ""
+  "*
+{
+  operands[2] = const0_rtx;
+  return \"nor\\t%0,%z2,%1\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+\f
+;;
+;;  ....................
+;;
+;;     LOGICAL
+;;
+;;  ....................
+;;
+
+(define_expand "andsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d,d")
+               (match_operand:SI 2 "nonmemory_operand" "d,K,N")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (and:SI (match_operand:SI 1 "uns_arith_operand" "%d,d,d")
+               (match_operand:SI 2 "nonmemory_operand" "d,K,N")))]
+  ""
+  "*
+{
+  if (which_alternative == 0)
+    return \"and\\t%0,%1,%2\";
+  else if (which_alternative == 1)
+    return \"andi\\t%0,%1,%x2\";
+  else if (which_alternative == 2)
+    {
+      if ((INTVAL (operands[2]) & 0xffff) == 0xffff)
+       {
+         operands[2] = GEN_INT (INTVAL (operands[2]) >> 16);
+         return \"andoui\\t%0,%1,%x2\";
+       }
+      else
+       {
+         operands[2] = GEN_INT (INTVAL (operands[2]) & 0xffff);
+         return \"andoi\\t%0,%1,%x2\";
+       }
+    }
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "iorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (ior:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  ""
+  "@
+   or\\t%0,%1,%2
+   ori\\t%0,%1,%x2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "xorsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (xor:SI (match_operand:SI 1 "uns_arith_operand" "%d,d")
+               (match_operand:SI 2 "uns_arith_operand" "d,K")))]
+  ""
+  "@
+   xor\\t%0,%1,%2
+   xori\\t%0,%1,%x2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_insn "*norsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (and:SI (not:SI (match_operand:SI 1 "register_operand" "d"))
+               (not:SI (match_operand:SI 2 "register_operand" "d"))))]
+  ""
+  "nor\\t%0,%z1,%z2"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+\f
+;;
+;;  ....................
+;;
+;;     ZERO EXTENSION
+;;
+;;  ....................
+
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+(define_expand "zero_extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (zero_extend:SI (match_operand:HI 1 "nonimmediate_operand" "d,R,m")))]
+  ""
+  "*
+{
+  if (which_alternative == 0)
+    return \"andi\\t%0,%1,0xffff\";
+  else
+    return iq2000_move_1word (operands, insn, TRUE);
+}"
+  [(set_attr "type"    "arith,load,load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "4,4,8")])
+
+(define_expand "zero_extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "")
+       (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:HI 0 "register_operand" "=d,d,d")
+       (zero_extend:HI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+  ""
+  "*
+{
+  if (which_alternative == 0)
+    return \"andi\\t%0,%1,0x00ff\";
+  else
+    return iq2000_move_1word (operands, insn, TRUE);
+}"
+  [(set_attr "type"    "arith,load,load")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "4,4,8")])
+
+(define_expand "zero_extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  ""
+  "")
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=d,d,d")
+       (zero_extend:SI (match_operand:QI 1 "nonimmediate_operand" "d,R,m")))]
+  ""
+  "*
+{
+  if (which_alternative == 0)
+    return \"andi\\t%0,%1,0x00ff\";
+  else
+    return iq2000_move_1word (operands, insn, TRUE);
+}"
+  [(set_attr "type"    "arith,load,load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "4,4,8")])
+
+;;
+;;  ....................
+;;
+;;     SIGN EXTENSION
+;;
+;;  ....................
+
+;; Extension insns.
+;; Those for integer source operand are ordered widest source type first.
+
+;; These patterns originally accepted general_operands, however, slightly
+;; better code is generated by only accepting register_operands, and then
+;; letting combine generate the lh and lb insns.
+
+(define_expand "extendhisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (sign_extend:SI (match_operand:HI 1 "nonimmediate_operand" "")))]
+  ""
+  "
+{
+  if (optimize && GET_CODE (operands[1]) == MEM)
+    operands[1] = force_not_mem (operands[1]);
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx op1   = gen_lowpart (SImode, operands[1]);
+      rtx temp  = gen_reg_rtx (SImode);
+      rtx shift = GEN_INT (16);
+
+      emit_insn (gen_ashlsi3 (temp, op1, shift));
+      emit_insn (gen_ashrsi3 (operands[0], temp, shift));
+      DONE;
+    }
+}")
+
+(define_insn "extendhisi2_internal"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (sign_extend:SI (match_operand:HI 1 "memory_operand" "R,m")))]
+  ""
+  "* return iq2000_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "4,8")])
+
+(define_expand "extendqihi2"
+  [(set (match_operand:HI 0 "register_operand" "")
+       (sign_extend:HI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  ""
+  "
+{
+  if (optimize && GET_CODE (operands[1]) == MEM)
+    operands[1] = force_not_mem (operands[1]);
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx op0   = gen_lowpart (SImode, operands[0]);
+      rtx op1   = gen_lowpart (SImode, operands[1]);
+      rtx temp  = gen_reg_rtx (SImode);
+      rtx shift = GEN_INT (24);
+
+      emit_insn (gen_ashlsi3 (temp, op1, shift));
+      emit_insn (gen_ashrsi3 (op0, temp, shift));
+      DONE;
+    }
+}")
+
+(define_insn "extendqihi2_internal"
+  [(set (match_operand:HI 0 "register_operand" "=d,d")
+       (sign_extend:HI (match_operand:QI 1 "memory_operand" "R,m")))]
+  ""
+  "* return iq2000_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "4,8")])
+
+
+(define_expand "extendqisi2"
+  [(set (match_operand:SI 0 "register_operand" "")
+       (sign_extend:SI (match_operand:QI 1 "nonimmediate_operand" "")))]
+  ""
+  "
+{
+  if (optimize && GET_CODE (operands[1]) == MEM)
+    operands[1] = force_not_mem (operands[1]);
+
+  if (GET_CODE (operands[1]) != MEM)
+    {
+      rtx op1   = gen_lowpart (SImode, operands[1]);
+      rtx temp  = gen_reg_rtx (SImode);
+      rtx shift = GEN_INT (24);
+
+      emit_insn (gen_ashlsi3 (temp, op1, shift));
+      emit_insn (gen_ashrsi3 (operands[0], temp, shift));
+      DONE;
+    }
+}")
+
+(define_insn "extendqisi2_insn"
+  [(set (match_operand:SI 0 "register_operand" "=d,d")
+       (sign_extend:SI (match_operand:QI 1 "memory_operand" "R,m")))]
+  ""
+  "* return iq2000_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "load")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "4,8")])
+\f
+;;
+;;  ........................
+;;
+;;      BIT FIELD EXTRACTION
+;;
+;;  ........................
+
+(define_insn "extzv"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+        (zero_extract:SI (match_operand:SI 1 "register_operand" "r")
+                         (match_operand:SI 2 "const_int_operand" "O")
+                         (match_operand:SI 3 "const_int_operand" "O")))]
+  ""
+  "*
+{
+  int value[4];
+  value[2] = INTVAL (operands[2]);
+  value[3] = INTVAL (operands[3]);
+  operands[2] = GEN_INT ((value[3]));
+  operands[3] = GEN_INT ((32 - value[2]));
+  return \"ram\\t%0,%1,%2,%3,0x0\";  
+}"
+  [(set_attr "type" "arith")])
+\f
+;;
+;;  ....................
+;;
+;;     DATA MOVEMENT
+;;
+;;  ....................
+
+/* Take care of constants that don't fit in single instruction */
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+        (match_operand:SI 1 "general_operand" ""))]
+  "(reload_in_progress || reload_completed)
+   && large_int (operands[1], SImode)"
+
+  [(set (match_dup 0 )
+        (high:SI (match_dup 1)))
+   (set (match_dup 0 )
+        (lo_sum:SI (match_dup 0)
+                   (match_dup 1)))]
+)
+
+;; ??? iq2000_move_1word has support for HIGH, so this pattern may be
+;; unnecessary.
+
+(define_insn "high"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (high:SI (match_operand:SI 1 "immediate_operand" "")))]
+  ""
+  "lui\\t%0,%%hi(%1) # high"
+  [(set_attr "type"    "move")])
+
+(define_insn "low"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+       (lo_sum:SI (match_operand:SI 1 "register_operand" "r")
+                  (match_operand:SI 2 "immediate_operand" "")))]
+  ""
+  "addiu\\t%0,%1,%%lo(%2) # low"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+;; 32-bit Integer moves
+
+(define_split
+  [(set (match_operand:SI 0 "register_operand" "")
+       (match_operand:SI 1 "large_int" ""))]
+  "reload_in_progress | reload_completed"
+  [(set (match_dup 0)
+       (match_dup 2))
+   (set (match_dup 0)
+       (ior:SI (match_dup 0)
+               (match_dup 3)))]
+  "
+{
+  operands[2] = GEN_INT (trunc_int_for_mode (INTVAL (operands[1])
+                                            & BITMASK_UPPER16,
+                                            SImode));
+  operands[3] = GEN_INT (INTVAL (operands[1]) & BITMASK_LOWER16);
+}")
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+
+(define_expand "movsi"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "")
+       (match_operand:SI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (iq2000_check_split (operands[1], SImode))
+    {
+      enum machine_mode mode = GET_MODE (operands[0]);
+      rtx tem = ((reload_in_progress | reload_completed)
+                ? operands[0] : gen_reg_rtx (mode));
+
+      emit_insn (gen_rtx_SET (VOIDmode, tem,
+                             gen_rtx_HIGH (mode, operands[1])));
+
+      operands[1] = gen_rtx_LO_SUM (mode, tem, operands[1]);
+    }
+
+  if ((reload_in_progress | reload_completed) == 0
+      && !register_operand (operands[0], SImode)
+      && !register_operand (operands[1], SImode)
+      && (GET_CODE (operands[1]) != CONST_INT
+         || INTVAL (operands[1]) != 0))
+    {
+      rtx temp = force_reg (SImode, operands[1]);
+      emit_move_insn (operands[0], temp);
+      DONE;
+    }
+
+  /* Take care of constants that don't fit in single instruction */
+  if ((reload_in_progress || reload_completed)
+      && CONSTANT_P (operands[1])
+      && GET_CODE (operands[1]) != HIGH
+      && GET_CODE (operands[1]) != LO_SUM
+      && ! SMALL_INT_UNSIGNED (operands[1]))
+    {
+      rtx tem = ((reload_in_progress | reload_completed)
+                ? operands[0] : gen_reg_rtx (SImode));
+
+      emit_insn (gen_rtx_SET (VOIDmode, tem,
+                             gen_rtx_HIGH (SImode, operands[1])));
+      operands[1] = gen_rtx_LO_SUM (SImode, tem, operands[1]);
+    }
+}")
+
+;; The difference between these two is whether or not ints are allowed
+;; in FP registers (off by default, use -mdebugh to enable).
+
+(define_insn "movsi_internal2"
+  [(set (match_operand:SI 0 "nonimmediate_operand" "=d,d,d,d,d,d,R,m,*d,*z,*x,*d,*x,*d")
+       (match_operand:SI 1 "move_operand" "d,S,IKL,Mnis,R,m,dJ,dJ,*z,*d,J,*x,*d,*a"))]
+  "(register_operand (operands[0], SImode)
+       || register_operand (operands[1], SImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+  "* return iq2000_move_1word (operands, insn, FALSE);"
+  [(set_attr "type"    "move,load,arith,arith,load,load,store,store,xfer,xfer,move,move,move,move")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "4,8,4,8,4,8,4,8,4,4,4,4,4,4")])
+
+;; 16-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined
+
+(define_expand "movhi"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "")
+       (match_operand:HI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if ((reload_in_progress | reload_completed) == 0
+      && !register_operand (operands[0], HImode)
+      && !register_operand (operands[1], HImode)
+      && ((GET_CODE (operands[1]) != CONST_INT
+         || INTVAL (operands[1]) != 0)))
+    {
+      rtx temp = force_reg (HImode, operands[1]);
+      emit_move_insn (operands[0], temp);
+      DONE;
+    }
+}")
+
+;; The difference between these two is whether or not ints are allowed
+;; in FP registers (off by default, use -mdebugh to enable).
+
+(define_insn "movhi_internal2"
+  [(set (match_operand:HI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d")
+       (match_operand:HI 1 "general_operand"       "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))]
+  "(register_operand (operands[0], HImode)
+       || register_operand (operands[1], HImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+  "* return iq2000_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "move,arith,load,load,store,store,xfer,xfer,move,move")
+   (set_attr "mode"    "HI")
+   (set_attr "length"  "4,4,4,8,4,8,4,4,4,4")])
+
+;; 8-bit Integer moves
+
+;; Unlike most other insns, the move insns can't be split with
+;; different predicates, because register spilling and other parts of
+;; the compiler, have memoized the insn number already.
+;; Unsigned loads are used because BYTE_LOADS_ZERO_EXTEND is defined
+
+(define_expand "movqi"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "")
+       (match_operand:QI 1 "general_operand" ""))]
+  ""
+  "
+{
+  if ((reload_in_progress | reload_completed) == 0
+      && !register_operand (operands[0], QImode)
+      && !register_operand (operands[1], QImode)
+      && (GET_CODE (operands[1]) != CONST_INT
+          || INTVAL (operands[1]) != 0))
+    {
+      rtx temp = force_reg (QImode, operands[1]);
+      emit_move_insn (operands[0], temp);
+      DONE;
+    }
+}")
+
+;; The difference between these two is whether or not ints are allowed
+;; in FP registers (off by default, use -mdebugh to enable).
+
+(define_insn "movqi_internal2"
+  [(set (match_operand:QI 0 "nonimmediate_operand" "=d,d,d,d,R,m,*d,*z,*x,*d")
+       (match_operand:QI 1 "general_operand"       "d,IK,R,m,dJ,dJ,*z,*d,*d,*x"))]
+  "(register_operand (operands[0], QImode)
+       || register_operand (operands[1], QImode)
+       || (GET_CODE (operands[1]) == CONST_INT && INTVAL (operands[1]) == 0))"
+  "* return iq2000_move_1word (operands, insn, TRUE);"
+  [(set_attr "type"    "move,arith,load,load,store,store,xfer,xfer,move,move")
+   (set_attr "mode"    "QI")
+   (set_attr "length"  "4,4,4,8,4,8,4,4,4,4")])
+
+;; 32-bit floating point moves
+
+(define_expand "movsf"
+  [(set (match_operand:SF 0 "general_operand" "")
+        (match_operand:SF 1 "general_operand" ""))]
+  ""
+  "
+{
+  if (!reload_in_progress
+      && !reload_completed
+      && GET_CODE (operands[0]) == MEM
+      && (GET_CODE (operands[1]) == MEM
+         || GET_CODE (operands[1]) == CONST_DOUBLE))
+    operands[1] = copy_to_mode_reg (SFmode, operands[1]);
+
+  /* Take care of reg <- SF constant */
+  if ( const_double_operand (operands[1], GET_MODE (operands[1]) ) )
+    {
+      emit_insn (gen_movsf_high (operands[0], operands[1]));
+      emit_insn (gen_movsf_lo_sum (operands[0], operands[0], operands[1]));
+      DONE;
+    }
+}")
+
+(define_insn "movsf_lo_sum"
+  [(set (match_operand:SF 0 "register_operand" "=r")
+        (lo_sum:SF (match_operand:SF 1 "register_operand" "r")
+                   (match_operand:SF 2 "const_double_operand" "")))]
+  ""
+  "*
+{
+  REAL_VALUE_TYPE r;
+  long i;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (r, operands[2]);
+  REAL_VALUE_TO_TARGET_SINGLE (r, i);
+  operands[2] = GEN_INT (i);
+  return \"addiu\\t%0,%1,%%lo(%2) # low\";
+}"
+  [(set_attr "length" "4")
+   (set_attr "type" "arith")])
+
+(define_insn "movsf_high"
+  [(set (match_operand:SF 0 "register_operand" "=r")
+        (high:SF (match_operand:SF 1 "const_double_operand" "")))]
+  ""
+  "*
+{
+  REAL_VALUE_TYPE r;
+  long i;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]);
+  REAL_VALUE_TO_TARGET_SINGLE (r, i);
+  operands[1] = GEN_INT (i);
+  return \"lui\\t%0,%%hi(%1) # high\";
+}"
+  [(set_attr "length" "4")
+   (set_attr "type" "arith")])
+
+(define_insn "*movsf_internal"
+  [(set (match_operand:SF 0 "nonimmediate_operand" "=r,r,m")
+        (match_operand:SF 1 "nonimmediate_operand" "r,m,r"))]
+  "!memory_operand (operands[0], SFmode) || !memory_operand (operands[1], SFmode)"
+  "*
+{
+  iq2000_fill_delay_slot (\"\", DELAY_LOAD, operands, insn);  
+  if (which_alternative == 0)
+    return \"or\\t%0,%1,%1\";
+  else if (which_alternative == 1)
+    return \"lw\\t%0,%1\";
+  else if (which_alternative == 2)
+    return \"sw\\t%1,%0\";
+}"
+  [(set_attr "length" "4,4,4")
+   (set_attr "type" "arith,load,store")]
+)
+\f
+;;
+;;  ....................
+;;
+;;     SHIFTS
+;;
+;;  ....................
+
+(define_expand "ashlsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+        (ashift:SI (match_operand:SI 1 "register_operand" "d")
+                   (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "")
+
+(define_insn "ashlsi3_internal1"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ashift:SI (match_operand:SI 1 "register_operand" "d")
+                  (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    {
+      operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+      return \"sll\\t%0,%1,%2\";
+    }
+  else
+    return \"sllv\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "ashrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "")
+
+(define_insn "ashrsi3_internal1"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ashiftrt:SI (match_operand:SI 1 "register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    {
+      operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+      return \"sra\\t%0,%1,%2\";
+    }
+  else
+    return \"srav\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "lshrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "")
+
+(define_insn "lshrsi3_internal1"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (lshiftrt:SI (match_operand:SI 1 "register_operand" "d")
+                    (match_operand:SI 2 "arith_operand" "dI")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[2]) == CONST_INT)
+    {
+      operands[2] = GEN_INT (INTVAL (operands[2]) & 0x1f);
+      return \"srl\\t%0,%1,%2\";
+    }
+  else
+    return \"srlv\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+;; Rotate Right
+(define_insn "rotrsi3"
+  [(set (match_operand:SI 0 "register_operand" "=r")
+        (rotatert:SI (match_operand:SI 1 "register_operand" "r")
+                     (match_operand:SI 2 "uns_arith_operand" "O")))]
+  ""
+  "ram %0,%1,%2,0x0,0x0"
+  [(set_attr "type" "arith")])
+
+\f
+;;
+;;  ....................
+;;
+;;     COMPARISONS
+;;
+;;  ....................
+
+;; Flow here is rather complex:
+;;
+;;  1) The cmp{si,di,sf,df} routine is called.  It deposits the
+;;     arguments into the branch_cmp array, and the type into
+;;     branch_type.  No RTL is generated.
+;;
+;;  2) The appropriate branch define_expand is called, which then
+;;     creates the appropriate RTL for the comparison and branch.
+;;     Different CC modes are used, based on what type of branch is
+;;     done, so that we can constrain things appropriately.  There
+;;     are assumptions in the rest of GCC that break if we fold the
+;;     operands into the branchs for integer operations, and use cc0
+;;     for floating point, so we use the fp status register instead.
+;;     If needed, an appropriate temporary is created to hold the
+;;     of the integer compare.
+
+(define_expand "cmpsi"
+  [(set (cc0)
+       (compare:CC (match_operand:SI 0 "register_operand" "")
+                   (match_operand:SI 1 "arith_operand" "")))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code message */
+    {
+      branch_cmp[0] = operands[0];
+      branch_cmp[1] = operands[1];
+      branch_type = CMP_SI;
+      DONE;
+    }
+}")
+
+(define_expand "tstsi"
+  [(set (cc0)
+       (match_operand:SI 0 "register_operand" ""))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code message */
+    {
+      branch_cmp[0] = operands[0];
+      branch_cmp[1] = const0_rtx;
+      branch_type = CMP_SI;
+      DONE;
+    }
+}")
+\f
+;;
+;;  ....................
+;;
+;;     CONDITIONAL BRANCHES
+;;
+;;  ....................
+
+;; Conditional branches on comparisons with zero.
+
+(define_insn "branch_zero"
+  [(set (pc)
+       (if_then_else
+         (match_operator:SI 0 "cmp_op"
+                           [(match_operand:SI 2 "register_operand" "d")
+                            (const_int 0)])
+        (label_ref (match_operand 1 "" ""))
+        (pc)))]
+  ""
+  "*
+{
+  return iq2000_output_conditional_branch (insn,
+                                        operands,
+                                        /*two_operands_p=*/0,
+                                        /*float_p=*/0,
+                                        /*inverted_p=*/0,
+                                        get_attr_length (insn));
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")])
+
+(define_insn "branch_zero_inverted"
+  [(set (pc)
+       (if_then_else
+         (match_operator:SI 0 "cmp_op"
+                           [(match_operand:SI 2 "register_operand" "d")
+                            (const_int 0)])
+        (pc)
+        (label_ref (match_operand 1 "" ""))))]
+  ""
+  "*
+{
+  return iq2000_output_conditional_branch (insn,
+                                        operands,
+                                        /*two_operands_p=*/0,
+                                        /*float_p=*/0,
+                                        /*inverted_p=*/1,
+                                        get_attr_length (insn));
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")])
+
+;; Conditional branch on equality comparision.
+
+(define_insn "branch_equality"
+  [(set (pc)
+       (if_then_else
+         (match_operator:SI 0 "equality_op"
+                           [(match_operand:SI 2 "register_operand" "d")
+                            (match_operand:SI 3 "register_operand" "d")])
+         (label_ref (match_operand 1 "" ""))
+         (pc)))]
+  ""
+  "*
+{
+  return iq2000_output_conditional_branch (insn,
+                                        operands,
+                                        /*two_operands_p=*/1,
+                                        /*float_p=*/0,
+                                        /*inverted_p=*/0,
+                                        get_attr_length (insn));
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")])
+
+(define_insn "branch_equality_inverted"
+  [(set (pc)
+       (if_then_else
+         (match_operator:SI 0 "equality_op"
+                           [(match_operand:SI 2 "register_operand" "d")
+                            (match_operand:SI 3 "register_operand" "d")])
+         (pc)
+         (label_ref (match_operand 1 "" ""))))]
+  ""
+  "*
+{
+  return iq2000_output_conditional_branch (insn,
+                                        operands,
+                                        /*two_operands_p=*/1,
+                                        /*float_p=*/0,
+                                        /*inverted_p=*/1,
+                                        get_attr_length (insn));
+}"
+  [(set_attr "type"    "branch")
+   (set_attr "mode"    "none")])
+
+(define_expand "beq"
+  [(set (pc)
+       (if_then_else (eq:CC (cc0)
+                            (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, EQ);
+      DONE;
+    }
+}")
+
+(define_expand "bne"
+  [(set (pc)
+       (if_then_else (ne:CC (cc0)
+                            (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, NE);
+      DONE;
+    }
+}")
+
+(define_expand "bgt"
+  [(set (pc)
+       (if_then_else (gt:CC (cc0)
+                            (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, GT);
+      DONE;
+    }
+}")
+
+(define_expand "bge"
+  [(set (pc)
+       (if_then_else (ge:CC (cc0)
+                            (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, GE);
+      DONE;
+    }
+}")
+
+(define_expand "blt"
+  [(set (pc)
+       (if_then_else (lt:CC (cc0)
+                            (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, LT);
+      DONE;
+    }
+}")
+
+(define_expand "ble"
+  [(set (pc)
+       (if_then_else (le:CC (cc0)
+                            (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, LE);
+      DONE;
+    }
+}")
+
+(define_expand "bgtu"
+  [(set (pc)
+       (if_then_else (gtu:CC (cc0)
+                             (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, GTU);
+      DONE;
+    }
+}")
+
+(define_expand "bgeu"
+  [(set (pc)
+       (if_then_else (geu:CC (cc0)
+                             (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, GEU);
+      DONE;
+    }
+}")
+
+
+(define_expand "bltu"
+  [(set (pc)
+       (if_then_else (ltu:CC (cc0)
+                             (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, LTU);
+      DONE;
+    }
+}")
+
+(define_expand "bleu"
+  [(set (pc)
+       (if_then_else (leu:CC (cc0)
+                             (const_int 0))
+                     (label_ref (match_operand 0 "" ""))
+                     (pc)))]
+  ""
+  "
+{
+  if (operands[0])             /* avoid unused code warning */
+    {
+      gen_conditional_branch (operands, LEU);
+      DONE;
+    }
+}")
+
+;; Recognize bbi and bbin instructions.  These use two unusual template
+;; patterns, %Ax and %Px.  %Ax outputs an 'i' if operand `x' is a LABEL_REF
+;; otherwise it outputs an 'in'.  %Px does nothing if `x' is PC 
+;; and outputs the operand if `x' is a LABEL_REF.
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (ne (sign_extract:SI (match_operand:SI 0 "register_operand" "r")
+                             (const_int 1)
+                             (match_operand:SI 1 "arith_operand" "I"))
+            (const_int 0))
+        (match_operand 2 "pc_or_label_operand" "")
+        (match_operand 3 "pc_or_label_operand" "")))]
+  ""
+  "bb%A2\\t%0(31-%1),%P2%P3"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (eq (sign_extract:SI (match_operand:SI 0 "register_operand" "r")
+                             (const_int 1)
+                             (match_operand:SI 1 "arith_operand" "I"))
+            (const_int 0))
+        (match_operand 2 "pc_or_label_operand" "")
+        (match_operand 3 "pc_or_label_operand" "")))]
+  ""
+  "bb%A3\\t%0(31-%1),%P2%P3"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (ne (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
+                             (const_int 1)
+                             (match_operand:SI 1 "arith_operand" "I"))
+            (const_int 0))
+        (match_operand 2 "pc_or_label_operand" "")
+        (match_operand 3 "pc_or_label_operand" "")))]
+  ""
+  "bb%A2\\t%0(31-%1),%P2%P3"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (eq (zero_extract:SI (match_operand:SI 0 "register_operand" "r")
+                             (const_int 1)
+                             (match_operand:SI 1 "arith_operand" "I"))
+            (const_int 0))
+        (match_operand 2 "pc_or_label_operand" "")
+        (match_operand 3 "pc_or_label_operand" "")))]
+  ""
+  "bb%A3\\t%0(31-%1),%P2%P3"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (eq (and:SI (match_operand:SI 0 "register_operand" "r")
+                    (match_operand:SI 1 "power_of_2_operand" "I"))
+             (const_int 0))
+        (match_operand 2 "pc_or_label_operand" "")
+        (match_operand 3 "pc_or_label_operand" "")))]
+  ""
+  "bb%A3\\t%0(%p1),%P2%P3"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+
+(define_insn ""
+  [(set (pc)
+       (if_then_else
+        (ne (and:SI (match_operand:SI 0 "register_operand" "r")
+                    (match_operand:SI 1 "power_of_2_operand" "I"))
+            (const_int 0))
+        (match_operand 2 "pc_or_label_operand" "")
+        (match_operand 3 "pc_or_label_operand" "")))]
+  ""
+  "bb%A2\\t%0(%p1),%P2%P3"
+  [(set_attr "length" "4")
+   (set_attr "type" "branch")])
+\f
+;;
+;;  ....................
+;;
+;;     SETTING A REGISTER FROM A COMPARISON
+;;
+;;  ....................
+
+(define_expand "seq"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (eq:SI (match_dup 1)
+              (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (EQ, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+
+(define_insn "seq_si_zero"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (eq:SI (match_operand:SI 1 "register_operand" "d")
+              (const_int 0)))]
+  ""
+  "sltiu\\t%0,%1,1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "sne"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ne:SI (match_dup 1)
+              (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (NE, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "sne_si_zero"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ne:SI (match_operand:SI 1 "register_operand" "d")
+              (const_int 0)))]
+  ""
+  "sltu\\t%0,%.,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "sgt"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (gt:SI (match_dup 1)
+              (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (GT, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "sgt_si"
+  [(set (match_operand:SI 0 "register_operand" "=d,=d")
+       (gt:SI (match_operand:SI 1 "register_operand" "d,d")
+              (match_operand:SI 2 "reg_or_0_operand" "d,J")))]
+  ""
+  "@
+   slt\\t%0,%z2,%1
+   slt\\t%0,%z2,%1"
+  [(set_attr "type"    "arith,arith")
+   (set_attr "mode"    "SI,SI")])
+
+(define_expand "sge"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ge:SI (match_dup 1)
+              (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (GE, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_expand "slt"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (lt:SI (match_dup 1)
+              (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (LT, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "slt_si"
+  [(set (match_operand:SI 0 "register_operand" "=d,=d")
+       (lt:SI (match_operand:SI 1 "register_operand" "d,d")
+              (match_operand:SI 2 "arith_operand" "d,I")))]
+  ""
+  "@
+   slt\\t%0,%1,%2
+   slti\\t%0,%1,%2"
+  [(set_attr "type"    "arith,arith")
+   (set_attr "mode"    "SI,SI")])
+
+(define_expand "sle"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (le:SI (match_dup 1)
+              (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (LE, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "sle_si_const"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (le:SI (match_operand:SI 1 "register_operand" "d")
+              (match_operand:SI 2 "small_int" "I")))]
+  "INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = GEN_INT (INTVAL (operands[2])+1);
+  return \"slti\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "sgtu"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (gtu:SI (match_dup 1)
+               (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (GTU, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "sgtu_si"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (gtu:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "reg_or_0_operand" "dJ")))]
+  ""
+  "sltu\\t%0,%z2,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_insn ""
+  [(set (match_operand:SI 0 "register_operand" "=t")
+       (gtu:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "register_operand" "d")))]
+  ""
+  "sltu\\t%2,%1"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+(define_expand "sgeu"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+        (geu:SI (match_dup 1)
+                (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (GEU, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_expand "sltu"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (ltu:SI (match_dup 1)
+               (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (LTU, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "sltu_si"
+  [(set (match_operand:SI 0 "register_operand" "=d,=d")
+       (ltu:SI (match_operand:SI 1 "register_operand" "d,d")
+               (match_operand:SI 2 "arith_operand" "d,I")))]
+  ""
+  "@
+   sltu\\t%0,%1,%2
+   sltiu\\t%0,%1,%2"
+  [(set_attr "type"    "arith,arith")
+   (set_attr "mode"    "SI,SI")])
+
+(define_expand "sleu"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (leu:SI (match_dup 1)
+               (match_dup 2)))]
+  ""
+  "
+{
+  if (branch_type != CMP_SI && (branch_type != CMP_DI))
+    FAIL;
+
+  /* set up operands from compare.  */
+  operands[1] = branch_cmp[0];
+  operands[2] = branch_cmp[1];
+
+  gen_int_relational (LEU, operands[0], operands[1], operands[2], (int *)0);
+  DONE;
+}")
+
+(define_insn "sleu_si_const"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+       (leu:SI (match_operand:SI 1 "register_operand" "d")
+               (match_operand:SI 2 "small_int" "I")))]
+  "INTVAL (operands[2]) < 32767"
+  "*
+{
+  operands[2] = GEN_INT (INTVAL (operands[2]) + 1);
+  return \"sltiu\\t%0,%1,%2\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")])
+
+\f
+;;
+;;  ....................
+;;
+;;     UNCONDITIONAL BRANCHES
+;;
+;;  ....................
+
+;; Unconditional branches.
+
+(define_insn "jump"
+  [(set (pc)
+       (label_ref (match_operand 0 "" "")))]
+  ""
+  "*
+{
+  if (GET_CODE (operands[0]) == REG)
+    return \"j\\t%0\";
+  return \"j\\t%l0\";
+  /* return \"b\\t%l0\";*/
+}"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")])
+
+(define_expand "indirect_jump"
+  [(set (pc) (match_operand 0 "register_operand" "d"))]
+  ""
+  "
+{
+  rtx dest;
+
+  if (operands[0])             /* eliminate unused code warnings */
+    {
+      dest = operands[0];
+      if (GET_CODE (dest) != REG || GET_MODE (dest) != Pmode)
+       operands[0] = copy_to_mode_reg (Pmode, dest);
+
+      if (!(Pmode == DImode))
+       emit_jump_insn (gen_indirect_jump_internal1 (operands[0]));
+      else
+       emit_jump_insn (gen_indirect_jump_internal2 (operands[0]));
+
+      DONE;
+    }
+}")
+
+(define_insn "indirect_jump_internal1"
+  [(set (pc) (match_operand:SI 0 "register_operand" "d"))]
+  "!(Pmode == DImode)"
+  "j\\t%0"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")])
+
+(define_expand "tablejump"
+  [(set (pc)
+       (match_operand 0 "register_operand" "d"))
+   (use (label_ref (match_operand 1 "" "")))]
+  ""
+  "
+{
+  if (operands[0])             /* eliminate unused code warnings */
+    {
+      if (GET_MODE (operands[0]) != Pmode)
+       abort ();
+
+      if (!(Pmode == DImode))
+       emit_jump_insn (gen_tablejump_internal1 (operands[0], operands[1]));
+      else
+       emit_jump_insn (gen_tablejump_internal2 (operands[0], operands[1]));
+
+      DONE;
+    }
+}")
+
+(define_insn "tablejump_internal1"
+  [(set (pc)
+       (match_operand:SI 0 "register_operand" "d"))
+   (use (label_ref (match_operand 1 "" "")))]
+  "!(Pmode == DImode)"
+  "j\\t%0"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")])
+
+(define_expand "tablejump_internal3"
+  [(parallel [(set (pc)
+                  (plus:SI (match_operand:SI 0 "register_operand" "d")
+                           (label_ref:SI (match_operand 1 "" ""))))
+             (use (label_ref:SI (match_dup 1)))])]
+  ""
+  "")
+
+;;; Make sure that this only matches the insn before ADDR_DIFF_VEC.  Otherwise
+;;; it is not valid.  ??? With the USE, the condition tests may not be required
+;;; any longer.
+
+;;; ??? The length depends on the ABI.  It is two for o32, and one for n32.
+;;; We just use the conservative number here.
+
+(define_insn ""
+  [(set (pc)
+       (plus:SI (match_operand:SI 0 "register_operand" "d")
+                (label_ref:SI (match_operand 1 "" ""))))
+   (use (label_ref:SI (match_dup 1)))]
+  "!(Pmode == DImode) && next_active_insn (insn) != 0
+   && GET_CODE (PATTERN (next_active_insn (insn))) == ADDR_DIFF_VEC
+   && PREV_INSN (next_active_insn (insn)) == operands[1]"
+  "*
+{
+  return \"j\\t%0\";
+}"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "8")])
+\f
+;;
+;;  ....................
+;;
+;;     Function prologue/epilogue
+;;
+;;  ....................
+;;
+
+(define_expand "prologue"
+  [(const_int 1)]
+  ""
+  "
+{
+  if (iq2000_isa >= 0)         /* avoid unused code warnings */
+    {
+      iq2000_expand_prologue ();
+      DONE;
+    }
+}")
+
+;; Block any insns from being moved before this point, since the
+;; profiling call to mcount can use various registers that aren't
+;; saved or used to pass arguments.
+
+(define_insn "blockage"
+  [(unspec_volatile [(const_int 0)] 0)]
+  ""
+  ""
+  [(set_attr "type"    "unknown")
+   (set_attr "mode"    "none")
+   (set_attr "length"  "0")])
+
+(define_expand "epilogue"
+  [(const_int 2)]
+  ""
+  "
+{
+  if (iq2000_isa >= 0)            /* avoid unused code warnings */
+    {
+      iq2000_expand_epilogue ();
+      DONE;
+    }
+}")
+
+;; Trivial return.  Make it look like a normal return insn as that
+;; allows jump optimizations to work better .
+(define_insn "return"
+  [(return)]
+  "iq2000_can_use_return_insn ()"
+  "j\\t%%31"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")])
+
+;; Normal return.
+
+(define_insn "return_internal"
+  [(use (match_operand 0 "pmode_register_operand" ""))
+   (return)]
+  ""
+  "*
+{
+  return \"j\\t%0\";
+}"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")])
+
+(define_insn "eh_return_internal"
+  [(const_int 4)
+   (return)
+   (use (reg:SI 26))
+   (use (reg:SI 31))]
+  ""
+  "j\\t%%26"
+  [(set_attr "type"    "jump")
+   (set_attr "mode"    "none")])
+
+(define_expand "eh_return"
+  [(use (match_operand:SI 0 "register_operand" "r"))]
+  ""
+  "
+{
+  iq2000_expand_eh_return (operands[0]);
+  DONE;
+}")
+
+\f
+;;
+;;  ....................
+;;
+;;     FUNCTION CALLS
+;;
+;;  ....................
+
+;; calls.c now passes a third argument, make saber happy
+
+(define_expand "call"
+  [(parallel [(call (match_operand 0 "memory_operand" "m")
+                   (match_operand 1 "" "i"))
+             (clobber (reg:SI 31))
+             (use (match_operand 2 "" ""))             ;; next_arg_reg
+             (use (match_operand 3 "" ""))])]          ;; struct_value_size_rtx
+  ""
+  "
+{
+  rtx addr;
+
+  if (operands[0])             /* eliminate unused code warnings */
+    {
+      addr = XEXP (operands[0], 0);
+      if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr)))
+         || ! call_insn_operand (addr, VOIDmode))
+       XEXP (operands[0], 0) = copy_to_mode_reg (Pmode, addr);
+
+      /* In order to pass small structures by value in registers
+        compatibly with the IQ2000 compiler, we need to shift the value
+        into the high part of the register.  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 insns,
+        and emit them separately.  */
+
+      if (operands[2] != (rtx)0 && GET_CODE (operands[2]) == PARALLEL)
+       {
+         rtvec adjust = XVEC (operands[2], 0);
+         int num = GET_NUM_ELEM (adjust);
+         int i;
+
+         for (i = 0; i < num; i++)
+           emit_insn (RTVEC_ELT (adjust, i));
+       }
+
+      emit_call_insn (gen_call_internal0 (operands[0], operands[1],
+                                         gen_rtx_REG (SImode,
+                                                      GP_REG_FIRST + 31)));
+      DONE;
+    }
+}")
+
+(define_expand "call_internal0"
+  [(parallel [(call (match_operand 0 "" "")
+                   (match_operand 1 "" ""))
+             (clobber (match_operand:SI 2 "" ""))])]
+  ""
+  "")
+
+(define_insn "call_internal1"
+  [(call (mem (match_operand 0 "call_insn_operand" "ri"))
+        (match_operand 1 "" "i"))
+   (clobber (match_operand:SI 2 "register_operand" "=d"))]
+  ""
+  "*
+{
+  register rtx target = operands[0];
+
+  if (GET_CODE (target) == CONST_INT)
+    return \"li\\t%@,%0\\n\\tjalr\\t%2,%@\";
+  else if (CONSTANT_ADDRESS_P (target))
+    return \"jal\\t%0\";
+  else
+    return \"jalr\\t%2,%0\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")])
+
+;; calls.c now passes a fourth argument, make saber happy
+
+(define_expand "call_value"
+  [(parallel [(set (match_operand 0 "register_operand" "=df")
+                  (call (match_operand 1 "memory_operand" "m")
+                        (match_operand 2 "" "i")))
+             (clobber (reg:SI 31))
+             (use (match_operand 3 "" ""))])]          ;; next_arg_reg
+  ""
+  "
+{
+  rtx addr;
+
+  if (operands[0])             /* eliminate unused code warning */
+    {
+      addr = XEXP (operands[1], 0);
+      if ((GET_CODE (addr) != REG && (!CONSTANT_ADDRESS_P (addr)))
+         || ! call_insn_operand (addr, VOIDmode))
+       XEXP (operands[1], 0) = copy_to_mode_reg (Pmode, addr);
+
+      /* In order to pass small structures by value in registers
+        compatibly with the IQ2000 compiler, we need to shift the value
+        into the high part of the register.  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 insns,
+        and emit them separately.  */
+
+      if (operands[3] != (rtx)0 && GET_CODE (operands[3]) == PARALLEL)
+       {
+         rtvec adjust = XVEC (operands[3], 0);
+         int num = GET_NUM_ELEM (adjust);
+         int i;
+
+         for (i = 0; i < num; i++)
+           emit_insn (RTVEC_ELT (adjust, i));
+       }
+
+      if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) > 1)
+       {
+         emit_call_insn (gen_call_value_multiple_internal0
+                         (XEXP (XVECEXP (operands[0], 0, 0), 0),
+                          operands[1], operands[2],
+                          XEXP (XVECEXP (operands[0], 0, 1), 0),
+                          gen_rtx_REG (SImode, GP_REG_FIRST + 31)));
+         DONE;
+       }
+
+      /* We have a call returning a DImode structure in an FP reg.
+        Strip off the now unnecessary PARALLEL.  */
+      if (GET_CODE (operands[0]) == PARALLEL)
+       operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
+
+      emit_call_insn (gen_call_value_internal0 (operands[0], operands[1], operands[2],
+                                               gen_rtx_REG (SImode,
+                                                            GP_REG_FIRST + 31)));
+
+      DONE;
+    }
+}")
+
+(define_expand "call_value_internal0"
+  [(parallel [(set (match_operand 0 "" "")
+                  (call (match_operand 1 "" "")
+                        (match_operand 2 "" "")))
+             (clobber (match_operand:SI 3 "" ""))])]
+  ""
+  "")
+
+(define_insn "call_value_internal1"
+  [(set (match_operand 0 "register_operand" "=df")
+        (call (mem (match_operand 1 "call_insn_operand" "ri"))
+              (match_operand 2 "" "i")))
+   (clobber (match_operand:SI 3 "register_operand" "=d"))]
+  ""
+  "*
+{
+  register rtx target = operands[1];
+
+  if (GET_CODE (target) == CONST_INT)
+    return \"li\\t%@,%1\\n\\tjalr\\t%3,%@\";
+  else if (CONSTANT_ADDRESS_P (target))
+    return \"jal\\t%1\";
+  else
+    return \"jalr\\t%3,%1\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")])
+
+(define_expand "call_value_multiple_internal0"
+  [(parallel [(set (match_operand 0 "" "")
+                  (call (match_operand 1 "" "")
+                        (match_operand 2 "" "")))
+             (set (match_operand 3 "" "")
+                  (call (match_dup 1)
+                        (match_dup 2)))
+             (clobber (match_operand:SI 4 "" ""))])]
+  ""
+  "")
+
+;; ??? May eventually need all 6 versions of the call patterns with multiple
+;; return values.
+
+(define_insn "call_value_multiple_internal1"
+  [(set (match_operand 0 "register_operand" "=df")
+        (call (mem (match_operand 1 "call_insn_operand" "ri"))
+              (match_operand 2 "" "i")))
+   (set (match_operand 3 "register_operand" "=df")
+       (call (mem (match_dup 1))
+              (match_dup 2)))
+  (clobber (match_operand:SI 4 "register_operand" "=d"))]
+  ""
+  "*
+{
+  register rtx target = operands[1];
+
+  if (GET_CODE (target) == CONST_INT)
+    return \"li\\t%@,%1\\n\\tjalr\\t%4,%@\";
+  else if (CONSTANT_ADDRESS_P (target))
+    return \"jal\\t%1\";
+  else
+    return \"jalr\\t%4,%1\";
+}"
+  [(set_attr "type"    "call")
+   (set_attr "mode"    "none")])
+
+;; Call subroutine returning any type.
+
+(define_expand "untyped_call"
+  [(parallel [(call (match_operand 0 "" "")
+                   (const_int 0))
+             (match_operand 1 "" "")
+             (match_operand 2 "" "")])]
+  ""
+  "
+{
+  if (operands[0])             /* silence statement not reached warnings */
+    {
+      int i;
+
+      emit_call_insn (GEN_CALL (operands[0], const0_rtx, NULL, const0_rtx));
+
+      for (i = 0; i < XVECLEN (operands[2], 0); i++)
+       {
+         rtx set = XVECEXP (operands[2], 0, i);
+         emit_move_insn (SET_DEST (set), SET_SRC (set));
+       }
+
+      emit_insn (gen_blockage ());
+      DONE;
+    }
+}")
+\f
+;;
+;;  ....................
+;;
+;;     MISC.
+;;
+;;  ....................
+;;
+
+(define_insn "nop"
+  [(const_int 0)]
+  ""
+  "nop"
+  [(set_attr "type"    "nop")
+   (set_attr "mode"    "none")])
+
+\f
+;; For the rare case where we need to load an address into a register
+;; that can not be recognized by the normal movsi/addsi instructions.
+;; I have no idea how many insns this can actually generate.  It should
+;; be rare, so over-estimating as 10 instructions should not have any
+;; real performance impact.
+(define_insn "leasi"
+  [(set (match_operand:SI 0 "register_operand" "=d")
+        (match_operand:SI 1 "address_operand" "p"))]
+  "Pmode == SImode"
+  "*
+{
+  rtx xoperands [3];
+
+  xoperands[0] = operands[0];
+  xoperands[1] = XEXP (operands[1], 0);
+  xoperands[2] = XEXP (operands[1], 1);
+  output_asm_insn (\"addiu\\t%0,%1,%2\", xoperands);
+  return \"\";
+}"
+  [(set_attr "type"    "arith")
+   (set_attr "mode"    "SI")
+   (set_attr "length"  "40")])
+
+(define_insn "ado16"
+  [(set (match_operand:SI             0 "register_operand" "=r")
+       (unspec:SI [(match_operand:SI 1 "register_operand" "r")
+                   (match_operand:SI 2 "register_operand" "r")]
+               UNSPEC_ADO16))]
+  ""
+  "ado16\\t%0, %1, %2"
+)
+
+(define_insn "ram"
+  [(set (match_operand:SI             0 "register_operand" "=r")
+             (unspec:SI [(match_operand:SI 1 "register_operand" "r")
+                               (match_operand:SI 2 "const_int_operand" "I")
+                               (match_operand:SI 3 "const_int_operand" "I")
+                               (match_operand:SI 4 "const_int_operand" "I")]
+                    UNSPEC_RAM))]
+  ""
+  "ram\\t%0, %1, %2, %3, %4"
+)
+
+(define_insn "chkhdr"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "=r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_CHKHDR)]
+  ""
+  "* return iq2000_fill_delay_slot (\"chkhdr\\t%0, %1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "pkrl"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_PKRL)]
+  ""
+  "* return iq2000_fill_delay_slot (\"pkrl\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "cfc0"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+    (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CFC0))]
+  ""
+  "* return iq2000_fill_delay_slot (\"cfc0\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "cfc1"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CFC1))]
+  ""
+  "* return iq2000_fill_delay_slot (\"cfc1\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "cfc2"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CFC2))]
+  ""
+  "* return iq2000_fill_delay_slot (\"cfc2\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "cfc3"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CFC3))]
+  ""
+  "* return iq2000_fill_delay_slot (\"cfc3\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "ctc0"
+  [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CTC0)]
+  ""
+  "* return iq2000_fill_delay_slot (\"ctc0\\t%z0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "ctc1"
+  [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CTC1)]
+  ""
+  "* return iq2000_fill_delay_slot (\"ctc1\\t%z0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "ctc2"
+  [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CTC2)]
+  ""
+  "* return iq2000_fill_delay_slot (\"ctc2\\t%z0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "ctc3"
+  [(unspec_volatile:SI [(match_operand:SI 0 "reg_or_0_operand" "rJ")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_CTC3)]
+  ""
+  "* return iq2000_fill_delay_slot (\"ctc3\\t%z0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "mfc0"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MFC0))]
+  ""
+  "* return iq2000_fill_delay_slot (\"mfc0\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "mfc1"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MFC1))]
+  ""
+  "* return iq2000_fill_delay_slot (\"mfc1\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "mfc2"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MFC2))]
+  ""
+  "* return iq2000_fill_delay_slot (\"mfc2\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "mfc3"
+   [(set (match_operand:SI                0 "register_operand" "=r")
+   (unspec_volatile:SI [(match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MFC3))]
+  ""
+  "* return iq2000_fill_delay_slot (\"mfc3\\t%0, %%%1\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "mtc0"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MTC0)]
+  ""
+  "* return iq2000_fill_delay_slot (\"mtc0\\t%0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "mtc1"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MTC1)]
+  ""
+  "* return iq2000_fill_delay_slot (\"mtc1\\t%0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "mtc2"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MTC2)]
+  ""
+  "* return iq2000_fill_delay_slot (\"mtc2\\t%0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "mtc3"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "const_int_operand" "I")]
+               UNSPEC_MTC3)]
+  ""
+  "* return iq2000_fill_delay_slot (\"mtc3\\t%0, %%%1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "lur"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUR)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lur\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "rb"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_RB)]
+  ""
+  "* return iq2000_fill_delay_slot (\"rb\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "rx"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_RX)]
+  ""
+  "* return iq2000_fill_delay_slot (\"rx\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "srrd"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
+               UNSPEC_SRRD)]
+  ""
+  "* return iq2000_fill_delay_slot (\"srrd\\t%0\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "srwr"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_SRWR)]
+  ""
+  "* return iq2000_fill_delay_slot (\"srwr\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "wb"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_WB)]
+  ""
+  "* return iq2000_fill_delay_slot (\"wb\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "wx"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_WX)]
+  ""
+  "* return iq2000_fill_delay_slot (\"wx\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "luc32"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUC32)]
+  ""
+  "* return iq2000_fill_delay_slot (\"luc32\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "luc32l"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUC32L)]
+  ""
+  "* return iq2000_fill_delay_slot (\"luc32l\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "luc64"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUC64)]
+  ""
+  "* return iq2000_fill_delay_slot (\"luc64\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "luc64l"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUC64L)]
+  ""
+  "* return iq2000_fill_delay_slot (\"luc64l\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "luk"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUK)]
+  ""
+  "* return iq2000_fill_delay_slot (\"luk\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "lulck"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
+               UNSPEC_LULCK)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lulck\\t%0\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "lum32"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUM32)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lum32\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "lum32l"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUM32L)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lum32l\\t%0, %1\", DELAY_NONE, operands, insn);" 
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "lum64"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUM64)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lum64\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "lum64l"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LUM64L)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lum64l\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "lurl"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_LURL)]
+  ""
+  "* return iq2000_fill_delay_slot (\"lurl\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "mrgb"
+  [(set (match_operand:SI                 0 "register_operand" "=r")
+       (unspec_volatile:SI [(match_operand:SI 1 "register_operand" "r")
+               (match_operand:SI 2 "register_operand" "r")
+               (match_operand:SI 3 "const_int_operand" "I")]
+               UNSPEC_MRGB))]
+  ""
+  "* return iq2000_fill_delay_slot (\"mrgb\\t%0, %1, %2, %3\", DELAY_LOAD, operands, insn);"
+  [(set_attr "dslot"   "ok_in_dslot")]
+)
+
+(define_insn "srrdl"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
+               UNSPEC_SRRDL)]
+  ""
+  "* return iq2000_fill_delay_slot (\"srrdl\\t%0\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "srulck"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
+               UNSPEC_SRULCK)]
+  ""
+  "* return iq2000_fill_delay_slot (\"srulck\\t%0\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "srwru"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_SRWRU)]
+  ""
+  "* return iq2000_fill_delay_slot (\"srwru\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "trapqfl"
+  [(unspec_volatile:SI [(const_int 1)] UNSPEC_TRAPQFL)]
+  ""
+  "* return iq2000_fill_delay_slot (\"trapqfl\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "trapqne"
+  [(unspec_volatile:SI [(const_int 2)] UNSPEC_TRAPQNE)]
+  ""
+  "* return iq2000_fill_delay_slot (\"trapqne\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "traprel"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")]
+               UNSPEC_TRAPREL)]
+  ""
+  "* return iq2000_fill_delay_slot (\"traprel %0\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "wbu"
+  [(unspec_volatile:SI [(match_operand:SI 0 "register_operand" "r")
+               (match_operand:SI 1 "register_operand" "r")]
+               UNSPEC_WBU)]
+  ""
+  "* return iq2000_fill_delay_slot (\"wbu\\t%0, %1\", DELAY_NONE, operands, insn);"
+  [(set_attr "dslot"   "not_in_dslot")]
+)
+
+(define_insn "syscall"
+  [(unspec_volatile:SI [(const_int 2)] UNSPEC_SYSCALL)]
+  ""
+  "syscall"
+  [(set_attr "dslot"    "not_in_dslot")]
+)
diff --git a/gcc/config/iq2000/lib2extra-funcs.c b/gcc/config/iq2000/lib2extra-funcs.c
new file mode 100644 (file)
index 0000000..e092bab
--- /dev/null
@@ -0,0 +1,17 @@
+typedef unsigned int USItype           __attribute__ ((mode (SI)));
+
+USItype
+__mulsi3 (USItype a, USItype b)
+{
+  USItype c = 0;
+
+  while (a != 0)
+    {
+      if (a & 1)
+       c += b;
+      a >>= 1;
+      b <<= 1;
+    }
+
+  return c;
+}
diff --git a/gcc/config/iq2000/t-iq2000 b/gcc/config/iq2000/t-iq2000
new file mode 100644 (file)
index 0000000..23dc955
--- /dev/null
@@ -0,0 +1,36 @@
+# Suppress building libgcc1.a, since the MIPS compiler port is complete
+# and does not need anything from libgcc1.a.
+LIBGCC1 =
+CROSS_LIBGCC1 =
+
+# We must build libgcc2.a with -G 0, in case the user wants to link
+# without the $gp register.
+TARGET_LIBGCC2_CFLAGS = -G 0
+
+LIB2FUNCS_EXTRA = $(srcdir)/config/udivmod.c $(srcdir)/config/divmod.c $(srcdir)/config/udivmodsi4.c $(srcdir)/config/iq2000/lib2extra-funcs.c
+
+# We want fine grained libraries, so use the new code to build the
+# floating point emulation libraries.
+FPBIT = fp-bit.c
+DPBIT = dp-bit.c
+
+fp-bit.c: $(srcdir)/config/fp-bit.c
+       echo '#define FLOAT'                            > fp-bit.c
+       cat $(srcdir)/config/fp-bit.c                   >> fp-bit.c
+
+dp-bit.c: $(srcdir)/config/fp-bit.c
+       cat $(srcdir)/config/fp-bit.c > dp-bit.c
+
+# Enable the following if multilibs are needed.
+# See gcc/genmultilib, gcc/gcc.texi and gcc/tm.texi for a
+# description of the options and their values.
+#
+# MULTILIB_OPTIONS    = 
+# MULTILIB_DIRNAMES   = 
+# MULTILIB_MATCHES    =
+# MULTILIB_EXCEPTIONS =
+# MULTILIB_EXTRA_OPTS = 
+#
+# LIBGCC = stmp-multilib
+# INSTALL_LIBGCC = install-multilib
+
diff --git a/gcc/config/iq2000/xm-iq2000.h b/gcc/config/iq2000/xm-iq2000.h
new file mode 100644 (file)
index 0000000..22e46bc
--- /dev/null
@@ -0,0 +1,35 @@
+/* Configuration for IQ based processors
+   Copyright (C) 2003 Free Software Foundation, Inc.
+   Contributed by Red Hat Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING.  If not, write to
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
+
+/* #defines that need visibility everywhere.  */
+#define FALSE 0
+#define TRUE 1
+
+/* This describes the machine the compiler is hosted on.  */
+#define HOST_BITS_PER_CHAR 8
+#define HOST_BITS_PER_SHORT 16
+#define HOST_BITS_PER_INT 32
+#define HOST_BITS_PER_LONG 32
+#define HOST_BITS_PER_LONGLONG 64
+
+/* Arguments to use with `exit'.  */
+#define SUCCESS_EXIT_CODE 0
+#define FATAL_EXIT_CODE 33
index efbe0492c73558dd0399fdacbfd6bcc7b8071acf..1ed6876743623d1adbfd0191aeb11356ea641b38 100644 (file)
@@ -1906,6 +1906,8 @@ GNU Compiler Collection on your machine.
 @item
 @uref{#ip2k-*-elf,,ip2k-*-elf}
 @item
+@uref{#iq2000-*-elf,,iq2000-*-elf}
+@item
 @uref{#m32r-*-elf,,m32r-*-elf}
 @item
 @uref{#m6811-elf,,m6811-elf}
@@ -2756,6 +2758,13 @@ There are no standard Unix configurations.
 
 Use @samp{configure --target=ip2k-elf --enable-languages=c} to configure GCC@.
 
+@html
+<hr />
+@end html
+@heading @anchor{iq2000-*-elf}iq2000-*-elf
+Vitesse IQ2000 processors.  These are used in embedded
+applications.  There are no standard Unix configurations.
+
 @html
 <hr />
 @end html