Index: tc-mips.c =================================================================== RCS file: /cvs/src/src/gas/config/tc-mips.c,v retrieving revision 1.461 diff -p -r1.461 tc-mips.c *** tc-mips.c 28 Feb 2011 16:26:45 -0000 1.461 --- tc-mips.c 19 Apr 2011 17:03:50 -0000 *************** struct mips_cl_insn *** 156,161 **** --- 156,164 ---- /* True for mips16 instructions that jump to an absolute address. */ unsigned int mips16_absolute_jump_p : 1; + + /* True if this instruction is complete. */ + unsigned int complete_p : 1; }; /* The ABI to use. */ *************** create_insn (struct mips_cl_insn *insn, *** 1384,1389 **** --- 1387,1393 ---- insn->fixed_p = (mips_opts.noreorder > 0); insn->noreorder_p = (mips_opts.noreorder > 0); insn->mips16_absolute_jump_p = 0; + insn->complete_p = 0; } /* Record the current MIPS16 mode in now_seg. */ *************** nops_for_vr4130 (const struct mips_cl_in *** 2680,2685 **** --- 2684,2890 ---- return 0; } + #define BASE_REG_EQ(INSN1, INSN2) \ + ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \ + == (((INSN2) >> OP_SH_RS) & OP_MASK_RS)) + + /* Return the minimum alignment for this store instruction. */ + + static int + fix_24k_align_to (const struct mips_opcode *mo) + { + + if (strcmp (mo->name, "sh") == 0) + return 2; + + if (strcmp (mo->name, "swc1") == 0 + || strcmp (mo->name, "swc2") == 0 + || strcmp (mo->name, "sw") == 0 + || strcmp (mo->name, "sc") == 0 + || strcmp (mo->name, "s.s") == 0) + return 4; + + if (strcmp (mo->name, "sdc1") == 0 + || strcmp (mo->name, "sdc2") == 0 + || strcmp (mo->name, "s.d") == 0) + return 8; + + /* sb, swl, swr */ + return 1; + } + + struct store_info + { + /* Immediate offset, if any, for this store instruction. */ + short off; + /* Alignment required by this store instruction. */ + int align_to; + /* True for register offsets. */ + int register_offset; + }; + + /* Comparison function used by qsort. */ + + static int + fix_24k_sort (const void *a, const void *b) + { + + const struct store_info *pos1 = a; + const struct store_info *pos2 = b; + + return (pos1->off - pos2->off); + } + + /* Setup store information to be used by nops_for_24k. */ + + static void + setup_24k_position_info (struct store_info *stinfo, const struct mips_cl_insn *insn) + { + + stinfo->align_to = fix_24k_align_to (insn->insn_mo); + + if (strstr (insn->insn_mo->args, "o(")) + { + stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE; + stinfo->register_offset = FALSE; + } + else + { + /* The only other stores on the 24k have a register index. */ + stinfo->off = 0; + stinfo->register_offset = TRUE; + } + } + + /* 24K Errata: Lost Data on Stores During Refill. + + Problem: The FSB (fetch store buffer) acts as an intermediate buffer + for the data cache refills and store data. The following describes + the scenario where the store data could be lost. + + * A data cache miss, due to either a load or a store, causing fill + data to be supplied by the memory subsystem + * The first three doublewords of fill data are returned and written + into the cache + * A sequence of four stores occurs in consecutive cycles around the + final doubleword of the fill: + * Store A + * Store B + * Store C + * Zero, One or more instructions + * Store D + + The four stores A-D must be to different doublewords of the line that + is being filled. The fourth instruction in the sequence above permits + the fill of the final doubleword to be transferred from the FSB into + the cache. In the sequence above, the stores may be either integer + (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2, + swxc1, sdxc1, suxc1) stores, as long as the four stores are to + different doublewords on the line. If the floating point unit is + running in 1:2 mode, it is not possible to create the sequence above + using only floating point store instructions. + + In this case, the cache line being filled is incorrectly marked + invalid, thereby losing the data from any store to the line that + occurs between the original miss and the completion of the five + cycle sequence shown above. + + The workarounds are: + + * Run the data cache in write-through mode. + * Insert a non-store instruction between + Store A and Store B or Store B and Store C. */ + + static int + nops_for_24k (const struct mips_cl_insn *hist, + const struct mips_cl_insn *insn) + { + + struct store_info pos[3]; + int align, i, base_offset; + + /* If INSN is definitely not a store, there's nothing to worry about. */ + if (insn && (insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0) + return 0; + + /* Likewise, the previous instruction wasn't a store. */ + if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0) + return 0; + + /* If we don't know what came before, assume the worst. */ + if (hist[1].frag == NULL) + return 1; + + /* If the instruction was not a store, there's nothing to worry about. */ + if ((hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0) + return 0; + + /* If we don't know the relationship between the store addresses, + assume the worst. */ + if (insn == NULL + || !BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode) + || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode)) + return 1; + + setup_24k_position_info (&pos[0], insn); + setup_24k_position_info (&pos[1], &hist[0]); + setup_24k_position_info (&pos[2], &hist[1]); + + /* If we don't know the offset, then assume overlap. */ + if (pos[0].register_offset == TRUE + || pos[1].register_offset == TRUE + || pos[2].register_offset == TRUE) + return 1; + + if (insn->complete_p == 0 + || history[0].complete_p == 0 + || history[1].complete_p == 0) + return 1; + + qsort (&pos, 3, sizeof (struct store_info), fix_24k_sort); + + /* Pick a value of ALIGN and X such that all offsets are adjusted by + X bytes and such that the base register + X is known to be aligned + to align bytes. */ + + if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP) + align = 8; + else + { + align = pos[0].align_to; + base_offset = pos[0].off; + for (i = 1; i < 3; i++) + if (align < pos[i].align_to) + { + align = pos[i].align_to; + base_offset = pos[i].off; + } + for (i = 0; i < 3; i++) + pos[i].off -= base_offset; + } + + pos[0].off &= ~align + 1; + pos[1].off &= ~align + 1; + pos[2].off &= ~align + 1; + + /* If any two stores write to the same chunk, they also write to the + same doubleword. The offsets are still sorted at this point. */ + if (pos[0].off == pos[1].off || pos[1].off == pos[2].off) + return 0; + + /* A range of at least 9 bytes is needed for the stores to be in + non-overlapping doublewords. */ + if (pos[2].off - pos[0].off <= 8) + return 0; + + if (pos[2].off - pos[1].off >= 24 + || pos[1].off - pos[0].off >= 24 + || pos[2].off - pos[0].off >= 32) + return 0; + + return 1; + } + /* Return the number of nops that would be needed if instruction INSN immediately followed the MAX_NOPS instructions given by HIST, where HIST[0] is the most recent instruction. If INSN is null, *************** nops_for_insn (const struct mips_cl_insn *** 2706,2711 **** --- 2911,2924 ---- nops = tmp_nops; } + if (mips_fix_24k) + { + tmp_nops = nops_for_24k (hist, insn); + if (tmp_nops > nops) + nops = tmp_nops; + } + + return nops; } *************** append_insn (struct mips_cl_insn *ip, ex *** 2836,2841 **** --- 3049,3130 ---- pinfo = ip->insn_mo->pinfo; pinfo2 = ip->insn_mo->pinfo2; + if (address_expr == NULL) + ip->complete_p = 1; + else if (*reloc_type <= BFD_RELOC_UNUSED + && address_expr->X_op == O_constant) + { + unsigned int tmp; + + ip->complete_p = 1; + switch (*reloc_type) + { + case BFD_RELOC_32: + ip->insn_opcode |= address_expr->X_add_number; + break; + + case BFD_RELOC_MIPS_HIGHEST: + tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48; + ip->insn_opcode |= tmp & 0xffff; + break; + + case BFD_RELOC_MIPS_HIGHER: + tmp = (address_expr->X_add_number + 0x80008000ull) >> 32; + ip->insn_opcode |= tmp & 0xffff; + break; + + case BFD_RELOC_HI16_S: + tmp = (address_expr->X_add_number + 0x8000) >> 16; + ip->insn_opcode |= tmp & 0xffff; + break; + + case BFD_RELOC_HI16: + ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff; + break; + + case BFD_RELOC_UNUSED: + case BFD_RELOC_LO16: + case BFD_RELOC_MIPS_GOT_DISP: + ip->insn_opcode |= address_expr->X_add_number & 0xffff; + break; + + case BFD_RELOC_MIPS_JMP: + if ((address_expr->X_add_number & 3) != 0) + as_bad (_("jump to misaligned address (0x%lx)"), + (unsigned long) address_expr->X_add_number); + ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff; + ip->complete_p = 0; + break; + + case BFD_RELOC_MIPS16_JMP: + if ((address_expr->X_add_number & 3) != 0) + as_bad (_("jump to misaligned address (0x%lx)"), + (unsigned long) address_expr->X_add_number); + ip->insn_opcode |= + (((address_expr->X_add_number & 0x7c0000) << 3) + | ((address_expr->X_add_number & 0xf800000) >> 7) + | ((address_expr->X_add_number & 0x3fffc) >> 2)); + ip->complete_p = 0; + break; + + case BFD_RELOC_16_PCREL_S2: + if ((address_expr->X_add_number & 3) != 0) + as_bad (_("branch to misaligned address (0x%lx)"), + (unsigned long) address_expr->X_add_number); + if (mips_relax_branch) + goto need_reloc; + if ((address_expr->X_add_number + 0x20000) & ~0x3ffff) + as_bad (_("branch address range overflow (0x%lx)"), + (unsigned long) address_expr->X_add_number); + ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff; + ip->complete_p = 0; + break; + + default: + internalError (); + } + } + if (mips_relax.sequence != 2 && !mips_opts.noreorder) { /* There are a lot of optimizations we could do that we don't. *************** append_insn (struct mips_cl_insn *ip, ex *** 3007,3081 **** if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED) { ! if (address_expr->X_op == O_constant) ! { ! unsigned int tmp; ! ! switch (*reloc_type) ! { ! case BFD_RELOC_32: ! ip->insn_opcode |= address_expr->X_add_number; ! break; ! ! case BFD_RELOC_MIPS_HIGHEST: ! tmp = (address_expr->X_add_number + 0x800080008000ull) >> 48; ! ip->insn_opcode |= tmp & 0xffff; ! break; ! ! case BFD_RELOC_MIPS_HIGHER: ! tmp = (address_expr->X_add_number + 0x80008000ull) >> 32; ! ip->insn_opcode |= tmp & 0xffff; ! break; ! ! case BFD_RELOC_HI16_S: ! tmp = (address_expr->X_add_number + 0x8000) >> 16; ! ip->insn_opcode |= tmp & 0xffff; ! break; ! ! case BFD_RELOC_HI16: ! ip->insn_opcode |= (address_expr->X_add_number >> 16) & 0xffff; ! break; ! ! case BFD_RELOC_UNUSED: ! case BFD_RELOC_LO16: ! case BFD_RELOC_MIPS_GOT_DISP: ! ip->insn_opcode |= address_expr->X_add_number & 0xffff; ! break; ! ! case BFD_RELOC_MIPS_JMP: ! if ((address_expr->X_add_number & 3) != 0) ! as_bad (_("jump to misaligned address (0x%lx)"), ! (unsigned long) address_expr->X_add_number); ! ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0x3ffffff; ! break; ! ! case BFD_RELOC_MIPS16_JMP: ! if ((address_expr->X_add_number & 3) != 0) ! as_bad (_("jump to misaligned address (0x%lx)"), ! (unsigned long) address_expr->X_add_number); ! ip->insn_opcode |= ! (((address_expr->X_add_number & 0x7c0000) << 3) ! | ((address_expr->X_add_number & 0xf800000) >> 7) ! | ((address_expr->X_add_number & 0x3fffc) >> 2)); ! break; ! ! case BFD_RELOC_16_PCREL_S2: ! if ((address_expr->X_add_number & 3) != 0) ! as_bad (_("branch to misaligned address (0x%lx)"), ! (unsigned long) address_expr->X_add_number); ! if (mips_relax_branch) ! goto need_reloc; ! if ((address_expr->X_add_number + 0x20000) & ~0x3ffff) ! as_bad (_("branch address range overflow (0x%lx)"), ! (unsigned long) address_expr->X_add_number); ! ip->insn_opcode |= (address_expr->X_add_number >> 2) & 0xffff; ! break; ! ! default: ! internalError (); ! } ! } ! else if (*reloc_type < BFD_RELOC_UNUSED) need_reloc: { reloc_howto_type *howto; --- 3296,3303 ---- if (address_expr != NULL && *reloc_type <= BFD_RELOC_UNUSED) { ! if (ip->complete_p == 0 ! && *reloc_type < BFD_RELOC_UNUSED) need_reloc: { reloc_howto_type *howto; *************** append_insn (struct mips_cl_insn *ip, ex *** 3374,3380 **** /* If the previous instruction is an ERET or DERET, avoid the swap. */ || (history[0].insn_opcode == INSN_ERET) ! || (history[0].insn_opcode == INSN_DERET)) { if (mips_opts.mips16 && (pinfo & INSN_UNCOND_BRANCH_DELAY) --- 3596,3605 ---- /* If the previous instruction is an ERET or DERET, avoid the swap. */ || (history[0].insn_opcode == INSN_ERET) ! || (history[0].insn_opcode == INSN_DERET) ! /* Don't swap if -mfix-24k and previous insn is a store. */ ! || (mips_fix_24k ! && (prev_pinfo & INSN_STORE_MEMORY))) { if (mips_opts.mips16 && (pinfo & INSN_UNCOND_BRANCH_DELAY)