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, <ext_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