--- gcc/config/mips.c 2018/04/24 17:51:31 1.1 +++ gcc/config/mips.c 2018/04/24 18:03:51 1.1.1.4 @@ -50,7 +50,7 @@ the Free Software Foundation, 675 Mass A #define X_OK 1 #endif -#ifdef USG +#if defined(USG) || defined(NO_STAB_H) #include "gstab.h" /* If doing DBX on sysV, use our own stab.h. */ #else #include /* On BSD, use the system's stab.h. */ @@ -62,36 +62,39 @@ the Free Software Foundation, 675 Mass A #define STAB_CODE_TYPE int #endif -extern char *getenv (); +extern void abort (); +extern int atoi (); +extern char *getenv (); +extern char *mktemp (); + +extern rtx adj_offsettable_operand (); +extern rtx copy_to_reg (); +extern void error (); +extern void fatal (); +extern tree lookup_name (); +extern void pfatal_with_name (); +extern void warning (); -extern char *permalloc (); -extern void debug_rtx (); -extern void abort_with_insn (); -extern rtx copy_to_reg (); -extern rtx adj_offsettable_operand (); -extern int offsettable_address_p (); -extern tree lookup_name (); - -extern rtx gen_movqi (); -extern rtx gen_movhi (); -extern rtx gen_movsi (); -extern rtx gen_movsi_ulw (); -extern rtx gen_movsi_usw (); -extern rtx gen_addsi3 (); -extern rtx gen_iorsi3 (); -extern rtx gen_andsi3 (); -extern rtx gen_bne (); -extern rtx gen_beq (); -extern rtx gen_cmpsi (); -extern rtx gen_jump (); - -extern char call_used_regs[]; -extern char *asm_file_name; -extern FILE *asm_out_file; extern tree current_function_decl; -extern char **save_argv; -extern char *version_string; -extern char *language_string; +extern FILE *asm_out_file; + +/* Enumeration for all of the relational tests, so that we can build + arrays indexed by the test type, and not worry about the order + of EQ, NE, etc. */ + +enum internal_test { + ITEST_EQ, + ITEST_NE, + ITEST_GT, + ITEST_GE, + ITEST_LT, + ITEST_LE, + ITEST_GTU, + ITEST_GEU, + ITEST_LTU, + ITEST_LEU, + ITEST_MAX + }; /* Global variables for machine-dependent things. */ @@ -104,10 +107,6 @@ int mips_section_threshold = -1; /* Count the number of .file directives, so that .loc is up to date. */ int num_source_filenames = 0; -/* Count of the number of functions created so far, in order to make - unique labels for omitting the frame pointer. */ -int number_functions_processed = 0; - /* Count the number of sdb related labels are generated (to find block start and end boundaries). */ int sdb_label_count = 0; @@ -135,9 +134,6 @@ struct extern_list { int size; /* size in bytes */ } *extern_head = 0; -/* Name of the current function. */ -char *current_function_name; - /* Name of the file containing the current function. */ char *current_function_file = ""; @@ -145,7 +141,7 @@ char *current_function_file = ""; within a function. */ int file_in_function_warning = FALSE; -/* Whether to supress issuing .loc's because the user attempted +/* Whether to suppress issuing .loc's because the user attempted to change the filename within a function. */ int ignore_line_number = FALSE; @@ -180,6 +176,10 @@ rtx branch_cmp[2]; /* what type of branch to use */ enum cmp_type branch_type; +/* Number of previously seen half-pic pointers and references. */ +static int prev_half_pic_ptrs = 0; +static int prev_half_pic_refs = 0; + /* which cpu are we scheduling for */ enum processor_type mips_cpu; @@ -404,19 +404,19 @@ large_int (op, mode) rtx op; enum machine_mode mode; { - long value; + HOST_WIDE_INT value; if (GET_CODE (op) != CONST_INT) return FALSE; value = INTVAL (op); - if ((value & 0xffff0000) == 0) /* ior reg,$r0,value */ + if ((value & ~0x0000ffff) == 0) /* ior reg,$r0,value */ return FALSE; - if ((value & 0xffff0000) == 0xffff0000) /* subu reg,$r0,value */ + if (((unsigned long)(value + 32768)) <= 32767) /* subu reg,$r0,value */ return FALSE; - if ((value & 0x0000ffff) == 0) /* lui reg,value>>16 */ + if ((value & 0xffff0000) == value) /* lui reg,value>>16 */ return FALSE; return TRUE; @@ -431,6 +431,9 @@ reg_or_0_operand (op, mode) { switch (GET_CODE (op)) { + default: + break; + case CONST_INT: return (INTVAL (op) == 0); @@ -461,6 +464,16 @@ md_register_operand (op, mode) && MD_REG_P (REGNO (op))); } +/* Return truth value of whether OP is the FP status register. */ + +int +fpsw_register_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == REG && ST_REG_P (REGNO (op))); +} + /* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */ int @@ -519,7 +532,6 @@ simple_memory_operand (op, mode) enum machine_mode mode; { rtx addr, plus0, plus1; - int offset = 0; /* Eliminate non-memory operations */ if (GET_CODE (op) != MEM) @@ -533,6 +545,9 @@ simple_memory_operand (op, mode) addr = XEXP (op, 0); switch (GET_CODE (addr)) { + default: + break; + case REG: return TRUE; @@ -570,17 +585,19 @@ simple_memory_operand (op, mode) if (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT) return FALSE; - addr = eliminate_constant_term (addr, &offset); - if (GET_CODE (op) != SYMBOL_REF) - return FALSE; - + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (addr, &offset); + if (GET_CODE (op) != SYMBOL_REF) + return FALSE; + + /* let's be paranoid.... */ + if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff) + return FALSE; + } /* fall through */ case SYMBOL_REF: - /* let's be paranoid.... */ - if (offset < 0 || offset > 0xffff) - return FALSE; - return SYMBOL_REF_FLAG (addr); #endif } @@ -588,34 +605,6 @@ simple_memory_operand (op, mode) return FALSE; } -/* Return true if the address is suitable for function call. */ - -int -call_memory_operand (op, mode) - rtx op; - enum machine_mode mode; -{ - rtx addr; - enum rtx_code code; - - if (GET_CODE (op) != MEM) - return FALSE; - - addr = XEXP (op, 0); - code = GET_CODE (addr); - - if (GET_MODE (addr) != FUNCTION_MODE) - return FALSE; - - if (code == REG || code == SUBREG) - return TRUE; - - if (CONSTANT_ADDRESS_P (addr)) - return TRUE; - - return FALSE; -} - /* Return true if the code of this rtx pattern is EQ or NE. */ int @@ -684,6 +673,23 @@ fcmp_op (op, mode) return (classify_op (op, mode) & CLASS_FCMP_OP) != 0; } + +/* Return true if the operand is either the PC or a label_ref. */ + +int +pc_or_label_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + if (op == pc_rtx) + return TRUE; + + if (GET_CODE (op) == LABEL_REF) + return TRUE; + + return FALSE; +} + /* Return an operand string if the given instruction's delay slot or wrap it in a .set noreorder section. This is for filling delay @@ -709,7 +715,7 @@ mips_fill_delay_slot (ret, type, operand register rtx next_insn = (cur_insn) ? NEXT_INSN (cur_insn) : (rtx)0; register int num_nops; - if (type == DELAY_LOAD) + if (type == DELAY_LOAD || type == DELAY_FCMP) num_nops = 1; else if (type == DELAY_HILO) @@ -774,7 +780,7 @@ mips_fill_delay_slot (ret, type, operand /* Determine whether a memory reference takes one (based off of the GP pointer), - two (normal), or three (label + reg) instructins, and bump the appropriate + two (normal), or three (label + reg) instructions, and bump the appropriate counter for -mstats. */ void @@ -803,6 +809,9 @@ mips_count_memory_refs (op, num) looping = FALSE; switch (GET_CODE (addr)) { + default: + break; + case REG: case CONST_INT: break; @@ -997,10 +1006,11 @@ mips_move_1word (operands, insn, unsigne target, so zero/sign extend can use this code as well. */ switch (GET_MODE (op1)) { - case SFmode: ret = "lw\t%0,%1"; break; - case SImode: ret = "lw\t%0,%1"; break; - case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break; - case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break; + default: break; + case SFmode: ret = "lw\t%0,%1"; break; + case SImode: ret = "lw\t%0,%1"; break; + case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break; + case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break; } } @@ -1028,17 +1038,12 @@ mips_move_1word (operands, insn, unsigne else if (FP_REG_P (regno0)) { delay = DELAY_LOAD; - return "mtc1\t%z1,%0"; + ret = "mtc1\t%z1,%0"; } } else if (GP_REG_P (regno0)) - { - if ((INTVAL (operands[1]) & 0x0000ffff) == 0) - ret = "lui\t%0,(%X1)>>16"; - else - ret = "li\t%0,%1"; - } + ret = (INTVAL (op1) < 0) ? "li\t%0,%1\t\t\t# %X1" : "li\t%0,%X1\t\t# %1"; } else if (code1 == CONST_DOUBLE && mode == SFmode) @@ -1063,20 +1068,51 @@ mips_move_1word (operands, insn, unsigne } else if (code1 == LABEL_REF) - ret = "la\t%0,%a1"; - - else if (code1 == SYMBOL_REF || code1 == CONST) { if (TARGET_STATS) mips_count_memory_refs (op1, 1); + ret = "la\t%0,%a1"; + } + + else if (code1 == SYMBOL_REF || code1 == CONST) + { if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1)) { - delay = DELAY_LOAD; - ret = "la\t%0,%a1\t\t# pic reference"; + rtx offset = const0_rtx; + + if (GET_CODE (op1) == CONST) + op1 = eliminate_constant_term (XEXP (op1, 0), &offset); + + if (GET_CODE (op1) == SYMBOL_REF) + { + operands[2] = HALF_PIC_PTR (op1); + + if (TARGET_STATS) + mips_count_memory_refs (operands[2], 1); + + if (INTVAL (offset) == 0) + { + delay = DELAY_LOAD; + ret = "lw\t%0,%2"; + } + else + { + dslots_load_total++; + operands[3] = offset; + ret = (SMALL_INT (offset)) + ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3" + : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]"; + } + } } else - ret = "la\t%0,%a1"; + { + if (TARGET_STATS) + mips_count_memory_refs (op1, 1); + + ret = "la\t%0,%a1"; + } } else if (code1 == PLUS) @@ -1109,6 +1145,7 @@ mips_move_1word (operands, insn, unsigne { switch (mode) { + default: break; case SFmode: ret = "sw\t%1,%0"; break; case SImode: ret = "sw\t%1,%0"; break; case HImode: ret = "sh\t%1,%0"; break; @@ -1124,6 +1161,7 @@ mips_move_1word (operands, insn, unsigne { switch (mode) { + default: break; case SFmode: ret = "sw\t%z1,%0"; break; case SImode: ret = "sw\t%z1,%0"; break; case HImode: ret = "sh\t%z1,%0"; break; @@ -1135,6 +1173,7 @@ mips_move_1word (operands, insn, unsigne { switch (mode) { + default: break; case SFmode: ret = "sw\t%.,%0"; break; case SImode: ret = "sw\t%.,%0"; break; case HImode: ret = "sh\t%.,%0"; break; @@ -1262,8 +1301,8 @@ mips_move_2words (operands, insn) else { - operands[2] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_LOW (op1)); - operands[3] = gen_rtx (CONST_INT, VOIDmode, CONST_DOUBLE_HIGH (op1)); + operands[2] = GEN_INT (CONST_DOUBLE_LOW (op1)); + operands[3] = GEN_INT (CONST_DOUBLE_HIGH (op1)); ret = "li\t%M0,%3\n\tli\t%L0,%2"; } } @@ -1299,7 +1338,7 @@ mips_move_2words (operands, insn) else if (code1 == CONST_INT && GET_MODE (op0) == DImode && GP_REG_P (regno0)) { - operands[2] = gen_rtx (CONST_INT, VOIDmode, INTVAL (operands[1]) >= 0 ? 0 : -1); + operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1); ret = "li\t%M0,%2\n\tli\t%L0,%1"; } @@ -1392,16 +1431,17 @@ mips_move_2words (operands, insn) /* Provide the costs of an addressing mode that contains ADDR. - If ADDR is not a valid address, its cost is irrelavent. */ + If ADDR is not a valid address, its cost is irrelevant. */ int mips_address_cost (addr) rtx addr; { - int offset; - switch (GET_CODE (addr)) { + default: + break; + case LO_SUM: case HIGH: return 1; @@ -1410,17 +1450,18 @@ mips_address_cost (addr) return 2; case CONST: - offset = 0; - addr = eliminate_constant_term (addr, &offset); - if (GET_CODE (addr) == LABEL_REF) - return 2; - - if (GET_CODE (addr) != SYMBOL_REF) - return 4; + { + rtx offset = const0_rtx; + addr = eliminate_constant_term (addr, &offset); + if (GET_CODE (addr) == LABEL_REF) + return 2; - if (offset < -32768 || offset > 32767) - return 2; + if (GET_CODE (addr) != SYMBOL_REF) + return 4; + if (INTVAL (offset) < -32768 || INTVAL (offset) > 32767) + return 2; + } /* fall through */ case SYMBOL_REF: @@ -1442,6 +1483,9 @@ mips_address_cost (addr) switch (GET_CODE (plus1)) { + default: + break; + case CONST_INT: { int value = INTVAL (plus1); @@ -1462,6 +1506,184 @@ mips_address_cost (addr) } +/* Make normal rtx_code into something we can index from an array */ + +static enum internal_test +map_test_to_internal_test (test_code) + enum rtx_code test_code; +{ + enum internal_test test = ITEST_MAX; + + switch (test_code) + { + default: break; + case EQ: test = ITEST_EQ; break; + case NE: test = ITEST_NE; break; + case GT: test = ITEST_GT; break; + case GE: test = ITEST_GE; break; + case LT: test = ITEST_LT; break; + case LE: test = ITEST_LE; break; + case GTU: test = ITEST_GTU; break; + case GEU: test = ITEST_GEU; break; + case LTU: test = ITEST_LTU; break; + case LEU: test = ITEST_LEU; break; + } + + return test; +} + + +/* Generate the code to compare two integer values. The return value is: + (reg:SI xx) The pseudo register the comparison is in + (rtx)0 No register, generate a simple branch. */ + +rtx +gen_int_relational (test_code, result, cmp0, cmp1, p_invert) + enum rtx_code test_code; /* relational test (EQ, etc) */ + rtx result; /* result to store comp. or 0 if branch */ + rtx cmp0; /* first operand to compare */ + rtx cmp1; /* second operand to compare */ + int *p_invert; /* NULL or ptr to hold whether branch needs */ + /* to reverse its test */ +{ + struct cmp_info { + enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */ + int const_low; /* low bound of constant we can accept */ + int const_high; /* high bound of constant we can accept */ + int const_add; /* constant to add (convert LE -> LT) */ + int reverse_regs; /* reverse registers in test */ + int invert_const; /* != 0 if invert value if cmp1 is constant */ + int invert_reg; /* != 0 if invert value if cmp1 is register */ + int unsignedp; /* != 0 for unsigned comparisons. */ + }; + + static struct cmp_info info[ (int)ITEST_MAX ] = { + + { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */ + { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */ + { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */ + { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */ + { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */ + { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */ + { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */ + { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */ + { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */ + { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */ + }; + + enum internal_test test; + struct cmp_info *p_info; + int branch_p; + int eqne_p; + int invert; + rtx reg; + rtx reg2; + + test = map_test_to_internal_test (test_code); + if (test == ITEST_MAX) + abort (); + + p_info = &info[ (int)test ]; + eqne_p = (p_info->test_code == XOR); + + /* Eliminate simple branches */ + branch_p = (result == (rtx)0); + if (branch_p) + { + if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG) + { + /* Comparisons against zero are simple branches */ + if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) + return (rtx)0; + + /* Test for beq/bne. */ + if (eqne_p) + return (rtx)0; + } + + /* allocate a pseudo to calculate the value in. */ + result = gen_reg_rtx (SImode); + } + + /* Make sure we can handle any constants given to us. */ + if (GET_CODE (cmp0) == CONST_INT) + cmp0 = force_reg (SImode, cmp0); + + if (GET_CODE (cmp1) == CONST_INT) + { + HOST_WIDE_INT value = INTVAL (cmp1); + if (value < p_info->const_low || value > p_info->const_high) + cmp1 = force_reg (SImode, cmp1); + } + + /* See if we need to invert the result. */ + invert = (GET_CODE (cmp1) == CONST_INT) + ? p_info->invert_const + : p_info->invert_reg; + + if (p_invert != (int *)0) + { + *p_invert = invert; + invert = FALSE; + } + + /* Comparison to constants, may involve adding 1 to change a LT into LE. + Comparison between two registers, may involve switching operands. */ + if (GET_CODE (cmp1) == CONST_INT) + { + if (p_info->const_add != 0) + { + HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add; + /* If modification of cmp1 caused overflow, + we would get the wrong answer if we follow the usual path; + thus, x > 0xffffffffu would turn into x > 0u. */ + if ((p_info->unsignedp + ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1) + : new > INTVAL (cmp1)) + != (p_info->const_add > 0)) + /* 1 is the right value in the LE and LEU case. + In the GT and GTU case, *p_invert is already set, + so this is effectively 0. */ + return force_reg (SImode, const1_rtx); + else + cmp1 = GEN_INT (new); + } + } + else if (p_info->reverse_regs) + { + rtx temp = cmp0; + cmp0 = cmp1; + cmp1 = temp; + } + + if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) + reg = cmp0; + else + { + reg = (invert || eqne_p) ? gen_reg_rtx (SImode) : result; + emit_move_insn (reg, gen_rtx (p_info->test_code, SImode, cmp0, cmp1)); + } + + if (test == ITEST_NE) + { + emit_move_insn (result, gen_rtx (GTU, SImode, reg, const0_rtx)); + invert = FALSE; + } + + else if (test == ITEST_EQ) + { + reg2 = (invert) ? gen_reg_rtx (SImode) : result; + emit_move_insn (reg2, gen_rtx (LTU, SImode, reg, const1_rtx)); + reg = reg2; + } + + if (invert) + emit_move_insn (result, gen_rtx (XOR, SImode, reg, const1_rtx)); + + return result; +} + + /* Emit the common code for doing conditional branches. operand[0] is the label to jump to. The comparison operands are saved away by cmp{si,sf,df}. */ @@ -1471,36 +1693,22 @@ gen_conditional_branch (operands, test_c rtx operands[]; enum rtx_code test_code; { - enum { - I_EQ, - I_NE, - I_GT, - I_GE, - I_LT, - I_LE, - I_GTU, - I_GEU, - I_LTU, - I_LEU, - I_MAX - } test = I_MAX; - - static enum machine_mode mode_map[(int)CMP_MAX][(int)I_MAX] = { + static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = { { /* CMP_SI */ - CC_EQmode, /* eq */ - CC_EQmode, /* ne */ - CCmode, /* gt */ - CCmode, /* ge */ - CCmode, /* lt */ - CCmode, /* le */ - CCmode, /* gtu */ - CCmode, /* geu */ - CCmode, /* ltu */ - CCmode, /* leu */ + SImode, /* eq */ + SImode, /* ne */ + SImode, /* gt */ + SImode, /* ge */ + SImode, /* lt */ + SImode, /* le */ + SImode, /* gtu */ + SImode, /* geu */ + SImode, /* ltu */ + SImode, /* leu */ }, { /* CMP_SF */ CC_FPmode, /* eq */ - CC_FPmode, /* ne */ + CC_REV_FPmode, /* ne */ CC_FPmode, /* gt */ CC_FPmode, /* ge */ CC_FPmode, /* lt */ @@ -1512,7 +1720,7 @@ gen_conditional_branch (operands, test_c }, { /* CMP_DF */ CC_FPmode, /* eq */ - CC_FPmode, /* ne */ + CC_REV_FPmode, /* ne */ CC_FPmode, /* gt */ CC_FPmode, /* ge */ CC_FPmode, /* lt */ @@ -1525,33 +1733,22 @@ gen_conditional_branch (operands, test_c }; enum machine_mode mode; - enum cmp_type type = branch_type; - rtx cmp0 = branch_cmp[0]; - rtx cmp1 = branch_cmp[1]; - rtx label = gen_rtx (LABEL_REF, VOIDmode, operands[0]); + enum cmp_type type = branch_type; + rtx cmp0 = branch_cmp[0]; + rtx cmp1 = branch_cmp[1]; + rtx label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]); + rtx label2 = pc_rtx; + rtx reg = (rtx)0; + int invert = 0; + enum internal_test test = map_test_to_internal_test (test_code); - /* Make normal rtx_code into something we can index from an array */ - switch (test_code) + if (test == ITEST_MAX) { - case EQ: test = I_EQ; break; - case NE: test = I_NE; break; - case GT: test = I_GT; break; - case GE: test = I_GE; break; - case LT: test = I_LT; break; - case LE: test = I_LE; break; - case GTU: test = I_GTU; break; - case GEU: test = I_GEU; break; - case LTU: test = I_LTU; break; - case LEU: test = I_LEU; break; - } - - if (test == I_MAX) - { - mode = CCmode; + mode = SImode; goto fail; } - /* Get the machine mode to use (CCmode, CC_EQmode, or CC_FPmode). */ + /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode). */ mode = mode_map[(int)type][(int)test]; if (mode == VOIDmode) goto fail; @@ -1562,50 +1759,46 @@ gen_conditional_branch (operands, test_c goto fail; case CMP_SI: - /* Change >, >=, <, <= tests against 0 to use CC_0mode, since we have - special instructions to do these tests directly. */ - - if (mode == CCmode && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) + reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert); + if (reg != (rtx)0) { - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, cmp0)); - mode = CC_0mode; + cmp0 = reg; + cmp1 = const0_rtx; + test_code = NE; } - else if (mode == CCmode && GET_CODE (cmp0) == CONST_INT && INTVAL (cmp0) == 0) - { - emit_insn (gen_rtx (SET, VOIDmode, cc0_rtx, cmp1)); - test_code = reverse_condition (test_code); - mode = CC_0mode; - } + /* Make sure not non-zero constant if ==/!= */ + else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) + cmp1 = force_reg (SImode, cmp1); - else - { - /* force args to register for equality comparisons. */ - if (mode == CC_EQmode && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) - cmp1 = force_reg (SImode, cmp1); - - emit_insn (gen_rtx (SET, VOIDmode, - cc0_rtx, - gen_rtx (COMPARE, mode, cmp0, cmp1))); - } break; case CMP_DF: - emit_insn (gen_cmpdf_internal (cmp0, cmp1)); - break; - case CMP_SF: - emit_insn (gen_cmpsf_internal (cmp0, cmp1)); + { + rtx reg = gen_rtx (REG, mode, FPSW_REGNUM); + emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1))); + cmp0 = reg; + cmp1 = const0_rtx; + test_code = NE; + } break; } - + /* Generate the jump */ + if (invert) + { + label2 = label1; + label1 = pc_rtx; + } + emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, - gen_rtx (test_code, mode, cc0_rtx, const0_rtx), - label, - pc_rtx))); + gen_rtx (test_code, mode, cmp0, cmp1), + label1, + label2))); + return; fail: @@ -1618,13 +1811,15 @@ fail: /* Internal code to generate the load and store of one word/short/byte. The load is emitted directly, and the store insn is returned. */ +#if 0 static rtx -block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align) - rtx src_reg; /* register holding source memory addresss */ - rtx dest_reg; /* register holding dest. memory addresss */ +block_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src) + rtx src_reg; /* register holding source memory address */ + rtx dest_reg; /* register holding dest. memory address */ int *p_bytes; /* pointer to # bytes remaining */ int *p_offset; /* pointer to current offset */ int align; /* alignment */ + rtx orig_src; /* original source for making a reg note */ { int bytes; /* # bytes remaining */ int offset; /* offset to use */ @@ -1632,7 +1827,9 @@ block_move_load_store (dest_reg, src_reg enum machine_mode mode; /* mode to use for load/store */ rtx reg; /* temporary register */ rtx src_addr; /* source address */ - rtx dest_addr; /* destintation address */ + rtx dest_addr; /* destination address */ + rtx insn; /* insn of the load */ + rtx orig_src_addr; /* original source address */ rtx (*load_func)(); /* function to generate load insn */ rtx (*store_func)(); /* function to generate destination insn */ @@ -1648,6 +1845,9 @@ block_move_load_store (dest_reg, src_reg store_func = gen_movsi; } +#if 0 + /* Don't generate unligned moves here, rather defer those to the + general movestrsi_internal pattern. */ else if (bytes >= UNITS_PER_WORD) { mode = SImode; @@ -1655,6 +1855,7 @@ block_move_load_store (dest_reg, src_reg load_func = gen_movsi_ulw; store_func = gen_movsi_usw; } +#endif else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT) { @@ -1683,14 +1884,21 @@ block_move_load_store (dest_reg, src_reg } else { - src_addr = gen_rtx (PLUS, Pmode, src_reg, gen_rtx (CONST_INT, VOIDmode, offset)); - dest_addr = gen_rtx (PLUS, Pmode, dest_reg, gen_rtx (CONST_INT, VOIDmode, offset)); + src_addr = gen_rtx (PLUS, Pmode, src_reg, GEN_INT (offset)); + dest_addr = gen_rtx (PLUS, Pmode, dest_reg, GEN_INT (offset)); } reg = gen_reg_rtx (mode); - emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr))); + insn = emit_insn ((*load_func) (reg, gen_rtx (MEM, mode, src_addr))); + orig_src_addr = XEXP (orig_src, 0); + if (CONSTANT_P (orig_src_addr)) + REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_EQUIV, + plus_constant (orig_src_addr, offset), + REG_NOTES (insn)); + return (*store_func) (gen_rtx (MEM, mode, dest_addr), reg); } +#endif /* Write a series of loads/stores to move some bytes. Generate load/stores as follows: @@ -1709,12 +1917,14 @@ block_move_load_store (dest_reg, src_reg two temp registers are needed. Two delay slots are used in deference to the R4000. */ +#if 0 static void -block_move_sequence (dest_reg, src_reg, bytes, align) +block_move_sequence (dest_reg, src_reg, bytes, align, orig_src) rtx dest_reg; /* register holding destination address */ rtx src_reg; /* register holding source address */ int bytes; /* # bytes to move */ int align; /* max alignment to assume */ + rtx orig_src; /* original source for making a reg note */ { int offset = 0; rtx prev2_store = (rtx)0; @@ -1729,7 +1939,9 @@ block_move_sequence (dest_reg, src_reg, prev2_store = prev_store; prev_store = cur_store; - cur_store = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align); + cur_store = block_move_load_store (dest_reg, src_reg, + &bytes, &offset, + align, orig_src); } /* Finish up last three stores. */ @@ -1742,6 +1954,7 @@ block_move_sequence (dest_reg, src_reg, if (cur_store) emit_insn (cur_store); } +#endif /* Write a loop to move a constant number of bytes. Generate load/stores as follows: @@ -1751,11 +1964,11 @@ block_move_sequence (dest_reg, src_reg, temp2 = src[1]; ... temp = src[MAX_MOVE_REGS-1]; - src += MAX_MOVE_REGS; dest[0] = temp1; dest[1] = temp2; ... dest[MAX_MOVE_REGS-1] = temp; + src += MAX_MOVE_REGS; dest += MAX_MOVE_REGS; } while (src != final); @@ -1770,19 +1983,20 @@ block_move_sequence (dest_reg, src_reg, #define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD) static void -block_move_loop (dest_reg, src_reg, bytes, align) +block_move_loop (dest_reg, src_reg, bytes, align, orig_src) rtx dest_reg; /* register holding destination address */ rtx src_reg; /* register holding source address */ int bytes; /* # bytes to move */ int align; /* alignment */ + rtx orig_src; /* original source for making a reg note */ { - rtx stores[MAX_MOVE_REGS]; + rtx dest_mem = gen_rtx (MEM, BLKmode, dest_reg); + rtx src_mem = gen_rtx (MEM, BLKmode, src_reg); + rtx align_rtx = GEN_INT (align); rtx label; rtx final_src; rtx bytes_rtx; - int i; int leftover; - int offset; if (bytes < 2*MAX_MOVE_BYTES) abort (); @@ -1792,7 +2006,7 @@ block_move_loop (dest_reg, src_reg, byte label = gen_label_rtx (); final_src = gen_reg_rtx (Pmode); - bytes_rtx = gen_rtx (CONST_INT, VOIDmode, bytes); + bytes_rtx = GEN_INT (bytes); if (bytes > 0x7fff) { @@ -1804,20 +2018,17 @@ block_move_loop (dest_reg, src_reg, byte emit_label (label); - offset = 0; - for (i = 0; i < MAX_MOVE_REGS; i++) - stores[i] = block_move_load_store (dest_reg, src_reg, &bytes, &offset, align); - - emit_insn (gen_addsi3 (src_reg, src_reg, gen_rtx (CONST_INT, VOIDmode, MAX_MOVE_BYTES))); - for (i = 0; i < MAX_MOVE_REGS; i++) - emit_insn (stores[i]); - - emit_insn (gen_addsi3 (dest_reg, dest_reg, gen_rtx (CONST_INT, VOIDmode, MAX_MOVE_BYTES))); + bytes_rtx = GEN_INT (MAX_MOVE_BYTES); + emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx)); + emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx)); + emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx)); emit_insn (gen_cmpsi (src_reg, final_src)); emit_jump_insn (gen_bne (label)); if (leftover) - block_move_sequence (dest_reg, src_reg, leftover, align); + emit_insn (gen_movstrsi_internal (dest_mem, src_mem, + GEN_INT (leftover), + align_rtx)); } @@ -1857,27 +2068,39 @@ expand_block_move (operands) rtx operands[]; { rtx bytes_rtx = operands[2]; + rtx align_rtx = operands[3]; int constp = (GET_CODE (bytes_rtx) == CONST_INT); int bytes = (constp ? INTVAL (bytes_rtx) : 0); - int align = INTVAL (operands[3]); + int align = INTVAL (align_rtx); + rtx orig_src = operands[1]; rtx src_reg; rtx dest_reg; if (constp && bytes <= 0) return; + if (align > UNITS_PER_WORD) + align = UNITS_PER_WORD; + /* Move the address into scratch registers. */ dest_reg = copy_addr_to_reg (XEXP (operands[0], 0)); - src_reg = copy_addr_to_reg (XEXP (operands[1], 0)); + src_reg = copy_addr_to_reg (XEXP (orig_src, 0)); if (TARGET_MEMCPY) block_move_call (dest_reg, src_reg, bytes_rtx); +#if 0 + else if (constp && bytes <= 3*align) + block_move_sequence (dest_reg, src_reg, bytes, align, orig_src); +#endif + else if (constp && bytes <= 2*MAX_MOVE_BYTES) - block_move_sequence (dest_reg, src_reg, bytes, align); + emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg), + gen_rtx (MEM, BLKmode, src_reg), + bytes_rtx, align_rtx)); else if (constp && align >= UNITS_PER_WORD && optimize) - block_move_loop (dest_reg, src_reg, bytes, align); + block_move_loop (dest_reg, src_reg, bytes, align, orig_src); else if (constp && optimize) { @@ -1893,23 +2116,34 @@ expand_block_move (operands) bytes -= leftover; emit_insn (gen_iorsi3 (temp, src_reg, dest_reg)); - emit_insn (gen_andsi3 (temp, temp, gen_rtx (CONST_INT, VOIDmode, UNITS_PER_WORD-1))); + emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD-1))); emit_insn (gen_cmpsi (temp, const0_rtx)); emit_jump_insn (gen_beq (aligned_label)); /* Unaligned loop. */ - block_move_loop (dest_reg, src_reg, bytes, 1); + block_move_loop (dest_reg, src_reg, bytes, 1, orig_src); emit_jump_insn (gen_jump (join_label)); emit_barrier (); /* Aligned loop. */ emit_label (aligned_label); - block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD); + block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_src); emit_label (join_label); /* Bytes at the end of the loop. */ if (leftover) - block_move_sequence (dest_reg, src_reg, leftover, align); + { +#if 0 + if (leftover <= 3*align) + block_move_sequence (dest_reg, src_reg, leftover, align, orig_src); + + else +#endif + emit_insn (gen_movstrsi_internal (gen_rtx (MEM, BLKmode, dest_reg), + gen_rtx (MEM, BLKmode, src_reg), + GEN_INT (leftover), + GEN_INT (align))); + } } else @@ -1917,6 +2151,258 @@ expand_block_move (operands) } +/* Emit load/stores for a small constant block_move. + + operands[0] is the memory address of the destination. + operands[1] is the memory address of the source. + operands[2] is the number of bytes to move. + operands[3] is the alignment. + operands[4] is a temp register. + operands[5] is a temp register. + ... + operands[3+num_regs] is the last temp register. + + The block move type can be one of the following: + BLOCK_MOVE_NORMAL Do all of the block move. + BLOCK_MOVE_NOT_LAST Do all but the last store. + BLOCK_MOVE_LAST Do just the last store. */ + +char * +output_block_move (insn, operands, num_regs, move_type) + rtx insn; + rtx operands[]; + int num_regs; + enum block_move_type move_type; +{ + rtx dest_reg = XEXP (operands[0], 0); + rtx src_reg = XEXP (operands[1], 0); + int bytes = INTVAL (operands[2]); + int align = INTVAL (operands[3]); + int num = 0; + int offset = 0; + int use_lwl_lwr = FALSE; + int last_operand = num_regs+4; + int i; + rtx xoperands[10]; + + struct { + char *load; /* load insn without nop */ + char *load_nop; /* load insn with trailing nop */ + char *store; /* store insn */ + char *final; /* if last_store used: NULL or swr */ + char *last_store; /* last store instruction */ + int offset; /* current offset */ + enum machine_mode mode; /* mode to use on (MEM) */ + } load_store[4]; + + /* Detect a bug in GCC, where it can give us a register + the same as one of the addressing registers. */ + for (i = 4; i < last_operand; i++) + { + if (reg_mentioned_p (operands[i], operands[0]) + || reg_mentioned_p (operands[i], operands[1])) + { + abort_with_insn (insn, "register passed as address and temp register to block move"); + } + } + + /* If we are given global or static addresses, and we would be + emitting a few instructions, try to save time by using a + temporary register for the pointer. */ + if (bytes > 2*align || move_type != BLOCK_MOVE_NORMAL) + { + if (CONSTANT_P (src_reg)) + { + if (TARGET_STATS) + mips_count_memory_refs (operands[1], 1); + + src_reg = operands[ 3 + num_regs-- ]; + if (move_type != BLOCK_MOVE_LAST) + { + xoperands[1] = operands[1]; + xoperands[0] = src_reg; + output_asm_insn ("la\t%0,%1", xoperands); + } + } + + if (CONSTANT_P (dest_reg)) + { + if (TARGET_STATS) + mips_count_memory_refs (operands[0], 1); + + dest_reg = operands[ 3 + num_regs-- ]; + if (move_type != BLOCK_MOVE_LAST) + { + xoperands[1] = operands[0]; + xoperands[0] = dest_reg; + output_asm_insn ("la\t%0,%1", xoperands); + } + } + } + + if (num_regs > (sizeof (load_store) / sizeof (load_store[0]))) + num_regs = (sizeof (load_store) / sizeof (load_store[0])); + + else if (num_regs < 1) + abort (); + + if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && set_noreorder++ == 0) + output_asm_insn (".set\tnoreorder", operands); + + while (bytes > 0) + { + load_store[num].offset = offset; + + if (bytes >= UNITS_PER_WORD && align >= UNITS_PER_WORD) + { + load_store[num].load = "lw\t%0,%1"; + load_store[num].load_nop = "lw\t%0,%1%#"; + load_store[num].store = "sw\t%0,%1"; + load_store[num].last_store = "sw\t%0,%1"; + load_store[num].final = (char *)0; + load_store[num].mode = SImode; + offset += UNITS_PER_WORD; + bytes -= UNITS_PER_WORD; + } + + else if (bytes >= UNITS_PER_WORD) + { +#if BYTES_BIG_ENDIAN + load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2"; + load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#"; + load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2"; + load_store[num].last_store = "swr\t%0,%2"; + load_store[num].final = "swl\t%0,%1"; +#else + load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1"; + load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#"; + load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1"; + load_store[num].last_store = "swr\t%0,%1"; + load_store[num].final = "swl\t%0,%2"; +#endif + load_store[num].mode = SImode; + offset += UNITS_PER_WORD; + bytes -= UNITS_PER_WORD; + use_lwl_lwr = TRUE; + } + + else if (bytes >= UNITS_PER_SHORT && align >= UNITS_PER_SHORT) + { + load_store[num].load = "lh\t%0,%1"; + load_store[num].load_nop = "lh\t%0,%1%#"; + load_store[num].store = "sh\t%0,%1"; + load_store[num].last_store = "sh\t%0,%1"; + load_store[num].final = (char *)0; + load_store[num].offset = offset; + load_store[num].mode = HImode; + offset += UNITS_PER_SHORT; + bytes -= UNITS_PER_SHORT; + } + + else + { + load_store[num].load = "lb\t%0,%1"; + load_store[num].load_nop = "lb\t%0,%1%#"; + load_store[num].store = "sb\t%0,%1"; + load_store[num].last_store = "sb\t%0,%1"; + load_store[num].final = (char *)0; + load_store[num].mode = QImode; + offset++; + bytes--; + } + + if (TARGET_STATS && move_type != BLOCK_MOVE_LAST) + { + dslots_load_total++; + dslots_load_filled++; + + if (CONSTANT_P (src_reg)) + mips_count_memory_refs (src_reg, 1); + + if (CONSTANT_P (dest_reg)) + mips_count_memory_refs (dest_reg, 1); + } + + /* Emit load/stores now if we have run out of registers or are + at the end of the move. */ + + if (++num == num_regs || bytes == 0) + { + /* If only load/store, we need a NOP after the load. */ + if (num == 1) + { + load_store[0].load = load_store[0].load_nop; + if (TARGET_STATS && move_type != BLOCK_MOVE_LAST) + dslots_load_filled--; + } + + if (move_type != BLOCK_MOVE_LAST) + { + for (i = 0; i < num; i++) + { + int offset; + + if (!operands[i+4]) + abort (); + + if (GET_MODE (operands[i+4]) != load_store[i].mode) + operands[i+4] = gen_rtx (REG, load_store[i].mode, REGNO (operands[i+4])); + + offset = load_store[i].offset; + xoperands[0] = operands[i+4]; + xoperands[1] = gen_rtx (MEM, load_store[i].mode, + plus_constant (src_reg, offset)); + + if (use_lwl_lwr) + xoperands[2] = gen_rtx (MEM, load_store[i].mode, + plus_constant (src_reg, UNITS_PER_WORD-1+offset)); + + output_asm_insn (load_store[i].load, xoperands); + } + } + + for (i = 0; i < num; i++) + { + int last_p = (i == num-1 && bytes == 0); + int offset = load_store[i].offset; + + xoperands[0] = operands[i+4]; + xoperands[1] = gen_rtx (MEM, load_store[i].mode, + plus_constant (dest_reg, offset)); + + + if (use_lwl_lwr) + xoperands[2] = gen_rtx (MEM, load_store[i].mode, + plus_constant (dest_reg, UNITS_PER_WORD-1+offset)); + + if (move_type == BLOCK_MOVE_NORMAL) + output_asm_insn (load_store[i].store, xoperands); + + else if (move_type == BLOCK_MOVE_NOT_LAST) + { + if (!last_p) + output_asm_insn (load_store[i].store, xoperands); + + else if (load_store[i].final != (char *)0) + output_asm_insn (load_store[i].final, xoperands); + } + + else if (last_p) + output_asm_insn (load_store[i].last_store, xoperands); + } + + num = 0; /* reset load_store */ + use_lwl_lwr = FALSE; /* reset whether or not we used lwl/lwr */ + } + } + + if (TARGET_GAS && move_type != BLOCK_MOVE_LAST && --set_noreorder == 0) + output_asm_insn (".set\treorder", operands); + + return ""; +} + + /* Argument support functions. */ /* Initialize CUMULATIVE_ARGS for a function. */ @@ -1927,14 +2413,25 @@ init_cumulative_args (cum, fntype, libna tree fntype; /* tree ptr for function decl */ rtx libname; /* SYMBOL_REF of library name or 0 */ { + static CUMULATIVE_ARGS zero_cum; tree param, next_param; if (TARGET_DEBUG_E_MODE) - fprintf (stderr, "\ninit_cumulative_args\n"); + { + fprintf (stderr, "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype); + if (!fntype) + fputc ('\n', stderr); + + else + { + tree ret_type = TREE_TYPE (fntype); + fprintf (stderr, ", fntype code = %s, ret code = %s\n", + tree_code_name[ (int)TREE_CODE (fntype) ], + tree_code_name[ (int)TREE_CODE (ret_type) ]); + } + } - cum->gp_reg_found = 0; - cum->arg_number = 0; - cum->arg_words = 0; + *cum = zero_cum; /* Determine if this function has variable arguments. This is indicated by the last argument being 'void_type_mode' if there @@ -1950,24 +2447,6 @@ init_cumulative_args (cum, fntype, libna 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. */ @@ -1977,10 +2456,11 @@ function_arg_advance (cum, mode, type, n 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; /* whether or not the argument was named */ { if (TARGET_DEBUG_E_MODE) fprintf (stderr, - "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n", + "function_adv( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, 0x%.8x, %d )\n\n", cum->gp_reg_found, cum->arg_number, cum->arg_words, GET_MODE_NAME (mode), type, named); @@ -2031,8 +2511,12 @@ function_arg (cum, mode, type, named) tree type; /* type of the argument or 0 if lib support */ int named; /* != 0 for normal args, == 0 for ... args */ { + rtx ret; int regbase = -1; int bias = 0; + int struct_p = ((type != (tree)0) + && (TREE_CODE (type) == RECORD_TYPE + || TREE_CODE (type) == UNION_TYPE)); if (TARGET_DEBUG_E_MODE) fprintf (stderr, @@ -2064,8 +2548,14 @@ function_arg (cum, mode, type, named) : FP_ARG_FIRST; break; - case VOIDmode: case BLKmode: + if (type != (tree)0 && TYPE_ALIGN (type) > BITS_PER_WORD) + cum->arg_words += (cum->arg_words & 1); + + regbase = GP_ARG_FIRST; + break; + + case VOIDmode: case QImode: case HImode: case SImode: @@ -2080,18 +2570,42 @@ function_arg (cum, mode, type, named) if (cum->arg_words >= MAX_ARGS_IN_REGISTERS) { if (TARGET_DEBUG_E_MODE) - fprintf (stderr, "\n"); + fprintf (stderr, "%s\n", struct_p ? ", [struct]" : ""); - return 0; + ret = (rtx)0; } + else + { + if (regbase == -1) + abort (); - if (regbase == -1) - abort (); + ret = gen_rtx (REG, mode, regbase + cum->arg_words + bias); - if (TARGET_DEBUG_E_MODE) - fprintf (stderr, "%s\n", reg_names[regbase + cum->arg_words + bias]); + if (TARGET_DEBUG_E_MODE) + fprintf (stderr, "%s%s\n", reg_names[regbase + cum->arg_words + bias], + struct_p ? ", [struct]" : ""); + + /* The following is a hack in order to pass 1 byte structures + the same way that the MIPS compiler does (namely by passing + the structure in the high byte or half word of the register). + This also makes varargs work. If we have such a structure, + we save the adjustment RTL, and the call define expands will + emit them. For the VOIDmode argument (argument after the + last real argument, pass back a parallel vector holding each + of the adjustments. */ - return gen_rtx (REG, mode, regbase + cum->arg_words + bias); + if (struct_p && (mode == QImode || mode == HImode)) + { + rtx amount = GEN_INT (BITS_PER_WORD - GET_MODE_BITSIZE (mode)); + rtx reg = gen_rtx (REG, SImode, regbase + cum->arg_words + bias); + cum->adjust[ cum->num_adjusts++ ] = gen_ashlsi3 (reg, reg, amount); + } + } + + if (mode == VOIDmode && cum->num_adjusts > 0) + ret = gen_rtx (PARALLEL, VOIDmode, gen_rtvec_v (cum->num_adjusts, cum->adjust)); + + return ret; } @@ -2228,52 +2742,15 @@ trace (s, s1, s2) #ifdef SIGINFO -#include - static void siginfo (signo) int signo; { - char print_pid[50]; - char *argv[4]; - pid_t pid; - int status; - fprintf (stderr, "compiling '%s' in '%s'\n", (current_function_name != (char *)0) ? current_function_name : "", (current_function_file != (char *)0) ? current_function_file : ""); - - /* Spawn a ps to tell about current memory usage, etc. */ - sprintf (print_pid, "-p%d,%d", getpid (), getppid ()); - argv[0] = "ps"; - argv[1] = print_pid; - argv[2] = "-ouser,state,pid,pri,nice,wchan,tt,start,usertime,systime,pcpu,cp,inblock,oublock,vsize,rss,pmem,ucomm"; - argv[3] = (char *)0; - - pid = vfork (); - if (pid == 0) /* child context */ - { - execv ("/usr/bin/ps", argv); - execv ("/usr/sbin/ps", argv); - execvp ("ps", argv); - perror ("ps"); - _exit (1); - } - - else if (pid > 0) /* parent context */ - { - void (*sigint)(int) = signal (SIGINT, SIG_IGN); - void (*sigquit)(int) = signal (SIGQUIT, SIG_IGN); - - (void) waitpid (pid, &status, 0); - - (void) signal (SIGINT, sigint); - (void) signal (SIGQUIT, sigquit); - } - - signal (SIGINFO, siginfo); + fflush (stderr); } - #endif /* SIGINFO */ @@ -2417,7 +2894,15 @@ override_options () } #endif - /* Set up the classificaiton arrays now. */ +#if defined(_IOLBF) +#if defined(ultrix) || defined(__ultrix) || defined(__OSF1__) || defined(__osf__) || defined(osf) + /* If -mstats and -quiet, make stderr line buffered. */ + if (quiet_flag && TARGET_STATS) + setvbuf (stderr, (char *)0, _IOLBF, BUFSIZ); +#endif +#endif + + /* Set up the classification arrays now. */ mips_rtx_classify[(int)PLUS] = CLASS_ADD_OP; mips_rtx_classify[(int)MINUS] = CLASS_ADD_OP; mips_rtx_classify[(int)DIV] = CLASS_DIVMOD_OP; @@ -2455,9 +2940,9 @@ override_options () mips_char_to_class['f'] = ((TARGET_HARD_FLOAT) ? FP_REGS : NO_REGS); mips_char_to_class['h'] = HI_REG; mips_char_to_class['l'] = LO_REG; - mips_char_to_class['s'] = ST_REGS; mips_char_to_class['x'] = MD_REGS; mips_char_to_class['y'] = GR_REGS; + mips_char_to_class['z'] = ST_REGS; /* Set up array to map GCC register number to debug register number. Ignore the special purpose register numbers. */ @@ -2477,7 +2962,7 @@ override_options () At present, restrict ints from being in FP registers, because reload is a little enthusiastic about storing extra values in FP registers, and this is not good for things like OS kernels. Also, due to the - manditory delay, it is as fast to load from cached memory as to move + mandatory delay, it is as fast to load from cached memory as to move from the FP register. */ for (mode = VOIDmode; @@ -2489,22 +2974,25 @@ override_options () for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) { - register int temp = FALSE; + register int temp; - if (GP_REG_P (regno)) + if (mode == CC_FPmode || mode == CC_REV_FPmode) + temp = (regno == FPSW_REGNUM); + + else if (GP_REG_P (regno)) temp = ((regno & 1) == 0 || (size <= UNITS_PER_WORD)); else if (FP_REG_P (regno)) temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)) - && (TARGET_DEBUG_H_MODE - || class == MODE_FLOAT - || class == MODE_COMPLEX_FLOAT)); + && (class == MODE_FLOAT + || class == MODE_COMPLEX_FLOAT + || (TARGET_DEBUG_H_MODE && class == MODE_INT))); else if (MD_REG_P (regno)) temp = (mode == SImode || (regno == MD_REG_FIRST && mode == DImode)); - else if (ST_REG_P (regno)) - temp = ((mode == SImode) || (class == MODE_CC)); + else + temp = FALSE; mips_hard_regno_mode_ok[(int)mode][regno] = temp; } @@ -2513,11 +3001,14 @@ override_options () /* - * If the frame pointer has been eliminated, the offset for an auto - * or argument will be based on the stack pointer. But this is not - * what the debugger expects--it needs to find an offset off of the - * frame pointer (whether it exists or not). So here we turn all - * offsets into those based on the (possibly virtual) frame pointer. + * The MIPS debug format wants all automatic variables and arguments + * to be in terms of the virtual frame pointer (stack pointer before + * any adjustment in the function), while the MIPS 3.0 linker wants + * the frame pointer to be the stack pointer after the initial + * adjustment. So, we do the adjustment here. The arg pointer (which + * is eliminated) points to the virtual frame pointer, while the frame + * pointer (which may be eliminated) points to the stack pointer after + * the initial adjustments. */ int @@ -2525,13 +3016,13 @@ mips_debugger_offset (addr, offset) rtx addr; int offset; { - int offset2 = 0; + rtx offset2 = const0_rtx; rtx reg = eliminate_constant_term (addr, &offset2); if (!offset) - offset = offset2; + offset = INTVAL (offset2); - if (reg == stack_pointer_rtx) + if (reg == stack_pointer_rtx || reg == frame_pointer_rtx) { int frame_size = (!current_frame_info.initialized) ? compute_frame_size (get_frame_size ()) @@ -2539,16 +3030,15 @@ mips_debugger_offset (addr, offset) offset = offset - frame_size; } - - /* Any other register is, we hope, either the frame pointer, - or a pseudo equivalent to the frame pointer. (Assign_parms - copies the arg pointer to a pseudo if ARG_POINTER_REGNUM is - equal to FRAME_POINTER_REGNUM, so references off of the - arg pointer are all off a pseudo.) Seems like all we can - do is to just return OFFSET and hope for the best. */ + /* sdbout_parms does not want this to crash for unrecognized cases. */ +#if 0 + else if (reg != arg_pointer_rtx) + abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer."); +#endif return offset; } + /* A C compound statement to output to stdio stream STREAM the assembler syntax for an instruction operand X. X is an RTL @@ -2842,6 +3332,9 @@ print_operand_address (file, addr) break; case REG: + if (REGNO (addr) == ARG_POINTER_REGNUM) + abort_with_insn (addr, "Arg pointer not eliminated."); + fprintf (file, "0(%s)", reg_names [REGNO (addr)]); break; @@ -2875,6 +3368,9 @@ print_operand_address (file, addr) if (!CONSTANT_P (offset)) abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, illegal insn #2"); + if (REGNO (reg) == ARG_POINTER_REGNUM) + abort_with_insn (addr, "Arg pointer not eliminated."); + output_addr_const (file, offset); fprintf (file, "(%s)", reg_names [REGNO (reg)]); } @@ -2982,6 +3478,8 @@ mips_output_filename (stream, name) SET_FILE_NUMBER (); current_function_file = name; fprintf (stream, "\t.file\t%d \"%s\"\n", num_source_filenames, name); + if (!TARGET_GAS && write_symbols == DBX_DEBUG) + fprintf (stream, "\t#@stabs\n"); } else if (!TARGET_GAS && write_symbols == DBX_DEBUG) @@ -3044,6 +3542,64 @@ mips_output_lineno (stream, line) } +/* If defined, a C statement to be executed just prior to the + output of assembler code for INSN, to modify the extracted + operands so they will be output differently. + + Here the argument OPVEC is the vector containing the operands + extracted from INSN, and NOPERANDS is the number of elements of + the vector which contain meaningful data for this insn. The + contents of this vector are what will be used to convert the + insn template into assembler code, so you can change the + assembler output by changing the contents of the vector. + + We use it to check if the current insn needs a nop in front of it + because of load delays, and also to update the delay slot + statistics. */ + +void +final_prescan_insn (insn, opvec, noperands) + rtx insn; + rtx opvec[]; + int noperands; +{ + if (dslots_number_nops > 0) + { + rtx pattern = PATTERN (insn); + int length = get_attr_length (insn); + + /* Do we need to emit a NOP? */ + if (length == 0 + || (mips_load_reg != (rtx)0 && reg_mentioned_p (mips_load_reg, pattern)) + || (mips_load_reg2 != (rtx)0 && reg_mentioned_p (mips_load_reg2, pattern)) + || (mips_load_reg3 != (rtx)0 && reg_mentioned_p (mips_load_reg3, pattern)) + || (mips_load_reg4 != (rtx)0 && reg_mentioned_p (mips_load_reg4, pattern))) + fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); + + else + dslots_load_filled++; + + while (--dslots_number_nops > 0) + fputs ((set_noreorder) ? "\tnop\n" : "\t#nop\n", asm_out_file); + + mips_load_reg = (rtx)0; + mips_load_reg2 = (rtx)0; + mips_load_reg3 = (rtx)0; + mips_load_reg4 = (rtx)0; + + if (set_noreorder && --set_noreorder == 0) + fputs ("\t.set\treorder\n", asm_out_file); + } + + if (TARGET_STATS) + { + enum rtx_code code = GET_CODE (insn); + if (code == JUMP_INSN || code == CALL_INSN) + dslots_jump_total++; + } +} + + /* 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. @@ -3070,9 +3626,6 @@ mips_asm_file_start (stream) if (TARGET_ABICALLS) fprintf (stream, "\t.abicalls\n"); - /* put gcc_compiled. in data, not text */ - data_section (); - if (TARGET_GP_OPT) { asm_out_data_file = stream; @@ -3103,6 +3656,9 @@ mips_asm_file_end (file) struct extern_list *p; int len; + if (HALF_PIC_P ()) + HALF_PIC_FINISH (file); + if (TARGET_GP_OPT) { if (extern_head) @@ -3111,9 +3667,11 @@ mips_asm_file_end (file) for (p = extern_head; p != 0; p = p->next) { name_tree = get_identifier (p->name); - if (!TREE_ADDRESSABLE (name_tree)) + + /* Positively ensure only one .extern for any given symbol. */ + if (! TREE_ASM_WRITTEN (name_tree)) { - TREE_ADDRESSABLE (name_tree) = 1; + TREE_ASM_WRITTEN (name_tree) = 1; fputs ("\t.extern\t", file); assemble_name (file, p->name); fprintf (file, ", %d\n", p->size); @@ -3138,6 +3696,135 @@ mips_asm_file_end (file) } +/* Emit either a label, .comm, or .lcomm directive, and mark + that the symbol is used, so that we don't emit an .extern + for it in mips_asm_file_end. */ + +void +mips_declare_object (stream, name, init_string, final_string, size) + FILE *stream; + char *name; + char *init_string; + char *final_string; + int size; +{ + fputs (init_string, stream); /* "", "\t.comm\t", or "\t.lcomm\t" */ + assemble_name (stream, name); + fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */ + + if (TARGET_GP_OPT && mips_section_threshold != 0) + { + tree name_tree = get_identifier (name); + TREE_ASM_WRITTEN (name_tree) = 1; + } +} + + +/* Output a double precision value to the assembler. If both the + host and target are IEEE, emit the values in hex. */ + +void +mips_output_double (stream, value) + FILE *stream; + REAL_VALUE_TYPE value; +{ +#ifdef REAL_VALUE_TO_TARGET_DOUBLE + long value_long[2]; + REAL_VALUE_TO_TARGET_DOUBLE (value, value_long); + + fprintf (stream, "\t.word\t0x%08lx\t\t# %.20g\n\t.word\t0x%08lx\n", + value_long[0], value, value_long[1]); +#else + fprintf (stream, "\t.double\t%.20g\n", value); +#endif +} + + +/* Output a single precision value to the assembler. If both the + host and target are IEEE, emit the values in hex. */ + +void +mips_output_float (stream, value) + FILE *stream; + REAL_VALUE_TYPE value; +{ +#ifdef REAL_VALUE_TO_TARGET_SINGLE + long value_long; + REAL_VALUE_TO_TARGET_SINGLE (value, value_long); + + fprintf (stream, "\t.word\t0x%08lx\t\t# %.12g (float)\n", value_long, value); +#else + fprintf (stream, "\t.float\t%.12g\n", value); +#endif +} + + +/* Return TRUE if any register used in the epilogue is used. This to insure + any insn put into the epilogue delay slots is safe. */ + +int +epilogue_reg_mentioned_p (insn) + rtx insn; +{ + register char *fmt; + register int i; + register enum rtx_code code; + register int regno; + + if (insn == (rtx)0) + return 0; + + if (GET_CODE (insn) == LABEL_REF) + return 0; + + code = GET_CODE (insn); + switch (code) + { + case REG: + regno = REGNO (insn); + if (regno == STACK_POINTER_REGNUM) + return 1; + + if (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) + return 1; + + if (!call_used_regs[regno]) + return 1; + + if (regno != MIPS_TEMP1_REGNUM && regno != MIPS_TEMP2_REGNUM) + return 0; + + if (!current_frame_info.initialized) + compute_frame_size (get_frame_size ()); + + return (current_frame_info.total_size >= 32768); + + case SCRATCH: + case CC0: + case PC: + case CONST_INT: + case CONST_DOUBLE: + return 0; + } + + fmt = GET_RTX_FORMAT (code); + for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) + { + if (fmt[i] == 'E') + { + register int j; + for (j = XVECLEN (insn, i) - 1; j >= 0; j--) + if (epilogue_reg_mentioned_p (XVECEXP (insn, i, j))) + return 1; + } + else if (fmt[i] == 'e' && epilogue_reg_mentioned_p (XEXP (insn, i))) + return 1; + } + + return 0; +} + + /* Return the bytes needed to compute the frame pointer from the current stack pointer. @@ -3163,10 +3850,6 @@ mips_asm_file_end (file) | GP save for V.4 abi | | | +-----------------------+ - | | - | local variables | - | | - +-----------------------+ | | | fp register save | | | @@ -3176,6 +3859,10 @@ mips_asm_file_end (file) | | +-----------------------+ | | + | local variables | + | | + +-----------------------+ + | | | alloca allocations | | | +-----------------------+ @@ -3191,33 +3878,40 @@ mips_asm_file_end (file) */ -unsigned long +long compute_frame_size (size) int size; /* # of var. bytes allocated */ { int regno; - unsigned long total_size; /* # bytes that the entire frame takes up */ - unsigned long var_size; /* # bytes that variables take up */ - unsigned long args_size; /* # bytes that outgoing arguments take up */ - unsigned long extra_size; /* # extra bytes */ - unsigned int gp_reg_rounded; /* # bytes needed to store gp after rounding */ - unsigned int gp_reg_size; /* # bytes needed to store gp regs */ - unsigned int fp_reg_size; /* # bytes needed to store fp regs */ - unsigned long mask; /* mask of saved gp registers */ - unsigned long fmask; /* mask of saved fp registers */ - int fp_inc; /* 1 or 2 depending on the size of fp regs */ - int fp_bits; /* bitmask to use for each fp register */ - - extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0) - -STARTING_FRAME_OFFSET); + long total_size; /* # bytes that the entire frame takes up */ + long var_size; /* # bytes that variables take up */ + long args_size; /* # bytes that outgoing arguments take up */ + long extra_size; /* # extra bytes */ + long gp_reg_rounded; /* # bytes needed to store gp after rounding */ + long gp_reg_size; /* # bytes needed to store gp regs */ + long fp_reg_size; /* # bytes needed to store fp regs */ + long mask; /* mask of saved gp registers */ + long fmask; /* mask of saved fp registers */ + int fp_inc; /* 1 or 2 depending on the size of fp regs */ + long fp_bits; /* bitmask to use for each fp register */ - var_size = MIPS_STACK_ALIGN (size); - args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size); - total_size = var_size + args_size + extra_size; gp_reg_size = 0; fp_reg_size = 0; mask = 0; fmask = 0; + extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0)); + var_size = MIPS_STACK_ALIGN (size); + args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size); + + /* The MIPS 3.0 linker does not like functions that dynamically + allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it + looks like we are trying to create a second frame pointer to the + function, so allocate some stack space to make it happy. */ + + if (args_size == 0 && current_function_calls_alloca) + args_size = 4*UNITS_PER_WORD; + + total_size = var_size + args_size + extra_size; /* Calculate space needed for gp registers. */ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++) @@ -3225,7 +3919,7 @@ compute_frame_size (size) if (MUST_SAVE_REGISTER (regno)) { gp_reg_size += UNITS_PER_WORD; - mask |= 1 << (regno - GP_REG_FIRST); + mask |= 1L << (regno - GP_REG_FIRST); } } @@ -3266,93 +3960,246 @@ compute_frame_size (size) current_frame_info.mask = mask; current_frame_info.fmask = fmask; current_frame_info.initialized = reload_completed; + current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD; + current_frame_info.num_fp = fp_reg_size / (2*UNITS_PER_WORD); if (mask) { - unsigned long offset = args_size + gp_reg_size - UNITS_PER_WORD; + unsigned long offset = args_size + var_size + gp_reg_size - UNITS_PER_WORD; current_frame_info.gp_sp_offset = offset; current_frame_info.gp_save_offset = offset - total_size; } + else + { + current_frame_info.gp_sp_offset = 0; + current_frame_info.gp_save_offset = 0; + } + if (fmask) { - unsigned long offset = args_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD; + unsigned long offset = args_size + var_size + gp_reg_rounded + fp_reg_size - 2*UNITS_PER_WORD; current_frame_info.fp_sp_offset = offset; current_frame_info.fp_save_offset = offset - total_size + UNITS_PER_WORD; } + else + { + current_frame_info.fp_sp_offset = 0; + current_frame_info.fp_save_offset = 0; + } /* Ok, we're done. */ return total_size; } -/* Common code to save/restore registers. */ +/* Common code to emit the insns (or to write the instructions to a file) + to save/restore registers. -void -save_restore (file, gp_op, gp_2word_op, fp_op) - FILE *file; /* stream to write to */ - char *gp_op; /* operation to do on gp registers */ - char *gp_2word_op; /* 2 word op to do on gp registers */ - char *fp_op; /* operation to do on fp registers */ + Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg) + is not modified within save_restore_insns. */ + +#define BITSET_P(value,bit) (((value) & (1L << (bit))) != 0) + +static void +save_restore_insns (store_p, large_reg, large_offset, file) + int store_p; /* true if this is prologue */ + rtx large_reg; /* register holding large offset constant or NULL */ + long large_offset; /* large constant offset value */ + FILE *file; /* file to write instructions to instead of making RTL */ { + long mask = current_frame_info.mask; + long fmask = current_frame_info.fmask; int regno; - unsigned long mask = current_frame_info.mask; - unsigned long fmask = current_frame_info.fmask; - unsigned long gp_offset; - unsigned long fp_offset; - unsigned long max_offset; - char *base_reg; + rtx base_reg_rtx; + long base_offset; + long gp_offset; + long fp_offset; + long end_offset; + + if (frame_pointer_needed && !BITSET_P (mask, FRAME_POINTER_REGNUM - GP_REG_FIRST)) + abort (); if (mask == 0 && fmask == 0) return; - base_reg = reg_names[STACK_POINTER_REGNUM]; - gp_offset = current_frame_info.gp_sp_offset; - fp_offset = current_frame_info.fp_sp_offset; - max_offset = (gp_offset > fp_offset) ? gp_offset : fp_offset; - - /* Deal with calling functions with a large structure. */ - if (max_offset >= 32768) - { - char *temp = reg_names[MIPS_TEMP2_REGNUM]; - fprintf (file, "\tli\t%s,%ld\n", temp, max_offset); - fprintf (file, "\taddu\t%s,%s,%s\n", temp, temp, base_reg); - base_reg = temp; - gp_offset = max_offset - gp_offset; - fp_offset = max_offset - fp_offset; - } - /* Save registers starting from high to low. The debuggers prefer at least the return register be stored at func+4, and also it allows us not to need a nop in the epilog if at least one register is reloaded in addition to return address. */ - if (mask || frame_pointer_needed) + /* Save GP registers if needed. */ + if (mask) { + /* Pick which pointer to use as a base register. For small + frames, just use the stack pointer. Otherwise, use a + temporary register. Save 2 cycles if the save area is near + the end of a large frame, by reusing the constant created in + the prologue/epilogue to adjust the stack frame. */ + + gp_offset = current_frame_info.gp_sp_offset; + end_offset = gp_offset - (current_frame_info.gp_reg_size - UNITS_PER_WORD); + + if (gp_offset < 0 || end_offset < 0) + fatal ("gp_offset (%ld) or end_offset (%ld) is less than zero.", + gp_offset, end_offset); + + else if (gp_offset < 32768) + { + base_reg_rtx = stack_pointer_rtx; + base_offset = 0; + } + + else if (large_reg != (rtx)0 + && (((unsigned long)(large_offset - gp_offset)) < 32768) + && (((unsigned long)(large_offset - end_offset)) < 32768)) + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = large_offset; + if (file == (FILE *)0) + emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + else + fprintf (file, "\taddu\t%s,%s,%s\n", + reg_names[MIPS_TEMP2_REGNUM], + reg_names[REGNO (large_reg)], + reg_names[STACK_POINTER_REGNUM]); + } + + else + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = gp_offset; + if (file == (FILE *)0) + { + emit_move_insn (base_reg_rtx, GEN_INT (gp_offset)); + emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); + } + else + fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n", + reg_names[MIPS_TEMP2_REGNUM], + (long)base_offset, + (long)base_offset, + reg_names[MIPS_TEMP2_REGNUM], + reg_names[MIPS_TEMP2_REGNUM], + reg_names[STACK_POINTER_REGNUM]); + } + for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) { - if ((mask & (1L << (regno - GP_REG_FIRST))) != 0 - || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)) + if (BITSET_P (mask, regno - GP_REG_FIRST)) { - fprintf (file, "\t%s\t%s,%d(%s)\n", - gp_op, reg_names[regno], - gp_offset, base_reg); + if (file == (FILE *)0) + { + rtx reg_rtx = gen_rtx (REG, Pmode, regno); + rtx mem_rtx = gen_rtx (MEM, Pmode, + gen_rtx (PLUS, Pmode, base_reg_rtx, + GEN_INT (gp_offset - base_offset))); + + if (store_p) + emit_move_insn (mem_rtx, reg_rtx); + else + emit_move_insn (reg_rtx, mem_rtx); + } + else + fprintf (file, "\t%s\t%s,%ld(%s)\n", + (store_p) ? "sw" : "lw", + reg_names[regno], + gp_offset - base_offset, + reg_names[REGNO(base_reg_rtx)]); gp_offset -= UNITS_PER_WORD; } } } + else + { + base_reg_rtx = (rtx)0; /* Make sure these are initialzed */ + base_offset = 0; + } + /* Save floating point registers if needed. */ if (fmask) { int fp_inc = (TARGET_FLOAT64) ? 1 : 2; + /* Pick which pointer to use as a base register. */ + fp_offset = current_frame_info.fp_sp_offset; + end_offset = fp_offset - (current_frame_info.fp_reg_size - UNITS_PER_WORD); + + if (fp_offset < 0 || end_offset < 0) + fatal ("fp_offset (%ld) or end_offset (%ld) is less than zero.", + fp_offset, end_offset); + + else if (fp_offset < 32768) + { + base_reg_rtx = stack_pointer_rtx; + base_offset = 0; + } + + else if (base_reg_rtx != (rtx)0 + && (((unsigned long)(base_offset - fp_offset)) < 32768) + && (((unsigned long)(base_offset - end_offset)) < 32768)) + { + ; /* already set up for gp registers above */ + } + + else if (large_reg != (rtx)0 + && (((unsigned long)(large_offset - fp_offset)) < 32768) + && (((unsigned long)(large_offset - end_offset)) < 32768)) + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = large_offset; + if (file == (FILE *)0) + emit_insn (gen_addsi3 (base_reg_rtx, large_reg, stack_pointer_rtx)); + else + fprintf (file, "\taddu\t%s,%s,%s\n", + reg_names[MIPS_TEMP2_REGNUM], + reg_names[REGNO (large_reg)], + reg_names[STACK_POINTER_REGNUM]); + } + + else + { + base_reg_rtx = gen_rtx (REG, Pmode, MIPS_TEMP2_REGNUM); + base_offset = fp_offset; + if (file == (FILE *)0) + { + emit_move_insn (base_reg_rtx, GEN_INT (fp_offset)); + emit_insn (gen_addsi3 (base_reg_rtx, base_reg_rtx, stack_pointer_rtx)); + } + else + fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n\taddu\t%s,%s,%s\n", + reg_names[MIPS_TEMP2_REGNUM], + (long)base_offset, + (long)base_offset, + reg_names[MIPS_TEMP2_REGNUM], + reg_names[MIPS_TEMP2_REGNUM], + reg_names[STACK_POINTER_REGNUM]); + } + for (regno = FP_REG_LAST-1; regno >= FP_REG_FIRST; regno -= fp_inc) { - if ((fmask & (1L << (regno - FP_REG_FIRST))) != 0) + if (BITSET_P (fmask, regno - FP_REG_FIRST)) { - fprintf (file, "\t%s\t%s,%d(%s)\n", - fp_op, reg_names[regno], fp_offset, base_reg); + if (file == (FILE *)0) + { + rtx reg_rtx = gen_rtx (REG, DFmode, regno); + rtx mem_rtx = gen_rtx (MEM, DFmode, + gen_rtx (PLUS, Pmode, base_reg_rtx, + GEN_INT (fp_offset - base_offset))); + + if (store_p) + emit_move_insn (mem_rtx, reg_rtx); + else + emit_move_insn (reg_rtx, mem_rtx); + } + else + fprintf (file, "\t%s\t%s,%ld(%s)\n", + (store_p) ? "s.d" : "l.d", + reg_names[regno], + fp_offset - base_offset, + reg_names[REGNO(base_reg_rtx)]); + fp_offset -= 2*UNITS_PER_WORD; } @@ -3368,29 +4215,18 @@ function_prologue (file, size) FILE *file; int size; { - int regno; - int tsize; - char *sp_str = reg_names[STACK_POINTER_REGNUM]; - char *fp_str = (!frame_pointer_needed) - ? sp_str - : reg_names[FRAME_POINTER_REGNUM]; - tree fndecl = current_function_decl; /* current... is tooo long */ - tree fntype = TREE_TYPE (fndecl); - tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE) - ? DECL_ARGUMENTS (fndecl) - : 0; - tree next_arg; - tree cur_arg; - char *arg_name = (char *)0; - CUMULATIVE_ARGS args_so_far; + long tsize = current_frame_info.total_size; ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl)); - ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); + + if (debug_info_level != DINFO_LEVEL_TERSE) + ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl)); inside_function = 1; fputs ("\t.ent\t", file); assemble_name (file, current_function_name); fputs ("\n", file); + assemble_name (file, current_function_name); fputs (":\n", file); @@ -3399,9 +4235,85 @@ function_prologue (file, size) "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n", reg_names[ GP_REG_FIRST + 25 ]); + tsize = current_frame_info.total_size; + if (tsize > 0 && TARGET_ABICALLS) + fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET); + + fprintf (file, "\t.frame\t%s,%d,%s\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n", + reg_names[ (frame_pointer_needed) ? FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM ], + tsize, + reg_names[31 + GP_REG_FIRST], + current_frame_info.var_size, + current_frame_info.num_gp, + current_frame_info.num_fp, + current_function_outgoing_args_size, + current_frame_info.extra_size); + + fprintf (file, "\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n", + current_frame_info.mask, + current_frame_info.gp_save_offset, + current_frame_info.fmask, + current_frame_info.fp_save_offset); +} + + +/* Expand the prologue into a bunch of separate insns. */ + +void +mips_expand_prologue () +{ + int regno; + long tsize; + rtx tmp_rtx = (rtx)0; + char *arg_name = (char *)0; + tree fndecl = current_function_decl; + tree fntype = TREE_TYPE (fndecl); + tree fnargs = (TREE_CODE (fntype) != METHOD_TYPE) + ? DECL_ARGUMENTS (fndecl) + : 0; + rtx next_arg_reg; + int i; + tree next_arg; + tree cur_arg; + CUMULATIVE_ARGS args_so_far; + /* Determine the last argument, and get its name. */ + + INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0); + regno = GP_ARG_FIRST; + for (cur_arg = fnargs; cur_arg != (tree)0; cur_arg = next_arg) { + tree type = DECL_ARG_TYPE (cur_arg); + enum machine_mode passed_mode = TYPE_MODE (type); + rtx entry_parm = FUNCTION_ARG (args_so_far, + passed_mode, + DECL_ARG_TYPE (cur_arg), + 1); + + if (entry_parm) + { + int words; + + /* passed in a register, so will get homed automatically */ + if (GET_MODE (entry_parm) == BLKmode) + words = (int_size_in_bytes (type) + 3) / 4; + else + words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; + + regno = REGNO (entry_parm) + words - 1; + } + else + { + regno = GP_ARG_LAST+1; + break; + } + + FUNCTION_ARG_ADVANCE (args_so_far, + passed_mode, + DECL_ARG_TYPE (cur_arg), + 1); + next_arg = TREE_CHAIN (cur_arg); if (next_arg == (tree)0) { @@ -3412,115 +4324,75 @@ function_prologue (file, size) } } + /* In order to pass small structures by value in registers + compatibly with the MIPS compiler, we need to shift the value + into the high part of the register. Function_arg has encoded a + PARALLEL rtx, holding a vector of adjustments to be made as the + next_arg_reg variable, so we split up the insns, and emit them + separately. */ + + next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1); + if (next_arg_reg != (rtx)0 && GET_CODE (next_arg_reg) == PARALLEL) + { + rtvec adjust = XVEC (next_arg_reg, 0); + int num = GET_NUM_ELEM (adjust); + + for (i = 0; i < num; i++) + { + rtx pattern = RTVEC_ELT (adjust, i); + if (GET_CODE (pattern) != SET + || GET_CODE (SET_SRC (pattern)) != ASHIFT) + abort_with_insn (pattern, "Insn is not a shift"); + + PUT_CODE (SET_SRC (pattern), ASHIFTRT); + emit_insn (pattern); + } + } + /* 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))) + || (arg_name != (char *)0 + && ((arg_name[0] == '_' && strcmp (arg_name, "__builtin_va_alist") == 0) + || (arg_name[0] == 'v' && strcmp (arg_name, "va_alist") == 0)))) { - tree parm; - - regno = GP_ARG_FIRST; - INIT_CUMULATIVE_ARGS (args_so_far, fntype, (rtx)0); - - for (parm = fnargs; (parm && (regno <= GP_ARG_LAST)); parm = TREE_CHAIN (parm)) + for (; regno <= GP_ARG_LAST; regno++) { - rtx entry_parm; - enum machine_mode passed_mode; - tree type; - - type = DECL_ARG_TYPE (parm); - passed_mode = TYPE_MODE (type); - entry_parm = FUNCTION_ARG (args_so_far, passed_mode, - DECL_ARG_TYPE (parm), 1); - - if (entry_parm) - { - int words; - - /* passed in a register, so will get homed automatically */ - if (GET_MODE (entry_parm) == BLKmode) - words = (int_size_in_bytes (type) + 3) / 4; - else - words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; - - regno = REGNO (entry_parm) + words - 1; - } - else - { - regno = GP_ARG_LAST+1; - break; - } + rtx ptr = stack_pointer_rtx; + if (regno != GP_ARG_FIRST) + ptr = gen_rtx (PLUS, Pmode, ptr, + GEN_INT ((regno - GP_ARG_FIRST) * UNITS_PER_WORD)); - FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, - DECL_ARG_TYPE (parm), 1); - } - - if (regno <= GP_ARG_LAST && (regno & 1) != 0) - { - fprintf (file, "\tsw\t%s,%d(%s)\t\t# varargs home register\n", - reg_names[regno], (regno - 4) * 4, sp_str); - regno++; - } - - for (; regno <= GP_ARG_LAST; regno += 2) - { - fprintf (file, "\tsd\t%s,%d(%s)\t\t# varargs home register\n", - reg_names[regno], (regno - 4) * 4, sp_str); + emit_move_insn (gen_rtx (MEM, Pmode, ptr), gen_rtx (REG, Pmode, regno)); } } - size = MIPS_STACK_ALIGN (size); - tsize = (!current_frame_info.initialized) - ? compute_frame_size (size) - : current_frame_info.total_size; - + tsize = compute_frame_size (get_frame_size ()); if (tsize > 0) { - if (tsize <= 32767) - fprintf (file, - "\tsubu\t%s,%s,%d\t\t# vars= %d, regs= %d/%d, args = %d, extra= %d\n", - sp_str, sp_str, tsize, current_frame_info.var_size, - current_frame_info.gp_reg_size / 4, - current_frame_info.fp_reg_size / 8, - current_function_outgoing_args_size, - current_frame_info.extra_size); - else - fprintf (file, - "\tli\t%s,%d\n\tsubu\t%s,%s,%s\t\t# vars= %d, regs= %d/%d, args = %d, sfo= %d\n", - reg_names[MIPS_TEMP1_REGNUM], tsize, sp_str, sp_str, - reg_names[MIPS_TEMP1_REGNUM], current_frame_info.var_size, - current_frame_info.gp_reg_size / 4, - current_frame_info.fp_reg_size / 8, - current_function_outgoing_args_size, - current_frame_info.extra_size); - } + rtx tsize_rtx = GEN_INT (tsize); - if (TARGET_ABICALLS) - fprintf (file, "\t.cprestore %d\n", tsize + STARTING_FRAME_OFFSET); - - fprintf (file, "\t.frame\t%s,%d,%s\n\t.mask\t0x%08lx,%d\n\t.fmask\t0x%08lx,%d\n", - fp_str, - (frame_pointer_needed) ? 0 : tsize, - reg_names[31 + GP_REG_FIRST], - current_frame_info.mask, - current_frame_info.gp_save_offset, - current_frame_info.fmask, - current_frame_info.fp_save_offset); + if (tsize > 32767) + { + tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM); + emit_move_insn (tmp_rtx, tsize_rtx); + tsize_rtx = tmp_rtx; + } - save_restore (file, "sw", "sd", "s.d"); + emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); - 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); + save_restore_insns (TRUE, tmp_rtx, tsize, (FILE *)0); - else - fprintf (file, "\taddu\t%s,%s,%s\t# set up frame pointer\n", fp_str, sp_str, - reg_names[MIPS_TEMP1_REGNUM]); + if (frame_pointer_needed) + emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); } + + /* If we are profiling, make sure no instructions are scheduled before + the call to mcount. */ + + if (profile_flag || profile_block_flag) + emit_insn (gen_blockage ()); } @@ -3531,7 +4403,7 @@ function_epilogue (file, size) FILE *file; int size; { - int tsize; + long tsize; char *sp_str = reg_names[STACK_POINTER_REGNUM]; char *t1_str = reg_names[MIPS_TEMP1_REGNUM]; rtx epilogue_delay = current_function_epilogue_delay_list; @@ -3539,6 +4411,9 @@ function_epilogue (file, size) int noepilogue = FALSE; int load_nop = FALSE; int load_only_r31; + rtx tmp_rtx = (rtx)0; + rtx restore_rtx; + int i; /* The epilogue does not depend on any registers, but the stack registers, so we assume that if we have 1 pending nop, it can be @@ -3619,20 +4494,16 @@ function_epilogue (file, size) fprintf (file, "\t.set\tnoreorder\n"); if (tsize > 32767) - fprintf (file, "\tli\t%s,%d\n", t1_str, tsize); - - if (frame_pointer_needed) { - char *fp_str = reg_names[FRAME_POINTER_REGNUM]; - if (tsize > 32767) - fprintf (file,"\tsubu\t%s,%s,%s\t\t# sp not trusted here\n", - sp_str, fp_str, t1_str); - else - fprintf (file,"\tsubu\t%s,%s,%d\t\t# sp not trusted here\n", - sp_str, fp_str, tsize); + fprintf (file, "\tli\t%s,0x%.08lx\t# %ld\n", t1_str, (long)tsize, (long)tsize); + tmp_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM); } - save_restore (file, "lw", "ld", "l.d"); + if (frame_pointer_needed) + fprintf (file, "\tmove\t%s,%s\t\t\t# sp not trusted here\n", + sp_str, reg_names[FRAME_POINTER_REGNUM]); + + save_restore_insns (FALSE, tmp_rtx, tsize, file); load_only_r31 = (current_frame_info.mask == (1 << 31) && current_frame_info.fmask == 0); @@ -3698,6 +4569,10 @@ function_epilogue (file, size) int num_gp_regs = current_frame_info.gp_reg_size / 4; int num_fp_regs = current_frame_info.fp_reg_size / 8; int num_regs = num_gp_regs + num_fp_regs; + char *name = current_function_name; + + if (name[0] == '*') + name++; dslots_load_total += num_regs; @@ -3719,8 +4594,8 @@ function_epilogue (file, size) } fprintf (stderr, - "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3ld reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d\n", - current_function_name, + "%-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", + name, (frame_pointer_needed) ? 'y' : 'n', ((current_frame_info.mask & (1 << 31)) != 0) ? 'n' : 'y', (current_function_calls_alloca) ? 'y' : 'n', @@ -3731,6 +4606,20 @@ function_epilogue (file, size) dslots_load_total, dslots_load_filled, dslots_jump_total, dslots_jump_filled, num_refs[0], num_refs[1], num_refs[2]); + + if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs) + { + fprintf (stderr, " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs); + prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS; + } + + if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs) + { + fprintf (stderr, " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs); + prev_half_pic_refs = HALF_PIC_NUMBER_REFS; + } + + fputc ('\n', stderr); } /* Reset state info for each function. */ @@ -3745,7 +4634,6 @@ function_epilogue (file, size) num_refs[2] = 0; mips_load_reg = (rtx)0; mips_load_reg2 = (rtx)0; - number_functions_processed++; current_frame_info = zero_frame_info; /* Restore the output file if optimizing the GP (optimizing the GP causes @@ -3757,6 +4645,36 @@ function_epilogue (file, size) } +/* Expand the epilogue into a bunch of separate insns. */ + +void +mips_expand_epilogue () +{ + long tsize = current_frame_info.total_size; + rtx tsize_rtx = GEN_INT (tsize); + rtx tmp_rtx = (rtx)0; + + if (tsize > 32767) + { + tmp_rtx = gen_rtx (REG, SImode, MIPS_TEMP1_REGNUM); + emit_move_insn (tmp_rtx, tsize_rtx); + tsize_rtx = tmp_rtx; + } + + if (tsize > 0) + { + if (frame_pointer_needed) + emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx)); + + save_restore_insns (FALSE, tmp_rtx, tsize, (FILE *)0); + + emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); + } + + emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode, GP_REG_FIRST+31))); +} + + /* Define the number of delay slots needed for the function epilogue. On the mips, we need a slot if either no stack has been allocated, @@ -3783,7 +4701,7 @@ mips_epilogue_delay_slots () was created. */ int -null_epilogue () +simple_epilogue_p () { if (!reload_completed) return 0;