/* 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. */
/* MIPS ISA we are using for this output file. */
static int file_mips_isa = ISA_UNKNOWN;
-/* True if -mips16 was passed or implied by arguments passed on the
- command line (e.g., by -march). */
+/* True if any MIPS16 code was produced. */
static int file_ase_mips16;
#define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \
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
{ NULL, NULL, 0 },
};
+/* Export the ABI address size for use by TC_ADDRESS_BYTES for the
+ purpose of the `.dc.a' internal pseudo-op. */
+
+int
+mips_address_bytes (void)
+{
+ return HAVE_64BIT_ADDRESSES ? 8 : 4;
+}
+
extern void pop_insert (const pseudo_typeS *);
void
/* The default target format to use. */
+#if defined (TE_FreeBSD)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips-freebsd"
+#elif defined (TE_TMIPS)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips"
+#else
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "mips"
+#endif
+
const char *
mips_target_format (void)
{
? "elf32-bigmips-vxworks"
: "elf32-littlemips-vxworks");
#endif
-#ifdef TE_TMIPS
- /* This is traditional mips. */
return (target_big_endian
? (HAVE_64BIT_OBJECTS
- ? "elf64-tradbigmips"
+ ? ELF_TARGET ("elf64-", "big")
: (HAVE_NEWABI
- ? "elf32-ntradbigmips" : "elf32-tradbigmips"))
+ ? ELF_TARGET ("elf32-n", "big")
+ : ELF_TARGET ("elf32-", "big")))
: (HAVE_64BIT_OBJECTS
- ? "elf64-tradlittlemips"
+ ? ELF_TARGET ("elf64-", "little")
: (HAVE_NEWABI
- ? "elf32-ntradlittlemips" : "elf32-tradlittlemips")));
-#else
- return (target_big_endian
- ? (HAVE_64BIT_OBJECTS
- ? "elf64-bigmips"
- : (HAVE_NEWABI
- ? "elf32-nbigmips" : "elf32-bigmips"))
- : (HAVE_64BIT_OBJECTS
- ? "elf64-littlemips"
- : (HAVE_NEWABI
- ? "elf32-nlittlemips" : "elf32-littlemips")));
-#endif
+ ? ELF_TARGET ("elf32-n", "little")
+ : ELF_TARGET ("elf32-", "little"))));
default:
abort ();
return NULL;
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;
- }
- 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;
- }
- 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;
}
bfd_reloc_code_real_type *reloc_type)
{
unsigned long prev_pinfo, pinfo;
+ unsigned long prev_pinfo2, pinfo2;
relax_stateT prev_insn_frag_type = 0;
bfd_boolean relaxed_branch = FALSE;
segment_info_type *si = seg_info (now_seg);
/* Mark instruction labels in mips16 mode. */
mips16_mark_labels ();
+ file_ase_mips16 |= mips_opts.mips16;
+
prev_pinfo = history[0].insn_mo->pinfo;
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
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)
{
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)
- 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 (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 (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_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)
unsigned long temp = (treg << 16) | (0x01);
macro_build (NULL, "c2", "C", temp);
}
- /* AT is not used, just return */
- return;
+ break;
case M_MSGLD:
{
unsigned long temp = (0x02);
macro_build (NULL, "c2", "C", temp);
}
- /* AT is not used, just return */
- return;
+ break;
case M_MSGLD_T:
{
unsigned long temp = (treg << 16) | (0x02);
macro_build (NULL, "c2", "C", temp);
}
- /* AT is not used, just return */
- return;
+ break;
case M_MSGWAIT:
macro_build (NULL, "c2", "C", 3);
- /* AT is not used, just return */
- return;
+ break;
case M_MSGWAIT_T:
{
unsigned long temp = (treg << 16) | 0x03;
macro_build (NULL, "c2", "C", temp);
}
- /* AT is not used, just return */
- return;
+ break;
case M_J_A:
/* The j instruction may not be used in PIC code, since it
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)";
case 'Q': USE_BITS (OP_MASK_SEQI, OP_SH_SEQI); break;
case 's': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
case 'S': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
+ case 'z': USE_BITS (OP_MASK_RZ, OP_SH_RZ); break;
+ case 'Z': USE_BITS (OP_MASK_FZ, OP_SH_FZ); break;
+ case 'a': USE_BITS (OP_MASK_OFFSET_A, OP_SH_OFFSET_A); break;
+ case 'b': USE_BITS (OP_MASK_OFFSET_B, OP_SH_OFFSET_B); break;
+ case 'c': USE_BITS (OP_MASK_OFFSET_C, OP_SH_OFFSET_C); break;
default:
as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
s = expr_end;
continue;
+ case 'a': /* 8-bit signed offset in bit 6 */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ min_range = -((OP_MASK_OFFSET_A + 1) >> 1);
+ max_range = ((OP_MASK_OFFSET_A + 1) >> 1) - 1;
+ if (imm_expr.X_add_number < min_range
+ || imm_expr.X_add_number > max_range)
+ {
+ as_bad (_("Offset not in range %ld..%ld (%ld)"),
+ (long) min_range, (long) max_range,
+ (long) imm_expr.X_add_number);
+ }
+ INSERT_OPERAND (OFFSET_A, *ip, imm_expr.X_add_number);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'b': /* 8-bit signed offset in bit 3 */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ min_range = -((OP_MASK_OFFSET_B + 1) >> 1);
+ max_range = ((OP_MASK_OFFSET_B + 1) >> 1) - 1;
+ if (imm_expr.X_add_number < min_range
+ || imm_expr.X_add_number > max_range)
+ {
+ as_bad (_("Offset not in range %ld..%ld (%ld)"),
+ (long) min_range, (long) max_range,
+ (long) imm_expr.X_add_number);
+ }
+ INSERT_OPERAND (OFFSET_B, *ip, imm_expr.X_add_number);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'c': /* 9-bit signed offset in bit 6 */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ min_range = -((OP_MASK_OFFSET_C + 1) >> 1);
+ max_range = ((OP_MASK_OFFSET_C + 1) >> 1) - 1;
+ /* We check the offset range before adjusted. */
+ min_range <<= 4;
+ max_range <<= 4;
+ if (imm_expr.X_add_number < min_range
+ || imm_expr.X_add_number > max_range)
+ {
+ as_bad (_("Offset not in range %ld..%ld (%ld)"),
+ (long) min_range, (long) max_range,
+ (long) imm_expr.X_add_number);
+ }
+ if (imm_expr.X_add_number & 0xf)
+ {
+ as_bad (_("Offset not 16 bytes alignment (%ld)"),
+ (long) imm_expr.X_add_number);
+ }
+ /* Right shift 4 bits to adjust the offset operand. */
+ INSERT_OPERAND (OFFSET_C, *ip, imm_expr.X_add_number >> 4);
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
+ case 'z':
+ if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
+ break;
+ if (regno == AT && mips_opts.at)
+ {
+ if (mips_opts.at == ATREG)
+ as_warn (_("used $at without \".set noat\""));
+ else
+ as_warn (_("used $%u with \".set at=$%u\""),
+ regno, mips_opts.at);
+ }
+ INSERT_OPERAND (RZ, *ip, regno);
+ continue;
+
+ case 'Z':
+ if (!reg_lookup (&s, RTYPE_FPU, ®no))
+ break;
+ INSERT_OPERAND (FZ, *ip, regno);
+ continue;
+
default:
as_bad (_("Internal error: bad mips opcode "
"(unknown extension operand type `+%c'): %s %s"),
INSERT_OPERAND (RS, *ip, regno);
break;
case 'd':
- case 'G':
case 'K':
case 'g':
INSERT_OPERAND (RD, *ip, regno);
is $0. This only matches $0, and is checked
outside the switch. */
break;
- case 'D':
- /* Itbl operand; not yet implemented. FIXME ?? */
- break;
- /* What about all other operands like 'i', which
- can be specified in the opcode table? */
}
lastregno = regno;
continue;
/* Check whether there is only a single bracketed expression
left. If so, it must be the base register and the
constant must be zero. */
- offset_reloc[0] = BFD_RELOC_LO16;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
if (*s == '(' && strchr (s + 1, '(') == 0)
{
offset_expr.X_op = O_constant;
my_getExpression (expressionS *ep, char *str)
{
char *save_in;
- valueT val;
save_in = input_line_pointer;
input_line_pointer = str;
expression (ep);
expr_end = input_line_pointer;
input_line_pointer = save_in;
-
- /* If we are in mips16 mode, and this is an expression based on `.',
- then we bump the value of the symbol by 1 since that is how other
- text symbols are handled. We don't bother to handle complex
- expressions, just `.' plus or minus a constant. */
- if (mips_opts.mips16
- && ep->X_op == O_symbol
- && strcmp (S_GET_NAME (ep->X_add_symbol), FAKE_LABEL_NAME) == 0
- && S_GET_SEGMENT (ep->X_add_symbol) == now_seg
- && symbol_get_frag (ep->X_add_symbol) == frag_now
- && symbol_constant_p (ep->X_add_symbol)
- && (val = S_GET_VALUE (ep->X_add_symbol)) == frag_now_fix ())
- S_SET_VALUE (ep->X_add_symbol, val + 1);
}
char *
list = bfd_target_list ();
for (l = list; *l != NULL; l++)
-#ifdef TE_TMIPS
- /* This is traditional mips */
- if (strcmp (*l, "elf64-tradbigmips") == 0
- || strcmp (*l, "elf64-tradlittlemips") == 0)
-#else
- if (strcmp (*l, "elf64-bigmips") == 0
- || strcmp (*l, "elf64-littlemips") == 0)
-#endif
+ if (strcmp (*l, ELF_TARGET ("elf64-", "big")) == 0
+ || strcmp (*l, ELF_TARGET ("elf64-", "little")) == 0)
break;
yes = (*l != NULL);
free (list);
mips_cpu_info_from_isa (mips_opts.isa)->name);
file_mips_isa = mips_opts.isa;
- file_ase_mips16 = mips_opts.mips16;
file_ase_mips3d = mips_opts.ase_mips3d;
file_ase_mdmx = mips_opts.ase_mdmx;
file_ase_smartmips = mips_opts.ase_smartmips;
mips_emit_delays ();
if (log_size > 0 && auto_align)
mips_align (log_size, 0, label);
- mips_clear_insn_labels ();
cons (1 << log_size);
+ mips_clear_insn_labels ();
}
static void
mips_align (2, 0, label);
}
- mips_clear_insn_labels ();
-
float_cons (type);
+ mips_clear_insn_labels ();
}
/* Handle .globl. We need to override it because on Irix 5 you are
mips_emit_delays ();
if (auto_align)
mips_align (2, 0, label);
- mips_clear_insn_labels ();
expression (&ex);
+ mips_clear_insn_labels ();
if (ex.X_op != O_symbol || ex.X_add_number != 0)
{
mips_emit_delays ();
if (auto_align)
mips_align (3, 0, label);
- mips_clear_insn_labels ();
expression (&ex);
+ mips_clear_insn_labels ();
if (ex.X_op != O_symbol || ex.X_add_number != 0)
{
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;
#endif
-/* This function is called whenever a label is defined. It is used
- when handling branch delays; if a branch has a label, we assume we
- can not move it. */
+/* This function is called whenever a label is defined, including fake
+ labels instantiated off the dot special symbol. It is used when
+ handling branch delays; if a branch has a label, we assume we cannot
+ move it. This also bumps the value of the symbol by 1 in compressed
+ code. */
void
-mips_define_label (symbolS *sym)
+mips_record_label (symbolS *sym)
{
segment_info_type *si = seg_info (now_seg);
struct insn_label_list *l;
l->label = sym;
l->next = si->label_list;
si->label_list = l;
+}
+
+/* This function is called as tc_frob_label() whenever a label is defined
+ and adds a DWARF-2 record we only want for true labels. */
+void
+mips_define_label (symbolS *sym)
+{
+ mips_record_label (sym);
#ifdef OBJ_ELF
dwarf2_emit_label (sym);
#endif