--- gcc/config/out-mips.c 2018/04/24 16:55:04 1.1.1.2 +++ gcc/config/out-mips.c 2018/04/24 17:00:28 1.1.1.4 @@ -1,6 +1,7 @@ /* Subroutines for insn-output.c for MIPS Contributed by A. Lichnewsky, lich@inria.inria.fr. - Copyright (C) 1989 Free Software Foundation, Inc. + Changes by Michael Meissner, meissner@osf.org. + Copyright (C) 1989, 1990 Free Software Foundation, Inc. This file is part of GNU CC. @@ -20,17 +21,98 @@ the Free Software Foundation, 675 Mass A #include -extern void my_print_rtx (); +#include +#include +#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; -/* When eliminating the frame pointer, this is the size of the frame - aside from explicit stack slots. */ -int frame_stack_difference; +/* 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 */ @@ -66,180 +148,227 @@ small_int (op, mode) } -#if 0 - /* Used to allow constant expression known - ** only at assembly time - */ -int -legitimize_constant_expr(expr) - rtx expr; -{ - if (GET_CODE (expr) == PLUS - || - GET_CODE (expr) == MINUS) - return (BASIC_CONSTANT_P (XEXP (expr, 0)) - && BASIC_CONSTANT_P (XEXP (expr, 1))); - return 0; -} -#endif - -char * -output_load_immediate (operands) - rtx *operands; +/* 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 */ { - rtx xops[3]; - xops[0] = operands[0]; - xops[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1])); + tree param, next_param; - if ((INTVAL (operands[1]) >> 16) - && (INTVAL (operands[1]) & 0xffff)) + if (TARGET_DEBUGE_MODE) { - if (!((-INTVAL (operands[1])) >> 16)) - { - xops[1] = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (operands[1])) & 0xffff); - output_asm_insn ("addi%:\t%0,$0,%x1\t#movsi low part of %2", - xops); - } - else + fprintf (stderr, "\ninit_cumulative_args\n"); + if (fntype != (tree)0) { - xops[1] = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (operands[1]))>>16); - output_asm_insn ("lui\t%0,%x1\t#movsi high part of %2", xops); - xops[1] = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (operands[1])) & 0xffff); - output_asm_insn ("ori\t%0,%x1\t#movsi low part of %2", xops); + putc ('\n', stderr); + debug_tree (fntype); + putc ('\n', stderr); } } - else if (INTVAL (operands[1]) >> 16) + + 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) { - xops[1] = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (operands[1]))>>16); - output_asm_insn ("lui\t%0,%x1\t#movsi high part of %2", xops); - xops[1] = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (operands[1])) & 0xffff); + next_param = TREE_CHAIN (param); + if (next_param == (tree)0 && TREE_VALUE (param) != void_type_node) + cum->gp_reg_found = 1; } - else + + /* 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) { - xops[1] = gen_rtx (CONST_INT, VOIDmode, - (INTVAL (operands[1])) & 0xffff); - output_asm_insn ("ori\t%0,$0,%x1\t#movsi low part of %2", - xops); + 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; + } } - return ""; } - - /* Used to obtain address of subregs */ - /* when in memory. This takes mode and */ - /* subreg number into account */ -rtx -addr_compensate (addr, submode, origmode, subnum) - rtx addr; - enum machine_mode submode; - enum machine_mode origmode; - int subnum; -{ - extern rtx change_address (); - extern rtx plus_constant (); +/* Advance the argument to the next argument position. */ - if (submode == QImode && origmode == SImode) - { -#ifdef BYTES_BIG_ENDIAN - return change_address (addr, QImode, - plus_constant (XEXP (addr, 0), 3 - subnum)); -#else - return change_address (addr, QImode, - plus_constant (XEXP (addr, 0), subnum)); -#endif - } - else if (submode == HImode && origmode == SImode) +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) { -#ifdef BYTES_BIG_ENDIAN - return change_address (addr, HImode, - plus_constant (XEXP (addr, 0), 2 - 2*subnum)); -#else - return change_address (addr, HImode, - plus_constant (XEXP (addr, 0), subnum)); -#endif + 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; } - else - abort_with_insn (addr, "addr_compensate does not support mode"); } +/* 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); - - /* VARARGS */ -int function_suspect; -int varargs_suspect = 0; -int this_varargs_suspect; + 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; + } - /* PARAMETER LIST CONSTRUCTION */ + if (cum->arg_words >= MAX_ARGS_IN_REGISTERS) + { + if (TARGET_DEBUGE_MODE) + fprintf (stderr, "\n"); + return 0; + } -static struct -{ - enum arg_state nxs_if_f, nxs_if_g; - short reg_if_f, reg_if_g; -} - arg_state_table[(int) (ARG_STA_GGGG + 1)] = ARG_STA_AUTOMA; + 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); +} - /* For use in Frame/ Stack pointer management*/ -char * current_function_name; -int current_function_total_framesize; -typedef CUMULATIVE_ARGS cumulative_args; -enum arg_state -function_arg_advance (cum, mode, type) - cumulative_args *cum; - enum machine_mode mode; - int type; +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 */ { - cum->arg_rec_state - = (FP_REGS == PREFERRED_RELOAD_CLASS_FM (mode, GR_REGS) - ? arg_state_table[(int)((cum->arg_rec_state))].nxs_if_f - : arg_state_table[(int)((cum->arg_rec_state))].nxs_if_g); + if (mode == BLKmode && cum->arg_words < MAX_ARGS_IN_REGISTERS) + { + int words = (int_size_in_bytes (type) + 3) / 4; - (cum->arg_num)++; -} + if (words + cum->arg_words < MAX_ARGS_IN_REGISTERS) + return 0; /* structure fits in registers */ -rtx -function_arg (cum, mode, type, named) - cumulative_args *cum; - enum machine_mode mode; - int type; - int named; -{ - int regnum; + 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; + } - regnum - = (FP_REGS == PREFERRED_RELOAD_CLASS_FM (mode, GR_REGS) - ? arg_state_table[(int)((cum->arg_rec_state))].reg_if_f - : arg_state_table[(int)((cum->arg_rec_state))].reg_if_g); - - if (mode == BLKmode) - return 0; - else if (regnum >= 0) - return gen_rtx (REG, mode, regnum); - else if (regnum == -2) + else if (mode == DImode && cum->arg_words == MAX_ARGS_IN_REGISTERS-1) { - if (mode == SFmode) - return gen_rtx (SUBREG, SFmode, gen_rtx (REG, DFmode, 6), 0); - else - return gen_rtx (REG, DFmode, 6); + if (TARGET_DEBUGE_MODE) + fprintf (stderr, "function_arg_partial_nregs = 1\n"); + + return 1; } - else - return 0; -} + 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; @@ -247,276 +376,711 @@ compare_collect (mode, op0, op1) { if (TARGET_DEBUGD_MODE) { - fprintf (stderr, "compare_collect mode = %d, operands::", mode); - my_print_rtx (op0); - my_print_rtx (op1); + 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; { - rtx previous; - rtx prev_par; - if (TARGET_DEBUGD_MODE) - { - fprintf (stderr, "compare_restore returning mode = %d, operands:%X,%X:", - branch_cmp_mode, branch_cmp_op[0], branch_cmp_op[1]); - my_print_rtx (branch_cmp_op[0]); - my_print_rtx (branch_cmp_op[1]); - } + if (!branch_cmp_op[0] || !branch_cmp_op[1]) + abort_with_insn (insn, "Compare_restore did not follow compare_collect"); - if ((! branch_cmp_op[0]) && (! branch_cmp_op[1])) + if (TARGET_DEBUGD_MODE) { - /* Signal that multiple branches following */ - /* a comparison have been found */ - if (TARGET_DEBUGD_MODE) - { - fprintf (stderr, "Not at ease in compare_restore\n"); - my_print_rtx (insn); - my_print_rtx (PREV_INSN (insn)); - } - /* Find the previous comparison */ - while ((GET_CODE (PREV_INSN (insn))) == JUMP_INSN) - { - insn = PREV_INSN (insn); - if (TARGET_DEBUGD_MODE) - my_print_rtx (PREV_INSN (insn)); - } - - previous = PATTERN (PREV_INSN (insn)); + fprintf (stderr, + "compare_restore returning mode = %s, operands:%X,%X:", + GET_MODE_NAME (branch_cmp_mode), + branch_cmp_op[0], + branch_cmp_op[1]); - if ((GET_CODE (previous)) == PARALLEL) - { - /* Signal that we have a very strange */ - /* RTL construct,... usually generated */ - /* by the optimizer, that seems to */ - /* contradict the documentation */ - - /* However, this construct holds the */ - /* correct information in a very reliable */ - /* way */ - - branch_cmp_op[0] = XVECEXP (previous, 0, 0); - branch_cmp_op[1] = XVECEXP (previous, 0, 1); - /* warning ("Check branch optimization with -mdebugd"); - */ - } - else - if (((GET_CODE (previous)) == SET) - && - ((GET_CODE (XEXP (previous, 0))) == CC0) - && - ((GET_CODE (XEXP (previous, 1))) == COMPARE)) - { /* Here we find the comparison info */ - /* in a more classical format */ - - previous = XEXP (previous, 1); - branch_cmp_op[0] = XEXP (previous, 0); - branch_cmp_op[1] = XEXP (previous, 1); - } - else - { /* Be prepared for other things popping out */ - abort (); - } + debug_rtx (branch_cmp_op[0]); + debug_rtx (branch_cmp_op[1]); } - - - if (! branch_cmp_op[0]) - operands[0] = gen_rtx (REG, VOIDmode, 0); - else - operands[0] = branch_cmp_op[0]; - if (! branch_cmp_op[1]) - operands[1] = gen_rtx (REG, VOIDmode, 0); - else - operands[1] = branch_cmp_op[1]; + operands[0] = branch_cmp_op[0]; + operands[1] = branch_cmp_op[1]; *mode = branch_cmp_mode; - branch_cmp_op[0] = NULL; - branch_cmp_op[1] = NULL; + /* 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; + } } -#if 0 - /* See flags.h and toplev.c */ -extern int optimize; -extern int flag_combine_regs; -extern int flag_strength_reduce; -extern int flag_no_peephole; -extern int flag_inline_functions; -extern int flag_omit_frame_pointer; -extern char *main_input_filename; -extern char *asm_file_name; - /* See c-tree.h and c-decl.c */ -extern int flag_signed_char; +/* Print the options used in the assembly file. */ -#include -#include -#include +static struct {char *name; int value;} target_switches [] + = TARGET_SWITCHES; void print_options (out) FILE *out; { - char *a_time; - long c_time; + int line_len; + int len; + int j; + char **p; + int mask = TARGET_DEFAULT; + extern char **save_argv; + extern char *version_string, *language_string; - fprintf (out, " #OPTIONS:\t%s%s%s%s%s%s%s\n", - (TARGET_NOFIXED_OVFL ? " -dnofixed-ovfl":" -dfixed-ovfl"), - (optimize ? " optimize" : ""), - (flag_combine_regs ? " -fcombine-regs" : " !combine-regs"), - (flag_strength_reduce ? "" : " !strength_reduce"), - (flag_omit_frame_pointer ?"" :" !omit_frame_pointer"), - (flag_no_peephole ? "" : " peephole"), - (flag_inline_functions ?" inline-functions":"")); - fprintf (out, " #OPTIONS:\t%s%s\n", - (flag_signed_char ? " signed-char" : " !signed-char"), - (TARGET_GP_OPT ? " gpOPT" : " !gpOPT")); - fprintf (out, " #Source:\t%s\n", main_input_filename); - fprintf (out, " #Destination:\t%s\n", asm_file_name); - c_time = time (0); - a_time = ctime (&c_time); - fprintf (out, " #Compiled:\t%s", a_time); -#ifdef __GNUC__ -#ifndef __VERSION__ -#define __VERSION__ "[unknown]" +#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 - fprintf (out, " # (META)compiled by GNU C version %s.\n", __VERSION__); +#ifdef __GNUC__ + fprintf (out, " compiled by GNU C\n\n"); #else - fprintf (out, " # (META)compiled by CC.\n"); + fprintf (out, " compiled by CC\n\n"); #endif -} - - /* DEBUGGING UTILITIES */ -rtx al_log_insn_debug; - -abort_show_logged () -{ - if (al_log_insn_debug) - my_print_rtx (al_log_insn_debug); - abort (); -} -extern FILE *outfile; + 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; + } -void -my_print_rtx (in_rtx) - register rtx in_rtx; -{ - FILE *old; - old = outfile; + fprintf (out, "\n\n # Cc1 arguments (-G value = %d):", + mips_section_threshold); - outfile = stderr; - print_rtx (in_rtx); - fprintf (outfile, "\n"); + 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); + } - outfile = old; +#endif } -void -my_print_insncode (insn) - rtx insn; -{ - FILE *old; - old = outfile; - - outfile = stderr; - print_rtx (insn); - fprintf (outfile, "\n"); - fprintf (outfile, "INSN_CODE (insn) = %X\n", INSN_CODE (insn)); + - outfile = old; -} +/* Abort after printing out a specific insn. */ void abort_with_insn (insn, reason) rtx insn; char *reason; { - fprintf (stderr, "About to Abort::%s\n", reason); - my_print_rtx (insn); + error (reason); + debug_rtx (insn); abort (); } -#endif + +/* Write a message to stderr (for use in macros expanded in files that do not + include stdio.h). */ void -abort_with_insn () +trace (s, s1, s2) + char *s, *s1, *s2; { - abort (); + fprintf (stderr, s, s1, s2); } -void -my_print_rtx (in_rtx) - register rtx in_rtx; + +/* 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 () { - abort(); + register int i; + + i = TARGET_GVALUE; + if (i >= 6) + i += 3; + mips_section_threshold = (i != 0) ? 1 << i : 0; } -#include "tree.h" - /* GET SECTION (DATA/SDATA) THRESHOLD */ -int mips_section_threshold = -1; +/* Compute a string to use as a temporary file name. */ -extern char *tree_code_name[]; +static FILE * +make_temp_file () +{ + char *temp_filename; + FILE *stream; + extern char *getenv (); + char *base = getenv ("TMPDIR"); + int len; -int -mips_section_get () + 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; { - register int i; - if (mips_section_threshold == -1) + if (TARGET_NAME_REGS) + fprintf (stream, "#include \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) { - i = TARGET_GVALUE; - if (i >= 6) i += 3; - if ((1 << i) != MIPS_GVALUE_DEFAULT) - warning ("G value this run == %d\n", 1 << i); - mips_section_threshold = 1 << i; + asm_out_data_file = stream; + asm_out_text_file = make_temp_file (); } + else + asm_out_data_file = asm_out_text_file = stream; - return mips_section_threshold; } + +/* 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, tree_exp, name) +mips_output_external (file, decl, name) FILE *file; - tree tree_exp; + tree decl; char *name; { - register int i; + extern char *permalloc (); + register struct extern_list *p; + int len; + if (TARGET_GP_OPT - && ((TREE_CODE (tree_exp)) != FUNCTION_DECL) - && - ((i = int_size_in_bytes (TREE_TYPE (tree_exp))) > 0)) - { - fputs ("\n#ifndef _gccx__", file); - assemble_name (file, name); - fputs ("\n.extern\t", (file)); - assemble_name ((file), (name)); - fprintf ((file), ", %d", i, (TREE_TYPE (tree_exp))); - fputs ("\n#define _gccx__", file); - assemble_name (file, name); - fputs ("\n#endif\n ", file); + && 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; } - -extern char *asm_file_name; + +/* 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) { - fprintf (file, "\n#else\n#ifndef %sRESCAN_GCC", "__x_"); - fprintf (file, "\n#define %sRESCAN_GCC\n#include \"%s\"", "__x_", - asm_file_name); - fprintf (file, "\n#endif\n#endif\n"); + 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; }