diff options
Diffstat (limited to 'gas')
-rw-r--r-- | gas/ChangeLog | 21 | ||||
-rw-r--r-- | gas/config/tc-mips.c | 200 |
2 files changed, 107 insertions, 114 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog index ed231a6..9172532 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,24 @@ +2012-09-23 Richard Sandiford <rdsandiford@googlemail.com> + + * config/tc-mips.c (mips_cl_insn): Remove use_extend and extend. + (MIPS16_EXTEND): New macro. + (mips16_opcode_length): New function. + (insn_length): Use it. + (create_insn): Update after mips_cl_insn change. + (write_compressed_insn): New function. + (install_insn): Use it. + (append_insn): Use insn_length to check for unextended MIPS16 + instructions. + (mips16_macro_build): Update call to mips16_immed. + (mips16_ip): Likewise. Use MIPS16_EXTEND to force an extended + instruction. + (mips16_immed): Remove use_extend and extend; install EXTEND + opcodes in the upper 16 bits of *INSN instead. Keep the + instruction extended if it already is. Replace warn, small + and ext with a forced_insn_length-like parameter. + (md_convert_frag): Update call mips16_immed. + Use write_compressed_insn. + 2012-09-20 Michael Zolotukhin <michael.v.zolotukhin@intel.com> * config/tc-i386.c (cpu_arch): Add .cx16. diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c index 9e2183c..480f1b7 100644 --- a/gas/config/tc-mips.c +++ b/gas/config/tc-mips.c @@ -145,15 +145,10 @@ struct mips_cl_insn /* The opcode's entry in mips_opcodes or mips16_opcodes. */ const struct mips_opcode *insn_mo; - /* True if this is a mips16 instruction and if we want the extended - form of INSN_MO. */ - bfd_boolean use_extend; - - /* The 16-bit extension instruction to use when USE_EXTEND is true. */ - unsigned short extend; - /* The 16-bit or 32-bit bitstring of the instruction itself. This is - a copy of INSN_MO->match with the operands filled in. */ + a copy of INSN_MO->match with the operands filled in. If we have + decided to use an extended MIPS16 instruction, this includes the + extension. */ unsigned long insn_opcode; /* The frag that contains the instruction. */ @@ -1240,6 +1235,9 @@ static int mips_relax_branch; EXTRACT_BITS ((INSN).insn_opcode, \ MIPS16OP_MASK_##FIELD, \ MIPS16OP_SH_##FIELD) + +/* The MIPS16 EXTEND opcode, shifted left 16 places. */ +#define MIPS16_EXTEND (0xf000U << 16) /* Whether or not we are emitting a branch-likely macro. */ static bfd_boolean emit_branch_likely_macro = FALSE; @@ -1323,8 +1321,7 @@ static void mips16_macro (struct mips_cl_insn * ip); static void mips_ip (char *str, struct mips_cl_insn * ip); static void mips16_ip (char *str, struct mips_cl_insn * ip); static void mips16_immed - (char *, unsigned int, int, offsetT, bfd_boolean, bfd_boolean, bfd_boolean, - unsigned long *, bfd_boolean *, unsigned short *); + (char *, unsigned int, int, offsetT, unsigned int, unsigned long *); static size_t my_getSmallExpression (expressionS *, bfd_reloc_code_real_type *, char *); static void my_getExpression (expressionS *, char *); @@ -1635,6 +1632,14 @@ micromips_insn_length (const struct mips_opcode *mo) return (mo->mask >> 16) == 0 ? 2 : 4; } +/* Return the length of MIPS16 instruction OPCODE. */ + +static inline unsigned int +mips16_opcode_length (unsigned long opcode) +{ + return (opcode >> 16) == 0 ? 2 : 4; +} + /* Return the length of instruction INSN. */ static inline unsigned int @@ -1643,7 +1648,7 @@ insn_length (const struct mips_cl_insn *insn) if (mips_opts.micromips) return micromips_insn_length (insn->insn_mo); else if (mips_opts.mips16) - return insn->mips16_absolute_jump_p || insn->use_extend ? 4 : 2; + return mips16_opcode_length (insn->insn_opcode); else return 4; } @@ -1656,8 +1661,6 @@ create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo) size_t i; insn->insn_mo = mo; - insn->use_extend = FALSE; - insn->extend = 0; insn->insn_opcode = mo->match; insn->frag = NULL; insn->where = 0; @@ -1683,42 +1686,29 @@ mips_record_compressed_mode (void) si->tc_segment_info_data.micromips = mips_opts.micromips; } +/* Write microMIPS or MIPS16 instruction INSN to BUF, given that the + instruction is LENGTH bytes long. Return a pointer to the next byte. */ + +static char * +write_compressed_insn (char *buf, unsigned int insn, unsigned int length) +{ + unsigned int i; + + for (i = 0; i < length; i += 2) + md_number_to_chars (buf + i, insn >> ((length - i - 2) * 8), 2); + return buf + length; +} + /* Install INSN at the location specified by its "frag" and "where" fields. */ static void install_insn (const struct mips_cl_insn *insn) { char *f = insn->frag->fr_literal + insn->where; - if (!HAVE_CODE_COMPRESSION) - md_number_to_chars (f, insn->insn_opcode, 4); - else if (mips_opts.micromips) - { - unsigned int length = insn_length (insn); - if (length == 2) - md_number_to_chars (f, insn->insn_opcode, 2); - else if (length == 4) - { - md_number_to_chars (f, insn->insn_opcode >> 16, 2); - f += 2; - md_number_to_chars (f, insn->insn_opcode & 0xffff, 2); - } - else - as_bad (_("48-bit microMIPS instructions are not supported")); - } - else if (insn->mips16_absolute_jump_p) - { - md_number_to_chars (f, insn->insn_opcode >> 16, 2); - md_number_to_chars (f + 2, insn->insn_opcode & 0xffff, 2); - } + if (HAVE_CODE_COMPRESSION) + write_compressed_insn (f, insn->insn_opcode, insn_length (insn)); else - { - if (insn->use_extend) - { - md_number_to_chars (f, 0xf000 | insn->extend, 2); - f += 2; - } - md_number_to_chars (f, insn->insn_opcode, 2); - } + md_number_to_chars (f, insn->insn_opcode, 4); mips_record_compressed_mode (); } @@ -4274,9 +4264,7 @@ append_insn (struct mips_cl_insn *ip, expressionS *address_expr, history[0].mips16_absolute_jump_p), make_expr_symbol (address_expr), 0); } - else if (mips_opts.mips16 - && ! ip->use_extend - && *reloc_type != BFD_RELOC_MIPS16_JMP) + else if (mips_opts.mips16 && insn_length (ip) == 2) { if (!delayed_branch_p (ip)) /* Make sure there is enough room to swap this instruction with @@ -5191,9 +5179,8 @@ mips16_macro_build (expressionS *ep, const char *name, const char *fmt, *r = (int) BFD_RELOC_UNUSED + c; else { - mips16_immed (NULL, 0, c, ep->X_add_number, FALSE, FALSE, - FALSE, &insn.insn_opcode, &insn.use_extend, - &insn.extend); + mips16_immed (NULL, 0, c, ep->X_add_number, + 0, &insn.insn_opcode); ep = NULL; *r = BFD_RELOC_UNUSED; } @@ -13370,9 +13357,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip) *offset_reloc = BFD_RELOC_UNUSED; mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED, - tmp, TRUE, forced_insn_length == 2, - forced_insn_length == 4, &ip->insn_opcode, - &ip->use_extend, &ip->extend); + tmp, forced_insn_length, &ip->insn_opcode); imm_expr.X_op = O_absent; *imm_reloc = BFD_RELOC_UNUSED; } @@ -13552,8 +13537,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip) if (imm_expr.X_op != O_constant) { forced_insn_length = 4; - ip->use_extend = TRUE; - ip->extend = 0; + ip->insn_opcode |= MIPS16_EXTEND; } else { @@ -13707,7 +13691,7 @@ mips16_ip (char *str, struct mips_cl_insn *ip) case 'm': /* Register list for save insn. */ case 'M': /* Register list for restore insn. */ { - int opcode = 0; + int opcode = ip->insn_opcode; int framesz = 0, seen_framesz = 0; int nargs = 0, statics = 0, sregs = 0; @@ -13861,11 +13845,8 @@ mips16_ip (char *str, struct mips_cl_insn *ip) /* Finally build the instruction. */ if ((opcode >> 16) != 0 || framesz == 0) - { - ip->use_extend = TRUE; - ip->extend = opcode >> 16; - } - ip->insn_opcode |= opcode & 0x7f; + opcode |= MIPS16_EXTEND; + ip->insn_opcode = opcode; } continue; @@ -13960,23 +13941,19 @@ static const struct mips16_immed_operand mips16_immed_operands[] = #define MIPS16_NUM_IMMED \ (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0]) -/* Handle a mips16 instruction with an immediate value. This or's the - small immediate value into *INSN. It sets *USE_EXTEND to indicate - whether an extended value is needed; if one is needed, it sets - *EXTEND to the value. The argument type is TYPE. The value is VAL. - If SMALL is true, an unextended opcode was explicitly requested. - If EXT is true, an extended opcode was explicitly requested. If - WARN is true, warn if EXT does not match reality. */ +/* Install immediate value VAL into MIPS16 instruction *INSN, + extending it if necessary. The instruction in *INSN may + already be extended. + + TYPE is the type of the immediate field. USER_INSN_LENGTH is the + length that the user requested, or 0 if none. */ static void mips16_immed (char *file, unsigned int line, int type, offsetT val, - bfd_boolean warn, bfd_boolean small, bfd_boolean ext, - unsigned long *insn, bfd_boolean *use_extend, - unsigned short *extend) + unsigned int user_insn_length, unsigned long *insn) { const struct mips16_immed_operand *op; int mintiny, maxtiny; - bfd_boolean needext; op = mips16_immed_operands; while (op->type != type) @@ -14011,21 +13988,26 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val, if ((val & ((1 << op->shift) - 1)) != 0 || val < (mintiny << op->shift) || val > (maxtiny << op->shift)) - needext = TRUE; - else - needext = FALSE; - - if (warn && ext && ! needext) - as_warn_where (file, line, - _("extended operand requested but not required")); - if (small && needext) - as_bad_where (file, line, _("invalid unextended operand value")); + { + /* We need an extended instruction. */ + if (user_insn_length == 2) + as_bad_where (file, line, _("invalid unextended operand value")); + else + *insn |= MIPS16_EXTEND; + } + else if (user_insn_length == 4) + { + /* The operand doesn't force an unextended instruction to be extended. + Warn if the user wanted an extended instruction anyway. */ + *insn |= MIPS16_EXTEND; + as_warn_where (file, line, + _("extended operand requested but not required")); + } - if (small || (! ext && ! needext)) + if (mips16_opcode_length (*insn) == 2) { int insnval; - *use_extend = FALSE; insnval = ((val >> op->shift) & ((1 << op->nbits) - 1)); insnval <<= op->op_shift; *insn |= insnval; @@ -14049,7 +14031,6 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val, as_bad_where (file, line, _("operand value out of range for instruction")); - *use_extend = TRUE; if (op->extbits == 16) { extval = ((val >> 11) & 0x1f) | (val & 0x7e0); @@ -14066,8 +14047,7 @@ mips16_immed (char *file, unsigned int line, int type, offsetT val, val = 0; } - *extend = (unsigned short) extval; - *insn |= val; + *insn |= (extval << 16) | val; } } @@ -18250,29 +18230,18 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) { int type; const struct mips16_immed_operand *op; - bfd_boolean small, ext; offsetT val; - bfd_byte *buf; + char *buf; + unsigned int user_length, length; unsigned long insn; - bfd_boolean use_extend; - unsigned short extend; + bfd_boolean ext; type = RELAX_MIPS16_TYPE (fragp->fr_subtype); op = mips16_immed_operands; while (op->type != type) ++op; - if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype)) - { - small = FALSE; - ext = TRUE; - } - else - { - small = TRUE; - ext = FALSE; - } - + ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype); val = resolve_symbol_value (fragp->fr_symbol); if (op->pcrel) { @@ -18312,27 +18281,30 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp) as_warn_where (fragp->fr_file, fragp->fr_line, _("extended instruction in delay slot")); - buf = (bfd_byte *) (fragp->fr_literal + fragp->fr_fix); + buf = fragp->fr_literal + fragp->fr_fix; if (target_big_endian) - insn = bfd_getb16 (buf); + insn = bfd_getb16 ((bfd_byte *) buf); else - insn = bfd_getl16 (buf); + insn = bfd_getl16 ((bfd_byte *) buf); - mips16_immed (fragp->fr_file, fragp->fr_line, type, val, - RELAX_MIPS16_USER_EXT (fragp->fr_subtype), - small, ext, &insn, &use_extend, &extend); + if (ext) + insn |= MIPS16_EXTEND; - if (use_extend) - { - md_number_to_chars ((char *) buf, 0xf000 | extend, 2); - fragp->fr_fix += 2; - buf += 2; - } + if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype)) + user_length = 4; + else if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype)) + user_length = 2; + else + user_length = 0; + + mips16_immed (fragp->fr_file, fragp->fr_line, type, val, + user_length, &insn); - md_number_to_chars ((char *) buf, insn, 2); - fragp->fr_fix += 2; - buf += 2; + length = (ext ? 4 : 2); + gas_assert (mips16_opcode_length (insn) == length); + write_compressed_insn (buf, insn, length); + fragp->fr_fix += length; } else { |