#define DBG(x)
#endif
+#define SKIP_SPACE_TABS(S) \
+ do { while (*(S) == ' ' || *(S) == '\t') ++(S); } while (0)
+
/* Clean up namespace so we can include obj-elf.h too. */
static int mips_output_flavor (void);
static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
/* 1 if -msingle-float, 0 if -mdouble-float. The default is 0. */
static int file_mips_single_float = 0;
+/* True if -mnan=2008, false if -mnan=legacy. */
+static bfd_boolean mips_flag_nan2008 = FALSE;
+
static struct mips_set_options mips_opts =
{
/* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
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[] =
16, 17, 2, 3, 4, 5, 6, 7
};
-/* Map normal MIPS register numbers to microMIPS register numbers. */
-
-#define mips32_to_micromips_reg_b_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_c_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_d_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_e_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_f_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_g_map mips32_to_16_reg_map
-#define mips32_to_micromips_reg_l_map mips32_to_16_reg_map
-
-#define X ILLEGAL_REG
-/* reg type h: 4, 5, 6. */
-static const int mips32_to_micromips_reg_h_map[] =
-{
- X, X, X, X, 4, 5, 6, X,
- X, X, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X
-};
-
-/* reg type m: 0, 17, 2, 3, 16, 18, 19, 20. */
-static const int mips32_to_micromips_reg_m_map[] =
-{
- 0, X, 2, 3, X, X, X, X,
- X, X, X, X, X, X, X, X,
- 4, 1, 5, 6, 7, X, X, X,
- X, X, X, X, X, X, X, X
-};
-
-/* reg type q: 0, 2-7. 17. */
-static const int mips32_to_micromips_reg_q_map[] =
-{
- 0, X, 2, 3, 4, 5, 6, 7,
- X, X, X, X, X, X, X, X,
- X, 1, X, X, X, X, X, X,
- X, X, X, X, X, X, X, X
-};
-
-#define mips32_to_micromips_reg_n_map mips32_to_micromips_reg_m_map
-#undef X
-
/* Map microMIPS register numbers to normal MIPS register numbers. */
#define micromips_to_32_reg_b_map mips16_to_32_reg_map
#define micromips_to_32_reg_g_map mips16_to_32_reg_map
/* The microMIPS registers with type h. */
-static const unsigned int micromips_to_32_reg_h_map[] =
+static const unsigned int micromips_to_32_reg_h_map1[] =
{
5, 5, 6, 4, 4, 4, 4, 4
};
-
-/* The microMIPS registers with type i. */
-static const unsigned int micromips_to_32_reg_i_map[] =
+static const unsigned int micromips_to_32_reg_h_map2[] =
{
6, 7, 7, 21, 22, 5, 6, 7
};
0, 17, 2, 3, 4, 5, 6, 7
};
-/* microMIPS imm type B. */
-static const int micromips_imm_b_map[] =
-{
- 1, 4, 8, 12, 16, 20, 24, -1
-};
-
-/* microMIPS imm type C. */
-static const int micromips_imm_c_map[] =
-{
- 128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, 255, 32768, 65535
-};
-
/* Classifies the kind of instructions we're interested in when
implementing -mfix-vr4120. */
enum fix_vr4120_class
(((x) &~ (offsetT) 0xffffffff) == 0 \
|| (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
-/* Replace bits MASK << SHIFT of STRUCT with the equivalent bits in
- VALUE << SHIFT. VALUE is evaluated exactly once. */
-#define INSERT_BITS(STRUCT, VALUE, MASK, SHIFT) \
- (STRUCT) = (((STRUCT) & ~((MASK) << (SHIFT))) \
- | (((VALUE) & (MASK)) << (SHIFT)))
-
/* Extract bits MASK << SHIFT from STRUCT and shift them right
SHIFT places. */
#define EXTRACT_BITS(STRUCT, MASK, SHIFT) \
(((STRUCT) >> (SHIFT)) & (MASK))
-/* Change INSN's opcode so that the operand given by FIELD has value VALUE.
- INSN is a mips_cl_insn structure and VALUE is evaluated exactly once.
-
- include/opcode/mips.h specifies operand fields using the macros
- OP_MASK_<FIELD> and OP_SH_<FIELD>. The MIPS16 equivalents start
- with "MIPS16OP" instead of "OP". */
-#define INSERT_OPERAND(MICROMIPS, FIELD, INSN, VALUE) \
- do \
- if (!(MICROMIPS)) \
- INSERT_BITS ((INSN).insn_opcode, VALUE, \
- OP_MASK_##FIELD, OP_SH_##FIELD); \
- else \
- INSERT_BITS ((INSN).insn_opcode, VALUE, \
- MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD); \
- while (0)
-#define MIPS16_INSERT_OPERAND(FIELD, INSN, VALUE) \
- INSERT_BITS ((INSN).insn_opcode, VALUE, \
- MIPS16OP_MASK_##FIELD, MIPS16OP_SH_##FIELD)
-
/* Extract the operand given by FIELD from mips_cl_insn INSN. */
#define EXTRACT_OPERAND(MICROMIPS, FIELD, INSN) \
(!(MICROMIPS) \
static void s_ehword (int);
static void s_cpadd (int);
static void s_insn (int);
+static void s_nan (int);
static void md_obj_begin (void);
static void md_obj_end (void);
static void s_mips_ent (int);
static void s_mips_loc (int);
static bfd_boolean pic_need_relax (symbolS *, asection *);
static int relaxed_branch_length (fragS *, asection *, int);
-static int validate_mips_insn (const struct mips_opcode *);
-static int validate_micromips_insn (const struct mips_opcode *);
static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
OPTION_PDR,
OPTION_NO_PDR,
OPTION_MVXWORKS_PIC,
+ OPTION_NAN,
OPTION_END_OF_ENUM
};
{"mpdr", no_argument, NULL, OPTION_PDR},
{"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
{"mvxworks-pic", no_argument, NULL, OPTION_MVXWORKS_PIC},
+ {"mnan", required_argument, NULL, OPTION_NAN},
{NULL, no_argument, NULL, 0}
};
{"ehword", s_ehword, 0},
{"cpadd", s_cpadd, 0},
{"insn", s_insn, 0},
+ {"nan", s_nan, 0},
/* Relatively generic pseudo-ops that happen to be used on MIPS
chips. */
\f
static char *expr_end;
-/* Expressions which appear in instructions. These are set by
- mips_ip. */
+/* Expressions which appear in macro instructions. These are set by
+ mips_ip and read by macro. */
static expressionS imm_expr;
static expressionS imm2_expr;
-static expressionS offset_expr;
-/* Relocs associated with imm_expr and offset_expr. */
+/* The relocatable field in an instruction and the relocs associated
+ with it. These variables are used for instructions like LUI and
+ JAL as well as true offsets. They are also used for address
+ operands in macros. */
-static bfd_reloc_code_real_type imm_reloc[3]
- = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+static expressionS offset_expr;
static bfd_reloc_code_real_type offset_reloc[3]
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
insn->cleared_p = 0;
}
+/* Install UVAL as the value of OPERAND in INSN. */
+
+static inline void
+insn_insert_operand (struct mips_cl_insn *insn,
+ const struct mips_operand *operand, unsigned int uval)
+{
+ insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
+}
+
/* Record the current MIPS16/microMIPS mode in now_seg. */
static void
{0, 0}
};
-/* Check if S points at a valid register specifier according to TYPES.
- If so, then return 1, advance S to consume the specifier and store
- the register's number in REGNOP, otherwise return 0. */
+/* Register symbols $v0 and $v1 map to GPRs 2 and 3, but they can also be
+ interpreted as vector registers 0 and 1. If SYMVAL is the value of one
+ of these register symbols, return the associated vector register,
+ otherwise return SYMVAL itself. */
-static int
-reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+static unsigned int
+mips_prefer_vec_regno (unsigned int symval)
{
- symbolS *symbolP;
- char *e;
+ if ((symval & -2) == (RTYPE_GP | 2))
+ return RTYPE_VEC | (symval & 1);
+ return symval;
+}
+
+/* Return true if the string at *SPTR is a valid register name. If so,
+ move *SPTR past the register and store the register's symbol value
+ in *SYMVAL. This symbol value includes the register number
+ (RNUM_MASK) and register type (RTYPE_MASK). */
+
+static bfd_boolean
+mips_parse_register (char **sptr, unsigned int *symval)
+{
+ symbolS *symbol;
+ char *s, *e;
char save_c;
- int reg = -1;
/* Find end of name. */
- e = *s;
+ s = e = *sptr;
if (is_name_beginner (*e))
++e;
while (is_part_of_name (*e))
save_c = *e;
*e = '\0';
- /* Look for a register symbol. */
- if ((symbolP = symbol_find (*s)) && S_GET_SEGMENT (symbolP) == reg_section)
+ /* Look up the name. */
+ symbol = symbol_find (s);
+ *e = save_c;
+
+ if (!symbol || S_GET_SEGMENT (symbol) != reg_section)
+ return FALSE;
+
+ *sptr = e;
+ *symval = S_GET_VALUE (symbol);
+ return TRUE;
+}
+
+/* Check if SPTR points at a valid register specifier according to TYPES.
+ If so, then return 1, advance S to consume the specifier and store
+ the register's number in REGNOP, otherwise return 0. */
+
+static int
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
+ unsigned int regno;
+
+ if (mips_parse_register (s, ®no))
{
- int r = S_GET_VALUE (symbolP);
- if (r & types)
- reg = r & RNUM_MASK;
- else if ((types & RTYPE_VEC) && (r & ~1) == (RTYPE_GP | 2))
- /* Convert GP reg $v0/1 to MDMX reg $v0/1! */
- reg = (r & RNUM_MASK) - 2;
+ if (types & RTYPE_VEC)
+ regno = mips_prefer_vec_regno (regno);
+ if (regno & types)
+ regno &= RNUM_MASK;
+ else
+ regno = ~0;
}
- /* Else see if this is a register defined in an itbl entry. */
- else if ((types & RTYPE_GP) && itbl_have_entries)
+ else
{
- char *n = *s;
- unsigned long r;
-
- if (*n == '$')
- ++n;
- if (itbl_get_reg_val (n, &r))
- reg = r & RNUM_MASK;
+ if (types & RWARN)
+ as_warn (_("Unrecognized register name `%s'"), *s);
+ regno = ~0;
}
-
- /* Advance to next token if a register was recognised. */
- if (reg >= 0)
- *s = e;
- else if (types & RWARN)
- as_warn (_("Unrecognized register name `%s'"), *s);
-
- *e = save_c;
if (regnop)
- *regnop = reg;
- return reg >= 0;
+ *regnop = regno;
+ return regno <= RNUM_MASK;
}
-/* Check if S points at a valid register list according to TYPES.
- If so, then return 1, advance S to consume the list and store
- the registers present on the list as a bitmask of ones in REGLISTP,
- otherwise return 0. A valid list comprises a comma-separated
- enumeration of valid single registers and/or dash-separated
- contiguous register ranges as determined by their numbers.
+/* Token types for parsed operand lists. */
+enum mips_operand_token_type {
+ /* A plain register, e.g. $f2. */
+ OT_REG,
- As a special exception if one of s0-s7 registers is specified as
- the range's lower delimiter and s8 (fp) is its upper one, then no
- registers whose numbers place them between s7 and s8 (i.e. $24-$29)
- are selected; they have to be listed separately if needed. */
+ /* An element of a vector, e.g. $v0[1]. */
+ OT_REG_ELEMENT,
-static int
-reglist_lookup (char **s, unsigned int types, unsigned int *reglistp)
-{
- unsigned int reglist = 0;
- unsigned int lastregno;
- bfd_boolean ok = TRUE;
- unsigned int regmask;
- char *s_endlist = *s;
- char *s_reset = *s;
+ /* A continuous range of registers, e.g. $s0-$s4. */
+ OT_REG_RANGE,
+
+ /* A (possibly relocated) expression. */
+ OT_INTEGER,
+
+ /* A floating-point value. */
+ OT_FLOAT,
+
+ /* A single character. This can be '(', ')' or ',', but '(' only appears
+ before OT_REGs. */
+ OT_CHAR,
+
+ /* The end of the operand list. */
+ OT_END
+};
+
+/* A parsed operand token. */
+struct mips_operand_token
+{
+ /* The type of token. */
+ enum mips_operand_token_type type;
+ union
+ {
+ /* The register symbol value for an OT_REG. */
+ unsigned int regno;
+
+ /* The register symbol value and index for an OT_REG_ELEMENT. */
+ struct {
+ unsigned int regno;
+ addressT index;
+ } reg_element;
+
+ /* The two register symbol values involved in an OT_REG_RANGE. */
+ struct {
+ unsigned int regno1;
+ unsigned int regno2;
+ } reg_range;
+
+ /* The value of an OT_INTEGER. The value is represented as an
+ expression and the relocation operators that were applied to
+ that expression. The reloc entries are BFD_RELOC_UNUSED if no
+ relocation operators were used. */
+ struct {
+ expressionS value;
+ bfd_reloc_code_real_type relocs[3];
+ } integer;
+
+ /* The binary data for an OT_FLOAT constant, and the number of bytes
+ in the constant. */
+ struct {
+ unsigned char data[8];
+ int length;
+ } flt;
+
+ /* The character represented by an OT_CHAR. */
+ char ch;
+ } u;
+};
+
+/* An obstack used to construct lists of mips_operand_tokens. */
+static struct obstack mips_operand_tokens;
+
+/* Give TOKEN type TYPE and add it to mips_operand_tokens. */
+
+static void
+mips_add_token (struct mips_operand_token *token,
+ enum mips_operand_token_type type)
+{
+ token->type = type;
+ obstack_grow (&mips_operand_tokens, token, sizeof (*token));
+}
+
+/* Check whether S is '(' followed by a register name. Add OT_CHAR
+ and OT_REG tokens for them if so, and return a pointer to the first
+ unconsumed character. Return null otherwise. */
+
+static char *
+mips_parse_base_start (char *s)
+{
+ struct mips_operand_token token;
unsigned int regno;
- while (reg_lookup (s, types, ®no))
+ if (*s != '(')
+ return 0;
+
+ ++s;
+ SKIP_SPACE_TABS (s);
+ if (!mips_parse_register (&s, ®no))
+ return 0;
+
+ token.u.ch = '(';
+ mips_add_token (&token, OT_CHAR);
+
+ token.u.regno = regno;
+ mips_add_token (&token, OT_REG);
+
+ return s;
+}
+
+/* Parse one or more tokens from S. Return a pointer to the first
+ unconsumed character on success. Return null if an error was found
+ and store the error text in insn_error. FLOAT_FORMAT is as for
+ mips_parse_arguments. */
+
+static char *
+mips_parse_argument_token (char *s, char float_format)
+{
+ char *end, *save_in, *err;
+ unsigned int regno1, regno2;
+ struct mips_operand_token token;
+
+ /* First look for "($reg", since we want to treat that as an
+ OT_CHAR and OT_REG rather than an expression. */
+ end = mips_parse_base_start (s);
+ if (end)
+ return end;
+
+ /* Handle other characters that end up as OT_CHARs. */
+ if (*s == ')' || *s == ',')
+ {
+ token.u.ch = *s;
+ mips_add_token (&token, OT_CHAR);
+ ++s;
+ return s;
+ }
+
+ /* Handle tokens that start with a register. */
+ if (mips_parse_register (&s, ®no1))
{
- lastregno = regno;
- if (**s == '-')
+ SKIP_SPACE_TABS (s);
+ if (*s == '-')
{
- (*s)++;
- ok = reg_lookup (s, types, &lastregno);
- if (ok && lastregno < regno)
- ok = FALSE;
- if (!ok)
- break;
- }
+ /* A register range. */
+ ++s;
+ SKIP_SPACE_TABS (s);
+ if (!mips_parse_register (&s, ®no2))
+ {
+ insn_error = _("Invalid register range");
+ return 0;
+ }
- if (lastregno == FP && regno >= S0 && regno <= S7)
+ token.u.reg_range.regno1 = regno1;
+ token.u.reg_range.regno2 = regno2;
+ mips_add_token (&token, OT_REG_RANGE);
+ return s;
+ }
+ else if (*s == '[')
{
- lastregno = S7;
- reglist |= 1 << FP;
+ /* A vector element. */
+ expressionS element;
+
+ ++s;
+ SKIP_SPACE_TABS (s);
+ my_getExpression (&element, s);
+ if (element.X_op != O_constant)
+ {
+ insn_error = _("Vector element must be constant");
+ return 0;
+ }
+ s = expr_end;
+ SKIP_SPACE_TABS (s);
+ if (*s != ']')
+ {
+ insn_error = _("Missing `]'");
+ return 0;
+ }
+ ++s;
+
+ token.u.reg_element.regno = regno1;
+ token.u.reg_element.index = element.X_add_number;
+ mips_add_token (&token, OT_REG_ELEMENT);
+ return s;
}
- regmask = 1 << lastregno;
- regmask = (regmask << 1) - 1;
- regmask ^= (1 << regno) - 1;
- reglist |= regmask;
- s_endlist = *s;
- if (**s != ',')
- break;
- (*s)++;
+ /* Looks like just a plain register. */
+ token.u.regno = regno1;
+ mips_add_token (&token, OT_REG);
+ return s;
}
- if (ok)
- *s = s_endlist;
- else
- *s = s_reset;
- if (reglistp)
- *reglistp = reglist;
- return ok && reglist != 0;
+ if (float_format)
+ {
+ /* First try to treat expressions as floats. */
+ save_in = input_line_pointer;
+ input_line_pointer = s;
+ err = md_atof (float_format, (char *) token.u.flt.data,
+ &token.u.flt.length);
+ end = input_line_pointer;
+ input_line_pointer = save_in;
+ if (err && *err)
+ {
+ insn_error = err;
+ return 0;
+ }
+ if (s != end)
+ {
+ mips_add_token (&token, OT_FLOAT);
+ return end;
+ }
+ }
+
+ /* Treat everything else as an integer expression. */
+ token.u.integer.relocs[0] = BFD_RELOC_UNUSED;
+ token.u.integer.relocs[1] = BFD_RELOC_UNUSED;
+ token.u.integer.relocs[2] = BFD_RELOC_UNUSED;
+ my_getSmallExpression (&token.u.integer.value, token.u.integer.relocs, s);
+ s = expr_end;
+ mips_add_token (&token, OT_INTEGER);
+ return s;
+}
+
+/* S points to the operand list for an instruction. FLOAT_FORMAT is 'f'
+ if expressions should be treated as 32-bit floating-point constants,
+ 'd' if they should be treated as 64-bit floating-point constants,
+ or 0 if they should be treated as integer expressions (the usual case).
+
+ Return a list of tokens on success, otherwise return 0. The caller
+ must obstack_free the list after use. */
+
+static struct mips_operand_token *
+mips_parse_arguments (char *s, char float_format)
+{
+ struct mips_operand_token token;
+
+ SKIP_SPACE_TABS (s);
+ while (*s)
+ {
+ s = mips_parse_argument_token (s, float_format);
+ if (!s)
+ {
+ obstack_free (&mips_operand_tokens,
+ obstack_finish (&mips_operand_tokens));
+ return 0;
+ }
+ SKIP_SPACE_TABS (s);
+ }
+ mips_add_token (&token, OT_END);
+ return (struct mips_operand_token *) obstack_finish (&mips_operand_tokens);
}
/* Return TRUE if opcode MO is valid on the currently selected ISA, ASE
return TRUE;
}
+/* For consistency checking, verify that all bits of OPCODE are
+ specified either by the match/mask part of the instruction
+ definition, or by the operand list. INSN_BITS says which
+ bits of the instruction are significant and DECODE_OPERAND
+ provides the mips_operand description of each operand. */
+
+static int
+validate_mips_insn (const struct mips_opcode *opcode,
+ unsigned long insn_bits,
+ const struct mips_operand *(*decode_operand) (const char *))
+{
+ const char *s;
+ unsigned long used_bits, doubled, undefined;
+ const struct mips_operand *operand;
+
+ if ((opcode->mask & opcode->match) != opcode->match)
+ {
+ as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+ opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits = 0;
+ for (s = opcode->args; *s; ++s)
+ switch (*s)
+ {
+ case ',':
+ case '(':
+ case ')':
+ break;
+
+ default:
+ operand = decode_operand (s);
+ if (!operand)
+ {
+ as_bad (_("internal: unknown operand type: %s %s"),
+ opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits |= ((1 << operand->size) - 1) << operand->lsb;
+ if (operand->type == OP_MDMX_IMM_REG)
+ /* Bit 5 is the format selector (OB vs QH). The opcode table
+ has separate entries for each format. */
+ used_bits &= ~(1 << (operand->lsb + 5));
+ /* Skip prefix characters. */
+ if (*s == '+' || *s == 'm')
+ ++s;
+ break;
+ }
+ doubled = used_bits & opcode->mask & insn_bits;
+ if (doubled)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx doubly defined):"
+ " %s %s"), doubled, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits |= opcode->mask;
+ undefined = ~used_bits & insn_bits;
+ if (undefined)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
+ undefined, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits &= ~insn_bits;
+ if (used_bits)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx defined): %s %s"),
+ used_bits, opcode->name, opcode->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* The microMIPS version of validate_mips_insn. */
+
+static int
+validate_micromips_insn (const struct mips_opcode *opc)
+{
+ unsigned long insn_bits;
+ unsigned long major;
+ unsigned int length;
+
+ length = micromips_insn_length (opc);
+ if (length != 2 && length != 4)
+ {
+ as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
+ "%s %s"), length, opc->name, opc->args);
+ return 0;
+ }
+ major = opc->match >> (10 + 8 * (length - 2));
+ if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
+ || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
+ {
+ as_bad (_("Internal error: bad microMIPS opcode "
+ "(opcode/length mismatch): %s %s"), opc->name, opc->args);
+ return 0;
+ }
+
+ /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
+ insn_bits = 1 << 4 * length;
+ insn_bits <<= 4 * length;
+ insn_bits -= 1;
+ return validate_mips_insn (opc, insn_bits, decode_micromips_operand);
+}
+
/* This function is called once, at assembler startup time. It should set up
all the tables, etc. that the MD part of the assembler will need. */
{
if (mips_opcodes[i].pinfo != INSN_MACRO)
{
- if (!validate_mips_insn (&mips_opcodes[i]))
+ if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
+ decode_mips_operand))
broken = 1;
if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
{
reg_names_o32[i].num, /* & RNUM_MASK, */
&zero_address_frag));
+ obstack_init (&mips_operand_tokens);
+
mips_no_prev_insn ();
mips_gprmask = 0;
imm_expr.X_op = O_absent;
imm2_expr.X_op = O_absent;
offset_expr.X_op = O_absent;
- imm_reloc[0] = BFD_RELOC_UNUSED;
- imm_reloc[1] = BFD_RELOC_UNUSED;
- imm_reloc[2] = BFD_RELOC_UNUSED;
offset_reloc[0] = BFD_RELOC_UNUSED;
offset_reloc[1] = BFD_RELOC_UNUSED;
offset_reloc[2] = BFD_RELOC_UNUSED;
}
else
{
- if (imm_expr.X_op != O_absent)
- append_insn (&insn, &imm_expr, imm_reloc, FALSE);
- else if (offset_expr.X_op != O_absent)
+ if (offset_expr.X_op != O_absent)
append_insn (&insn, &offset_expr, offset_reloc, FALSE);
else
append_insn (&insn, NULL, unused_reloc, FALSE);
return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
}
+static inline bfd_boolean
+gprel16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_GPREL16 || reloc == BFD_RELOC_MIPS16_GPREL
+ || reloc == BFD_RELOC_MICROMIPS_GPREL16);
+}
+
/* Return true if RELOC is a PC-relative relocation that does not have
full address range. */
&& fixp->fx_offset == fixp->fx_next->fx_offset);
}
-/* This function returns true if modifying a register requires a
- delay. */
-
-static int
-reg_needs_delay (unsigned int reg)
-{
- unsigned long prev_pinfo;
-
- prev_pinfo = history[0].insn_mo->pinfo;
- if (! mips_opts.noreorder
- && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY)
- && ! gpr_interlocks)
- || ((prev_pinfo & INSN_LOAD_COPROC_DELAY)
- && ! cop_interlocks)))
- {
- /* A load from a coprocessor or from memory. All load delays
- delay the use of general register rt for one instruction. */
- /* Itbl support may require additional care here. */
- know (prev_pinfo & INSN_WRITE_GPR_T);
- if (reg == EXTRACT_OPERAND (mips_opts.micromips, RT, history[0]))
- return 1;
- }
-
- return 0;
-}
-
/* Move all labels in LABELS to the current insertion point. TEXT_P
says whether the labels refer to text or data. */
static inline bfd_boolean
compact_branch_p (const struct mips_cl_insn *ip)
{
- if (mips_opts.mips16)
- return (ip->insn_mo->pinfo & (MIPS16_INSN_UNCOND_BRANCH
- | MIPS16_INSN_COND_BRANCH)) != 0;
- else
- return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
- | INSN2_COND_BRANCH)) != 0;
+ return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
+ | INSN2_COND_BRANCH)) != 0;
}
/* Return true if IP is an unconditional branch or jump. */
uncond_branch_p (const struct mips_cl_insn *ip)
{
return ((ip->insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0
- || (mips_opts.mips16
- ? (ip->insn_mo->pinfo & MIPS16_INSN_UNCOND_BRANCH) != 0
- : (ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0));
+ || (ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0);
}
/* Return true if IP is a branch-likely instruction. */
mask |= 1 << micromips_to_32_reg_d_map[EXTRACT_OPERAND (1, MD, *ip)];
if (pinfo2 & INSN2_MOD_GPR_MF)
mask |= 1 << micromips_to_32_reg_f_map[EXTRACT_OPERAND (1, MF, *ip)];
- if (pinfo2 & INSN2_MOD_SP)
- mask |= 1 << SP;
}
+ if (pinfo2 & INSN2_MOD_SP)
+ mask |= 1 << SP;
return mask;
}
mask |= 1 << TREG;
if (pinfo & MIPS16_INSN_READ_SP)
mask |= 1 << SP;
- if (pinfo & MIPS16_INSN_READ_31)
- mask |= 1 << RA;
if (pinfo & MIPS16_INSN_READ_Z)
mask |= 1 << (mips16_to_32_reg_map
[MIPS16_EXTRACT_OPERAND (MOVE32Z, *ip)]);
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
if (pinfo2 & INSN2_READ_GP)
mask |= 1 << GP;
- if (pinfo2 & INSN2_READ_GPR_31)
- mask |= 1 << RA;
if (pinfo2 & INSN2_READ_GPR_Z)
mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
}
+ if (pinfo2 & INSN2_READ_GPR_31)
+ mask |= 1 << RA;
if (mips_opts.micromips)
{
if (pinfo2 & INSN2_READ_GPR_MC)
mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RZ, *ip)];
if (pinfo & MIPS16_INSN_WRITE_T)
mask |= 1 << TREG;
- if (pinfo & MIPS16_INSN_WRITE_SP)
- mask |= 1 << SP;
if (pinfo & MIPS16_INSN_WRITE_31)
mask |= 1 << RA;
if (pinfo & MIPS16_INSN_WRITE_GPR_Y)
{
if (pinfo2 & INSN2_WRITE_GPR_MB)
mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
- if (pinfo2 & INSN2_WRITE_GPR_MHI)
+ if (pinfo2 & INSN2_WRITE_GPR_MH)
{
- mask |= 1 << micromips_to_32_reg_h_map[EXTRACT_OPERAND (1, MH, *ip)];
- mask |= 1 << micromips_to_32_reg_i_map[EXTRACT_OPERAND (1, MI, *ip)];
+ mask |= 1 << micromips_to_32_reg_h_map1[EXTRACT_OPERAND (1, MH, *ip)];
+ mask |= 1 << micromips_to_32_reg_h_map2[EXTRACT_OPERAND (1, MH, *ip)];
}
if (pinfo2 & INSN2_WRITE_GPR_MJ)
mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
return mask;
}
-/* Classify an instruction according to the FIX_VR4120_* enumeration.
- Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
- by VR4120 errata. */
+/* Operand OPNUM of INSN is an odd-numbered floating-point register.
+ Check whether that is allowed. */
-static unsigned int
-classify_vr4120_insn (const char *name)
+static bfd_boolean
+mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum)
{
- if (strncmp (name, "macc", 4) == 0)
- return FIX_VR4120_MACC;
- if (strncmp (name, "dmacc", 5) == 0)
- return FIX_VR4120_DMACC;
- if (strncmp (name, "mult", 4) == 0)
- return FIX_VR4120_MULT;
- if (strncmp (name, "dmult", 5) == 0)
- return FIX_VR4120_DMULT;
- if (strstr (name, "div"))
- return FIX_VR4120_DIV;
- if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
- return FIX_VR4120_MTHILO;
- return NUM_FIX_VR4120_CLASSES;
-}
-
-#define INSN_ERET 0x42000018
-#define INSN_DERET 0x4200001f
+ const char *s = insn->name;
-/* Return the number of instructions that must separate INSN1 and INSN2,
- where INSN1 is the earlier instruction. Return the worst-case value
- for any INSN2 if INSN2 is null. */
+ if (insn->pinfo == INSN_MACRO)
+ /* Let a macro pass, we'll catch it later when it is expanded. */
+ return TRUE;
-static unsigned int
-insns_between (const struct mips_cl_insn *insn1,
- const struct mips_cl_insn *insn2)
-{
- unsigned long pinfo1, pinfo2;
- unsigned int mask;
-
- /* This function needs to know which pinfo flags are set for INSN2
- and which registers INSN2 uses. The former is stored in PINFO2 and
- the latter is tested via INSN2_USES_GPR. If INSN2 is null, PINFO2
- will have every flag set and INSN2_USES_GPR will always return true. */
- pinfo1 = insn1->insn_mo->pinfo;
- pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
-
-#define INSN2_USES_GPR(REG) \
- (insn2 == NULL || (gpr_read_mask (insn2) & (1U << (REG))) != 0)
-
- /* For most targets, write-after-read dependencies on the HI and LO
- registers must be separated by at least two instructions. */
- if (!hilo_interlocks)
- {
- if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
- return 2;
- if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
- return 2;
- }
-
- /* If we're working around r7000 errata, there must be two instructions
- between an mfhi or mflo and any instruction that uses the result. */
- if (mips_7000_hilo_fix
- && !mips_opts.micromips
- && MF_HILO_INSN (pinfo1)
- && INSN2_USES_GPR (EXTRACT_OPERAND (0, RD, *insn1)))
- return 2;
-
- /* If we're working around 24K errata, one instruction is required
- if an ERET or DERET is followed by a branch instruction. */
- if (mips_fix_24k && !mips_opts.micromips)
+ if (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa) || mips_opts.arch == CPU_R5900)
{
- if (insn1->insn_opcode == INSN_ERET
- || insn1->insn_opcode == INSN_DERET)
+ /* Allow odd registers for single-precision ops. */
+ switch (insn->pinfo & (FP_S | FP_D))
{
- if (insn2 == NULL
- || insn2->insn_opcode == INSN_ERET
- || insn2->insn_opcode == INSN_DERET
- || delayed_branch_p (insn2))
- return 1;
+ 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'));
}
- /* If working around VR4120 errata, check for combinations that need
- a single intervening instruction. */
- if (mips_fix_vr4120 && !mips_opts.micromips)
- {
- unsigned int class1, class2;
+ /* 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;
- class1 = classify_vr4120_insn (insn1->insn_mo->name);
- if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
- {
- if (insn2 == NULL)
- return 1;
- class2 = classify_vr4120_insn (insn2->insn_mo->name);
- if (vr4120_conflicts[class1] & (1 << class2))
- return 1;
- }
- }
+ return FALSE;
+}
- if (!HAVE_CODE_COMPRESSION)
- {
- /* Check for GPR or coprocessor load delays. All such delays
- are on the RT register. */
- /* Itbl support may require additional care here. */
- if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
- || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
- {
- know (pinfo1 & INSN_WRITE_GPR_T);
- if (INSN2_USES_GPR (EXTRACT_OPERAND (0, RT, *insn1)))
- return 1;
- }
+/* 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. */
- /* Check for generic coprocessor hazards.
+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);
+}
- This case is not handled very well. There is no special
- knowledge of CP0 handling, and the coprocessors other than
- the floating point unit are not distinguished at all. */
- /* Itbl support may require additional care here. FIXME!
- Need to modify this to include knowledge about
- user specified delays! */
- else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE_DELAY))
- || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
- {
- /* Handle cases where INSN1 writes to a known general coprocessor
- register. There must be a one instruction delay before INSN2
- if INSN2 reads that register, otherwise no delay is needed. */
- mask = fpr_write_mask (insn1);
- if (mask != 0)
- {
- if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
- return 1;
- }
- else
- {
- /* Read-after-write dependencies on the control registers
- require a two-instruction gap. */
- if ((pinfo1 & INSN_WRITE_COND_CODE)
- && (pinfo2 & INSN_READ_COND_CODE))
- return 2;
+/* Report an invalid combination of position and size operands for a bitfield
+ operation. POS and SIZE are the values that were given. */
- /* We don't know exactly what INSN1 does. If INSN2 is
- also a coprocessor instruction, assume there must be
- a one instruction gap. */
- if (pinfo2 & INSN_COP)
- return 1;
- }
- }
+static void
+report_bad_field (offsetT pos, offsetT size)
+{
+ as_bad (_("Invalid field specification (position %ld, size %ld)"),
+ (unsigned long) pos, (unsigned long) size);
+}
- /* Check for read-after-write dependencies on the coprocessor
- control registers in cases where INSN1 does not need a general
- coprocessor delay. This means that INSN1 is a floating point
- comparison instruction. */
- /* Itbl support may require additional care here. */
- else if (!cop_interlocks
- && (pinfo1 & INSN_WRITE_COND_CODE)
- && (pinfo2 & INSN_READ_COND_CODE))
- return 1;
- }
+/* Information about an instruction argument that we're trying to match. */
+struct mips_arg_info
+{
+ /* The instruction so far. */
+ struct mips_cl_insn *insn;
-#undef INSN2_USES_GPR
+ /* The first unconsumed operand token. */
+ struct mips_operand_token *token;
- return 0;
-}
+ /* The 1-based operand number, in terms of insn->insn_mo->args. */
+ int opnum;
-/* Return the number of nops that would be needed to work around the
- VR4130 mflo/mfhi errata if instruction INSN immediately followed
- the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
- that are contained within the first IGNORE instructions of HIST. */
+ /* The 1-based argument number, for error reporting. This does not
+ count elided optional registers, etc.. */
+ int argnum;
-static int
-nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
-{
- int i, j;
- unsigned int mask;
+ /* The last OP_REG operand seen, or ILLEGAL_REG if none. */
+ unsigned int last_regno;
- /* Check if the instruction writes to HI or LO. MTHI and MTLO
- are not affected by the errata. */
- if (insn != 0
- && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
- || strcmp (insn->insn_mo->name, "mtlo") == 0
- || strcmp (insn->insn_mo->name, "mthi") == 0))
- return 0;
+ /* If the first operand was an OP_REG, this is the register that it
+ specified, otherwise it is ILLEGAL_REG. */
+ unsigned int dest_regno;
- /* Search for the first MFLO or MFHI. */
- for (i = 0; i < MAX_VR4130_NOPS; i++)
- if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
- {
- /* Extract the destination register. */
- mask = gpr_write_mask (&hist[i]);
+ /* The value of the last OP_INT operand. Only used for OP_MSB,
+ where it gives the lsb position. */
+ unsigned int last_op_int;
- /* No nops are needed if INSN reads that register. */
- if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
- return 0;
+ /* 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;
- /* ...or if any of the intervening instructions do. */
- for (j = 0; j < i; j++)
- if (gpr_read_mask (&hist[j]) & mask)
- return 0;
+ /* 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;
- if (i >= ignore)
- return MAX_VR4130_NOPS - i;
- }
- return 0;
-}
+ /* 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;
-#define BASE_REG_EQ(INSN1, INSN2) \
- ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
- == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+ /* True if a reference to the current AT register was seen. */
+ bfd_boolean seen_at;
+};
-/* Return the minimum alignment for this store instruction. */
+/* Try to match an OT_CHAR token for character CH. Consume the token
+ and return true on success, otherwise return false. */
-static int
-fix_24k_align_to (const struct mips_opcode *mo)
+static bfd_boolean
+match_char (struct mips_arg_info *arg, char ch)
{
- if (strcmp (mo->name, "sh") == 0)
- return 2;
+ if (arg->token->type == OT_CHAR && arg->token->u.ch == ch)
+ {
+ ++arg->token;
+ if (ch == ',')
+ arg->argnum += 1;
+ return TRUE;
+ }
+ return FALSE;
+}
- if (strcmp (mo->name, "swc1") == 0
- || strcmp (mo->name, "swc2") == 0
- || strcmp (mo->name, "sw") == 0
- || strcmp (mo->name, "sc") == 0
- || strcmp (mo->name, "s.s") == 0)
- return 4;
+/* Try to get an expression from the next tokens in ARG. Consume the
+ tokens and return true on success, storing the expression value in
+ VALUE and relocation types in R. */
- if (strcmp (mo->name, "sdc1") == 0
- || strcmp (mo->name, "sdc2") == 0
- || strcmp (mo->name, "s.d") == 0)
- return 8;
+static bfd_boolean
+match_expression (struct mips_arg_info *arg, expressionS *value,
+ bfd_reloc_code_real_type *r)
+{
+ if (arg->token->type == OT_INTEGER)
+ {
+ *value = arg->token->u.integer.value;
+ memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+ ++arg->token;
+ return TRUE;
+ }
- /* sb, swl, swr */
- return 1;
+ /* Error-reporting is more consistent if we treat registers as O_register
+ rather than rejecting them outright. "$1", "($1)" and "(($1))" are
+ then handled in the same way. */
+ if (arg->token->type == OT_REG)
+ {
+ value->X_add_number = arg->token->u.regno;
+ ++arg->token;
+ }
+ else if (arg->token[0].type == OT_CHAR
+ && arg->token[0].u.ch == '('
+ && arg->token[1].type == OT_REG
+ && arg->token[2].type == OT_CHAR
+ && arg->token[2].u.ch == ')')
+ {
+ value->X_add_number = arg->token[1].u.regno;
+ arg->token += 3;
+ }
+ else
+ return FALSE;
+
+ value->X_op = O_register;
+ r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+ return TRUE;
}
-struct fix_24k_store_info
- {
- /* Immediate offset, if any, for this store instruction. */
- short off;
- /* Alignment required by this store instruction. */
- int align_to;
- /* True for register offsets. */
- int register_offset;
- };
+/* Try to get a constant expression from the next tokens in ARG. Consume
+ the tokens and return return true on success, storing the constant value
+ in *VALUE. Use FALLBACK as the value if the match succeeded with an
+ error. */
-/* Comparison function used by qsort. */
+static bfd_boolean
+match_const_int (struct mips_arg_info *arg, offsetT *value, offsetT fallback)
+{
+ expressionS ex;
+ bfd_reloc_code_real_type r[3];
-static int
-fix_24k_sort (const void *a, const void *b)
+ if (!match_expression (arg, &ex, r))
+ return FALSE;
+
+ if (r[0] == BFD_RELOC_UNUSED && ex.X_op == O_constant)
+ *value = ex.X_add_number;
+ else
+ {
+ if (arg->soft_match)
+ return FALSE;
+ as_bad (_("Operand %d of `%s' must be constant"),
+ arg->argnum, arg->insn->insn_mo->name);
+ *value = fallback;
+ }
+ return TRUE;
+}
+
+/* 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)
{
- const struct fix_24k_store_info *pos1 = a;
- const struct fix_24k_store_info *pos2 = b;
+ 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 ();
+}
- return (pos1->off - pos2->off);
+/* 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);
+ }
}
-/* INSN is a store instruction. Try to record the store information
- in STINFO. Return false if the information isn't known. */
+/* ARG is a register with symbol value SYMVAL. Try to interpret it as
+ a register of type TYPE. Return true on success, storing the register
+ number in *REGNO and warning about any dubious uses. */
static bfd_boolean
-fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
- const struct mips_cl_insn *insn)
+match_regno (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int symval, unsigned int *regno)
{
- /* The instruction must have a known offset. */
- if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+ if (type == OP_REG_VEC)
+ symval = mips_prefer_vec_regno (symval);
+ if (!(symval & convert_reg_type (arg->insn->insn_mo, type)))
return FALSE;
- stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
- stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+ *regno = symval & RNUM_MASK;
+ check_regno (arg, type, *regno);
return TRUE;
}
-/* Return the number of nops that would be needed to work around the 24k
- "lost data on stores during refill" errata if instruction INSN
- immediately followed the 2 instructions described by HIST.
- Ignore hazards that are contained within the first IGNORE
- instructions of HIST.
-
- Problem: The FSB (fetch store buffer) acts as an intermediate buffer
- for the data cache refills and store data. The following describes
- the scenario where the store data could be lost.
+/* Try to interpret the next token in ARG as a register of type TYPE.
+ Consume the token and return true on success, storing the register
+ number in *REGNO. Return false on failure. */
- * A data cache miss, due to either a load or a store, causing fill
- data to be supplied by the memory subsystem
- * The first three doublewords of fill data are returned and written
- into the cache
- * A sequence of four stores occurs in consecutive cycles around the
- final doubleword of the fill:
- * Store A
- * Store B
- * Store C
- * Zero, One or more instructions
- * Store D
+static bfd_boolean
+match_reg (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int *regno)
+{
+ if (arg->token->type == OT_REG
+ && match_regno (arg, type, arg->token->u.regno, regno))
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
+}
- The four stores A-D must be to different doublewords of the line that
- is being filled. The fourth instruction in the sequence above permits
- the fill of the final doubleword to be transferred from the FSB into
- the cache. In the sequence above, the stores may be either integer
- (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
- swxc1, sdxc1, suxc1) stores, as long as the four stores are to
- different doublewords on the line. If the floating point unit is
- running in 1:2 mode, it is not possible to create the sequence above
- using only floating point store instructions.
+/* Try to interpret the next token in ARG as a range of registers of type TYPE.
+ Consume the token and return true on success, storing the register numbers
+ in *REGNO1 and *REGNO2. Return false on failure. */
- In this case, the cache line being filled is incorrectly marked
- invalid, thereby losing the data from any store to the line that
- occurs between the original miss and the completion of the five
- cycle sequence shown above.
+static bfd_boolean
+match_reg_range (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int *regno1, unsigned int *regno2)
+{
+ if (match_reg (arg, type, regno1))
+ {
+ *regno2 = *regno1;
+ return TRUE;
+ }
+ if (arg->token->type == OT_REG_RANGE
+ && match_regno (arg, type, arg->token->u.reg_range.regno1, regno1)
+ && match_regno (arg, type, arg->token->u.reg_range.regno2, regno2)
+ && *regno1 <= *regno2)
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
+}
- The workarounds are:
+/* OP_INT matcher. */
- * Run the data cache in write-through mode.
- * Insert a non-store instruction between
- Store A and Store B or Store B and Store C. */
-
-static int
-nops_for_24k (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
+static bfd_boolean
+match_int_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
{
- struct fix_24k_store_info pos[3];
- int align, i, base_offset;
+ 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 (arg->token->type == OT_CHAR && arg->token->u.ch == '(')
+ /* Assume we have an elided offset. The later match will fail
+ if this turns out to be wrong. */
+ sval = 0;
+ else 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. */
+ if (!match_expression (arg, &offset_expr, offset_reloc))
+ return FALSE;
- if (ignore >= 2)
- return 0;
+ if (offset_reloc[0] != BFD_RELOC_UNUSED)
+ /* Relocation operators were used. Accept the arguent and
+ leave the relocation value in offset_expr and offset_relocs
+ for the caller to process. */
+ return TRUE;
- /* If the previous instruction wasn't a store, there's nothing to
- worry about. */
- if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
- return 0;
+ 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. */
+ if (!arg->allow_nonconst)
+ return FALSE;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ return TRUE;
+ }
- /* If the instructions after the previous one are unknown, we have
- to assume the worst. */
- if (!insn)
- return 1;
+ /* Clear the global state; we're going to install the operand
+ ourselves. */
+ sval = offset_expr.X_add_number;
+ offset_expr.X_op = O_absent;
+ }
+ else
+ {
+ if (!match_const_int (arg, &sval, min_val))
+ return FALSE;
+ }
- /* Check whether we are dealing with three consecutive stores. */
- if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
- || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
- return 0;
+ arg->last_op_int = sval;
- /* If we don't know the relationship between the store addresses,
- assume the worst. */
- if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
- || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
- return 1;
+ /* 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.
- if (!fix_24k_record_store_info (&pos[0], insn)
- || !fix_24k_record_store_info (&pos[1], &hist[0])
- || !fix_24k_record_store_info (&pos[2], &hist[1]))
- return 1;
+ 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 FALSE;
+ 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 FALSE;
+ 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;
+ }
- qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+ uval = (unsigned int) sval >> operand->shift;
+ uval -= operand->bias;
- /* Pick a value of ALIGN and X such that all offsets are adjusted by
- X bytes and such that the base register + X is known to be aligned
- to align bytes. */
+ /* 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;
- if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
- align = 8;
- else
- {
- align = pos[0].align_to;
- base_offset = pos[0].off;
- for (i = 1; i < 3; i++)
- if (align < pos[i].align_to)
- {
- align = pos[i].align_to;
- base_offset = pos[i].off;
- }
- for (i = 0; i < 3; i++)
- pos[i].off -= base_offset;
- }
+ default:
+ /* The rest must be changed to 28. */
+ uval = 28;
+ break;
+ }
- pos[0].off &= ~align + 1;
- pos[1].off &= ~align + 1;
- pos[2].off &= ~align + 1;
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
- /* If any two stores write to the same chunk, they also write to the
- same doubleword. The offsets are still sorted at this point. */
- if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
- return 0;
+/* OP_MAPPED_INT matcher. */
- /* A range of at least 9 bytes is needed for the stores to be in
- non-overlapping doublewords. */
- if (pos[2].off - pos[0].off <= 8)
- return 0;
+static bfd_boolean
+match_mapped_int_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_mapped_int_operand *operand;
+ unsigned int uval, num_vals;
+ offsetT sval;
- if (pos[2].off - pos[1].off >= 24
- || pos[1].off - pos[0].off >= 24
- || pos[2].off - pos[0].off >= 32)
- return 0;
+ operand = (const struct mips_mapped_int_operand *) operand_base;
+ if (!match_const_int (arg, &sval, operand->int_map[0]))
+ return FALSE;
- return 1;
-}
+ 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 FALSE;
-/* Return the number of nops that would be needed if instruction INSN
- immediately followed the MAX_NOPS instructions given by HIST,
- where HIST[0] is the most recent instruction. Ignore hazards
- between INSN and the first IGNORE instructions in HIST.
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
- If INSN is null, return the worse-case number of nops for any
- instruction. */
+/* OP_MSB matcher. */
-static int
-nops_for_insn (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
+static bfd_boolean
+match_msb_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
{
- int i, nops, tmp_nops;
+ const struct mips_msb_operand *operand;
+ int min_val, max_val, max_high;
+ offsetT size, sval, high;
- nops = 0;
- for (i = ignore; i < MAX_DELAY_NOPS; i++)
- {
- tmp_nops = insns_between (hist + i, insn) - i;
- if (tmp_nops > nops)
- nops = tmp_nops;
- }
+ 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;
- if (mips_fix_vr4130 && !mips_opts.micromips)
+ if (!match_const_int (arg, &size, 1))
+ return FALSE;
+
+ high = size + arg->last_op_int;
+ sval = operand->add_lsb ? high : size;
+
+ if (size < 0 || high > max_high || sval < min_val || sval > max_val)
{
- tmp_nops = nops_for_vr4130 (ignore, hist, insn);
- if (tmp_nops > nops)
- nops = tmp_nops;
+ if (arg->soft_match)
+ return FALSE;
+ report_bad_field (arg->last_op_int, size);
+ sval = min_val;
}
+ insn_insert_operand (arg->insn, operand_base, sval - min_val);
+ return TRUE;
+}
- if (mips_fix_24k && !mips_opts.micromips)
+/* OP_REG matcher. */
+
+static bfd_boolean
+match_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_reg_operand *operand;
+ unsigned int regno, uval, num_vals;
+
+ operand = (const struct mips_reg_operand *) operand_base;
+ if (!match_reg (arg, operand->reg_type, ®no))
+ return FALSE;
+
+ if (operand->reg_map)
{
- tmp_nops = nops_for_24k (ignore, hist, insn);
- if (tmp_nops > nops)
- nops = tmp_nops;
+ 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 FALSE;
}
+ else
+ uval = regno;
- return nops;
+ arg->last_regno = regno;
+ if (arg->opnum == 1)
+ arg->dest_regno = regno;
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
}
-/* The variable arguments provide NUM_INSNS extra instructions that
- might be added to HIST. Return the largest number of nops that
- would be needed after the extended sequence, ignoring hazards
- in the first IGNORE instructions. */
+/* OP_REG_PAIR matcher. */
-static int
-nops_for_sequence (int num_insns, int ignore,
- const struct mips_cl_insn *hist, ...)
+static bfd_boolean
+match_reg_pair_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
{
- va_list args;
- struct mips_cl_insn buffer[MAX_NOPS];
- struct mips_cl_insn *cursor;
- int nops;
+ const struct mips_reg_pair_operand *operand;
+ unsigned int regno1, regno2, uval, num_vals;
- va_start (args, hist);
- cursor = buffer + num_insns;
- memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
- while (cursor > buffer)
- *--cursor = *va_arg (args, const struct mips_cl_insn *);
+ operand = (const struct mips_reg_pair_operand *) operand_base;
+ if (!match_reg (arg, operand->reg_type, ®no1)
+ || !match_char (arg, ',')
+ || !match_reg (arg, operand->reg_type, ®no2))
+ return FALSE;
- nops = nops_for_insn (ignore, buffer, NULL);
- va_end (args);
- return nops;
+ 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 FALSE;
+
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
}
-/* Like nops_for_insn, but if INSN is a branch, take into account the
- worst-case delay for the branch target. */
+/* OP_PCREL matcher. The caller chooses the relocation type. */
-static int
-nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
- const struct mips_cl_insn *insn)
+static bfd_boolean
+match_pcrel_operand (struct mips_arg_info *arg)
{
- int nops, tmp_nops;
+ bfd_reloc_code_real_type r[3];
- nops = nops_for_insn (ignore, hist, insn);
- if (delayed_branch_p (insn))
- {
- tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
- hist, insn, get_delay_slot_nop (insn));
- if (tmp_nops > nops)
- nops = tmp_nops;
- }
- else if (compact_branch_p (insn))
- {
- tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
- if (tmp_nops > nops)
- nops = tmp_nops;
- }
- return nops;
+ return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
}
-/* Fix NOP issue: Replace nops by "or at,at,zero". */
+/* OP_PERF_REG matcher. */
-static void
-fix_loongson2f_nop (struct mips_cl_insn * ip)
+static bfd_boolean
+match_perf_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- gas_assert (!HAVE_CODE_COMPRESSION);
- if (strcmp (ip->insn_mo->name, "nop") == 0)
- ip->insn_opcode = LOONGSON2F_NOP_INSN;
-}
+ offsetT sval;
-/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
- jr target pc &= 'hffff_ffff_cfff_ffff. */
+ if (!match_const_int (arg, &sval, 0))
+ return FALSE;
-static void
-fix_loongson2f_jump (struct mips_cl_insn * ip)
-{
- gas_assert (!HAVE_CODE_COMPRESSION);
- if (strcmp (ip->insn_mo->name, "j") == 0
- || strcmp (ip->insn_mo->name, "jr") == 0
- || strcmp (ip->insn_mo->name, "jalr") == 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))))
{
- int sreg;
- expressionS ep;
-
- if (! mips_opts.at)
- return;
-
- sreg = EXTRACT_OPERAND (0, RS, *ip);
- if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
- return;
-
- ep.X_op = O_constant;
- ep.X_add_number = 0xcfff0000;
- macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
- ep.X_add_number = 0xffff;
- macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
- macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
+ if (arg->soft_match)
+ return FALSE;
+ as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval);
}
-}
-
-static void
-fix_loongson2f (struct mips_cl_insn * ip)
-{
- if (mips_fix_loongson2f_nop)
- fix_loongson2f_nop (ip);
- if (mips_fix_loongson2f_jump)
- fix_loongson2f_jump (ip);
+ insn_insert_operand (arg->insn, operand, sval);
+ return TRUE;
}
-/* IP is a branch that has a delay slot, and we need to fill it
- automatically. Return true if we can do that by swapping IP
- with the previous instruction.
- ADDRESS_EXPR is an operand of the instruction to be used with
- RELOC_TYPE. */
+/* OP_ADDIUSP matcher. */
static bfd_boolean
-can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
- bfd_reloc_code_real_type *reloc_type)
+match_addiusp_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
- unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+ offsetT sval;
+ unsigned int uval;
- /* -O2 and above is required for this optimization. */
- if (mips_optimize < 2)
+ if (!match_const_int (arg, &sval, -256))
return FALSE;
- /* If we have seen .set volatile or .set nomove, don't optimize. */
- if (mips_opts.nomove)
+ if (sval % 4)
return FALSE;
- /* We can't swap if the previous instruction's position is fixed. */
- if (history[0].fixed_p)
+ sval /= 4;
+ if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
return FALSE;
- /* If the previous previous insn was in a .set noreorder, we can't
- swap. Actually, the MIPS assembler will swap in this situation.
- However, gcc configured -with-gnu-as will generate code like
+ uval = (unsigned int) sval;
+ uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
+}
- .set noreorder
- lw $4,XXX
- .set reorder
- INSN
- bne $4,$0,foo
+/* OP_CLO_CLZ_DEST matcher. */
- in which we can not swap the bne and INSN. If gcc is not configured
- -with-gnu-as, it does not output the .set pseudo-ops. */
- if (history[1].noreorder_p)
- return FALSE;
+static bfd_boolean
+match_clo_clz_dest_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
- /* If the previous instruction had a fixup in mips16 mode, we can not swap.
- This means that the previous instruction was a 4-byte one anyhow. */
- if (mips_opts.mips16 && history[0].fixp[0])
+ if (!match_reg (arg, OP_REG_GP, ®no))
return FALSE;
- /* If the branch is itself the target of a branch, we can not swap.
- We cheat on this; all we check for is whether there is a label on
- this instruction. If there are any branches to anything other than
- a label, users must use .set noreorder. */
- if (seg_info (now_seg)->label_list)
- return FALSE;
+ insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+ return TRUE;
+}
- /* If the previous instruction is in a variant frag other than this
- branch's one, we cannot do the swap. This does not apply to
- MIPS16 code, which uses variant frags for different purposes. */
- if (!mips_opts.mips16
- && history[0].frag
- && history[0].frag->fr_type == rs_machine_dependent)
- return FALSE;
+/* OP_LWM_SWM_LIST matcher. */
- /* We do not swap with instructions that cannot architecturally
- be placed in a branch delay slot, such as SYNC or ERET. We
- also refrain from swapping with a trap instruction, since it
- complicates trap handlers to have the trap instruction be in
- a delay slot. */
- prev_pinfo = history[0].insn_mo->pinfo;
- if (prev_pinfo & INSN_NO_DELAY_SLOT)
- return FALSE;
+static bfd_boolean
+match_lwm_swm_list_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int reglist, sregs, ra, regno1, regno2;
+ struct mips_arg_info reset;
- /* Check for conflicts between the branch and the instructions
- before the candidate delay slot. */
- if (nops_for_insn (0, history + 1, ip) > 0)
+ reglist = 0;
+ if (!match_reg_range (arg, OP_REG_GP, ®no1, ®no2))
return FALSE;
+ do
+ {
+ if (regno2 == FP && regno1 >= S0 && regno1 <= S7)
+ {
+ reglist |= 1 << FP;
+ regno2 = S7;
+ }
+ reglist |= ((1U << regno2 << 1) - 1) & -(1U << regno1);
+ reset = *arg;
+ }
+ while (match_char (arg, ',')
+ && match_reg_range (arg, OP_REG_GP, ®no1, ®no2));
+ *arg = reset;
- /* Check for conflicts between the swapped sequence and the
- target of the branch. */
- if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
- return FALSE;
+ if (operand->size == 2)
+ {
+ /* The list must include both ra and s0-sN, for 0 <= N <= 3. E.g.:
- /* If the branch reads a register that the previous
- instruction sets, we can not swap. */
- gpr_read = gpr_read_mask (ip);
- prev_gpr_write = gpr_write_mask (&history[0]);
- if (gpr_read & prev_gpr_write)
- return FALSE;
+ s0, ra
+ s0, s1, ra, s2, s3
+ s0-s2, ra
- /* If the branch writes a register that the previous
- instruction sets, we can not swap. */
- gpr_write = gpr_write_mask (ip);
- if (gpr_write & prev_gpr_write)
- return FALSE;
+ and any permutations of these. */
+ if ((reglist & 0xfff1ffff) != 0x80010000)
+ return FALSE;
- /* If the branch writes a register that the previous
- instruction reads, we can not swap. */
- prev_gpr_read = gpr_read_mask (&history[0]);
- if (gpr_write & prev_gpr_read)
- return FALSE;
+ 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 FALSE;
- /* If one instruction sets a condition code and the
- other one uses a condition code, we can not swap. */
- pinfo = ip->insn_mo->pinfo;
- if ((pinfo & INSN_READ_COND_CODE)
- && (prev_pinfo & INSN_WRITE_COND_CODE))
- return FALSE;
- if ((pinfo & INSN_WRITE_COND_CODE)
- && (prev_pinfo & INSN_READ_COND_CODE))
+ ra = (reglist >> 27) & 0x10;
+ sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
+ }
+ sregs += 1;
+ if ((sregs & -sregs) != sregs)
return FALSE;
- /* If the previous instruction uses the PC, we can not swap. */
- prev_pinfo2 = history[0].insn_mo->pinfo2;
- if (mips_opts.mips16 && (prev_pinfo & MIPS16_INSN_READ_PC))
- return FALSE;
- if (mips_opts.micromips && (prev_pinfo2 & INSN2_READ_PC))
- return FALSE;
+ insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
+ return TRUE;
+}
- /* If the previous instruction has an incorrect size for a fixed
- branch delay slot in microMIPS mode, we cannot swap. */
- pinfo2 = ip->insn_mo->pinfo2;
- if (mips_opts.micromips
- && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
- && insn_length (history) != 2)
- return FALSE;
- if (mips_opts.micromips
- && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
- && insn_length (history) != 4)
- return FALSE;
+/* OP_ENTRY_EXIT_LIST matcher. */
- /* On R5900 short loops need to be fixed by inserting a nop in
- the branch delay slots.
- A short loop can be terminated too early. */
- if (mips_opts.arch == CPU_R5900
- /* Check if instruction has a parameter, ignore "j $31". */
- && (address_expr != NULL)
- /* Parameter must be 16 bit. */
- && (*reloc_type == BFD_RELOC_16_PCREL_S2)
- /* Branch to same segment. */
- && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg)
- /* Branch to same code fragment. */
- && (symbol_get_frag(address_expr->X_add_symbol) == frag_now)
- /* Can only calculate branch offset if value is known. */
- && symbol_constant_p(address_expr->X_add_symbol)
- /* Check if branch is really conditional. */
- && !((ip->insn_opcode & 0xffff0000) == 0x10000000 /* beq $0,$0 */
- || (ip->insn_opcode & 0xffff0000) == 0x04010000 /* bgez $0 */
- || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+static unsigned int
+match_entry_exit_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ 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);
+ do
{
- int distance;
- /* Check if loop is shorter than 6 instructions including
- branch and delay slot. */
- distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
- if (distance <= 20)
- {
- int i;
- int rv;
+ unsigned int regno1, regno2;
+ bfd_boolean is_freg;
- rv = FALSE;
- /* When the loop includes branches or jumps,
- it is not a short loop. */
- for (i = 0; i < (distance / 4); i++)
- {
- if ((history[i].cleared_p)
- || delayed_branch_p(&history[i]))
- {
- rv = TRUE;
- break;
- }
- }
- if (rv == FALSE)
- {
- /* Insert nop after branch to fix short loop. */
- return FALSE;
- }
- }
+ if (match_reg_range (arg, OP_REG_GP, ®no1, ®no2))
+ is_freg = FALSE;
+ else if (match_reg_range (arg, OP_REG_FP, ®no1, ®no2))
+ is_freg = TRUE;
+ else
+ return FALSE;
+
+ 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 FALSE;
}
+ while (match_char (arg, ','));
+ insn_insert_operand (arg->insn, operand, mask);
return TRUE;
}
-/* Decide how we should add IP to the instruction stream.
- ADDRESS_EXPR is an operand of the instruction to be used with
- RELOC_TYPE. */
+/* OP_SAVE_RESTORE_LIST matcher. */
-static enum append_method
-get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
- bfd_reloc_code_real_type *reloc_type)
+static bfd_boolean
+match_save_restore_list_operand (struct mips_arg_info *arg)
{
- unsigned long pinfo;
+ unsigned int opcode, args, statics, sregs;
+ unsigned int num_frame_sizes, num_args, num_statics, num_sregs;
+ 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;
+ do
+ {
+ unsigned int regno1, regno2;
- /* The relaxed version of a macro sequence must be inherently
- hazard-free. */
- if (mips_relax.sequence == 2)
- return APPEND_ADD;
+ if (arg->token->type == OT_INTEGER)
+ {
+ /* Handle the frame size. */
+ if (!match_const_int (arg, &frame_size, 0))
+ return FALSE;
+ num_frame_sizes += 1;
+ }
+ else
+ {
+ if (!match_reg_range (arg, OP_REG_GP, ®no1, ®no2))
+ return FALSE;
- /* We must not dabble with instructions in a ".set norerorder" block. */
- if (mips_opts.noreorder)
- return APPEND_ADD;
+ 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 FALSE;
+ regno1 += 1;
+ if (regno1 == 24)
+ regno1 = 30;
+ }
+ }
+ }
+ while (match_char (arg, ','));
- /* Otherwise, it's our responsibility to fill branch delay slots. */
- if (delayed_branch_p (ip))
+ /* Encode args/statics combination. */
+ if (args & statics)
+ return FALSE;
+ 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
{
- if (!branch_likely_p (ip)
- && can_swap_branch_p (ip, address_expr, reloc_type))
- return APPEND_SWAP;
+ /* Count arg registers. */
+ num_args = 0;
+ while (args & 0x1)
+ {
+ args >>= 1;
+ num_args += 1;
+ }
+ if (args != 0)
+ return FALSE;
- pinfo = ip->insn_mo->pinfo;
- if (mips_opts.mips16
- && ISA_SUPPORTS_MIPS16E
- && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
- return APPEND_ADD_COMPACT;
+ /* Count static registers. */
+ num_statics = 0;
+ while (statics & 0x8)
+ {
+ statics = (statics << 1) & 0xf;
+ num_statics += 1;
+ }
+ if (statics != 0)
+ return FALSE;
- return APPEND_ADD_WITH_NOP;
+ /* Encode args/statics. */
+ opcode |= ((num_args << 2) | num_statics) << 16;
}
- return APPEND_ADD;
-}
-
-/* IP is a MIPS16 instruction whose opcode we have just changed.
- Point IP->insn_mo to the new opcode's definition. */
+ /* Encode $s0/$s1. */
+ if (sregs & (1 << 0)) /* $s0 */
+ opcode |= 0x20;
+ if (sregs & (1 << 1)) /* $s1 */
+ opcode |= 0x10;
+ sregs >>= 2;
-static void
-find_altered_mips16_opcode (struct mips_cl_insn *ip)
-{
- const struct mips_opcode *mo, *end;
+ /* Encode $s2-$s8. */
+ num_sregs = 0;
+ while (sregs & 1)
+ {
+ sregs >>= 1;
+ num_sregs += 1;
+ }
+ if (sregs != 0)
+ return FALSE;
+ 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));
+ }
- end = &mips16_opcodes[bfd_mips16_num_opcodes];
- for (mo = ip->insn_mo; mo < end; mo++)
- if ((ip->insn_opcode & mo->mask) == mo->match)
- {
- ip->insn_mo = mo;
- return;
- }
- abort ();
+ if (error)
+ {
+ if (arg->soft_match)
+ return FALSE;
+ as_bad ("%s", error);
+ }
+
+ /* Finally build the instruction. */
+ if ((opcode >> 16) != 0 || frame_size == 0)
+ opcode |= MIPS16_EXTEND;
+ arg->insn->insn_opcode = opcode;
+ return TRUE;
}
-/* For microMIPS macros, we need to generate a local number label
- as the target of branches. */
-#define MICROMIPS_LABEL_CHAR '\037'
-static unsigned long micromips_target_label;
-static char micromips_target_name[32];
+/* OP_MDMX_IMM_REG matcher. */
-static char *
-micromips_label_name (void)
+static bfd_boolean
+match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- char *p = micromips_target_name;
- char symbol_name_temporary[24];
- unsigned long l;
- int i;
+ unsigned int regno, uval;
+ bfd_boolean is_qh;
+ const struct mips_opcode *opcode;
- if (*p)
- return p;
+ /* 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);
- i = 0;
- l = micromips_target_label;
-#ifdef LOCAL_LABEL_PREFIX
- *p++ = LOCAL_LABEL_PREFIX;
-#endif
- *p++ = 'L';
- *p++ = MICROMIPS_LABEL_CHAR;
- do
+ if (arg->token->type == OT_REG || arg->token->type == OT_REG_ELEMENT)
{
- symbol_name_temporary[i++] = l % 10 + '0';
- l /= 10;
+ if ((opcode->membership & INSN_5400)
+ && strcmp (opcode->name, "rzu.ob") == 0)
+ {
+ if (arg->soft_match)
+ return FALSE;
+ 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. */
+ if (arg->token->type == OT_REG_ELEMENT)
+ {
+ if (!match_regno (arg, OP_REG_VEC, arg->token->u.reg_element.regno,
+ ®no))
+ return FALSE;
+ if (arg->token->u.reg_element.index > (is_qh ? 3 : 7))
+ {
+ if (arg->soft_match)
+ return FALSE;
+ as_bad (_("Invalid element selector"));
+ }
+ else
+ uval |= arg->token->u.reg_element.index << (is_qh ? 2 : 1) << 5;
+ }
+ 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 FALSE;
+ as_bad (_("Operand %d of `%s' must be scalar"),
+ arg->argnum, opcode->name);
+ }
+
+ if (!match_regno (arg, OP_REG_VEC, arg->token->u.regno, ®no))
+ return FALSE;
+ if (is_qh)
+ uval |= MDMX_FMTSEL_VEC_QH << 5;
+ else
+ uval |= MDMX_FMTSEL_VEC_OB << 5;
+ }
+ uval |= regno;
+ ++arg->token;
}
- while (l != 0);
- while (i > 0)
- *p++ = symbol_name_temporary[--i];
- *p = '\0';
+ else
+ {
+ offsetT sval;
- return micromips_target_name;
+ if (!match_const_int (arg, &sval, 0))
+ return FALSE;
+ if (sval < 0 || sval > 31)
+ {
+ if (arg->soft_match)
+ return FALSE;
+ 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 TRUE;
}
-static void
-micromips_label_expr (expressionS *label_expr)
-{
- label_expr->X_op = O_symbol;
- label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
- label_expr->X_add_number = 0;
-}
+/* OP_PC matcher. */
-static void
-micromips_label_inc (void)
+static bfd_boolean
+match_pc_operand (struct mips_arg_info *arg)
{
- micromips_target_label++;
- *micromips_target_name = '\0';
+ if (arg->token->type == OT_REG && (arg->token->u.regno & RTYPE_PC))
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
}
-static void
-micromips_add_label (void)
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher. OTHER_REGNO is the
+ register that we need to match. */
+
+static bfd_boolean
+match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
{
- symbolS *s;
+ unsigned int regno;
- s = colon (micromips_label_name ());
- micromips_label_inc ();
- S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+ return match_reg (arg, OP_REG_GP, ®no) && regno == other_regno;
}
-/* If assembling microMIPS code, then return the microMIPS reloc
- corresponding to the requested one if any. Otherwise return
- the reloc unchanged. */
+/* Read a floating-point constant from S for LI.S or LI.D. LENGTH is
+ the length of the value in bytes (4 for float, 8 for double) and
+ USING_GPRS says whether the destination is a GPR rather than an FPR.
-static bfd_reloc_code_real_type
-micromips_map_reloc (bfd_reloc_code_real_type reloc)
+ Return the constant in IMM and OFFSET as follows:
+
+ - If the constant should be loaded via memory, set IMM to O_absent and
+ OFFSET to the memory address.
+
+ - Otherwise, if the constant should be loaded into two 32-bit registers,
+ set IMM to the O_constant to load into the high register and OFFSET
+ to the corresponding value for the low register.
+
+ - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent.
+
+ These constants only appear as the last operand in an instruction,
+ and every instruction that accepts them in any variant accepts them
+ in all variants. This means we don't have to worry about backing out
+ any changes if the instruction does not match. We just match
+ unconditionally and report an error if the constant is invalid. */
+
+static bfd_boolean
+match_float_constant (struct mips_arg_info *arg, expressionS *imm,
+ expressionS *offset, int length, bfd_boolean using_gprs)
{
- static const bfd_reloc_code_real_type relocs[][2] =
+ char *p;
+ segT seg, new_seg;
+ subsegT subseg;
+ const char *newname;
+ unsigned char *data;
+
+ /* Where the constant is placed is based on how the MIPS assembler
+ does things:
+
+ length == 4 && using_gprs -- immediate value only
+ length == 8 && using_gprs -- .rdata or immediate value
+ length == 4 && !using_gprs -- .lit4 or immediate value
+ length == 8 && !using_gprs -- .lit8 or immediate value
+
+ The .lit4 and .lit8 sections are only used if permitted by the
+ -G argument. */
+ if (arg->token->type != OT_FLOAT)
+ return FALSE;
+
+ gas_assert (arg->token->u.flt.length == length);
+ data = arg->token->u.flt.data;
+ ++arg->token;
+
+ /* Handle 32-bit constants for which an immediate value is best. */
+ if (length == 4
+ && (using_gprs
+ || g_switch_value < 4
+ || (data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0)))
{
- /* Keep sorted incrementally by the left-hand key. */
- { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
- { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
- { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
- { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
- { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
- { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
- { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
- { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
- { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
- { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
- { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
- { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
- { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
- { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
- { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
- { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
- { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
- { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
- { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
- { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
- { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
- { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
- { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
- { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
- { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
- { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
- { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
- };
- bfd_reloc_code_real_type r;
- size_t i;
+ imm->X_op = O_constant;
+ if (!target_big_endian)
+ imm->X_add_number = bfd_getl32 (data);
+ else
+ imm->X_add_number = bfd_getb32 (data);
+ offset->X_op = O_absent;
+ return TRUE;
+ }
- if (!mips_opts.micromips)
- return reloc;
- for (i = 0; i < ARRAY_SIZE (relocs); i++)
+ /* Handle 64-bit constants for which an immediate value is best. */
+ if (length == 8
+ && !mips_disable_float_construction
+ /* Constants can only be constructed in GPRs and copied
+ to FPRs if the GPRs are at least as wide as the FPRs.
+ Force the constant into memory if we are using 64-bit FPRs
+ but the GPRs are only 32 bits wide. */
+ /* ??? No longer true with the addition of MTHC1, but this
+ is legacy code... */
+ && (using_gprs || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
+ && ((data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0))
+ && ((data[4] == 0 && data[5] == 0)
+ || (data[6] == 0 && data[7] == 0)))
{
- r = relocs[i][0];
- if (r > reloc)
- return reloc;
- if (r == reloc)
- return relocs[i][1];
+ /* The value is simple enough to load with a couple of instructions.
+ If using 32-bit registers, set IMM to the high order 32 bits and
+ OFFSET to the low order 32 bits. Otherwise, set IMM to the entire
+ 64 bit constant. */
+ if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
+ {
+ imm->X_op = O_constant;
+ offset->X_op = O_constant;
+ if (!target_big_endian)
+ {
+ imm->X_add_number = bfd_getl32 (data + 4);
+ offset->X_add_number = bfd_getl32 (data);
+ }
+ else
+ {
+ imm->X_add_number = bfd_getb32 (data);
+ offset->X_add_number = bfd_getb32 (data + 4);
+ }
+ if (offset->X_add_number == 0)
+ offset->X_op = O_absent;
+ }
+ else
+ {
+ imm->X_op = O_constant;
+ if (!target_big_endian)
+ imm->X_add_number = bfd_getl64 (data);
+ else
+ imm->X_add_number = bfd_getb64 (data);
+ offset->X_op = O_absent;
+ }
+ return TRUE;
}
- return reloc;
+
+ /* Switch to the right section. */
+ seg = now_seg;
+ subseg = now_subseg;
+ if (length == 4)
+ {
+ gas_assert (!using_gprs && g_switch_value >= 4);
+ newname = ".lit4";
+ }
+ else
+ {
+ if (using_gprs || g_switch_value < 8)
+ newname = RDATA_SECTION_NAME;
+ else
+ newname = ".lit8";
+ }
+
+ new_seg = subseg_new (newname, (subsegT) 0);
+ bfd_set_section_flags (stdoutput, new_seg,
+ SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+ frag_align (length == 4 ? 2 : 3, 0, 0);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (new_seg, 4);
+ else
+ record_alignment (new_seg, length == 4 ? 2 : 3);
+ if (seg == now_seg)
+ as_bad (_("Can't use floating point insn in this section"));
+
+ /* Set the argument to the current address in the section. */
+ imm->X_op = O_absent;
+ offset->X_op = O_symbol;
+ offset->X_add_symbol = symbol_temp_new_now ();
+ offset->X_add_number = 0;
+
+ /* Put the floating point number into the section. */
+ p = frag_more (length);
+ memcpy (p, data, length);
+
+ /* Switch back to the original section. */
+ subseg_set (seg, subseg);
+ return TRUE;
}
-/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
- Return true on success, storing the resolved value in RESULT. */
+/* 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 bfd_boolean
-calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
- offsetT *result)
+match_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
{
- switch (reloc)
+ switch (operand->type)
{
- case BFD_RELOC_MIPS_HIGHEST:
- case BFD_RELOC_MICROMIPS_HIGHEST:
- *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
- return TRUE;
+ case OP_INT:
+ return match_int_operand (arg, operand);
- case BFD_RELOC_MIPS_HIGHER:
- case BFD_RELOC_MICROMIPS_HIGHER:
- *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
- return TRUE;
+ case OP_MAPPED_INT:
+ return match_mapped_int_operand (arg, operand);
- case BFD_RELOC_HI16_S:
- case BFD_RELOC_MICROMIPS_HI16_S:
- case BFD_RELOC_MIPS16_HI16_S:
- *result = ((operand + 0x8000) >> 16) & 0xffff;
- return TRUE;
+ case OP_MSB:
+ return match_msb_operand (arg, operand);
- case BFD_RELOC_HI16:
- case BFD_RELOC_MICROMIPS_HI16:
- case BFD_RELOC_MIPS16_HI16:
- *result = (operand >> 16) & 0xffff;
- return TRUE;
+ case OP_REG:
+ return match_reg_operand (arg, operand);
- case BFD_RELOC_LO16:
- case BFD_RELOC_MICROMIPS_LO16:
- case BFD_RELOC_MIPS16_LO16:
- *result = operand & 0xffff;
- return TRUE;
+ case OP_REG_PAIR:
+ return match_reg_pair_operand (arg, operand);
- case BFD_RELOC_UNUSED:
- *result = operand;
- return TRUE;
+ case OP_PCREL:
+ return match_pcrel_operand (arg);
- default:
- return FALSE;
+ case OP_PERF_REG:
+ return match_perf_reg_operand (arg, operand);
+
+ case OP_ADDIUSP_INT:
+ return match_addiusp_operand (arg, operand);
+
+ case OP_CLO_CLZ_DEST:
+ return match_clo_clz_dest_operand (arg, operand);
+
+ case OP_LWM_SWM_LIST:
+ return match_lwm_swm_list_operand (arg, operand);
+
+ case OP_ENTRY_EXIT_LIST:
+ return match_entry_exit_operand (arg, operand);
+
+ case OP_SAVE_RESTORE_LIST:
+ return match_save_restore_list_operand (arg);
+
+ case OP_MDMX_IMM_REG:
+ return match_mdmx_imm_reg_operand (arg, operand);
+
+ case OP_REPEAT_DEST_REG:
+ return match_tied_reg_operand (arg, arg->dest_regno);
+
+ case OP_REPEAT_PREV_REG:
+ return match_tied_reg_operand (arg, arg->last_regno);
+
+ case OP_PC:
+ return match_pc_operand (arg);
}
+ abort ();
}
-/* Output an instruction. IP is the instruction information.
- ADDRESS_EXPR is an operand of the instruction to be used with
- RELOC_TYPE. EXPANSIONP is true if the instruction is part of
- a macro expansion. */
+/* ARG is the state after successfully matching an instruction.
+ Issue any queued-up warnings. */
static void
-append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
- bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
+check_completed_insn (struct mips_arg_info *arg)
{
- unsigned long prev_pinfo2, pinfo;
- bfd_boolean relaxed_branch = FALSE;
- enum append_method method;
- bfd_boolean relax32;
- int branch_disp;
+ 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);
+ }
+}
- if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
- fix_loongson2f (ip);
+/* Return true if modifying general-purpose register REG needs a delay. */
- file_ase_mips16 |= mips_opts.mips16;
- file_ase_micromips |= mips_opts.micromips;
+static bfd_boolean
+reg_needs_delay (unsigned int reg)
+{
+ unsigned long prev_pinfo;
- prev_pinfo2 = history[0].insn_mo->pinfo2;
- pinfo = ip->insn_mo->pinfo;
+ prev_pinfo = history[0].insn_mo->pinfo;
+ if (!mips_opts.noreorder
+ && (((prev_pinfo & INSN_LOAD_MEMORY_DELAY) && !gpr_interlocks)
+ || ((prev_pinfo & INSN_LOAD_COPROC_DELAY) && !cop_interlocks))
+ && (gpr_write_mask (&history[0]) & (1 << reg)))
+ return TRUE;
- if (mips_opts.micromips
- && !expansionp
- && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
- && micromips_insn_length (ip->insn_mo) != 2)
- || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
- && micromips_insn_length (ip->insn_mo) != 4)))
- as_warn (_("Wrong size instruction in a %u-bit branch delay slot"),
- (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
+ return FALSE;
+}
- if (address_expr == NULL)
- ip->complete_p = 1;
- else if (reloc_type[0] <= BFD_RELOC_UNUSED
- && reloc_type[1] == BFD_RELOC_UNUSED
- && reloc_type[2] == BFD_RELOC_UNUSED
- && address_expr->X_op == O_constant)
- {
- switch (*reloc_type)
- {
- case BFD_RELOC_MIPS_JMP:
- {
- int shift;
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
+ Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
+ by VR4120 errata. */
- shift = mips_opts.micromips ? 1 : 2;
- if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
- as_bad (_("jump to misaligned address (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- ip->insn_opcode |= ((address_expr->X_add_number >> shift)
- & 0x3ffffff);
- ip->complete_p = 1;
- }
- break;
+static unsigned int
+classify_vr4120_insn (const char *name)
+{
+ if (strncmp (name, "macc", 4) == 0)
+ return FIX_VR4120_MACC;
+ if (strncmp (name, "dmacc", 5) == 0)
+ return FIX_VR4120_DMACC;
+ if (strncmp (name, "mult", 4) == 0)
+ return FIX_VR4120_MULT;
+ if (strncmp (name, "dmult", 5) == 0)
+ return FIX_VR4120_DMULT;
+ if (strstr (name, "div"))
+ return FIX_VR4120_DIV;
+ if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
+ return FIX_VR4120_MTHILO;
+ return NUM_FIX_VR4120_CLASSES;
+}
- 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)
- | ((address_expr->X_add_number & 0x3fffc) >> 2));
- ip->complete_p = 1;
- break;
+#define INSN_ERET 0x42000018
+#define INSN_DERET 0x4200001f
- case BFD_RELOC_16_PCREL_S2:
- {
- int shift;
+/* Return the number of instructions that must separate INSN1 and INSN2,
+ where INSN1 is the earlier instruction. Return the worst-case value
+ for any INSN2 if INSN2 is null. */
- shift = mips_opts.micromips ? 1 : 2;
- if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
- as_bad (_("branch to misaligned address (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- if (!mips_relax_branch)
- {
- if ((address_expr->X_add_number + (1 << (shift + 15)))
- & ~((1 << (shift + 16)) - 1))
- as_bad (_("branch address range overflow (0x%lx)"),
- (unsigned long) address_expr->X_add_number);
- ip->insn_opcode |= ((address_expr->X_add_number >> shift)
- & 0xffff);
- }
- }
- break;
+static unsigned int
+insns_between (const struct mips_cl_insn *insn1,
+ const struct mips_cl_insn *insn2)
+{
+ unsigned long pinfo1, pinfo2;
+ unsigned int mask;
- default:
- {
- offsetT value;
+ /* If INFO2 is null, pessimistically assume that all flags are set for
+ the second instruction. */
+ pinfo1 = insn1->insn_mo->pinfo;
+ pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
- if (calculate_reloc (*reloc_type, address_expr->X_add_number,
- &value))
- {
- ip->insn_opcode |= value & 0xffff;
- ip->complete_p = 1;
- }
- }
- break;
+ /* For most targets, write-after-read dependencies on the HI and LO
+ registers must be separated by at least two instructions. */
+ if (!hilo_interlocks)
+ {
+ if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
+ return 2;
+ if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
+ return 2;
+ }
+
+ /* If we're working around r7000 errata, there must be two instructions
+ between an mfhi or mflo and any instruction that uses the result. */
+ if (mips_7000_hilo_fix
+ && !mips_opts.micromips
+ && MF_HILO_INSN (pinfo1)
+ && (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1))))
+ return 2;
+
+ /* If we're working around 24K errata, one instruction is required
+ if an ERET or DERET is followed by a branch instruction. */
+ if (mips_fix_24k && !mips_opts.micromips)
+ {
+ if (insn1->insn_opcode == INSN_ERET
+ || insn1->insn_opcode == INSN_DERET)
+ {
+ if (insn2 == NULL
+ || insn2->insn_opcode == INSN_ERET
+ || insn2->insn_opcode == INSN_DERET
+ || delayed_branch_p (insn2))
+ return 1;
}
}
- if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+ /* If working around VR4120 errata, check for combinations that need
+ a single intervening instruction. */
+ if (mips_fix_vr4120 && !mips_opts.micromips)
{
- /* There are a lot of optimizations we could do that we don't.
- In particular, we do not, in general, reorder instructions.
- If you use gcc with optimization, it will reorder
- instructions and generally do much more optimization then we
- do here; repeating all that work in the assembler would only
- benefit hand written assembly code, and does not seem worth
- it. */
- int nops = (mips_optimize == 0
- ? nops_for_insn (0, history, NULL)
- : nops_for_insn_or_target (0, history, ip));
- if (nops > 0)
+ unsigned int class1, class2;
+
+ class1 = classify_vr4120_insn (insn1->insn_mo->name);
+ if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
{
- fragS *old_frag;
- unsigned long old_frag_offset;
- int i;
+ if (insn2 == NULL)
+ return 1;
+ class2 = classify_vr4120_insn (insn2->insn_mo->name);
+ if (vr4120_conflicts[class1] & (1 << class2))
+ return 1;
+ }
+ }
- old_frag = frag_now;
- old_frag_offset = frag_now_fix ();
+ if (!HAVE_CODE_COMPRESSION)
+ {
+ /* Check for GPR or coprocessor load delays. All such delays
+ are on the RT register. */
+ /* Itbl support may require additional care here. */
+ if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY_DELAY))
+ || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
+ {
+ if (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1)))
+ return 1;
+ }
- for (i = 0; i < nops; i++)
- add_fixed_insn (NOP_INSN);
- insert_into_history (0, nops, NOP_INSN);
+ /* Check for generic coprocessor hazards.
- if (listing)
+ This case is not handled very well. There is no special
+ knowledge of CP0 handling, and the coprocessors other than
+ the floating point unit are not distinguished at all. */
+ /* Itbl support may require additional care here. FIXME!
+ Need to modify this to include knowledge about
+ user specified delays! */
+ else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE_DELAY))
+ || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
+ {
+ /* Handle cases where INSN1 writes to a known general coprocessor
+ register. There must be a one instruction delay before INSN2
+ if INSN2 reads that register, otherwise no delay is needed. */
+ mask = fpr_write_mask (insn1);
+ if (mask != 0)
{
- listing_prev_line ();
- /* We may be at the start of a variant frag. In case we
- are, make sure there is enough space for the frag
- after the frags created by listing_prev_line. The
- argument to frag_grow here must be at least as large
- as the argument to all other calls to frag_grow in
- this file. We don't have to worry about being in the
- middle of a variant frag, because the variants insert
- all needed nop instructions themselves. */
- frag_grow (40);
+ if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
+ return 1;
}
+ else
+ {
+ /* Read-after-write dependencies on the control registers
+ require a two-instruction gap. */
+ if ((pinfo1 & INSN_WRITE_COND_CODE)
+ && (pinfo2 & INSN_READ_COND_CODE))
+ return 2;
- mips_move_text_labels ();
-
-#ifndef NO_ECOFF_DEBUGGING
- if (ECOFF_DEBUGGING)
- ecoff_fix_loc (old_frag, old_frag_offset);
-#endif
+ /* We don't know exactly what INSN1 does. If INSN2 is
+ also a coprocessor instruction, assume there must be
+ a one instruction gap. */
+ if (pinfo2 & INSN_COP)
+ return 1;
+ }
}
+
+ /* Check for read-after-write dependencies on the coprocessor
+ control registers in cases where INSN1 does not need a general
+ coprocessor delay. This means that INSN1 is a floating point
+ comparison instruction. */
+ /* Itbl support may require additional care here. */
+ else if (!cop_interlocks
+ && (pinfo1 & INSN_WRITE_COND_CODE)
+ && (pinfo2 & INSN_READ_COND_CODE))
+ return 1;
}
- else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
- {
- int nops;
- /* Work out how many nops in prev_nop_frag are needed by IP,
- ignoring hazards generated by the first prev_nop_frag_since
- instructions. */
- nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
- gas_assert (nops <= prev_nop_frag_holds);
+ return 0;
+}
- /* Enforce NOPS as a minimum. */
- if (nops > prev_nop_frag_required)
- prev_nop_frag_required = nops;
+/* Return the number of nops that would be needed to work around the
+ VR4130 mflo/mfhi errata if instruction INSN immediately followed
+ the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
+ that are contained within the first IGNORE instructions of HIST. */
- if (prev_nop_frag_holds == prev_nop_frag_required)
- {
- /* Settle for the current number of nops. Update the history
- accordingly (for the benefit of any future .set reorder code). */
- prev_nop_frag = NULL;
- insert_into_history (prev_nop_frag_since,
- prev_nop_frag_holds, NOP_INSN);
- }
- else
- {
- /* Allow this instruction to replace one of the nops that was
- tentatively added to prev_nop_frag. */
- prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
- prev_nop_frag_holds--;
- prev_nop_frag_since++;
- }
- }
+static int
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int i, j;
+ unsigned int mask;
- method = get_append_method (ip, address_expr, reloc_type);
- branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
+ /* Check if the instruction writes to HI or LO. MTHI and MTLO
+ are not affected by the errata. */
+ if (insn != 0
+ && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
+ || strcmp (insn->insn_mo->name, "mtlo") == 0
+ || strcmp (insn->insn_mo->name, "mthi") == 0))
+ return 0;
- dwarf2_emit_insn (0);
- /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
- so "move" the instruction address accordingly.
+ /* Search for the first MFLO or MFHI. */
+ for (i = 0; i < MAX_VR4130_NOPS; i++)
+ if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
+ {
+ /* Extract the destination register. */
+ mask = gpr_write_mask (&hist[i]);
- Also, it doesn't seem appropriate for the assembler to reorder .loc
- entries. If this instruction is a branch that we are going to swap
- with the previous instruction, the two instructions should be
- treated as a unit, and the debug information for both instructions
- should refer to the start of the branch sequence. Using the
- current position is certainly wrong when swapping a 32-bit branch
- and a 16-bit delay slot, since the current position would then be
- in the middle of a branch. */
- dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
+ /* No nops are needed if INSN reads that register. */
+ if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
+ return 0;
- relax32 = (mips_relax_branch
- /* Don't try branch relaxation within .set nomacro, or within
- .set noat if we use $at for PIC computations. If it turns
- out that the branch was out-of-range, we'll get an error. */
- && !mips_opts.warn_about_macros
- && (mips_opts.at || mips_pic == NO_PIC)
- /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
- as they have no complementing branches. */
- && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
+ /* ...or if any of the intervening instructions do. */
+ for (j = 0; j < i; j++)
+ if (gpr_read_mask (&hist[j]) & mask)
+ return 0;
- if (!HAVE_CODE_COMPRESSION
- && address_expr
- && relax32
- && *reloc_type == BFD_RELOC_16_PCREL_S2
- && delayed_branch_p (ip))
- {
- relaxed_branch = TRUE;
- add_relaxed_insn (ip, (relaxed_branch_length
- (NULL, NULL,
- uncond_branch_p (ip) ? -1
- : branch_likely_p (ip) ? 1
- : 0)), 4,
- RELAX_BRANCH_ENCODE
- (AT,
- uncond_branch_p (ip),
- branch_likely_p (ip),
- pinfo & INSN_WRITE_GPR_31,
- 0),
- address_expr->X_add_symbol,
- address_expr->X_add_number);
- *reloc_type = BFD_RELOC_UNUSED;
- }
- else if (mips_opts.micromips
- && address_expr
- && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
- || *reloc_type > BFD_RELOC_UNUSED)
- && (delayed_branch_p (ip) || compact_branch_p (ip))
- /* Don't try branch relaxation when users specify
- 16-bit/32-bit instructions. */
- && !forced_insn_length)
- {
- bfd_boolean relax16 = *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 al = pinfo & INSN_WRITE_GPR_31;
- int length32;
+ if (i >= ignore)
+ return MAX_VR4130_NOPS - i;
+ }
+ return 0;
+}
- gas_assert (address_expr != NULL);
- gas_assert (!mips_relax.sequence);
+#define BASE_REG_EQ(INSN1, INSN2) \
+ ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+ == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
- relaxed_branch = TRUE;
- length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
- add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
- RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
- relax32, 0, 0),
- address_expr->X_add_symbol,
- address_expr->X_add_number);
- *reloc_type = BFD_RELOC_UNUSED;
- }
- else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
- {
- /* We need to set up a variant frag. */
- gas_assert (address_expr != NULL);
- add_relaxed_insn (ip, 4, 0,
- RELAX_MIPS16_ENCODE
- (*reloc_type - BFD_RELOC_UNUSED,
- forced_insn_length == 2, forced_insn_length == 4,
- delayed_branch_p (&history[0]),
- history[0].mips16_absolute_jump_p),
- make_expr_symbol (address_expr), 0);
- }
- 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
- a following jump instruction. */
- frag_grow (6);
- add_fixed_insn (ip);
- }
- else
- {
- if (mips_opts.mips16
- && mips_opts.noreorder
- && delayed_branch_p (&history[0]))
- as_warn (_("extended instruction in delay slot"));
+/* Return the minimum alignment for this store instruction. */
- if (mips_relax.sequence)
- {
- /* If we've reached the end of this frag, turn it into a variant
- frag and record the information for the instructions we've
- written so far. */
- if (frag_room () < 4)
- relax_close_frag ();
- mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
- }
+static int
+fix_24k_align_to (const struct mips_opcode *mo)
+{
+ if (strcmp (mo->name, "sh") == 0)
+ return 2;
- if (mips_relax.sequence != 2)
- {
- if (mips_macro_warning.first_insn_sizes[0] == 0)
- mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
- mips_macro_warning.sizes[0] += insn_length (ip);
- mips_macro_warning.insns[0]++;
- }
- if (mips_relax.sequence != 1)
- {
- if (mips_macro_warning.first_insn_sizes[1] == 0)
- mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
- mips_macro_warning.sizes[1] += insn_length (ip);
- mips_macro_warning.insns[1]++;
- }
+ if (strcmp (mo->name, "swc1") == 0
+ || strcmp (mo->name, "swc2") == 0
+ || strcmp (mo->name, "sw") == 0
+ || strcmp (mo->name, "sc") == 0
+ || strcmp (mo->name, "s.s") == 0)
+ return 4;
- if (mips_opts.mips16)
- {
- ip->fixed_p = 1;
- ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
- }
- add_fixed_insn (ip);
- }
+ if (strcmp (mo->name, "sdc1") == 0
+ || strcmp (mo->name, "sdc2") == 0
+ || strcmp (mo->name, "s.d") == 0)
+ return 8;
- if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
- {
- bfd_reloc_code_real_type final_type[3];
- reloc_howto_type *howto0;
- reloc_howto_type *howto;
- int i;
+ /* sb, swl, swr */
+ return 1;
+}
- /* Perform any necessary conversion to microMIPS relocations
- and find out how many relocations there actually are. */
- for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
- final_type[i] = micromips_map_reloc (reloc_type[i]);
+struct fix_24k_store_info
+ {
+ /* Immediate offset, if any, for this store instruction. */
+ short off;
+ /* Alignment required by this store instruction. */
+ int align_to;
+ /* True for register offsets. */
+ int register_offset;
+ };
- /* In a compound relocation, it is the final (outermost)
- operator that determines the relocated field. */
- howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
- if (!howto)
- abort ();
+/* Comparison function used by qsort. */
- if (i > 1)
- howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
- ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
- bfd_get_reloc_size (howto),
- address_expr,
- howto0 && howto0->pc_relative,
- final_type[0]);
+static int
+fix_24k_sort (const void *a, const void *b)
+{
+ const struct fix_24k_store_info *pos1 = a;
+ const struct fix_24k_store_info *pos2 = b;
- /* Tag symbols that have a R_MIPS16_26 relocation against them. */
- if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
- *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
+ return (pos1->off - pos2->off);
+}
- /* These relocations can have an addend that won't fit in
- 4 octets for 64bit assembly. */
- if (HAVE_64BIT_GPRS
- && ! howto->partial_inplace
- && (reloc_type[0] == BFD_RELOC_16
- || reloc_type[0] == BFD_RELOC_32
- || reloc_type[0] == BFD_RELOC_MIPS_JMP
- || reloc_type[0] == BFD_RELOC_GPREL16
- || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
- || reloc_type[0] == BFD_RELOC_GPREL32
- || reloc_type[0] == BFD_RELOC_64
- || reloc_type[0] == BFD_RELOC_CTOR
- || reloc_type[0] == BFD_RELOC_MIPS_SUB
- || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
- || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
- || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
- || reloc_type[0] == BFD_RELOC_MIPS_REL16
- || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
- || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
- || hi16_reloc_p (reloc_type[0])
- || lo16_reloc_p (reloc_type[0])))
- ip->fixp[0]->fx_no_overflow = 1;
+/* INSN is a store instruction. Try to record the store information
+ in STINFO. Return false if the information isn't known. */
- /* These relocations can have an addend that won't fit in 2 octets. */
- if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
- || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
- ip->fixp[0]->fx_no_overflow = 1;
+static bfd_boolean
+fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
+ const struct mips_cl_insn *insn)
+{
+ /* The instruction must have a known offset. */
+ if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+ return FALSE;
- if (mips_relax.sequence)
- {
- if (mips_relax.first_fixup == 0)
- mips_relax.first_fixup = ip->fixp[0];
- }
- else if (reloc_needs_lo_p (*reloc_type))
- {
- struct mips_hi_fixup *hi_fixup;
+ stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+ stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+ return TRUE;
+}
- /* Reuse the last entry if it already has a matching %lo. */
- hi_fixup = mips_hi_fixup_list;
- if (hi_fixup == 0
- || !fixup_has_matching_lo_p (hi_fixup->fixp))
- {
- hi_fixup = ((struct mips_hi_fixup *)
- xmalloc (sizeof (struct mips_hi_fixup)));
- hi_fixup->next = mips_hi_fixup_list;
- mips_hi_fixup_list = hi_fixup;
- }
- hi_fixup->fixp = ip->fixp[0];
- hi_fixup->seg = now_seg;
- }
+/* Return the number of nops that would be needed to work around the 24k
+ "lost data on stores during refill" errata if instruction INSN
+ immediately followed the 2 instructions described by HIST.
+ Ignore hazards that are contained within the first IGNORE
+ instructions of HIST.
- /* Add fixups for the second and third relocations, if given.
- Note that the ABI allows the second relocation to be
- against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
- moment we only use RSS_UNDEF, but we could add support
- for the others if it ever becomes necessary. */
- for (i = 1; i < 3; i++)
- if (reloc_type[i] != BFD_RELOC_UNUSED)
- {
- ip->fixp[i] = fix_new (ip->frag, ip->where,
- ip->fixp[0]->fx_size, NULL, 0,
- FALSE, final_type[i]);
+ Problem: The FSB (fetch store buffer) acts as an intermediate buffer
+ for the data cache refills and store data. The following describes
+ the scenario where the store data could be lost.
- /* Use fx_tcbit to mark compound relocs. */
- ip->fixp[0]->fx_tcbit = 1;
- ip->fixp[i]->fx_tcbit = 1;
- }
- }
- install_insn (ip);
+ * A data cache miss, due to either a load or a store, causing fill
+ data to be supplied by the memory subsystem
+ * The first three doublewords of fill data are returned and written
+ into the cache
+ * A sequence of four stores occurs in consecutive cycles around the
+ final doubleword of the fill:
+ * Store A
+ * Store B
+ * Store C
+ * Zero, One or more instructions
+ * Store D
- /* Update the register mask information. */
- mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
- mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
+ The four stores A-D must be to different doublewords of the line that
+ is being filled. The fourth instruction in the sequence above permits
+ the fill of the final doubleword to be transferred from the FSB into
+ the cache. In the sequence above, the stores may be either integer
+ (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
+ swxc1, sdxc1, suxc1) stores, as long as the four stores are to
+ different doublewords on the line. If the floating point unit is
+ running in 1:2 mode, it is not possible to create the sequence above
+ using only floating point store instructions.
- switch (method)
- {
- case APPEND_ADD:
- insert_into_history (0, 1, ip);
- break;
+ In this case, the cache line being filled is incorrectly marked
+ invalid, thereby losing the data from any store to the line that
+ occurs between the original miss and the completion of the five
+ cycle sequence shown above.
- case APPEND_ADD_WITH_NOP:
- {
- struct mips_cl_insn *nop;
+ The workarounds are:
- insert_into_history (0, 1, ip);
- nop = get_delay_slot_nop (ip);
- add_fixed_insn (nop);
- insert_into_history (0, 1, nop);
- if (mips_relax.sequence)
- mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
- }
- break;
+ * Run the data cache in write-through mode.
+ * Insert a non-store instruction between
+ Store A and Store B or Store B and Store C. */
+
+static int
+nops_for_24k (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ struct fix_24k_store_info pos[3];
+ int align, i, base_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);
- install_insn (ip);
- insert_into_history (0, 1, ip);
- break;
+ if (ignore >= 2)
+ return 0;
- case APPEND_SWAP:
- {
- struct mips_cl_insn delay = history[0];
- if (mips_opts.mips16)
- {
- know (delay.frag == ip->frag);
- move_insn (ip, delay.frag, delay.where);
- move_insn (&delay, ip->frag, ip->where + insn_length (ip));
- }
- else if (relaxed_branch || delay.frag != ip->frag)
- {
- /* Add the delay slot instruction to the end of the
- current frag and shrink the fixed part of the
- original frag. If the branch occupies the tail of
- the latter, move it backwards to cover the gap. */
- delay.frag->fr_fix -= branch_disp;
- if (delay.frag == ip->frag)
- move_insn (ip, ip->frag, ip->where - branch_disp);
- add_fixed_insn (&delay);
- }
- else
- {
- move_insn (&delay, ip->frag,
- ip->where - branch_disp + insn_length (ip));
- move_insn (ip, history[0].frag, history[0].where);
- }
- history[0] = *ip;
- delay.fixed_p = 1;
- insert_into_history (0, 1, &delay);
- }
- break;
- }
+ /* If the previous instruction wasn't a store, there's nothing to
+ worry about. */
+ if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
- /* If we have just completed an unconditional branch, clear the history. */
- if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
- || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
- {
- unsigned int i;
+ /* If the instructions after the previous one are unknown, we have
+ to assume the worst. */
+ if (!insn)
+ return 1;
- mips_no_prev_insn ();
+ /* Check whether we are dealing with three consecutive stores. */
+ if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+ || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
- for (i = 0; i < ARRAY_SIZE (history); i++)
- history[i].cleared_p = 1;
- }
+ /* If we don't know the relationship between the store addresses,
+ assume the worst. */
+ if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
+ || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
+ return 1;
- /* We need to emit a label at the end of branch-likely macros. */
- if (emit_branch_likely_macro)
+ if (!fix_24k_record_store_info (&pos[0], insn)
+ || !fix_24k_record_store_info (&pos[1], &hist[0])
+ || !fix_24k_record_store_info (&pos[2], &hist[1]))
+ return 1;
+
+ qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+
+ /* Pick a value of ALIGN and X such that all offsets are adjusted by
+ X bytes and such that the base register + X is known to be aligned
+ to align bytes. */
+
+ if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
+ align = 8;
+ else
{
- emit_branch_likely_macro = FALSE;
- micromips_add_label ();
+ align = pos[0].align_to;
+ base_offset = pos[0].off;
+ for (i = 1; i < 3; i++)
+ if (align < pos[i].align_to)
+ {
+ align = pos[i].align_to;
+ base_offset = pos[i].off;
+ }
+ for (i = 0; i < 3; i++)
+ pos[i].off -= base_offset;
}
- /* We just output an insn, so the next one doesn't have a label. */
- mips_clear_insn_labels ();
-}
+ pos[0].off &= ~align + 1;
+ pos[1].off &= ~align + 1;
+ pos[2].off &= ~align + 1;
-/* Forget that there was any previous instruction or label.
- When BRANCH is true, the branch history is also flushed. */
+ /* If any two stores write to the same chunk, they also write to the
+ same doubleword. The offsets are still sorted at this point. */
+ if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
+ return 0;
-static void
-mips_no_prev_insn (void)
-{
- prev_nop_frag = NULL;
- insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
- mips_clear_insn_labels ();
+ /* A range of at least 9 bytes is needed for the stores to be in
+ non-overlapping doublewords. */
+ if (pos[2].off - pos[0].off <= 8)
+ return 0;
+
+ if (pos[2].off - pos[1].off >= 24
+ || pos[1].off - pos[0].off >= 24
+ || pos[2].off - pos[0].off >= 32)
+ return 0;
+
+ return 1;
}
-/* This function must be called before we emit something other than
- instructions. It is like mips_no_prev_insn except that it inserts
- any NOPS that might be needed by previous instructions. */
+/* Return the number of nops that would be needed if instruction INSN
+ immediately followed the MAX_NOPS instructions given by HIST,
+ where HIST[0] is the most recent instruction. Ignore hazards
+ between INSN and the first IGNORE instructions in HIST.
-void
-mips_emit_delays (void)
+ If INSN is null, return the worse-case number of nops for any
+ instruction. */
+
+static int
+nops_for_insn (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
{
- if (! mips_opts.noreorder)
+ int i, nops, tmp_nops;
+
+ nops = 0;
+ for (i = ignore; i < MAX_DELAY_NOPS; i++)
{
- int nops = nops_for_insn (0, history, NULL);
- if (nops > 0)
- {
- while (nops-- > 0)
- add_fixed_insn (NOP_INSN);
- mips_move_text_labels ();
- }
+ tmp_nops = insns_between (hist + i, insn) - i;
+ if (tmp_nops > nops)
+ nops = tmp_nops;
}
- mips_no_prev_insn ();
-}
-/* Start a (possibly nested) noreorder block. */
+ if (mips_fix_vr4130 && !mips_opts.micromips)
+ {
+ tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
-static void
-start_noreorder (void)
-{
- if (mips_opts.noreorder == 0)
+ if (mips_fix_24k && !mips_opts.micromips)
{
- unsigned int i;
- int nops;
+ tmp_nops = nops_for_24k (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
- /* None of the instructions before the .set noreorder can be moved. */
- for (i = 0; i < ARRAY_SIZE (history); i++)
- history[i].fixed_p = 1;
+ return nops;
+}
- /* Insert any nops that might be needed between the .set noreorder
- block and the previous instructions. We will later remove any
- nops that turn out not to be needed. */
- nops = nops_for_insn (0, history, NULL);
- if (nops > 0)
- {
- if (mips_optimize != 0)
- {
- /* Record the frag which holds the nop instructions, so
- that we can remove them if we don't need them. */
- frag_grow (nops * NOP_INSN_SIZE);
- prev_nop_frag = frag_now;
- prev_nop_frag_holds = nops;
- prev_nop_frag_required = 0;
- prev_nop_frag_since = 0;
- }
+/* The variable arguments provide NUM_INSNS extra instructions that
+ might be added to HIST. Return the largest number of nops that
+ would be needed after the extended sequence, ignoring hazards
+ in the first IGNORE instructions. */
- for (; nops > 0; --nops)
- add_fixed_insn (NOP_INSN);
+static int
+nops_for_sequence (int num_insns, int ignore,
+ const struct mips_cl_insn *hist, ...)
+{
+ va_list args;
+ struct mips_cl_insn buffer[MAX_NOPS];
+ struct mips_cl_insn *cursor;
+ int nops;
- /* 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);
- mips_move_text_labels ();
- }
- mips_mark_labels ();
- mips_clear_insn_labels ();
- }
- mips_opts.noreorder++;
- mips_any_noreorder = 1;
+ va_start (args, hist);
+ cursor = buffer + num_insns;
+ memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
+ while (cursor > buffer)
+ *--cursor = *va_arg (args, const struct mips_cl_insn *);
+
+ nops = nops_for_insn (ignore, buffer, NULL);
+ va_end (args);
+ return nops;
}
-/* End a nested noreorder block. */
+/* Like nops_for_insn, but if INSN is a branch, take into account the
+ worst-case delay for the branch target. */
-static void
-end_noreorder (void)
+static int
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
{
- mips_opts.noreorder--;
- if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+ int nops, tmp_nops;
+
+ nops = nops_for_insn (ignore, hist, insn);
+ if (delayed_branch_p (insn))
{
- /* Commit to inserting prev_nop_frag_required nops and go back to
- handling nop insertion the .set reorder way. */
- prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
- * NOP_INSN_SIZE);
- insert_into_history (prev_nop_frag_since,
- prev_nop_frag_required, NOP_INSN);
- prev_nop_frag = NULL;
+ tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+ hist, insn, get_delay_slot_nop (insn));
+ if (tmp_nops > nops)
+ nops = tmp_nops;
}
-}
-
-/* Set up global variables for the start of a new macro. */
-
-static void
-macro_start (void)
-{
- memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
- memset (&mips_macro_warning.first_insn_sizes, 0,
- sizeof (mips_macro_warning.first_insn_sizes));
- 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))
+ else if (compact_branch_p (insn))
{
- 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;
+ tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
}
- mips_macro_warning.first_frag = NULL;
+ return nops;
}
-/* Given that a macro is longer than one instruction or of the wrong size,
- return the appropriate warning for it. Return null if no warning is
- needed. SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
- RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
- and RELAX_NOMACRO. */
+/* Fix NOP issue: Replace nops by "or at,at,zero". */
-static const char *
-macro_warning (relax_substateT subtype)
+static void
+fix_loongson2f_nop (struct mips_cl_insn * ip)
{
- if (subtype & RELAX_DELAY_SLOT)
- return _("Macro instruction expanded into multiple instructions"
- " in a branch delay slot");
- else if (subtype & RELAX_NOMACRO)
- return _("Macro instruction expanded into multiple instructions");
- else if (subtype & (RELAX_DELAY_SLOT_SIZE_FIRST
- | RELAX_DELAY_SLOT_SIZE_SECOND))
- return ((subtype & RELAX_DELAY_SLOT_16BIT)
- ? _("Macro instruction expanded into a wrong size instruction"
- " in a 16-bit branch delay slot")
- : _("Macro instruction expanded into a wrong size instruction"
- " in a 32-bit branch delay slot"));
- else
- return 0;
+ gas_assert (!HAVE_CODE_COMPRESSION);
+ if (strcmp (ip->insn_mo->name, "nop") == 0)
+ ip->insn_opcode = LOONGSON2F_NOP_INSN;
}
-/* Finish up a macro. Emit warnings as appropriate. */
+/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
+ jr target pc &= 'hffff_ffff_cfff_ffff. */
static void
-macro_end (void)
+fix_loongson2f_jump (struct mips_cl_insn * ip)
{
- /* Relaxation warning flags. */
- relax_substateT subtype = 0;
-
- /* Check delay slot size requirements. */
- if (mips_macro_warning.delay_slot_length == 2)
- subtype |= RELAX_DELAY_SLOT_16BIT;
- if (mips_macro_warning.delay_slot_length != 0)
+ gas_assert (!HAVE_CODE_COMPRESSION);
+ if (strcmp (ip->insn_mo->name, "j") == 0
+ || strcmp (ip->insn_mo->name, "jr") == 0
+ || strcmp (ip->insn_mo->name, "jalr") == 0)
{
- if (mips_macro_warning.delay_slot_length
- != mips_macro_warning.first_insn_sizes[0])
- subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
- if (mips_macro_warning.delay_slot_length
- != mips_macro_warning.first_insn_sizes[1])
- subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
- }
+ int sreg;
+ expressionS ep;
- /* Check instruction count requirements. */
- if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
- {
- if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
- subtype |= RELAX_SECOND_LONGER;
- if (mips_opts.warn_about_macros)
- subtype |= RELAX_NOMACRO;
- if (mips_macro_warning.delay_slot_p)
- subtype |= RELAX_DELAY_SLOT;
- }
+ if (! mips_opts.at)
+ return;
- /* If both alternatives fail to fill a delay slot correctly,
- emit the warning now. */
- if ((subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0
- && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0)
- {
- relax_substateT s;
- const char *msg;
+ sreg = EXTRACT_OPERAND (0, RS, *ip);
+ if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
+ return;
- s = subtype & (RELAX_DELAY_SLOT_16BIT
- | RELAX_DELAY_SLOT_SIZE_FIRST
- | RELAX_DELAY_SLOT_SIZE_SECOND);
- msg = macro_warning (s);
- if (msg != NULL)
- as_warn ("%s", msg);
- subtype &= ~s;
+ ep.X_op = O_constant;
+ ep.X_add_number = 0xcfff0000;
+ macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
+ ep.X_add_number = 0xffff;
+ macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
+ macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
}
+}
- /* If both implementations are longer than 1 instruction, then emit the
- warning now. */
- if (mips_macro_warning.insns[0] > 1 && mips_macro_warning.insns[1] > 1)
- {
- relax_substateT s;
- const char *msg;
-
- s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
- msg = macro_warning (s);
- if (msg != NULL)
- as_warn ("%s", msg);
- subtype &= ~s;
- }
+static void
+fix_loongson2f (struct mips_cl_insn * ip)
+{
+ if (mips_fix_loongson2f_nop)
+ fix_loongson2f_nop (ip);
- /* If any flags still set, then one implementation might need a warning
- and the other either will need one of a different kind or none at all.
- Pass any remaining flags over to relaxation. */
- if (mips_macro_warning.first_frag != NULL)
- mips_macro_warning.first_frag->fr_subtype |= subtype;
+ if (mips_fix_loongson2f_jump)
+ fix_loongson2f_jump (ip);
}
-/* Instruction operand formats used in macros that vary between
- standard MIPS and microMIPS code. */
-
-static const char * const brk_fmt[2][2] = { { "c", "c" }, { "mF", "c" } };
-static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
-static const char * const jalr_fmt[2] = { "d,s", "t,s" };
-static const char * const lui_fmt[2] = { "t,u", "s,u" };
-static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
-static const char * const mfhl_fmt[2][2] = { { "d", "d" }, { "mj", "s" } };
-static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
-static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
-
-#define BRK_FMT (brk_fmt[mips_opts.micromips][mips_opts.insn32])
-#define COP12_FMT (cop12_fmt[mips_opts.micromips])
-#define JALR_FMT (jalr_fmt[mips_opts.micromips])
-#define LUI_FMT (lui_fmt[mips_opts.micromips])
-#define MEM12_FMT (mem12_fmt[mips_opts.micromips])
-#define MFHL_FMT (mfhl_fmt[mips_opts.micromips][mips_opts.insn32])
-#define SHFT_FMT (shft_fmt[mips_opts.micromips])
-#define TRAP_FMT (trap_fmt[mips_opts.micromips])
-
-/* Read a macro's relocation codes from *ARGS and store them in *R.
- The first argument in *ARGS will be either the code for a single
- relocation or -1 followed by the three codes that make up a
- composite relocation. */
-
-static void
-macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
-{
- int i, next;
-
- next = va_arg (*args, int);
- if (next >= 0)
- r[0] = (bfd_reloc_code_real_type) next;
- else
- for (i = 0; i < 3; i++)
- r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
-}
-
-/* Build an instruction created by a macro expansion. This is passed
- a pointer to the count of instructions created so far, an
- expression, the name of the instruction to build, an operand format
- string, and corresponding arguments. */
+/* IP is a branch that has a delay slot, and we need to fill it
+ automatically. Return true if we can do that by swapping IP
+ with the previous instruction.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
-static void
-macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
{
- const struct mips_opcode *mo = NULL;
- bfd_reloc_code_real_type r[3];
- const struct mips_opcode *amo;
- struct hash_control *hash;
- struct mips_cl_insn insn;
- va_list args;
-
- va_start (args, fmt);
+ unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
+ unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
- if (mips_opts.mips16)
- {
- mips16_macro_build (ep, name, fmt, &args);
- va_end (args);
- return;
- }
+ /* -O2 and above is required for this optimization. */
+ if (mips_optimize < 2)
+ return FALSE;
- r[0] = BFD_RELOC_UNUSED;
- r[1] = BFD_RELOC_UNUSED;
- r[2] = BFD_RELOC_UNUSED;
- hash = mips_opts.micromips ? micromips_op_hash : op_hash;
- amo = (struct mips_opcode *) hash_find (hash, name);
- gas_assert (amo);
- gas_assert (strcmp (name, amo->name) == 0);
+ /* If we have seen .set volatile or .set nomove, don't optimize. */
+ if (mips_opts.nomove)
+ return FALSE;
- do
- {
- /* Search until we get a match for NAME. It is assumed here that
- macros will never generate MDMX, MIPS-3D, or MT instructions.
- We try to match an instruction that fulfils the branch delay
- slot instruction length requirement (if any) of the previous
- instruction. While doing this we record the first instruction
- seen that matches all the other conditions and use it anyway
- if the requirement cannot be met; we will issue an appropriate
- warning later on. */
- if (strcmp (fmt, amo->args) == 0
- && amo->pinfo != INSN_MACRO
- && is_opcode_valid (amo)
- && is_size_valid (amo))
- {
- if (is_delay_slot_valid (amo))
- {
- mo = amo;
- break;
- }
- else if (!mo)
- mo = amo;
- }
+ /* We can't swap if the previous instruction's position is fixed. */
+ if (history[0].fixed_p)
+ return FALSE;
- ++amo;
- gas_assert (amo->name);
- }
- while (strcmp (name, amo->name) == 0);
+ /* If the previous previous insn was in a .set noreorder, we can't
+ swap. Actually, the MIPS assembler will swap in this situation.
+ However, gcc configured -with-gnu-as will generate code like
- gas_assert (mo);
- create_insn (&insn, mo);
- for (;;)
- {
- switch (*fmt++)
- {
- case '\0':
- break;
+ .set noreorder
+ lw $4,XXX
+ .set reorder
+ INSN
+ bne $4,$0,foo
- case ',':
- case '(':
- case ')':
- continue;
+ in which we can not swap the bne and INSN. If gcc is not configured
+ -with-gnu-as, it does not output the .set pseudo-ops. */
+ if (history[1].noreorder_p)
+ return FALSE;
- case '+':
- switch (*fmt++)
- {
- case 'A':
- case 'E':
- INSERT_OPERAND (mips_opts.micromips,
- EXTLSB, insn, va_arg (args, int));
- continue;
+ /* If the previous instruction had a fixup in mips16 mode, we can not swap.
+ This means that the previous instruction was a 4-byte one anyhow. */
+ if (mips_opts.mips16 && history[0].fixp[0])
+ return FALSE;
- case 'B':
- case 'F':
- /* Note that in the macro case, these arguments are already
- in MSB form. (When handling the instruction in the
- non-macro case, these arguments are sizes from which
- MSB values must be calculated.) */
- INSERT_OPERAND (mips_opts.micromips,
- INSMSB, insn, va_arg (args, int));
- continue;
+ /* If the branch is itself the target of a branch, we can not swap.
+ We cheat on this; all we check for is whether there is a label on
+ this instruction. If there are any branches to anything other than
+ a label, users must use .set noreorder. */
+ if (seg_info (now_seg)->label_list)
+ return FALSE;
- case 'J':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE10, insn, va_arg (args, int));
- continue;
+ /* If the previous instruction is in a variant frag other than this
+ branch's one, we cannot do the swap. This does not apply to
+ MIPS16 code, which uses variant frags for different purposes. */
+ if (!mips_opts.mips16
+ && history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent)
+ return FALSE;
- case 'C':
- case 'G':
- case 'H':
- /* Note that in the macro case, these arguments are already
- in MSBD form. (When handling the instruction in the
- non-macro case, these arguments are sizes from which
- MSBD values must be calculated.) */
- INSERT_OPERAND (mips_opts.micromips,
- EXTMSBD, insn, va_arg (args, int));
- continue;
+ /* We do not swap with instructions that cannot architecturally
+ be placed in a branch delay slot, such as SYNC or ERET. We
+ also refrain from swapping with a trap instruction, since it
+ complicates trap handlers to have the trap instruction be in
+ a delay slot. */
+ prev_pinfo = history[0].insn_mo->pinfo;
+ if (prev_pinfo & INSN_NO_DELAY_SLOT)
+ return FALSE;
- case 'Q':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, SEQI, insn, va_arg (args, int));
- continue;
+ /* Check for conflicts between the branch and the instructions
+ before the candidate delay slot. */
+ if (nops_for_insn (0, history + 1, ip) > 0)
+ return FALSE;
- case 'j':
- INSERT_OPERAND (mips_opts.micromips, EVAOFFSET, insn, va_arg (args, int));
- continue;
+ /* Check for conflicts between the swapped sequence and the
+ target of the branch. */
+ if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
+ return FALSE;
- default:
- abort ();
- }
- continue;
+ /* If the branch reads a register that the previous
+ instruction sets, we can not swap. */
+ gpr_read = gpr_read_mask (ip);
+ prev_gpr_write = gpr_write_mask (&history[0]);
+ if (gpr_read & prev_gpr_write)
+ return FALSE;
- case '2':
- INSERT_OPERAND (mips_opts.micromips, BP, insn, va_arg (args, int));
- continue;
+ /* If the branch writes a register that the previous
+ instruction sets, we can not swap. */
+ gpr_write = gpr_write_mask (ip);
+ if (gpr_write & prev_gpr_write)
+ return FALSE;
- case 'n':
- gas_assert (mips_opts.micromips);
- case 't':
- case 'w':
- case 'E':
- INSERT_OPERAND (mips_opts.micromips, RT, insn, va_arg (args, int));
- continue;
+ /* If the branch writes a register that the previous
+ instruction reads, we can not swap. */
+ prev_gpr_read = gpr_read_mask (&history[0]);
+ if (gpr_write & prev_gpr_read)
+ return FALSE;
- case 'c':
- INSERT_OPERAND (mips_opts.micromips, CODE, insn, va_arg (args, int));
- continue;
+ /* If one instruction sets a condition code and the
+ other one uses a condition code, we can not swap. */
+ pinfo = ip->insn_mo->pinfo;
+ if ((pinfo & INSN_READ_COND_CODE)
+ && (prev_pinfo & INSN_WRITE_COND_CODE))
+ return FALSE;
+ if ((pinfo & INSN_WRITE_COND_CODE)
+ && (prev_pinfo & INSN_READ_COND_CODE))
+ return FALSE;
- case 'W':
- gas_assert (!mips_opts.micromips);
- case 'T':
- INSERT_OPERAND (mips_opts.micromips, FT, insn, va_arg (args, int));
- continue;
+ /* If the previous instruction uses the PC, we can not swap. */
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
+ if (prev_pinfo2 & INSN2_READ_PC)
+ return FALSE;
- case 'G':
- if (mips_opts.micromips)
- INSERT_OPERAND (1, RS, insn, va_arg (args, int));
- else
- INSERT_OPERAND (0, RD, insn, va_arg (args, int));
- continue;
+ /* If the previous instruction has an incorrect size for a fixed
+ branch delay slot in microMIPS mode, we cannot swap. */
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (mips_opts.micromips
+ && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
+ && insn_length (history) != 2)
+ return FALSE;
+ if (mips_opts.micromips
+ && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
+ && insn_length (history) != 4)
+ return FALSE;
- case 'K':
- gas_assert (!mips_opts.micromips);
- case 'd':
- INSERT_OPERAND (mips_opts.micromips, RD, insn, va_arg (args, int));
- continue;
+ /* On R5900 short loops need to be fixed by inserting a nop in
+ the branch delay slots.
+ A short loop can be terminated too early. */
+ if (mips_opts.arch == CPU_R5900
+ /* Check if instruction has a parameter, ignore "j $31". */
+ && (address_expr != NULL)
+ /* Parameter must be 16 bit. */
+ && (*reloc_type == BFD_RELOC_16_PCREL_S2)
+ /* Branch to same segment. */
+ && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg)
+ /* Branch to same code fragment. */
+ && (symbol_get_frag(address_expr->X_add_symbol) == frag_now)
+ /* Can only calculate branch offset if value is known. */
+ && symbol_constant_p(address_expr->X_add_symbol)
+ /* Check if branch is really conditional. */
+ && !((ip->insn_opcode & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ int distance;
+ /* Check if loop is shorter than 6 instructions including
+ branch and delay slot. */
+ distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
+ if (distance <= 20)
+ {
+ int i;
+ int rv;
- case 'U':
- gas_assert (!mips_opts.micromips);
- {
- int tmp = va_arg (args, int);
-
- INSERT_OPERAND (0, RT, insn, tmp);
- INSERT_OPERAND (0, RD, insn, tmp);
- }
- continue;
-
- case 'V':
- case 'S':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, FS, insn, va_arg (args, int));
- continue;
-
- case 'z':
- continue;
-
- case '<':
- INSERT_OPERAND (mips_opts.micromips,
- SHAMT, insn, va_arg (args, int));
- continue;
-
- case 'D':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, FD, insn, va_arg (args, int));
- continue;
-
- case 'B':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE20, insn, va_arg (args, int));
- continue;
-
- case 'J':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE19, insn, va_arg (args, int));
- continue;
-
- case 'q':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, CODE2, insn, va_arg (args, int));
- continue;
-
- case 'b':
- case 's':
- case 'r':
- case 'v':
- INSERT_OPERAND (mips_opts.micromips, RS, insn, va_arg (args, int));
- continue;
-
- case 'i':
- case 'j':
- macro_read_relocs (&args, r);
- gas_assert (*r == BFD_RELOC_GPREL16
- || *r == BFD_RELOC_MIPS_HIGHER
- || *r == BFD_RELOC_HI16_S
- || *r == BFD_RELOC_LO16
- || *r == BFD_RELOC_MIPS_GOT_OFST);
- continue;
-
- case 'o':
- macro_read_relocs (&args, r);
- continue;
-
- case 'u':
- macro_read_relocs (&args, r);
- gas_assert (ep != NULL
- && (ep->X_op == O_constant
- || (ep->X_op == O_symbol
- && (*r == BFD_RELOC_MIPS_HIGHEST
- || *r == BFD_RELOC_HI16_S
- || *r == BFD_RELOC_HI16
- || *r == BFD_RELOC_GPREL16
- || *r == BFD_RELOC_MIPS_GOT_HI16
- || *r == BFD_RELOC_MIPS_CALL_HI16))));
- continue;
+ rv = FALSE;
+ /* When the loop includes branches or jumps,
+ it is not a short loop. */
+ for (i = 0; i < (distance / 4); i++)
+ {
+ if ((history[i].cleared_p)
+ || delayed_branch_p(&history[i]))
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ if (rv == FALSE)
+ {
+ /* Insert nop after branch to fix short loop. */
+ return FALSE;
+ }
+ }
+ }
- case 'p':
- gas_assert (ep != NULL);
+ return TRUE;
+}
- /*
- * This allows macro() to pass an immediate expression for
- * creating short branches without creating a symbol.
- *
- * We don't allow branch relaxation for these branches, as
- * they should only appear in ".set nomacro" anyway.
- */
- if (ep->X_op == O_constant)
- {
- /* For microMIPS we always use relocations for branches.
- So we should not resolve immediate values. */
- gas_assert (!mips_opts.micromips);
+/* Decide how we should add IP to the instruction stream.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
- if ((ep->X_add_number & 3) != 0)
- as_bad (_("branch to misaligned address (0x%lx)"),
- (unsigned long) ep->X_add_number);
- if ((ep->X_add_number + 0x20000) & ~0x3ffff)
- as_bad (_("branch address range overflow (0x%lx)"),
- (unsigned long) ep->X_add_number);
- insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
- ep = NULL;
- }
- else
- *r = BFD_RELOC_16_PCREL_S2;
- continue;
+static enum append_method
+get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
+{
+ unsigned long pinfo, pinfo2;
- case 'a':
- gas_assert (ep != NULL);
- *r = BFD_RELOC_MIPS_JMP;
- continue;
+ /* The relaxed version of a macro sequence must be inherently
+ hazard-free. */
+ if (mips_relax.sequence == 2)
+ return APPEND_ADD;
- case 'C':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, COPZ, insn, va_arg (args, unsigned long));
- continue;
+ /* We must not dabble with instructions in a ".set norerorder" block. */
+ if (mips_opts.noreorder)
+ return APPEND_ADD;
- case 'k':
- INSERT_OPERAND (mips_opts.micromips,
- CACHE, insn, va_arg (args, unsigned long));
- continue;
+ /* Otherwise, it's our responsibility to fill branch delay slots. */
+ if (delayed_branch_p (ip))
+ {
+ if (!branch_likely_p (ip)
+ && can_swap_branch_p (ip, address_expr, reloc_type))
+ return APPEND_SWAP;
- case '|':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, TRAP, insn, va_arg (args, int));
- continue;
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (mips_opts.mips16
+ && ISA_SUPPORTS_MIPS16E
+ && ((pinfo & MIPS16_INSN_READ_X) != 0
+ || (pinfo2 & INSN2_READ_GPR_31) != 0))
+ return APPEND_ADD_COMPACT;
- case '.':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, OFFSET10, insn, va_arg (args, int));
- continue;
+ return APPEND_ADD_WITH_NOP;
+ }
- case '\\':
- INSERT_OPERAND (mips_opts.micromips,
- 3BITPOS, insn, va_arg (args, unsigned int));
- continue;
+ return APPEND_ADD;
+}
- case '~':
- INSERT_OPERAND (mips_opts.micromips,
- OFFSET12, insn, va_arg (args, unsigned long));
- continue;
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+ Point IP->insn_mo to the new opcode's definition. */
- case 'N':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, BCC, insn, va_arg (args, int));
- continue;
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+ const struct mips_opcode *mo, *end;
- case 'm': /* Opcode extension character. */
- gas_assert (mips_opts.micromips);
- switch (*fmt++)
- {
- case 'j':
- INSERT_OPERAND (1, MJ, insn, va_arg (args, int));
- break;
+ end = &mips16_opcodes[bfd_mips16_num_opcodes];
+ for (mo = ip->insn_mo; mo < end; mo++)
+ if ((ip->insn_opcode & mo->mask) == mo->match)
+ {
+ ip->insn_mo = mo;
+ return;
+ }
+ abort ();
+}
- case 'p':
- INSERT_OPERAND (1, MP, insn, va_arg (args, int));
- break;
+/* For microMIPS macros, we need to generate a local number label
+ as the target of branches. */
+#define MICROMIPS_LABEL_CHAR '\037'
+static unsigned long micromips_target_label;
+static char micromips_target_name[32];
- case 'F':
- INSERT_OPERAND (1, IMMF, insn, va_arg (args, int));
- break;
+static char *
+micromips_label_name (void)
+{
+ char *p = micromips_target_name;
+ char symbol_name_temporary[24];
+ unsigned long l;
+ int i;
- default:
- abort ();
- }
- continue;
+ if (*p)
+ return p;
- default:
- abort ();
- }
- break;
+ i = 0;
+ l = micromips_target_label;
+#ifdef LOCAL_LABEL_PREFIX
+ *p++ = LOCAL_LABEL_PREFIX;
+#endif
+ *p++ = 'L';
+ *p++ = MICROMIPS_LABEL_CHAR;
+ do
+ {
+ symbol_name_temporary[i++] = l % 10 + '0';
+ l /= 10;
}
- va_end (args);
- gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+ while (l != 0);
+ while (i > 0)
+ *p++ = symbol_name_temporary[--i];
+ *p = '\0';
- append_insn (&insn, ep, r, TRUE);
+ return micromips_target_name;
}
static void
-mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
- va_list *args)
+micromips_label_expr (expressionS *label_expr)
{
- struct mips_opcode *mo;
- struct mips_cl_insn insn;
- bfd_reloc_code_real_type r[3]
- = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
-
- mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
- gas_assert (mo);
- gas_assert (strcmp (name, mo->name) == 0);
-
- while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
- {
- ++mo;
- gas_assert (mo->name);
- gas_assert (strcmp (name, mo->name) == 0);
- }
-
- create_insn (&insn, mo);
- for (;;)
- {
- int c;
+ label_expr->X_op = O_symbol;
+ label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
+ label_expr->X_add_number = 0;
+}
- c = *fmt++;
- switch (c)
- {
- case '\0':
- break;
+static void
+micromips_label_inc (void)
+{
+ micromips_target_label++;
+ *micromips_target_name = '\0';
+}
- case ',':
- case '(':
- case ')':
- continue;
+static void
+micromips_add_label (void)
+{
+ symbolS *s;
- case 'y':
- case 'w':
- MIPS16_INSERT_OPERAND (RY, insn, va_arg (*args, int));
- continue;
+ s = colon (micromips_label_name ());
+ micromips_label_inc ();
+ S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+}
- case 'x':
- case 'v':
- MIPS16_INSERT_OPERAND (RX, insn, va_arg (*args, int));
- continue;
+/* If assembling microMIPS code, then return the microMIPS reloc
+ corresponding to the requested one if any. Otherwise return
+ the reloc unchanged. */
- case 'z':
- MIPS16_INSERT_OPERAND (RZ, insn, va_arg (*args, int));
- continue;
-
- case 'Z':
- MIPS16_INSERT_OPERAND (MOVE32Z, insn, va_arg (*args, int));
- continue;
+static bfd_reloc_code_real_type
+micromips_map_reloc (bfd_reloc_code_real_type reloc)
+{
+ static const bfd_reloc_code_real_type relocs[][2] =
+ {
+ /* Keep sorted incrementally by the left-hand key. */
+ { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
+ { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
+ { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
+ { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
+ { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
+ { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
+ { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
+ { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
+ { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
+ { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
+ { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
+ { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
+ { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
+ { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
+ { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
+ { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
+ { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
+ { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
+ { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
+ { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
+ { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
+ };
+ bfd_reloc_code_real_type r;
+ size_t i;
- case '0':
- case 'S':
- case 'P':
- case 'R':
- continue;
+ if (!mips_opts.micromips)
+ return reloc;
+ for (i = 0; i < ARRAY_SIZE (relocs); i++)
+ {
+ r = relocs[i][0];
+ if (r > reloc)
+ return reloc;
+ if (r == reloc)
+ return relocs[i][1];
+ }
+ return reloc;
+}
- case 'X':
- MIPS16_INSERT_OPERAND (REGR32, insn, va_arg (*args, int));
- continue;
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+ Return true on success, storing the resolved value in RESULT. */
- case 'Y':
- {
- int regno;
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+ offsetT *result)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_MIPS_HIGHEST:
+ case BFD_RELOC_MICROMIPS_HIGHEST:
+ *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+ return TRUE;
- regno = va_arg (*args, int);
- regno = ((regno & 7) << 2) | ((regno & 0x18) >> 3);
- MIPS16_INSERT_OPERAND (REG32R, insn, regno);
- }
- continue;
+ case BFD_RELOC_MIPS_HIGHER:
+ case BFD_RELOC_MICROMIPS_HIGHER:
+ *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+ return TRUE;
- case '<':
- case '>':
- case '4':
- case '5':
- case 'H':
- case 'W':
- case 'D':
- case 'j':
- case '8':
- case 'V':
- case 'C':
- case 'U':
- case 'k':
- case 'K':
- case 'p':
- case 'q':
- {
- offsetT value;
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MIPS16_HI16_S:
+ *result = ((operand + 0x8000) >> 16) & 0xffff;
+ return TRUE;
- gas_assert (ep != NULL);
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_MICROMIPS_HI16:
+ case BFD_RELOC_MIPS16_HI16:
+ *result = (operand >> 16) & 0xffff;
+ return TRUE;
- if (ep->X_op != O_constant)
- *r = (int) BFD_RELOC_UNUSED + c;
- else if (calculate_reloc (*r, ep->X_add_number, &value))
- {
- mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode);
- ep = NULL;
- *r = BFD_RELOC_UNUSED;
- }
- }
- continue;
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_MICROMIPS_LO16:
+ case BFD_RELOC_MIPS16_LO16:
+ *result = operand & 0xffff;
+ return TRUE;
- case '6':
- MIPS16_INSERT_OPERAND (IMM6, insn, va_arg (*args, int));
- continue;
- }
+ case BFD_RELOC_UNUSED:
+ *result = operand;
+ return TRUE;
- break;
+ default:
+ return FALSE;
}
-
- gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-
- append_insn (&insn, ep, r, TRUE);
}
-/*
- * Sign-extend 32-bit mode constants that have bit 31 set and all
- * higher bits unset.
- */
-static void
-normalize_constant_expr (expressionS *ex)
-{
- if (ex->X_op == O_constant
- && IS_ZEXT_32BIT_NUM (ex->X_add_number))
- ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
- - 0x80000000);
-}
+/* Output an instruction. IP is the instruction information.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. EXPANSIONP is true if the instruction is part of
+ a macro expansion. */
-/*
- * Sign-extend 32-bit mode address offsets that have bit 31 set and
- * all higher bits unset.
- */
static void
-normalize_address_expr (expressionS *ex)
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
{
- if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
- || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
- && IS_ZEXT_32BIT_NUM (ex->X_add_number))
- ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
- - 0x80000000);
-}
+ unsigned long prev_pinfo2, pinfo;
+ bfd_boolean relaxed_branch = FALSE;
+ enum append_method method;
+ bfd_boolean relax32;
+ int branch_disp;
-/*
- * Generate a "jalr" instruction with a relocation hint to the called
- * function. This occurs in NewABI PIC code.
- */
-static void
-macro_build_jalr (expressionS *ep, int cprestore)
-{
- static const bfd_reloc_code_real_type jalr_relocs[2]
- = { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
- bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
- const char *jalr;
- char *f = NULL;
+ if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
+ fix_loongson2f (ip);
- if (MIPS_JALR_HINT_P (ep))
- {
- frag_grow (8);
- f = frag_more (0);
- }
- if (mips_opts.micromips)
- {
- jalr = ((mips_opts.noreorder && !cprestore) || mips_opts.insn32
- ? "jalr" : "jalrs");
- if (MIPS_JALR_HINT_P (ep)
- || mips_opts.insn32
- || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
- macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
- else
- macro_build (NULL, jalr, "mj", PIC_CALL_REG);
- }
- else
- macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
- if (MIPS_JALR_HINT_P (ep))
- fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
-}
+ file_ase_mips16 |= mips_opts.mips16;
+ file_ase_micromips |= mips_opts.micromips;
-/*
- * Generate a "lui" instruction.
- */
-static void
-macro_build_lui (expressionS *ep, int regnum)
-{
- gas_assert (! mips_opts.mips16);
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
+ pinfo = ip->insn_mo->pinfo;
- if (ep->X_op != O_constant)
+ if (mips_opts.micromips
+ && !expansionp
+ && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
+ && micromips_insn_length (ip->insn_mo) != 2)
+ || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
+ && micromips_insn_length (ip->insn_mo) != 4)))
+ as_warn (_("Wrong size instruction in a %u-bit branch delay slot"),
+ (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
+
+ if (address_expr == NULL)
+ ip->complete_p = 1;
+ else if (reloc_type[0] <= BFD_RELOC_UNUSED
+ && reloc_type[1] == BFD_RELOC_UNUSED
+ && reloc_type[2] == BFD_RELOC_UNUSED
+ && address_expr->X_op == O_constant)
{
- gas_assert (ep->X_op == O_symbol);
- /* _gp_disp is a special case, used from s_cpload.
- __gnu_local_gp is used if mips_no_shared. */
- gas_assert (mips_pic == NO_PIC
- || (! HAVE_NEWABI
- && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
- || (! mips_in_shared
- && strcmp (S_GET_NAME (ep->X_add_symbol),
- "__gnu_local_gp") == 0));
- }
+ switch (*reloc_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ {
+ int shift;
- macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
-}
+ shift = mips_opts.micromips ? 1 : 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x3ffffff);
+ ip->complete_p = 1;
+ }
+ break;
-/* Generate a sequence of instructions to do a load or store from a constant
- offset off of a base register (breg) into/from a target register (treg),
- using AT if necessary. */
-static void
-macro_build_ldst_constoffset (expressionS *ep, const char *op,
- int treg, int breg, int dbl)
-{
- gas_assert (ep->X_op == O_constant);
+ 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)
+ | ((address_expr->X_add_number & 0x3fffc) >> 2));
+ ip->complete_p = 1;
+ break;
- /* Sign-extending 32-bit constants makes their handling easier. */
- if (!dbl)
- normalize_constant_expr (ep);
+ case BFD_RELOC_16_PCREL_S2:
+ {
+ int shift;
- /* Right now, this routine can only handle signed 32-bit constants. */
- if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
- as_warn (_("operand overflow"));
+ shift = mips_opts.micromips ? 1 : 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if (!mips_relax_branch)
+ {
+ if ((address_expr->X_add_number + (1 << (shift + 15)))
+ & ~((1 << (shift + 16)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0xffff);
+ }
+ }
+ break;
- if (IS_SEXT_16BIT_NUM(ep->X_add_number))
- {
- /* Signed 16-bit offset will fit in the op. Easy! */
- macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
- }
- else
- {
- /* 32-bit offset, need multiple instructions and AT, like:
- lui $tempreg,const_hi (BFD_RELOC_HI16_S)
- addu $tempreg,$tempreg,$breg
- <op> $treg,const_lo($tempreg) (BFD_RELOC_LO16)
- to handle the complete offset. */
- macro_build_lui (ep, AT);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
- macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ default:
+ {
+ offsetT value;
- if (!mips_opts.at)
- as_bad (_("Macro used $at after \".set noat\""));
+ if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+ &value))
+ {
+ ip->insn_opcode |= value & 0xffff;
+ ip->complete_p = 1;
+ }
+ }
+ break;
+ }
}
-}
-/* set_at()
- * Generates code to set the $at register to true (one)
- * if reg is less than the immediate expression.
- */
-static void
-set_at (int reg, int unsignedp)
-{
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
- macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
- AT, reg, BFD_RELOC_LO16);
- else
+ if (mips_relax.sequence != 2 && !mips_opts.noreorder)
{
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
- }
-}
-
-/* Warn if an expression is not a constant. */
+ /* There are a lot of optimizations we could do that we don't.
+ In particular, we do not, in general, reorder instructions.
+ If you use gcc with optimization, it will reorder
+ instructions and generally do much more optimization then we
+ do here; repeating all that work in the assembler would only
+ benefit hand written assembly code, and does not seem worth
+ it. */
+ int nops = (mips_optimize == 0
+ ? nops_for_insn (0, history, NULL)
+ : nops_for_insn_or_target (0, history, ip));
+ if (nops > 0)
+ {
+ fragS *old_frag;
+ unsigned long old_frag_offset;
+ int i;
-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);
+ old_frag = frag_now;
+ old_frag_offset = frag_now_fix ();
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (ex);
-}
+ for (i = 0; i < nops; i++)
+ add_fixed_insn (NOP_INSN);
+ insert_into_history (0, nops, NOP_INSN);
-/* 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:
- for (lcnt = 0; (lcnt < 32); lcnt++)
- if ((v) & (1 << (31 - lcnt)))
- break;
- However it is not code size friendly, and the gain will drop a bit
- on certain cached systems.
-*/
-#define COUNT_TOP_ZEROES(v) \
- (((v) & ~0xffff) == 0 \
- ? ((v) & ~0xff) == 0 \
- ? ((v) & ~0xf) == 0 \
- ? ((v) & ~0x3) == 0 \
- ? ((v) & ~0x1) == 0 \
- ? !(v) \
- ? 32 \
- : 31 \
- : 30 \
- : ((v) & ~0x7) == 0 \
- ? 29 \
- : 28 \
- : ((v) & ~0x3f) == 0 \
- ? ((v) & ~0x1f) == 0 \
- ? 27 \
- : 26 \
- : ((v) & ~0x7f) == 0 \
- ? 25 \
- : 24 \
- : ((v) & ~0xfff) == 0 \
- ? ((v) & ~0x3ff) == 0 \
- ? ((v) & ~0x1ff) == 0 \
- ? 23 \
- : 22 \
- : ((v) & ~0x7ff) == 0 \
- ? 21 \
- : 20 \
- : ((v) & ~0x3fff) == 0 \
- ? ((v) & ~0x1fff) == 0 \
- ? 19 \
- : 18 \
- : ((v) & ~0x7fff) == 0 \
- ? 17 \
- : 16 \
- : ((v) & ~0xffffff) == 0 \
- ? ((v) & ~0xfffff) == 0 \
- ? ((v) & ~0x3ffff) == 0 \
- ? ((v) & ~0x1ffff) == 0 \
- ? 15 \
- : 14 \
- : ((v) & ~0x7ffff) == 0 \
- ? 13 \
- : 12 \
- : ((v) & ~0x3fffff) == 0 \
- ? ((v) & ~0x1fffff) == 0 \
- ? 11 \
- : 10 \
- : ((v) & ~0x7fffff) == 0 \
- ? 9 \
- : 8 \
- : ((v) & ~0xfffffff) == 0 \
- ? ((v) & ~0x3ffffff) == 0 \
- ? ((v) & ~0x1ffffff) == 0 \
- ? 7 \
- : 6 \
- : ((v) & ~0x7ffffff) == 0 \
- ? 5 \
- : 4 \
- : ((v) & ~0x3fffffff) == 0 \
- ? ((v) & ~0x1fffffff) == 0 \
- ? 3 \
- : 2 \
- : ((v) & ~0x7fffffff) == 0 \
- ? 1 \
- : 0)
+ if (listing)
+ {
+ listing_prev_line ();
+ /* We may be at the start of a variant frag. In case we
+ are, make sure there is enough space for the frag
+ after the frags created by listing_prev_line. The
+ argument to frag_grow here must be at least as large
+ as the argument to all other calls to frag_grow in
+ this file. We don't have to worry about being in the
+ middle of a variant frag, because the variants insert
+ all needed nop instructions themselves. */
+ frag_grow (40);
+ }
-/* load_register()
- * This routine generates the least number of instructions necessary to load
- * an absolute expression value into a register.
- */
-static void
-load_register (int reg, expressionS *ep, int dbl)
-{
- int freg;
- expressionS hi32, lo32;
+ mips_move_text_labels ();
- if (ep->X_op != O_big)
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING)
+ ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
+ }
+ }
+ else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
{
- gas_assert (ep->X_op == O_constant);
+ int nops;
- /* Sign-extending 32-bit constants makes their handling easier. */
- if (!dbl)
- normalize_constant_expr (ep);
+ /* Work out how many nops in prev_nop_frag are needed by IP,
+ ignoring hazards generated by the first prev_nop_frag_since
+ instructions. */
+ nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
+ gas_assert (nops <= prev_nop_frag_holds);
- if (IS_SEXT_16BIT_NUM (ep->X_add_number))
+ /* Enforce NOPS as a minimum. */
+ if (nops > prev_nop_frag_required)
+ prev_nop_frag_required = nops;
+
+ if (prev_nop_frag_holds == prev_nop_frag_required)
{
- /* We can handle 16 bit signed values with an addiu to
- $zero. No need to ever use daddiu here, since $zero and
- the result are always correct in 32 bit mode. */
- macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
- return;
+ /* Settle for the current number of nops. Update the history
+ accordingly (for the benefit of any future .set reorder code). */
+ prev_nop_frag = NULL;
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_holds, NOP_INSN);
}
- else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+ else
{
- /* We can handle 16 bit unsigned values with an ori to
- $zero. */
- macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
- return;
- }
- else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
- {
- /* 32 bit values require an lui. */
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
- if ((ep->X_add_number & 0xffff) != 0)
- macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
- return;
+ /* Allow this instruction to replace one of the nops that was
+ tentatively added to prev_nop_frag. */
+ prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
+ prev_nop_frag_holds--;
+ prev_nop_frag_since++;
}
}
- /* The value is larger than 32 bits. */
+ method = get_append_method (ip, address_expr, reloc_type);
+ branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
- if (!dbl || HAVE_32BIT_GPRS)
- {
- char value[32];
+ dwarf2_emit_insn (0);
+ /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
+ so "move" the instruction address accordingly.
- sprintf_vma (value, ep->X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
- macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
- return;
+ Also, it doesn't seem appropriate for the assembler to reorder .loc
+ entries. If this instruction is a branch that we are going to swap
+ with the previous instruction, the two instructions should be
+ treated as a unit, and the debug information for both instructions
+ should refer to the start of the branch sequence. Using the
+ current position is certainly wrong when swapping a 32-bit branch
+ and a 16-bit delay slot, since the current position would then be
+ in the middle of a branch. */
+ dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
+
+ relax32 = (mips_relax_branch
+ /* Don't try branch relaxation within .set nomacro, or within
+ .set noat if we use $at for PIC computations. If it turns
+ out that the branch was out-of-range, we'll get an error. */
+ && !mips_opts.warn_about_macros
+ && (mips_opts.at || mips_pic == NO_PIC)
+ /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
+ as they have no complementing branches. */
+ && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
+
+ if (!HAVE_CODE_COMPRESSION
+ && address_expr
+ && relax32
+ && *reloc_type == BFD_RELOC_16_PCREL_S2
+ && delayed_branch_p (ip))
+ {
+ relaxed_branch = TRUE;
+ add_relaxed_insn (ip, (relaxed_branch_length
+ (NULL, NULL,
+ uncond_branch_p (ip) ? -1
+ : branch_likely_p (ip) ? 1
+ : 0)), 4,
+ RELAX_BRANCH_ENCODE
+ (AT,
+ uncond_branch_p (ip),
+ branch_likely_p (ip),
+ pinfo & INSN_WRITE_GPR_31,
+ 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
}
+ else if (mips_opts.micromips
+ && address_expr
+ && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
+ || *reloc_type > BFD_RELOC_UNUSED)
+ && (delayed_branch_p (ip) || compact_branch_p (ip))
+ /* Don't try branch relaxation when users specify
+ 16-bit/32-bit instructions. */
+ && !forced_insn_length)
+ {
+ bfd_boolean relax16 = *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 al = pinfo & INSN_WRITE_GPR_31;
+ int length32;
- if (ep->X_op != O_big)
+ 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,
+ RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
+ relax32, 0, 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
+ }
+ else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
{
- hi32 = *ep;
- hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
- hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
- hi32.X_add_number &= 0xffffffff;
- lo32 = *ep;
- lo32.X_add_number &= 0xffffffff;
+ /* We need to set up a variant frag. */
+ gas_assert (address_expr != NULL);
+ add_relaxed_insn (ip, 4, 0,
+ RELAX_MIPS16_ENCODE
+ (*reloc_type - BFD_RELOC_UNUSED,
+ forced_insn_length == 2, forced_insn_length == 4,
+ delayed_branch_p (&history[0]),
+ history[0].mips16_absolute_jump_p),
+ make_expr_symbol (address_expr), 0);
}
- else
+ else if (mips_opts.mips16 && insn_length (ip) == 2)
{
- gas_assert (ep->X_add_number > 2);
- if (ep->X_add_number == 3)
- generic_bignum[3] = 0;
- else if (ep->X_add_number > 4)
- as_bad (_("Number larger than 64 bits"));
- lo32.X_op = O_constant;
- lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
- hi32.X_op = O_constant;
- hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+ if (!delayed_branch_p (ip))
+ /* Make sure there is enough room to swap this instruction with
+ a following jump instruction. */
+ frag_grow (6);
+ add_fixed_insn (ip);
}
-
- if (hi32.X_add_number == 0)
- freg = 0;
else
{
- int shift, bit;
- unsigned long hi, lo;
-
- if (hi32.X_add_number == (offsetT) 0xffffffff)
- {
- if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
- {
- macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
- return;
- }
- if (lo32.X_add_number & 0x80000000)
- {
- macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
- if (lo32.X_add_number & 0xffff)
- macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
- return;
- }
- }
+ if (mips_opts.mips16
+ && mips_opts.noreorder
+ && delayed_branch_p (&history[0]))
+ as_warn (_("extended instruction in delay slot"));
- /* Check for 16bit shifted constant. We know that hi32 is
- non-zero, so start the mask on the first bit of the hi32
- value. */
- shift = 17;
- do
+ if (mips_relax.sequence)
{
- unsigned long himask, lomask;
-
- if (shift < 32)
- {
- himask = 0xffff >> (32 - shift);
- lomask = (0xffff << shift) & 0xffffffff;
- }
- else
- {
- himask = 0xffff << (shift - 32);
- lomask = 0;
- }
- if ((hi32.X_add_number & ~(offsetT) himask) == 0
- && (lo32.X_add_number & ~(offsetT) lomask) == 0)
- {
- expressionS tmp;
-
- tmp.X_op = O_constant;
- if (shift < 32)
- tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
- | (lo32.X_add_number >> shift));
- else
- tmp.X_add_number = hi32.X_add_number >> (shift - 32);
- macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
- macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", SHFT_FMT,
- reg, reg, (shift >= 32) ? shift - 32 : shift);
- return;
- }
- ++shift;
+ /* If we've reached the end of this frag, turn it into a variant
+ frag and record the information for the instructions we've
+ written so far. */
+ if (frag_room () < 4)
+ relax_close_frag ();
+ mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
}
- while (shift <= (64 - 16));
- /* Find the bit number of the lowest one bit, and store the
- shifted value in hi/lo. */
- hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
- lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
- if (lo != 0)
+ if (mips_relax.sequence != 2)
{
- bit = 0;
- while ((lo & 1) == 0)
- {
- lo >>= 1;
- ++bit;
- }
- lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
- hi >>= bit;
+ if (mips_macro_warning.first_insn_sizes[0] == 0)
+ mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
+ mips_macro_warning.sizes[0] += insn_length (ip);
+ mips_macro_warning.insns[0]++;
}
- else
+ if (mips_relax.sequence != 1)
{
- bit = 32;
- while ((hi & 1) == 0)
- {
- hi >>= 1;
- ++bit;
- }
- lo = hi;
- hi = 0;
+ if (mips_macro_warning.first_insn_sizes[1] == 0)
+ mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
+ mips_macro_warning.sizes[1] += insn_length (ip);
+ mips_macro_warning.insns[1]++;
}
- /* Optimize if the shifted value is a (power of 2) - 1. */
- if ((hi == 0 && ((lo + 1) & lo) == 0)
- || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
+ if (mips_opts.mips16)
{
- shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
- if (shift != 0)
- {
- expressionS tmp;
-
- /* This instruction will set the register to be all
- ones. */
- tmp.X_op = O_constant;
- tmp.X_add_number = (offsetT) -1;
- macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
- if (bit != 0)
- {
- bit += shift;
- macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", SHFT_FMT,
- reg, reg, (bit >= 32) ? bit - 32 : bit);
- }
- macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", SHFT_FMT,
- reg, reg, (shift >= 32) ? shift - 32 : shift);
- return;
- }
+ ip->fixed_p = 1;
+ ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
}
-
- /* Sign extend hi32 before calling load_register, because we can
- generally get better code when we load a sign extended value. */
- if ((hi32.X_add_number & 0x80000000) != 0)
- hi32.X_add_number |= ~(offsetT) 0xffffffff;
- load_register (reg, &hi32, 0);
- freg = reg;
+ add_fixed_insn (ip);
}
- if ((lo32.X_add_number & 0xffff0000) == 0)
- {
- if (freg != 0)
- {
- macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
- freg = reg;
- }
- }
- else
+
+ if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
{
- expressionS mid16;
+ bfd_reloc_code_real_type final_type[3];
+ reloc_howto_type *howto0;
+ reloc_howto_type *howto;
+ int i;
- if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
+ /* Perform any necessary conversion to microMIPS relocations
+ and find out how many relocations there actually are. */
+ for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
+ final_type[i] = micromips_map_reloc (reloc_type[i]);
+
+ /* In a compound relocation, it is the final (outermost)
+ operator that determines the relocated field. */
+ howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
+ if (!howto)
+ abort ();
+
+ if (i > 1)
+ howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
+ ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ address_expr,
+ howto0 && howto0->pc_relative,
+ final_type[0]);
+
+ /* Tag symbols that have a R_MIPS16_26 relocation against them. */
+ if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
+ *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
+
+ /* These relocations can have an addend that won't fit in
+ 4 octets for 64bit assembly. */
+ if (HAVE_64BIT_GPRS
+ && ! howto->partial_inplace
+ && (reloc_type[0] == BFD_RELOC_16
+ || reloc_type[0] == BFD_RELOC_32
+ || reloc_type[0] == BFD_RELOC_MIPS_JMP
+ || reloc_type[0] == BFD_RELOC_GPREL16
+ || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+ || reloc_type[0] == BFD_RELOC_GPREL32
+ || reloc_type[0] == BFD_RELOC_64
+ || reloc_type[0] == BFD_RELOC_CTOR
+ || reloc_type[0] == BFD_RELOC_MIPS_SUB
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
+ || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+ || reloc_type[0] == BFD_RELOC_MIPS_REL16
+ || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
+ || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
+ || hi16_reloc_p (reloc_type[0])
+ || lo16_reloc_p (reloc_type[0])))
+ ip->fixp[0]->fx_no_overflow = 1;
+
+ /* These relocations can have an addend that won't fit in 2 octets. */
+ if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
+ || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
+ ip->fixp[0]->fx_no_overflow = 1;
+
+ if (mips_relax.sequence)
{
- macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
- macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
- return;
+ if (mips_relax.first_fixup == 0)
+ mips_relax.first_fixup = ip->fixp[0];
}
-
- if (freg != 0)
+ else if (reloc_needs_lo_p (*reloc_type))
{
- macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
- freg = reg;
+ struct mips_hi_fixup *hi_fixup;
+
+ /* Reuse the last entry if it already has a matching %lo. */
+ hi_fixup = mips_hi_fixup_list;
+ if (hi_fixup == 0
+ || !fixup_has_matching_lo_p (hi_fixup->fixp))
+ {
+ hi_fixup = ((struct mips_hi_fixup *)
+ xmalloc (sizeof (struct mips_hi_fixup)));
+ hi_fixup->next = mips_hi_fixup_list;
+ mips_hi_fixup_list = hi_fixup;
+ }
+ hi_fixup->fixp = ip->fixp[0];
+ hi_fixup->seg = now_seg;
}
- mid16 = lo32;
- mid16.X_add_number >>= 16;
- macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
- macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
- freg = reg;
- }
- if ((lo32.X_add_number & 0xffff) != 0)
- macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
-}
-static inline void
-load_delay_nop (void)
-{
- if (!gpr_interlocks)
- macro_build (NULL, "nop", "");
-}
+ /* Add fixups for the second and third relocations, if given.
+ Note that the ABI allows the second relocation to be
+ against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
+ moment we only use RSS_UNDEF, but we could add support
+ for the others if it ever becomes necessary. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] != BFD_RELOC_UNUSED)
+ {
+ ip->fixp[i] = fix_new (ip->frag, ip->where,
+ ip->fixp[0]->fx_size, NULL, 0,
+ FALSE, final_type[i]);
-/* Load an address into a register. */
+ /* Use fx_tcbit to mark compound relocs. */
+ ip->fixp[0]->fx_tcbit = 1;
+ ip->fixp[i]->fx_tcbit = 1;
+ }
+ }
+ install_insn (ip);
-static void
-load_address (int reg, expressionS *ep, int *used_at)
-{
- if (ep->X_op != O_constant
- && ep->X_op != O_symbol)
+ /* Update the register mask information. */
+ mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
+ mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
+
+ switch (method)
{
- as_bad (_("expression too complex"));
- ep->X_op = O_constant;
+ case APPEND_ADD:
+ insert_into_history (0, 1, ip);
+ break;
+
+ case APPEND_ADD_WITH_NOP:
+ {
+ struct mips_cl_insn *nop;
+
+ insert_into_history (0, 1, ip);
+ nop = get_delay_slot_nop (ip);
+ add_fixed_insn (nop);
+ insert_into_history (0, 1, nop);
+ if (mips_relax.sequence)
+ mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
+ }
+ break;
+
+ 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);
+ install_insn (ip);
+ insert_into_history (0, 1, ip);
+ break;
+
+ case APPEND_SWAP:
+ {
+ struct mips_cl_insn delay = history[0];
+ if (mips_opts.mips16)
+ {
+ know (delay.frag == ip->frag);
+ move_insn (ip, delay.frag, delay.where);
+ move_insn (&delay, ip->frag, ip->where + insn_length (ip));
+ }
+ else if (relaxed_branch || delay.frag != ip->frag)
+ {
+ /* Add the delay slot instruction to the end of the
+ current frag and shrink the fixed part of the
+ original frag. If the branch occupies the tail of
+ the latter, move it backwards to cover the gap. */
+ delay.frag->fr_fix -= branch_disp;
+ if (delay.frag == ip->frag)
+ move_insn (ip, ip->frag, ip->where - branch_disp);
+ add_fixed_insn (&delay);
+ }
+ else
+ {
+ move_insn (&delay, ip->frag,
+ ip->where - branch_disp + insn_length (ip));
+ move_insn (ip, history[0].frag, history[0].where);
+ }
+ history[0] = *ip;
+ delay.fixed_p = 1;
+ insert_into_history (0, 1, &delay);
+ }
+ break;
}
- if (ep->X_op == O_constant)
+ /* If we have just completed an unconditional branch, clear the history. */
+ if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
+ || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
{
- load_register (reg, ep, HAVE_64BIT_ADDRESSES);
- return;
+ unsigned int i;
+
+ mips_no_prev_insn ();
+
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].cleared_p = 1;
}
- if (mips_pic == NO_PIC)
+ /* We need to emit a label at the end of branch-likely macros. */
+ if (emit_branch_likely_macro)
{
- /* If this is a reference to a GP relative symbol, we want
- addiu $reg,$gp,<sym> (BFD_RELOC_GPREL16)
- Otherwise we want
- lui $reg,<sym> (BFD_RELOC_HI16_S)
- addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
- If we have an addend, we always use the latter form.
+ emit_branch_likely_macro = FALSE;
+ micromips_add_label ();
+ }
- With 64bit address space and a usable $at we want
- lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- lui $at,<sym> (BFD_RELOC_HI16_S)
- daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
- daddiu $at,<sym> (BFD_RELOC_LO16)
- dsll32 $reg,0
- daddu $reg,$reg,$at
+ /* We just output an insn, so the next one doesn't have a label. */
+ mips_clear_insn_labels ();
+}
- If $at is already in use, we use a path which is suboptimal
- on superscalar processors.
- lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
- dsll $reg,16
- daddiu $reg,<sym> (BFD_RELOC_HI16_S)
- dsll $reg,16
- daddiu $reg,<sym> (BFD_RELOC_LO16)
+/* Forget that there was any previous instruction or label.
+ When BRANCH is true, the branch history is also flushed. */
- For GP relative symbols in 64bit address space we can use
- the same sequence as in 32bit address space. */
- if (HAVE_64BIT_SYMBOLS)
- {
- if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (ep->X_add_symbol, 1))
- {
- relax_start (ep->X_add_symbol);
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
- mips_gp_register, BFD_RELOC_GPREL16);
- relax_switch ();
- }
+static void
+mips_no_prev_insn (void)
+{
+ prev_nop_frag = NULL;
+ insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
+ mips_clear_insn_labels ();
+}
- if (*used_at == 0 && mips_opts.at)
- {
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
- macro_build (ep, "daddiu", "t,r,j", reg, reg,
- BFD_RELOC_MIPS_HIGHER);
- macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
- macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
- macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
- *used_at = 1;
- }
- else
- {
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (ep, "daddiu", "t,r,j", reg, reg,
- BFD_RELOC_MIPS_HIGHER);
- macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
- macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
- macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
- macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
- }
+/* This function must be called before we emit something other than
+ instructions. It is like mips_no_prev_insn except that it inserts
+ any NOPS that might be needed by previous instructions. */
- if (mips_relax.sequence)
- relax_end ();
- }
- else
+void
+mips_emit_delays (void)
+{
+ if (! mips_opts.noreorder)
+ {
+ int nops = nops_for_insn (0, history, NULL);
+ if (nops > 0)
{
- if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (ep->X_add_symbol, 1))
- {
- relax_start (ep->X_add_symbol);
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
- mips_gp_register, BFD_RELOC_GPREL16);
- relax_switch ();
- }
- macro_build_lui (ep, reg);
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
- reg, reg, BFD_RELOC_LO16);
- if (mips_relax.sequence)
- relax_end ();
+ while (nops-- > 0)
+ add_fixed_insn (NOP_INSN);
+ mips_move_text_labels ();
}
}
- else if (!mips_big_got)
- {
- expressionS ex;
-
- /* If this is a reference to an external symbol, we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- Otherwise we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
- If there is a constant, it must be added in after.
+ mips_no_prev_insn ();
+}
- If we have NewABI, we want
- lw $reg,<sym+cst>($gp) (BFD_RELOC_MIPS_GOT_DISP)
- unless we're referencing a global symbol with a non-zero
- offset, in which case cst must be added separately. */
- if (HAVE_NEWABI)
- {
- if (ep->X_add_number)
- {
- ex.X_add_number = ep->X_add_number;
- ep->X_add_number = 0;
- relax_start (ep->X_add_symbol);
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
- if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- ex.X_op = O_constant;
- macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
- reg, reg, BFD_RELOC_LO16);
- ep->X_add_number = ex.X_add_number;
- relax_switch ();
- }
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
- if (mips_relax.sequence)
- relax_end ();
- }
- else
- {
- ex.X_add_number = ep->X_add_number;
- ep->X_add_number = 0;
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- load_delay_nop ();
- relax_start (ep->X_add_symbol);
- relax_switch ();
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
- BFD_RELOC_LO16);
- relax_end ();
+/* Start a (possibly nested) noreorder block. */
- if (ex.X_add_number != 0)
- {
- if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- ex.X_op = O_constant;
- macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
- reg, reg, BFD_RELOC_LO16);
- }
- }
- }
- else if (mips_big_got)
+static void
+start_noreorder (void)
+{
+ if (mips_opts.noreorder == 0)
{
- expressionS ex;
-
- /* This is the large GOT case. If this is a reference to an
- external symbol, we want
- lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $reg,$reg,$gp
- lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16)
+ unsigned int i;
+ int nops;
- Otherwise, for a reference to a local symbol in old ABI, we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
- If there is a constant, it must be added in after.
+ /* None of the instructions before the .set noreorder can be moved. */
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].fixed_p = 1;
- In the NewABI, for local symbols, with or without offsets, we want:
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
- addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
- */
- if (HAVE_NEWABI)
+ /* Insert any nops that might be needed between the .set noreorder
+ block and the previous instructions. We will later remove any
+ nops that turn out not to be needed. */
+ nops = nops_for_insn (0, history, NULL);
+ if (nops > 0)
{
- ex.X_add_number = ep->X_add_number;
- ep->X_add_number = 0;
- relax_start (ep->X_add_symbol);
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- reg, reg, mips_gp_register);
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
- reg, BFD_RELOC_MIPS_GOT_LO16, reg);
- if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- else if (ex.X_add_number)
+ if (mips_optimize != 0)
{
- ex.X_op = O_constant;
- macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
- BFD_RELOC_LO16);
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (nops * NOP_INSN_SIZE);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
}
- ep->X_add_number = ex.X_add_number;
- relax_switch ();
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
- BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
- BFD_RELOC_MIPS_GOT_OFST);
- relax_end ();
- }
- else
- {
- ex.X_add_number = ep->X_add_number;
- ep->X_add_number = 0;
- relax_start (ep->X_add_symbol);
- macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- reg, reg, mips_gp_register);
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
- reg, BFD_RELOC_MIPS_GOT_LO16, reg);
- relax_switch ();
- if (reg_needs_delay (mips_gp_register))
- {
- /* We need a nop before loading from $gp. This special
- check is required because the lui which starts the main
- instruction stream does not refer to $gp, and so will not
- insert the nop which may be required. */
- macro_build (NULL, "nop", "");
- }
- macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- load_delay_nop ();
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
- BFD_RELOC_LO16);
- relax_end ();
+ for (; nops > 0; --nops)
+ add_fixed_insn (NOP_INSN);
- if (ex.X_add_number != 0)
- {
- if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- ex.X_op = O_constant;
- macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
- BFD_RELOC_LO16);
- }
+ /* 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);
+ mips_move_text_labels ();
}
+ mips_mark_labels ();
+ mips_clear_insn_labels ();
}
- else
- abort ();
-
- if (!mips_opts.at && *used_at == 1)
- as_bad (_("Macro used $at after \".set noat\""));
+ mips_opts.noreorder++;
+ mips_any_noreorder = 1;
}
-/* Move the contents of register SOURCE into register DEST. */
+/* End a nested noreorder block. */
static void
-move_register (int dest, int source)
+end_noreorder (void)
{
- /* Prefer to use a 16-bit microMIPS instruction unless the previous
- instruction specifically requires a 32-bit one. */
- if (mips_opts.micromips
- && !mips_opts.insn32
- && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
- macro_build (NULL, "move", "mp,mj", dest, source);
- else
- macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
- dest, source, 0);
+ mips_opts.noreorder--;
+ if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+ {
+ /* Commit to inserting prev_nop_frag_required nops and go back to
+ handling nop insertion the .set reorder way. */
+ prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+ * NOP_INSN_SIZE);
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_required, NOP_INSN);
+ prev_nop_frag = NULL;
+ }
}
-/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
- LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
- The two alternatives are:
-
- Global symbol Local sybmol
- ------------- ------------
- lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
- ... ...
- addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
-
- load_got_offset emits the first instruction and add_got_offset
- emits the second for a 16-bit offset or add_got_offset_hilo emits
- a sequence to add a 32-bit offset using a scratch register. */
+/* Set up global variables for the start of a new macro. */
static void
-load_got_offset (int dest, expressionS *local)
+macro_start (void)
{
- expressionS global;
-
- global = *local;
- global.X_add_number = 0;
-
- relax_start (local->X_add_symbol);
- macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- relax_switch ();
- macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- relax_end ();
+ memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+ memset (&mips_macro_warning.first_insn_sizes, 0,
+ sizeof (mips_macro_warning.first_insn_sizes));
+ 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;
+ }
+ mips_macro_warning.first_frag = NULL;
}
-static void
-add_got_offset (int dest, expressionS *local)
-{
- expressionS global;
-
- global.X_op = O_constant;
- global.X_op_symbol = NULL;
- global.X_add_symbol = NULL;
- global.X_add_number = local->X_add_number;
+/* Given that a macro is longer than one instruction or of the wrong size,
+ return the appropriate warning for it. Return null if no warning is
+ needed. SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
+ RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
+ and RELAX_NOMACRO. */
- relax_start (local->X_add_symbol);
- macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
- dest, dest, BFD_RELOC_LO16);
- relax_switch ();
- macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
- relax_end ();
+static const char *
+macro_warning (relax_substateT subtype)
+{
+ if (subtype & RELAX_DELAY_SLOT)
+ return _("Macro instruction expanded into multiple instructions"
+ " in a branch delay slot");
+ else if (subtype & RELAX_NOMACRO)
+ return _("Macro instruction expanded into multiple instructions");
+ else if (subtype & (RELAX_DELAY_SLOT_SIZE_FIRST
+ | RELAX_DELAY_SLOT_SIZE_SECOND))
+ return ((subtype & RELAX_DELAY_SLOT_16BIT)
+ ? _("Macro instruction expanded into a wrong size instruction"
+ " in a 16-bit branch delay slot")
+ : _("Macro instruction expanded into a wrong size instruction"
+ " in a 32-bit branch delay slot"));
+ else
+ return 0;
}
+/* Finish up a macro. Emit warnings as appropriate. */
+
static void
-add_got_offset_hilo (int dest, expressionS *local, int tmp)
+macro_end (void)
{
- expressionS global;
- int hold_mips_optimize;
+ /* Relaxation warning flags. */
+ relax_substateT subtype = 0;
- global.X_op = O_constant;
- global.X_op_symbol = NULL;
- global.X_add_symbol = NULL;
- global.X_add_number = local->X_add_number;
+ /* Check delay slot size requirements. */
+ if (mips_macro_warning.delay_slot_length == 2)
+ subtype |= RELAX_DELAY_SLOT_16BIT;
+ if (mips_macro_warning.delay_slot_length != 0)
+ {
+ if (mips_macro_warning.delay_slot_length
+ != mips_macro_warning.first_insn_sizes[0])
+ subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
+ if (mips_macro_warning.delay_slot_length
+ != mips_macro_warning.first_insn_sizes[1])
+ subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
+ }
- relax_start (local->X_add_symbol);
- load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
- relax_switch ();
- /* Set mips_optimize around the lui instruction to avoid
- inserting an unnecessary nop after the lw. */
- hold_mips_optimize = mips_optimize;
- mips_optimize = 2;
- macro_build_lui (&global, tmp);
- mips_optimize = hold_mips_optimize;
- macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
- relax_end ();
+ /* Check instruction count requirements. */
+ if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
+ {
+ if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
+ subtype |= RELAX_SECOND_LONGER;
+ if (mips_opts.warn_about_macros)
+ subtype |= RELAX_NOMACRO;
+ if (mips_macro_warning.delay_slot_p)
+ subtype |= RELAX_DELAY_SLOT;
+ }
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
-}
+ /* If both alternatives fail to fill a delay slot correctly,
+ emit the warning now. */
+ if ((subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0
+ && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0)
+ {
+ relax_substateT s;
+ const char *msg;
-/* Emit a sequence of instructions to emulate a branch likely operation.
- BR is an ordinary branch corresponding to one to be emulated. BRNEG
- is its complementing branch with the original condition negated.
- CALL is set if the original branch specified the link operation.
- EP, FMT, SREG and TREG specify the usual macro_build() parameters.
+ s = subtype & (RELAX_DELAY_SLOT_16BIT
+ | RELAX_DELAY_SLOT_SIZE_FIRST
+ | RELAX_DELAY_SLOT_SIZE_SECOND);
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn ("%s", msg);
+ subtype &= ~s;
+ }
- Code like this is produced in the noreorder mode:
+ /* If both implementations are longer than 1 instruction, then emit the
+ warning now. */
+ if (mips_macro_warning.insns[0] > 1 && mips_macro_warning.insns[1] > 1)
+ {
+ relax_substateT s;
+ const char *msg;
- BRNEG <args>, 1f
- nop
- b <sym>
- delay slot (executed only if branch taken)
- 1:
+ s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn ("%s", msg);
+ subtype &= ~s;
+ }
- or, if CALL is set:
+ /* If any flags still set, then one implementation might need a warning
+ and the other either will need one of a different kind or none at all.
+ Pass any remaining flags over to relaxation. */
+ if (mips_macro_warning.first_frag != NULL)
+ mips_macro_warning.first_frag->fr_subtype |= subtype;
+}
- BRNEG <args>, 1f
- nop
- bal <sym>
- delay slot (executed only if branch taken)
- 1:
+/* Instruction operand formats used in macros that vary between
+ standard MIPS and microMIPS code. */
- In the reorder mode the delay slot would be filled with a nop anyway,
- so code produced is simply:
+static const char * const brk_fmt[2][2] = { { "c", "c" }, { "mF", "c" } };
+static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
+static const char * const jalr_fmt[2] = { "d,s", "t,s" };
+static const char * const lui_fmt[2] = { "t,u", "s,u" };
+static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
+static const char * const mfhl_fmt[2][2] = { { "d", "d" }, { "mj", "s" } };
+static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
+static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
- BR <args>, <sym>
- nop
+#define BRK_FMT (brk_fmt[mips_opts.micromips][mips_opts.insn32])
+#define COP12_FMT (cop12_fmt[mips_opts.micromips])
+#define JALR_FMT (jalr_fmt[mips_opts.micromips])
+#define LUI_FMT (lui_fmt[mips_opts.micromips])
+#define MEM12_FMT (mem12_fmt[mips_opts.micromips])
+#define MFHL_FMT (mfhl_fmt[mips_opts.micromips][mips_opts.insn32])
+#define SHFT_FMT (shft_fmt[mips_opts.micromips])
+#define TRAP_FMT (trap_fmt[mips_opts.micromips])
- This function is used when producing code for the microMIPS ASE that
- does not implement branch likely instructions in hardware. */
+/* Read a macro's relocation codes from *ARGS and store them in *R.
+ The first argument in *ARGS will be either the code for a single
+ relocation or -1 followed by the three codes that make up a
+ composite relocation. */
static void
-macro_build_branch_likely (const char *br, const char *brneg,
- int call, expressionS *ep, const char *fmt,
- unsigned int sreg, unsigned int treg)
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
{
- int noreorder = mips_opts.noreorder;
- expressionS expr1;
-
- gas_assert (mips_opts.micromips);
- start_noreorder ();
- if (noreorder)
- {
- micromips_label_expr (&expr1);
- macro_build (&expr1, brneg, fmt, sreg, treg);
- macro_build (NULL, "nop", "");
- macro_build (ep, call ? "bal" : "b", "p");
+ int i, next;
- /* Set to true so that append_insn adds a label. */
- emit_branch_likely_macro = TRUE;
- }
+ next = va_arg (*args, int);
+ if (next >= 0)
+ r[0] = (bfd_reloc_code_real_type) next;
else
{
- macro_build (ep, br, fmt, sreg, treg);
- macro_build (NULL, "nop", "");
+ for (i = 0; i < 3; i++)
+ r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
+ /* This function is only used for 16-bit relocation fields.
+ To make the macro code simpler, treat an unrelocated value
+ in the same way as BFD_RELOC_LO16. */
+ if (r[0] == BFD_RELOC_UNUSED)
+ r[0] = BFD_RELOC_LO16;
}
- end_noreorder ();
}
-/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
- the condition code tested. EP specifies the branch target. */
+/* Build an instruction created by a macro expansion. This is passed
+ a pointer to the count of instructions created so far, an
+ expression, the name of the instruction to build, an operand format
+ string, and corresponding arguments. */
static void
-macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
{
- const int call = 0;
- const char *brneg;
- const char *br;
+ const struct mips_opcode *mo = NULL;
+ bfd_reloc_code_real_type r[3];
+ const struct mips_opcode *amo;
+ const struct mips_operand *operand;
+ struct hash_control *hash;
+ struct mips_cl_insn insn;
+ va_list args;
+ unsigned int uval;
- switch (type)
+ va_start (args, fmt);
+
+ if (mips_opts.mips16)
{
- case M_BC1FL:
- br = "bc1f";
- brneg = "bc1t";
- break;
- case M_BC1TL:
- br = "bc1t";
- brneg = "bc1f";
- break;
- case M_BC2FL:
- br = "bc2f";
- brneg = "bc2t";
- break;
- case M_BC2TL:
- br = "bc2t";
- brneg = "bc2f";
- break;
- default:
- abort ();
+ mips16_macro_build (ep, name, fmt, &args);
+ va_end (args);
+ return;
}
- macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
-}
-/* Emit a two-argument branch macro specified by TYPE, using SREG as
- the register tested. EP specifies the branch target. */
+ r[0] = BFD_RELOC_UNUSED;
+ r[1] = BFD_RELOC_UNUSED;
+ r[2] = BFD_RELOC_UNUSED;
+ hash = mips_opts.micromips ? micromips_op_hash : op_hash;
+ amo = (struct mips_opcode *) hash_find (hash, name);
+ gas_assert (amo);
+ gas_assert (strcmp (name, amo->name) == 0);
-static void
-macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
-{
- const char *brneg = NULL;
- const char *br;
- int call = 0;
+ do
+ {
+ /* Search until we get a match for NAME. It is assumed here that
+ macros will never generate MDMX, MIPS-3D, or MT instructions.
+ We try to match an instruction that fulfils the branch delay
+ slot instruction length requirement (if any) of the previous
+ instruction. While doing this we record the first instruction
+ seen that matches all the other conditions and use it anyway
+ if the requirement cannot be met; we will issue an appropriate
+ warning later on. */
+ if (strcmp (fmt, amo->args) == 0
+ && amo->pinfo != INSN_MACRO
+ && is_opcode_valid (amo)
+ && is_size_valid (amo))
+ {
+ if (is_delay_slot_valid (amo))
+ {
+ mo = amo;
+ break;
+ }
+ else if (!mo)
+ mo = amo;
+ }
- switch (type)
+ ++amo;
+ gas_assert (amo->name);
+ }
+ while (strcmp (name, amo->name) == 0);
+
+ gas_assert (mo);
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
{
- case M_BGEZ:
- br = "bgez";
- break;
- case M_BGEZL:
- br = mips_opts.micromips ? "bgez" : "bgezl";
- brneg = "bltz";
- break;
- case M_BGEZALL:
- gas_assert (mips_opts.micromips);
- br = mips_opts.insn32 ? "bgezal" : "bgezals";
- brneg = "bltz";
- call = 1;
- break;
- case M_BGTZ:
- br = "bgtz";
- break;
- case M_BGTZL:
- br = mips_opts.micromips ? "bgtz" : "bgtzl";
- brneg = "blez";
- break;
- case M_BLEZ:
- br = "blez";
- break;
- case M_BLEZL:
- br = mips_opts.micromips ? "blez" : "blezl";
- brneg = "bgtz";
- break;
- case M_BLTZ:
- br = "bltz";
- break;
- case M_BLTZL:
- br = mips_opts.micromips ? "bltz" : "bltzl";
- brneg = "bgez";
- break;
- case M_BLTZALL:
- gas_assert (mips_opts.micromips);
- br = mips_opts.insn32 ? "bltzal" : "bltzals";
- brneg = "bgez";
- call = 1;
- break;
- default:
- abort ();
+ switch (*fmt)
+ {
+ case ',':
+ case '(':
+ case ')':
+ case 'z':
+ break;
+
+ case 'i':
+ case 'j':
+ macro_read_relocs (&args, r);
+ gas_assert (*r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_HIGHER
+ || *r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_LO16
+ || *r == BFD_RELOC_MIPS_GOT_OFST);
+ break;
+
+ case 'o':
+ macro_read_relocs (&args, r);
+ break;
+
+ case 'u':
+ macro_read_relocs (&args, r);
+ gas_assert (ep != NULL
+ && (ep->X_op == O_constant
+ || (ep->X_op == O_symbol
+ && (*r == BFD_RELOC_MIPS_HIGHEST
+ || *r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_HI16
+ || *r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_GOT_HI16
+ || *r == BFD_RELOC_MIPS_CALL_HI16))));
+ break;
+
+ case 'p':
+ gas_assert (ep != NULL);
+
+ /*
+ * This allows macro() to pass an immediate expression for
+ * creating short branches without creating a symbol.
+ *
+ * We don't allow branch relaxation for these branches, as
+ * they should only appear in ".set nomacro" anyway.
+ */
+ if (ep->X_op == O_constant)
+ {
+ /* For microMIPS we always use relocations for branches.
+ So we should not resolve immediate values. */
+ gas_assert (!mips_opts.micromips);
+
+ if ((ep->X_add_number & 3) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ if ((ep->X_add_number + 0x20000) & ~0x3ffff)
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
+ ep = NULL;
+ }
+ else
+ *r = BFD_RELOC_16_PCREL_S2;
+ break;
+
+ case 'a':
+ gas_assert (ep != NULL);
+ *r = BFD_RELOC_MIPS_JMP;
+ break;
+
+ default:
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (fmt)
+ : decode_mips_operand (fmt));
+ if (!operand)
+ abort ();
+
+ uval = va_arg (args, int);
+ if (operand->type == OP_CLO_CLZ_DEST)
+ uval |= (uval << 5);
+ insn_insert_operand (&insn, operand, uval);
+
+ if (*fmt == '+' || *fmt == 'm')
+ ++fmt;
+ break;
+ }
}
- if (mips_opts.micromips && brneg)
- macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
- else
- macro_build (ep, br, "s,p", sreg);
-}
+ va_end (args);
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-/* Emit a three-argument branch macro specified by TYPE, using SREG and
- TREG as the registers tested. EP specifies the branch target. */
+ append_insn (&insn, ep, r, TRUE);
+}
static void
-macro_build_branch_rsrt (int type, expressionS *ep,
- unsigned int sreg, unsigned int treg)
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+ va_list *args)
{
- const char *brneg = NULL;
- const int call = 0;
- const char *br;
+ struct mips_opcode *mo;
+ struct mips_cl_insn insn;
+ const struct mips_operand *operand;
+ bfd_reloc_code_real_type r[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
- switch (type)
+ mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+ gas_assert (mo);
+ gas_assert (strcmp (name, mo->name) == 0);
+
+ while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
{
- case M_BEQ:
- case M_BEQ_I:
- br = "beq";
- break;
- case M_BEQL:
- case M_BEQL_I:
- br = mips_opts.micromips ? "beq" : "beql";
- brneg = "bne";
- break;
- case M_BNE:
- case M_BNE_I:
- br = "bne";
- break;
- case M_BNEL:
- case M_BNEL_I:
- br = mips_opts.micromips ? "bne" : "bnel";
- brneg = "beq";
- break;
- default:
- abort ();
+ ++mo;
+ gas_assert (mo->name);
+ gas_assert (strcmp (name, mo->name) == 0);
}
- if (mips_opts.micromips && brneg)
- macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
- else
- macro_build (ep, br, "s,t,p", sreg, treg);
-}
-/*
- * Build macros
- * This routine implements the seemingly endless macro or synthesized
- * instructions and addressing modes in the mips assembly language. Many
- * of these macros are simple and are similar to each other. These could
- * probably be handled by some kind of table or grammar approach instead of
- * this verbose method. Others are not simple macros but are more like
- * 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
- * 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
- * first load register 'at' with zero and use it as the base register. The
- * mips assembler simply uses register $zero. Just one tiny optimization
- * we're missing.
- */
-static void
-macro (struct mips_cl_insn *ip, char *str)
-{
- unsigned int treg, sreg, dreg, breg;
- unsigned int tempreg;
- int mask;
- int used_at = 0;
- expressionS label_expr;
- expressionS expr1;
- expressionS *ep;
- const char *s;
- const char *s2;
- const char *fmt;
- int likely = 0;
- int coproc = 0;
- int offbits = 16;
- int call = 0;
- int jals = 0;
- int dbl = 0;
- int imm = 0;
- int ust = 0;
- int lp = 0;
- int ab = 0;
- int off;
- bfd_reloc_code_real_type r;
- int hold_mips_optimize;
-
- gas_assert (! mips_opts.mips16);
-
- treg = EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
- dreg = EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
- sreg = breg = EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
- mask = ip->insn_mo->mask;
-
- label_expr.X_op = O_constant;
- label_expr.X_op_symbol = NULL;
- label_expr.X_add_symbol = NULL;
- label_expr.X_add_number = 0;
-
- expr1.X_op = O_constant;
- expr1.X_op_symbol = NULL;
- expr1.X_add_symbol = NULL;
- expr1.X_add_number = 1;
-
- switch (mask)
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
{
- case M_DABS:
- dbl = 1;
- case M_ABS:
- /* bgez $a0,1f
- move v0,$a0
- sub v0,$zero,$a0
- 1:
- */
-
- start_noreorder ();
-
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = 8;
- macro_build (&label_expr, "bgez", "s,p", sreg);
- if (dreg == sreg)
- macro_build (NULL, "nop", "");
- else
- move_register (dreg, sreg);
- macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
- if (mips_opts.micromips)
- micromips_add_label ();
-
- end_noreorder ();
- break;
+ int c;
- case M_ADD_I:
- s = "addi";
- s2 = "add";
- goto do_addi;
- case M_ADDU_I:
- s = "addiu";
- s2 = "addu";
- goto do_addi;
- case M_DADD_I:
- dbl = 1;
- s = "daddi";
- s2 = "dadd";
- if (!mips_opts.micromips)
- goto do_addi;
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x200
- && imm_expr.X_add_number < 0x200)
- {
- macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
- break;
- }
- goto do_addi_i;
- case M_DADDU_I:
- dbl = 1;
- s = "daddiu";
- s2 = "daddu";
- do_addi:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
+ c = *fmt;
+ switch (c)
{
- macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+ case ',':
+ case '(':
+ case ')':
break;
- }
- do_addi_i:
- used_at = 1;
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
- break;
- case M_AND_I:
- s = "andi";
- s2 = "and";
- goto do_bit;
- case M_OR_I:
- s = "ori";
- s2 = "or";
- goto do_bit;
- case M_NOR_I:
- s = "";
- s2 = "nor";
- goto do_bit;
- case M_XOR_I:
- s = "xori";
- s2 = "xor";
- do_bit:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= 0
- && imm_expr.X_add_number < 0x10000)
- {
- if (mask != M_NOR_I)
- macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
- else
- {
- macro_build (&imm_expr, "ori", "t,r,i",
- treg, sreg, BFD_RELOC_LO16);
- macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
- }
+ case '0':
+ case 'S':
+ case 'P':
+ case 'R':
break;
- }
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
- break;
+ case '<':
+ case '>':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ {
+ offsetT value;
- case M_BALIGN:
- switch (imm_expr.X_add_number)
- {
- case 0:
- macro_build (NULL, "nop", "");
- break;
- case 2:
- macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
- break;
- case 1:
- case 3:
- macro_build (NULL, "balign", "t,s,2", treg, sreg,
- (int) imm_expr.X_add_number);
+ gas_assert (ep != NULL);
+
+ if (ep->X_op != O_constant)
+ *r = (int) BFD_RELOC_UNUSED + c;
+ else if (calculate_reloc (*r, ep->X_add_number, &value))
+ {
+ mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode);
+ ep = NULL;
+ *r = BFD_RELOC_UNUSED;
+ }
+ }
break;
+
default:
- as_bad (_("BALIGN immediate not 0, 1, 2 or 3 (%lu)"),
- (unsigned long) imm_expr.X_add_number);
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
+
+ insn_insert_operand (&insn, operand, va_arg (*args, int));
break;
}
- break;
+ }
- case M_BC1FL:
- case M_BC1TL:
- case M_BC2FL:
- case M_BC2TL:
- gas_assert (mips_opts.micromips);
- macro_build_branch_ccl (mask, &offset_expr,
- EXTRACT_OPERAND (1, BCC, *ip));
- break;
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
- case M_BEQ_I:
- case M_BEQL_I:
- case M_BNE_I:
- case M_BNEL_I:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- treg = 0;
- else
- {
- treg = AT;
- used_at = 1;
- load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
- }
- /* Fall through. */
- case M_BEQL:
- case M_BNEL:
- macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
- break;
+ append_insn (&insn, ep, r, TRUE);
+}
- case M_BGEL:
- likely = 1;
- case M_BGE:
- if (treg == 0)
- macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, sreg);
- else if (sreg == 0)
- macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, treg);
- else
- {
- used_at = 1;
- macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- }
- break;
+/*
+ * Sign-extend 32-bit mode constants that have bit 31 set and all
+ * higher bits unset.
+ */
+static void
+normalize_constant_expr (expressionS *ex)
+{
+ if (ex->X_op == O_constant
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
- case M_BGEZL:
- case M_BGEZALL:
- case M_BGTZL:
- case M_BLEZL:
- case M_BLTZL:
- case M_BLTZALL:
- macro_build_branch_rs (mask, &offset_expr, sreg);
- break;
+/*
+ * Sign-extend 32-bit mode address offsets that have bit 31 set and
+ * all higher bits unset.
+ */
+static void
+normalize_address_expr (expressionS *ex)
+{
+ if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+ || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
- case M_BGTL_I:
- likely = 1;
- case M_BGT_I:
- /* Check for > max integer. */
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= GPR_SMAX)
- {
- do_false:
- /* Result is always false. */
- if (! likely)
- macro_build (NULL, "nop", "");
- else
- macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
- break;
- }
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
- /* FALLTHROUGH */
- case M_BGE_I:
- case M_BGEL_I:
- if (mask == M_BGEL_I)
- likely = 1;
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- {
- macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
- &offset_expr, sreg);
- break;
- }
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
- {
- macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
- &offset_expr, sreg);
- break;
- }
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number <= GPR_SMIN)
- {
- do_true:
- /* result is always true */
- as_warn (_("Branch %s is always true"), ip->insn_mo->name);
- macro_build (&offset_expr, "b", "p");
- break;
- }
- used_at = 1;
- set_at (sreg, 0);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- break;
+/*
+ * Generate a "jalr" instruction with a relocation hint to the called
+ * function. This occurs in NewABI PIC code.
+ */
+static void
+macro_build_jalr (expressionS *ep, int cprestore)
+{
+ static const bfd_reloc_code_real_type jalr_relocs[2]
+ = { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
+ bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
+ const char *jalr;
+ char *f = NULL;
- case M_BGEUL:
- likely = 1;
- case M_BGEU:
- if (treg == 0)
- goto do_true;
- else if (sreg == 0)
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, ZERO, treg);
+ if (MIPS_JALR_HINT_P (ep))
+ {
+ frag_grow (8);
+ f = frag_more (0);
+ }
+ if (mips_opts.micromips)
+ {
+ jalr = ((mips_opts.noreorder && !cprestore) || mips_opts.insn32
+ ? "jalr" : "jalrs");
+ if (MIPS_JALR_HINT_P (ep)
+ || mips_opts.insn32
+ || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
else
- {
- used_at = 1;
- macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- }
- break;
+ macro_build (NULL, jalr, "mj", PIC_CALL_REG);
+ }
+ else
+ macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
+ if (MIPS_JALR_HINT_P (ep))
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
+}
- case M_BGTUL_I:
- likely = 1;
- case M_BGTU_I:
- if (sreg == 0
- || (HAVE_32BIT_GPRS
- && imm_expr.X_op == O_constant
- && imm_expr.X_add_number == -1))
- goto do_false;
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
- /* FALLTHROUGH */
- case M_BGEU_I:
- case M_BGEUL_I:
- if (mask == M_BGEUL_I)
- likely = 1;
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- goto do_true;
- else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, sreg, ZERO);
- else
- {
- used_at = 1;
- set_at (sreg, 1);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- }
- break;
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+ gas_assert (! mips_opts.mips16);
- case M_BGTL:
- likely = 1;
- case M_BGT:
- if (treg == 0)
- macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, sreg);
- else if (sreg == 0)
- macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, treg);
- else
- {
- used_at = 1;
- macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
+ if (ep->X_op != O_constant)
+ {
+ gas_assert (ep->X_op == O_symbol);
+ /* _gp_disp is a special case, used from s_cpload.
+ __gnu_local_gp is used if mips_no_shared. */
+ gas_assert (mips_pic == NO_PIC
+ || (! HAVE_NEWABI
+ && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
+ || (! mips_in_shared
+ && strcmp (S_GET_NAME (ep->X_add_symbol),
+ "__gnu_local_gp") == 0));
+ }
- case M_BGTUL:
- likely = 1;
- case M_BGTU:
- if (treg == 0)
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, sreg, ZERO);
- else if (sreg == 0)
- goto do_false;
- else
- {
- used_at = 1;
- macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
+ macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+}
- case M_BLEL:
- likely = 1;
- case M_BLE:
- if (treg == 0)
- macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
- else if (sreg == 0)
- macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, treg);
- else
- {
- used_at = 1;
- macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- }
- break;
+/* Generate a sequence of instructions to do a load or store from a constant
+ offset off of a base register (breg) into/from a target register (treg),
+ using AT if necessary. */
+static void
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+ int treg, int breg, int dbl)
+{
+ gas_assert (ep->X_op == O_constant);
- case M_BLEL_I:
- likely = 1;
- case M_BLE_I:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= GPR_SMAX)
- goto do_true;
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
- /* FALLTHROUGH */
- case M_BLT_I:
- case M_BLTL_I:
- if (mask == M_BLTL_I)
- likely = 1;
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
- else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
- macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
- else
- {
- used_at = 1;
- set_at (sreg, 0);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
- case M_BLEUL:
- likely = 1;
- case M_BLEU:
- if (treg == 0)
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, sreg, ZERO);
- else if (sreg == 0)
- goto do_true;
- else
- {
- used_at = 1;
- macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, AT, ZERO);
- }
- break;
+ /* Right now, this routine can only handle signed 32-bit constants. */
+ if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
+ as_warn (_("operand overflow"));
- case M_BLEUL_I:
- likely = 1;
- case M_BLEU_I:
- if (sreg == 0
- || (HAVE_32BIT_GPRS
- && imm_expr.X_op == O_constant
- && imm_expr.X_add_number == -1))
- goto do_true;
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
- /* FALLTHROUGH */
- case M_BLTU_I:
- case M_BLTUL_I:
- if (mask == M_BLTUL_I)
- likely = 1;
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- goto do_false;
- else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
- macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
- &offset_expr, sreg, ZERO);
- else
+ if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+ {
+ /* Signed 16-bit offset will fit in the op. Easy! */
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ }
+ else
+ {
+ /* 32-bit offset, need multiple instructions and AT, like:
+ lui $tempreg,const_hi (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,const_lo($tempreg) (BFD_RELOC_LO16)
+ to handle the complete offset. */
+ macro_build_lui (ep, AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+
+ if (!mips_opts.at)
+ as_bad (_("Macro used $at after \".set noat\""));
+ }
+}
+
+/* set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (int reg, int unsignedp)
+{
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
+ AT, reg, BFD_RELOC_LO16);
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+ }
+}
+
+/* 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:
+ for (lcnt = 0; (lcnt < 32); lcnt++)
+ if ((v) & (1 << (31 - lcnt)))
+ break;
+ However it is not code size friendly, and the gain will drop a bit
+ on certain cached systems.
+*/
+#define COUNT_TOP_ZEROES(v) \
+ (((v) & ~0xffff) == 0 \
+ ? ((v) & ~0xff) == 0 \
+ ? ((v) & ~0xf) == 0 \
+ ? ((v) & ~0x3) == 0 \
+ ? ((v) & ~0x1) == 0 \
+ ? !(v) \
+ ? 32 \
+ : 31 \
+ : 30 \
+ : ((v) & ~0x7) == 0 \
+ ? 29 \
+ : 28 \
+ : ((v) & ~0x3f) == 0 \
+ ? ((v) & ~0x1f) == 0 \
+ ? 27 \
+ : 26 \
+ : ((v) & ~0x7f) == 0 \
+ ? 25 \
+ : 24 \
+ : ((v) & ~0xfff) == 0 \
+ ? ((v) & ~0x3ff) == 0 \
+ ? ((v) & ~0x1ff) == 0 \
+ ? 23 \
+ : 22 \
+ : ((v) & ~0x7ff) == 0 \
+ ? 21 \
+ : 20 \
+ : ((v) & ~0x3fff) == 0 \
+ ? ((v) & ~0x1fff) == 0 \
+ ? 19 \
+ : 18 \
+ : ((v) & ~0x7fff) == 0 \
+ ? 17 \
+ : 16 \
+ : ((v) & ~0xffffff) == 0 \
+ ? ((v) & ~0xfffff) == 0 \
+ ? ((v) & ~0x3ffff) == 0 \
+ ? ((v) & ~0x1ffff) == 0 \
+ ? 15 \
+ : 14 \
+ : ((v) & ~0x7ffff) == 0 \
+ ? 13 \
+ : 12 \
+ : ((v) & ~0x3fffff) == 0 \
+ ? ((v) & ~0x1fffff) == 0 \
+ ? 11 \
+ : 10 \
+ : ((v) & ~0x7fffff) == 0 \
+ ? 9 \
+ : 8 \
+ : ((v) & ~0xfffffff) == 0 \
+ ? ((v) & ~0x3ffffff) == 0 \
+ ? ((v) & ~0x1ffffff) == 0 \
+ ? 7 \
+ : 6 \
+ : ((v) & ~0x7ffffff) == 0 \
+ ? 5 \
+ : 4 \
+ : ((v) & ~0x3fffffff) == 0 \
+ ? ((v) & ~0x1fffffff) == 0 \
+ ? 3 \
+ : 2 \
+ : ((v) & ~0x7fffffff) == 0 \
+ ? 1 \
+ : 0)
+
+/* load_register()
+ * This routine generates the least number of instructions necessary to load
+ * an absolute expression value into a register.
+ */
+static void
+load_register (int reg, expressionS *ep, int dbl)
+{
+ int freg;
+ expressionS hi32, lo32;
+
+ if (ep->X_op != O_big)
+ {
+ gas_assert (ep->X_op == O_constant);
+
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
+
+ if (IS_SEXT_16BIT_NUM (ep->X_add_number))
{
- used_at = 1;
- set_at (sreg, 1);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
+ /* We can handle 16 bit signed values with an addiu to
+ $zero. No need to ever use daddiu here, since $zero and
+ the result are always correct in 32 bit mode. */
+ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
}
- break;
-
- case M_BLTL:
- likely = 1;
- case M_BLT:
- if (treg == 0)
- macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
- else if (sreg == 0)
- macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, treg);
- else
+ else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
{
- used_at = 1;
- macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
+ /* We can handle 16 bit unsigned values with an ori to
+ $zero. */
+ macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+ return;
}
- break;
-
- case M_BLTUL:
- likely = 1;
- case M_BLTU:
- if (treg == 0)
- goto do_false;
- else if (sreg == 0)
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, ZERO, treg);
- else
+ else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
{
- used_at = 1;
- macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
+ /* 32 bit values require an lui. */
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ if ((ep->X_add_number & 0xffff) != 0)
+ macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
}
- break;
+ }
- case M_DEXT:
- {
- /* Use unsigned arithmetic. */
- addressT pos;
- addressT size;
+ /* The value is larger than 32 bits. */
- if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
- {
- as_bad (_("Unsupported large constant"));
- pos = size = 1;
- }
- else
- {
- pos = imm_expr.X_add_number;
- size = imm2_expr.X_add_number;
- }
+ if (!dbl || HAVE_32BIT_GPRS)
+ {
+ char value[32];
- if (pos > 63)
- {
- as_bad (_("Improper position (%lu)"), (unsigned long) pos);
- pos = 1;
- }
- if (size == 0 || size > 64 || (pos + size - 1) > 63)
- {
- as_bad (_("Improper extract size (%lu, position %lu)"),
- (unsigned long) size, (unsigned long) pos);
- size = 1;
- }
+ sprintf_vma (value, ep->X_add_number);
+ as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
- if (size <= 32 && pos < 32)
- {
- s = "dext";
- fmt = "t,r,+A,+C";
- }
- else if (size <= 32)
- {
- s = "dextu";
- fmt = "t,r,+E,+H";
- }
- else
- {
- s = "dextm";
- fmt = "t,r,+A,+G";
- }
- macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
- (int) (size - 1));
- }
- break;
-
- case M_DINS:
- {
- /* Use unsigned arithmetic. */
- addressT pos;
- addressT size;
-
- if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
- {
- as_bad (_("Unsupported large constant"));
- pos = size = 1;
- }
- else
- {
- pos = imm_expr.X_add_number;
- size = imm2_expr.X_add_number;
- }
+ if (ep->X_op != O_big)
+ {
+ hi32 = *ep;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number &= 0xffffffff;
+ lo32 = *ep;
+ lo32.X_add_number &= 0xffffffff;
+ }
+ else
+ {
+ gas_assert (ep->X_add_number > 2);
+ if (ep->X_add_number == 3)
+ generic_bignum[3] = 0;
+ else if (ep->X_add_number > 4)
+ as_bad (_("Number larger than 64 bits"));
+ lo32.X_op = O_constant;
+ lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
+ hi32.X_op = O_constant;
+ hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+ }
- if (pos > 63)
- {
- as_bad (_("Improper position (%lu)"), (unsigned long) pos);
- pos = 1;
- }
- if (size == 0 || size > 64 || (pos + size - 1) > 63)
- {
- as_bad (_("Improper insert size (%lu, position %lu)"),
- (unsigned long) size, (unsigned long) pos);
- size = 1;
- }
+ if (hi32.X_add_number == 0)
+ freg = 0;
+ else
+ {
+ int shift, bit;
+ unsigned long hi, lo;
- if (pos < 32 && (pos + size - 1) < 32)
- {
- s = "dins";
- fmt = "t,r,+A,+B";
- }
- else if (pos >= 32)
- {
- s = "dinsu";
- fmt = "t,r,+E,+F";
- }
- else
- {
- s = "dinsm";
- fmt = "t,r,+A,+F";
- }
- macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
- (int) (pos + size - 1));
- }
- break;
+ if (hi32.X_add_number == (offsetT) 0xffffffff)
+ {
+ if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+ {
+ macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ if (lo32.X_add_number & 0x80000000)
+ {
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ if (lo32.X_add_number & 0xffff)
+ macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
+ }
+ }
- case M_DDIV_3:
- dbl = 1;
- case M_DIV_3:
- s = "mflo";
- goto do_div3;
- case M_DREM_3:
- dbl = 1;
- case M_REM_3:
- s = "mfhi";
- do_div3:
- if (treg == 0)
+ /* Check for 16bit shifted constant. We know that hi32 is
+ non-zero, so start the mask on the first bit of the hi32
+ value. */
+ shift = 17;
+ do
{
- as_warn (_("Divide by zero."));
- if (mips_trap)
- macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+ unsigned long himask, lomask;
+
+ if (shift < 32)
+ {
+ himask = 0xffff >> (32 - shift);
+ lomask = (0xffff << shift) & 0xffffffff;
+ }
else
- macro_build (NULL, "break", BRK_FMT, 7);
- break;
+ {
+ himask = 0xffff << (shift - 32);
+ lomask = 0;
+ }
+ if ((hi32.X_add_number & ~(offsetT) himask) == 0
+ && (lo32.X_add_number & ~(offsetT) lomask) == 0)
+ {
+ expressionS tmp;
+
+ tmp.X_op = O_constant;
+ if (shift < 32)
+ tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+ | (lo32.X_add_number >> shift));
+ else
+ tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+ macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+ macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", SHFT_FMT,
+ reg, reg, (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ ++shift;
}
+ while (shift <= (64 - 16));
- start_noreorder ();
- if (mips_trap)
+ /* Find the bit number of the lowest one bit, and store the
+ shifted value in hi/lo. */
+ hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+ lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+ if (lo != 0)
{
- macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
- macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+ bit = 0;
+ while ((lo & 1) == 0)
+ {
+ lo >>= 1;
+ ++bit;
+ }
+ lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+ hi >>= bit;
}
else
{
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = 8;
- macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
- macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
- macro_build (NULL, "break", BRK_FMT, 7);
- if (mips_opts.micromips)
- micromips_add_label ();
+ bit = 32;
+ while ((hi & 1) == 0)
+ {
+ hi >>= 1;
+ ++bit;
+ }
+ lo = hi;
+ hi = 0;
}
- expr1.X_add_number = -1;
- used_at = 1;
- load_register (AT, &expr1, dbl);
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
- macro_build (&label_expr, "bne", "s,t,p", treg, AT);
- if (dbl)
+
+ /* Optimize if the shifted value is a (power of 2) - 1. */
+ if ((hi == 0 && ((lo + 1) & lo) == 0)
+ || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
{
- expr1.X_add_number = 1;
- load_register (AT, &expr1, dbl);
- macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
+ shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+ if (shift != 0)
+ {
+ expressionS tmp;
+
+ /* This instruction will set the register to be all
+ ones. */
+ tmp.X_op = O_constant;
+ tmp.X_add_number = (offsetT) -1;
+ macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ if (bit != 0)
+ {
+ bit += shift;
+ macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", SHFT_FMT,
+ reg, reg, (bit >= 32) ? bit - 32 : bit);
+ }
+ macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", SHFT_FMT,
+ reg, reg, (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
}
- else
+
+ /* Sign extend hi32 before calling load_register, because we can
+ generally get better code when we load a sign extended value. */
+ if ((hi32.X_add_number & 0x80000000) != 0)
+ hi32.X_add_number |= ~(offsetT) 0xffffffff;
+ load_register (reg, &hi32, 0);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff0000) == 0)
+ {
+ if (freg != 0)
{
- expr1.X_add_number = 0x80000000;
- macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
+ macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+ freg = reg;
}
- if (mips_trap)
+ }
+ else
+ {
+ expressionS mid16;
+
+ if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
{
- macro_build (NULL, "teq", TRAP_FMT, sreg, AT, 6);
- /* We want to close the noreorder block as soon as possible, so
- that later insns are available for delay slot filling. */
- end_noreorder ();
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+ return;
}
- else
- {
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = 8;
- macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
- macro_build (NULL, "nop", "");
-
- /* We want to close the noreorder block as soon as possible, so
- that later insns are available for delay slot filling. */
- end_noreorder ();
- macro_build (NULL, "break", BRK_FMT, 6);
- }
- if (mips_opts.micromips)
- micromips_add_label ();
- macro_build (NULL, s, MFHL_FMT, dreg);
- break;
-
- case M_DIV_3I:
- s = "div";
- s2 = "mflo";
- goto do_divi;
- case M_DIVU_3I:
- s = "divu";
- s2 = "mflo";
- goto do_divi;
- case M_REM_3I:
- s = "div";
- s2 = "mfhi";
- goto do_divi;
- case M_REMU_3I:
- s = "divu";
- s2 = "mfhi";
- goto do_divi;
- case M_DDIV_3I:
- dbl = 1;
- s = "ddiv";
- s2 = "mflo";
- goto do_divi;
- case M_DDIVU_3I:
- dbl = 1;
- s = "ddivu";
- s2 = "mflo";
- goto do_divi;
- case M_DREM_3I:
- dbl = 1;
- s = "ddiv";
- s2 = "mfhi";
- goto do_divi;
- case M_DREMU_3I:
- dbl = 1;
- s = "ddivu";
- s2 = "mfhi";
- do_divi:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- {
- as_warn (_("Divide by zero."));
- if (mips_trap)
- macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
- else
- macro_build (NULL, "break", BRK_FMT, 7);
- break;
- }
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
- {
- if (strcmp (s2, "mflo") == 0)
- move_register (dreg, sreg);
- else
- move_register (dreg, ZERO);
- break;
- }
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number == -1
- && s[strlen (s) - 1] != 'u')
+ if (freg != 0)
{
- if (strcmp (s2, "mflo") == 0)
- {
- macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
- }
- else
- move_register (dreg, ZERO);
- break;
+ macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
+ freg = reg;
}
+ mid16 = lo32;
+ mid16.X_add_number >>= 16;
+ macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff) != 0)
+ macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
- used_at = 1;
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, s, "z,s,t", sreg, AT);
- macro_build (NULL, s2, MFHL_FMT, dreg);
- break;
+static inline void
+load_delay_nop (void)
+{
+ if (!gpr_interlocks)
+ macro_build (NULL, "nop", "");
+}
- case M_DIVU_3:
- s = "divu";
- s2 = "mflo";
- goto do_divu3;
- case M_REMU_3:
- s = "divu";
- s2 = "mfhi";
- goto do_divu3;
- case M_DDIVU_3:
- s = "ddivu";
- s2 = "mflo";
- goto do_divu3;
- case M_DREMU_3:
- s = "ddivu";
- s2 = "mfhi";
- do_divu3:
- start_noreorder ();
- if (mips_trap)
- {
- macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
- macro_build (NULL, s, "z,s,t", sreg, treg);
- /* We want to close the noreorder block as soon as possible, so
- that later insns are available for delay slot filling. */
- end_noreorder ();
- }
- else
- {
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = 8;
- macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
- macro_build (NULL, s, "z,s,t", sreg, treg);
+/* Load an address into a register. */
- /* We want to close the noreorder block as soon as possible, so
- that later insns are available for delay slot filling. */
- end_noreorder ();
- macro_build (NULL, "break", BRK_FMT, 7);
- if (mips_opts.micromips)
- micromips_add_label ();
- }
- macro_build (NULL, s2, MFHL_FMT, dreg);
- break;
+static void
+load_address (int reg, expressionS *ep, int *used_at)
+{
+ if (ep->X_op != O_constant
+ && ep->X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ ep->X_op = O_constant;
+ }
- case M_DLCA_AB:
- dbl = 1;
- case M_LCA_AB:
- call = 1;
- goto do_la;
- case M_DLA_AB:
- dbl = 1;
- case M_LA_AB:
- do_la:
- /* Load the address of a symbol into a register. If breg is not
- zero, we then add a base register to it. */
+ if (ep->X_op == O_constant)
+ {
+ load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+ return;
+ }
- if (dbl && HAVE_32BIT_GPRS)
- as_warn (_("dla used to load 32-bit register"));
+ if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $reg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $reg,<sym> (BFD_RELOC_HI16_S)
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If we have an addend, we always use the latter form.
- if (!dbl && HAVE_64BIT_OBJECTS)
- as_warn (_("la used to load 64-bit address"));
+ With 64bit address space and a usable $at we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddiu $at,<sym> (BFD_RELOC_LO16)
+ dsll32 $reg,0
+ daddu $reg,$reg,$at
- if (offset_expr.X_op == O_constant
- && offset_expr.X_add_number >= -0x8000
- && offset_expr.X_add_number < 0x8000)
- {
- macro_build (&offset_expr, ADDRESS_ADDI_INSN,
- "t,r,j", treg, sreg, BFD_RELOC_LO16);
- break;
- }
+ If $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $reg,16
+ daddiu $reg,<sym> (BFD_RELOC_HI16_S)
+ dsll $reg,16
+ daddiu $reg,<sym> (BFD_RELOC_LO16)
- if (mips_opts.at && (treg == breg))
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
{
- tempreg = AT;
- used_at = 1;
+ if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (ep->X_add_symbol, 1))
+ {
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+
+ if (*used_at == 0 && mips_opts.at)
+ {
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
+ macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
+ *used_at = 1;
+ }
+ else
+ {
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
+ }
+
+ if (mips_relax.sequence)
+ relax_end ();
}
else
{
- tempreg = treg;
- }
-
- if (offset_expr.X_op != O_symbol
- && offset_expr.X_op != O_constant)
- {
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
+ if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (ep->X_add_symbol, 1))
+ {
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+ macro_build_lui (ep, reg);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ if (mips_relax.sequence)
+ relax_end ();
}
+ }
+ else if (!mips_big_got)
+ {
+ expressionS ex;
- if (offset_expr.X_op == O_constant)
- load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
- else if (mips_pic == NO_PIC)
- {
- /* If this is a reference to a GP relative symbol, we want
- addiu $tempreg,$gp,<sym> (BFD_RELOC_GPREL16)
- Otherwise we want
- lui $tempreg,<sym> (BFD_RELOC_HI16_S)
- addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
- If we have a constant, we need two instructions anyhow,
- so we may as well always use the latter form.
-
- With 64bit address space and a usable $at we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- lui $at,<sym> (BFD_RELOC_HI16_S)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- daddiu $at,<sym> (BFD_RELOC_LO16)
- dsll32 $tempreg,0
- daddu $tempreg,$tempreg,$at
-
- If $at is already in use, we use a path which is suboptimal
- on superscalar processors.
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- dsll $tempreg,16
- daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
- dsll $tempreg,16
- daddiu $tempreg,<sym> (BFD_RELOC_LO16)
-
- For GP relative symbols in 64bit address space we can use
- the same sequence as in 32bit address space. */
- if (HAVE_64BIT_SYMBOLS)
- {
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, mips_gp_register, BFD_RELOC_GPREL16);
- relax_switch ();
- }
-
- if (used_at == 0 && mips_opts.at)
- {
- macro_build (&offset_expr, "lui", LUI_FMT,
- tempreg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (&offset_expr, "lui", LUI_FMT,
- AT, BFD_RELOC_HI16_S);
- macro_build (&offset_expr, "daddiu", "t,r,j",
- tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
- macro_build (&offset_expr, "daddiu", "t,r,j",
- AT, AT, BFD_RELOC_LO16);
- macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
- macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
- used_at = 1;
- }
- else
- {
- macro_build (&offset_expr, "lui", LUI_FMT,
- tempreg, BFD_RELOC_MIPS_HIGHEST);
- macro_build (&offset_expr, "daddiu", "t,r,j",
- tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
- macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
- macro_build (&offset_expr, "daddiu", "t,r,j",
- tempreg, tempreg, BFD_RELOC_HI16_S);
- macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
- macro_build (&offset_expr, "daddiu", "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- }
+ /* If this is a reference to an external symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ Otherwise we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
- if (mips_relax.sequence)
- relax_end ();
- }
- else
+ If we have NewABI, we want
+ lw $reg,<sym+cst>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ unless we're referencing a global symbol with a non-zero
+ offset, in which case cst must be added separately. */
+ if (HAVE_NEWABI)
+ {
+ if (ep->X_add_number)
{
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, mips_gp_register, BFD_RELOC_GPREL16);
- relax_switch ();
- }
- if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
- as_bad (_("Offset too large"));
- macro_build_lui (&offset_expr, tempreg);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- if (mips_relax.sequence)
- relax_end ();
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ ep->X_add_number = ex.X_add_number;
+ relax_switch ();
}
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (mips_relax.sequence)
+ relax_end ();
}
- else if (!mips_big_got && !HAVE_NEWABI)
+ else
{
- int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ relax_start (ep->X_add_symbol);
+ relax_switch ();
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
- /* If this is a reference to an external symbol, and there
- is no constant, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- or for lca or if tempreg is PIC_CALL_REG
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
- For a local symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ }
+ }
+ }
+ else if (mips_big_got)
+ {
+ expressionS ex;
- If we have a small constant, and this is a reference to
- an external symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $tempreg,$tempreg,<constant>
- For a local symbol, we want the same instruction
- sequence, but we output a BFD_RELOC_LO16 reloc on the
- addiu instruction.
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $reg,$reg,$gp
+ lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16)
- If we have a large constant, and this is a reference to
- an external symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- lui $at,<hiconstant>
- addiu $at,$at,<loconstant>
- addu $tempreg,$tempreg,$at
- For a local symbol, we want the same instruction
- sequence, but we output a BFD_RELOC_LO16 reloc on the
- addiu instruction.
- */
+ Otherwise, for a reference to a local symbol in old ABI, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
- if (offset_expr.X_add_number == 0)
+ In the NewABI, for local symbols, with or without offsets, we want:
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
+ */
+ if (HAVE_NEWABI)
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ reg, reg, mips_gp_register);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+ reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ else if (ex.X_add_number)
{
- if (mips_pic == SVR4_PIC
- && breg == 0
- && (call || tempreg == PIC_CALL_REG))
- lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
-
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- lw_reloc_type, mips_gp_register);
- if (breg != 0)
- {
- /* We're going to put in an addu instruction using
- tempreg, so we may as well insert the nop right
- now. */
- load_delay_nop ();
- }
- relax_switch ();
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
- load_delay_nop ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- relax_end ();
- /* FIXME: If breg == 0, and the next instruction uses
- $tempreg, then if this variant case is used an extra
- nop will be generated. */
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
}
- else if (offset_expr.X_add_number >= -0x8000
- && offset_expr.X_add_number < 0x8000)
+
+ ep->X_add_number = ex.X_add_number;
+ relax_switch ();
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_GOT_OFST);
+ relax_end ();
+ }
+ else
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ reg, reg, mips_gp_register);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+ reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+ relax_switch ();
+ if (reg_needs_delay (mips_gp_register))
{
- load_got_offset (tempreg, &offset_expr);
- load_delay_nop ();
- add_got_offset (tempreg, &offset_expr);
+ /* We need a nop before loading from $gp. This special
+ check is required because the lui which starts the main
+ instruction stream does not refer to $gp, and so will not
+ insert the nop which may be required. */
+ macro_build (NULL, "nop", "");
}
- else
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
+
+ if (ex.X_add_number != 0)
{
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number =
- SEXT_16BIT (offset_expr.X_add_number);
- load_got_offset (tempreg, &offset_expr);
- offset_expr.X_add_number = expr1.X_add_number;
- /* If we are going to add in a base register, and the
- target register and the base register are the same,
- then we are using AT as a temporary register. Since
- we want to load the constant into AT, we add our
- current AT (from the global offset table) and the
- register into the register now, and pretend we were
- not using a base register. */
- if (breg == treg)
- {
- load_delay_nop ();
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, AT, breg);
- breg = 0;
- tempreg = treg;
- }
- add_got_offset_hilo (tempreg, &offset_expr, AT);
- used_at = 1;
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
}
}
- else if (!mips_big_got && HAVE_NEWABI)
- {
- int add_breg_early = 0;
+ }
+ else
+ abort ();
- /* If this is a reference to an external, and there is no
- constant, or local symbol (*), with or without a
- constant, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
- or for lca or if tempreg is PIC_CALL_REG
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ if (!mips_opts.at && *used_at == 1)
+ as_bad (_("Macro used $at after \".set noat\""));
+}
- If we have a small constant, and this is a reference to
- an external symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
- addiu $tempreg,$tempreg,<constant>
+/* Move the contents of register SOURCE into register DEST. */
- If we have a large constant, and this is a reference to
- an external symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
- lui $at,<hiconstant>
- addiu $at,$at,<loconstant>
- addu $tempreg,$tempreg,$at
+static void
+move_register (int dest, int source)
+{
+ /* Prefer to use a 16-bit microMIPS instruction unless the previous
+ instruction specifically requires a 32-bit one. */
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, "move", "mp,mj", dest, source);
+ else
+ macro_build (NULL, HAVE_32BIT_GPRS ? "addu" : "daddu", "d,v,t",
+ dest, source, 0);
+}
- (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
- local symbols, even though it introduces an additional
- instruction. */
+/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
+ LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
+ The two alternatives are:
- if (offset_expr.X_add_number)
- {
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
+ Global symbol Local sybmol
+ ------------- ------------
+ lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
+ ... ...
+ addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ load_got_offset emits the first instruction and add_got_offset
+ emits the second for a 16-bit offset or add_got_offset_hilo emits
+ a sequence to add a 32-bit offset using a scratch register. */
- if (expr1.X_add_number >= -0x8000
- && expr1.X_add_number < 0x8000)
- {
- macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- }
- else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
- {
- /* If we are going to add in a base register, and the
- target register and the base register are the same,
- then we are using AT as a temporary register. Since
- we want to load the constant into AT, we add our
- current AT (from the global offset table) and the
- register into the register now, and pretend we were
- not using a base register. */
- if (breg != treg)
- dreg = tempreg;
- else
- {
- gas_assert (tempreg == AT);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, AT, breg);
- dreg = treg;
- add_breg_early = 1;
- }
+static void
+load_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- dreg, dreg, AT);
+ global = *local;
+ global.X_add_number = 0;
- used_at = 1;
- }
- else
- as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ relax_switch ();
+ macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ relax_end ();
+}
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
+static void
+add_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
- if (add_breg_early)
- {
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, tempreg, breg);
- breg = 0;
- tempreg = treg;
- }
- relax_end ();
- }
- else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_CALL16, mips_gp_register);
- relax_switch ();
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
- relax_end ();
- }
- else
- {
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
- }
- }
- else if (mips_big_got && !HAVE_NEWABI)
- {
- int gpdelay;
- int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
- int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
- int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
- /* This is the large GOT case. If this is a reference to an
- external symbol, and there is no constant, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- or for lca or if tempreg is PIC_CALL_REG
- lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
- addu $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
- For a local symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
+ dest, dest, BFD_RELOC_LO16);
+ relax_switch ();
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
+ relax_end ();
+}
- If we have a small constant, and this is a reference to
- an external symbol, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- nop
- addiu $tempreg,$tempreg,<constant>
- For a local symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+ expressionS global;
+ int hold_mips_optimize;
- If we have a large constant, and this is a reference to
- an external symbol, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- lui $at,<hiconstant>
- addiu $at,$at,<loconstant>
- addu $tempreg,$tempreg,$at
- For a local symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- lui $at,<hiconstant>
- addiu $at,$at,<loconstant> (BFD_RELOC_LO16)
- addu $tempreg,$tempreg,$at
- */
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
- relax_start (offset_expr.X_add_symbol);
- gpdelay = reg_needs_delay (mips_gp_register);
- if (expr1.X_add_number == 0 && breg == 0
- && (call || tempreg == PIC_CALL_REG))
- {
- lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
- lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
- }
- macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- tempreg, lw_reloc_type, tempreg);
- if (expr1.X_add_number == 0)
- {
- if (breg != 0)
- {
- /* We're going to put in an addu instruction using
- tempreg, so we may as well insert the nop right
- now. */
- load_delay_nop ();
- }
- }
- else if (expr1.X_add_number >= -0x8000
- && expr1.X_add_number < 0x8000)
- {
- load_delay_nop ();
- macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- }
- else
- {
- /* If we are going to add in a base register, and the
- target register and the base register are the same,
- then we are using AT as a temporary register. Since
- we want to load the constant into AT, we add our
- current AT (from the global offset table) and the
- register into the register now, and pretend we were
- not using a base register. */
- if (breg != treg)
- dreg = tempreg;
- else
- {
- gas_assert (tempreg == AT);
- load_delay_nop ();
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, AT, breg);
- dreg = treg;
- }
+ relax_start (local->X_add_symbol);
+ load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
+ relax_switch ();
+ /* Set mips_optimize around the lui instruction to avoid
+ inserting an unnecessary nop after the lw. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ macro_build_lui (&global, tmp);
+ mips_optimize = hold_mips_optimize;
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
+ relax_end ();
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
- used_at = 1;
- }
- offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
- relax_switch ();
+/* Emit a sequence of instructions to emulate a branch likely operation.
+ BR is an ordinary branch corresponding to one to be emulated. BRNEG
+ is its complementing branch with the original condition negated.
+ CALL is set if the original branch specified the link operation.
+ EP, FMT, SREG and TREG specify the usual macro_build() parameters.
- if (gpdelay)
- {
- /* This is needed because this instruction uses $gp, but
- the first instruction on the main stream does not. */
- macro_build (NULL, "nop", "");
- }
+ Code like this is produced in the noreorder mode:
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- local_reloc_type, mips_gp_register);
- if (expr1.X_add_number >= -0x8000
- && expr1.X_add_number < 0x8000)
- {
- load_delay_nop ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- /* FIXME: If add_number is 0, and there was no base
- register, the external symbol case ended with a load,
- so if the symbol turns out to not be external, and
- the next instruction uses tempreg, an unnecessary nop
- will be inserted. */
- }
- else
- {
- if (breg == treg)
- {
- /* We must add in the base register now, as in the
- external symbol case. */
- gas_assert (tempreg == AT);
- load_delay_nop ();
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, AT, breg);
- tempreg = treg;
- /* We set breg to 0 because we have arranged to add
- it in in both cases. */
- breg = 0;
- }
+ BRNEG <args>, 1f
+ nop
+ b <sym>
+ delay slot (executed only if branch taken)
+ 1:
- macro_build_lui (&expr1, AT);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- AT, AT, BFD_RELOC_LO16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, AT);
- used_at = 1;
- }
- relax_end ();
- }
- else if (mips_big_got && HAVE_NEWABI)
- {
- int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
- int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
- int add_breg_early = 0;
+ or, if CALL is set:
- /* This is the large GOT case. If this is a reference to an
- external symbol, and there is no constant, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- add $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- or for lca or if tempreg is PIC_CALL_REG
- lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
- add $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+ BRNEG <args>, 1f
+ nop
+ bal <sym>
+ delay slot (executed only if branch taken)
+ 1:
- If we have a small constant, and this is a reference to
- an external symbol, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- add $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- addi $tempreg,$tempreg,<constant>
+ In the reorder mode the delay slot would be filled with a nop anyway,
+ so code produced is simply:
- If we have a large constant, and this is a reference to
- an external symbol, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- lui $at,<hiconstant>
- addi $at,$at,<loconstant>
- add $tempreg,$tempreg,$at
+ BR <args>, <sym>
+ nop
- If we have NewABI, and we know it's a local symbol, we want
- lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
- addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
- otherwise we have to resort to GOT_HI16/GOT_LO16. */
+ This function is used when producing code for the microMIPS ASE that
+ does not implement branch likely instructions in hardware. */
- relax_start (offset_expr.X_add_symbol);
+static void
+macro_build_branch_likely (const char *br, const char *brneg,
+ int call, expressionS *ep, const char *fmt,
+ unsigned int sreg, unsigned int treg)
+{
+ int noreorder = mips_opts.noreorder;
+ expressionS expr1;
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
+ gas_assert (mips_opts.micromips);
+ start_noreorder ();
+ if (noreorder)
+ {
+ micromips_label_expr (&expr1);
+ macro_build (&expr1, brneg, fmt, sreg, treg);
+ macro_build (NULL, "nop", "");
+ macro_build (ep, call ? "bal" : "b", "p");
- if (expr1.X_add_number == 0 && breg == 0
- && (call || tempreg == PIC_CALL_REG))
- {
- lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
- lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
- }
- macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- tempreg, lw_reloc_type, tempreg);
+ /* Set to true so that append_insn adds a label. */
+ emit_branch_likely_macro = TRUE;
+ }
+ else
+ {
+ macro_build (ep, br, fmt, sreg, treg);
+ macro_build (NULL, "nop", "");
+ }
+ end_noreorder ();
+}
- if (expr1.X_add_number == 0)
- ;
- else if (expr1.X_add_number >= -0x8000
- && expr1.X_add_number < 0x8000)
- {
- macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, tempreg, BFD_RELOC_LO16);
- }
- else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
- {
- /* If we are going to add in a base register, and the
- target register and the base register are the same,
- then we are using AT as a temporary register. Since
- we want to load the constant into AT, we add our
- current AT (from the global offset table) and the
- register into the register now, and pretend we were
- not using a base register. */
- if (breg != treg)
- dreg = tempreg;
- else
- {
- gas_assert (tempreg == AT);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, AT, breg);
- dreg = treg;
- add_breg_early = 1;
- }
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+ the condition code tested. EP specifies the branch target. */
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+ const int call = 0;
+ const char *brneg;
+ const char *br;
- used_at = 1;
- }
- else
- as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+ switch (type)
+ {
+ case M_BC1FL:
+ br = "bc1f";
+ brneg = "bc1t";
+ break;
+ case M_BC1TL:
+ br = "bc1t";
+ brneg = "bc1f";
+ break;
+ case M_BC2FL:
+ br = "bc2f";
+ brneg = "bc2t";
+ break;
+ case M_BC2TL:
+ br = "bc2t";
+ brneg = "bc2f";
+ break;
+ default:
+ abort ();
+ }
+ macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
+}
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
- tempreg, BFD_RELOC_MIPS_GOT_OFST);
- if (add_breg_early)
- {
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- treg, tempreg, breg);
- breg = 0;
- tempreg = treg;
- }
- relax_end ();
- }
- else
- abort ();
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+ the register tested. EP specifies the branch target. */
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
- break;
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+ const char *brneg = NULL;
+ const char *br;
+ int call = 0;
- case M_MSGSND:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (treg << 16) | (0x01);
- macro_build (NULL, "c2", "C", temp);
- }
+ switch (type)
+ {
+ case M_BGEZ:
+ br = "bgez";
break;
-
- case M_MSGLD:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (0x02);
- macro_build (NULL, "c2", "C", temp);
- }
+ case M_BGEZL:
+ br = mips_opts.micromips ? "bgez" : "bgezl";
+ brneg = "bltz";
break;
-
- case M_MSGLD_T:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (treg << 16) | (0x02);
- macro_build (NULL, "c2", "C", temp);
- }
+ case M_BGEZALL:
+ gas_assert (mips_opts.micromips);
+ br = mips_opts.insn32 ? "bgezal" : "bgezals";
+ brneg = "bltz";
+ call = 1;
break;
-
- case M_MSGWAIT:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", 3);
+ case M_BGTZ:
+ br = "bgtz";
break;
-
- case M_MSGWAIT_T:
- gas_assert (!mips_opts.micromips);
- {
- unsigned long temp = (treg << 16) | 0x03;
- macro_build (NULL, "c2", "C", temp);
- }
+ case M_BGTZL:
+ br = mips_opts.micromips ? "bgtz" : "bgtzl";
+ brneg = "blez";
break;
-
- case M_J_A:
- /* The j instruction may not be used in PIC code, since it
- requires an absolute address. We convert it to a b
- instruction. */
- if (mips_pic == NO_PIC)
- macro_build (&offset_expr, "j", "a");
- else
- macro_build (&offset_expr, "b", "p");
+ case M_BLEZ:
+ br = "blez";
break;
-
- /* The jal instructions must be handled as macros because when
- generating PIC code they expand to multi-instruction
- sequences. Normally they are simple instructions. */
- case M_JALS_1:
- dreg = RA;
- /* Fall through. */
- case M_JALS_2:
+ case M_BLEZL:
+ br = mips_opts.micromips ? "blez" : "blezl";
+ brneg = "bgtz";
+ break;
+ case M_BLTZ:
+ br = "bltz";
+ break;
+ case M_BLTZL:
+ br = mips_opts.micromips ? "bltz" : "bltzl";
+ brneg = "bgez";
+ break;
+ case M_BLTZALL:
gas_assert (mips_opts.micromips);
- if (mips_opts.insn32)
- {
- as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
- break;
- }
- jals = 1;
- goto jal;
- case M_JAL_1:
- dreg = RA;
- /* Fall through. */
- case M_JAL_2:
- jal:
- if (mips_pic == NO_PIC)
- {
- s = jals ? "jalrs" : "jalr";
- if (mips_opts.micromips
- && !mips_opts.insn32
- && dreg == RA
- && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
- macro_build (NULL, s, "mj", sreg);
- else
- macro_build (NULL, s, JALR_FMT, dreg, sreg);
- }
- else
- {
- int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
- && mips_cprestore_offset >= 0);
+ br = mips_opts.insn32 ? "bltzal" : "bltzals";
+ brneg = "bgez";
+ call = 1;
+ break;
+ default:
+ abort ();
+ }
+ if (mips_opts.micromips && brneg)
+ macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
+ else
+ macro_build (ep, br, "s,p", sreg);
+}
- if (sreg != PIC_CALL_REG)
- as_warn (_("MIPS PIC call to register other than $25"));
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+ TREG as the registers tested. EP specifies the branch target. */
- s = ((mips_opts.micromips
- && !mips_opts.insn32
- && (!mips_opts.noreorder || cprestore))
- ? "jalrs" : "jalr");
- if (mips_opts.micromips
- && !mips_opts.insn32
- && dreg == RA
- && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
- macro_build (NULL, s, "mj", sreg);
- else
- macro_build (NULL, s, JALR_FMT, dreg, sreg);
- if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
- {
- if (mips_cprestore_offset < 0)
- as_warn (_("No .cprestore pseudo-op used in PIC code"));
- else
- {
- if (!mips_frame_reg_valid)
- {
- as_warn (_("No .frame pseudo-op used in PIC code"));
- /* Quiet this warning. */
- mips_frame_reg_valid = 1;
- }
- if (!mips_cprestore_valid)
- {
- as_warn (_("No .cprestore pseudo-op used in PIC code"));
- /* Quiet this warning. */
- mips_cprestore_valid = 1;
- }
- if (mips_opts.noreorder)
- macro_build (NULL, "nop", "");
- expr1.X_add_number = mips_cprestore_offset;
- macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
- mips_gp_register,
- mips_frame_reg,
- HAVE_64BIT_ADDRESSES);
- }
- }
- }
+static void
+macro_build_branch_rsrt (int type, expressionS *ep,
+ unsigned int sreg, unsigned int treg)
+{
+ const char *brneg = NULL;
+ const int call = 0;
+ const char *br;
+ switch (type)
+ {
+ case M_BEQ:
+ case M_BEQ_I:
+ br = "beq";
break;
+ case M_BEQL:
+ case M_BEQL_I:
+ br = mips_opts.micromips ? "beq" : "beql";
+ brneg = "bne";
+ break;
+ case M_BNE:
+ case M_BNE_I:
+ br = "bne";
+ break;
+ case M_BNEL:
+ case M_BNEL_I:
+ br = mips_opts.micromips ? "bne" : "bnel";
+ brneg = "beq";
+ break;
+ default:
+ abort ();
+ }
+ if (mips_opts.micromips && brneg)
+ macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
+ else
+ macro_build (ep, br, "s,t,p", sreg, treg);
+}
- case M_JALS_A:
- gas_assert (mips_opts.micromips);
- if (mips_opts.insn32)
- {
- as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
- break;
- }
- jals = 1;
- /* Fall through. */
- case M_JAL_A:
- if (mips_pic == NO_PIC)
- macro_build (&offset_expr, jals ? "jals" : "jal", "a");
- else if (mips_pic == SVR4_PIC)
- {
- /* If this is a reference to an external symbol, and we are
- using a small GOT, we want
- lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
- nop
- jalr $ra,$25
- nop
- lw $gp,cprestore($sp)
- The cprestore value is set using the .cprestore
- pseudo-op. If we are using a big GOT, we want
- lui $25,<sym> (BFD_RELOC_MIPS_CALL_HI16)
- addu $25,$25,$gp
- lw $25,<sym>($25) (BFD_RELOC_MIPS_CALL_LO16)
- nop
- jalr $ra,$25
- nop
- lw $gp,cprestore($sp)
- If the symbol is not external, we want
- lw $25,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $25,$25,<sym> (BFD_RELOC_LO16)
- jalr $ra,$25
- nop
- lw $gp,cprestore($sp)
+/* Return the high part that should be loaded in order to make the low
+ part of VALUE accessible using an offset of OFFBITS bits. */
- For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
- sequences above, minus nops, unless the symbol is local,
- which enables us to use GOT_PAGE/GOT_OFST (big got) or
- GOT_DISP. */
- if (HAVE_NEWABI)
- {
- if (!mips_big_got)
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
- mips_gp_register);
- relax_switch ();
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
- mips_gp_register);
- relax_end ();
- }
- else
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
- BFD_RELOC_MIPS_CALL_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
- PIC_CALL_REG, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
- PIC_CALL_REG);
- relax_switch ();
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
- mips_gp_register);
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- PIC_CALL_REG, PIC_CALL_REG,
- BFD_RELOC_MIPS_GOT_OFST);
- relax_end ();
- }
+static offsetT
+offset_high_part (offsetT value, unsigned int offbits)
+{
+ offsetT bias;
+ addressT low_mask;
+
+ if (offbits == 0)
+ return value;
+ bias = 1 << (offbits - 1);
+ low_mask = bias * 2 - 1;
+ return (value + bias) & ~low_mask;
+}
- macro_build_jalr (&offset_expr, 0);
- }
- else
- {
- relax_start (offset_expr.X_add_symbol);
- if (!mips_big_got)
- {
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
- mips_gp_register);
- load_delay_nop ();
- relax_switch ();
- }
- else
- {
- int gpdelay;
+/* Return true if the value stored in offset_expr and offset_reloc
+ fits into a signed offset of OFFBITS bits. RANGE is the maximum
+ amount that the caller wants to add without inducing overflow
+ and ALIGN is the known alignment of the value in bytes. */
- gpdelay = reg_needs_delay (mips_gp_register);
- macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
- BFD_RELOC_MIPS_CALL_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
- PIC_CALL_REG, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
- PIC_CALL_REG);
- load_delay_nop ();
- relax_switch ();
- if (gpdelay)
- macro_build (NULL, "nop", "");
- }
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
- mips_gp_register);
- load_delay_nop ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
- relax_end ();
- macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
+static bfd_boolean
+small_offset_p (unsigned int range, unsigned int align, unsigned int offbits)
+{
+ if (offbits == 16)
+ {
+ /* Accept any relocation operator if overflow isn't a concern. */
+ if (range < align && *offset_reloc != BFD_RELOC_UNUSED)
+ return TRUE;
- if (mips_cprestore_offset < 0)
- as_warn (_("No .cprestore pseudo-op used in PIC code"));
- else
- {
- if (!mips_frame_reg_valid)
- {
- as_warn (_("No .frame pseudo-op used in PIC code"));
- /* Quiet this warning. */
- mips_frame_reg_valid = 1;
- }
- if (!mips_cprestore_valid)
- {
- as_warn (_("No .cprestore pseudo-op used in PIC code"));
- /* Quiet this warning. */
- mips_cprestore_valid = 1;
- }
- if (mips_opts.noreorder)
- macro_build (NULL, "nop", "");
- expr1.X_add_number = mips_cprestore_offset;
- macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
- mips_gp_register,
- mips_frame_reg,
- HAVE_64BIT_ADDRESSES);
- }
- }
- }
- else if (mips_pic == VXWORKS_PIC)
- as_bad (_("Non-PIC jump used in PIC library"));
- else
- abort ();
+ /* These relocations are guaranteed not to overflow in correct links. */
+ if (*offset_reloc == BFD_RELOC_MIPS_LITERAL
+ || gprel16_reloc_p (*offset_reloc))
+ return TRUE;
+ }
+ if (offset_expr.X_op == O_constant
+ && offset_high_part (offset_expr.X_add_number, offbits) == 0
+ && offset_high_part (offset_expr.X_add_number + range, offbits) == 0)
+ return TRUE;
+ return FALSE;
+}
- break;
+/*
+ * Build macros
+ * This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammar approach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * 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
+ * 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
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (struct mips_cl_insn *ip, char *str)
+{
+ unsigned int treg, sreg, dreg, breg;
+ unsigned int tempreg;
+ int mask;
+ int used_at = 0;
+ expressionS label_expr;
+ expressionS expr1;
+ expressionS *ep;
+ const char *s;
+ const char *s2;
+ const char *fmt;
+ int likely = 0;
+ int coproc = 0;
+ int offbits = 16;
+ int call = 0;
+ int jals = 0;
+ int dbl = 0;
+ int imm = 0;
+ int ust = 0;
+ int lp = 0;
+ bfd_boolean large_offset;
+ int off;
+ int hold_mips_optimize;
+ unsigned int align;
- case M_LBUE_AB:
- ab = 1;
- case M_LBUE_OB:
- s = "lbue";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LHUE_AB:
- ab = 1;
- case M_LHUE_OB:
- s = "lhue";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LBE_AB:
- ab = 1;
- case M_LBE_OB:
- s = "lbe";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LHE_AB:
- ab = 1;
- case M_LHE_OB:
- s = "lhe";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LLE_AB:
- ab = 1;
- case M_LLE_OB:
- s = "lle";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LWE_AB:
- ab = 1;
- case M_LWE_OB:
- s = "lwe";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LWLE_AB:
- ab = 1;
- case M_LWLE_OB:
- s = "lwle";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_LWRE_AB:
- ab = 1;
- case M_LWRE_OB:
- s = "lwre";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SBE_AB:
- ab = 1;
- case M_SBE_OB:
- s = "sbe";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SCE_AB:
- ab = 1;
- case M_SCE_OB:
- s = "sce";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SHE_AB:
- ab = 1;
- case M_SHE_OB:
- s = "she";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SWE_AB:
- ab = 1;
- case M_SWE_OB:
- s = "swe";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SWLE_AB:
- ab = 1;
- case M_SWLE_OB:
- s = "swle";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SWRE_AB:
- ab = 1;
- case M_SWRE_OB:
- s = "swre";
- fmt = "t,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_ACLR_AB:
- ab = 1;
- case M_ACLR_OB:
- s = "aclr";
- treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
- fmt = "\\,~(b)";
- offbits = 12;
- goto ld_st;
- case M_ASET_AB:
- ab = 1;
- case M_ASET_OB:
- s = "aset";
- treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
- fmt = "\\,~(b)";
- offbits = 12;
- goto ld_st;
- case M_LB_AB:
- ab = 1;
- s = "lb";
- fmt = "t,o(b)";
- goto ld;
- case M_LBU_AB:
- ab = 1;
- s = "lbu";
- fmt = "t,o(b)";
- goto ld;
- case M_LH_AB:
- ab = 1;
- s = "lh";
- fmt = "t,o(b)";
- goto ld;
- case M_LHU_AB:
- ab = 1;
- s = "lhu";
- fmt = "t,o(b)";
- goto ld;
- case M_LW_AB:
- ab = 1;
- s = "lw";
- fmt = "t,o(b)";
- goto ld;
- case M_LWC0_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "lwc0";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWC1_AB:
- ab = 1;
- s = "lwc1";
- fmt = "T,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWC2_AB:
- ab = 1;
- case M_LWC2_OB:
- s = "lwc2";
- fmt = COP12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWC3_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "lwc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LWL_AB:
- ab = 1;
- case M_LWL_OB:
- s = "lwl";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_LWR_AB:
- ab = 1;
- case M_LWR_OB:
- s = "lwr";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_LDC1_AB:
- ab = 1;
- s = "ldc1";
- fmt = "T,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LDC2_AB:
- ab = 1;
- case M_LDC2_OB:
- s = "ldc2";
- fmt = COP12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LQC2_AB:
- ab = 1;
- s = "lqc2";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LDC3_AB:
- ab = 1;
- s = "ldc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_LDL_AB:
- ab = 1;
- case M_LDL_OB:
- s = "ldl";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_LDR_AB:
- ab = 1;
- case M_LDR_OB:
- s = "ldr";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_LL_AB:
- ab = 1;
- case M_LL_OB:
- s = "ll";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld;
- case M_LLD_AB:
- ab = 1;
- case M_LLD_OB:
- s = "lld";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld;
- case M_LWU_AB:
- ab = 1;
- case M_LWU_OB:
- s = "lwu";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld;
- case M_LWP_AB:
- ab = 1;
- case M_LWP_OB:
- gas_assert (mips_opts.micromips);
- s = "lwp";
- fmt = "t,~(b)";
- offbits = 12;
- lp = 1;
- goto ld;
- case M_LDP_AB:
- ab = 1;
- case M_LDP_OB:
- gas_assert (mips_opts.micromips);
- s = "ldp";
- fmt = "t,~(b)";
- offbits = 12;
- lp = 1;
- goto ld;
- case M_LWM_AB:
- ab = 1;
- case M_LWM_OB:
- gas_assert (mips_opts.micromips);
- s = "lwm";
- fmt = "n,~(b)";
- offbits = 12;
- goto ld_st;
- case M_LDM_AB:
- ab = 1;
- case M_LDM_OB:
- gas_assert (mips_opts.micromips);
- s = "ldm";
- fmt = "n,~(b)";
- offbits = 12;
- goto ld_st;
+ gas_assert (! mips_opts.mips16);
- ld:
- /* We don't want to use $0 as tempreg. */
- if (breg == treg + lp || treg + lp == ZERO)
- goto ld_st;
- else
- tempreg = treg + lp;
- goto ld_noat;
+ treg = EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
+ dreg = EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
+ sreg = breg = EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+ mask = ip->insn_mo->mask;
- case M_SB_AB:
- ab = 1;
- s = "sb";
- fmt = "t,o(b)";
- goto ld_st;
- case M_SH_AB:
- ab = 1;
- s = "sh";
- fmt = "t,o(b)";
- goto ld_st;
- case M_SW_AB:
- ab = 1;
- s = "sw";
- fmt = "t,o(b)";
- goto ld_st;
- case M_SWC0_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "swc0";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWC1_AB:
- ab = 1;
- s = "swc1";
- fmt = "T,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWC2_AB:
- ab = 1;
- case M_SWC2_OB:
- s = "swc2";
- fmt = COP12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWC3_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "swc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SWL_AB:
- ab = 1;
- case M_SWL_OB:
- s = "swl";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_SWR_AB:
- ab = 1;
- case M_SWR_OB:
- s = "swr";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_SC_AB:
- ab = 1;
- case M_SC_OB:
- s = "sc";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_SCD_AB:
- ab = 1;
- case M_SCD_OB:
- s = "scd";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_CACHE_AB:
- ab = 1;
- case M_CACHE_OB:
- s = "cache";
- fmt = mips_opts.micromips ? "k,~(b)" : "k,o(b)";
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_CACHEE_AB:
- ab = 1;
- case M_CACHEE_OB:
- s = "cachee";
- fmt = "k,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_PREF_AB:
- ab = 1;
- case M_PREF_OB:
- s = "pref";
- fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_PREFE_AB:
- ab = 1;
- case M_PREFE_OB:
- s = "prefe";
- fmt = "k,+j(b)";
- offbits = 9;
- goto ld_st;
- case M_SDC1_AB:
- ab = 1;
- s = "sdc1";
- fmt = "T,o(b)";
- coproc = 1;
- /* Itbl support may require additional care here. */
- goto ld_st;
- case M_SDC2_AB:
- ab = 1;
- case M_SDC2_OB:
- s = "sdc2";
- fmt = COP12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SQC2_AB:
- ab = 1;
- s = "sqc2";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SDC3_AB:
- ab = 1;
- gas_assert (!mips_opts.micromips);
- s = "sdc3";
- fmt = "E,o(b)";
- /* Itbl support may require additional care here. */
- coproc = 1;
- goto ld_st;
- case M_SDL_AB:
- ab = 1;
- case M_SDL_OB:
- s = "sdl";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_SDR_AB:
- ab = 1;
- case M_SDR_OB:
- s = "sdr";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- goto ld_st;
- case M_SWP_AB:
- ab = 1;
- case M_SWP_OB:
- gas_assert (mips_opts.micromips);
- s = "swp";
- fmt = "t,~(b)";
- offbits = 12;
- goto ld_st;
- case M_SDP_AB:
- ab = 1;
- case M_SDP_OB:
- gas_assert (mips_opts.micromips);
- s = "sdp";
- fmt = "t,~(b)";
- offbits = 12;
- goto ld_st;
- case M_SWM_AB:
- ab = 1;
- case M_SWM_OB:
- gas_assert (mips_opts.micromips);
- s = "swm";
- fmt = "n,~(b)";
- offbits = 12;
- goto ld_st;
- case M_SDM_AB:
- ab = 1;
- case M_SDM_OB:
- gas_assert (mips_opts.micromips);
- s = "sdm";
- fmt = "n,~(b)";
- offbits = 12;
+ label_expr.X_op = O_constant;
+ label_expr.X_op_symbol = NULL;
+ label_expr.X_add_symbol = NULL;
+ label_expr.X_add_number = 0;
- ld_st:
- tempreg = AT;
- used_at = 1;
- ld_noat:
- if (offset_expr.X_op != O_constant
- && offset_expr.X_op != O_symbol)
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+ align = 1;
+
+ switch (mask)
+ {
+ case M_DABS:
+ dbl = 1;
+ case M_ABS:
+ /* bgez $a0,1f
+ move v0,$a0
+ sub v0,$zero,$a0
+ 1:
+ */
+
+ start_noreorder ();
+
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bgez", "s,p", sreg);
+ if (dreg == sreg)
+ macro_build (NULL, "nop", "");
+ else
+ move_register (dreg, sreg);
+ macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", dreg, 0, sreg);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+
+ end_noreorder ();
+ break;
+
+ case M_ADD_I:
+ s = "addi";
+ s2 = "add";
+ goto do_addi;
+ case M_ADDU_I:
+ s = "addiu";
+ s2 = "addu";
+ goto do_addi;
+ case M_DADD_I:
+ dbl = 1;
+ s = "daddi";
+ s2 = "dadd";
+ if (!mips_opts.micromips)
+ goto do_addi;
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x200
+ && imm_expr.X_add_number < 0x200)
{
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
+ macro_build (NULL, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
+ break;
}
-
- if (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ goto do_addi_i;
+ case M_DADDU_I:
+ dbl = 1;
+ s = "daddiu";
+ s2 = "daddu";
+ do_addi:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
{
- char value [32];
-
- sprintf_vma (value, offset_expr.X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
+ break;
}
+ do_addi_i:
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+ break;
- /* A constant expression in PIC code can be handled just as it
- is in non PIC code. */
- if (offset_expr.X_op == O_constant)
+ case M_AND_I:
+ s = "andi";
+ s2 = "and";
+ goto do_bit;
+ case M_OR_I:
+ s = "ori";
+ s2 = "or";
+ goto do_bit;
+ case M_NOR_I:
+ s = "";
+ s2 = "nor";
+ goto do_bit;
+ case M_XOR_I:
+ s = "xori";
+ s2 = "xor";
+ do_bit:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
{
- int hipart = 0;
-
- expr1.X_add_number = offset_expr.X_add_number;
- normalize_address_expr (&expr1);
- if ((offbits == 0 || offbits == 16)
- && !IS_SEXT_16BIT_NUM (expr1.X_add_number))
- {
- expr1.X_add_number = ((expr1.X_add_number + 0x8000)
- & ~(bfd_vma) 0xffff);
- hipart = 1;
- }
- else if (offbits == 12 && !IS_SEXT_12BIT_NUM (expr1.X_add_number))
- {
- expr1.X_add_number = ((expr1.X_add_number + 0x800)
- & ~(bfd_vma) 0xfff);
- hipart = 1;
- }
- else if (offbits == 9 && !IS_SEXT_9BIT_NUM (expr1.X_add_number))
- {
- expr1.X_add_number = ((expr1.X_add_number + 0x100)
- & ~(bfd_vma) 0x1ff);
- hipart = 1;
- }
- if (hipart)
- {
- load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- breg = tempreg;
- }
- if (offbits == 0)
- {
- if (offset_expr.X_add_number == 0)
- tempreg = breg;
- else
- macro_build (&offset_expr, ADDRESS_ADDI_INSN,
- "t,r,j", tempreg, breg, BFD_RELOC_LO16);
- macro_build (NULL, s, fmt, treg, tempreg);
- }
- else if (offbits == 16)
- macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, breg);
+ if (mask != M_NOR_I)
+ macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
else
- macro_build (NULL, s, fmt,
- treg, (unsigned long) offset_expr.X_add_number, breg);
- }
- 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
- register. In the case of "A(b)" addresses, we first load
- absolute address "A" into the register and then add base
- register "b". In the case of "o(b)" addresses, we simply
- need to add 16-bit offset "o" to base register "b", and
- offset_reloc already contains the relocations associated
- with "o". */
- if (ab)
{
- load_address (tempreg, &offset_expr, &used_at);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
+ macro_build (&imm_expr, "ori", "t,r,i",
+ treg, sreg, BFD_RELOC_LO16);
+ macro_build (NULL, "nor", "d,v,t", treg, treg, 0);
}
- else
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
- tempreg, breg, -1,
- offset_reloc[0], offset_reloc[1], offset_reloc[2]);
- expr1.X_add_number = 0;
- if (offbits == 0)
- macro_build (NULL, s, fmt, treg, tempreg);
- else
- macro_build (NULL, s, fmt,
- treg, (unsigned long) expr1.X_add_number, tempreg);
+ break;
}
- else if (mips_pic == NO_PIC)
- {
- /* If this is a reference to a GP relative symbol, and there
- is no base register, we want
- <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
- Otherwise, if there is no base register, we want
- lui $tempreg,<sym> (BFD_RELOC_HI16_S)
- <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
- If we have a constant, we need two instructions anyhow,
- so we always use the latter form.
- If we have a base register, and this is a reference to a
- GP relative symbol, we want
- addu $tempreg,$breg,$gp
- <op> $treg,<sym>($tempreg) (BFD_RELOC_GPREL16)
- Otherwise we want
- lui $tempreg,<sym> (BFD_RELOC_HI16_S)
- addu $tempreg,$tempreg,$breg
- <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
- With a constant we always use the latter case.
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
+ break;
- With 64bit address space and no base register and $at usable,
- we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- lui $at,<sym> (BFD_RELOC_HI16_S)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- dsll32 $tempreg,0
- daddu $tempreg,$at
- <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
- If we have a base register, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- lui $at,<sym> (BFD_RELOC_HI16_S)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- daddu $at,$breg
- dsll32 $tempreg,0
- daddu $tempreg,$at
- <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ case M_BALIGN:
+ switch (imm_expr.X_add_number)
+ {
+ case 0:
+ macro_build (NULL, "nop", "");
+ break;
+ case 2:
+ macro_build (NULL, "packrl.ph", "d,s,t", treg, treg, sreg);
+ break;
+ case 1:
+ case 3:
+ macro_build (NULL, "balign", "t,s,2", treg, sreg,
+ (int) imm_expr.X_add_number);
+ break;
+ default:
+ as_bad (_("BALIGN immediate not 0, 1, 2 or 3 (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ break;
+ }
+ break;
- Without $at we can't generate the optimal path for superscalar
- processors here since this would require two temporary registers.
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- dsll $tempreg,16
- daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
- dsll $tempreg,16
- <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
- If we have a base register, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
- daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
- dsll $tempreg,16
- daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
- dsll $tempreg,16
- daddu $tempreg,$tempreg,$breg
- <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ case M_BC1FL:
+ case M_BC1TL:
+ case M_BC2FL:
+ case M_BC2TL:
+ gas_assert (mips_opts.micromips);
+ macro_build_branch_ccl (mask, &offset_expr,
+ EXTRACT_OPERAND (1, BCC, *ip));
+ break;
- For GP relative symbols in 64bit address space we can use
- the same sequence as in 32bit address space. */
- if (HAVE_64BIT_SYMBOLS)
- {
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- if (breg == 0)
- {
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_GPREL16, mips_gp_register);
- }
- else
- {
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, breg, mips_gp_register);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_GPREL16, tempreg);
- }
- relax_switch ();
- }
+ case M_BEQ_I:
+ case M_BEQL_I:
+ case M_BNE_I:
+ case M_BNEL_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ treg = 0;
+ else
+ {
+ treg = AT;
+ used_at = 1;
+ load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
+ }
+ /* Fall through. */
+ case M_BEQL:
+ case M_BNEL:
+ macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
+ break;
- if (used_at == 0 && mips_opts.at)
- {
- macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
- BFD_RELOC_MIPS_HIGHEST);
- macro_build (&offset_expr, "lui", LUI_FMT, AT,
- BFD_RELOC_HI16_S);
- macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
- tempreg, BFD_RELOC_MIPS_HIGHER);
- if (breg != 0)
- macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
- macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
- macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
- macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
- tempreg);
- used_at = 1;
- }
- else
- {
- macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
- BFD_RELOC_MIPS_HIGHEST);
- macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
- tempreg, BFD_RELOC_MIPS_HIGHER);
- macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
- macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
- tempreg, BFD_RELOC_HI16_S);
- macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
- if (breg != 0)
- macro_build (NULL, "daddu", "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_LO16, tempreg);
- }
+ case M_BGEL:
+ likely = 1;
+ case M_BGE:
+ if (treg == 0)
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, sreg);
+ else if (sreg == 0)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, treg);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- if (mips_relax.sequence)
- relax_end ();
- break;
- }
+ case M_BGEZL:
+ case M_BGEZALL:
+ case M_BGTZL:
+ case M_BLEZL:
+ case M_BLTZL:
+ case M_BLTZALL:
+ macro_build_branch_rs (mask, &offset_expr, sreg);
+ break;
- if (breg == 0)
- {
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
- mips_gp_register);
- relax_switch ();
- }
- macro_build_lui (&offset_expr, tempreg);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_LO16, tempreg);
- if (mips_relax.sequence)
- relax_end ();
- }
+ case M_BGTL_I:
+ likely = 1;
+ case M_BGT_I:
+ /* Check for > max integer. */
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= GPR_SMAX)
+ {
+ do_false:
+ /* Result is always false. */
+ if (! likely)
+ macro_build (NULL, "nop", "");
else
- {
- if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
- {
- relax_start (offset_expr.X_add_symbol);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, breg, mips_gp_register);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_GPREL16, tempreg);
- relax_switch ();
- }
- macro_build_lui (&offset_expr, tempreg);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_LO16, tempreg);
- if (mips_relax.sequence)
- relax_end ();
- }
+ macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
+ break;
}
- else if (!mips_big_got)
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BGE_I:
+ case M_BGEL_I:
+ if (mask == M_BGEL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
{
- int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
+ &offset_expr, sreg);
+ break;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ {
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
+ &offset_expr, sreg);
+ break;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number <= GPR_SMIN)
+ {
+ do_true:
+ /* result is always true */
+ as_warn (_("Branch %s is always true"), ip->insn_mo->name);
+ macro_build (&offset_expr, "b", "p");
+ break;
+ }
+ used_at = 1;
+ set_at (sreg, 0);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ break;
- /* If this is a reference to an external symbol, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- <op> $treg,0($tempreg)
- Otherwise we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
- <op> $treg,0($tempreg)
+ case M_BGEUL:
+ likely = 1;
+ case M_BGEU:
+ if (treg == 0)
+ goto do_true;
+ else if (sreg == 0)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, ZERO, treg);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- For NewABI, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
- <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
+ case M_BGTUL_I:
+ likely = 1;
+ case M_BGTU_I:
+ if (sreg == 0
+ || (HAVE_32BIT_GPRS
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == -1))
+ goto do_false;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BGEU_I:
+ case M_BGEUL_I:
+ if (mask == M_BGEUL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ goto do_true;
+ else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, sreg, ZERO);
+ else
+ {
+ used_at = 1;
+ set_at (sreg, 1);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- If there is a base register, we add it to $tempreg before
- the <op>. If there is a constant, we stick it in the
- <op> instruction. We don't handle constants larger than
- 16 bits, because we have no way to load the upper 16 bits
- (actually, we could handle them for the subset of cases
- in which we are not using $at). */
- gas_assert (offset_expr.X_op == O_symbol);
- if (HAVE_NEWABI)
- {
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_MIPS_GOT_OFST, tempreg);
- break;
- }
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
- if (expr1.X_add_number < -0x8000
- || expr1.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- lw_reloc_type, mips_gp_register);
- load_delay_nop ();
- relax_start (offset_expr.X_add_symbol);
- relax_switch ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
- tempreg, BFD_RELOC_LO16);
- relax_end ();
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ case M_BGTL:
+ likely = 1;
+ case M_BGT:
+ if (treg == 0)
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, sreg);
+ else if (sreg == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, treg);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
}
- else if (mips_big_got && !HAVE_NEWABI)
+ break;
+
+ case M_BGTUL:
+ likely = 1;
+ case M_BGTU:
+ if (treg == 0)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, sreg, ZERO);
+ else if (sreg == 0)
+ goto do_false;
+ else
{
- int gpdelay;
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- /* If this is a reference to an external symbol, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- <op> $treg,0($tempreg)
- Otherwise we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
- <op> $treg,0($tempreg)
- If there is a base register, we add it to $tempreg before
- the <op>. If there is a constant, we stick it in the
- <op> instruction. We don't handle constants larger than
- 16 bits, because we have no way to load the upper 16 bits
- (actually, we could handle them for the subset of cases
- in which we are not using $at). */
- gas_assert (offset_expr.X_op == O_symbol);
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
- if (expr1.X_add_number < -0x8000
- || expr1.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- gpdelay = reg_needs_delay (mips_gp_register);
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
- BFD_RELOC_MIPS_GOT_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
- mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_LO16, tempreg);
- relax_switch ();
- if (gpdelay)
- macro_build (NULL, "nop", "");
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- load_delay_nop ();
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
- tempreg, BFD_RELOC_LO16);
- relax_end ();
+ case M_BLEL:
+ likely = 1;
+ case M_BLE:
+ if (treg == 0)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
+ else if (sreg == 0)
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, treg);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, treg, sreg);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ case M_BLEL_I:
+ likely = 1;
+ case M_BLE_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number >= GPR_SMAX)
+ goto do_true;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BLT_I:
+ case M_BLTL_I:
+ if (mask == M_BLTL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
+ else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, sreg);
+ else
+ {
+ used_at = 1;
+ set_at (sreg, 0);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
}
- else if (mips_big_got && HAVE_NEWABI)
+ break;
+
+ case M_BLEUL:
+ likely = 1;
+ case M_BLEU:
+ if (treg == 0)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, sreg, ZERO);
+ else if (sreg == 0)
+ goto do_true;
+ else
{
- /* If this is a reference to an external symbol, we want
- lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- add $tempreg,$tempreg,$gp
- lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
- <op> $treg,<ofst>($tempreg)
- Otherwise, for local symbols, we want:
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
- <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST) */
- gas_assert (offset_expr.X_op == O_symbol);
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
- if (expr1.X_add_number < -0x8000
- || expr1.X_add_number >= 0x8000)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
- BFD_RELOC_MIPS_GOT_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
- mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_LO16, tempreg);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, treg, sreg);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
- BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- macro_build (&offset_expr, s, fmt, treg,
- BFD_RELOC_MIPS_GOT_OFST, tempreg);
- relax_end ();
+ case M_BLEUL_I:
+ likely = 1;
+ case M_BLEU_I:
+ if (sreg == 0
+ || (HAVE_32BIT_GPRS
+ && imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == -1))
+ goto do_true;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BLTU_I:
+ case M_BLTUL_I:
+ if (mask == M_BLTUL_I)
+ likely = 1;
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ goto do_false;
+ else if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, sreg, ZERO);
+ else
+ {
+ used_at = 1;
+ set_at (sreg, 1);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
}
+ break;
+
+ case M_BLTL:
+ likely = 1;
+ case M_BLT:
+ if (treg == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, sreg);
+ else if (sreg == 0)
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, treg);
else
- abort ();
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, sreg, treg);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLTUL:
+ likely = 1;
+ case M_BLTU:
+ if (treg == 0)
+ goto do_false;
+ else if (sreg == 0)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, ZERO, treg);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, sreg, treg);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_DEXT:
+ {
+ /* Use unsigned arithmetic. */
+ addressT pos;
+ addressT size;
+
+ if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+ {
+ as_bad (_("Unsupported large constant"));
+ pos = size = 1;
+ }
+ else
+ {
+ pos = imm_expr.X_add_number;
+ size = imm2_expr.X_add_number;
+ }
+
+ if (pos > 63)
+ {
+ report_bad_range (ip, 3, pos, 0, 63, FALSE);
+ pos = 1;
+ }
+ if (size == 0 || size > 64 || (pos + size - 1) > 63)
+ {
+ report_bad_field (pos, size);
+ size = 1;
+ }
+ if (size <= 32 && pos < 32)
+ {
+ s = "dext";
+ fmt = "t,r,+A,+C";
+ }
+ else if (size <= 32)
+ {
+ s = "dextu";
+ fmt = "t,r,+E,+H";
+ }
+ else
+ {
+ s = "dextm";
+ fmt = "t,r,+A,+G";
+ }
+ macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
+ (int) (size - 1));
+ }
break;
- case M_JRADDIUSP:
- gas_assert (mips_opts.micromips);
- gas_assert (mips_opts.insn32);
- start_noreorder ();
- macro_build (NULL, "jr", "s", RA);
- expr1.X_add_number = EXTRACT_OPERAND (1, IMMP, *ip) << 2;
- macro_build (&expr1, "addiu", "t,r,j", SP, SP, BFD_RELOC_LO16);
- end_noreorder ();
- break;
+ case M_DINS:
+ {
+ /* Use unsigned arithmetic. */
+ addressT pos;
+ addressT size;
- case M_JRC:
- gas_assert (mips_opts.micromips);
- gas_assert (mips_opts.insn32);
- macro_build (NULL, "jr", "s", sreg);
- if (mips_opts.noreorder)
- macro_build (NULL, "nop", "");
- break;
+ if (imm_expr.X_op != O_constant || imm2_expr.X_op != O_constant)
+ {
+ as_bad (_("Unsupported large constant"));
+ pos = size = 1;
+ }
+ else
+ {
+ pos = imm_expr.X_add_number;
+ size = imm2_expr.X_add_number;
+ }
- case M_LI:
- case M_LI_S:
- load_register (treg, &imm_expr, 0);
- break;
+ if (pos > 63)
+ {
+ report_bad_range (ip, 3, pos, 0, 63, FALSE);
+ pos = 1;
+ }
+ if (size == 0 || size > 64 || (pos + size - 1) > 63)
+ {
+ report_bad_field (pos, size);
+ size = 1;
+ }
- case M_DLI:
- load_register (treg, &imm_expr, 1);
+ if (pos < 32 && (pos + size - 1) < 32)
+ {
+ s = "dins";
+ fmt = "t,r,+A,+B";
+ }
+ else if (pos >= 32)
+ {
+ s = "dinsu";
+ fmt = "t,r,+E,+F";
+ }
+ else
+ {
+ s = "dinsm";
+ fmt = "t,r,+A,+F";
+ }
+ macro_build ((expressionS *) NULL, s, fmt, treg, sreg, (int) pos,
+ (int) (pos + size - 1));
+ }
break;
- case M_LI_SS:
- if (imm_expr.X_op == O_constant)
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ if (treg == 0)
{
- used_at = 1;
- load_register (AT, &imm_expr, 0);
- macro_build (NULL, "mtc1", "t,G", AT, treg);
+ as_warn (_("Divide by zero."));
+ if (mips_trap)
+ macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+ else
+ macro_build (NULL, "break", BRK_FMT, 7);
break;
}
- else
+
+ start_noreorder ();
+ if (mips_trap)
{
- gas_assert (offset_expr.X_op == O_symbol
- && strcmp (segment_name (S_GET_SEGMENT
- (offset_expr.X_add_symbol)),
- ".lit4") == 0
- && offset_expr.X_add_number == 0);
- macro_build (&offset_expr, "lwc1", "T,o(b)", treg,
- BFD_RELOC_MIPS_LITERAL, mips_gp_register);
- break;
+ macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
}
-
- case M_LI_D:
- /* Check if we have a constant in IMM_EXPR. If the GPRs are 64 bits
- wide, IMM_EXPR is the entire value. Otherwise IMM_EXPR is the high
- order 32 bits of the value and the low order 32 bits are either
- zero or in OFFSET_EXPR. */
- if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ else
{
- if (HAVE_64BIT_GPRS)
- load_register (treg, &imm_expr, 1);
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
else
- {
- int hreg, lreg;
-
- if (target_big_endian)
- {
- hreg = treg;
- lreg = treg + 1;
- }
- else
- {
- hreg = treg + 1;
- lreg = treg;
- }
-
- if (hreg <= 31)
- load_register (hreg, &imm_expr, 0);
- if (lreg <= 31)
- {
- if (offset_expr.X_op == O_absent)
- move_register (lreg, 0);
- else
- {
- gas_assert (offset_expr.X_op == O_constant);
- load_register (lreg, &offset_expr, 0);
- }
- }
- }
- break;
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
+ macro_build (NULL, "break", BRK_FMT, 7);
+ if (mips_opts.micromips)
+ micromips_add_label ();
}
-
- /* We know that sym is in the .rdata section. First we get the
- upper 16 bits of the address. */
- if (mips_pic == NO_PIC)
+ expr1.X_add_number = -1;
+ used_at = 1;
+ load_register (AT, &expr1, dbl);
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
+ macro_build (&label_expr, "bne", "s,t,p", treg, AT);
+ if (dbl)
{
- macro_build_lui (&offset_expr, AT);
- used_at = 1;
+ expr1.X_add_number = 1;
+ load_register (AT, &expr1, dbl);
+ macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
}
else
{
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- used_at = 1;
+ expr1.X_add_number = 0x80000000;
+ macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
}
-
- /* Now we load the register(s). */
- if (HAVE_64BIT_GPRS)
+ if (mips_trap)
{
- used_at = 1;
- macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ macro_build (NULL, "teq", TRAP_FMT, sreg, AT, 6);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
}
else
{
- used_at = 1;
- macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT);
- if (treg != RA)
- {
- /* FIXME: How in the world do we deal with the possible
- overflow here? */
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, "lw", "t,o(b)",
- treg + 1, BFD_RELOC_LO16, AT);
- }
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", sreg, AT);
+ macro_build (NULL, "nop", "");
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+
+ macro_build (NULL, "break", BRK_FMT, 6);
}
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ macro_build (NULL, s, MFHL_FMT, dreg);
break;
- case M_LI_DD:
- /* Check if we have a constant in IMM_EXPR. If the FPRs are 64 bits
- wide, IMM_EXPR is the entire value and the GPRs are known to be 64
- bits wide as well. Otherwise IMM_EXPR is the high order 32 bits of
- the value and the low order 32 bits are either zero or in
- OFFSET_EXPR. */
- if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ case M_DIV_3I:
+ s = "div";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DIVU_3I:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_REM_3I:
+ s = "div";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_REMU_3I:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DDIV_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DDIVU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DREM_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DREMU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divi:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ as_warn (_("Divide by zero."));
+ if (mips_trap)
+ macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+ else
+ macro_build (NULL, "break", BRK_FMT, 7);
+ break;
+ }
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 1)
{
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
- if (HAVE_64BIT_FPRS)
- {
- gas_assert (HAVE_64BIT_GPRS);
- macro_build (NULL, "dmtc1", "t,S", AT, treg);
- }
+ if (strcmp (s2, "mflo") == 0)
+ move_register (dreg, sreg);
else
+ move_register (dreg, ZERO);
+ break;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number == -1
+ && s[strlen (s) - 1] != 'u')
+ {
+ if (strcmp (s2, "mflo") == 0)
{
- macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
- if (offset_expr.X_op == O_absent)
- macro_build (NULL, "mtc1", "t,G", 0, treg);
- else
- {
- gas_assert (offset_expr.X_op == O_constant);
- load_register (AT, &offset_expr, 0);
- macro_build (NULL, "mtc1", "t,G", AT, treg);
- }
+ macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
}
+ else
+ move_register (dreg, ZERO);
break;
}
- gas_assert (offset_expr.X_op == O_symbol
- && offset_expr.X_add_number == 0);
- s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
- if (strcmp (s, ".lit8") == 0)
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s, "z,s,t", sreg, AT);
+ macro_build (NULL, s2, MFHL_FMT, dreg);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ start_noreorder ();
+ if (mips_trap)
{
- if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
- {
- macro_build (&offset_expr, "ldc1", "T,o(b)", treg,
- BFD_RELOC_MIPS_LITERAL, mips_gp_register);
- break;
- }
- breg = mips_gp_register;
- r = BFD_RELOC_MIPS_LITERAL;
- goto dob;
+ macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
+ macro_build (NULL, s, "z,s,t", sreg, treg);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
}
else
{
- gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
- used_at = 1;
- if (mips_pic != NO_PIC)
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
else
- {
- /* FIXME: This won't work for a 64 bit address. */
- macro_build_lui (&offset_expr, AT);
- }
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", treg, ZERO);
+ macro_build (NULL, s, "z,s,t", sreg, treg);
- if (CPU_HAS_LDC1_SDC1 (mips_opts.arch) || mips_opts.micromips)
- {
- macro_build (&offset_expr, "ldc1", "T,o(b)",
- treg, BFD_RELOC_LO16, AT);
- break;
- }
- breg = AT;
- r = BFD_RELOC_LO16;
- goto dob;
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+ macro_build (NULL, "break", BRK_FMT, 7);
+ if (mips_opts.micromips)
+ micromips_add_label ();
}
-
- case M_L_DOB:
- /* Even on a big endian machine $fn comes before $fn+1. We have
- to adjust when loading from memory. */
- r = BFD_RELOC_LO16;
- dob:
- gas_assert (!mips_opts.micromips);
- gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
- macro_build (&offset_expr, "lwc1", "T,o(b)",
- target_big_endian ? treg + 1 : treg, r, breg);
- /* FIXME: A possible overflow which I don't know how to deal
- with. */
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, "lwc1", "T,o(b)",
- target_big_endian ? treg : treg + 1, r, breg);
+ macro_build (NULL, s2, MFHL_FMT, dreg);
break;
- case M_S_DOB:
- gas_assert (!mips_opts.micromips);
- gas_assert (!CPU_HAS_LDC1_SDC1 (mips_opts.arch));
- /* Even on a big endian machine $fn comes before $fn+1. We have
- to adjust when storing to memory. */
- macro_build (&offset_expr, "swc1", "T,o(b)",
- target_big_endian ? treg + 1 : treg, BFD_RELOC_LO16, breg);
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, "swc1", "T,o(b)",
- target_big_endian ? treg : treg + 1, BFD_RELOC_LO16, breg);
- break;
+ case M_DLCA_AB:
+ dbl = 1;
+ case M_LCA_AB:
+ call = 1;
+ goto do_la;
+ case M_DLA_AB:
+ dbl = 1;
+ case M_LA_AB:
+ do_la:
+ /* Load the address of a symbol into a register. If breg is not
+ zero, we then add a base register to it. */
- case M_L_DAB:
- gas_assert (!mips_opts.micromips);
- /*
- * The MIPS assembler seems to check for X_add_number not
- * being double aligned and generating:
- * lui at,%hi(foo+1)
- * addu at,at,v1
- * addiu at,at,%lo(foo+1)
- * lwc1 f2,0(at)
- * lwc1 f3,4(at)
- * But, the resulting address is the same after relocation so why
- * generate the extra instruction?
- */
- /* Itbl support may require additional care here. */
- coproc = 1;
- fmt = "T,o(b)";
- if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
- {
- s = "ldc1";
- goto ld_st;
- }
- s = "lwc1";
- goto ldd_std;
+ if (dbl && HAVE_32BIT_GPRS)
+ as_warn (_("dla used to load 32-bit register"));
- case M_S_DAB:
- gas_assert (!mips_opts.micromips);
- /* Itbl support may require additional care here. */
- coproc = 1;
- fmt = "T,o(b)";
- if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
+ if (!dbl && HAVE_64BIT_OBJECTS)
+ as_warn (_("la used to load 64-bit address"));
+
+ if (small_offset_p (0, align, 16))
{
- s = "sdc1";
- goto ld_st;
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, breg,
+ -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ break;
}
- s = "swc1";
- goto ldd_std;
-
- case M_LQ_AB:
- fmt = "t,o(b)";
- s = "lq";
- goto ld;
-
- case M_SQ_AB:
- fmt = "t,o(b)";
- s = "sq";
- goto ld_st;
- case M_LD_AB:
- fmt = "t,o(b)";
- if (HAVE_64BIT_GPRS)
+ if (mips_opts.at && (treg == breg))
{
- s = "ld";
- goto ld;
+ tempreg = AT;
+ used_at = 1;
}
- s = "lw";
- goto ldd_std;
-
- case M_SD_AB:
- fmt = "t,o(b)";
- if (HAVE_64BIT_GPRS)
+ else
{
- s = "sd";
- goto ld_st;
+ tempreg = treg;
}
- s = "sw";
- ldd_std:
if (offset_expr.X_op != O_symbol
&& offset_expr.X_op != O_constant)
{
offset_expr.X_op = O_constant;
}
- if (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ if (offset_expr.X_op == O_constant)
+ load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+ else if (mips_pic == NO_PIC)
{
- char value [32];
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $tempreg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we may as well always use the latter form.
- sprintf_vma (value, offset_expr.X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
- }
+ With 64bit address space and a usable $at we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddiu $at,<sym> (BFD_RELOC_LO16)
+ dsll32 $tempreg,0
+ daddu $tempreg,$tempreg,$at
- /* Even on a big endian machine $fn comes before $fn+1. We have
- to adjust when loading from memory. We set coproc if we must
- load $fn+1 first. */
- /* Itbl support may require additional care here. */
- if (!target_big_endian)
- coproc = 0;
+ If $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_LO16)
- if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
- {
- /* If this is a reference to a GP relative symbol, we want
- <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
- <op> $treg+1,<sym>+4($gp) (BFD_RELOC_GPREL16)
- If we have a base register, we use this
- addu $at,$breg,$gp
- <op> $treg,<sym>($at) (BFD_RELOC_GPREL16)
- <op> $treg+1,<sym>+4($at) (BFD_RELOC_GPREL16)
- If this is not a GP relative symbol, we want
- lui $at,<sym> (BFD_RELOC_HI16_S)
- <op> $treg,<sym>($at) (BFD_RELOC_LO16)
- <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
- If there is a base register, we add it to $at after the
- lui instruction. If there is a constant, we always use
- the last case. */
- if (offset_expr.X_op == O_symbol
- && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
- && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
{
- relax_start (offset_expr.X_add_symbol);
- if (breg == 0)
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
{
- tempreg = mips_gp_register;
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+
+ if (used_at == 0 && mips_opts.at)
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ tempreg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ AT, BFD_RELOC_HI16_S);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
+ macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+ used_at = 1;
}
else
{
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- AT, breg, mips_gp_register);
- tempreg = AT;
- used_at = 1;
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ tempreg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
}
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_GPREL16, tempreg);
- offset_expr.X_add_number += 4;
-
- /* Set mips_optimize to 2 to avoid inserting an
- undesired nop. */
- hold_mips_optimize = mips_optimize;
- mips_optimize = 2;
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_GPREL16, tempreg);
- mips_optimize = hold_mips_optimize;
-
- relax_switch ();
-
- offset_expr.X_add_number -= 4;
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+ if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ as_bad (_("Offset too large"));
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ if (mips_relax.sequence)
+ relax_end ();
}
- used_at = 1;
- macro_build_lui (&offset_expr, AT);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_LO16, AT);
- /* FIXME: How do we handle overflow here? */
- offset_expr.X_add_number += 4;
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_LO16, AT);
- if (mips_relax.sequence)
- relax_end ();
- }
- else if (!mips_big_got)
- {
- /* If this is a reference to an external symbol, we want
- lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- <op> $treg,0($at)
- <op> $treg+1,4($at)
- Otherwise we want
- lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
- nop
- <op> $treg,<sym>($at) (BFD_RELOC_LO16)
- <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
- If there is a base register we add it to $at before the
- lwc1 instructions. If there is a constant we include it
- in the lwc1 instructions. */
- used_at = 1;
- expr1.X_add_number = offset_expr.X_add_number;
- if (expr1.X_add_number < -0x8000
- || expr1.X_add_number >= 0x8000 - 4)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- load_got_offset (AT, &offset_expr);
- load_delay_nop ();
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
-
- /* Set mips_optimize to 2 to avoid inserting an undesired
- nop. */
- hold_mips_optimize = mips_optimize;
- mips_optimize = 2;
-
- /* Itbl support may require additional care here. */
- relax_start (offset_expr.X_add_symbol);
- macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_LO16, AT);
- expr1.X_add_number += 4;
- macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_LO16, AT);
- relax_switch ();
- macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_LO16, AT);
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_LO16, AT);
- relax_end ();
-
- mips_optimize = hold_mips_optimize;
}
- else if (mips_big_got)
+ else if (!mips_big_got && !HAVE_NEWABI)
{
- int gpdelay;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
- /* If this is a reference to an external symbol, we want
- lui $at,<sym> (BFD_RELOC_MIPS_GOT_HI16)
- addu $at,$at,$gp
- lw $at,<sym>($at) (BFD_RELOC_MIPS_GOT_LO16)
+ /* If this is a reference to an external symbol, and there
+ is no constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
nop
- <op> $treg,0($at)
- <op> $treg+1,4($at)
- Otherwise we want
- lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
nop
- <op> $treg,<sym>($at) (BFD_RELOC_LO16)
- <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
- If there is a base register we add it to $at before the
- lwc1 instructions. If there is a constant we include it
- in the lwc1 instructions. */
- used_at = 1;
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
- if (expr1.X_add_number < -0x8000
- || expr1.X_add_number >= 0x8000 - 4)
- as_bad (_("PIC code offset overflow (max 16 signed bits)"));
- gpdelay = reg_needs_delay (mips_gp_register);
- relax_start (offset_expr.X_add_symbol);
- macro_build (&offset_expr, "lui", LUI_FMT,
- AT, BFD_RELOC_MIPS_GOT_HI16);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- AT, AT, mips_gp_register);
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
- AT, BFD_RELOC_MIPS_GOT_LO16, AT);
- load_delay_nop ();
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
- /* Itbl support may require additional care here. */
- macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_LO16, AT);
- expr1.X_add_number += 4;
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
- /* Set mips_optimize to 2 to avoid inserting an undesired
- nop. */
- hold_mips_optimize = mips_optimize;
- mips_optimize = 2;
- /* Itbl support may require additional care here. */
- macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_LO16, AT);
- mips_optimize = hold_mips_optimize;
- expr1.X_add_number -= 4;
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
+ */
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
- if (gpdelay)
- macro_build (NULL, "nop", "");
- macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
- BFD_RELOC_MIPS_GOT16, mips_gp_register);
- load_delay_nop ();
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
- BFD_RELOC_LO16, AT);
- offset_expr.X_add_number += 4;
+ if (offset_expr.X_add_number == 0)
+ {
+ if (mips_pic == SVR4_PIC
+ && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
- /* Set mips_optimize to 2 to avoid inserting an undesired
- nop. */
- hold_mips_optimize = mips_optimize;
- mips_optimize = 2;
- /* Itbl support may require additional care here. */
- macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
- BFD_RELOC_LO16, AT);
- mips_optimize = hold_mips_optimize;
- relax_end ();
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ lw_reloc_type, mips_gp_register);
+ if (breg != 0)
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ load_delay_nop ();
+ }
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ relax_end ();
+ /* FIXME: If breg == 0, and the next instruction uses
+ $tempreg, then if this variant case is used an extra
+ nop will be generated. */
+ }
+ else if (offset_expr.X_add_number >= -0x8000
+ && offset_expr.X_add_number < 0x8000)
+ {
+ load_got_offset (tempreg, &offset_expr);
+ load_delay_nop ();
+ add_got_offset (tempreg, &offset_expr);
+ }
+ else
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number =
+ SEXT_16BIT (offset_expr.X_add_number);
+ load_got_offset (tempreg, &offset_expr);
+ offset_expr.X_add_number = expr1.X_add_number;
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg == treg)
+ {
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ breg = 0;
+ tempreg = treg;
+ }
+ add_got_offset_hilo (tempreg, &offset_expr, AT);
+ used_at = 1;
+ }
}
- else
- abort ();
-
- break;
-
- case M_LD_OB:
- s = HAVE_64BIT_GPRS ? "ld" : "lw";
- goto sd_ob;
- case M_SD_OB:
- s = HAVE_64BIT_GPRS ? "sd" : "sw";
- sd_ob:
- macro_build (&offset_expr, s, "t,o(b)", treg,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
- breg);
- if (!HAVE_64BIT_GPRS)
+ else if (!mips_big_got && HAVE_NEWABI)
{
- offset_expr.X_add_number += 4;
- macro_build (&offset_expr, s, "t,o(b)", treg + 1,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2],
- breg);
- }
- break;
-
-
- case M_SAA_AB:
- ab = 1;
- case M_SAA_OB:
- s = "saa";
- offbits = 0;
- fmt = "t,(b)";
- goto ld_st;
- case M_SAAD_AB:
- ab = 1;
- case M_SAAD_OB:
- s = "saad";
- offbits = 0;
- fmt = "t,(b)";
- goto ld_st;
-
- /* New code added to support COPZ instructions.
- This code builds table entries out of the macros in mip_opcodes.
- R4000 uses interlocks to handle coproc delays.
- Other chips (like the R3000) require nops to be inserted for delays.
-
- FIXME: Currently, we require that the user handle delays.
- In order to fill delay slots for non-interlocked chips,
- we must have a way to specify delays based on the coprocessor.
- Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
- What are the side-effects of the cop instruction?
- What cache support might we have and what are its effects?
- Both coprocessor & memory require delays. how long???
- What registers are read/set/modified?
+ int add_breg_early = 0;
- If an itbl is provided to interpret cop instructions,
- this knowledge can be encoded in the itbl spec. */
+ /* If this is a reference to an external, and there is no
+ constant, or local symbol (*), with or without a
+ constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
- case M_COP0:
- s = "c0";
- goto copz;
- case M_COP1:
- s = "c1";
- goto copz;
- case M_COP2:
- s = "c2";
- goto copz;
- case M_COP3:
- s = "c3";
- copz:
- gas_assert (!mips_opts.micromips);
- /* For now we just do C (same as Cz). The parameter will be
- stored in insn_opcode by mips_ip. */
- macro_build (NULL, s, "C", ip->insn_opcode);
- break;
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ addiu $tempreg,$tempreg,<constant>
- case M_MOVE:
- move_register (dreg, sreg);
- break;
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
- case M_MOVEP:
- gas_assert (mips_opts.micromips);
- gas_assert (mips_opts.insn32);
- dreg = micromips_to_32_reg_h_map[EXTRACT_OPERAND (1, MH, *ip)];
- breg = micromips_to_32_reg_i_map[EXTRACT_OPERAND (1, MI, *ip)];
- sreg = micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
- treg = micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
- move_register (dreg, sreg);
- move_register (breg, treg);
- break;
+ (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+ local symbols, even though it introduces an additional
+ instruction. */
- case M_DMUL:
- dbl = 1;
- case M_MUL:
- if (mips_opts.arch == CPU_R5900)
- {
- macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", dreg, sreg, treg);
- }
- else
- {
- macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
- macro_build (NULL, "mflo", MFHL_FMT, dreg);
- }
- break;
+ if (offset_expr.X_add_number)
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
- case M_DMUL_I:
- dbl = 1;
- 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
- anyway. */
- used_at = 1;
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
- macro_build (NULL, "mflo", MFHL_FMT, dreg);
- break;
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
- case M_DMULO_I:
- dbl = 1;
- case M_MULO_I:
- imm = 1;
- goto do_mulo;
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ dreg = treg;
+ add_breg_early = 1;
+ }
- case M_DMULO:
- dbl = 1;
- case M_MULO:
- do_mulo:
- start_noreorder ();
- used_at = 1;
- if (imm)
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
- macro_build (NULL, "mflo", MFHL_FMT, dreg);
- macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, dreg, dreg, RA);
- macro_build (NULL, "mfhi", MFHL_FMT, AT);
- if (mips_trap)
- macro_build (NULL, "tne", TRAP_FMT, dreg, AT, 6);
- else
- {
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = 8;
- macro_build (&label_expr, "beq", "s,t,p", dreg, AT);
- macro_build (NULL, "nop", "");
- macro_build (NULL, "break", BRK_FMT, 6);
- if (mips_opts.micromips)
- micromips_add_label ();
- }
- end_noreorder ();
- macro_build (NULL, "mflo", MFHL_FMT, dreg);
- break;
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ dreg, dreg, AT);
- case M_DMULOU_I:
- dbl = 1;
- case M_MULOU_I:
- imm = 1;
- goto do_mulou;
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
- case M_DMULOU:
- dbl = 1;
- case M_MULOU:
- do_mulou:
- start_noreorder ();
- used_at = 1;
- if (imm)
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
- sreg, imm ? AT : treg);
- macro_build (NULL, "mfhi", MFHL_FMT, AT);
- macro_build (NULL, "mflo", MFHL_FMT, dreg);
- if (mips_trap)
- macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 6);
- else
- {
- if (mips_opts.micromips)
- micromips_label_expr (&label_expr);
- else
- label_expr.X_add_number = 8;
- macro_build (&label_expr, "beq", "s,t,p", AT, ZERO);
- macro_build (NULL, "nop", "");
- macro_build (NULL, "break", BRK_FMT, 6);
- if (mips_opts.micromips)
- micromips_add_label ();
- }
- end_noreorder ();
- break;
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
- case M_DROL:
- if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
- {
- if (dreg == sreg)
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (add_breg_early)
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, tempreg, breg);
+ breg = 0;
+ tempreg = treg;
+ }
+ relax_end ();
+ }
+ else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
{
- tempreg = AT;
- used_at = 1;
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_CALL16, mips_gp_register);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ relax_end ();
}
else
{
- tempreg = dreg;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
}
- macro_build (NULL, "dnegu", "d,w", tempreg, treg);
- macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
- break;
}
- used_at = 1;
- macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
- macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT);
- macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- break;
-
- case M_ROL:
- if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ else if (mips_big_got && !HAVE_NEWABI)
{
- if (dreg == sreg)
+ int gpdelay;
+ int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+ int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant> (BFD_RELOC_LO16)
+ addu $tempreg,$tempreg,$at
+ */
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ relax_start (offset_expr.X_add_symbol);
+ gpdelay = reg_needs_delay (mips_gp_register);
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
{
- tempreg = AT;
- used_at = 1;
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
}
- else
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, lw_reloc_type, tempreg);
+ if (expr1.X_add_number == 0)
{
- tempreg = dreg;
+ if (breg != 0)
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ load_delay_nop ();
+ }
}
- macro_build (NULL, "negu", "d,w", tempreg, treg);
- macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg);
- break;
- }
- used_at = 1;
- macro_build (NULL, "subu", "d,v,t", AT, ZERO, treg);
- macro_build (NULL, "srlv", "d,t,s", AT, sreg, AT);
- macro_build (NULL, "sllv", "d,t,s", dreg, sreg, treg);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- break;
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ load_delay_nop ();
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else
+ {
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ dreg = treg;
+ }
- case M_DROL_I:
- {
- unsigned int rot;
- char *l;
- char *rr;
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
- if (imm_expr.X_op != O_constant)
- as_bad (_("Improper rotate count"));
- rot = imm_expr.X_add_number & 0x3f;
- if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
- {
- rot = (64 - rot) & 0x3f;
- if (rot >= 32)
- macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
- else
- macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
- break;
- }
- if (rot == 0)
- {
- macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
- break;
- }
- l = (rot < 0x20) ? "dsll" : "dsll32";
- rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
- rot &= 0x1f;
- used_at = 1;
- macro_build (NULL, l, SHFT_FMT, AT, sreg, rot);
- macro_build (NULL, rr, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- }
- break;
+ used_at = 1;
+ }
+ offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
+ relax_switch ();
- case M_ROL_I:
- {
- unsigned int rot;
+ if (gpdelay)
+ {
+ /* This is needed because this instruction uses $gp, but
+ the first instruction on the main stream does not. */
+ macro_build (NULL, "nop", "");
+ }
- if (imm_expr.X_op != O_constant)
- as_bad (_("Improper rotate count"));
- rot = imm_expr.X_add_number & 0x1f;
- if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
- {
- macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, (32 - rot) & 0x1f);
- break;
- }
- if (rot == 0)
- {
- macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
- break;
- }
- used_at = 1;
- macro_build (NULL, "sll", SHFT_FMT, AT, sreg, rot);
- macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- }
- break;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ local_reloc_type, mips_gp_register);
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ /* FIXME: If add_number is 0, and there was no base
+ register, the external symbol case ended with a load,
+ so if the symbol turns out to not be external, and
+ the next instruction uses tempreg, an unnecessary nop
+ will be inserted. */
+ }
+ else
+ {
+ if (breg == treg)
+ {
+ /* We must add in the base register now, as in the
+ external symbol case. */
+ gas_assert (tempreg == AT);
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ tempreg = treg;
+ /* We set breg to 0 because we have arranged to add
+ it in in both cases. */
+ breg = 0;
+ }
- case M_DROR:
- if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
- {
- macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
- break;
+ macro_build_lui (&expr1, AT);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, AT);
+ used_at = 1;
+ }
+ relax_end ();
}
- used_at = 1;
- macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
- macro_build (NULL, "dsllv", "d,t,s", AT, sreg, AT);
- macro_build (NULL, "dsrlv", "d,t,s", dreg, sreg, treg);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- break;
+ else if (mips_big_got && HAVE_NEWABI)
+ {
+ int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+ int add_breg_early = 0;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ addi $tempreg,$tempreg,<constant>
- case M_ROR:
- if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
- {
- macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg);
- break;
- }
- used_at = 1;
- macro_build (NULL, "subu", "d,v,t", AT, ZERO, treg);
- macro_build (NULL, "sllv", "d,t,s", AT, sreg, AT);
- macro_build (NULL, "srlv", "d,t,s", dreg, sreg, treg);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- break;
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addi $at,$at,<loconstant>
+ add $tempreg,$tempreg,$at
- case M_DROR_I:
- {
- unsigned int rot;
- char *l;
- char *rr;
+ If we have NewABI, and we know it's a local symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
+ otherwise we have to resort to GOT_HI16/GOT_LO16. */
- if (imm_expr.X_op != O_constant)
- as_bad (_("Improper rotate count"));
- rot = imm_expr.X_add_number & 0x3f;
- if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
- {
- if (rot >= 32)
- macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
- else
- macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
- break;
- }
- if (rot == 0)
- {
- macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
- break;
- }
- rr = (rot < 0x20) ? "dsrl" : "dsrl32";
- l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
- rot &= 0x1f;
- used_at = 1;
- macro_build (NULL, rr, SHFT_FMT, AT, sreg, rot);
- macro_build (NULL, l, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- }
- break;
+ relax_start (offset_expr.X_add_symbol);
- case M_ROR_I:
- {
- unsigned int rot;
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
- if (imm_expr.X_op != O_constant)
- as_bad (_("Improper rotate count"));
- rot = imm_expr.X_add_number & 0x1f;
- if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
- {
- macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, rot);
- break;
- }
- if (rot == 0)
- {
- macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
- break;
- }
- used_at = 1;
- macro_build (NULL, "srl", SHFT_FMT, AT, sreg, rot);
- macro_build (NULL, "sll", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
- macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
- }
- break;
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ {
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+ }
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, lw_reloc_type, tempreg);
- case M_SEQ:
- if (sreg == 0)
- macro_build (&expr1, "sltiu", "t,r,j", dreg, treg, BFD_RELOC_LO16);
- else if (treg == 0)
- macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- else
- {
- macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
- macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
- }
- break;
+ if (expr1.X_add_number == 0)
+ ;
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != treg)
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, AT, breg);
+ dreg = treg;
+ add_breg_early = 1;
+ }
- case M_SEQ_I:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- {
- macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- break;
- }
- if (sreg == 0)
- {
- as_warn (_("Instruction %s: result is always false"),
- ip->insn_mo->name);
- move_register (dreg, 0);
- break;
- }
- if (CPU_HAS_SEQ (mips_opts.arch)
- && -512 <= imm_expr.X_add_number
- && imm_expr.X_add_number < 512)
- {
- macro_build (NULL, "seqi", "t,r,+Q", dreg, sreg,
- (int) imm_expr.X_add_number);
- break;
- }
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= 0
- && imm_expr.X_add_number < 0x10000)
- {
- macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
- }
- else if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x8000
- && imm_expr.X_add_number < 0)
- {
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
- "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- }
- else if (CPU_HAS_SEQ (mips_opts.arch))
- {
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "seq", "d,v,t", dreg, sreg, AT);
- break;
- }
- else
- {
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
- used_at = 1;
- }
- macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
- break;
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
- case M_SGE: /* sreg >= treg <==> not (sreg < treg) */
- s = "slt";
- goto sge;
- case M_SGEU:
- s = "sltu";
- sge:
- macro_build (NULL, s, "d,v,t", dreg, sreg, treg);
- macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
- break;
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
- case M_SGE_I: /* sreg >= I <==> not (sreg < I) */
- case M_SGEU_I:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
- {
- macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
- dreg, sreg, BFD_RELOC_LO16);
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_GOT_OFST);
+ if (add_breg_early)
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ treg, tempreg, breg);
+ breg = 0;
+ tempreg = treg;
+ }
+ relax_end ();
}
else
- {
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
- dreg, sreg, AT);
- used_at = 1;
- }
- macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
- break;
-
- case M_SGT: /* sreg > treg <==> treg < sreg */
- s = "slt";
- goto sgt;
- case M_SGTU:
- s = "sltu";
- sgt:
- macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
- break;
+ abort ();
- case M_SGT_I: /* sreg > I <==> I < sreg */
- s = "slt";
- goto sgti;
- case M_SGTU_I:
- s = "sltu";
- sgti:
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
break;
-
- case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */
- s = "slt";
- goto sle;
- case M_SLEU:
- s = "sltu";
- sle:
- macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
- macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+
+ case M_MSGSND:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (treg << 16) | 0x01);
break;
- case M_SLE_I: /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
- s = "slt";
- goto slei;
- case M_SLEU_I:
- s = "sltu";
- slei:
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
- macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ case M_MSGLD:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 0x02);
break;
- case M_SLT_I:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
- {
- macro_build (&imm_expr, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- break;
- }
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
+ case M_MSGLD_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (treg << 16) | 0x02);
break;
- case M_SLTU_I:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= -0x8000
- && imm_expr.X_add_number < 0x8000)
- {
- macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
- BFD_RELOC_LO16);
- break;
- }
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT);
+ case M_MSGWAIT:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 3);
break;
- case M_SNE:
- if (sreg == 0)
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg);
- else if (treg == 0)
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+ case M_MSGWAIT_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (treg << 16) | 0x03);
+ break;
+
+ case M_J_A:
+ /* The j instruction may not be used in PIC code, since it
+ requires an absolute address. We convert it to a b
+ instruction. */
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, "j", "a");
else
- {
- macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
- }
+ macro_build (&offset_expr, "b", "p");
break;
- case M_SNE_I:
- if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
- {
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
- break;
- }
- if (sreg == 0)
- {
- as_warn (_("Instruction %s: result is always true"),
- ip->insn_mo->name);
- macro_build (&expr1, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j",
- dreg, 0, BFD_RELOC_LO16);
- break;
- }
- if (CPU_HAS_SEQ (mips_opts.arch)
- && -512 <= imm_expr.X_add_number
- && imm_expr.X_add_number < 512)
+ /* The jal instructions must be handled as macros because when
+ generating PIC code they expand to multi-instruction
+ sequences. Normally they are simple instructions. */
+ case M_JALS_1:
+ dreg = RA;
+ /* Fall through. */
+ case M_JALS_2:
+ gas_assert (mips_opts.micromips);
+ if (mips_opts.insn32)
{
- macro_build (NULL, "snei", "t,r,+Q", dreg, sreg,
- (int) imm_expr.X_add_number);
+ as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
break;
}
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number >= 0
- && imm_expr.X_add_number < 0x10000)
- {
- macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
- }
- else if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x8000
- && imm_expr.X_add_number < 0)
- {
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
- "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- }
- else if (CPU_HAS_SEQ (mips_opts.arch))
+ jals = 1;
+ goto jal;
+ case M_JAL_1:
+ dreg = RA;
+ /* Fall through. */
+ case M_JAL_2:
+ jal:
+ if (mips_pic == NO_PIC)
{
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
- break;
+ s = jals ? "jalrs" : "jalr";
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && dreg == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, s, "mj", sreg);
+ else
+ macro_build (NULL, s, JALR_FMT, dreg, sreg);
}
else
{
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
- used_at = 1;
+ int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
+ && mips_cprestore_offset >= 0);
+
+ if (sreg != PIC_CALL_REG)
+ as_warn (_("MIPS PIC call to register other than $25"));
+
+ s = ((mips_opts.micromips
+ && !mips_opts.insn32
+ && (!mips_opts.noreorder || cprestore))
+ ? "jalrs" : "jalr");
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && dreg == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, s, "mj", sreg);
+ else
+ macro_build (NULL, s, JALR_FMT, dreg, sreg);
+ if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
+ {
+ if (mips_cprestore_offset < 0)
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (!mips_frame_reg_valid)
+ {
+ as_warn (_("No .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (!mips_cprestore_valid)
+ {
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
}
- macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+
break;
- case M_SUB_I:
- s = "addi";
- s2 = "sub";
- goto do_subi;
- case M_SUBU_I:
- s = "addiu";
- s2 = "subu";
- goto do_subi;
- case M_DSUB_I:
- dbl = 1;
- s = "daddi";
- s2 = "dsub";
- if (!mips_opts.micromips)
- goto do_subi;
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x200
- && imm_expr.X_add_number <= 0x200)
+ case M_JALS_A:
+ gas_assert (mips_opts.micromips);
+ if (mips_opts.insn32)
{
- macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
+ as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
break;
}
- goto do_subi_i;
- case M_DSUBU_I:
- dbl = 1;
- s = "daddiu";
- s2 = "dsubu";
- do_subi:
- if (imm_expr.X_op == O_constant
- && imm_expr.X_add_number > -0x8000
- && imm_expr.X_add_number <= 0x8000)
+ jals = 1;
+ /* Fall through. */
+ case M_JAL_A:
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, jals ? "jals" : "jal", "a");
+ else if (mips_pic == SVR4_PIC)
{
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
- break;
- }
- do_subi_i:
- used_at = 1;
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
- break;
+ /* If this is a reference to an external symbol, and we are
+ using a small GOT, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ nop
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+ The cprestore value is set using the .cprestore
+ pseudo-op. If we are using a big GOT, we want
+ lui $25,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $25,$25,$gp
+ lw $25,<sym>($25) (BFD_RELOC_MIPS_CALL_LO16)
+ nop
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+ If the symbol is not external, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $25,$25,<sym> (BFD_RELOC_LO16)
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+
+ For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
+ sequences above, minus nops, unless the symbol is local,
+ which enables us to use GOT_PAGE/GOT_OFST (big got) or
+ GOT_DISP. */
+ if (HAVE_NEWABI)
+ {
+ if (!mips_big_got)
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+ mips_gp_register);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
+ mips_gp_register);
+ relax_end ();
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+ BFD_RELOC_MIPS_CALL_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+ PIC_CALL_REG, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+ PIC_CALL_REG);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ PIC_CALL_REG, PIC_CALL_REG,
+ BFD_RELOC_MIPS_GOT_OFST);
+ relax_end ();
+ }
- case M_TEQ_I:
- s = "teq";
- goto trap;
- case M_TGE_I:
- s = "tge";
- goto trap;
- case M_TGEU_I:
- s = "tgeu";
- goto trap;
- case M_TLT_I:
- s = "tlt";
- goto trap;
- case M_TLTU_I:
- s = "tltu";
- goto trap;
- case M_TNE_I:
- s = "tne";
- trap:
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, s, "s,t", sreg, AT);
- break;
+ macro_build_jalr (&offset_expr, 0);
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (!mips_big_got)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+ mips_gp_register);
+ load_delay_nop ();
+ relax_switch ();
+ }
+ else
+ {
+ int gpdelay;
- case M_TRUNCWS:
- case M_TRUNCWD:
- gas_assert (!mips_opts.micromips);
- gas_assert (mips_opts.isa == ISA_MIPS1);
- used_at = 1;
- sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */
- dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */
+ gpdelay = reg_needs_delay (mips_gp_register);
+ macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+ BFD_RELOC_MIPS_CALL_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+ PIC_CALL_REG, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+ PIC_CALL_REG);
+ load_delay_nop ();
+ relax_switch ();
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ }
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
+ mips_gp_register);
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
+ relax_end ();
+ macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
+
+ if (mips_cprestore_offset < 0)
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (!mips_frame_reg_valid)
+ {
+ as_warn (_("No .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (!mips_cprestore_valid)
+ {
+ as_warn (_("No .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
+ }
+ else if (mips_pic == VXWORKS_PIC)
+ as_bad (_("Non-PIC jump used in PIC library"));
+ else
+ abort ();
- /*
- * Is the double cfc1 instruction a bug in the mips assembler;
- * or is there a reason for it?
- */
- start_noreorder ();
- macro_build (NULL, "cfc1", "t,G", treg, RA);
- macro_build (NULL, "cfc1", "t,G", treg, RA);
- macro_build (NULL, "nop", "");
- expr1.X_add_number = 3;
- macro_build (&expr1, "ori", "t,r,i", AT, treg, BFD_RELOC_LO16);
- expr1.X_add_number = 2;
- macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16);
- macro_build (NULL, "ctc1", "t,G", AT, RA);
- macro_build (NULL, "nop", "");
- macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S",
- dreg, sreg);
- macro_build (NULL, "ctc1", "t,G", treg, RA);
- macro_build (NULL, "nop", "");
- end_noreorder ();
break;
- case M_ULH_A:
- ab = 1;
- case M_ULH:
+ case M_LBUE_AB:
+ s = "lbue";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LHUE_AB:
+ s = "lhue";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LBE_AB:
+ s = "lbe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LHE_AB:
+ s = "lhe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LLE_AB:
+ s = "lle";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LWE_AB:
+ s = "lwe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LWLE_AB:
+ s = "lwle";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LWRE_AB:
+ s = "lwre";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SBE_AB:
+ s = "sbe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SCE_AB:
+ s = "sce";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SHE_AB:
+ s = "she";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SWE_AB:
+ s = "swe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SWLE_AB:
+ s = "swle";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SWRE_AB:
+ s = "swre";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_ACLR_AB:
+ s = "aclr";
+ treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
+ fmt = "\\,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_ASET_AB:
+ s = "aset";
+ treg = EXTRACT_OPERAND (mips_opts.micromips, 3BITPOS, *ip);
+ fmt = "\\,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_LB_AB:
s = "lb";
- s2 = "lbu";
- off = 1;
- goto uld_st;
- case M_ULHU_A:
- ab = 1;
- case M_ULHU:
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LBU_AB:
s = "lbu";
- s2 = "lbu";
- off = 1;
- goto uld_st;
- case M_ULW_A:
- ab = 1;
- case M_ULW:
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LH_AB:
+ s = "lh";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LHU_AB:
+ s = "lhu";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LW_AB:
+ s = "lw";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LWC0_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "lwc0";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWC1_AB:
+ s = "lwc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWC2_AB:
+ s = "lwc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWC3_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "lwc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWL_AB:
s = "lwl";
- s2 = "lwr";
+ fmt = MEM12_FMT;
offbits = (mips_opts.micromips ? 12 : 16);
- off = 3;
- goto uld_st;
- case M_ULD_A:
- ab = 1;
- case M_ULD:
+ goto ld_st;
+ case M_LWR_AB:
+ s = "lwr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_LDC1_AB:
+ s = "ldc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LDC2_AB:
+ s = "ldc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LQC2_AB:
+ s = "lqc2";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LDC3_AB:
+ s = "ldc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LDL_AB:
s = "ldl";
- s2 = "ldr";
+ fmt = MEM12_FMT;
offbits = (mips_opts.micromips ? 12 : 16);
- off = 7;
- goto uld_st;
- case M_USH_A:
- ab = 1;
- case M_USH:
+ goto ld_st;
+ case M_LDR_AB:
+ s = "ldr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_LL_AB:
+ s = "ll";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld;
+ case M_LLD_AB:
+ s = "lld";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld;
+ case M_LWU_AB:
+ s = "lwu";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld;
+ case M_LWP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "lwp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ lp = 1;
+ goto ld;
+ case M_LDP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "ldp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ lp = 1;
+ goto ld;
+ case M_LWM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "lwm";
+ fmt = "n,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_LDM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "ldm";
+ fmt = "n,~(b)";
+ offbits = 12;
+ goto ld_st;
+
+ ld:
+ /* We don't want to use $0 as tempreg. */
+ if (breg == treg + lp || treg + lp == ZERO)
+ goto ld_st;
+ else
+ tempreg = treg + lp;
+ goto ld_noat;
+
+ case M_SB_AB:
s = "sb";
- s2 = "sb";
- off = 1;
- ust = 1;
- goto uld_st;
- case M_USW_A:
- ab = 1;
- case M_USW:
+ fmt = "t,o(b)";
+ goto ld_st;
+ case M_SH_AB:
+ s = "sh";
+ fmt = "t,o(b)";
+ goto ld_st;
+ case M_SW_AB:
+ s = "sw";
+ fmt = "t,o(b)";
+ goto ld_st;
+ case M_SWC0_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "swc0";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWC1_AB:
+ s = "swc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWC2_AB:
+ s = "swc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWC3_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "swc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWL_AB:
s = "swl";
- s2 = "swr";
+ fmt = MEM12_FMT;
offbits = (mips_opts.micromips ? 12 : 16);
- off = 3;
- ust = 1;
- goto uld_st;
- case M_USD_A:
- ab = 1;
- case M_USD:
+ goto ld_st;
+ case M_SWR_AB:
+ s = "swr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SC_AB:
+ s = "sc";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SCD_AB:
+ s = "scd";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_CACHE_AB:
+ s = "cache";
+ fmt = mips_opts.micromips ? "k,~(b)" : "k,o(b)";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_CACHEE_AB:
+ s = "cachee";
+ fmt = "k,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_PREF_AB:
+ s = "pref";
+ fmt = !mips_opts.micromips ? "k,o(b)" : "k,~(b)";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_PREFE_AB:
+ s = "prefe";
+ fmt = "k,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SDC1_AB:
+ s = "sdc1";
+ fmt = "T,o(b)";
+ coproc = 1;
+ /* Itbl support may require additional care here. */
+ goto ld_st;
+ case M_SDC2_AB:
+ s = "sdc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SQC2_AB:
+ s = "sqc2";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SDC3_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "sdc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SDL_AB:
s = "sdl";
- s2 = "sdr";
+ fmt = MEM12_FMT;
offbits = (mips_opts.micromips ? 12 : 16);
- off = 7;
- ust = 1;
-
- uld_st:
- if (!ab && offset_expr.X_add_number >= 0x8000 - off)
- as_bad (_("Operand overflow"));
+ goto ld_st;
+ case M_SDR_AB:
+ s = "sdr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SWP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "swp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_SDP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "sdp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_SWM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "swm";
+ fmt = "n,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_SDM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "sdm";
+ fmt = "n,~(b)";
+ offbits = 12;
- ep = &offset_expr;
- expr1.X_add_number = 0;
- if (ab)
- {
- used_at = 1;
- tempreg = AT;
- load_address (tempreg, ep, &used_at);
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- tempreg, tempreg, breg);
- breg = tempreg;
- tempreg = treg;
- ep = &expr1;
- }
- else if (offbits == 12
- && (offset_expr.X_op != O_constant
- || !IS_SEXT_12BIT_NUM (offset_expr.X_add_number)
- || !IS_SEXT_12BIT_NUM (offset_expr.X_add_number + off)))
- {
- used_at = 1;
- tempreg = AT;
- macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
- breg = tempreg;
- tempreg = treg;
- ep = &expr1;
- }
- else if (!ust && treg == breg)
+ ld_st:
+ tempreg = AT;
+ ld_noat:
+ if (small_offset_p (0, align, 16))
{
- used_at = 1;
- tempreg = AT;
+ /* The first case exists for M_LD_AB and M_SD_AB, which are
+ macros for o32 but which should act like normal instructions
+ otherwise. */
+ if (offbits == 16)
+ macro_build (&offset_expr, s, fmt, treg, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ else if (small_offset_p (0, align, offbits))
+ {
+ if (offbits == 0)
+ macro_build (NULL, s, fmt, treg, breg);
+ else
+ macro_build (NULL, s, fmt, treg,
+ (int) offset_expr.X_add_number, breg);
+ }
+ else
+ {
+ if (tempreg == AT)
+ used_at = 1;
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, breg, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2]);
+ if (offbits == 0)
+ macro_build (NULL, s, fmt, treg, tempreg);
+ else
+ macro_build (NULL, s, fmt, treg, 0, tempreg);
+ }
+ break;
}
- else
- tempreg = treg;
-
- if (off == 1)
- goto ulh_sh;
-
- if (!target_big_endian)
- ep->X_add_number += off;
- if (offbits != 12)
- macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
- else
- macro_build (NULL, s, "t,~(b)",
- tempreg, (unsigned long) ep->X_add_number, breg);
- if (!target_big_endian)
- ep->X_add_number -= off;
- else
- ep->X_add_number += off;
- if (offbits != 12)
- macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
- else
- macro_build (NULL, s2, "t,~(b)",
- tempreg, (unsigned long) ep->X_add_number, breg);
+ if (tempreg == AT)
+ used_at = 1;
- /* If necessary, move the result in tempreg to the final destination. */
- if (!ust && treg != tempreg)
- {
- /* Protect second load's delay slot. */
- load_delay_nop ();
- move_register (treg, tempreg);
+ if (offset_expr.X_op != O_constant
+ && offset_expr.X_op != O_symbol)
+ {
+ as_bad (_("Expression too complex"));
+ offset_expr.X_op = O_constant;
}
- break;
-
- ulh_sh:
- used_at = 1;
- if (target_big_endian == ust)
- ep->X_add_number += off;
- tempreg = ust || ab ? treg : AT;
- macro_build (ep, s, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
- /* For halfword transfers we need a temporary register to shuffle
- bytes. Unfortunately for M_USH_A we have none available before
- the next store as AT holds the base address. We deal with this
- case by clobbering TREG and then restoring it as with ULH. */
- tempreg = ust == ab ? treg : AT;
- if (ust)
- macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
- if (target_big_endian == ust)
- ep->X_add_number -= off;
- else
- ep->X_add_number += off;
- macro_build (ep, s2, "t,o(b)", tempreg, BFD_RELOC_LO16, breg);
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ }
- /* For M_USH_A re-retrieve the LSB. */
- if (ust && ab)
+ /* A constant expression in PIC code can be handled just as it
+ is in non PIC code. */
+ if (offset_expr.X_op == O_constant)
{
- if (target_big_endian)
- ep->X_add_number += off;
+ expr1.X_add_number = offset_high_part (offset_expr.X_add_number,
+ offbits == 0 ? 16 : offbits);
+ offset_expr.X_add_number -= expr1.X_add_number;
+
+ load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ if (offbits == 0)
+ {
+ if (offset_expr.X_add_number != 0)
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN,
+ "t,r,j", tempreg, tempreg, BFD_RELOC_LO16);
+ macro_build (NULL, s, fmt, treg, tempreg);
+ }
+ else if (offbits == 16)
+ macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16, tempreg);
else
- ep->X_add_number -= off;
- macro_build (&expr1, "lbu", "t,o(b)", AT, BFD_RELOC_LO16, AT);
+ macro_build (NULL, s, fmt, treg,
+ (int) offset_expr.X_add_number, tempreg);
}
- /* For ULH and M_USH_A OR the LSB in. */
- if (!ust || ab)
+ else if (offbits != 16)
{
- tempreg = !ab ? AT : treg;
- macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
- macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ /* The offset field is too narrow to be used for a low-part
+ relocation, so load the whole address into the auxillary
+ register. */
+ load_address (tempreg, &offset_expr, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ if (offbits == 0)
+ macro_build (NULL, s, fmt, treg, tempreg);
+ else
+ macro_build (NULL, s, fmt, treg, 0, tempreg);
}
- break;
-
- default:
- /* FIXME: Check if this is one of the itbl macros, since they
- are added dynamically. */
- as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
- break;
- }
- if (!mips_opts.at && used_at)
- as_bad (_("Macro used $at after \".set noat\""));
-}
-
-/* Implement macros in mips16 mode. */
-
-static void
-mips16_macro (struct mips_cl_insn *ip)
-{
- int mask;
- int xreg, yreg, zreg, tmp;
- expressionS expr1;
- int dbl;
- const char *s, *s2, *s3;
-
- mask = ip->insn_mo->mask;
-
- xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
- yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
- zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
-
- expr1.X_op = O_constant;
- expr1.X_op_symbol = NULL;
- expr1.X_add_symbol = NULL;
- expr1.X_add_number = 1;
-
- dbl = 0;
-
- switch (mask)
- {
- default:
- abort ();
-
- case M_DDIV_3:
- dbl = 1;
- case M_DIV_3:
- s = "mflo";
- goto do_div3;
- case M_DREM_3:
- dbl = 1;
- case M_REM_3:
- s = "mfhi";
- do_div3:
- start_noreorder ();
- macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
- expr1.X_add_number = 2;
- macro_build (&expr1, "bnez", "x,p", yreg);
- macro_build (NULL, "break", "6", 7);
-
- /* FIXME: The normal code checks for of -1 / -0x80000000 here,
- since that causes an overflow. We should do that as well,
- but I don't see how to do the comparisons without a temporary
- register. */
- end_noreorder ();
- macro_build (NULL, s, "x", zreg);
- break;
-
- case M_DIVU_3:
- s = "divu";
- s2 = "mflo";
- goto do_divu3;
- case M_REMU_3:
- s = "divu";
- s2 = "mfhi";
- goto do_divu3;
- case M_DDIVU_3:
- s = "ddivu";
- s2 = "mflo";
- goto do_divu3;
- case M_DREMU_3:
- s = "ddivu";
- s2 = "mfhi";
- do_divu3:
- start_noreorder ();
- macro_build (NULL, s, "0,x,y", xreg, yreg);
- expr1.X_add_number = 2;
- macro_build (&expr1, "bnez", "x,p", yreg);
- macro_build (NULL, "break", "6", 7);
- end_noreorder ();
- macro_build (NULL, s2, "x", zreg);
- break;
-
- case M_DMUL:
- dbl = 1;
- case M_MUL:
- macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
- macro_build (NULL, "mflo", "x", zreg);
- break;
-
- case M_DSUBU_I:
- dbl = 1;
- goto do_subu;
- case M_SUBU_I:
- do_subu:
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
- break;
-
- case M_SUBU_I_2:
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, "addiu", "x,k", xreg);
- break;
+ else if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, and there
+ is no base register, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
+ Otherwise, if there is no base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we always use the latter form.
- case M_DSUBU_I_2:
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, "daddiu", "y,j", yreg);
- break;
+ If we have a base register, and this is a reference to a
+ GP relative symbol, we want
+ addu $tempreg,$breg,$gp
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ With a constant we always use the latter case.
- case M_BEQ:
- s = "cmp";
- s2 = "bteqz";
- goto do_branch;
- case M_BNE:
- s = "cmp";
- s2 = "btnez";
- goto do_branch;
- case M_BLT:
- s = "slt";
- s2 = "btnez";
- goto do_branch;
- case M_BLTU:
- s = "sltu";
- s2 = "btnez";
- goto do_branch;
- case M_BLE:
- s = "slt";
- s2 = "bteqz";
- goto do_reverse_branch;
- case M_BLEU:
- s = "sltu";
- s2 = "bteqz";
- goto do_reverse_branch;
- case M_BGE:
- s = "slt";
- s2 = "bteqz";
- goto do_branch;
- case M_BGEU:
- s = "sltu";
- s2 = "bteqz";
- goto do_branch;
- case M_BGT:
- s = "slt";
- s2 = "btnez";
- goto do_reverse_branch;
- case M_BGTU:
- s = "sltu";
- s2 = "btnez";
+ With 64bit address space and no base register and $at usable,
+ we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll32 $tempreg,0
+ daddu $tempreg,$at
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddu $at,$breg
+ dsll32 $tempreg,0
+ daddu $tempreg,$at
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
- do_reverse_branch:
- tmp = xreg;
- xreg = yreg;
- yreg = tmp;
+ Without $at we can't generate the optimal path for superscalar
+ processors here since this would require two temporary registers.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddu $tempreg,$tempreg,$breg
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_LO16)
- do_branch:
- macro_build (NULL, s, "x,y", xreg, yreg);
- macro_build (&offset_expr, s2, "p");
- break;
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (breg == 0)
+ {
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_GPREL16, mips_gp_register);
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_GPREL16, tempreg);
+ }
+ relax_switch ();
+ }
- case M_BEQ_I:
- s = "cmpi";
- s2 = "bteqz";
- s3 = "x,U";
- goto do_branch_i;
- case M_BNE_I:
- s = "cmpi";
- s2 = "btnez";
- s3 = "x,U";
- goto do_branch_i;
- case M_BLT_I:
- s = "slti";
- s2 = "btnez";
- s3 = "x,8";
- goto do_branch_i;
- case M_BLTU_I:
- s = "sltiu";
- s2 = "btnez";
- s3 = "x,8";
- goto do_branch_i;
- case M_BLE_I:
- s = "slti";
- s2 = "btnez";
- s3 = "x,8";
- goto do_addone_branch_i;
- case M_BLEU_I:
- s = "sltiu";
- s2 = "btnez";
- s3 = "x,8";
- goto do_addone_branch_i;
- case M_BGE_I:
- s = "slti";
- s2 = "bteqz";
- s3 = "x,8";
- goto do_branch_i;
- case M_BGEU_I:
- s = "sltiu";
- s2 = "bteqz";
- s3 = "x,8";
- goto do_branch_i;
- case M_BGT_I:
- s = "slti";
- s2 = "bteqz";
- s3 = "x,8";
- goto do_addone_branch_i;
- case M_BGTU_I:
- s = "sltiu";
- s2 = "bteqz";
- s3 = "x,8";
+ if (used_at == 0 && mips_opts.at)
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "lui", LUI_FMT, AT,
+ BFD_RELOC_HI16_S);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_HIGHER);
+ if (breg != 0)
+ macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
+ macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
+ macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+ macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_LO16,
+ tempreg);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ if (breg != 0)
+ macro_build (NULL, "daddu", "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_LO16, tempreg);
+ }
- do_addone_branch_i:
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
+ if (mips_relax.sequence)
+ relax_end ();
+ break;
+ }
- do_branch_i:
- macro_build (&imm_expr, s, s3, xreg);
- macro_build (&offset_expr, s2, "p");
- break;
+ if (breg == 0)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, s, fmt, treg, BFD_RELOC_GPREL16,
+ mips_gp_register);
+ relax_switch ();
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_GPREL16, tempreg);
+ relax_switch ();
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (!mips_big_got)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
- case M_ABS:
- expr1.X_add_number = 0;
- macro_build (&expr1, "slti", "x,8", yreg);
- if (xreg != yreg)
- move_register (xreg, yreg);
- expr1.X_add_number = 2;
- macro_build (&expr1, "bteqz", "p");
- macro_build (NULL, "neg", "x,w", xreg, xreg);
- }
-}
+ /* If this is a reference to an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> $treg,0($tempreg)
-/* For consistency checking, verify that all bits are specified either
- by the match/mask part of the instruction definition, or by the
- operand list. */
-static int
-validate_mips_insn (const struct mips_opcode *opc)
-{
- const char *p = opc->args;
- char c;
- unsigned long used_bits = opc->mask;
+ For NewABI, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
- if ((used_bits & opc->match) != opc->match)
- {
- as_bad (_("internal: bad mips opcode (mask error): %s %s"),
- opc->name, opc->args);
- return 0;
- }
-#define USE_BITS(mask,shift) (used_bits |= ((mask) << (shift)))
- while (*p)
- switch (c = *p++)
- {
- case ',': break;
- case '(': break;
- case ')': break;
- case '+':
- switch (c = *p++)
- {
- case '1': USE_BITS (OP_MASK_UDI1, OP_SH_UDI1); break;
- case '2': USE_BITS (OP_MASK_UDI2, OP_SH_UDI2); break;
- case '3': USE_BITS (OP_MASK_UDI3, OP_SH_UDI3); break;
- case '4': USE_BITS (OP_MASK_UDI4, OP_SH_UDI4); break;
- case 'A': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'B': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
- case 'C': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'D': USE_BITS (OP_MASK_RD, OP_SH_RD);
- USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'E': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'F': USE_BITS (OP_MASK_INSMSB, OP_SH_INSMSB); break;
- case 'G': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'H': USE_BITS (OP_MASK_EXTMSBD, OP_SH_EXTMSBD); break;
- case 'I': break;
- case 'J': USE_BITS (OP_MASK_CODE10, OP_SH_CODE10); break;
- case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'T': USE_BITS (OP_MASK_RT, OP_SH_RT);
- USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'x': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break;
- case 'X': USE_BITS (OP_MASK_BBITIND, OP_SH_BBITIND); break;
- case 'p': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break;
- case 'P': USE_BITS (OP_MASK_CINSPOS, OP_SH_CINSPOS); break;
- case 'Q': USE_BITS (OP_MASK_SEQI, OP_SH_SEQI); break;
- case 's': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
- case 'S': USE_BITS (OP_MASK_CINSLM1, OP_SH_CINSLM1); break;
- case 'z': USE_BITS (OP_MASK_RZ, OP_SH_RZ); break;
- case 'Z': USE_BITS (OP_MASK_FZ, OP_SH_FZ); break;
- case 'a': USE_BITS (OP_MASK_OFFSET_A, OP_SH_OFFSET_A); break;
- case 'b': USE_BITS (OP_MASK_OFFSET_B, OP_SH_OFFSET_B); break;
- case 'c': USE_BITS (OP_MASK_OFFSET_C, OP_SH_OFFSET_C); break;
- case 'j': USE_BITS (OP_MASK_EVAOFFSET, OP_SH_EVAOFFSET); break;
-
- default:
- as_bad (_("internal: bad mips opcode (unknown extension operand type `+%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
- break;
- case '<': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case '>': USE_BITS (OP_MASK_SHAMT, OP_SH_SHAMT); break;
- case 'A': break;
- case 'B': USE_BITS (OP_MASK_CODE20, OP_SH_CODE20); break;
- case 'C': USE_BITS (OP_MASK_COPZ, OP_SH_COPZ); break;
- case 'D': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
- case 'E': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'F': break;
- case 'G': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'H': USE_BITS (OP_MASK_SEL, OP_SH_SEL); break;
- case 'I': break;
- case 'J': USE_BITS (OP_MASK_CODE19, OP_SH_CODE19); break;
- case 'K': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'L': break;
- case 'M': USE_BITS (OP_MASK_CCC, OP_SH_CCC); break;
- case 'N': USE_BITS (OP_MASK_BCC, OP_SH_BCC); break;
- case 'O': USE_BITS (OP_MASK_ALN, OP_SH_ALN); break;
- case 'Q': USE_BITS (OP_MASK_VSEL, OP_SH_VSEL);
- USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'R': USE_BITS (OP_MASK_FR, OP_SH_FR); break;
- case 'S': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'T': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'V': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'W': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'X': USE_BITS (OP_MASK_FD, OP_SH_FD); break;
- case 'Y': USE_BITS (OP_MASK_FS, OP_SH_FS); break;
- case 'Z': USE_BITS (OP_MASK_FT, OP_SH_FT); break;
- case 'a': USE_BITS (OP_MASK_TARGET, OP_SH_TARGET); break;
- case 'b': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 'c': USE_BITS (OP_MASK_CODE, OP_SH_CODE); break;
- case 'd': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- case 'f': break;
- case 'h': USE_BITS (OP_MASK_PREFX, OP_SH_PREFX); break;
- case 'i': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
- case 'j': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'k': USE_BITS (OP_MASK_CACHE, OP_SH_CACHE); break;
- case 'l': break;
- case 'o': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'p': USE_BITS (OP_MASK_DELTA, OP_SH_DELTA); break;
- case 'q': USE_BITS (OP_MASK_CODE2, OP_SH_CODE2); break;
- case 'r': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 's': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 't': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'u': USE_BITS (OP_MASK_IMMEDIATE, OP_SH_IMMEDIATE); break;
- case 'v': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case 'w': USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'x': break;
- case 'z': break;
- case 'P': USE_BITS (OP_MASK_PERFREG, OP_SH_PERFREG); break;
- case 'U': USE_BITS (OP_MASK_RD, OP_SH_RD);
- USE_BITS (OP_MASK_RT, OP_SH_RT); break;
- case 'e': USE_BITS (OP_MASK_VECBYTE, OP_SH_VECBYTE); break;
- case '%': USE_BITS (OP_MASK_VECALIGN, OP_SH_VECALIGN); break;
- case '[': break;
- case ']': break;
- case '1': USE_BITS (OP_MASK_STYPE, OP_SH_STYPE); break;
- case '2': USE_BITS (OP_MASK_BP, OP_SH_BP); break;
- case '3': USE_BITS (OP_MASK_SA3, OP_SH_SA3); break;
- case '4': USE_BITS (OP_MASK_SA4, OP_SH_SA4); break;
- case '5': USE_BITS (OP_MASK_IMM8, OP_SH_IMM8); break;
- case '6': USE_BITS (OP_MASK_RS, OP_SH_RS); break;
- case '7': USE_BITS (OP_MASK_DSPACC, OP_SH_DSPACC); break;
- case '8': USE_BITS (OP_MASK_WRDSP, OP_SH_WRDSP); break;
- case '9': USE_BITS (OP_MASK_DSPACC_S, OP_SH_DSPACC_S);break;
- case '0': USE_BITS (OP_MASK_DSPSFT, OP_SH_DSPSFT); break;
- case '\'': USE_BITS (OP_MASK_RDDSP, OP_SH_RDDSP); break;
- case ':': USE_BITS (OP_MASK_DSPSFT_7, OP_SH_DSPSFT_7);break;
- case '@': USE_BITS (OP_MASK_IMM10, OP_SH_IMM10); break;
- case '!': USE_BITS (OP_MASK_MT_U, OP_SH_MT_U); break;
- case '$': USE_BITS (OP_MASK_MT_H, OP_SH_MT_H); break;
- case '*': USE_BITS (OP_MASK_MTACC_T, OP_SH_MTACC_T); break;
- case '&': USE_BITS (OP_MASK_MTACC_D, OP_SH_MTACC_D); break;
- case '\\': USE_BITS (OP_MASK_3BITPOS, OP_SH_3BITPOS); break;
- case '~': USE_BITS (OP_MASK_OFFSET12, OP_SH_OFFSET12); break;
- case 'g': USE_BITS (OP_MASK_RD, OP_SH_RD); break;
- default:
- as_bad (_("internal: bad mips opcode (unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
-#undef USE_BITS
- if (used_bits != 0xffffffff)
- {
- as_bad (_("internal: bad mips opcode (bits 0x%lx undefined): %s %s"),
- ~used_bits & 0xffffffff, opc->name, opc->args);
- return 0;
- }
- return 1;
-}
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ gas_assert (offset_expr.X_op == O_symbol);
+ if (HAVE_NEWABI)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+ break;
+ }
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ lw_reloc_type, mips_gp_register);
+ load_delay_nop ();
+ relax_start (offset_expr.X_add_symbol);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_LO16);
+ relax_end ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_big_got && !HAVE_NEWABI)
+ {
+ int gpdelay;
-/* For consistency checking, verify that the length implied matches the
- major opcode and that all bits are specified either by the match/mask
- part of the instruction definition, or by the operand list. */
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> $treg,0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> $treg,0($tempreg)
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ gas_assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ gpdelay = reg_needs_delay (mips_gp_register);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_LO16, tempreg);
+ relax_switch ();
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_LO16);
+ relax_end ();
-static int
-validate_micromips_insn (const struct mips_opcode *opc)
-{
- unsigned long match = opc->match;
- unsigned long mask = opc->mask;
- const char *p = opc->args;
- unsigned long insn_bits;
- unsigned long used_bits;
- unsigned long major;
- unsigned int length;
- char e;
- char c;
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_big_got && HAVE_NEWABI)
+ {
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> $treg,<ofst>($tempreg)
+ Otherwise, for local symbols, we want:
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST) */
+ gas_assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_LO16, tempreg);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, treg, BFD_RELOC_LO16, tempreg);
- if ((mask & match) != match)
- {
- as_bad (_("Internal error: bad microMIPS opcode (mask error): %s %s"),
- opc->name, opc->args);
- return 0;
- }
- length = micromips_insn_length (opc);
- if (length != 2 && length != 4)
- {
- as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
- "%s %s"), length, opc->name, opc->args);
- return 0;
- }
- major = match >> (10 + 8 * (length - 2));
- if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
- || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
- {
- as_bad (_("Internal error: bad microMIPS opcode "
- "(opcode/length mismatch): %s %s"), opc->name, opc->args);
- return 0;
- }
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, treg,
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+ relax_end ();
+ }
+ else
+ abort ();
- /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
- insn_bits = 1 << 4 * length;
- insn_bits <<= 4 * length;
- insn_bits -= 1;
- used_bits = mask;
-#define USE_BITS(field) \
- (used_bits |= MICROMIPSOP_MASK_##field << MICROMIPSOP_SH_##field)
- while (*p)
- switch (c = *p++)
- {
- case ',': break;
- case '(': break;
- case ')': break;
- case '+':
- e = c;
- switch (c = *p++)
- {
- case 'A': USE_BITS (EXTLSB); break;
- case 'B': USE_BITS (INSMSB); break;
- case 'C': USE_BITS (EXTMSBD); break;
- case 'D': USE_BITS (RS); USE_BITS (SEL); break;
- case 'E': USE_BITS (EXTLSB); break;
- case 'F': USE_BITS (INSMSB); break;
- case 'G': USE_BITS (EXTMSBD); break;
- case 'H': USE_BITS (EXTMSBD); break;
- case 'j': USE_BITS (EVAOFFSET); break;
- default:
- as_bad (_("Internal error: bad mips opcode "
- "(unknown extension operand type `%c%c'): %s %s"),
- e, c, opc->name, opc->args);
- return 0;
- }
- break;
- case 'm':
- e = c;
- switch (c = *p++)
- {
- case 'A': USE_BITS (IMMA); break;
- case 'B': USE_BITS (IMMB); break;
- case 'C': USE_BITS (IMMC); break;
- case 'D': USE_BITS (IMMD); break;
- case 'E': USE_BITS (IMME); break;
- case 'F': USE_BITS (IMMF); break;
- case 'G': USE_BITS (IMMG); break;
- case 'H': USE_BITS (IMMH); break;
- case 'I': USE_BITS (IMMI); break;
- case 'J': USE_BITS (IMMJ); break;
- case 'L': USE_BITS (IMML); break;
- case 'M': USE_BITS (IMMM); break;
- case 'N': USE_BITS (IMMN); break;
- case 'O': USE_BITS (IMMO); break;
- case 'P': USE_BITS (IMMP); break;
- case 'Q': USE_BITS (IMMQ); break;
- case 'U': USE_BITS (IMMU); break;
- case 'W': USE_BITS (IMMW); break;
- case 'X': USE_BITS (IMMX); break;
- case 'Y': USE_BITS (IMMY); break;
- case 'Z': break;
- case 'a': break;
- case 'b': USE_BITS (MB); break;
- case 'c': USE_BITS (MC); break;
- case 'd': USE_BITS (MD); break;
- case 'e': USE_BITS (ME); break;
- case 'f': USE_BITS (MF); break;
- case 'g': USE_BITS (MG); break;
- case 'h': USE_BITS (MH); break;
- case 'i': USE_BITS (MI); break;
- case 'j': USE_BITS (MJ); break;
- case 'l': USE_BITS (ML); break;
- case 'm': USE_BITS (MM); break;
- case 'n': USE_BITS (MN); break;
- case 'p': USE_BITS (MP); break;
- case 'q': USE_BITS (MQ); break;
- case 'r': break;
- case 's': break;
- case 't': break;
- case 'x': break;
- case 'y': break;
- case 'z': break;
- default:
- as_bad (_("Internal error: bad mips opcode "
- "(unknown extension operand type `%c%c'): %s %s"),
- e, c, opc->name, opc->args);
- return 0;
- }
- break;
- case '.': USE_BITS (OFFSET10); break;
- case '1': USE_BITS (STYPE); break;
- case '2': USE_BITS (BP); break;
- case '3': USE_BITS (SA3); break;
- case '4': USE_BITS (SA4); break;
- case '5': USE_BITS (IMM8); break;
- case '6': USE_BITS (RS); break;
- case '7': USE_BITS (DSPACC); break;
- case '8': USE_BITS (WRDSP); break;
- case '0': USE_BITS (DSPSFT); break;
- case '<': USE_BITS (SHAMT); break;
- case '>': USE_BITS (SHAMT); break;
- case '@': USE_BITS (IMM10); break;
- case 'B': USE_BITS (CODE10); break;
- case 'C': USE_BITS (COPZ); break;
- case 'D': USE_BITS (FD); break;
- case 'E': USE_BITS (RT); break;
- case 'G': USE_BITS (RS); break;
- case 'H': USE_BITS (SEL); break;
- case 'K': USE_BITS (RS); break;
- case 'M': USE_BITS (CCC); break;
- case 'N': USE_BITS (BCC); break;
- case 'R': USE_BITS (FR); break;
- case 'S': USE_BITS (FS); break;
- case 'T': USE_BITS (FT); break;
- case 'V': USE_BITS (FS); break;
- case '\\': USE_BITS (3BITPOS); break;
- case '^': USE_BITS (RD); break;
- case 'a': USE_BITS (TARGET); break;
- case 'b': USE_BITS (RS); break;
- case 'c': USE_BITS (CODE); break;
- case 'd': USE_BITS (RD); break;
- case 'h': USE_BITS (PREFX); break;
- case 'i': USE_BITS (IMMEDIATE); break;
- case 'j': USE_BITS (DELTA); break;
- case 'k': USE_BITS (CACHE); break;
- case 'n': USE_BITS (RT); break;
- case 'o': USE_BITS (DELTA); break;
- case 'p': USE_BITS (DELTA); break;
- case 'q': USE_BITS (CODE2); break;
- case 'r': USE_BITS (RS); break;
- case 's': USE_BITS (RS); break;
- case 't': USE_BITS (RT); break;
- case 'u': USE_BITS (IMMEDIATE); break;
- case 'v': USE_BITS (RS); break;
- case 'w': USE_BITS (RT); break;
- case 'y': USE_BITS (RS3); break;
- case 'z': break;
- case '|': USE_BITS (TRAP); break;
- case '~': USE_BITS (OFFSET12); break;
- default:
- as_bad (_("Internal error: bad microMIPS opcode "
- "(unknown operand type `%c'): %s %s"),
- c, opc->name, opc->args);
- return 0;
- }
-#undef USE_BITS
- if (used_bits != insn_bits)
- {
- if (~used_bits & insn_bits)
- as_bad (_("Internal error: bad microMIPS opcode "
- "(bits 0x%lx undefined): %s %s"),
- ~used_bits & insn_bits, opc->name, opc->args);
- if (used_bits & ~insn_bits)
- as_bad (_("Internal error: bad microMIPS opcode "
- "(bits 0x%lx defined): %s %s"),
- used_bits & ~insn_bits, opc->name, opc->args);
- return 0;
- }
- return 1;
-}
+ break;
-/* UDI immediates. */
-struct mips_immed {
- char type;
- unsigned int shift;
- unsigned long mask;
- const char * desc;
-};
+ case M_JRADDIUSP:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ start_noreorder ();
+ macro_build (NULL, "jr", "s", RA);
+ expr1.X_add_number = EXTRACT_OPERAND (1, IMMP, *ip) << 2;
+ macro_build (&expr1, "addiu", "t,r,j", SP, SP, BFD_RELOC_LO16);
+ end_noreorder ();
+ break;
-static const struct mips_immed mips_immed[] = {
- { '1', OP_SH_UDI1, OP_MASK_UDI1, 0},
- { '2', OP_SH_UDI2, OP_MASK_UDI2, 0},
- { '3', OP_SH_UDI3, OP_MASK_UDI3, 0},
- { '4', OP_SH_UDI4, OP_MASK_UDI4, 0},
- { 0,0,0,0 }
-};
+ case M_JRC:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ macro_build (NULL, "jr", "s", sreg);
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ break;
-/* 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;
+ case M_LI:
+ case M_LI_S:
+ load_register (treg, &imm_expr, 0);
+ break;
- if (insn->pinfo == INSN_MACRO)
- /* Let a macro pass, we'll catch it later when it is expanded. */
- return 1;
+ case M_DLI:
+ load_register (treg, &imm_expr, 1);
+ break;
- 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 M_LI_SS:
+ if (imm_expr.X_op == O_constant)
{
- case FP_S:
- case 0:
- return 1; /* both single precision - ok */
- case FP_D:
- return 0; /* both double precision - fail */
- default:
+ used_at = 1;
+ load_register (AT, &imm_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, treg);
+ break;
+ }
+ else
+ {
+ gas_assert (offset_expr.X_op == O_symbol
+ && strcmp (segment_name (S_GET_SEGMENT
+ (offset_expr.X_add_symbol)),
+ ".lit4") == 0
+ && offset_expr.X_add_number == 0);
+ macro_build (&offset_expr, "lwc1", "T,o(b)", treg,
+ BFD_RELOC_MIPS_LITERAL, mips_gp_register);
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;
-}
+ case M_LI_D:
+ /* Check if we have a constant in IMM_EXPR. If the GPRs are 64 bits
+ wide, IMM_EXPR is the entire value. Otherwise IMM_EXPR is the high
+ order 32 bits of the value and the low order 32 bits are either
+ zero or in OFFSET_EXPR. */
+ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ {
+ if (HAVE_64BIT_GPRS)
+ load_register (treg, &imm_expr, 1);
+ else
+ {
+ int hreg, lreg;
-/* Check if EXPR is a constant between MIN (inclusive) and MAX (exclusive)
- taking bits from BIT up. */
-static int
-expr_const_in_range (expressionS *ep, offsetT min, offsetT max, int bit)
-{
- return (ep->X_op == O_constant
- && (ep->X_add_number & ((1 << bit) - 1)) == 0
- && ep->X_add_number >= min << bit
- && ep->X_add_number < max << bit);
-}
+ if (target_big_endian)
+ {
+ hreg = treg;
+ lreg = treg + 1;
+ }
+ else
+ {
+ hreg = treg + 1;
+ lreg = treg;
+ }
-/* This routine assembles an instruction into its binary format. As a
- side effect, it sets one of the global variables imm_reloc or
- offset_reloc to the type of relocation to do if one of the operands
- is an address expression. */
+ if (hreg <= 31)
+ load_register (hreg, &imm_expr, 0);
+ if (lreg <= 31)
+ {
+ if (offset_expr.X_op == O_absent)
+ move_register (lreg, 0);
+ else
+ {
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (lreg, &offset_expr, 0);
+ }
+ }
+ }
+ break;
+ }
-static void
-mips_ip (char *str, struct mips_cl_insn *ip)
-{
- bfd_boolean wrong_delay_slot_insns = FALSE;
- bfd_boolean need_delay_slot_ok = TRUE;
- struct mips_opcode *firstinsn = NULL;
- const struct mips_opcode *past;
- struct hash_control *hash;
- char *s;
- const char *args;
- char c = 0;
- struct mips_opcode *insn;
- char *argsStart;
- unsigned int regno;
- unsigned int lastregno;
- unsigned int destregno = 0;
- unsigned int lastpos = 0;
- unsigned int limlo, limhi;
- int sizelo;
- char *s_reset;
- offsetT min_range, max_range;
- long opend;
- char *name;
- int argnum;
- unsigned int rtype;
- char *dot;
- long end;
+ /* We know that sym is in the .rdata section. First we get the
+ upper 16 bits of the address. */
+ if (mips_pic == NO_PIC)
+ {
+ macro_build_lui (&offset_expr, AT);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ used_at = 1;
+ }
- insn_error = NULL;
+ /* Now we load the register(s). */
+ if (HAVE_64BIT_GPRS)
+ {
+ used_at = 1;
+ macro_build (&offset_expr, "ld", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ }
+ else
+ {
+ used_at = 1;
+ macro_build (&offset_expr, "lw", "t,o(b)", treg, BFD_RELOC_LO16, AT);
+ if (treg != RA)
+ {
+ /* FIXME: How in the world do we deal with the possible
+ overflow here? */
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, "lw", "t,o(b)",
+ treg + 1, BFD_RELOC_LO16, AT);
+ }
+ }
+ break;
- if (mips_opts.micromips)
- {
- hash = micromips_op_hash;
- past = µmips_opcodes[bfd_micromips_num_opcodes];
- }
- else
- {
- hash = op_hash;
- past = &mips_opcodes[NUMOPCODES];
- }
- forced_insn_length = 0;
- insn = NULL;
+ case M_LI_DD:
+ /* Check if we have a constant in IMM_EXPR. If the FPRs are 64 bits
+ wide, IMM_EXPR is the entire value and the GPRs are known to be 64
+ bits wide as well. Otherwise IMM_EXPR is the high order 32 bits of
+ the value and the low order 32 bits are either zero or in
+ OFFSET_EXPR. */
+ if (imm_expr.X_op == O_constant || imm_expr.X_op == O_big)
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_FPRS);
+ if (HAVE_64BIT_FPRS)
+ {
+ gas_assert (HAVE_64BIT_GPRS);
+ macro_build (NULL, "dmtc1", "t,S", AT, treg);
+ }
+ else
+ {
+ macro_build (NULL, "mtc1", "t,G", AT, treg + 1);
+ if (offset_expr.X_op == O_absent)
+ macro_build (NULL, "mtc1", "t,G", 0, treg);
+ else
+ {
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (AT, &offset_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, treg);
+ }
+ }
+ break;
+ }
- /* We first try to match an instruction up to a space or to the end. */
- for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
- continue;
+ gas_assert (offset_expr.X_op == O_symbol
+ && offset_expr.X_add_number == 0);
+ s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
+ if (strcmp (s, ".lit8") == 0)
+ {
+ breg = mips_gp_register;
+ offset_reloc[0] = BFD_RELOC_MIPS_LITERAL;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ }
+ else
+ {
+ gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+ used_at = 1;
+ if (mips_pic != NO_PIC)
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ else
+ {
+ /* FIXME: This won't work for a 64 bit address. */
+ macro_build_lui (&offset_expr, AT);
+ }
- /* Make a copy of the instruction so that we can fiddle with it. */
- name = alloca (end + 1);
- memcpy (name, str, end);
- name[end] = '\0';
+ breg = AT;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ }
+ align = 8;
+ /* Fall through */
- for (;;)
- {
- insn = (struct mips_opcode *) hash_find (hash, name);
+ case M_L_DAB:
+ /*
+ * The MIPS assembler seems to check for X_add_number not
+ * being double aligned and generating:
+ * lui at,%hi(foo+1)
+ * addu at,at,v1
+ * addiu at,at,%lo(foo+1)
+ * lwc1 f2,0(at)
+ * lwc1 f3,4(at)
+ * But, the resulting address is the same after relocation so why
+ * generate the extra instruction?
+ */
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ fmt = "T,o(b)";
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
+ {
+ s = "ldc1";
+ goto ld_st;
+ }
+ s = "lwc1";
+ goto ldd_std;
- if (insn != NULL || !mips_opts.micromips)
- break;
- if (forced_insn_length)
- break;
+ case M_S_DAB:
+ gas_assert (!mips_opts.micromips);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ fmt = "T,o(b)";
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
+ {
+ s = "sdc1";
+ goto ld_st;
+ }
+ s = "swc1";
+ goto ldd_std;
- /* See if there's an instruction size override suffix,
- either `16' or `32', at the end of the mnemonic proper,
- that defines the operation, i.e. before the first `.'
- character if any. Strip it and retry. */
- dot = strchr (name, '.');
- opend = dot != NULL ? dot - name : end;
- if (opend < 3)
- break;
- if (name[opend - 2] == '1' && name[opend - 1] == '6')
- forced_insn_length = 2;
- else if (name[opend - 2] == '3' && name[opend - 1] == '2')
- forced_insn_length = 4;
- else
- break;
- memcpy (name + opend - 2, name + opend, end - opend + 1);
- }
- if (insn == NULL)
- {
- insn_error = _("Unrecognized opcode");
- return;
- }
+ case M_LQ_AB:
+ fmt = "t,o(b)";
+ s = "lq";
+ goto ld;
- /* For microMIPS instructions placed in a fixed-length branch delay slot
- we make up to two passes over the relevant fragment of the opcode
- table. First we try instructions that meet the delay slot's length
- requirement. If none matched, then we retry with the remaining ones
- and if one matches, then we use it and then issue an appropriate
- warning later on. */
- argsStart = s = str + end;
- for (;;)
- {
- bfd_boolean delay_slot_ok;
- bfd_boolean size_ok;
- bfd_boolean ok;
+ case M_SQ_AB:
+ fmt = "t,o(b)";
+ s = "sq";
+ goto ld_st;
- gas_assert (strcmp (insn->name, name) == 0);
+ case M_LD_AB:
+ fmt = "t,o(b)";
+ if (HAVE_64BIT_GPRS)
+ {
+ s = "ld";
+ goto ld;
+ }
+ s = "lw";
+ goto ldd_std;
- ok = is_opcode_valid (insn);
- size_ok = is_size_valid (insn);
- delay_slot_ok = is_delay_slot_valid (insn);
- if (!delay_slot_ok && !wrong_delay_slot_insns)
+ case M_SD_AB:
+ fmt = "t,o(b)";
+ if (HAVE_64BIT_GPRS)
{
- firstinsn = insn;
- wrong_delay_slot_insns = TRUE;
+ s = "sd";
+ goto ld_st;
}
- if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
- {
- static char buf[256];
+ s = "sw";
+
+ ldd_std:
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when loading from memory. We set coproc if we must
+ load $fn+1 first. */
+ /* Itbl support may require additional care here. */
+ if (!target_big_endian)
+ coproc = 0;
- if (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
+ if (small_offset_p (0, align, 16))
+ {
+ ep = &offset_expr;
+ if (!small_offset_p (4, align, 16))
{
- ++insn;
- continue;
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", AT, breg,
+ -1, offset_reloc[0], offset_reloc[1],
+ offset_reloc[2]);
+ expr1.X_add_number = 0;
+ ep = &expr1;
+ breg = AT;
+ used_at = 1;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
}
- if (wrong_delay_slot_insns && need_delay_slot_ok)
+ if (strcmp (s, "lw") == 0 && treg == breg)
{
- gas_assert (firstinsn);
- need_delay_slot_ok = FALSE;
- past = insn + 1;
- insn = firstinsn;
- continue;
+ ep->X_add_number += 4;
+ macro_build (ep, s, fmt, treg + 1, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ ep->X_add_number -= 4;
+ macro_build (ep, s, fmt, treg, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
}
-
- if (insn_error)
- return;
-
- if (!ok)
- sprintf (buf, _("Opcode not supported on this processor: %s (%s)"),
- mips_cpu_info_from_arch (mips_opts.arch)->name,
- mips_cpu_info_from_isa (mips_opts.isa)->name);
- else if (mips_opts.insn32)
- sprintf (buf, _("Opcode not supported in the `insn32' mode"));
else
- sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
- 8 * forced_insn_length);
- insn_error = buf;
-
- return;
+ {
+ macro_build (ep, s, fmt, coproc ? treg + 1 : treg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2],
+ breg);
+ ep->X_add_number += 4;
+ macro_build (ep, s, fmt, coproc ? treg : treg + 1, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2],
+ breg);
+ }
+ break;
}
- create_insn (ip, insn);
- insn_error = NULL;
- argnum = 1;
- lastregno = 0xffffffff;
- for (args = insn->args;; ++args)
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
{
- int is_mdmx;
-
- s += strspn (s, " \t");
- is_mdmx = 0;
- switch (*args)
- {
- case '\0': /* end of args */
- if (*s == '\0')
- return;
- break;
-
- case '2':
- /* DSP 2-bit unsigned immediate in bit 11 (for standard MIPS
- code) or 14 (for microMIPS code). */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number != 1
- && (unsigned long) imm_expr.X_add_number != 3)
- {
- as_bad (_("BALIGN immediate not 1 or 3 (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (mips_opts.micromips,
- BP, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case '3':
- /* DSP 3-bit unsigned immediate in bit 21 (for standard MIPS
- code) or 13 (for microMIPS code). */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_SA3 : OP_MASK_SA3);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- SA3, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '4':
- /* DSP 4-bit unsigned immediate in bit 21 (for standard MIPS
- code) or 12 (for microMIPS code). */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_SA4 : OP_MASK_SA4);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- SA4, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '5':
- /* DSP 8-bit unsigned immediate in bit 16 (for standard MIPS
- code) or 13 (for microMIPS code). */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_IMM8 : OP_MASK_IMM8);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- IMM8, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '6':
- /* DSP 5-bit unsigned immediate in bit 21 (for standard MIPS
- code) or 16 (for microMIPS code). */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_RS : OP_MASK_RS);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- RS, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '7':
- /* Four DSP accumulators in bit 11 (for standard MIPS code)
- or 14 (for microMIPS code). */
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c'
- && s[3] >= '0' && s[3] <= '3')
- {
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (mips_opts.micromips, DSPACC, *ip, regno);
- continue;
- }
- else
- as_bad (_("Invalid dsp acc register"));
- break;
-
- case '8':
- /* DSP 6-bit unsigned immediate in bit 11 (for standard MIPS
- code) or 14 (for microMIPS code). */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_WRDSP
- : OP_MASK_WRDSP);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_bad (_("DSP immediate not in range 0..%lu (%lu)"),
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- WRDSP, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '9': /* Four DSP accumulators in bits 21,22. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c'
- && s[3] >= '0' && s[3] <= '3')
- {
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, DSPACC_S, *ip, regno);
- continue;
- }
- else
- as_bad (_("Invalid dsp acc register"));
- break;
-
- case '0':
- /* DSP 6-bit signed immediate in bit 20 (for standard MIPS
- code) or 16 (for microMIPS code). */
- {
- long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_DSPSFT : OP_MASK_DSPSFT);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((mask + 1) >> 1);
- max_range = ((mask + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range
- || imm_expr.X_add_number > max_range)
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- DSPSFT, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '\'': /* DSP 6-bit unsigned immediate in bit 16. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~OP_MASK_RDDSP)
- {
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- OP_MASK_RDDSP,
- (unsigned long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, RDDSP, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case ':': /* DSP 7-bit signed immediate in bit 19. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_DSPSFT_7 + 1) >> 1);
- max_range = ((OP_MASK_DSPSFT_7 + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range ||
- imm_expr.X_add_number > max_range)
- {
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, DSPSFT_7, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case '@': /* DSP 10-bit signed immediate in bit 16. */
- {
- long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_IMM10 : OP_MASK_IMM10);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((mask + 1) >> 1);
- max_range = ((mask + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range
- || imm_expr.X_add_number > max_range)
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- IMM10, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case '^': /* DSP 5-bit unsigned immediate in bit 11. */
- gas_assert (mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~MICROMIPSOP_MASK_RD)
- as_bad (_("DSP immediate not in range 0..%d (%lu)"),
- MICROMIPSOP_MASK_RD,
- (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (1, RD, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case '!': /* MT usermode flag bit. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~OP_MASK_MT_U)
- as_bad (_("MT usermode bit not 0 or 1 (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (0, MT_U, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ as_bad (_("Expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
- case '$': /* MT load high flag bit. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number & ~OP_MASK_MT_H)
- as_bad (_("MT load high bit not 0 or 1 (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (0, MT_H, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
- case '*': /* Four DSP accumulators in bits 18,19. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
- s[3] >= '0' && s[3] <= '3')
- {
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, MTACC_T, *ip, regno);
- continue;
- }
- else
- as_bad (_("Invalid dsp/smartmips acc register"));
- break;
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("Number (0x%s) larger than 32 bits"), value);
+ }
- case '&': /* Four DSP accumulators in bits 13,14. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && s[1] == 'a' && s[2] == 'c' &&
- s[3] >= '0' && s[3] <= '3')
+ if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ <op> $treg,<sym>($gp) (BFD_RELOC_GPREL16)
+ <op> $treg+1,<sym>+4($gp) (BFD_RELOC_GPREL16)
+ If we have a base register, we use this
+ addu $at,$breg,$gp
+ <op> $treg,<sym>($at) (BFD_RELOC_GPREL16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_GPREL16)
+ If this is not a GP relative symbol, we want
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register, we add it to $at after the
+ lui instruction. If there is a constant, we always use
+ the last case. */
+ if (offset_expr.X_op == O_symbol
+ && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (breg == 0)
{
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, MTACC_D, *ip, regno);
- continue;
+ tempreg = mips_gp_register;
}
else
- as_bad (_("Invalid dsp/smartmips acc register"));
- break;
-
- case '\\': /* 3-bit bit position. */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_3BITPOS
- : OP_MASK_3BITPOS);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Bit position for %s not in range 0..%lu (%lu)"),
- ip->insn_mo->name,
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- 3BITPOS, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case ',':
- ++argnum;
- if (*s++ == *args)
- continue;
- s--;
- switch (*++args)
{
- case 'r':
- case 'v':
- INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
- continue;
-
- case 'w':
- INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
- continue;
-
- case 'W':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, FT, *ip, lastregno);
- continue;
-
- case 'V':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
- continue;
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, breg, mips_gp_register);
+ tempreg = AT;
+ used_at = 1;
}
- break;
-
- case '(':
- /* Handle optional base register.
- Either the base register is omitted or
- we must have a left paren. */
- /* This is dependent on the next operand specifier
- is a base register specification. */
- gas_assert (args[1] == 'b'
- || (mips_opts.micromips
- && args[1] == 'm'
- && (args[2] == 'l' || args[2] == 'n'
- || args[2] == 's' || args[2] == 'a')));
- if (*s == '\0' && args[1] == 'b')
- return;
- /* Fall through. */
-
- case ')': /* These must match exactly. */
- if (*s++ == *args)
- continue;
- break;
-
- case '[': /* These must match exactly. */
- case ']':
- gas_assert (!mips_opts.micromips);
- if (*s++ == *args)
- continue;
- break;
- case '+': /* Opcode extension character. */
- switch (*++args)
- {
- case '1': /* UDI immediates. */
- case '2':
- case '3':
- case '4':
- gas_assert (!mips_opts.micromips);
- {
- const struct mips_immed *imm = mips_immed;
-
- while (imm->type && imm->type != *args)
- ++imm;
- if (! imm->type)
- abort ();
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number & ~imm->mask)
- {
- as_warn (_("Illegal %s number (%lu, 0x%lx)"),
- imm->desc ? imm->desc : ip->insn_mo->name,
- (unsigned long) imm_expr.X_add_number,
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number &= imm->mask;
- }
- ip->insn_opcode |= ((unsigned long) imm_expr.X_add_number
- << imm->shift);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case 'J': /* 10-bit hypcall code. */
- gas_assert (!mips_opts.micromips);
- {
- unsigned long mask = OP_MASK_CODE10;
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Code for %s not in range 0..%lu (%lu)"),
- ip->insn_mo->name,
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (0, CODE10, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
-
- case 'A': /* ins/ext position, becomes LSB. */
- limlo = 0;
- limhi = 31;
- goto do_lsb;
- case 'E':
- limlo = 32;
- limhi = 63;
- goto do_lsb;
- do_lsb:
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number < limlo
- || (unsigned long) imm_expr.X_add_number > limhi)
- {
- as_bad (_("Improper position (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = limlo;
- }
- lastpos = imm_expr.X_add_number;
- INSERT_OPERAND (mips_opts.micromips,
- EXTLSB, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'B': /* ins size, becomes MSB. */
- limlo = 1;
- limhi = 32;
- goto do_msb;
- case 'F':
- limlo = 33;
- limhi = 64;
- goto do_msb;
- do_msb:
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- /* Check for negative input so that small negative numbers
- will not succeed incorrectly. The checks against
- (pos+size) transitively check "size" itself,
- assuming that "pos" is reasonable. */
- if ((long) imm_expr.X_add_number < 0
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) < limlo
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) > limhi)
- {
- as_bad (_("Improper insert size (%lu, position %lu)"),
- (unsigned long) imm_expr.X_add_number,
- (unsigned long) lastpos);
- imm_expr.X_add_number = limlo - lastpos;
- }
- INSERT_OPERAND (mips_opts.micromips, INSMSB, *ip,
- lastpos + imm_expr.X_add_number - 1);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'C': /* ext size, becomes MSBD. */
- limlo = 1;
- limhi = 32;
- sizelo = 1;
- goto do_msbd;
- case 'G':
- limlo = 33;
- limhi = 64;
- sizelo = 33;
- goto do_msbd;
- case 'H':
- limlo = 33;
- limhi = 64;
- sizelo = 1;
- goto do_msbd;
- do_msbd:
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- /* The checks against (pos+size) don't transitively check
- "size" itself, assuming that "pos" is reasonable.
- We also need to check the lower bound of "size". */
- if ((long) imm_expr.X_add_number < sizelo
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) < limlo
- || ((unsigned long) imm_expr.X_add_number
- + lastpos) > limhi)
- {
- as_bad (_("Improper extract size (%lu, position %lu)"),
- (unsigned long) imm_expr.X_add_number,
- (unsigned long) lastpos);
- imm_expr.X_add_number = limlo - lastpos;
- }
- INSERT_OPERAND (mips_opts.micromips,
- EXTMSBD, *ip, imm_expr.X_add_number - 1);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- case 'D':
- /* +D is for disassembly only; never match. */
- break;
-
- case 'I':
- /* "+I" is like "I", except that imm2_expr is used. */
- my_getExpression (&imm2_expr, s);
- if (imm2_expr.X_op != O_big
- && imm2_expr.X_op != O_constant)
- insn_error = _("absolute expression required");
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (&imm2_expr);
- s = expr_end;
- continue;
-
- case 'T': /* Coprocessor register. */
- gas_assert (!mips_opts.micromips);
- /* +T is for disassembly only; never match. */
- break;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_GPREL16, tempreg);
+ offset_expr.X_add_number += 4;
- case 't': /* Coprocessor register number. */
- gas_assert (!mips_opts.micromips);
- if (s[0] == '$' && ISDIGIT (s[1]))
- {
- ++s;
- regno = 0;
- do
- {
- regno *= 10;
- regno += *s - '0';
- ++s;
- }
- while (ISDIGIT (*s));
- if (regno > 31)
- as_bad (_("Invalid register number (%d)"), regno);
- else
- {
- INSERT_OPERAND (0, RT, *ip, regno);
- continue;
- }
- }
- else
- as_bad (_("Invalid coprocessor 0 register number"));
- break;
+ /* Set mips_optimize to 2 to avoid inserting an
+ undesired nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_GPREL16, tempreg);
+ mips_optimize = hold_mips_optimize;
- case 'x':
- /* bbit[01] and bbit[01]32 bit index. Give error if index
- is not in the valid range. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned) imm_expr.X_add_number > 31)
- {
- as_bad (_("Improper bit index (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ relax_switch ();
- case 'X':
- /* bbit[01] bit index when bbit is used but we generate
- bbit[01]32 because the index is over 32. Move to the
- next candidate if index is not in the valid range. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned) imm_expr.X_add_number < 32
- || (unsigned) imm_expr.X_add_number > 63)
- break;
- INSERT_OPERAND (0, BBITIND, *ip, imm_expr.X_add_number - 32);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ offset_expr.X_add_number -= 4;
+ }
+ used_at = 1;
+ if (offset_high_part (offset_expr.X_add_number, 16)
+ != offset_high_part (offset_expr.X_add_number + 4, 16))
+ {
+ load_address (AT, &offset_expr, &used_at);
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else
+ macro_build_lui (&offset_expr, AT);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ /* FIXME: How do we handle overflow here? */
+ offset_expr.X_add_number += 4;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else if (!mips_big_got)
+ {
+ /* If this is a reference to an external symbol, we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,0($at)
+ <op> $treg+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ load_got_offset (AT, &offset_expr);
+ load_delay_nop ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
- case 'p':
- /* cins, cins32, exts and exts32 position field. Give error
- if it's not in the valid range. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned) imm_expr.X_add_number > 31)
- {
- as_bad (_("Improper position (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- /* Make the pos explicit to simplify +S. */
- lastpos = imm_expr.X_add_number + 32;
- INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
- case 'P':
- /* cins, cins32, exts and exts32 position field. Move to
- the next candidate if it's not in the valid range. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned) imm_expr.X_add_number < 32
- || (unsigned) imm_expr.X_add_number > 63)
- break;
- lastpos = imm_expr.X_add_number;
- INSERT_OPERAND (0, CINSPOS, *ip, imm_expr.X_add_number - 32);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* Itbl support may require additional care here. */
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+ macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ relax_switch ();
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ relax_end ();
- case 's':
- /* cins and exts length-minus-one field. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 31)
- {
- as_bad (_("Improper size (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ mips_optimize = hold_mips_optimize;
+ }
+ else if (mips_big_got)
+ {
+ int gpdelay;
- case 'S':
- /* cins32/exts32 and cins/exts aliasing cint32/exts32
- length-minus-one field. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((long) imm_expr.X_add_number < 0
- || (unsigned long) imm_expr.X_add_number + lastpos > 63)
- {
- as_bad (_("Improper size (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- INSERT_OPERAND (0, CINSLM1, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* If this is a reference to an external symbol, we want
+ lui $at,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $at,$at,$gp
+ lw $at,<sym>($at) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ <op> $treg,0($at)
+ <op> $treg+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> $treg,<sym>($at) (BFD_RELOC_LO16)
+ <op> $treg+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ gpdelay = reg_needs_delay (mips_gp_register);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ AT, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, AT, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ AT, BFD_RELOC_MIPS_GOT_LO16, AT);
+ load_delay_nop ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&expr1, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
- case 'Q':
- /* seqi/snei immediate field. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((long) imm_expr.X_add_number < -512
- || (long) imm_expr.X_add_number >= 512)
- {
- as_bad (_("Improper immediate (%ld)"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
- INSERT_OPERAND (0, SEQI, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&expr1, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ expr1.X_add_number -= 4;
- case 'a': /* 8-bit signed offset in bit 6 */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_OFFSET_A + 1) >> 1);
- max_range = ((OP_MASK_OFFSET_A + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range
- || imm_expr.X_add_number > max_range)
- {
- as_bad (_("Offset not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, OFFSET_A, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg + 1 : treg,
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
- case 'b': /* 8-bit signed offset in bit 3 */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_OFFSET_B + 1) >> 1);
- max_range = ((OP_MASK_OFFSET_B + 1) >> 1) - 1;
- if (imm_expr.X_add_number < min_range
- || imm_expr.X_add_number > max_range)
- {
- as_bad (_("Offset not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- INSERT_OPERAND (0, OFFSET_B, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? treg : treg + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ relax_end ();
+ }
+ else
+ abort ();
- case 'c': /* 9-bit signed offset in bit 6 */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- min_range = -((OP_MASK_OFFSET_C + 1) >> 1);
- max_range = ((OP_MASK_OFFSET_C + 1) >> 1) - 1;
- /* We check the offset range before adjusted. */
- min_range <<= 4;
- max_range <<= 4;
- if (imm_expr.X_add_number < min_range
- || imm_expr.X_add_number > max_range)
- {
- as_bad (_("Offset not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
- }
- if (imm_expr.X_add_number & 0xf)
- {
- as_bad (_("Offset not 16 bytes alignment (%ld)"),
- (long) imm_expr.X_add_number);
- }
- /* Right shift 4 bits to adjust the offset operand. */
- INSERT_OPERAND (0, OFFSET_C, *ip,
- imm_expr.X_add_number >> 4);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ break;
+
+ case M_SAA_AB:
+ s = "saa";
+ offbits = 0;
+ fmt = "t,(b)";
+ goto ld_st;
+ case M_SAAD_AB:
+ s = "saad";
+ offbits = 0;
+ fmt = "t,(b)";
+ goto ld_st;
- case 'z':
- gas_assert (!mips_opts.micromips);
- if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
- break;
- 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);
- }
- INSERT_OPERAND (0, RZ, *ip, regno);
- continue;
+ /* New code added to support COPZ instructions.
+ This code builds table entries out of the macros in mip_opcodes.
+ R4000 uses interlocks to handle coproc delays.
+ Other chips (like the R3000) require nops to be inserted for delays.
- case 'Z':
- gas_assert (!mips_opts.micromips);
- if (!reg_lookup (&s, RTYPE_FPU, ®no))
- break;
- INSERT_OPERAND (0, FZ, *ip, regno);
- continue;
+ FIXME: Currently, we require that the user handle delays.
+ In order to fill delay slots for non-interlocked chips,
+ we must have a way to specify delays based on the coprocessor.
+ Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+ What are the side-effects of the cop instruction?
+ What cache support might we have and what are its effects?
+ Both coprocessor & memory require delays. how long???
+ What registers are read/set/modified?
- case 'j':
- {
- int shift = 8;
- size_t i;
- /* Check whether there is only a single bracketed expression
- left. If so, it must be the base register and the
- constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- continue;
+ If an itbl is provided to interpret cop instructions,
+ this knowledge can be encoded in the itbl spec. */
- /* If this value won't fit into the offset, then go find
- a macro that will generate a 16- or 32-bit offset code
- pattern. */
- i = my_getSmallExpression (&imm_expr, imm_reloc, s);
- if ((i == 0 && (imm_expr.X_op != O_constant
- || imm_expr.X_add_number >= 1 << shift
- || imm_expr.X_add_number < -1 << shift))
- || i > 0)
- {
- imm_expr.X_op = O_absent;
- break;
- }
- INSERT_OPERAND (mips_opts.micromips, EVAOFFSET, *ip,
- imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ case M_COP0:
+ s = "c0";
+ goto copz;
+ case M_COP1:
+ s = "c1";
+ goto copz;
+ case M_COP2:
+ s = "c2";
+ goto copz;
+ case M_COP3:
+ s = "c3";
+ copz:
+ gas_assert (!mips_opts.micromips);
+ /* For now we just do C (same as Cz). The parameter will be
+ stored in insn_opcode by mips_ip. */
+ macro_build (NULL, s, "C", (int) ip->insn_opcode);
+ break;
- default:
- as_bad (_("Internal error: bad %s opcode "
- "(unknown extension operand type `+%c'): %s %s"),
- mips_opts.micromips ? "microMIPS" : "MIPS",
- *args, insn->name, insn->args);
- /* Further processing is fruitless. */
- return;
- }
- break;
+ case M_MOVE:
+ move_register (dreg, sreg);
+ break;
- case '.': /* 10-bit offset. */
- gas_assert (mips_opts.micromips);
- case '~': /* 12-bit offset. */
- {
- int shift = *args == '.' ? 9 : 11;
- size_t i;
+ case M_MOVEP:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ dreg = micromips_to_32_reg_h_map1[EXTRACT_OPERAND (1, MH, *ip)];
+ breg = micromips_to_32_reg_h_map2[EXTRACT_OPERAND (1, MH, *ip)];
+ sreg = micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
+ treg = micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
+ move_register (dreg, sreg);
+ move_register (breg, treg);
+ break;
- /* Check whether there is only a single bracketed expression
- left. If so, it must be the base register and the
- constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- continue;
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ if (mips_opts.arch == CPU_R5900)
+ {
+ macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", dreg, sreg, treg);
+ }
+ else
+ {
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", sreg, treg);
+ macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ }
+ break;
- /* If this value won't fit into the offset, then go find
- a macro that will generate a 16- or 32-bit offset code
- pattern. */
- i = my_getSmallExpression (&imm_expr, imm_reloc, s);
- if ((i == 0 && (imm_expr.X_op != O_constant
- || imm_expr.X_add_number >= 1 << shift
- || imm_expr.X_add_number < -1 << shift))
- || i > 0)
- {
- imm_expr.X_op = O_absent;
- break;
- }
- if (shift == 9)
- INSERT_OPERAND (1, OFFSET10, *ip, imm_expr.X_add_number);
- else
- INSERT_OPERAND (mips_opts.micromips,
- OFFSET12, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ case M_DMUL_I:
+ dbl = 1;
+ 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
+ anyway. */
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, AT);
+ macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ break;
- case '<': /* must be at least one digit */
- /*
- * According to the manual, if the shift amount is greater
- * than 31 or less than 0, then the shift amount should be
- * mod 32. In reality the mips assembler issues an error.
- * We issue a warning and mask out all but the low 5 bits.
- */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 31)
- as_warn (_("Improper shift amount (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- SHAMT, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_DMULO_I:
+ dbl = 1;
+ case M_MULO_I:
+ imm = 1;
+ goto do_mulo;
- case '>': /* shift amount minus 32 */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number < 32
- || (unsigned long) imm_expr.X_add_number > 63)
- break;
- INSERT_OPERAND (mips_opts.micromips,
- SHAMT, *ip, imm_expr.X_add_number - 32);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_DMULO:
+ dbl = 1;
+ case M_MULO:
+ do_mulo:
+ start_noreorder ();
+ used_at = 1;
+ if (imm)
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmult" : "mult", "s,t", sreg, imm ? AT : treg);
+ macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, dreg, dreg, RA);
+ macro_build (NULL, "mfhi", MFHL_FMT, AT);
+ if (mips_trap)
+ macro_build (NULL, "tne", TRAP_FMT, dreg, AT, 6);
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "beq", "s,t,p", dreg, AT);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, "break", BRK_FMT, 6);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ end_noreorder ();
+ macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ break;
- case 'k': /* CACHE code. */
- case 'h': /* PREFX code. */
- case '1': /* SYNC type. */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 31)
- as_warn (_("Invalid value for `%s' (%lu)"),
- ip->insn_mo->name,
- (unsigned long) imm_expr.X_add_number);
- switch (*args)
- {
- case 'k':
- if (mips_fix_cn63xxp1
- && !mips_opts.micromips
- && strcmp ("pref", insn->name) == 0)
- switch (imm_expr.X_add_number)
- {
- 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. */
- imm_expr.X_add_number = 28;
- break;
- }
- INSERT_OPERAND (mips_opts.micromips,
- CACHE, *ip, imm_expr.X_add_number);
- break;
- case 'h':
- INSERT_OPERAND (mips_opts.micromips,
- PREFX, *ip, imm_expr.X_add_number);
- break;
- case '1':
- INSERT_OPERAND (mips_opts.micromips,
- STYPE, *ip, imm_expr.X_add_number);
- break;
- }
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_DMULOU_I:
+ dbl = 1;
+ case M_MULOU_I:
+ imm = 1;
+ goto do_mulou;
- case 'c': /* BREAK code. */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_CODE
- : OP_MASK_CODE);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Code for %s not in range 0..%lu (%lu)"),
- ip->insn_mo->name,
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- CODE, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ case M_DMULOU:
+ dbl = 1;
+ case M_MULOU:
+ do_mulou:
+ start_noreorder ();
+ used_at = 1;
+ if (imm)
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
+ sreg, imm ? AT : treg);
+ macro_build (NULL, "mfhi", MFHL_FMT, AT);
+ macro_build (NULL, "mflo", MFHL_FMT, dreg);
+ if (mips_trap)
+ macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 6);
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "beq", "s,t,p", AT, ZERO);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, "break", BRK_FMT, 6);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ end_noreorder ();
+ break;
- case 'q': /* Lower BREAK code. */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_CODE2
- : OP_MASK_CODE2);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Lower code for %s not in range 0..%lu (%lu)"),
- ip->insn_mo->name,
- mask, (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- CODE2, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ case M_DROL:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (dreg == sreg)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = dreg;
+ }
+ macro_build (NULL, "dnegu", "d,w", tempreg, treg);
+ macro_build (NULL, "drorv", "d,t,s", dreg, sreg, tempreg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
+ macro_build (NULL, "dsrlv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "dsllv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
- case 'B': /* 20- or 10-bit syscall/break/wait code. */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_CODE10
- : OP_MASK_CODE20);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Code for %s not in range 0..%lu (%lu)"),
- ip->insn_mo->name,
- mask, (unsigned long) imm_expr.X_add_number);
- if (mips_opts.micromips)
- INSERT_OPERAND (1, CODE10, *ip, imm_expr.X_add_number);
- else
- INSERT_OPERAND (0, CODE20, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ case M_ROL:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ if (dreg == sreg)
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ {
+ tempreg = dreg;
+ }
+ macro_build (NULL, "negu", "d,w", tempreg, treg);
+ macro_build (NULL, "rorv", "d,t,s", dreg, sreg, tempreg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "subu", "d,v,t", AT, ZERO, treg);
+ macro_build (NULL, "srlv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "sllv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
- case 'C': /* 25- or 23-bit coprocessor code. */
- {
- unsigned long mask = (mips_opts.micromips
- ? MICROMIPSOP_MASK_COPZ
- : OP_MASK_COPZ);
-
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > mask)
- as_warn (_("Coproccesor code > %u bits (%lu)"),
- mips_opts.micromips ? 23U : 25U,
- (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (mips_opts.micromips,
- COPZ, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- }
- continue;
+ case M_DROL_I:
+ {
+ unsigned int rot;
+ char *l;
+ char *rr;
- case 'J': /* 19-bit WAIT code. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > OP_MASK_CODE19)
- {
- as_warn (_("Illegal 19-bit code (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number &= OP_MASK_CODE19;
- }
- INSERT_OPERAND (0, CODE19, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x3f;
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ rot = (64 - rot) & 0x3f;
+ if (rot >= 32)
+ macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
+ else
+ macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
+ break;
+ }
+ l = (rot < 0x20) ? "dsll" : "dsll32";
+ rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
+ rot &= 0x1f;
+ used_at = 1;
+ macro_build (NULL, l, SHFT_FMT, AT, sreg, rot);
+ macro_build (NULL, rr, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
- case 'P': /* Performance register. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if (imm_expr.X_add_number != 0 && imm_expr.X_add_number != 1)
- as_warn (_("Invalid performance register (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- if (imm_expr.X_add_number != 0 && mips_opts.arch == CPU_R5900
- && (!strcmp(insn->name,"mfps") || !strcmp(insn->name,"mtps")))
- as_warn (_("Invalid performance register (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- INSERT_OPERAND (0, PERFREG, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_ROL_I:
+ {
+ unsigned int rot;
- case 'G': /* Coprocessor destination register. */
- {
- unsigned long opcode = ip->insn_opcode;
- unsigned long mask;
- unsigned int types;
- int cop0;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x1f;
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, (32 - rot) & 0x1f);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "sll", SHFT_FMT, AT, sreg, rot);
+ macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
- if (mips_opts.micromips)
- {
- mask = ~((MICROMIPSOP_MASK_RT << MICROMIPSOP_SH_RT)
- | (MICROMIPSOP_MASK_RS << MICROMIPSOP_SH_RS)
- | (MICROMIPSOP_MASK_SEL << MICROMIPSOP_SH_SEL));
- opcode &= mask;
- switch (opcode)
- {
- case 0x000000fc: /* mfc0 */
- case 0x000002fc: /* mtc0 */
- case 0x580000fc: /* dmfc0 */
- case 0x580002fc: /* dmtc0 */
- cop0 = 1;
- break;
- default:
- cop0 = 0;
- break;
- }
- }
- else
- {
- opcode = (opcode >> OP_SH_OP) & OP_MASK_OP;
- cop0 = opcode == OP_OP_COP0;
- }
- types = RTYPE_NUM | (cop0 ? RTYPE_CP0 : RTYPE_GP);
- ok = reg_lookup (&s, types, ®no);
- if (mips_opts.micromips)
- INSERT_OPERAND (1, RS, *ip, regno);
- else
- INSERT_OPERAND (0, RD, *ip, regno);
- if (ok)
- {
- lastregno = regno;
- continue;
- }
- }
- break;
+ case M_DROR:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ macro_build (NULL, "drorv", "d,t,s", dreg, sreg, treg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, treg);
+ macro_build (NULL, "dsllv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "dsrlv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
- case 'y': /* ALNV.PS source register. */
- gas_assert (mips_opts.micromips);
- goto do_reg;
- case 'x': /* Ignore register name. */
- case 'U': /* Destination register (CLO/CLZ). */
- case 'g': /* Coprocessor destination register. */
- gas_assert (!mips_opts.micromips);
- case 'b': /* Base register. */
- case 'd': /* Destination register. */
- case 's': /* Source register. */
- case 't': /* Target register. */
- case 'r': /* Both target and source. */
- case 'v': /* Both dest and source. */
- case 'w': /* Both dest and target. */
- case 'E': /* Coprocessor target register. */
- case 'K': /* RDHWR destination register. */
- case 'z': /* Must be zero register. */
- do_reg:
- s_reset = s;
- if (*args == 'E' || *args == 'K')
- ok = reg_lookup (&s, RTYPE_NUM, ®no);
- else
- {
- ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no);
- 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);
- }
- }
- if (ok)
- {
- c = *args;
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'r' || c == 'v' || c == 'w')
- {
- regno = lastregno;
- s = s_reset;
- ++args;
- }
- }
- /* 'z' only matches $0. */
- if (c == 'z' && regno != 0)
- break;
+ case M_ROR:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "rorv", "d,t,s", dreg, sreg, treg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "subu", "d,v,t", AT, ZERO, treg);
+ macro_build (NULL, "sllv", "d,t,s", AT, sreg, AT);
+ macro_build (NULL, "srlv", "d,t,s", dreg, sreg, treg);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ break;
- if (c == 's' && !strncmp (ip->insn_mo->name, "jalr", 4))
- {
- if (regno == lastregno)
- {
- insn_error
- = _("Source and destination must be different");
- continue;
- }
- if (regno == 31 && lastregno == 0xffffffff)
- {
- insn_error
- = _("A destination register must be supplied");
- continue;
- }
- }
- /* Now that we have assembled one operand, we use the args
- string to figure out where it goes in the instruction. */
- switch (c)
- {
- case 'r':
- case 's':
- case 'v':
- case 'b':
- INSERT_OPERAND (mips_opts.micromips, RS, *ip, regno);
- break;
-
- case 'K':
- if (mips_opts.micromips)
- INSERT_OPERAND (1, RS, *ip, regno);
- else
- INSERT_OPERAND (0, RD, *ip, regno);
- break;
-
- case 'd':
- case 'g':
- INSERT_OPERAND (mips_opts.micromips, RD, *ip, regno);
- break;
-
- case 'U':
- gas_assert (!mips_opts.micromips);
- INSERT_OPERAND (0, RD, *ip, regno);
- INSERT_OPERAND (0, RT, *ip, regno);
- break;
-
- case 'w':
- case 't':
- case 'E':
- INSERT_OPERAND (mips_opts.micromips, RT, *ip, regno);
- break;
-
- case 'y':
- gas_assert (mips_opts.micromips);
- INSERT_OPERAND (1, RS3, *ip, regno);
- break;
-
- case 'x':
- /* This case exists because on the r3000 trunc
- expands into a macro which requires a gp
- register. On the r6000 or r4000 it is
- assembled into a single instruction which
- ignores the register. Thus the insn version
- is MIPS_ISA2 and uses 'x', and the macro
- version is MIPS_ISA1 and uses 't'. */
- break;
-
- case 'z':
- /* This case is for the div instruction, which
- acts differently if the destination argument
- is $0. This only matches $0, and is checked
- outside the switch. */
- break;
- }
- lastregno = regno;
- continue;
- }
- switch (*args++)
- {
- case 'r':
- case 'v':
- INSERT_OPERAND (mips_opts.micromips, RS, *ip, lastregno);
- continue;
+ case M_DROR_I:
+ {
+ unsigned int rot;
+ char *l;
+ char *rr;
- case 'w':
- INSERT_OPERAND (mips_opts.micromips, RT, *ip, lastregno);
- continue;
- }
- break;
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x3f;
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (rot >= 32)
+ macro_build (NULL, "dror32", SHFT_FMT, dreg, sreg, rot - 32);
+ else
+ macro_build (NULL, "dror", SHFT_FMT, dreg, sreg, rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", SHFT_FMT, dreg, sreg, 0);
+ break;
+ }
+ rr = (rot < 0x20) ? "dsrl" : "dsrl32";
+ l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
+ rot &= 0x1f;
+ used_at = 1;
+ macro_build (NULL, rr, SHFT_FMT, AT, sreg, rot);
+ macro_build (NULL, l, SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
- case 'O': /* MDMX alignment immediate constant. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > OP_MASK_ALN)
- as_warn (_("Improper align amount (%ld), using low bits"),
- (long) imm_expr.X_add_number);
- INSERT_OPERAND (0, ALN, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case M_ROR_I:
+ {
+ unsigned int rot;
- case 'Q': /* MDMX vector, element sel, or const. */
- if (s[0] != '$')
- {
- /* MDMX Immediate. */
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > OP_MASK_FT)
- as_warn (_("Invalid MDMX Immediate (%ld)"),
- (long) imm_expr.X_add_number);
- INSERT_OPERAND (0, FT, *ip, imm_expr.X_add_number);
- if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
- ip->insn_opcode |= MDMX_FMTSEL_IMM_QH << OP_SH_VSEL;
- else
- ip->insn_opcode |= MDMX_FMTSEL_IMM_OB << OP_SH_VSEL;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
- }
- /* Not MDMX Immediate. Fall through. */
- case 'X': /* MDMX destination register. */
- case 'Y': /* MDMX source register. */
- case 'Z': /* MDMX target register. */
- is_mdmx = 1;
- case 'W':
- gas_assert (!mips_opts.micromips);
- case 'D': /* Floating point destination register. */
- case 'S': /* Floating point source register. */
- case 'T': /* Floating point target register. */
- case 'R': /* Floating point source register. */
- case 'V':
- rtype = RTYPE_FPU;
- if (is_mdmx
- || ((mips_opts.ase & ASE_MDMX)
- && (ip->insn_mo->pinfo & FP_D)
- && (ip->insn_mo->pinfo & (INSN_COPROC_MOVE_DELAY
- | INSN_COPROC_MEMORY_DELAY
- | INSN_LOAD_COPROC_DELAY
- | INSN_LOAD_MEMORY_DELAY
- | INSN_STORE_MEMORY))))
- rtype |= RTYPE_VEC;
- s_reset = s;
- if (reg_lookup (&s, rtype, ®no))
- {
- if ((regno & 1) != 0
- && HAVE_32BIT_FPRS
- && !mips_oddfpreg_ok (ip->insn_mo, argnum))
- as_warn (_("Float register should be even, was %d"),
- regno);
-
- c = *args;
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'V' || c == 'W')
- {
- regno = lastregno;
- s = s_reset;
- ++args;
- }
- }
- switch (c)
- {
- case 'D':
- case 'X':
- INSERT_OPERAND (mips_opts.micromips, FD, *ip, regno);
- break;
-
- case 'V':
- case 'S':
- case 'Y':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, regno);
- break;
-
- case 'Q':
- /* This is like 'Z', but also needs to fix the MDMX
- vector/scalar select bits. Note that the
- scalar immediate case is handled above. */
- if (*s == '[')
- {
- int is_qh = (ip->insn_opcode & (1 << OP_SH_VSEL));
- int max_el = (is_qh ? 3 : 7);
- s++;
- my_getExpression(&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- s = expr_end;
- if (imm_expr.X_add_number > max_el)
- as_bad (_("Bad element selector %ld"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number &= max_el;
- ip->insn_opcode |= (imm_expr.X_add_number
- << (OP_SH_VSEL +
- (is_qh ? 2 : 1)));
- imm_expr.X_op = O_absent;
- if (*s != ']')
- as_warn (_("Expecting ']' found '%s'"), s);
- else
- s++;
- }
- else
- {
- if (ip->insn_opcode & (OP_MASK_VSEL << OP_SH_VSEL))
- ip->insn_opcode |= (MDMX_FMTSEL_VEC_QH
- << OP_SH_VSEL);
- else
- ip->insn_opcode |= (MDMX_FMTSEL_VEC_OB <<
- OP_SH_VSEL);
- }
- /* Fall through. */
- case 'W':
- case 'T':
- case 'Z':
- INSERT_OPERAND (mips_opts.micromips, FT, *ip, regno);
- break;
-
- case 'R':
- INSERT_OPERAND (mips_opts.micromips, FR, *ip, regno);
- break;
- }
- lastregno = regno;
- continue;
- }
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Improper rotate count"));
+ rot = imm_expr.X_add_number & 0x1f;
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "ror", SHFT_FMT, dreg, sreg, rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", SHFT_FMT, dreg, sreg, 0);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "srl", SHFT_FMT, AT, sreg, rot);
+ macro_build (NULL, "sll", SHFT_FMT, dreg, sreg, (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", dreg, dreg, AT);
+ }
+ break;
- switch (*args++)
- {
- case 'V':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
- continue;
+ case M_SEQ:
+ if (sreg == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, treg, BFD_RELOC_LO16);
+ else if (treg == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+ }
+ break;
- case 'W':
- INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
- continue;
- }
- break;
+ case M_SEQ_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ break;
+ }
+ if (sreg == 0)
+ {
+ as_warn (_("Instruction %s: result is always false"),
+ ip->insn_mo->name);
+ move_register (dreg, 0);
+ break;
+ }
+ if (CPU_HAS_SEQ (mips_opts.arch)
+ && -512 <= imm_expr.X_add_number
+ && imm_expr.X_add_number < 512)
+ {
+ macro_build (NULL, "seqi", "t,r,+Q", dreg, sreg,
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+ }
+ else if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
+ "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ }
+ else if (CPU_HAS_SEQ (mips_opts.arch))
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "seq", "d,v,t", dreg, sreg, AT);
+ break;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "sltiu", "t,r,j", dreg, dreg, BFD_RELOC_LO16);
+ break;
- case 'I':
- my_getExpression (&imm_expr, s);
- if (imm_expr.X_op != O_big
- && imm_expr.X_op != O_constant)
- insn_error = _("absolute expression required");
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (&imm_expr);
- s = expr_end;
- continue;
+ case M_SGE: /* sreg >= treg <==> not (sreg < treg) */
+ s = "slt";
+ goto sge;
+ case M_SGEU:
+ s = "sltu";
+ sge:
+ macro_build (NULL, s, "d,v,t", dreg, sreg, treg);
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ break;
- case 'A':
- my_getExpression (&offset_expr, s);
- normalize_address_expr (&offset_expr);
- *imm_reloc = BFD_RELOC_32;
- s = expr_end;
- continue;
+ case M_SGE_I: /* sreg >= I <==> not (sreg < I) */
+ case M_SGEU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
+ dreg, sreg, BFD_RELOC_LO16);
+ }
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
+ dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ break;
- case 'F':
- case 'L':
- case 'f':
- case 'l':
- {
- int f64;
- int using_gprs;
- char *save_in;
- char *err;
- unsigned char temp[8];
- int len;
- unsigned int length;
- segT seg;
- subsegT subseg;
- char *p;
-
- /* These only appear as the last operand in an
- instruction, and every instruction that accepts
- them in any variant accepts them in all variants.
- This means we don't have to worry about backing out
- any changes if the instruction does not match.
-
- The difference between them is the size of the
- floating point constant and where it goes. For 'F'
- and 'L' the constant is 64 bits; for 'f' and 'l' it
- is 32 bits. Where the constant is placed is based
- on how the MIPS assembler does things:
- F -- .rdata
- L -- .lit8
- f -- immediate value
- l -- .lit4
-
- The .lit4 and .lit8 sections are only used if
- permitted by the -G argument.
-
- The code below needs to know whether the target register
- is 32 or 64 bits wide. It relies on the fact 'f' and
- 'F' are used with GPR-based instructions and 'l' and
- 'L' are used with FPR-based instructions. */
-
- f64 = *args == 'F' || *args == 'L';
- using_gprs = *args == 'F' || *args == 'f';
-
- save_in = input_line_pointer;
- input_line_pointer = s;
- err = md_atof (f64 ? 'd' : 'f', (char *) temp, &len);
- length = len;
- s = input_line_pointer;
- input_line_pointer = save_in;
- if (err != NULL && *err != '\0')
- {
- as_bad (_("Bad floating point constant: %s"), err);
- memset (temp, '\0', sizeof temp);
- length = f64 ? 8 : 4;
- }
+ case M_SGT: /* sreg > treg <==> treg < sreg */
+ s = "slt";
+ goto sgt;
+ case M_SGTU:
+ s = "sltu";
+ sgt:
+ macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+ break;
- gas_assert (length == (unsigned) (f64 ? 8 : 4));
+ case M_SGT_I: /* sreg > I <==> I < sreg */
+ s = "slt";
+ goto sgti;
+ case M_SGTU_I:
+ s = "sltu";
+ sgti:
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
+ break;
- if (*args == 'f'
- || (*args == 'l'
- && (g_switch_value < 4
- || (temp[0] == 0 && temp[1] == 0)
- || (temp[2] == 0 && temp[3] == 0))))
- {
- imm_expr.X_op = O_constant;
- if (!target_big_endian)
- imm_expr.X_add_number = bfd_getl32 (temp);
- else
- imm_expr.X_add_number = bfd_getb32 (temp);
- }
- else if (length > 4
- && !mips_disable_float_construction
- /* Constants can only be constructed in GPRs and
- copied to FPRs if the GPRs are at least as wide
- as the FPRs. Force the constant into memory if
- we are using 64-bit FPRs but the GPRs are only
- 32 bits wide. */
- && (using_gprs
- || !(HAVE_64BIT_FPRS && HAVE_32BIT_GPRS))
- && ((temp[0] == 0 && temp[1] == 0)
- || (temp[2] == 0 && temp[3] == 0))
- && ((temp[4] == 0 && temp[5] == 0)
- || (temp[6] == 0 && temp[7] == 0)))
- {
- /* The value is simple enough to load with a couple of
- instructions. If using 32-bit registers, set
- imm_expr to the high order 32 bits and offset_expr to
- the low order 32 bits. Otherwise, set imm_expr to
- the entire 64 bit constant. */
- if (using_gprs ? HAVE_32BIT_GPRS : HAVE_32BIT_FPRS)
- {
- imm_expr.X_op = O_constant;
- offset_expr.X_op = O_constant;
- if (!target_big_endian)
- {
- imm_expr.X_add_number = bfd_getl32 (temp + 4);
- offset_expr.X_add_number = bfd_getl32 (temp);
- }
- else
- {
- imm_expr.X_add_number = bfd_getb32 (temp);
- offset_expr.X_add_number = bfd_getb32 (temp + 4);
- }
- if (offset_expr.X_add_number == 0)
- offset_expr.X_op = O_absent;
- }
- else
- {
- imm_expr.X_op = O_constant;
- if (!target_big_endian)
- imm_expr.X_add_number = bfd_getl64 (temp);
- else
- imm_expr.X_add_number = bfd_getb64 (temp);
- }
- }
- else
- {
- const char *newname;
- segT new_seg;
-
- /* Switch to the right section. */
- seg = now_seg;
- subseg = now_subseg;
- switch (*args)
- {
- default: /* unused default case avoids warnings. */
- case 'L':
- newname = RDATA_SECTION_NAME;
- if (g_switch_value >= 8)
- newname = ".lit8";
- break;
- case 'F':
- newname = RDATA_SECTION_NAME;
- break;
- case 'l':
- gas_assert (g_switch_value >= 4);
- newname = ".lit4";
- break;
- }
- new_seg = subseg_new (newname, (subsegT) 0);
- bfd_set_section_flags (stdoutput, new_seg,
- (SEC_ALLOC
- | SEC_LOAD
- | SEC_READONLY
- | SEC_DATA));
- frag_align (*args == 'l' ? 2 : 3, 0, 0);
- if (strncmp (TARGET_OS, "elf", 3) != 0)
- record_alignment (new_seg, 4);
- else
- record_alignment (new_seg, *args == 'l' ? 2 : 3);
- if (seg == now_seg)
- as_bad (_("Can't use floating point insn in this section"));
-
- /* Set the argument to the current address in the
- section. */
- offset_expr.X_op = O_symbol;
- offset_expr.X_add_symbol = symbol_temp_new_now ();
- offset_expr.X_add_number = 0;
-
- /* Put the floating point number into the section. */
- p = frag_more ((int) length);
- memcpy (p, temp, length);
-
- /* Switch back to the original section. */
- subseg_set (seg, subseg);
- }
- }
- continue;
+ case M_SLE: /* sreg <= treg <==> treg >= sreg <==> not (treg < sreg) */
+ s = "slt";
+ goto sle;
+ case M_SLEU:
+ s = "sltu";
+ sle:
+ macro_build (NULL, s, "d,v,t", dreg, treg, sreg);
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ break;
- case 'i': /* 16-bit unsigned immediate. */
- case 'j': /* 16-bit signed immediate. */
- *imm_reloc = BFD_RELOC_LO16;
- if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0)
- {
- int more;
- offsetT minval, maxval;
-
- more = (insn + 1 < past
- && strcmp (insn->name, insn[1].name) == 0);
-
- /* For compatibility with older assemblers, we accept
- 0x8000-0xffff as signed 16-bit numbers when only
- signed numbers are allowed. */
- if (*args == 'i')
- minval = 0, maxval = 0xffff;
- else if (more)
- minval = -0x8000, maxval = 0x7fff;
- else
- minval = -0x8000, maxval = 0xffff;
+ case M_SLE_I: /* sreg <= I <==> I >= sreg <==> not (I < sreg) */
+ s = "slt";
+ goto slei;
+ case M_SLEU_I:
+ s = "sltu";
+ slei:
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s, "d,v,t", dreg, AT, sreg);
+ macro_build (&expr1, "xori", "t,r,i", dreg, dreg, BFD_RELOC_LO16);
+ break;
- if (imm_expr.X_op != O_constant
- || imm_expr.X_add_number < minval
- || imm_expr.X_add_number > maxval)
- {
- if (more)
- break;
- if (imm_expr.X_op == O_constant
- || imm_expr.X_op == O_big)
- as_bad (_("Expression out of range"));
- }
- }
- s = expr_end;
- continue;
+ case M_SLT_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "slti", "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ break;
+ }
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "slt", "d,v,t", dreg, sreg, AT);
+ break;
- case 'o': /* 16-bit offset. */
- offset_reloc[0] = BFD_RELOC_LO16;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
+ case M_SLTU_I:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "sltiu", "t,r,j", dreg, sreg,
+ BFD_RELOC_LO16);
+ break;
+ }
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "sltu", "d,v,t", dreg, sreg, AT);
+ break;
- /* Check whether there is only a single bracketed expression
- left. If so, it must be the base register and the
- constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- offset_expr.X_op = O_constant;
- offset_expr.X_add_number = 0;
- continue;
- }
+ case M_SNE:
+ if (sreg == 0)
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, treg);
+ else if (treg == 0)
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, treg);
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+ }
+ break;
- /* If this value won't fit into a 16 bit offset, then go
- find a macro that will generate the 32 bit offset
- code pattern. */
- if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
- && (offset_expr.X_op != O_constant
- || offset_expr.X_add_number >= 0x8000
- || offset_expr.X_add_number < -0x8000))
- break;
+ case M_SNE_I:
+ if (imm_expr.X_op == O_constant && imm_expr.X_add_number == 0)
+ {
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, sreg);
+ break;
+ }
+ if (sreg == 0)
+ {
+ as_warn (_("Instruction %s: result is always true"),
+ ip->insn_mo->name);
+ macro_build (&expr1, HAVE_32BIT_GPRS ? "addiu" : "daddiu", "t,r,j",
+ dreg, 0, BFD_RELOC_LO16);
+ break;
+ }
+ if (CPU_HAS_SEQ (mips_opts.arch)
+ && -512 <= imm_expr.X_add_number
+ && imm_expr.X_add_number < 512)
+ {
+ macro_build (NULL, "snei", "t,r,+Q", dreg, sreg,
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build (&imm_expr, "xori", "t,r,i", dreg, sreg, BFD_RELOC_LO16);
+ }
+ else if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, HAVE_32BIT_GPRS ? "addiu" : "daddiu",
+ "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ }
+ else if (CPU_HAS_SEQ (mips_opts.arch))
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "sne", "d,v,t", dreg, sreg, AT);
+ break;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, "xor", "d,v,t", dreg, sreg, AT);
+ used_at = 1;
+ }
+ macro_build (NULL, "sltu", "d,v,t", dreg, 0, dreg);
+ break;
- s = expr_end;
- continue;
+ case M_SUB_I:
+ s = "addi";
+ s2 = "sub";
+ goto do_subi;
+ case M_SUBU_I:
+ s = "addiu";
+ s2 = "subu";
+ goto do_subi;
+ case M_DSUB_I:
+ dbl = 1;
+ s = "daddi";
+ s2 = "dsub";
+ if (!mips_opts.micromips)
+ goto do_subi;
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x200
+ && imm_expr.X_add_number <= 0x200)
+ {
+ macro_build (NULL, s, "t,r,.", dreg, sreg, -imm_expr.X_add_number);
+ break;
+ }
+ goto do_subi_i;
+ case M_DSUBU_I:
+ dbl = 1;
+ s = "daddiu";
+ s2 = "dsubu";
+ do_subi:
+ if (imm_expr.X_op == O_constant
+ && imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number <= 0x8000)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ break;
+ }
+ do_subi_i:
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
+ break;
- case 'p': /* PC-relative offset. */
- *offset_reloc = BFD_RELOC_16_PCREL_S2;
- my_getExpression (&offset_expr, s);
- s = expr_end;
- continue;
+ case M_TEQ_I:
+ s = "teq";
+ goto trap;
+ case M_TGE_I:
+ s = "tge";
+ goto trap;
+ case M_TGEU_I:
+ s = "tgeu";
+ goto trap;
+ case M_TLT_I:
+ s = "tlt";
+ goto trap;
+ case M_TLTU_I:
+ s = "tltu";
+ goto trap;
+ case M_TNE_I:
+ s = "tne";
+ trap:
+ used_at = 1;
+ load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (NULL, s, "s,t", sreg, AT);
+ break;
- case 'u': /* Upper 16 bits. */
- *imm_reloc = BFD_RELOC_LO16;
- if (my_getSmallExpression (&imm_expr, imm_reloc, s) == 0
- && imm_expr.X_op == O_constant
- && (imm_expr.X_add_number < 0
- || imm_expr.X_add_number >= 0x10000))
- as_bad (_("lui expression (%lu) not in range 0..65535"),
- (unsigned long) imm_expr.X_add_number);
- s = expr_end;
- continue;
+ case M_TRUNCWS:
+ case M_TRUNCWD:
+ gas_assert (!mips_opts.micromips);
+ gas_assert (mips_opts.isa == ISA_MIPS1);
+ used_at = 1;
+ sreg = (ip->insn_opcode >> 11) & 0x1f; /* floating reg */
+ dreg = (ip->insn_opcode >> 06) & 0x1f; /* floating reg */
- case 'a': /* 26-bit address. */
- *offset_reloc = BFD_RELOC_MIPS_JMP;
- my_getExpression (&offset_expr, s);
- s = expr_end;
- continue;
+ /*
+ * Is the double cfc1 instruction a bug in the mips assembler;
+ * or is there a reason for it?
+ */
+ start_noreorder ();
+ macro_build (NULL, "cfc1", "t,G", treg, RA);
+ macro_build (NULL, "cfc1", "t,G", treg, RA);
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = 3;
+ macro_build (&expr1, "ori", "t,r,i", AT, treg, BFD_RELOC_LO16);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "ctc1", "t,G", AT, RA);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S",
+ dreg, sreg);
+ macro_build (NULL, "ctc1", "t,G", treg, RA);
+ macro_build (NULL, "nop", "");
+ end_noreorder ();
+ break;
- case 'N': /* 3-bit branch condition code. */
- case 'M': /* 3-bit compare condition code. */
- rtype = RTYPE_CCC;
- if (ip->insn_mo->pinfo & (FP_D | FP_S))
- rtype |= RTYPE_FCC;
- if (!reg_lookup (&s, rtype, ®no))
- break;
- if ((strcmp (str + strlen (str) - 3, ".ps") == 0
- || strcmp (str + strlen (str) - 5, "any2f") == 0
- || strcmp (str + strlen (str) - 5, "any2t") == 0)
- && (regno & 1) != 0)
- as_warn (_("Condition code register should be even for %s, "
- "was %d"),
- str, regno);
- if ((strcmp (str + strlen (str) - 5, "any4f") == 0
- || strcmp (str + strlen (str) - 5, "any4t") == 0)
- && (regno & 3) != 0)
- as_warn (_("Condition code register should be 0 or 4 for %s, "
- "was %d"),
- str, regno);
- if (*args == 'N')
- INSERT_OPERAND (mips_opts.micromips, BCC, *ip, regno);
- else
- INSERT_OPERAND (mips_opts.micromips, CCC, *ip, regno);
- continue;
+ case M_ULH_AB:
+ s = "lb";
+ s2 = "lbu";
+ off = 1;
+ goto uld_st;
+ case M_ULHU_AB:
+ s = "lbu";
+ s2 = "lbu";
+ off = 1;
+ goto uld_st;
+ case M_ULW_AB:
+ s = "lwl";
+ s2 = "lwr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 3;
+ goto uld_st;
+ case M_ULD_AB:
+ s = "ldl";
+ s2 = "ldr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 7;
+ goto uld_st;
+ case M_USH_AB:
+ s = "sb";
+ s2 = "sb";
+ off = 1;
+ ust = 1;
+ goto uld_st;
+ case M_USW_AB:
+ s = "swl";
+ s2 = "swr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 3;
+ ust = 1;
+ goto uld_st;
+ case M_USD_AB:
+ s = "sdl";
+ s2 = "sdr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 7;
+ ust = 1;
- case 'H':
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
- s += 2;
- if (ISDIGIT (*s))
- {
- c = 0;
- do
- {
- c *= 10;
- c += *s - '0';
- ++s;
- }
- while (ISDIGIT (*s));
- }
- else
- c = 8; /* Invalid sel value. */
+ uld_st:
+ large_offset = !small_offset_p (off, align, offbits);
+ ep = &offset_expr;
+ expr1.X_add_number = 0;
+ if (large_offset)
+ {
+ used_at = 1;
+ tempreg = AT;
+ if (small_offset_p (0, align, 16))
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ else
+ {
+ load_address (tempreg, ep, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ }
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ breg = tempreg;
+ tempreg = treg;
+ ep = &expr1;
+ }
+ else if (!ust && treg == breg)
+ {
+ used_at = 1;
+ tempreg = AT;
+ }
+ else
+ tempreg = treg;
- if (c > 7)
- as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
- INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
- continue;
+ if (off == 1)
+ goto ulh_sh;
- case 'e':
- gas_assert (!mips_opts.micromips);
- /* Must be at least one digit. */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
+ if (!target_big_endian)
+ ep->X_add_number += off;
+ if (offbits == 12)
+ macro_build (NULL, s, "t,~(b)", tempreg, (int) ep->X_add_number, breg);
+ else
+ macro_build (ep, s, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- if ((unsigned long) imm_expr.X_add_number
- > (unsigned long) OP_MASK_VECBYTE)
- {
- as_bad (_("bad byte vector index (%ld)"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
+ if (!target_big_endian)
+ ep->X_add_number -= off;
+ else
+ ep->X_add_number += off;
+ if (offbits == 12)
+ macro_build (NULL, s2, "t,~(b)",
+ tempreg, (int) ep->X_add_number, breg);
+ else
+ macro_build (ep, s2, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- INSERT_OPERAND (0, VECBYTE, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ /* If necessary, move the result in tempreg to the final destination. */
+ if (!ust && treg != tempreg)
+ {
+ /* Protect second load's delay slot. */
+ load_delay_nop ();
+ move_register (treg, tempreg);
+ }
+ break;
- case '%':
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
+ ulh_sh:
+ used_at = 1;
+ if (target_big_endian == ust)
+ ep->X_add_number += off;
+ tempreg = ust || large_offset ? treg : AT;
+ macro_build (ep, s, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- if ((unsigned long) imm_expr.X_add_number
- > (unsigned long) OP_MASK_VECALIGN)
- {
- as_bad (_("bad byte vector index (%ld)"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
- }
+ /* For halfword transfers we need a temporary register to shuffle
+ bytes. Unfortunately for M_USH_A we have none available before
+ the next store as AT holds the base address. We deal with this
+ case by clobbering TREG and then restoring it as with ULH. */
+ tempreg = ust == large_offset ? treg : AT;
+ if (ust)
+ macro_build (NULL, "srl", SHFT_FMT, tempreg, treg, 8);
- INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ if (target_big_endian == ust)
+ ep->X_add_number -= off;
+ else
+ ep->X_add_number += off;
+ macro_build (ep, s2, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- case 'm': /* Opcode extension character. */
- gas_assert (mips_opts.micromips);
- c = *++args;
- switch (c)
- {
- case 'r':
- if (strncmp (s, "$pc", 3) == 0)
- {
- s += 3;
- continue;
- }
- break;
+ /* For M_USH_A re-retrieve the LSB. */
+ if (ust && large_offset)
+ {
+ if (target_big_endian)
+ ep->X_add_number += off;
+ else
+ ep->X_add_number -= off;
+ macro_build (&expr1, "lbu", "t,o(b)", AT, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], AT);
+ }
+ /* For ULH and M_USH_A OR the LSB in. */
+ if (!ust || large_offset)
+ {
+ tempreg = !large_offset ? AT : treg;
+ macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
+ macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ }
+ break;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- case 'g':
- case 'h':
- case 'i':
- case 'j':
- case 'l':
- case 'm':
- case 'n':
- case 'p':
- case 'q':
- case 's':
- case 't':
- case 'x':
- case 'y':
- case 'z':
- s_reset = s;
- ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no);
- 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);
- }
- if (!ok)
- {
- if (c == 'c')
- {
- gas_assert (args[1] == ',');
- regno = lastregno;
- ++args;
- }
- else if (c == 't')
- {
- gas_assert (args[1] == ',');
- ++args;
- continue; /* Nothing to do. */
- }
- else
- break;
- }
+ default:
+ /* FIXME: Check if this is one of the itbl macros, since they
+ are added dynamically. */
+ as_bad (_("Macro %s not implemented yet"), ip->insn_mo->name);
+ break;
+ }
+ if (!mips_opts.at && used_at)
+ as_bad (_("Macro used $at after \".set noat\""));
+}
- if (c == 'j' && !strncmp (ip->insn_mo->name, "jalr", 4))
- {
- if (regno == lastregno)
- {
- insn_error
- = _("Source and destination must be different");
- continue;
- }
- if (regno == 31 && lastregno == 0xffffffff)
- {
- insn_error
- = _("A destination register must be supplied");
- continue;
- }
- }
+/* Implement macros in mips16 mode. */
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'e')
- {
- gas_assert (args[1] == ',');
- regno = lastregno;
- s = s_reset;
- ++args;
- }
- else if (c == 't')
- {
- gas_assert (args[1] == ',');
- s = s_reset;
- ++args;
- continue; /* Nothing to do. */
- }
- }
+static void
+mips16_macro (struct mips_cl_insn *ip)
+{
+ int mask;
+ int xreg, yreg, zreg, tmp;
+ expressionS expr1;
+ int dbl;
+ const char *s, *s2, *s3;
- /* Make sure regno is the same as lastregno. */
- if (c == 't' && regno != lastregno)
- break;
+ mask = ip->insn_mo->mask;
- /* Make sure regno is the same as destregno. */
- if (c == 'x' && regno != destregno)
- break;
+ xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
+ yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
+ zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
- /* We need to save regno, before regno maps to the
- microMIPS register encoding. */
- lastregno = regno;
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
- if (c == 'f')
- destregno = regno;
+ dbl = 0;
- switch (c)
- {
- case 'a':
- if (regno != GP)
- regno = ILLEGAL_REG;
- break;
-
- case 'b':
- regno = mips32_to_micromips_reg_b_map[regno];
- break;
-
- case 'c':
- regno = mips32_to_micromips_reg_c_map[regno];
- break;
-
- case 'd':
- regno = mips32_to_micromips_reg_d_map[regno];
- break;
-
- case 'e':
- regno = mips32_to_micromips_reg_e_map[regno];
- break;
-
- case 'f':
- regno = mips32_to_micromips_reg_f_map[regno];
- break;
-
- case 'g':
- regno = mips32_to_micromips_reg_g_map[regno];
- break;
-
- case 'h':
- regno = mips32_to_micromips_reg_h_map[regno];
- break;
-
- case 'i':
- switch (EXTRACT_OPERAND (1, MI, *ip))
- {
- case 4:
- if (regno == 21)
- regno = 3;
- else if (regno == 22)
- regno = 4;
- else if (regno == 5)
- regno = 5;
- else if (regno == 6)
- regno = 6;
- else if (regno == 7)
- regno = 7;
- else
- regno = ILLEGAL_REG;
- break;
-
- case 5:
- if (regno == 6)
- regno = 0;
- else if (regno == 7)
- regno = 1;
- else
- regno = ILLEGAL_REG;
- break;
-
- case 6:
- if (regno == 7)
- regno = 2;
- else
- regno = ILLEGAL_REG;
- break;
-
- default:
- regno = ILLEGAL_REG;
- break;
- }
- break;
-
- case 'l':
- regno = mips32_to_micromips_reg_l_map[regno];
- break;
-
- case 'm':
- regno = mips32_to_micromips_reg_m_map[regno];
- break;
-
- case 'n':
- regno = mips32_to_micromips_reg_n_map[regno];
- break;
-
- case 'q':
- regno = mips32_to_micromips_reg_q_map[regno];
- break;
-
- case 's':
- if (regno != SP)
- regno = ILLEGAL_REG;
- break;
-
- case 'y':
- if (regno != 31)
- regno = ILLEGAL_REG;
- break;
-
- case 'z':
- if (regno != ZERO)
- regno = ILLEGAL_REG;
- break;
-
- case 'j': /* Do nothing. */
- case 'p':
- case 't':
- case 'x':
- break;
-
- default:
- abort ();
- }
+ switch (mask)
+ {
+ default:
+ abort ();
- if (regno == ILLEGAL_REG)
- break;
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ start_noreorder ();
+ macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", yreg);
+ macro_build (NULL, "break", "6", 7);
- switch (c)
- {
- case 'b':
- INSERT_OPERAND (1, MB, *ip, regno);
- break;
-
- case 'c':
- INSERT_OPERAND (1, MC, *ip, regno);
- break;
-
- case 'd':
- INSERT_OPERAND (1, MD, *ip, regno);
- break;
-
- case 'e':
- INSERT_OPERAND (1, ME, *ip, regno);
- break;
-
- case 'f':
- INSERT_OPERAND (1, MF, *ip, regno);
- break;
-
- case 'g':
- INSERT_OPERAND (1, MG, *ip, regno);
- break;
-
- case 'h':
- INSERT_OPERAND (1, MH, *ip, regno);
- break;
-
- case 'i':
- INSERT_OPERAND (1, MI, *ip, regno);
- break;
-
- case 'j':
- INSERT_OPERAND (1, MJ, *ip, regno);
- break;
-
- case 'l':
- INSERT_OPERAND (1, ML, *ip, regno);
- break;
-
- case 'm':
- INSERT_OPERAND (1, MM, *ip, regno);
- break;
-
- case 'n':
- INSERT_OPERAND (1, MN, *ip, regno);
- break;
-
- case 'p':
- INSERT_OPERAND (1, MP, *ip, regno);
- break;
-
- case 'q':
- INSERT_OPERAND (1, MQ, *ip, regno);
- break;
-
- case 'a': /* Do nothing. */
- case 's': /* Do nothing. */
- case 't': /* Do nothing. */
- case 'x': /* Do nothing. */
- case 'y': /* Do nothing. */
- case 'z': /* Do nothing. */
- break;
-
- default:
- abort ();
- }
- continue;
+ /* FIXME: The normal code checks for of -1 / -0x80000000 here,
+ since that causes an overflow. We should do that as well,
+ but I don't see how to do the comparisons without a temporary
+ register. */
+ end_noreorder ();
+ macro_build (NULL, s, "x", zreg);
+ break;
- case 'A':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMA, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -64, 64, 2))
- break;
-
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMA, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ start_noreorder ();
+ macro_build (NULL, s, "0,x,y", xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", yreg);
+ macro_build (NULL, "break", "6", 7);
+ end_noreorder ();
+ macro_build (NULL, s2, "x", zreg);
+ break;
- case 'B':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", xreg, yreg);
+ macro_build (NULL, "mflo", "x", zreg);
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || ep.X_op != O_constant)
- break;
+ case M_DSUBU_I:
+ dbl = 1;
+ goto do_subu;
+ case M_SUBU_I:
+ do_subu:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", yreg, xreg);
+ break;
- for (imm = 0; imm < 8; imm++)
- if (micromips_imm_b_map[imm] == ep.X_add_number)
- break;
- if (imm >= 8)
- break;
+ case M_SUBU_I_2:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "addiu", "x,k", xreg);
+ break;
- INSERT_OPERAND (1, IMMB, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_DSUBU_I_2:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "daddiu", "y,j", yreg);
+ break;
- case 'C':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_BEQ:
+ s = "cmp";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BNE:
+ s = "cmp";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLTU:
+ s = "sltu";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BLEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BGE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_reverse_branch;
+ case M_BGTU:
+ s = "sltu";
+ s2 = "btnez";
- if (my_getSmallExpression (&ep, r, s) > 0
- || ep.X_op != O_constant)
- break;
+ do_reverse_branch:
+ tmp = xreg;
+ xreg = yreg;
+ yreg = tmp;
- for (imm = 0; imm < 16; imm++)
- if (micromips_imm_c_map[imm] == ep.X_add_number)
- break;
- if (imm >= 16)
- break;
+ do_branch:
+ macro_build (NULL, s, "x,y", xreg, yreg);
+ macro_build (&offset_expr, s2, "p");
+ break;
- INSERT_OPERAND (1, IMMC, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_BEQ_I:
+ s = "cmpi";
+ s2 = "bteqz";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BNE_I:
+ s = "cmpi";
+ s2 = "btnez";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BLT_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLTU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLE_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BLEU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGE_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGEU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGT_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGTU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
- case 'D': /* pc relative offset */
- case 'E': /* pc relative offset */
- my_getExpression (&offset_expr, s);
- if (offset_expr.X_op == O_register)
- break;
+ do_addone_branch_i:
+ if (imm_expr.X_op != O_constant)
+ as_bad (_("Unsupported large constant"));
+ ++imm_expr.X_add_number;
- if (!forced_insn_length)
- *offset_reloc = (int) BFD_RELOC_UNUSED + c;
- else if (c == 'D')
- *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
- else
- *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
- s = expr_end;
- continue;
+ do_branch_i:
+ macro_build (&imm_expr, s, s3, xreg);
+ macro_build (&offset_expr, s2, "p");
+ break;
- case 'F':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_ABS:
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "slti", "x,8", yreg);
+ if (xreg != yreg)
+ move_register (xreg, yreg);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bteqz", "p");
+ macro_build (NULL, "neg", "x,w", xreg, xreg);
+ }
+}
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
+/* Assemble an instruction into its binary format. If the instruction
+ is a macro, set imm_expr, imm2_expr and offset_expr to the values
+ associated with "I", "+I" and "A" operands respectively. Otherwise
+ store the value of the relocatable field (if any) in offset_expr.
+ In both cases set offset_reloc to the relocation operators applied
+ to offset_expr. */
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMF, *ip, imm);
- }
- s = expr_end;
- continue;
+static void
+mips_ip (char *str, struct mips_cl_insn *ip)
+{
+ bfd_boolean wrong_delay_slot_insns = FALSE;
+ bfd_boolean need_delay_slot_ok = TRUE;
+ struct mips_opcode *firstinsn = NULL;
+ const struct mips_opcode *past;
+ struct hash_control *hash;
+ const char *args;
+ char c = 0;
+ struct mips_opcode *insn;
+ long opend;
+ char *name;
+ char *dot;
+ char format;
+ long end;
+ const struct mips_operand *operand;
+ struct mips_arg_info arg;
+ struct mips_operand_token *tokens;
+ bfd_boolean optional_reg;
- case 'G':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMG, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -1, 15, 0))
- break;
-
- imm = ep.X_add_number & 15;
- INSERT_OPERAND (1, IMMG, *ip, imm);
- }
- s = expr_end;
- continue;
+ insn_error = NULL;
- case 'H':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMH, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 1))
- break;
-
- imm = ep.X_add_number >> 1;
- INSERT_OPERAND (1, IMMH, *ip, imm);
- }
- s = expr_end;
- continue;
+ if (mips_opts.micromips)
+ {
+ hash = micromips_op_hash;
+ past = µmips_opcodes[bfd_micromips_num_opcodes];
+ }
+ else
+ {
+ hash = op_hash;
+ past = &mips_opcodes[NUMOPCODES];
+ }
+ forced_insn_length = 0;
+ insn = NULL;
- case 'I':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ /* We first try to match an instruction up to a space or to the end. */
+ for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
+ continue;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -1, 127, 0))
- break;
+ /* Make a copy of the instruction so that we can fiddle with it. */
+ name = alloca (end + 1);
+ memcpy (name, str, end);
+ name[end] = '\0';
- imm = ep.X_add_number & 127;
- INSERT_OPERAND (1, IMMI, *ip, imm);
- }
- s = expr_end;
- continue;
+ for (;;)
+ {
+ insn = (struct mips_opcode *) hash_find (hash, name);
- case 'J':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMJ, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 2))
- break;
-
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMJ, *ip, imm);
- }
- s = expr_end;
- continue;
+ if (insn != NULL || !mips_opts.micromips)
+ break;
+ if (forced_insn_length)
+ break;
- case 'L':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMML, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
-
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMML, *ip, imm);
- }
- s = expr_end;
- continue;
+ /* See if there's an instruction size override suffix,
+ either `16' or `32', at the end of the mnemonic proper,
+ that defines the operation, i.e. before the first `.'
+ character if any. Strip it and retry. */
+ dot = strchr (name, '.');
+ opend = dot != NULL ? dot - name : end;
+ if (opend < 3)
+ break;
+ if (name[opend - 2] == '1' && name[opend - 1] == '6')
+ forced_insn_length = 2;
+ else if (name[opend - 2] == '3' && name[opend - 1] == '2')
+ forced_insn_length = 4;
+ else
+ break;
+ memcpy (name + opend - 2, name + opend, end - opend + 1);
+ }
+ if (insn == NULL)
+ {
+ insn_error = _("Unrecognized opcode");
+ return;
+ }
- case 'M':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ if (strcmp (name, "li.s") == 0)
+ format = 'f';
+ else if (strcmp (name, "li.d") == 0)
+ format = 'd';
+ else
+ format = 0;
+ tokens = mips_parse_arguments (str + end, format);
+ if (!tokens)
+ return;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 1, 9, 0))
- break;
+ /* For microMIPS instructions placed in a fixed-length branch delay slot
+ we make up to two passes over the relevant fragment of the opcode
+ table. First we try instructions that meet the delay slot's length
+ requirement. If none matched, then we retry with the remaining ones
+ and if one matches, then we use it and then issue an appropriate
+ warning later on. */
+ for (;;)
+ {
+ bfd_boolean delay_slot_ok;
+ bfd_boolean size_ok;
+ bfd_boolean ok;
+ bfd_boolean more_alts;
- imm = ep.X_add_number & 7;
- INSERT_OPERAND (1, IMMM, *ip, imm);
- }
- s = expr_end;
- continue;
+ gas_assert (strcmp (insn->name, name) == 0);
- case 'N': /* Register list for lwm and swm. */
- {
- /* A comma-separated list of registers and/or
- dash-separated contiguous ranges including
- both ra and a set of one or more registers
- starting at s0 up to s3 which have to be
- consecutive, e.g.:
+ ok = is_opcode_valid (insn);
+ size_ok = is_size_valid (insn);
+ delay_slot_ok = is_delay_slot_valid (insn);
+ if (!delay_slot_ok && !wrong_delay_slot_insns)
+ {
+ firstinsn = insn;
+ wrong_delay_slot_insns = TRUE;
+ }
+ more_alts = (insn + 1 < past
+ && strcmp (insn[0].name, insn[1].name) == 0);
+ if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
+ {
+ static char buf[256];
- s0, ra
- s0, s1, ra, s2, s3
- s0-s2, ra
+ if (more_alts)
+ {
+ ++insn;
+ continue;
+ }
+ if (wrong_delay_slot_insns && need_delay_slot_ok)
+ {
+ gas_assert (firstinsn);
+ need_delay_slot_ok = FALSE;
+ past = insn + 1;
+ insn = firstinsn;
+ continue;
+ }
- and any permutations of these. */
- unsigned int reglist;
- int imm;
+ obstack_free (&mips_operand_tokens, tokens);
+ if (insn_error)
+ return;
- if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
- break;
+ if (!ok)
+ sprintf (buf, _("Opcode not supported on this processor: %s (%s)"),
+ mips_cpu_info_from_arch (mips_opts.arch)->name,
+ mips_cpu_info_from_isa (mips_opts.isa)->name);
+ else if (mips_opts.insn32)
+ sprintf (buf, _("Opcode not supported in the `insn32' mode"));
+ else
+ sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
+ 8 * forced_insn_length);
+ insn_error = buf;
- if ((reglist & 0xfff1ffff) != 0x80010000)
- break;
+ return;
+ }
- reglist = (reglist >> 17) & 7;
- reglist += 1;
- if ((reglist & -reglist) != reglist)
- break;
+ imm_expr.X_op = O_absent;
+ imm2_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
- imm = ffs (reglist) - 1;
- INSERT_OPERAND (1, IMMN, *ip, imm);
+ create_insn (ip, insn);
+ insn_error = NULL;
+ memset (&arg, 0, sizeof (arg));
+ arg.insn = ip;
+ arg.token = tokens;
+ arg.argnum = 1;
+ arg.last_regno = ILLEGAL_REG;
+ arg.dest_regno = ILLEGAL_REG;
+ arg.soft_match = (more_alts
+ || (wrong_delay_slot_insns && need_delay_slot_ok));
+ for (args = insn->args;; ++args)
+ {
+ if (arg.token->type == OT_END)
+ {
+ /* 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 'r':
+ case 'v':
+ case 'w':
+ case 'W':
+ case 'V':
+ arg.token = tokens;
+ arg.argnum = 1;
+ continue;
}
- continue;
- case 'O': /* sdbbp 4-bit code. */
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ /* Treat elided base registers as $0. */
+ if (strcmp (args, "(b)") == 0)
+ args += 3;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ break;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMO, *ip, imm);
- }
- s = expr_end;
- continue;
+ /* Successful match. */
+ if (arg.dest_regno == arg.last_regno
+ && strncmp (ip->insn_mo->name, "jalr", 4) == 0)
+ {
+ if (arg.opnum == 2)
+ as_bad (_("Source and destination must be different"));
+ else if (arg.last_regno == 31)
+ as_bad (_("A destination register must be supplied"));
+ }
+ check_completed_insn (&arg);
+ obstack_free (&mips_operand_tokens, tokens);
+ return;
+ }
- case 'P':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 32, 2))
- break;
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
+ {
+ if (match_char (&arg, *args))
+ continue;
+ break;
+ }
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMP, *ip, imm);
- }
- s = expr_end;
+ /* Handle special macro operands. Work out the properties of
+ other operands. */
+ arg.opnum += 1;
+ arg.lax_max = FALSE;
+ optional_reg = FALSE;
+ switch (*args)
+ {
+ case '+':
+ switch (args[1])
+ {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case 'B':
+ case 'C':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'J':
+ case 'Q':
+ case 'S':
+ case 's':
+ /* If these integer forms come last, there is no other
+ form of the instruction that could match. Prefer to
+ give detailed error messages where possible. */
+ if (args[2] == 0)
+ arg.soft_match = FALSE;
+ break;
+
+ case 'I':
+ /* "+I" is like "I", except that imm2_expr is used. */
+ if (match_const_int (&arg, &imm2_expr.X_add_number, 0))
+ imm2_expr.X_op = O_constant;
+ else
+ insn_error = _("absolute expression required");
+ if (HAVE_32BIT_GPRS)
+ normalize_constant_expr (&imm2_expr);
+ ++args;
continue;
- case 'Q':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case 'i':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
+ }
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
- break;
+ case '\'':
+ case ':':
+ case '@':
+ case '^':
+ case '$':
+ case '\\':
+ case '%':
+ case '|':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '8':
+ case 'B':
+ case 'C':
+ case 'J':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'c':
+ case 'h':
+ case 'q':
+ /* If these integer forms come last, there is no other
+ form of the instruction that could match. Prefer to
+ give detailed error messages where possible. */
+ if (args[1] == 0)
+ arg.soft_match = FALSE;
+ break;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMQ, *ip, imm);
- }
- s = expr_end;
- continue;
+ case 'r':
+ case 'v':
+ case 'w':
+ case 'W':
+ case 'V':
+ /* We have already matched a comma by this point, so the register
+ is only optional if there is another operand to come. */
+ gas_assert (arg.opnum == 2);
+ optional_reg = (args[1] == ',');
+ break;
- case 'U':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- /* Check whether there is only a single bracketed
- expression left. If so, it must be the base register
- and the constant must be zero. */
- if (*s == '(' && strchr (s + 1, '(') == 0)
- {
- INSERT_OPERAND (1, IMMU, *ip, 0);
- continue;
- }
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 32, 2))
- break;
-
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMU, *ip, imm);
- }
- s = expr_end;
- continue;
+ case 'I':
+ if (match_const_int (&arg, &imm_expr.X_add_number, 0))
+ imm_expr.X_op = O_constant;
+ else
+ insn_error = _("absolute expression required");
+ if (HAVE_32BIT_GPRS)
+ normalize_constant_expr (&imm_expr);
+ continue;
- case 'W':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case 'A':
+ if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+ {
+ /* Assume that the offset has been elided and that what
+ we saw was a base register. The match will fail later
+ if that assumption turns out to be wrong. */
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else if (match_expression (&arg, &offset_expr, offset_reloc))
+ normalize_address_expr (&offset_expr);
+ else
+ insn_error = _("absolute expression required");
+ continue;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 64, 2))
- break;
+ case 'F':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, TRUE))
+ insn_error = _("floating-point expression required");
+ continue;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMW, *ip, imm);
- }
- s = expr_end;
- continue;
+ case 'L':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, FALSE))
+ insn_error = _("floating-point expression required");
+ continue;
- case 'X':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case 'f':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, TRUE))
+ insn_error = _("floating-point expression required");
+ continue;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -8, 8, 0))
- break;
+ case 'l':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, FALSE))
+ insn_error = _("floating-point expression required");
+ continue;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMX, *ip, imm);
- }
- s = expr_end;
- continue;
+ /* ??? This is the traditional behavior, but is flaky if
+ there are alternative versions of the same instruction
+ for different subarchitectures. The next alternative
+ might not be suitable. */
+ case 'j':
+ /* For compatibility with older assemblers, we accept
+ 0x8000-0xffff as signed 16-bit numbers when only
+ signed numbers are allowed. */
+ arg.lax_max = !more_alts;
+ case 'i':
+ /* Only accept non-constant operands if this is the
+ final alternative. Later alternatives might include
+ a macro implementation. */
+ arg.allow_nonconst = !more_alts;
+ break;
- case 'Y':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || expr_const_in_range (&ep, -2, 2, 2)
- || !expr_const_in_range (&ep, -258, 258, 2))
- break;
-
- imm = ep.X_add_number >> 2;
- imm = ((imm >> 1) & ~0xff) | (imm & 0xff);
- INSERT_OPERAND (1, IMMY, *ip, imm);
- }
- s = expr_end;
- continue;
+ case 'u':
+ /* There are no macro implementations for out-of-range values. */
+ arg.allow_nonconst = TRUE;
+ break;
- case 'Z':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
+ case 'o':
+ /* There should always be a macro implementation. */
+ arg.allow_nonconst = FALSE;
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 1, 0))
- break;
- }
- s = expr_end;
- continue;
+ case 'p':
+ *offset_reloc = BFD_RELOC_16_PCREL_S2;
+ break;
- default:
- as_bad (_("Internal error: bad microMIPS opcode "
- "(unknown extension operand type `m%c'): %s %s"),
- *args, insn->name, insn->args);
- /* Further processing is fruitless. */
- return;
- }
+ case 'a':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
break;
- case 'n': /* Register list for 32-bit lwm and swm. */
+ case 'm':
gas_assert (mips_opts.micromips);
- {
- /* A comma-separated list of registers and/or
- dash-separated contiguous ranges including
- at least one of ra and a set of one or more
- registers starting at s0 up to s7 and then
- s8 which have to be consecutive, e.g.:
-
- ra
- s0
- ra, s0, s1, s2
- s0-s8
- s0-s5, ra
-
- and any permutations of these. */
- unsigned int reglist;
- int imm;
- int ra;
-
- if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
+ c = args[1];
+ switch (c)
+ {
+ case 't':
+ case 'c':
+ case 'e':
+ /* We have already matched a comma by this point,
+ so the register is only optional if there is another
+ operand to come. */
+ gas_assert (arg.opnum == 2);
+ optional_reg = (args[2] == ',');
break;
- if ((reglist & 0x3f00ffff) != 0)
+ case 'D':
+ case 'E':
+ if (!forced_insn_length)
+ *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+ else if (c == 'D')
+ *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+ else
+ *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
break;
+ }
+ break;
+ }
- ra = (reglist >> 27) & 0x10;
- reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
- reglist += 1;
- if ((reglist & -reglist) != reglist)
- break;
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (args)
+ : decode_mips_operand (args));
+ if (!operand)
+ abort ();
- imm = (ffs (reglist) - 1) | ra;
- INSERT_OPERAND (1, RT, *ip, imm);
- imm_expr.X_op = O_absent;
- }
- continue;
+ if (optional_reg
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
+ {
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
+ }
- case '|': /* 4-bit trap code. */
- gas_assert (mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number
- > MICROMIPSOP_MASK_TRAP)
- as_bad (_("Trap code (%lu) for %s not in 0..15 range"),
- (unsigned long) imm_expr.X_add_number,
- ip->insn_mo->name);
- INSERT_OPERAND (1, TRAP, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ if (!match_operand (&arg, operand))
+ break;
- default:
- as_bad (_("Bad char = '%c'\n"), *args);
- abort ();
- }
- break;
+ /* Skip prefixes. */
+ if (*args == '+' || *args == 'm')
+ args++;
+
+ continue;
}
/* Args don't match. */
- s = argsStart;
insn_error = _("Illegal operands");
- if (insn + 1 < past && !strcmp (insn->name, insn[1].name))
+ if (more_alts)
{
++insn;
continue;
}
- else if (wrong_delay_slot_insns && need_delay_slot_ok)
+ if (wrong_delay_slot_insns && need_delay_slot_ok)
{
gas_assert (firstinsn);
need_delay_slot_ok = FALSE;
insn = firstinsn;
continue;
}
+ obstack_free (&mips_operand_tokens, tokens);
return;
}
}
-#define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); }
-
-/* This routine assembles an instruction into its binary format when
- assembling for the mips16. As a side effect, it sets one of the
- global variables imm_reloc or offset_reloc to the type of relocation
- to do if one of the operands is an address expression. It also sets
- forced_insn_length to the resulting instruction size in bytes if the
- user explicitly requested a small or extended instruction. */
+/* As for mips_ip, but used when assembling MIPS16 code.
+ Also set forced_insn_length to the resulting instruction size in
+ bytes if the user explicitly requested a small or extended instruction. */
static void
mips16_ip (char *str, struct mips_cl_insn *ip)
char *s;
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;
+ struct mips_operand_token *tokens;
+ bfd_boolean optional_reg;
insn_error = NULL;
return;
}
- argsstart = s;
+ tokens = mips_parse_arguments (s, 0);
+ if (!tokens)
+ return;
+
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;
mips_cpu_info_from_isa (mips_opts.isa)->name);
insn_error = buf;
}
+ obstack_free (&mips_operand_tokens, tokens);
return;
}
}
create_insn (ip, insn);
imm_expr.X_op = O_absent;
- imm_reloc[0] = BFD_RELOC_UNUSED;
- imm_reloc[1] = BFD_RELOC_UNUSED;
- imm_reloc[2] = BFD_RELOC_UNUSED;
imm2_expr.X_op = O_absent;
offset_expr.X_op = O_absent;
offset_reloc[0] = BFD_RELOC_UNUSED;
offset_reloc[1] = BFD_RELOC_UNUSED;
offset_reloc[2] = BFD_RELOC_UNUSED;
+ relax_char = 0;
+
+ memset (&arg, 0, sizeof (arg));
+ arg.insn = ip;
+ arg.token = tokens;
+ 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)
+ if (arg.token->type == OT_END)
{
- case '\0':
- if (*s == '\0')
- {
- offsetT value;
-
- /* Stuff the immediate value in now, if we can. */
- if (imm_expr.X_op == O_constant
- && *imm_reloc > BFD_RELOC_UNUSED
- && insn->pinfo != INSN_MACRO
- && calculate_reloc (*offset_reloc,
- imm_expr.X_add_number, &value))
- {
- mips16_immed (NULL, 0, *imm_reloc - BFD_RELOC_UNUSED,
- *offset_reloc, value, forced_insn_length,
- &ip->insn_opcode);
- imm_expr.X_op = O_absent;
- *imm_reloc = BFD_RELOC_UNUSED;
- *offset_reloc = BFD_RELOC_UNUSED;
- }
+ offsetT value;
- 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.token = tokens;
+ arg.argnum = 1;
+ 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] != '$')
- {
- if (c == 'v')
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- else
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- ++args;
- continue;
- }
- /* 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))
+ /* Successful match. Stuff the immediate value in now, if
+ we can. */
+ if (insn->pinfo == INSN_MACRO)
{
- if (c == 'v' || c == 'w')
- {
- if (c == 'v')
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- else
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- ++args;
- continue;
- }
- break;
+ gas_assert (relax_char == 0);
+ gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
}
-
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
+ 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')
- {
- regno = mips16_to_32_reg_map[lastregno];
- s = s_reset;
- ++args;
- }
+ 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;
}
-
- switch (c)
+ else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
{
- 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;
-
- default:
- abort ();
+ 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;
- if (regno == ILLEGAL_REG)
- break;
+ check_completed_insn (&arg);
+ obstack_free (&mips_operand_tokens, tokens);
+ return;
+ }
- 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 ();
- }
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ break;
- lastregno = regno;
- continue;
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
+ {
+ if (match_char (&arg, *args))
+ continue;
+ break;
+ }
- case 'P':
- if (strncmp (s, "$pc", 3) == 0)
- {
- s += 3;
- continue;
- }
+ arg.opnum += 1;
+ optional_reg = FALSE;
+ c = *args;
+ switch (c)
+ {
+ case 'v':
+ case 'w':
+ 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 (&imm_expr, imm_reloc, s);
- if (i > 0)
- {
- if (imm_expr.X_op != O_constant)
- {
- forced_insn_length = 4;
- ip->insn_opcode |= MIPS16_EXTEND;
- }
- else
- {
- /* We need to relax this instruction. */
- *offset_reloc = *imm_reloc;
- *imm_reloc = (int) BFD_RELOC_UNUSED + c;
- }
- s = expr_end;
- continue;
- }
- *imm_reloc = BFD_RELOC_UNUSED;
- /* Fall through. */
- case '<':
- case '>':
- case '[':
- case ']':
- case '4':
- case '8':
- my_getExpression (&imm_expr, s);
- if (imm_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 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;
- }
-
- /* We need to relax this instruction. */
- *imm_reloc = (int) BFD_RELOC_UNUSED + 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. */
- *offset_reloc = (int) BFD_RELOC_UNUSED + c;
- s = expr_end;
- continue;
+ relax_char = c;
+ break;
- 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;
+ case 'I':
+ if (match_const_int (&arg, &imm_expr.X_add_number, 0))
+ imm_expr.X_op = O_constant;
+ else
+ insn_error = _("absolute expression required");
+ if (HAVE_32BIT_GPRS)
+ normalize_constant_expr (&imm_expr);
continue;
- case 'a': /* 26 bit address */
- my_getExpression (&offset_expr, s);
- s = expr_end;
+ case 'a':
+ case 'i':
*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;
- }
+ break;
+ }
- /* Encode $s0/$s1. */
- if (sregs & (1 << 0)) /* $s0 */
- opcode |= 0x20;
- if (sregs & (1 << 1)) /* $s1 */
- opcode |= 0x10;
- sregs >>= 2;
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
- 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;
- }
+ /* '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)
+ {
+ if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+ {
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ relax_char = c;
+ continue;
+ }
- /* 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));
- }
+ /* We need the OT_INTEGER check because some MIPS16
+ immediate variants are listed before the register ones. */
+ if (arg.token->type != OT_INTEGER
+ || !match_expression (&arg, &offset_expr, offset_reloc))
+ break;
- /* Finally build the instruction. */
- if ((opcode >> 16) != 0 || framesz == 0)
- opcode |= MIPS16_EXTEND;
- ip->insn_opcode = opcode;
- }
- continue;
+ /* '8' is used for SLTI(U) and has traditionally not
+ been allowed to take relocation operators. */
+ if (offset_reloc[0] != BFD_RELOC_UNUSED
+ && (ext_operand->size != 16 || c == '8'))
+ break;
- 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;
+ relax_char = c;
+ continue;
}
- ip->insn_opcode |= imm_expr.X_add_number;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ }
- default:
- abort ();
+ if (optional_reg
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
+ {
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
}
- break;
+
+ if (!match_operand (&arg, operand))
+ 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;
continue;
}
insn_error = _("illegal operands");
+ obstack_free (&mips_operand_tokens, tokens);
return;
}
}
mips_pic = VXWORKS_PIC;
break;
+ case OPTION_NAN:
+ if (strcmp (arg, "2008") == 0)
+ mips_flag_nan2008 = TRUE;
+ else if (strcmp (arg, "legacy") == 0)
+ mips_flag_nan2008 = FALSE;
+ else
+ {
+ as_fatal (_("Invalid NaN setting -mnan=%s"), arg);
+ return 0;
+ }
+ break;
+
default:
return 0;
}
demand_empty_rest_of_line ();
}
+/* Handle the .nan pseudo-op. */
+
+static void
+s_nan (int ignore ATTRIBUTE_UNUSED)
+{
+ static const char str_legacy[] = "legacy";
+ static const char str_2008[] = "2008";
+ size_t i;
+
+ for (i = 0; !is_end_of_line[(unsigned char) input_line_pointer[i]]; i++);
+
+ if (i == sizeof (str_2008) - 1
+ && memcmp (input_line_pointer, str_2008, i) == 0)
+ mips_flag_nan2008 = TRUE;
+ else if (i == sizeof (str_legacy) - 1
+ && memcmp (input_line_pointer, str_legacy, i) == 0)
+ mips_flag_nan2008 = FALSE;
+ else
+ as_bad (_("Bad .nan directive"));
+
+ input_line_pointer += i;
+ demand_empty_rest_of_line ();
+}
+
/* Handle a .stab[snd] directive. Ideally these directives would be
implemented in a transparent way, so that removing them would not
have any effect on the generated instructions. However, s_stab
if (mips_32bitmode)
elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
+ if (mips_flag_nan2008)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NAN2008;
+
#if 0 /* XXX FIXME */
/* 32 bit code with 64 bit FP registers. */
if (!file_mips_fp32 && ABI_NEEDS_32BIT_REGS (mips_abi))
-msingle-float only allow 32-bit floating-point operations\n\
-mdouble-float allow 32-bit and 64-bit floating-point operations\n\
--[no-]construct-floats [dis]allow floating point values to be constructed\n\
---[no-]relax-branch [dis]allow out-of-range branches to be relaxed\n"
- ));
+--[no-]relax-branch [dis]allow out-of-range branches to be relaxed\n\
+-mnan=ENCODING select an IEEE 754 NaN encoding convention, either of:\n"));
+
+ first = 1;
+
+ show (stream, "legacy", &column, &first);
+ show (stream, "2008", &column, &first);
+
+ fputc ('\n', stream);
+
fprintf (stream, _("\
-KPIC, -call_shared generate SVR4 position independent code\n\
-call_nonpic generate non-PIC code that can operate with DSOs\n\