From: Richard Sandiford Date: Sun, 14 Jul 2013 13:51:52 +0000 (+0000) Subject: gas/ X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=364215c8452ec89e9dd66d5cd92ae6e62bee607d;p=binutils-gdb.git gas/ * config/tc-mips.c (mips32_to_16_reg_map): Delete. (match_entry_exit_operand): New function. (match_save_restore_list_operand): Likewise. (match_operand): Use them. (check_absolute_expr): Delete. (mips16_ip): Rewrite main parsing loop to use mips_operands. --- diff --git a/gas/ChangeLog b/gas/ChangeLog index ceeb1b2353a..9b36c85b8fe 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,12 @@ +2013-07-14 Richard Sandiford + + * config/tc-mips.c (mips32_to_16_reg_map): Delete. + (match_entry_exit_operand): New function. + (match_save_restore_list_operand): Likewise. + (match_operand): Use them. + (check_absolute_expr): Delete. + (mips16_ip): Rewrite main parsing loop to use mips_operands. + 2013-07-14 Richard Sandiford * config/tc-mips.c: Enable functions commented out in previous patch. diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index c7e0edc5748..ff861653fa8 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -755,18 +755,6 @@ static struct mips_hi_fixup *mips_hi_fixup_list; static fragS *prev_reloc_op_frag; -/* Map normal MIPS register numbers to mips16 register numbers. */ - -#define X ILLEGAL_REG -static const int mips32_to_16_reg_map[] = -{ - X, X, 2, 3, 4, 5, 6, 7, - X, X, X, X, X, X, X, X, - 0, 1, X, X, X, X, X, X, - X, X, X, X, X, X, X, X -}; -#undef X - /* Map mips16 register numbers to normal MIPS register numbers. */ static const unsigned int mips16_to_32_reg_map[] = @@ -4280,6 +4268,236 @@ match_lwm_swm_list_operand (struct mips_arg_info *arg, return s; } +/* OP_ENTRY_EXIT_LIST matcher. */ + +static char * +match_entry_exit_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + unsigned int mask; + bfd_boolean is_exit; + + /* The format is the same for both ENTRY and EXIT, but the constraints + are different. */ + is_exit = strcmp (arg->insn->insn_mo->name, "exit") == 0; + mask = (is_exit ? 7 << 3 : 0); + for (;;) + { + unsigned int regno1, regno2; + bfd_boolean is_freg; + + if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®no1)) + is_freg = FALSE; + else if (reg_lookup (&s, RTYPE_FPU, ®no1)) + is_freg = TRUE; + else + return 0; + + SKIP_SPACE_TABS (s); + if (*s == '-') + { + ++s; + SKIP_SPACE_TABS (s); + if (!reg_lookup (&s, (is_freg ? RTYPE_FPU + : RTYPE_GP | RTYPE_NUM), ®no2)) + return 0; + SKIP_SPACE_TABS (s); + } + else + regno2 = regno1; + + if (is_exit && is_freg && regno1 == 0 && regno2 < 2) + { + mask &= ~(7 << 3); + mask |= (5 + regno2) << 3; + } + else if (!is_exit && regno1 == 4 && regno2 >= 4 && regno2 <= 7) + mask |= (regno2 - 3) << 3; + else if (regno1 == 16 && regno2 >= 16 && regno2 <= 17) + mask |= (regno2 - 15) << 1; + else if (regno1 == RA && regno2 == RA) + mask |= 1; + else + return 0; + + if (!*s) + break; + if (*s != ',') + return 0; + arg->argnum += 1; + ++s; + SKIP_SPACE_TABS (s); + } + insn_insert_operand (arg->insn, operand, mask); + return s; +} + +/* OP_SAVE_RESTORE_LIST matcher. */ + +static char * +match_save_restore_list_operand (struct mips_arg_info *arg, char *s) +{ + unsigned int opcode, args, statics, sregs; + unsigned int num_frame_sizes, num_args, num_statics, num_sregs; + expressionS value; + offsetT frame_size; + const char *error; + + error = 0; + opcode = arg->insn->insn_opcode; + frame_size = 0; + num_frame_sizes = 0; + args = 0; + statics = 0; + sregs = 0; + for (;;) + { + unsigned int regno1, regno2; + + my_getExpression (&value, s); + if (value.X_op == O_constant) + { + /* Handle the frame size. */ + num_frame_sizes += 1; + frame_size = value.X_add_number; + s = expr_end; + SKIP_SPACE_TABS (s); + } + else + { + if (!reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®no1)) + return 0; + + SKIP_SPACE_TABS (s); + if (*s == '-') + { + ++s; + SKIP_SPACE_TABS (s); + if (!reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®no2) + || regno2 < regno1) + return 0; + SKIP_SPACE_TABS (s); + } + else + regno2 = regno1; + + while (regno1 <= regno2) + { + if (regno1 >= 4 && regno1 <= 7) + { + if (num_frame_sizes == 0) + /* args $a0-$a3 */ + args |= 1 << (regno1 - 4); + else + /* statics $a0-$a3 */ + statics |= 1 << (regno1 - 4); + } + else if (regno1 >= 16 && regno1 <= 23) + /* $s0-$s7 */ + sregs |= 1 << (regno1 - 16); + else if (regno1 == 30) + /* $s8 */ + sregs |= 1 << 8; + else if (regno1 == 31) + /* Add $ra to insn. */ + opcode |= 0x40; + else + return 0; + regno1 += 1; + if (regno1 == 24) + regno1 = 30; + } + } + if (!*s) + break; + if (*s != ',') + return 0; + arg->argnum += 1; + ++s; + SKIP_SPACE_TABS (s); + } + + /* Encode args/statics combination. */ + if (args & statics) + return 0; + else if (args == 0xf) + /* All $a0-$a3 are args. */ + opcode |= MIPS16_ALL_ARGS << 16; + else if (statics == 0xf) + /* All $a0-$a3 are statics. */ + opcode |= MIPS16_ALL_STATICS << 16; + else + { + /* Count arg registers. */ + num_args = 0; + while (args & 0x1) + { + args >>= 1; + num_args += 1; + } + if (args != 0) + return 0; + + /* Count static registers. */ + num_statics = 0; + while (statics & 0x8) + { + statics = (statics << 1) & 0xf; + num_statics += 1; + } + if (statics != 0) + return 0; + + /* Encode args/statics. */ + opcode |= ((num_args << 2) | num_statics) << 16; + } + + /* Encode $s0/$s1. */ + if (sregs & (1 << 0)) /* $s0 */ + opcode |= 0x20; + if (sregs & (1 << 1)) /* $s1 */ + opcode |= 0x10; + sregs >>= 2; + + /* Encode $s2-$s8. */ + num_sregs = 0; + while (sregs & 1) + { + sregs >>= 1; + num_sregs += 1; + } + if (sregs != 0) + return 0; + opcode |= num_sregs << 24; + + /* Encode frame size. */ + if (num_frame_sizes == 0) + error = _("Missing frame size"); + else if (num_frame_sizes > 1) + error = _("Frame size specified twice"); + else if ((frame_size & 7) != 0 || frame_size < 0 || frame_size > 0xff * 8) + error = _("Invalid frame size"); + else if (frame_size != 128 || (opcode >> 16) != 0) + { + frame_size /= 8; + opcode |= (((frame_size & 0xf0) << 16) + | (frame_size & 0x0f)); + } + + if (error) + { + if (arg->soft_match) + return 0; + as_bad (error); + } + + /* Finally build the instruction. */ + if ((opcode >> 16) != 0 || frame_size == 0) + opcode |= MIPS16_EXTEND; + arg->insn->insn_opcode = opcode; + return s; +} + /* OP_MDMX_IMM_REG matcher. */ static char * @@ -4454,8 +4672,10 @@ match_operand (struct mips_arg_info *arg, return match_lwm_swm_list_operand (arg, operand, s); case OP_ENTRY_EXIT_LIST: + return match_entry_exit_operand (arg, operand, s); + case OP_SAVE_RESTORE_LIST: - abort (); + return match_save_restore_list_operand (arg, s); case OP_MDMX_IMM_REG: return match_mdmx_imm_reg_operand (arg, operand, s); @@ -6573,21 +6793,6 @@ set_at (int reg, int unsignedp) } } -/* Warn if an expression is not a constant. */ - -static void -check_absolute_expr (struct mips_cl_insn *ip, expressionS *ex) -{ - if (ex->X_op == O_big) - as_bad (_("unsupported large constant")); - else if (ex->X_op != O_constant) - as_bad (_("Instruction %s requires absolute expression"), - ip->insn_mo->name); - - if (HAVE_32BIT_GPRS) - normalize_constant_expr (ex); -} - /* Count the leading zeroes by performing a binary chop. This is a bulky bit of source, but performance is a LOT better for the majority of values than a simple loop to count the bits: @@ -12064,10 +12269,10 @@ mips16_ip (char *str, struct mips_cl_insn *ip) const char *args; struct mips_opcode *insn; char *argsstart; - unsigned int regno; - unsigned int lastregno = 0; - char *s_reset; size_t i; + const struct mips_operand *operand; + const struct mips_operand *ext_operand; + struct mips_arg_info arg; insn_error = NULL; @@ -12118,15 +12323,17 @@ mips16_ip (char *str, struct mips_cl_insn *ip) for (;;) { bfd_boolean ok; + bfd_boolean more_alts; char relax_char; gas_assert (strcmp (insn->name, str) == 0); ok = is_opcode_valid_16 (insn); + more_alts = (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] + && strcmp (insn[0].name, insn[1].name) == 0); if (! ok) { - if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] - && strcmp (insn->name, insn[1].name) == 0) + if (more_alts) { ++insn; continue; @@ -12154,292 +12361,103 @@ mips16_ip (char *str, struct mips_cl_insn *ip) offset_reloc[1] = BFD_RELOC_UNUSED; offset_reloc[2] = BFD_RELOC_UNUSED; relax_char = 0; + + memset (&arg, 0, sizeof (arg)); + arg.insn = ip; + arg.argnum = 1; + arg.last_regno = ILLEGAL_REG; + arg.dest_regno = ILLEGAL_REG; + arg.soft_match = more_alts; + relax_char = 0; for (args = insn->args; 1; ++args) { int c; - if (*s == ' ') - ++s; - - /* In this switch statement we call break if we did not find - a match, continue if we did find a match, or return if we - are done. */ - - c = *args; - switch (c) + SKIP_SPACE_TABS (s); + if (*s == 0) { - case '\0': - if (*s == '\0') - { - offsetT value; + offsetT value; - /* Stuff the immediate value in now, if we can. */ - if (insn->pinfo == INSN_MACRO) - { - gas_assert (relax_char == 0); - gas_assert (*offset_reloc == BFD_RELOC_UNUSED); - } - else if (relax_char - && offset_expr.X_op == O_constant - && calculate_reloc (*offset_reloc, - offset_expr.X_add_number, - &value)) - { - mips16_immed (NULL, 0, relax_char, *offset_reloc, value, - forced_insn_length, &ip->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) - as_bad (_("invalid unextended operand value")); - forced_insn_length = 4; - ip->insn_opcode |= MIPS16_EXTEND; - } - else if (relax_char) - *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char; - - return; - } - break; - - case ',': - if (*s++ == c) - continue; - s--; - switch (*++args) - { - case 'v': - MIPS16_INSERT_OPERAND (RX, *ip, lastregno); - continue; - case 'w': - MIPS16_INSERT_OPERAND (RY, *ip, lastregno); - continue; - } - break; + /* Handle unary instructions in which only one operand is given. + The source is then the same as the destination. */ + if (arg.opnum == 1 && *args == ',') + switch (args[1]) + { + case 'v': + case 'w': + arg.argnum = 1; + s = argsstart; + continue; + } - case '(': - case ')': - if (*s++ == c) - continue; - break; + /* Fail the match if there were too few operands. */ + if (*args) + break; - case 'v': - case 'w': - if (s[0] != '$') + /* Successful match. Stuff the immediate value in now, if + we can. */ + if (insn->pinfo == INSN_MACRO) { - if (c == 'v') - MIPS16_INSERT_OPERAND (RX, *ip, lastregno); - else - MIPS16_INSERT_OPERAND (RY, *ip, lastregno); - ++args; - continue; + gas_assert (relax_char == 0); + gas_assert (*offset_reloc == BFD_RELOC_UNUSED); } - /* Fall through. */ - case 'x': - case 'y': - case 'z': - case 'Z': - case '0': - case 'S': - case 'R': - case 'X': - case 'Y': - s_reset = s; - if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no)) + else if (relax_char + && offset_expr.X_op == O_constant + && calculate_reloc (*offset_reloc, + offset_expr.X_add_number, + &value)) { - if (c == 'v' || c == 'w') - { - if (c == 'v') - MIPS16_INSERT_OPERAND (RX, *ip, lastregno); - else - MIPS16_INSERT_OPERAND (RY, *ip, lastregno); - ++args; - continue; - } - break; + mips16_immed (NULL, 0, relax_char, *offset_reloc, value, + forced_insn_length, &ip->insn_opcode); + offset_expr.X_op = O_absent; + *offset_reloc = BFD_RELOC_UNUSED; } - - if (*s == ' ') - ++s; - if (args[1] != *s) + else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED) { - if (c == 'v' || c == 'w') - { - regno = mips16_to_32_reg_map[lastregno]; - s = s_reset; - ++args; - } + if (forced_insn_length == 2) + as_bad (_("invalid unextended operand value")); + forced_insn_length = 4; + ip->insn_opcode |= MIPS16_EXTEND; } + else if (relax_char) + *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char; - switch (c) - { - case 'x': - case 'y': - case 'z': - case 'v': - case 'w': - case 'Z': - regno = mips32_to_16_reg_map[regno]; - break; - - case '0': - if (regno != 0) - regno = ILLEGAL_REG; - break; - - case 'S': - if (regno != SP) - regno = ILLEGAL_REG; - break; - - case 'R': - if (regno != RA) - regno = ILLEGAL_REG; - break; - - case 'X': - case 'Y': - 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); - } - break; + check_completed_insn (&arg); + return; + } - default: - abort (); - } + /* Fail the match if the line has too many operands. */ + if (*args == 0) + break; - if (regno == ILLEGAL_REG) + /* Handle characters that need to match exactly. */ + if (*args == '(' || *args == ')' || *args == ',') + { + if (*s != *args) break; - - switch (c) - { - case 'x': - case 'v': - MIPS16_INSERT_OPERAND (RX, *ip, regno); - break; - case 'y': - case 'w': - MIPS16_INSERT_OPERAND (RY, *ip, regno); - break; - case 'z': - MIPS16_INSERT_OPERAND (RZ, *ip, regno); - break; - case 'Z': - MIPS16_INSERT_OPERAND (MOVE32Z, *ip, regno); - case '0': - case 'S': - case 'R': - break; - case 'X': - MIPS16_INSERT_OPERAND (REGR32, *ip, regno); - break; - case 'Y': - regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3); - MIPS16_INSERT_OPERAND (REG32R, *ip, regno); - break; - default: - abort (); - } - - lastregno = regno; + if (*s == ',') + arg.argnum += 1; + ++s; continue; + } - case 'P': - if (strncmp (s, "$pc", 3) == 0) - { - s += 3; - continue; - } + arg.opnum += 1; + arg.optional_reg = FALSE; + c = *args; + switch (c) + { + case 'v': + case 'w': + arg.optional_reg = (args[1] == ','); break; - case '5': - case 'H': - case 'W': - case 'D': - case 'j': - case 'V': - case 'C': - case 'U': - case 'k': - case 'K': - i = my_getSmallExpression (&offset_expr, offset_reloc, s); - if (i > 0) - { - relax_char = c; - s = expr_end; - continue; - } - *offset_reloc = BFD_RELOC_UNUSED; - /* Fall through. */ - case '<': - case '>': - case '[': - case ']': - case '4': - case '8': - my_getExpression (&offset_expr, s); - if (offset_expr.X_op == O_register) - { - /* 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 offset_expr, so that we handle - explicit extensions correctly. */ - offset_expr.X_op = O_constant; - offset_expr.X_add_number = 0; - relax_char = c; - continue; - } - - break; - } - - /* We need to relax this instruction. */ - relax_char = c; - s = expr_end; - continue; - case 'p': case 'q': case 'A': case 'B': case 'E': - /* We use offset_reloc rather than imm_reloc for the PC - relative operands. This lets macros with both - immediate and address operands work correctly. */ - my_getExpression (&offset_expr, s); - - if (offset_expr.X_op == O_register) - break; - - /* We need to relax this instruction. */ relax_char = c; - s = expr_end; - continue; - - case '6': /* break code */ - my_getExpression (&imm_expr, s); - check_absolute_expr (ip, &imm_expr); - if ((unsigned long) imm_expr.X_add_number > 63) - as_warn (_("Invalid value for `%s' (%lu)"), - ip->insn_mo->name, - (unsigned long) imm_expr.X_add_number); - MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number); - imm_expr.X_op = O_absent; - s = expr_end; - continue; + break; case 'I': my_getExpression (&imm_expr, s); @@ -12451,268 +12469,73 @@ mips16_ip (char *str, struct mips_cl_insn *ip) s = expr_end; continue; - case 'a': /* 26 bit address */ + case 'a': case 'i': - my_getExpression (&offset_expr, s); - s = expr_end; *offset_reloc = BFD_RELOC_MIPS16_JMP; ip->insn_opcode <<= 16; - continue; - - case 'l': /* register list for entry macro */ - case 'L': /* register list for exit macro */ - { - int mask; - - if (c == 'l') - mask = 0; - else - mask = 7 << 3; - while (*s != '\0') - { - unsigned int freg, reg1, reg2; - - while (*s == ' ' || *s == ',') - ++s; - if (reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1)) - freg = 0; - else if (reg_lookup (&s, RTYPE_FPU, ®1)) - freg = 1; - else - { - as_bad (_("can't parse register list")); - break; - } - if (*s == ' ') - ++s; - if (*s != '-') - reg2 = reg1; - else - { - ++s; - if (!reg_lookup (&s, freg ? RTYPE_FPU - : (RTYPE_GP | RTYPE_NUM), ®2)) - { - as_bad (_("invalid register list")); - break; - } - } - 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 == RA && reg2 == RA) - mask |= 1; - else - { - 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 'm': /* Register list for save insn. */ - case 'M': /* Register list for restore insn. */ - { - int opcode = ip->insn_opcode; - int framesz = 0, seen_framesz = 0; - int nargs = 0, statics = 0, sregs = 0; - - while (*s != '\0') - { - unsigned int reg1, reg2; - - SKIP_SPACE_TABS (s); - while (*s == ',') - ++s; - SKIP_SPACE_TABS (s); - - my_getExpression (&imm_expr, s); - if (imm_expr.X_op == O_constant) - { - /* Handle the frame size. */ - if (seen_framesz) - { - as_bad (_("more than one frame size in list")); - break; - } - seen_framesz = 1; - framesz = imm_expr.X_add_number; - imm_expr.X_op = O_absent; - s = expr_end; - continue; - } - - if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1)) - { - as_bad (_("can't parse register list")); - break; - } - - while (*s == ' ') - ++s; - - if (*s != '-') - reg2 = reg1; - else - { - ++s; - if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®2) - || reg2 < reg1) - { - as_bad (_("can't parse register list")); - break; - } - } - - while (reg1 <= reg2) - { - if (reg1 >= 4 && reg1 <= 7) - { - if (!seen_framesz) - /* args $a0-$a3 */ - nargs |= 1 << (reg1 - 4); - else - /* statics $a0-$a3 */ - statics |= 1 << (reg1 - 4); - } - else if ((reg1 >= 16 && reg1 <= 23) || reg1 == 30) - { - /* $s0-$s8 */ - sregs |= 1 << ((reg1 == 30) ? 8 : (reg1 - 16)); - } - else if (reg1 == 31) - { - /* Add $ra to insn. */ - opcode |= 0x40; - } - else - { - as_bad (_("unexpected register in list")); - break; - } - if (++reg1 == 24) - reg1 = 30; - } - } - - /* Encode args/statics combination. */ - if (nargs & statics) - as_bad (_("arg/static registers overlap")); - else if (nargs == 0xf) - /* All $a0-$a3 are args. */ - opcode |= MIPS16_ALL_ARGS << 16; - else if (statics == 0xf) - /* All $a0-$a3 are statics. */ - opcode |= MIPS16_ALL_STATICS << 16; - else - { - int narg = 0, nstat = 0; - - /* Count arg registers. */ - while (nargs & 0x1) - { - nargs >>= 1; - narg++; - } - if (nargs != 0) - as_bad (_("invalid arg register list")); - - /* Count static registers. */ - while (statics & 0x8) - { - statics = (statics << 1) & 0xf; - nstat++; - } - if (statics != 0) - as_bad (_("invalid static register list")); - - /* Encode args/statics. */ - opcode |= ((narg << 2) | nstat) << 16; - } - - /* Encode $s0/$s1. */ - if (sregs & (1 << 0)) /* $s0 */ - opcode |= 0x20; - if (sregs & (1 << 1)) /* $s1 */ - opcode |= 0x10; - sregs >>= 2; - - if (sregs != 0) - { - /* Count regs $s2-$s8. */ - int nsreg = 0; - while (sregs & 1) - { - sregs >>= 1; - nsreg++; - } - if (sregs != 0) - as_bad (_("invalid static register list")); - /* Encode $s2-$s8. */ - opcode |= nsreg << 24; - } - - /* Encode frame size. */ - if (!seen_framesz) - as_bad (_("missing frame size")); - else if ((framesz & 7) != 0 || framesz < 0 - || framesz > 0xff * 8) - as_bad (_("invalid frame size")); - else if (framesz != 128 || (opcode >> 16) != 0) - { - framesz /= 8; - opcode |= (((framesz & 0xf0) << 16) - | (framesz & 0x0f)); - } + break; + } - /* Finally build the instruction. */ - if ((opcode >> 16) != 0 || framesz == 0) - opcode |= MIPS16_EXTEND; - ip->insn_opcode = opcode; - } - continue; + operand = decode_mips16_operand (c, FALSE); + if (!operand) + abort (); - case 'e': /* extend code */ - my_getExpression (&imm_expr, s); - check_absolute_expr (ip, &imm_expr); - if ((unsigned long) imm_expr.X_add_number > 0x7ff) + /* '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') + { + ext_operand = decode_mips16_operand (c, TRUE); + if (operand != ext_operand) { - as_warn (_("Invalid value for `%s' (%lu)"), - ip->insn_mo->name, - (unsigned long) imm_expr.X_add_number); - imm_expr.X_add_number &= 0x7ff; + /* Parse the expression, allowing relocation operators. */ + i = my_getSmallExpression (&offset_expr, offset_reloc, s); + s = expr_end; + + if (offset_expr.X_op == O_register) + { + /* Handle elided offsets, which are equivalent to 0. */ + if (*s == '(') + { + offset_expr.X_op = O_constant; + offset_expr.X_add_number = 0; + relax_char = c; + continue; + } + /* Fail the match. */ + break; + } + /* '8' is used for SLTI(U) and has traditionally not + been allowed to take relocation operators. */ + if (i > 0 && (ext_operand->size != 16 || c == '8')) + break; + relax_char = c; + continue; } - ip->insn_opcode |= imm_expr.X_add_number; - imm_expr.X_op = O_absent; - s = expr_end; - continue; + } - default: - abort (); + s = match_operand (&arg, operand, s); + if (!s && arg.optional_reg) + { + /* Assume that the register has been elided and is the + same as the first operand. */ + arg.optional_reg = FALSE; + arg.argnum = 1; + s = argsstart; + SKIP_SPACE_TABS (s); + s = match_operand (&arg, operand, s); } - break; + if (!s) + break; + continue; } /* Args don't match. */ - if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] && - strcmp (insn->name, insn[1].name) == 0) + if (more_alts) { ++insn; s = argsstart;