/* 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. */
but it's not clear that it would actually improve performance. */
-#define RELAX_BRANCH_ENCODE(uncond, likely, link, toofar) \
- ((relax_substateT) \
- (0xc0000000 \
- | ((toofar) ? 1 : 0) \
- | ((link) ? 2 : 0) \
- | ((likely) ? 4 : 0) \
- | ((uncond) ? 8 : 0)))
+#define RELAX_BRANCH_ENCODE(at, uncond, likely, link, toofar) \
+ ((relax_substateT) \
+ (0xc0000000 \
+ | ((at) & 0x1f) \
+ | ((toofar) ? 0x20 : 0) \
+ | ((link) ? 0x40 : 0) \
+ | ((likely) ? 0x80 : 0) \
+ | ((uncond) ? 0x100 : 0)))
#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
-#define RELAX_BRANCH_UNCOND(i) (((i) & 8) != 0)
-#define RELAX_BRANCH_LIKELY(i) (((i) & 4) != 0)
-#define RELAX_BRANCH_LINK(i) (((i) & 2) != 0)
-#define RELAX_BRANCH_TOOFAR(i) (((i) & 1) != 0)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 0x100) != 0)
+#define RELAX_BRANCH_LIKELY(i) (((i) & 0x80) != 0)
+#define RELAX_BRANCH_LINK(i) (((i) & 0x40) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 0x20) != 0)
+#define RELAX_BRANCH_AT(i) ((i) & 0x1f)
/* For mips16 code, we use an entirely different form of relaxation.
mips16 supports two versions of most instructions which take
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. */
void
md_mips_end (void)
{
+ mips_emit_delays ();
if (! ECOFF_DEBUGGING)
md_obj_end ();
}
&& fixp->fx_offset == fixp->fx_next->fx_offset);
}
-/* See whether instruction IP reads register REG. CLASS is the type
- of register. */
-
-static int
-insn_uses_reg (const struct mips_cl_insn *ip, unsigned int reg,
- enum mips_regclass regclass)
-{
- if (regclass == MIPS16_REG)
- {
- gas_assert (mips_opts.mips16);
- reg = mips16_to_32_reg_map[reg];
- regclass = MIPS_GR_REG;
- }
-
- /* Don't report on general register ZERO, since it never changes. */
- if (regclass == MIPS_GR_REG && reg == ZERO)
- return 0;
-
- if (regclass == MIPS_FP_REG)
- {
- gas_assert (! mips_opts.mips16);
- /* If we are called with either $f0 or $f1, we must check $f0.
- This is not optimal, because it will introduce an unnecessary
- NOP between "lwc1 $f0" and "swc1 $f1". To fix this we would
- need to distinguish reading both $f0 and $f1 or just one of
- them. Note that we don't have to check the other way,
- because there is no instruction that sets both $f0 and $f1
- and requires a delay. */
- if ((ip->insn_mo->pinfo & INSN_READ_FPR_S)
- && ((EXTRACT_OPERAND (FS, *ip) & ~(unsigned) 1)
- == (reg &~ (unsigned) 1)))
- return 1;
- if ((ip->insn_mo->pinfo & INSN_READ_FPR_T)
- && ((EXTRACT_OPERAND (FT, *ip) & ~(unsigned) 1)
- == (reg &~ (unsigned) 1)))
- return 1;
- if ((ip->insn_mo->pinfo2 & INSN2_READ_FPR_Z)
- && ((EXTRACT_OPERAND (FZ, *ip) & ~(unsigned) 1)
- == (reg &~ (unsigned) 1)))
- return 1;
- }
- else if (! mips_opts.mips16)
- {
- if ((ip->insn_mo->pinfo & INSN_READ_GPR_S)
- && EXTRACT_OPERAND (RS, *ip) == reg)
- return 1;
- if ((ip->insn_mo->pinfo & INSN_READ_GPR_T)
- && EXTRACT_OPERAND (RT, *ip) == reg)
- return 1;
- if ((ip->insn_mo->pinfo2 & INSN2_READ_GPR_D)
- && EXTRACT_OPERAND (RD, *ip) == reg)
- return 1;
- if ((ip->insn_mo->pinfo2 & INSN2_READ_GPR_Z)
- && EXTRACT_OPERAND (RZ, *ip) == reg)
- return 1;
- }
- else
- {
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_X)
- && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)] == reg)
- return 1;
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Y)
- && mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)] == reg)
- return 1;
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_Z)
- && (mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]
- == reg))
- return 1;
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_T) && reg == TREG)
- return 1;
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_SP) && reg == SP)
- return 1;
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_31) && reg == RA)
- return 1;
- if ((ip->insn_mo->pinfo & MIPS16_INSN_READ_GPR_X)
- && MIPS16_EXTRACT_OPERAND (REGR32, *ip) == reg)
- return 1;
- }
-
- return 0;
-}
-
/* This function returns true if modifying a register requires a
delay. */
mips_relax.sequence = 0;
}
+/* Return the mask of core registers that IP reads. */
+
+static unsigned int
+gpr_read_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = 0;
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (mips_opts.mips16)
+ {
+ if (pinfo & MIPS16_INSN_READ_X)
+ mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
+ if (pinfo & MIPS16_INSN_READ_Y)
+ mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
+ if (pinfo & MIPS16_INSN_READ_T)
+ mask |= 1 << TREG;
+ if (pinfo & MIPS16_INSN_READ_SP)
+ mask |= 1 << SP;
+ if (pinfo & MIPS16_INSN_READ_31)
+ mask |= 1 << RA;
+ if (pinfo & MIPS16_INSN_READ_Z)
+ mask |= 1 << (mips16_to_32_reg_map
+ [MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
+ if (pinfo & MIPS16_INSN_READ_GPR_X)
+ mask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
+ }
+ else
+ {
+ if (pinfo2 & INSN2_READ_GPR_D)
+ mask |= 1 << EXTRACT_OPERAND (RD, *ip);
+ if (pinfo & INSN_READ_GPR_T)
+ mask |= 1 << EXTRACT_OPERAND (RT, *ip);
+ if (pinfo & INSN_READ_GPR_S)
+ mask |= 1 << EXTRACT_OPERAND (RS, *ip);
+ if (pinfo2 & INSN2_READ_GPR_Z)
+ mask |= 1 << EXTRACT_OPERAND (RZ, *ip);
+ }
+ return mask & ~0;
+}
+
+/* Return the mask of core registers that IP writes. */
+
+static unsigned int
+gpr_write_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = 0;
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (mips_opts.mips16)
+ {
+ if (pinfo & MIPS16_INSN_WRITE_X)
+ mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
+ if (pinfo & MIPS16_INSN_WRITE_Y)
+ mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
+ if (pinfo & MIPS16_INSN_WRITE_Z)
+ mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RZ, *ip)];
+ if (pinfo & MIPS16_INSN_WRITE_T)
+ mask |= 1 << TREG;
+ if (pinfo & MIPS16_INSN_WRITE_SP)
+ mask |= 1 << SP;
+ if (pinfo & MIPS16_INSN_WRITE_31)
+ mask |= 1 << RA;
+ if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
+ mask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
+ }
+ else
+ {
+ if (pinfo & INSN_WRITE_GPR_D)
+ mask |= 1 << EXTRACT_OPERAND (RD, *ip);
+ if (pinfo & INSN_WRITE_GPR_T)
+ mask |= 1 << EXTRACT_OPERAND (RT, *ip);
+ if (pinfo & INSN_WRITE_GPR_31)
+ mask |= 1 << RA;
+ if (pinfo2 & INSN2_WRITE_GPR_Z)
+ mask |= 1 << EXTRACT_OPERAND (RZ, *ip);
+ }
+ return mask & ~0;
+}
+
+/* Return the mask of floating-point registers that IP reads. */
+
+static unsigned int
+fpr_read_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = 0;
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (!mips_opts.mips16)
+ {
+ if (pinfo & INSN_READ_FPR_S)
+ mask |= 1 << EXTRACT_OPERAND (FS, *ip);
+ if (pinfo & INSN_READ_FPR_T)
+ mask |= 1 << EXTRACT_OPERAND (FT, *ip);
+ if (pinfo & INSN_READ_FPR_R)
+ mask |= 1 << EXTRACT_OPERAND (FR, *ip);
+ if (pinfo2 & INSN2_READ_FPR_Z)
+ mask |= 1 << EXTRACT_OPERAND (FZ, *ip);
+ }
+ /* Conservatively treat all operands to an FP_D instruction are doubles.
+ (This is overly pessimistic for things like cvt.d.s.) */
+ if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+ mask |= mask << 1;
+ return mask;
+}
+
+/* Return the mask of floating-point registers that IP writes. */
+
+static unsigned int
+fpr_write_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = 0;
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (!mips_opts.mips16)
+ {
+ if (pinfo & INSN_WRITE_FPR_D)
+ mask |= 1 << EXTRACT_OPERAND (FD, *ip);
+ if (pinfo & INSN_WRITE_FPR_S)
+ mask |= 1 << EXTRACT_OPERAND (FS, *ip);
+ if (pinfo & INSN_WRITE_FPR_T)
+ mask |= 1 << EXTRACT_OPERAND (FT, *ip);
+ if (pinfo2 & INSN2_WRITE_FPR_Z)
+ mask |= 1 << EXTRACT_OPERAND (FZ, *ip);
+ }
+ /* Conservatively treat all operands to an FP_D instruction are doubles.
+ (This is overly pessimistic for things like cvt.s.d.) */
+ if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+ mask |= mask << 1;
+ return mask;
+}
+
/* Classify an instruction according to the FIX_VR4120_* enumeration.
Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
by VR4120 errata. */
const struct mips_cl_insn *insn2)
{
unsigned long pinfo1, pinfo2;
+ unsigned int mask;
/* This function needs to know which pinfo flags are set for INSN2
and which registers INSN2 uses. The former is stored in PINFO2 and
- the latter is tested via INSN2_USES_REG. If INSN2 is null, PINFO2
- will have every flag set and INSN2_USES_REG will always return true. */
+ the latter is tested via INSN2_USES_GPR. If INSN2 is null, PINFO2
+ will have every flag set and INSN2_USES_GPR will always return true. */
pinfo1 = insn1->insn_mo->pinfo;
pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
-#define INSN2_USES_REG(REG, CLASS) \
- (insn2 == NULL || insn_uses_reg (insn2, REG, CLASS))
+#define INSN2_USES_GPR(REG) \
+ (insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
/* For most targets, write-after-read dependencies on the HI and LO
registers must be separated by at least two instructions. */
between an mfhi or mflo and any instruction that uses the result. */
if (mips_7000_hilo_fix
&& MF_HILO_INSN (pinfo1)
- && INSN2_USES_REG (EXTRACT_OPERAND (RD, *insn1), MIPS_GR_REG))
+ && INSN2_USES_GPR (EXTRACT_OPERAND (RD, *insn1)))
return 2;
/* If we're working around 24K errata, one instruction is required
|| (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
{
know (pinfo1 & INSN_WRITE_GPR_T);
- if (INSN2_USES_REG (EXTRACT_OPERAND (RT, *insn1), MIPS_GR_REG))
+ if (INSN2_USES_GPR (EXTRACT_OPERAND (RT, *insn1)))
return 1;
}
/* Handle cases where INSN1 writes to a known general coprocessor
register. There must be a one instruction delay before INSN2
if INSN2 reads that register, otherwise no delay is needed. */
- if (pinfo1 & INSN_WRITE_FPR_T)
+ mask = fpr_write_mask (insn1);
+ if (mask != 0)
{
- if (INSN2_USES_REG (EXTRACT_OPERAND (FT, *insn1), MIPS_FP_REG))
- return 1;
- }
- else if (pinfo1 & INSN_WRITE_FPR_S)
- {
- if (INSN2_USES_REG (EXTRACT_OPERAND (FS, *insn1), MIPS_FP_REG))
+ if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
return 1;
}
else
return 1;
}
-#undef INSN2_USES_REG
+#undef INSN2_USES_GPR
return 0;
}
/* Return the number of nops that would be needed to work around the
VR4130 mflo/mfhi errata if instruction INSN immediately followed
- the MAX_VR4130_NOPS instructions described by HIST. */
+ the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
+ that are contained within the first IGNORE instructions of HIST. */
static int
-nops_for_vr4130 (const struct mips_cl_insn *hist,
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
const struct mips_cl_insn *insn)
{
- int i, j, reg;
+ int i, j;
+ unsigned int mask;
/* Check if the instruction writes to HI or LO. MTHI and MTLO
are not affected by the errata. */
if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
{
/* Extract the destination register. */
- if (mips_opts.mips16)
- reg = mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, hist[i])];
- else
- reg = EXTRACT_OPERAND (RD, hist[i]);
+ mask = gpr_write_mask (&hist[i]);
/* No nops are needed if INSN reads that register. */
- if (insn != NULL && insn_uses_reg (insn, reg, MIPS_GR_REG))
+ if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
return 0;
/* ...or if any of the intervening instructions do. */
for (j = 0; j < i; j++)
- if (insn_uses_reg (&hist[j], reg, MIPS_GR_REG))
+ if (gpr_read_mask (&hist[j]) & mask)
return 0;
- return MAX_VR4130_NOPS - i;
+ if (i >= ignore)
+ return MAX_VR4130_NOPS - i;
}
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;
+}
+
+/* Return the number of nops that would be needed to work around the 24k
+ "lost data on stores during refill" errata if instruction INSN
+ immediately followed the 2 instructions described by HIST.
+ Ignore hazards that are contained within the first IGNORE
+ instructions of HIST.
+
+ 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 (int ignore, 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 (ignore >= 2)
+ return 0;
+
+ /* If the previous instruction wasn't a store, there's nothing to
+ worry about. */
+ if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
+
+ /* If the instructions after the previous one are unknown, we have
+ to assume the worst. */
+ if (!insn)
+ return 1;
+
+ /* Check whether we are dealing with three consecutive stores. */
+ if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+ || (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 (!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,
- return the worse-case number of nops for any instruction. */
+ where HIST[0] is the most recent instruction. Ignore hazards
+ between INSN and the first IGNORE instructions in HIST.
+
+ If INSN is null, return the worse-case number of nops for any
+ instruction. */
static int
-nops_for_insn (const struct mips_cl_insn *hist,
+nops_for_insn (int ignore, const struct mips_cl_insn *hist,
const struct mips_cl_insn *insn)
{
int i, nops, tmp_nops;
nops = 0;
- for (i = 0; i < MAX_DELAY_NOPS; i++)
+ for (i = ignore; i < MAX_DELAY_NOPS; i++)
{
tmp_nops = insns_between (hist + i, insn) - i;
if (tmp_nops > nops)
if (mips_fix_vr4130)
{
- tmp_nops = nops_for_vr4130 (hist, insn);
+ tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+
+ if (mips_fix_24k)
+ {
+ tmp_nops = nops_for_24k (ignore, hist, insn);
if (tmp_nops > nops)
nops = tmp_nops;
}
/* The variable arguments provide NUM_INSNS extra instructions that
might be added to HIST. Return the largest number of nops that
- would be needed after the extended sequence. */
+ would be needed after the extended sequence, ignoring hazards
+ in the first IGNORE instructions. */
static int
-nops_for_sequence (int num_insns, const struct mips_cl_insn *hist, ...)
+nops_for_sequence (int num_insns, int ignore,
+ const struct mips_cl_insn *hist, ...)
{
va_list args;
struct mips_cl_insn buffer[MAX_NOPS];
while (cursor > buffer)
*--cursor = *va_arg (args, const struct mips_cl_insn *);
- nops = nops_for_insn (buffer, NULL);
+ nops = nops_for_insn (ignore, buffer, NULL);
va_end (args);
return nops;
}
worst-case delay for the branch target. */
static int
-nops_for_insn_or_target (const struct mips_cl_insn *hist,
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
const struct mips_cl_insn *insn)
{
int nops, tmp_nops;
- nops = nops_for_insn (hist, insn);
+ nops = nops_for_insn (ignore, hist, insn);
if (insn->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
| INSN_COND_BRANCH_DELAY
| INSN_COND_BRANCH_LIKELY))
{
- tmp_nops = nops_for_sequence (2, hist, insn, NOP_INSN);
+ tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+ hist, insn, NOP_INSN);
if (tmp_nops > nops)
nops = tmp_nops;
}
&& (insn->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
| MIPS16_INSN_COND_BRANCH)))
{
- tmp_nops = nops_for_sequence (1, hist, insn);
+ tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
if (tmp_nops > nops)
nops = tmp_nops;
}
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.
benefit hand written assembly code, and does not seem worth
it. */
int nops = (mips_optimize == 0
- ? nops_for_insn (history, NULL)
- : nops_for_insn_or_target (history, ip));
+ ? nops_for_insn (0, history, NULL)
+ : nops_for_insn_or_target (0, history, ip));
if (nops > 0)
{
fragS *old_frag;
}
else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
{
- /* Work out how many nops in prev_nop_frag are needed by IP. */
- int nops = nops_for_insn_or_target (history, ip);
+ int nops;
+
+ /* Work out how many nops in prev_nop_frag are needed by IP,
+ ignoring hazards generated by the first prev_nop_frag_since
+ instructions. */
+ nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
gas_assert (nops <= prev_nop_frag_holds);
/* Enforce NOPS as a minimum. */
out that the branch was out-of-range, we'll get an error. */
&& !mips_opts.warn_about_macros
&& (mips_opts.at || mips_pic == NO_PIC)
+ /* Don't relax BPOSGE32/64 as they have no complementing branches. */
+ && !(ip->insn_mo->membership & (INSN_DSP64 | INSN_DSP))
&& !mips_opts.mips16)
{
relaxed_branch = TRUE;
: (pinfo & INSN_COND_BRANCH_LIKELY) ? 1
: 0)), 4,
RELAX_BRANCH_ENCODE
- (pinfo & INSN_UNCOND_BRANCH_DELAY,
+ (AT,
+ pinfo & INSN_UNCOND_BRANCH_DELAY,
pinfo & INSN_COND_BRANCH_LIKELY,
pinfo & INSN_WRITE_GPR_31,
0),
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;
install_insn (ip);
/* Update the register mask information. */
- if (! mips_opts.mips16)
- {
- if ((pinfo & INSN_WRITE_GPR_D) || (pinfo2 & INSN2_READ_GPR_D))
- mips_gprmask |= 1 << EXTRACT_OPERAND (RD, *ip);
- if ((pinfo & (INSN_WRITE_GPR_T | INSN_READ_GPR_T)) != 0)
- mips_gprmask |= 1 << EXTRACT_OPERAND (RT, *ip);
- if (pinfo & INSN_READ_GPR_S)
- mips_gprmask |= 1 << EXTRACT_OPERAND (RS, *ip);
- if (pinfo & INSN_WRITE_GPR_31)
- mips_gprmask |= 1 << RA;
- if (pinfo2 & (INSN2_WRITE_GPR_Z | INSN2_READ_GPR_Z))
- mips_gprmask |= 1 << EXTRACT_OPERAND (RZ, *ip);
- if (pinfo & INSN_WRITE_FPR_D)
- mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FD, *ip);
- if ((pinfo & (INSN_WRITE_FPR_S | INSN_READ_FPR_S)) != 0)
- mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FS, *ip);
- if ((pinfo & (INSN_WRITE_FPR_T | INSN_READ_FPR_T)) != 0)
- mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FT, *ip);
- if ((pinfo & INSN_READ_FPR_R) != 0)
- mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FR, *ip);
- if (pinfo2 & (INSN2_WRITE_FPR_Z | INSN2_READ_FPR_Z))
- mips_cprmask[1] |= 1 << EXTRACT_OPERAND (FZ, *ip);
- if (pinfo & INSN_COP)
- {
- /* We don't keep enough information to sort these cases out.
- The itbl support does keep this information however, although
- we currently don't support itbl fprmats as part of the cop
- instruction. May want to add this support in the future. */
- }
- /* Never set the bit for $0, which is always zero. */
- mips_gprmask &= ~1 << 0;
- }
- else
- {
- if (pinfo & (MIPS16_INSN_WRITE_X | MIPS16_INSN_READ_X))
- mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RX, *ip);
- if (pinfo & (MIPS16_INSN_WRITE_Y | MIPS16_INSN_READ_Y))
- mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RY, *ip);
- if (pinfo & MIPS16_INSN_WRITE_Z)
- mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (RZ, *ip);
- if (pinfo & (MIPS16_INSN_WRITE_T | MIPS16_INSN_READ_T))
- mips_gprmask |= 1 << TREG;
- if (pinfo & (MIPS16_INSN_WRITE_SP | MIPS16_INSN_READ_SP))
- mips_gprmask |= 1 << SP;
- if (pinfo & (MIPS16_INSN_WRITE_31 | MIPS16_INSN_READ_31))
- mips_gprmask |= 1 << RA;
- if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
- mips_gprmask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
- if (pinfo & MIPS16_INSN_READ_Z)
- mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip);
- if (pinfo & MIPS16_INSN_READ_GPR_X)
- mips_gprmask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
- }
+ mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
+ mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
if (mips_relax.sequence != 2 && !mips_opts.noreorder)
{
&& prev_insn_frag_type == rs_machine_dependent)
/* Check for conflicts between the branch and the instructions
before the candidate delay slot. */
- || nops_for_insn (history + 1, ip) > 0
+ || nops_for_insn (0, history + 1, ip) > 0
/* Check for conflicts between the swapped sequence and the
target of the branch. */
- || nops_for_sequence (2, history + 1, ip, history) > 0
+ || nops_for_sequence (2, 0, history + 1, ip, history) > 0
/* We do not swap with a trap instruction, since it
complicates trap handlers to have the trap
instruction be in a delay slot. */
|| (prev_pinfo & INSN_TRAP)
/* If the branch reads a register that the previous
instruction sets, we can not swap. */
- || (! mips_opts.mips16
- && (prev_pinfo & INSN_WRITE_GPR_T)
- && insn_uses_reg (ip, EXTRACT_OPERAND (RT, history[0]),
- MIPS_GR_REG))
- || (! mips_opts.mips16
- && (prev_pinfo & INSN_WRITE_GPR_D)
- && insn_uses_reg (ip, EXTRACT_OPERAND (RD, history[0]),
- MIPS_GR_REG))
- || (! mips_opts.mips16
- && (prev_pinfo2 & INSN2_WRITE_GPR_Z)
- && insn_uses_reg (ip, EXTRACT_OPERAND (RZ, history[0]),
- MIPS_GR_REG))
- || (mips_opts.mips16
- && (((prev_pinfo & MIPS16_INSN_WRITE_X)
- && (insn_uses_reg
- (ip, MIPS16_EXTRACT_OPERAND (RX, history[0]),
- MIPS16_REG)))
- || ((prev_pinfo & MIPS16_INSN_WRITE_Y)
- && (insn_uses_reg
- (ip, MIPS16_EXTRACT_OPERAND (RY, history[0]),
- MIPS16_REG)))
- || ((prev_pinfo & MIPS16_INSN_WRITE_Z)
- && (insn_uses_reg
- (ip, MIPS16_EXTRACT_OPERAND (RZ, history[0]),
- MIPS16_REG)))
- || ((prev_pinfo & MIPS16_INSN_WRITE_T)
- && insn_uses_reg (ip, TREG, MIPS_GR_REG))
- || ((prev_pinfo & MIPS16_INSN_WRITE_31)
- && insn_uses_reg (ip, RA, MIPS_GR_REG))
- || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
- && insn_uses_reg (ip,
- MIPS16OP_EXTRACT_REG32R
- (history[0].insn_opcode),
- MIPS_GR_REG))))
+ || (gpr_read_mask (ip) & gpr_write_mask (&history[0])) != 0
/* If the branch writes a register that the previous
- instruction sets, we can not swap (we know that
- branches write only to RD or to $31). */
- || (! mips_opts.mips16
- && (prev_pinfo & INSN_WRITE_GPR_T)
- && (((pinfo & INSN_WRITE_GPR_D)
- && (EXTRACT_OPERAND (RT, history[0])
- == EXTRACT_OPERAND (RD, *ip)))
- || ((pinfo & INSN_WRITE_GPR_31)
- && EXTRACT_OPERAND (RT, history[0]) == RA)))
- || (! mips_opts.mips16
- && (prev_pinfo & INSN_WRITE_GPR_D)
- && (((pinfo & INSN_WRITE_GPR_D)
- && (EXTRACT_OPERAND (RD, history[0])
- == EXTRACT_OPERAND (RD, *ip)))
- || ((pinfo & INSN_WRITE_GPR_31)
- && EXTRACT_OPERAND (RD, history[0]) == RA)))
- || (mips_opts.mips16
- && (pinfo & MIPS16_INSN_WRITE_31)
- && ((prev_pinfo & MIPS16_INSN_WRITE_31)
- || ((prev_pinfo & MIPS16_INSN_WRITE_GPR_Y)
- && (MIPS16OP_EXTRACT_REG32R (history[0].insn_opcode)
- == RA))))
+ instruction sets, we can not swap. */
+ || (gpr_write_mask (ip) & gpr_write_mask (&history[0])) != 0
/* If the branch writes a register that the previous
- instruction reads, we can not swap (we know that
- branches only write to RD or to $31). */
- || (! mips_opts.mips16
- && (pinfo & INSN_WRITE_GPR_D)
- && insn_uses_reg (&history[0],
- EXTRACT_OPERAND (RD, *ip),
- MIPS_GR_REG))
- || (! mips_opts.mips16
- && (pinfo & INSN_WRITE_GPR_31)
- && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
- || (mips_opts.mips16
- && (pinfo & MIPS16_INSN_WRITE_31)
- && insn_uses_reg (&history[0], RA, MIPS_GR_REG))
+ instruction reads, we can not swap. */
+ || (gpr_write_mask (ip) & gpr_read_mask (&history[0])) != 0
/* If one instruction sets a condition code and the
other one uses a condition code, we can not swap. */
|| ((pinfo & INSN_READ_COND_CODE)
{
if (! mips_opts.noreorder)
{
- int nops = nops_for_insn (history, NULL);
+ int nops = nops_for_insn (0, history, NULL);
if (nops > 0)
{
while (nops-- > 0)
/* Insert any nops that might be needed between the .set noreorder
block and the previous instructions. We will later remove any
nops that turn out not to be needed. */
- nops = nops_for_insn (history, NULL);
+ nops = nops_for_insn (0, history, NULL);
if (nops > 0)
{
if (mips_optimize != 0)
case M_CACHE_AB:
s = "cache";
goto st;
+ case M_PREF_AB:
+ s = "pref";
+ goto st;
case M_SDC1_AB:
s = "sdc1";
coproc = 1;
|| mask == M_L_DAB
|| mask == M_S_DAB)
fmt = "T,o(b)";
- else if (mask == M_CACHE_AB)
+ else if (mask == M_CACHE_AB || mask == M_PREF_AB)
fmt = "k,o(b)";
else if (coproc)
fmt = "E,o(b)";
if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
fragp->fr_subtype
- = RELAX_BRANCH_ENCODE (RELAX_BRANCH_UNCOND (fragp->fr_subtype),
+ = RELAX_BRANCH_ENCODE (RELAX_BRANCH_AT (fragp->fr_subtype),
+ RELAX_BRANCH_UNCOND (fragp->fr_subtype),
RELAX_BRANCH_LIKELY (fragp->fr_subtype),
RELAX_BRANCH_LINK (fragp->fr_subtype),
toofar);
&& (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
return 0;
- /* There is no place to store an in-place offset for JALR relocations. */
- if (fixp->fx_r_type == BFD_RELOC_MIPS_JALR && HAVE_IN_PLACE_ADDENDS)
+ /* There is no place to store an in-place offset for JALR relocations.
+ Likewise an in-range offset of PC-relative relocations may overflow
+ the in-place relocatable field if recalculated against the start
+ address of the symbol's containing section. */
+ if (HAVE_IN_PLACE_ADDENDS
+ && (fixp->fx_pcrel || fixp->fx_r_type == BFD_RELOC_MIPS_JALR))
return 0;
#ifdef OBJ_ELF
int i;
as_warn_where (fragp->fr_file, fragp->fr_line,
- _("relaxed out-of-range branch into a jump"));
+ _("Relaxed out-of-range branch into a jump"));
if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
goto uncond;
}
else
{
+ unsigned long at = RELAX_BRANCH_AT (fragp->fr_subtype);
+
/* lw/ld $at, <sym>($gp) R_MIPS_GOT16 */
- insn = HAVE_64BIT_ADDRESSES ? 0xdf810000 : 0x8f810000;
+ insn = HAVE_64BIT_ADDRESSES ? 0xdf800000 : 0x8f800000;
+ insn |= at << OP_SH_RT;
exp.X_op = O_symbol;
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
}
/* d/addiu $at, $at, <sym> R_MIPS_LO16 */
- insn = HAVE_64BIT_ADDRESSES ? 0x64210000 : 0x24210000;
+ insn = HAVE_64BIT_ADDRESSES ? 0x64000000 : 0x24000000;
+ insn |= at << OP_SH_RS | at << OP_SH_RT;
fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
4, &exp, FALSE, BFD_RELOC_LO16);
/* j(al)r $at. */
if (RELAX_BRANCH_LINK (fragp->fr_subtype))
- insn = 0x0020f809;
+ insn = 0x0000f809;
else
- insn = 0x00200008;
+ insn = 0x00000008;
+ insn |= at << OP_SH_RS;
md_number_to_chars ((char *) buf, insn, 4);
buf += 4;