mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-28 04:25:10 +08:00
2011-04-20 Catherine Moore <clm@codesourcery.com>
David Ung <davidu@mips.com> * config/mips.c (mips_cl_insn): Add new field complete_p. (create_insn): Initialize complete_p to zero. (BASE_REG_EQ): New. (fix_24k_align_to): New. (fix_24k_store_info): Declare. (fix_24k_sort): New. (fix_24k_record_store_info): New. (nops_for_24k): New. (nops_for_insn): Call nops_for_24k. (append_insn): Move O_constant expression handling.
This commit is contained in:
parent
17b09558c0
commit
15be625dff
@ -1,3 +1,17 @@
|
||||
2011-04-20 Catherine Moore <clm@codesourcery.com>
|
||||
David Ung <davidu@mips.com>
|
||||
|
||||
* config/mips.c (mips_cl_insn): Add new field complete_p.
|
||||
(create_insn): Initialize complete_p to zero.
|
||||
(BASE_REG_EQ): New.
|
||||
(fix_24k_align_to): New.
|
||||
(fix_24k_store_info): Declare.
|
||||
(fix_24k_sort): New.
|
||||
(fix_24k_record_store_info): New.
|
||||
(nops_for_24k): New.
|
||||
(nops_for_insn): Call nops_for_24k.
|
||||
(append_insn): Move O_constant expression handling.
|
||||
|
||||
2011-04-20 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* hash.c (set_gas_hash_table_size): Use bfd_hash_set_default_size.
|
||||
|
@ -156,6 +156,9 @@ struct mips_cl_insn
|
||||
|
||||
/* 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. */
|
||||
@ -1384,6 +1387,7 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
|
||||
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. */
|
||||
@ -2680,6 +2684,189 @@ nops_for_vr4130 (const struct mips_cl_insn *hist,
|
||||
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 fix_24k_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 fix_24k_store_info *pos1 = a;
|
||||
const struct fix_24k_store_info *pos2 = b;
|
||||
|
||||
return (pos1->off - pos2->off);
|
||||
}
|
||||
|
||||
/* INSN is a store instruction. Try to record the store information
|
||||
in STINFO. Return false if the information isn't known. */
|
||||
|
||||
static bfd_boolean
|
||||
fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
|
||||
const struct mips_cl_insn *insn)
|
||||
{
|
||||
/* The instruction must have a known offset. */
|
||||
if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
|
||||
return FALSE;
|
||||
|
||||
stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
|
||||
stinfo->align_to = fix_24k_align_to (insn->insn_mo);
|
||||
return 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 fix_24k_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;
|
||||
|
||||
if (!fix_24k_record_store_info (&pos[0], insn)
|
||||
|| !fix_24k_record_store_info (&pos[1], &hist[0])
|
||||
|| !fix_24k_record_store_info (&pos[2], &hist[1]))
|
||||
return 1;
|
||||
|
||||
qsort (&pos, 3, sizeof (struct fix_24k_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,
|
||||
@ -2706,6 +2893,13 @@ nops_for_insn (const struct mips_cl_insn *hist,
|
||||
nops = tmp_nops;
|
||||
}
|
||||
|
||||
if (mips_fix_24k)
|
||||
{
|
||||
tmp_nops = nops_for_24k (hist, insn);
|
||||
if (tmp_nops > nops)
|
||||
nops = tmp_nops;
|
||||
}
|
||||
|
||||
return nops;
|
||||
}
|
||||
|
||||
@ -2836,6 +3030,82 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
||||
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.
|
||||
@ -3007,75 +3277,8 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
|
||||
|
||||
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)
|
||||
if (!ip->complete_p
|
||||
&& *reloc_type < BFD_RELOC_UNUSED)
|
||||
need_reloc:
|
||||
{
|
||||
reloc_howto_type *howto;
|
||||
|
Loading…
Reference in New Issue
Block a user