code found in the opcode file for this relocation, the register
selected as the assembler temporary, whether in the 32-bit
instruction mode, whether the branch is unconditional, whether it is
- compact, whether it stores the link address implicitly in $ra,
- whether relaxation of out-of-range 32-bit branches to a sequence of
+ compact, whether there is no delay-slot instruction available to fill
+ in, whether it stores the link address implicitly in $ra, whether
+ relaxation of out-of-range 32-bit branches to a sequence of
instructions is enabled, and whether the displacement of a branch is
too large to fit as an immediate argument of a 16-bit and a 32-bit
branch, respectively. */
#define RELAX_MICROMIPS_ENCODE(type, at, insn32, \
- uncond, compact, link, \
+ uncond, compact, link, nods, \
relax32, toofar16, toofar32) \
(0x40000000 \
| ((type) & 0xff) \
| ((uncond) ? 0x4000 : 0) \
| ((compact) ? 0x8000 : 0) \
| ((link) ? 0x10000 : 0) \
- | ((relax32) ? 0x20000 : 0) \
- | ((toofar16) ? 0x40000 : 0) \
- | ((toofar32) ? 0x80000 : 0))
+ | ((nods) ? 0x20000 : 0) \
+ | ((relax32) ? 0x40000 : 0) \
+ | ((toofar16) ? 0x80000 : 0) \
+ | ((toofar32) ? 0x100000 : 0))
#define RELAX_MICROMIPS_P(i) (((i) & 0xc0000000) == 0x40000000)
#define RELAX_MICROMIPS_TYPE(i) ((i) & 0xff)
#define RELAX_MICROMIPS_AT(i) (((i) >> 8) & 0x1f)
#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x4000) != 0)
#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x8000) != 0)
#define RELAX_MICROMIPS_LINK(i) (((i) & 0x10000) != 0)
-#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_NODS(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x40000) != 0)
-#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x40000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x40000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x40000)
-#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x80000) != 0)
-#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x80000)
-#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x80000)
+#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x80000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x80000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x80000)
+#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x100000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x100000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x100000)
/* Sign-extend 16-bit value X. */
#define SEXT_16BIT(X) ((((X) + 0x8000) & 0xffff) - 0x8000)
static inline unsigned int
micromips_insn_length (const struct mips_opcode *mo)
{
- return (mo->mask >> 16) == 0 ? 2 : 4;
+ return mips_opcode_32bit_p (mo) ? 4 : 2;
}
/* Return the length of MIPS16 instruction OPCODE. */
int fp_s, fp_d;
unsigned int i;
- if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+ if (ISA_HAS_64BIT_REGS (isa))
for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
if ((ase & mips_ases[i].flags) == mips_ases[i].flags)
ase |= mips_ases[i].flags64;
}
/* Return TRUE if the size of the microMIPS opcode MO matches one
- explicitly requested. Always TRUE in the standard MIPS mode. */
+ explicitly requested. Always TRUE in the standard MIPS mode.
+ Use is_size_valid_16 for MIPS16 opcodes. */
static bfd_boolean
is_size_valid (const struct mips_opcode *mo)
return forced_insn_length == micromips_insn_length (mo);
}
+/* Return TRUE if the size of the MIPS16 opcode MO matches one
+ explicitly requested. */
+
+static bfd_boolean
+is_size_valid_16 (const struct mips_opcode *mo)
+{
+ if (!forced_insn_length)
+ return TRUE;
+ if (mo->pinfo == INSN_MACRO)
+ return FALSE;
+ if (forced_insn_length == 2 && mips_opcode_32bit_p (mo))
+ return FALSE;
+ if (forced_insn_length == 4 && (mo->pinfo2 & INSN2_SHORT_ONLY))
+ return FALSE;
+ return TRUE;
+}
+
/* Return TRUE if the microMIPS opcode MO is valid for the delay slot
of the preceding instruction. Always TRUE in the standard MIPS mode.
default:
if (!decode_operand)
- operand = decode_mips16_operand (*s, FALSE);
+ operand = decode_mips16_operand (*s, mips_opcode_32bit_p (opcode));
else
operand = decode_operand (s);
if (!operand && opcode->pinfo != INSN_MACRO)
validate_mips16_insn (const struct mips_opcode *opcode,
struct mips_operand_array *operands)
{
- if (opcode->args[0] == 'a' || opcode->args[0] == 'i')
- {
- /* In this case OPCODE defines the first 16 bits in a 32-bit jump
- instruction. Use TMP to describe the full instruction. */
- struct mips_opcode tmp;
+ unsigned long insn_bits = mips_opcode_32bit_p (opcode) ? 0xffffffff : 0xffff;
- tmp = *opcode;
- tmp.match <<= 16;
- tmp.mask <<= 16;
- return validate_mips_insn (&tmp, 0xffffffff, 0, operands);
- }
- return validate_mips_insn (opcode, 0xffff, 0, operands);
+ return validate_mips_insn (opcode, insn_bits, 0, operands);
}
/* The microMIPS version of validate_mips_insn. */
unsigned int last_op_int;
/* If true, match routines should assume that no later instruction
- alternative matches and should therefore be as accomodating as
+ alternative matches and should therefore be as accommodating as
possible. Match routines should not report errors if something
is only invalid for !LAX_MATCH. */
bfd_boolean lax_match;
&& gpr_read_mask (ip) != 0)
return APPEND_ADD_COMPACT;
+ if (mips_opts.micromips
+ && ((ip->insn_opcode & 0xffe0) == 0x4580
+ || (!forced_insn_length
+ && ((ip->insn_opcode & 0xfc00) == 0xcc00
+ || (ip->insn_opcode & 0xdc00) == 0x8c00))
+ || (ip->insn_opcode & 0xdfe00000) == 0x94000000
+ || (ip->insn_opcode & 0xdc1f0000) == 0x94000000))
+ return APPEND_ADD_COMPACT;
+
return APPEND_ADD_WITH_NOP;
}
return APPEND_ADD;
}
-/* IP is a MIPS16 instruction whose opcode we have just changed.
- Point IP->insn_mo to the new opcode's definition. */
+/* IP is an instruction whose opcode we have just changed, END points
+ to the end of the opcode table processed. Point IP->insn_mo to the
+ new opcode's definition. */
static void
-find_altered_mips16_opcode (struct mips_cl_insn *ip)
+find_altered_opcode (struct mips_cl_insn *ip, const struct mips_opcode *end)
{
- const struct mips_opcode *mo, *end;
+ const struct mips_opcode *mo;
- end = &mips16_opcodes[bfd_mips16_num_opcodes];
for (mo = ip->insn_mo; mo < end; mo++)
- if ((ip->insn_opcode & mo->mask) == mo->match)
+ if (mo->pinfo != INSN_MACRO
+ && (ip->insn_opcode & mo->mask) == mo->match)
{
ip->insn_mo = mo;
return;
abort ();
}
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+ Point IP->insn_mo to the new opcode's definition. */
+
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+ find_altered_opcode (ip, &mips16_opcodes[bfd_mips16_num_opcodes]);
+}
+
+/* IP is a microMIPS instruction whose opcode we have just changed.
+ Point IP->insn_mo to the new opcode's definition. */
+
+static void
+find_altered_micromips_opcode (struct mips_cl_insn *ip)
+{
+ find_altered_opcode (ip, µmips_opcodes[bfd_micromips_num_opcodes]);
+}
+
/* For microMIPS macros, we need to generate a local number label
as the target of branches. */
#define MICROMIPS_LABEL_CHAR '\037'
prev_pinfo2 = history[0].insn_mo->pinfo2;
pinfo = ip->insn_mo->pinfo;
+ /* Don't raise alarm about `nods' frags as they'll fill in the right
+ kind of nop in relaxation if required. */
if (mips_opts.micromips
&& !expansionp
+ && !(history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent
+ && RELAX_MICROMIPS_P (history[0].frag->fr_subtype)
+ && RELAX_MICROMIPS_NODS (history[0].frag->fr_subtype))
&& (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
&& micromips_insn_length (ip->insn_mo) != 2)
|| ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
16-bit/32-bit instructions. */
&& !forced_insn_length)
{
- bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
+ bfd_boolean relax16 = (method != APPEND_ADD_COMPACT
+ && *reloc_type > BFD_RELOC_UNUSED);
int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
int uncond = uncond_branch_p (ip) ? -1 : 0;
- int compact = compact_branch_p (ip);
+ int compact = compact_branch_p (ip) || method == APPEND_ADD_COMPACT;
+ int nods = method == APPEND_ADD_WITH_NOP;
int al = pinfo & INSN_WRITE_GPR_31;
- int length32;
+ int length32 = nods ? 8 : 4;
gas_assert (address_expr != NULL);
gas_assert (!mips_relax.sequence);
relaxed_branch = TRUE;
- length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
- add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
+ if (nods)
+ method = APPEND_ADD;
+ if (relax32)
+ length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
+ add_relaxed_insn (ip, length32, relax16 ? 2 : 4,
RELAX_MICROMIPS_ENCODE (type, AT, mips_opts.insn32,
- uncond, compact, al,
+ uncond, compact, al, nods,
relax32, 0, 0),
address_expr->X_add_symbol,
address_expr->X_add_number);
}
else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
{
+ bfd_boolean require_unextended;
+ bfd_boolean require_extended;
symbolS *symbol;
offsetT offset;
+ if (forced_insn_length != 0)
+ {
+ require_unextended = forced_insn_length == 2;
+ require_extended = forced_insn_length == 4;
+ }
+ else
+ {
+ require_unextended = (mips_opts.noautoextend
+ && !mips_opcode_32bit_p (ip->insn_mo));
+ require_extended = 0;
+ }
+
/* We need to set up a variant frag. */
gas_assert (address_expr != NULL);
/* Pass any `O_symbol' expression unchanged as an `expr_section'
add_relaxed_insn (ip, 4, 0,
RELAX_MIPS16_ENCODE
(*reloc_type - BFD_RELOC_UNUSED,
- forced_insn_length == 2, forced_insn_length == 4,
+ require_unextended, require_extended,
delayed_branch_p (&history[0]),
history[0].mips16_absolute_jump_p),
symbol, offset);
case APPEND_ADD_COMPACT:
/* Convert MIPS16 jr/jalr into a "compact" jump. */
- gas_assert (mips_opts.mips16);
- ip->insn_opcode |= 0x0080;
- find_altered_mips16_opcode (ip);
+ if (mips_opts.mips16)
+ {
+ ip->insn_opcode |= 0x0080;
+ find_altered_mips16_opcode (ip);
+ }
+ /* Convert microMIPS instructions. */
+ else if (mips_opts.micromips)
+ {
+ /* jr16->jrc */
+ if ((ip->insn_opcode & 0xffe0) == 0x4580)
+ ip->insn_opcode |= 0x0020;
+ /* b16->bc */
+ else if ((ip->insn_opcode & 0xfc00) == 0xcc00)
+ ip->insn_opcode = 0x40e00000;
+ /* beqz16->beqzc, bnez16->bnezc */
+ else if ((ip->insn_opcode & 0xdc00) == 0x8c00)
+ {
+ unsigned long regno;
+
+ regno = ip->insn_opcode >> MICROMIPSOP_SH_MD;
+ regno &= MICROMIPSOP_MASK_MD;
+ regno = micromips_to_32_reg_d_map[regno];
+ ip->insn_opcode = (((ip->insn_opcode << 9) & 0x00400000)
+ | (regno << MICROMIPSOP_SH_RS)
+ | 0x40a00000) ^ 0x00400000;
+ }
+ /* beqz->beqzc, bnez->bnezc */
+ else if ((ip->insn_opcode & 0xdfe00000) == 0x94000000)
+ ip->insn_opcode = ((ip->insn_opcode & 0x001f0000)
+ | ((ip->insn_opcode >> 7) & 0x00400000)
+ | 0x40a00000) ^ 0x00400000;
+ /* beq $0->beqzc, bne $0->bnezc */
+ else if ((ip->insn_opcode & 0xdc1f0000) == 0x94000000)
+ ip->insn_opcode = (((ip->insn_opcode >>
+ (MICROMIPSOP_SH_RT - MICROMIPSOP_SH_RS))
+ & (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS))
+ | ((ip->insn_opcode >> 7) & 0x00400000)
+ | 0x40a00000) ^ 0x00400000;
+ else
+ abort ();
+ find_altered_micromips_opcode (ip);
+ }
+ else
+ abort ();
install_insn (ip);
insert_into_history (0, 1, ip);
break;
const char *args;
const struct mips_operand *operand;
const struct mips_operand *ext_operand;
+ int required_insn_length;
struct mips_arg_info arg;
int relax_char;
+ if (forced_insn_length)
+ required_insn_length = forced_insn_length;
+ else if (mips_opts.noautoextend && !mips_opcode_32bit_p (opcode))
+ required_insn_length = 2;
+ else
+ required_insn_length = 0;
+
create_insn (insn, opcode);
imm_expr.X_op = O_absent;
offset_expr.X_op = O_absent;
&value))
{
mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
- forced_insn_length, &insn->insn_opcode);
+ required_insn_length, &insn->insn_opcode);
offset_expr.X_op = O_absent;
*offset_reloc = BFD_RELOC_UNUSED;
}
else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
{
- if (forced_insn_length == 2)
+ if (required_insn_length == 2)
set_insn_error (0, _("invalid unextended operand value"));
forced_insn_length = 4;
insn->insn_opcode |= MIPS16_EXTEND;
case 'a':
case 'i':
*offset_reloc = BFD_RELOC_MIPS16_JMP;
- insn->insn_opcode <<= 16;
break;
}
- operand = decode_mips16_operand (c, FALSE);
+ operand = decode_mips16_operand (c, mips_opcode_32bit_p (opcode));
if (!operand)
abort ();
- /* '6' is a special case. It is used for BREAK and SDBBP,
- whose operands are only meaningful to the software that decodes
- them. This means that there is no architectural reason why
- they cannot be prefixed by EXTEND, but in practice,
- exception handlers will only look at the instruction
- itself. We therefore allow '6' to be extended when
- disassembling but not when assembling. */
- if (operand->type != OP_PCREL && c != '6')
+ if (operand->type != OP_PCREL)
{
ext_operand = decode_mips16_operand (c, TRUE);
if (operand != ext_operand)
{
const struct mips_opcode *opcode;
bfd_boolean seen_valid_for_isa;
+ bfd_boolean seen_valid_for_size;
/* Search for a match, ignoring alternatives that don't satisfy the
current ISA. There are no separate entries for extended forms so
we deal with forced_length later. */
seen_valid_for_isa = FALSE;
+ seen_valid_for_size = FALSE;
opcode = first;
do
{
if (is_opcode_valid_16 (opcode))
{
seen_valid_for_isa = TRUE;
- if (match_mips16_insn (insn, opcode, tokens))
- return TRUE;
+ if (is_size_valid_16 (opcode))
+ {
+ seen_valid_for_size = TRUE;
+ if (match_mips16_insn (insn, opcode, tokens))
+ return TRUE;
+ }
}
++opcode;
}
return TRUE;
}
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were of the wrong size. */
+ if (!seen_valid_for_size)
+ {
+ if (forced_insn_length == 2)
+ set_insn_error
+ (0, _("unrecognized unextended version of MIPS16 opcode"));
+ else
+ set_insn_error
+ (0, _("unrecognized extended version of MIPS16 opcode"));
+ return TRUE;
+ }
+
return FALSE;
}
memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
mips_macro_warning.delay_slot_p = (mips_opts.noreorder
&& delayed_branch_p (&history[0]));
- switch (history[0].insn_mo->pinfo2
- & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
- {
- case INSN2_BRANCH_DELAY_32BIT:
- mips_macro_warning.delay_slot_length = 4;
- break;
- case INSN2_BRANCH_DELAY_16BIT:
- mips_macro_warning.delay_slot_length = 2;
- break;
- default:
- mips_macro_warning.delay_slot_length = 0;
- break;
- }
+ if (history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent
+ && RELAX_MICROMIPS_P (history[0].frag->fr_subtype)
+ && RELAX_MICROMIPS_NODS (history[0].frag->fr_subtype))
+ mips_macro_warning.delay_slot_length = 0;
+ else
+ switch (history[0].insn_mo->pinfo2
+ & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
+ {
+ case INSN2_BRANCH_DELAY_32BIT:
+ mips_macro_warning.delay_slot_length = 4;
+ break;
+ case INSN2_BRANCH_DELAY_16BIT:
+ mips_macro_warning.delay_slot_length = 2;
+ break;
+ default:
+ mips_macro_warning.delay_slot_length = 0;
+ break;
+ }
mips_macro_warning.first_frag = NULL;
}
break;
case '<':
- case '>':
case '4':
case '5':
case 'H':
* optimizing code generation.
* One interesting optimization is when several store macros appear
* consecutively that would load AT with the upper half of the same address.
- * The ensuing load upper instructions are ommited. This implies some kind
+ * The ensuing load upper instructions are omitted. This implies some kind
* of global optimization. We currently only optimize within a single macro.
* For many of the load and store macros if the address is specified as a
* constant expression in the first 64k of memory (ie ld $2,0x4000c) we
{
case M_DABS:
dbl = 1;
+ /* Fall through. */
case M_ABS:
/* bgez $a0,1f
move v0,$a0
case M_BGEL:
likely = 1;
+ /* Fall through. */
case M_BGE:
if (op[1] == 0)
macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[0]);
case M_BGTL_I:
likely = 1;
+ /* Fall through. */
case M_BGT_I:
/* Check for > max integer. */
if (imm_expr.X_add_number >= GPR_SMAX)
case M_BGEUL:
likely = 1;
+ /* Fall through. */
case M_BGEU:
if (op[1] == 0)
goto do_true;
case M_BGTUL_I:
likely = 1;
+ /* Fall through. */
case M_BGTU_I:
if (op[0] == 0
|| (GPR_SIZE == 32
case M_BGTL:
likely = 1;
+ /* Fall through. */
case M_BGT:
if (op[1] == 0)
macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[0]);
case M_BGTUL:
likely = 1;
+ /* Fall through. */
case M_BGTU:
if (op[1] == 0)
macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
case M_BLEL:
likely = 1;
+ /* Fall through. */
case M_BLE:
if (op[1] == 0)
macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
case M_BLEL_I:
likely = 1;
+ /* Fall through. */
case M_BLE_I:
if (imm_expr.X_add_number >= GPR_SMAX)
goto do_true;
case M_BLEUL:
likely = 1;
+ /* Fall through. */
case M_BLEU:
if (op[1] == 0)
macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
case M_BLEUL_I:
likely = 1;
+ /* Fall through. */
case M_BLEU_I:
if (op[0] == 0
|| (GPR_SIZE == 32
case M_BLTL:
likely = 1;
+ /* Fall through. */
case M_BLT:
if (op[1] == 0)
macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
case M_BLTUL:
likely = 1;
+ /* Fall through. */
case M_BLTU:
if (op[1] == 0)
goto do_false;
case M_DDIV_3:
dbl = 1;
+ /* Fall through. */
case M_DIV_3:
s = "mflo";
goto do_div3;
case M_DREM_3:
dbl = 1;
+ /* Fall through. */
case M_REM_3:
s = "mfhi";
do_div3:
case M_DLCA_AB:
dbl = 1;
+ /* Fall through. */
case M_LCA_AB:
call = 1;
goto do_la;
case M_DLA_AB:
dbl = 1;
+ /* Fall through. */
case M_LA_AB:
do_la:
/* Load the address of a symbol into a register. If breg is not
else if (offbits != 16)
{
/* The offset field is too narrow to be used for a low-part
- relocation, so load the whole address into the auxillary
+ relocation, so load the whole address into the auxiliary
register. */
load_address (tempreg, &offset_expr, &used_at);
if (breg != 0)
case M_DMUL:
dbl = 1;
+ /* Fall through. */
case M_MUL:
if (mips_opts.arch == CPU_R5900)
macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", op[0], op[1],
case M_DMUL_I:
dbl = 1;
+ /* Fall through. */
case M_MUL_I:
/* The MIPS assembler some times generates shifts and adds. I'm
not trying to be that fancy. GCC should do this for us
case M_DMULO_I:
dbl = 1;
+ /* Fall through. */
case M_MULO_I:
imm = 1;
goto do_mulo;
case M_DMULO:
dbl = 1;
+ /* Fall through. */
case M_MULO:
do_mulo:
start_noreorder ();
case M_DMULOU_I:
dbl = 1;
+ /* Fall through. */
case M_MULOU_I:
imm = 1;
goto do_mulou;
case M_DMULOU:
dbl = 1;
+ /* Fall through. */
case M_MULOU:
do_mulou:
start_noreorder ();
case M_DDIV_3:
dbl = 1;
+ /* Fall through. */
case M_DIV_3:
s = "mflo";
goto do_div3;
case M_DREM_3:
dbl = 1;
+ /* Fall through. */
case M_REM_3:
s = "mfhi";
do_div3:
case M_DMUL:
dbl = 1;
+ /* Fall through. */
case M_MUL:
macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", op[1], op[2]);
macro_build (NULL, "mflo", "x", op[0]);
char *end, *s, c;
struct mips_opcode *first;
struct mips_operand_token *tokens;
-
- forced_insn_length = 0;
+ unsigned int l;
for (s = str; ISLOWER (*s); ++s)
;
end = s;
c = *end;
+
+ l = 0;
switch (c)
{
case '\0':
break;
case '.':
- if (s[1] == 't' && s[2] == ' ')
+ s++;
+ if (*s == 't')
{
- forced_insn_length = 2;
- s += 3;
- break;
+ l = 2;
+ s++;
}
- else if (s[1] == 'e' && s[2] == ' ')
+ else if (*s == 'e')
{
- forced_insn_length = 4;
- s += 3;
- break;
+ l = 4;
+ s++;
}
+ if (*s == '\0')
+ break;
+ else if (*s++ == ' ')
+ break;
/* Fall through. */
default:
set_insn_error (0, _("unrecognized opcode"));
return;
}
-
- if (mips_opts.noautoextend && !forced_insn_length)
- forced_insn_length = 2;
+ forced_insn_length = l;
*end = 0;
first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
return length;
}
+/* Get a FRAG's branch instruction delay slot size, either from the
+ short-delay-slot bit of a branch-and-link instruction if AL is TRUE,
+ or SHORT_INSN_SIZE otherwise. */
+
+static int
+frag_branch_delay_slot_size (fragS *fragp, bfd_boolean al, int short_insn_size)
+{
+ char *buf = fragp->fr_literal + fragp->fr_fix;
+
+ if (al)
+ return (read_compressed_insn (buf, 4) & 0x02000000) ? 2 : 4;
+ else
+ return short_insn_size;
+}
+
/* Compute the length of a branch sequence, and adjust the
RELAX_MICROMIPS_TOOFAR32 bit accordingly. If FRAGP is NULL, the
worst-case length is computed, with UPDATE being used to indicate
static int
relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
{
+ bfd_boolean insn32 = TRUE;
+ bfd_boolean nods = TRUE;
+ bfd_boolean al = TRUE;
+ int short_insn_size;
bfd_boolean toofar;
int length;
+ if (fragp)
+ {
+ insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
+ nods = RELAX_MICROMIPS_NODS (fragp->fr_subtype);
+ al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
+ }
+ short_insn_size = insn32 ? 4 : 2;
+
if (fragp
&& S_IS_DEFINED (fragp->fr_symbol)
&& !S_IS_WEAK (fragp->fr_symbol)
{
bfd_boolean compact_known = fragp != NULL;
bfd_boolean compact = FALSE;
- bfd_boolean insn32 = TRUE;
bfd_boolean uncond;
- int short_insn_size;
if (fragp)
{
compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
- insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
}
else
uncond = update < 0;
- short_insn_size = insn32 ? 4 : 2;
/* If label is out of range, we turn branch <br>:
if (mips_pic != NO_PIC)
length += 4 + short_insn_size;
+ /* Add an extra nop if the jump has no compact form and we need
+ to fill the delay slot. */
+ if ((mips_pic == NO_PIC || al) && nods)
+ length += (fragp
+ ? frag_branch_delay_slot_size (fragp, al, short_insn_size)
+ : short_insn_size);
+
/* If branch <br> is conditional, we prepend negated branch <brneg>:
<brneg> 0f # 4 bytes
if (!uncond)
length += (compact_known && compact) ? 4 : 4 + short_insn_size;
}
+ else if (nods)
+ {
+ /* Add an extra nop to fill the delay slot. */
+ gas_assert (fragp);
+ length += frag_branch_delay_slot_size (fragp, al, short_insn_size);
+ }
return length;
}
char *buf = fragp->fr_literal + fragp->fr_fix;
bfd_boolean compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
bfd_boolean insn32 = RELAX_MICROMIPS_INSN32 (fragp->fr_subtype);
+ bfd_boolean nods = RELAX_MICROMIPS_NODS (fragp->fr_subtype);
bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
bfd_boolean short_ds;
fixp->fx_line = fragp->fr_line;
if (type == 0)
- return;
+ {
+ insn = read_compressed_insn (buf, 4);
+ buf += 4;
+
+ if (nods)
+ {
+ /* Check the short-delay-slot bit. */
+ if (!al || (insn & 0x02000000) != 0)
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ else
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ }
+
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+ return;
+ }
}
/* Relax 16-bit branches to 32-bit branches. */
|| !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
{
buf = write_compressed_insn (buf, insn, 4);
+ if (nods)
+ buf = write_compressed_insn (buf, 0x0c00, 2);
gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
return;
}
_("relaxed out-of-range branch into a jump"));
/* Set the short-delay-slot bit. */
- short_ds = al && (insn & 0x02000000) != 0;
+ short_ds = !al || (insn & 0x02000000) != 0;
if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
{
if (mips_pic == NO_PIC)
{
- unsigned long jal = short_ds ? 0x74000000 : 0xf4000000; /* jal/s */
+ unsigned long jal = (short_ds || nods
+ ? 0x74000000 : 0xf4000000); /* jal/s */
/* j/jal/jals <sym> R_MICROMIPS_26_S1 */
insn = al ? jal : 0xd4000000;
buf = write_compressed_insn (buf, insn, 4);
- if (compact)
+ if (compact || nods)
{
/* nop */
if (insn32)
buf = write_compressed_insn (buf, insn, 4);
- if (compact)
+ if (compact || nods)
/* nop */
buf = write_compressed_insn (buf, 0x00000000, 4);
}
{
/* jr/jrc/jalr/jalrs $at */
unsigned long jalr = short_ds ? 0x45e0 : 0x45c0; /* jalr/s */
- unsigned long jr = compact ? 0x45a0 : 0x4580; /* jr/c */
+ unsigned long jr = compact || nods ? 0x45a0 : 0x4580; /* jr/c */
insn = al ? jalr : jr;
insn |= at << MICROMIPSOP_SH_MJ;
buf = write_compressed_insn (buf, insn, 2);
+ if (al && nods)
+ {
+ /* nop */
+ if (short_ds)
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ else
+ buf = write_compressed_insn (buf, 0x00000000, 4);
+ }
}
}
_("unsupported relocation"));
break;
}
- if (reloc != BFD_RELOC_NONE)
+ if (reloc == BFD_RELOC_NONE)
+ ;
+ else if (ext)
{
- gas_assert (ext);
-
exp.X_op = O_symbol;
exp.X_add_symbol = fragp->fr_symbol;
exp.X_add_number = fragp->fr_offset;
in 2 octets. */
fixp->fx_no_overflow = 1;
}
+ else
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("invalid unextended operand value"));
}
else
mips16_immed (fragp->fr_file, fragp->fr_line, type,