diff options
author | Richard Sandiford <rdsandiford@googlemail.com> | 2013-07-14 13:44:25 +0000 |
---|---|---|
committer | Richard Sandiford <rdsandiford@googlemail.com> | 2013-07-14 13:44:25 +0000 |
commit | a1d785644e079c4c8de128e3148e7c7886352df5 (patch) | |
tree | d4fd536d8596324e4104382ac83e72cbbc083090 /gas | |
parent | e077a1c8de38e8ad1ce26e127b24ed4ecf77a840 (diff) | |
download | gdb-a1d785644e079c4c8de128e3148e7c7886352df5.zip gdb-a1d785644e079c4c8de128e3148e7c7886352df5.tar.gz gdb-a1d785644e079c4c8de128e3148e7c7886352df5.tar.bz2 |
gas/
* config/tc-mips.c (mips_oddfpreg_ok): Move further up file.
Change return type to bfd_boolean.
(report_bad_range, report_bad_field): New functions.
(mips_arg_info): New structure.
(match_const_int, convert_reg_type, check_regno, match_int_operand)
(match_mapped_int_operand, match_msb_operand, match_reg_operand)
(match_reg_pair_operand, match_pcrel_operand, match_perf_reg_operand)
(match_addiusp_operand, match_clo_clz_dest_operand)
(match_lwm_swm_list_operand, match_mdmx_imm_reg_operand)
(match_pc_operand, match_tied_reg_operand, match_operand)
(check_completed_insn): New functions, commented out for now.
Diffstat (limited to 'gas')
-rw-r--r-- | gas/ChangeLog | 14 | ||||
-rw-r--r-- | gas/config/tc-mips.c | 881 |
2 files changed, 855 insertions, 40 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index 353474f..77dc629 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,5 +1,19 @@ 2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * config/tc-mips.c (mips_oddfpreg_ok): Move further up file. + Change return type to bfd_boolean. + (report_bad_range, report_bad_field): New functions. + (mips_arg_info): New structure. + (match_const_int, convert_reg_type, check_regno, match_int_operand) + (match_mapped_int_operand, match_msb_operand, match_reg_operand) + (match_reg_pair_operand, match_pcrel_operand, match_perf_reg_operand) + (match_addiusp_operand, match_clo_clz_dest_operand) + (match_lwm_swm_list_operand, match_mdmx_imm_reg_operand) + (match_pc_operand, match_tied_reg_operand, match_operand) + (check_completed_insn): New functions, commented out for now. + +2013-07-14 Richard Sandiford <rdsandiford@googlemail.com> + * config/tc-mips.c (insn_insert_operand): New function. (macro_build, mips16_macro_build): Put null character check in the for loop and convert continues to breaks. Use operand diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index 2a33f42..e0e2d1d 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -3702,6 +3702,847 @@ fpr_write_mask (const struct mips_cl_insn *ip) return mask; } +/* Operand OPNUM of INSN is an odd-numbered floating-point register. + Check whether that is allowed. */ + +static bfd_boolean +mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum) +{ + const char *s = insn->name; + + if (insn->pinfo == INSN_MACRO) + /* Let a macro pass, we'll catch it later when it is expanded. */ + return TRUE; + + 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)) + { + case FP_S: + case 0: + return TRUE; + case FP_D: + return FALSE; + default: + break; + } + + /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */ + s = strchr (insn->name, '.'); + if (s != NULL && opnum == 2) + s = strchr (s + 1, '.'); + return (s != NULL && (s[1] == 'w' || s[1] == 's')); + } + + /* Single-precision coprocessor loads and moves are OK too. */ + if ((insn->pinfo & FP_S) + && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY + | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY))) + return TRUE; + + return FALSE; +} + +#if 0 +/* Report that user-supplied argument ARGNUM for INSN was VAL, but should + have been in the range [MIN_VAL, MAX_VAL]. PRINT_HEX says whether + this operand is normally printed in hex or decimal. */ + +static void +report_bad_range (struct mips_cl_insn *insn, int argnum, + offsetT val, int min_val, int max_val, + bfd_boolean print_hex) +{ + if (print_hex && val >= 0) + as_bad (_("Operand %d of `%s' must be in the range [0x%x, 0x%x]," + " was 0x%lx."), + argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val); + else if (print_hex) + as_bad (_("Operand %d of `%s' must be in the range [0x%x, 0x%x]," + " was %ld."), + argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val); + else + as_bad (_("Operand %d of `%s' must be in the range [%d, %d]," + " was %ld."), + argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val); +} + +/* Report an invalid combination of position and size operands for a bitfield + operation. POS and SIZE are the values that were given. */ + +static void +report_bad_field (offsetT pos, offsetT size) +{ + as_bad (_("Invalid field specification (position %ld, size %ld)"), + (unsigned long) pos, (unsigned long) size); +} + +/* Information about an instruction argument that we're trying to match. */ +struct mips_arg_info +{ + /* The instruction so far. */ + struct mips_cl_insn *insn; + + /* The 1-based operand number, in terms of insn->insn_mo->args. */ + int opnum; + + /* The 1-based argument number, for error reporting. This does not + count elided optional registers, etc.. */ + int argnum; + + /* The last OP_REG operand seen, or ILLEGAL_REG if none. */ + unsigned int last_regno; + + /* If the first operand was an OP_REG, this is the register that it + specified, otherwise it is ILLEGAL_REG. */ + unsigned int dest_regno; + + /* The value of the last OP_INT operand. Only used for OP_MSB, + where it gives the lsb position. */ + unsigned int last_op_int; + + /* If true, match routines should silently reject invalid arguments. + If false, match routines can accept invalid arguments as long as + they report an appropriate error. They still have the option of + silently rejecting arguments, in which case a generic "Invalid operands" + style of error will be used instead. */ + bfd_boolean soft_match; + + /* If true, the OP_INT match routine should treat plain symbolic operands + as if a relocation operator like %lo(...) had been used. This is only + ever true if the operand can be relocated. */ + bfd_boolean allow_nonconst; + + /* When true, the OP_INT match routine should allow unsigned N-bit + arguments to be used where a signed N-bit operand is expected. */ + bfd_boolean lax_max; + + /* When true, the OP_REG match routine should assume that another operand + appears after this one. It should fail the match if the register it + sees is at the end of the argument list. */ + bfd_boolean optional_reg; + + /* True if a reference to the current AT register was seen. */ + bfd_boolean seen_at; +}; + +/* Match a constant integer at S for ARG. Return null if the match failed. + Otherwise return the end of the matched string and store the constant value + in *VALUE. In the latter case, use FALLBACK as the value if the match + succeeded with an error. */ + +static char * +match_const_int (struct mips_arg_info *arg, char *s, offsetT *value, + offsetT fallback) +{ + expressionS ex; + bfd_reloc_code_real_type r[3]; + int num_relocs; + + num_relocs = my_getSmallExpression (&ex, r, s); + if (*s == '(' && ex.X_op == O_register) + { + /* Assume that the constant has been elided and that S is a base + register. The rest of the match will fail if the assumption + turns out to be wrong. */ + *value = 0; + return s; + } + + if (num_relocs == 0 && ex.X_op == O_constant) + *value = ex.X_add_number; + else + { + /* If we got a register rather than an expression, the default + "Invalid operands" style of error seems more appropriate. */ + if (arg->soft_match || ex.X_op == O_register) + return 0; + as_bad (_("Operand %d of `%s' must be constant"), + arg->argnum, arg->insn->insn_mo->name); + *value = fallback; + } + return expr_end; +} + +/* Return the RTYPE_* flags for a register operand of type TYPE that + appears in instruction OPCODE. */ + +static unsigned int +convert_reg_type (const struct mips_opcode *opcode, + enum mips_reg_operand_type type) +{ + switch (type) + { + case OP_REG_GP: + return RTYPE_NUM | RTYPE_GP; + + case OP_REG_FP: + /* Allow vector register names for MDMX if the instruction is a 64-bit + FPR load, store or move (including moves to and from GPRs). */ + if ((mips_opts.ase & ASE_MDMX) + && (opcode->pinfo & FP_D) + && (opcode->pinfo & (INSN_COPROC_MOVE_DELAY + | INSN_COPROC_MEMORY_DELAY + | INSN_LOAD_COPROC_DELAY + | INSN_LOAD_MEMORY_DELAY + | INSN_STORE_MEMORY))) + return RTYPE_FPU | RTYPE_VEC; + return RTYPE_FPU; + + case OP_REG_CCC: + if (opcode->pinfo & (FP_D | FP_S)) + return RTYPE_CCC | RTYPE_FCC; + return RTYPE_CCC; + + case OP_REG_VEC: + if (opcode->membership & INSN_5400) + return RTYPE_FPU; + return RTYPE_FPU | RTYPE_VEC; + + case OP_REG_ACC: + return RTYPE_ACC; + + case OP_REG_COPRO: + if (opcode->name[strlen (opcode->name) - 1] == '0') + return RTYPE_NUM | RTYPE_CP0; + return RTYPE_NUM; + + case OP_REG_HW: + return RTYPE_NUM; + } + abort (); +} + +/* ARG is register REGNO, of type TYPE. Warn about any dubious registers. */ + +static void +check_regno (struct mips_arg_info *arg, + enum mips_reg_operand_type type, unsigned int regno) +{ + if (AT && type == OP_REG_GP && regno == AT) + arg->seen_at = TRUE; + + if (type == OP_REG_FP + && (regno & 1) != 0 + && HAVE_32BIT_FPRS + && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum)) + as_warn (_("Float register should be even, was %d"), regno); + + if (type == OP_REG_CCC) + { + const char *name; + size_t length; + + name = arg->insn->insn_mo->name; + length = strlen (name); + if ((regno & 1) != 0 + && ((length >= 3 && strcmp (name + length - 3, ".ps") == 0) + || (length >= 5 && strncmp (name + length - 5, "any2", 4) == 0))) + as_warn (_("Condition code register should be even for %s, was %d"), + name, regno); + + if ((regno & 3) != 0 + && (length >= 5 && strncmp (name + length - 5, "any4", 4) == 0)) + as_warn (_("Condition code register should be 0 or 4 for %s, was %d"), + name, regno); + } +} + +/* OP_INT matcher. */ + +static char * +match_int_operand (struct mips_arg_info *arg, + const struct mips_operand *operand_base, char *s) +{ + const struct mips_int_operand *operand; + unsigned int uval, mask; + int min_val, max_val, factor; + offsetT sval; + bfd_boolean print_hex; + + operand = (const struct mips_int_operand *) operand_base; + factor = 1 << operand->shift; + mask = (1 << operand_base->size) - 1; + max_val = (operand->max_val + operand->bias) << operand->shift; + min_val = max_val - (mask << operand->shift); + if (arg->lax_max) + max_val = mask << operand->shift; + + if (operand_base->lsb == 0 + && operand_base->size == 16 + && operand->shift == 0 + && operand->bias == 0 + && (operand->max_val == 32767 || operand->max_val == 65535)) + { + /* The operand can be relocated. */ + offset_reloc[0] = BFD_RELOC_LO16; + offset_reloc[1] = BFD_RELOC_UNUSED; + offset_reloc[2] = BFD_RELOC_UNUSED; + if (my_getSmallExpression (&offset_expr, offset_reloc, s) > 0) + /* Relocation operators were used. Accept the arguent and + leave the relocation value in offset_expr and offset_relocs + for the caller to process. */ + return expr_end; + if (*s == '(' && offset_expr.X_op == O_register) + /* Assume that the constant has been elided and that S is a base + register. The rest of the match will fail if the assumption + turns out to be wrong. */ + sval = 0; + else + { + s = expr_end; + if (offset_expr.X_op != O_constant) + /* If non-constant operands are allowed then leave them for + the caller to process, otherwise fail the match. */ + return arg->allow_nonconst ? s : 0; + sval = offset_expr.X_add_number; + } + /* Clear the global state; we're going to install the operand + ourselves. */ + offset_reloc[0] = BFD_RELOC_UNUSED; + offset_expr.X_op = O_absent; + } + else + { + s = match_const_int (arg, s, &sval, min_val); + if (!s) + return 0; + } + + arg->last_op_int = sval; + + /* Check the range. If there's a problem, record the lowest acceptable + value in arg->last_op_int in order to prevent an unhelpful error + from OP_MSB too. + + Bit counts have traditionally been printed in hex by the disassembler + but printed as decimal in error messages. Only resort to hex if + the operand is bigger than 6 bits. */ + print_hex = operand->print_hex && operand_base->size > 6; + if (sval < min_val || sval > max_val) + { + if (arg->soft_match) + return 0; + report_bad_range (arg->insn, arg->argnum, sval, min_val, max_val, + print_hex); + arg->last_op_int = min_val; + } + else if (sval % factor) + { + if (arg->soft_match) + return 0; + as_bad (print_hex && sval >= 0 + ? _("Operand %d of `%s' must be a factor of %d, was 0x%lx.") + : _("Operand %d of `%s' must be a factor of %d, was %ld."), + arg->argnum, arg->insn->insn_mo->name, factor, + (unsigned long) sval); + arg->last_op_int = min_val; + } + + uval = (unsigned int) sval >> operand->shift; + uval -= operand->bias; + + /* Handle -mfix-cn63xxp1. */ + if (arg->opnum == 1 + && mips_fix_cn63xxp1 + && !mips_opts.micromips + && strcmp ("pref", arg->insn->insn_mo->name) == 0) + switch (uval) + { + case 5: + case 25: + case 26: + case 27: + case 28: + case 29: + case 30: + case 31: + /* These are ok. */ + break; + + default: + /* The rest must be changed to 28. */ + uval = 28; + break; + } + + insn_insert_operand (arg->insn, operand_base, uval); + return s; +} + +/* OP_MAPPED_INT matcher. */ + +static char * +match_mapped_int_operand (struct mips_arg_info *arg, + const struct mips_operand *operand_base, char *s) +{ + const struct mips_mapped_int_operand *operand; + unsigned int uval, num_vals; + offsetT sval; + + operand = (const struct mips_mapped_int_operand *) operand_base; + s = match_const_int (arg, s, &sval, operand->int_map[0]); + if (!s) + return 0; + + num_vals = 1 << operand_base->size; + for (uval = 0; uval < num_vals; uval++) + if (operand->int_map[uval] == sval) + break; + if (uval == num_vals) + return 0; + + insn_insert_operand (arg->insn, operand_base, uval); + return s; +} + +/* OP_MSB matcher. */ + +static char * +match_msb_operand (struct mips_arg_info *arg, + const struct mips_operand *operand_base, char *s) +{ + const struct mips_msb_operand *operand; + int min_val, max_val, max_high; + offsetT size, sval, high; + + operand = (const struct mips_msb_operand *) operand_base; + min_val = operand->bias; + max_val = min_val + (1 << operand_base->size) - 1; + max_high = operand->opsize; + + s = match_const_int (arg, s, &size, 1); + if (!s) + return 0; + + high = size + arg->last_op_int; + sval = operand->add_lsb ? high : size; + + if (size < 0 || high > max_high || sval < min_val || sval > max_val) + { + if (arg->soft_match) + return 0; + report_bad_field (arg->last_op_int, size); + sval = min_val; + } + insn_insert_operand (arg->insn, operand_base, sval - min_val); + return s; +} + +/* OP_REG matcher. */ + +static char * +match_reg_operand (struct mips_arg_info *arg, + const struct mips_operand *operand_base, char *s) +{ + const struct mips_reg_operand *operand; + unsigned int regno, uval, num_vals, types; + + operand = (const struct mips_reg_operand *) operand_base; + types = convert_reg_type (arg->insn->insn_mo, operand->reg_type); + if (!reg_lookup (&s, types, ®no)) + return 0; + + SKIP_SPACE_TABS (s); + if (arg->optional_reg && *s == 0) + return 0; + + if (operand->reg_map) + { + num_vals = 1 << operand->root.size; + for (uval = 0; uval < num_vals; uval++) + if (operand->reg_map[uval] == regno) + break; + if (num_vals == uval) + return 0; + } + else + uval = regno; + + check_regno (arg, operand->reg_type, regno); + arg->last_regno = regno; + if (arg->opnum == 1) + arg->dest_regno = regno; + insn_insert_operand (arg->insn, operand_base, uval); + return s; +} + +/* OP_REG_PAIR matcher. */ + +static char * +match_reg_pair_operand (struct mips_arg_info *arg, + const struct mips_operand *operand_base, char *s) +{ + const struct mips_reg_pair_operand *operand; + unsigned int regno1, regno2, uval, num_vals, types; + + operand = (const struct mips_reg_pair_operand *) operand_base; + types = convert_reg_type (arg->insn->insn_mo, operand->reg_type); + + if (!reg_lookup (&s, types, ®no1)) + return 0; + + SKIP_SPACE_TABS (s); + if (*s++ != ',') + return 0; + arg->argnum += 1; + + if (!reg_lookup (&s, types, ®no2)) + return 0; + + num_vals = 1 << operand_base->size; + for (uval = 0; uval < num_vals; uval++) + if (operand->reg1_map[uval] == regno1 && operand->reg2_map[uval] == regno2) + break; + if (uval == num_vals) + return 0; + + check_regno (arg, operand->reg_type, regno1); + check_regno (arg, operand->reg_type, regno2); + insn_insert_operand (arg->insn, operand_base, uval); + return s; +} + +/* OP_PCREL matcher. The caller chooses the relocation type. */ + +static char * +match_pcrel_operand (char *s) +{ + my_getExpression (&offset_expr, s); + return expr_end; +} + +/* OP_PERF_REG matcher. */ + +static char * +match_perf_reg_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + offsetT sval; + + s = match_const_int (arg, s, &sval, 0); + if (!s) + return 0; + + if (sval != 0 + && (sval != 1 + || (mips_opts.arch == CPU_R5900 + && (strcmp (arg->insn->insn_mo->name, "mfps") == 0 + || strcmp (arg->insn->insn_mo->name, "mtps") == 0)))) + { + if (arg->soft_match) + return 0; + as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval); + } + + insn_insert_operand (arg->insn, operand, sval); + return s; +} + +/* OP_ADDIUSP matcher. */ + +static char * +match_addiusp_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + offsetT sval; + unsigned int uval; + + s = match_const_int (arg, s, &sval, -256); + if (!s) + return 0; + + if (sval % 4) + return 0; + + sval /= 4; + if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1)) + return 0; + + uval = (unsigned int) sval; + uval = ((uval >> 1) & ~0xff) | (uval & 0xff); + insn_insert_operand (arg->insn, operand, uval); + return s; +} + +/* OP_CLO_CLZ_DEST matcher. */ + +static char * +match_clo_clz_dest_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + unsigned int regno; + + if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no)) + return 0; + + check_regno (arg, OP_REG_GP, regno); + insn_insert_operand (arg->insn, operand, regno | (regno << 5)); + return s; +} + +/* OP_LWM_SWM_LIST matcher. */ + +static char * +match_lwm_swm_list_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + unsigned int reglist, sregs, ra; + + if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list)) + return 0; + + if (operand->size == 2) + { + /* The list must include both ra and s0-sN, for 0 <= N <= 3. E.g.: + + s0, ra + s0, s1, ra, s2, s3 + s0-s2, ra + + and any permutations of these. */ + if ((reglist & 0xfff1ffff) != 0x80010000) + return 0; + + sregs = (reglist >> 17) & 7; + ra = 0; + } + else + { + /* The list must include at least one of ra and s0-sN, + for 0 <= N <= 8. (Note that there is a gap between s7 and s8, + which are $23 and $30 respectively.) E.g.: + + ra + s0 + ra, s0, s1, s2 + s0-s8 + s0-s5, ra + + and any permutations of these. */ + if ((reglist & 0x3f00ffff) != 0) + return 0; + + ra = (reglist >> 27) & 0x10; + sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff); + } + sregs += 1; + if ((sregs & -sregs) != sregs) + return 0; + + insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra); + return s; +} + +/* OP_MDMX_IMM_REG matcher. */ + +static char * +match_mdmx_imm_reg_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + unsigned int regno, uval, types; + bfd_boolean is_qh; + const struct mips_opcode *opcode; + + /* The mips_opcode records whether this is an octobyte or quadhalf + instruction. Start out with that bit in place. */ + opcode = arg->insn->insn_mo; + uval = mips_extract_operand (operand, opcode->match); + is_qh = (uval != 0); + + types = convert_reg_type (arg->insn->insn_mo, OP_REG_VEC); + if (reg_lookup (&s, types, ®no)) + { + if ((opcode->membership & INSN_5400) + && strcmp (opcode->name, "rzu.ob") == 0) + { + if (arg->soft_match) + return 0; + as_bad (_("Operand %d of `%s' must be an immediate"), + arg->argnum, opcode->name); + } + + /* Check whether this is a vector register or a broadcast of + a single element. */ + SKIP_SPACE_TABS (s); + if (*s == '[') + { + /* Read the element number. */ + expressionS value; + + ++s; + SKIP_SPACE_TABS (s); + my_getExpression (&value, s); + s = expr_end; + if (value.X_op != O_constant + || value.X_add_number < 0 + || value.X_add_number > (is_qh ? 3 : 7)) + { + if (arg->soft_match) + return 0; + as_bad (_("Invalid element selector")); + value.X_add_number = 0; + } + uval |= (unsigned int) value.X_add_number << (is_qh ? 2 : 1) << 5; + SKIP_SPACE_TABS (s); + if (*s == ']') + ++s; + else + { + if (arg->soft_match) + return 0; + as_bad (_("Expecting ']' found '%s'"), s); + } + } + else + { + /* A full vector. */ + if ((opcode->membership & INSN_5400) + && (strcmp (opcode->name, "sll.ob") == 0 + || strcmp (opcode->name, "srl.ob") == 0)) + { + if (arg->soft_match) + return 0; + as_bad (_("Operand %d of `%s' must be scalar"), + arg->argnum, opcode->name); + } + + if (is_qh) + uval |= MDMX_FMTSEL_VEC_QH << 5; + else + uval |= MDMX_FMTSEL_VEC_OB << 5; + } + check_regno (arg, OP_REG_FP, regno); + uval |= regno; + } + else + { + offsetT sval; + + s = match_const_int (arg, s, &sval, 0); + if (!s) + return 0; + if (sval < 0 || sval > 31) + { + if (arg->soft_match) + return 0; + report_bad_range (arg->insn, arg->argnum, sval, 0, 31, FALSE); + } + uval |= (sval & 31); + if (is_qh) + uval |= MDMX_FMTSEL_IMM_QH << 5; + else + uval |= MDMX_FMTSEL_IMM_OB << 5; + } + insn_insert_operand (arg->insn, operand, uval); + return s; +} + +/* OP_PC matcher. */ + +static char * +match_pc_operand (char *s) +{ + if (strncmp (s, "$pc", 3) != 0) + return 0; + s += 3; + SKIP_SPACE_TABS (s); + return s; +} + +/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher. OTHER_REGNO is the + register that we need to match. */ + +static char * +match_tied_reg_operand (struct mips_arg_info *arg, char *s, + unsigned int other_regno) +{ + unsigned int regno; + + if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no) + || regno != other_regno) + return 0; + SKIP_SPACE_TABS (s); + if (arg->optional_reg && *s == 0) + return 0; + return s; +} + +/* S is the text seen for ARG. Match it against OPERAND. Return the end + of the argument text if the match is successful, otherwise return null. */ + +static char * +match_operand (struct mips_arg_info *arg, + const struct mips_operand *operand, char *s) +{ + switch (operand->type) + { + case OP_INT: + return match_int_operand (arg, operand, s); + + case OP_MAPPED_INT: + return match_mapped_int_operand (arg, operand, s); + + case OP_MSB: + return match_msb_operand (arg, operand, s); + + case OP_REG: + return match_reg_operand (arg, operand, s); + + case OP_REG_PAIR: + return match_reg_pair_operand (arg, operand, s); + + case OP_PCREL: + return match_pcrel_operand (s); + + case OP_PERF_REG: + return match_perf_reg_operand (arg, operand, s); + + case OP_ADDIUSP_INT: + return match_addiusp_operand (arg, operand, s); + + case OP_CLO_CLZ_DEST: + return match_clo_clz_dest_operand (arg, operand, s); + + case OP_LWM_SWM_LIST: + return match_lwm_swm_list_operand (arg, operand, s); + + case OP_ENTRY_EXIT_LIST: + case OP_SAVE_RESTORE_LIST: + abort (); + + case OP_MDMX_IMM_REG: + return match_mdmx_imm_reg_operand (arg, operand, s); + + case OP_REPEAT_DEST_REG: + return match_tied_reg_operand (arg, s, arg->dest_regno); + + case OP_REPEAT_PREV_REG: + return match_tied_reg_operand (arg, s, arg->last_regno); + + case OP_PC: + return match_pc_operand (s); + } + abort (); +} + +/* ARG is the state after successfully matching an instruction. + Issue any queued-up warnings. */ + +static void +check_completed_insn (struct mips_arg_info *arg) +{ + if (arg->seen_at) + { + if (AT == ATREG) + as_warn (_("Used $at without \".set noat\"")); + else + as_warn (_("Used $%u with \".set at=$%u\""), AT, AT); + } +} +#endif + /* Classify an instruction according to the FIX_VR4120_* enumeration. Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected by VR4120 errata. */ @@ -10676,46 +11517,6 @@ static const struct mips_immed mips_immed[] = { { 0,0,0,0 } }; -/* Check whether an odd floating-point register is allowed. */ -static int -mips_oddfpreg_ok (const struct mips_opcode *insn, int argnum) -{ - const char *s = insn->name; - - if (insn->pinfo == INSN_MACRO) - /* Let a macro pass, we'll catch it later when it is expanded. */ - return 1; - - 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)) - { - case FP_S: - case 0: - return 1; /* both single precision - ok */ - case FP_D: - return 0; /* both double precision - fail */ - default: - break; - } - - /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */ - s = strchr (insn->name, '.'); - if (argnum == 2) - s = s != NULL ? strchr (s + 1, '.') : NULL; - return (s != NULL && (s[1] == 'w' || s[1] == 's')); - } - - /* Single-precision coprocessor loads and moves are OK too. */ - if ((insn->pinfo & FP_S) - && (insn->pinfo & (INSN_COPROC_MEMORY_DELAY | INSN_STORE_MEMORY - | INSN_LOAD_COPROC_DELAY | INSN_COPROC_MOVE_DELAY))) - return 1; - - return 0; -} - /* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive) taking bits from BIT up. */ static int |