aboutsummaryrefslogtreecommitdiff
path: root/gcc-1.40/config/out-mips.c
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@protonmail.com>2021-08-15 00:34:05 -0400
committerAndrew Lee <alee14498@protonmail.com>2021-08-15 00:34:05 -0400
commit60cc83bf91bfc9bb02f6304b5d6c8234ba6d210f (patch)
treefdc0be85a1ca35e34c3ae2c805fe9b718e3c1091 /gcc-1.40/config/out-mips.c
parentdd8dfab51b832a654365ed00c06bf802ff628bfa (diff)
downloadlinux-0.01-distro-master.tar.gz
linux-0.01-distro-master.tar.bz2
linux-0.01-distro-master.zip
Added gccHEADmaster
Diffstat (limited to 'gcc-1.40/config/out-mips.c')
-rw-r--r--gcc-1.40/config/out-mips.c1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/gcc-1.40/config/out-mips.c b/gcc-1.40/config/out-mips.c
new file mode 100644
index 0000000..ab1dac0
--- /dev/null
+++ b/gcc-1.40/config/out-mips.c
@@ -0,0 +1,1086 @@
+/* Subroutines for insn-output.c for MIPS
+ Contributed by A. Lichnewsky, lich@inria.inria.fr.
+ Changes by Michael Meissner, meissner@osf.org.
+ Copyright (C) 1989, 1990 Free Software Foundation, Inc.
+
+This file is part of GNU CC.
+
+GNU CC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU CC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU CC; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include "tree.h"
+#include "flags.h"
+
+#ifndef R_OK
+#define R_OK 4
+#define W_OK 2
+#define X_OK 1
+#endif
+
+extern void debug_rtx ();
+extern void abort_with_insn ();
+
+extern FILE *asm_out_file;
+extern tree current_function_decl;
+
+/* Global variables for machine-dependent things. */
+
+char *reg_numchar[] = REGISTER_NUMCHAR;
+
+/* Threshold for data being put into the small data/bss area, instead
+ of the normal data area (references to the small data/bss area take
+ 1 instruction, and use the global pointer, references to the normal
+ data area takes 2 instructions). */
+int mips_section_threshold = -1;
+
+/* Count the number of .file directives, so that .loc is up to date. */
+int num_source_filenames = 0;
+
+/* Count the number of words that are pushed to pass arguments. */
+int stack_args_pushed = 0;
+
+/* # bytes for args preallocated by function_prolog. */
+int stack_args_preallocated = 0;
+
+/* Count of the number of functions created so far, in order to make
+ unique labels for omitting the frame pointer. */
+int number_functions_processed = 0;
+
+/* Count the number of sdb related labels are generated (to find block
+ start and end boundaries). */
+int sdb_label_count = 0;
+
+/* Next label # for each statment for Silicon Graphics IRIS systems. */
+int sym_lineno = 0;
+
+/* Non-zero if inside of a function, because the stupid MIPS asm can't
+ handle .files inside of functions. */
+int inside_function = 0;
+
+/* String to be used for the unique name given to the difference between
+ the stack pointer and frame pointer when the frame pointer is to be
+ omitted. */
+char *sp_fp_difference = 0;
+
+/* Files to separate the text and the data output, so that all of the data
+ can be emitted before the text, which will mean that the assembler will
+ generate smaller code, based on the global pointer. */
+FILE *asm_out_data_file;
+FILE *asm_out_text_file;
+
+/* Linked list of all externals that are to be emitted when optimizing
+ for the global pointer if they haven't been declared by the end of
+ the program with an appropriate .comm or initialization. */
+
+struct extern_list {
+ struct extern_list *next; /* next external */
+ char *name; /* name of the external */
+ int size; /* size in bytes */
+} *extern_head = 0;
+
+/* Name of the current function. */
+char *current_function_name;
+
+/* Size of the frame allocated for this function. */
+int current_function_total_framesize;
+
+/* Number of bytes used to hold saved registers. */
+int current_function_saved_reg_size;
+
+/* Return truth value of whether OP can be used as an operands
+ where a register or 16 bit unsigned integer is needed. */
+
+int
+uns_arith_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (register_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op)));
+}
+
+/* Return truth value of whether OP can be used as an operands
+ where a 16 bit integer is needed */
+
+int
+arith_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (register_operand (op, mode)
+ || (GET_CODE (op) == CONST_INT && SMALL_INT (op)));
+}
+
+/* Return truth value of whether OP can be used as an operand in a two
+ address arithmetic insn (such as set 123456,%o4) of mode MODE. */
+
+int
+arith32_operand (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (register_operand (op, mode) || GET_CODE (op) == CONST_INT);
+}
+
+/* Return truth value of whether OP is a integer which fits in 16 bits */
+
+int
+small_int (op, mode)
+ rtx op;
+ enum machine_mode mode;
+{
+ return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
+}
+
+
+/* Argument support functions. */
+
+/* Initialize CUMULATIVE_ARGS for a function. */
+
+void
+init_cumulative_args (cum, fntype)
+ CUMULATIVE_ARGS cum; /* argument info to initialize */
+ tree fntype; /* tree ptr for function decl */
+{
+ tree param, next_param;
+
+ if (TARGET_DEBUGE_MODE)
+ {
+ fprintf (stderr, "\ninit_cumulative_args\n");
+ if (fntype != (tree)0)
+ {
+ putc ('\n', stderr);
+ debug_tree (fntype);
+ putc ('\n', stderr);
+ }
+ }
+
+ cum->gp_reg_found = 0;
+ cum->arg_number = 0;
+ cum->arg_words = 0;
+
+ /* Determine if this function has variable arguments. This is
+ indicated by the last argument being 'void_type_mode' if there
+ are no variable arguments. The standard MIPS calling sequence
+ passes all arguments in the general purpose registers in this
+ case. */
+
+ for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0;
+ param != (tree)0;
+ param = next_param)
+ {
+ next_param = TREE_CHAIN (param);
+ if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node)
+ cum->gp_reg_found = 1;
+ }
+
+ /* Determine if the function is returning a structure, if so,
+ advance by one argument. */
+
+ if (fntype
+ && (TREE_CODE (fntype) == FUNCTION_TYPE || TREE_CODE (fntype) == METHOD_TYPE)
+ && TREE_TYPE (fntype) != 0)
+ {
+ tree ret_type = TREE_TYPE (fntype);
+ enum tree_code ret_code = TREE_CODE (ret_type);
+
+ if (ret_code == RECORD_TYPE || ret_code == UNION_TYPE)
+ {
+ cum->gp_reg_found = 1;
+ cum->arg_number = 1;
+ cum->arg_words = 1;
+ }
+ }
+}
+
+/* Advance the argument to the next argument position. */
+
+void
+function_arg_advance (cum, mode, type, named)
+ CUMULATIVE_ARGS cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+{
+ if (TARGET_DEBUGE_MODE)
+ fprintf (stderr,
+ "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n",
+ cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
+ type, named);
+
+ cum->arg_number++;
+ switch (mode)
+ {
+ default:
+ error ("Illegal mode given to function_arg_advance");
+ break;
+
+ case VOIDmode:
+ break;
+
+ case BLKmode:
+ cum->gp_reg_found = 1;
+ cum->arg_words += (int_size_in_bytes (type) + 3) / 4;
+ break;
+
+ case SFmode:
+ cum->arg_words++;
+ break;
+
+ case DFmode:
+ cum->arg_words += 2;
+ break;
+
+ case DImode:
+ cum->gp_reg_found = 1;
+ cum->arg_words += 2;
+ break;
+
+ case QImode:
+ case HImode:
+ case SImode:
+ cum->gp_reg_found = 1;
+ cum->arg_words++;
+ break;
+ }
+}
+
+/* Return a RTL expression containing the register for the given mode,
+ or 0 if the argument is too be passed on the stack. */
+
+struct rtx_def *
+function_arg (cum, mode, type, named)
+ CUMULATIVE_ARGS cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ int named; /* != 0 for normal args, == 0 for ... args */
+{
+ int regbase = -1;
+ int bias = 0;
+
+ if (TARGET_DEBUGE_MODE)
+ fprintf (stderr,
+ "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d ) = ",
+ cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode),
+ type, named);
+
+ switch (mode)
+ {
+ default:
+ error ("Illegal mode given to function_arg");
+ break;
+
+ case SFmode:
+ if (cum->gp_reg_found || cum->arg_number >= 2)
+ regbase = GP_ARG_FIRST;
+ else {
+ regbase = FP_ARG_FIRST;
+ if (cum->arg_words == 1) /* first arg was float */
+ bias = 1; /* use correct reg */
+ }
+
+ break;
+
+ case DFmode:
+ cum->arg_words += (cum->arg_words & 1);
+ regbase = (cum->gp_reg_found) ? GP_ARG_FIRST : FP_ARG_FIRST;
+ break;
+
+ case VOIDmode:
+ case BLKmode:
+ case QImode:
+ case HImode:
+ case SImode:
+ case DImode:
+ regbase = GP_ARG_FIRST;
+ break;
+ }
+
+ if (cum->arg_words >= MAX_ARGS_IN_REGISTERS)
+ {
+ if (TARGET_DEBUGE_MODE)
+ fprintf (stderr, "<stack>\n");
+
+ return 0;
+ }
+
+ if (regbase == -1)
+ abort ();
+
+ if (TARGET_DEBUGE_MODE)
+ fprintf (stderr, "%s\n", reg_numchar[ regbase + cum->arg_number + bias ]);
+
+ return gen_rtx (REG, mode, regbase + cum->arg_words + bias);
+}
+
+
+int
+function_arg_partial_nregs (cum, mode, type, named)
+ CUMULATIVE_ARGS cum; /* current arg information */
+ enum machine_mode mode; /* current arg mode */
+ tree type; /* type of the argument or 0 if lib support */
+ int named; /* != 0 for normal args, == 0 for ... args */
+{
+ if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS)
+ {
+ int words = (int_size_in_bytes (type) + 3) / 4;
+
+ if (words + cum->arg_words < MAX_ARGS_IN_REGISTERS)
+ return 0; /* structure fits in registers */
+
+ if (TARGET_DEBUGE_MODE)
+ fprintf (stderr, "function_arg_partial_nregs = %d\n",
+ MAX_ARGS_IN_REGISTERS - cum->arg_words);
+
+ return MAX_ARGS_IN_REGISTERS - cum->arg_words;
+ }
+
+ else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1)
+ {
+ if (TARGET_DEBUGE_MODE)
+ fprintf (stderr, "function_arg_partial_nregs = 1\n");
+
+ return 1;
+ }
+
+ return 0;
+}
+
+
+/* Routines to merge the compare and branch operators into a single entity. */
+
+static rtx branch_cmp_op[2];
+static enum machine_mode branch_cmp_mode;
+
+/* Save the mode and operands on the current compare operator. */
+
+void
+compare_collect (mode, op0, op1)
+ enum machine_mode mode;
+ rtx op0;
+ rtx op1;
+{
+ if (TARGET_DEBUGD_MODE)
+ {
+ fprintf (stderr, "compare_collect mode = %s, operands::",
+ GET_MODE_NAME (mode));
+ debug_rtx (op0);
+ debug_rtx (op1);
+ }
+ branch_cmp_op[0] = op0;
+ branch_cmp_op[1] = op1;
+ branch_cmp_mode = mode;
+}
+
+/* Return the mode and operands saved with compare_collect for use
+ in a branch operator. */
+
+void
+compare_restore (operands, mode, insn)
+ rtx *operands;
+ enum machine_mode *mode;
+ rtx insn;
+{
+ if (!branch_cmp_op[0] || !branch_cmp_op[1])
+ abort_with_insn (insn, "Compare_restore did not follow compare_collect");
+
+ if (TARGET_DEBUGD_MODE)
+ {
+ fprintf (stderr,
+ "compare_restore returning mode = %s, operands:%X,%X:",
+ GET_MODE_NAME (branch_cmp_mode),
+ branch_cmp_op[0],
+ branch_cmp_op[1]);
+
+ debug_rtx (branch_cmp_op[0]);
+ debug_rtx (branch_cmp_op[1]);
+ }
+
+ operands[0] = branch_cmp_op[0];
+ operands[1] = branch_cmp_op[1];
+ *mode = branch_cmp_mode;
+
+ /* If the next insn is not a JUMP (after accounting for line numbers),
+ zero out the branch_cmp_array. Switch statements implemented as if's
+ tend to have multiple jumps. */
+ do
+ {
+ insn = NEXT_INSN (insn);
+ }
+ while (insn && GET_CODE (insn) == NOTE);
+
+ if (!insn || GET_CODE (insn) != JUMP_INSN)
+ {
+ branch_cmp_op[0] = NULL;
+ branch_cmp_op[1] = NULL;
+ branch_cmp_mode = VOIDmode;
+ }
+
+}
+
+/* Print the options used in the assembly file. */
+
+static struct {char *name; int value;} target_switches []
+ = TARGET_SWITCHES;
+
+void
+print_options (out)
+ FILE *out;
+{
+ int line_len;
+ int len;
+ int j;
+ char **p;
+ int mask = TARGET_DEFAULT;
+ extern char **save_argv;
+ extern char *version_string, *language_string;
+
+#if 0
+ /* Allow assembly language comparisons with -mdebug eliminating the
+ compiler version number and switch lists. */
+ if (!TARGET_DEBUG_MODE)
+ {
+ fprintf (out, "\n # %s %s", language_string, version_string);
+#ifdef TARGET_VERSION_INTERNAL
+ TARGET_VERSION_INTERNAL (out);
+#endif
+#ifdef __GNUC__
+ fprintf (out, " compiled by GNU C\n\n");
+#else
+ fprintf (out, " compiled by CC\n\n");
+#endif
+
+ fprintf (out, " # Cc1 defaults:");
+ line_len = 32767;
+ for (j = 0; j < sizeof target_switches / sizeof target_switches[0]; j++)
+ if (target_switches[j].name[0] != '\0'
+ && target_switches[j].value > 0
+ && (target_switches[j].value & mask) == target_switches[j].value)
+ {
+ len = strlen (target_switches[j].name) + 1;
+ if (len + line_len > 79)
+ {
+ line_len = 2;
+ fputs ("\n #", out);
+ }
+ fprintf (out, " -m%s", target_switches[j].name);
+ line_len += len;
+ }
+
+ fprintf (out, "\n\n # Cc1 arguments (-G value = %d):",
+ mips_section_threshold);
+
+ line_len = 32767;
+ for (p = &save_argv[1]; *p != (char *)0; p++)
+ if (**p == '-')
+ {
+ len = strlen (*p) + 1;
+ if (len + line_len > 79)
+ {
+ line_len = 2;
+ fputs ("\n #", out);
+ }
+ fprintf (out, " %s", *p);
+ line_len += len;
+ }
+ fputs ("\n\n", out);
+ }
+
+#endif
+}
+
+
+
+/* Abort after printing out a specific insn. */
+
+void
+abort_with_insn (insn, reason)
+ rtx insn;
+ char *reason;
+{
+ error (reason);
+ debug_rtx (insn);
+ abort ();
+}
+
+/* Write a message to stderr (for use in macros expanded in files that do not
+ include stdio.h). */
+
+void
+trace (s, s1, s2)
+ char *s, *s1, *s2;
+{
+ fprintf (stderr, s, s1, s2);
+}
+
+
+/* Set up the threshold for data to go into the small data area, instead
+ of the normal data area, and detect any conflicts in the switches. */
+
+void
+overide_options ()
+{
+ register int i;
+
+ i = TARGET_GVALUE;
+ if (i >= 6)
+ i += 3;
+ mips_section_threshold = (i != 0) ? 1 << i : 0;
+}
+
+/* Compute a string to use as a temporary file name. */
+
+static FILE *
+make_temp_file ()
+{
+ char *temp_filename;
+ FILE *stream;
+ extern char *getenv ();
+ char *base = getenv ("TMPDIR");
+ int len;
+
+ if (base == (char *)0)
+ {
+#ifdef P_tmpdir
+ if (access (P_tmpdir, R_OK | W_OK) == 0)
+ base = P_tmpdir;
+ else
+#endif
+ if (access ("/usr/tmp", R_OK | W_OK) == 0)
+ base = "/usr/tmp/";
+ else
+ base = "/tmp/";
+ }
+
+ len = strlen (base);
+ temp_filename = (char *) alloca (len + sizeof("/ccXXXXXX"));
+ strcpy (temp_filename, base);
+ if (len > 0 && temp_filename[len-1] != '/')
+ temp_filename[len++] = '/';
+
+ strcpy (temp_filename + len, "ccXXXXXX");
+ mktemp (temp_filename);
+
+ stream = fopen (temp_filename, "w+");
+ if (!stream)
+ pfatal_with_name (temp_filename);
+
+ unlink (temp_filename);
+ return stream;
+}
+
+/* Output at beginning of assembler file.
+ If we are optimizing to use the global pointer, create a temporary
+ file to hold all of the text stuff, and write it out to the end.
+ This is needed because the MIPS assembler is evidently one pass,
+ and if it hasn't seen the relevant .comm/.lcomm/.extern/.sdata
+ declaration when the code is processed, it generates a two
+ instruction sequence. */
+
+void
+mips_asm_file_start (stream)
+ FILE *stream;
+{
+ if (TARGET_NAME_REGS)
+ fprintf (stream, "#include <regdef.h>\n");
+
+ ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
+
+ print_options (stream);
+ data_section (); /* put gcc_compiled. in data, not text*/
+
+ if (TARGET_GP_OPT)
+ {
+ asm_out_data_file = stream;
+ asm_out_text_file = make_temp_file ();
+ }
+ else
+ asm_out_data_file = asm_out_text_file = stream;
+
+}
+
+/* If optimizing for the global pointer, keep track of all of
+ the externs, so that at the end of the file, we can emit
+ the appropriate .extern declaration for them, before writing
+ out the text section. We assume that all names passed to
+ us are in the permanent obstack, so that they will be valid
+ at the end of the compilation.
+
+ If we have -G 0, or the extern size is unknown, don't bother
+ emitting the .externs. */
+
+int
+mips_output_external (file, decl, name)
+ FILE *file;
+ tree decl;
+ char *name;
+{
+ extern char *permalloc ();
+ register struct extern_list *p;
+ int len;
+
+ if (TARGET_GP_OPT
+ && mips_section_threshold != 0
+ && ((TREE_CODE (decl)) != FUNCTION_DECL)
+ && ((len = int_size_in_bytes (TREE_TYPE (decl))) > 0))
+ {
+ p = (struct extern_list *)permalloc ((long) sizeof (struct extern_list));
+ p->next = extern_head;
+ p->name = name;
+ p->size = len;
+ extern_head = p;
+ }
+ return 0;
+}
+
+/* If we are optimizing the global pointer, emit the text section now
+ and any small externs which did not have .comm, etc that are
+ needed. Also, give a warning if the data area is more than 32K and
+ -pic because 3 instructions are needed to reference the data
+ pointers. */
+
+int
+mips_asm_file_end (file)
+ FILE *file;
+{
+ char buffer[8192];
+ tree name_tree;
+ struct extern_list *p;
+ int len;
+ extern tree lookup_name ();
+
+ if (TARGET_GP_OPT)
+ {
+ if (extern_head)
+ fputs ("\n", file);
+
+ for (p = extern_head; p != 0; p = p->next)
+ {
+ name_tree = get_identifier (p->name);
+ if (!TREE_ADDRESSABLE (name_tree))
+ {
+ TREE_ADDRESSABLE (name_tree) = 1;
+ fprintf (file, "\t.extern\t%s, %d\n", p->name, p->size);
+ }
+ }
+
+ fprintf (file, "\n\t.text\n");
+ rewind (asm_out_text_file);
+ if (ferror (asm_out_text_file))
+ fatal_io_error ("write of text assembly file in mips_asm_file_end");
+
+ while ((len = fread (buffer, 1, sizeof (buffer), asm_out_text_file)) > 0)
+ if (fwrite (buffer, 1, len, file) != len)
+ pfatal_with_name ("write of final assembly file in mips_asm_file_end");
+
+ if (len < 0)
+ pfatal_with_name ("read of text assembly file in mips_asm_file_end");
+
+ if (fclose (asm_out_text_file) != 0)
+ pfatal_with_name ("close of tempfile in mips_asm_file_end");
+ }
+}
+
+/* Fix references to the frame pointer to be off of the stack pointer. */
+
+struct rtx_def *
+mips_fix_frame_pointer (oldaddr, depth)
+ rtx oldaddr;
+ int depth;
+{
+ rtx newaddr;
+ rtx sp_diff_rtx;
+ char temp[40];
+ int frame_offset = 0;
+ extern rtx eliminate_constant_term ();
+
+ newaddr = eliminate_constant_term (oldaddr, &frame_offset);
+ if (newaddr != frame_pointer_rtx)
+ return oldaddr;
+
+ if (sp_fp_difference == (char *)0)
+ {
+ sprintf (temp, "$Ls%d", number_functions_processed);
+ sp_fp_difference = IDENTIFIER_POINTER (get_identifier (temp));
+ }
+
+ sp_diff_rtx = gen_rtx (SYMBOL_REF, SImode, sp_fp_difference);
+ if (frame_offset + depth == 0)
+ newaddr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, sp_diff_rtx);
+ else
+ newaddr = gen_rtx (PLUS, Pmode,
+ stack_pointer_rtx,
+ gen_rtx (CONST, Pmode,
+ gen_rtx (PLUS, Pmode,
+ sp_diff_rtx,
+ gen_rtx (CONST_INT, VOIDmode,
+ frame_offset + depth))));
+
+ if (TARGET_DEBUGC_MODE)
+ {
+ fprintf (stderr,
+ "\n==================== FIX_FRAME, depth = %d, sp prealloc = %d, offset = %d\n",
+ depth, stack_args_preallocated, frame_offset);
+
+ fprintf (stderr, "old INSN:");
+ debug_rtx (oldaddr);
+
+ fprintf (stderr, "\nnew INSN:");
+ debug_rtx (newaddr);
+ }
+
+ return newaddr;
+}
+
+
+/* Set up the stack and frame (if desired) for the function. */
+
+void
+function_prologue (file, size)
+ FILE *file;
+ int size;
+{
+ extern char call_used_regs[];
+ extern char *reg_numchar[];
+ extern tree current_function_decl;
+ int regno;
+ int mask;
+ int fmask;
+ int push_loc;
+ int tsize;
+ int num_regs;
+ char **reg_name_ptr = (TARGET_NAME_REGS) ? reg_names : reg_numchar;
+ char *base_str;
+ char *sp_str = reg_name_ptr[STACK_POINTER_REGNUM];
+ char *fp_str = (!frame_pointer_needed)
+ ? sp_str
+ : reg_name_ptr[FRAME_POINTER_REGNUM];
+ tree fndecl = current_function_decl; /* current... is tooo long */
+ tree fntype = TREE_TYPE (fndecl);
+ tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE)
+ ? DECL_ARGUMENTS (fndecl)
+ : 0;
+ tree next_arg;
+ tree cur_arg;
+ char *arg_name = (char *)0;
+ CUMULATIVE_ARGS args_so_far;
+
+
+ inside_function = 1;
+
+
+ if (write_symbols != NO_DEBUG)
+ ASM_OUTPUT_SOURCE_LINE (file,
+ DECL_SOURCE_LINE (current_function_decl));
+
+ fprintf (file, "\t.ent\t%s\n%s:\n", current_function_name,
+ current_function_name);
+
+ fprintf (file, " #PROLOGUE\n");
+
+ /* Determine the last argument, and get it's name. */
+ for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg)
+ {
+ next_arg = TREE_CHAIN (cur_arg);
+ if (next_arg == (tree)0)
+ {
+ if (DECL_NAME (cur_arg))
+ arg_name = IDENTIFIER_POINTER (DECL_NAME (cur_arg));
+
+ break;
+ }
+ }
+
+ /* If this function is a varargs function, store any registers that
+ would normally hold arguments ($4 - $7) on the stack. */
+ if ((TYPE_ARG_TYPES (fntype) != 0
+ && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node))
+ || (arg_name
+ && (strcmp (arg_name, "__builtin_va_alist") == 0
+ || strcmp (arg_name, "va_alist") == 0)))
+ {
+ tree parm;
+
+ regno = 4;
+ INIT_CUMULATIVE_ARGS (args_so_far, fntype);
+
+ for (parm = fnargs; (parm && (regno <= 7)); parm = TREE_CHAIN (parm))
+ {
+ rtx entry_parm;
+ enum machine_mode passed_mode;
+ tree type;
+
+ type = DECL_ARG_TYPE (parm);
+ passed_mode = TYPE_MODE (type);
+ entry_parm = FUNCTION_ARG (args_so_far, passed_mode,
+ DECL_ARG_TYPE (parm), 1);
+
+ if (entry_parm)
+ {
+ int words;
+
+ /* passed in a register, so will get homed automatically */
+ if (GET_MODE (entry_parm) == BLKmode)
+ words = (int_size_in_bytes (type) + 3) / 4;
+ else
+ words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
+
+ regno = REGNO (entry_parm) + words - 1;
+ }
+ else
+ {
+ regno = 8;
+ break;
+ }
+
+ FUNCTION_ARG_ADVANCE (args_so_far, passed_mode,
+ DECL_ARG_TYPE (parm), 1);
+ }
+
+ switch (regno)
+ {
+ case 4:
+ fprintf(file, "\tsd\t%s,0(%s)\t#varargs: home regs 4-5\n",
+ reg_name_ptr[4], sp_str);
+
+ fprintf(file, "\tsd\t%s,8(%s)\t#varargs: home regs 6-7\n",
+ reg_name_ptr[6], sp_str);
+ break;
+
+ case 5:
+ fprintf(file, "\tsw\t%s,4(%s)\t#varargs: home reg 5\n",
+ reg_name_ptr[5], sp_str);
+
+ fprintf(file, "\tsd\t%s,8(%s)\t#varargs: home regs 6-7\n",
+ reg_name_ptr[6], sp_str);
+ break;
+
+ case 6:
+ fprintf(file, "\tsd\t%s,8(%s)\t#varargs: home regs 6-7\n",
+ reg_name_ptr[6], sp_str);
+ break;
+
+ case 7:
+ fprintf(file, "\tsw\t%s,12(%s)\t#varargs: home reg 7\n",
+ reg_name_ptr[7], sp_str);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ mask = 0;
+ fmask = 0;
+ num_regs = 0;
+ push_loc = stack_args_preallocated;
+ tsize = AL_ADJUST_ALIGN (size) + stack_args_preallocated;
+
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (MUST_SAVE_REGISTER (regno))
+ {
+ tsize += 4;
+ num_regs += 4;
+ mask |= 1 << (regno - GP_REG_FIRST);
+ }
+
+ tsize = AL_ADJUST_ALIGN (tsize);
+ num_regs = AL_ADJUST_ALIGN (num_regs);
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += 2)
+ if (regs_ever_live[regno] && !call_used_regs[regno])
+ {
+ tsize += 8;
+ num_regs += 8;
+ fmask |= 1 << (regno - FP_REG_FIRST);
+ }
+
+ if (tsize)
+ tsize -= STARTING_FRAME_OFFSET;
+
+
+ if (!frame_pointer_needed && sp_fp_difference != (char *)0)
+ fprintf (file,"%s\t= %d\t\t\t#Difference between SP & FP\n\n",
+ sp_fp_difference, tsize);
+
+ current_function_total_framesize = tsize;
+ current_function_saved_reg_size = num_regs;
+ if (tsize > 0)
+ {
+ if (tsize <= 32767)
+ fprintf (file,
+ "\tsubu\t%s,%s,%d\t# temp= %d, regs= %d, args= %d, sfo= %d\n",
+ sp_str, sp_str, tsize, size, num_regs,
+ stack_args_preallocated, STARTING_FRAME_OFFSET);
+ else
+ fprintf (file,
+ "\tli\t%s,%d\n\tsubu\t%s,%s,%s\t# temp= %d, regs= %d, args= %d, sfo= %d\n",
+ reg_name_ptr[MIPS_TEMP1_REGNUM], tsize, sp_str, sp_str,
+ reg_name_ptr[MIPS_TEMP1_REGNUM], size, num_regs,
+ stack_args_preallocated, STARTING_FRAME_OFFSET);
+ }
+
+ fprintf (file, "\t.frame\t%s,%d,%s\n", fp_str,
+ (frame_pointer_needed) ? 0 : tsize,
+ reg_name_ptr[31]);
+
+ if (push_loc > 32767 && num_regs > 0)
+ {
+ if ((tsize - (push_loc + num_regs)) <= 32767)
+ {
+ base_str = reg_name_ptr[MIPS_TEMP1_REGNUM];
+ push_loc = tsize - push_loc;
+ }
+ else
+ {
+ base_str = reg_name_ptr[MIPS_TEMP2_REGNUM];
+ fprintf (file, "\tli\t%s,%d\n", base_str, push_loc);
+ push_loc = 0;
+ }
+ }
+ else
+ base_str = sp_str;
+
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if ((mask & (1 << (regno - GP_REG_FIRST))) != 0)
+ {
+ fprintf (file, "\tsw\t%s,%d(%s)\n", reg_name_ptr[regno], push_loc,
+ base_str);
+ push_loc += 4;
+ }
+
+ fprintf (file, "\t.mask\t0x%08x,%d\n", mask, push_loc - tsize - 4);
+
+ push_loc = AL_ADJUST_ALIGN (push_loc);
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += 2)
+ if ((fmask & (1 << (regno - FP_REG_FIRST))) != 0)
+ {
+ fprintf (file, "\ts.d\t%s,%d(%s)\n", reg_name_ptr[regno], push_loc,
+ base_str);
+ push_loc += 8;
+ }
+
+ fprintf (file, "\t.fmask\t0x%08x,%d\n", fmask, push_loc - tsize - 4);
+
+ if (frame_pointer_needed)
+ {
+ if (tsize <= 32767)
+ fprintf (file, "\taddu\t%s,%s,%d\t# set up frame pointer\n", fp_str, sp_str, tsize);
+ else
+ fprintf (file, "\taddu\t%s,%s,%s\t# set up frame pointer\n", fp_str, sp_str,
+ reg_name_ptr[MIPS_TEMP1_REGNUM]);
+ }
+
+ fprintf (file," #END PROLOGUE\n");
+}
+
+
+/* Do any necessary cleanup after a function to restore stack, frame, and regs. */
+
+void
+function_epilogue (file, size)
+ FILE *file;
+ int size;
+{
+ extern FILE *asm_out_data_file, *asm_out_file;
+ extern char call_used_regs[];
+ extern char *reg_numchar[];
+ extern char *current_function_name;
+ extern int frame_pointer_needed;
+ int regno;
+ int push_loc = stack_args_preallocated;
+ int tsize = current_function_total_framesize;
+ int num_regs = current_function_saved_reg_size;
+ char **reg_name_ptr = (TARGET_NAME_REGS) ? reg_names : reg_numchar;
+ char *sp_str = reg_name_ptr[STACK_POINTER_REGNUM];
+ char *t1_str = reg_name_ptr[MIPS_TEMP1_REGNUM];
+ char *base_str;
+
+
+ fprintf (file," #EPILOGUE\n");
+
+ if (tsize > 32767)
+ fprintf (file, "\tli\t%s,%d\n", t1_str, tsize);
+
+ if (frame_pointer_needed)
+ {
+ char *fp_str = reg_name_ptr[FRAME_POINTER_REGNUM];
+ if (tsize > 32767)
+ fprintf (file,"\tsubu\t%s,%s,%s\t# sp not trusted here\n",
+ sp_str, fp_str, t1_str);
+ else
+ fprintf (file,"\tsubu\t%s,%s,%d\t# sp not trusted here\n",
+ sp_str, fp_str, tsize);
+ }
+
+ if (push_loc > 32767 && num_regs > 0)
+ {
+ if ((tsize - (push_loc + num_regs)) <= 32767)
+ {
+ base_str = t1_str;
+ push_loc = tsize - push_loc;
+ }
+ else
+ {
+ base_str = reg_name_ptr[MIPS_TEMP2_REGNUM];
+ fprintf (file, "\tli\t%s,%d\n", base_str, push_loc);
+ push_loc = 0;
+ }
+ }
+ else
+ base_str = sp_str;
+
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (MUST_SAVE_REGISTER (regno))
+ {
+ fprintf (file,"\tlw\t%s,%d(%s)\n", reg_name_ptr[regno], push_loc,
+ base_str);
+ push_loc += 4;
+ }
+
+ push_loc = AL_ADJUST_ALIGN (push_loc);
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += 2)
+ if (regs_ever_live[regno] && !call_used_regs[regno])
+ {
+ fprintf (file, "\tl.d\t%s,%d(%s)\n", reg_name_ptr[regno], push_loc,
+ base_str);
+ push_loc += 8;
+ }
+
+ if (tsize > 32767)
+ fprintf (file, "\taddu\t%s,%s,%s\n", sp_str, sp_str, t1_str);
+
+ else if (tsize > 0)
+ fprintf (file, "\taddu\t%s,%s,%d\n", sp_str, sp_str, tsize);
+
+ fprintf (file,"\tj\t%s\n", reg_name_ptr[31]);
+ fprintf (file," #END EPILOGUE\n");
+ fprintf (file,"\t.end\t%s\n", current_function_name);
+
+ /* Reset state info for each function. */
+ stack_args_pushed = 0;
+ stack_args_preallocated = 0;
+ inside_function = 0;
+ sp_fp_difference = (char *)0;
+ number_functions_processed++;
+
+ /* Restore the output file if optimizing the GP (optimizing the GP causes
+ the text to be diverted to a tempfile, so that data decls come before
+ references to the data). */
+
+ if (TARGET_GP_OPT)
+ asm_out_file = asm_out_data_file;
+}