Initial revision
authorMichael Meissner <meissner@gcc.gnu.org>
Thu, 6 Feb 1992 20:09:50 +0000 (20:09 +0000)
committerMichael Meissner <meissner@gcc.gnu.org>
Thu, 6 Feb 1992 20:09:50 +0000 (20:09 +0000)
From-SVN: r284

gcc/config/mips/mips.c [new file with mode: 0644]

diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
new file mode 100644 (file)
index 0000000..0e6d2b2
--- /dev/null
@@ -0,0 +1,3852 @@
+/* Subroutines for insn-output.c for MIPS
+   Contributed by A. Lichnewsky, lich@inria.inria.fr.
+   Changes by     Michael Meissner, meissner@osf.org.
+   Copyright (C) 1989, 1990, 1991 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, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+
+#include "config.h"
+#include "rtl.h"
+#include "regs.h"
+#include "hard-reg-set.h"
+#include "real.h"
+#include "insn-config.h"
+#include "conditions.h"
+#include "insn-flags.h"
+#include "insn-attr.h"
+#include "insn-codes.h"
+#include "recog.h"
+#include "output.h"
+
+#undef MAX                     /* sys/param.h may also define these */
+#undef MIN
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <ctype.h>
+#include "tree.h"
+#include "expr.h"
+#include "flags.h"
+
+#ifndef R_OK
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#endif
+
+#ifdef USG
+#include "gstab.h"  /* If doing DBX on sysV, use our own stab.h.  */
+#else
+#include <stab.h>  /* On BSD, use the system's stab.h.  */
+#endif /* not USG */
+
+#ifdef __GNU_STAB__
+#define STAB_CODE_TYPE enum __stab_debug_code
+#else
+#define STAB_CODE_TYPE int
+#endif
+
+extern char *getenv ();
+
+extern char *permalloc ();
+extern void  debug_rtx ();
+extern void  abort_with_insn ();
+extern rtx   copy_to_reg ();
+extern rtx   adj_offsettable_operand ();
+extern int   offsettable_address_p ();
+extern tree  lookup_name ();
+
+extern rtx gen_movqi ();
+extern rtx gen_movhi ();
+extern rtx gen_movsi ();
+extern rtx gen_movsi_ulw ();
+extern rtx gen_movsi_usw ();
+extern rtx gen_addsi3 ();
+extern rtx gen_iorsi3 ();
+extern rtx gen_andsi3 ();
+extern rtx gen_bne ();
+extern rtx gen_beq ();
+extern rtx gen_cmpsi ();
+extern rtx gen_jump ();
+
+extern char   call_used_regs[];
+extern FILE  *asm_out_file;
+extern tree   current_function_decl;
+extern char **save_argv;
+extern char  *version_string;
+extern char  *language_string;
+
+/* Global variables for machine-dependent things.  */
+
+/* Threshold for data being put into the small data/bss area, instead
+   of the normal data area (references to the small data/bss area take
+   1 instruction, and use the global pointer, references to the normal
+   data area takes 2 instructions).  */
+int mips_section_threshold = -1;
+
+/* Count the number of .file directives, so that .loc is up to date.  */
+int num_source_filenames = 0;
+
+/* Count of the number of functions created so far, in order to make
+   unique labels for omitting the frame pointer.  */
+int number_functions_processed = 0;
+
+/* Count the number of sdb related labels are generated (to find block
+   start and end boundaries).  */
+int sdb_label_count = 0;
+
+/* Next label # for each statment for Silicon Graphics IRIS systems. */
+int sym_lineno = 0;
+
+/* Non-zero if inside of a function, because the stupid MIPS asm can't
+   handle .files inside of functions.  */
+int inside_function = 0;
+
+/* Global half-pic flag.  */
+int flag_half_pic;
+
+/* 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;
+
+/* Linked list of all externals that are to be emitted when optimizing
+   for the global pointer if they haven't been declared by the end of
+   the program with an appropriate .comm or initialization.  */
+
+struct extern_list {
+  struct extern_list *next;    /* next external */
+  char *name;                  /* name of the external */
+  int size;                    /* size in bytes */
+} *extern_head = 0;
+
+/* Name of the current function.  */
+char *current_function_name;
+
+/* Name of the file containing the current function.  */
+char *current_function_file = "";
+
+/* Warning given that Mips ECOFF can't support changing files
+   within a function.  */
+int file_in_function_warning = FALSE;
+
+/* Whether to supress issuing .loc's because the user attempted
+   to change the filename within a function.  */
+int ignore_line_number = FALSE;
+
+/* Number of nested .set noreorder, noat, nomacro, and volatile requests.  */
+int set_noreorder;
+int set_noat;
+int set_nomacro;
+int set_volatile;
+
+/* The next branch instruction is a branch likely, not branch normal.  */
+int mips_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 mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4;
+
+/* Cached operands, and operator to compare for use in set/branch on
+   condition codes.  */
+rtx branch_cmp[2];
+
+/* what type of branch to use */
+enum cmp_type branch_type;
+
+/* which cpu are we scheduling for */
+enum processor_type mips_cpu;
+
+/* which instruction set architecture to use.  */
+int mips_isa;
+
+/* Strings to hold which cpu and instruction set architecture to use.  */
+char *mips_cpu_string;         /* for -mcpu=<xxx> */
+char *mips_isa_string;         /* for -mips{1,2,3} */
+
+/* Array to RTX class classification.  At present, we care about
+   whether the operator is an add-type operator, or a divide/modulus,
+   and if divide/modulus, whether it is unsigned.  This is for the
+   peephole code.  */
+char mips_rtx_classify[NUM_RTX_CODE];
+
+/* Array giving truth value on whether or not a given hard register
+   can support a given mode.  */
+char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+
+/* Current frame information calculated by compute_frame_size.  */
+struct mips_frame_info current_frame_info;
+
+/* Zero structure to initialize current_frame_info.  */
+struct mips_frame_info zero_frame_info;
+
+/* List of all MIPS punctuation characters used by print_operand.  */
+char mips_print_operand_punct[256];
+
+/* Map GCC register number to debugger register number.  */
+int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+
+/* Buffer to use to enclose a load/store operation with %{ %} to
+   turn on .set volatile.  */
+static char volatile_buffer[60];
+
+/* Hardware names for the registers.  If -mrnames is used, this
+   will be overwritten with mips_sw_reg_names.  */
+
+char mips_reg_names[][8] =
+{
+ "$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",  "$sp",  "$fp",  "$31",
+ "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
+ "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+ "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+ "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
+ "hi",   "lo",   "$fcr31"
+};
+
+/* Mips software names for the registers, used to overwrite the
+   mips_reg_names array.  */
+
+char mips_sw_reg_names[][8] =
+{
+  "$0",   "at",   "v0",   "v1",   "a0",   "a1",   "a2",   "a3",
+  "t0",   "t1",   "t2",   "t3",   "t4",   "t5",   "t6",   "t7",
+  "s0",   "s1",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
+  "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "$fp",   "ra",
+  "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
+  "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
+  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
+  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
+  "hi",   "lo",   "$fcr31"
+};
+
+/* Map hard register number to register class */
+enum reg_class mips_regno_to_class[] =
+{
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  GR_REGS,     GR_REGS,        GR_REGS,        GR_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  FP_REGS,     FP_REGS,        FP_REGS,        FP_REGS,
+  HI_REG,      LO_REG,         ST_REGS
+};
+
+/* Map register constraint character to register class.  */
+enum reg_class mips_char_to_class[256] =
+{
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+  NO_REGS,     NO_REGS,        NO_REGS,        NO_REGS,
+};
+
+\f
+/* Return truth value of whether OP can be used as an operands
+   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 TRUE;
+
+  return register_operand (op, mode);
+}
+
+/* Return truth value of whether OP can be used as an operands
+   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 TRUE;
+
+  return register_operand (op, mode);
+}
+
+/* Return truth value of whether OP can be used as an operand in a two
+   address arithmetic insn (such as set 123456,%o4) of mode MODE.  */
+
+int
+arith32_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) == CONST_INT)
+    return TRUE;
+
+  return register_operand (op, mode);
+}
+
+/* Return truth value of whether OP is a integer which fits in 16 bits  */
+
+int
+small_int (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
+}
+
+/* Return truth value of whether OP is an integer which is too big to
+   be loaded with one instruction.  */
+
+int
+large_int (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  long value;
+
+  if (GET_CODE (op) != CONST_INT)
+    return FALSE;
+
+  value = INTVAL (op);
+  if ((value & 0xffff0000) == 0)               /* ior reg,$r0,value */
+    return FALSE;
+
+  if ((value & 0xffff0000) == 0xffff0000)      /* subu reg,$r0,value */
+    return FALSE;
+
+  if ((value & 0x0000ffff) == 0)               /* lui reg,value>>16 */
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Return truth value of whether 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:
+      if (CONST_DOUBLE_HIGH (op) != 0 || CONST_DOUBLE_LOW (op) != 0)
+       return FALSE;
+
+      return TRUE;
+
+    case REG:
+    case SUBREG:
+      return register_operand (op, mode);
+    }
+
+  return FALSE;
+}
+
+/* Return truth value of whether OP is one of the special multiply/divide
+   registers (hi, lo).  */
+
+int
+md_register_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (GET_MODE_CLASS (mode) == MODE_INT
+         && GET_CODE (op) == REG
+         && MD_REG_P (REGNO (op)));
+}
+
+/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant.  */
+
+int
+mips_const_double_ok (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (GET_CODE (op) != CONST_DOUBLE)
+    return FALSE;
+
+  if (mode == DImode)
+    return TRUE;
+
+  if (mode != SFmode && mode != DFmode)
+    return FALSE;
+
+  if (CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0)
+    return TRUE;
+
+#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+  if (TARGET_MIPS_AS)          /* gas doesn't like li.d/li.s yet */
+    {
+      union { double d; int i[2]; } u;
+      double d;
+
+      u.i[0] = CONST_DOUBLE_LOW (op);
+      u.i[1] = CONST_DOUBLE_HIGH (op);
+      d = u.d;
+
+      if (d != d)
+       return FALSE;           /* NAN */
+
+      if (d < 0.0)
+       d = - d;
+
+      /* Rather than trying to get the accuracy down to the last bit,
+        just use approximate ranges.  */
+
+      if (mode == DFmode && d > 1.0e-300 && d < 1.0e300)
+       return TRUE;
+
+      if (mode == SFmode && d > 1.0e-38 && d < 1.0e+38)
+       return TRUE;
+    }
+#endif
+
+  return FALSE;
+}
+
+/* Return truth value if a memory operand fits in a single instruction
+   (ie, register + small offset).  */
+
+int
+simple_memory_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  rtx addr, plus0, plus1;
+  int offset = 0;
+
+  /* Eliminate non-memory operations */
+  if (GET_CODE (op) != MEM)
+    return FALSE;
+
+  /* dword operations really put out 2 instructions, so eliminate them.  */
+  if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
+    return FALSE;
+
+  /* Decode the address now.  */
+  addr = XEXP (op, 0);
+  switch (GET_CODE (addr))
+    {
+    case REG:
+      return TRUE;
+
+    case CONST_INT:
+      return SMALL_INT (op);
+
+    case PLUS:
+      plus0 = XEXP (addr, 0);
+      plus1 = XEXP (addr, 1);
+      if (GET_CODE (plus0) == REG
+         && GET_CODE (plus1) == CONST_INT
+         && SMALL_INT (plus1))
+       return TRUE;
+
+      else if (GET_CODE (plus1) == REG
+              && GET_CODE (plus0) == CONST_INT
+              && SMALL_INT (plus0))
+       return TRUE;
+
+      else
+       return FALSE;
+
+#if 0
+      /* We used to allow small symbol refs here (ie, stuff in .sdata
+        or .sbss), but this causes some bugs in G++.  Also, it won't
+        interfere if the MIPS linker rewrites the store instruction
+        because the function is PIC.  */
+
+    case LABEL_REF:            /* never gp relative */
+      break;
+
+    case CONST:
+      /* If -G 0, we can never have a GP relative memory operation.
+        Also, save some time if not optimizing.  */
+      if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT)
+       return FALSE;
+
+      addr = eliminate_constant_term (addr, &offset);
+      if (GET_CODE (op) != SYMBOL_REF)
+       return FALSE;
+
+      /* fall through */
+
+    case SYMBOL_REF:
+      /* let's be paranoid.... */
+      if (offset < 0 || offset > 0xffff)
+       return FALSE;
+
+      return SYMBOL_REF_FLAG (addr);
+#endif
+    }
+
+  return FALSE;
+}
+
+/* Return true if the address is suitable for function call.  */
+
+int
+call_memory_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  rtx addr;
+  enum rtx_code code;
+
+  if (GET_CODE (op) != MEM)
+    return FALSE;
+
+  addr = XEXP (op, 0);
+  code = GET_CODE (addr);
+
+  if (GET_MODE (addr) != FUNCTION_MODE)
+    return FALSE;
+
+  if (code == REG || code == SUBREG)
+    return TRUE;
+
+  if (CONSTANT_ADDRESS_P (addr))
+    return TRUE;
+
+  return FALSE;
+}
+
+/* Return true 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 FALSE;
+
+  return (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0;
+}
+
+/* Return true 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 FALSE;
+
+  return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+}
+
+
+/* Genrecog does not take the type of match_operator into consideration,
+   and would complain about two patterns being the same if the same
+   function is used, so make it believe they are different.  */
+
+int
+cmp2_op (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (mode != GET_MODE (op))
+    return FALSE;
+
+  return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
+}
+
+/* Return true if the code is an unsigned relational operations (LEU, etc.) */
+
+int
+uns_cmp_op (op,mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (mode != GET_MODE (op))
+    return FALSE;
+
+  return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP;
+}
+
+/* Return true if the code is a relational operation FP can use.  */
+
+int
+fcmp_op (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (mode != GET_MODE (op))
+    return FALSE;
+
+  return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;
+}
+
+\f
+/* Return an operand string if the given instruction's delay slot or
+   wrap it in a .set noreorder section.  This is for filling delay
+   slots on load type instructions under GAS, which does no reordering
+   on its own.  For the MIPS assembler, all we do is update the 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.  */
+
+char *
+mips_fill_delay_slot (ret, type, operands, cur_insn)
+     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) : (rtx)0;
+  register int num_nops;
+
+  if (type == DELAY_LOAD)
+    num_nops = 1;
+
+  else if (type == DELAY_HILO)
+    num_nops = 2;
+
+  else
+    num_nops = 0;
+
+  /* Make sure that we don't put nop's after labels.  */
+  next_insn = NEXT_INSN (cur_insn);
+  while (next_insn != (rtx)0 && GET_CODE (next_insn) == NOTE)
+    next_insn = NEXT_INSN (next_insn);
+
+  dslots_load_total += num_nops;
+  if (TARGET_DEBUG_F_MODE
+      || !optimize
+      || type == DELAY_NONE
+      || operands == (rtx *)0
+      || cur_insn == (rtx)0
+      || next_insn == (rtx)0
+      || GET_CODE (next_insn) == CODE_LABEL
+      || (set_reg = operands[0]) == (rtx)0)
+    {
+      dslots_number_nops = 0;
+      mips_load_reg  = (rtx)0;
+      mips_load_reg2 = (rtx)0;
+      mips_load_reg3 = (rtx)0;
+      mips_load_reg4 = (rtx)0;
+      return ret;
+    }
+
+  set_reg = operands[0];
+  if (set_reg == (rtx)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;
+  mips_load_reg  = set_reg;
+  mips_load_reg2 = (mode == DImode || mode == DFmode)
+                       ? gen_rtx (REG, SImode, REGNO (set_reg) + 1)
+                       : (rtx)0;
+
+  if (type == DELAY_HILO)
+    {
+      mips_load_reg3 = gen_rtx (REG, SImode, MD_REG_FIRST);
+      mips_load_reg4 = gen_rtx (REG, SImode, MD_REG_FIRST+1);
+    }
+  else
+    {
+      mips_load_reg3 = 0;
+      mips_load_reg4 = 0;
+    }
+
+  if (TARGET_GAS && set_noreorder++ == 0)
+    fputs ("\t.set\tnoreorder\n", asm_out_file);
+
+  return ret;
+}
+
+\f
+/* Determine whether a memory reference takes one (based off of the GP pointer),
+   two (normal), or three (label + reg) instructins, and bump the appropriate
+   counter for -mstats.  */
+
+void
+mips_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========== mips_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:
+         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 = TRUE;
+             continue;
+           }
+
+         if (code0 == CONST_INT)
+           {
+             addr = plus1;
+             looping = TRUE;
+             continue;
+           }
+
+         if (code1 == REG)
+           {
+             additional++;
+             addr = plus0;
+             looping = TRUE;
+             continue;
+           }
+
+         if (code1 == CONST_INT)
+           {
+             addr = plus0;
+             looping = TRUE;
+             continue;
+           }
+
+         if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
+           {
+             addr = plus0;
+             looping = TRUE;
+             continue;
+           }
+
+         if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
+           {
+             addr = plus1;
+             looping = TRUE;
+             continue;
+           }
+
+         break;
+
+       case LABEL_REF:
+         n_words = 2;          /* always 2 words */
+         break;
+
+       case CONST:
+         addr = XEXP (addr, 0);
+         looping = TRUE;
+         continue;
+
+       case SYMBOL_REF:
+         n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
+         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.  */
+
+char *
+mips_move_1word (operands, insn, unsignedp)
+     rtx operands[];
+     rtx insn;
+     int unsignedp;
+{
+  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_word0 = 0;
+  int subreg_word1 = 0;
+  enum delay_type delay = DELAY_NONE;
+
+  while (code0 == SUBREG)
+    {
+      subreg_word0 += SUBREG_WORD (op0);
+      op0 = SUBREG_REG (op0);
+      code0 = GET_CODE (op0);
+    }
+
+  while (code1 == SUBREG)
+    {
+      subreg_word1 += SUBREG_WORD (op1);
+      op1 = SUBREG_REG (op1);
+      code1 = GET_CODE (op1);
+    }
+
+  if (code0 == REG)
+    {
+      int regno0 = REGNO (op0) + subreg_word0;
+
+      if (code1 == REG)
+       {
+         int regno1 = REGNO (op1) + subreg_word1;
+
+         /* Just in case, don't do anything for assigning a register
+            to itself, unless we are filling a delay slot.  */
+         if (regno0 == regno1 && set_nomacro == 0)
+           ret = "";
+
+         else if (GP_REG_P (regno0))
+           {
+             if (GP_REG_P (regno1))
+               ret = "move\t%0,%1";
+
+             else if (MD_REG_P (regno1))
+               {
+                 delay = DELAY_HILO;
+                 ret = "mf%1\t%0";
+               }
+
+             else
+               {
+                 delay = DELAY_LOAD;
+                 if (FP_REG_P (regno1))
+                   ret = "mfc1\t%0,%1";
+
+                 else if (regno1 == FPSW_REGNUM)
+                   ret = "cfc1\t%0,$31";
+               }
+           }
+
+         else if (FP_REG_P (regno0))
+           {
+             if (GP_REG_P (regno1))
+               {
+                 delay = DELAY_LOAD;
+                 ret = "mtc1\t%1,%0";
+               }
+
+             if (FP_REG_P (regno1))
+               ret = "mov.s\t%0,%1";
+           }
+
+         else if (MD_REG_P (regno0))
+           {
+             if (GP_REG_P (regno1))
+               {
+                 delay = DELAY_HILO;
+                 ret = "mt%0\t%1";
+               }
+           }
+
+         else if (regno0 == FPSW_REGNUM)
+           {
+             if (GP_REG_P (regno1))
+               {
+                 delay = DELAY_LOAD;
+                 ret = "ctc1\t%0,$31";
+               }
+           }
+       }
+
+      else if (code1 == MEM)
+       {
+         delay = DELAY_LOAD;
+
+         if (TARGET_STATS)
+           mips_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))
+               {
+               case SFmode: ret = "lw\t%0,%1"; break;
+               case SImode: 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 (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
+           ret = "l.s\t%0,%1";
+
+         if (ret != (char *)0 && MEM_VOLATILE_P (op1))
+           {
+             int i = strlen (ret);
+             if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
+               abort ();
+
+             sprintf (volatile_buffer, "%%{%s%%}", ret);
+             ret = volatile_buffer;
+           }
+       }
+
+      else if (code1 == CONST_INT)
+       {
+         if (INTVAL (op1) == 0)
+           {
+             if (GP_REG_P (regno0))
+               ret = "move\t%0,%z1";
+
+             else if (FP_REG_P (regno0))
+               {
+                 delay = DELAY_LOAD;
+                 return "mtc1\t%z1,%0";
+               }
+           }
+
+         else if (GP_REG_P (regno0))
+           {
+             if ((INTVAL (operands[1]) & 0x0000ffff) == 0)
+               ret = "lui\t%0,(%X1)>>16";
+             else
+               ret = "li\t%0,%1";
+           }
+       }
+
+      else if (code1 == CONST_DOUBLE && mode == SFmode)
+       {
+         if (CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
+           {
+             if (GP_REG_P (regno0))
+               ret = "move\t%0,%.";
+
+             else if (FP_REG_P (regno0))
+               {
+                 delay = DELAY_LOAD;
+                 ret = "mtc1\t%.,%0";
+               }
+           }
+
+         else
+           {
+             delay = DELAY_LOAD;
+             ret = "li.s\t%0,%1";
+           }
+       }
+
+      else if (code1 == LABEL_REF)
+       ret = "la\t%0,%a1";
+
+      else if (code1 == SYMBOL_REF || code1 == CONST)
+       {
+         if (TARGET_STATS)
+           mips_count_memory_refs (op1, 1);
+
+         if (flag_half_pic && !mips_constant_address_p (op1))
+           {
+             delay = DELAY_LOAD;
+             ret = "la\t%0,%a1\t\t# pic reference";
+           }
+         else
+           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);          /* reverse operands */
+             add_op1 = XEXP (op1, 0);
+           }
+
+         operands[2] = add_op0;
+         operands[3] = add_op1;
+         ret = "add%:\t%0,%2,%3";
+       }
+    }
+
+  else if (code0 == MEM)
+    {
+      if (TARGET_STATS)
+       mips_count_memory_refs (op0, 1);
+
+      if (code1 == REG)
+       {
+         int regno1 = REGNO (op1) + subreg_word1;
+
+         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;
+               }
+           }
+
+         else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
+           ret = "s.s\t%1,%0";
+       }
+
+      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;
+           }
+       }
+
+      else if (code1 == CONST_DOUBLE && CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
+       {
+         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;
+           }
+       }
+
+      if (ret != (char *)0 && MEM_VOLATILE_P (op0))
+       {
+         int i = strlen (ret);
+         if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
+           abort ();
+         
+         sprintf (volatile_buffer, "%%{%s%%}", ret);
+         ret = volatile_buffer;
+       }
+    }
+
+  if (ret == (char *)0)
+    {
+      abort_with_insn (insn, "Bad move");
+      return 0;
+    }
+
+  if (delay != DELAY_NONE)
+    return mips_fill_delay_slot (ret, delay, operands, insn);
+
+  return ret;
+}
+
+\f
+/* Return the appropriate instructions to move 2 words */
+
+char *
+mips_move_2words (operands, insn)
+     rtx operands[];
+     rtx insn;
+{
+  char *ret = 0;
+  rtx op0 = operands[0];
+  rtx op1 = operands[1];
+  enum rtx_code code0 = GET_CODE (operands[0]);
+  enum rtx_code code1 = GET_CODE (operands[1]);
+  int subreg_word0 = 0;
+  int subreg_word1 = 0;
+  enum delay_type delay = DELAY_NONE;
+
+  while (code0 == SUBREG)
+    {
+      subreg_word0 += SUBREG_WORD (op0);
+      op0 = SUBREG_REG (op0);
+      code0 = GET_CODE (op0);
+    }
+
+  while (code1 == SUBREG)
+    {
+      subreg_word1 += SUBREG_WORD (op1);
+      op1 = SUBREG_REG (op1);
+      code1 = GET_CODE (op1);
+    }
+      
+  if (code0 == REG)
+    {
+      int regno0 = REGNO (op0) + subreg_word0;
+
+      if (code1 == REG)
+       {
+         int regno1 = REGNO (op1) + subreg_word1;
+
+         /* Just in case, don't do anything for assigning a register
+            to itself, unless we are filling a delay slot.  */
+         if (regno0 == regno1 && set_nomacro == 0)
+           ret = "";
+
+         else if (FP_REG_P (regno0))
+           {
+             if (FP_REG_P (regno1))
+               ret = "mov.d\t%0,%1";
+
+             else
+               {
+                 delay = DELAY_LOAD;
+                 ret = (TARGET_FLOAT64)
+                               ? "dmtc1\t%1,%0"
+                               : "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
+               }
+           }
+
+         else if (FP_REG_P (regno1))
+           {
+             delay = DELAY_LOAD;
+             ret = (TARGET_FLOAT64)
+                       ? "dmfc1\t%0,%1"
+                       : "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
+           }
+
+         else if (MD_REG_P (regno0) && GP_REG_P (regno1))
+           {
+             delay = DELAY_HILO;
+             ret = "mthi\t%M1\n\tmtlo\t%L1";
+           }
+
+         else if (GP_REG_P (regno0) && MD_REG_P (regno1))
+           {
+             delay = DELAY_HILO;
+             ret = "mfhi\t%M0\n\tmflo\t%L0";
+           }
+
+         else if (regno0 != (regno1+1))
+           ret = "move\t%0,%1\n\tmove\t%D0,%D1";
+
+         else
+           ret = "move\t%D0,%D1\n\tmove\t%0,%1";
+       }
+
+      else if (code1 == CONST_DOUBLE)
+       {
+         if (CONST_DOUBLE_HIGH (op1) != 0 || CONST_DOUBLE_LOW (op1) != 0)
+           {
+             if (GET_MODE (op1) == DFmode)
+               {
+                 delay = DELAY_LOAD;
+                 ret = "li.d\t%0,%1";
+               }
+
+             else
+               {
+                 operands[2] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (op1));
+                 operands[3] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (op1));
+                 ret = "li\t%M0,%3\n\tli\t%L0,%2";
+               }
+           }
+
+         else
+           {
+             if (GP_REG_P (regno0))
+               ret = "move\t%0,%.\n\tmove\t%D0,%.";
+
+             else if (FP_REG_P (regno0))
+               {
+                 delay = DELAY_LOAD;
+                 ret = (TARGET_FLOAT64)
+                               ? "dmtc1\t%.,%0"
+                               : "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
+               }
+           }
+       }
+
+      else if (code1 == CONST_INT && INTVAL (op1) == 0)
+       {
+         if (GP_REG_P (regno0))
+           ret = "move\t%0,%.\n\tmove\t%D0,%.";
+         
+         else if (FP_REG_P (regno0))
+           {
+             delay = DELAY_LOAD;
+             ret = (TARGET_FLOAT64)
+                               ? "dmtc1\t%.,%0"
+                               : "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
+           }
+       }
+       
+      else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0))
+       {
+         operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) >= 0 ? 0 : -1);
+         ret = "li\t%M0,%2\n\tli\t%L0,%1";
+       }
+
+      else if (code1 == MEM)
+       {
+         delay = DELAY_LOAD;
+
+         if (TARGET_STATS)
+           mips_count_memory_refs (op1, 2);
+
+         if (FP_REG_P (regno0))
+           ret = "l.d\t%0,%1";
+
+         else if (offsettable_address_p (1, DFmode, XEXP (op1, 0)))
+           {
+             operands[2] = adj_offsettable_operand (op1, 4);
+             if (reg_mentioned_p (op0, op1))
+               ret = "lw\t%D0,%2\n\tlw\t%0,%1";
+             else
+               ret = "lw\t%0,%1\n\tlw\t%D0,%2";
+           }
+
+         if (ret != (char *)0 && MEM_VOLATILE_P (op1))
+           {
+             int i = strlen (ret);
+             if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
+               abort ();
+
+             sprintf (volatile_buffer, "%%{%s%%}", ret);
+             ret = volatile_buffer;
+           }
+       }
+    }
+
+  else if (code0 == MEM)
+    {
+      if (code1 == REG)
+       {
+         int regno1 = REGNO (op1) + subreg_word1;
+
+         if (FP_REG_P (regno1))
+           ret = "s.d\t%1,%0";
+
+         else if (offsettable_address_p (1, DFmode, XEXP (op0, 0)))
+           {
+             operands[2] = adj_offsettable_operand (op0, 4);
+             ret = "sw\t%1,%0\n\tsw\t%D1,%2";
+           }
+       }
+
+      else if (code1 == CONST_DOUBLE
+              && CONST_DOUBLE_HIGH (op1) == 0
+              && CONST_DOUBLE_LOW (op1) == 0
+              && offsettable_address_p (1, DFmode, XEXP (op0, 0)))
+       {
+         if (TARGET_FLOAT64)
+           ret = "sd\t%.,%0";
+         else
+           {
+             operands[2] = adj_offsettable_operand (op0, 4);
+             ret = "sw\t%.,%0\n\tsw\t%.,%2";
+           }
+       }
+
+      if (TARGET_STATS)
+       mips_count_memory_refs (op0, 2);
+
+      if (ret != (char *)0 && MEM_VOLATILE_P (op0))
+       {
+         int i = strlen (ret);
+         if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
+           abort ();
+         
+         sprintf (volatile_buffer, "%%{%s%%}", ret);
+         ret = volatile_buffer;
+       }
+    }
+
+  if (ret == (char *)0)
+    {
+      abort_with_insn (insn, "Bad move");
+      return 0;
+    }
+
+  if (delay != DELAY_NONE)
+    return mips_fill_delay_slot (ret, delay, operands, insn);
+
+  return ret;
+}
+
+\f
+/* Provide the costs of an addressing mode that contains ADDR.
+   If ADDR is not a valid address, its cost is irrelavent.  */
+
+int
+mips_address_cost (addr)
+     rtx addr;
+{
+  int offset;
+
+  switch (GET_CODE (addr))
+    {
+    case LO_SUM:
+    case HIGH:
+      return 1;
+
+    case LABEL_REF:
+      return 2;
+
+    case CONST:
+      offset = 0;
+      addr = eliminate_constant_term (addr, &offset);
+      if (GET_CODE (addr) == LABEL_REF)
+       return 2;
+
+      if (GET_CODE (addr) != SYMBOL_REF)
+       return 4;
+
+      if (offset < -32768 || offset > 32767)
+       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:
+           {
+             int value = INTVAL (plus1);
+             return (value < -32768 || value > 32767) ? 2 : 1;
+           }
+
+         case CONST:
+         case SYMBOL_REF:
+         case LABEL_REF:
+         case HIGH:
+         case LO_SUM:
+           return mips_address_cost (plus1) + 1;
+         }
+      }
+    }
+
+  return 4;
+}
+
+\f
+/* A C expression that is 1 if the RTX ADDR is a constant which is a
+   valid address.  On most machines, this can be defined as
+   `CONSTANT_P (ADDR)', but a few machines are more restrictive in
+   which constant addresses are supported.
+
+   `CONSTANT_P' accepts integer-values expressions whose values are
+   not explicitly known, such as `symbol_ref', `label_ref', and
+   `high' expressions and `const' arithmetic expressions, in
+   addition to `const_int' and `const_double' expressions.  */
+
+int
+mips_constant_address_p (addr)
+     rtx addr;
+{
+  int offset;
+  char *name;
+
+  if (!CONSTANT_P (addr))
+    return FALSE;
+
+  /* For -mpic-extern, don't allow any reference to an external in
+     normal code.  Force it to be PIC-ized.  */
+
+  if (flag_half_pic)
+    {
+      switch (GET_CODE (addr))
+       {
+       case CONST:
+         addr = eliminate_constant_term (addr, &offset);
+         if (GET_CODE (addr) != SYMBOL_REF)
+           return FALSE;
+
+         /* fall through */
+
+       case SYMBOL_REF:
+         name = XSTR (addr, 0);
+
+         /* Skip '*' that appears in front of labels and such.  */
+         if (name[0] == '*')
+           name++;
+
+         /* Internally generated labels are not pic.  */
+         if (name[0] == '$' && name[1] == 'L')
+           return TRUE;
+
+         return FALSE;
+       }
+    }
+
+  return TRUE;
+}
+
+\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,sf,df}.  */
+
+void
+gen_conditional_branch (operands, test_code)
+     rtx operands[];
+     enum rtx_code test_code;
+{
+  enum {
+    I_EQ,
+    I_NE,
+    I_GT,
+    I_GE,
+    I_LT,
+    I_LE,
+    I_GTU,
+    I_GEU,
+    I_LTU,
+    I_LEU,
+    I_MAX
+  } test = I_MAX;
+
+  static enum machine_mode mode_map[(int)CMP_MAX][(int)I_MAX] = {
+    {                          /* CMP_SI */
+      CC_EQmode,               /* eq  */
+      CC_EQmode,               /* ne  */
+      CCmode,                  /* gt  */
+      CCmode,                  /* ge  */
+      CCmode,                  /* lt  */
+      CCmode,                  /* le  */
+      CCmode,                  /* gtu */
+      CCmode,                  /* geu */
+      CCmode,                  /* ltu */
+      CCmode,                  /* leu */
+    },
+    {                          /* CMP_SF */
+      CC_FPmode,               /* eq  */
+      CC_FPmode,               /* ne  */
+      CC_FPmode,               /* gt  */
+      CC_FPmode,               /* ge  */
+      CC_FPmode,               /* lt  */
+      CC_FPmode,               /* le  */
+      VOIDmode,                        /* gtu */
+      VOIDmode,                        /* geu */
+      VOIDmode,                        /* ltu */
+      VOIDmode,                        /* leu */
+    },
+    {                          /* CMP_DF */
+      CC_FPmode,               /* eq  */
+      CC_FPmode,               /* ne  */
+      CC_FPmode,               /* gt  */
+      CC_FPmode,               /* ge  */
+      CC_FPmode,               /* lt  */
+      CC_FPmode,               /* le  */
+      VOIDmode,                        /* gtu */
+      VOIDmode,                        /* geu */
+      VOIDmode,                        /* ltu */
+      VOIDmode,                        /* leu */
+    },
+  };
+
+  enum machine_mode mode;
+  enum cmp_type type = branch_type;
+  rtx cmp0 = branch_cmp[0];
+  rtx cmp1 = branch_cmp[1];
+  rtx label = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
+
+  /* Make normal rtx_code into something we can index from an array */
+  switch (test_code)
+    {
+    case EQ:  test = I_EQ;  break;
+    case NE:  test = I_NE;  break;
+    case GT:  test = I_GT;  break;
+    case GE:  test = I_GE;  break;
+    case LT:  test = I_LT;  break;
+    case LE:  test = I_LE;  break;
+    case GTU: test = I_GTU; break;
+    case GEU: test = I_GEU; break;
+    case LTU: test = I_LTU; break;
+    case LEU: test = I_LEU; break;
+    }
+
+  if (test == I_MAX)
+    {
+      mode = CCmode;
+      goto fail;
+    }
+
+  /* Get the machine mode to use (CCmode, CC_EQmode, or CC_FPmode).  */
+  mode = mode_map[(int)type][(int)test];
+  if (mode == VOIDmode)
+    goto fail;
+
+  switch (branch_type)
+    {
+    default:
+      goto fail;
+
+    case CMP_SI:
+      /* Change >, >=, <, <= tests against 0 to use CC_0mode, since we have
+        special instructions to do these tests directly. */
+
+      if (mode == CCmode && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
+       {
+         emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, cmp0));
+         mode = CC_0mode;
+       }
+
+      else if (mode == CCmode && GET_CODE (cmp0) == CONST_INT && INTVAL (cmp0) == 0)
+       {
+         emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, cmp1));
+         test_code = reverse_condition (test_code);
+         mode = CC_0mode;
+       }
+
+      else
+       {
+         /* force args to register for equality comparisons. */
+         if (mode == CC_EQmode && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
+           cmp1 = force_reg (SImode, cmp1);
+
+         emit_insn (gen_rtx (SET, VOIDmode,
+                             cc0_rtx,
+                             gen_rtx (COMPARE, mode, cmp0, cmp1)));
+       }
+      break;
+
+    case CMP_DF:
+      emit_insn (gen_cmpdf_internal (cmp0, cmp1));
+      break;
+
+    case CMP_SF:
+      emit_insn (gen_cmpsf_internal (cmp0, cmp1));
+      break;
+    }
+  
+  /* Generate the jump */
+  emit_jump_insn (gen_rtx (SET, VOIDmode,
+                          pc_rtx,
+                          gen_rtx (IF_THEN_ELSE, VOIDmode,
+                                   gen_rtx (test_code, mode, cc0_rtx, const0_rtx),
+                                   label,
+                                   pc_rtx)));
+  return;
+
+fail:
+  abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test");
+}
+
+\f
+#define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT)
+
+/* Internal code to generate the load and store of one word/short/byte.
+   The load is emitted directly, and the store insn is returned.  */
+
+static rtx
+block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align)
+     rtx src_reg;              /* register holding source memory addresss */
+     rtx dest_reg;             /* register holding dest. memory addresss */
+     int *p_bytes;             /* pointer to # bytes remaining */
+     int *p_offset;            /* pointer to current offset */
+     int align;                        /* alignment */
+{
+  int bytes;                   /* # bytes remaining */
+  int offset;                  /* offset to use */
+  int size;                    /* size in bytes of load/store */
+  enum machine_mode mode;      /* mode to use for load/store */
+  rtx reg;                     /* temporary register */
+  rtx src_addr;                        /* source address */
+  rtx dest_addr;               /* destintation address */
+  rtx (*load_func)();          /* function to generate load insn */
+  rtx (*store_func)();         /* function to generate destination insn */
+
+  bytes = *p_bytes;
+  if (bytes <= 0 || align <= 0)
+    abort ();
+
+  if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
+    {
+      mode = SImode;
+      size = UNITS_PER_WORD;
+      load_func = gen_movsi;
+      store_func = gen_movsi;
+    }
+
+  else if (bytes >= UNITS_PER_WORD)
+    {
+      mode = SImode;
+      size = UNITS_PER_WORD;
+      load_func = gen_movsi_ulw;
+      store_func = gen_movsi_usw;
+    }
+
+  else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
+    {
+      mode = HImode;
+      size = UNITS_PER_SHORT;
+      load_func = gen_movhi;
+      store_func = gen_movhi;
+    }
+
+  else
+    {
+      mode = QImode;
+      size = 1;
+      load_func = gen_movqi;
+      store_func = gen_movqi;
+    }
+
+  offset = *p_offset;
+  *p_offset = offset + size;
+  *p_bytes = bytes - size;
+
+  if (offset == 0)
+    {
+      src_addr  = src_reg;
+      dest_addr = dest_reg;
+    }
+  else
+    {
+      src_addr  = gen_rtx (PLUS, Pmode, src_reg,  gen_rtx (CONST_INT, VOIDmode, offset));
+      dest_addr = gen_rtx (PLUS, Pmode, dest_reg, gen_rtx (CONST_INT, VOIDmode, offset));
+    }
+
+  reg = gen_reg_rtx (mode);
+  emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
+  return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);
+}
+
+\f
+/* Write a series of loads/stores to move some bytes.  Generate load/stores as follows:
+
+   load  1
+   load  2
+   load  3
+   store 1
+   load  4
+   store 2
+   load  5
+   store 3
+   ...
+
+   This way, no NOP's are needed, except at the end, and only
+   two temp registers are needed.  Two delay slots are used
+   in deference to the R4000.  */
+
+static void
+block_move_sequence (dest_reg, src_reg, bytes, align)
+     rtx dest_reg;             /* register holding destination address */
+     rtx src_reg;              /* register holding source address */
+     int bytes;                        /* # bytes to move */
+     int align;                        /* max alignment to assume */
+{
+  int offset           = 0;
+  rtx prev2_store      = (rtx)0;
+  rtx prev_store       = (rtx)0;
+  rtx cur_store                = (rtx)0;
+
+  while (bytes > 0)
+    {
+      /* Is there a store to do? */
+      if (prev2_store)
+       emit_insn (prev2_store);
+
+      prev2_store = prev_store;
+      prev_store = cur_store;
+      cur_store = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align);
+    }
+
+  /* Finish up last three stores.  */
+  if (prev2_store)
+    emit_insn (prev2_store);
+
+  if (prev_store)
+    emit_insn (prev_store);
+
+  if (cur_store)
+    emit_insn (cur_store);
+}
+
+\f
+/* Write a loop to move a constant number of bytes.  Generate load/stores as follows:
+
+   do {
+     temp1 = src[0];
+     temp2 = src[1];
+     ...
+     temp<last> = src[MAX_MOVE_REGS-1];
+     src += MAX_MOVE_REGS;
+     dest[0] = temp1;
+     dest[1] = temp2;
+     ...
+     dest[MAX_MOVE_REGS-1] = temp<last>;
+     dest += MAX_MOVE_REGS;
+   } while (src != final);
+
+   This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp
+   registers are needed.
+
+   Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3
+   cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every
+   (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses.  */
+
+#define MAX_MOVE_REGS 4
+#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
+
+static void
+block_move_loop (dest_reg, src_reg, bytes, align)
+     rtx dest_reg;             /* register holding destination address */
+     rtx src_reg;              /* register holding source address */
+     int bytes;                        /* # bytes to move */
+     int align;                        /* alignment */
+{
+  rtx stores[MAX_MOVE_REGS];
+  rtx label;
+  rtx final_src;
+  rtx bytes_rtx;
+  int i;
+  int leftover;
+  int offset;
+
+  if (bytes < 2*MAX_MOVE_BYTES)
+    abort ();
+
+  leftover = bytes % MAX_MOVE_BYTES;
+  bytes -= leftover;
+
+  label = gen_label_rtx ();
+  final_src = gen_reg_rtx (Pmode);
+  bytes_rtx = gen_rtx (CONST_INT, VOIDmode, bytes);
+
+  if (bytes > 0x7fff)
+    {
+      emit_insn (gen_movsi (final_src, bytes_rtx));
+      emit_insn (gen_addsi3 (final_src, final_src, src_reg));
+    }
+  else
+    emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
+
+  emit_label (label);
+
+  offset = 0;
+  for (i = 0; i < MAX_MOVE_REGS; i++)
+    stores[i] = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align);
+
+  emit_insn (gen_addsi3 (src_reg, src_reg, gen_rtx (CONST_INT, VOIDmode, MAX_MOVE_BYTES)));
+  for (i = 0; i < MAX_MOVE_REGS; i++)
+    emit_insn (stores[i]);
+
+  emit_insn (gen_addsi3 (dest_reg, dest_reg, gen_rtx (CONST_INT, VOIDmode, MAX_MOVE_BYTES)));
+  emit_insn (gen_cmpsi (src_reg, final_src));
+  emit_jump_insn (gen_bne (label));
+
+  if (leftover)
+    block_move_sequence (dest_reg, src_reg, leftover, align);
+}
+
+\f
+/* Use a library function to move some bytes.  */
+
+static void
+block_move_call (dest_reg, src_reg, bytes_rtx)
+     rtx dest_reg;
+     rtx src_reg;
+     rtx bytes_rtx;
+{
+#ifdef TARGET_MEM_FUNCTIONS
+  emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
+                    VOIDmode, 3,
+                    dest_reg, Pmode,
+                    src_reg, Pmode,
+                    bytes_rtx, SImode);
+#else
+  emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0,
+                    VOIDmode, 3,
+                    src_reg, Pmode,
+                    dest_reg, Pmode,
+                    bytes_rtx, SImode);
+#endif
+}
+
+\f
+/* Expand string/block move operations.
+
+   operands[0] is the pointer to the destination.
+   operands[1] is the pointer to the source.
+   operands[2] is the number of bytes to move.
+   operands[3] is the alignment.  */
+
+void
+expand_block_move (operands)
+     rtx operands[];
+{
+  rtx bytes_rtx        = operands[2];
+  int constp   = (GET_CODE (bytes_rtx) == CONST_INT);
+  int bytes    = (constp ? INTVAL (bytes_rtx) : 0);
+  int align    = INTVAL (operands[3]);
+  rtx src_reg;
+  rtx dest_reg;
+
+  if (constp && bytes <= 0)
+    return;
+
+  /* Move the address into scratch registers.  */
+  dest_reg = copy_to_reg (XEXP (operands[0], 0));
+  src_reg  = copy_to_reg (XEXP (operands[1], 0));
+
+  if (TARGET_MEMCPY)
+    block_move_call (dest_reg, src_reg, bytes_rtx);
+
+  else if (constp && bytes <= 2*MAX_MOVE_BYTES)
+    block_move_sequence (dest_reg, src_reg, bytes, align);
+
+  else if (constp && align >= UNITS_PER_WORD && optimize)
+    block_move_loop (dest_reg, src_reg, bytes, align);
+
+  else if (constp && optimize)
+    {
+      /* If the alignment is not word aligned, generate a test at
+        runtime, to see whether things wound up aligned, and we
+        can use the faster lw/sw instead ulw/usw.  */
+
+      rtx temp         = gen_reg_rtx (Pmode);
+      rtx aligned_label = gen_label_rtx ();
+      rtx join_label   = gen_label_rtx ();
+      int leftover     = bytes % MAX_MOVE_BYTES;
+
+      bytes -= leftover;
+
+      emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
+      emit_insn (gen_andsi3 (temp, temp, gen_rtx (CONST_INT, VOIDmode, UNITS_PER_WORD-1)));
+      emit_insn (gen_cmpsi (temp, const0_rtx));
+      emit_jump_insn (gen_beq (aligned_label));
+
+      /* Unaligned loop.  */
+      block_move_loop (dest_reg, src_reg, bytes, 1);
+      emit_jump_insn (gen_jump (join_label));
+      emit_barrier ();
+
+      /* Aligned loop.  */
+      emit_label (aligned_label);
+      block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD);
+      emit_label (join_label);
+
+      /* Bytes at the end of the loop.  */
+      if (leftover)
+       block_move_sequence (dest_reg, src_reg, leftover, align);
+    }
+
+  else
+    block_move_call (dest_reg, src_reg, bytes_rtx);
+}
+
+\f
+/* Argument support functions.  */
+
+/* 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;              /* SYMBOL_REF of library name or 0 */
+{
+  tree param, next_param;
+
+  if (TARGET_DEBUG_E_MODE)
+    fprintf (stderr, "\ninit_cumulative_args\n");
+
+  cum->gp_reg_found = 0;
+  cum->arg_number = 0;
+  cum->arg_words = 0;
+
+  /* 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 MIPS calling sequence
+     passes all arguments in the general purpose registers in this
+     case. */
+
+  for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
+       param != (tree)0;
+       param = next_param)
+    {
+      next_param = TREE_CHAIN (param);
+      if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node)
+       cum->gp_reg_found = 1;
+    }
+
+  /* Determine if the function is returning a structure, if so,
+     advance by one argument.  */
+
+  if (fntype
+      && (TREE_CODE (fntype) == FUNCTION_TYPE || TREE_CODE (fntype) == METHOD_TYPE)
+      && TREE_TYPE (fntype) != 0)
+    {
+      tree ret_type = TREE_TYPE (fntype);
+      enum tree_code ret_code = TREE_CODE (ret_type);
+
+      if (ret_code == RECORD_TYPE || ret_code == UNION_TYPE)
+       {
+         cum->gp_reg_found = 1;
+         cum->arg_number = 1;
+         cum->arg_words = 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 */
+{
+  if (TARGET_DEBUG_E_MODE)
+    fprintf (stderr,
+            "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n",
+            cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
+            type, named);
+
+  cum->arg_number++;
+  switch (mode)
+    {
+    default:
+      error ("Illegal mode given to function_arg_advance");
+      break;
+
+    case VOIDmode:
+      break;
+
+    case BLKmode:
+      cum->gp_reg_found = 1;
+      cum->arg_words += (int_size_in_bytes (type) + 3) / 4;
+      break;
+
+    case SFmode:
+      cum->arg_words++;
+      break;
+
+    case DFmode:
+      cum->arg_words += 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 a RTL expression containing the register for the given mode,
+   or 0 if the argument is too 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 */
+{
+  int regbase = -1;
+  int bias = 0;
+
+  if (TARGET_DEBUG_E_MODE)
+    fprintf (stderr,
+            "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d ) = ",
+            cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
+            type, named);
+
+  switch (mode)
+    {
+    default:
+      error ("Illegal mode given to function_arg");
+      break;
+
+    case SFmode:
+      if (cum->gp_reg_found || cum->arg_number >= 2)
+       regbase = GP_ARG_FIRST;
+      else {
+       regbase = (TARGET_SOFT_FLOAT) ? GP_ARG_FIRST : FP_ARG_FIRST;
+       if (cum->arg_words == 1)        /* first arg was float */
+         bias = 1;                     /* use correct reg */
+      }
+
+      break;
+
+    case DFmode:
+      cum->arg_words += (cum->arg_words & 1);
+      regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT)
+                       ? GP_ARG_FIRST
+                       : FP_ARG_FIRST;
+      break;
+
+    case VOIDmode:
+    case BLKmode:
+    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 (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
+    {
+      if (TARGET_DEBUG_E_MODE)
+       fprintf (stderr, "<stack>\n");
+
+      return 0;
+    }
+
+  if (regbase == -1)
+    abort ();
+
+  if (TARGET_DEBUG_E_MODE)
+    fprintf (stderr, "%s\n", reg_names[regbase + cum->arg_words + bias]);
+
+  return gen_rtx (REG, mode, regbase + cum->arg_words + bias);
+}
+
+
+int
+function_arg_partial_nregs (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 */
+{
+  if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS)
+    {
+      int words = (int_size_in_bytes (type) + 3) / 4;
+
+      if (words + cum->arg_words < MAX_ARGS_IN_REGISTERS)
+       return 0;               /* structure fits in registers */
+
+      if (TARGET_DEBUG_E_MODE)
+       fprintf (stderr, "function_arg_partial_nregs = %d\n",
+                MAX_ARGS_IN_REGISTERS - cum->arg_words);
+
+      return MAX_ARGS_IN_REGISTERS - cum->arg_words;
+    }
+
+  else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1)
+    {
+      if (TARGET_DEBUG_E_MODE)
+       fprintf (stderr, "function_arg_partial_nregs = 1\n");
+
+      return 1;
+    }
+
+  return 0;
+}
+
+\f
+/* Print the options used in the assembly file.  */
+
+static struct {char *name; int value;} target_switches []
+  = TARGET_SWITCHES;
+
+void
+print_options (out)
+     FILE *out;
+{
+  int line_len;
+  int len;
+  int j;
+  char **p;
+  int mask = TARGET_DEFAULT;
+
+  /* Allow assembly language comparisons with -mdebug eliminating the
+     compiler version number and switch lists.  */
+
+  if (TARGET_DEBUG_MODE)
+    return;
+
+  fprintf (out, "\n # %s %s", language_string, version_string);
+#ifdef TARGET_VERSION_INTERNAL
+  TARGET_VERSION_INTERNAL (out);
+#endif
+#ifdef __GNUC__
+  fprintf (out, " compiled by GNU C\n\n");
+#else
+  fprintf (out, " compiled by CC\n\n");
+#endif
+
+  fprintf (out, " # Cc1 defaults:");
+  line_len = 32767;
+  for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
+    {
+      if (target_switches[j].name[0] != '\0'
+         && target_switches[j].value > 0
+         && (target_switches[j].value & mask) == target_switches[j].value)
+       {
+         mask &= ~ target_switches[j].value;
+         len = strlen (target_switches[j].name) + 1;
+         if (len + line_len > 79)
+           {
+             line_len = 2;
+             fputs ("\n #", out);
+           }
+         fprintf (out, " -m%s", target_switches[j].name);
+         line_len += len;
+       }
+    }
+
+  fprintf (out, "\n\n # Cc1 arguments (-G value = %d, Cpu = %s, ISA = %d):",
+          mips_section_threshold, mips_cpu_string, mips_isa);
+
+  line_len = 32767;
+  for (p = &save_argv[1]; *p != (char *)0; p++)
+    {
+      char *arg = *p;
+      if (*arg == '-')
+       {
+         len = strlen (arg) + 1;
+         if (len + line_len > 79)
+           {
+             line_len = 2;
+             fputs ("\n #", out);
+           }
+         fprintf (out, " %s", *p);
+         line_len += len;
+       }
+    }
+
+  fputs ("\n\n", out);
+}
+
+\f
+/* Abort after printing out a specific insn.  */
+
+void
+abort_with_insn (insn, reason)
+     rtx insn;
+     char *reason;
+{
+  error (reason);
+  debug_rtx (insn);
+  abort ();
+}
+
+/* Write a message to stderr (for use in macros expanded in files that do not
+   include stdio.h).  */
+
+void
+trace (s, s1, s2)
+     char *s, *s1, *s2;
+{
+  fprintf (stderr, s, s1, s2);
+}
+
+\f
+#ifdef SIGINFO
+
+#include <sys/wait.h>
+
+static void
+siginfo (signo)
+     int signo;
+{
+  char print_pid[50];
+  char *argv[4];
+  pid_t pid;
+  int status;
+
+  fprintf (stderr, "compiling '%s' in '%s'\n",
+          (current_function_name != (char *)0) ? current_function_name : "<toplevel>",
+          (current_function_file != (char *)0) ? current_function_file : "<no file>");
+
+  /* Spawn a ps to tell about current memory usage, etc. */
+  sprintf (print_pid, "-p%d,%d", getpid (), getppid ());
+  argv[0] = "ps";
+  argv[1] = print_pid;
+  argv[2] = "-ouser,state,pid,pri,nice,wchan,tt,start,usertime,systime,pcpu,cp,inblock,oublock,vsize,rss,pmem,ucomm";
+  argv[3] = (char *)0;
+
+  pid = vfork ();
+  if (pid == 0)                        /* child context */
+    {
+      execv ("/usr/bin/ps", argv);
+      execv ("/usr/sbin/ps", argv);
+      execvp ("ps", argv);
+      perror ("ps");
+      _exit (1);
+    }
+
+  else if (pid > 0)            /* parent context */
+    {
+      void (*sigint)(int)  = signal (SIGINT, SIG_IGN);
+      void (*sigquit)(int) = signal (SIGQUIT, SIG_IGN);
+
+      (void) waitpid (pid, &status, 0);
+
+      (void) signal (SIGINT,  sigint);
+      (void) signal (SIGQUIT, sigquit);
+    }
+
+  signal (SIGINFO, siginfo);
+}
+
+#endif /* SIGINFO */
+
+\f
+/* Set up the threshold for data to go into the small data area, instead
+   of the normal data area, and detect any conflicts in the switches.  */
+
+void
+override_options ()
+{
+  register int i, start;
+  register int regno;
+  register enum machine_mode mode;
+
+  if (g_switch_set)
+    mips_section_threshold = g_switch_value;
+
+  else
+    mips_section_threshold = (TARGET_MIPS_AS) ? 8 : 0;
+
+  /* Identify the processor type */
+  if (mips_cpu_string == (char *)0
+      || !strcmp (mips_cpu_string, "default")
+      || !strcmp (mips_cpu_string, "DEFAULT"))
+    {
+      mips_cpu_string = "default";
+      mips_cpu = PROCESSOR_DEFAULT;
+    }
+
+  else
+    {
+      char *p = mips_cpu_string;
+
+      if (*p == 'r' || *p == 'R')
+       p++;
+
+      /* Since there is no difference between a R2000 and R3000 in
+        terms of the scheduler, we collapse them into just an R3000. */
+
+      mips_cpu = PROCESSOR_DEFAULT;
+      switch (*p)
+       {
+       case '2':
+         if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K"))
+           mips_cpu = PROCESSOR_R3000;
+         break;
+
+       case '3':
+         if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K"))
+           mips_cpu = PROCESSOR_R3000;
+         break;
+
+       case '4':
+         if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
+           mips_cpu = PROCESSOR_R4000;
+         break;
+
+       case '6':
+         if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K"))
+           mips_cpu = PROCESSOR_R6000;
+         break;
+       }
+
+      if (mips_cpu == PROCESSOR_DEFAULT)
+       {
+         error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
+         mips_cpu_string = "default";
+       }
+    }
+
+  /* Now get the architectural level.  */
+  if (mips_isa_string == (char *)0)
+    mips_isa = 1;
+
+  else if (isdigit (*mips_isa_string))
+    mips_isa = atoi (mips_isa_string);
+
+  else
+    {
+      error ("bad value (%s) for -mips switch", mips_isa_string);
+      mips_isa = 1;
+    }
+
+  if (mips_isa < 0 || mips_isa > 3)
+    error ("-mips%d not supported", mips_isa);
+
+  else if (mips_isa > 1
+          && (mips_cpu == PROCESSOR_DEFAULT || mips_cpu == PROCESSOR_R3000))
+    error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
+
+  else if (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)
+    error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
+
+  /* make sure sizes of ints/longs/etc. are ok */
+  if (mips_isa < 3)
+    {
+      if (TARGET_INT64)
+       fatal ("Only the r4000 can support 64 bit ints");
+
+      else if (TARGET_LONG64)
+       fatal ("Only the r4000 can support 64 bit longs");
+
+      else if (TARGET_LLONG128)
+       fatal ("Only the r4000 can support 128 bit long longs");
+
+      else if (TARGET_FLOAT64)
+       fatal ("Only the r4000 can support 64 bit fp registers");
+    }
+  else if (TARGET_INT64 || TARGET_LONG64 || TARGET_LLONG128 || TARGET_FLOAT64)
+    warning ("r4000 64/128 bit types not yet supported");
+
+  /* Tell halfpic.c that we have half-pic code if we do.  */
+  if (TARGET_HALF_PIC)
+    flag_half_pic = TRUE;
+
+  /* -mrnames says to use the MIPS software convention for register
+     names instead of the hardware names (ie, a0 instead of $4).
+     We do this by switching the names in mips_reg_names, which the
+     reg_names points into via the REGISTER_NAMES macro.  */
+
+  if (TARGET_NAME_REGS)
+    {
+      if (TARGET_GAS)
+       {
+         target_flags &= ~ MASK_NAME_REGS;
+         error ("Gas does not support the MIPS software register name convention.");
+       }
+      else
+       bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names));
+    }
+
+  /* If this is OSF/1, set up a SIGINFO handler so we can see what function
+     is currently being compiled.  */
+#ifdef SIGINFO
+  if (getenv ("GCC_SIGINFO") != (char *)0)
+    {
+      struct sigaction action;
+      action.sa_handler = siginfo;
+      action.sa_mask = 0;
+      action.sa_flags = SA_RESTART;
+      sigaction (SIGINFO, &action, (struct sigaction *)0);
+    }
+#endif
+
+  /* Set up the classificaiton arrays now.  */
+  mips_rtx_classify[(int)PLUS]  = CLASS_ADD_OP;
+  mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP;
+  mips_rtx_classify[(int)DIV]   = CLASS_DIVMOD_OP;
+  mips_rtx_classify[(int)MOD]   = CLASS_DIVMOD_OP;
+  mips_rtx_classify[(int)UDIV]  = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
+  mips_rtx_classify[(int)UMOD]  = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
+  mips_rtx_classify[(int)EQ]    = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
+  mips_rtx_classify[(int)NE]    = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
+  mips_rtx_classify[(int)GT]    = CLASS_CMP_OP | CLASS_FCMP_OP;
+  mips_rtx_classify[(int)GE]    = CLASS_CMP_OP | CLASS_FCMP_OP;
+  mips_rtx_classify[(int)LT]    = CLASS_CMP_OP | CLASS_FCMP_OP;
+  mips_rtx_classify[(int)LE]    = CLASS_CMP_OP | CLASS_FCMP_OP;
+  mips_rtx_classify[(int)GTU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
+  mips_rtx_classify[(int)GEU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
+  mips_rtx_classify[(int)LTU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
+  mips_rtx_classify[(int)LEU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
+
+  mips_print_operand_punct['?'] = TRUE;
+  mips_print_operand_punct['#'] = TRUE;
+  mips_print_operand_punct['&'] = TRUE;
+  mips_print_operand_punct['!'] = TRUE;
+  mips_print_operand_punct['*'] = TRUE;
+  mips_print_operand_punct['@'] = TRUE;
+  mips_print_operand_punct['.'] = TRUE;
+  mips_print_operand_punct['('] = TRUE;
+  mips_print_operand_punct[')'] = TRUE;
+  mips_print_operand_punct['['] = TRUE;
+  mips_print_operand_punct[']'] = TRUE;
+  mips_print_operand_punct['<'] = TRUE;
+  mips_print_operand_punct['>'] = TRUE;
+  mips_print_operand_punct['{'] = TRUE;
+  mips_print_operand_punct['}'] = TRUE;
+
+  mips_char_to_class['d'] = GR_REGS;
+  mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
+  mips_char_to_class['h'] = HI_REG;
+  mips_char_to_class['l'] = LO_REG;
+  mips_char_to_class['s'] = ST_REGS;
+  mips_char_to_class['x'] = MD_REGS;
+  mips_char_to_class['y'] = GR_REGS;
+
+  /* Set up array to map GCC register number to debug register number.
+     Ignore the special purpose register numbers.  */
+
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    mips_dbx_regno[i] = -1;
+
+  start = GP_DBX_FIRST - GP_REG_FIRST;
+  for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
+    mips_dbx_regno[i] = i + start;
+
+  start = FP_DBX_FIRST - FP_REG_FIRST;
+  for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+    mips_dbx_regno[i] = i + start;
+
+  /* Set up array giving whether a given register can hold a given mode.
+     At present, restrict ints from being in FP registers, because reload
+     is a little enthusiastic about storing extra values in FP registers,
+     and this is not good for things like OS kernels.  Also, due to the
+     manditory delay, it is as fast to load from cached memory as to move
+     from the FP register.  */
+
+  for (mode = VOIDmode;
+       mode != MAX_MACHINE_MODE;
+       mode = (enum machine_mode)((int)mode + 1))
+    {
+      register int size                     = GET_MODE_SIZE (mode);
+      register enum mode_class class = GET_MODE_CLASS (mode);
+
+      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+       {
+         register int temp = FALSE;
+
+         if (GP_REG_P (regno))
+           temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
+
+         else if (FP_REG_P (regno))
+           temp = ((TARGET_FLOAT64 || ((regno & 1) == 0))
+                   && (TARGET_DEBUG_H_MODE
+                       || class == MODE_FLOAT
+                       || class == MODE_COMPLEX_FLOAT));
+
+         else if (MD_REG_P (regno))
+           temp = (mode == SImode || (regno == MD_REG_FIRST && mode == DImode));
+
+         else if (ST_REG_P (regno))
+           temp = ((mode == SImode) || (class == MODE_CC));
+
+         mips_hard_regno_mode_ok[(int)mode][regno] = temp;
+       }
+    }
+}
+
+\f
+/*
+ * If the frame pointer has been eliminated, the offset for an auto
+ * or argument will be based on the stack pointer.  But this is not
+ * what the debugger expects--it needs to find an offset off of the
+ * frame pointer (whether it exists or not).  So here we turn all
+ * offsets into those based on the (possibly virtual) frame pointer.
+ */
+
+int
+mips_debugger_offset (addr, offset)
+     rtx addr;
+     int offset;
+{
+  int offset2 = 0;
+  rtx reg = eliminate_constant_term (addr, &offset2);
+
+  if (!offset)
+    offset = offset2;
+
+  if (reg == stack_pointer_rtx)
+    offset = offset - ((!current_frame_info.initialized)
+                      ? compute_frame_size (get_frame_size ())
+                      : current_frame_info.total_size);
+
+  /* Any other register is, we hope, either the frame pointer,
+     or a pseudo equivalent to the frame pointer.  (Assign_parms
+     copies the arg pointer to a pseudo if ARG_POINTER_REGNUM is
+     equal to FRAME_POINTER_REGNUM, so references off of the
+     arg pointer are all off a pseudo.)  Seems like all we can
+     do is to just return OFFSET and hope for the best.  */
+
+  return offset;
+}
+\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 MIPS specific codes are:
+
+   'X'  X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
+   'x'  X is CONST_INT, prints 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 register of double-word register operand.
+   'L'  print low-order register of double-word register operand.
+   'M'  print high-order register of double-word register operand.
+   'C'  print part of opcode for a branch condition.
+   'N'  print part of opcode for a branch condition, inverted.
+   '(' Turn on .set noreorder
+   ')' Turn on .set reorder
+   '[' Turn on .set noat
+   ']' Turn on .set at
+   '<' Turn on .set nomacro
+   '>' Turn on .set macro
+   '{' Turn on .set volatile (not GAS)
+   '}' Turn on .set novolatile (not GAS)
+   '&' Turn on .set noreorder if filling delay slots
+   '*' Turn on both .set noreorder and .set nomacro if filling delay slots
+   '!' Turn on .set nomacro if filling delay slots
+   '#' Print nop if in a .set noreorder section.
+   '?' 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).  */
+
+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)
+       {
+       default:
+         error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
+         break;
+
+       case '?':
+         if (mips_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 '&':
+         if (final_sequence != 0 && set_noreorder++ == 0)
+           fputs (".set\tnoreorder\n\t", file);
+         break;
+
+       case '*':
+         if (final_sequence != 0)
+           {
+             if (set_noreorder++ == 0)
+               fputs (".set\tnoreorder\n\t", file);
+
+             if (set_nomacro++ == 0)
+               fputs (".set\tnomacro\n\t", file);
+           }
+         break;
+
+       case '!':
+         if (final_sequence != 0 && set_nomacro++ == 0)
+           fputs ("\n\t.set\tnomacro", file);
+         break;
+
+       case '#':
+         if (set_noreorder != 0)
+           fputs ("\n\tnop", file);
+
+         else if (TARGET_GAS || TARGET_STATS)
+           fputs ("\n\t#nop", file);
+
+         break;
+
+       case '(':
+         if (set_noreorder++ == 0)
+           fputs (".set\tnoreorder\n\t", file);
+         break;
+
+       case ')':
+         if (set_noreorder == 0)
+           error ("internal error: %%) found without a %%( in assembler pattern");
+
+         else if (--set_noreorder == 0)
+           fputs ("\n\t.set\treorder", file);
+
+         break;
+
+       case '[':
+         if (set_noat++ == 0)
+           fputs (".set\tnoat\n\t", file);
+         break;
+
+       case ']': 
+         if (set_noat == 0)
+           error ("internal error: %%] found without a %%[ in assembler pattern");
+
+         else if (--set_noat == 0)
+           fputs ("\n\t.set\tat", file);
+
+         break;
+
+       case '<':
+         if (set_nomacro++ == 0)
+           fputs (".set\tnomacro\n\t", file);
+         break;
+
+       case '>':
+         if (set_nomacro == 0)
+           error ("internal error: %%> found without a %%< in assembler pattern");
+
+         else if (--set_nomacro == 0)
+           fputs ("\n\t.set\tmacro", file);
+
+         break;
+
+       case '{':
+         if (set_volatile++ == 0)
+           fprintf (file, "%s.set\tvolatile\n\t", (TARGET_MIPS_AS) ? "" : "#");
+         break;
+
+       case '}':
+         if (set_volatile == 0)
+           error ("internal error: %%} found without a %%{ in assembler pattern");
+
+         else if (--set_volatile == 0)
+           fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#");
+
+         break;
+       }
+      return;
+    }
+
+  if (! op)
+    {
+      error ("PRINT_OPERAND null pointer");
+      return;
+    }
+
+  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 ("gtu", file); break;
+      case GEU: fputs ("geu", file); break;
+      case LTU: fputs ("ltu", file); break;
+      case LEU: fputs ("leu", file); break;
+
+      default:
+       abort_with_insn (op, "PRINT_OPERAND, illegal 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, illegal insn for %%N");
+      }
+
+  else if (code == REG)
+    {
+      register int regnum = REGNO (op);
+
+      if (letter == 'M')
+       regnum += MOST_SIGNIFICANT_WORD;
+
+      else if (letter == 'L')
+       regnum += LEAST_SIGNIFICANT_WORD;
+
+      else if (letter == 'D')
+       regnum++;
+
+      fprintf (file, "%s", reg_names[regnum]);
+    }
+
+  else if (code == MEM)
+    output_address (XEXP (op, 0));
+
+  else if (code == CONST_DOUBLE)
+    {
+#if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
+      union { double d; int i[2]; } u;
+      u.i[0] = CONST_DOUBLE_LOW (op);
+      u.i[1] = CONST_DOUBLE_HIGH (op);
+      if (GET_MODE (op) == SFmode)
+       {
+         float f;
+         f = u.d;
+         u.d = f;
+       }
+      fprintf (file, "%.20e", u.d);
+#else
+      fatal ("CONST_DOUBLE found in cross compilation");
+#endif
+    }
+
+  else if ((letter == 'x') && (GET_CODE(op) == CONST_INT))
+    fprintf (file, "0x%04x", 0xffff & (INTVAL(op)));
+
+  else if ((letter == 'X') && (GET_CODE(op) == CONST_INT))
+    fprintf (file, "0x%08x", INTVAL(op));
+
+  else if ((letter == 'd') && (GET_CODE(op) == CONST_INT))
+    fprintf (file, "%d", (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')
+    fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", letter);
+
+  else
+    output_addr_const (file, op);
+}
+
+\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.
+
+   On some machines, the syntax for a symbolic address depends on
+   the section that the address refers to.  On these machines,
+   define the macro `ENCODE_SECTION_INFO' to store the information
+   into the `symbol_ref', and then check for it here.  */
+
+void
+print_operand_address (file, addr)
+     FILE *file;
+     rtx addr;
+{
+  if (!addr)
+    error ("PRINT_OPERAND_ADDRESS, null pointer");
+
+  else
+    switch (GET_CODE (addr))
+      {
+      default:
+       abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #1");
+       break;
+
+      case REG:
+       fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
+       break;
+
+      case PLUS:
+       {
+         register rtx reg    = (rtx)0;
+         register rtx offset = (rtx)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, illegal insn #2");
+
+         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);
+       break;
+    }
+}
+
+\f
+/* If optimizing for the global pointer, keep track of all of
+   the externs, so that at the end of the file, we can emit
+   the appropriate .extern declaration for them, before writing
+   out the text section.  We assume that all names passed to
+   us are in the permanent obstack, so that they will be valid
+   at the end of the compilation.
+
+   If we have -G 0, or the extern size is unknown, don't bother
+   emitting the .externs.  */
+
+int
+mips_output_external (file, decl, name)
+     FILE *file;
+     tree decl;
+     char *name;
+{
+  register struct extern_list *p;
+  int len;
+
+  if (TARGET_GP_OPT
+      && mips_section_threshold != 0
+      && ((TREE_CODE (decl)) != FUNCTION_DECL)
+      && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0))
+    {
+      p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list));
+      p->next = extern_head;
+      p->name = name;
+      p->size = len;
+      extern_head = p;
+    }
+  return 0;
+}
+
+\f
+/* Compute a string to use as a temporary file name.  */
+
+static FILE *
+make_temp_file ()
+{
+  char *temp_filename;
+  FILE *stream;
+  char *base = getenv ("TMPDIR");
+  int len;
+
+  if (base == (char *)0)
+    {
+#ifdef P_tmpdir
+      if (access (P_tmpdir, R_OK | W_OK) == 0)
+       base = P_tmpdir;
+      else
+#endif
+       if (access ("/usr/tmp", R_OK | W_OK) == 0)
+         base = "/usr/tmp/";
+       else
+         base = "/tmp/";
+    }
+
+  len = strlen (base);
+  temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX"));
+  strcpy (temp_filename, base);
+  if (len > 0 && temp_filename[len-1] != '/')
+    temp_filename[len++] = '/';
+
+  strcpy (temp_filename + len, "ccXXXXXX");
+  mktemp (temp_filename);
+
+  stream = fopen (temp_filename, "w+");
+  if (!stream)
+    pfatal_with_name (temp_filename);
+
+  unlink (temp_filename);
+  return stream;
+}
+
+\f
+/* Emit a new filename to a stream.  If this is MIPS ECOFF, watch out
+   for .file's that start within a function.  If we are smuggling stabs, try to
+   put out a MIPS ECOFF file and a stab.  */
+
+void
+mips_output_filename (stream, name)
+     FILE *stream;
+     char *name;
+{
+  static int first_time = TRUE;
+  char ltext_label_name[100];
+
+  if (first_time)
+    {
+      first_time = FALSE;
+      SET_FILE_NUMBER ();
+      current_function_file = name;
+      fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name);
+    }
+
+  else if (!TARGET_GAS && write_symbols == DBX_DEBUG)
+    {
+      ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
+      fprintf (stream, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP,
+              name, N_SOL, &ltext_label_name[1]);
+    }
+
+  else if (name != current_function_file
+      && strcmp (name, current_function_file) != 0)
+    {
+      if (inside_function && !TARGET_GAS)
+       {
+         if (!file_in_function_warning)
+           {
+             file_in_function_warning = TRUE;
+             ignore_line_number = TRUE;
+             warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
+           }
+
+         fprintf (stream, "\t#.file\t%d \"%s\"\n", num_source_filenames, name);
+       }
+
+      else
+       {
+         SET_FILE_NUMBER ();
+         current_function_file = name;
+         fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name);
+       }
+    }
+}
+
+\f
+/* Emit a linenumber.  For encapsulated stabs, we need to put out a stab
+   as well as a .loc, since it is possible that MIPS ECOFF might not be
+   able to represent the location for inlines that come from a different
+   file.  */
+
+void
+mips_output_lineno (stream, line)
+     FILE *stream;
+     int line;
+{
+  if (!TARGET_GAS && write_symbols == DBX_DEBUG)
+    {
+      ++sym_lineno;
+      fprintf (stream, "$LM%d:\n\t%s %d,0,%d,$LM%d\n",
+              sym_lineno, ASM_STABN_OP, N_SLINE, line, sym_lineno);
+    }
+
+  else
+    {
+      fprintf (stream, "\n\t%s.loc\t%d %d\n",
+              (ignore_line_number) ? "#" : "",
+              num_source_filenames, line);
+  
+      LABEL_AFTER_LOC (stream);
+    }
+}
+
+\f
+/* Output at beginning of assembler file.
+   If we are optimizing to use the global pointer, create a temporary
+   file to hold all of the text stuff, and write it out to the end.
+   This is needed because the MIPS assembler is evidently one pass,
+   and if it hasn't seen the relevant .comm/.lcomm/.extern/.sdata
+   declaration when the code is processed, it generates a two
+   instruction sequence.  */
+
+void
+mips_asm_file_start (stream)
+     FILE *stream;
+{
+  ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
+
+  /* Versions of the MIPS assembler before 2.20 generate errors
+     if a branch inside of a .set noreorder section jumps to a
+     label outside of the .set noreorder section.  Revision 2.20
+     just set nobopt silently rather than fixing the bug.  */
+
+  if (TARGET_MIPS_AS && optimize && flag_delayed_branch)
+    fprintf (stream, "\t.set\tnobopt\n");
+
+  /* Generate the pseudo ops that the Pyramid based System V.4 wants.  */
+  if (TARGET_ABICALLS)
+    fprintf (stream, "\t.abicalls\n");
+
+  /* put gcc_compiled. in data, not text */
+  data_section ();
+
+  if (TARGET_GP_OPT)
+    {
+      asm_out_data_file = stream;
+      asm_out_text_file = make_temp_file ();
+    }
+  else
+    asm_out_data_file = asm_out_text_file = stream;
+
+  if (TARGET_NAME_REGS)
+    fprintf (asm_out_file, "#include <regdef.h>\n");
+
+  print_options (stream);
+}
+
+\f
+/* If we are optimizing the global pointer, emit the text section now
+   and any small externs which did not have .comm, etc that are
+   needed.  Also, give a warning if the data area is more than 32K and
+   -pic because 3 instructions are needed to reference the data
+   pointers.  */
+
+void
+mips_asm_file_end (file)
+     FILE *file;
+{
+  char buffer[8192];
+  tree name_tree;
+  struct extern_list *p;
+  int len;
+
+  if (TARGET_GP_OPT)
+    {
+      if (extern_head)
+       fputs ("\n", file);
+
+      for (p = extern_head; p != 0; p = p->next)
+       {
+         name_tree = get_identifier (p->name);
+         if (!TREE_ADDRESSABLE (name_tree))
+           {
+             TREE_ADDRESSABLE (name_tree) = 1;
+             fputs ("\t.extern\t", file);
+             assemble_name (file, p->name);
+             fprintf (file, ", %d\n", p->size);
+           }
+       }
+
+      fprintf (file, "\n\t.text\n");
+      rewind (asm_out_text_file);
+      if (ferror (asm_out_text_file))
+       fatal_io_error ("write of text assembly file in mips_asm_file_end");
+
+      while ((len = fread (buffer, 1, sizeof (buffer), asm_out_text_file)) > 0)
+       if (fwrite (buffer, 1, len, file) != len)
+         pfatal_with_name ("write of final assembly file in mips_asm_file_end");
+
+      if (len < 0)
+       pfatal_with_name ("read of text assembly file in mips_asm_file_end");
+
+      if (fclose (asm_out_text_file) != 0)
+       pfatal_with_name ("close of tempfile in mips_asm_file_end");
+    }
+}
+
+\f
+/* Return the bytes needed to compute the frame pointer from the current
+   stack pointer.
+
+   Mips 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.       |   FP->|  if not passed.      |
+       +-----------------------+       +-----------------------+
+                                       |                       |
+                                       |  GP save for V.4 abi  |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                       |  local variables      |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                        |  fp register save     |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                        |  gp register save     |
+                                        |                      |
+                                       +-----------------------+
+                                       |                       |
+                                        |  alloca allocations   |
+                                       |                       |
+                                       +-----------------------+
+                                       |                       |
+                                        |  arguments on stack   |
+                                       |                       |
+                                       +-----------------------+
+                                        |  4 words to save      |
+                                       |  arguments passed     |
+                                        |  in registers, even   |
+   low                              SP->|  if not passed.       |
+   memory                              +-----------------------+
+
+*/
+
+unsigned long
+compute_frame_size (size)
+     int size;                 /* # of var. bytes allocated */
+{
+  int regno;
+  unsigned long total_size;    /* # bytes that the entire frame takes up */
+  unsigned long var_size;      /* # bytes that variables take up */
+  unsigned long args_size;     /* # bytes that outgoing arguments take up */
+  unsigned long extra_size;    /* # extra bytes */
+  unsigned int  gp_reg_rounded;        /* # bytes needed to store gp after rounding */
+  unsigned int  gp_reg_size;   /* # bytes needed to store gp regs */
+  unsigned int  fp_reg_size;   /* # bytes needed to store fp regs */
+  unsigned long mask;          /* mask of saved gp registers */
+  unsigned long fmask;         /* mask of saved fp registers */
+  int fp_inc;                  /* 1 or 2 depending on the size of fp regs */
+  int fp_bits;                 /* bitmask to use for each fp register */
+
+  extra_size    = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0)
+                                    -STARTING_FRAME_OFFSET);
+
+  var_size      = MIPS_STACK_ALIGN (size);
+  args_size     = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
+  total_size    = var_size + args_size + extra_size;
+  gp_reg_size   = 0;
+  fp_reg_size   = 0;
+  mask          = 0;
+  fmask                 = 0;
+
+  /* Calculate space needed for gp registers.  */
+  for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+    {
+      if (MUST_SAVE_REGISTER (regno))
+       {
+         gp_reg_size += UNITS_PER_WORD;
+         mask |= 1 << (regno - GP_REG_FIRST);
+       }
+    }
+
+  /* Calculate space needed for fp registers.  */
+  if (TARGET_FLOAT64)
+    {
+      fp_inc = 1;
+      fp_bits = 1;
+    }
+  else
+    {
+      fp_inc = 2;
+      fp_bits = 3;
+    }
+
+  for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += fp_inc)
+    {
+      if (regs_ever_live[regno] && !call_used_regs[regno])
+       {
+         fp_reg_size += 2*UNITS_PER_WORD;
+         fmask |= fp_bits << (regno - FP_REG_FIRST);
+       }
+    }
+
+  gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
+  total_size += gp_reg_rounded + fp_reg_size;
+
+  if (total_size == extra_size)
+    total_size = extra_size = 0;
+
+  /* Save other computed information.  */
+  current_frame_info.total_size  = total_size;
+  current_frame_info.var_size    = var_size;
+  current_frame_info.args_size   = args_size;
+  current_frame_info.extra_size  = extra_size;
+  current_frame_info.gp_reg_size = gp_reg_size;
+  current_frame_info.fp_reg_size = fp_reg_size;
+  current_frame_info.mask       = mask;
+  current_frame_info.fmask      = fmask;
+  current_frame_info.initialized = reload_completed;
+
+  if (mask)
+    {
+      unsigned long offset = args_size + gp_reg_size - UNITS_PER_WORD;
+      current_frame_info.gp_sp_offset = offset;
+      current_frame_info.gp_save_offset = offset - total_size;
+    }
+
+  if (fmask)
+    {
+      unsigned long offset = args_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD;
+      current_frame_info.fp_sp_offset = offset;
+      current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
+    }
+
+  /* Ok, we're done.  */
+  return total_size;
+}
+
+\f
+/* Common code to save/restore registers.  */
+
+void
+save_restore (file, gp_op, gp_2word_op, fp_op)
+     FILE *file;               /* stream to write to */
+     char *gp_op;              /* operation to do on gp registers */
+     char *gp_2word_op;                /* 2 word op to do on gp registers */
+     char *fp_op;              /* operation to do on fp registers */
+{
+  int regno;
+  unsigned long mask     = current_frame_info.mask;
+  unsigned long fmask    = current_frame_info.fmask;
+  unsigned long gp_offset;
+  unsigned long fp_offset;
+  unsigned long max_offset;
+  char *base_reg;
+
+  if (mask == 0 && fmask == 0)
+    return;
+
+  base_reg   = reg_names[STACK_POINTER_REGNUM];
+  gp_offset  = current_frame_info.gp_sp_offset;
+  fp_offset  = current_frame_info.fp_sp_offset;
+  max_offset = (gp_offset > fp_offset) ? gp_offset : fp_offset;
+
+  /* Deal with calling functions with a large structure.  */
+  if (max_offset >= 32768)
+    {
+      char *temp = reg_names[MIPS_TEMP2_REGNUM];
+      fprintf (file, "\tli\t%s,%ld\n", temp, max_offset);
+      fprintf (file, "\taddu\t%s,%s,%s\n", temp, temp, base_reg);
+      base_reg = temp;
+      gp_offset = max_offset - gp_offset;
+      fp_offset = max_offset - fp_offset;
+    }
+
+  /* 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.  */
+
+  if (mask || frame_pointer_needed)
+    {
+      for  (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+       {
+         if ((mask & (1L << (regno - GP_REG_FIRST))) != 0
+             || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed))
+           {
+             fprintf (file, "\t%s\t%s,%d(%s)\n",
+                      gp_op, reg_names[regno],
+                      gp_offset, base_reg);
+
+             gp_offset -= UNITS_PER_WORD;
+           }
+       }
+    }
+
+  if (fmask)
+    {
+      int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
+
+      for  (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc)
+       {
+         if ((fmask & (1L << (regno - FP_REG_FIRST))) != 0)
+           {
+             fprintf (file, "\t%s\t%s,%d(%s)\n",
+                      fp_op, reg_names[regno], fp_offset, base_reg);
+
+             fp_offset -= 2*UNITS_PER_WORD;
+           }
+       }
+    }
+}
+
+\f
+/* Set up the stack and frame (if desired) for the function.  */
+
+void
+function_prologue (file, size)
+     FILE *file;
+     int size;
+{
+  int regno;
+  int tsize;
+  char *sp_str = reg_names[STACK_POINTER_REGNUM];
+  char *fp_str = (!frame_pointer_needed)
+                       ? sp_str
+                       : reg_names[FRAME_POINTER_REGNUM];
+  tree fndecl = current_function_decl; /* current... is tooo long */
+  tree fntype = TREE_TYPE (fndecl);
+  tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
+                       ? DECL_ARGUMENTS (fndecl)
+                       : 0;
+  tree next_arg;
+  tree cur_arg;
+  char *arg_name = (char *)0;
+  CUMULATIVE_ARGS args_so_far;
+
+  ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
+  ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+
+  inside_function = 1;
+  fputs ("\t.ent\t", file);
+  assemble_name (file, current_function_name);
+  fputs ("\n", file);
+  assemble_name (file, current_function_name);
+  fputs (":\n", file);
+
+  if (TARGET_ABICALLS)
+    fprintf (file,
+            "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
+            reg_names[ GP_REG_FIRST + 25 ]);
+
+  /* Determine the last argument, and get its name.  */
+  for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
+    {
+      next_arg = TREE_CHAIN (cur_arg);
+      if (next_arg == (tree)0)
+       {
+         if (DECL_NAME (cur_arg))
+           arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg));
+
+         break;
+       }
+    }
+
+  /* If this function is a varargs function, store any registers that
+     would normally hold arguments ($4 - $7) on the stack.  */
+  if ((TYPE_ARG_TYPES (fntype) != 0
+       && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
+      || (arg_name
+         && (strcmp (arg_name, "__builtin_va_alist") == 0
+             || strcmp (arg_name, "va_alist") == 0)))
+    {
+      tree parm;
+
+      regno = GP_ARG_FIRST;
+      INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
+
+      for (parm = fnargs; (parm && (regno <= GP_ARG_LAST)); parm = TREE_CHAIN (parm))
+       {
+         rtx entry_parm;
+         enum machine_mode passed_mode;
+         tree type;
+
+         type = DECL_ARG_TYPE (parm);
+         passed_mode = TYPE_MODE (type);
+         entry_parm = FUNCTION_ARG (args_so_far, passed_mode,
+                                    DECL_ARG_TYPE (parm), 1);
+
+         if (entry_parm)
+           {
+             int words;
+
+             /* passed in a register, so will get homed automatically */
+             if (GET_MODE (entry_parm) == BLKmode)
+               words = (int_size_in_bytes (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;
+           }
+
+         FUNCTION_ARG_ADVANCE (args_so_far, passed_mode,
+                               DECL_ARG_TYPE (parm), 1);
+       }
+
+      if (regno <= GP_ARG_LAST && (regno & 1) != 0)
+       {
+         fprintf (file, "\tsw\t%s,%d(%s)\t\t# varargs home register\n",
+                  reg_names[regno], (regno - 4) * 4, sp_str);
+         regno++;
+       }
+
+      for (; regno <= GP_ARG_LAST; regno += 2)
+       {
+         fprintf (file, "\tsd\t%s,%d(%s)\t\t# varargs home register\n",
+                  reg_names[regno], (regno - 4) * 4, sp_str);
+       }
+    }
+
+  size = MIPS_STACK_ALIGN (size);
+  tsize = (!current_frame_info.initialized)
+               ? compute_frame_size (size)
+               : current_frame_info.total_size;
+
+  if (tsize > 0)
+    {
+      if (tsize <= 32767)
+       fprintf (file,
+                "\tsubu\t%s,%s,%d\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n",
+                sp_str, sp_str, tsize, current_frame_info.var_size,
+                current_frame_info.gp_reg_size / 4,
+                current_frame_info.fp_reg_size / 8,
+                current_function_outgoing_args_size,
+                current_frame_info.extra_size);
+      else
+       fprintf (file,
+                "\tli\t%s,%d\n\tsubu\t%s,%s,%s\t\t# vars= %d, regs= %d/%d, args = %d, sfo= %d\n",
+                reg_names[MIPS_TEMP1_REGNUM], tsize, sp_str, sp_str,
+                reg_names[MIPS_TEMP1_REGNUM], current_frame_info.var_size,
+                current_frame_info.gp_reg_size / 4,
+                current_frame_info.fp_reg_size / 8,
+                current_function_outgoing_args_size,
+                current_frame_info.extra_size);
+    }
+
+  if (TARGET_ABICALLS)
+    fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET);
+
+  fprintf (file, "\t.frame\t%s,%d,%s\n\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n",
+          fp_str,
+          (frame_pointer_needed) ? 0 : tsize,
+          reg_names[31 + GP_REG_FIRST],
+          current_frame_info.mask,
+          current_frame_info.gp_save_offset,
+          current_frame_info.fmask,
+          current_frame_info.fp_save_offset);
+
+  save_restore (file, "sw", "sd", "s.d");
+
+  if (frame_pointer_needed)
+    {
+      if (tsize <= 32767)
+       fprintf (file, "\taddu\t%s,%s,%d\t# set up frame pointer\n", fp_str, sp_str, tsize);
+
+      else
+       fprintf (file, "\taddu\t%s,%s,%s\t# set up frame pointer\n", fp_str, sp_str,
+                reg_names[MIPS_TEMP1_REGNUM]);
+    }
+}
+
+\f
+/* Do any necessary cleanup after a function to restore stack, frame, and regs. */
+
+void
+function_epilogue (file, size)
+     FILE *file;
+     int size;
+{
+  int tsize;
+  char *sp_str = reg_names[STACK_POINTER_REGNUM];
+  char *t1_str = reg_names[MIPS_TEMP1_REGNUM];
+  rtx epilogue_delay = current_function_epilogue_delay_list;
+  int noreorder = !TARGET_MIPS_AS || (epilogue_delay != 0);
+  int noepilogue = FALSE;
+  int load_nop = FALSE;
+  int load_only_r31;
+
+  /* The epilogue does not depend on any registers, but the stack
+     registers, so we assume that if we have 1 pending nop, it can be
+     ignored, and 2 it must be filled (2 nops occur for integer
+     multiply and divide).  */
+
+  if (dslots_number_nops > 0)
+    {
+      if (dslots_number_nops == 1)
+       {
+         dslots_number_nops = 0;
+         dslots_load_filled++;
+       }
+      else
+       {
+         while (--dslots_number_nops > 0)
+           fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
+       }
+
+      if (set_noreorder > 0 && --set_noreorder == 0)
+       fputs ("\t.set\treorder\n", file);
+    }
+
+  if (set_noat != 0)
+    {
+      set_noat = 0;
+      fputs ("\t.set\tat\n", file);
+      error ("internal gcc error: .set noat left on in epilogue");
+    }
+
+  if (set_nomacro != 0)
+    {
+      set_nomacro = 0;
+      fputs ("\t.set\tmacro\n", file);
+      error ("internal gcc error: .set nomacro left on in epilogue");
+    }
+
+  if (set_noreorder != 0)
+    {
+      set_noreorder = 0;
+      fputs ("\t.set\treorder\n", file);
+      error ("internal gcc error: .set noreorder left on in epilogue");
+    }
+
+  if (set_volatile != 0)
+    {
+      set_volatile = 0;
+      fprintf (file, "\t#.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#");
+      error ("internal gcc error: .set volatile left on in epilogue");
+    }
+
+  size = MIPS_STACK_ALIGN (size);
+  tsize = (!current_frame_info.initialized)
+               ? compute_frame_size (size)
+               : current_frame_info.total_size;
+
+  if (tsize == 0 && epilogue_delay == 0)
+    {
+      rtx insn = get_last_insn ();
+
+      /* If the last insn was a BARRIER, we don't have to write any code
+        because a jump (aka return) was put there.  */
+      if (GET_CODE (insn) == NOTE)
+       insn = prev_nonnote_insn (insn);
+      if (insn && GET_CODE (insn) == BARRIER)
+       noepilogue = TRUE;
+
+      noreorder = FALSE;
+    }
+
+  if (!noepilogue)
+    {
+      /* In the reload sequence, we don't need to fill the load delay
+        slots for most of the loads, also see if we can fill the final
+        delay slot if not otherwise filled by the reload sequence.  */
+
+      if (noreorder)
+       fprintf (file, "\t.set\tnoreorder\n");
+
+      if (tsize > 32767)
+       fprintf (file, "\tli\t%s,%d\n", t1_str, tsize);
+
+      if (frame_pointer_needed)
+       {
+         char *fp_str = reg_names[FRAME_POINTER_REGNUM];
+         if (tsize > 32767)
+           fprintf (file,"\tsubu\t%s,%s,%s\t\t# sp not trusted  here\n",
+                    sp_str, fp_str, t1_str);
+         else
+           fprintf (file,"\tsubu\t%s,%s,%d\t\t# sp not trusted  here\n",
+                    sp_str, fp_str, tsize);
+       }
+
+      save_restore (file, "lw", "ld", "l.d");
+
+      load_only_r31 = (current_frame_info.mask == (1 << 31)
+                      && current_frame_info.fmask == 0);
+
+      if (noreorder)
+       {
+         /* If the only register saved is the return address, we need a
+            nop, unless we have an instruction to put into it.  Otherwise
+            we don't since reloading multiple registers doesn't reference
+            the register being loaded.  */
+
+         if (load_only_r31)
+           {
+             if (epilogue_delay)
+                 final_scan_insn (XEXP (epilogue_delay, 0),
+                                  file,
+                                  1,                   /* optimize */
+                                  -2,                  /* prescan */
+                                  1);                  /* nopeepholes */
+             else
+               {
+                 fprintf (file, "\tnop\n");
+                 load_nop = TRUE;
+               }
+           }
+
+         fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
+
+         if (tsize > 32767)
+           fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
+
+         else if (tsize > 0)
+           fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+
+         else if (!load_only_r31 && epilogue_delay != 0)
+           final_scan_insn (XEXP (epilogue_delay, 0),
+                            file,
+                            1,                 /* optimize */
+                            -2,                /* prescan */
+                            1);                /* nopeepholes */
+
+         fprintf (file, "\t.set\treorder\n");
+       }
+
+      else
+       {
+         if (tsize > 32767)
+           fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
+
+         else if (tsize > 0)
+           fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+
+         fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
+       }
+    }
+
+  fputs ("\t.end\t", file);
+  assemble_name (file, current_function_name);
+  fputs ("\n", file);
+
+  if (TARGET_STATS)
+    {
+      int num_gp_regs = current_frame_info.gp_reg_size / 4;
+      int num_fp_regs = current_frame_info.fp_reg_size / 8;
+      int num_regs    = num_gp_regs + num_fp_regs;
+
+      dslots_load_total += num_regs;
+
+      if (!noepilogue)
+       dslots_jump_total++;
+
+      if (noreorder)
+       {
+         dslots_load_filled += num_regs;
+
+         /* If the only register saved is the return register, we
+            can't fill this register's delay slot.  */
+
+         if (load_only_r31 && epilogue_delay == 0)
+           dslots_load_filled--;
+
+         if (tsize > 0 || (!load_only_r31 && epilogue_delay != 0))
+           dslots_jump_filled++;
+       }
+
+      fprintf (stderr,
+              "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3ld reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d\n",
+              current_function_name,
+              (frame_pointer_needed) ? 'y' : 'n',
+              ((current_frame_info.mask & (1 << 31)) != 0) ? 'n' : 'y',
+              (current_function_calls_alloca) ? 'y' : 'n',
+              (current_function_calls_setjmp) ? 'y' : 'n',
+              (long)current_frame_info.total_size,
+              (long)current_function_outgoing_args_size,
+              num_gp_regs, num_fp_regs,
+              dslots_load_total, dslots_load_filled,
+              dslots_jump_total, dslots_jump_filled,
+              num_refs[0], num_refs[1], num_refs[2]);
+    }
+
+  /* Reset state info for each function.  */
+  inside_function    = FALSE;
+  ignore_line_number = FALSE;
+  dslots_load_total  = 0;
+  dslots_jump_total  = 0;
+  dslots_load_filled = 0;
+  dslots_jump_filled = 0;
+  num_refs[0]       = 0;
+  num_refs[1]       = 0;
+  num_refs[2]       = 0;
+  mips_load_reg      = (rtx)0;
+  mips_load_reg2     = (rtx)0;
+  number_functions_processed++;
+  current_frame_info = zero_frame_info;
+
+  /* Restore the output file if optimizing the GP (optimizing the GP causes
+     the text to be diverted to a tempfile, so that data decls come before
+     references to the data).  */
+
+  if (TARGET_GP_OPT)
+    asm_out_file = asm_out_data_file;
+}
+
+\f
+/* Define the number of delay slots needed for the function epilogue.
+
+   On the mips, we need a slot if either no stack has been allocated,
+   or the only register saved is the return register.  */
+
+int
+mips_epilogue_delay_slots ()
+{
+  if (!current_frame_info.initialized)
+    (void) compute_frame_size (get_frame_size ());
+
+  if (current_frame_info.total_size == 0)
+    return 1;
+
+  if (current_frame_info.mask == (1 << 31) && current_frame_info.fmask == 0)
+    return 1;
+
+  return 0;
+}
+
+\f
+/* Return true 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
+null_epilogue ()
+{
+  if (!reload_completed)
+    return 0;
+
+  if (current_frame_info.initialized)
+    return current_frame_info.total_size == 0;
+
+  return (compute_frame_size (get_frame_size ())) == 0;
+}
+
+\f
+/* Encode in a declaration whether or not it is half-pic.  */
+
+void
+half_pic_encode_section_info (decl)
+     tree decl;
+{
+}