aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorMichael Meissner <meissner@gcc.gnu.org>1992-02-06 20:09:50 +0000
committerMichael Meissner <meissner@gcc.gnu.org>1992-02-06 20:09:50 +0000
commitcee98a590e92a137da73b0e2bb04eb77aa0011b8 (patch)
tree472ee47e111dbddb2dd898c03bc2fe2e67ec628a /gcc
parent7b3710184f03cf25de8a517b692be564382f18e7 (diff)
downloadgcc-cee98a590e92a137da73b0e2bb04eb77aa0011b8.zip
gcc-cee98a590e92a137da73b0e2bb04eb77aa0011b8.tar.gz
gcc-cee98a590e92a137da73b0e2bb04eb77aa0011b8.tar.bz2
Initial revision
From-SVN: r284
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/mips/mips.c3852
1 files changed, 3852 insertions, 0 deletions
diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c
new file mode 100644
index 0000000..0e6d2b2
--- /dev/null
+++ b/gcc/config/mips/mips.c
@@ -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,
+};
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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");
+}
+
+
+#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);
+}
+
+
+/* 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);
+}
+
+
+/* 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);
+}
+
+
+/* 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
+}
+
+
+/* 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);
+}
+
+
+/* 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;
+}
+
+
+/* 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);
+}
+
+
+/* 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);
+}
+
+
+#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 */
+
+
+/* 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;
+ }
+ }
+}
+
+
+/*
+ * 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;
+}
+
+/* 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);
+}
+
+
+/* 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;
+ }
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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);
+ }
+ }
+}
+
+
+/* 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);
+ }
+}
+
+
+/* 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);
+}
+
+
+/* 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");
+ }
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+ }
+ }
+ }
+}
+
+
+/* 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]);
+ }
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* 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;
+}
+
+
+/* Encode in a declaration whether or not it is half-pic. */
+
+void
+half_pic_encode_section_info (decl)
+ tree decl;
+{
+}