/* tc-mips.c -- assemble code for a MIPS chip.
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+ 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
Free Software Foundation, Inc.
Contributed by the OSF and Ralph Campbell.
Written by Keith Knowles and Ralph Campbell, working independently.
/* True if this instruction is complete. */
unsigned int complete_p : 1;
+
+ /* True if this instruction is cleared from history by unconditional
+ branch. */
+ unsigned int cleared_p : 1;
};
/* The ABI to use. */
/* True if CPU has seq/sne and seqi/snei instructions. */
#define CPU_HAS_SEQ(CPU) (CPU_IS_OCTEON (CPU))
+/* True, if CPU has support for ldc1 and sdc1. */
+#define CPU_HAS_LDC1_SDC1(CPU) \
+ ((mips_opts.isa != ISA_MIPS1) && ((CPU) != CPU_R5900))
+
/* True if mflo and mfhi can be immediately followed by instructions
which write to the HI and LO registers.
|| mips_opts.isa == ISA_MIPS64 \
|| mips_opts.isa == ISA_MIPS64R2 \
|| mips_opts.arch == CPU_R4010 \
+ || mips_opts.arch == CPU_R5900 \
|| mips_opts.arch == CPU_R10000 \
|| mips_opts.arch == CPU_R12000 \
|| mips_opts.arch == CPU_R14000 \
#define gpr_interlocks \
(mips_opts.isa != ISA_MIPS1 \
|| mips_opts.arch == CPU_R3900 \
+ || mips_opts.arch == CPU_R5900 \
|| mips_opts.micromips \
)
\f
/* Prototypes for static functions. */
-#define internalError() \
- as_fatal (_("internal Error, line %d, %s"), __LINE__, __FILE__)
-
enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
static void append_insn
{"section", s_change_section, 0},
{"short", s_cons, 1},
{"single", s_float_cons, 'f'},
+ {"stabd", s_mips_stab, 'd'},
{"stabn", s_mips_stab, 'n'},
+ {"stabs", s_mips_stab, 's'},
{"text", s_change_sec, 't'},
{"word", s_cons, 2},
static unsigned int forced_insn_length;
+/* True if we are assembling an instruction. All dot symbols defined during
+ this time should be treated as code labels. */
+
+static bfd_boolean mips_assembling_insn;
+
#ifdef OBJ_ELF
/* The pdr segment for per procedure frame/regmask info. Not used for
ECOFF debugging. */
insn->noreorder_p = (mips_opts.noreorder > 0);
insn->mips16_absolute_jump_p = 0;
insn->complete_p = 0;
+ insn->cleared_p = 0;
}
/* Record the current MIPS16/microMIPS mode in now_seg. */
}
/* Return TRUE if the microMIPS opcode MO is valid for the delay slot
- of the preceding instruction. Always TRUE in the standard MIPS mode. */
+ of the preceding instruction. Always TRUE in the standard MIPS mode.
+
+ We don't accept macros in 16-bit delay slots to avoid a case where
+ a macro expansion fails because it relies on a preceding 32-bit real
+ instruction to have matched and does not handle the operands correctly.
+ The only macros that may expand to 16-bit instructions are JAL that
+ cannot be placed in a delay slot anyway, and corner cases of BALIGN
+ and BGT (that likewise cannot be placed in a delay slot) that decay to
+ a NOP. In all these cases the macros precede any corresponding real
+ instruction definitions in the opcode table, so they will match in the
+ second pass where the size of the delay slot is ignored and therefore
+ produce correct code. */
static bfd_boolean
is_delay_slot_valid (const struct mips_opcode *mo)
return TRUE;
if (mo->pinfo == INSN_MACRO)
- return TRUE;
+ return (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) == 0;
if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
&& micromips_insn_length (mo) != 4)
return FALSE;
offset_reloc[1] = BFD_RELOC_UNUSED;
offset_reloc[2] = BFD_RELOC_UNUSED;
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
if (mips_opts.mips16)
mips16_ip (str, &insn);
else
}
if (insn_error)
- {
- as_bad ("%s `%s'", insn_error, str);
- return;
- }
-
- if (insn.insn_mo->pinfo == INSN_MACRO)
+ as_bad ("%s `%s'", insn_error, str);
+ else if (insn.insn_mo->pinfo == INSN_MACRO)
{
macro_start ();
if (mips_opts.mips16)
else
append_insn (&insn, NULL, unused_reloc, FALSE);
}
+
+ mips_assembling_insn = FALSE;
}
/* Convenience functions for abstracting away the differences between
return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
}
+/* Return true if RELOC is a PC-relative relocation that does not have
+ full address range. */
+
+static inline bfd_boolean
+limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ return TRUE;
+
+ case BFD_RELOC_32_PCREL:
+ return HAVE_64BIT_ADDRESSES;
+
+ default:
+ return FALSE;
+ }
+}
+
/* Return true if the given relocation might need a matching %lo().
This is only "might" because SVR4 R_MIPS_GOT16 relocations only
need a matching %lo() when applied to local symbols. */
return linkonce;
}
-/* Mark instruction labels in MIPS16/microMIPS mode. This permits the
+/* Mark MIPS16 or microMIPS instruction label LABEL. This permits the
linker to handle them specially, such as generating jalx instructions
when needed. We also make them odd for the duration of the assembly,
in order to generate the right sort of code. We will make them even
to make them odd again. */
static void
-mips_compressed_mark_labels (void)
+mips_compressed_mark_label (symbolS *label)
{
- segment_info_type *si = seg_info (now_seg);
- struct insn_label_list *l;
-
gas_assert (HAVE_CODE_COMPRESSION);
- for (l = si->label_list; l != NULL; l = l->next)
- {
- symbolS *label = l->label;
-
#if defined(OBJ_ELF) || defined(OBJ_MAYBE_ELF)
- if (IS_ELF)
- {
- if (mips_opts.mips16)
- S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
- else
- S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
- }
-#endif
- if ((S_GET_VALUE (label) & 1) == 0
- /* Don't adjust the address if the label is global or weak, or
- in a link-once section, since we'll be emitting symbol reloc
- references to it which will be patched up by the linker, and
- the final value of the symbol may or may not be MIPS16/microMIPS. */
- && ! S_IS_WEAK (label)
- && ! S_IS_EXTERNAL (label)
- && ! s_is_linkonce (label, now_seg))
- S_SET_VALUE (label, S_GET_VALUE (label) | 1);
+ if (IS_ELF)
+ {
+ if (mips_opts.mips16)
+ S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
+ else
+ S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
}
+#endif
+ if ((S_GET_VALUE (label) & 1) == 0
+ /* Don't adjust the address if the label is global or weak, or
+ in a link-once section, since we'll be emitting symbol reloc
+ references to it which will be patched up by the linker, and
+ the final value of the symbol may or may not be MIPS16/microMIPS. */
+ && !S_IS_WEAK (label)
+ && !S_IS_EXTERNAL (label)
+ && !s_is_linkonce (label, now_seg))
+ S_SET_VALUE (label, S_GET_VALUE (label) | 1);
+}
+
+/* Mark preceding MIPS16 or microMIPS instruction labels. */
+
+static void
+mips_compressed_mark_labels (void)
+{
+ struct insn_label_list *l;
+
+ for (l = seg_info (now_seg)->label_list; l != NULL; l = l->next)
+ mips_compressed_mark_label (l->label);
}
/* End the current frag. Make it a variant frag and record the
/* IP is a branch that has a delay slot, and we need to fill it
automatically. Return true if we can do that by swapping IP
- with the previous instruction. */
+ with the previous instruction.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
static bfd_boolean
-can_swap_branch_p (struct mips_cl_insn *ip)
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
{
unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
&& insn_length (history) != 4)
return FALSE;
+ /* On R5900 short loops need to be fixed by inserting a nop in
+ the branch delay slots.
+ A short loop can be terminated too early. */
+ if (mips_opts.arch == CPU_R5900
+ /* Check if instruction has a parameter, ignore "j $31". */
+ && (address_expr != NULL)
+ /* Parameter must be 16 bit. */
+ && (*reloc_type == BFD_RELOC_16_PCREL_S2)
+ /* Branch to same segment. */
+ && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg)
+ /* Branch to same code fragment. */
+ && (symbol_get_frag(address_expr->X_add_symbol) == frag_now)
+ /* Can only calculate branch offset if value is known. */
+ && symbol_constant_p(address_expr->X_add_symbol)
+ /* Check if branch is really conditional. */
+ && !((ip->insn_opcode & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ int distance;
+ /* Check if loop is shorter than 6 instructions including
+ branch and delay slot. */
+ distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
+ if (distance <= 20)
+ {
+ int i;
+ int rv;
+
+ rv = FALSE;
+ /* When the loop includes branches or jumps,
+ it is not a short loop. */
+ for (i = 0; i < (distance / 4); i++)
+ {
+ if ((history[i].cleared_p)
+ || delayed_branch_p(&history[i]))
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ if (rv == FALSE)
+ {
+ /* Insert nop after branch to fix short loop. */
+ return FALSE;
+ }
+ }
+ }
+
return TRUE;
}
-/* Decide how we should add IP to the instruction stream. */
+/* Decide how we should add IP to the instruction stream.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
static enum append_method
-get_append_method (struct mips_cl_insn *ip)
+get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
{
unsigned long pinfo;
/* Otherwise, it's our responsibility to fill branch delay slots. */
if (delayed_branch_p (ip))
{
- if (!branch_likely_p (ip) && can_swap_branch_p (ip))
+ if (!branch_likely_p (ip)
+ && can_swap_branch_p (ip, address_expr, reloc_type))
return APPEND_SWAP;
pinfo = ip->insn_mo->pinfo;
return reloc;
}
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+ Return true on success, storing the resolved value in RESULT. */
+
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+ offsetT *result)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_MIPS_HIGHEST:
+ case BFD_RELOC_MICROMIPS_HIGHEST:
+ *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_MIPS_HIGHER:
+ case BFD_RELOC_MICROMIPS_HIGHER:
+ *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MIPS16_HI16_S:
+ *result = ((operand + 0x8000) >> 16) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_MICROMIPS_HI16:
+ case BFD_RELOC_MIPS16_HI16:
+ *result = (operand >> 16) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_MICROMIPS_LO16:
+ case BFD_RELOC_MIPS16_LO16:
+ *result = operand & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_UNUSED:
+ *result = operand;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
/* Output an instruction. IP is the instruction information.
ADDRESS_EXPR is an operand of the instruction to be used with
RELOC_TYPE. EXPANSIONP is true if the instruction is part of
if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
fix_loongson2f (ip);
- mips_mark_labels ();
-
file_ase_mips16 |= mips_opts.mips16;
file_ase_micromips |= mips_opts.micromips;
if (address_expr == NULL)
ip->complete_p = 1;
- else if (*reloc_type <= BFD_RELOC_UNUSED
+ else if (reloc_type[0] <= BFD_RELOC_UNUSED
+ && reloc_type[1] == BFD_RELOC_UNUSED
+ && reloc_type[2] == 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:
{
int shift;
(unsigned long) address_expr->X_add_number);
ip->insn_opcode |= ((address_expr->X_add_number >> shift)
& 0x3ffffff);
- ip->complete_p = 0;
+ ip->complete_p = 1;
}
break;
(((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;
+ ip->complete_p = 1;
break;
case BFD_RELOC_16_PCREL_S2:
ip->insn_opcode |= ((address_expr->X_add_number >> shift)
& 0xffff);
}
- ip->complete_p = 0;
}
break;
default:
- internalError ();
- }
+ {
+ offsetT value;
+
+ if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+ &value))
+ {
+ ip->insn_opcode |= value & 0xffff;
+ ip->complete_p = 1;
+ }
+ }
+ break;
+ }
}
if (mips_relax.sequence != 2 && !mips_opts.noreorder)
}
}
- method = get_append_method (ip);
+ method = get_append_method (ip, address_expr, reloc_type);
branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
#ifdef OBJ_ELF
|| lo16_reloc_p (reloc_type[0])))
ip->fixp[0]->fx_no_overflow = 1;
+ /* These relocations can have an addend that won't fit in 2 octets. */
+ if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
+ || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
+ ip->fixp[0]->fx_no_overflow = 1;
+
if (mips_relax.sequence)
{
if (mips_relax.first_fixup == 0)
/* If we have just completed an unconditional branch, clear the history. */
if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
|| (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
- mips_no_prev_insn ();
+ {
+ unsigned int i;
+
+ mips_no_prev_insn ();
+
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].cleared_p = 1;
+ }
/* We need to emit a label at the end of branch-likely macros. */
if (emit_branch_likely_macro)
mips_clear_insn_labels ();
}
-/* Forget that there was any previous instruction or label. */
+/* Forget that there was any previous instruction or label.
+ When BRANCH is true, the branch history is also flushed. */
static void
mips_no_prev_insn (void)
continue;
default:
- internalError ();
+ abort ();
}
continue;
break;
default:
- internalError ();
+ abort ();
}
continue;
default:
- internalError ();
+ abort ();
}
break;
}
case 'p':
case 'q':
{
+ offsetT value;
+
gas_assert (ep != NULL);
if (ep->X_op != O_constant)
*r = (int) BFD_RELOC_UNUSED + c;
- else
+ else if (calculate_reloc (*r, ep->X_add_number, &value))
{
- mips16_immed (NULL, 0, c, *r, ep->X_add_number,
- 0, &insn.insn_opcode);
+ mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode);
ep = NULL;
*r = BFD_RELOC_UNUSED;
}
if (mips_opts.micromips)
{
jalr = mips_opts.noreorder && !cprestore ? "jalr" : "jalrs";
- if (MIPS_JALR_HINT_P (ep))
+ if (MIPS_JALR_HINT_P (ep)
+ || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
else
macro_build (NULL, jalr, "mj", PIC_CALL_REG);
if (mips_pic == NO_PIC)
{
s = jals ? "jalrs" : "jalr";
- if (mips_opts.micromips && dreg == RA)
+ if (mips_opts.micromips
+ && dreg == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
macro_build (NULL, s, "mj", sreg);
else
macro_build (NULL, s, JALR_FMT, dreg, sreg);
s = (mips_opts.micromips && (!mips_opts.noreorder || cprestore)
? "jalrs" : "jalr");
- if (mips_opts.micromips && dreg == RA)
+ if (mips_opts.micromips
+ && dreg == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
macro_build (NULL, s, "mj", sreg);
else
macro_build (NULL, s, JALR_FMT, dreg, sreg);
s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
if (strcmp (s, ".lit8") == 0)
{
- if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips)
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
{
macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
BFD_RELOC_MIPS_LITERAL, mips_gp_register);
macro_build_lui (&offset_expr, AT);
}
- if (mips_opts.isa != ISA_MIPS1 || mips_opts.micromips)
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
{
macro_build (&offset_expr, "ldc1", "T,o(b)",
treg, BFD_RELOC_LO16, AT);
r = BFD_RELOC_LO16;
dob:
gas_assert (!mips_opts.micromips);
- gas_assert (mips_opts.isa == ISA_MIPS1);
+ gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
macro_build (&offset_expr, "lwc1", "T,o(b)",
target_big_endian ? treg + 1 : treg, r, breg);
/* FIXME: A possible overflow which I don't know how to deal
case M_S_DOB:
gas_assert (!mips_opts.micromips);
- gas_assert (mips_opts.isa == ISA_MIPS1);
+ gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
/* Even on a big endian machine $fn comes before $fn+1. We have
to adjust when storing to memory. */
macro_build (&offset_expr, "swc1", "T,o(b)",
/* Itbl support may require additional care here. */
coproc = 1;
fmt = "T,o(b)";
- if (mips_opts.isa != ISA_MIPS1)
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
{
s = "ldc1";
goto ld_st;
/* Itbl support may require additional care here. */
coproc = 1;
fmt = "T,o(b)";
- if (mips_opts.isa != ISA_MIPS1)
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
{
s = "sdc1";
goto ld_st;
s = "swc1";
goto ldd_std;
+ case M_LQ_AB:
+ fmt = "t,o(b)";
+ s = "lq";
+ goto ld;
+
+ case M_SQ_AB:
+ fmt = "t,o(b)";
+ s = "sq";
+ goto ld_st;
+
case M_LD_AB:
fmt = "t,o(b)";
if (HAVE_64BIT_GPRS)
case M_DMUL:
dbl = 1;
case M_MUL:
+ if (mips_opts.arch == CPU_R5900)
+ {
+ macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", dreg, sreg, treg);
+ }
+ else
+ {
macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ }
break;
case M_DMUL_I:
switch (mask)
{
default:
- internalError ();
+ abort ();
case M_DDIV_3:
dbl = 1;
/* Let a macro pass, we'll catch it later when it is expanded. */
return 1;
- if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa))
+ if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || (mips_opts.arch == CPU_R5900))
{
/* Allow odd registers for single-precision ops. */
switch (insn->pinfo & (FP_S | FP_D))
unsigned int destregno = 0;
unsigned int lastpos = 0;
unsigned int limlo, limhi;
+ int sizelo;
char *s_reset;
offsetT min_range, max_range;
long opend;
while (imm->type && imm->type != *args)
++imm;
if (! imm->type)
- internalError ();
+ abort ();
my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr);
if ((unsigned long) imm_expr.X_add_number & ~imm->mask)
case 'C': /* ext size, becomes MSBD. */
limlo = 1;
limhi = 32;
+ sizelo = 1;
goto do_msbd;
case 'G':
limlo = 33;
limhi = 64;
+ sizelo = 33;
goto do_msbd;
case 'H':
limlo = 33;
limhi = 64;
+ sizelo = 1;
goto do_msbd;
do_msbd:
my_getExpression (&imm_expr, s);
check_absolute_expr (ip, &imm_expr);
- /* Check for negative input so that small negative numbers
- will not succeed incorrectly. The checks against
- (pos+size) transitively check "size" itself,
- assuming that "pos" is reasonable. */
- if ((long) imm_expr.X_add_number < 0
+ /* The checks against (pos+size) don't transitively check
+ "size" itself, assuming that "pos" is reasonable.
+ We also need to check the lower bound of "size". */
+ if ((long) imm_expr.X_add_number < sizelo
|| ((unsigned long) imm_expr.X_add_number
+ lastpos) < limlo
|| ((unsigned long) imm_expr.X_add_number
if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
as_warn (_("Invalid performance register (%lu)"),
(unsigned long) imm_expr.X_add_number);
+ if (imm_expr.X_add_number != 0 && mips_opts.arch == CPU_R5900
+ && (!strcmp(insn->name,"mfps") || !strcmp(insn->name,"mtps")))
+ as_warn (_("Invalid performance register (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
INSERT_OPERAND (0, PERFREG, *ip, imm_expr.X_add_number);
imm_expr.X_op = O_absent;
s = expr_end;
continue;
case 'u': /* Upper 16 bits. */
+ *imm_reloc = BFD_RELOC_LO16;
if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
&& imm_expr.X_op == O_constant
&& (imm_expr.X_add_number < 0
break;
default:
- internalError ();
+ abort ();
}
if (regno == ILLEGAL_REG)
break;
default:
- internalError ();
+ abort ();
}
continue;
default:
as_bad (_("Bad char = '%c'\n"), *args);
- internalError ();
+ abort ();
}
break;
}
case '\0':
if (*s == '\0')
{
+ offsetT value;
+
/* Stuff the immediate value in now, if we can. */
if (imm_expr.X_op == O_constant
&& *imm_reloc > BFD_RELOC_UNUSED
- && *imm_reloc != BFD_RELOC_MIPS16_GOT16
- && *imm_reloc != BFD_RELOC_MIPS16_CALL16
- && insn->pinfo != INSN_MACRO)
+ && insn->pinfo != INSN_MACRO
+ && calculate_reloc (*offset_reloc,
+ imm_expr.X_add_number, &value))
{
- valueT tmp;
-
- switch (*offset_reloc)
- {
- case BFD_RELOC_MIPS16_HI16_S:
- tmp = (imm_expr.X_add_number + 0x8000) >> 16;
- break;
-
- case BFD_RELOC_MIPS16_HI16:
- tmp = imm_expr.X_add_number >> 16;
- break;
-
- case BFD_RELOC_MIPS16_LO16:
- tmp = ((imm_expr.X_add_number + 0x8000) & 0xffff)
- - 0x8000;
- break;
-
- case BFD_RELOC_UNUSED:
- tmp = imm_expr.X_add_number;
- break;
-
- default:
- internalError ();
- }
-
mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
- *offset_reloc, tmp, forced_insn_length,
+ *offset_reloc, value, forced_insn_length,
&ip->insn_opcode);
imm_expr.X_op = O_absent;
*imm_reloc = BFD_RELOC_UNUSED;
break;
default:
- internalError ();
+ abort ();
}
if (regno == ILLEGAL_REG)
MIPS16_INSERT_OPERAND (REG32R, *ip, regno);
break;
default:
- internalError ();
+ abort ();
}
lastregno = regno;
continue;
default:
- internalError ();
+ abort ();
}
break;
}
#define MIPS16_NUM_IMMED \
(sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+/* Marshal immediate value VAL for an extended MIPS16 instruction.
+ NBITS is the number of significant bits in VAL. */
+
+static unsigned long
+mips16_immed_extend (offsetT val, unsigned int nbits)
+{
+ int extval;
+ if (nbits == 16)
+ {
+ extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+ val &= 0x1f;
+ }
+ else if (nbits == 15)
+ {
+ extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+ val &= 0xf;
+ }
+ else
+ {
+ extval = ((val & 0x1f) << 6) | (val & 0x20);
+ val = 0;
+ }
+ return (extval << 16) | val;
+}
+
/* Install immediate value VAL into MIPS16 instruction *INSN,
extending it if necessary. The instruction in *INSN may
already be extended.
else
{
long minext, maxext;
- int extval;
if (reloc == BFD_RELOC_UNUSED)
{
_("operand value out of range for instruction"));
}
- if (op->extbits == 16)
- {
- extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
- val &= 0x1f;
- }
- else if (op->extbits == 15)
- {
- extval = ((val >> 11) & 0xf) | (val & 0x7f0);
- val &= 0xf;
- }
- else
- {
- extval = ((val & 0x1f) << 6) | (val & 0x20);
- val = 0;
- }
-
- *insn |= (extval << 16) | val;
+ *insn |= mips16_immed_extend (val, op->extbits);
}
}
\f
/* Return the address of the delay slot. */
return addr + 4;
+ case BFD_RELOC_32_PCREL:
+ return addr;
+
default:
/* We have no relocation type for PC relative MIPS16 instructions. */
if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
gas_assert (reloc_needs_lo_p (l->fixp->fx_r_type));
/* If a GOT16 relocation turns out to be against a global symbol,
- there isn't supposed to be a matching LO. */
+ there isn't supposed to be a matching LO. Ignore %gots against
+ constants; we'll report an error for those later. */
if (got16_reloc_p (l->fixp->fx_r_type)
- && !pic_need_relax (l->fixp->fx_addsy, l->seg))
+ && !(l->fixp->fx_addsy
+ && pic_need_relax (l->fixp->fx_addsy, l->seg)))
continue;
/* Check quickly whether the next fixup happens to be a matching %lo. */
}
}
-/* We may have combined relocations without symbols in the N32/N64 ABI.
- We have to prevent gas from dropping them. */
-
int
mips_force_relocation (fixS *fixp)
{
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
return 1;
- if (HAVE_NEWABI
- && S_GET_SEGMENT (fixp->fx_addsy) == bfd_abs_section_ptr
- && (fixp->fx_r_type == BFD_RELOC_MIPS_SUB
- || hi16_reloc_p (fixp->fx_r_type)
- || lo16_reloc_p (fixp->fx_r_type)))
- return 1;
-
return 0;
}
+/* Read the instruction associated with RELOC from BUF. */
+
+static unsigned int
+read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc)
+{
+ if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+ return read_compressed_insn (buf, 4);
+ else
+ return read_insn (buf);
+}
+
+/* Write instruction INSN to BUF, given that it has been relocated
+ by RELOC. */
+
+static void
+write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
+ unsigned long insn)
+{
+ if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+ write_compressed_insn (buf, insn, 4);
+ else
+ write_insn (buf, insn);
+}
+
/* Apply a fixup to the object file. */
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
{
char *buf;
- long insn;
+ unsigned long insn;
reloc_howto_type *howto;
/* We ignore generic BFD relocations we don't know about. */
gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
|| fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
- || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1);
+ || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
+ || fixP->fx_r_type == BFD_RELOC_32_PCREL);
/* Don't treat parts of a composite relocation as done. There are two
reasons for this:
case BFD_RELOC_MIPS16_TLS_GOTTPREL:
case BFD_RELOC_MIPS16_TLS_TPREL_HI16:
case BFD_RELOC_MIPS16_TLS_TPREL_LO16:
+ if (!fixP->fx_addsy)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("TLS relocation against a constant"));
+ break;
+ }
S_SET_THREAD_LOCAL (fixP->fx_addsy);
/* fall through */
case BFD_RELOC_MIPS_JALR:
case BFD_RELOC_HI16:
case BFD_RELOC_HI16_S:
+ case BFD_RELOC_LO16:
case BFD_RELOC_GPREL16:
case BFD_RELOC_MIPS_LITERAL:
case BFD_RELOC_MIPS_CALL16:
case BFD_RELOC_MIPS16_CALL16:
case BFD_RELOC_MIPS16_HI16:
case BFD_RELOC_MIPS16_HI16_S:
+ case BFD_RELOC_MIPS16_LO16:
case BFD_RELOC_MIPS16_JMP:
case BFD_RELOC_MICROMIPS_JMP:
case BFD_RELOC_MICROMIPS_GOT_DISP:
case BFD_RELOC_MICROMIPS_JALR:
case BFD_RELOC_MICROMIPS_HI16:
case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MICROMIPS_LO16:
case BFD_RELOC_MICROMIPS_GPREL16:
case BFD_RELOC_MICROMIPS_LITERAL:
case BFD_RELOC_MICROMIPS_CALL16:
case BFD_RELOC_MICROMIPS_GOT_LO16:
case BFD_RELOC_MICROMIPS_CALL_HI16:
case BFD_RELOC_MICROMIPS_CALL_LO16:
- /* Nothing needed to do. The value comes from the reloc entry. */
+ if (fixP->fx_done)
+ {
+ offsetT value;
+
+ if (calculate_reloc (fixP->fx_r_type, *valP, &value))
+ {
+ insn = read_reloc_insn (buf, fixP->fx_r_type);
+ if (mips16_reloc_p (fixP->fx_r_type))
+ insn |= mips16_immed_extend (value, 16);
+ else
+ insn |= (value & 0xffff);
+ write_reloc_insn (buf, fixP->fx_r_type, insn);
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Unsupported constant in relocation"));
+ }
break;
case BFD_RELOC_64:
case BFD_RELOC_RVA:
case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
case BFD_RELOC_16:
/* If we are deleting this reloc entry, we must fill in the
value now. This can happen if we have a .word which is not
md_number_to_chars (buf, *valP, fixP->fx_size);
break;
- case BFD_RELOC_LO16:
- case BFD_RELOC_MIPS16_LO16:
- case BFD_RELOC_MICROMIPS_LO16:
- /* FIXME: Now that embedded-PIC is gone, some of this code/comment
- may be safe to remove, but if so it's not obvious. */
- /* When handling an embedded PIC switch statement, we can wind
- up deleting a LO16 reloc. See the 'o' case in mips_ip. */
- if (fixP->fx_done)
- {
- if (*valP + 0x8000 > 0xffff)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("relocation overflow"));
- /* 32-bit microMIPS instructions are divided into two halfwords.
- Relocations always refer to the second halfword, regardless
- of endianness. */
- if (target_big_endian || fixP->fx_r_type == BFD_RELOC_MICROMIPS_LO16)
- buf += 2;
- md_number_to_chars (buf, *valP, 2);
- }
- break;
-
case BFD_RELOC_16_PCREL_S2:
if ((*valP & 0x3) != 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
break;
default:
- internalError ();
+ abort ();
}
/* Remember value for tc_gen_reloc. */
case ISA_MIPS64:
case ISA_MIPS64R2:
mips_opts.gp32 = 0;
+ if (mips_opts.arch == CPU_R5900)
+ {
+ mips_opts.fp32 = 1;
+ }
+ else
+ {
mips_opts.fp32 = 0;
+ }
break;
default:
as_bad (_("unknown ISA level %s"), name + 4);
/* In ELF, this symbol is implicitly an STT_OBJECT symbol. */
symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
macro_build_lui (&ex, mips_gp_register);
macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
mips_gp_register, reg);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
SKIP_WHITESPACE ();
expression (&ex_sym);
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
if (mips_cpreturn_register == -1)
{
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
ex.X_op_symbol = NULL;
ex.X_add_number = mips_cprestore_offset;
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register,
SP, HAVE_64BIT_ADDRESSES);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
return;
}
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
macro_start ();
if (mips_cpreturn_register == -1)
{
mips_cpreturn_register, 0);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
return;
}
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
/* Add $gp to the register named as an argument. */
macro_start ();
reg = tc_get_register (0);
macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
macro_end ();
+ mips_assembling_insn = FALSE;
demand_empty_rest_of_line ();
}
demand_empty_rest_of_line ();
}
-/* Handle a .stabn directive. We need these in order to mark a label
- as being a mips16 text label correctly. Sometimes the compiler
- will emit a label, followed by a .stabn, and then switch sections.
- If the label and .stabn are in mips16 mode, then the label is
- really a mips16 text label. */
+/* Handle a .stab[snd] directive. Ideally these directives would be
+ implemented in a transparent way, so that removing them would not
+ have any effect on the generated instructions. However, s_stab
+ internally changes the section, so in practice we need to decide
+ now whether the preceding label marks compressed code. We do not
+ support changing the compression mode of a label after a .stab*
+ directive, such as in:
+
+ foo:
+ .stabs ...
+ .set mips16
+
+ so the current mode wins. */
static void
s_mips_stab (int type)
{
- if (type == 'n')
- mips_mark_labels ();
-
+ mips_mark_labels ();
s_stab (type);
}
return 0;
/* 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. */
+ Likewise an in-range offset of limited 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 || jalr_reloc_p (fixp->fx_r_type)))
+ && (limited_pcrel_reloc_p (fixp->fx_r_type)
+ || jalr_reloc_p (fixp->fx_r_type)))
return 0;
#ifdef OBJ_ELF
gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
|| fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
- || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1);
+ || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_32_PCREL);
/* At this point, fx_addnumber is "symbol offset - pcrel address".
Relocations want only the symbol offset. */
user_length = 0;
mips16_immed (fragp->fr_file, fragp->fr_line, type,
- BFD_RELOC_NONE, val, user_length, &insn);
+ BFD_RELOC_UNUSED, val, user_length, &insn);
length = (ext ? 4 : 2);
gas_assert (mips16_opcode_length (insn) == length);
move it. This also bumps the value of the symbol by 1 in compressed
code. */
-void
+static void
mips_record_label (symbolS *sym)
{
segment_info_type *si = seg_info (now_seg);
dwarf2_emit_label (sym);
#endif
}
+
+/* This function is called by tc_new_dot_label whenever a new dot symbol
+ is defined. */
+
+void
+mips_add_dot_label (symbolS *sym)
+{
+ mips_record_label (sym);
+ if (mips_assembling_insn && HAVE_CODE_COMPRESSION)
+ mips_compressed_mark_label (sym);
+}
\f
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
{ "r4600", 0, ISA_MIPS3, CPU_R4600 },
{ "orion", 0, ISA_MIPS3, CPU_R4600 },
{ "r4650", 0, ISA_MIPS3, CPU_R4650 },
+ { "r5900", 0, ISA_MIPS3, CPU_R5900 },
/* ST Microelectronics Loongson 2E and 2F cores */
{ "loongson2e", 0, ISA_MIPS3, CPU_LOONGSON_2E },
{ "loongson2f", 0, ISA_MIPS3, CPU_LOONGSON_2F },