/* Whether the 4100 MADD16 and DMADD16 are permitted. */
static int mips_4100 = -1;
+/* start-sanitize-r5900 */
+/* Whether Toshiba r5900 instructions are permitted. */
+static int mips_5900 = -1;
+/* end-sanitize-r5900 */
+
/* Whether the processor uses hardware interlocks, and thus does not
require nops to be inserted. */
static int interlocks = -1;
noreorder. */
static int prev_prev_insn_unreordered;
+/* If this is set, it points to a frag holding nop instructions which
+ were inserted before the start of a noreorder section. If those
+ nops turn out to be unnecessary, the size of the frag can be
+ decreased. */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag. */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+ prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag. */
+static int prev_nop_frag_since;
+
/* For ECOFF and ELF, relocations against symbols are done in two
parts, with a HI relocation and a LO relocation. Each relocation
has only 16 bits of space to store an addend. This means that in
expressionS * p,
bfd_reloc_code_real_type r,
boolean));
-static void mips_no_prev_insn PARAMS ((void));
+static void mips_no_prev_insn PARAMS ((int));
static void mips_emit_delays PARAMS ((boolean));
#ifdef USE_STDARG
static void macro_build PARAMS ((char *place, int *counter, expressionS * ep,
static void s_cprestore PARAMS ((int));
static void s_gpword PARAMS ((int));
static void s_cpadd PARAMS ((int));
+static void s_insn PARAMS ((int));
static void md_obj_begin PARAMS ((void));
static void md_obj_end PARAMS ((void));
static long get_number PARAMS ((void));
{"cprestore", s_cprestore, 0},
{"gpword", s_gpword, 0},
{"cpadd", s_cpadd, 0},
+ {"insn", s_insn, 0},
/* Relatively generic pseudo-ops that happen to be used on MIPS
chips. */
if (mips_cpu == -1)
mips_cpu = 5000;
}
+ /* start-sanitize-r5900 */
+ else if (strcmp (cpu, "r5900") == 0
+ || strcmp (cpu, "mips64vr5900") == 0
+ || strcmp (cpu, "mips64vr5900el") == 0)
+ {
+ mips_isa = 3;
+ if (mips_cpu == -1)
+ mips_cpu = 5900;
+ if (mips_5900 == -1)
+ mips_5900 = 1;
+ }
+ /* end-sanitize-r5900 */
else if (strcmp (cpu, "r8000") == 0
|| strcmp (cpu, "mips4") == 0)
{
if (mips_4100 < 0)
mips_4100 = 0;
+ /* start-sanitize-r5900 */
+ if (mips_5900 < 0)
+ mips_5900 = 0;
+ /* end-sanitize-r5900 */
+
if (mips_4010 || mips_4100 || mips_cpu == 4300)
interlocks = 1;
else
&& strcmp (mips16_opcodes[i].name, name) == 0);
}
- mips_no_prev_insn ();
+ /* We add all the general register names to the symbol table. This
+ helps us detect invalid uses of them. */
+ for (i = 0; i < 32; i++)
+ {
+ char buf[5];
+
+ sprintf (buf, "$%d", i);
+ symbol_table_insert (symbol_new (buf, reg_section, i,
+ &zero_address_frag));
+ }
+ symbol_table_insert (symbol_new ("$fp", reg_section, FP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$sp", reg_section, SP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$gp", reg_section, GP,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$at", reg_section, AT,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$kt0", reg_section, KT0,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$kt1", reg_section, KT1,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("$pc", reg_section, -1,
+ &zero_address_frag));
+
+ mips_no_prev_insn (false);
mips_gprmask = 0;
mips_cprmask[0] = 0;
prev_pinfo = prev_insn.insn_mo->pinfo;
pinfo = ip->insn_mo->pinfo;
- if (place == NULL && ! mips_noreorder)
+ if (place == NULL && (! mips_noreorder || prev_nop_frag != NULL))
{
+ int prev_prev_nop;
+
/* If the previous insn required any delay slots, see if we need
to insert a NOP or two. There are eight kinds of possible
hazards, of which an instruction can have at most one type.
nops += 2;
}
+ /* If the previous instruction was in a noreorder section, then
+ we don't want to insert the nop after all. */
+ if (prev_insn_unreordered)
+ nops = 0;
+
/* There are two cases which require two intervening
instructions: 1) setting the condition codes using a move to
coprocessor instruction which requires a general coprocessor
which have interlocks). If we are not already emitting a NOP
instruction, we must check for these cases compared to the
instruction previous to the previous instruction. */
- if (nops == 0
- && ((! mips16
- && mips_isa < 4
- && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
- && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
- && (pinfo & INSN_READ_COND_CODE)
- && ! cop_interlocks)
- || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
- && (pinfo & INSN_WRITE_LO)
- && ! interlocks)
- || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
- && (pinfo & INSN_WRITE_HI)
- && ! interlocks)))
+ if ((! mips16
+ && mips_isa < 4
+ && (prev_prev_insn.insn_mo->pinfo & INSN_COPROC_MOVE_DELAY)
+ && (prev_prev_insn.insn_mo->pinfo & INSN_WRITE_COND_CODE)
+ && (pinfo & INSN_READ_COND_CODE)
+ && ! cop_interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_LO)
+ && (pinfo & INSN_WRITE_LO)
+ && ! interlocks)
+ || ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
+ && (pinfo & INSN_WRITE_HI)
+ && ! interlocks))
+ prev_prev_nop = 1;
+ else
+ prev_prev_nop = 0;
+
+ if (prev_prev_insn_unreordered)
+ prev_prev_nop = 0;
+
+ if (prev_prev_nop && nops == 0)
++nops;
/* If we are being given a nop instruction, don't bother with
one of the nops we would otherwise output. This will only
happen when a nop instruction is used with mips_optimize set
to 0. */
- if (nops > 0 && ip->insn_opcode == (mips16 ? 0x6500 : 0))
+ if (nops > 0
+ && ! mips_noreorder
+ && ip->insn_opcode == (mips16 ? 0x6500 : 0))
--nops;
/* Now emit the right number of NOP instructions. */
- if (nops > 0)
+ if (nops > 0 && ! mips_noreorder)
{
fragS *old_frag;
unsigned long old_frag_offset;
ecoff_fix_loc (old_frag, old_frag_offset);
#endif
}
+ else if (prev_nop_frag != NULL)
+ {
+ /* We have a frag holding nops we may be able to remove. If
+ we don't need any nops, we can decrease the size of
+ prev_nop_frag by the size of one instruction. If we do
+ need some nops, we count them in prev_nops_required. */
+ if (prev_nop_frag_since == 0)
+ {
+ if (nops == 0)
+ {
+ prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ prev_nop_frag_required += nops;
+ }
+ else
+ {
+ if (prev_prev_nop == 0)
+ {
+ prev_nop_frag->fr_fix -= mips16 ? 2 : 4;
+ --prev_nop_frag_holds;
+ }
+ else
+ ++prev_nop_frag_required;
+ }
+
+ if (prev_nop_frag_holds <= prev_nop_frag_required)
+ prev_nop_frag = NULL;
+
+ ++prev_nop_frag_since;
+
+ /* Sanity check: by the time we reach the second instruction
+ after prev_nop_frag, we should have used up all the nops
+ one way or another. */
+ assert (prev_nop_frag_since <= 1 || prev_nop_frag == NULL);
+ }
}
-
+
if (reloc_type > BFD_RELOC_UNUSED)
{
/* We need to set up a variant frag. */
f = frag_more (2);
}
else
- f = frag_more (4);
+ {
+ if (mips16
+ && mips_noreorder
+ && (prev_pinfo & INSN_UNCOND_BRANCH_DELAY) != 0)
+ as_warn ("extended instruction in delay slot");
+
+ f = frag_more (4);
+ }
+
fixp = NULL;
if (address_expr != NULL && reloc_type < BFD_RELOC_UNUSED)
{
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)
}
}
- if (! mips16 || reloc_type == BFD_RELOC_MIPS16_JMP)
+ if (! mips16)
md_number_to_chars (f, ip->insn_opcode, 4);
+ else if (reloc_type == BFD_RELOC_MIPS16_JMP)
+ {
+ md_number_to_chars (f, ip->insn_opcode >> 16, 2);
+ md_number_to_chars (f + 2, ip->insn_opcode & 0xffff, 2);
+ }
else
{
if (ip->use_extend)
fixp->fx_where = prev_insn_where;
}
}
- else if (reloc_type > BFD_RELOC_UNUSED)
- {
- char *prev_f;
- char temp[2];
-
- /* We are in mips16 mode, and we have just created a
- variant frag. We need to extract the old
- instruction from the end of the previous frag,
- and add it to a new frag. */
- prev_f = prev_insn_frag->fr_literal + prev_insn_where;
- memcpy (temp, prev_f, 2);
- prev_insn_frag->fr_fix -= 2;
- if (prev_insn_frag->fr_type == rs_machine_dependent)
- {
- assert (prev_insn_where == prev_insn_frag->fr_fix);
- memcpy (prev_f, prev_f + 2, 2);
- }
- memcpy (frag_more (2), temp, 2);
- }
else
{
char *prev_f;
memcpy (temp, prev_f, 2);
memcpy (prev_f, f, 2);
if (reloc_type != BFD_RELOC_MIPS16_JMP)
- memcpy (f, temp, 2);
+ {
+ assert (reloc_type == BFD_RELOC_UNUSED);
+ memcpy (f, temp, 2);
+ }
else
{
memcpy (f, f + 2, 2);
/* We need to record a bit of information even when we are not
reordering, in order to determine the base address for mips16
PC relative relocs. */
+ prev_prev_insn = prev_insn;
prev_insn = *ip;
prev_insn_reloc_type = reloc_type;
+ prev_prev_insn_unreordered = prev_insn_unreordered;
+ prev_insn_unreordered = 1;
}
/* We just output an insn, so the next one doesn't have a label. */
}
/* This function forgets that there was any previous instruction or
- label. */
+ label. If PRESERVE is non-zero, it remembers enough information to
+ know whether nops are needed before a noreorder section. */
static void
-mips_no_prev_insn ()
+mips_no_prev_insn (preserve)
+ int preserve;
{
- prev_insn.insn_mo = &dummy_opcode;
- prev_prev_insn.insn_mo = &dummy_opcode;
+ if (! preserve)
+ {
+ prev_insn.insn_mo = &dummy_opcode;
+ prev_prev_insn.insn_mo = &dummy_opcode;
+ prev_nop_frag = NULL;
+ prev_nop_frag_holds = 0;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
prev_insn_valid = 0;
prev_insn_is_delay_slot = 0;
prev_insn_unreordered = 0;
{
if (! mips_noreorder)
{
- int nop;
+ int nops;
- nop = 0;
+ nops = 0;
if ((! mips16
&& mips_isa < 4
&& (! cop_interlocks
& (INSN_LOAD_MEMORY_DELAY
| INSN_COPROC_MEMORY_DELAY))))
{
- nop = 1;
+ ++nops;
if ((! mips16
&& mips_isa < 4
&& (! cop_interlocks
|| (! interlocks
&& ((prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_insn.insn_mo->pinfo & INSN_READ_LO))))
- emit_nop ();
+ ++nops;
+
+ if (prev_insn_unreordered)
+ nops = 0;
}
else if ((! mips16
&& mips_isa < 4
|| (! interlocks
&& ((prev_prev_insn.insn_mo->pinfo & INSN_READ_HI)
|| (prev_prev_insn.insn_mo->pinfo & INSN_READ_LO))))
- nop = 1;
- if (nop)
+ {
+ if (! prev_prev_insn_unreordered)
+ ++nops;
+ }
+
+ if (nops > 0)
{
struct insn_label_list *l;
- emit_nop ();
+ if (insns)
+ {
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (mips16 ? nops * 2 : nops * 4);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+
+ for (; nops > 0; --nops)
+ emit_nop ();
+
+ if (insns)
+ {
+ /* Move on to a new frag, so that it is safe to simply
+ decrease the size of prev_nop_frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+
for (l = insn_labels; l != NULL; l = l->next)
{
assert (S_GET_SEGMENT (l->label) == now_seg);
}
}
- mips_no_prev_insn ();
+ mips_no_prev_insn (insns);
}
/* Build an instruction created by a macro expansion. This is passed
|| ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4010
&& ! mips_4010)
|| ((insn.insn_mo->pinfo & INSN_ISA) == INSN_4100
- && ! mips_4100))
+ && ! mips_4100)
+ /* start-sanitize-r5900 */
+ || ((insn.insn_mo->pinfo & INSN_ISA) == INSN_5900
+ && ! mips_5900)
+ /* end-sanitize-r5900 */
+ )
{
++insn.insn_mo;
assert (insn.insn_mo->name);
insn_isa = 1;
if (insn_isa > mips_isa
- || ((insn->pinfo & INSN_ISA) == INSN_4650
- && ! mips_4650)
- || ((insn->pinfo & INSN_ISA) == INSN_4010
- && ! mips_4010)
- || ((insn->pinfo & INSN_ISA) == INSN_4100
- && ! mips_4100))
+ || (insn->pinfo != INSN_MACRO
+ && (((insn->pinfo & INSN_ISA) == INSN_4650
+ && ! mips_4650)
+ || ((insn->pinfo & INSN_ISA) == INSN_4010
+ && ! mips_4010)
+ || ((insn->pinfo & INSN_ISA) == INSN_4100
+ && ! mips_4100)
+ /* start-sanitize-r5900 */
+ || ((insn->pinfo & INSN_ISA) == INSN_5900
+ && ! mips_5900)
+ /* end-sanitize-r5900 */
+ )))
{
if (insn + 1 < &mips_opcodes[NUMOPCODES]
&& strcmp (insn->name, insn[1].name) == 0)
case 'U':
case 'k':
case 'K':
- if (s[0] == '$' && isdigit (s[1]))
+ if (s[0] == '%'
+ && strncmp (s + 1, "gprel(", sizeof "gprel(" - 1) == 0)
{
- /* Looks like a register name. */
- break;
+ /* This is %gprel(SYMBOL). We need to read SYMBOL,
+ and generate the appropriate reloc. If the text
+ inside %gprel is not a symbol name with an
+ optional offset, then we generate a normal reloc
+ and will probably fail later. */
+ my_getExpression (&imm_expr, s + sizeof "%gprel" - 1);
+ if (imm_expr.X_op == O_symbol)
+ {
+ mips16_ext = true;
+ imm_reloc = BFD_RELOC_MIPS16_GPREL;
+ s = expr_end;
+ ip->use_extend = true;
+ ip->extend = 0;
+ continue;
+ }
+ }
+ else
+ {
+ /* Just pick up a normal expression. */
+ my_getExpression (&imm_expr, s);
}
- if (s[0] == '('
- && args[1] == '('
- && s[1] == '$')
+ if (imm_expr.X_op == O_register)
{
- /* It looks like the expression was omitted before a
- register indirection, which means that the
- expression is implicitly zero. We still set up
- imm_expr, so that we handle explicit extensions
- correctly. */
- imm_expr.X_op = O_constant;
- imm_expr.X_add_number = 0;
- imm_reloc = (int) BFD_RELOC_UNUSED + c;
- continue;
+ /* What we thought was an expression turned out to
+ be a register. */
+
+ if (s[0] == '(' && args[1] == '(')
+ {
+ /* It looks like the expression was omitted
+ before a register indirection, which means
+ that the expression is implicitly zero. We
+ still set up imm_expr, so that we handle
+ explicit extensions correctly. */
+ imm_expr.X_op = O_constant;
+ imm_expr.X_add_number = 0;
+ imm_reloc = (int) BFD_RELOC_UNUSED + c;
+ continue;
+ }
+
+ break;
}
- my_getExpression (&imm_expr, s);
/* We need to relax this instruction. */
imm_reloc = (int) BFD_RELOC_UNUSED + c;
s = expr_end;
/* We use offset_reloc rather than imm_reloc for the PC
relative operands. This lets macros with both
immediate and address operands work correctly. */
- if (s[0] == '$' && isdigit (s[1]))
- {
- /* Looks like a register name. */
- break;
- }
my_getExpression (&offset_expr, s);
+
+ if (offset_expr.X_op == O_register)
+ break;
+
/* We need to relax this instruction. */
offset_reloc = (int) BFD_RELOC_UNUSED + c;
s = expr_end;
mask = 7 << 3;
while (*s != '\0')
{
- int reg1, reg2;
+ int freg, reg1, reg2;
while (*s == ' ' || *s == ',')
++s;
break;
}
++s;
+ if (*s != 'f')
+ freg = 0;
+ else
+ {
+ freg = 1;
+ ++s;
+ }
reg1 = 0;
while (isdigit (*s))
{
if (*s != '$')
break;
++s;
+ if (freg)
+ {
+ if (*s == 'f')
+ ++s;
+ else
+ {
+ as_bad ("invalid register list");
+ break;
+ }
+ }
reg2 = 0;
while (isdigit (*s))
{
++s;
}
}
- if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
+ if (freg && reg1 == 0 && reg2 == 0 && c == 'L')
+ {
+ mask &= ~ (7 << 3);
+ mask |= 5 << 3;
+ }
+ else if (freg && reg1 == 0 && reg2 == 1 && c == 'L')
+ {
+ mask &= ~ (7 << 3);
+ mask |= 6 << 3;
+ }
+ else if (reg1 == 4 && reg2 >= 4 && reg2 <= 7 && c != 'L')
mask |= (reg2 - 3) << 3;
else if (reg1 == 16 && reg2 >= 16 && reg2 <= 17)
mask |= (reg2 - 15) << 1;
else if (reg1 == 31 && reg2 == 31)
mask |= 1;
else
- as_bad ("invalid register list");
+ {
+ as_bad ("invalid register list");
+ break;
+ }
}
+ /* The mask is filled in in the opcode table for the
+ benefit of the disassembler. We remove it before
+ applying the actual mask. */
+ ip->insn_opcode &= ~ ((7 << 3) << MIPS16OP_SH_IMM6);
ip->insn_opcode |= mask << MIPS16OP_SH_IMM6;
}
continue;
+ case 'e': /* extend code */
+ my_getExpression (&imm_expr, s);
+ check_absolute_expr (ip, &imm_expr);
+ if ((unsigned long) imm_expr.X_add_number > 0x7ff)
+ {
+ as_warn ("Invalid value for `%s' (%lu)",
+ ip->insn_mo->name,
+ (unsigned long) imm_expr.X_add_number);
+ imm_expr.X_add_number &= 0x7ff;
+ }
+ ip->insn_opcode |= imm_expr.X_add_number;
+ imm_expr.X_op = O_absent;
+ s = expr_end;
+ continue;
+
default:
internalError ();
}
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 (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
+ && ep->X_add_symbol->sy_frag == frag_now
+ && ep->X_add_symbol->sy_value.X_op == O_constant
+ && ep->X_add_symbol->sy_value.X_add_number == frag_now_fix ())
+ ++ep->X_add_symbol->sy_value.X_add_number;
}
/* Turn a string in input_line_pointer into a floating point constant
{"mips16", no_argument, NULL, OPTION_MIPS16},
#define OPTION_NO_MIPS16 (OPTION_MD_BASE + 23)
{"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+ /* start-sanitize-5900 */
+#define OPTION_M5900 (OPTION_MD_BASE + 24)
+ {"m5900", no_argument, NULL, OPTION_M5900},
+#define OPTION_NO_M5900 (OPTION_MD_BASE + 25)
+ {"no-m5900", no_argument, NULL, OPTION_NO_M5900},
+ /* end-sanitize-5900 */
#define OPTION_CALL_SHARED (OPTION_MD_BASE + 7)
#define OPTION_NON_SHARED (OPTION_MD_BASE + 8)
optimizations which limit full symbolic debugging. We take
that to be equivalent to -O0. */
if (mips_debug == 2)
- mips_optimize = 0;
+ mips_optimize = 1;
break;
case OPTION_MIPS1:
|| strcmp (p, "5k") == 0
|| strcmp (p, "5K") == 0)
mips_cpu = 5000;
+ /* start-sanitize-r5900 */
+ else if (strcmp (p, "5900") == 0)
+ mips_cpu = 5900;
+ /* end-sanitize-r5900 */
break;
case '6':
mips_4100 = 0;
break;
+ /* start-sanitize-r5900 */
+ case OPTION_M5900:
+ mips_5900 = 1;
+ break;
+
+ case OPTION_NO_M5900:
+ mips_5900 = 0;
+ break;
+ /* end-sanitize-r5900 */
+
case OPTION_MIPS16:
mips16 = 1;
- mips_no_prev_insn ();
+ mips_no_prev_insn (false);
break;
case OPTION_NO_MIPS16:
mips16 = 0;
- mips_no_prev_insn ();
+ mips_no_prev_insn (false);
break;
case OPTION_MEMBEDDED_PIC:
|| fixP->fx_r_type == BFD_RELOC_64);
value = *valueP;
+
+ /* If we aren't adjusting this fixup to be against the section
+ symbol, we need to adjust the value. */
+#ifdef S_GET_OTHER
+ if (fixP->fx_addsy != NULL
+ && OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && S_GET_OTHER (fixP->fx_addsy) == STO_MIPS16)
+ {
+ value -= S_GET_VALUE (fixP->fx_addsy);
+ if (value != 0 && ! fixP->fx_pcrel)
+ {
+ /* In this case, the bfd_install_relocation routine will
+ incorrectly add the symbol value back in. We just want
+ the addend to appear in the object file. */
+ value -= S_GET_VALUE (fixP->fx_addsy);
+ }
+ }
+#endif
+
fixP->fx_addnumber = value; /* Remember value for tc_gen_reloc */
if (fixP->fx_addsy == NULL && ! fixP->fx_pcrel)
case BFD_RELOC_MIPS_GOT_LO16:
case BFD_RELOC_MIPS_CALL_HI16:
case BFD_RELOC_MIPS_CALL_LO16:
+ case BFD_RELOC_MIPS16_GPREL:
if (fixP->fx_pcrel)
as_bad_where (fixP->fx_file, fixP->fx_line,
"Invalid PC relative reloc");
* the current segment).
*/
if ((value & 0x3) != 0)
- as_warn_where (fixP->fx_file, fixP->fx_line,
- "Branch to odd address (%lx)", value);
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "Branch to odd address (%lx)", value);
value >>= 2;
/* update old instruction data */
if (strcmp (name, "reorder") == 0)
{
- if (mips_noreorder)
+ if (mips_noreorder && prev_nop_frag != NULL)
{
- prev_insn_unreordered = 1;
- prev_prev_insn_unreordered = 1;
+ /* If we still have pending nops, we can discard them. The
+ usual nop handling will insert any that are still
+ needed. */
+ prev_nop_frag->fr_fix -= prev_nop_frag_holds * (mips16 ? 2 : 4);
+ prev_nop_frag = NULL;
}
mips_noreorder = 0;
}
demand_empty_rest_of_line ();
}
+/* Handle the .insn pseudo-op. This marks instruction labels in
+ mips16 mode. 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 in the adjust_symtab
+ routine, while leaving them marked. This is convenient for the
+ debugger and the disassembler. The linker knows to make them odd
+ again. */
+
+static void
+s_insn (ignore)
+ int ignore;
+{
+ if (mips16)
+ {
+ struct insn_label_list *l;
+
+ for (l = insn_labels; l != NULL; l = l->next)
+ {
+#ifdef S_SET_OTHER
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ S_SET_OTHER (l->label, STO_MIPS16);
+#endif
+ ++l->label->sy_value.X_add_number;
+ }
+
+ mips_clear_insn_labels ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+
/* Parse a register string into a number. Called from the ECOFF code
to parse .frame. The argument is non-zero if this is the frame
register, so that we can record it in mips_frame_reg. */
/* The base address rules are complicated. The base address of
a branch is the following instruction. The base address of a
PC relative load or add is the instruction itself, but if it
- is extended add 2, and if it is in a delay slot (in which
- case it can not be extended) use the address of the
- instruction whose delay slot it is in. */
+ is in a delay slot (in which case it can not be extended) use
+ the address of the instruction whose delay slot it is in. */
if (type == 'p' || type == 'q')
{
addr += 2;
+
+ /* If we are currently assuming that this frag should be
+ extended, then, the current address is two bytes
+ higher. */
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ addr += 2;
+
/* Ignore the low bit in the target, since it will be set
for a text label. */
if ((val & 1) != 0)
else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
addr -= 2;
- /* If we are currently assuming that this frag should be
- extended, then the current address is two bytes higher. */
- if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
- addr += 2;
-
val -= addr & ~ ((1 << op->shift) - 1);
/* Branch offsets have an implicit 0 in the lowest bit. */
return RELAX_NEW (fragp->fr_subtype) - RELAX_OLD (fragp->fr_subtype);
}
+/* This is called to see whether a reloc against a defined symbol
+ should be converted into a reloc against a section. Don't adjust
+ MIPS16 jump relocations, so we don't have to worry about the format
+ of the offset in the .o file. Don't adjust relocations against
+ mips16 symbols, so that the linker can find them if it needs to set
+ up a stub. */
+
+int
+mips_fix_adjustable (fixp)
+ fixS *fixp;
+{
+ if (fixp->fx_r_type == BFD_RELOC_MIPS16_JMP)
+ return 0;
+ if (fixp->fx_addsy == NULL)
+ return 1;
+#ifdef S_GET_OTHER
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && S_GET_OTHER (fixp->fx_addsy) == STO_MIPS16)
+ return 0;
+#endif
+ return 1;
+}
+
/* Translate internal representation of relocation info to BFD target
format. */
if (type == 'p' || type == 'q')
{
addr += 2;
+ if (ext)
+ addr += 2;
/* Ignore the low bit in the target, since it will be
set for a text label. */
if ((val & 1) != 0)
else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
addr -= 2;
- if (ext)
- addr += 2;
addr &= ~ (addressT) ((1 << op->shift) - 1);
val -= addr;
record_alignment (asec, op->shift);
}
+ if (ext
+ && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+ || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ "extended instruction in delay slot");
+
buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix);
if (target_big_endian)
else
insn = bfd_getl16 (buf);
- mips16_immed (fragp->fr_file, fragp->fr_line, type, val, false, small,
- ext, &insn, &use_extend, &extend);
+ mips16_immed (fragp->fr_file, fragp->fr_line, type, val,
+ RELAX_MIPS16_USER_EXT (fragp->fr_subtype),
+ small, ext, &insn, &use_extend, &extend);
if (use_extend)
{