161768Sbostic /* Subroutines for insn-output.c for MIPS
261768Sbostic    Contributed by A. Lichnewsky, lich@inria.inria.fr.
361768Sbostic    Changes by     Michael Meissner, meissner@osf.org.
461768Sbostic    Copyright (C) 1989, 1990, 1991 Free Software Foundation, Inc.
561768Sbostic 
661768Sbostic This file is part of GNU CC.
761768Sbostic 
861768Sbostic GNU CC is free software; you can redistribute it and/or modify
961768Sbostic it under the terms of the GNU General Public License as published by
1061768Sbostic the Free Software Foundation; either version 2, or (at your option)
1161768Sbostic any later version.
1261768Sbostic 
1361768Sbostic GNU CC is distributed in the hope that it will be useful,
1461768Sbostic but WITHOUT ANY WARRANTY; without even the implied warranty of
1561768Sbostic MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1661768Sbostic GNU General Public License for more details.
1761768Sbostic 
1861768Sbostic You should have received a copy of the GNU General Public License
1961768Sbostic along with GNU CC; see the file COPYING.  If not, write to
2061768Sbostic the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
2161768Sbostic 
2261768Sbostic #include "config.h"
2361768Sbostic #include "rtl.h"
2461768Sbostic #include "regs.h"
2561768Sbostic #include "hard-reg-set.h"
2661768Sbostic #include "real.h"
2761768Sbostic #include "insn-config.h"
2861768Sbostic #include "conditions.h"
2961768Sbostic #include "insn-flags.h"
3061768Sbostic #include "insn-attr.h"
3161768Sbostic #include "insn-codes.h"
3261768Sbostic #include "recog.h"
3361768Sbostic #include "output.h"
3461768Sbostic 
3561768Sbostic #undef MAX			/* sys/param.h may also define these */
3661768Sbostic #undef MIN
3761768Sbostic 
3861768Sbostic #include <stdio.h>
3961768Sbostic #include <signal.h>
4061768Sbostic #include <sys/types.h>
4161768Sbostic #include <sys/file.h>
4261768Sbostic #include <ctype.h>
4361768Sbostic #include "tree.h"
4461768Sbostic #include "expr.h"
4561768Sbostic #include "flags.h"
4661768Sbostic 
4761768Sbostic #ifndef R_OK
4861768Sbostic #define R_OK 4
4961768Sbostic #define W_OK 2
5061768Sbostic #define X_OK 1
5161768Sbostic #endif
5261768Sbostic 
5361768Sbostic #if defined(USG) || defined(NO_STAB_H)
5461768Sbostic #include "gstab.h"  /* If doing DBX on sysV, use our own stab.h.  */
5561768Sbostic #else
5661768Sbostic #include <stab.h>  /* On BSD, use the system's stab.h.  */
5761768Sbostic #endif /* not USG */
5861768Sbostic 
5961768Sbostic #ifdef __GNU_STAB__
6061768Sbostic #define STAB_CODE_TYPE enum __stab_debug_code
6161768Sbostic #else
6261768Sbostic #define STAB_CODE_TYPE int
6361768Sbostic #endif
6461768Sbostic 
6561768Sbostic extern void   abort ();
6661768Sbostic extern int    atoi ();
6761768Sbostic extern char  *getenv ();
6861768Sbostic extern char  *mktemp ();
6961768Sbostic 
7061768Sbostic extern rtx    adj_offsettable_operand ();
7161768Sbostic extern rtx    copy_to_reg ();
7261768Sbostic extern void   error ();
7361768Sbostic extern void   fatal ();
7461768Sbostic extern tree   lookup_name ();
7561768Sbostic extern void   pfatal_with_name ();
7661768Sbostic extern void   warning ();
7761768Sbostic 
7861768Sbostic extern tree   current_function_decl;
7961768Sbostic extern FILE  *asm_out_file;
8061768Sbostic 
8161768Sbostic /* Enumeration for all of the relational tests, so that we can build
8261768Sbostic    arrays indexed by the test type, and not worry about the order
8361768Sbostic    of EQ, NE, etc. */
8461768Sbostic 
8561768Sbostic enum internal_test {
8661768Sbostic     ITEST_EQ,
8761768Sbostic     ITEST_NE,
8861768Sbostic     ITEST_GT,
8961768Sbostic     ITEST_GE,
9061768Sbostic     ITEST_LT,
9161768Sbostic     ITEST_LE,
9261768Sbostic     ITEST_GTU,
9361768Sbostic     ITEST_GEU,
9461768Sbostic     ITEST_LTU,
9561768Sbostic     ITEST_LEU,
9661768Sbostic     ITEST_MAX
9761768Sbostic   };
9861768Sbostic 
9961768Sbostic /* Global variables for machine-dependent things.  */
10061768Sbostic 
10161768Sbostic /* Threshold for data being put into the small data/bss area, instead
10261768Sbostic    of the normal data area (references to the small data/bss area take
10361768Sbostic    1 instruction, and use the global pointer, references to the normal
10461768Sbostic    data area takes 2 instructions).  */
10561768Sbostic int mips_section_threshold = -1;
10661768Sbostic 
10761768Sbostic /* Count the number of .file directives, so that .loc is up to date.  */
10861768Sbostic int num_source_filenames = 0;
10961768Sbostic 
11061768Sbostic /* Count the number of sdb related labels are generated (to find block
11161768Sbostic    start and end boundaries).  */
11261768Sbostic int sdb_label_count = 0;
11361768Sbostic 
11461768Sbostic /* Non-zero if inside of a function, because the stupid MIPS asm can't
11561768Sbostic    handle .files inside of functions.  */
11661768Sbostic int inside_function = 0;
11761768Sbostic 
11861768Sbostic /* Files to separate the text and the data output, so that all of the data
11961768Sbostic    can be emitted before the text, which will mean that the assembler will
12061768Sbostic    generate smaller code, based on the global pointer.  */
12161768Sbostic FILE *asm_out_data_file;
12261768Sbostic FILE *asm_out_text_file;
12361768Sbostic 
12461768Sbostic /* Linked list of all externals that are to be emitted when optimizing
12561768Sbostic    for the global pointer if they haven't been declared by the end of
12661768Sbostic    the program with an appropriate .comm or initialization.  */
12761768Sbostic 
12861768Sbostic struct extern_list {
12961768Sbostic   struct extern_list *next;	/* next external */
13061768Sbostic   char *name;			/* name of the external */
13161768Sbostic   int size;			/* size in bytes */
13261768Sbostic } *extern_head = 0;
13361768Sbostic 
13461768Sbostic /* Name of the file containing the current function.  */
13561768Sbostic char *current_function_file = "";
13661768Sbostic 
13761768Sbostic /* Warning given that Mips ECOFF can't support changing files
13861768Sbostic    within a function.  */
13961768Sbostic int file_in_function_warning = FALSE;
14061768Sbostic 
14161768Sbostic /* Whether to suppress issuing .loc's because the user attempted
14261768Sbostic    to change the filename within a function.  */
14361768Sbostic int ignore_line_number = FALSE;
14461768Sbostic 
14561768Sbostic /* Number of nested .set noreorder, noat, nomacro, and volatile requests.  */
14661768Sbostic int set_noreorder;
14761768Sbostic int set_noat;
14861768Sbostic int set_nomacro;
14961768Sbostic int set_volatile;
15061768Sbostic 
15161768Sbostic /* The next branch instruction is a branch likely, not branch normal.  */
15261768Sbostic int mips_branch_likely;
15361768Sbostic 
15461768Sbostic /* Count of delay slots and how many are filled.  */
15561768Sbostic int dslots_load_total;
15661768Sbostic int dslots_load_filled;
15761768Sbostic int dslots_jump_total;
15861768Sbostic int dslots_jump_filled;
15961768Sbostic 
16061768Sbostic /* # of nops needed by previous insn */
16161768Sbostic int dslots_number_nops;
16261768Sbostic 
16361768Sbostic /* Number of 1/2/3 word references to data items (ie, not jal's).  */
16461768Sbostic int num_refs[3];
16561768Sbostic 
16661768Sbostic /* registers to check for load delay */
16761768Sbostic rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4;
16861768Sbostic 
16961768Sbostic /* Cached operands, and operator to compare for use in set/branch on
17061768Sbostic    condition codes.  */
17161768Sbostic rtx branch_cmp[2];
17261768Sbostic 
17361768Sbostic /* what type of branch to use */
17461768Sbostic enum cmp_type branch_type;
17561768Sbostic 
17661768Sbostic /* Number of previously seen half-pic pointers and references.  */
17761768Sbostic static int prev_half_pic_ptrs = 0;
17861768Sbostic static int prev_half_pic_refs = 0;
17961768Sbostic 
18061768Sbostic /* which cpu are we scheduling for */
18161768Sbostic enum processor_type mips_cpu;
18261768Sbostic 
18361768Sbostic /* which instruction set architecture to use.  */
18461768Sbostic int mips_isa;
18561768Sbostic 
18661768Sbostic /* Strings to hold which cpu and instruction set architecture to use.  */
18761768Sbostic char *mips_cpu_string;		/* for -mcpu=<xxx> */
18861768Sbostic char *mips_isa_string;		/* for -mips{1,2,3} */
18961768Sbostic 
19061768Sbostic /* Array to RTX class classification.  At present, we care about
19161768Sbostic    whether the operator is an add-type operator, or a divide/modulus,
19261768Sbostic    and if divide/modulus, whether it is unsigned.  This is for the
19361768Sbostic    peephole code.  */
19461768Sbostic char mips_rtx_classify[NUM_RTX_CODE];
19561768Sbostic 
19661768Sbostic /* Array giving truth value on whether or not a given hard register
19761768Sbostic    can support a given mode.  */
19861768Sbostic char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
19961768Sbostic 
20061768Sbostic /* Current frame information calculated by compute_frame_size.  */
20161768Sbostic struct mips_frame_info current_frame_info;
20261768Sbostic 
20361768Sbostic /* Zero structure to initialize current_frame_info.  */
20461768Sbostic struct mips_frame_info zero_frame_info;
20561768Sbostic 
20661768Sbostic /* Temporary filename used to buffer .text until end of program
20761768Sbostic    for -mgpopt.  */
20861768Sbostic static char *temp_filename;
20961768Sbostic 
21061768Sbostic /* List of all MIPS punctuation characters used by print_operand.  */
21161768Sbostic char mips_print_operand_punct[256];
21261768Sbostic 
21361768Sbostic /* Map GCC register number to debugger register number.  */
21461768Sbostic int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
21561768Sbostic 
21661768Sbostic /* Buffer to use to enclose a load/store operation with %{ %} to
21761768Sbostic    turn on .set volatile.  */
21861768Sbostic static char volatile_buffer[60];
21961768Sbostic 
22061768Sbostic /* Hardware names for the registers.  If -mrnames is used, this
22161768Sbostic    will be overwritten with mips_sw_reg_names.  */
22261768Sbostic 
22361768Sbostic char mips_reg_names[][8] =
22461768Sbostic {
22561768Sbostic  "$0",   "$1",   "$2",   "$3",   "$4",   "$5",   "$6",   "$7",
22661768Sbostic  "$8",   "$9",   "$10",  "$11",  "$12",  "$13",  "$14",  "$15",
22761768Sbostic  "$16",  "$17",  "$18",  "$19",  "$20",  "$21",  "$22",  "$23",
22861768Sbostic  "$24",  "$25",  "$26",  "$27",  "$28",  "$sp",  "$fp",  "$31",
22961768Sbostic  "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
23061768Sbostic  "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
23161768Sbostic  "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
23261768Sbostic  "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
23361768Sbostic  "hi",   "lo",   "$fcr31"
23461768Sbostic };
23561768Sbostic 
23661768Sbostic /* Mips software names for the registers, used to overwrite the
23761768Sbostic    mips_reg_names array.  */
23861768Sbostic 
23961768Sbostic char mips_sw_reg_names[][8] =
24061768Sbostic {
24161768Sbostic   "$0",   "at",   "v0",   "v1",   "a0",   "a1",   "a2",   "a3",
24261768Sbostic   "t0",   "t1",   "t2",   "t3",   "t4",   "t5",   "t6",   "t7",
24361768Sbostic   "s0",   "s1",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
24461768Sbostic   "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "$fp",   "ra",
24561768Sbostic   "$f0",  "$f1",  "$f2",  "$f3",  "$f4",  "$f5",  "$f6",  "$f7",
24661768Sbostic   "$f8",  "$f9",  "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
24761768Sbostic   "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
24861768Sbostic   "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
24961768Sbostic   "hi",   "lo",   "$fcr31"
25061768Sbostic };
25161768Sbostic 
25261768Sbostic /* Map hard register number to register class */
25361768Sbostic enum reg_class mips_regno_to_class[] =
25461768Sbostic {
25561768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
25661768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
25761768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
25861768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
25961768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
26061768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
26161768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
26261768Sbostic   GR_REGS,	GR_REGS,	GR_REGS,	GR_REGS,
26361768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
26461768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
26561768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
26661768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
26761768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
26861768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
26961768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
27061768Sbostic   FP_REGS,	FP_REGS,	FP_REGS,	FP_REGS,
27161768Sbostic   HI_REG,	LO_REG,		ST_REGS
27261768Sbostic };
27361768Sbostic 
27461768Sbostic /* Map register constraint character to register class.  */
27561768Sbostic enum reg_class mips_char_to_class[256] =
27661768Sbostic {
27761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
27861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
27961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28161768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28261768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28361768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28461768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28561768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28661768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
28961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29161768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29261768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29361768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29461768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29561768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29661768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
29961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30161768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30261768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30361768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30461768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30561768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30661768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
30961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31161768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31261768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31361768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31461768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31561768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31661768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
31961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32161768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32261768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32361768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32461768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32561768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32661768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
32961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33161768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33261768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33361768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33461768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33561768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33661768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33761768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33861768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
33961768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
34061768Sbostic   NO_REGS,	NO_REGS,	NO_REGS,	NO_REGS,
34161768Sbostic };
34261768Sbostic 
34361768Sbostic 
34461768Sbostic /* Return truth value of whether OP can be used as an operands
34561768Sbostic    where a register or 16 bit unsigned integer is needed.  */
34661768Sbostic 
34761768Sbostic int
uns_arith_operand(op,mode)34861768Sbostic uns_arith_operand (op, mode)
34961768Sbostic      rtx op;
35061768Sbostic      enum machine_mode mode;
35161768Sbostic {
35261768Sbostic   if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
35361768Sbostic     return TRUE;
35461768Sbostic 
35561768Sbostic   return register_operand (op, mode);
35661768Sbostic }
35761768Sbostic 
35861768Sbostic /* Return truth value of whether OP can be used as an operands
35961768Sbostic    where a 16 bit integer is needed  */
36061768Sbostic 
36161768Sbostic int
arith_operand(op,mode)36261768Sbostic arith_operand (op, mode)
36361768Sbostic      rtx op;
36461768Sbostic      enum machine_mode mode;
36561768Sbostic {
36661768Sbostic   if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
36761768Sbostic     return TRUE;
36861768Sbostic 
36961768Sbostic   return register_operand (op, mode);
37061768Sbostic }
37161768Sbostic 
37261768Sbostic /* Return truth value of whether OP can be used as an operand in a two
37361768Sbostic    address arithmetic insn (such as set 123456,%o4) of mode MODE.  */
37461768Sbostic 
37561768Sbostic int
arith32_operand(op,mode)37661768Sbostic arith32_operand (op, mode)
37761768Sbostic      rtx op;
37861768Sbostic      enum machine_mode mode;
37961768Sbostic {
38061768Sbostic   if (GET_CODE (op) == CONST_INT)
38161768Sbostic     return TRUE;
38261768Sbostic 
38361768Sbostic   return register_operand (op, mode);
38461768Sbostic }
38561768Sbostic 
38661768Sbostic /* Return truth value of whether OP is a integer which fits in 16 bits  */
38761768Sbostic 
38861768Sbostic int
small_int(op,mode)38961768Sbostic small_int (op, mode)
39061768Sbostic      rtx op;
39161768Sbostic      enum machine_mode mode;
39261768Sbostic {
39361768Sbostic   return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
39461768Sbostic }
39561768Sbostic 
39661768Sbostic /* Return truth value of whether OP is an integer which is too big to
39761768Sbostic    be loaded with one instruction.  */
39861768Sbostic 
39961768Sbostic int
large_int(op,mode)40061768Sbostic large_int (op, mode)
40161768Sbostic      rtx op;
40261768Sbostic      enum machine_mode mode;
40361768Sbostic {
40461768Sbostic   HOST_WIDE_INT value;
40561768Sbostic 
40661768Sbostic   if (GET_CODE (op) != CONST_INT)
40761768Sbostic     return FALSE;
40861768Sbostic 
40961768Sbostic   value = INTVAL (op);
41061768Sbostic   if ((value & ~0x0000ffff) == 0)			/* ior reg,$r0,value */
41161768Sbostic     return FALSE;
41261768Sbostic 
41361768Sbostic   if (((unsigned long)(value + 32768)) <= 32767)	/* subu reg,$r0,value */
41461768Sbostic     return FALSE;
41561768Sbostic 
41661768Sbostic   if ((value & 0xffff0000) == value)			/* lui reg,value>>16 */
41761768Sbostic     return FALSE;
41861768Sbostic 
41961768Sbostic   return TRUE;
42061768Sbostic }
42161768Sbostic 
42261768Sbostic /* Return truth value of whether OP is a register or the constant 0.  */
42361768Sbostic 
42461768Sbostic int
reg_or_0_operand(op,mode)42561768Sbostic reg_or_0_operand (op, mode)
42661768Sbostic      rtx op;
42761768Sbostic      enum machine_mode mode;
42861768Sbostic {
42961768Sbostic   switch (GET_CODE (op))
43061768Sbostic     {
43161768Sbostic     default:
43261768Sbostic       break;
43361768Sbostic 
43461768Sbostic     case CONST_INT:
43561768Sbostic       return (INTVAL (op) == 0);
43661768Sbostic 
43761768Sbostic     case CONST_DOUBLE:
43861768Sbostic       if (CONST_DOUBLE_HIGH (op) != 0 || CONST_DOUBLE_LOW (op) != 0)
43961768Sbostic 	return FALSE;
44061768Sbostic 
44161768Sbostic       return TRUE;
44261768Sbostic 
44361768Sbostic     case REG:
44461768Sbostic     case SUBREG:
44561768Sbostic       return register_operand (op, mode);
44661768Sbostic     }
44761768Sbostic 
44861768Sbostic   return FALSE;
44961768Sbostic }
45061768Sbostic 
45161768Sbostic /* Return truth value of whether OP is one of the special multiply/divide
45261768Sbostic    registers (hi, lo).  */
45361768Sbostic 
45461768Sbostic int
md_register_operand(op,mode)45561768Sbostic md_register_operand (op, mode)
45661768Sbostic      rtx op;
45761768Sbostic      enum machine_mode mode;
45861768Sbostic {
45961768Sbostic   return (GET_MODE_CLASS (mode) == MODE_INT
46061768Sbostic 	  && GET_CODE (op) == REG
46161768Sbostic 	  && MD_REG_P (REGNO (op)));
46261768Sbostic }
46361768Sbostic 
46461768Sbostic /* Return truth value of whether OP is the FP status register.  */
46561768Sbostic 
46661768Sbostic int
fpsw_register_operand(op,mode)46761768Sbostic fpsw_register_operand (op, mode)
46861768Sbostic      rtx op;
46961768Sbostic      enum machine_mode mode;
47061768Sbostic {
47161768Sbostic   return (GET_CODE (op) == REG && ST_REG_P (REGNO (op)));
47261768Sbostic }
47361768Sbostic 
47461768Sbostic /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant.  */
47561768Sbostic 
47661768Sbostic int
mips_const_double_ok(op,mode)47761768Sbostic mips_const_double_ok (op, mode)
47861768Sbostic      rtx op;
47961768Sbostic      enum machine_mode mode;
48061768Sbostic {
48161768Sbostic   if (GET_CODE (op) != CONST_DOUBLE)
48261768Sbostic     return FALSE;
48361768Sbostic 
48461768Sbostic   if (mode == DImode)
48561768Sbostic     return TRUE;
48661768Sbostic 
48761768Sbostic   if (mode != SFmode && mode != DFmode)
48861768Sbostic     return FALSE;
48961768Sbostic 
49061768Sbostic   if (CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0)
49161768Sbostic     return TRUE;
49261768Sbostic 
49361768Sbostic #if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
49461768Sbostic   if (TARGET_MIPS_AS)		/* gas doesn't like li.d/li.s yet */
49561768Sbostic     {
49661768Sbostic       union { double d; int i[2]; } u;
49761768Sbostic       double d;
49861768Sbostic 
49961768Sbostic       u.i[0] = CONST_DOUBLE_LOW (op);
50061768Sbostic       u.i[1] = CONST_DOUBLE_HIGH (op);
50161768Sbostic       d = u.d;
50261768Sbostic 
50361768Sbostic       if (d != d)
50461768Sbostic 	return FALSE;		/* NAN */
50561768Sbostic 
50661768Sbostic       if (d < 0.0)
50761768Sbostic 	d = - d;
50861768Sbostic 
50961768Sbostic       /* Rather than trying to get the accuracy down to the last bit,
51061768Sbostic 	 just use approximate ranges.  */
51161768Sbostic 
51261768Sbostic       if (mode == DFmode && d > 1.0e-300 && d < 1.0e300)
51361768Sbostic 	return TRUE;
51461768Sbostic 
51561768Sbostic       if (mode == SFmode && d > 1.0e-38 && d < 1.0e+38)
51661768Sbostic 	return TRUE;
51761768Sbostic     }
51861768Sbostic #endif
51961768Sbostic 
52061768Sbostic   return FALSE;
52161768Sbostic }
52261768Sbostic 
52361768Sbostic /* Return truth value if a memory operand fits in a single instruction
52461768Sbostic    (ie, register + small offset).  */
52561768Sbostic 
52661768Sbostic int
simple_memory_operand(op,mode)52761768Sbostic simple_memory_operand (op, mode)
52861768Sbostic      rtx op;
52961768Sbostic      enum machine_mode mode;
53061768Sbostic {
53161768Sbostic   rtx addr, plus0, plus1;
53261768Sbostic 
53361768Sbostic   /* Eliminate non-memory operations */
53461768Sbostic   if (GET_CODE (op) != MEM)
53561768Sbostic     return FALSE;
53661768Sbostic 
53761768Sbostic   /* dword operations really put out 2 instructions, so eliminate them.  */
53861768Sbostic   if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4))
53961768Sbostic     return FALSE;
54061768Sbostic 
54161768Sbostic   /* Decode the address now.  */
54261768Sbostic   addr = XEXP (op, 0);
54361768Sbostic   switch (GET_CODE (addr))
54461768Sbostic     {
54561768Sbostic     default:
54661768Sbostic       break;
54761768Sbostic 
54861768Sbostic     case REG:
54961768Sbostic       return TRUE;
55061768Sbostic 
55161768Sbostic     case CONST_INT:
55261768Sbostic       return SMALL_INT (op);
55361768Sbostic 
55461768Sbostic     case PLUS:
55561768Sbostic       plus0 = XEXP (addr, 0);
55661768Sbostic       plus1 = XEXP (addr, 1);
55761768Sbostic       if (GET_CODE (plus0) == REG
55861768Sbostic 	  && GET_CODE (plus1) == CONST_INT
55961768Sbostic 	  && SMALL_INT (plus1))
56061768Sbostic 	return TRUE;
56161768Sbostic 
56261768Sbostic       else if (GET_CODE (plus1) == REG
56361768Sbostic 	       && GET_CODE (plus0) == CONST_INT
56461768Sbostic 	       && SMALL_INT (plus0))
56561768Sbostic 	return TRUE;
56661768Sbostic 
56761768Sbostic       else
56861768Sbostic 	return FALSE;
56961768Sbostic 
57061768Sbostic #if 0
57161768Sbostic       /* We used to allow small symbol refs here (ie, stuff in .sdata
57261768Sbostic 	 or .sbss), but this causes some bugs in G++.  Also, it won't
57361768Sbostic 	 interfere if the MIPS linker rewrites the store instruction
57461768Sbostic 	 because the function is PIC.  */
57561768Sbostic 
57661768Sbostic     case LABEL_REF:		/* never gp relative */
57761768Sbostic       break;
57861768Sbostic 
57961768Sbostic     case CONST:
58061768Sbostic       /* If -G 0, we can never have a GP relative memory operation.
58161768Sbostic 	 Also, save some time if not optimizing.  */
58261768Sbostic       if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT)
58361768Sbostic 	return FALSE;
58461768Sbostic 
58561768Sbostic       {
58661768Sbostic 	rtx offset = const0_rtx;
58761768Sbostic 	addr = eliminate_constant_term (addr, &offset);
58861768Sbostic 	if (GET_CODE (op) != SYMBOL_REF)
58961768Sbostic 	  return FALSE;
59061768Sbostic 
59161768Sbostic 	/* let's be paranoid.... */
59261768Sbostic 	if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff)
59361768Sbostic 	  return FALSE;
59461768Sbostic       }
59561768Sbostic       /* fall through */
59661768Sbostic 
59761768Sbostic     case SYMBOL_REF:
59861768Sbostic       return SYMBOL_REF_FLAG (addr);
59961768Sbostic #endif
60061768Sbostic     }
60161768Sbostic 
60261768Sbostic   return FALSE;
60361768Sbostic }
60461768Sbostic 
60561768Sbostic /* Return true if the code of this rtx pattern is EQ or NE.  */
60661768Sbostic 
60761768Sbostic int
equality_op(op,mode)60861768Sbostic equality_op (op, mode)
60961768Sbostic      rtx op;
61061768Sbostic      enum machine_mode mode;
61161768Sbostic {
61261768Sbostic   if (mode != GET_MODE (op))
61361768Sbostic     return FALSE;
61461768Sbostic 
61561768Sbostic   return (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0;
61661768Sbostic }
61761768Sbostic 
61861768Sbostic /* Return true if the code is a relational operations (EQ, LE, etc.) */
61961768Sbostic 
62061768Sbostic int
cmp_op(op,mode)62161768Sbostic cmp_op (op, mode)
62261768Sbostic      rtx op;
62361768Sbostic      enum machine_mode mode;
62461768Sbostic {
62561768Sbostic   if (mode != GET_MODE (op))
62661768Sbostic     return FALSE;
62761768Sbostic 
62861768Sbostic   return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
62961768Sbostic }
63061768Sbostic 
63161768Sbostic 
63261768Sbostic /* Genrecog does not take the type of match_operator into consideration,
63361768Sbostic    and would complain about two patterns being the same if the same
63461768Sbostic    function is used, so make it believe they are different.  */
63561768Sbostic 
63661768Sbostic int
cmp2_op(op,mode)63761768Sbostic cmp2_op (op, mode)
63861768Sbostic      rtx op;
63961768Sbostic      enum machine_mode mode;
64061768Sbostic {
64161768Sbostic   if (mode != GET_MODE (op))
64261768Sbostic     return FALSE;
64361768Sbostic 
64461768Sbostic   return (classify_op (op, mode) & CLASS_CMP_OP) != 0;
64561768Sbostic }
64661768Sbostic 
64761768Sbostic /* Return true if the code is an unsigned relational operations (LEU, etc.) */
64861768Sbostic 
64961768Sbostic int
uns_cmp_op(op,mode)65061768Sbostic uns_cmp_op (op,mode)
65161768Sbostic      rtx op;
65261768Sbostic      enum machine_mode mode;
65361768Sbostic {
65461768Sbostic   if (mode != GET_MODE (op))
65561768Sbostic     return FALSE;
65661768Sbostic 
65761768Sbostic   return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP;
65861768Sbostic }
65961768Sbostic 
66061768Sbostic /* Return true if the code is a relational operation FP can use.  */
66161768Sbostic 
66261768Sbostic int
fcmp_op(op,mode)66361768Sbostic fcmp_op (op, mode)
66461768Sbostic      rtx op;
66561768Sbostic      enum machine_mode mode;
66661768Sbostic {
66761768Sbostic   if (mode != GET_MODE (op))
66861768Sbostic     return FALSE;
66961768Sbostic 
67061768Sbostic   return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;
67161768Sbostic }
67261768Sbostic 
67361768Sbostic 
67461768Sbostic /* Return true if the operand is either the PC or a label_ref.  */
67561768Sbostic 
67661768Sbostic int
pc_or_label_operand(op,mode)67761768Sbostic pc_or_label_operand (op, mode)
67861768Sbostic      rtx op;
67961768Sbostic      enum machine_mode mode;
68061768Sbostic {
68161768Sbostic   if (op == pc_rtx)
68261768Sbostic     return TRUE;
68361768Sbostic 
68461768Sbostic   if (GET_CODE (op) == LABEL_REF)
68561768Sbostic     return TRUE;
68661768Sbostic 
68761768Sbostic   return FALSE;
68861768Sbostic }
68961768Sbostic 
69061768Sbostic 
69161768Sbostic /* Return an operand string if the given instruction's delay slot or
69261768Sbostic    wrap it in a .set noreorder section.  This is for filling delay
69361768Sbostic    slots on load type instructions under GAS, which does no reordering
69461768Sbostic    on its own.  For the MIPS assembler, all we do is update the filled
69561768Sbostic    delay slot statistics.
69661768Sbostic 
69761768Sbostic    We assume that operands[0] is the target register that is set.
69861768Sbostic 
69961768Sbostic    In order to check the next insn, most of this functionality is moved
70061768Sbostic    to FINAL_PRESCAN_INSN, and we just set the global variables that
70161768Sbostic    it needs.  */
70261768Sbostic 
70361768Sbostic char *
mips_fill_delay_slot(ret,type,operands,cur_insn)70461768Sbostic mips_fill_delay_slot (ret, type, operands, cur_insn)
70561768Sbostic      char *ret;			/* normal string to return */
70661768Sbostic      enum delay_type type;	/* type of delay */
70761768Sbostic      rtx operands[];		/* operands to use */
70861768Sbostic      rtx cur_insn;		/* current insn */
70961768Sbostic {
71061768Sbostic   register rtx set_reg;
71161768Sbostic   register enum machine_mode mode;
71261768Sbostic   register rtx next_insn	= (cur_insn) ? NEXT_INSN (cur_insn) : (rtx)0;
71361768Sbostic   register int num_nops;
71461768Sbostic 
71561768Sbostic   if (type == DELAY_LOAD || type == DELAY_FCMP)
71661768Sbostic     num_nops = 1;
71761768Sbostic 
71861768Sbostic   else if (type == DELAY_HILO)
71961768Sbostic     num_nops = 2;
72061768Sbostic 
72161768Sbostic   else
72261768Sbostic     num_nops = 0;
72361768Sbostic 
72461768Sbostic   /* Make sure that we don't put nop's after labels.  */
72561768Sbostic   next_insn = NEXT_INSN (cur_insn);
72661768Sbostic   while (next_insn != (rtx)0 && GET_CODE (next_insn) == NOTE)
72761768Sbostic     next_insn = NEXT_INSN (next_insn);
72861768Sbostic 
72961768Sbostic   dslots_load_total += num_nops;
73061768Sbostic   if (TARGET_DEBUG_F_MODE
73161768Sbostic       || !optimize
73261768Sbostic       || type == DELAY_NONE
73361768Sbostic       || operands == (rtx *)0
73461768Sbostic       || cur_insn == (rtx)0
73561768Sbostic       || next_insn == (rtx)0
73661768Sbostic       || GET_CODE (next_insn) == CODE_LABEL
73761768Sbostic       || (set_reg = operands[0]) == (rtx)0)
73861768Sbostic     {
73961768Sbostic       dslots_number_nops = 0;
74061768Sbostic       mips_load_reg  = (rtx)0;
74161768Sbostic       mips_load_reg2 = (rtx)0;
74261768Sbostic       mips_load_reg3 = (rtx)0;
74361768Sbostic       mips_load_reg4 = (rtx)0;
74461768Sbostic       return ret;
74561768Sbostic     }
74661768Sbostic 
74761768Sbostic   set_reg = operands[0];
74861768Sbostic   if (set_reg == (rtx)0)
74961768Sbostic     return ret;
75061768Sbostic 
75161768Sbostic   while (GET_CODE (set_reg) == SUBREG)
75261768Sbostic     set_reg = SUBREG_REG (set_reg);
75361768Sbostic 
75461768Sbostic   mode = GET_MODE (set_reg);
75561768Sbostic   dslots_number_nops = num_nops;
75661768Sbostic   mips_load_reg  = set_reg;
75761768Sbostic   mips_load_reg2 = (mode == DImode || mode == DFmode)
75861768Sbostic 			? gen_rtx (REG, SImode, REGNO (set_reg) + 1)
75961768Sbostic 			: (rtx)0;
76061768Sbostic 
76161768Sbostic   if (type == DELAY_HILO)
76261768Sbostic     {
76361768Sbostic       mips_load_reg3 = gen_rtx (REG, SImode, MD_REG_FIRST);
76461768Sbostic       mips_load_reg4 = gen_rtx (REG, SImode, MD_REG_FIRST+1);
76561768Sbostic     }
76661768Sbostic   else
76761768Sbostic     {
76861768Sbostic       mips_load_reg3 = 0;
76961768Sbostic       mips_load_reg4 = 0;
77061768Sbostic     }
77161768Sbostic 
77261768Sbostic   if (TARGET_GAS && set_noreorder++ == 0)
77361768Sbostic     fputs ("\t.set\tnoreorder\n", asm_out_file);
77461768Sbostic 
77561768Sbostic   return ret;
77661768Sbostic }
77761768Sbostic 
77861768Sbostic 
77961768Sbostic /* Determine whether a memory reference takes one (based off of the GP pointer),
78061768Sbostic    two (normal), or three (label + reg) instructions, and bump the appropriate
78161768Sbostic    counter for -mstats.  */
78261768Sbostic 
78361768Sbostic void
mips_count_memory_refs(op,num)78461768Sbostic mips_count_memory_refs (op, num)
78561768Sbostic      rtx op;
78661768Sbostic      int num;
78761768Sbostic {
78861768Sbostic   int additional = 0;
78961768Sbostic   int n_words = 0;
79061768Sbostic   rtx addr, plus0, plus1;
79161768Sbostic   enum rtx_code code0, code1;
79261768Sbostic   int looping;
79361768Sbostic 
79461768Sbostic   if (TARGET_DEBUG_B_MODE)
79561768Sbostic     {
79661768Sbostic       fprintf (stderr, "\n========== mips_count_memory_refs:\n");
79761768Sbostic       debug_rtx (op);
79861768Sbostic     }
79961768Sbostic 
80061768Sbostic   /* Skip MEM if passed, otherwise handle movsi of address.  */
80161768Sbostic   addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
80261768Sbostic 
80361768Sbostic   /* Loop, going through the address RTL */
80461768Sbostic   do
80561768Sbostic     {
80661768Sbostic       looping = FALSE;
80761768Sbostic       switch (GET_CODE (addr))
80861768Sbostic 	{
80961768Sbostic 	default:
81061768Sbostic 	  break;
81161768Sbostic 
81261768Sbostic 	case REG:
81361768Sbostic 	case CONST_INT:
81461768Sbostic 	  break;
81561768Sbostic 
81661768Sbostic 	case PLUS:
81761768Sbostic 	  plus0 = XEXP (addr, 0);
81861768Sbostic 	  plus1 = XEXP (addr, 1);
81961768Sbostic 	  code0 = GET_CODE (plus0);
82061768Sbostic 	  code1 = GET_CODE (plus1);
82161768Sbostic 
82261768Sbostic 	  if (code0 == REG)
82361768Sbostic 	    {
82461768Sbostic 	      additional++;
82561768Sbostic 	      addr = plus1;
82661768Sbostic 	      looping = TRUE;
82761768Sbostic 	      continue;
82861768Sbostic 	    }
82961768Sbostic 
83061768Sbostic 	  if (code0 == CONST_INT)
83161768Sbostic 	    {
83261768Sbostic 	      addr = plus1;
83361768Sbostic 	      looping = TRUE;
83461768Sbostic 	      continue;
83561768Sbostic 	    }
83661768Sbostic 
83761768Sbostic 	  if (code1 == REG)
83861768Sbostic 	    {
83961768Sbostic 	      additional++;
84061768Sbostic 	      addr = plus0;
84161768Sbostic 	      looping = TRUE;
84261768Sbostic 	      continue;
84361768Sbostic 	    }
84461768Sbostic 
84561768Sbostic 	  if (code1 == CONST_INT)
84661768Sbostic 	    {
84761768Sbostic 	      addr = plus0;
84861768Sbostic 	      looping = TRUE;
84961768Sbostic 	      continue;
85061768Sbostic 	    }
85161768Sbostic 
85261768Sbostic 	  if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
85361768Sbostic 	    {
85461768Sbostic 	      addr = plus0;
85561768Sbostic 	      looping = TRUE;
85661768Sbostic 	      continue;
85761768Sbostic 	    }
85861768Sbostic 
85961768Sbostic 	  if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
86061768Sbostic 	    {
86161768Sbostic 	      addr = plus1;
86261768Sbostic 	      looping = TRUE;
86361768Sbostic 	      continue;
86461768Sbostic 	    }
86561768Sbostic 
86661768Sbostic 	  break;
86761768Sbostic 
86861768Sbostic 	case LABEL_REF:
86961768Sbostic 	  n_words = 2;		/* always 2 words */
87061768Sbostic 	  break;
87161768Sbostic 
87261768Sbostic 	case CONST:
87361768Sbostic 	  addr = XEXP (addr, 0);
87461768Sbostic 	  looping = TRUE;
87561768Sbostic 	  continue;
87661768Sbostic 
87761768Sbostic 	case SYMBOL_REF:
87861768Sbostic 	  n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
87961768Sbostic 	  break;
88061768Sbostic 	}
88161768Sbostic     }
88261768Sbostic   while (looping);
88361768Sbostic 
88461768Sbostic   if (n_words == 0)
88561768Sbostic     return;
88661768Sbostic 
88761768Sbostic   n_words += additional;
88861768Sbostic   if (n_words > 3)
88961768Sbostic     n_words = 3;
89061768Sbostic 
89161768Sbostic   num_refs[n_words-1] += num;
89261768Sbostic }
89361768Sbostic 
89461768Sbostic 
89561768Sbostic /* Return the appropriate instructions to move one operand to another.  */
89661768Sbostic 
89761768Sbostic char *
mips_move_1word(operands,insn,unsignedp)89861768Sbostic mips_move_1word (operands, insn, unsignedp)
89961768Sbostic      rtx operands[];
90061768Sbostic      rtx insn;
90161768Sbostic      int unsignedp;
90261768Sbostic {
90361768Sbostic   char *ret = 0;
90461768Sbostic   rtx op0 = operands[0];
90561768Sbostic   rtx op1 = operands[1];
90661768Sbostic   enum rtx_code code0 = GET_CODE (op0);
90761768Sbostic   enum rtx_code code1 = GET_CODE (op1);
90861768Sbostic   enum machine_mode mode = GET_MODE (op0);
90961768Sbostic   int subreg_word0 = 0;
91061768Sbostic   int subreg_word1 = 0;
91161768Sbostic   enum delay_type delay = DELAY_NONE;
91261768Sbostic 
91361768Sbostic   while (code0 == SUBREG)
91461768Sbostic     {
91561768Sbostic       subreg_word0 += SUBREG_WORD (op0);
91661768Sbostic       op0 = SUBREG_REG (op0);
91761768Sbostic       code0 = GET_CODE (op0);
91861768Sbostic     }
91961768Sbostic 
92061768Sbostic   while (code1 == SUBREG)
92161768Sbostic     {
92261768Sbostic       subreg_word1 += SUBREG_WORD (op1);
92361768Sbostic       op1 = SUBREG_REG (op1);
92461768Sbostic       code1 = GET_CODE (op1);
92561768Sbostic     }
92661768Sbostic 
92761768Sbostic   if (code0 == REG)
92861768Sbostic     {
92961768Sbostic       int regno0 = REGNO (op0) + subreg_word0;
93061768Sbostic 
93161768Sbostic       if (code1 == REG)
93261768Sbostic 	{
93361768Sbostic 	  int regno1 = REGNO (op1) + subreg_word1;
93461768Sbostic 
93561768Sbostic 	  /* Just in case, don't do anything for assigning a register
93661768Sbostic 	     to itself, unless we are filling a delay slot.  */
93761768Sbostic 	  if (regno0 == regno1 && set_nomacro == 0)
93861768Sbostic 	    ret = "";
93961768Sbostic 
94061768Sbostic 	  else if (GP_REG_P (regno0))
94161768Sbostic 	    {
94261768Sbostic 	      if (GP_REG_P (regno1))
94361768Sbostic 		ret = "move\t%0,%1";
94461768Sbostic 
94561768Sbostic 	      else if (MD_REG_P (regno1))
94661768Sbostic 		{
94761768Sbostic 		  delay = DELAY_HILO;
94861768Sbostic 		  ret = "mf%1\t%0";
94961768Sbostic 		}
95061768Sbostic 
95161768Sbostic 	      else
95261768Sbostic 		{
95361768Sbostic 		  delay = DELAY_LOAD;
95461768Sbostic 		  if (FP_REG_P (regno1))
95561768Sbostic 		    ret = "mfc1\t%0,%1";
95661768Sbostic 
95761768Sbostic 		  else if (regno1 == FPSW_REGNUM)
95861768Sbostic 		    ret = "cfc1\t%0,$31";
95961768Sbostic 		}
96061768Sbostic 	    }
96161768Sbostic 
96261768Sbostic 	  else if (FP_REG_P (regno0))
96361768Sbostic 	    {
96461768Sbostic 	      if (GP_REG_P (regno1))
96561768Sbostic 		{
96661768Sbostic 		  delay = DELAY_LOAD;
96761768Sbostic 		  ret = "mtc1\t%1,%0";
96861768Sbostic 		}
96961768Sbostic 
97061768Sbostic 	      if (FP_REG_P (regno1))
97161768Sbostic 		ret = "mov.s\t%0,%1";
97261768Sbostic 	    }
97361768Sbostic 
97461768Sbostic 	  else if (MD_REG_P (regno0))
97561768Sbostic 	    {
97661768Sbostic 	      if (GP_REG_P (regno1))
97761768Sbostic 		{
97861768Sbostic 		  delay = DELAY_HILO;
97961768Sbostic 		  ret = "mt%0\t%1";
98061768Sbostic 		}
98161768Sbostic 	    }
98261768Sbostic 
98361768Sbostic 	  else if (regno0 == FPSW_REGNUM)
98461768Sbostic 	    {
98561768Sbostic 	      if (GP_REG_P (regno1))
98661768Sbostic 		{
98761768Sbostic 		  delay = DELAY_LOAD;
98861768Sbostic 		  ret = "ctc1\t%0,$31";
98961768Sbostic 		}
99061768Sbostic 	    }
99161768Sbostic 	}
99261768Sbostic 
99361768Sbostic       else if (code1 == MEM)
99461768Sbostic 	{
99561768Sbostic 	  delay = DELAY_LOAD;
99661768Sbostic 
99761768Sbostic 	  if (TARGET_STATS)
99861768Sbostic 	    mips_count_memory_refs (op1, 1);
99961768Sbostic 
100061768Sbostic 	  if (GP_REG_P (regno0))
100161768Sbostic 	    {
100261768Sbostic 	      /* For loads, use the mode of the memory item, instead of the
100361768Sbostic 		 target, so zero/sign extend can use this code as well.  */
100461768Sbostic 	      switch (GET_MODE (op1))
100561768Sbostic 		{
100661768Sbostic 		default:							break;
100761768Sbostic 		case SFmode: ret = "lw\t%0,%1";					break;
100861768Sbostic 		case SImode: ret = "lw\t%0,%1";					break;
100961768Sbostic 		case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";	break;
101061768Sbostic 		case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";	break;
101161768Sbostic 		}
101261768Sbostic 	    }
101361768Sbostic 
101461768Sbostic 	  else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
101561768Sbostic 	    ret = "l.s\t%0,%1";
101661768Sbostic 
101761768Sbostic 	  if (ret != (char *)0 && MEM_VOLATILE_P (op1))
101861768Sbostic 	    {
101961768Sbostic 	      int i = strlen (ret);
102061768Sbostic 	      if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
102161768Sbostic 		abort ();
102261768Sbostic 
102361768Sbostic 	      sprintf (volatile_buffer, "%%{%s%%}", ret);
102461768Sbostic 	      ret = volatile_buffer;
102561768Sbostic 	    }
102661768Sbostic 	}
102761768Sbostic 
102861768Sbostic       else if (code1 == CONST_INT)
102961768Sbostic 	{
103061768Sbostic 	  if (INTVAL (op1) == 0)
103161768Sbostic 	    {
103261768Sbostic 	      if (GP_REG_P (regno0))
103361768Sbostic 		ret = "move\t%0,%z1";
103461768Sbostic 
103561768Sbostic 	      else if (FP_REG_P (regno0))
103661768Sbostic 		{
103761768Sbostic 		  delay = DELAY_LOAD;
103861768Sbostic 		  ret = "mtc1\t%z1,%0";
103961768Sbostic 		}
104061768Sbostic 	    }
104161768Sbostic 
104261768Sbostic 	  else if (GP_REG_P (regno0))
104361768Sbostic 	    ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1";
104461768Sbostic 	}
104561768Sbostic 
104661768Sbostic       else if (code1 == CONST_DOUBLE && mode == SFmode)
104761768Sbostic 	{
104861768Sbostic 	  if (CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
104961768Sbostic 	    {
105061768Sbostic 	      if (GP_REG_P (regno0))
105161768Sbostic 		ret = "move\t%0,%.";
105261768Sbostic 
105361768Sbostic 	      else if (FP_REG_P (regno0))
105461768Sbostic 		{
105561768Sbostic 		  delay = DELAY_LOAD;
105661768Sbostic 		  ret = "mtc1\t%.,%0";
105761768Sbostic 		}
105861768Sbostic 	    }
105961768Sbostic 
106061768Sbostic 	  else
106161768Sbostic 	    {
106261768Sbostic 	      delay = DELAY_LOAD;
106361768Sbostic 	      ret = "li.s\t%0,%1";
106461768Sbostic 	    }
106561768Sbostic 	}
106661768Sbostic 
106761768Sbostic       else if (code1 == LABEL_REF)
106861768Sbostic 	{
106961768Sbostic 	  if (TARGET_STATS)
107061768Sbostic 	    mips_count_memory_refs (op1, 1);
107161768Sbostic 
107261768Sbostic 	  ret = "la\t%0,%a1";
107361768Sbostic 	}
107461768Sbostic 
107561768Sbostic       else if (code1 == SYMBOL_REF || code1 == CONST)
107661768Sbostic 	{
107761768Sbostic 	  if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1))
107861768Sbostic 	    {
107961768Sbostic 	      rtx offset = const0_rtx;
108061768Sbostic 
108161768Sbostic 	      if (GET_CODE (op1) == CONST)
108261768Sbostic 		op1 = eliminate_constant_term (XEXP (op1, 0), &offset);
108361768Sbostic 
108461768Sbostic 	      if (GET_CODE (op1) == SYMBOL_REF)
108561768Sbostic 		{
108661768Sbostic 		  operands[2] = HALF_PIC_PTR (op1);
108761768Sbostic 
108861768Sbostic 		  if (TARGET_STATS)
108961768Sbostic 		    mips_count_memory_refs (operands[2], 1);
109061768Sbostic 
109161768Sbostic 		  if (INTVAL (offset) == 0)
109261768Sbostic 		    {
109361768Sbostic 		      delay = DELAY_LOAD;
109461768Sbostic 		      ret = "lw\t%0,%2";
109561768Sbostic 		    }
109661768Sbostic 		  else
109761768Sbostic 		    {
109861768Sbostic 		      dslots_load_total++;
109961768Sbostic 		      operands[3] = offset;
110061768Sbostic 		      ret = (SMALL_INT (offset))
110161768Sbostic 				? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
110261768Sbostic 				: "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]";
110361768Sbostic 		    }
110461768Sbostic 		}
110561768Sbostic 	    }
110661768Sbostic 	  else
110761768Sbostic 	    {
110861768Sbostic 	      if (TARGET_STATS)
110961768Sbostic 		mips_count_memory_refs (op1, 1);
111061768Sbostic 
111161768Sbostic 	      ret = "la\t%0,%a1";
111261768Sbostic 	    }
111361768Sbostic 	}
111461768Sbostic 
111561768Sbostic       else if (code1 == PLUS)
111661768Sbostic 	{
111761768Sbostic 	  rtx add_op0 = XEXP (op1, 0);
111861768Sbostic 	  rtx add_op1 = XEXP (op1, 1);
111961768Sbostic 
112061768Sbostic 	  if (GET_CODE (XEXP (op1, 1)) == REG && GET_CODE (XEXP (op1, 0)) == CONST_INT)
112161768Sbostic 	    {
112261768Sbostic 	      add_op0 = XEXP (op1, 1);		/* reverse operands */
112361768Sbostic 	      add_op1 = XEXP (op1, 0);
112461768Sbostic 	    }
112561768Sbostic 
112661768Sbostic 	  operands[2] = add_op0;
112761768Sbostic 	  operands[3] = add_op1;
112861768Sbostic 	  ret = "add%:\t%0,%2,%3";
112961768Sbostic 	}
113061768Sbostic     }
113161768Sbostic 
113261768Sbostic   else if (code0 == MEM)
113361768Sbostic     {
113461768Sbostic       if (TARGET_STATS)
113561768Sbostic 	mips_count_memory_refs (op0, 1);
113661768Sbostic 
113761768Sbostic       if (code1 == REG)
113861768Sbostic 	{
113961768Sbostic 	  int regno1 = REGNO (op1) + subreg_word1;
114061768Sbostic 
114161768Sbostic 	  if (GP_REG_P (regno1))
114261768Sbostic 	    {
114361768Sbostic 	      switch (mode)
114461768Sbostic 		{
114561768Sbostic 		default: break;
114661768Sbostic 		case SFmode: ret = "sw\t%1,%0"; break;
114761768Sbostic 		case SImode: ret = "sw\t%1,%0"; break;
114861768Sbostic 		case HImode: ret = "sh\t%1,%0"; break;
114961768Sbostic 		case QImode: ret = "sb\t%1,%0"; break;
115061768Sbostic 		}
115161768Sbostic 	    }
115261768Sbostic 
115361768Sbostic 	  else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
115461768Sbostic 	    ret = "s.s\t%1,%0";
115561768Sbostic 	}
115661768Sbostic 
115761768Sbostic       else if (code1 == CONST_INT && INTVAL (op1) == 0)
115861768Sbostic 	{
115961768Sbostic 	  switch (mode)
116061768Sbostic 	    {
116161768Sbostic 	    default: break;
116261768Sbostic 	    case SFmode: ret = "sw\t%z1,%0"; break;
116361768Sbostic 	    case SImode: ret = "sw\t%z1,%0"; break;
116461768Sbostic 	    case HImode: ret = "sh\t%z1,%0"; break;
116561768Sbostic 	    case QImode: ret = "sb\t%z1,%0"; break;
116661768Sbostic 	    }
116761768Sbostic 	}
116861768Sbostic 
116961768Sbostic       else if (code1 == CONST_DOUBLE && CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) == 0)
117061768Sbostic 	{
117161768Sbostic 	  switch (mode)
117261768Sbostic 	    {
117361768Sbostic 	    default: break;
117461768Sbostic 	    case SFmode: ret = "sw\t%.,%0"; break;
117561768Sbostic 	    case SImode: ret = "sw\t%.,%0"; break;
117661768Sbostic 	    case HImode: ret = "sh\t%.,%0"; break;
117761768Sbostic 	    case QImode: ret = "sb\t%.,%0"; break;
117861768Sbostic 	    }
117961768Sbostic 	}
118061768Sbostic 
118161768Sbostic       if (ret != (char *)0 && MEM_VOLATILE_P (op0))
118261768Sbostic 	{
118361768Sbostic 	  int i = strlen (ret);
118461768Sbostic 	  if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
118561768Sbostic 	    abort ();
118661768Sbostic 
118761768Sbostic 	  sprintf (volatile_buffer, "%%{%s%%}", ret);
118861768Sbostic 	  ret = volatile_buffer;
118961768Sbostic 	}
119061768Sbostic     }
119161768Sbostic 
119261768Sbostic   if (ret == (char *)0)
119361768Sbostic     {
119461768Sbostic       abort_with_insn (insn, "Bad move");
119561768Sbostic       return 0;
119661768Sbostic     }
119761768Sbostic 
119861768Sbostic   if (delay != DELAY_NONE)
119961768Sbostic     return mips_fill_delay_slot (ret, delay, operands, insn);
120061768Sbostic 
120161768Sbostic   return ret;
120261768Sbostic }
120361768Sbostic 
120461768Sbostic 
120561768Sbostic /* Return the appropriate instructions to move 2 words */
120661768Sbostic 
120761768Sbostic char *
mips_move_2words(operands,insn)120861768Sbostic mips_move_2words (operands, insn)
120961768Sbostic      rtx operands[];
121061768Sbostic      rtx insn;
121161768Sbostic {
121261768Sbostic   char *ret = 0;
121361768Sbostic   rtx op0 = operands[0];
121461768Sbostic   rtx op1 = operands[1];
121561768Sbostic   enum rtx_code code0 = GET_CODE (operands[0]);
121661768Sbostic   enum rtx_code code1 = GET_CODE (operands[1]);
121761768Sbostic   int subreg_word0 = 0;
121861768Sbostic   int subreg_word1 = 0;
121961768Sbostic   enum delay_type delay = DELAY_NONE;
122061768Sbostic 
122161768Sbostic   while (code0 == SUBREG)
122261768Sbostic     {
122361768Sbostic       subreg_word0 += SUBREG_WORD (op0);
122461768Sbostic       op0 = SUBREG_REG (op0);
122561768Sbostic       code0 = GET_CODE (op0);
122661768Sbostic     }
122761768Sbostic 
122861768Sbostic   while (code1 == SUBREG)
122961768Sbostic     {
123061768Sbostic       subreg_word1 += SUBREG_WORD (op1);
123161768Sbostic       op1 = SUBREG_REG (op1);
123261768Sbostic       code1 = GET_CODE (op1);
123361768Sbostic     }
123461768Sbostic 
123561768Sbostic   if (code0 == REG)
123661768Sbostic     {
123761768Sbostic       int regno0 = REGNO (op0) + subreg_word0;
123861768Sbostic 
123961768Sbostic       if (code1 == REG)
124061768Sbostic 	{
124161768Sbostic 	  int regno1 = REGNO (op1) + subreg_word1;
124261768Sbostic 
124361768Sbostic 	  /* Just in case, don't do anything for assigning a register
124461768Sbostic 	     to itself, unless we are filling a delay slot.  */
124561768Sbostic 	  if (regno0 == regno1 && set_nomacro == 0)
124661768Sbostic 	    ret = "";
124761768Sbostic 
124861768Sbostic 	  else if (FP_REG_P (regno0))
124961768Sbostic 	    {
125061768Sbostic 	      if (FP_REG_P (regno1))
125161768Sbostic 		ret = "mov.d\t%0,%1";
125261768Sbostic 
125361768Sbostic 	      else
125461768Sbostic 		{
125561768Sbostic 		  delay = DELAY_LOAD;
125661768Sbostic 		  ret = (TARGET_FLOAT64)
125761768Sbostic 				? "dmtc1\t%1,%0"
125861768Sbostic 				: "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
125961768Sbostic 		}
126061768Sbostic 	    }
126161768Sbostic 
126261768Sbostic 	  else if (FP_REG_P (regno1))
126361768Sbostic 	    {
126461768Sbostic 	      delay = DELAY_LOAD;
126561768Sbostic 	      ret = (TARGET_FLOAT64)
126661768Sbostic 			? "dmfc1\t%0,%1"
126761768Sbostic 			: "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
126861768Sbostic 	    }
126961768Sbostic 
127061768Sbostic 	  else if (MD_REG_P (regno0) && GP_REG_P (regno1))
127161768Sbostic 	    {
127261768Sbostic 	      delay = DELAY_HILO;
127361768Sbostic 	      ret = "mthi\t%M1\n\tmtlo\t%L1";
127461768Sbostic 	    }
127561768Sbostic 
127661768Sbostic 	  else if (GP_REG_P (regno0) && MD_REG_P (regno1))
127761768Sbostic 	    {
127861768Sbostic 	      delay = DELAY_HILO;
127961768Sbostic 	      ret = "mfhi\t%M0\n\tmflo\t%L0";
128061768Sbostic 	    }
128161768Sbostic 
128261768Sbostic 	  else if (regno0 != (regno1+1))
128361768Sbostic 	    ret = "move\t%0,%1\n\tmove\t%D0,%D1";
128461768Sbostic 
128561768Sbostic 	  else
128661768Sbostic 	    ret = "move\t%D0,%D1\n\tmove\t%0,%1";
128761768Sbostic 	}
128861768Sbostic 
128961768Sbostic       else if (code1 == CONST_DOUBLE)
129061768Sbostic 	{
129161768Sbostic 	  if (CONST_DOUBLE_HIGH (op1) != 0 || CONST_DOUBLE_LOW (op1) != 0)
129261768Sbostic 	    {
129361768Sbostic 	      if (GET_MODE (op1) == DFmode)
129461768Sbostic 		{
129561768Sbostic 		  delay = DELAY_LOAD;
129661768Sbostic 		  ret = "li.d\t%0,%1";
129761768Sbostic 		}
129861768Sbostic 
129961768Sbostic 	      else
130061768Sbostic 		{
130161768Sbostic 		  operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1));
130261768Sbostic 		  operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1));
130361768Sbostic 		  ret = "li\t%M0,%3\n\tli\t%L0,%2";
130461768Sbostic 		}
130561768Sbostic 	    }
130661768Sbostic 
130761768Sbostic 	  else
130861768Sbostic 	    {
130961768Sbostic 	      if (GP_REG_P (regno0))
131061768Sbostic 		ret = "move\t%0,%.\n\tmove\t%D0,%.";
131161768Sbostic 
131261768Sbostic 	      else if (FP_REG_P (regno0))
131361768Sbostic 		{
131461768Sbostic 		  delay = DELAY_LOAD;
131561768Sbostic 		  ret = (TARGET_FLOAT64)
131661768Sbostic 				? "dmtc1\t%.,%0"
131761768Sbostic 				: "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
131861768Sbostic 		}
131961768Sbostic 	    }
132061768Sbostic 	}
132161768Sbostic 
132261768Sbostic       else if (code1 == CONST_INT && INTVAL (op1) == 0)
132361768Sbostic 	{
132461768Sbostic 	  if (GP_REG_P (regno0))
132561768Sbostic 	    ret = "move\t%0,%.\n\tmove\t%D0,%.";
132661768Sbostic 
132761768Sbostic 	  else if (FP_REG_P (regno0))
132861768Sbostic 	    {
132961768Sbostic 	      delay = DELAY_LOAD;
133061768Sbostic 	      ret = (TARGET_FLOAT64)
133161768Sbostic 				? "dmtc1\t%.,%0"
133261768Sbostic 				: "mtc1\t%.,%0\n\tmtc1\t%.,%D0";
133361768Sbostic 	    }
133461768Sbostic 	}
133561768Sbostic 
133661768Sbostic       else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0))
133761768Sbostic 	{
133861768Sbostic 	  operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
133961768Sbostic 	  ret = "li\t%M0,%2\n\tli\t%L0,%1";
134061768Sbostic 	}
134161768Sbostic 
134261768Sbostic       else if (code1 == MEM)
134361768Sbostic 	{
134461768Sbostic 	  delay = DELAY_LOAD;
134561768Sbostic 
134661768Sbostic 	  if (TARGET_STATS)
134761768Sbostic 	    mips_count_memory_refs (op1, 2);
134861768Sbostic 
134961768Sbostic 	  if (FP_REG_P (regno0))
135061768Sbostic 	    ret = "l.d\t%0,%1";
135161768Sbostic 
135261768Sbostic 	  else if (offsettable_address_p (1, DFmode, XEXP (op1, 0)))
135361768Sbostic 	    {
135461768Sbostic 	      operands[2] = adj_offsettable_operand (op1, 4);
135561768Sbostic 	      if (reg_mentioned_p (op0, op1))
135661768Sbostic 		ret = "lw\t%D0,%2\n\tlw\t%0,%1";
135761768Sbostic 	      else
135861768Sbostic 		ret = "lw\t%0,%1\n\tlw\t%D0,%2";
135961768Sbostic 	    }
136061768Sbostic 
136161768Sbostic 	  if (ret != (char *)0 && MEM_VOLATILE_P (op1))
136261768Sbostic 	    {
136361768Sbostic 	      int i = strlen (ret);
136461768Sbostic 	      if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
136561768Sbostic 		abort ();
136661768Sbostic 
136761768Sbostic 	      sprintf (volatile_buffer, "%%{%s%%}", ret);
136861768Sbostic 	      ret = volatile_buffer;
136961768Sbostic 	    }
137061768Sbostic 	}
137161768Sbostic     }
137261768Sbostic 
137361768Sbostic   else if (code0 == MEM)
137461768Sbostic     {
137561768Sbostic       if (code1 == REG)
137661768Sbostic 	{
137761768Sbostic 	  int regno1 = REGNO (op1) + subreg_word1;
137861768Sbostic 
137961768Sbostic 	  if (FP_REG_P (regno1))
138061768Sbostic 	    ret = "s.d\t%1,%0";
138161768Sbostic 
138261768Sbostic 	  else if (offsettable_address_p (1, DFmode, XEXP (op0, 0)))
138361768Sbostic 	    {
138461768Sbostic 	      operands[2] = adj_offsettable_operand (op0, 4);
138561768Sbostic 	      ret = "sw\t%1,%0\n\tsw\t%D1,%2";
138661768Sbostic 	    }
138761768Sbostic 	}
138861768Sbostic 
138961768Sbostic       else if (code1 == CONST_DOUBLE
139061768Sbostic 	       && CONST_DOUBLE_HIGH (op1) == 0
139161768Sbostic 	       && CONST_DOUBLE_LOW (op1) == 0
139261768Sbostic 	       && offsettable_address_p (1, DFmode, XEXP (op0, 0)))
139361768Sbostic 	{
139461768Sbostic 	  if (TARGET_FLOAT64)
139561768Sbostic 	    ret = "sd\t%.,%0";
139661768Sbostic 	  else
139761768Sbostic 	    {
139861768Sbostic 	      operands[2] = adj_offsettable_operand (op0, 4);
139961768Sbostic 	      ret = "sw\t%.,%0\n\tsw\t%.,%2";
140061768Sbostic 	    }
140161768Sbostic 	}
140261768Sbostic 
140361768Sbostic       if (TARGET_STATS)
140461768Sbostic 	mips_count_memory_refs (op0, 2);
140561768Sbostic 
140661768Sbostic       if (ret != (char *)0 && MEM_VOLATILE_P (op0))
140761768Sbostic 	{
140861768Sbostic 	  int i = strlen (ret);
140961768Sbostic 	  if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
141061768Sbostic 	    abort ();
141161768Sbostic 
141261768Sbostic 	  sprintf (volatile_buffer, "%%{%s%%}", ret);
141361768Sbostic 	  ret = volatile_buffer;
141461768Sbostic 	}
141561768Sbostic     }
141661768Sbostic 
141761768Sbostic   if (ret == (char *)0)
141861768Sbostic     {
141961768Sbostic       abort_with_insn (insn, "Bad move");
142061768Sbostic       return 0;
142161768Sbostic     }
142261768Sbostic 
142361768Sbostic   if (delay != DELAY_NONE)
142461768Sbostic     return mips_fill_delay_slot (ret, delay, operands, insn);
142561768Sbostic 
142661768Sbostic   return ret;
142761768Sbostic }
142861768Sbostic 
142961768Sbostic 
143061768Sbostic /* Provide the costs of an addressing mode that contains ADDR.
143161768Sbostic    If ADDR is not a valid address, its cost is irrelevant.  */
143261768Sbostic 
143361768Sbostic int
mips_address_cost(addr)143461768Sbostic mips_address_cost (addr)
143561768Sbostic      rtx addr;
143661768Sbostic {
143761768Sbostic   switch (GET_CODE (addr))
143861768Sbostic     {
143961768Sbostic     default:
144061768Sbostic       break;
144161768Sbostic 
144261768Sbostic     case LO_SUM:
144361768Sbostic     case HIGH:
144461768Sbostic       return 1;
144561768Sbostic 
144661768Sbostic     case LABEL_REF:
144761768Sbostic       return 2;
144861768Sbostic 
144961768Sbostic     case CONST:
145061768Sbostic       {
145161768Sbostic 	rtx offset = const0_rtx;
145261768Sbostic 	addr = eliminate_constant_term (addr, &offset);
145361768Sbostic 	if (GET_CODE (addr) == LABEL_REF)
145461768Sbostic 	  return 2;
145561768Sbostic 
145661768Sbostic 	if (GET_CODE (addr) != SYMBOL_REF)
145761768Sbostic 	  return 4;
145861768Sbostic 
145961768Sbostic 	if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767)
146061768Sbostic 	  return 2;
146161768Sbostic       }
146261768Sbostic       /* fall through */
146361768Sbostic 
146461768Sbostic     case SYMBOL_REF:
146561768Sbostic       return SYMBOL_REF_FLAG (addr) ? 1 : 2;
146661768Sbostic 
146761768Sbostic     case PLUS:
146861768Sbostic       {
146961768Sbostic 	register rtx plus0 = XEXP (addr, 0);
147061768Sbostic 	register rtx plus1 = XEXP (addr, 1);
147161768Sbostic 
147261768Sbostic 	if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
147361768Sbostic 	  {
147461768Sbostic 	    plus0 = XEXP (addr, 1);
147561768Sbostic 	    plus1 = XEXP (addr, 0);
147661768Sbostic 	  }
147761768Sbostic 
147861768Sbostic 	if (GET_CODE (plus0) != REG)
147961768Sbostic 	  break;
148061768Sbostic 
148161768Sbostic 	switch (GET_CODE (plus1))
148261768Sbostic 	  {
148361768Sbostic 	  default:
148461768Sbostic 	    break;
148561768Sbostic 
148661768Sbostic 	  case CONST_INT:
148761768Sbostic 	    {
148861768Sbostic 	      int value = INTVAL (plus1);
148961768Sbostic 	      return (value < -32768 || value > 32767) ? 2 : 1;
149061768Sbostic 	    }
149161768Sbostic 
149261768Sbostic 	  case CONST:
149361768Sbostic 	  case SYMBOL_REF:
149461768Sbostic 	  case LABEL_REF:
149561768Sbostic 	  case HIGH:
149661768Sbostic 	  case LO_SUM:
149761768Sbostic 	    return mips_address_cost (plus1) + 1;
149861768Sbostic 	  }
149961768Sbostic       }
150061768Sbostic     }
150161768Sbostic 
150261768Sbostic   return 4;
150361768Sbostic }
150461768Sbostic 
150561768Sbostic 
150661768Sbostic /* Make normal rtx_code into something we can index from an array */
150761768Sbostic 
150861768Sbostic static enum internal_test
map_test_to_internal_test(test_code)150961768Sbostic map_test_to_internal_test (test_code)
151061768Sbostic      enum rtx_code test_code;
151161768Sbostic {
151261768Sbostic   enum internal_test test = ITEST_MAX;
151361768Sbostic 
151461768Sbostic   switch (test_code)
151561768Sbostic     {
151661768Sbostic     default:			break;
151761768Sbostic     case EQ:  test = ITEST_EQ;  break;
151861768Sbostic     case NE:  test = ITEST_NE;  break;
151961768Sbostic     case GT:  test = ITEST_GT;  break;
152061768Sbostic     case GE:  test = ITEST_GE;  break;
152161768Sbostic     case LT:  test = ITEST_LT;  break;
152261768Sbostic     case LE:  test = ITEST_LE;  break;
152361768Sbostic     case GTU: test = ITEST_GTU; break;
152461768Sbostic     case GEU: test = ITEST_GEU; break;
152561768Sbostic     case LTU: test = ITEST_LTU; break;
152661768Sbostic     case LEU: test = ITEST_LEU; break;
152761768Sbostic     }
152861768Sbostic 
152961768Sbostic   return test;
153061768Sbostic }
153161768Sbostic 
153261768Sbostic 
153361768Sbostic /* Generate the code to compare two integer values.  The return value is:
153461768Sbostic    (reg:SI xx)		The pseudo register the comparison is in
153561768Sbostic    (rtx)0	       	No register, generate a simple branch.  */
153661768Sbostic 
153761768Sbostic rtx
gen_int_relational(test_code,result,cmp0,cmp1,p_invert)153861768Sbostic gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
153961768Sbostic      enum rtx_code test_code;	/* relational test (EQ, etc) */
154061768Sbostic      rtx result;		/* result to store comp. or 0 if branch */
154161768Sbostic      rtx cmp0;			/* first operand to compare */
154261768Sbostic      rtx cmp1;			/* second operand to compare */
154361768Sbostic      int *p_invert;		/* NULL or ptr to hold whether branch needs */
154461768Sbostic 				/* to reverse its test */
154561768Sbostic {
154661768Sbostic   struct cmp_info {
154761768Sbostic     enum rtx_code test_code;	/* code to use in instruction (LT vs. LTU) */
154861768Sbostic     int const_low;		/* low bound of constant we can accept */
154961768Sbostic     int const_high;		/* high bound of constant we can accept */
155061768Sbostic     int const_add;		/* constant to add (convert LE -> LT) */
155161768Sbostic     int reverse_regs;		/* reverse registers in test */
155261768Sbostic     int invert_const;		/* != 0 if invert value if cmp1 is constant */
155361768Sbostic     int invert_reg;		/* != 0 if invert value if cmp1 is register */
155461768Sbostic     int unsignedp;		/* != 0 for unsigned comparisons.  */
155561768Sbostic   };
155661768Sbostic 
155761768Sbostic   static struct cmp_info info[ (int)ITEST_MAX ] = {
155861768Sbostic 
155961768Sbostic     { XOR,	 0,  65535,  0,	 0,  0,	 0, 0 },	/* EQ  */
156061768Sbostic     { XOR,	 0,  65535,  0,	 0,  1,	 1, 0 },	/* NE  */
156161768Sbostic     { LT,   -32769,  32766,  1,	 1,  1,	 0, 0 },	/* GT  */
156261768Sbostic     { LT,   -32768,  32767,  0,	 0,  1,	 1, 0 },	/* GE  */
156361768Sbostic     { LT,   -32768,  32767,  0,	 0,  0,	 0, 0 },	/* LT  */
156461768Sbostic     { LT,   -32769,  32766,  1,	 1,  0,	 1, 0 },	/* LE  */
156561768Sbostic     { LTU,  -32769,  32766,  1,	 1,  1,	 0, 1 },	/* GTU */
156661768Sbostic     { LTU,  -32768,  32767,  0,	 0,  1,	 1, 1 },	/* GEU */
156761768Sbostic     { LTU,  -32768,  32767,  0,	 0,  0,	 0, 1 },	/* LTU */
156861768Sbostic     { LTU,  -32769,  32766,  1,	 1,  0,	 1, 1 },	/* LEU */
156961768Sbostic   };
157061768Sbostic 
157161768Sbostic   enum internal_test test;
157261768Sbostic   struct cmp_info *p_info;
157361768Sbostic   int branch_p;
157461768Sbostic   int eqne_p;
157561768Sbostic   int invert;
157661768Sbostic   rtx reg;
157761768Sbostic   rtx reg2;
157861768Sbostic 
157961768Sbostic   test = map_test_to_internal_test (test_code);
158061768Sbostic   if (test == ITEST_MAX)
158161768Sbostic     abort ();
158261768Sbostic 
158361768Sbostic   p_info = &info[ (int)test ];
158461768Sbostic   eqne_p = (p_info->test_code == XOR);
158561768Sbostic 
158661768Sbostic   /* Eliminate simple branches */
158761768Sbostic   branch_p = (result == (rtx)0);
158861768Sbostic   if (branch_p)
158961768Sbostic     {
159061768Sbostic       if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
159161768Sbostic 	{
159261768Sbostic 	  /* Comparisons against zero are simple branches */
159361768Sbostic 	  if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
159461768Sbostic 	    return (rtx)0;
159561768Sbostic 
159661768Sbostic 	  /* Test for beq/bne.  */
159761768Sbostic 	  if (eqne_p)
159861768Sbostic 	    return (rtx)0;
159961768Sbostic 	}
160061768Sbostic 
160161768Sbostic       /* allocate a pseudo to calculate the value in.  */
160261768Sbostic       result = gen_reg_rtx (SImode);
160361768Sbostic     }
160461768Sbostic 
160561768Sbostic   /* Make sure we can handle any constants given to us.  */
160661768Sbostic   if (GET_CODE (cmp0) == CONST_INT)
160761768Sbostic     cmp0 = force_reg (SImode, cmp0);
160861768Sbostic 
160961768Sbostic   if (GET_CODE (cmp1) == CONST_INT)
161061768Sbostic     {
161161768Sbostic       HOST_WIDE_INT value = INTVAL (cmp1);
161261768Sbostic       if (value < p_info->const_low || value > p_info->const_high)
161361768Sbostic 	cmp1 = force_reg (SImode, cmp1);
161461768Sbostic     }
161561768Sbostic 
161661768Sbostic   /* See if we need to invert the result.  */
161761768Sbostic   invert = (GET_CODE (cmp1) == CONST_INT)
161861768Sbostic 		? p_info->invert_const
161961768Sbostic 		: p_info->invert_reg;
162061768Sbostic 
162161768Sbostic   if (p_invert != (int *)0)
162261768Sbostic     {
162361768Sbostic       *p_invert = invert;
162461768Sbostic       invert = FALSE;
162561768Sbostic     }
162661768Sbostic 
162761768Sbostic   /* Comparison to constants, may involve adding 1 to change a LT into LE.
162861768Sbostic      Comparison between two registers, may involve switching operands.  */
162961768Sbostic   if (GET_CODE (cmp1) == CONST_INT)
163061768Sbostic     {
163161768Sbostic       if (p_info->const_add != 0)
163261768Sbostic 	{
163361768Sbostic 	  HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
163461768Sbostic 	  /* If modification of cmp1 caused overflow,
163561768Sbostic 	     we would get the wrong answer if we follow the usual path;
163661768Sbostic 	     thus, x > 0xffffffffu would turn into x > 0u.  */
163761768Sbostic 	  if ((p_info->unsignedp
163861768Sbostic 	       ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1)
163961768Sbostic 	       : new > INTVAL (cmp1))
164061768Sbostic 	      != (p_info->const_add > 0))
164161768Sbostic 	    /* 1 is the right value in the LE and LEU case.
164261768Sbostic 	       In the GT and GTU case, *p_invert is already set,
164361768Sbostic 	       so this is effectively 0.  */
164461768Sbostic 	    return force_reg (SImode, const1_rtx);
164561768Sbostic 	  else
164661768Sbostic 	    cmp1 = GEN_INT (new);
164761768Sbostic 	}
164861768Sbostic     }
164961768Sbostic   else if (p_info->reverse_regs)
165061768Sbostic     {
165161768Sbostic       rtx temp = cmp0;
165261768Sbostic       cmp0 = cmp1;
165361768Sbostic       cmp1 = temp;
165461768Sbostic     }
165561768Sbostic 
165661768Sbostic   if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
165761768Sbostic     reg = cmp0;
165861768Sbostic   else
165961768Sbostic     {
166061768Sbostic       reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result;
166161768Sbostic       emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1));
166261768Sbostic     }
166361768Sbostic 
166461768Sbostic   if (test == ITEST_NE)
166561768Sbostic     {
166661768Sbostic       emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx));
166761768Sbostic       invert = FALSE;
166861768Sbostic     }
166961768Sbostic 
167061768Sbostic   else if (test == ITEST_EQ)
167161768Sbostic     {
167261768Sbostic       reg2 = (invert) ? gen_reg_rtx (SImode) : result;
167361768Sbostic       emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx));
167461768Sbostic       reg = reg2;
167561768Sbostic     }
167661768Sbostic 
167761768Sbostic   if (invert)
167861768Sbostic     emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx));
167961768Sbostic 
168061768Sbostic   return result;
168161768Sbostic }
168261768Sbostic 
168361768Sbostic 
168461768Sbostic /* Emit the common code for doing conditional branches.
168561768Sbostic    operand[0] is the label to jump to.
168661768Sbostic    The comparison operands are saved away by cmp{si,sf,df}.  */
168761768Sbostic 
168861768Sbostic void
gen_conditional_branch(operands,test_code)168961768Sbostic gen_conditional_branch (operands, test_code)
169061768Sbostic      rtx operands[];
169161768Sbostic      enum rtx_code test_code;
169261768Sbostic {
169361768Sbostic   static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = {
169461768Sbostic     {				/* CMP_SI */
169561768Sbostic       SImode,			/* eq  */
169661768Sbostic       SImode,			/* ne  */
169761768Sbostic       SImode,			/* gt  */
169861768Sbostic       SImode,			/* ge  */
169961768Sbostic       SImode,			/* lt  */
170061768Sbostic       SImode,			/* le  */
170161768Sbostic       SImode,			/* gtu */
170261768Sbostic       SImode,			/* geu */
170361768Sbostic       SImode,			/* ltu */
170461768Sbostic       SImode,			/* leu */
170561768Sbostic     },
170661768Sbostic     {				/* CMP_SF */
170761768Sbostic       CC_FPmode,		/* eq  */
170861768Sbostic       CC_REV_FPmode,		/* ne  */
170961768Sbostic       CC_FPmode,		/* gt  */
171061768Sbostic       CC_FPmode,		/* ge  */
171161768Sbostic       CC_FPmode,		/* lt  */
171261768Sbostic       CC_FPmode,		/* le  */
171361768Sbostic       VOIDmode,			/* gtu */
171461768Sbostic       VOIDmode,			/* geu */
171561768Sbostic       VOIDmode,			/* ltu */
171661768Sbostic       VOIDmode,			/* leu */
171761768Sbostic     },
171861768Sbostic     {				/* CMP_DF */
171961768Sbostic       CC_FPmode,		/* eq  */
172061768Sbostic       CC_REV_FPmode,		/* ne  */
172161768Sbostic       CC_FPmode,		/* gt  */
172261768Sbostic       CC_FPmode,		/* ge  */
172361768Sbostic       CC_FPmode,		/* lt  */
172461768Sbostic       CC_FPmode,		/* le  */
172561768Sbostic       VOIDmode,			/* gtu */
172661768Sbostic       VOIDmode,			/* geu */
172761768Sbostic       VOIDmode,			/* ltu */
172861768Sbostic       VOIDmode,			/* leu */
172961768Sbostic     },
173061768Sbostic   };
173161768Sbostic 
173261768Sbostic   enum machine_mode mode;
173361768Sbostic   enum cmp_type type	  = branch_type;
173461768Sbostic   rtx cmp0		  = branch_cmp[0];
173561768Sbostic   rtx cmp1		  = branch_cmp[1];
173661768Sbostic   rtx label1		  = gen_rtx (LABEL_REF, VOIDmode, operands[0]);
173761768Sbostic   rtx label2		  = pc_rtx;
173861768Sbostic   rtx reg		  = (rtx)0;
173961768Sbostic   int invert		  = 0;
174061768Sbostic   enum internal_test test = map_test_to_internal_test (test_code);
174161768Sbostic 
174261768Sbostic   if (test == ITEST_MAX)
174361768Sbostic     {
174461768Sbostic       mode = SImode;
174561768Sbostic       goto fail;
174661768Sbostic     }
174761768Sbostic 
174861768Sbostic   /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode).  */
174961768Sbostic   mode = mode_map[(int)type][(int)test];
175061768Sbostic   if (mode == VOIDmode)
175161768Sbostic     goto fail;
175261768Sbostic 
175361768Sbostic   switch (branch_type)
175461768Sbostic     {
175561768Sbostic     default:
175661768Sbostic       goto fail;
175761768Sbostic 
175861768Sbostic     case CMP_SI:
175961768Sbostic       reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert);
176061768Sbostic       if (reg != (rtx)0)
176161768Sbostic 	{
176261768Sbostic 	  cmp0 = reg;
176361768Sbostic 	  cmp1 = const0_rtx;
176461768Sbostic 	  test_code = NE;
176561768Sbostic 	}
176661768Sbostic 
176761768Sbostic       /* Make sure not non-zero constant if ==/!= */
176861768Sbostic       else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
176961768Sbostic 	cmp1 = force_reg (SImode, cmp1);
177061768Sbostic 
177161768Sbostic       break;
177261768Sbostic 
177361768Sbostic     case CMP_DF:
177461768Sbostic     case CMP_SF:
177561768Sbostic       {
177661768Sbostic 	rtx reg = gen_rtx (REG, mode, FPSW_REGNUM);
177761768Sbostic 	emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1)));
177861768Sbostic 	cmp0 = reg;
177961768Sbostic 	cmp1 = const0_rtx;
178061768Sbostic 	test_code = NE;
178161768Sbostic       }
178261768Sbostic       break;
178361768Sbostic     }
178461768Sbostic 
178561768Sbostic   /* Generate the jump */
178661768Sbostic   if (invert)
178761768Sbostic     {
178861768Sbostic       label2 = label1;
178961768Sbostic       label1 = pc_rtx;
179061768Sbostic     }
179161768Sbostic 
179261768Sbostic   emit_jump_insn (gen_rtx (SET, VOIDmode,
179361768Sbostic 			   pc_rtx,
179461768Sbostic 			   gen_rtx (IF_THEN_ELSE, VOIDmode,
179561768Sbostic 				    gen_rtx (test_code, mode, cmp0, cmp1),
179661768Sbostic 				    label1,
179761768Sbostic 				    label2)));
179861768Sbostic 
179961768Sbostic   return;
180061768Sbostic 
180161768Sbostic fail:
180261768Sbostic   abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test");
180361768Sbostic }
180461768Sbostic 
180561768Sbostic 
180661768Sbostic #define UNITS_PER_SHORT (SHORT_TYPE_SIZE / BITS_PER_UNIT)
180761768Sbostic 
180861768Sbostic /* Internal code to generate the load and store of one word/short/byte.
180961768Sbostic    The load is emitted directly, and the store insn is returned.  */
181061768Sbostic 
181161768Sbostic #if 0
181261768Sbostic static rtx
181361768Sbostic block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src)
181461768Sbostic      rtx src_reg;		/* register holding source memory address */
181561768Sbostic      rtx dest_reg;		/* register holding dest. memory address */
181661768Sbostic      int *p_bytes;		/* pointer to # bytes remaining */
181761768Sbostic      int *p_offset;		/* pointer to current offset */
181861768Sbostic      int align;			/* alignment */
181961768Sbostic      rtx orig_src;		/* original source for making a reg note */
182061768Sbostic {
182161768Sbostic   int bytes;			/* # bytes remaining */
182261768Sbostic   int offset;			/* offset to use */
182361768Sbostic   int size;			/* size in bytes of load/store */
182461768Sbostic   enum machine_mode mode;	/* mode to use for load/store */
182561768Sbostic   rtx reg;			/* temporary register */
182661768Sbostic   rtx src_addr;			/* source address */
182761768Sbostic   rtx dest_addr;		/* destination address */
182861768Sbostic   rtx insn;			/* insn of the load */
182961768Sbostic   rtx orig_src_addr;		/* original source address */
183061768Sbostic   rtx (*load_func)();		/* function to generate load insn */
183161768Sbostic   rtx (*store_func)();		/* function to generate destination insn */
183261768Sbostic 
183361768Sbostic   bytes = *p_bytes;
183461768Sbostic   if (bytes <= 0 || align <= 0)
183561768Sbostic     abort ();
183661768Sbostic 
183761768Sbostic   if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
183861768Sbostic     {
183961768Sbostic       mode = SImode;
184061768Sbostic       size = UNITS_PER_WORD;
184161768Sbostic       load_func = gen_movsi;
184261768Sbostic       store_func = gen_movsi;
184361768Sbostic     }
184461768Sbostic 
184561768Sbostic #if 0
184661768Sbostic   /* Don't generate unligned moves here, rather defer those to the
184761768Sbostic      general movestrsi_internal pattern.  */
184861768Sbostic   else if (bytes >= UNITS_PER_WORD)
184961768Sbostic     {
185061768Sbostic       mode = SImode;
185161768Sbostic       size = UNITS_PER_WORD;
185261768Sbostic       load_func = gen_movsi_ulw;
185361768Sbostic       store_func = gen_movsi_usw;
185461768Sbostic     }
185561768Sbostic #endif
185661768Sbostic 
185761768Sbostic   else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
185861768Sbostic     {
185961768Sbostic       mode = HImode;
186061768Sbostic       size = UNITS_PER_SHORT;
186161768Sbostic       load_func = gen_movhi;
186261768Sbostic       store_func = gen_movhi;
186361768Sbostic     }
186461768Sbostic 
186561768Sbostic   else
186661768Sbostic     {
186761768Sbostic       mode = QImode;
186861768Sbostic       size = 1;
186961768Sbostic       load_func = gen_movqi;
187061768Sbostic       store_func = gen_movqi;
187161768Sbostic     }
187261768Sbostic 
187361768Sbostic   offset = *p_offset;
187461768Sbostic   *p_offset = offset + size;
187561768Sbostic   *p_bytes = bytes - size;
187661768Sbostic 
187761768Sbostic   if (offset == 0)
187861768Sbostic     {
187961768Sbostic       src_addr  = src_reg;
188061768Sbostic       dest_addr = dest_reg;
188161768Sbostic     }
188261768Sbostic   else
188361768Sbostic     {
188461768Sbostic       src_addr  = gen_rtx (PLUS, Pmode, src_reg,  GEN_INT (offset));
188561768Sbostic       dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset));
188661768Sbostic     }
188761768Sbostic 
188861768Sbostic   reg = gen_reg_rtx (mode);
188961768Sbostic   insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr)));
189061768Sbostic   orig_src_addr = XEXP (orig_src, 0);
189161768Sbostic   if (CONSTANT_P (orig_src_addr))
189261768Sbostic     REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV,
189361768Sbostic 				plus_constant (orig_src_addr, offset),
189461768Sbostic 				REG_NOTES (insn));
189561768Sbostic 
189661768Sbostic   return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg);
189761768Sbostic }
189861768Sbostic #endif
189961768Sbostic 
190061768Sbostic 
190161768Sbostic /* Write a series of loads/stores to move some bytes.  Generate load/stores as follows:
190261768Sbostic 
190361768Sbostic    load  1
190461768Sbostic    load  2
190561768Sbostic    load  3
190661768Sbostic    store 1
190761768Sbostic    load  4
190861768Sbostic    store 2
190961768Sbostic    load  5
191061768Sbostic    store 3
191161768Sbostic    ...
191261768Sbostic 
191361768Sbostic    This way, no NOP's are needed, except at the end, and only
191461768Sbostic    two temp registers are needed.  Two delay slots are used
191561768Sbostic    in deference to the R4000.  */
191661768Sbostic 
191761768Sbostic #if 0
191861768Sbostic static void
191961768Sbostic block_move_sequence (dest_reg, src_reg, bytes, align, orig_src)
192061768Sbostic      rtx dest_reg;		/* register holding destination address */
192161768Sbostic      rtx src_reg;		/* register holding source address */
192261768Sbostic      int bytes;			/* # bytes to move */
192361768Sbostic      int align;			/* max alignment to assume */
192461768Sbostic      rtx orig_src;		/* original source for making a reg note */
192561768Sbostic {
192661768Sbostic   int offset		= 0;
192761768Sbostic   rtx prev2_store	= (rtx)0;
192861768Sbostic   rtx prev_store	= (rtx)0;
192961768Sbostic   rtx cur_store		= (rtx)0;
193061768Sbostic 
193161768Sbostic   while (bytes > 0)
193261768Sbostic     {
193361768Sbostic       /* Is there a store to do? */
193461768Sbostic       if (prev2_store)
193561768Sbostic 	emit_insn (prev2_store);
193661768Sbostic 
193761768Sbostic       prev2_store = prev_store;
193861768Sbostic       prev_store = cur_store;
193961768Sbostic       cur_store = block_move_load_store (dest_reg, src_reg,
194061768Sbostic 					 &bytes, &offset,
194161768Sbostic 					 align, orig_src);
194261768Sbostic     }
194361768Sbostic 
194461768Sbostic   /* Finish up last three stores.  */
194561768Sbostic   if (prev2_store)
194661768Sbostic     emit_insn (prev2_store);
194761768Sbostic 
194861768Sbostic   if (prev_store)
194961768Sbostic     emit_insn (prev_store);
195061768Sbostic 
195161768Sbostic   if (cur_store)
195261768Sbostic     emit_insn (cur_store);
195361768Sbostic }
195461768Sbostic #endif
195561768Sbostic 
195661768Sbostic 
195761768Sbostic /* Write a loop to move a constant number of bytes.  Generate load/stores as follows:
195861768Sbostic 
195961768Sbostic    do {
196061768Sbostic      temp1 = src[0];
196161768Sbostic      temp2 = src[1];
196261768Sbostic      ...
196361768Sbostic      temp<last> = src[MAX_MOVE_REGS-1];
196461768Sbostic      dest[0] = temp1;
196561768Sbostic      dest[1] = temp2;
196661768Sbostic      ...
196761768Sbostic      dest[MAX_MOVE_REGS-1] = temp<last>;
196861768Sbostic      src += MAX_MOVE_REGS;
196961768Sbostic      dest += MAX_MOVE_REGS;
197061768Sbostic    } while (src != final);
197161768Sbostic 
197261768Sbostic    This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp
197361768Sbostic    registers are needed.
197461768Sbostic 
197561768Sbostic    Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3
197661768Sbostic    cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every
197761768Sbostic    (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses.  */
197861768Sbostic 
197961768Sbostic #define MAX_MOVE_REGS 4
198061768Sbostic #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
198161768Sbostic 
198261768Sbostic static void
block_move_loop(dest_reg,src_reg,bytes,align,orig_src)198361768Sbostic block_move_loop (dest_reg, src_reg, bytes, align, orig_src)
198461768Sbostic      rtx dest_reg;		/* register holding destination address */
198561768Sbostic      rtx src_reg;		/* register holding source address */
198661768Sbostic      int bytes;			/* # bytes to move */
198761768Sbostic      int align;			/* alignment */
198861768Sbostic      rtx orig_src;		/* original source for making a reg note */
198961768Sbostic {
199061768Sbostic   rtx dest_mem		= gen_rtx (MEM, BLKmode, dest_reg);
199161768Sbostic   rtx src_mem		= gen_rtx (MEM, BLKmode, src_reg);
199261768Sbostic   rtx align_rtx		= GEN_INT (align);
199361768Sbostic   rtx label;
199461768Sbostic   rtx final_src;
199561768Sbostic   rtx bytes_rtx;
199661768Sbostic   int leftover;
199761768Sbostic 
199861768Sbostic   if (bytes < 2*MAX_MOVE_BYTES)
199961768Sbostic     abort ();
200061768Sbostic 
200161768Sbostic   leftover = bytes % MAX_MOVE_BYTES;
200261768Sbostic   bytes -= leftover;
200361768Sbostic 
200461768Sbostic   label = gen_label_rtx ();
200561768Sbostic   final_src = gen_reg_rtx (Pmode);
200661768Sbostic   bytes_rtx = GEN_INT (bytes);
200761768Sbostic 
200861768Sbostic   if (bytes > 0x7fff)
200961768Sbostic     {
201061768Sbostic       emit_insn (gen_movsi (final_src, bytes_rtx));
201161768Sbostic       emit_insn (gen_addsi3 (final_src, final_src, src_reg));
201261768Sbostic     }
201361768Sbostic   else
201461768Sbostic     emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
201561768Sbostic 
201661768Sbostic   emit_label (label);
201761768Sbostic 
201861768Sbostic   bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
201961768Sbostic   emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
202061768Sbostic   emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
202161768Sbostic   emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
202261768Sbostic   emit_insn (gen_cmpsi (src_reg, final_src));
202361768Sbostic   emit_jump_insn (gen_bne (label));
202461768Sbostic 
202561768Sbostic   if (leftover)
202661768Sbostic     emit_insn (gen_movstrsi_internal (dest_mem, src_mem,
202761768Sbostic 				      GEN_INT (leftover),
202861768Sbostic 				      align_rtx));
202961768Sbostic }
203061768Sbostic 
203161768Sbostic 
203261768Sbostic /* Use a library function to move some bytes.  */
203361768Sbostic 
203461768Sbostic static void
block_move_call(dest_reg,src_reg,bytes_rtx)203561768Sbostic block_move_call (dest_reg, src_reg, bytes_rtx)
203661768Sbostic      rtx dest_reg;
203761768Sbostic      rtx src_reg;
203861768Sbostic      rtx bytes_rtx;
203961768Sbostic {
204061768Sbostic #ifdef TARGET_MEM_FUNCTIONS
204161768Sbostic   emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "memcpy"), 0,
204261768Sbostic 		     VOIDmode, 3,
204361768Sbostic 		     dest_reg, Pmode,
204461768Sbostic 		     src_reg, Pmode,
204561768Sbostic 		     bytes_rtx, SImode);
204661768Sbostic #else
204761768Sbostic   emit_library_call (gen_rtx (SYMBOL_REF, Pmode, "bcopy"), 0,
204861768Sbostic 		     VOIDmode, 3,
204961768Sbostic 		     src_reg, Pmode,
205061768Sbostic 		     dest_reg, Pmode,
205161768Sbostic 		     bytes_rtx, SImode);
205261768Sbostic #endif
205361768Sbostic }
205461768Sbostic 
205561768Sbostic 
205661768Sbostic /* Expand string/block move operations.
205761768Sbostic 
205861768Sbostic    operands[0] is the pointer to the destination.
205961768Sbostic    operands[1] is the pointer to the source.
206061768Sbostic    operands[2] is the number of bytes to move.
206161768Sbostic    operands[3] is the alignment.  */
206261768Sbostic 
206361768Sbostic void
expand_block_move(operands)206461768Sbostic expand_block_move (operands)
206561768Sbostic      rtx operands[];
206661768Sbostic {
206761768Sbostic   rtx bytes_rtx	= operands[2];
206861768Sbostic   rtx align_rtx = operands[3];
206961768Sbostic   int constp	= (GET_CODE (bytes_rtx) == CONST_INT);
207061768Sbostic   int bytes	= (constp ? INTVAL (bytes_rtx) : 0);
207161768Sbostic   int align	= INTVAL (align_rtx);
207261768Sbostic   rtx orig_src	= operands[1];
207361768Sbostic   rtx src_reg;
207461768Sbostic   rtx dest_reg;
207561768Sbostic 
207661768Sbostic   if (constp && bytes <= 0)
207761768Sbostic     return;
207861768Sbostic 
207961768Sbostic   if (align > UNITS_PER_WORD)
208061768Sbostic     align = UNITS_PER_WORD;
208161768Sbostic 
208261768Sbostic   /* Move the address into scratch registers.  */
208361768Sbostic   dest_reg = copy_addr_to_reg (XEXP (operands[0], 0));
208461768Sbostic   src_reg  = copy_addr_to_reg (XEXP (orig_src, 0));
208561768Sbostic 
208661768Sbostic   if (TARGET_MEMCPY)
208761768Sbostic     block_move_call (dest_reg, src_reg, bytes_rtx);
208861768Sbostic 
208961768Sbostic #if 0
209061768Sbostic   else if (constp && bytes <= 3*align)
209161768Sbostic     block_move_sequence (dest_reg, src_reg, bytes, align, orig_src);
209261768Sbostic #endif
209361768Sbostic 
209461768Sbostic   else if (constp && bytes <= 2*MAX_MOVE_BYTES)
209561768Sbostic     emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
209661768Sbostic 				      gen_rtx (MEM, BLKmode, src_reg),
209761768Sbostic 				      bytes_rtx, align_rtx));
209861768Sbostic 
209961768Sbostic   else if (constp && align >= UNITS_PER_WORD && optimize)
210061768Sbostic     block_move_loop (dest_reg, src_reg, bytes, align, orig_src);
210161768Sbostic 
210261768Sbostic   else if (constp && optimize)
210361768Sbostic     {
210461768Sbostic       /* If the alignment is not word aligned, generate a test at
210561768Sbostic 	 runtime, to see whether things wound up aligned, and we
210661768Sbostic 	 can use the faster lw/sw instead ulw/usw.  */
210761768Sbostic 
210861768Sbostic       rtx temp		= gen_reg_rtx (Pmode);
210961768Sbostic       rtx aligned_label = gen_label_rtx ();
211061768Sbostic       rtx join_label	= gen_label_rtx ();
211161768Sbostic       int leftover	= bytes % MAX_MOVE_BYTES;
211261768Sbostic 
211361768Sbostic       bytes -= leftover;
211461768Sbostic 
211561768Sbostic       emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
211661768Sbostic       emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1)));
211761768Sbostic       emit_insn (gen_cmpsi (temp, const0_rtx));
211861768Sbostic       emit_jump_insn (gen_beq (aligned_label));
211961768Sbostic 
212061768Sbostic       /* Unaligned loop.  */
212161768Sbostic       block_move_loop (dest_reg, src_reg, bytes, 1, orig_src);
212261768Sbostic       emit_jump_insn (gen_jump (join_label));
212361768Sbostic       emit_barrier ();
212461768Sbostic 
212561768Sbostic       /* Aligned loop.  */
212661768Sbostic       emit_label (aligned_label);
212761768Sbostic       block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src);
212861768Sbostic       emit_label (join_label);
212961768Sbostic 
213061768Sbostic       /* Bytes at the end of the loop.  */
213161768Sbostic       if (leftover)
213261768Sbostic 	{
213361768Sbostic #if 0
213461768Sbostic 	  if (leftover <= 3*align)
213561768Sbostic 	    block_move_sequence (dest_reg, src_reg, leftover, align, orig_src);
213661768Sbostic 
213761768Sbostic 	  else
213861768Sbostic #endif
213961768Sbostic 	    emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg),
214061768Sbostic 					      gen_rtx (MEM, BLKmode, src_reg),
214161768Sbostic 					      GEN_INT (leftover),
214261768Sbostic 					      GEN_INT (align)));
214361768Sbostic 	}
214461768Sbostic     }
214561768Sbostic 
214661768Sbostic   else
214761768Sbostic     block_move_call (dest_reg, src_reg, bytes_rtx);
214861768Sbostic }
214961768Sbostic 
215061768Sbostic 
215161768Sbostic /* Emit load/stores for a small constant block_move.
215261768Sbostic 
215361768Sbostic    operands[0] is the memory address of the destination.
215461768Sbostic    operands[1] is the memory address of the source.
215561768Sbostic    operands[2] is the number of bytes to move.
215661768Sbostic    operands[3] is the alignment.
215761768Sbostic    operands[4] is a temp register.
215861768Sbostic    operands[5] is a temp register.
215961768Sbostic    ...
216061768Sbostic    operands[3+num_regs] is the last temp register.
216161768Sbostic 
216261768Sbostic    The block move type can be one of the following:
216361768Sbostic 	BLOCK_MOVE_NORMAL	Do all of the block move.
216461768Sbostic 	BLOCK_MOVE_NOT_LAST	Do all but the last store.
216561768Sbostic 	BLOCK_MOVE_LAST		Do just the last store. */
216661768Sbostic 
216761768Sbostic char *
output_block_move(insn,operands,num_regs,move_type)216861768Sbostic output_block_move (insn, operands, num_regs, move_type)
216961768Sbostic      rtx insn;
217061768Sbostic      rtx operands[];
217161768Sbostic      int num_regs;
217261768Sbostic      enum block_move_type move_type;
217361768Sbostic {
217461768Sbostic   rtx dest_reg		= XEXP (operands[0], 0);
217561768Sbostic   rtx src_reg		= XEXP (operands[1], 0);
217661768Sbostic   int bytes		= INTVAL (operands[2]);
217761768Sbostic   int align		= INTVAL (operands[3]);
217861768Sbostic   int num		= 0;
217961768Sbostic   int offset		= 0;
218061768Sbostic   int use_lwl_lwr	= FALSE;
218161768Sbostic   int last_operand	= num_regs+4;
218261768Sbostic   int i;
218361768Sbostic   rtx xoperands[10];
218461768Sbostic 
218561768Sbostic   struct {
218661768Sbostic     char *load;			/* load insn without nop */
218761768Sbostic     char *load_nop;		/* load insn with trailing nop */
218861768Sbostic     char *store;		/* store insn */
218961768Sbostic     char *final;		/* if last_store used: NULL or swr */
219061768Sbostic     char *last_store;		/* last store instruction */
219161768Sbostic     int offset;			/* current offset */
219261768Sbostic     enum machine_mode mode;	/* mode to use on (MEM) */
219361768Sbostic   } load_store[4];
219461768Sbostic 
219561768Sbostic   /* Detect a bug in GCC, where it can give us a register
219661768Sbostic      the same as one of the addressing registers.  */
219761768Sbostic   for (i = 4; i < last_operand; i++)
219861768Sbostic     {
219961768Sbostic       if (reg_mentioned_p (operands[i], operands[0])
220061768Sbostic 	  || reg_mentioned_p (operands[i], operands[1]))
220161768Sbostic 	{
220261768Sbostic 	  abort_with_insn (insn, "register passed as address and temp register to block move");
220361768Sbostic 	}
220461768Sbostic     }
220561768Sbostic 
220661768Sbostic   /* If we are given global or static addresses, and we would be
220761768Sbostic      emitting a few instructions, try to save time by using a
220861768Sbostic      temporary register for the pointer.  */
220961768Sbostic   if (bytes > 2*align || move_type != BLOCK_MOVE_NORMAL)
221061768Sbostic     {
221161768Sbostic       if (CONSTANT_P (src_reg))
221261768Sbostic 	{
221361768Sbostic 	  if (TARGET_STATS)
221461768Sbostic 	    mips_count_memory_refs (operands[1], 1);
221561768Sbostic 
221661768Sbostic 	  src_reg = operands[ 3 + num_regs-- ];
221761768Sbostic 	  if (move_type != BLOCK_MOVE_LAST)
221861768Sbostic 	    {
221961768Sbostic 	      xoperands[1] = operands[1];
222061768Sbostic 	      xoperands[0] = src_reg;
222161768Sbostic 	      output_asm_insn ("la\t%0,%1", xoperands);
222261768Sbostic 	    }
222361768Sbostic 	}
222461768Sbostic 
222561768Sbostic       if (CONSTANT_P (dest_reg))
222661768Sbostic 	{
222761768Sbostic 	  if (TARGET_STATS)
222861768Sbostic 	    mips_count_memory_refs (operands[0], 1);
222961768Sbostic 
223061768Sbostic 	  dest_reg = operands[ 3 + num_regs-- ];
223161768Sbostic 	  if (move_type != BLOCK_MOVE_LAST)
223261768Sbostic 	    {
223361768Sbostic 	      xoperands[1] = operands[0];
223461768Sbostic 	      xoperands[0] = dest_reg;
223561768Sbostic 	      output_asm_insn ("la\t%0,%1", xoperands);
223661768Sbostic 	    }
223761768Sbostic 	}
223861768Sbostic     }
223961768Sbostic 
224061768Sbostic   if (num_regs > (sizeof (load_store) / sizeof (load_store[0])))
224161768Sbostic     num_regs = (sizeof (load_store) / sizeof (load_store[0]));
224261768Sbostic 
224361768Sbostic   else if (num_regs < 1)
224461768Sbostic     abort ();
224561768Sbostic 
224661768Sbostic   if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0)
224761768Sbostic     output_asm_insn (".set\tnoreorder", operands);
224861768Sbostic 
224961768Sbostic   while (bytes > 0)
225061768Sbostic     {
225161768Sbostic       load_store[num].offset = offset;
225261768Sbostic 
225361768Sbostic       if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD)
225461768Sbostic 	{
225561768Sbostic 	  load_store[num].load       = "lw\t%0,%1";
225661768Sbostic 	  load_store[num].load_nop   = "lw\t%0,%1%#";
225761768Sbostic 	  load_store[num].store      = "sw\t%0,%1";
225861768Sbostic 	  load_store[num].last_store = "sw\t%0,%1";
225961768Sbostic 	  load_store[num].final      = (char *)0;
226061768Sbostic 	  load_store[num].mode       = SImode;
226161768Sbostic 	  offset += UNITS_PER_WORD;
226261768Sbostic 	  bytes -= UNITS_PER_WORD;
226361768Sbostic 	}
226461768Sbostic 
226561768Sbostic       else if (bytes >= UNITS_PER_WORD)
226661768Sbostic 	{
226761768Sbostic #if BYTES_BIG_ENDIAN
226861768Sbostic 	  load_store[num].load       = "lwl\t%0,%1\n\tlwr\t%0,%2";
226961768Sbostic 	  load_store[num].load_nop   = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
227061768Sbostic 	  load_store[num].store      = "swl\t%0,%1\n\tswr\t%0,%2";
227161768Sbostic 	  load_store[num].last_store = "swr\t%0,%2";
227261768Sbostic 	  load_store[num].final      = "swl\t%0,%1";
227361768Sbostic #else
227461768Sbostic 	  load_store[num].load	     = "lwl\t%0,%2\n\tlwr\t%0,%1";
227561768Sbostic 	  load_store[num].load_nop   = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
227661768Sbostic 	  load_store[num].store	     = "swl\t%0,%2\n\tswr\t%0,%1";
227761768Sbostic 	  load_store[num].last_store = "swr\t%0,%1";
227861768Sbostic 	  load_store[num].final      = "swl\t%0,%2";
227961768Sbostic #endif
228061768Sbostic 	  load_store[num].mode = SImode;
228161768Sbostic 	  offset += UNITS_PER_WORD;
228261768Sbostic 	  bytes -= UNITS_PER_WORD;
228361768Sbostic 	  use_lwl_lwr = TRUE;
228461768Sbostic 	}
228561768Sbostic 
228661768Sbostic       else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT)
228761768Sbostic 	{
228861768Sbostic 	  load_store[num].load	     = "lh\t%0,%1";
228961768Sbostic 	  load_store[num].load_nop   = "lh\t%0,%1%#";
229061768Sbostic 	  load_store[num].store	     = "sh\t%0,%1";
229161768Sbostic 	  load_store[num].last_store = "sh\t%0,%1";
229261768Sbostic 	  load_store[num].final      = (char *)0;
229361768Sbostic 	  load_store[num].offset     = offset;
229461768Sbostic 	  load_store[num].mode	     = HImode;
229561768Sbostic 	  offset += UNITS_PER_SHORT;
229661768Sbostic 	  bytes -= UNITS_PER_SHORT;
229761768Sbostic 	}
229861768Sbostic 
229961768Sbostic       else
230061768Sbostic 	{
230161768Sbostic 	  load_store[num].load	     = "lb\t%0,%1";
230261768Sbostic 	  load_store[num].load_nop   = "lb\t%0,%1%#";
230361768Sbostic 	  load_store[num].store	     = "sb\t%0,%1";
230461768Sbostic 	  load_store[num].last_store = "sb\t%0,%1";
230561768Sbostic 	  load_store[num].final      = (char *)0;
230661768Sbostic 	  load_store[num].mode	     = QImode;
230761768Sbostic 	  offset++;
230861768Sbostic 	  bytes--;
230961768Sbostic 	}
231061768Sbostic 
231161768Sbostic       if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
231261768Sbostic 	{
231361768Sbostic 	  dslots_load_total++;
231461768Sbostic 	  dslots_load_filled++;
231561768Sbostic 
231661768Sbostic 	  if (CONSTANT_P (src_reg))
231761768Sbostic 	    mips_count_memory_refs (src_reg, 1);
231861768Sbostic 
231961768Sbostic 	  if (CONSTANT_P (dest_reg))
232061768Sbostic 	    mips_count_memory_refs (dest_reg, 1);
232161768Sbostic 	}
232261768Sbostic 
232361768Sbostic       /* Emit load/stores now if we have run out of registers or are
232461768Sbostic 	 at the end of the move.  */
232561768Sbostic 
232661768Sbostic       if (++num == num_regs || bytes == 0)
232761768Sbostic 	{
232861768Sbostic 	  /* If only load/store, we need a NOP after the load.  */
232961768Sbostic 	  if (num == 1)
233061768Sbostic 	    {
233161768Sbostic 	      load_store[0].load = load_store[0].load_nop;
233261768Sbostic 	      if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
233361768Sbostic 		dslots_load_filled--;
233461768Sbostic 	    }
233561768Sbostic 
233661768Sbostic 	  if (move_type != BLOCK_MOVE_LAST)
233761768Sbostic 	    {
233861768Sbostic 	      for (i = 0; i < num; i++)
233961768Sbostic 		{
234061768Sbostic 		  int offset;
234161768Sbostic 
234261768Sbostic 		  if (!operands[i+4])
234361768Sbostic 		    abort ();
234461768Sbostic 
234561768Sbostic 		  if (GET_MODE (operands[i+4]) != load_store[i].mode)
234661768Sbostic 		    operands[i+4] = gen_rtx (REG, load_store[i].mode, REGNO (operands[i+4]));
234761768Sbostic 
234861768Sbostic 		  offset = load_store[i].offset;
234961768Sbostic 		  xoperands[0] = operands[i+4];
235061768Sbostic 		  xoperands[1] = gen_rtx (MEM, load_store[i].mode,
235161768Sbostic 					  plus_constant (src_reg, offset));
235261768Sbostic 
235361768Sbostic 		  if (use_lwl_lwr)
235461768Sbostic 		    xoperands[2] = gen_rtx (MEM, load_store[i].mode,
235561768Sbostic 					    plus_constant (src_reg, UNITS_PER_WORD-1+offset));
235661768Sbostic 
235761768Sbostic 		  output_asm_insn (load_store[i].load, xoperands);
235861768Sbostic 		}
235961768Sbostic 	    }
236061768Sbostic 
236161768Sbostic 	  for (i = 0; i < num; i++)
236261768Sbostic 	    {
236361768Sbostic 	      int last_p = (i == num-1 && bytes == 0);
236461768Sbostic 	      int offset = load_store[i].offset;
236561768Sbostic 
236661768Sbostic 	      xoperands[0] = operands[i+4];
236761768Sbostic 	      xoperands[1] = gen_rtx (MEM, load_store[i].mode,
236861768Sbostic 				      plus_constant (dest_reg, offset));
236961768Sbostic 
237061768Sbostic 
237161768Sbostic 	      if (use_lwl_lwr)
237261768Sbostic 		xoperands[2] = gen_rtx (MEM, load_store[i].mode,
237361768Sbostic 					plus_constant (dest_reg, UNITS_PER_WORD-1+offset));
237461768Sbostic 
237561768Sbostic 	      if (move_type == BLOCK_MOVE_NORMAL)
237661768Sbostic 		output_asm_insn (load_store[i].store, xoperands);
237761768Sbostic 
237861768Sbostic 	      else if (move_type == BLOCK_MOVE_NOT_LAST)
237961768Sbostic 		{
238061768Sbostic 		  if (!last_p)
238161768Sbostic 		    output_asm_insn (load_store[i].store, xoperands);
238261768Sbostic 
238361768Sbostic 		  else if (load_store[i].final != (char *)0)
238461768Sbostic 		    output_asm_insn (load_store[i].final, xoperands);
238561768Sbostic 		}
238661768Sbostic 
238761768Sbostic 	      else if (last_p)
238861768Sbostic 		output_asm_insn (load_store[i].last_store, xoperands);
238961768Sbostic 	    }
239061768Sbostic 
239161768Sbostic 	  num = 0;		/* reset load_store */
239261768Sbostic 	  use_lwl_lwr = FALSE;	/* reset whether or not we used lwl/lwr */
239361768Sbostic 	}
239461768Sbostic     }
239561768Sbostic 
239661768Sbostic   if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0)
239761768Sbostic     output_asm_insn (".set\treorder", operands);
239861768Sbostic 
239961768Sbostic   return "";
240061768Sbostic }
240161768Sbostic 
240261768Sbostic 
240361768Sbostic /* Argument support functions.  */
240461768Sbostic 
240561768Sbostic /* Initialize CUMULATIVE_ARGS for a function.  */
240661768Sbostic 
240761768Sbostic void
init_cumulative_args(cum,fntype,libname)240861768Sbostic init_cumulative_args (cum, fntype, libname)
240961768Sbostic      CUMULATIVE_ARGS *cum;	/* argument info to initialize */
241061768Sbostic      tree fntype;		/* tree ptr for function decl */
241161768Sbostic      rtx libname;		/* SYMBOL_REF of library name or 0 */
241261768Sbostic {
241361768Sbostic   static CUMULATIVE_ARGS zero_cum;
241461768Sbostic   tree param, next_param;
241561768Sbostic 
241661768Sbostic   if (TARGET_DEBUG_E_MODE)
241761768Sbostic     {
241861768Sbostic       fprintf (stderr, "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
241961768Sbostic       if (!fntype)
242061768Sbostic 	fputc ('\n', stderr);
242161768Sbostic 
242261768Sbostic       else
242361768Sbostic 	{
242461768Sbostic 	  tree ret_type = TREE_TYPE (fntype);
242561768Sbostic 	  fprintf (stderr, ", fntype code = %s, ret code = %s\n",
242661768Sbostic 		   tree_code_name[ (int)TREE_CODE (fntype) ],
242761768Sbostic 		   tree_code_name[ (int)TREE_CODE (ret_type) ]);
242861768Sbostic 	}
242961768Sbostic     }
243061768Sbostic 
243161768Sbostic   *cum = zero_cum;
243261768Sbostic 
243361768Sbostic   /* Determine if this function has variable arguments.  This is
243461768Sbostic      indicated by the last argument being 'void_type_mode' if there
243561768Sbostic      are no variable arguments.  The standard MIPS calling sequence
243661768Sbostic      passes all arguments in the general purpose registers in this
243761768Sbostic      case. */
243861768Sbostic 
243961768Sbostic   for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
244061768Sbostic        param != (tree)0;
244161768Sbostic        param = next_param)
244261768Sbostic     {
244361768Sbostic       next_param = TREE_CHAIN (param);
244461768Sbostic       if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node)
244561768Sbostic 	cum->gp_reg_found = 1;
244661768Sbostic     }
244761768Sbostic }
244861768Sbostic 
244961768Sbostic /* Advance the argument to the next argument position.  */
245061768Sbostic 
245161768Sbostic void
function_arg_advance(cum,mode,type,named)245261768Sbostic function_arg_advance (cum, mode, type, named)
245361768Sbostic      CUMULATIVE_ARGS *cum;	/* current arg information */
245461768Sbostic      enum machine_mode mode;	/* current arg mode */
245561768Sbostic      tree type;			/* type of the argument or 0 if lib support */
245661768Sbostic      int named;			/* whether or not the argument was named */
245761768Sbostic {
245861768Sbostic   if (TARGET_DEBUG_E_MODE)
245961768Sbostic     fprintf (stderr,
246061768Sbostic 	     "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n\n",
246161768Sbostic 	     cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
246261768Sbostic 	     type, named);
246361768Sbostic 
246461768Sbostic   cum->arg_number++;
246561768Sbostic   switch (mode)
246661768Sbostic     {
246761768Sbostic     default:
246861768Sbostic       error ("Illegal mode given to function_arg_advance");
246961768Sbostic       break;
247061768Sbostic 
247161768Sbostic     case VOIDmode:
247261768Sbostic       break;
247361768Sbostic 
247461768Sbostic     case BLKmode:
247561768Sbostic       cum->gp_reg_found = 1;
247661768Sbostic       cum->arg_words += (int_size_in_bytes (type) + 3) / 4;
247761768Sbostic       break;
247861768Sbostic 
247961768Sbostic     case SFmode:
248061768Sbostic       cum->arg_words++;
248161768Sbostic       break;
248261768Sbostic 
248361768Sbostic     case DFmode:
248461768Sbostic       cum->arg_words += 2;
248561768Sbostic       break;
248661768Sbostic 
248761768Sbostic     case DImode:
248861768Sbostic       cum->gp_reg_found = 1;
248961768Sbostic       cum->arg_words += 2;
249061768Sbostic       break;
249161768Sbostic 
249261768Sbostic     case QImode:
249361768Sbostic     case HImode:
249461768Sbostic     case SImode:
249561768Sbostic       cum->gp_reg_found = 1;
249661768Sbostic       cum->arg_words++;
249761768Sbostic       break;
249861768Sbostic     }
249961768Sbostic }
250061768Sbostic 
250161768Sbostic /* Return a RTL expression containing the register for the given mode,
250261768Sbostic    or 0 if the argument is too be passed on the stack.  */
250361768Sbostic 
250461768Sbostic struct rtx_def *
function_arg(cum,mode,type,named)250561768Sbostic function_arg (cum, mode, type, named)
250661768Sbostic      CUMULATIVE_ARGS *cum;	/* current arg information */
250761768Sbostic      enum machine_mode mode;	/* current arg mode */
250861768Sbostic      tree type;			/* type of the argument or 0 if lib support */
250961768Sbostic      int named;			/* != 0 for normal args, == 0 for ... args */
251061768Sbostic {
251161768Sbostic   rtx ret;
251261768Sbostic   int regbase = -1;
251361768Sbostic   int bias = 0;
251461768Sbostic   int struct_p = ((type != (tree)0)
251561768Sbostic 		  && (TREE_CODE (type) == RECORD_TYPE
251661768Sbostic 		      || TREE_CODE (type) == UNION_TYPE));
251761768Sbostic 
251861768Sbostic   if (TARGET_DEBUG_E_MODE)
251961768Sbostic     fprintf (stderr,
252061768Sbostic 	     "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d ) = ",
252161768Sbostic 	     cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
252261768Sbostic 	     type, named);
252361768Sbostic 
252461768Sbostic   switch (mode)
252561768Sbostic     {
252661768Sbostic     default:
252761768Sbostic       error ("Illegal mode given to function_arg");
252861768Sbostic       break;
252961768Sbostic 
253061768Sbostic     case SFmode:
253161768Sbostic       if (cum->gp_reg_found || cum->arg_number >= 2)
253261768Sbostic 	regbase = GP_ARG_FIRST;
253361768Sbostic       else {
253461768Sbostic 	regbase = (TARGET_SOFT_FLOAT) ? GP_ARG_FIRST : FP_ARG_FIRST;
253561768Sbostic 	if (cum->arg_words == 1)	/* first arg was float */
253661768Sbostic 	  bias = 1;			/* use correct reg */
253761768Sbostic       }
253861768Sbostic 
253961768Sbostic       break;
254061768Sbostic 
254161768Sbostic     case DFmode:
254261768Sbostic       cum->arg_words += (cum->arg_words & 1);
254361768Sbostic       regbase = (cum->gp_reg_found || TARGET_SOFT_FLOAT)
254461768Sbostic 			? GP_ARG_FIRST
254561768Sbostic 			: FP_ARG_FIRST;
254661768Sbostic       break;
254761768Sbostic 
254861768Sbostic     case BLKmode:
254961768Sbostic       if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD)
255061768Sbostic 	cum->arg_words += (cum->arg_words & 1);
255161768Sbostic 
255261768Sbostic       regbase = GP_ARG_FIRST;
255361768Sbostic       break;
255461768Sbostic 
255561768Sbostic     case VOIDmode:
255661768Sbostic     case QImode:
255761768Sbostic     case HImode:
255861768Sbostic     case SImode:
255961768Sbostic       regbase = GP_ARG_FIRST;
256061768Sbostic       break;
256161768Sbostic 
256261768Sbostic     case DImode:
256361768Sbostic       cum->arg_words += (cum->arg_words & 1);
256461768Sbostic       regbase = GP_ARG_FIRST;
256561768Sbostic     }
256661768Sbostic 
256761768Sbostic   if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
256861768Sbostic     {
256961768Sbostic       if (TARGET_DEBUG_E_MODE)
257061768Sbostic 	fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
257161768Sbostic 
257261768Sbostic       ret = (rtx)0;
257361768Sbostic     }
257461768Sbostic   else
257561768Sbostic     {
257661768Sbostic       if (regbase == -1)
257761768Sbostic 	abort ();
257861768Sbostic 
257961768Sbostic       ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias);
258061768Sbostic 
258161768Sbostic       if (TARGET_DEBUG_E_MODE)
258261768Sbostic 	fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias],
258361768Sbostic 		 struct_p ? ", [struct]" : "");
258461768Sbostic 
258561768Sbostic       /* The following is a hack in order to pass 1 byte structures
258661768Sbostic 	 the same way that the MIPS compiler does (namely by passing
258761768Sbostic 	 the structure in the high byte or half word of the register).
258861768Sbostic 	 This also makes varargs work.  If we have such a structure,
258961768Sbostic 	 we save the adjustment RTL, and the call define expands will
259061768Sbostic 	 emit them.  For the VOIDmode argument (argument after the
259161768Sbostic 	 last real argument, pass back a parallel vector holding each
259261768Sbostic 	 of the adjustments.  */
259361768Sbostic 
259461768Sbostic       if (struct_p && (mode == QImode || mode == HImode))
259561768Sbostic 	{
259661768Sbostic 	  rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode));
259761768Sbostic 	  rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias);
259861768Sbostic 	  cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount);
259961768Sbostic 	}
260061768Sbostic     }
260161768Sbostic 
260261768Sbostic   if (mode == VOIDmode && cum->num_adjusts > 0)
260361768Sbostic     ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust));
260461768Sbostic 
260561768Sbostic   return ret;
260661768Sbostic }
260761768Sbostic 
260861768Sbostic 
260961768Sbostic int
function_arg_partial_nregs(cum,mode,type,named)261061768Sbostic function_arg_partial_nregs (cum, mode, type, named)
261161768Sbostic      CUMULATIVE_ARGS *cum;	/* current arg information */
261261768Sbostic      enum machine_mode mode;	/* current arg mode */
261361768Sbostic      tree type;			/* type of the argument or 0 if lib support */
261461768Sbostic      int named;			/* != 0 for normal args, == 0 for ... args */
261561768Sbostic {
261661768Sbostic   if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS)
261761768Sbostic     {
261861768Sbostic       int words = (int_size_in_bytes (type) + 3) / 4;
261961768Sbostic 
262061768Sbostic       if (words + cum->arg_words < MAX_ARGS_IN_REGISTERS)
262161768Sbostic 	return 0;		/* structure fits in registers */
262261768Sbostic 
262361768Sbostic       if (TARGET_DEBUG_E_MODE)
262461768Sbostic 	fprintf (stderr, "function_arg_partial_nregs = %d\n",
262561768Sbostic 		 MAX_ARGS_IN_REGISTERS - cum->arg_words);
262661768Sbostic 
262761768Sbostic       return MAX_ARGS_IN_REGISTERS - cum->arg_words;
262861768Sbostic     }
262961768Sbostic 
263061768Sbostic   else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1)
263161768Sbostic     {
263261768Sbostic       if (TARGET_DEBUG_E_MODE)
263361768Sbostic 	fprintf (stderr, "function_arg_partial_nregs = 1\n");
263461768Sbostic 
263561768Sbostic       return 1;
263661768Sbostic     }
263761768Sbostic 
263861768Sbostic   return 0;
263961768Sbostic }
264061768Sbostic 
264161768Sbostic 
264261768Sbostic /* Print the options used in the assembly file.  */
264361768Sbostic 
264461768Sbostic static struct {char *name; int value;} target_switches []
264561768Sbostic   = TARGET_SWITCHES;
264661768Sbostic 
264761768Sbostic void
print_options(out)264861768Sbostic print_options (out)
264961768Sbostic      FILE *out;
265061768Sbostic {
265161768Sbostic   int line_len;
265261768Sbostic   int len;
265361768Sbostic   int j;
265461768Sbostic   char **p;
265561768Sbostic   int mask = TARGET_DEFAULT;
265661768Sbostic 
265761768Sbostic   /* Allow assembly language comparisons with -mdebug eliminating the
265861768Sbostic      compiler version number and switch lists.  */
265961768Sbostic 
266061768Sbostic   if (TARGET_DEBUG_MODE)
266161768Sbostic     return;
266261768Sbostic 
266361768Sbostic   fprintf (out, "\n # %s %s", language_string, version_string);
266461768Sbostic #ifdef TARGET_VERSION_INTERNAL
266561768Sbostic   TARGET_VERSION_INTERNAL (out);
266661768Sbostic #endif
266761768Sbostic #ifdef __GNUC__
266861768Sbostic   fprintf (out, " compiled by GNU C\n\n");
266961768Sbostic #else
267061768Sbostic   fprintf (out, " compiled by CC\n\n");
267161768Sbostic #endif
267261768Sbostic 
267361768Sbostic   fprintf (out, " # Cc1 defaults:");
267461768Sbostic   line_len = 32767;
267561768Sbostic   for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
267661768Sbostic     {
267761768Sbostic       if (target_switches[j].name[0] != '\0'
267861768Sbostic 	  && target_switches[j].value > 0
267961768Sbostic 	  && (target_switches[j].value & mask) == target_switches[j].value)
268061768Sbostic 	{
268161768Sbostic 	  mask &= ~ target_switches[j].value;
268261768Sbostic 	  len = strlen (target_switches[j].name) + 1;
268361768Sbostic 	  if (len + line_len > 79)
268461768Sbostic 	    {
268561768Sbostic 	      line_len = 2;
268661768Sbostic 	      fputs ("\n #", out);
268761768Sbostic 	    }
268861768Sbostic 	  fprintf (out, " -m%s", target_switches[j].name);
268961768Sbostic 	  line_len += len;
269061768Sbostic 	}
269161768Sbostic     }
269261768Sbostic 
269361768Sbostic   fprintf (out, "\n\n # Cc1 arguments (-G value = %d, Cpu = %s, ISA = %d):",
269461768Sbostic 	   mips_section_threshold, mips_cpu_string, mips_isa);
269561768Sbostic 
269661768Sbostic   line_len = 32767;
269761768Sbostic   for (p = &save_argv[1]; *p != (char *)0; p++)
269861768Sbostic     {
269961768Sbostic       char *arg = *p;
270061768Sbostic       if (*arg == '-')
270161768Sbostic 	{
270261768Sbostic 	  len = strlen (arg) + 1;
270361768Sbostic 	  if (len + line_len > 79)
270461768Sbostic 	    {
270561768Sbostic 	      line_len = 2;
270661768Sbostic 	      fputs ("\n #", out);
270761768Sbostic 	    }
270861768Sbostic 	  fprintf (out, " %s", *p);
270961768Sbostic 	  line_len += len;
271061768Sbostic 	}
271161768Sbostic     }
271261768Sbostic 
271361768Sbostic   fputs ("\n\n", out);
271461768Sbostic }
271561768Sbostic 
271661768Sbostic 
271761768Sbostic /* Abort after printing out a specific insn.  */
271861768Sbostic 
271961768Sbostic void
abort_with_insn(insn,reason)272061768Sbostic abort_with_insn (insn, reason)
272161768Sbostic      rtx insn;
272261768Sbostic      char *reason;
272361768Sbostic {
272461768Sbostic   error (reason);
272561768Sbostic   debug_rtx (insn);
272661768Sbostic   abort ();
272761768Sbostic }
272861768Sbostic 
272961768Sbostic /* Write a message to stderr (for use in macros expanded in files that do not
273061768Sbostic    include stdio.h).  */
273161768Sbostic 
273261768Sbostic void
trace(s,s1,s2)273361768Sbostic trace (s, s1, s2)
273461768Sbostic      char *s, *s1, *s2;
273561768Sbostic {
273661768Sbostic   fprintf (stderr, s, s1, s2);
273761768Sbostic }
273861768Sbostic 
273961768Sbostic 
274061768Sbostic #ifdef SIGINFO
274161768Sbostic 
274261768Sbostic static void
siginfo(signo)274361768Sbostic siginfo (signo)
274461768Sbostic      int signo;
274561768Sbostic {
274661768Sbostic   fprintf (stderr, "compiling '%s' in '%s'\n",
274761768Sbostic 	   (current_function_name != (char *)0) ? current_function_name : "<toplevel>",
274861768Sbostic 	   (current_function_file != (char *)0) ? current_function_file : "<no file>");
274961768Sbostic   fflush (stderr);
275061768Sbostic }
275161768Sbostic #endif /* SIGINFO */
275261768Sbostic 
275361768Sbostic 
275461768Sbostic /* Set up the threshold for data to go into the small data area, instead
275561768Sbostic    of the normal data area, and detect any conflicts in the switches.  */
275661768Sbostic 
275761768Sbostic void
override_options()275861768Sbostic override_options ()
275961768Sbostic {
276061768Sbostic   register int i, start;
276161768Sbostic   register int regno;
276261768Sbostic   register enum machine_mode mode;
276361768Sbostic 
276461768Sbostic   if (g_switch_set)
276561768Sbostic     mips_section_threshold = g_switch_value;
276661768Sbostic 
276761768Sbostic   else
276861768Sbostic     mips_section_threshold = (TARGET_MIPS_AS) ? 8 : 0;
276961768Sbostic 
277061768Sbostic   /* Identify the processor type */
277161768Sbostic   if (mips_cpu_string == (char *)0
277261768Sbostic       || !strcmp (mips_cpu_string, "default")
277361768Sbostic       || !strcmp (mips_cpu_string, "DEFAULT"))
277461768Sbostic     {
277561768Sbostic       mips_cpu_string = "default";
277661768Sbostic       mips_cpu = PROCESSOR_DEFAULT;
277761768Sbostic     }
277861768Sbostic 
277961768Sbostic   else
278061768Sbostic     {
278161768Sbostic       char *p = mips_cpu_string;
278261768Sbostic 
278361768Sbostic       if (*p == 'r' || *p == 'R')
278461768Sbostic 	p++;
278561768Sbostic 
278661768Sbostic       /* Since there is no difference between a R2000 and R3000 in
278761768Sbostic 	 terms of the scheduler, we collapse them into just an R3000. */
278861768Sbostic 
278961768Sbostic       mips_cpu = PROCESSOR_DEFAULT;
279061768Sbostic       switch (*p)
279161768Sbostic 	{
279261768Sbostic 	case '2':
279361768Sbostic 	  if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K"))
279461768Sbostic 	    mips_cpu = PROCESSOR_R3000;
279561768Sbostic 	  break;
279661768Sbostic 
279761768Sbostic 	case '3':
279861768Sbostic 	  if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K"))
279961768Sbostic 	    mips_cpu = PROCESSOR_R3000;
280061768Sbostic 	  break;
280161768Sbostic 
280261768Sbostic 	case '4':
280361768Sbostic 	  if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
280461768Sbostic 	    mips_cpu = PROCESSOR_R4000;
280561768Sbostic 	  break;
280661768Sbostic 
280761768Sbostic 	case '6':
280861768Sbostic 	  if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K"))
280961768Sbostic 	    mips_cpu = PROCESSOR_R6000;
281061768Sbostic 	  break;
281161768Sbostic 	}
281261768Sbostic 
281361768Sbostic       if (mips_cpu == PROCESSOR_DEFAULT)
281461768Sbostic 	{
281561768Sbostic 	  error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
281661768Sbostic 	  mips_cpu_string = "default";
281761768Sbostic 	}
281861768Sbostic     }
281961768Sbostic 
282061768Sbostic   /* Now get the architectural level.  */
282161768Sbostic   if (mips_isa_string == (char *)0)
282261768Sbostic     mips_isa = 1;
282361768Sbostic 
282461768Sbostic   else if (isdigit (*mips_isa_string))
282561768Sbostic     mips_isa = atoi (mips_isa_string);
282661768Sbostic 
282761768Sbostic   else
282861768Sbostic     {
282961768Sbostic       error ("bad value (%s) for -mips switch", mips_isa_string);
283061768Sbostic       mips_isa = 1;
283161768Sbostic     }
283261768Sbostic 
283361768Sbostic   if (mips_isa < 0 || mips_isa > 3)
283461768Sbostic     error ("-mips%d not supported", mips_isa);
283561768Sbostic 
283661768Sbostic   else if (mips_isa > 1
283761768Sbostic 	   && (mips_cpu == PROCESSOR_DEFAULT || mips_cpu == PROCESSOR_R3000))
283861768Sbostic     error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
283961768Sbostic 
284061768Sbostic   else if (mips_cpu == PROCESSOR_R6000 && mips_isa > 2)
284161768Sbostic     error ("-mcpu=%s does not support -mips%d", mips_cpu_string, mips_isa);
284261768Sbostic 
284361768Sbostic   /* make sure sizes of ints/longs/etc. are ok */
284461768Sbostic   if (mips_isa < 3)
284561768Sbostic     {
284661768Sbostic       if (TARGET_INT64)
284761768Sbostic 	fatal ("Only the r4000 can support 64 bit ints");
284861768Sbostic 
284961768Sbostic       else if (TARGET_LONG64)
285061768Sbostic 	fatal ("Only the r4000 can support 64 bit longs");
285161768Sbostic 
285261768Sbostic       else if (TARGET_LLONG128)
285361768Sbostic 	fatal ("Only the r4000 can support 128 bit long longs");
285461768Sbostic 
285561768Sbostic       else if (TARGET_FLOAT64)
285661768Sbostic 	fatal ("Only the r4000 can support 64 bit fp registers");
285761768Sbostic     }
285861768Sbostic   else if (TARGET_INT64 || TARGET_LONG64 || TARGET_LLONG128 || TARGET_FLOAT64)
285961768Sbostic     warning ("r4000 64/128 bit types not yet supported");
286061768Sbostic 
286161768Sbostic   /* Tell halfpic.c that we have half-pic code if we do.  */
286261768Sbostic   if (TARGET_HALF_PIC)
286361768Sbostic     HALF_PIC_INIT ();
286461768Sbostic 
286561768Sbostic   /* -mrnames says to use the MIPS software convention for register
286661768Sbostic      names instead of the hardware names (ie, a0 instead of $4).
286761768Sbostic      We do this by switching the names in mips_reg_names, which the
286861768Sbostic      reg_names points into via the REGISTER_NAMES macro.  */
286961768Sbostic 
287061768Sbostic   if (TARGET_NAME_REGS)
287161768Sbostic     {
287261768Sbostic       if (TARGET_GAS)
287361768Sbostic 	{
287461768Sbostic 	  target_flags &= ~ MASK_NAME_REGS;
287561768Sbostic 	  error ("Gas does not support the MIPS software register name convention.");
287661768Sbostic 	}
287761768Sbostic       else
287861768Sbostic 	bcopy ((char *) mips_sw_reg_names, (char *) mips_reg_names, sizeof (mips_reg_names));
287961768Sbostic     }
288061768Sbostic 
288161768Sbostic   /* If this is OSF/1, set up a SIGINFO handler so we can see what function
288261768Sbostic      is currently being compiled.  */
288361768Sbostic #ifdef SIGINFO
288461768Sbostic   if (getenv ("GCC_SIGINFO") != (char *)0)
288561768Sbostic     {
288661768Sbostic       struct sigaction action;
288761768Sbostic       action.sa_handler = siginfo;
288861768Sbostic       action.sa_mask = 0;
288961768Sbostic       action.sa_flags = SA_RESTART;
289061768Sbostic       sigaction (SIGINFO, &action, (struct sigaction *)0);
289161768Sbostic     }
289261768Sbostic #endif
289361768Sbostic 
289461768Sbostic #if defined(_IOLBF)
289561768Sbostic #if defined(ultrix) || defined(__ultrix) || defined(__OSF1__) || defined(__osf__) || defined(osf)
289661768Sbostic   /* If -mstats and -quiet, make stderr line buffered.  */
289761768Sbostic   if (quiet_flag && TARGET_STATS)
289861768Sbostic     setvbuf (stderr, (char *)0, _IOLBF, BUFSIZ);
289961768Sbostic #endif
290061768Sbostic #endif
290161768Sbostic 
290261768Sbostic   /* Set up the classification arrays now.  */
290361768Sbostic   mips_rtx_classify[(int)PLUS]  = CLASS_ADD_OP;
290461768Sbostic   mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP;
290561768Sbostic   mips_rtx_classify[(int)DIV]   = CLASS_DIVMOD_OP;
290661768Sbostic   mips_rtx_classify[(int)MOD]   = CLASS_DIVMOD_OP;
290761768Sbostic   mips_rtx_classify[(int)UDIV]  = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
290861768Sbostic   mips_rtx_classify[(int)UMOD]  = CLASS_DIVMOD_OP | CLASS_UNSIGNED_OP;
290961768Sbostic   mips_rtx_classify[(int)EQ]    = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
291061768Sbostic   mips_rtx_classify[(int)NE]    = CLASS_CMP_OP | CLASS_EQUALITY_OP | CLASS_FCMP_OP;
291161768Sbostic   mips_rtx_classify[(int)GT]    = CLASS_CMP_OP | CLASS_FCMP_OP;
291261768Sbostic   mips_rtx_classify[(int)GE]    = CLASS_CMP_OP | CLASS_FCMP_OP;
291361768Sbostic   mips_rtx_classify[(int)LT]    = CLASS_CMP_OP | CLASS_FCMP_OP;
291461768Sbostic   mips_rtx_classify[(int)LE]    = CLASS_CMP_OP | CLASS_FCMP_OP;
291561768Sbostic   mips_rtx_classify[(int)GTU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
291661768Sbostic   mips_rtx_classify[(int)GEU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
291761768Sbostic   mips_rtx_classify[(int)LTU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
291861768Sbostic   mips_rtx_classify[(int)LEU]   = CLASS_CMP_OP | CLASS_UNSIGNED_OP;
291961768Sbostic 
292061768Sbostic   mips_print_operand_punct['?'] = TRUE;
292161768Sbostic   mips_print_operand_punct['#'] = TRUE;
292261768Sbostic   mips_print_operand_punct['&'] = TRUE;
292361768Sbostic   mips_print_operand_punct['!'] = TRUE;
292461768Sbostic   mips_print_operand_punct['*'] = TRUE;
292561768Sbostic   mips_print_operand_punct['@'] = TRUE;
292661768Sbostic   mips_print_operand_punct['.'] = TRUE;
292761768Sbostic   mips_print_operand_punct['('] = TRUE;
292861768Sbostic   mips_print_operand_punct[')'] = TRUE;
292961768Sbostic   mips_print_operand_punct['['] = TRUE;
293061768Sbostic   mips_print_operand_punct[']'] = TRUE;
293161768Sbostic   mips_print_operand_punct['<'] = TRUE;
293261768Sbostic   mips_print_operand_punct['>'] = TRUE;
293361768Sbostic   mips_print_operand_punct['{'] = TRUE;
293461768Sbostic   mips_print_operand_punct['}'] = TRUE;
293561768Sbostic 
293661768Sbostic   mips_char_to_class['d'] = GR_REGS;
293761768Sbostic   mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS);
293861768Sbostic   mips_char_to_class['h'] = HI_REG;
293961768Sbostic   mips_char_to_class['l'] = LO_REG;
294061768Sbostic   mips_char_to_class['x'] = MD_REGS;
294161768Sbostic   mips_char_to_class['y'] = GR_REGS;
294261768Sbostic   mips_char_to_class['z'] = ST_REGS;
294361768Sbostic 
294461768Sbostic   /* Set up array to map GCC register number to debug register number.
294561768Sbostic      Ignore the special purpose register numbers.  */
294661768Sbostic 
294761768Sbostic   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
294861768Sbostic     mips_dbx_regno[i] = -1;
294961768Sbostic 
295061768Sbostic   start = GP_DBX_FIRST - GP_REG_FIRST;
295161768Sbostic   for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
295261768Sbostic     mips_dbx_regno[i] = i + start;
295361768Sbostic 
295461768Sbostic   start = FP_DBX_FIRST - FP_REG_FIRST;
295561768Sbostic   for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
295661768Sbostic     mips_dbx_regno[i] = i + start;
295761768Sbostic 
295861768Sbostic   /* Set up array giving whether a given register can hold a given mode.
295961768Sbostic      At present, restrict ints from being in FP registers, because reload
296061768Sbostic      is a little enthusiastic about storing extra values in FP registers,
296161768Sbostic      and this is not good for things like OS kernels.  Also, due to the
296261768Sbostic      mandatory delay, it is as fast to load from cached memory as to move
296361768Sbostic      from the FP register.  */
296461768Sbostic 
296561768Sbostic   for (mode = VOIDmode;
296661768Sbostic        mode != MAX_MACHINE_MODE;
296761768Sbostic        mode = (enum machine_mode)((int)mode + 1))
296861768Sbostic     {
296961768Sbostic       register int size		     = GET_MODE_SIZE (mode);
297061768Sbostic       register enum mode_class class = GET_MODE_CLASS (mode);
297161768Sbostic 
297261768Sbostic       for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
297361768Sbostic 	{
297461768Sbostic 	  register int temp;
297561768Sbostic 
297661768Sbostic 	  if (mode == CC_FPmode || mode == CC_REV_FPmode)
297761768Sbostic 	    temp = (regno == FPSW_REGNUM);
297861768Sbostic 
297961768Sbostic 	  else if (GP_REG_P (regno))
298061768Sbostic 	    temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD));
298161768Sbostic 
298261768Sbostic 	  else if (FP_REG_P (regno))
298361768Sbostic 	    temp = ((TARGET_FLOAT64 || ((regno & 1) == 0))
298461768Sbostic 		    && (class == MODE_FLOAT
298561768Sbostic 			|| class == MODE_COMPLEX_FLOAT
298661768Sbostic 			|| (TARGET_DEBUG_H_MODE && class == MODE_INT)));
298761768Sbostic 
298861768Sbostic 	  else if (MD_REG_P (regno))
298961768Sbostic 	    temp = (mode == SImode || (regno == MD_REG_FIRST && mode == DImode));
299061768Sbostic 
299161768Sbostic 	  else
299261768Sbostic 	    temp = FALSE;
299361768Sbostic 
299461768Sbostic 	  mips_hard_regno_mode_ok[(int)mode][regno] = temp;
299561768Sbostic 	}
299661768Sbostic     }
299761768Sbostic }
299861768Sbostic 
299961768Sbostic 
300061768Sbostic /*
300161768Sbostic  * The MIPS debug format wants all automatic variables and arguments
300261768Sbostic  * to be in terms of the virtual frame pointer (stack pointer before
300361768Sbostic  * any adjustment in the function), while the MIPS 3.0 linker wants
300461768Sbostic  * the frame pointer to be the stack pointer after the initial
300561768Sbostic  * adjustment.  So, we do the adjustment here.  The arg pointer (which
300661768Sbostic  * is eliminated) points to the virtual frame pointer, while the frame
300761768Sbostic  * pointer (which may be eliminated) points to the stack pointer after
300861768Sbostic  * the initial adjustments.
300961768Sbostic  */
301061768Sbostic 
301161768Sbostic int
mips_debugger_offset(addr,offset)301261768Sbostic mips_debugger_offset (addr, offset)
301361768Sbostic      rtx addr;
301461768Sbostic      int offset;
301561768Sbostic {
301661768Sbostic   rtx offset2 = const0_rtx;
301761768Sbostic   rtx reg = eliminate_constant_term (addr, &offset2);
301861768Sbostic 
301961768Sbostic   if (!offset)
302061768Sbostic     offset = INTVAL (offset2);
302161768Sbostic 
302261768Sbostic   if (reg == stack_pointer_rtx || reg == frame_pointer_rtx)
302361768Sbostic     {
302461768Sbostic       int frame_size = (!current_frame_info.initialized)
302561768Sbostic 				? compute_frame_size (get_frame_size ())
302661768Sbostic 				: current_frame_info.total_size;
302761768Sbostic 
302861768Sbostic       offset = offset - frame_size;
302961768Sbostic     }
303061768Sbostic   /* sdbout_parms does not want this to crash for unrecognized cases.  */
303161768Sbostic #if 0
303261768Sbostic   else if (reg != arg_pointer_rtx)
303361768Sbostic     abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer.");
303461768Sbostic #endif
303561768Sbostic 
303661768Sbostic   return offset;
303761768Sbostic }
303861768Sbostic 
303961768Sbostic 
304061768Sbostic /* A C compound statement to output to stdio stream STREAM the
304161768Sbostic    assembler syntax for an instruction operand X.  X is an RTL
304261768Sbostic    expression.
304361768Sbostic 
304461768Sbostic    CODE is a value that can be used to specify one of several ways
304561768Sbostic    of printing the operand.  It is used when identical operands
304661768Sbostic    must be printed differently depending on the context.  CODE
304761768Sbostic    comes from the `%' specification that was used to request
304861768Sbostic    printing of the operand.  If the specification was just `%DIGIT'
304961768Sbostic    then CODE is 0; if the specification was `%LTR DIGIT' then CODE
305061768Sbostic    is the ASCII code for LTR.
305161768Sbostic 
305261768Sbostic    If X is a register, this macro should print the register's name.
305361768Sbostic    The names can be found in an array `reg_names' whose type is
305461768Sbostic    `char *[]'.  `reg_names' is initialized from `REGISTER_NAMES'.
305561768Sbostic 
305661768Sbostic    When the machine description has a specification `%PUNCT' (a `%'
305761768Sbostic    followed by a punctuation character), this macro is called with
305861768Sbostic    a null pointer for X and the punctuation character for CODE.
305961768Sbostic 
306061768Sbostic    The MIPS specific codes are:
306161768Sbostic 
306261768Sbostic    'X'  X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
306361768Sbostic    'x'  X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
306461768Sbostic    'd'  output integer constant in decimal,
306561768Sbostic    'z'	if the operand is 0, use $0 instead of normal operand.
306661768Sbostic    'D'  print second register of double-word register operand.
306761768Sbostic    'L'  print low-order register of double-word register operand.
306861768Sbostic    'M'  print high-order register of double-word register operand.
306961768Sbostic    'C'  print part of opcode for a branch condition.
307061768Sbostic    'N'  print part of opcode for a branch condition, inverted.
307161768Sbostic    '('	Turn on .set noreorder
307261768Sbostic    ')'	Turn on .set reorder
307361768Sbostic    '['	Turn on .set noat
307461768Sbostic    ']'	Turn on .set at
307561768Sbostic    '<'	Turn on .set nomacro
307661768Sbostic    '>'	Turn on .set macro
307761768Sbostic    '{'	Turn on .set volatile (not GAS)
307861768Sbostic    '}'	Turn on .set novolatile (not GAS)
307961768Sbostic    '&'	Turn on .set noreorder if filling delay slots
308061768Sbostic    '*'	Turn on both .set noreorder and .set nomacro if filling delay slots
308161768Sbostic    '!'	Turn on .set nomacro if filling delay slots
308261768Sbostic    '#'	Print nop if in a .set noreorder section.
308361768Sbostic    '?'	Print 'l' if we are to use a branch likely instead of normal branch.
308461768Sbostic    '@'	Print the name of the assembler temporary register (at or $1).
308561768Sbostic    '.'	Print the name of the register with a hard-wired zero (zero or $0).  */
308661768Sbostic 
308761768Sbostic void
print_operand(file,op,letter)308861768Sbostic print_operand (file, op, letter)
308961768Sbostic      FILE *file;		/* file to write to */
309061768Sbostic      rtx op;			/* operand to print */
309161768Sbostic      int letter;		/* %<letter> or 0 */
309261768Sbostic {
309361768Sbostic   register enum rtx_code code;
309461768Sbostic 
309561768Sbostic   if (PRINT_OPERAND_PUNCT_VALID_P (letter))
309661768Sbostic     {
309761768Sbostic       switch (letter)
309861768Sbostic 	{
309961768Sbostic 	default:
310061768Sbostic 	  error ("PRINT_OPERAND: Unknown punctuation '%c'", letter);
310161768Sbostic 	  break;
310261768Sbostic 
310361768Sbostic 	case '?':
310461768Sbostic 	  if (mips_branch_likely)
310561768Sbostic 	    putc ('l', file);
310661768Sbostic 	  break;
310761768Sbostic 
310861768Sbostic 	case '@':
310961768Sbostic 	  fputs (reg_names [GP_REG_FIRST + 1], file);
311061768Sbostic 	  break;
311161768Sbostic 
311261768Sbostic 	case '.':
311361768Sbostic 	  fputs (reg_names [GP_REG_FIRST + 0], file);
311461768Sbostic 	  break;
311561768Sbostic 
311661768Sbostic 	case '&':
311761768Sbostic 	  if (final_sequence != 0 && set_noreorder++ == 0)
311861768Sbostic 	    fputs (".set\tnoreorder\n\t", file);
311961768Sbostic 	  break;
312061768Sbostic 
312161768Sbostic 	case '*':
312261768Sbostic 	  if (final_sequence != 0)
312361768Sbostic 	    {
312461768Sbostic 	      if (set_noreorder++ == 0)
312561768Sbostic 		fputs (".set\tnoreorder\n\t", file);
312661768Sbostic 
312761768Sbostic 	      if (set_nomacro++ == 0)
312861768Sbostic 		fputs (".set\tnomacro\n\t", file);
312961768Sbostic 	    }
313061768Sbostic 	  break;
313161768Sbostic 
313261768Sbostic 	case '!':
313361768Sbostic 	  if (final_sequence != 0 && set_nomacro++ == 0)
313461768Sbostic 	    fputs ("\n\t.set\tnomacro", file);
313561768Sbostic 	  break;
313661768Sbostic 
313761768Sbostic 	case '#':
313861768Sbostic 	  if (set_noreorder != 0)
313961768Sbostic 	    fputs ("\n\tnop", file);
314061768Sbostic 
314161768Sbostic 	  else if (TARGET_GAS || TARGET_STATS)
314261768Sbostic 	    fputs ("\n\t#nop", file);
314361768Sbostic 
314461768Sbostic 	  break;
314561768Sbostic 
314661768Sbostic 	case '(':
314761768Sbostic 	  if (set_noreorder++ == 0)
314861768Sbostic 	    fputs (".set\tnoreorder\n\t", file);
314961768Sbostic 	  break;
315061768Sbostic 
315161768Sbostic 	case ')':
315261768Sbostic 	  if (set_noreorder == 0)
315361768Sbostic 	    error ("internal error: %%) found without a %%( in assembler pattern");
315461768Sbostic 
315561768Sbostic 	  else if (--set_noreorder == 0)
315661768Sbostic 	    fputs ("\n\t.set\treorder", file);
315761768Sbostic 
315861768Sbostic 	  break;
315961768Sbostic 
316061768Sbostic 	case '[':
316161768Sbostic 	  if (set_noat++ == 0)
316261768Sbostic 	    fputs (".set\tnoat\n\t", file);
316361768Sbostic 	  break;
316461768Sbostic 
316561768Sbostic 	case ']':
316661768Sbostic 	  if (set_noat == 0)
316761768Sbostic 	    error ("internal error: %%] found without a %%[ in assembler pattern");
316861768Sbostic 
316961768Sbostic 	  else if (--set_noat == 0)
317061768Sbostic 	    fputs ("\n\t.set\tat", file);
317161768Sbostic 
317261768Sbostic 	  break;
317361768Sbostic 
317461768Sbostic 	case '<':
317561768Sbostic 	  if (set_nomacro++ == 0)
317661768Sbostic 	    fputs (".set\tnomacro\n\t", file);
317761768Sbostic 	  break;
317861768Sbostic 
317961768Sbostic 	case '>':
318061768Sbostic 	  if (set_nomacro == 0)
318161768Sbostic 	    error ("internal error: %%> found without a %%< in assembler pattern");
318261768Sbostic 
318361768Sbostic 	  else if (--set_nomacro == 0)
318461768Sbostic 	    fputs ("\n\t.set\tmacro", file);
318561768Sbostic 
318661768Sbostic 	  break;
318761768Sbostic 
318861768Sbostic 	case '{':
318961768Sbostic 	  if (set_volatile++ == 0)
319061768Sbostic 	    fprintf (file, "%s.set\tvolatile\n\t", (TARGET_MIPS_AS) ? "" : "#");
319161768Sbostic 	  break;
319261768Sbostic 
319361768Sbostic 	case '}':
319461768Sbostic 	  if (set_volatile == 0)
319561768Sbostic 	    error ("internal error: %%} found without a %%{ in assembler pattern");
319661768Sbostic 
319761768Sbostic 	  else if (--set_volatile == 0)
319861768Sbostic 	    fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#");
319961768Sbostic 
320061768Sbostic 	  break;
320161768Sbostic 	}
320261768Sbostic       return;
320361768Sbostic     }
320461768Sbostic 
320561768Sbostic   if (! op)
320661768Sbostic     {
320761768Sbostic       error ("PRINT_OPERAND null pointer");
320861768Sbostic       return;
320961768Sbostic     }
321061768Sbostic 
321161768Sbostic   code = GET_CODE (op);
321261768Sbostic   if (letter == 'C')
321361768Sbostic     switch (code)
321461768Sbostic       {
321561768Sbostic       case EQ:	fputs ("eq",  file); break;
321661768Sbostic       case NE:	fputs ("ne",  file); break;
321761768Sbostic       case GT:	fputs ("gt",  file); break;
321861768Sbostic       case GE:	fputs ("ge",  file); break;
321961768Sbostic       case LT:	fputs ("lt",  file); break;
322061768Sbostic       case LE:	fputs ("le",  file); break;
322161768Sbostic       case GTU: fputs ("gtu", file); break;
322261768Sbostic       case GEU: fputs ("geu", file); break;
322361768Sbostic       case LTU: fputs ("ltu", file); break;
322461768Sbostic       case LEU: fputs ("leu", file); break;
322561768Sbostic 
322661768Sbostic       default:
322761768Sbostic 	abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%C");
322861768Sbostic       }
322961768Sbostic 
323061768Sbostic   else if (letter == 'N')
323161768Sbostic     switch (code)
323261768Sbostic       {
323361768Sbostic       case EQ:	fputs ("ne",  file); break;
323461768Sbostic       case NE:	fputs ("eq",  file); break;
323561768Sbostic       case GT:	fputs ("le",  file); break;
323661768Sbostic       case GE:	fputs ("lt",  file); break;
323761768Sbostic       case LT:	fputs ("ge",  file); break;
323861768Sbostic       case LE:	fputs ("gt",  file); break;
323961768Sbostic       case GTU: fputs ("leu", file); break;
324061768Sbostic       case GEU: fputs ("ltu", file); break;
324161768Sbostic       case LTU: fputs ("geu", file); break;
324261768Sbostic       case LEU: fputs ("gtu", file); break;
324361768Sbostic 
324461768Sbostic       default:
324561768Sbostic 	abort_with_insn (op, "PRINT_OPERAND, illegal insn for %%N");
324661768Sbostic       }
324761768Sbostic 
324861768Sbostic   else if (code == REG)
324961768Sbostic     {
325061768Sbostic       register int regnum = REGNO (op);
325161768Sbostic 
325261768Sbostic       if (letter == 'M')
325361768Sbostic 	regnum += MOST_SIGNIFICANT_WORD;
325461768Sbostic 
325561768Sbostic       else if (letter == 'L')
325661768Sbostic 	regnum += LEAST_SIGNIFICANT_WORD;
325761768Sbostic 
325861768Sbostic       else if (letter == 'D')
325961768Sbostic 	regnum++;
326061768Sbostic 
326161768Sbostic       fprintf (file, "%s", reg_names[regnum]);
326261768Sbostic     }
326361768Sbostic 
326461768Sbostic   else if (code == MEM)
326561768Sbostic     output_address (XEXP (op, 0));
326661768Sbostic 
326761768Sbostic   else if (code == CONST_DOUBLE)
326861768Sbostic     {
326961768Sbostic #if HOST_FLOAT_FORMAT == TARGET_FLOAT_FORMAT
327061768Sbostic       union { double d; int i[2]; } u;
327161768Sbostic       u.i[0] = CONST_DOUBLE_LOW (op);
327261768Sbostic       u.i[1] = CONST_DOUBLE_HIGH (op);
327361768Sbostic       if (GET_MODE (op) == SFmode)
327461768Sbostic 	{
327561768Sbostic 	  float f;
327661768Sbostic 	  f = u.d;
327761768Sbostic 	  u.d = f;
327861768Sbostic 	}
327961768Sbostic       fprintf (file, "%.20e", u.d);
328061768Sbostic #else
328161768Sbostic       fatal ("CONST_DOUBLE found in cross compilation");
328261768Sbostic #endif
328361768Sbostic     }
328461768Sbostic 
328561768Sbostic   else if ((letter == 'x') && (GET_CODE(op) == CONST_INT))
328661768Sbostic     fprintf (file, "0x%04x", 0xffff & (INTVAL(op)));
328761768Sbostic 
328861768Sbostic   else if ((letter == 'X') && (GET_CODE(op) == CONST_INT))
328961768Sbostic     fprintf (file, "0x%08x", INTVAL(op));
329061768Sbostic 
329161768Sbostic   else if ((letter == 'd') && (GET_CODE(op) == CONST_INT))
329261768Sbostic     fprintf (file, "%d", (INTVAL(op)));
329361768Sbostic 
329461768Sbostic   else if (letter == 'z'
329561768Sbostic 	   && (GET_CODE (op) == CONST_INT)
329661768Sbostic 	   && INTVAL (op) == 0)
329761768Sbostic     fputs (reg_names[GP_REG_FIRST], file);
329861768Sbostic 
329961768Sbostic   else if (letter == 'd' || letter == 'x' || letter == 'X')
330061768Sbostic     fatal ("PRINT_OPERAND: letter %c was found & insn was not CONST_INT", letter);
330161768Sbostic 
330261768Sbostic   else
330361768Sbostic     output_addr_const (file, op);
330461768Sbostic }
330561768Sbostic 
330661768Sbostic 
330761768Sbostic /* A C compound statement to output to stdio stream STREAM the
330861768Sbostic    assembler syntax for an instruction operand that is a memory
330961768Sbostic    reference whose address is ADDR.  ADDR is an RTL expression.
331061768Sbostic 
331161768Sbostic    On some machines, the syntax for a symbolic address depends on
331261768Sbostic    the section that the address refers to.  On these machines,
331361768Sbostic    define the macro `ENCODE_SECTION_INFO' to store the information
331461768Sbostic    into the `symbol_ref', and then check for it here.  */
331561768Sbostic 
331661768Sbostic void
print_operand_address(file,addr)331761768Sbostic print_operand_address (file, addr)
331861768Sbostic      FILE *file;
331961768Sbostic      rtx addr;
332061768Sbostic {
332161768Sbostic   if (!addr)
332261768Sbostic     error ("PRINT_OPERAND_ADDRESS, null pointer");
332361768Sbostic 
332461768Sbostic   else
332561768Sbostic     switch (GET_CODE (addr))
332661768Sbostic       {
332761768Sbostic       default:
332861768Sbostic 	abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #1");
332961768Sbostic 	break;
333061768Sbostic 
333161768Sbostic       case REG:
333261768Sbostic 	if (REGNO (addr) == ARG_POINTER_REGNUM)
333361768Sbostic 	  abort_with_insn (addr, "Arg pointer not eliminated.");
333461768Sbostic 
333561768Sbostic 	fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
333661768Sbostic 	break;
333761768Sbostic 
333861768Sbostic       case PLUS:
333961768Sbostic 	{
334061768Sbostic 	  register rtx reg    = (rtx)0;
334161768Sbostic 	  register rtx offset = (rtx)0;
334261768Sbostic 	  register rtx arg0   = XEXP (addr, 0);
334361768Sbostic 	  register rtx arg1   = XEXP (addr, 1);
334461768Sbostic 
334561768Sbostic 	  if (GET_CODE (arg0) == REG)
334661768Sbostic 	    {
334761768Sbostic 	      reg = arg0;
334861768Sbostic 	      offset = arg1;
334961768Sbostic 	      if (GET_CODE (offset) == REG)
335061768Sbostic 		abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
335161768Sbostic 	    }
335261768Sbostic 	  else if (GET_CODE (arg1) == REG)
335361768Sbostic 	    {
335461768Sbostic 	      reg = arg1;
335561768Sbostic 	      offset = arg0;
335661768Sbostic 	    }
335761768Sbostic 	  else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
335861768Sbostic 	    {
335961768Sbostic 	      output_addr_const (file, addr);
336061768Sbostic 	      break;
336161768Sbostic 	    }
336261768Sbostic 	  else
336361768Sbostic 	    abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
336461768Sbostic 
336561768Sbostic 	  if (!CONSTANT_P (offset))
336661768Sbostic 	    abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2");
336761768Sbostic 
336861768Sbostic 	if (REGNO (reg) == ARG_POINTER_REGNUM)
336961768Sbostic 	  abort_with_insn (addr, "Arg pointer not eliminated.");
337061768Sbostic 
337161768Sbostic 	  output_addr_const (file, offset);
337261768Sbostic 	  fprintf (file, "(%s)", reg_names [REGNO (reg)]);
337361768Sbostic 	}
337461768Sbostic 	break;
337561768Sbostic 
337661768Sbostic       case LABEL_REF:
337761768Sbostic       case SYMBOL_REF:
337861768Sbostic       case CONST_INT:
337961768Sbostic       case CONST:
338061768Sbostic 	output_addr_const (file, addr);
338161768Sbostic 	break;
338261768Sbostic     }
338361768Sbostic }
338461768Sbostic 
338561768Sbostic 
338661768Sbostic /* If optimizing for the global pointer, keep track of all of
338761768Sbostic    the externs, so that at the end of the file, we can emit
338861768Sbostic    the appropriate .extern declaration for them, before writing
338961768Sbostic    out the text section.  We assume that all names passed to
339061768Sbostic    us are in the permanent obstack, so that they will be valid
339161768Sbostic    at the end of the compilation.
339261768Sbostic 
339361768Sbostic    If we have -G 0, or the extern size is unknown, don't bother
339461768Sbostic    emitting the .externs.  */
339561768Sbostic 
339661768Sbostic int
mips_output_external(file,decl,name)339761768Sbostic mips_output_external (file, decl, name)
339861768Sbostic      FILE *file;
339961768Sbostic      tree decl;
340061768Sbostic      char *name;
340161768Sbostic {
340261768Sbostic   register struct extern_list *p;
340361768Sbostic   int len;
340461768Sbostic 
340561768Sbostic   if (TARGET_GP_OPT
340661768Sbostic       && mips_section_threshold != 0
340761768Sbostic       && ((TREE_CODE (decl)) != FUNCTION_DECL)
340861768Sbostic       && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0))
340961768Sbostic     {
341061768Sbostic       p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list));
341161768Sbostic       p->next = extern_head;
341261768Sbostic       p->name = name;
341361768Sbostic       p->size = len;
341461768Sbostic       extern_head = p;
341561768Sbostic     }
341661768Sbostic   return 0;
341761768Sbostic }
341861768Sbostic 
341961768Sbostic 
342061768Sbostic /* Compute a string to use as a temporary file name.  */
342161768Sbostic 
342261768Sbostic static FILE *
make_temp_file()342361768Sbostic make_temp_file ()
342461768Sbostic {
342561768Sbostic   FILE *stream;
342661768Sbostic   char *base = getenv ("TMPDIR");
342761768Sbostic   int len;
342861768Sbostic 
342961768Sbostic   if (base == (char *)0)
343061768Sbostic     {
343161768Sbostic #ifdef P_tmpdir
343261768Sbostic       if (access (P_tmpdir, R_OK | W_OK) == 0)
343361768Sbostic 	base = P_tmpdir;
343461768Sbostic       else
343561768Sbostic #endif
343661768Sbostic 	if (access ("/usr/tmp", R_OK | W_OK) == 0)
343761768Sbostic 	  base = "/usr/tmp/";
343861768Sbostic 	else
343961768Sbostic 	  base = "/tmp/";
344061768Sbostic     }
344161768Sbostic 
344261768Sbostic   len = strlen (base);
344361768Sbostic   temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX"));
344461768Sbostic   strcpy (temp_filename, base);
344561768Sbostic   if (len > 0 && temp_filename[len-1] != '/')
344661768Sbostic     temp_filename[len++] = '/';
344761768Sbostic 
344861768Sbostic   strcpy (temp_filename + len, "ccXXXXXX");
344961768Sbostic   mktemp (temp_filename);
345061768Sbostic 
345161768Sbostic   stream = fopen (temp_filename, "w+");
345261768Sbostic   if (!stream)
345361768Sbostic     pfatal_with_name (temp_filename);
345461768Sbostic 
345561768Sbostic   unlink (temp_filename);
345661768Sbostic   return stream;
345761768Sbostic }
345861768Sbostic 
345961768Sbostic 
346061768Sbostic /* Emit a new filename to a stream.  If this is MIPS ECOFF, watch out
346161768Sbostic    for .file's that start within a function.  If we are smuggling stabs, try to
346261768Sbostic    put out a MIPS ECOFF file and a stab.  */
346361768Sbostic 
346461768Sbostic void
mips_output_filename(stream,name)346561768Sbostic mips_output_filename (stream, name)
346661768Sbostic      FILE *stream;
346761768Sbostic      char *name;
346861768Sbostic {
346961768Sbostic   static int first_time = TRUE;
347061768Sbostic   char ltext_label_name[100];
347161768Sbostic 
347261768Sbostic   if (first_time)
347361768Sbostic     {
347461768Sbostic       first_time = FALSE;
347561768Sbostic       SET_FILE_NUMBER ();
347661768Sbostic       current_function_file = name;
347761768Sbostic       fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name);
347861768Sbostic       if (!TARGET_GAS && write_symbols == DBX_DEBUG)
347961768Sbostic 	fprintf (stream, "\t#@stabs\n");
348061768Sbostic     }
348161768Sbostic 
3482*61769Sbostic   else if (write_symbols == DBX_DEBUG)
348361768Sbostic     {
348461768Sbostic       ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
348561768Sbostic       fprintf (stream, "%s \"%s\",%d,0,0,%s\n", ASM_STABS_OP,
348661768Sbostic 	       name, N_SOL, &ltext_label_name[1]);
348761768Sbostic     }
348861768Sbostic 
348961768Sbostic   else if (name != current_function_file
349061768Sbostic       && strcmp (name, current_function_file) != 0)
349161768Sbostic     {
349261768Sbostic       if (inside_function && !TARGET_GAS)
349361768Sbostic 	{
349461768Sbostic 	  if (!file_in_function_warning)
349561768Sbostic 	    {
349661768Sbostic 	      file_in_function_warning = TRUE;
349761768Sbostic 	      ignore_line_number = TRUE;
349861768Sbostic 	      warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
349961768Sbostic 	    }
350061768Sbostic 
350161768Sbostic 	  fprintf (stream, "\t#.file\t%d \"%s\"\n", num_source_filenames, name);
350261768Sbostic 	}
350361768Sbostic 
350461768Sbostic       else
350561768Sbostic 	{
350661768Sbostic 	  SET_FILE_NUMBER ();
350761768Sbostic 	  current_function_file = name;
350861768Sbostic 	  fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name);
350961768Sbostic 	}
351061768Sbostic     }
351161768Sbostic }
351261768Sbostic 
351361768Sbostic 
351461768Sbostic /* Emit a linenumber.  For encapsulated stabs, we need to put out a stab
351561768Sbostic    as well as a .loc, since it is possible that MIPS ECOFF might not be
351661768Sbostic    able to represent the location for inlines that come from a different
351761768Sbostic    file.  */
351861768Sbostic 
351961768Sbostic void
mips_output_lineno(stream,line)352061768Sbostic mips_output_lineno (stream, line)
352161768Sbostic      FILE *stream;
352261768Sbostic      int line;
352361768Sbostic {
3524*61769Sbostic   if (write_symbols == DBX_DEBUG)
352561768Sbostic     {
3526*61769Sbostic       fprintf (stream, "\t%s %d,0,%d\n", ASM_STABD_OP, N_SLINE, line);
352761768Sbostic     }
352861768Sbostic 
352961768Sbostic   else
353061768Sbostic     {
353161768Sbostic       fprintf (stream, "\n\t%s.loc\t%d %d\n",
353261768Sbostic 	       (ignore_line_number) ? "#" : "",
353361768Sbostic 	       num_source_filenames, line);
353461768Sbostic 
353561768Sbostic       LABEL_AFTER_LOC (stream);
353661768Sbostic     }
353761768Sbostic }
353861768Sbostic 
353961768Sbostic 
354061768Sbostic /* If defined, a C statement to be executed just prior to the
354161768Sbostic    output of assembler code for INSN, to modify the extracted
354261768Sbostic    operands so they will be output differently.
354361768Sbostic 
354461768Sbostic    Here the argument OPVEC is the vector containing the operands
354561768Sbostic    extracted from INSN, and NOPERANDS is the number of elements of
354661768Sbostic    the vector which contain meaningful data for this insn.  The
354761768Sbostic    contents of this vector are what will be used to convert the
354861768Sbostic    insn template into assembler code, so you can change the
354961768Sbostic    assembler output by changing the contents of the vector.
355061768Sbostic 
355161768Sbostic    We use it to check if the current insn needs a nop in front of it
355261768Sbostic    because of load delays, and also to update the delay slot
355361768Sbostic    statistics.  */
355461768Sbostic 
355561768Sbostic void
final_prescan_insn(insn,opvec,noperands)355661768Sbostic final_prescan_insn (insn, opvec, noperands)
355761768Sbostic      rtx insn;
355861768Sbostic      rtx opvec[];
355961768Sbostic      int noperands;
356061768Sbostic {
356161768Sbostic   if (dslots_number_nops > 0)
356261768Sbostic     {
356361768Sbostic       rtx pattern = PATTERN (insn);
356461768Sbostic       int length = get_attr_length (insn);
356561768Sbostic 
356661768Sbostic       /* Do we need to emit a NOP? */
356761768Sbostic       if (length == 0
356861768Sbostic 	  || (mips_load_reg  != (rtx)0 && reg_mentioned_p (mips_load_reg,  pattern))
356961768Sbostic 	  || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern))
357061768Sbostic 	  || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern))
357161768Sbostic 	  || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern)))
357261768Sbostic 	fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
357361768Sbostic 
357461768Sbostic       else
357561768Sbostic 	dslots_load_filled++;
357661768Sbostic 
357761768Sbostic       while (--dslots_number_nops > 0)
357861768Sbostic 	fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
357961768Sbostic 
358061768Sbostic       mips_load_reg  = (rtx)0;
358161768Sbostic       mips_load_reg2 = (rtx)0;
358261768Sbostic       mips_load_reg3 = (rtx)0;
358361768Sbostic       mips_load_reg4 = (rtx)0;
358461768Sbostic 
358561768Sbostic       if (set_noreorder && --set_noreorder == 0)
358661768Sbostic 	fputs ("\t.set\treorder\n", asm_out_file);
358761768Sbostic     }
358861768Sbostic 
358961768Sbostic   if (TARGET_STATS)
359061768Sbostic     {
359161768Sbostic       enum rtx_code code = GET_CODE (insn);
359261768Sbostic       if (code == JUMP_INSN || code == CALL_INSN)
359361768Sbostic 	dslots_jump_total++;
359461768Sbostic     }
359561768Sbostic }
359661768Sbostic 
359761768Sbostic 
359861768Sbostic /* Output at beginning of assembler file.
359961768Sbostic    If we are optimizing to use the global pointer, create a temporary
360061768Sbostic    file to hold all of the text stuff, and write it out to the end.
360161768Sbostic    This is needed because the MIPS assembler is evidently one pass,
360261768Sbostic    and if it hasn't seen the relevant .comm/.lcomm/.extern/.sdata
360361768Sbostic    declaration when the code is processed, it generates a two
360461768Sbostic    instruction sequence.  */
360561768Sbostic 
360661768Sbostic void
mips_asm_file_start(stream)360761768Sbostic mips_asm_file_start (stream)
360861768Sbostic      FILE *stream;
360961768Sbostic {
361061768Sbostic   ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
361161768Sbostic 
361261768Sbostic   /* Versions of the MIPS assembler before 2.20 generate errors
361361768Sbostic      if a branch inside of a .set noreorder section jumps to a
361461768Sbostic      label outside of the .set noreorder section.  Revision 2.20
361561768Sbostic      just set nobopt silently rather than fixing the bug.  */
361661768Sbostic 
361761768Sbostic   if (TARGET_MIPS_AS && optimize && flag_delayed_branch)
361861768Sbostic     fprintf (stream, "\t.set\tnobopt\n");
361961768Sbostic 
362061768Sbostic   /* Generate the pseudo ops that the Pyramid based System V.4 wants.  */
362161768Sbostic   if (TARGET_ABICALLS)
362261768Sbostic     fprintf (stream, "\t.abicalls\n");
362361768Sbostic 
362461768Sbostic   if (TARGET_GP_OPT)
362561768Sbostic     {
362661768Sbostic       asm_out_data_file = stream;
362761768Sbostic       asm_out_text_file = make_temp_file ();
362861768Sbostic     }
362961768Sbostic   else
363061768Sbostic     asm_out_data_file = asm_out_text_file = stream;
363161768Sbostic 
363261768Sbostic   if (TARGET_NAME_REGS)
363361768Sbostic     fprintf (asm_out_file, "#include <regdef.h>\n");
363461768Sbostic 
363561768Sbostic   print_options (stream);
363661768Sbostic }
363761768Sbostic 
363861768Sbostic 
363961768Sbostic /* If we are optimizing the global pointer, emit the text section now
364061768Sbostic    and any small externs which did not have .comm, etc that are
364161768Sbostic    needed.  Also, give a warning if the data area is more than 32K and
364261768Sbostic    -pic because 3 instructions are needed to reference the data
364361768Sbostic    pointers.  */
364461768Sbostic 
364561768Sbostic void
mips_asm_file_end(file)364661768Sbostic mips_asm_file_end (file)
364761768Sbostic      FILE *file;
364861768Sbostic {
364961768Sbostic   char buffer[8192];
365061768Sbostic   tree name_tree;
365161768Sbostic   struct extern_list *p;
365261768Sbostic   int len;
365361768Sbostic 
365461768Sbostic   if (HALF_PIC_P ())
365561768Sbostic     HALF_PIC_FINISH (file);
365661768Sbostic 
365761768Sbostic   if (TARGET_GP_OPT)
365861768Sbostic     {
365961768Sbostic       if (extern_head)
366061768Sbostic 	fputs ("\n", file);
366161768Sbostic 
366261768Sbostic       for (p = extern_head; p != 0; p = p->next)
366361768Sbostic 	{
366461768Sbostic 	  name_tree = get_identifier (p->name);
366561768Sbostic 
366661768Sbostic 	  /* Positively ensure only one .extern for any given symbol.  */
366761768Sbostic 	  if (! TREE_ASM_WRITTEN (name_tree))
366861768Sbostic 	    {
366961768Sbostic 	      TREE_ASM_WRITTEN (name_tree) = 1;
367061768Sbostic 	      fputs ("\t.extern\t", file);
367161768Sbostic 	      assemble_name (file, p->name);
367261768Sbostic 	      fprintf (file, ", %d\n", p->size);
367361768Sbostic 	    }
367461768Sbostic 	}
367561768Sbostic 
367661768Sbostic       fprintf (file, "\n\t.text\n");
367761768Sbostic       rewind (asm_out_text_file);
367861768Sbostic       if (ferror (asm_out_text_file))
367961768Sbostic 	fatal_io_error (temp_filename);
368061768Sbostic 
368161768Sbostic       while ((len = fread (buffer, 1, sizeof (buffer), asm_out_text_file)) > 0)
368261768Sbostic 	if (fwrite (buffer, 1, len, file) != len)
368361768Sbostic 	  pfatal_with_name (asm_file_name);
368461768Sbostic 
368561768Sbostic       if (len < 0)
368661768Sbostic 	pfatal_with_name (temp_filename);
368761768Sbostic 
368861768Sbostic       if (fclose (asm_out_text_file) != 0)
368961768Sbostic 	pfatal_with_name (temp_filename);
369061768Sbostic     }
369161768Sbostic }
369261768Sbostic 
369361768Sbostic 
369461768Sbostic /* Emit either a label, .comm, or .lcomm directive, and mark
369561768Sbostic    that the symbol is used, so that we don't emit an .extern
369661768Sbostic    for it in mips_asm_file_end.  */
369761768Sbostic 
369861768Sbostic void
mips_declare_object(stream,name,init_string,final_string,size)369961768Sbostic mips_declare_object (stream, name, init_string, final_string, size)
370061768Sbostic      FILE *stream;
370161768Sbostic      char *name;
370261768Sbostic      char *init_string;
370361768Sbostic      char *final_string;
370461768Sbostic      int size;
370561768Sbostic {
370661768Sbostic   fputs (init_string, stream);		/* "", "\t.comm\t", or "\t.lcomm\t" */
370761768Sbostic   assemble_name (stream, name);
370861768Sbostic   fprintf (stream, final_string, size);	/* ":\n", ",%u\n", ",%u\n" */
370961768Sbostic 
371061768Sbostic   if (TARGET_GP_OPT && mips_section_threshold != 0)
371161768Sbostic     {
371261768Sbostic       tree name_tree = get_identifier (name);
371361768Sbostic       TREE_ASM_WRITTEN (name_tree) = 1;
371461768Sbostic     }
371561768Sbostic }
371661768Sbostic 
371761768Sbostic 
371861768Sbostic /* Output a double precision value to the assembler.  If both the
371961768Sbostic    host and target are IEEE, emit the values in hex.  */
372061768Sbostic 
372161768Sbostic void
mips_output_double(stream,value)372261768Sbostic mips_output_double (stream, value)
372361768Sbostic      FILE *stream;
372461768Sbostic      REAL_VALUE_TYPE value;
372561768Sbostic {
372661768Sbostic #ifdef REAL_VALUE_TO_TARGET_DOUBLE
372761768Sbostic   long value_long[2];
372861768Sbostic   REAL_VALUE_TO_TARGET_DOUBLE (value, value_long);
372961768Sbostic 
373061768Sbostic   fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n",
373161768Sbostic 	   value_long[0], value, value_long[1]);
373261768Sbostic #else
373361768Sbostic   fprintf (stream, "\t.double\t%.20g\n", value);
373461768Sbostic #endif
373561768Sbostic }
373661768Sbostic 
373761768Sbostic 
373861768Sbostic /* Output a single precision value to the assembler.  If both the
373961768Sbostic    host and target are IEEE, emit the values in hex.  */
374061768Sbostic 
374161768Sbostic void
mips_output_float(stream,value)374261768Sbostic mips_output_float (stream, value)
374361768Sbostic      FILE *stream;
374461768Sbostic      REAL_VALUE_TYPE value;
374561768Sbostic {
374661768Sbostic #ifdef REAL_VALUE_TO_TARGET_SINGLE
374761768Sbostic   long value_long;
374861768Sbostic   REAL_VALUE_TO_TARGET_SINGLE (value, value_long);
374961768Sbostic 
375061768Sbostic   fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value);
375161768Sbostic #else
375261768Sbostic   fprintf (stream, "\t.float\t%.12g\n", value);
375361768Sbostic #endif
375461768Sbostic }
375561768Sbostic 
375661768Sbostic 
375761768Sbostic /* Return TRUE if any register used in the epilogue is used.  This to insure
375861768Sbostic    any insn put into the epilogue delay slots is safe.  */
375961768Sbostic 
376061768Sbostic int
epilogue_reg_mentioned_p(insn)376161768Sbostic epilogue_reg_mentioned_p (insn)
376261768Sbostic      rtx insn;
376361768Sbostic {
376461768Sbostic   register char *fmt;
376561768Sbostic   register int i;
376661768Sbostic   register enum rtx_code code;
376761768Sbostic   register int regno;
376861768Sbostic 
376961768Sbostic   if (insn == (rtx)0)
377061768Sbostic     return 0;
377161768Sbostic 
377261768Sbostic   if (GET_CODE (insn) == LABEL_REF)
377361768Sbostic     return 0;
377461768Sbostic 
377561768Sbostic   code = GET_CODE (insn);
377661768Sbostic   switch (code)
377761768Sbostic     {
377861768Sbostic     case REG:
377961768Sbostic       regno = REGNO (insn);
378061768Sbostic       if (regno == STACK_POINTER_REGNUM)
378161768Sbostic 	return 1;
378261768Sbostic 
378361768Sbostic       if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)
378461768Sbostic 	return 1;
378561768Sbostic 
378661768Sbostic       if (!call_used_regs[regno])
378761768Sbostic 	return 1;
378861768Sbostic 
378961768Sbostic       if (regno != MIPS_TEMP1_REGNUM && regno != MIPS_TEMP2_REGNUM)
379061768Sbostic 	return 0;
379161768Sbostic 
379261768Sbostic       if (!current_frame_info.initialized)
379361768Sbostic 	compute_frame_size (get_frame_size ());
379461768Sbostic 
379561768Sbostic       return (current_frame_info.total_size >= 32768);
379661768Sbostic 
379761768Sbostic     case SCRATCH:
379861768Sbostic     case CC0:
379961768Sbostic     case PC:
380061768Sbostic     case CONST_INT:
380161768Sbostic     case CONST_DOUBLE:
380261768Sbostic       return 0;
380361768Sbostic     }
380461768Sbostic 
380561768Sbostic   fmt = GET_RTX_FORMAT (code);
380661768Sbostic   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
380761768Sbostic     {
380861768Sbostic       if (fmt[i] == 'E')
380961768Sbostic 	{
381061768Sbostic 	  register int j;
381161768Sbostic 	  for (j = XVECLEN (insn, i) - 1; j >= 0; j--)
381261768Sbostic 	    if (epilogue_reg_mentioned_p (XVECEXP (insn, i, j)))
381361768Sbostic 	      return 1;
381461768Sbostic 	}
381561768Sbostic       else if (fmt[i] == 'e' && epilogue_reg_mentioned_p (XEXP (insn, i)))
381661768Sbostic 	return 1;
381761768Sbostic     }
381861768Sbostic 
381961768Sbostic   return 0;
382061768Sbostic }
382161768Sbostic 
382261768Sbostic 
382361768Sbostic /* Return the bytes needed to compute the frame pointer from the current
382461768Sbostic    stack pointer.
382561768Sbostic 
382661768Sbostic    Mips stack frames look like:
382761768Sbostic 
382861768Sbostic              Before call		        After call
382961768Sbostic         +-----------------------+	+-----------------------+
383061768Sbostic    high |			|       |      			|
383161768Sbostic    mem. |		        |	|			|
383261768Sbostic         |  caller's temps.    	|       |  caller's temps.    	|
383361768Sbostic 	|       		|       |       	        |
383461768Sbostic         +-----------------------+	+-----------------------+
383561768Sbostic  	|       		|	|		        |
383661768Sbostic         |  arguments on stack.  |	|  arguments on stack.  |
383761768Sbostic 	|       		|	|			|
383861768Sbostic         +-----------------------+	+-----------------------+
383961768Sbostic  	|  4 words to save     	|	|  4 words to save	|
384061768Sbostic 	|  arguments passed	|	|  arguments passed	|
384161768Sbostic 	|  in registers, even	|	|  in registers, even	|
384261768Sbostic     SP->|  if not passed.       |   FP->|  if not passed.	|
384361768Sbostic 	+-----------------------+       +-----------------------+
384461768Sbostic 					|			|
384561768Sbostic 					|  GP save for V.4 abi	|
384661768Sbostic 					|			|
384761768Sbostic 					+-----------------------+
384861768Sbostic 					|		        |
384961768Sbostic                                         |  fp register save     |
385061768Sbostic 					|			|
385161768Sbostic 					+-----------------------+
385261768Sbostic 					|		        |
385361768Sbostic                                         |  gp register save     |
385461768Sbostic                                         |       		|
385561768Sbostic 					+-----------------------+
385661768Sbostic 					|			|
385761768Sbostic 					|  local variables	|
385861768Sbostic 					|			|
385961768Sbostic 					+-----------------------+
386061768Sbostic 					|			|
386161768Sbostic                                         |  alloca allocations   |
386261768Sbostic         				|			|
386361768Sbostic 					+-----------------------+
386461768Sbostic 					|			|
386561768Sbostic                                         |  arguments on stack   |
386661768Sbostic         				|		        |
386761768Sbostic 					+-----------------------+
386861768Sbostic                                         |  4 words to save      |
386961768Sbostic 					|  arguments passed     |
387061768Sbostic                                         |  in registers, even   |
387161768Sbostic    low                              SP->|  if not passed.       |
387261768Sbostic    memory        			+-----------------------+
387361768Sbostic 
387461768Sbostic */
387561768Sbostic 
387661768Sbostic long
compute_frame_size(size)387761768Sbostic compute_frame_size (size)
387861768Sbostic      int size;			/* # of var. bytes allocated */
387961768Sbostic {
388061768Sbostic   int regno;
388161768Sbostic   long total_size;		/* # bytes that the entire frame takes up */
388261768Sbostic   long var_size;		/* # bytes that variables take up */
388361768Sbostic   long args_size;		/* # bytes that outgoing arguments take up */
388461768Sbostic   long extra_size;		/* # extra bytes */
388561768Sbostic   long gp_reg_rounded;		/* # bytes needed to store gp after rounding */
388661768Sbostic   long gp_reg_size;		/* # bytes needed to store gp regs */
388761768Sbostic   long fp_reg_size;		/* # bytes needed to store fp regs */
388861768Sbostic   long mask;			/* mask of saved gp registers */
388961768Sbostic   long fmask;			/* mask of saved fp registers */
389061768Sbostic   int  fp_inc;			/* 1 or 2 depending on the size of fp regs */
389161768Sbostic   long fp_bits;			/* bitmask to use for each fp register */
389261768Sbostic 
389361768Sbostic   gp_reg_size	 = 0;
389461768Sbostic   fp_reg_size	 = 0;
389561768Sbostic   mask		 = 0;
389661768Sbostic   fmask		 = 0;
389761768Sbostic   extra_size	 = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
389861768Sbostic   var_size	 = MIPS_STACK_ALIGN (size);
389961768Sbostic   args_size	 = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
390061768Sbostic 
390161768Sbostic   /* The MIPS 3.0 linker does not like functions that dynamically
390261768Sbostic      allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
390361768Sbostic      looks like we are trying to create a second frame pointer to the
390461768Sbostic      function, so allocate some stack space to make it happy.  */
390561768Sbostic 
390661768Sbostic   if (args_size == 0 && current_function_calls_alloca)
390761768Sbostic     args_size = 4*UNITS_PER_WORD;
390861768Sbostic 
390961768Sbostic   total_size = var_size + args_size + extra_size;
391061768Sbostic 
391161768Sbostic   /* Calculate space needed for gp registers.  */
391261768Sbostic   for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
391361768Sbostic     {
391461768Sbostic       if (MUST_SAVE_REGISTER (regno))
391561768Sbostic 	{
391661768Sbostic 	  gp_reg_size += UNITS_PER_WORD;
391761768Sbostic 	  mask |= 1L << (regno - GP_REG_FIRST);
391861768Sbostic 	}
391961768Sbostic     }
392061768Sbostic 
392161768Sbostic   /* Calculate space needed for fp registers.  */
392261768Sbostic   if (TARGET_FLOAT64)
392361768Sbostic     {
392461768Sbostic       fp_inc = 1;
392561768Sbostic       fp_bits = 1;
392661768Sbostic     }
392761768Sbostic   else
392861768Sbostic     {
392961768Sbostic       fp_inc = 2;
393061768Sbostic       fp_bits = 3;
393161768Sbostic     }
393261768Sbostic 
393361768Sbostic   for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += fp_inc)
393461768Sbostic     {
393561768Sbostic       if (regs_ever_live[regno] && !call_used_regs[regno])
393661768Sbostic 	{
393761768Sbostic 	  fp_reg_size += 2*UNITS_PER_WORD;
393861768Sbostic 	  fmask |= fp_bits << (regno - FP_REG_FIRST);
393961768Sbostic 	}
394061768Sbostic     }
394161768Sbostic 
394261768Sbostic   gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
394361768Sbostic   total_size += gp_reg_rounded + fp_reg_size;
394461768Sbostic 
394561768Sbostic   if (total_size == extra_size)
394661768Sbostic     total_size = extra_size = 0;
394761768Sbostic 
394861768Sbostic   /* Save other computed information.  */
394961768Sbostic   current_frame_info.total_size  = total_size;
395061768Sbostic   current_frame_info.var_size    = var_size;
395161768Sbostic   current_frame_info.args_size   = args_size;
395261768Sbostic   current_frame_info.extra_size  = extra_size;
395361768Sbostic   current_frame_info.gp_reg_size = gp_reg_size;
395461768Sbostic   current_frame_info.fp_reg_size = fp_reg_size;
395561768Sbostic   current_frame_info.mask	 = mask;
395661768Sbostic   current_frame_info.fmask	 = fmask;
395761768Sbostic   current_frame_info.initialized = reload_completed;
395861768Sbostic   current_frame_info.num_gp	 = gp_reg_size / UNITS_PER_WORD;
395961768Sbostic   current_frame_info.num_fp	 = fp_reg_size / (2*UNITS_PER_WORD);
396061768Sbostic 
396161768Sbostic   if (mask)
396261768Sbostic     {
396361768Sbostic       unsigned long offset = args_size + var_size + gp_reg_size - UNITS_PER_WORD;
396461768Sbostic       current_frame_info.gp_sp_offset = offset;
396561768Sbostic       current_frame_info.gp_save_offset = offset - total_size;
396661768Sbostic     }
396761768Sbostic   else
396861768Sbostic     {
396961768Sbostic       current_frame_info.gp_sp_offset = 0;
397061768Sbostic       current_frame_info.gp_save_offset = 0;
397161768Sbostic     }
397261768Sbostic 
397361768Sbostic 
397461768Sbostic   if (fmask)
397561768Sbostic     {
397661768Sbostic       unsigned long offset = args_size + var_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD;
397761768Sbostic       current_frame_info.fp_sp_offset = offset;
397861768Sbostic       current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD;
397961768Sbostic     }
398061768Sbostic   else
398161768Sbostic     {
398261768Sbostic       current_frame_info.fp_sp_offset = 0;
398361768Sbostic       current_frame_info.fp_save_offset = 0;
398461768Sbostic     }
398561768Sbostic 
398661768Sbostic   /* Ok, we're done.  */
398761768Sbostic   return total_size;
398861768Sbostic }
398961768Sbostic 
399061768Sbostic 
399161768Sbostic /* Common code to emit the insns (or to write the instructions to a file)
399261768Sbostic    to save/restore registers.
399361768Sbostic 
399461768Sbostic    Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg)
399561768Sbostic    is not modified within save_restore_insns.  */
399661768Sbostic 
399761768Sbostic #define BITSET_P(value,bit) (((value) & (1L << (bit))) != 0)
399861768Sbostic 
399961768Sbostic static void
save_restore_insns(store_p,large_reg,large_offset,file)400061768Sbostic save_restore_insns (store_p, large_reg, large_offset, file)
400161768Sbostic      int store_p;		/* true if this is prologue */
400261768Sbostic      rtx large_reg;		/* register holding large offset constant or NULL */
400361768Sbostic      long large_offset;		/* large constant offset value */
400461768Sbostic      FILE *file;		/* file to write instructions to instead of making RTL */
400561768Sbostic {
400661768Sbostic   long mask		= current_frame_info.mask;
400761768Sbostic   long fmask		= current_frame_info.fmask;
400861768Sbostic   int regno;
400961768Sbostic   rtx base_reg_rtx;
401061768Sbostic   long base_offset;
401161768Sbostic   long gp_offset;
401261768Sbostic   long fp_offset;
401361768Sbostic   long end_offset;
401461768Sbostic 
401561768Sbostic   if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST))
401661768Sbostic     abort ();
401761768Sbostic 
401861768Sbostic   if (mask == 0 && fmask == 0)
401961768Sbostic     return;
402061768Sbostic 
402161768Sbostic   /* Save registers starting from high to low.  The debuggers prefer
402261768Sbostic      at least the return register be stored at func+4, and also it
402361768Sbostic      allows us not to need a nop in the epilog if at least one
402461768Sbostic      register is reloaded in addition to return address.  */
402561768Sbostic 
402661768Sbostic   /* Save GP registers if needed.  */
402761768Sbostic   if (mask)
402861768Sbostic     {
402961768Sbostic       /* Pick which pointer to use as a base register.  For small
403061768Sbostic 	 frames, just use the stack pointer.  Otherwise, use a
403161768Sbostic 	 temporary register.  Save 2 cycles if the save area is near
403261768Sbostic 	 the end of a large frame, by reusing the constant created in
403361768Sbostic 	 the prologue/epilogue to adjust the stack frame.  */
403461768Sbostic 
403561768Sbostic       gp_offset  = current_frame_info.gp_sp_offset;
403661768Sbostic       end_offset = gp_offset - (current_frame_info.gp_reg_size - UNITS_PER_WORD);
403761768Sbostic 
403861768Sbostic       if (gp_offset < 0 || end_offset < 0)
403961768Sbostic 	fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.",
404061768Sbostic 	       gp_offset, end_offset);
404161768Sbostic 
404261768Sbostic       else if (gp_offset < 32768)
404361768Sbostic 	{
404461768Sbostic 	  base_reg_rtx = stack_pointer_rtx;
404561768Sbostic 	  base_offset  = 0;
404661768Sbostic 	}
404761768Sbostic 
404861768Sbostic       else if (large_reg != (rtx)0
404961768Sbostic 	       && (((unsigned long)(large_offset - gp_offset))  < 32768)
405061768Sbostic 	       && (((unsigned long)(large_offset - end_offset)) < 32768))
405161768Sbostic 	{
405261768Sbostic 	  base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
405361768Sbostic 	  base_offset  = large_offset;
405461768Sbostic 	  if (file == (FILE *)0)
405561768Sbostic 	    emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
405661768Sbostic 	  else
405761768Sbostic 	    fprintf (file, "\taddu\t%s,%s,%s\n",
405861768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
405961768Sbostic 		     reg_names[REGNO (large_reg)],
406061768Sbostic 		     reg_names[STACK_POINTER_REGNUM]);
406161768Sbostic 	}
406261768Sbostic 
406361768Sbostic       else
406461768Sbostic 	{
406561768Sbostic 	  base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
406661768Sbostic 	  base_offset  = gp_offset;
406761768Sbostic 	  if (file == (FILE *)0)
406861768Sbostic 	    {
406961768Sbostic 	      emit_move_insn (base_reg_rtx, GEN_INT (gp_offset));
407061768Sbostic 	      emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
407161768Sbostic 	    }
407261768Sbostic 	  else
407361768Sbostic 	    fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
407461768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
407561768Sbostic 		     (long)base_offset,
407661768Sbostic 		     (long)base_offset,
407761768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
407861768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
407961768Sbostic 		     reg_names[STACK_POINTER_REGNUM]);
408061768Sbostic 	}
408161768Sbostic 
408261768Sbostic       for  (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
408361768Sbostic 	{
408461768Sbostic 	  if (BITSET_P (mask, regno - GP_REG_FIRST))
408561768Sbostic 	    {
408661768Sbostic 	      if (file == (FILE *)0)
408761768Sbostic 		{
408861768Sbostic 		  rtx reg_rtx = gen_rtx (REG, Pmode, regno);
408961768Sbostic 		  rtx mem_rtx = gen_rtx (MEM, Pmode,
409061768Sbostic 					 gen_rtx (PLUS, Pmode, base_reg_rtx,
409161768Sbostic 						  GEN_INT (gp_offset - base_offset)));
409261768Sbostic 
409361768Sbostic 		  if (store_p)
409461768Sbostic 		    emit_move_insn (mem_rtx, reg_rtx);
409561768Sbostic 		  else
409661768Sbostic 		    emit_move_insn (reg_rtx, mem_rtx);
409761768Sbostic 		}
409861768Sbostic 	      else
409961768Sbostic 		fprintf (file, "\t%s\t%s,%ld(%s)\n",
410061768Sbostic 			 (store_p) ? "sw" : "lw",
410161768Sbostic 			 reg_names[regno],
410261768Sbostic 			 gp_offset - base_offset,
410361768Sbostic 			 reg_names[REGNO(base_reg_rtx)]);
410461768Sbostic 
410561768Sbostic 	      gp_offset -= UNITS_PER_WORD;
410661768Sbostic 	    }
410761768Sbostic 	}
410861768Sbostic     }
410961768Sbostic   else
411061768Sbostic     {
411161768Sbostic       base_reg_rtx = (rtx)0;		/* Make sure these are initialzed */
411261768Sbostic       base_offset  = 0;
411361768Sbostic     }
411461768Sbostic 
411561768Sbostic   /* Save floating point registers if needed.  */
411661768Sbostic   if (fmask)
411761768Sbostic     {
411861768Sbostic       int fp_inc = (TARGET_FLOAT64) ? 1 : 2;
411961768Sbostic 
412061768Sbostic       /* Pick which pointer to use as a base register.  */
412161768Sbostic       fp_offset  = current_frame_info.fp_sp_offset;
412261768Sbostic       end_offset = fp_offset - (current_frame_info.fp_reg_size - UNITS_PER_WORD);
412361768Sbostic 
412461768Sbostic       if (fp_offset < 0 || end_offset < 0)
412561768Sbostic 	fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.",
412661768Sbostic 	       fp_offset, end_offset);
412761768Sbostic 
412861768Sbostic       else if (fp_offset < 32768)
412961768Sbostic 	{
413061768Sbostic 	  base_reg_rtx = stack_pointer_rtx;
413161768Sbostic 	  base_offset  = 0;
413261768Sbostic 	}
413361768Sbostic 
413461768Sbostic       else if (base_reg_rtx != (rtx)0
413561768Sbostic 	       && (((unsigned long)(base_offset - fp_offset))  < 32768)
413661768Sbostic 	       && (((unsigned long)(base_offset - end_offset)) < 32768))
413761768Sbostic 	{
413861768Sbostic 	  ;			/* already set up for gp registers above */
413961768Sbostic 	}
414061768Sbostic 
414161768Sbostic       else if (large_reg != (rtx)0
414261768Sbostic 	       && (((unsigned long)(large_offset - fp_offset))  < 32768)
414361768Sbostic 	       && (((unsigned long)(large_offset - end_offset)) < 32768))
414461768Sbostic 	{
414561768Sbostic 	  base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
414661768Sbostic 	  base_offset  = large_offset;
414761768Sbostic 	  if (file == (FILE *)0)
414861768Sbostic 	    emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx));
414961768Sbostic 	  else
415061768Sbostic 	    fprintf (file, "\taddu\t%s,%s,%s\n",
415161768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
415261768Sbostic 		     reg_names[REGNO (large_reg)],
415361768Sbostic 		     reg_names[STACK_POINTER_REGNUM]);
415461768Sbostic 	}
415561768Sbostic 
415661768Sbostic       else
415761768Sbostic 	{
415861768Sbostic 	  base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM);
415961768Sbostic 	  base_offset  = fp_offset;
416061768Sbostic 	  if (file == (FILE *)0)
416161768Sbostic 	    {
416261768Sbostic 	      emit_move_insn (base_reg_rtx, GEN_INT (fp_offset));
416361768Sbostic 	      emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx));
416461768Sbostic 	    }
416561768Sbostic 	  else
416661768Sbostic 	    fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n",
416761768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
416861768Sbostic 		     (long)base_offset,
416961768Sbostic 		     (long)base_offset,
417061768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
417161768Sbostic 		     reg_names[MIPS_TEMP2_REGNUM],
417261768Sbostic 		     reg_names[STACK_POINTER_REGNUM]);
417361768Sbostic 	}
417461768Sbostic 
417561768Sbostic       for  (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc)
417661768Sbostic 	{
417761768Sbostic 	  if (BITSET_P (fmask, regno - FP_REG_FIRST))
417861768Sbostic 	    {
417961768Sbostic 	      if (file == (FILE *)0)
418061768Sbostic 		{
418161768Sbostic 		  rtx reg_rtx = gen_rtx (REG, DFmode, regno);
418261768Sbostic 		  rtx mem_rtx = gen_rtx (MEM, DFmode,
418361768Sbostic 					 gen_rtx (PLUS, Pmode, base_reg_rtx,
418461768Sbostic 						  GEN_INT (fp_offset - base_offset)));
418561768Sbostic 
418661768Sbostic 		  if (store_p)
418761768Sbostic 		    emit_move_insn (mem_rtx, reg_rtx);
418861768Sbostic 		  else
418961768Sbostic 		    emit_move_insn (reg_rtx, mem_rtx);
419061768Sbostic 		}
419161768Sbostic 	      else
419261768Sbostic 		fprintf (file, "\t%s\t%s,%ld(%s)\n",
419361768Sbostic 			 (store_p) ? "s.d" : "l.d",
419461768Sbostic 			 reg_names[regno],
419561768Sbostic 			 fp_offset - base_offset,
419661768Sbostic 			 reg_names[REGNO(base_reg_rtx)]);
419761768Sbostic 
419861768Sbostic 
419961768Sbostic 	      fp_offset -= 2*UNITS_PER_WORD;
420061768Sbostic 	    }
420161768Sbostic 	}
420261768Sbostic     }
420361768Sbostic }
420461768Sbostic 
420561768Sbostic 
420661768Sbostic /* Set up the stack and frame (if desired) for the function.  */
420761768Sbostic 
420861768Sbostic void
function_prologue(file,size)420961768Sbostic function_prologue (file, size)
421061768Sbostic      FILE *file;
421161768Sbostic      int size;
421261768Sbostic {
421361768Sbostic   long tsize = current_frame_info.total_size;
421461768Sbostic 
421561768Sbostic   ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
421661768Sbostic 
421761768Sbostic   if (debug_info_level != DINFO_LEVEL_TERSE)
421861768Sbostic     ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
421961768Sbostic 
422061768Sbostic   inside_function = 1;
422161768Sbostic   fputs ("\t.ent\t", file);
422261768Sbostic   assemble_name (file, current_function_name);
422361768Sbostic   fputs ("\n", file);
422461768Sbostic 
422561768Sbostic   assemble_name (file, current_function_name);
422661768Sbostic   fputs (":\n", file);
422761768Sbostic 
422861768Sbostic   if (TARGET_ABICALLS)
422961768Sbostic     fprintf (file,
423061768Sbostic 	     "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
423161768Sbostic 	     reg_names[ GP_REG_FIRST + 25 ]);
423261768Sbostic 
423361768Sbostic   tsize = current_frame_info.total_size;
423461768Sbostic   if (tsize > 0 && TARGET_ABICALLS)
423561768Sbostic     fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET);
423661768Sbostic 
423761768Sbostic   fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n",
423861768Sbostic 	   reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ],
423961768Sbostic 	   tsize,
424061768Sbostic 	   reg_names[31 + GP_REG_FIRST],
424161768Sbostic 	   current_frame_info.var_size,
424261768Sbostic 	   current_frame_info.num_gp,
424361768Sbostic 	   current_frame_info.num_fp,
424461768Sbostic 	   current_function_outgoing_args_size,
424561768Sbostic 	   current_frame_info.extra_size);
424661768Sbostic 
424761768Sbostic   fprintf (file, "\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n",
424861768Sbostic 	   current_frame_info.mask,
424961768Sbostic 	   current_frame_info.gp_save_offset,
425061768Sbostic 	   current_frame_info.fmask,
425161768Sbostic 	   current_frame_info.fp_save_offset);
425261768Sbostic }
425361768Sbostic 
425461768Sbostic 
425561768Sbostic /* Expand the prologue into a bunch of separate insns.  */
425661768Sbostic 
425761768Sbostic void
mips_expand_prologue()425861768Sbostic mips_expand_prologue ()
425961768Sbostic {
426061768Sbostic   int regno;
426161768Sbostic   long tsize;
426261768Sbostic   rtx tmp_rtx	 = (rtx)0;
426361768Sbostic   char *arg_name = (char *)0;
426461768Sbostic   tree fndecl	 = current_function_decl;
426561768Sbostic   tree fntype	 = TREE_TYPE (fndecl);
426661768Sbostic   tree fnargs	 = (TREE_CODE (fntype) != METHOD_TYPE)
426761768Sbostic 			? DECL_ARGUMENTS (fndecl)
426861768Sbostic 			: 0;
426961768Sbostic   rtx next_arg_reg;
427061768Sbostic   int i;
427161768Sbostic   tree next_arg;
427261768Sbostic   tree cur_arg;
427361768Sbostic   CUMULATIVE_ARGS args_so_far;
427461768Sbostic 
427561768Sbostic   /* Determine the last argument, and get its name.  */
427661768Sbostic 
427761768Sbostic   INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0);
427861768Sbostic   regno = GP_ARG_FIRST;
427961768Sbostic 
428061768Sbostic   for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
428161768Sbostic     {
428261768Sbostic       tree type = DECL_ARG_TYPE (cur_arg);
428361768Sbostic       enum machine_mode passed_mode = TYPE_MODE (type);
428461768Sbostic       rtx entry_parm = FUNCTION_ARG (args_so_far,
428561768Sbostic 				     passed_mode,
428661768Sbostic 				     DECL_ARG_TYPE (cur_arg),
428761768Sbostic 				     1);
428861768Sbostic 
428961768Sbostic       if (entry_parm)
429061768Sbostic 	{
429161768Sbostic 	  int words;
429261768Sbostic 
429361768Sbostic 	  /* passed in a register, so will get homed automatically */
429461768Sbostic 	  if (GET_MODE (entry_parm) == BLKmode)
429561768Sbostic 	    words = (int_size_in_bytes (type) + 3) / 4;
429661768Sbostic 	  else
429761768Sbostic 	    words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
429861768Sbostic 
429961768Sbostic 	  regno = REGNO (entry_parm) + words - 1;
430061768Sbostic 	}
430161768Sbostic       else
430261768Sbostic 	{
430361768Sbostic 	  regno = GP_ARG_LAST+1;
430461768Sbostic 	  break;
430561768Sbostic 	}
430661768Sbostic 
430761768Sbostic       FUNCTION_ARG_ADVANCE (args_so_far,
430861768Sbostic 			    passed_mode,
430961768Sbostic 			    DECL_ARG_TYPE (cur_arg),
431061768Sbostic 			    1);
431161768Sbostic 
431261768Sbostic       next_arg = TREE_CHAIN (cur_arg);
431361768Sbostic       if (next_arg == (tree)0)
431461768Sbostic 	{
431561768Sbostic 	  if (DECL_NAME (cur_arg))
431661768Sbostic 	    arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg));
431761768Sbostic 
431861768Sbostic 	  break;
431961768Sbostic 	}
432061768Sbostic     }
432161768Sbostic 
432261768Sbostic   /* In order to pass small structures by value in registers
432361768Sbostic      compatibly with the MIPS compiler, we need to shift the value
432461768Sbostic      into the high part of the register.  Function_arg has encoded a
432561768Sbostic      PARALLEL rtx, holding a vector of adjustments to be made as the
432661768Sbostic      next_arg_reg variable, so we split up the insns, and emit them
432761768Sbostic      separately.  */
432861768Sbostic 
432961768Sbostic   next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
433061768Sbostic   if (next_arg_reg != (rtx)0 && GET_CODE (next_arg_reg) == PARALLEL)
433161768Sbostic     {
433261768Sbostic       rtvec adjust = XVEC (next_arg_reg, 0);
433361768Sbostic       int num = GET_NUM_ELEM (adjust);
433461768Sbostic 
433561768Sbostic       for (i = 0; i < num; i++)
433661768Sbostic 	{
433761768Sbostic 	  rtx pattern = RTVEC_ELT (adjust, i);
433861768Sbostic 	  if (GET_CODE (pattern) != SET
433961768Sbostic 	      || GET_CODE (SET_SRC (pattern)) != ASHIFT)
434061768Sbostic 	    abort_with_insn (pattern, "Insn is not a shift");
434161768Sbostic 
434261768Sbostic 	  PUT_CODE (SET_SRC (pattern), ASHIFTRT);
434361768Sbostic 	  emit_insn (pattern);
434461768Sbostic 	}
434561768Sbostic     }
434661768Sbostic 
434761768Sbostic   /* If this function is a varargs function, store any registers that
434861768Sbostic      would normally hold arguments ($4 - $7) on the stack.  */
434961768Sbostic   if ((TYPE_ARG_TYPES (fntype) != 0
435061768Sbostic        && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
435161768Sbostic       || (arg_name != (char *)0
435261768Sbostic 	  && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0)
435361768Sbostic 	      || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0))))
435461768Sbostic     {
435561768Sbostic       for (; regno <= GP_ARG_LAST; regno++)
435661768Sbostic 	{
435761768Sbostic 	  rtx ptr = stack_pointer_rtx;
435861768Sbostic 	  if (regno != GP_ARG_FIRST)
435961768Sbostic 	    ptr = gen_rtx (PLUS, Pmode, ptr,
436061768Sbostic 			   GEN_INT ((regno - GP_ARG_FIRST) * UNITS_PER_WORD));
436161768Sbostic 
436261768Sbostic 	  emit_move_insn (gen_rtx (MEM, Pmode, ptr), gen_rtx (REG, Pmode, regno));
436361768Sbostic 	}
436461768Sbostic     }
436561768Sbostic 
436661768Sbostic   tsize = compute_frame_size (get_frame_size ());
436761768Sbostic   if (tsize > 0)
436861768Sbostic     {
436961768Sbostic       rtx tsize_rtx = GEN_INT (tsize);
437061768Sbostic 
437161768Sbostic       if (tsize > 32767)
437261768Sbostic 	{
437361768Sbostic 	  tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
437461768Sbostic 	  emit_move_insn (tmp_rtx, tsize_rtx);
437561768Sbostic 	  tsize_rtx = tmp_rtx;
437661768Sbostic 	}
437761768Sbostic 
437861768Sbostic       emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
437961768Sbostic 
438061768Sbostic       save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0);
438161768Sbostic 
438261768Sbostic       if (frame_pointer_needed)
438361768Sbostic 	emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
438461768Sbostic     }
438561768Sbostic 
438661768Sbostic   /* If we are profiling, make sure no instructions are scheduled before
438761768Sbostic      the call to mcount.  */
438861768Sbostic 
438961768Sbostic   if (profile_flag || profile_block_flag)
439061768Sbostic     emit_insn (gen_blockage ());
439161768Sbostic }
439261768Sbostic 
439361768Sbostic 
439461768Sbostic /* Do any necessary cleanup after a function to restore stack, frame, and regs. */
439561768Sbostic 
439661768Sbostic void
function_epilogue(file,size)439761768Sbostic function_epilogue (file, size)
439861768Sbostic      FILE *file;
439961768Sbostic      int size;
440061768Sbostic {
440161768Sbostic   long tsize;
440261768Sbostic   char *sp_str = reg_names[STACK_POINTER_REGNUM];
440361768Sbostic   char *t1_str = reg_names[MIPS_TEMP1_REGNUM];
440461768Sbostic   rtx epilogue_delay = current_function_epilogue_delay_list;
440561768Sbostic   int noreorder = !TARGET_MIPS_AS || (epilogue_delay != 0);
440661768Sbostic   int noepilogue = FALSE;
440761768Sbostic   int load_nop = FALSE;
440861768Sbostic   int load_only_r31;
440961768Sbostic   rtx tmp_rtx = (rtx)0;
441061768Sbostic   rtx restore_rtx;
441161768Sbostic   int i;
441261768Sbostic 
441361768Sbostic   /* The epilogue does not depend on any registers, but the stack
441461768Sbostic      registers, so we assume that if we have 1 pending nop, it can be
441561768Sbostic      ignored, and 2 it must be filled (2 nops occur for integer
441661768Sbostic      multiply and divide).  */
441761768Sbostic 
441861768Sbostic   if (dslots_number_nops > 0)
441961768Sbostic     {
442061768Sbostic       if (dslots_number_nops == 1)
442161768Sbostic 	{
442261768Sbostic 	  dslots_number_nops = 0;
442361768Sbostic 	  dslots_load_filled++;
442461768Sbostic 	}
442561768Sbostic       else
442661768Sbostic 	{
442761768Sbostic 	  while (--dslots_number_nops > 0)
442861768Sbostic 	    fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file);
442961768Sbostic 	}
443061768Sbostic 
443161768Sbostic       if (set_noreorder > 0 && --set_noreorder == 0)
443261768Sbostic 	fputs ("\t.set\treorder\n", file);
443361768Sbostic     }
443461768Sbostic 
443561768Sbostic   if (set_noat != 0)
443661768Sbostic     {
443761768Sbostic       set_noat = 0;
443861768Sbostic       fputs ("\t.set\tat\n", file);
443961768Sbostic       error ("internal gcc error: .set noat left on in epilogue");
444061768Sbostic     }
444161768Sbostic 
444261768Sbostic   if (set_nomacro != 0)
444361768Sbostic     {
444461768Sbostic       set_nomacro = 0;
444561768Sbostic       fputs ("\t.set\tmacro\n", file);
444661768Sbostic       error ("internal gcc error: .set nomacro left on in epilogue");
444761768Sbostic     }
444861768Sbostic 
444961768Sbostic   if (set_noreorder != 0)
445061768Sbostic     {
445161768Sbostic       set_noreorder = 0;
445261768Sbostic       fputs ("\t.set\treorder\n", file);
445361768Sbostic       error ("internal gcc error: .set noreorder left on in epilogue");
445461768Sbostic     }
445561768Sbostic 
445661768Sbostic   if (set_volatile != 0)
445761768Sbostic     {
445861768Sbostic       set_volatile = 0;
445961768Sbostic       fprintf (file, "\t#.set\tnovolatile\n", (TARGET_MIPS_AS) ? "" : "#");
446061768Sbostic       error ("internal gcc error: .set volatile left on in epilogue");
446161768Sbostic     }
446261768Sbostic 
446361768Sbostic   size = MIPS_STACK_ALIGN (size);
446461768Sbostic   tsize = (!current_frame_info.initialized)
446561768Sbostic 		? compute_frame_size (size)
446661768Sbostic 		: current_frame_info.total_size;
446761768Sbostic 
446861768Sbostic   if (tsize == 0 && epilogue_delay == 0)
446961768Sbostic     {
447061768Sbostic       rtx insn = get_last_insn ();
447161768Sbostic 
447261768Sbostic       /* If the last insn was a BARRIER, we don't have to write any code
447361768Sbostic 	 because a jump (aka return) was put there.  */
447461768Sbostic       if (GET_CODE (insn) == NOTE)
447561768Sbostic 	insn = prev_nonnote_insn (insn);
447661768Sbostic       if (insn && GET_CODE (insn) == BARRIER)
447761768Sbostic 	noepilogue = TRUE;
447861768Sbostic 
447961768Sbostic       noreorder = FALSE;
448061768Sbostic     }
448161768Sbostic 
448261768Sbostic   if (!noepilogue)
448361768Sbostic     {
448461768Sbostic       /* In the reload sequence, we don't need to fill the load delay
448561768Sbostic 	 slots for most of the loads, also see if we can fill the final
448661768Sbostic 	 delay slot if not otherwise filled by the reload sequence.  */
448761768Sbostic 
448861768Sbostic       if (noreorder)
448961768Sbostic 	fprintf (file, "\t.set\tnoreorder\n");
449061768Sbostic 
449161768Sbostic       if (tsize > 32767)
449261768Sbostic 	{
449361768Sbostic 	  fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n", t1_str, (long)tsize, (long)tsize);
449461768Sbostic 	  tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
449561768Sbostic 	}
449661768Sbostic 
449761768Sbostic       if (frame_pointer_needed)
449861768Sbostic 	fprintf (file, "\tmove\t%s,%s\t\t\t# sp not trusted here\n",
449961768Sbostic 		 sp_str, reg_names[FRAME_POINTER_REGNUM]);
450061768Sbostic 
450161768Sbostic       save_restore_insns (FALSE, tmp_rtx, tsize, file);
450261768Sbostic 
450361768Sbostic       load_only_r31 = (current_frame_info.mask == (1 << 31)
450461768Sbostic 		       && current_frame_info.fmask == 0);
450561768Sbostic 
450661768Sbostic       if (noreorder)
450761768Sbostic 	{
450861768Sbostic 	  /* If the only register saved is the return address, we need a
450961768Sbostic 	     nop, unless we have an instruction to put into it.  Otherwise
451061768Sbostic 	     we don't since reloading multiple registers doesn't reference
451161768Sbostic 	     the register being loaded.  */
451261768Sbostic 
451361768Sbostic 	  if (load_only_r31)
451461768Sbostic 	    {
451561768Sbostic 	      if (epilogue_delay)
451661768Sbostic 		  final_scan_insn (XEXP (epilogue_delay, 0),
451761768Sbostic 				   file,
451861768Sbostic 				   1,	 		/* optimize */
451961768Sbostic 				   -2,	 		/* prescan */
452061768Sbostic 				   1);			/* nopeepholes */
452161768Sbostic 	      else
452261768Sbostic 		{
452361768Sbostic 		  fprintf (file, "\tnop\n");
452461768Sbostic 		  load_nop = TRUE;
452561768Sbostic 		}
452661768Sbostic 	    }
452761768Sbostic 
452861768Sbostic 	  fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
452961768Sbostic 
453061768Sbostic 	  if (tsize > 32767)
453161768Sbostic 	    fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
453261768Sbostic 
453361768Sbostic 	  else if (tsize > 0)
453461768Sbostic 	    fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
453561768Sbostic 
453661768Sbostic 	  else if (!load_only_r31 && epilogue_delay != 0)
453761768Sbostic 	    final_scan_insn (XEXP (epilogue_delay, 0),
453861768Sbostic 			     file,
453961768Sbostic 			     1, 		/* optimize */
454061768Sbostic 			     -2, 		/* prescan */
454161768Sbostic 			     1);		/* nopeepholes */
454261768Sbostic 
454361768Sbostic 	  fprintf (file, "\t.set\treorder\n");
454461768Sbostic 	}
454561768Sbostic 
454661768Sbostic       else
454761768Sbostic 	{
454861768Sbostic 	  if (tsize > 32767)
454961768Sbostic 	    fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
455061768Sbostic 
455161768Sbostic 	  else if (tsize > 0)
455261768Sbostic 	    fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
455361768Sbostic 
455461768Sbostic 	  fprintf (file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 31]);
455561768Sbostic 	}
455661768Sbostic     }
455761768Sbostic 
455861768Sbostic   fputs ("\t.end\t", file);
455961768Sbostic   assemble_name (file, current_function_name);
456061768Sbostic   fputs ("\n", file);
456161768Sbostic 
456261768Sbostic   if (TARGET_STATS)
456361768Sbostic     {
456461768Sbostic       int num_gp_regs = current_frame_info.gp_reg_size / 4;
456561768Sbostic       int num_fp_regs = current_frame_info.fp_reg_size / 8;
456661768Sbostic       int num_regs    = num_gp_regs + num_fp_regs;
456761768Sbostic       char *name      = current_function_name;
456861768Sbostic 
456961768Sbostic       if (name[0] == '*')
457061768Sbostic 	name++;
457161768Sbostic 
457261768Sbostic       dslots_load_total += num_regs;
457361768Sbostic 
457461768Sbostic       if (!noepilogue)
457561768Sbostic 	dslots_jump_total++;
457661768Sbostic 
457761768Sbostic       if (noreorder)
457861768Sbostic 	{
457961768Sbostic 	  dslots_load_filled += num_regs;
458061768Sbostic 
458161768Sbostic 	  /* If the only register saved is the return register, we
458261768Sbostic 	     can't fill this register's delay slot.  */
458361768Sbostic 
458461768Sbostic 	  if (load_only_r31 && epilogue_delay == 0)
458561768Sbostic 	    dslots_load_filled--;
458661768Sbostic 
458761768Sbostic 	  if (tsize > 0 || (!load_only_r31 && epilogue_delay != 0))
458861768Sbostic 	    dslots_jump_filled++;
458961768Sbostic 	}
459061768Sbostic 
459161768Sbostic       fprintf (stderr,
459261768Sbostic 	       "%-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",
459361768Sbostic 	       name,
459461768Sbostic 	       (frame_pointer_needed) ? 'y' : 'n',
459561768Sbostic 	       ((current_frame_info.mask & (1 << 31)) != 0) ? 'n' : 'y',
459661768Sbostic 	       (current_function_calls_alloca) ? 'y' : 'n',
459761768Sbostic 	       (current_function_calls_setjmp) ? 'y' : 'n',
459861768Sbostic 	       (long)current_frame_info.total_size,
459961768Sbostic 	       (long)current_function_outgoing_args_size,
460061768Sbostic 	       num_gp_regs, num_fp_regs,
460161768Sbostic 	       dslots_load_total, dslots_load_filled,
460261768Sbostic 	       dslots_jump_total, dslots_jump_filled,
460361768Sbostic 	       num_refs[0], num_refs[1], num_refs[2]);
460461768Sbostic 
460561768Sbostic       if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs)
460661768Sbostic 	{
460761768Sbostic 	  fprintf (stderr, " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs);
460861768Sbostic 	  prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS;
460961768Sbostic 	}
461061768Sbostic 
461161768Sbostic       if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs)
461261768Sbostic 	{
461361768Sbostic 	  fprintf (stderr, " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs);
461461768Sbostic 	  prev_half_pic_refs = HALF_PIC_NUMBER_REFS;
461561768Sbostic 	}
461661768Sbostic 
461761768Sbostic       fputc ('\n', stderr);
461861768Sbostic     }
461961768Sbostic 
462061768Sbostic   /* Reset state info for each function.  */
462161768Sbostic   inside_function    = FALSE;
462261768Sbostic   ignore_line_number = FALSE;
462361768Sbostic   dslots_load_total  = 0;
462461768Sbostic   dslots_jump_total  = 0;
462561768Sbostic   dslots_load_filled = 0;
462661768Sbostic   dslots_jump_filled = 0;
462761768Sbostic   num_refs[0]	     = 0;
462861768Sbostic   num_refs[1]	     = 0;
462961768Sbostic   num_refs[2]	     = 0;
463061768Sbostic   mips_load_reg      = (rtx)0;
463161768Sbostic   mips_load_reg2     = (rtx)0;
463261768Sbostic   current_frame_info = zero_frame_info;
463361768Sbostic 
463461768Sbostic   /* Restore the output file if optimizing the GP (optimizing the GP causes
463561768Sbostic      the text to be diverted to a tempfile, so that data decls come before
463661768Sbostic      references to the data).  */
463761768Sbostic 
463861768Sbostic   if (TARGET_GP_OPT)
463961768Sbostic     asm_out_file = asm_out_data_file;
464061768Sbostic }
464161768Sbostic 
464261768Sbostic 
464361768Sbostic /* Expand the epilogue into a bunch of separate insns.  */
464461768Sbostic 
464561768Sbostic void
mips_expand_epilogue()464661768Sbostic mips_expand_epilogue ()
464761768Sbostic {
464861768Sbostic   long tsize = current_frame_info.total_size;
464961768Sbostic   rtx tsize_rtx = GEN_INT (tsize);
465061768Sbostic   rtx tmp_rtx = (rtx)0;
465161768Sbostic 
465261768Sbostic   if (tsize > 32767)
465361768Sbostic     {
465461768Sbostic       tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM);
465561768Sbostic       emit_move_insn (tmp_rtx, tsize_rtx);
465661768Sbostic       tsize_rtx = tmp_rtx;
465761768Sbostic     }
465861768Sbostic 
465961768Sbostic   if (tsize > 0)
466061768Sbostic     {
466161768Sbostic       if (frame_pointer_needed)
466261768Sbostic 	emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
466361768Sbostic 
466461768Sbostic       save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0);
466561768Sbostic 
466661768Sbostic       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx));
466761768Sbostic     }
466861768Sbostic 
466961768Sbostic   emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31)));
467061768Sbostic }
467161768Sbostic 
467261768Sbostic 
467361768Sbostic /* Define the number of delay slots needed for the function epilogue.
467461768Sbostic 
467561768Sbostic    On the mips, we need a slot if either no stack has been allocated,
467661768Sbostic    or the only register saved is the return register.  */
467761768Sbostic 
467861768Sbostic int
mips_epilogue_delay_slots()467961768Sbostic mips_epilogue_delay_slots ()
468061768Sbostic {
468161768Sbostic   if (!current_frame_info.initialized)
468261768Sbostic     (void) compute_frame_size (get_frame_size ());
468361768Sbostic 
468461768Sbostic   if (current_frame_info.total_size == 0)
468561768Sbostic     return 1;
468661768Sbostic 
468761768Sbostic   if (current_frame_info.mask == (1 << 31) && current_frame_info.fmask == 0)
468861768Sbostic     return 1;
468961768Sbostic 
469061768Sbostic   return 0;
469161768Sbostic }
469261768Sbostic 
469361768Sbostic 
469461768Sbostic /* Return true if this function is known to have a null epilogue.
469561768Sbostic    This allows the optimizer to omit jumps to jumps if no stack
469661768Sbostic    was created.  */
469761768Sbostic 
469861768Sbostic int
simple_epilogue_p()469961768Sbostic simple_epilogue_p ()
470061768Sbostic {
470161768Sbostic   if (!reload_completed)
470261768Sbostic     return 0;
470361768Sbostic 
470461768Sbostic   if (current_frame_info.initialized)
470561768Sbostic     return current_frame_info.total_size == 0;
470661768Sbostic 
470761768Sbostic   return (compute_frame_size (get_frame_size ())) == 0;
470861768Sbostic }
4709