Index: binutils/gas/config/tc-mips.c =================================================================== RCS file: /cvsroot/src/gnu/dist/binutils/gas/config/tc-mips.c,v retrieving revision 1.5 diff -u -r1.5 tc-mips.c --- binutils/gas/config/tc-mips.c 8 Dec 2004 13:38:10 -0000 1.5 +++ binutils/gas/config/tc-mips.c 6 Feb 2006 19:25:59 -0000 @@ -11080,6 +11080,116 @@ return 1; } +static int is_jump (unsigned long insn) +{ + unsigned long op, subop; + op = insn >> 26; + + switch (op) { + case 16: /* cop0 */ + case 17: /* cop1 */ + case 18: /* cop2 */ + subop = insn >> 21; subop &= 0x1f; + if (subop != 8) + break; + /* bc?f, bc?t, bc?fl, bc?tl */ + /* fall through */ + case 1: /* branch or trap */ + case 2: /* j xx */ + case 3: /* jal xx */ + case 4: /* beq s,t, xx */ + case 5: /* bne s,t, xx */ + case 6: /* blez s,t, xx */ + case 7: /* bgtz s,t, xx */ + case 20: /* beql s,t, xx */ + case 21: /* bnezl/bnel */ + case 22: /* blezl s, xx */ + case 23: /* bgtzl s, xx */ + return 1; + case 0: + subop = insn & 0x3f; + if ((subop != 8) /* jr */ + && (subop != 9) /* jalr */ + && (subop != 15)) /* sync */ + break; + return 1; + } + + return 0; +} + + +/* + * On the R5900, we must warn loops that satisfy all of the following + * conditions: + * + * - a loop consists of less than equal to six instructions(includes + * branch delay slot). + * - a loop contains only one conditional branch instruction at the + * end of the loop. + * - a loop does not contain any other branch or jump instructions. + * - a branch delay slot of the loop is not nop. ( EE#2.9 or later ) + * + * We need to do this because of a bug in the chip. + */ + +#define R5900_MINIMUM_LOOP_INSTRUCTIONS 6 + +/* return 1, if we must warn */ +static int check_short_loop(int loop_target, fixS *fixP) +{ + unsigned char *buf; + unsigned long insn; + int place = fixP->fx_where; /* place of this jump in bytes */ + int i; + int frag_size; + + /* check loop length */ + if ((loop_target < 0) || + (loop_target > R5900_MINIMUM_LOOP_INSTRUCTIONS - 2)) + return 0; + + /* check jump beyond the boundary */ + if (loop_target > (place/4)) + /* XXX: something to be done ?? */ + return 0; + + /* examin if insn in BD slot is nop */ + frag_size = fixP->fx_frag->fr_fix + + fixP->fx_frag->fr_offset * fixP->fx_frag->fr_var; + + if ((place + 4) <= frag_size) { + buf = (unsigned char *) (place + fixP->fx_frag->fr_literal); + buf += 4; + if (target_big_endian) + insn = (buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]; + else + insn = (buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + + if (insn == 0) /* nop */ + return 0; + } + + buf = (unsigned char *) (place + fixP->fx_frag->fr_literal); + for (i = 0; i < loop_target ; i++) { + buf -= 4; + if (target_big_endian) + insn = (buf[0] << 24) | (buf[1] << 16) | + (buf[2] << 8) | buf[3]; + else + insn = (buf[3] << 24) | (buf[2] << 16) | + (buf[1] << 8) | buf[0]; + + if (is_jump(insn)) + /* jump insn is found */ + return 0; + } + + return 1; +} + /* Apply a fixup to the object file. */ void @@ -11270,6 +11380,15 @@ else insn = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + /* check short loop */ + if (((insn & 0xffff0000) != 0x10000000) /* beq $0,$0 */ + && ((insn & 0xffff0000) != 0x04010000) /* bgez $0 */ + && ((insn & 0xffff0000) != 0x04110000)) { /* bgezal $0 */ + if (check_short_loop(-(*valP +1), fixP)) + as_warn_where (fixP->fx_file, fixP->fx_line, + _("Loop length is too short for R5900.")); + } + if (*valP + 0x20000 <= 0x3ffff) { insn |= (*valP >> 2) & 0xffff; Index: gcc/gcc/toplev.c =================================================================== RCS file: /cvsroot/src/gnu/dist/gcc/gcc/toplev.c,v retrieving revision 1.1.1.3 diff -u -r1.1.1.3 toplev.c --- gcc/gcc/toplev.c 10 Feb 2004 12:09:19 -0000 1.1.1.3 +++ gcc/gcc/toplev.c 6 Feb 2006 19:26:10 -0000 @@ -3561,6 +3561,18 @@ convert_to_eh_region_ranges (); +#ifdef MACHINE_DEPENDENT_REORG_FINAL + timevar_push (TV_MACH_DEP); + open_dump_file (DFI_mach, decl); + + MACHINE_DEPENDENT_REORG_FINAL (insns); + + close_dump_file (DFI_mach, print_rtl, insns); + timevar_pop (TV_MACH_DEP); + + ggc_collect (); +#endif + /* Shorten branches. */ timevar_push (TV_SHORTEN_BRANCH); shorten_branches (get_insns ()); Index: gcc/gcc/config/mips/mips-protos.h =================================================================== RCS file: /cvsroot/src/gnu/dist/gcc/gcc/config/mips/mips-protos.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 mips-protos.h --- gcc/gcc/config/mips/mips-protos.h 23 Jul 2003 02:41:51 -0000 1.1.1.1 +++ gcc/gcc/config/mips/mips-protos.h 6 Feb 2006 19:26:11 -0000 @@ -90,6 +90,7 @@ extern void mips_emit_fcc_reload PARAMS ((rtx, rtx, rtx)); extern void mips_set_return_address PARAMS ((rtx, rtx)); extern void machine_dependent_reorg PARAMS ((rtx)); +extern void machine_dependent_reorg_final PARAMS ((rtx)); extern int mips_address_cost PARAMS ((rtx)); extern void mips_count_memory_refs PARAMS ((rtx, int)); extern HOST_WIDE_INT mips_debugger_offset PARAMS ((rtx, HOST_WIDE_INT)); Index: gcc/gcc/config/mips/mips.c =================================================================== RCS file: /cvsroot/src/gnu/dist/gcc/gcc/config/mips/mips.c,v retrieving revision 1.2 diff -u -r1.2 mips.c --- gcc/gcc/config/mips/mips.c 25 Oct 2003 21:55:37 -0000 1.2 +++ gcc/gcc/config/mips/mips.c 6 Feb 2006 19:26:19 -0000 @@ -144,6 +144,10 @@ static int mips_adjust_cost PARAMS ((rtx, rtx, rtx, int)); static int mips_issue_rate PARAMS ((void)); +static enum sequence_type r5900_detect_sequence_type PARAMS ((rtx, int *, rtx *, + int *, int)); +static void r5900_lengthen_loops PARAMS ((rtx, int)); + static struct machine_function * mips_init_machine_status PARAMS ((void)); static void mips_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT)) ATTRIBUTE_UNUSED; @@ -9743,6 +9747,8 @@ if (! TARGET_MIPS16) return; + r5900_lengthen_loops (first, 0); + /* If $gp is used, try to remove stores, and replace loads with copies from $gp. */ if (optimize) @@ -9932,6 +9938,203 @@ constant table, but we have no way to prevent that. */ } +/* + * On the R5900, we must ensure that the compiler never generates loops + * that satisfy all of the following conditions: + * + * - a loop consists of less than equal to six instructions (including the + * branch delay slot). + * + * - a loop contains only one conditional branch instruction at the + * end of the loop. + * + * - a loop does not contain any other branch or jump instructions. + * + * - a branch delay slot of the loop is not nop. ( EE#2.9 or later ) + * + * We need to do this because of a bug in the chip. + */ + +#define NOP_INSN_P(INSN) (rtx_equal_p (INSN, gen_nop ())) + +enum sequence_type {Jump, Reset, Other}; + +/* Detect if possible loop jump presents in the sequence */ + +static enum sequence_type +r5900_detect_sequence_type (insn, count_p, jump_insn_p, seen_before, + after_delayed_branch_sched) + rtx insn; + int *count_p; + rtx *jump_insn_p; + int *seen_before; + int after_delayed_branch_sched; +{ + int i; + int detect_later = 0; + + for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++) + { + rtx v_insn; + v_insn = XVECEXP (PATTERN (insn), 0, i); + + switch (GET_CODE (v_insn)) + { + case CODE_LABEL: + seen_before [INSN_UID (v_insn)] = *count_p; + break; + case INSN: + /* The attr_length may be bigger than acctual insn length. + So don't use it in the after_delayed_branch_sched phase. */ + *count_p += ( get_attr_length (insn) > 0 + && !after_delayed_branch_sched ) + ? get_attr_length (insn) : 4 ; + if (detect_later) + { + /* inspect BD slot */ + if (NOP_INSN_P (PATTERN (v_insn))) + return Reset; + return Jump; + } + break; + case JUMP_INSN: + *jump_insn_p = v_insn; + detect_later = 1; + break; + case CALL_INSN: + return Reset; + break; + default: + break; + } + } + + if (detect_later) + /* Since there's no instruction in the delay slot, the assembler will + insert a "nop" later. */ + return Reset; + + return Other; +} + +/* XXX: Fix this. */ + +static void +r5900_lengthen_loops (first, after_delayed_branch_sched) + rtx first; + int after_delayed_branch_sched; +{ + rtx insn; + rtx this_jump_insn; + const int hazardous_distance = 6; + int n = 4; + int max_uid = get_max_uid (); + int *seen_before = alloca (sizeof seen_before[0] * max_uid); + int in_sequence = 0; + enum sequence_type result; + + /*bzero ((char *) seen_before, sizeof seen_before[0] * max_uid);*/ + memset((char *) seen_before, 0, sizeof seen_before[0] * max_uid); + + for (insn = first; insn; insn = NEXT_INSN (insn)) + { + in_sequence = 0; + switch (GET_CODE (insn)) + { + case CODE_LABEL: + seen_before [INSN_UID (insn)] = n; + break; + + case INSN: + if (GET_CODE (PATTERN (insn)) != SEQUENCE ) + { + /* The attr_length may be bigger than acctual insn length. + So don't use it in the after_delayed_branch_sched phase. */ + n += ( get_attr_length (insn) > 0 + && !after_delayed_branch_sched ) + ? get_attr_length (insn) : 4 ; + break; + } + result = r5900_detect_sequence_type(insn, &n, &this_jump_insn, + seen_before, after_delayed_branch_sched); + switch(result) + { + case Reset: + goto reset; + case Jump: + in_sequence=1; + goto jump_insn; + case Other: + default: + break; + } + break; + + case JUMP_INSN: + this_jump_insn = insn; + in_sequence=0; + jump_insn: + if (condjump_p (this_jump_insn)) + { + rtx target = condjump_label (this_jump_insn); + + if (seen_before [INSN_UID (XEXP (target, 0))]) + { + int distance + = (n - seen_before [INSN_UID (XEXP (target, 0))]) >> 2; + + if (!in_sequence) + { + /* try to insert one nop before jump + * if distance is in hazardous_distance */ + + if ( distance <= hazardous_distance ) + { + rtx prev_insn = PREV_INSN (this_jump_insn); + while ( prev_insn && ( GET_CODE (prev_insn) != INSN )) + prev_insn = PREV_INSN (prev_insn); + if (prev_insn && ! NOP_INSN_P (PATTERN (prev_insn))) + emit_insn_before (gen_nop (), this_jump_insn); + + } + } + else + { + if ( distance <= hazardous_distance ) + { + for (; distance <= hazardous_distance; distance++) + emit_insn_before (gen_nop(), insn); + } + } + } + } + + /* Falls through */ + + case CALL_INSN: + reset: + /*bzero ((char *) seen_before, sizeof seen_before[0] * max_uid);*/ + memset((char *) seen_before, 0, sizeof seen_before[0] * max_uid); + break; + + default: + break; + } + } +} + +/* Exported to toplev.c. + + Do a final pass over the function, just before rtl to asm conversion + (after "delayed branch scheduling" and "shorten jump"). */ + +void +machine_dependent_reorg_final (first) + rtx first; +{ + r5900_lengthen_loops (first, 1); +} + /* Return nonzero if X is a SIGN or ZERO extend operator. */ int extend_operator (x, mode) Index: gcc/gcc/config/mips/mips.h =================================================================== RCS file: /cvsroot/src/gnu/dist/gcc/gcc/config/mips/mips.h,v retrieving revision 1.1.1.1 diff -u -r1.1.1.1 mips.h --- gcc/gcc/config/mips/mips.h 23 Jul 2003 02:41:51 -0000 1.1.1.1 +++ gcc/gcc/config/mips/mips.h 6 Feb 2006 19:26:23 -0000 @@ -4611,6 +4611,9 @@ PC relative loads that are out of range. */ #define MACHINE_DEPENDENT_REORG(X) machine_dependent_reorg (X) +/* This is used to lengthen short loops on the r5900. */ +#define MACHINE_DEPENDENT_REORG_FINAL(X) machine_dependent_reorg_final (X) + /* We need to use a special set of functions to handle hard floating point code in mips16 mode. */