/* tc-mips.c -- assemble code for a MIPS chip.
- Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
- Free Software Foundation, Inc.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
Contributed by the OSF and Ralph Campbell.
Written by Keith Knowles and Ralph Campbell, working independently.
Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
#define DBG(x)
#endif
+#define streq(a, b) (strcmp (a, b) == 0)
+
+#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; }
/* Restrict general purpose registers and floating point registers
to 32 bit. This is initially determined when -mgp32 or -mfp32
is passed but can changed if the assembler code uses .set mipsN. */
- int gp32;
- int fp32;
+ int gp;
+ int fp;
/* MIPS architecture (CPU) type. Changed by .set arch=FOO, the -march
command line option, and the default CPU. */
int arch;
bfd_boolean single_float;
};
-/* This is the struct we use to hold the current set of options. Note
- that we must set the isa field to ISA_UNKNOWN and the ASE fields to
- -1 to indicate that they have not been initialized. */
-
-/* True if -mgp32 was passed. */
-static int file_mips_gp32 = -1;
-
-/* True if -mfp32 was passed. */
-static int file_mips_fp32 = -1;
+/* True if -mnan=2008, false if -mnan=legacy. */
+static bfd_boolean mips_flag_nan2008 = FALSE;
-/* 1 if -msoft-float, 0 if -mhard-float. The default is 0. */
-static int file_mips_soft_float = 0;
+/* This is the struct we use to hold the module level set of options.
+ Note that we must set the isa field to ISA_UNKNOWN and the ASE, gp and
+ fp fields to -1 to indicate that they have not been initialized. */
-/* 1 if -msingle-float, 0 if -mdouble-float. The default is 0. */
-static int file_mips_single_float = 0;
+static struct mips_set_options file_mips_opts =
+{
+ /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
+ /* noreorder */ 0, /* at */ ATREG, /* warn_about_macros */ 0,
+ /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
+ /* gp */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+ /* soft_float */ FALSE, /* single_float */ FALSE
+};
-/* True if -mnan=2008, false if -mnan=legacy. */
-static bfd_boolean mips_flag_nan2008 = FALSE;
+/* This is similar to file_mips_opts, but for the current set of options. */
static struct mips_set_options mips_opts =
{
/* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
/* noreorder */ 0, /* at */ ATREG, /* warn_about_macros */ 0,
/* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
- /* gp32 */ 0, /* fp32 */ 0, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+ /* gp */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
/* soft_float */ FALSE, /* single_float */ FALSE
};
unsigned long mips_gprmask;
unsigned long mips_cprmask[4];
-/* MIPS ISA we are using for this output file. */
-static int file_mips_isa = ISA_UNKNOWN;
-
/* True if any MIPS16 code was produced. */
static int file_ase_mips16;
#define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \
|| mips_opts.isa == ISA_MIPS32R2 \
+ || mips_opts.isa == ISA_MIPS32R3 \
+ || mips_opts.isa == ISA_MIPS32R5 \
|| mips_opts.isa == ISA_MIPS64 \
- || mips_opts.isa == ISA_MIPS64R2)
+ || mips_opts.isa == ISA_MIPS64R2 \
+ || mips_opts.isa == ISA_MIPS64R3 \
+ || mips_opts.isa == ISA_MIPS64R5)
/* True if any microMIPS code was produced. */
static int file_ase_micromips;
#endif
/* The argument of the -march= flag. The architecture we are assembling. */
-static int file_mips_arch = CPU_UNKNOWN;
static const char *mips_arch_string;
/* The argument of the -mtune= flag. The architecture for which we
|| (ISA) == ISA_MIPS4 \
|| (ISA) == ISA_MIPS5 \
|| (ISA) == ISA_MIPS64 \
- || (ISA) == ISA_MIPS64R2)
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5)
/* Return true if ISA supports 64 bit wide float registers. */
#define ISA_HAS_64BIT_FPRS(ISA) \
|| (ISA) == ISA_MIPS4 \
|| (ISA) == ISA_MIPS5 \
|| (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
|| (ISA) == ISA_MIPS64 \
- || (ISA) == ISA_MIPS64R2)
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 )
/* Return true if ISA supports 64-bit right rotate (dror et al.)
instructions. */
#define ISA_HAS_DROR(ISA) \
((ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
|| (mips_opts.micromips \
&& ISA_HAS_64BIT_REGS (ISA)) \
)
instructions. */
#define ISA_HAS_ROR(ISA) \
((ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
|| (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
|| (mips_opts.ase & ASE_SMARTMIPS) \
|| mips_opts.micromips \
)
#define ISA_HAS_ODD_SINGLE_FPR(ISA) \
((ISA) == ISA_MIPS32 \
|| (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
|| (ISA) == ISA_MIPS64 \
- || (ISA) == ISA_MIPS64R2)
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5)
/* Return true if ISA supports move to/from high part of a 64-bit
floating-point register. */
#define ISA_HAS_MXHC1(ISA) \
((ISA) == ISA_MIPS32R2 \
- || (ISA) == ISA_MIPS64R2)
-
-#define HAVE_32BIT_GPRS \
- (mips_opts.gp32 || !ISA_HAS_64BIT_REGS (mips_opts.isa))
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5)
-#define HAVE_32BIT_FPRS \
- (mips_opts.fp32 || !ISA_HAS_64BIT_FPRS (mips_opts.isa))
+#define GPR_SIZE \
+ (mips_opts.gp == 64 && !ISA_HAS_64BIT_REGS (mips_opts.isa) \
+ ? 32 \
+ : mips_opts.gp)
-#define HAVE_64BIT_GPRS (!HAVE_32BIT_GPRS)
-#define HAVE_64BIT_FPRS (!HAVE_32BIT_FPRS)
+#define FPR_SIZE \
+ (mips_opts.fp == 64 && !ISA_HAS_64BIT_FPRS (mips_opts.isa) \
+ ? 32 \
+ : mips_opts.fp)
#define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI)
/* The ABI-derived address size. */
#define HAVE_64BIT_ADDRESSES \
- (HAVE_64BIT_GPRS && (mips_abi == EABI_ABI || mips_abi == N64_ABI))
+ (GPR_SIZE == 64 && (mips_abi == EABI_ABI || mips_abi == N64_ABI))
#define HAVE_32BIT_ADDRESSES (!HAVE_64BIT_ADDRESSES)
/* The size of symbolic constants (i.e., expressions of the form
#define hilo_interlocks \
(mips_opts.isa == ISA_MIPS32 \
|| mips_opts.isa == ISA_MIPS32R2 \
+ || mips_opts.isa == ISA_MIPS32R3 \
+ || mips_opts.isa == ISA_MIPS32R5 \
|| mips_opts.isa == ISA_MIPS64 \
|| mips_opts.isa == ISA_MIPS64R2 \
+ || mips_opts.isa == ISA_MIPS64R3 \
+ || mips_opts.isa == ISA_MIPS64R5 \
|| mips_opts.arch == CPU_R4010 \
|| mips_opts.arch == CPU_R5900 \
|| mips_opts.arch == CPU_R10000 \
/* Whether the processor uses hardware interlocks to protect reads
from the GPRs after they are loaded from memory, and thus does not
require nops to be inserted. This applies to instructions marked
- INSN_LOAD_MEMORY_DELAY. These nops are only required at MIPS ISA
+ INSN_LOAD_MEMORY. These nops are only required at MIPS ISA
level I and microMIPS mode instructions are always interlocked. */
#define gpr_interlocks \
(mips_opts.isa != ISA_MIPS1 \
((mips_opts.mips16 | mips_opts.micromips) != 0)
/* The minimum and maximum signed values that can be stored in a GPR. */
-#define GPR_SMAX ((offsetT) (((valueT) 1 << (HAVE_64BIT_GPRS ? 63 : 31)) - 1))
+#define GPR_SMAX ((offsetT) (((valueT) 1 << (GPR_SIZE - 1)) - 1))
#define GPR_SMIN (-GPR_SMAX - 1)
/* MIPS PIC level. */
but nothing is ideal around here.
*/
-static char *insn_error;
+/* Types of printf format used for instruction-related error messages.
+ "I" means int ("%d") and "S" means string ("%s"). */
+enum mips_insn_error_format {
+ ERR_FMT_PLAIN,
+ ERR_FMT_I,
+ ERR_FMT_SS,
+};
+
+/* Information about an error that was found while assembling the current
+ instruction. */
+struct mips_insn_error {
+ /* We sometimes need to match an instruction against more than one
+ opcode table entry. Errors found during this matching are reported
+ against a particular syntactic argument rather than against the
+ instruction as a whole. We grade these messages so that errors
+ against argument N have a greater priority than an error against
+ any argument < N, since the former implies that arguments up to N
+ were acceptable and that the opcode entry was therefore a closer match.
+ If several matches report an error against the same argument,
+ we only use that error if it is the same in all cases.
+
+ min_argnum is the minimum argument number for which an error message
+ should be accepted. It is 0 if MSG is against the instruction as
+ a whole. */
+ int min_argnum;
+
+ /* The printf()-style message, including its format and arguments. */
+ enum mips_insn_error_format format;
+ const char *msg;
+ union {
+ int i;
+ const char *ss[2];
+ } u;
+};
+
+/* The error that should be reported for the current instruction. */
+static struct mips_insn_error insn_error;
static int auto_align = 1;
fill a branch delay slot. */
static struct mips_cl_insn history[1 + MAX_NOPS];
+/* Arrays of operands for each instruction. */
+#define MAX_OPERANDS 6
+struct mips_operand_array {
+ const struct mips_operand *operand[MAX_OPERANDS];
+};
+static struct mips_operand_array *mips_operands;
+static struct mips_operand_array *mips16_operands;
+static struct mips_operand_array *micromips_operands;
+
/* Nop instructions used by emit_nop. */
static struct mips_cl_insn nop_insn;
static struct mips_cl_insn mips16_nop_insn;
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 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_c_map mips16_to_32_reg_map
#define micromips_to_32_reg_d_map mips16_to_32_reg_map
-#define micromips_to_32_reg_e_map mips16_to_32_reg_map
-#define micromips_to_32_reg_f_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_map1[] =
6, 7, 7, 21, 22, 5, 6, 7
};
-#define micromips_to_32_reg_l_map mips16_to_32_reg_map
-
/* The microMIPS registers with type m. */
static const unsigned int micromips_to_32_reg_m_map[] =
{
#define micromips_to_32_reg_n_map micromips_to_32_reg_m_map
-/* The microMIPS registers with type q. */
-static const unsigned int micromips_to_32_reg_q_map[] =
-{
- 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
/* ...likewise -mfix-24k. */
static int mips_fix_24k;
+/* ...likewise -mfix-rm7000 */
+static int mips_fix_rm7000;
+
/* ...likewise -mfix-cn63xxp1 */
static bfd_boolean mips_fix_cn63xxp1;
(((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) \
OPTION_MIPS32,
OPTION_MIPS64,
OPTION_MIPS32R2,
+ OPTION_MIPS32R3,
+ OPTION_MIPS32R5,
OPTION_MIPS64R2,
+ OPTION_MIPS64R3,
+ OPTION_MIPS64R5,
OPTION_MIPS16,
OPTION_NO_MIPS16,
OPTION_MIPS3D,
OPTION_NO_MT,
OPTION_VIRT,
OPTION_NO_VIRT,
+ OPTION_MSA,
+ OPTION_NO_MSA,
OPTION_SMARTMIPS,
OPTION_NO_SMARTMIPS,
OPTION_DSPR2,
OPTION_NO_DSPR2,
OPTION_EVA,
OPTION_NO_EVA,
+ OPTION_XPA,
+ OPTION_NO_XPA,
OPTION_MICROMIPS,
OPTION_NO_MICROMIPS,
OPTION_MCU,
OPTION_MNO_7000_HILO_FIX,
OPTION_FIX_24K,
OPTION_NO_FIX_24K,
+ OPTION_FIX_RM7000,
+ OPTION_NO_FIX_RM7000,
OPTION_FIX_LOONGSON2F_JUMP,
OPTION_NO_FIX_LOONGSON2F_JUMP,
OPTION_FIX_LOONGSON2F_NOP,
{"mips32", no_argument, NULL, OPTION_MIPS32},
{"mips64", no_argument, NULL, OPTION_MIPS64},
{"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+ {"mips32r3", no_argument, NULL, OPTION_MIPS32R3},
+ {"mips32r5", no_argument, NULL, OPTION_MIPS32R5},
{"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
+ {"mips64r3", no_argument, NULL, OPTION_MIPS64R3},
+ {"mips64r5", no_argument, NULL, OPTION_MIPS64R5},
/* Options which specify Application Specific Extensions (ASEs). */
{"mips16", no_argument, NULL, OPTION_MIPS16},
{"mno-mcu", no_argument, NULL, OPTION_NO_MCU},
{"mvirt", no_argument, NULL, OPTION_VIRT},
{"mno-virt", no_argument, NULL, OPTION_NO_VIRT},
+ {"mmsa", no_argument, NULL, OPTION_MSA},
+ {"mno-msa", no_argument, NULL, OPTION_NO_MSA},
+ {"mxpa", no_argument, NULL, OPTION_XPA},
+ {"mno-xpa", no_argument, NULL, OPTION_NO_XPA},
/* Old-style architecture options. Don't add more of these. */
{"m4650", no_argument, NULL, OPTION_M4650},
{"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130},
{"mfix-24k", no_argument, NULL, OPTION_FIX_24K},
{"mno-fix-24k", no_argument, NULL, OPTION_NO_FIX_24K},
+ {"mfix-rm7000", no_argument, NULL, OPTION_FIX_RM7000},
+ {"mno-fix-rm7000", no_argument, NULL, OPTION_NO_FIX_RM7000},
{"mfix-cn63xxp1", no_argument, NULL, OPTION_FIX_CN63XXP1},
{"mno-fix-cn63xxp1", no_argument, NULL, OPTION_NO_FIX_CN63XXP1},
{ "virt", ASE_VIRT, ASE_VIRT64,
OPTION_VIRT, OPTION_NO_VIRT,
- 2, 2, 2, 2 }
+ 2, 2, 2, 2 },
+
+ { "msa", ASE_MSA, ASE_MSA64,
+ OPTION_MSA, OPTION_NO_MSA,
+ 2, 2, 2, 2 },
+
+ { "xpa", ASE_XPA, 0,
+ OPTION_XPA, OPTION_NO_XPA,
+ 2, 2, -1, -1 }
};
/* The set of ASEs that require -mfp64. */
\f
static char *expr_end;
-/* Expressions which appear in macro instructions. These are set by
- mips_ip and read by macro. */
+/* An expression in a macro instruction. This is set by mips_ip and
+ mips16_ip and when populated is always an O_constant. */
static expressionS imm_expr;
-static expressionS imm2_expr;
/* The relocatable field in an instruction and the relocs associated
with it. These variables are used for instructions like LUI and
if (mips_opts.isa == ISA_MIPS32R2 || mips_opts.isa == ISA_MIPS64R2)
return 2;
+ if (mips_opts.isa == ISA_MIPS32R3 || mips_opts.isa == ISA_MIPS64R3)
+ return 3;
+
+ if (mips_opts.isa == ISA_MIPS32R5 || mips_opts.isa == ISA_MIPS64R5)
+ return 5;
+
/* microMIPS implies revision 2 or above. */
if (mips_opts.micromips)
return 2;
base = mips_opts.micromips ? "microMIPS" : "MIPS";
size = ISA_HAS_64BIT_REGS (mips_opts.isa) ? 64 : 32;
if (min_rev < 0)
- as_warn (_("The %d-bit %s architecture does not support the"
+ as_warn (_("the %d-bit %s architecture does not support the"
" `%s' extension"), size, base, ase->name);
else
- as_warn (_("The `%s' extension requires %s%d revision %d or greater"),
+ as_warn (_("the `%s' extension requires %s%d revision %d or greater"),
ase->name, base, size, min_rev);
}
if ((ase->flags & FP64_ASES)
- && mips_opts.fp32
+ && mips_opts.fp != 64
&& (warned_fp32 & ase->flags) != ase->flags)
{
warned_fp32 |= ase->flags;
- as_warn (_("The `%s' extension requires 64-bit FPRs"), ase->name);
+ as_warn (_("the `%s' extension requires 64-bit FPRs"), ase->name);
}
}
insn->cleared_p = 0;
}
+/* Get a list of all the operands in INSN. */
+
+static const struct mips_operand_array *
+insn_operands (const struct mips_cl_insn *insn)
+{
+ if (insn->insn_mo >= &mips_opcodes[0]
+ && insn->insn_mo < &mips_opcodes[NUMOPCODES])
+ return &mips_operands[insn->insn_mo - &mips_opcodes[0]];
+
+ if (insn->insn_mo >= &mips16_opcodes[0]
+ && insn->insn_mo < &mips16_opcodes[bfd_mips16_num_opcodes])
+ return &mips16_operands[insn->insn_mo - &mips16_opcodes[0]];
+
+ if (insn->insn_mo >= µmips_opcodes[0]
+ && insn->insn_mo < µmips_opcodes[bfd_micromips_num_opcodes])
+ return µmips_operands[insn->insn_mo - µmips_opcodes[0]];
+
+ abort ();
+}
+
+/* Get a description of operand OPNO of INSN. */
+
+static const struct mips_operand *
+insn_opno (const struct mips_cl_insn *insn, unsigned opno)
+{
+ const struct mips_operand_array *operands;
+
+ operands = insn_operands (insn);
+ if (opno >= MAX_OPERANDS || !operands->operand[opno])
+ abort ();
+ return operands->operand[opno];
+}
+
/* Install UVAL as the value of OPERAND in INSN. */
static inline void
insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
}
+/* Extract the value of OPERAND from INSN. */
+
+static inline unsigned
+insn_extract_operand (const struct mips_cl_insn *insn,
+ const struct mips_operand *operand)
+{
+ return mips_extract_operand (operand, insn->insn_opcode);
+}
+
/* Record the current MIPS16/microMIPS mode in now_seg. */
static void
}
}
+/* Clear the error in insn_error. */
+
+static void
+clear_insn_error (void)
+{
+ memset (&insn_error, 0, sizeof (insn_error));
+}
+
+/* Possibly record error message MSG for the current instruction.
+ If the error is about a particular argument, ARGNUM is the 1-based
+ number of that argument, otherwise it is 0. FORMAT is the format
+ of MSG. Return true if MSG was used, false if the current message
+ was kept. */
+
+static bfd_boolean
+set_insn_error_format (int argnum, enum mips_insn_error_format format,
+ const char *msg)
+{
+ if (argnum == 0)
+ {
+ /* Give priority to errors against specific arguments, and to
+ the first whole-instruction message. */
+ if (insn_error.msg)
+ return FALSE;
+ }
+ else
+ {
+ /* Keep insn_error if it is against a later argument. */
+ if (argnum < insn_error.min_argnum)
+ return FALSE;
+
+ /* If both errors are against the same argument but are different,
+ give up on reporting a specific error for this argument.
+ See the comment about mips_insn_error for details. */
+ if (argnum == insn_error.min_argnum
+ && insn_error.msg
+ && strcmp (insn_error.msg, msg) != 0)
+ {
+ insn_error.msg = 0;
+ insn_error.min_argnum += 1;
+ return FALSE;
+ }
+ }
+ insn_error.min_argnum = argnum;
+ insn_error.format = format;
+ insn_error.msg = msg;
+ return TRUE;
+}
+
+/* Record an instruction error with no % format fields. ARGNUM and MSG are
+ as for set_insn_error_format. */
+
+static void
+set_insn_error (int argnum, const char *msg)
+{
+ set_insn_error_format (argnum, ERR_FMT_PLAIN, msg);
+}
+
+/* Record an instruction error with one %d field I. ARGNUM and MSG are
+ as for set_insn_error_format. */
+
+static void
+set_insn_error_i (int argnum, const char *msg, int i)
+{
+ if (set_insn_error_format (argnum, ERR_FMT_I, msg))
+ insn_error.u.i = i;
+}
+
+/* Record an instruction error with two %s fields S1 and S2. ARGNUM and MSG
+ are as for set_insn_error_format. */
+
+static void
+set_insn_error_ss (int argnum, const char *msg, const char *s1, const char *s2)
+{
+ if (set_insn_error_format (argnum, ERR_FMT_SS, msg))
+ {
+ insn_error.u.ss[0] = s1;
+ insn_error.u.ss[1] = s2;
+ }
+}
+
+/* Report the error in insn_error, which is against assembly code STR. */
+
+static void
+report_insn_error (const char *str)
+{
+ const char *msg;
+
+ msg = ACONCAT ((insn_error.msg, " `%s'", NULL));
+ switch (insn_error.format)
+ {
+ case ERR_FMT_PLAIN:
+ as_bad (msg, str);
+ break;
+
+ case ERR_FMT_I:
+ as_bad (msg, insn_error.u.i, str);
+ break;
+
+ case ERR_FMT_SS:
+ as_bad (msg, insn_error.u.ss[0], insn_error.u.ss[1], str);
+ break;
+ }
+}
+
/* Initialize vr4120_conflicts. There is a bit of duplication here:
the idea is to make it obvious at a glance that each errata is
included. */
unsigned int num;
};
-#define RTYPE_MASK 0x1ff00
-#define RTYPE_NUM 0x00100
-#define RTYPE_FPU 0x00200
-#define RTYPE_FCC 0x00400
-#define RTYPE_VEC 0x00800
-#define RTYPE_GP 0x01000
-#define RTYPE_CP0 0x02000
-#define RTYPE_PC 0x04000
-#define RTYPE_ACC 0x08000
-#define RTYPE_CCC 0x10000
-#define RNUM_MASK 0x000ff
-#define RWARN 0x80000
+#define RNUM_MASK 0x00000ff
+#define RTYPE_MASK 0x0ffff00
+#define RTYPE_NUM 0x0000100
+#define RTYPE_FPU 0x0000200
+#define RTYPE_FCC 0x0000400
+#define RTYPE_VEC 0x0000800
+#define RTYPE_GP 0x0001000
+#define RTYPE_CP0 0x0002000
+#define RTYPE_PC 0x0004000
+#define RTYPE_ACC 0x0008000
+#define RTYPE_CCC 0x0010000
+#define RTYPE_VI 0x0020000
+#define RTYPE_VF 0x0040000
+#define RTYPE_R5900_I 0x0080000
+#define RTYPE_R5900_Q 0x0100000
+#define RTYPE_R5900_R 0x0200000
+#define RTYPE_R5900_ACC 0x0400000
+#define RTYPE_MSA 0x0800000
+#define RWARN 0x8000000
#define GENERIC_REGISTER_NUMBERS \
{"$0", RTYPE_NUM | 0}, \
{"$v30", RTYPE_VEC | 30}, \
{"$v31", RTYPE_VEC | 31}
+#define R5900_I_NAMES \
+ {"$I", RTYPE_R5900_I | 0}
+
+#define R5900_Q_NAMES \
+ {"$Q", RTYPE_R5900_Q | 0}
+
+#define R5900_R_NAMES \
+ {"$R", RTYPE_R5900_R | 0}
+
+#define R5900_ACC_NAMES \
+ {"$ACC", RTYPE_R5900_ACC | 0 }
+
#define MIPS_DSP_ACCUMULATOR_NAMES \
{"$ac0", RTYPE_ACC | 0}, \
{"$ac1", RTYPE_ACC | 1}, \
MIPS16_SPECIAL_REGISTER_NAMES,
MDMX_VECTOR_REGISTER_NAMES,
+ R5900_I_NAMES,
+ R5900_Q_NAMES,
+ R5900_R_NAMES,
+ R5900_ACC_NAMES,
MIPS_DSP_ACCUMULATOR_NAMES,
{0, 0}
};
{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)
+{
+ if ((symval & -2) == (RTYPE_GP | 2))
+ return RTYPE_VEC | (symval & 1);
+ return symval;
+}
+
+/* Return true if string [S, E) is a valid register name, storing its
+ symbol value in *SYMVAL_PTR if so. */
+
+static bfd_boolean
+mips_parse_register_1 (char *s, char *e, unsigned int *symval_ptr)
{
- symbolS *symbolP;
- char *e;
char save_c;
- int reg = -1;
+ symbolS *symbol;
+
+ /* Terminate name. */
+ save_c = *e;
+ *e = '\0';
+
+ /* Look up the name. */
+ symbol = symbol_find (s);
+ *e = save_c;
+
+ if (!symbol || S_GET_SEGMENT (symbol) != reg_section)
+ return FALSE;
+
+ *symval_ptr = S_GET_VALUE (symbol);
+ return TRUE;
+}
+
+/* Return true if the string at *SPTR is a valid register name. Allow it
+ to have a VU0-style channel suffix of the form x?y?z?w? if CHANNELS_PTR
+ is nonnull.
+
+ When returning true, move *SPTR past the register, store the
+ register's symbol value in *SYMVAL_PTR and the channel mask in
+ *CHANNELS_PTR (if nonnull). The symbol value includes the register
+ number (RNUM_MASK) and register type (RTYPE_MASK). The channel mask
+ is a 4-bit value of the form XYZW and is 0 if no suffix was given. */
+
+static bfd_boolean
+mips_parse_register (char **sptr, unsigned int *symval_ptr,
+ unsigned int *channels_ptr)
+{
+ char *s, *e, *m;
+ const char *q;
+ unsigned int channels, symval, bit;
/* Find end of name. */
- e = *s;
+ s = e = *sptr;
if (is_name_beginner (*e))
++e;
while (is_part_of_name (*e))
++e;
- /* Terminate name. */
- save_c = *e;
- *e = '\0';
-
- /* Look for a register symbol. */
- if ((symbolP = symbol_find (*s)) && S_GET_SEGMENT (symbolP) == reg_section)
- {
- 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;
- }
- /* Else see if this is a register defined in an itbl entry. */
- else if ((types & RTYPE_GP) && itbl_have_entries)
+ channels = 0;
+ if (!mips_parse_register_1 (s, e, &symval))
{
- char *n = *s;
- unsigned long r;
+ if (!channels_ptr)
+ return FALSE;
- if (*n == '$')
- ++n;
- if (itbl_get_reg_val (n, &r))
- reg = r & RNUM_MASK;
- }
+ /* Eat characters from the end of the string that are valid
+ channel suffixes. The preceding register must be $ACC or
+ end with a digit, so there is no ambiguity. */
+ bit = 1;
+ m = e;
+ for (q = "wzyx"; *q; q++, bit <<= 1)
+ if (m > s && m[-1] == *q)
+ {
+ --m;
+ channels |= bit;
+ }
- /* 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);
+ if (channels == 0
+ || !mips_parse_register_1 (s, m, &symval)
+ || (symval & (RTYPE_VI | RTYPE_VF | RTYPE_R5900_ACC)) == 0)
+ return FALSE;
+ }
- *e = save_c;
- if (regnop)
- *regnop = reg;
- return reg >= 0;
+ *sptr = e;
+ *symval_ptr = symval;
+ if (channels_ptr)
+ *channels_ptr = channels;
+ return TRUE;
}
-/* 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.
-
- 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. */
+/* 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
-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;
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
unsigned int regno;
- while (reg_lookup (s, types, ®no))
+ if (mips_parse_register (s, ®no, NULL))
{
- lastregno = regno;
- if (**s == '-')
- {
- (*s)++;
- ok = reg_lookup (s, types, &lastregno);
- if (ok && lastregno < regno)
- ok = FALSE;
- if (!ok)
- break;
- }
-
- if (lastregno == FP && regno >= S0 && regno <= S7)
- {
- lastregno = S7;
- reglist |= 1 << FP;
- }
- regmask = 1 << lastregno;
- regmask = (regmask << 1) - 1;
- regmask ^= (1 << regno) - 1;
- reglist |= regmask;
-
- s_endlist = *s;
- if (**s != ',')
- break;
- (*s)++;
+ if (types & RTYPE_VEC)
+ regno = mips_prefer_vec_regno (regno);
+ if (regno & types)
+ regno &= RNUM_MASK;
+ else
+ regno = ~0;
}
-
- if (ok)
- *s = s_endlist;
else
- *s = s_reset;
- if (reglistp)
- *reglistp = reglist;
- return ok && reglist != 0;
+ {
+ if (types & RWARN)
+ as_warn (_("unrecognized register name `%s'"), *s);
+ regno = ~0;
+ }
+ if (regnop)
+ *regnop = regno;
+ return regno <= RNUM_MASK;
}
-static unsigned int
-mips_lookup_reg_pair (unsigned int regno1, unsigned int regno2,
- const unsigned int *map1, const unsigned int *map2,
- unsigned int count)
+/* Parse a VU0 "x?y?z?w?" channel mask at S and store the associated
+ mask in *CHANNELS. Return a pointer to the first unconsumed character. */
+
+static char *
+mips_parse_vu0_channels (char *s, unsigned int *channels)
{
unsigned int i;
- for (i = 0; i < count; i++)
- if (map1[i] == regno1 && map2[i] == regno2)
- return i;
- return ILLEGAL_REG;
+ *channels = 0;
+ for (i = 0; i < 4; i++)
+ if (*s == "xyzw"[i])
+ {
+ *channels |= 1 << (3 - i);
+ ++s;
+ }
+ return s;
}
-/* Return TRUE if opcode MO is valid on the currently selected ISA, ASE
- and architecture. Use is_opcode_valid_16 for MIPS16 opcodes. */
+/* Token types for parsed operand lists. */
+enum mips_operand_token_type {
+ /* A plain register, e.g. $f2. */
+ OT_REG,
-static bfd_boolean
-is_opcode_valid (const struct mips_opcode *mo)
-{
- int isa = mips_opts.isa;
- int ase = mips_opts.ase;
- int fp_s, fp_d;
- unsigned int i;
+ /* A 4-bit XYZW channel mask. */
+ OT_CHANNELS,
- if (ISA_HAS_64BIT_REGS (mips_opts.isa))
- for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
- if ((ase & mips_ases[i].flags) == mips_ases[i].flags)
- ase |= mips_ases[i].flags64;
+ /* A constant vector index, e.g. [1]. */
+ OT_INTEGER_INDEX,
- if (!opcode_is_member (mo, isa, ase, mips_opts.arch))
- return FALSE;
+ /* A register vector index, e.g. [$2]. */
+ OT_REG_INDEX,
- /* Check whether the instruction or macro requires single-precision or
- double-precision floating-point support. Note that this information is
- stored differently in the opcode table for insns and macros. */
- if (mo->pinfo == INSN_MACRO)
- {
- fp_s = mo->pinfo2 & INSN2_M_FP_S;
- fp_d = mo->pinfo2 & INSN2_M_FP_D;
- }
- else
- {
- fp_s = mo->pinfo & FP_S;
- fp_d = mo->pinfo & FP_D;
- }
+ /* A continuous range of registers, e.g. $s0-$s4. */
+ OT_REG_RANGE,
- if (fp_d && (mips_opts.soft_float || mips_opts.single_float))
- return FALSE;
+ /* A (possibly relocated) expression. */
+ OT_INTEGER,
- if (fp_s && mips_opts.soft_float)
- return FALSE;
+ /* A floating-point value. */
+ OT_FLOAT,
- return TRUE;
-}
+ /* A single character. This can be '(', ')' or ',', but '(' only appears
+ before OT_REGs. */
+ OT_CHAR,
-/* Return TRUE if the MIPS16 opcode MO is valid on the currently
- selected ISA and architecture. */
+ /* A doubled character, either "--" or "++". */
+ OT_DOUBLE_CHAR,
-static bfd_boolean
-is_opcode_valid_16 (const struct mips_opcode *mo)
+ /* The end of the operand list. */
+ OT_END
+};
+
+/* A parsed operand token. */
+struct mips_operand_token
{
- return opcode_is_member (mo, mips_opts.isa, 0, mips_opts.arch);
+ /* The type of token. */
+ enum mips_operand_token_type type;
+ union
+ {
+ /* The register symbol value for an OT_REG or OT_REG_INDEX. */
+ unsigned int regno;
+
+ /* The 4-bit channel mask for an OT_CHANNEL_SUFFIX. */
+ unsigned int channels;
+
+ /* The integer value of an OT_INTEGER_INDEX. */
+ addressT index;
+
+ /* 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 or OT_DOUBLE_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));
}
-/* Return TRUE if the size of the microMIPS opcode MO matches one
- explicitly requested. Always TRUE in the standard MIPS mode. */
+/* 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 bfd_boolean
-is_size_valid (const struct mips_opcode *mo)
+static char *
+mips_parse_base_start (char *s)
{
- if (!mips_opts.micromips)
- return TRUE;
+ struct mips_operand_token token;
+ unsigned int regno, channels;
+ bfd_boolean decrement_p;
+
+ if (*s != '(')
+ return 0;
+
+ ++s;
+ SKIP_SPACE_TABS (s);
+
+ /* Only match "--" as part of a base expression. In other contexts "--X"
+ is a double negative. */
+ decrement_p = (s[0] == '-' && s[1] == '-');
+ if (decrement_p)
+ {
+ s += 2;
+ SKIP_SPACE_TABS (s);
+ }
+
+ /* Allow a channel specifier because that leads to better error messages
+ than treating something like "$vf0x++" as an expression. */
+ if (!mips_parse_register (&s, ®no, &channels))
+ return 0;
+
+ token.u.ch = '(';
+ mips_add_token (&token, OT_CHAR);
+
+ if (decrement_p)
+ {
+ token.u.ch = '-';
+ mips_add_token (&token, OT_DOUBLE_CHAR);
+ }
+
+ token.u.regno = regno;
+ mips_add_token (&token, OT_REG);
+
+ if (channels)
+ {
+ token.u.channels = channels;
+ mips_add_token (&token, OT_CHANNELS);
+ }
+
+ /* For consistency, only match "++" as part of base expressions too. */
+ SKIP_SPACE_TABS (s);
+ if (s[0] == '+' && s[1] == '+')
+ {
+ s += 2;
+ token.u.ch = '+';
+ mips_add_token (&token, OT_DOUBLE_CHAR);
+ }
+
+ 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, channels;
+ 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, &channels))
+ {
+ if (channels)
+ {
+ /* A register and a VU0 channel suffix. */
+ token.u.regno = regno1;
+ mips_add_token (&token, OT_REG);
+
+ token.u.channels = channels;
+ mips_add_token (&token, OT_CHANNELS);
+ return s;
+ }
+
+ SKIP_SPACE_TABS (s);
+ if (*s == '-')
+ {
+ /* A register range. */
+ ++s;
+ SKIP_SPACE_TABS (s);
+ if (!mips_parse_register (&s, ®no2, NULL))
+ {
+ set_insn_error (0, _("invalid register range"));
+ return 0;
+ }
+
+ token.u.reg_range.regno1 = regno1;
+ token.u.reg_range.regno2 = regno2;
+ mips_add_token (&token, OT_REG_RANGE);
+ return s;
+ }
+
+ /* Add the register itself. */
+ token.u.regno = regno1;
+ mips_add_token (&token, OT_REG);
+
+ /* Check for a vector index. */
+ if (*s == '[')
+ {
+ ++s;
+ SKIP_SPACE_TABS (s);
+ if (mips_parse_register (&s, &token.u.regno, NULL))
+ mips_add_token (&token, OT_REG_INDEX);
+ else
+ {
+ expressionS element;
+
+ my_getExpression (&element, s);
+ if (element.X_op != O_constant)
+ {
+ set_insn_error (0, _("vector element must be constant"));
+ return 0;
+ }
+ s = expr_end;
+ token.u.index = element.X_add_number;
+ mips_add_token (&token, OT_INTEGER_INDEX);
+ }
+ SKIP_SPACE_TABS (s);
+ if (*s != ']')
+ {
+ set_insn_error (0, _("missing `]'"));
+ return 0;
+ }
+ ++s;
+ }
+ return s;
+ }
+
+ 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)
+ {
+ set_insn_error (0, 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
+ and architecture. Use is_opcode_valid_16 for MIPS16 opcodes. */
+
+static bfd_boolean
+is_opcode_valid (const struct mips_opcode *mo)
+{
+ int isa = mips_opts.isa;
+ int ase = mips_opts.ase;
+ int fp_s, fp_d;
+ unsigned int i;
+
+ if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ if ((ase & mips_ases[i].flags) == mips_ases[i].flags)
+ ase |= mips_ases[i].flags64;
+
+ if (!opcode_is_member (mo, isa, ase, mips_opts.arch))
+ return FALSE;
+
+ /* Check whether the instruction or macro requires single-precision or
+ double-precision floating-point support. Note that this information is
+ stored differently in the opcode table for insns and macros. */
+ if (mo->pinfo == INSN_MACRO)
+ {
+ fp_s = mo->pinfo2 & INSN2_M_FP_S;
+ fp_d = mo->pinfo2 & INSN2_M_FP_D;
+ }
+ else
+ {
+ fp_s = mo->pinfo & FP_S;
+ fp_d = mo->pinfo & FP_D;
+ }
+
+ if (fp_d && (mips_opts.soft_float || mips_opts.single_float))
+ return FALSE;
+
+ if (fp_s && mips_opts.soft_float)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Return TRUE if the MIPS16 opcode MO is valid on the currently
+ selected ISA and architecture. */
+
+static bfd_boolean
+is_opcode_valid_16 (const struct mips_opcode *mo)
+{
+ return opcode_is_member (mo, mips_opts.isa, 0, mips_opts.arch);
+}
+
+/* Return TRUE if the size of the microMIPS opcode MO matches one
+ explicitly requested. Always TRUE in the standard MIPS mode. */
+
+static bfd_boolean
+is_size_valid (const struct mips_opcode *mo)
+{
+ if (!mips_opts.micromips)
+ return TRUE;
if (mips_opts.insn32)
{
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. */
+/* 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. Also build up a list of operands in OPERANDS.
+
+ INSN_BITS says which bits of the instruction are significant.
+ If OPCODE is a standard or microMIPS instruction, DECODE_OPERAND
+ provides the mips_operand description of each operand. DECODE_OPERAND
+ is null for MIPS16 instructions. */
static int
validate_mips_insn (const struct mips_opcode *opcode,
unsigned long insn_bits,
- const struct mips_operand *(*decode_operand) (const char *))
+ const struct mips_operand *(*decode_operand) (const char *),
+ struct mips_operand_array *operands)
{
const char *s;
- unsigned long used_bits, doubled, undefined;
+ unsigned long used_bits, doubled, undefined, opno, mask;
const struct mips_operand *operand;
- if ((opcode->mask & opcode->match) != opcode->match)
+ mask = (opcode->pinfo == INSN_MACRO ? 0 : opcode->mask);
+ if ((mask & opcode->match) != opcode->match)
{
as_bad (_("internal: bad mips opcode (mask error): %s %s"),
opcode->name, opcode->args);
return 0;
}
used_bits = 0;
+ opno = 0;
+ if (opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX)
+ used_bits = mips_insert_operand (&mips_vu0_channel_mask, used_bits, -1);
for (s = opcode->args; *s; ++s)
switch (*s)
{
case ')':
break;
+ case '#':
+ s++;
+ break;
+
default:
- operand = decode_operand (s);
- if (!operand)
+ if (!decode_operand)
+ operand = decode_mips16_operand (*s, FALSE);
+ else
+ operand = decode_operand (s);
+ if (!operand && opcode->pinfo != INSN_MACRO)
{
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));
+ gas_assert (opno < MAX_OPERANDS);
+ operands->operand[opno] = operand;
+ if (operand && operand->type != OP_VU0_MATCH_SUFFIX)
+ {
+ used_bits = mips_insert_operand (operand, used_bits, -1);
+ 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));
+ if (operand->type == OP_ENTRY_EXIT_LIST)
+ used_bits &= ~(mask & 0x700);
+ }
/* Skip prefix characters. */
- if (*s == '+' || *s == 'm')
+ if (decode_operand && (*s == '+' || *s == 'm'))
++s;
+ opno += 1;
break;
}
- doubled = used_bits & opcode->mask & insn_bits;
+ doubled = used_bits & 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;
+ used_bits |= mask;
undefined = ~used_bits & insn_bits;
- if (undefined)
+ if (opcode->pinfo != INSN_MACRO && undefined)
{
as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
undefined, opcode->name, opcode->args);
return 1;
}
+/* The MIPS16 version of validate_mips_insn. */
+
+static int
+validate_mips16_insn (const struct mips_opcode *opcode,
+ struct mips_operand_array *operands)
+{
+ if (opcode->args[0] == 'a' || opcode->args[0] == 'i')
+ {
+ /* In this case OPCODE defines the first 16 bits in a 32-bit jump
+ instruction. Use TMP to describe the full instruction. */
+ struct mips_opcode tmp;
+
+ tmp = *opcode;
+ tmp.match <<= 16;
+ tmp.mask <<= 16;
+ return validate_mips_insn (&tmp, 0xffffffff, 0, operands);
+ }
+ return validate_mips_insn (opcode, 0xffff, 0, operands);
+}
+
/* The microMIPS version of validate_mips_insn. */
static int
-validate_micromips_insn (const struct mips_opcode *opc)
+validate_micromips_insn (const struct mips_opcode *opc,
+ struct mips_operand_array *operands)
{
unsigned long insn_bits;
unsigned long major;
unsigned int length;
+ if (opc->pinfo == INSN_MACRO)
+ return validate_mips_insn (opc, 0xffffffff, decode_micromips_operand,
+ operands);
+
length = micromips_insn_length (opc);
if (length != 2 && length != 4)
{
- as_bad (_("Internal error: bad microMIPS opcode (incorrect length: %u): "
+ as_bad (_("internal error: bad microMIPS opcode (incorrect length: %u): "
"%s %s"), length, opc->name, opc->args);
return 0;
}
if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
|| (length == 4 && (major & 7) != 0 && (major & 4) != 4))
{
- as_bad (_("Internal error: bad microMIPS opcode "
+ as_bad (_("internal error: bad microMIPS opcode "
"(opcode/length mismatch): %s %s"), opc->name, opc->args);
return 0;
}
insn_bits = 1 << 4 * length;
insn_bits <<= 4 * length;
insn_bits -= 1;
- return validate_mips_insn (opc, insn_bits, decode_micromips_operand);
+ return validate_mips_insn (opc, insn_bits, decode_micromips_operand,
+ operands);
}
/* This function is called once, at assembler startup time. It should set up
g_switch_value = 0;
}
- if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_arch))
- as_warn (_("Could not set architecture and machine"));
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+ as_warn (_("could not set architecture and machine"));
op_hash = hash_new ();
+ mips_operands = XCNEWVEC (struct mips_operand_array, NUMOPCODES);
for (i = 0; i < NUMOPCODES;)
{
const char *name = mips_opcodes[i].name;
fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
mips_opcodes[i].name, retval);
/* Probably a memory allocation problem? Give up now. */
- as_fatal (_("Broken assembler. No assembly attempted."));
+ as_fatal (_("broken assembler, no assembly attempted"));
}
do
{
- if (mips_opcodes[i].pinfo != INSN_MACRO)
+ if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
+ decode_mips_operand, &mips_operands[i]))
+ broken = 1;
+ if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
{
- if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
- decode_mips_operand))
- broken = 1;
- if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
- {
- create_insn (&nop_insn, mips_opcodes + i);
- if (mips_fix_loongson2f_nop)
- nop_insn.insn_opcode = LOONGSON2F_NOP_INSN;
- nop_insn.fixed_p = 1;
- }
+ create_insn (&nop_insn, mips_opcodes + i);
+ if (mips_fix_loongson2f_nop)
+ nop_insn.insn_opcode = LOONGSON2F_NOP_INSN;
+ nop_insn.fixed_p = 1;
}
++i;
}
}
mips16_op_hash = hash_new ();
+ mips16_operands = XCNEWVEC (struct mips_operand_array,
+ bfd_mips16_num_opcodes);
i = 0;
while (i < bfd_mips16_num_opcodes)
mips16_opcodes[i].name, retval);
do
{
- if (mips16_opcodes[i].pinfo != INSN_MACRO
- && ((mips16_opcodes[i].match & mips16_opcodes[i].mask)
- != mips16_opcodes[i].match))
- {
- fprintf (stderr, _("internal error: bad mips16 opcode: %s %s\n"),
- mips16_opcodes[i].name, mips16_opcodes[i].args);
- broken = 1;
- }
+ if (!validate_mips16_insn (&mips16_opcodes[i], &mips16_operands[i]))
+ broken = 1;
if (mips16_nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
{
create_insn (&mips16_nop_insn, mips16_opcodes + i);
}
micromips_op_hash = hash_new ();
+ micromips_operands = XCNEWVEC (struct mips_operand_array,
+ bfd_micromips_num_opcodes);
i = 0;
while (i < bfd_micromips_num_opcodes)
as_fatal (_("internal: can't hash `%s': %s"),
micromips_opcodes[i].name, retval);
do
- if (micromips_opcodes[i].pinfo != INSN_MACRO)
- {
- struct mips_cl_insn *micromips_nop_insn;
+ {
+ struct mips_cl_insn *micromips_nop_insn;
- if (!validate_micromips_insn (µmips_opcodes[i]))
- broken = 1;
+ if (!validate_micromips_insn (µmips_opcodes[i],
+ µmips_operands[i]))
+ broken = 1;
- if (micromips_insn_length (micromips_opcodes + i) == 2)
- micromips_nop_insn = µmips_nop16_insn;
- else if (micromips_insn_length (micromips_opcodes + i) == 4)
- micromips_nop_insn = µmips_nop32_insn;
- else
- continue;
+ if (micromips_opcodes[i].pinfo != INSN_MACRO)
+ {
+ if (micromips_insn_length (micromips_opcodes + i) == 2)
+ micromips_nop_insn = µmips_nop16_insn;
+ else if (micromips_insn_length (micromips_opcodes + i) == 4)
+ micromips_nop_insn = µmips_nop32_insn;
+ else
+ continue;
- if (micromips_nop_insn->insn_mo == NULL
- && strcmp (name, "nop") == 0)
- {
- create_insn (micromips_nop_insn, micromips_opcodes + i);
- micromips_nop_insn->fixed_p = 1;
- }
- }
+ if (micromips_nop_insn->insn_mo == NULL
+ && strcmp (name, "nop") == 0)
+ {
+ create_insn (micromips_nop_insn, micromips_opcodes + i);
+ micromips_nop_insn->fixed_p = 1;
+ }
+ }
+ }
while (++i < bfd_micromips_num_opcodes
&& strcmp (micromips_opcodes[i].name, name) == 0);
}
if (broken)
- as_fatal (_("Broken assembler. No assembly attempted."));
+ as_fatal (_("broken assembler, no assembly attempted"));
/* We add all the general register names to the symbol table. This
helps us detect invalid uses of them. */
reg_names_o32[i].num, /* & RNUM_MASK, */
&zero_address_frag));
+ for (i = 0; i < 32; i++)
+ {
+ char regname[7];
+
+ /* R5900 VU0 floating-point register. */
+ regname[sizeof (rename) - 1] = 0;
+ snprintf (regname, sizeof (regname) - 1, "$vf%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_VF | i, &zero_address_frag));
+
+ /* R5900 VU0 integer register. */
+ snprintf (regname, sizeof (regname) - 1, "$vi%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_VI | i, &zero_address_frag));
+
+ /* MSA register. */
+ snprintf (regname, sizeof (regname) - 1, "$w%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_MSA | i, &zero_address_frag));
+ }
+
+ obstack_init (&mips_operand_tokens);
+
mips_no_prev_insn ();
mips_gprmask = 0;
= {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
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;
mips_mark_labels ();
mips_assembling_insn = TRUE;
+ clear_insn_error ();
if (mips_opts.mips16)
mips16_ip (str, &insn);
str, insn.insn_opcode));
}
- if (insn_error)
- as_bad ("%s `%s'", insn_error, str);
+ if (insn_error.msg)
+ report_insn_error (str);
else if (insn.insn_mo->pinfo == INSN_MACRO)
{
macro_start ();
&& 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. */
return NOP_INSN;
}
-/* Return the mask of core registers that IP reads or writes. */
+/* Return a mask that has bit N set if OPCODE reads the register(s)
+ in operand N. */
static unsigned int
-gpr_mod_mask (const struct mips_cl_insn *ip)
+insn_read_mask (const struct mips_opcode *opcode)
{
- unsigned long pinfo2;
- unsigned int mask;
-
- mask = 0;
- pinfo2 = ip->insn_mo->pinfo2;
- if (mips_opts.micromips)
- {
- if (pinfo2 & INSN2_MOD_GPR_MD)
- 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;
- }
- return mask;
+ return (opcode->pinfo & INSN_READ_ALL) >> INSN_READ_SHIFT;
}
-/* Return the mask of core registers that IP reads. */
+/* Return a mask that has bit N set if OPCODE writes to the register(s)
+ in operand N. */
static unsigned int
-gpr_read_mask (const struct mips_cl_insn *ip)
+insn_write_mask (const struct mips_opcode *opcode)
{
- unsigned long pinfo, pinfo2;
- unsigned int mask;
-
- mask = gpr_mod_mask (ip);
- pinfo = ip->insn_mo->pinfo;
- pinfo2 = ip->insn_mo->pinfo2;
- if (mips_opts.mips16)
- {
- if (pinfo & MIPS16_INSN_READ_X)
- mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
- if (pinfo & MIPS16_INSN_READ_Y)
- mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
- if (pinfo & MIPS16_INSN_READ_T)
- 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)]);
- if (pinfo & MIPS16_INSN_READ_GPR_X)
- mask |= 1 << MIPS16_EXTRACT_OPERAND (REGR32, *ip);
- }
- else
- {
- if (pinfo2 & INSN2_READ_GPR_D)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
- if (pinfo & INSN_READ_GPR_T)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
- if (pinfo & INSN_READ_GPR_S)
- 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 (mips_opts.micromips)
- {
- if (pinfo2 & INSN2_READ_GPR_MC)
- mask |= 1 << micromips_to_32_reg_c_map[EXTRACT_OPERAND (1, MC, *ip)];
- if (pinfo2 & INSN2_READ_GPR_ME)
- mask |= 1 << micromips_to_32_reg_e_map[EXTRACT_OPERAND (1, ME, *ip)];
- if (pinfo2 & INSN2_READ_GPR_MG)
- mask |= 1 << micromips_to_32_reg_g_map[EXTRACT_OPERAND (1, MG, *ip)];
- if (pinfo2 & INSN2_READ_GPR_MJ)
- mask |= 1 << EXTRACT_OPERAND (1, MJ, *ip);
- if (pinfo2 & INSN2_READ_GPR_MMN)
- {
- mask |= 1 << micromips_to_32_reg_m_map[EXTRACT_OPERAND (1, MM, *ip)];
- mask |= 1 << micromips_to_32_reg_n_map[EXTRACT_OPERAND (1, MN, *ip)];
- }
- if (pinfo2 & INSN2_READ_GPR_MP)
- mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
- if (pinfo2 & INSN2_READ_GPR_MQ)
- mask |= 1 << micromips_to_32_reg_q_map[EXTRACT_OPERAND (1, MQ, *ip)];
- }
- /* Don't include register 0. */
- return mask & ~1;
+ return (opcode->pinfo & INSN_WRITE_ALL) >> INSN_WRITE_SHIFT;
}
-/* Return the mask of core registers that IP writes. */
+/* Return a mask of the registers specified by operand OPERAND of INSN.
+ Ignore registers of type OP_REG_<t> unless bit OP_REG_<t> of TYPE_MASK
+ is set. */
static unsigned int
-gpr_write_mask (const struct mips_cl_insn *ip)
+operand_reg_mask (const struct mips_cl_insn *insn,
+ const struct mips_operand *operand,
+ unsigned int type_mask)
{
- unsigned long pinfo, pinfo2;
- unsigned int mask;
+ unsigned int uval, vsel;
- mask = gpr_mod_mask (ip);
- pinfo = ip->insn_mo->pinfo;
- pinfo2 = ip->insn_mo->pinfo2;
- if (mips_opts.mips16)
- {
- if (pinfo & MIPS16_INSN_WRITE_X)
- mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RX, *ip)];
- if (pinfo & MIPS16_INSN_WRITE_Y)
- mask |= 1 << mips16_to_32_reg_map[MIPS16_EXTRACT_OPERAND (RY, *ip)];
- if (pinfo & MIPS16_INSN_WRITE_Z)
- 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)
- mask |= 1 << MIPS16OP_EXTRACT_REG32R (ip->insn_opcode);
- }
- else
- {
- if (pinfo & INSN_WRITE_GPR_D)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
- if (pinfo & INSN_WRITE_GPR_T)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
- if (pinfo & INSN_WRITE_GPR_S)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
- if (pinfo & INSN_WRITE_GPR_31)
- mask |= 1 << RA;
- if (pinfo2 & INSN2_WRITE_GPR_Z)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RZ, *ip);
- }
- if (mips_opts.micromips)
+ switch (operand->type)
{
- if (pinfo2 & INSN2_WRITE_GPR_MB)
- mask |= 1 << micromips_to_32_reg_b_map[EXTRACT_OPERAND (1, MB, *ip)];
- if (pinfo2 & INSN2_WRITE_GPR_MH)
- {
- 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);
- if (pinfo2 & INSN2_WRITE_GPR_MP)
- mask |= 1 << EXTRACT_OPERAND (1, MP, *ip);
- }
- /* Don't include register 0. */
- return mask & ~1;
-}
-
-/* Return the mask of floating-point registers that IP reads. */
-
-static unsigned int
-fpr_read_mask (const struct mips_cl_insn *ip)
+ case OP_INT:
+ case OP_MAPPED_INT:
+ case OP_MSB:
+ case OP_PCREL:
+ case OP_PERF_REG:
+ case OP_ADDIUSP_INT:
+ case OP_ENTRY_EXIT_LIST:
+ case OP_REPEAT_DEST_REG:
+ case OP_REPEAT_PREV_REG:
+ case OP_PC:
+ case OP_VU0_SUFFIX:
+ case OP_VU0_MATCH_SUFFIX:
+ case OP_IMM_INDEX:
+ abort ();
+
+ case OP_REG:
+ case OP_OPTIONAL_REG:
+ {
+ const struct mips_reg_operand *reg_op;
+
+ reg_op = (const struct mips_reg_operand *) operand;
+ if (!(type_mask & (1 << reg_op->reg_type)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return 1 << mips_decode_reg_operand (reg_op, uval);
+ }
+
+ case OP_REG_PAIR:
+ {
+ const struct mips_reg_pair_operand *pair_op;
+
+ pair_op = (const struct mips_reg_pair_operand *) operand;
+ if (!(type_mask & (1 << pair_op->reg_type)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return (1 << pair_op->reg1_map[uval]) | (1 << pair_op->reg2_map[uval]);
+ }
+
+ case OP_CLO_CLZ_DEST:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return (1 << (uval & 31)) | (1 << (uval >> 5));
+
+ case OP_LWM_SWM_LIST:
+ abort ();
+
+ case OP_SAVE_RESTORE_LIST:
+ abort ();
+
+ case OP_MDMX_IMM_REG:
+ if (!(type_mask & (1 << OP_REG_VEC)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ vsel = uval >> 5;
+ if ((vsel & 0x18) == 0x18)
+ return 0;
+ return 1 << (uval & 31);
+
+ case OP_REG_INDEX:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ return 1 << insn_extract_operand (insn, operand);
+ }
+ abort ();
+}
+
+/* Return a mask of the registers specified by operands OPNO_MASK of INSN,
+ where bit N of OPNO_MASK is set if operand N should be included.
+ Ignore registers of type OP_REG_<t> unless bit OP_REG_<t> of TYPE_MASK
+ is set. */
+
+static unsigned int
+insn_reg_mask (const struct mips_cl_insn *insn,
+ unsigned int type_mask, unsigned int opno_mask)
+{
+ unsigned int opno, reg_mask;
+
+ opno = 0;
+ reg_mask = 0;
+ while (opno_mask != 0)
+ {
+ if (opno_mask & 1)
+ reg_mask |= operand_reg_mask (insn, insn_opno (insn, opno), type_mask);
+ opno_mask >>= 1;
+ opno += 1;
+ }
+ return reg_mask;
+}
+
+/* Return the mask of core registers that IP reads. */
+
+static unsigned int
+gpr_read_mask (const struct mips_cl_insn *ip)
{
unsigned long pinfo, pinfo2;
unsigned int mask;
- mask = 0;
+ mask = insn_reg_mask (ip, 1 << OP_REG_GP, insn_read_mask (ip->insn_mo));
pinfo = ip->insn_mo->pinfo;
pinfo2 = ip->insn_mo->pinfo2;
- if (!mips_opts.mips16)
- {
- if (pinfo2 & INSN2_READ_FPR_D)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
- if (pinfo & INSN_READ_FPR_S)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
- if (pinfo & INSN_READ_FPR_T)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
- if (pinfo & INSN_READ_FPR_R)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FR, *ip);
- if (pinfo2 & INSN2_READ_FPR_Z)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
- }
+ if (pinfo & INSN_UDI)
+ {
+ /* UDI instructions have traditionally been assumed to read RS
+ and RT. */
+ mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
+ mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+ }
+ if (pinfo & INSN_READ_GPR_24)
+ mask |= 1 << 24;
+ if (pinfo2 & INSN2_READ_GPR_16)
+ mask |= 1 << 16;
+ if (pinfo2 & INSN2_READ_SP)
+ mask |= 1 << SP;
+ if (pinfo2 & INSN2_READ_GPR_31)
+ mask |= 1 << 31;
+ /* Don't include register 0. */
+ return mask & ~1;
+}
+
+/* Return the mask of core registers that IP writes. */
+
+static unsigned int
+gpr_write_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = insn_reg_mask (ip, 1 << OP_REG_GP, insn_write_mask (ip->insn_mo));
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (pinfo & INSN_WRITE_GPR_24)
+ mask |= 1 << 24;
+ if (pinfo & INSN_WRITE_GPR_31)
+ mask |= 1 << 31;
+ if (pinfo & INSN_UDI)
+ /* UDI instructions have traditionally been assumed to write to RD. */
+ mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
+ if (pinfo2 & INSN2_WRITE_SP)
+ mask |= 1 << SP;
+ /* Don't include register 0. */
+ return mask & ~1;
+}
+
+/* Return the mask of floating-point registers that IP reads. */
+
+static unsigned int
+fpr_read_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo;
+ unsigned int mask;
+
+ mask = insn_reg_mask (ip, ((1 << OP_REG_FP) | (1 << OP_REG_VEC)
+ | (1 << OP_REG_MSA)),
+ insn_read_mask (ip->insn_mo));
+ pinfo = ip->insn_mo->pinfo;
/* Conservatively treat all operands to an FP_D instruction are doubles.
(This is overly pessimistic for things like cvt.d.s.) */
- if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+ if (FPR_SIZE != 64 && (pinfo & FP_D))
mask |= mask << 1;
return mask;
}
static unsigned int
fpr_write_mask (const struct mips_cl_insn *ip)
{
- unsigned long pinfo, pinfo2;
+ unsigned long pinfo;
unsigned int mask;
- mask = 0;
+ mask = insn_reg_mask (ip, ((1 << OP_REG_FP) | (1 << OP_REG_VEC)
+ | (1 << OP_REG_MSA)),
+ insn_write_mask (ip->insn_mo));
pinfo = ip->insn_mo->pinfo;
- pinfo2 = ip->insn_mo->pinfo2;
- if (!mips_opts.mips16)
- {
- if (pinfo & INSN_WRITE_FPR_D)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FD, *ip);
- if (pinfo & INSN_WRITE_FPR_S)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FS, *ip);
- if (pinfo & INSN_WRITE_FPR_T)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FT, *ip);
- if (pinfo2 & INSN2_WRITE_FPR_Z)
- mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, FZ, *ip);
- }
/* Conservatively treat all operands to an FP_D instruction are doubles.
(This is overly pessimistic for things like cvt.s.d.) */
- if (HAVE_32BIT_FPRS && (pinfo & FP_D))
+ if (FPR_SIZE != 64 && (pinfo & FP_D))
mask |= mask << 1;
return mask;
}
return FALSE;
}
-#if 0
-/* Report that user-supplied argument ARGNUM for INSN was VAL, but should
- have been in the range [MIN_VAL, MAX_VAL]. PRINT_HEX says whether
- this operand is normally printed in hex or decimal. */
-
-static void
-report_bad_range (struct mips_cl_insn *insn, int argnum,
- offsetT val, int min_val, int max_val,
- bfd_boolean print_hex)
-{
- if (print_hex && val >= 0)
- as_bad (_("Operand %d of `%s' must be in the range [0x%x, 0x%x],"
- " was 0x%lx."),
- argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val);
- else if (print_hex)
- as_bad (_("Operand %d of `%s' must be in the range [0x%x, 0x%x],"
- " was %ld."),
- argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val);
- else
- as_bad (_("Operand %d of `%s' must be in the range [%d, %d],"
- " was %ld."),
- argnum, insn->insn_mo->name, min_val, max_val, (unsigned long) val);
-}
-
-/* Report an invalid combination of position and size operands for a bitfield
- operation. POS and SIZE are the values that were given. */
-
-static void
-report_bad_field (offsetT pos, offsetT size)
-{
- as_bad (_("Invalid field specification (position %ld, size %ld)"),
- (unsigned long) pos, (unsigned long) size);
-}
-
/* Information about an instruction argument that we're trying to match. */
struct mips_arg_info
{
/* The instruction so far. */
struct mips_cl_insn *insn;
+ /* The first unconsumed operand token. */
+ struct mips_operand_token *token;
+
/* The 1-based operand number, in terms of insn->insn_mo->args. */
int opnum;
where it gives the lsb position. */
unsigned int last_op_int;
- /* If true, match routines should silently reject invalid arguments.
- If false, match routines can accept invalid arguments as long as
- they report an appropriate error. They still have the option of
- silently rejecting arguments, in which case a generic "Invalid operands"
- style of error will be used instead. */
- bfd_boolean soft_match;
-
- /* If true, the OP_INT match routine should treat plain symbolic operands
- as if a relocation operator like %lo(...) had been used. This is only
- ever true if the operand can be relocated. */
- bfd_boolean allow_nonconst;
-
- /* When true, the OP_INT match routine should allow unsigned N-bit
- arguments to be used where a signed N-bit operand is expected. */
- bfd_boolean lax_max;
-
- /* When true, the OP_REG match routine should assume that another operand
- appears after this one. It should fail the match if the register it
- sees is at the end of the argument list. */
- bfd_boolean optional_reg;
+ /* If true, match routines should assume that no later instruction
+ alternative matches and should therefore be as accomodating as
+ possible. Match routines should not report errors if something
+ is only invalid for !LAX_MATCH. */
+ bfd_boolean lax_match;
/* True if a reference to the current AT register was seen. */
bfd_boolean seen_at;
};
-/* Match a constant integer at S for ARG. Return null if the match failed.
- Otherwise return the end of the matched string and store the constant value
- in *VALUE. In the latter case, use FALLBACK as the value if the match
- succeeded with an error. */
+/* Record that the argument is out of range. */
-static char *
-match_const_int (struct mips_arg_info *arg, char *s, offsetT *value,
- offsetT fallback)
+static void
+match_out_of_range (struct mips_arg_info *arg)
{
- expressionS ex;
- bfd_reloc_code_real_type r[3];
- int num_relocs;
+ set_insn_error_i (arg->argnum, _("operand %d out of range"), arg->argnum);
+}
+
+/* Record that the argument isn't constant but needs to be. */
+
+static void
+match_not_constant (struct mips_arg_info *arg)
+{
+ set_insn_error_i (arg->argnum, _("operand %d must be constant"),
+ arg->argnum);
+}
- num_relocs = my_getSmallExpression (&ex, r, s);
- if (*s == '(' && ex.X_op == O_register)
+/* Try to match an OT_CHAR token for character CH. Consume the token
+ and return true on success, otherwise return false. */
+
+static bfd_boolean
+match_char (struct mips_arg_info *arg, char ch)
+{
+ if (arg->token->type == OT_CHAR && arg->token->u.ch == ch)
{
- /* Assume that the constant has been elided and that S is a base
- register. The rest of the match will fail if the assumption
- turns out to be wrong. */
- *value = 0;
- return s;
+ ++arg->token;
+ if (ch == ',')
+ arg->argnum += 1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* 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. */
+
+static bfd_boolean
+match_expression (struct mips_arg_info *arg, expressionS *value,
+ bfd_reloc_code_real_type *r)
+{
+ /* If the next token is a '(' that was parsed as being part of a base
+ expression, assume we have an elided offset. The later match will fail
+ if this turns out to be wrong. */
+ if (arg->token->type == OT_CHAR && arg->token->u.ch == '(')
+ {
+ value->X_op = O_constant;
+ value->X_add_number = 0;
+ r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+ return TRUE;
+ }
+
+ /* Reject register-based expressions such as "0+$2" and "(($2))".
+ For plain registers the default error seems more appropriate. */
+ if (arg->token->type == OT_INTEGER
+ && arg->token->u.integer.value.X_op == O_register)
+ {
+ set_insn_error (arg->argnum, _("register value used as expression"));
+ return FALSE;
+ }
+
+ 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;
}
- if (num_relocs == 0 && ex.X_op == O_constant)
+ set_insn_error_i
+ (arg->argnum, _("operand %d must be an immediate expression"),
+ arg->argnum);
+ return FALSE;
+}
+
+/* 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. */
+
+static bfd_boolean
+match_const_int (struct mips_arg_info *arg, offsetT *value)
+{
+ expressionS ex;
+ bfd_reloc_code_real_type r[3];
+
+ 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 we got a register rather than an expression, the default
- "Invalid operands" style of error seems more appropriate. */
- if (arg->soft_match || ex.X_op == O_register)
- return 0;
- as_bad (_("Operand %d of `%s' must be constant"),
- arg->argnum, arg->insn->insn_mo->name);
- *value = fallback;
+ match_not_constant (arg);
+ return FALSE;
}
- return expr_end;
+ return TRUE;
}
/* Return the RTYPE_* flags for a register operand of type TYPE that
&& (opcode->pinfo & (INSN_COPROC_MOVE_DELAY
| INSN_COPROC_MEMORY_DELAY
| INSN_LOAD_COPROC_DELAY
- | INSN_LOAD_MEMORY_DELAY
+ | INSN_LOAD_MEMORY
| INSN_STORE_MEMORY)))
return RTYPE_FPU | RTYPE_VEC;
return RTYPE_FPU;
case OP_REG_HW:
return RTYPE_NUM;
+
+ case OP_REG_VI:
+ return RTYPE_NUM | RTYPE_VI;
+
+ case OP_REG_VF:
+ return RTYPE_NUM | RTYPE_VF;
+
+ case OP_REG_R5900_I:
+ return RTYPE_R5900_I;
+
+ case OP_REG_R5900_Q:
+ return RTYPE_R5900_Q;
+
+ case OP_REG_R5900_R:
+ return RTYPE_R5900_R;
+
+ case OP_REG_R5900_ACC:
+ return RTYPE_R5900_ACC;
+
+ case OP_REG_MSA:
+ return RTYPE_MSA;
+
+ case OP_REG_MSA_CTRL:
+ return RTYPE_NUM;
}
abort ();
}
if (type == OP_REG_FP
&& (regno & 1) != 0
- && HAVE_32BIT_FPRS
+ && FPR_SIZE != 64
&& !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
- as_warn (_("Float register should be even, was %d"), regno);
+ as_warn (_("float register should be even, was %d"), regno);
if (type == OP_REG_CCC)
{
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"),
+ 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"),
+ as_warn (_("condition code register should be 0 or 4 for %s, was %d"),
name, regno);
}
}
+/* 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
+match_regno (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int symval, unsigned int *regno)
+{
+ if (type == OP_REG_VEC)
+ symval = mips_prefer_vec_regno (symval);
+ if (!(symval & convert_reg_type (arg->insn->insn_mo, type)))
+ return FALSE;
+
+ *regno = symval & RNUM_MASK;
+ check_regno (arg, type, *regno);
+ return TRUE;
+}
+
+/* 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. */
+
+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;
+}
+
+/* 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. */
+
+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;
+}
+
/* OP_INT matcher. */
-static char *
+static bfd_boolean
match_int_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand_base, char *s)
+ const struct mips_operand *operand_base)
{
const struct mips_int_operand *operand;
- unsigned int uval, mask;
+ unsigned int uval;
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;
+ min_val = mips_int_operand_min (operand);
+ max_val = mips_int_operand_max (operand);
if (operand_base->lsb == 0
&& operand_base->size == 16
&& (operand->max_val == 32767 || operand->max_val == 65535))
{
/* The operand can be relocated. */
- offset_reloc[0] = BFD_RELOC_LO16;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
- if (my_getSmallExpression (&offset_expr, offset_reloc, s) > 0)
+ if (!match_expression (arg, &offset_expr, offset_reloc))
+ return FALSE;
+
+ 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 expr_end;
- if (*s == '(' && offset_expr.X_op == O_register)
- /* Assume that the constant has been elided and that S is a base
- register. The rest of the match will fail if the assumption
- turns out to be wrong. */
- sval = 0;
- else
+ return TRUE;
+
+ if (offset_expr.X_op != O_constant)
{
- s = expr_end;
- if (offset_expr.X_op != O_constant)
- /* If non-constant operands are allowed then leave them for
- the caller to process, otherwise fail the match. */
- return arg->allow_nonconst ? s : 0;
- sval = offset_expr.X_add_number;
+ /* Accept non-constant operands if no later alternative matches,
+ leaving it for the caller to process. */
+ if (!arg->lax_match)
+ return FALSE;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ return TRUE;
}
+
/* Clear the global state; we're going to install the operand
ourselves. */
- offset_reloc[0] = BFD_RELOC_UNUSED;
+ sval = offset_expr.X_add_number;
offset_expr.X_op = O_absent;
+
+ /* For compatibility with older assemblers, we accept
+ 0x8000-0xffff as signed 16-bit numbers when only
+ signed numbers are allowed. */
+ if (sval > max_val)
+ {
+ max_val = ((1 << operand_base->size) - 1) << operand->shift;
+ if (!arg->lax_match && sval <= max_val)
+ return FALSE;
+ }
}
else
{
- s = match_const_int (arg, s, &sval, min_val);
- if (!s)
- return 0;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
}
arg->last_op_int = sval;
- /* Check the range. If there's a problem, record the lowest acceptable
- value in arg->last_op_int in order to prevent an unhelpful error
- from OP_MSB too.
-
- Bit counts have traditionally been printed in hex by the disassembler
- but printed as decimal in error messages. Only resort to hex if
- the operand is bigger than 6 bits. */
- print_hex = operand->print_hex && operand_base->size > 6;
- if (sval < min_val || sval > max_val)
+ if (sval < min_val || sval > max_val || sval % factor)
{
- if (arg->soft_match)
- return 0;
- report_bad_range (arg->insn, arg->argnum, sval, min_val, max_val,
- print_hex);
- arg->last_op_int = min_val;
- }
- else if (sval % factor)
- {
- if (arg->soft_match)
- return 0;
- as_bad (print_hex && sval >= 0
- ? _("Operand %d of `%s' must be a factor of %d, was 0x%lx.")
- : _("Operand %d of `%s' must be a factor of %d, was %ld."),
- arg->argnum, arg->insn->insn_mo->name, factor,
- (unsigned long) sval);
- arg->last_op_int = min_val;
+ match_out_of_range (arg);
+ return FALSE;
}
uval = (unsigned int) sval >> operand->shift;
}
insn_insert_operand (arg->insn, operand_base, uval);
- return s;
+ return TRUE;
}
/* OP_MAPPED_INT matcher. */
-static char *
+static bfd_boolean
match_mapped_int_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand_base, char *s)
+ const struct mips_operand *operand_base)
{
const struct mips_mapped_int_operand *operand;
unsigned int uval, num_vals;
offsetT sval;
operand = (const struct mips_mapped_int_operand *) operand_base;
- s = match_const_int (arg, s, &sval, operand->int_map[0]);
- if (!s)
- return 0;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
num_vals = 1 << operand_base->size;
for (uval = 0; uval < num_vals; uval++)
if (operand->int_map[uval] == sval)
break;
if (uval == num_vals)
- return 0;
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
insn_insert_operand (arg->insn, operand_base, uval);
- return s;
+ return TRUE;
}
/* OP_MSB matcher. */
-static char *
+static bfd_boolean
match_msb_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand_base, char *s)
+ const struct mips_operand *operand_base)
{
const struct mips_msb_operand *operand;
int min_val, max_val, max_high;
max_val = min_val + (1 << operand_base->size) - 1;
max_high = operand->opsize;
- s = match_const_int (arg, s, &size, 1);
- if (!s)
- return 0;
+ if (!match_const_int (arg, &size))
+ 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)
{
- if (arg->soft_match)
- return 0;
- report_bad_field (arg->last_op_int, size);
- sval = min_val;
+ match_out_of_range (arg);
+ return FALSE;
}
insn_insert_operand (arg->insn, operand_base, sval - min_val);
- return s;
+ return TRUE;
}
/* OP_REG matcher. */
-static char *
+static bfd_boolean
match_reg_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand_base, char *s)
+ const struct mips_operand *operand_base)
{
const struct mips_reg_operand *operand;
- unsigned int regno, uval, num_vals, types;
+ unsigned int regno, uval, num_vals;
operand = (const struct mips_reg_operand *) operand_base;
- types = convert_reg_type (arg->insn->insn_mo, operand->reg_type);
- if (!reg_lookup (&s, types, ®no))
- return 0;
-
- SKIP_SPACE_TABS (s);
- if (arg->optional_reg && *s == 0)
- return 0;
+ if (!match_reg (arg, operand->reg_type, ®no))
+ return FALSE;
if (operand->reg_map)
{
if (operand->reg_map[uval] == regno)
break;
if (num_vals == uval)
- return 0;
+ return FALSE;
}
else
uval = regno;
- check_regno (arg, operand->reg_type, regno);
arg->last_regno = regno;
if (arg->opnum == 1)
arg->dest_regno = regno;
insn_insert_operand (arg->insn, operand_base, uval);
- return s;
+ return TRUE;
}
/* OP_REG_PAIR matcher. */
-static char *
+static bfd_boolean
match_reg_pair_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand_base, char *s)
+ const struct mips_operand *operand_base)
{
const struct mips_reg_pair_operand *operand;
- unsigned int regno1, regno2, uval, num_vals, types;
+ unsigned int regno1, regno2, uval, num_vals;
operand = (const struct mips_reg_pair_operand *) operand_base;
- types = convert_reg_type (arg->insn->insn_mo, operand->reg_type);
-
- if (!reg_lookup (&s, types, ®no1))
- return 0;
-
- SKIP_SPACE_TABS (s);
- if (*s++ != ',')
- return 0;
- arg->argnum += 1;
-
- if (!reg_lookup (&s, types, ®no2))
- return 0;
+ if (!match_reg (arg, operand->reg_type, ®no1)
+ || !match_char (arg, ',')
+ || !match_reg (arg, operand->reg_type, ®no2))
+ return FALSE;
num_vals = 1 << operand_base->size;
for (uval = 0; uval < num_vals; uval++)
if (operand->reg1_map[uval] == regno1 && operand->reg2_map[uval] == regno2)
break;
if (uval == num_vals)
- return 0;
+ return FALSE;
- check_regno (arg, operand->reg_type, regno1);
- check_regno (arg, operand->reg_type, regno2);
insn_insert_operand (arg->insn, operand_base, uval);
- return s;
+ return TRUE;
}
/* OP_PCREL matcher. The caller chooses the relocation type. */
-static char *
-match_pcrel_operand (char *s)
+static bfd_boolean
+match_pcrel_operand (struct mips_arg_info *arg)
{
- my_getExpression (&offset_expr, s);
- return expr_end;
+ bfd_reloc_code_real_type r[3];
+
+ return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
}
/* OP_PERF_REG matcher. */
-static char *
+static bfd_boolean
match_perf_reg_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand, char *s)
+ const struct mips_operand *operand)
{
offsetT sval;
- s = match_const_int (arg, s, &sval, 0);
- if (!s)
- return 0;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
if (sval != 0
&& (sval != 1
&& (strcmp (arg->insn->insn_mo->name, "mfps") == 0
|| strcmp (arg->insn->insn_mo->name, "mtps") == 0))))
{
- if (arg->soft_match)
- return 0;
- as_bad (_("Invalid performance register (%ld)"), (unsigned long) sval);
+ set_insn_error (arg->argnum, _("invalid performance register"));
+ return FALSE;
}
insn_insert_operand (arg->insn, operand, sval);
- return s;
+ return TRUE;
}
/* OP_ADDIUSP matcher. */
-static char *
+static bfd_boolean
match_addiusp_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand, char *s)
+ const struct mips_operand *operand)
{
offsetT sval;
unsigned int uval;
- s = match_const_int (arg, s, &sval, -256);
- if (!s)
- return 0;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
if (sval % 4)
- return 0;
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
sval /= 4;
if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
- return 0;
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
uval = (unsigned int) sval;
uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
insn_insert_operand (arg->insn, operand, uval);
- return s;
+ return TRUE;
}
/* OP_CLO_CLZ_DEST matcher. */
-static char *
+static bfd_boolean
match_clo_clz_dest_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand, char *s)
+ const struct mips_operand *operand)
{
unsigned int regno;
- if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
- return 0;
+ if (!match_reg (arg, OP_REG_GP, ®no))
+ return FALSE;
- check_regno (arg, OP_REG_GP, regno);
insn_insert_operand (arg->insn, operand, regno | (regno << 5));
- return s;
+ return TRUE;
}
/* OP_LWM_SWM_LIST matcher. */
-static char *
+static bfd_boolean
match_lwm_swm_list_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand, char *s)
+ const struct mips_operand *operand)
{
- unsigned int reglist, sregs, ra;
+ unsigned int reglist, sregs, ra, regno1, regno2;
+ struct mips_arg_info reset;
- if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
- return 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;
if (operand->size == 2)
{
and any permutations of these. */
if ((reglist & 0xfff1ffff) != 0x80010000)
- return 0;
+ return FALSE;
sregs = (reglist >> 17) & 7;
ra = 0;
and any permutations of these. */
if ((reglist & 0x3f00ffff) != 0)
- return 0;
+ return FALSE;
ra = (reglist >> 27) & 0x10;
sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
}
sregs += 1;
if ((sregs & -sregs) != sregs)
- return 0;
+ return FALSE;
insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
- return s;
+ return TRUE;
+}
+
+/* OP_ENTRY_EXIT_LIST matcher. */
+
+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
+ {
+ unsigned int regno1, regno2;
+ bfd_boolean is_freg;
+
+ 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;
+}
+
+/* OP_SAVE_RESTORE_LIST matcher. */
+
+static bfd_boolean
+match_save_restore_list_operand (struct mips_arg_info *arg)
+{
+ unsigned int opcode, args, statics, sregs;
+ unsigned int num_frame_sizes, num_args, num_statics, num_sregs;
+ offsetT frame_size;
+
+ opcode = arg->insn->insn_opcode;
+ frame_size = 0;
+ num_frame_sizes = 0;
+ args = 0;
+ statics = 0;
+ sregs = 0;
+ do
+ {
+ unsigned int regno1, regno2;
+
+ if (arg->token->type == OT_INTEGER)
+ {
+ /* Handle the frame size. */
+ if (!match_const_int (arg, &frame_size))
+ return FALSE;
+ num_frame_sizes += 1;
+ }
+ else
+ {
+ if (!match_reg_range (arg, OP_REG_GP, ®no1, ®no2))
+ return FALSE;
+
+ 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, ','));
+
+ /* 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
+ {
+ /* Count arg registers. */
+ num_args = 0;
+ while (args & 0x1)
+ {
+ args >>= 1;
+ num_args += 1;
+ }
+ if (args != 0)
+ return FALSE;
+
+ /* Count static registers. */
+ num_statics = 0;
+ while (statics & 0x8)
+ {
+ statics = (statics << 1) & 0xf;
+ num_statics += 1;
+ }
+ if (statics != 0)
+ return FALSE;
+
+ /* Encode args/statics. */
+ opcode |= ((num_args << 2) | num_statics) << 16;
+ }
+
+ /* Encode $s0/$s1. */
+ if (sregs & (1 << 0)) /* $s0 */
+ opcode |= 0x20;
+ if (sregs & (1 << 1)) /* $s1 */
+ opcode |= 0x10;
+ sregs >>= 2;
+
+ /* Encode $s2-$s8. */
+ num_sregs = 0;
+ while (sregs & 1)
+ {
+ sregs >>= 1;
+ num_sregs += 1;
+ }
+ if (sregs != 0)
+ return FALSE;
+ opcode |= num_sregs << 24;
+
+ /* Encode frame size. */
+ if (num_frame_sizes == 0)
+ {
+ set_insn_error (arg->argnum, _("missing frame size"));
+ return FALSE;
+ }
+ if (num_frame_sizes > 1)
+ {
+ set_insn_error (arg->argnum, _("frame size specified twice"));
+ return FALSE;
+ }
+ if ((frame_size & 7) != 0 || frame_size < 0 || frame_size > 0xff * 8)
+ {
+ set_insn_error (arg->argnum, _("invalid frame size"));
+ return FALSE;
+ }
+ if (frame_size != 128 || (opcode >> 16) != 0)
+ {
+ frame_size /= 8;
+ opcode |= (((frame_size & 0xf0) << 16)
+ | (frame_size & 0x0f));
+ }
+
+ /* Finally build the instruction. */
+ if ((opcode >> 16) != 0 || frame_size == 0)
+ opcode |= MIPS16_EXTEND;
+ arg->insn->insn_opcode = opcode;
+ return TRUE;
}
/* OP_MDMX_IMM_REG matcher. */
-static char *
+static bfd_boolean
match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand, char *s)
+ const struct mips_operand *operand)
{
- unsigned int regno, uval, types;
+ unsigned int regno, uval;
bfd_boolean is_qh;
const struct mips_opcode *opcode;
uval = mips_extract_operand (operand, opcode->match);
is_qh = (uval != 0);
- types = convert_reg_type (arg->insn->insn_mo, OP_REG_VEC);
- if (reg_lookup (&s, types, ®no))
+ if (arg->token->type == OT_REG)
{
if ((opcode->membership & INSN_5400)
&& strcmp (opcode->name, "rzu.ob") == 0)
{
- if (arg->soft_match)
- return 0;
- as_bad (_("Operand %d of `%s' must be an immediate"),
- arg->argnum, opcode->name);
+ set_insn_error_i (arg->argnum, _("operand %d must be an immediate"),
+ arg->argnum);
+ return FALSE;
}
+ if (!match_regno (arg, OP_REG_VEC, arg->token->u.regno, ®no))
+ return FALSE;
+ ++arg->token;
+
/* Check whether this is a vector register or a broadcast of
a single element. */
- SKIP_SPACE_TABS (s);
- if (*s == '[')
+ if (arg->token->type == OT_INTEGER_INDEX)
{
- /* Read the element number. */
- expressionS value;
-
- ++s;
- SKIP_SPACE_TABS (s);
- my_getExpression (&value, s);
- s = expr_end;
- if (value.X_op != O_constant
- || value.X_add_number < 0
- || value.X_add_number > (is_qh ? 3 : 7))
- {
- if (arg->soft_match)
- return 0;
- as_bad (_("Invalid element selector"));
- value.X_add_number = 0;
- }
- uval |= (unsigned int) value.X_add_number << (is_qh ? 2 : 1) << 5;
- SKIP_SPACE_TABS (s);
- if (*s == ']')
- ++s;
- else
+ if (arg->token->u.index > (is_qh ? 3 : 7))
{
- if (arg->soft_match)
- return 0;
- as_bad (_("Expecting ']' found '%s'"), s);
+ set_insn_error (arg->argnum, _("invalid element selector"));
+ return FALSE;
}
+ uval |= arg->token->u.index << (is_qh ? 2 : 1) << 5;
+ ++arg->token;
}
else
{
&& (strcmp (opcode->name, "sll.ob") == 0
|| strcmp (opcode->name, "srl.ob") == 0))
{
- if (arg->soft_match)
- return 0;
- as_bad (_("Operand %d of `%s' must be scalar"),
- arg->argnum, opcode->name);
+ set_insn_error_i (arg->argnum, _("operand %d must be scalar"),
+ arg->argnum);
+ return FALSE;
}
if (is_qh)
else
uval |= MDMX_FMTSEL_VEC_OB << 5;
}
- check_regno (arg, OP_REG_FP, regno);
uval |= regno;
}
else
{
offsetT sval;
- s = match_const_int (arg, s, &sval, 0);
- if (!s)
- return 0;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
if (sval < 0 || sval > 31)
{
- if (arg->soft_match)
- return 0;
- report_bad_range (arg->insn, arg->argnum, sval, 0, 31, FALSE);
+ match_out_of_range (arg);
+ return FALSE;
}
uval |= (sval & 31);
if (is_qh)
uval |= MDMX_FMTSEL_IMM_OB << 5;
}
insn_insert_operand (arg->insn, operand, uval);
- return s;
+ return TRUE;
+}
+
+/* OP_IMM_INDEX matcher. */
+
+static bfd_boolean
+match_imm_index_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int max_val;
+
+ if (arg->token->type != OT_INTEGER_INDEX)
+ return FALSE;
+
+ max_val = (1 << operand->size) - 1;
+ if (arg->token->u.index > max_val)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+ insn_insert_operand (arg->insn, operand, arg->token->u.index);
+ ++arg->token;
+ return TRUE;
+}
+
+/* OP_REG_INDEX matcher. */
+
+static bfd_boolean
+match_reg_index_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
+
+ if (arg->token->type != OT_REG_INDEX)
+ return FALSE;
+
+ if (!match_regno (arg, OP_REG_GP, arg->token->u.regno, ®no))
+ return FALSE;
+
+ insn_insert_operand (arg->insn, operand, regno);
+ ++arg->token;
+ return TRUE;
}
/* OP_PC matcher. */
-static char *
-match_pc_operand (char *s)
+static bfd_boolean
+match_pc_operand (struct mips_arg_info *arg)
{
- if (strncmp (s, "$pc", 3) != 0)
- return 0;
- s += 3;
- SKIP_SPACE_TABS (s);
- return s;
+ if (arg->token->type == OT_REG && (arg->token->u.regno & RTYPE_PC))
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
}
/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher. OTHER_REGNO is the
register that we need to match. */
-static char *
-match_tied_reg_operand (struct mips_arg_info *arg, char *s,
- unsigned int other_regno)
+static bfd_boolean
+match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
{
unsigned int regno;
- if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no)
- || regno != other_regno)
- return 0;
- SKIP_SPACE_TABS (s);
- if (arg->optional_reg && *s == 0)
- return 0;
- return s;
+ return match_reg (arg, OP_REG_GP, ®no) && regno == other_regno;
+}
+
+/* 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.
+
+ 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)
+{
+ 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)
+ {
+ set_insn_error (arg->argnum, _("floating-point expression required"));
+ 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)))
+ {
+ 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;
+ }
+
+ /* 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 || !(FPR_SIZE == 64 && GPR_SIZE == 32))
+ && ((data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0))
+ && ((data[4] == 0 && data[5] == 0)
+ || (data[6] == 0 && data[7] == 0)))
+ {
+ /* 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 ? GPR_SIZE == 32 : FPR_SIZE != 64)
+ {
+ 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;
+ }
+
+ /* 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 (_("cannot use `%s' in this section"), arg->insn->insn_mo->name);
+
+ /* 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;
+}
+
+/* OP_VU0_SUFFIX and OP_VU0_MATCH_SUFFIX matcher; MATCH_P selects between
+ them. */
+
+static bfd_boolean
+match_vu0_suffix_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand,
+ bfd_boolean match_p)
+{
+ unsigned int uval;
+
+ /* The operand can be an XYZW mask or a single 2-bit channel index
+ (with X being 0). */
+ gas_assert (operand->size == 2 || operand->size == 4);
+
+ /* The suffix can be omitted when it is already part of the opcode. */
+ if (arg->token->type != OT_CHANNELS)
+ return match_p;
+
+ uval = arg->token->u.channels;
+ if (operand->size == 2)
+ {
+ /* Check that a single bit is set and convert it into a 2-bit index. */
+ if ((uval & -uval) != uval)
+ return FALSE;
+ uval = 4 - ffs (uval);
+ }
+
+ if (match_p && insn_extract_operand (arg->insn, operand) != uval)
+ return FALSE;
+
+ ++arg->token;
+ if (!match_p)
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
}
/* S is the text seen for ARG. Match it against OPERAND. Return the end
of the argument text if the match is successful, otherwise return null. */
-static char *
+static bfd_boolean
match_operand (struct mips_arg_info *arg,
- const struct mips_operand *operand, char *s)
+ const struct mips_operand *operand)
{
switch (operand->type)
{
case OP_INT:
- return match_int_operand (arg, operand, s);
+ return match_int_operand (arg, operand);
case OP_MAPPED_INT:
- return match_mapped_int_operand (arg, operand, s);
+ return match_mapped_int_operand (arg, operand);
case OP_MSB:
- return match_msb_operand (arg, operand, s);
+ return match_msb_operand (arg, operand);
case OP_REG:
- return match_reg_operand (arg, operand, s);
+ case OP_OPTIONAL_REG:
+ return match_reg_operand (arg, operand);
case OP_REG_PAIR:
- return match_reg_pair_operand (arg, operand, s);
+ return match_reg_pair_operand (arg, operand);
case OP_PCREL:
- return match_pcrel_operand (s);
+ return match_pcrel_operand (arg);
case OP_PERF_REG:
- return match_perf_reg_operand (arg, operand, s);
+ return match_perf_reg_operand (arg, operand);
case OP_ADDIUSP_INT:
- return match_addiusp_operand (arg, operand, s);
+ return match_addiusp_operand (arg, operand);
case OP_CLO_CLZ_DEST:
- return match_clo_clz_dest_operand (arg, operand, s);
+ return match_clo_clz_dest_operand (arg, operand);
case OP_LWM_SWM_LIST:
- return match_lwm_swm_list_operand (arg, operand, s);
+ 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:
- abort ();
+ return match_save_restore_list_operand (arg);
case OP_MDMX_IMM_REG:
- return match_mdmx_imm_reg_operand (arg, operand, s);
+ return match_mdmx_imm_reg_operand (arg, operand);
case OP_REPEAT_DEST_REG:
- return match_tied_reg_operand (arg, s, arg->dest_regno);
+ return match_tied_reg_operand (arg, arg->dest_regno);
case OP_REPEAT_PREV_REG:
- return match_tied_reg_operand (arg, s, arg->last_regno);
+ return match_tied_reg_operand (arg, arg->last_regno);
case OP_PC:
- return match_pc_operand (s);
+ return match_pc_operand (arg);
+
+ case OP_VU0_SUFFIX:
+ return match_vu0_suffix_operand (arg, operand, FALSE);
+
+ case OP_VU0_MATCH_SUFFIX:
+ return match_vu0_suffix_operand (arg, operand, TRUE);
+
+ case OP_IMM_INDEX:
+ return match_imm_index_operand (arg, operand);
+
+ case OP_REG_INDEX:
+ return match_reg_index_operand (arg, operand);
}
abort ();
}
if (arg->seen_at)
{
if (AT == ATREG)
- as_warn (_("Used $at without \".set noat\""));
+ as_warn (_("used $at without \".set noat\""));
else
- as_warn (_("Used $%u with \".set at=$%u\""), AT, AT);
+ as_warn (_("used $%u with \".set at=$%u\""), AT, AT);
}
}
-#endif
-/* Classify an instruction according to the FIX_VR4120_* enumeration.
+/* Return true if modifying general-purpose register REG needs a delay. */
+
+static bfd_boolean
+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) && !gpr_interlocks)
+ || ((prev_pinfo & INSN_LOAD_COPROC_DELAY) && !cop_interlocks))
+ && (gpr_write_mask (&history[0]) & (1 << reg)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
by VR4120 errata. */
return NUM_FIX_VR4120_CLASSES;
}
-#define INSN_ERET 0x42000018
-#define INSN_DERET 0x4200001f
+#define INSN_ERET 0x42000018
+#define INSN_DERET 0x4200001f
+#define INSN_DMULT 0x1c
+#define INSN_DMULTU 0x1d
/* Return the number of instructions that must separate INSN1 and INSN2,
where INSN1 is the earlier instruction. Return the worst-case value
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. */
+ /* 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;
-#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 (mips_7000_hilo_fix
&& !mips_opts.micromips
&& MF_HILO_INSN (pinfo1)
- && INSN2_USES_GPR (EXTRACT_OPERAND (0, RD, *insn1)))
+ && (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1))))
return 2;
/* If we're working around 24K errata, one instruction is required
}
}
+ /* If we're working around PMC RM7000 errata, there must be three
+ nops between a dmult and a load instruction. */
+ if (mips_fix_rm7000 && !mips_opts.micromips)
+ {
+ if ((insn1->insn_opcode & insn1->insn_mo->mask) == INSN_DMULT
+ || (insn1->insn_opcode & insn1->insn_mo->mask) == INSN_DMULTU)
+ {
+ if (pinfo2 & INSN_LOAD_MEMORY)
+ return 3;
+ }
+ }
+
/* If working around VR4120 errata, check for combinations that need
a single intervening instruction. */
if (mips_fix_vr4120 && !mips_opts.micromips)
/* 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))
+ if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY))
|| (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC_DELAY)))
{
- know (pinfo1 & INSN_WRITE_GPR_T);
- if (INSN2_USES_GPR (EXTRACT_OPERAND (0, RT, *insn1)))
+ if (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1)))
return 1;
}
return 1;
}
-#undef INSN2_USES_GPR
-
return 0;
}
static bfd_boolean
can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
- bfd_reloc_code_real_type *reloc_type)
+ bfd_reloc_code_real_type *reloc_type)
{
unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+ unsigned int fpr_read, prev_fpr_write;
/* -O2 and above is required for this optimization. */
if (mips_optimize < 2)
if (gpr_read & prev_gpr_write)
return FALSE;
+ fpr_read = fpr_read_mask (ip);
+ prev_fpr_write = fpr_write_mask (&history[0]);
+ if (fpr_read & prev_fpr_write)
+ return FALSE;
+
/* If the branch writes a register that the previous
instruction sets, we can not swap. */
gpr_write = gpr_write_mask (ip);
/* 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))
+ if (prev_pinfo2 & INSN2_READ_PC)
return FALSE;
/* If the previous instruction has an incorrect size for a fixed
static enum append_method
get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
- bfd_reloc_code_real_type *reloc_type)
+ bfd_reloc_code_real_type *reloc_type)
{
- unsigned long pinfo;
-
/* The relaxed version of a macro sequence must be inherently
hazard-free. */
if (mips_relax.sequence == 2)
&& can_swap_branch_p (ip, address_expr, reloc_type))
return APPEND_SWAP;
- pinfo = ip->insn_mo->pinfo;
if (mips_opts.mips16
&& ISA_SUPPORTS_MIPS16E
- && (pinfo & (MIPS16_INSN_READ_X | MIPS16_INSN_READ_31)))
+ && gpr_read_mask (ip) != 0)
return APPEND_ADD_COMPACT;
return APPEND_ADD_WITH_NOP;
&& 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"),
+ 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)
/* These relocations can have an addend that won't fit in
4 octets for 64bit assembly. */
- if (HAVE_64BIT_GPRS
+ if (GPR_SIZE == 64
&& ! howto->partial_inplace
&& (reloc_type[0] == BFD_RELOC_16
|| reloc_type[0] == BFD_RELOC_32
}
}
-/* Set up global variables for the start of a new macro. */
+/* Sign-extend 32-bit mode constants that have bit 31 set and all
+ higher bits unset. */
static void
-macro_start (void)
+normalize_constant_expr (expressionS *ex)
{
- 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;
+ 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);
}
-/* 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. */
+/* Sign-extend 32-bit mode address offsets that have bit 31 set and
+ all higher bits unset. */
-static const char *
-macro_warning (relax_substateT subtype)
+static void
+normalize_address_expr (expressionS *ex)
{
- 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;
+ 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);
}
-/* Finish up a macro. Emit warnings as appropriate. */
+/* Try to match TOKENS against OPCODE, storing the result in INSN.
+ Return true if the match was successful.
-static void
-macro_end (void)
-{
- /* Relaxation warning flags. */
- relax_substateT subtype = 0;
+ OPCODE_EXTRA is a value that should be ORed into the opcode
+ (used for VU0 channel suffixes, etc.). MORE_ALTS is true if
+ there are more alternatives after OPCODE and SOFT_MATCH is
+ as for mips_arg_info. */
- /* 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;
- }
+static bfd_boolean
+match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
+ struct mips_operand_token *tokens, unsigned int opcode_extra,
+ bfd_boolean lax_match, bfd_boolean complete_p)
+{
+ const char *args;
+ struct mips_arg_info arg;
+ const struct mips_operand *operand;
+ char c;
- /* 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;
- }
+ imm_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;
- /* 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;
+ create_insn (insn, opcode);
+ /* When no opcode suffix is specified, assume ".xyzw". */
+ if ((opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
+ insn->insn_opcode |= 0xf << mips_vu0_channel_mask.lsb;
+ else
+ insn->insn_opcode |= opcode_extra;
+ memset (&arg, 0, sizeof (arg));
+ arg.insn = insn;
+ arg.token = tokens;
+ arg.argnum = 1;
+ arg.last_regno = ILLEGAL_REG;
+ arg.dest_regno = ILLEGAL_REG;
+ arg.lax_match = lax_match;
+ for (args = opcode->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 == ',')
+ {
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (args + 1)
+ : decode_mips_operand (args + 1));
+ if (operand && mips_optional_operand_p (operand))
+ {
+ arg.token = tokens;
+ arg.argnum = 1;
+ continue;
+ }
+ }
- 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;
- }
+ /* Treat elided base registers as $0. */
+ if (strcmp (args, "(b)") == 0)
+ args += 3;
- /* 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;
+ if (args[0] == '+')
+ switch (args[1])
+ {
+ case 'K':
+ case 'N':
+ /* The register suffix is optional. */
+ args += 2;
+ break;
+ }
- s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
- msg = macro_warning (s);
- if (msg != NULL)
- as_warn ("%s", msg);
- subtype &= ~s;
- }
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ return FALSE;
- /* 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;
-}
+ /* Successful match. */
+ if (!complete_p)
+ return TRUE;
+ clear_insn_error ();
+ if (arg.dest_regno == arg.last_regno
+ && strncmp (insn->insn_mo->name, "jalr", 4) == 0)
+ {
+ if (arg.opnum == 2)
+ set_insn_error
+ (0, _("source and destination must be different"));
+ else if (arg.last_regno == 31)
+ set_insn_error
+ (0, _("a destination register must be supplied"));
+ }
+ else if (arg.last_regno == 31
+ && (strncmp (insn->insn_mo->name, "bltzal", 6) == 0
+ || strncmp (insn->insn_mo->name, "bgezal", 6) == 0))
+ set_insn_error (0, _("the source register must not be $31"));
+ check_completed_insn (&arg);
+ return TRUE;
+ }
-/* Instruction operand formats used in macros that vary between
- standard MIPS and microMIPS code. */
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ return FALSE;
-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,|" };
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
+ {
+ if (match_char (&arg, *args))
+ continue;
+ return FALSE;
+ }
+ if (*args == '#')
+ {
+ ++args;
+ if (arg.token->type == OT_DOUBLE_CHAR
+ && arg.token->u.ch == *args)
+ {
+ ++arg.token;
+ continue;
+ }
+ return FALSE;
+ }
-#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])
+ /* Handle special macro operands. Work out the properties of
+ other operands. */
+ arg.opnum += 1;
+ switch (*args)
+ {
+ case '+':
+ switch (args[1])
+ {
+ case 'i':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
+ }
+ break;
-/* 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. */
+ case 'I':
+ if (!match_const_int (&arg, &imm_expr.X_add_number))
+ return FALSE;
+ imm_expr.X_op = O_constant;
+ if (GPR_SIZE == 32)
+ normalize_constant_expr (&imm_expr);
+ continue;
-static void
-macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
-{
- int i, next;
+ 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))
+ return FALSE;
+ normalize_address_expr (&offset_expr);
+ }
+ continue;
- 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);
- /* 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;
- }
-}
+ case 'F':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, TRUE))
+ return FALSE;
+ continue;
-/* 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. */
+ case 'L':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, FALSE))
+ return FALSE;
+ continue;
-static void
-macro_build (expressionS *ep, const char *name, const char *fmt, ...)
-{
- 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;
+ case 'f':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, TRUE))
+ return FALSE;
+ continue;
- va_start (args, fmt);
+ case 'l':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, FALSE))
+ return FALSE;
+ continue;
- if (mips_opts.mips16)
- {
- mips16_macro_build (ep, name, fmt, &args);
- va_end (args);
- return;
- }
+ case 'p':
+ *offset_reloc = BFD_RELOC_16_PCREL_S2;
+ break;
- 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);
+ case 'a':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
- 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))
+ case 'm':
+ gas_assert (mips_opts.micromips);
+ c = args[1];
+ switch (c)
{
- mo = amo;
+ 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;
}
- else if (!mo)
- mo = amo;
+ break;
}
- ++amo;
- gas_assert (amo->name);
- }
- while (strcmp (name, amo->name) == 0);
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (args)
+ : decode_mips_operand (args));
+ if (!operand)
+ abort ();
- gas_assert (mo);
- create_insn (&insn, mo);
- for (; *fmt; ++fmt)
- {
- switch (*fmt)
+ /* Skip prefixes. */
+ if (*args == '+' || *args == 'm')
+ args++;
+
+ if (mips_optional_operand_p (operand)
+ && args[1] == ','
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
{
- case ',':
- case '(':
- case ')':
- case 'z':
- break;
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
+ }
- 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;
+ if (!match_operand (&arg, operand))
+ return FALSE;
+ }
+}
- case 'o':
- macro_read_relocs (&args, r);
- break;
+/* Like match_insn, but for MIPS16. */
- 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;
+static bfd_boolean
+match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
+ struct mips_operand_token *tokens)
+{
+ const char *args;
+ const struct mips_operand *operand;
+ const struct mips_operand *ext_operand;
+ struct mips_arg_info arg;
+ int relax_char;
- case 'p':
- gas_assert (ep != NULL);
+ create_insn (insn, opcode);
+ imm_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;
- /*
- * 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);
+ memset (&arg, 0, sizeof (arg));
+ arg.insn = insn;
+ arg.token = tokens;
+ arg.argnum = 1;
+ arg.last_regno = ILLEGAL_REG;
+ arg.dest_regno = ILLEGAL_REG;
+ relax_char = 0;
+ for (args = opcode->args;; ++args)
+ {
+ int c;
- 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;
+ if (arg.token->type == OT_END)
+ {
+ offsetT value;
- case 'a':
- gas_assert (ep != NULL);
- *r = BFD_RELOC_MIPS_JMP;
- 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 == ',')
+ {
+ operand = decode_mips16_operand (args[1], FALSE);
+ if (operand && mips_optional_operand_p (operand))
+ {
+ arg.token = tokens;
+ arg.argnum = 1;
+ continue;
+ }
+ }
- default:
- operand = (mips_opts.micromips
- ? decode_micromips_operand (fmt)
- : decode_mips_operand (fmt));
- if (!operand)
- abort ();
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ return FALSE;
- uval = va_arg (args, int);
- if (operand->type == OP_CLO_CLZ_DEST)
- uval |= (uval << 5);
- insn_insert_operand (&insn, operand, uval);
+ /* Successful match. Stuff the immediate value in now, if
+ we can. */
+ clear_insn_error ();
+ if (opcode->pinfo == INSN_MACRO)
+ {
+ gas_assert (relax_char == 0 || relax_char == 'p');
+ gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
+ }
+ else if (relax_char
+ && offset_expr.X_op == O_constant
+ && calculate_reloc (*offset_reloc,
+ offset_expr.X_add_number,
+ &value))
+ {
+ mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
+ forced_insn_length, &insn->insn_opcode);
+ offset_expr.X_op = O_absent;
+ *offset_reloc = BFD_RELOC_UNUSED;
+ }
+ else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
+ {
+ if (forced_insn_length == 2)
+ set_insn_error (0, _("invalid unextended operand value"));
+ forced_insn_length = 4;
+ insn->insn_opcode |= MIPS16_EXTEND;
+ }
+ else if (relax_char)
+ *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
- if (*fmt == '+' || *fmt == 'm')
- ++fmt;
- break;
+ check_completed_insn (&arg);
+ return TRUE;
}
- }
- va_end (args);
- gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-
- append_insn (&insn, ep, r, TRUE);
-}
-
-static void
-mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
- va_list *args)
-{
- 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};
-
- 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);
- }
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ return FALSE;
- create_insn (&insn, mo);
- for (; *fmt; ++fmt)
- {
- int c;
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
+ {
+ if (match_char (&arg, *args))
+ continue;
+ return FALSE;
+ }
- c = *fmt;
+ arg.opnum += 1;
+ c = *args;
switch (c)
{
- case ',':
- case '(':
- case ')':
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ relax_char = c;
break;
- case '0':
- case 'S':
- case 'P':
- case 'R':
+ case 'I':
+ if (!match_const_int (&arg, &imm_expr.X_add_number))
+ return FALSE;
+ imm_expr.X_op = O_constant;
+ if (GPR_SIZE == 32)
+ normalize_constant_expr (&imm_expr);
+ continue;
+
+ case 'a':
+ case 'i':
+ *offset_reloc = BFD_RELOC_MIPS16_JMP;
+ insn->insn_opcode <<= 16;
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;
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
- gas_assert (ep != NULL);
+ /* '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;
+ }
- 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;
+ /* 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))
+ return FALSE;
- default:
- operand = decode_mips16_operand (c, FALSE);
- if (!operand)
- abort ();
+ /* '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'))
+ return FALSE;
- insn_insert_operand (&insn, operand, va_arg (args, int));
- break;
+ relax_char = c;
+ continue;
+ }
}
- }
- gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+ if (mips_optional_operand_p (operand)
+ && args[1] == ','
+ && (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;
+ }
- append_insn (&insn, ep, r, TRUE);
+ if (!match_operand (&arg, operand))
+ return FALSE;
+ }
}
-/*
- * Sign-extend 32-bit mode constants that have bit 31 set and all
- * higher bits unset.
- */
+/* Record that the current instruction is invalid for the current ISA. */
+
static void
-normalize_constant_expr (expressionS *ex)
+match_invalid_for_isa (void)
{
- 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);
+ set_insn_error_ss
+ (0, _("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);
}
-/*
- * 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);
-}
+/* Try to match TOKENS against a series of opcode entries, starting at FIRST.
+ Return true if a definite match or failure was found, storing any match
+ in INSN. OPCODE_EXTRA is a value that should be ORed into the opcode
+ (to handle things like VU0 suffixes). LAX_MATCH is true if we have already
+ tried and failed to match under normal conditions and now want to try a
+ more relaxed match. */
-/*
- * 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 bfd_boolean
+match_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+ const struct mips_opcode *past, struct mips_operand_token *tokens,
+ int opcode_extra, bfd_boolean lax_match)
{
- 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;
+ const struct mips_opcode *opcode;
+ const struct mips_opcode *invalid_delay_slot;
+ bfd_boolean seen_valid_for_isa, seen_valid_for_size;
+
+ /* Search for a match, ignoring alternatives that don't satisfy the
+ current ISA or forced_length. */
+ invalid_delay_slot = 0;
+ seen_valid_for_isa = FALSE;
+ seen_valid_for_size = FALSE;
+ opcode = first;
+ do
+ {
+ gas_assert (strcmp (opcode->name, first->name) == 0);
+ if (is_opcode_valid (opcode))
+ {
+ seen_valid_for_isa = TRUE;
+ if (is_size_valid (opcode))
+ {
+ bfd_boolean delay_slot_ok;
- if (MIPS_JALR_HINT_P (ep))
+ seen_valid_for_size = TRUE;
+ delay_slot_ok = is_delay_slot_valid (opcode);
+ if (match_insn (insn, opcode, tokens, opcode_extra,
+ lax_match, delay_slot_ok))
+ {
+ if (!delay_slot_ok)
+ {
+ if (!invalid_delay_slot)
+ invalid_delay_slot = opcode;
+ }
+ else
+ return TRUE;
+ }
+ }
+ }
+ ++opcode;
+ }
+ while (opcode < past && strcmp (opcode->name, first->name) == 0);
+
+ /* If the only matches we found had the wrong length for the delay slot,
+ pick the first such match. We'll issue an appropriate warning later. */
+ if (invalid_delay_slot)
{
- frag_grow (8);
- f = frag_more (0);
+ if (match_insn (insn, invalid_delay_slot, tokens, opcode_extra,
+ lax_match, TRUE))
+ return TRUE;
+ abort ();
}
- if (mips_opts.micromips)
+
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were incompatible with the current ISA. */
+ if (!seen_valid_for_isa)
{
- 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);
+ match_invalid_for_isa ();
+ return TRUE;
+ }
+
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were of the wrong size. */
+ if (!seen_valid_for_size)
+ {
+ if (mips_opts.insn32)
+ set_insn_error (0, _("opcode not supported in the `insn32' mode"));
else
- macro_build (NULL, jalr, "mj", PIC_CALL_REG);
+ set_insn_error_i
+ (0, _("unrecognized %d-bit version of microMIPS opcode"),
+ 8 * forced_insn_length);
+ return TRUE;
}
- 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);
+
+ return FALSE;
}
-/*
- * Generate a "lui" instruction.
- */
-static void
-macro_build_lui (expressionS *ep, int regnum)
+/* Like match_insns, but for MIPS16. */
+
+static bfd_boolean
+match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+ struct mips_operand_token *tokens)
{
- gas_assert (! mips_opts.mips16);
+ const struct mips_opcode *opcode;
+ bfd_boolean seen_valid_for_isa;
- if (ep->X_op != O_constant)
+ /* Search for a match, ignoring alternatives that don't satisfy the
+ current ISA. There are no separate entries for extended forms so
+ we deal with forced_length later. */
+ seen_valid_for_isa = FALSE;
+ opcode = first;
+ do
{
- 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));
+ gas_assert (strcmp (opcode->name, first->name) == 0);
+ if (is_opcode_valid_16 (opcode))
+ {
+ seen_valid_for_isa = TRUE;
+ if (match_mips16_insn (insn, opcode, tokens))
+ return TRUE;
+ }
+ ++opcode;
}
+ while (opcode < &mips16_opcodes[bfd_mips16_num_opcodes]
+ && strcmp (opcode->name, first->name) == 0);
- macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were incompatible with the current ISA. */
+ if (!seen_valid_for_isa)
+ {
+ match_invalid_for_isa ();
+ return TRUE;
+ }
+
+ return FALSE;
}
-/* 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. */
+/* Set up global variables for the start of a new macro. */
+
static void
-macro_build_ldst_constoffset (expressionS *ep, const char *op,
- int treg, int breg, int dbl)
+macro_start (void)
{
- gas_assert (ep->X_op == O_constant);
+ 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;
+}
- /* Sign-extending 32-bit constants makes their handling easier. */
- if (!dbl)
- normalize_constant_expr (ep);
+/* 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. */
- /* 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"));
+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;
+}
- if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+/* Finish up a macro. Emit warnings as appropriate. */
+
+static void
+macro_end (void)
+{
+ /* 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)
{
- /* Signed 16-bit offset will fit in the op. Easy! */
- macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ 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;
}
- else
+
+ /* Check instruction count requirements. */
+ if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
{
- /* 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_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)
- as_bad (_("Macro used $at after \".set noat\""));
+ /* 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;
+
+ 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;
}
-}
-/* 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 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)
{
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+ 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;
}
+
+ /* 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;
}
-/* Warn if an expression is not a constant. */
+/* Instruction operand formats used in macros that vary between
+ standard MIPS and microMIPS code. */
-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);
+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,|" };
- if (HAVE_32BIT_GPRS)
- normalize_constant_expr (ex);
-}
+#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])
-/* 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)
+/* 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. */
-/* 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)
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
{
- int freg;
- expressionS hi32, lo32;
+ int i, next;
- if (ep->X_op != O_big)
+ next = va_arg (*args, int);
+ if (next >= 0)
+ r[0] = (bfd_reloc_code_real_type) next;
+ else
{
- gas_assert (ep->X_op == O_constant);
+ 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;
+ }
+}
- /* Sign-extending 32-bit constants makes their handling easier. */
- if (!dbl)
- normalize_constant_expr (ep);
+/* 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. */
- if (IS_SEXT_16BIT_NUM (ep->X_add_number))
- {
- /* 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;
- }
- else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
- {
- /* 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;
- }
- }
+static void
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+{
+ 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;
- /* The value is larger than 32 bits. */
+ va_start (args, fmt);
- if (!dbl || HAVE_32BIT_GPRS)
+ if (mips_opts.mips16)
{
- char value[32];
-
- 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);
+ mips16_macro_build (ep, name, fmt, &args);
+ va_end (args);
return;
}
- 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);
- }
+ 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 (hi32.X_add_number == 0)
- freg = 0;
- else
+ do
{
- int shift, bit;
- unsigned long hi, lo;
-
- if (hi32.X_add_number == (offsetT) 0xffffffff)
+ /* 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 ((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)
+ if (is_delay_slot_valid (amo))
{
- 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;
+ mo = amo;
+ break;
}
+ else if (!mo)
+ mo = amo;
}
- /* 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
- {
- unsigned long himask, lomask;
+ ++amo;
+ gas_assert (amo->name);
+ }
+ while (strcmp (name, amo->name) == 0);
- 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;
+ gas_assert (mo);
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
+ {
+ switch (*fmt)
+ {
+ case ',':
+ case '(':
+ case ')':
+ case 'z':
+ break;
- 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));
+ 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;
- /* 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)
- {
- bit = 0;
- while ((lo & 1) == 0)
- {
- lo >>= 1;
- ++bit;
- }
- lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
- hi >>= bit;
- }
- else
- {
- bit = 32;
- while ((hi & 1) == 0)
- {
- hi >>= 1;
- ++bit;
- }
- lo = hi;
- hi = 0;
- }
+ case 'o':
+ macro_read_relocs (&args, r);
+ break;
- /* Optimize if the shifted value is a (power of 2) - 1. */
- if ((hi == 0 && ((lo + 1) & lo) == 0)
- || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
- {
- shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
- if (shift != 0)
+ 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)
{
- expressionS tmp;
+ /* For microMIPS we always use relocations for branches.
+ So we should not resolve immediate values. */
+ gas_assert (!mips_opts.micromips);
- /* 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;
+ 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;
- /* 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)
- {
- macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
- freg = reg;
- }
- }
- else
- {
- expressionS mid16;
+ case 'a':
+ gas_assert (ep != NULL);
+ *r = BFD_RELOC_MIPS_JMP;
+ break;
- if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
- {
- macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
- macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
- return;
- }
+ default:
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (fmt)
+ : decode_mips_operand (fmt));
+ if (!operand)
+ abort ();
- if (freg != 0)
- {
- macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
- freg = reg;
+ 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;
}
- 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);
-}
+ va_end (args);
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
-static inline void
-load_delay_nop (void)
-{
- if (!gpr_interlocks)
- macro_build (NULL, "nop", "");
+ append_insn (&insn, ep, r, TRUE);
}
-/* Load an address into a register. */
-
static void
-load_address (int reg, expressionS *ep, int *used_at)
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+ va_list *args)
{
- if (ep->X_op != O_constant
- && ep->X_op != O_symbol)
- {
- as_bad (_("expression too complex"));
- ep->X_op = O_constant;
- }
+ 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};
- if (ep->X_op == O_constant)
+ 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)
{
- load_register (reg, ep, HAVE_64BIT_ADDRESSES);
- return;
+ ++mo;
+ gas_assert (mo->name);
+ gas_assert (strcmp (name, mo->name) == 0);
}
- if (mips_pic == NO_PIC)
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
{
- /* 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.
-
- 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 $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)
+ int c;
- For GP relative symbols in 64bit address space we can use
- the same sequence as in 32bit address space. */
- if (HAVE_64BIT_SYMBOLS)
+ c = *fmt;
+ switch (c)
{
- 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 ();
- }
+ case ',':
+ case '(':
+ case ')':
+ break;
- 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);
- }
+ case '0':
+ case 'S':
+ case 'P':
+ case 'R':
+ break;
- if (mips_relax.sequence)
- relax_end ();
- }
- else
- {
- 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;
+ 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;
- /* 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.
+ gas_assert (ep != NULL);
- 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 ();
+ 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;
- 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);
- }
+ default:
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
+
+ insn_insert_operand (&insn, operand, va_arg (*args, int));
+ break;
}
}
- else if (mips_big_got)
- {
- 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)
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
- 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.
+ append_insn (&insn, ep, r, TRUE);
+}
- 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)
- {
- ex.X_op = O_constant;
- macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
- BFD_RELOC_LO16);
- }
+/*
+ * 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;
- 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 ();
- }
+ 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
- {
- 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 ();
-
- 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);
- }
- }
+ macro_build (NULL, jalr, "mj", PIC_CALL_REG);
}
else
- abort ();
-
- if (!mips_opts.at && *used_at == 1)
- as_bad (_("Macro used $at after \".set noat\""));
+ 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);
}
-/* Move the contents of register SOURCE into register DEST. */
-
+/*
+ * Generate a "lui" instruction.
+ */
static void
-move_register (int dest, int source)
+macro_build_lui (expressionS *ep, int regnum)
{
- /* 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);
-}
-
-/* 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:
+ gas_assert (! mips_opts.mips16);
- 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. */
-
-static void
-load_got_offset (int dest, expressionS *local)
-{
- expressionS global;
-
- global = *local;
- global.X_add_number = 0;
+ 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));
+ }
- 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 ();
+ macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
}
+/* 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
-add_got_offset (int dest, expressionS *local)
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+ int treg, int breg, int dbl)
{
- 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;
-
- 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 ();
-}
+ gas_assert (ep->X_op == O_constant);
-static void
-add_got_offset_hilo (int dest, expressionS *local, int tmp)
-{
- expressionS global;
- int hold_mips_optimize;
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
- global.X_op = O_constant;
- global.X_op_symbol = NULL;
- global.X_add_symbol = NULL;
- global.X_add_number = local->X_add_number;
+ /* 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"));
- 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 ();
+ 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);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+ if (!mips_opts.at)
+ as_bad (_("macro used $at after \".set noat\""));
+ }
}
-/* 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.
-
- Code like this is produced in the noreorder mode:
-
- BRNEG <args>, 1f
- nop
- b <sym>
- delay slot (executed only if branch taken)
- 1:
-
- or, if CALL is set:
-
- BRNEG <args>, 1f
- nop
- bal <sym>
- delay slot (executed only if branch taken)
- 1:
-
- In the reorder mode the delay slot would be filled with a nop anyway,
- so code produced is simply:
-
- BR <args>, <sym>
- nop
-
- This function is used when producing code for the microMIPS ASE that
- does not implement branch likely instructions in hardware. */
-
+/* set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
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)
+set_at (int reg, int unsignedp)
{
- 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");
-
- /* Set to true so that append_insn adds a label. */
- emit_branch_likely_macro = TRUE;
- }
+ if (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
{
- macro_build (ep, br, fmt, sreg, treg);
- macro_build (NULL, "nop", "");
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
}
- end_noreorder ();
}
-/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
- the condition code tested. EP specifies the branch target. */
+/* 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
-macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+load_register (int reg, expressionS *ep, int dbl)
{
- const int call = 0;
- const char *brneg;
- const char *br;
+ int freg;
+ expressionS hi32, lo32;
- switch (type)
+ if (ep->X_op != O_big)
{
- 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);
-}
-
-/* Emit a two-argument branch macro specified by TYPE, using SREG as
- the register tested. EP specifies the branch target. */
+ gas_assert (ep->X_op == O_constant);
-static void
-macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
-{
- const char *brneg = NULL;
- const char *br;
- int call = 0;
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
- switch (type)
- {
- 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 ();
+ if (IS_SEXT_16BIT_NUM (ep->X_add_number))
+ {
+ /* 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;
+ }
+ else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+ {
+ /* 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;
+ }
}
- 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);
-}
-/* Emit a three-argument branch macro specified by TYPE, using SREG and
- TREG as the registers tested. EP specifies the branch target. */
+ /* The value is larger than 32 bits. */
-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;
+ if (!dbl || GPR_SIZE == 32)
+ {
+ char value[32];
- switch (type)
+ 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 (ep->X_op != O_big)
{
- 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 ();
+ 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;
}
- 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);
-}
-
-/* Return the high part that should be loaded in order to make the low
- part of VALUE accessible using an offset of OFFBITS bits. */
+ {
+ 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);
+ }
-static offsetT
-offset_high_part (offsetT value, unsigned int offbits)
-{
- offsetT bias;
- addressT low_mask;
+ if (hi32.X_add_number == 0)
+ freg = 0;
+ else
+ {
+ int shift, bit;
+ unsigned long hi, lo;
- if (offbits == 0)
- return value;
- bias = 1 << (offbits - 1);
- low_mask = bias * 2 - 1;
- return (value + bias) & ~low_mask;
-}
+ 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;
+ }
+ }
-/* 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. */
+ /* 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
+ {
+ unsigned long himask, lomask;
-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 (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;
- /* 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;
-}
+ 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));
-/*
- * 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;
-
- 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;
- 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)
+ /* 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, s, "t,r,.", treg, sreg, imm_expr.X_add_number);
- break;
+ bit = 0;
+ while ((lo & 1) == 0)
+ {
+ lo >>= 1;
+ ++bit;
+ }
+ lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+ hi >>= bit;
}
- 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)
+ else
{
- macro_build (&imm_expr, s, "t,r,j", treg, sreg, BFD_RELOC_LO16);
- break;
+ bit = 32;
+ while ((hi & 1) == 0)
+ {
+ hi >>= 1;
+ ++bit;
+ }
+ lo = hi;
+ hi = 0;
}
- 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)
+ /* 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 (mask != M_NOR_I)
- macro_build (&imm_expr, s, "t,r,i", treg, sreg, BFD_RELOC_LO16);
- else
+ shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+ if (shift != 0)
{
- macro_build (&imm_expr, "ori", "t,r,i",
- treg, sreg, BFD_RELOC_LO16);
- macro_build (NULL, "nor", "d,v,t", treg, treg, 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;
}
- break;
}
- used_at = 1;
- load_register (AT, &imm_expr, HAVE_64BIT_GPRS);
- macro_build (NULL, s2, "d,v,t", treg, sreg, AT);
- break;
-
- case M_BALIGN:
- switch (imm_expr.X_add_number)
+ /* 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)
{
- 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;
+ macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+ freg = reg;
}
- 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;
+ }
+ else
+ {
+ expressionS mid16;
- 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
+ if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
{
- treg = AT;
- used_at = 1;
- load_register (treg, &imm_expr, HAVE_64BIT_GPRS);
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+ return;
}
- /* Fall through. */
- case M_BEQL:
- case M_BNEL:
- macro_build_branch_rsrt (mask, &offset_expr, sreg, treg);
- break;
- 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
+ if (freg != 0)
{
- 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);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
+ freg = reg;
}
- break;
+ 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);
+}
- 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;
+static inline void
+load_delay_nop (void)
+{
+ if (!gpr_interlocks)
+ macro_build (NULL, "nop", "");
+}
- 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)
+/* Load an address into a register. */
+
+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;
+ }
+
+ if (ep->X_op == O_constant)
+ {
+ load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+ return;
+ }
+
+ 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.
+
+ 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 $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)
+
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
{
- do_false:
- /* Result is always false. */
- if (! likely)
- macro_build (NULL, "nop", "");
+ 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_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;
+ {
+ 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);
+ }
- 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);
+ if (mips_relax.sequence)
+ relax_end ();
}
- break;
-
- 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);
+ 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 ();
}
- break;
+ }
+ else if (!mips_big_got)
+ {
+ expressionS ex;
- 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 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.
- 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
+ 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)
{
- 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);
+ 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 ();
}
- break;
-
- 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);
+ 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 (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);
+ }
}
- break;
+ }
+ else if (mips_big_got)
+ {
+ expressionS ex;
- 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;
+ /* 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)
- 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;
+ 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.
- 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
+ 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)
{
- used_at = 1;
- set_at (sreg, 1);
- macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
- &offset_expr, AT, ZERO);
- }
- break;
+ 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)
+ {
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ }
- 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
- {
- 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);
+ 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 ();
}
- 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);
+ 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 ();
+
+ 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);
+ }
}
- break;
+ }
+ else
+ abort ();
- case M_DEXT:
- {
- /* Use unsigned arithmetic. */
- addressT pos;
- addressT size;
+ if (!mips_opts.at && *used_at == 1)
+ as_bad (_("macro used $at after \".set noat\""));
+}
- 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;
- }
+/* Move the contents of register SOURCE into register DEST. */
- 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;
- }
+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, GPR_SIZE == 32 ? "addu" : "daddu", "d,v,t",
+ dest, source, 0);
+}
- 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;
+/* 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:
- case M_DINS:
- {
- /* Use unsigned arithmetic. */
- addressT pos;
- addressT size;
+ Global symbol Local sybmol
+ ------------- ------------
+ lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
+ ... ...
+ addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
- 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;
- }
+ 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 (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;
- }
+static void
+load_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
- 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;
+ global = *local;
+ global.X_add_number = 0;
- 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)
- {
- 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;
- }
+ 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 ();
+}
- start_noreorder ();
- if (mips_trap)
- {
- macro_build (NULL, "teq", TRAP_FMT, treg, ZERO, 7);
- macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", sreg, treg);
- }
- 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 ();
- }
- 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)
- {
- expr1.X_add_number = 1;
- load_register (AT, &expr1, dbl);
- macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
- }
- else
- {
- expr1.X_add_number = 0x80000000;
- macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
- }
- if (mips_trap)
- {
- 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
- {
- 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 ();
+static void
+add_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
- macro_build (NULL, "break", BRK_FMT, 6);
- }
- if (mips_opts.micromips)
- micromips_add_label ();
- macro_build (NULL, s, MFHL_FMT, dreg);
- break;
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
- 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 (strcmp (s2, "mflo") == 0)
- {
- macro_build (NULL, dbl ? "dneg" : "neg", "d,w", dreg, sreg);
- }
- else
- move_register (dreg, ZERO);
- break;
- }
+ 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 ();
+}
- 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 void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+ expressionS global;
+ int hold_mips_optimize;
- 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);
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
- /* 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;
+ 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 ();
- 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. */
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
- if (dbl && HAVE_32BIT_GPRS)
- as_warn (_("dla used to load 32-bit register"));
+/* 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 (!dbl && HAVE_64BIT_OBJECTS)
- as_warn (_("la used to load 64-bit address"));
+ Code like this is produced in the noreorder mode:
- if (small_offset_p (0, align, 16))
- {
- macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", treg, breg,
- -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
- break;
- }
+ BRNEG <args>, 1f
+ nop
+ b <sym>
+ delay slot (executed only if branch taken)
+ 1:
- if (mips_opts.at && (treg == breg))
- {
- tempreg = AT;
- used_at = 1;
- }
- else
- {
- tempreg = treg;
- }
+ or, if CALL is set:
- if (offset_expr.X_op != O_symbol
- && offset_expr.X_op != O_constant)
- {
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
- }
+ BRNEG <args>, 1f
+ nop
+ bal <sym>
+ delay slot (executed only if branch taken)
+ 1:
- 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.
+ In the reorder mode the delay slot would be filled with a nop anyway,
+ so code produced is simply:
- 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
+ BR <args>, <sym>
+ nop
- 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)
+ This function is used when producing code for the microMIPS ASE that
+ does not implement branch likely instructions in hardware. */
- 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 ();
- }
+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;
- 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 (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 ();
- }
- }
- else if (!mips_big_got && !HAVE_NEWABI)
- {
- int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+ 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 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)
+ /* 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 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.
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+ the condition code tested. EP specifies the branch target. */
- 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.
- */
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+ const int call = 0;
+ const char *brneg;
+ const char *br;
- 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;
+ 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_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 if (!mips_big_got && HAVE_NEWABI)
- {
- int add_breg_early = 0;
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+ the register tested. EP specifies the branch target. */
- /* 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)
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+ const char *brneg = NULL;
+ const char *br;
+ int call = 0;
- 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>
+ switch (type)
+ {
+ 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 ();
+ }
+ 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 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
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+ TREG as the registers tested. EP specifies the branch target. */
- (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
- local symbols, even though it introduces an additional
- instruction. */
+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;
- if (offset_expr.X_add_number)
- {
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
+ 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);
+}
- 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);
+/* Return the high part that should be loaded in order to make the low
+ part of VALUE accessible using an offset of OFFBITS bits. */
- 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 offsetT
+offset_high_part (offsetT value, unsigned int offbits)
+{
+ offsetT bias;
+ addressT low_mask;
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- dreg, dreg, AT);
+ if (offbits == 0)
+ return value;
+ bias = 1 << (offbits - 1);
+ low_mask = bias * 2 - 1;
+ return (value + bias) & ~low_mask;
+}
- used_at = 1;
- }
- else
- as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+/* 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. */
- relax_switch ();
- offset_expr.X_add_number = expr1.X_add_number;
+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;
- 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;
+ /* 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;
+}
- /* 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)
+/*
+ * 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)
+{
+ const struct mips_operand_array *operands;
+ unsigned int breg, i;
+ 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;
+ unsigned int op[MAX_OPERANDS];
- 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)
+ gas_assert (! mips_opts.mips16);
- 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
- */
+ operands = insn_operands (ip);
+ for (i = 0; i < MAX_OPERANDS; i++)
+ if (operands->operand[i])
+ op[i] = insn_extract_operand (ip, operands->operand[i]);
+ else
+ op[i] = -1;
- 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;
- }
-
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
-
- used_at = 1;
- }
- offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
- relax_switch ();
-
- if (gpdelay)
- {
- /* This is needed because this instruction uses $gp, but
- the first instruction on the main stream does not. */
- macro_build (NULL, "nop", "");
- }
-
- 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;
- }
-
- 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;
-
- /* 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>
+ mask = ip->insn_mo->mask;
- 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
+ label_expr.X_op = O_constant;
+ label_expr.X_op_symbol = NULL;
+ label_expr.X_add_symbol = NULL;
+ label_expr.X_add_number = 0;
- 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. */
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+ align = 1;
- relax_start (offset_expr.X_add_symbol);
+ switch (mask)
+ {
+ case M_DABS:
+ dbl = 1;
+ case M_ABS:
+ /* bgez $a0,1f
+ move v0,$a0
+ sub v0,$zero,$a0
+ 1:
+ */
- expr1.X_add_number = offset_expr.X_add_number;
- offset_expr.X_add_number = 0;
+ start_noreorder ();
- 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 (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bgez", "s,p", op[1]);
+ if (op[0] == op[1])
+ macro_build (NULL, "nop", "");
+ else
+ move_register (op[0], op[1]);
+ macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", op[0], 0, op[1]);
+ if (mips_opts.micromips)
+ micromips_add_label ();
- 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;
- }
+ end_noreorder ();
+ break;
- load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+ 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_add_number >= -0x200
+ && imm_expr.X_add_number < 0x200)
+ {
+ macro_build (NULL, s, "t,r,.", op[0], op[1],
+ (int) 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_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, s, "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ break;
+ }
+ do_addi_i:
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+ break;
- used_at = 1;
- }
+ 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_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ if (mask != M_NOR_I)
+ macro_build (&imm_expr, s, "t,r,i", op[0], op[1], BFD_RELOC_LO16);
else
- as_bad (_("PIC code offset overflow (max 32 signed bits)"));
-
- 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;
+ macro_build (&imm_expr, "ori", "t,r,i",
+ op[0], op[1], BFD_RELOC_LO16);
+ macro_build (NULL, "nor", "d,v,t", op[0], op[0], 0);
}
- relax_end ();
+ break;
}
- else
- abort ();
- if (breg != 0)
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", treg, tempreg, breg);
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
break;
- case M_MSGSND:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", (treg << 16) | 0x01);
- break;
-
- case M_MSGLD:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", 0x02);
+ 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", op[0], op[0], op[1]);
+ break;
+ case 1:
+ case 3:
+ macro_build (NULL, "balign", "t,s,2", op[0], op[1],
+ (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;
- case M_MSGLD_T:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", (treg << 16) | 0x02);
+ 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;
- case M_MSGWAIT:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", 3);
+ case M_BEQ_I:
+ case M_BEQL_I:
+ case M_BNE_I:
+ case M_BNEL_I:
+ if (imm_expr.X_add_number == 0)
+ op[1] = 0;
+ else
+ {
+ op[1] = AT;
+ used_at = 1;
+ load_register (op[1], &imm_expr, GPR_SIZE == 64);
+ }
+ /* Fall through. */
+ case M_BEQL:
+ case M_BNEL:
+ macro_build_branch_rsrt (mask, &offset_expr, op[0], op[1]);
break;
- case M_MSGWAIT_T:
- gas_assert (!mips_opts.micromips);
- macro_build (NULL, "c2", "C", (treg << 16) | 0x03);
+ case M_BGEL:
+ likely = 1;
+ case M_BGE:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
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_BGEZL:
+ case M_BGEZALL:
+ case M_BGTZL:
+ case M_BLEZL:
+ case M_BLTZL:
+ case M_BLTZALL:
+ macro_build_branch_rs (mask, &offset_expr, op[0]);
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:
- gas_assert (mips_opts.micromips);
- if (mips_opts.insn32)
+ case M_BGTL_I:
+ likely = 1;
+ case M_BGT_I:
+ /* Check for > max integer. */
+ if (imm_expr.X_add_number >= GPR_SMAX)
{
- as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
+ do_false:
+ /* Result is always false. */
+ if (! likely)
+ macro_build (NULL, "nop", "");
+ else
+ macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
break;
}
- jals = 1;
- goto jal;
- case M_JAL_1:
- dreg = RA;
- /* Fall through. */
- case M_JAL_2:
- jal:
- if (mips_pic == NO_PIC)
+ ++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_add_number == 0)
{
- 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);
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
+ &offset_expr, op[0]);
+ break;
}
- else
+ if (imm_expr.X_add_number == 1)
{
- int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
- && mips_cprestore_offset >= 0);
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
+ &offset_expr, op[0]);
+ break;
+ }
+ if (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 (op[0], 0);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ break;
- if (sreg != PIC_CALL_REG)
- as_warn (_("MIPS PIC call to register other than $25"));
+ case M_BGEUL:
+ likely = 1;
+ case M_BGEU:
+ if (op[1] == 0)
+ goto do_true;
+ else if (op[0] == 0)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, ZERO, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- 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);
- }
- }
+ case M_BGTUL_I:
+ likely = 1;
+ case M_BGTU_I:
+ if (op[0] == 0
+ || (GPR_SIZE == 32
+ && imm_expr.X_add_number == -1))
+ goto do_false;
+ ++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_add_number == 0)
+ goto do_true;
+ else if (imm_expr.X_add_number == 1)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, op[0], ZERO);
+ else
+ {
+ used_at = 1;
+ set_at (op[0], 1);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
}
+ break;
+ case M_BGTL:
+ likely = 1;
+ case M_BGT:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
break;
- case M_JALS_A:
- gas_assert (mips_opts.micromips);
- if (mips_opts.insn32)
+ case M_BGTUL:
+ likely = 1;
+ case M_BGTU:
+ if (op[1] == 0)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, op[0], ZERO);
+ else if (op[0] == 0)
+ goto do_false;
+ else
{
- as_bad (_("Opcode not supported in the `insn32' mode `%s'"), str);
- break;
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
}
- 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)
+ break;
+
+ case M_BLEL:
+ likely = 1;
+ case M_BLE:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[1]);
+ else
{
- /* 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)
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- 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_BLEL_I:
+ likely = 1;
+ case M_BLE_I:
+ if (imm_expr.X_add_number >= GPR_SMAX)
+ goto do_true;
+ ++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_add_number == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
+ else if (imm_expr.X_add_number == 1)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
+ else
+ {
+ used_at = 1;
+ set_at (op[0], 0);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ 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_BLEUL:
+ likely = 1;
+ case M_BLEU:
+ if (op[1] == 0)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, op[0], ZERO);
+ else if (op[0] == 0)
+ goto do_true;
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- 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);
+ case M_BLEUL_I:
+ likely = 1;
+ case M_BLEU_I:
+ if (op[0] == 0
+ || (GPR_SIZE == 32
+ && imm_expr.X_add_number == -1))
+ goto do_true;
+ ++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_add_number == 0)
+ goto do_false;
+ else if (imm_expr.X_add_number == 1)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, op[0], ZERO);
+ else
+ {
+ used_at = 1;
+ set_at (op[0], 1);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
- 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);
- }
- }
+ case M_BLTL:
+ likely = 1;
+ case M_BLT:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
}
- else if (mips_pic == VXWORKS_PIC)
- as_bad (_("Non-PIC jump used in PIC library"));
+ break;
+
+ case M_BLTUL:
+ likely = 1;
+ case M_BLTU:
+ if (op[1] == 0)
+ goto do_false;
+ else if (op[0] == 0)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, ZERO, op[1]);
else
- abort ();
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ 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:
+ if (op[2] == 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;
+ }
+
+ start_noreorder ();
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", TRAP_FMT, op[2], ZERO, 7);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", op[1], op[2]);
+ }
+ 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", op[2], ZERO);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", op[1], op[2]);
+ macro_build (NULL, "break", BRK_FMT, 7);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ 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", op[2], AT);
+ if (dbl)
+ {
+ expr1.X_add_number = 1;
+ load_register (AT, &expr1, dbl);
+ macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
+ }
+ else
+ {
+ expr1.X_add_number = 0x80000000;
+ macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
+ }
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", TRAP_FMT, op[1], 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
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", op[1], 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, op[0]);
break;
- 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";
- fmt = "t,o(b)";
- goto ld;
- case M_LBU_AB:
- s = "lbu";
- 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";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- 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";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- 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";
- 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";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- 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";
- fmt = MEM12_FMT;
- offbits = (mips_opts.micromips ? 12 : 16);
- 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;
-
- ld_st:
- tempreg = AT;
- ld_noat:
- if (small_offset_p (0, align, 16))
- {
- /* 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;
- }
-
- if (tempreg == AT)
- used_at = 1;
-
- if (offset_expr.X_op != O_constant
- && offset_expr.X_op != O_symbol)
- {
- as_bad (_("Expression too complex"));
- offset_expr.X_op = O_constant;
- }
-
- if (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
- {
- char value [32];
-
- sprintf_vma (value, offset_expr.X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
- }
-
- /* A constant expression in PIC code can be handled just as it
- is in non PIC code. */
- if (offset_expr.X_op == O_constant)
- {
- 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
- macro_build (NULL, s, fmt, treg,
- (int) offset_expr.X_add_number, tempreg);
- }
- 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. */
- 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);
- }
- 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.
-
- 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)
-
- 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)
-
- 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 ();
- }
-
- 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);
- }
-
- if (mips_relax.sequence)
- relax_end ();
- 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;
-
- /* 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 NewABI, we want
- lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
- <op> $treg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
-
- 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;
-
- /* 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 ();
-
- 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);
-
- 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 ();
-
- 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_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;
-
- case M_LI:
- case M_LI_S:
- load_register (treg, &imm_expr, 0);
- break;
-
- case M_DLI:
- load_register (treg, &imm_expr, 1);
- break;
-
- case M_LI_SS:
- if (imm_expr.X_op == O_constant)
- {
- 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;
- }
-
- 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;
-
- 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;
- }
-
- /* 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;
- }
-
- /* 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;
-
- 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;
- }
-
- 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);
- }
-
- breg = AT;
- offset_reloc[0] = BFD_RELOC_LO16;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
- }
- align = 8;
- /* Fall through */
-
- 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;
-
- 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;
-
- 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)
- {
- s = "ld";
- goto ld;
- }
- s = "lw";
- goto ldd_std;
-
- case M_SD_AB:
- fmt = "t,o(b)";
- if (HAVE_64BIT_GPRS)
- {
- s = "sd";
- goto ld_st;
- }
- 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 (small_offset_p (0, align, 16))
- {
- ep = &offset_expr;
- if (!small_offset_p (4, align, 16))
- {
- 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 (strcmp (s, "lw") == 0 && treg == breg)
- {
- 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);
- }
- else
- {
- 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;
- }
-
- 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 (HAVE_32BIT_ADDRESSES
- && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
- {
- char value [32];
-
- sprintf_vma (value, offset_expr.X_add_number);
- as_bad (_("Number (0x%s) larger than 32 bits"), value);
- }
-
- 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)
- {
- tempreg = mips_gp_register;
- }
- else
- {
- macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
- AT, breg, mips_gp_register);
- tempreg = AT;
- used_at = 1;
- }
-
- /* 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;
- }
- 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);
-
- /* 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)
- {
- int gpdelay;
-
- /* 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;
-
- /* 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;
-
- 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;
-
- /* 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 ();
-
- 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;
-
- /* 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?
-
- If an itbl is provided to interpret cop instructions,
- this knowledge can be encoded in the itbl spec. */
-
- 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;
-
- case M_MOVE:
- move_register (dreg, sreg);
- break;
-
- 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;
-
- 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;
-
- 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 M_DMULO_I:
- dbl = 1;
- case M_MULO_I:
- imm = 1;
- goto do_mulo;
-
- 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 M_DMULOU_I:
- dbl = 1;
- case M_MULOU_I:
- imm = 1;
- goto do_mulou;
-
- 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 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 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 M_DROL_I:
- {
- unsigned int rot;
- char *l;
- char *rr;
-
- 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 M_ROL_I:
- {
- unsigned int rot;
-
- 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;
-
- 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 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;
-
- case M_DROR_I:
- {
- unsigned int rot;
- char *l;
- char *rr;
-
- 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 M_ROR_I:
- {
- unsigned int rot;
-
- 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;
-
- 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 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 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 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 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;
-
- 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;
-
- 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 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;
-
- 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 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;
-
- 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;
-
- 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;
-
- 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:
+ 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 = "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:
+ s = "ddiv";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DDIVU_3I:
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)
+ 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_add_number == 0)
{
- imm_expr.X_add_number = -imm_expr.X_add_number;
- macro_build (&imm_expr, s, "t,r,j", dreg, sreg, BFD_RELOC_LO16);
+ 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;
}
- do_subi_i:
- used_at = 1;
- load_register (AT, &imm_expr, dbl);
- macro_build (NULL, s2, "d,v,t", dreg, sreg, AT);
- break;
-
- 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 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 */
-
- /*
- * 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_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;
-
- uld_st:
- large_offset = !small_offset_p (off, align, offbits);
- ep = &offset_expr;
- expr1.X_add_number = 0;
- if (large_offset)
+ if (imm_expr.X_add_number == 1)
{
- 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]);
+ if (strcmp (s2, "mflo") == 0)
+ move_register (op[0], op[1]);
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;
+ move_register (op[0], ZERO);
+ break;
}
- else if (!ust && treg == breg)
+ if (imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u')
{
- used_at = 1;
- tempreg = AT;
+ if (strcmp (s2, "mflo") == 0)
+ macro_build (NULL, dbl ? "dneg" : "neg", "d,w", op[0], op[1]);
+ else
+ move_register (op[0], ZERO);
+ break;
}
- else
- tempreg = treg;
- if (off == 1)
- goto ulh_sh;
-
- 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);
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s, "z,s,t", op[1], AT);
+ macro_build (NULL, s2, MFHL_FMT, op[0]);
+ break;
- 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);
+ 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, op[2], ZERO, 7);
+ macro_build (NULL, s, "z,s,t", op[1], op[2]);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+ }
else
- macro_build (ep, s2, "t,o(b)", tempreg, -1,
- offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", op[2], ZERO);
+ macro_build (NULL, s, "z,s,t", op[1], op[2]);
- /* 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);
+ /* 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, op[0]);
break;
- 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);
+ 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. */
- /* 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);
+ breg = op[2];
+ if (dbl && GPR_SIZE == 32)
+ as_warn (_("dla used to load 32-bit register"));
- 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);
+ if (!dbl && HAVE_64BIT_OBJECTS)
+ as_warn (_("la used to load 64-bit address"));
- /* For M_USH_A re-retrieve the LSB. */
- if (ust && large_offset)
+ if (small_offset_p (0, align, 16))
{
- 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);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", op[0], breg,
+ -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ break;
}
- /* For ULH and M_USH_A OR the LSB in. */
- if (!ust || large_offset)
+
+ if (mips_opts.at && (op[0] == breg))
{
- tempreg = !large_offset ? AT : treg;
- macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
- macro_build (NULL, "or", "d,v,t", treg, treg, AT);
+ tempreg = AT;
+ used_at = 1;
}
- 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. */
+ else
+ tempreg = op[0];
-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;
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
- mask = ip->insn_mo->mask;
+ 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.
- xreg = MIPS16_EXTRACT_OPERAND (RX, *ip);
- yreg = MIPS16_EXTRACT_OPERAND (RY, *ip);
- zreg = MIPS16_EXTRACT_OPERAND (RZ, *ip);
+ 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
- expr1.X_op = O_constant;
- expr1.X_op_symbol = NULL;
- expr1.X_add_symbol = NULL;
- expr1.X_add_number = 1;
+ 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)
- dbl = 0;
+ 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 ();
+ }
- switch (mask)
- {
- default:
- abort ();
+ 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);
+ }
- 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);
+ 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 ();
+ }
+ }
+ else if (!mips_big_got && !HAVE_NEWABI)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
- /* 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;
+ /* 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)
- 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;
+ 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.
- 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 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.
+ */
- 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;
+ 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;
- 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;
+ 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 == op[0])
+ {
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ add_got_offset_hilo (tempreg, &offset_expr, AT);
+ used_at = 1;
+ }
+ }
+ else if (!mips_big_got && HAVE_NEWABI)
+ {
+ int add_breg_early = 0;
- 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 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_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 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>
- do_reverse_branch:
- tmp = xreg;
- xreg = yreg;
- yreg = tmp;
+ 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
- do_branch:
- macro_build (NULL, s, "x,y", xreg, yreg);
- macro_build (&offset_expr, s2, "p");
- break;
+ (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+ local symbols, even though it introduces an additional
+ instruction. */
- 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 (offset_expr.X_add_number)
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
- do_addone_branch_i:
- if (imm_expr.X_op != O_constant)
- as_bad (_("Unsupported large constant"));
- ++imm_expr.X_add_number;
+ 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);
- do_branch_i:
- macro_build (&imm_expr, s, s3, xreg);
- macro_build (&offset_expr, s2, "p");
- break;
+ 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))
+ {
+ unsigned int dreg;
- 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 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 != op[0])
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ dreg = op[0];
+ add_breg_early = 1;
+ }
-/* UDI immediates. */
-struct mips_immed {
- char type;
- unsigned int shift;
- unsigned long mask;
- const char * desc;
-};
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ dreg, dreg, AT);
-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 }
-};
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
-/* 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);
-}
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
-/* 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. */
+ 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",
+ op[0], tempreg, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ 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;
-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, regno2;
- 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;
+ /* 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)
- insn_error = NULL;
+ 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 (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;
+ 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
+ */
- /* 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;
+ 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
+ {
+ unsigned int dreg;
- /* Make a copy of the instruction so that we can fiddle with it. */
- name = alloca (end + 1);
- memcpy (name, str, end);
- name[end] = '\0';
+ /* 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 != op[0])
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ dreg = op[0];
+ }
- for (;;)
- {
- insn = (struct mips_opcode *) hash_find (hash, name);
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
+ relax_switch ();
+
+ if (gpdelay)
+ {
+ /* This is needed because this instruction uses $gp, but
+ the first instruction on the main stream does not. */
+ macro_build (NULL, "nop", "");
+ }
+
+ 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 == op[0])
+ {
+ /* 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",
+ op[0], AT, breg);
+ tempreg = op[0];
+ /* We set breg to 0 because we have arranged to add
+ it in in both cases. */
+ breg = 0;
+ }
+
+ 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;
+
+ /* 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 (insn != NULL || !mips_opts.micromips)
- break;
- if (forced_insn_length)
- break;
+ 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>
- /* 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;
- }
+ 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
- /* 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;
+ 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. */
- gas_assert (strcmp (insn->name, name) == 0);
+ relax_start (offset_expr.X_add_symbol);
- 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;
- }
- if (!ok || !size_ok || delay_slot_ok != need_delay_slot_ok)
- {
- static char buf[256];
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
- if (insn + 1 < past && strcmp (insn->name, insn[1].name) == 0)
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
{
- ++insn;
- continue;
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
}
- if (wrong_delay_slot_insns && need_delay_slot_ok)
+ 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)
+ ;
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
{
- gas_assert (firstinsn);
- need_delay_slot_ok = FALSE;
- past = insn + 1;
- insn = firstinsn;
- continue;
+ 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))
+ {
+ unsigned int dreg;
- if (insn_error)
- return;
+ /* 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 != op[0])
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ dreg = op[0];
+ add_breg_early = 1;
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
- 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"));
+ used_at = 1;
+ }
else
- sprintf (buf, _("Unrecognized %u-bit version of microMIPS opcode"),
- 8 * forced_insn_length);
- insn_error = buf;
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
- return;
+ 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",
+ op[0], tempreg, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ relax_end ();
}
+ else
+ abort ();
- 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;
-
- create_insn (ip, insn);
- insn_error = NULL;
- argnum = 1;
- lastregno = 0xffffffff;
- for (args = insn->args;; ++args)
- {
- int is_mdmx;
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", op[0], tempreg, breg);
+ break;
- s += strspn (s, " \t");
- is_mdmx = 0;
- switch (*args)
- {
- case '\0': /* end of args */
- if (*s == '\0')
- return;
- break;
+ case M_MSGSND:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x01);
+ 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 M_MSGLD:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 0x02);
+ break;
- 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 M_MSGLD_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x02);
+ break;
- 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 M_MSGWAIT:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 3);
+ break;
- 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 M_MSGWAIT_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x03);
+ break;
- 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 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");
+ break;
- 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;
+ /* 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:
+ op[1] = op[0];
+ op[0] = RA;
+ /* Fall through. */
+ case M_JALS_2:
+ 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:
+ op[1] = op[0];
+ op[0] = RA;
+ /* Fall through. */
+ case M_JAL_2:
+ jal:
+ if (mips_pic == NO_PIC)
+ {
+ s = jals ? "jalrs" : "jalr";
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && op[0] == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, s, "mj", op[1]);
+ else
+ macro_build (NULL, s, JALR_FMT, op[0], op[1]);
+ }
+ else
+ {
+ int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
+ && mips_cprestore_offset >= 0);
- 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;
+ if (op[1] != PIC_CALL_REG)
+ as_warn (_("MIPS PIC call to register other than $25"));
- 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')
+ s = ((mips_opts.micromips
+ && !mips_opts.insn32
+ && (!mips_opts.noreorder || cprestore))
+ ? "jalrs" : "jalr");
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && op[0] == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, s, "mj", op[1]);
+ else
+ macro_build (NULL, s, JALR_FMT, op[0], op[1]);
+ if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
+ {
+ if (mips_cprestore_offset < 0)
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ else
{
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, DSPACC_S, *ip, regno);
- continue;
+ 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
- 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;
+ break;
- 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 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)
- 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)
+ 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)
{
- as_bad (_("DSP immediate not in range %ld..%ld (%ld)"),
- (long) min_range, (long) max_range,
- (long) imm_expr.X_add_number);
+ 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 ();
}
- 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;
-
- 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;
-
- 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')
+ else
{
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, MTACC_T, *ip, regno);
- continue;
+ 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 ();
}
- else
- as_bad (_("Invalid dsp/smartmips acc register"));
- break;
- 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')
+ macro_build_jalr (&offset_expr, 0);
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (!mips_big_got)
{
- regno = s[3] - '0';
- s += 4;
- INSERT_OPERAND (0, MTACC_D, *ip, regno);
- continue;
+ 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
- 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;
+ int gpdelay;
- case 'V':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
- continue;
+ 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", "");
}
- 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;
+ 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);
- case '+': /* Opcode extension character. */
- switch (*++args)
+ if (mips_cprestore_offset < 0)
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ else
{
- 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)
+ if (!mips_frame_reg_valid)
{
- as_bad (_("Improper position (%lu)"),
- (unsigned long) imm_expr.X_add_number);
- imm_expr.X_add_number = limlo;
+ as_warn (_("no .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
}
- 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)
+ if (!mips_cprestore_valid)
{
- 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;
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
}
- INSERT_OPERAND (mips_opts.micromips, INSMSB, *ip,
- lastpos + imm_expr.X_add_number - 1);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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 ();
+
+ break;
+
+ 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";
+ fmt = "\\,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_ASET_AB:
+ s = "aset";
+ fmt = "\\,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_LB_AB:
+ s = "lb";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LBU_AB:
+ s = "lbu";
+ 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";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ 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 = "+7,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";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ 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;
- 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;
+ ld:
+ /* We don't want to use $0 as tempreg. */
+ if (op[2] == op[0] + lp || op[0] + lp == ZERO)
+ goto ld_st;
+ else
+ tempreg = op[0] + lp;
+ goto ld_noat;
- 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 M_SB_AB:
+ s = "sb";
+ 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";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ 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 = "+7,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";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ 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;
- 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;
+ ld_st:
+ tempreg = AT;
+ ld_noat:
+ breg = op[2];
+ if (small_offset_p (0, align, 16))
+ {
+ /* 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, op[0], -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, op[0], breg);
+ else
+ macro_build (NULL, s, fmt, op[0],
+ (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, op[0], tempreg);
+ else
+ macro_build (NULL, s, fmt, op[0], 0, tempreg);
+ }
+ break;
+ }
- 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;
+ if (tempreg == AT)
+ used_at = 1;
- 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;
+ if (offset_expr.X_op != O_constant
+ && offset_expr.X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
- 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;
- }
- lastpos = imm_expr.X_add_number;
- INSERT_OPERAND (0, CINSPOS, *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 '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;
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
+ }
- case 's':
- /* cins32 and exts32 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
- || (unsigned long) imm_expr.X_add_number + lastpos > 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;
+ /* A constant expression in PIC code can be handled just as it
+ is in non PIC code. */
+ if (offset_expr.X_op == O_constant)
+ {
+ 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;
- case 'S':
- /* cins/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
- || (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;
+ 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, op[0], tempreg);
+ }
+ else if (offbits == 16)
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
+ else
+ macro_build (NULL, s, fmt, op[0],
+ (int) offset_expr.X_add_number, tempreg);
+ }
+ 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. */
+ 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, op[0], tempreg);
+ else
+ macro_build (NULL, s, fmt, op[0], 0, tempreg);
+ }
+ 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> op[0],<sym>($gp) (BFD_RELOC_GPREL16)
+ Otherwise, if there is no base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we always use the latter form.
- 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;
+ If we have a base register, and this is a reference to a
+ GP relative symbol, we want
+ addu $tempreg,$breg,$gp
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+ With a constant we always use the latter case.
- 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;
+ 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> op[0],<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> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
- 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;
+ 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> op[0],<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> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
- 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)
+ 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)
{
- as_bad (_("Offset not 16 bytes alignment (%ld)"),
- (long) imm_expr.X_add_number);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_GPREL16, mips_gp_register);
}
- /* 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;
-
- case 'z':
- gas_assert (!mips_opts.micromips);
- if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no))
- break;
- if (regno == AT && mips_opts.at)
+ else
{
- 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);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_GPREL16, tempreg);
}
- INSERT_OPERAND (0, RZ, *ip, regno);
- continue;
-
- case 'Z':
- gas_assert (!mips_opts.micromips);
- if (!reg_lookup (&s, RTYPE_FPU, ®no))
- break;
- INSERT_OPERAND (0, FZ, *ip, regno);
- continue;
-
- case 'i':
- goto jump;
-
- case 'j':
- {
- int shift = 8;
- size_t i;
- bfd_reloc_code_real_type r[3];
-
- /* 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 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, r, 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;
-
- 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;
+ relax_switch ();
}
- break;
-
- case '.': /* 10-bit offset. */
- gas_assert (mips_opts.micromips);
- case '~': /* 12-bit offset. */
- {
- int shift = *args == '.' ? 9 : 11;
- size_t i;
- bfd_reloc_code_real_type r[3];
-
- /* 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 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, r, 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 '<': /* 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 '>': /* 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 '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)
+ if (used_at == 0 && mips_opts.at)
{
- 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;
+ 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, op[0], BFD_RELOC_LO16,
+ tempreg);
+ used_at = 1;
}
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
-
- 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 '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 '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 '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 '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;
-
- 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 'G': /* Coprocessor destination register. */
- {
- unsigned long opcode = ip->insn_opcode;
- unsigned long mask;
- unsigned int types;
- int cop0;
-
- 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 '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;
-
- 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;
+ 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, op[0],
+ BFD_RELOC_LO16, tempreg);
}
- 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;
- }
+ if (mips_relax.sequence)
+ relax_end ();
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 'Q': /* MDMX vector, element sel, or const. */
- if (s[0] != '$')
+ if (breg == 0)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
{
- /* 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;
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_GPREL16,
+ mips_gp_register);
+ relax_switch ();
}
- /* Not MDMX Immediate. Fall through. */
- case 'X': /* MDMX destination register. */
- case 'Y': /* MDMX source register. */
- case 'Z': /* MDMX target register. */
- is_mdmx = !(insn->membership & INSN_5400);
- 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))
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, s, fmt, op[0],
+ 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))
{
- 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 ((ip->insn_mo->membership & INSN_5400)
- && strcmp (insn->name, "rzu.ob") == 0)
- as_bad (_("Operand %d of `%s' must be an immediate"),
- argnum, ip->insn_mo->name);
-
- 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_mo->membership & INSN_5400)
- && (strcmp (insn->name, "sll.ob") == 0
- || strcmp (insn->name, "srl.ob") == 0))
- as_bad (_("Operand %d of `%s' must be scalar"),
- argnum, ip->insn_mo->name);
-
- 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;
+ 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, op[0],
+ 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, op[0],
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (!mips_big_got)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
- switch (*args++)
- {
- case 'V':
- INSERT_OPERAND (mips_opts.micromips, FS, *ip, lastregno);
- continue;
-
- case 'W':
- INSERT_OPERAND (mips_opts.micromips, FT, *ip, lastregno);
- continue;
- }
- break;
+ /* If this is a reference to an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> op[0],0($tempreg)
- 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;
+ For NewABI, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
- case 'A':
- my_getSmallExpression (&offset_expr, offset_reloc, s);
- if (offset_expr.X_op == O_register)
- {
- /* 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
- {
- normalize_address_expr (&offset_expr);
- s = expr_end;
- }
- continue;
+ 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, op[0],
+ 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, op[0], BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_big_got && !HAVE_NEWABI)
+ {
+ int gpdelay;
- 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;
- }
-
- gas_assert (length == (unsigned) (f64 ? 8 : 4));
-
- 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;
+ /* 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> op[0],0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> op[0],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 'i': /* 16-bit unsigned immediate. */
- case 'j': /* 16-bit signed immediate. */
- *offset_reloc = BFD_RELOC_LO16;
- if (my_getSmallExpression (&offset_expr, offset_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;
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, op[0], 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> op[0],<ofst>($tempreg)
+ Otherwise, for local symbols, we want:
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> op[0],<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, op[0], BFD_RELOC_LO16, tempreg);
- if (offset_expr.X_op != O_constant
- || offset_expr.X_add_number < minval
- || offset_expr.X_add_number > maxval)
- {
- if (more)
- break;
- if (offset_expr.X_op == O_constant
- || offset_expr.X_op == O_big)
- as_bad (_("Expression out of range"));
- }
- }
- s = expr_end;
- continue;
+ 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, op[0],
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+ relax_end ();
+ }
+ else
+ abort ();
- case 'o': /* 16-bit offset. */
- offset_reloc[0] = BFD_RELOC_LO16;
- offset_reloc[1] = BFD_RELOC_UNUSED;
- offset_reloc[2] = BFD_RELOC_UNUSED;
+ 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_JRADDIUSP:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ start_noreorder ();
+ macro_build (NULL, "jr", "s", RA);
+ expr1.X_add_number = op[0] << 2;
+ macro_build (&expr1, "addiu", "t,r,j", SP, SP, BFD_RELOC_LO16);
+ end_noreorder ();
+ 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_JRC:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ macro_build (NULL, "jr", "s", op[0]);
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ break;
- s = expr_end;
- continue;
+ case M_LI:
+ case M_LI_S:
+ load_register (op[0], &imm_expr, 0);
+ break;
- case 'p': /* PC-relative offset. */
- *offset_reloc = BFD_RELOC_16_PCREL_S2;
- my_getExpression (&offset_expr, s);
- s = expr_end;
- continue;
+ case M_DLI:
+ load_register (op[0], &imm_expr, 1);
+ break;
- case 'u': /* Upper 16 bits. */
- *offset_reloc = BFD_RELOC_LO16;
- if (my_getSmallExpression (&offset_expr, offset_reloc, s) == 0
- && offset_expr.X_op == O_constant
- && (offset_expr.X_add_number < 0
- || offset_expr.X_add_number >= 0x10000))
- as_bad (_("lui expression (%lu) not in range 0..65535"),
- (unsigned long) offset_expr.X_add_number);
- s = expr_end;
- continue;
+ case M_LI_SS:
+ if (imm_expr.X_op == O_constant)
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, op[0]);
+ break;
+ }
+ else
+ {
+ gas_assert (imm_expr.X_op == O_absent
+ && 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)", op[0],
+ BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+ break;
+ }
- case 'a': /* 26-bit address. */
- jump:
- *offset_reloc = BFD_RELOC_MIPS_JMP;
- my_getExpression (&offset_expr, s);
- s = expr_end;
- continue;
+ 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)
+ {
+ if (GPR_SIZE == 64)
+ load_register (op[0], &imm_expr, 1);
+ else
+ {
+ int hreg, lreg;
- 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);
+ if (target_big_endian)
+ {
+ hreg = op[0];
+ lreg = op[0] + 1;
+ }
else
- INSERT_OPERAND (mips_opts.micromips, CCC, *ip, regno);
- continue;
+ {
+ hreg = op[0] + 1;
+ lreg = op[0];
+ }
- case 'H':
- if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
- s += 2;
- if (ISDIGIT (*s))
+ if (hreg <= 31)
+ load_register (hreg, &imm_expr, 0);
+ if (lreg <= 31)
{
- c = 0;
- do
+ if (offset_expr.X_op == O_absent)
+ move_register (lreg, 0);
+ else
{
- c *= 10;
- c += *s - '0';
- ++s;
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (lreg, &offset_expr, 0);
}
- while (ISDIGIT (*s));
}
- else
- c = 8; /* Invalid sel value. */
+ }
+ break;
+ }
+ gas_assert (imm_expr.X_op == O_absent);
- if (c > 7)
- as_bad (_("Invalid coprocessor sub-selection value (0-7)"));
- INSERT_OPERAND (mips_opts.micromips, SEL, *ip, c);
- continue;
+ /* 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;
+ }
- case 'e':
- gas_assert (!mips_opts.micromips);
- /* Must be at least one digit. */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
+ /* Now we load the register(s). */
+ if (GPR_SIZE == 64)
+ {
+ used_at = 1;
+ macro_build (&offset_expr, "ld", "t,o(b)", op[0],
+ BFD_RELOC_LO16, AT);
+ }
+ else
+ {
+ used_at = 1;
+ macro_build (&offset_expr, "lw", "t,o(b)", op[0],
+ BFD_RELOC_LO16, AT);
+ if (op[0] != 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)",
+ op[0] + 1, BFD_RELOC_LO16, AT);
+ }
+ }
+ break;
- if ((unsigned long) imm_expr.X_add_number
- > (unsigned long) OP_MASK_VECBYTE)
+ 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)
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, FPR_SIZE == 64);
+ if (FPR_SIZE == 64)
+ {
+ gas_assert (GPR_SIZE == 64);
+ macro_build (NULL, "dmtc1", "t,S", AT, op[0]);
+ }
+ else
+ {
+ macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1);
+ if (offset_expr.X_op == O_absent)
+ macro_build (NULL, "mtc1", "t,G", 0, op[0]);
+ else
{
- as_bad (_("bad byte vector index (%ld)"),
- (long) imm_expr.X_add_number);
- imm_expr.X_add_number = 0;
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (AT, &offset_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, op[0]);
}
+ }
+ break;
+ }
- INSERT_OPERAND (0, VECBYTE, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ gas_assert (imm_expr.X_op == O_absent
+ && 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)
+ {
+ op[2] = 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);
+ }
- case '%':
- gas_assert (!mips_opts.micromips);
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
+ op[2] = AT;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ }
+ align = 8;
+ /* Fall through */
- 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;
- }
+ 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;
- INSERT_OPERAND (0, VECALIGN, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ 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;
- 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;
+ case M_LQ_AB:
+ fmt = "t,o(b)";
+ s = "lq";
+ goto ld;
- case 'a':
- case 'b':
- case 'c':
- case 'd':
- case 'e':
- case 'f':
- case 'g':
- case 'h':
- 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;
- }
+ case M_SQ_AB:
+ fmt = "t,o(b)";
+ s = "sq";
+ goto ld_st;
- 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;
- }
- }
+ case M_LD_AB:
+ fmt = "t,o(b)";
+ if (GPR_SIZE == 64)
+ {
+ s = "ld";
+ goto ld;
+ }
+ s = "lw";
+ goto ldd_std;
- 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. */
- }
- }
+ case M_SD_AB:
+ fmt = "t,o(b)";
+ if (GPR_SIZE == 64)
+ {
+ s = "sd";
+ goto ld_st;
+ }
+ s = "sw";
- /* Make sure regno is the same as lastregno. */
- if (c == 't' && regno != lastregno)
- break;
+ 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;
- /* Make sure regno is the same as destregno. */
- if (c == 'x' && regno != destregno)
- break;
+ breg = op[2];
+ if (small_offset_p (0, align, 16))
+ {
+ ep = &offset_expr;
+ if (!small_offset_p (4, align, 16))
+ {
+ 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 (strcmp (s, "lw") == 0 && op[0] == breg)
+ {
+ ep->X_add_number += 4;
+ macro_build (ep, s, fmt, op[0] + 1, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ ep->X_add_number -= 4;
+ macro_build (ep, s, fmt, op[0], -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ }
+ else
+ {
+ macro_build (ep, s, fmt, coproc ? op[0] + 1 : op[0], -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2],
+ breg);
+ ep->X_add_number += 4;
+ macro_build (ep, s, fmt, coproc ? op[0] : op[0] + 1, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2],
+ breg);
+ }
+ break;
+ }
+
+ 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 (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
- /* We need to save regno, before regno maps to the
- microMIPS register encoding. */
- lastregno = regno;
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
+ }
- if (c == 'f')
- destregno = regno;
+ if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ <op> op[0],<sym>($gp) (BFD_RELOC_GPREL16)
+ <op> op[0]+1,<sym>+4($gp) (BFD_RELOC_GPREL16)
+ If we have a base register, we use this
+ addu $at,$breg,$gp
+ <op> op[0],<sym>($at) (BFD_RELOC_GPREL16)
+ <op> op[0]+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> op[0],<sym>($at) (BFD_RELOC_LO16)
+ <op> op[0]+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)
+ {
+ tempreg = mips_gp_register;
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, breg, mips_gp_register);
+ tempreg = AT;
+ used_at = 1;
+ }
- 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':
- s += strspn (s, " \t");
- if (*s != ',')
- {
- regno = ILLEGAL_REG;
- break;
- }
- ++s;
- s += strspn (s, " \t");
- ok = reg_lookup (&s, RTYPE_NUM | RTYPE_GP, ®no2);
- if (!ok)
- {
- regno = ILLEGAL_REG;
- break;
- }
- if (regno2 == 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\""),
- regno2, mips_opts.at);
- }
- regno = (mips_lookup_reg_pair
- (regno, regno2,
- micromips_to_32_reg_h_map1,
- micromips_to_32_reg_h_map2, 8));
- 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 ();
- }
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_GPREL16, tempreg);
+ offset_expr.X_add_number += 4;
- if (regno == ILLEGAL_REG)
- 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 ? op[0] : op[0] + 1,
+ BFD_RELOC_GPREL16, tempreg);
+ mips_optimize = hold_mips_optimize;
- 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 '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;
+ relax_switch ();
- 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;
+ 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 ? op[0] + 1 : op[0],
+ 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 ? op[0] : op[0] + 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> op[0],0($at)
+ <op> op[0]+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],<sym>($at) (BFD_RELOC_LO16)
+ <op> op[0]+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 'B':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || ep.X_op != O_constant)
- break;
-
- for (imm = 0; imm < 8; imm++)
- if (micromips_imm_b_map[imm] == ep.X_add_number)
- break;
- if (imm >= 8)
- break;
-
- INSERT_OPERAND (1, IMMB, *ip, imm);
- }
- s = expr_end;
- continue;
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
- case 'C':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
-
- if (my_getSmallExpression (&ep, r, s) > 0
- || ep.X_op != O_constant)
- break;
-
- for (imm = 0; imm < 16; imm++)
- if (micromips_imm_c_map[imm] == ep.X_add_number)
- break;
- if (imm >= 16)
- break;
-
- INSERT_OPERAND (1, IMMC, *ip, imm);
- }
- s = expr_end;
- continue;
+ /* Itbl support may require additional care here. */
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&expr1, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+ macro_build (&expr1, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ relax_switch ();
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ relax_end ();
- case 'D': /* pc relative offset */
- case 'E': /* pc relative offset */
- my_getExpression (&offset_expr, s);
- if (offset_expr.X_op == O_register)
- break;
+ mips_optimize = hold_mips_optimize;
+ }
+ else if (mips_big_got)
+ {
+ int gpdelay;
- 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;
+ /* 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> op[0],0($at)
+ <op> op[0]+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],<sym>($at) (BFD_RELOC_LO16)
+ <op> op[0]+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 ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
- case 'F':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ /* 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 ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ expr1.X_add_number -= 4;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
+ 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 ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMF, *ip, imm);
- }
- 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 ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ relax_end ();
+ }
+ else
+ abort ();
- 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;
+ 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 '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;
+ /* 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 'I':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ 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?
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -1, 127, 0))
- break;
+ If an itbl is provided to interpret cop instructions,
+ this knowledge can be encoded in the itbl spec. */
- imm = ep.X_add_number & 127;
- INSERT_OPERAND (1, IMMI, *ip, imm);
- }
- 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;
- 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;
+ case M_MOVE:
+ move_register (op[0], op[1]);
+ 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;
+ case M_MOVEP:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ move_register (micromips_to_32_reg_h_map1[op[0]],
+ micromips_to_32_reg_m_map[op[1]]);
+ move_register (micromips_to_32_reg_h_map2[op[0]],
+ micromips_to_32_reg_n_map[op[2]]);
+ break;
- case 'M':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ if (mips_opts.arch == CPU_R5900)
+ macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", op[0], op[1],
+ op[2]);
+ else
+ {
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", op[1], op[2]);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ }
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 1, 9, 0))
- break;
+ 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", op[1], AT);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ break;
- imm = ep.X_add_number & 7;
- INSERT_OPERAND (1, IMMM, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_DMULO_I:
+ dbl = 1;
+ case M_MULO_I:
+ imm = 1;
+ goto do_mulo;
- 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.:
+ 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",
+ op[1], imm ? AT : op[2]);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, op[0], op[0], 31);
+ macro_build (NULL, "mfhi", MFHL_FMT, AT);
+ if (mips_trap)
+ macro_build (NULL, "tne", TRAP_FMT, op[0], 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", op[0], 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, op[0]);
+ break;
- s0, ra
- s0, s1, ra, s2, s3
- s0-s2, ra
+ case M_DMULOU_I:
+ dbl = 1;
+ case M_MULOU_I:
+ imm = 1;
+ goto do_mulou;
- and any permutations of these. */
- unsigned int reglist;
- int imm;
+ 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",
+ op[1], imm ? AT : op[2]);
+ macro_build (NULL, "mfhi", MFHL_FMT, AT);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ 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;
- if (!reglist_lookup (&s, RTYPE_NUM | RTYPE_GP, ®list))
- break;
+ case M_DROL:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (op[0] == op[1])
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ tempreg = op[0];
+ macro_build (NULL, "dnegu", "d,w", tempreg, op[2]);
+ macro_build (NULL, "drorv", "d,t,s", op[0], op[1], tempreg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "dsrlv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "dsllv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
- if ((reglist & 0xfff1ffff) != 0x80010000)
- break;
+ case M_ROL:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ if (op[0] == op[1])
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ tempreg = op[0];
+ macro_build (NULL, "negu", "d,w", tempreg, op[2]);
+ macro_build (NULL, "rorv", "d,t,s", op[0], op[1], tempreg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "subu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "srlv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "sllv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
- reglist = (reglist >> 17) & 7;
- reglist += 1;
- if ((reglist & -reglist) != reglist)
- break;
+ case M_DROL_I:
+ {
+ unsigned int rot;
+ char *l;
+ char *rr;
- imm = ffs (reglist) - 1;
- INSERT_OPERAND (1, IMMN, *ip, imm);
- }
- continue;
+ 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, op[0], op[1], rot - 32);
+ else
+ macro_build (NULL, "dror", SHFT_FMT, op[0], op[1], rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", SHFT_FMT, op[0], op[1], 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, op[1], rot);
+ macro_build (NULL, rr, SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
- case 'O': /* sdbbp 4-bit code. */
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_ROL_I:
+ {
+ unsigned int rot;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 16, 0))
- break;
+ 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, op[0], op[1],
+ (32 - rot) & 0x1f);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], 0);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "sll", SHFT_FMT, AT, op[1], rot);
+ macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMO, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_DROR:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ macro_build (NULL, "drorv", "d,t,s", op[0], op[1], op[2]);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "dsllv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "dsrlv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
- case 'P':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_ROR:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "rorv", "d,t,s", op[0], op[1], op[2]);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "subu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "sllv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "srlv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 32, 2))
- break;
+ case M_DROR_I:
+ {
+ unsigned int rot;
+ char *l;
+ char *rr;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMP, *ip, imm);
- }
- s = expr_end;
- continue;
+ 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, op[0], op[1], rot - 32);
+ else
+ macro_build (NULL, "dror", SHFT_FMT, op[0], op[1], rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", SHFT_FMT, op[0], op[1], 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, op[1], rot);
+ macro_build (NULL, l, SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
- case 'Q':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_ROR_I:
+ {
+ unsigned int rot;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -0x400000, 0x400000, 2))
- break;
+ 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, op[0], op[1], rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], 0);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "srl", SHFT_FMT, AT, op[1], rot);
+ macro_build (NULL, "sll", SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMQ, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_SEQ:
+ if (op[1] == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[2], BFD_RELOC_LO16);
+ else if (op[2] == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[0], BFD_RELOC_LO16);
+ }
+ 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 M_SEQ_I:
+ if (imm_expr.X_add_number == 0)
+ {
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ break;
+ }
+ if (op[1] == 0)
+ {
+ as_warn (_("instruction %s: result is always false"),
+ ip->insn_mo->name);
+ move_register (op[0], 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", op[0], op[1],
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ if (imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ macro_build (&imm_expr, "xori", "t,r,i", op[0], op[1], BFD_RELOC_LO16);
+ else if (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, GPR_SIZE == 32 ? "addiu" : "daddiu",
+ "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ }
+ else if (CPU_HAS_SEQ (mips_opts.arch))
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "seq", "d,v,t", op[0], op[1], AT);
+ break;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[0], BFD_RELOC_LO16);
+ break;
- case 'W':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_SGE: /* X >= Y <==> not (X < Y) */
+ s = "slt";
+ goto sge;
+ case M_SGEU:
+ s = "sltu";
+ sge:
+ macro_build (NULL, s, "d,v,t", op[0], op[1], op[2]);
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 64, 2))
- break;
+ case M_SGE_I: /* X >= I <==> not (X < I) */
+ case M_SGEU_I:
+ if (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",
+ op[0], op[1], BFD_RELOC_LO16);
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
+ op[0], op[1], AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
- imm = ep.X_add_number >> 2;
- INSERT_OPERAND (1, IMMW, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_SGT: /* X > Y <==> Y < X */
+ s = "slt";
+ goto sgt;
+ case M_SGTU:
+ s = "sltu";
+ sgt:
+ macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+ break;
- case 'X':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
- int imm;
+ case M_SGT_I: /* X > I <==> I < X */
+ s = "slt";
+ goto sgti;
+ case M_SGTU_I:
+ s = "sltu";
+ sgti:
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, -8, 8, 0))
- break;
+ case M_SLE: /* X <= Y <==> Y >= X <==> not (Y < X) */
+ s = "slt";
+ goto sle;
+ case M_SLEU:
+ s = "sltu";
+ sle:
+ macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
- imm = ep.X_add_number;
- INSERT_OPERAND (1, IMMX, *ip, imm);
- }
- s = expr_end;
- continue;
+ case M_SLE_I: /* X <= I <==> I >= X <==> not (I < X) */
+ s = "slt";
+ goto slei;
+ case M_SLEU_I:
+ s = "sltu";
+ slei:
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ 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 M_SLT_I:
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "slti", "t,r,j", op[0], op[1],
+ BFD_RELOC_LO16);
+ break;
+ }
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "slt", "d,v,t", op[0], op[1], AT);
+ break;
- case 'Z':
- {
- bfd_reloc_code_real_type r[3];
- expressionS ep;
+ case M_SLTU_I:
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "sltiu", "t,r,j", op[0], op[1],
+ BFD_RELOC_LO16);
+ break;
+ }
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "sltu", "d,v,t", op[0], op[1], AT);
+ break;
- if (my_getSmallExpression (&ep, r, s) > 0
- || !expr_const_in_range (&ep, 0, 1, 0))
- break;
- }
- s = expr_end;
- continue;
+ case M_SNE:
+ if (op[1] == 0)
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[2]);
+ else if (op[2] == 0)
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
+ }
+ 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;
- }
- break;
+ case M_SNE_I:
+ if (imm_expr.X_add_number == 0)
+ {
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
+ break;
+ }
+ if (op[1] == 0)
+ {
+ as_warn (_("instruction %s: result is always true"),
+ ip->insn_mo->name);
+ macro_build (&expr1, GPR_SIZE == 32 ? "addiu" : "daddiu", "t,r,j",
+ op[0], 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", op[0], op[1],
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ if (imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build (&imm_expr, "xori", "t,r,i", op[0], op[1],
+ BFD_RELOC_LO16);
+ }
+ else if (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, GPR_SIZE == 32 ? "addiu" : "daddiu",
+ "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ }
+ else if (CPU_HAS_SEQ (mips_opts.arch))
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "sne", "d,v,t", op[0], op[1], AT);
+ break;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
+ used_at = 1;
+ }
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
+ break;
- case 'n': /* Register list for 32-bit lwm and swm. */
- 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))
- 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_add_number > -0x200
+ && imm_expr.X_add_number <= 0x200)
+ {
+ macro_build (NULL, s, "t,r,.", op[0], op[1],
+ (int) -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_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", op[0], op[1], BFD_RELOC_LO16);
+ break;
+ }
+ do_subi_i:
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+ break;
- if ((reglist & 0x3f00ffff) != 0)
- break;
+ 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, GPR_SIZE == 64);
+ macro_build (NULL, s, "s,t", op[0], AT);
+ break;
- ra = (reglist >> 27) & 0x10;
- reglist = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
- reglist += 1;
- if ((reglist & -reglist) != reglist)
- break;
+ case M_TRUNCWS:
+ case M_TRUNCWD:
+ gas_assert (!mips_opts.micromips);
+ gas_assert (mips_opts.isa == ISA_MIPS1);
+ used_at = 1;
- imm = (ffs (reglist) - 1) | ra;
- INSERT_OPERAND (1, RT, *ip, imm);
- imm_expr.X_op = O_absent;
- }
- 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", op[2], RA);
+ macro_build (NULL, "cfc1", "t,G", op[2], RA);
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = 3;
+ macro_build (&expr1, "ori", "t,r,i", AT, op[2], 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",
+ op[0], op[1]);
+ macro_build (NULL, "ctc1", "t,G", op[2], RA);
+ macro_build (NULL, "nop", "");
+ end_noreorder ();
+ break;
- 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;
+ 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;
- default:
- as_bad (_("Bad char = '%c'\n"), *args);
- abort ();
- }
- break;
- }
- /* Args don't match. */
- s = argsStart;
- insn_error = _("Illegal operands");
- if (insn + 1 < past && !strcmp (insn->name, insn[1].name))
+ uld_st:
+ breg = op[2];
+ large_offset = !small_offset_p (off, align, offbits);
+ ep = &offset_expr;
+ expr1.X_add_number = 0;
+ if (large_offset)
{
- ++insn;
- continue;
+ 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 = op[0];
+ ep = &expr1;
}
- else if (wrong_delay_slot_insns && need_delay_slot_ok)
+ else if (!ust && op[0] == breg)
{
- gas_assert (firstinsn);
- need_delay_slot_ok = FALSE;
- past = insn + 1;
- insn = firstinsn;
- continue;
+ used_at = 1;
+ tempreg = AT;
}
- return;
- }
-}
+ else
+ tempreg = op[0];
-#define SKIP_SPACE_TABS(S) { while (*(S) == ' ' || *(S) == '\t') ++(S); }
+ if (off == 1)
+ goto ulh_sh;
-/* 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. */
+ 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);
-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;
+ 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);
- insn_error = NULL;
+ /* If necessary, move the result in tempreg to the final destination. */
+ if (!ust && op[0] != tempreg)
+ {
+ /* Protect second load's delay slot. */
+ load_delay_nop ();
+ move_register (op[0], tempreg);
+ }
+ break;
- forced_insn_length = 0;
+ ulh_sh:
+ used_at = 1;
+ if (target_big_endian == ust)
+ ep->X_add_number += off;
+ tempreg = ust || large_offset ? op[0] : AT;
+ macro_build (ep, s, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
- for (s = str; ISLOWER (*s); ++s)
- ;
- switch (*s)
- {
- case '\0':
- break;
+ /* 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 ? op[0] : AT;
+ if (ust)
+ macro_build (NULL, "srl", SHFT_FMT, tempreg, op[0], 8);
- case ' ':
- *s++ = '\0';
- break;
+ 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 '.':
- if (s[1] == 't' && s[2] == ' ')
+ /* For M_USH_A re-retrieve the LSB. */
+ if (ust && large_offset)
{
- *s = '\0';
- forced_insn_length = 2;
- s += 3;
- break;
+ 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);
}
- else if (s[1] == 'e' && s[2] == ' ')
+ /* For ULH and M_USH_A OR the LSB in. */
+ if (!ust || large_offset)
{
- *s = '\0';
- forced_insn_length = 4;
- s += 3;
- break;
+ tempreg = !large_offset ? AT : op[0];
+ macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
}
- /* Fall through. */
+ break;
+
default:
- insn_error = _("unknown opcode");
- return;
+ /* 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 (mips_opts.noautoextend && !forced_insn_length)
- forced_insn_length = 2;
+/* Implement macros in mips16 mode. */
- if ((insn = (struct mips_opcode *) hash_find (mips16_op_hash, str)) == NULL)
- {
- insn_error = _("unrecognized opcode");
- return;
- }
+static void
+mips16_macro (struct mips_cl_insn *ip)
+{
+ const struct mips_operand_array *operands;
+ int mask;
+ int tmp;
+ expressionS expr1;
+ int dbl;
+ const char *s, *s2, *s3;
+ unsigned int op[MAX_OPERANDS];
+ unsigned int i;
- argsstart = s;
- for (;;)
- {
- bfd_boolean ok;
- char relax_char;
+ mask = ip->insn_mo->mask;
- gas_assert (strcmp (insn->name, str) == 0);
+ operands = insn_operands (ip);
+ for (i = 0; i < MAX_OPERANDS; i++)
+ if (operands->operand[i])
+ op[i] = insn_extract_operand (ip, operands->operand[i]);
+ else
+ op[i] = -1;
- ok = is_opcode_valid_16 (insn);
- if (! ok)
- {
- if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes]
- && strcmp (insn->name, insn[1].name) == 0)
- {
- ++insn;
- continue;
- }
- else
- {
- if (!insn_error)
- {
- static char buf[100];
- 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);
- insn_error = buf;
- }
- return;
- }
- }
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
- create_insn (ip, insn);
- 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;
- relax_char = 0;
- for (args = insn->args; 1; ++args)
- {
- int c;
+ dbl = 0;
- if (*s == ' ')
- ++s;
+ switch (mask)
+ {
+ default:
+ abort ();
- /* 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. */
+ 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", op[1], op[2]);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", op[2]);
+ macro_build (NULL, "break", "6", 7);
- c = *args;
- switch (c)
- {
- case '\0':
- if (*s == '\0')
- {
- offsetT value;
+ /* 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", op[0]);
+ break;
- /* Stuff the immediate value in now, if we can. */
- if (insn->pinfo == INSN_MACRO)
- {
- gas_assert (relax_char == 0);
- gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
- }
- else if (relax_char
- && offset_expr.X_op == O_constant
- && calculate_reloc (*offset_reloc,
- offset_expr.X_add_number,
- &value))
- {
- mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
- forced_insn_length, &ip->insn_opcode);
- offset_expr.X_op = O_absent;
- *offset_reloc = BFD_RELOC_UNUSED;
- }
- else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
- {
- if (forced_insn_length == 2)
- as_bad (_("invalid unextended operand value"));
- forced_insn_length = 4;
- ip->insn_opcode |= MIPS16_EXTEND;
- }
- else if (relax_char)
- *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
+ 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", op[1], op[2]);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", op[2]);
+ macro_build (NULL, "break", "6", 7);
+ end_noreorder ();
+ macro_build (NULL, s2, "x", op[0]);
+ break;
- return;
- }
- break;
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", op[1], op[2]);
+ macro_build (NULL, "mflo", "x", op[0]);
+ 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;
+ case M_DSUBU_I:
+ dbl = 1;
+ goto do_subu;
+ case M_SUBU_I:
+ do_subu:
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", op[0], op[1]);
+ break;
- case '(':
- case ')':
- if (*s++ == c)
- continue;
- break;
+ case M_SUBU_I_2:
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "addiu", "x,k", op[0]);
+ 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))
- {
- if (c == 'v' || c == 'w')
- {
- if (c == 'v')
- MIPS16_INSERT_OPERAND (RX, *ip, lastregno);
- else
- MIPS16_INSERT_OPERAND (RY, *ip, lastregno);
- ++args;
- continue;
- }
- break;
- }
+ case M_DSUBU_I_2:
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "daddiu", "y,j", op[0]);
+ break;
- if (*s == ' ')
- ++s;
- if (args[1] != *s)
- {
- if (c == 'v' || c == 'w')
- {
- regno = mips16_to_32_reg_map[lastregno];
- s = s_reset;
- ++args;
- }
- }
+ 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";
- switch (c)
- {
- case 'x':
- case 'y':
- case 'z':
- case 'v':
- case 'w':
- case 'Z':
- regno = mips32_to_16_reg_map[regno];
- break;
+ do_reverse_branch:
+ tmp = op[1];
+ op[1] = op[0];
+ op[0] = tmp;
- case '0':
- if (regno != 0)
- regno = ILLEGAL_REG;
- break;
+ do_branch:
+ macro_build (NULL, s, "x,y", op[0], op[1]);
+ macro_build (&offset_expr, s2, "p");
+ break;
- case 'S':
- if (regno != SP)
- regno = ILLEGAL_REG;
- break;
+ 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 'R':
- if (regno != RA)
- regno = ILLEGAL_REG;
- break;
+ do_addone_branch_i:
+ ++imm_expr.X_add_number;
- 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;
+ do_branch_i:
+ macro_build (&imm_expr, s, s3, op[0]);
+ macro_build (&offset_expr, s2, "p");
+ break;
- default:
- abort ();
- }
+ case M_ABS:
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "slti", "x,8", op[1]);
+ if (op[0] != op[1])
+ macro_build (NULL, "move", "y,X", op[0], mips16_to_32_reg_map[op[1]]);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bteqz", "p");
+ macro_build (NULL, "neg", "x,w", op[0], op[0]);
+ break;
+ }
+}
+
+/* Look up instruction [START, START + LENGTH) in HASH. Record any extra
+ opcode bits in *OPCODE_EXTRA. */
+
+static struct mips_opcode *
+mips_lookup_insn (struct hash_control *hash, const char *start,
+ ssize_t length, unsigned int *opcode_extra)
+{
+ char *name, *dot, *p;
+ unsigned int mask, suffix;
+ ssize_t opend;
+ struct mips_opcode *insn;
+
+ /* Make a copy of the instruction so that we can fiddle with it. */
+ name = alloca (length + 1);
+ memcpy (name, start, length);
+ name[length] = '\0';
- if (regno == ILLEGAL_REG)
- break;
+ /* Look up the instruction as-is. */
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ if (insn)
+ return insn;
- 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 ();
- }
+ dot = strchr (name, '.');
+ if (dot && dot[1])
+ {
+ /* Try to interpret the text after the dot as a VU0 channel suffix. */
+ p = mips_parse_vu0_channels (dot + 1, &mask);
+ if (*p == 0 && mask != 0)
+ {
+ *dot = 0;
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ *dot = '.';
+ if (insn && (insn->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0)
+ {
+ *opcode_extra |= mask << mips_vu0_channel_mask.lsb;
+ return insn;
+ }
+ }
+ }
- lastregno = regno;
- continue;
+ if (mips_opts.micromips)
+ {
+ /* 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. */
+ opend = dot != NULL ? dot - name : length;
+ if (opend >= 3 && name[opend - 2] == '1' && name[opend - 1] == '6')
+ suffix = 2;
+ else if (name[opend - 2] == '3' && name[opend - 1] == '2')
+ suffix = 4;
+ else
+ suffix = 0;
+ if (suffix)
+ {
+ memcpy (name + opend - 2, name + opend, length - opend + 1);
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ if (insn)
+ {
+ forced_insn_length = suffix;
+ return insn;
+ }
+ }
+ }
- case 'P':
- if (strncmp (s, "$pc", 3) == 0)
- {
- s += 3;
- continue;
- }
- break;
+ return NULL;
+}
- case '5':
- case 'H':
- case 'W':
- case 'D':
- case 'j':
- case 'V':
- case 'C':
- case 'U':
- case 'k':
- case 'K':
- i = my_getSmallExpression (&offset_expr, offset_reloc, s);
- if (i > 0)
- {
- relax_char = c;
- s = expr_end;
- continue;
- }
- *offset_reloc = BFD_RELOC_UNUSED;
- /* Fall through. */
- case '<':
- case '>':
- case '[':
- case ']':
- case '4':
- case '8':
- my_getExpression (&offset_expr, s);
- if (offset_expr.X_op == O_register)
- {
- /* What we thought was an expression turned out to
- be a register. */
+/* Assemble an instruction into its binary format. If the instruction
+ is a macro, set imm_expr and offset_expr to the values associated
+ with "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. */
- if (s[0] == '(' && args[1] == '(')
- {
- /* It looks like the expression was omitted
- before a register indirection, which means
- that the expression is implicitly zero. We
- still set up offset_expr, so that we handle
- explicit extensions correctly. */
- offset_expr.X_op = O_constant;
- offset_expr.X_add_number = 0;
- relax_char = c;
- continue;
- }
+static void
+mips_ip (char *str, struct mips_cl_insn *insn)
+{
+ const struct mips_opcode *first, *past;
+ struct hash_control *hash;
+ char format;
+ size_t end;
+ struct mips_operand_token *tokens;
+ unsigned int opcode_extra;
- 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;
+ opcode_extra = 0;
- /* We need to relax this instruction. */
- relax_char = c;
- s = expr_end;
- continue;
+ /* 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;
- 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);
+ first = mips_lookup_insn (hash, str, end, &opcode_extra);
+ if (first == NULL)
+ {
+ set_insn_error (0, _("unrecognized opcode"));
+ return;
+ }
- if (offset_expr.X_op == O_register)
- break;
+ if (strcmp (first->name, "li.s") == 0)
+ format = 'f';
+ else if (strcmp (first->name, "li.d") == 0)
+ format = 'd';
+ else
+ format = 0;
+ tokens = mips_parse_arguments (str + end, format);
+ if (!tokens)
+ return;
- /* We need to relax this instruction. */
- relax_char = c;
- s = expr_end;
- continue;
+ if (!match_insns (insn, first, past, tokens, opcode_extra, FALSE)
+ && !match_insns (insn, first, past, tokens, opcode_extra, TRUE))
+ set_insn_error (0, _("invalid operands"));
- case '6': /* break code */
- my_getExpression (&imm_expr, s);
- check_absolute_expr (ip, &imm_expr);
- if ((unsigned long) imm_expr.X_add_number > 63)
- as_warn (_("Invalid value for `%s' (%lu)"),
- ip->insn_mo->name,
- (unsigned long) imm_expr.X_add_number);
- MIPS16_INSERT_OPERAND (IMM6, *ip, imm_expr.X_add_number);
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ obstack_free (&mips_operand_tokens, tokens);
+}
- 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;
+/* 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. */
- case 'a': /* 26 bit address */
- case 'i':
- my_getExpression (&offset_expr, s);
- s = expr_end;
- *offset_reloc = BFD_RELOC_MIPS16_JMP;
- ip->insn_opcode <<= 16;
- continue;
+static void
+mips16_ip (char *str, struct mips_cl_insn *insn)
+{
+ char *end, *s, c;
+ struct mips_opcode *first;
+ struct mips_operand_token *tokens;
- 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;
+ forced_insn_length = 0;
- case 'm': /* Register list for save insn. */
- case 'M': /* Register list for restore insn. */
- {
- int opcode = ip->insn_opcode;
- int framesz = 0, seen_framesz = 0;
- int nargs = 0, statics = 0, sregs = 0;
-
- while (*s != '\0')
- {
- unsigned int reg1, reg2;
-
- SKIP_SPACE_TABS (s);
- while (*s == ',')
- ++s;
- SKIP_SPACE_TABS (s);
-
- my_getExpression (&imm_expr, s);
- if (imm_expr.X_op == O_constant)
- {
- /* Handle the frame size. */
- if (seen_framesz)
- {
- as_bad (_("more than one frame size in list"));
- break;
- }
- seen_framesz = 1;
- framesz = imm_expr.X_add_number;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
- }
-
- if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®1))
- {
- as_bad (_("can't parse register list"));
- break;
- }
-
- while (*s == ' ')
- ++s;
-
- if (*s != '-')
- reg2 = reg1;
- else
- {
- ++s;
- if (! reg_lookup (&s, RTYPE_GP | RTYPE_NUM, ®2)
- || reg2 < reg1)
- {
- as_bad (_("can't parse register list"));
- break;
- }
- }
-
- while (reg1 <= reg2)
- {
- if (reg1 >= 4 && reg1 <= 7)
- {
- if (!seen_framesz)
- /* args $a0-$a3 */
- nargs |= 1 << (reg1 - 4);
- else
- /* statics $a0-$a3 */
- statics |= 1 << (reg1 - 4);
- }
- else if ((reg1 >= 16 && reg1 <= 23) || reg1 == 30)
- {
- /* $s0-$s8 */
- sregs |= 1 << ((reg1 == 30) ? 8 : (reg1 - 16));
- }
- else if (reg1 == 31)
- {
- /* Add $ra to insn. */
- opcode |= 0x40;
- }
- else
- {
- as_bad (_("unexpected register in list"));
- break;
- }
- if (++reg1 == 24)
- reg1 = 30;
- }
- }
-
- /* Encode args/statics combination. */
- if (nargs & statics)
- as_bad (_("arg/static registers overlap"));
- else if (nargs == 0xf)
- /* All $a0-$a3 are args. */
- opcode |= MIPS16_ALL_ARGS << 16;
- else if (statics == 0xf)
- /* All $a0-$a3 are statics. */
- opcode |= MIPS16_ALL_STATICS << 16;
- else
- {
- int narg = 0, nstat = 0;
-
- /* Count arg registers. */
- while (nargs & 0x1)
- {
- nargs >>= 1;
- narg++;
- }
- if (nargs != 0)
- as_bad (_("invalid arg register list"));
-
- /* Count static registers. */
- while (statics & 0x8)
- {
- statics = (statics << 1) & 0xf;
- nstat++;
- }
- if (statics != 0)
- as_bad (_("invalid static register list"));
-
- /* Encode args/statics. */
- opcode |= ((narg << 2) | nstat) << 16;
- }
-
- /* Encode $s0/$s1. */
- if (sregs & (1 << 0)) /* $s0 */
- opcode |= 0x20;
- if (sregs & (1 << 1)) /* $s1 */
- opcode |= 0x10;
- sregs >>= 2;
-
- if (sregs != 0)
- {
- /* Count regs $s2-$s8. */
- int nsreg = 0;
- while (sregs & 1)
- {
- sregs >>= 1;
- nsreg++;
- }
- if (sregs != 0)
- as_bad (_("invalid static register list"));
- /* Encode $s2-$s8. */
- opcode |= nsreg << 24;
- }
-
- /* Encode frame size. */
- if (!seen_framesz)
- as_bad (_("missing frame size"));
- else if ((framesz & 7) != 0 || framesz < 0
- || framesz > 0xff * 8)
- as_bad (_("invalid frame size"));
- else if (framesz != 128 || (opcode >> 16) != 0)
- {
- framesz /= 8;
- opcode |= (((framesz & 0xf0) << 16)
- | (framesz & 0x0f));
- }
-
- /* Finally build the instruction. */
- if ((opcode >> 16) != 0 || framesz == 0)
- opcode |= MIPS16_EXTEND;
- ip->insn_opcode = opcode;
- }
- continue;
+ for (s = str; ISLOWER (*s); ++s)
+ ;
+ end = s;
+ c = *end;
+ switch (c)
+ {
+ case '\0':
+ 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;
- }
- ip->insn_opcode |= imm_expr.X_add_number;
- imm_expr.X_op = O_absent;
- s = expr_end;
- continue;
+ case ' ':
+ s++;
+ break;
- default:
- abort ();
- }
+ case '.':
+ if (s[1] == 't' && s[2] == ' ')
+ {
+ forced_insn_length = 2;
+ s += 3;
break;
}
-
- /* Args don't match. */
- if (insn + 1 < &mips16_opcodes[bfd_mips16_num_opcodes] &&
- strcmp (insn->name, insn[1].name) == 0)
+ else if (s[1] == 'e' && s[2] == ' ')
{
- ++insn;
- s = argsstart;
- continue;
+ forced_insn_length = 4;
+ s += 3;
+ break;
}
+ /* Fall through. */
+ default:
+ set_insn_error (0, _("unrecognized opcode"));
+ return;
+ }
+
+ if (mips_opts.noautoextend && !forced_insn_length)
+ forced_insn_length = 2;
- insn_error = _("illegal operands");
+ *end = 0;
+ first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
+ *end = c;
+ if (!first)
+ {
+ set_insn_error (0, _("unrecognized opcode"));
return;
}
-}
-/* This structure holds information we know about a mips16 immediate
- argument type. */
-
-struct mips16_immed_operand
-{
- /* The type code used in the argument string in the opcode table. */
- int type;
- /* The number of bits in the short form of the opcode. */
- int nbits;
- /* The number of bits in the extended form of the opcode. */
- int extbits;
- /* The amount by which the short form is shifted when it is used;
- for example, the sw instruction has a shift count of 2. */
- int shift;
- /* The amount by which the short form is shifted when it is stored
- into the instruction code. */
- int op_shift;
- /* Non-zero if the short form is unsigned. */
- int unsp;
- /* Non-zero if the extended form is unsigned. */
- int extu;
- /* Non-zero if the value is PC relative. */
- int pcrel;
-};
+ tokens = mips_parse_arguments (s, 0);
+ if (!tokens)
+ return;
-/* The mips16 immediate operand types. */
-
-static const struct mips16_immed_operand mips16_immed_operands[] =
-{
- { '<', 3, 5, 0, MIPS16OP_SH_RZ, 1, 1, 0 },
- { '>', 3, 5, 0, MIPS16OP_SH_RX, 1, 1, 0 },
- { '[', 3, 6, 0, MIPS16OP_SH_RZ, 1, 1, 0 },
- { ']', 3, 6, 0, MIPS16OP_SH_RX, 1, 1, 0 },
- { '4', 4, 15, 0, MIPS16OP_SH_IMM4, 0, 0, 0 },
- { '5', 5, 16, 0, MIPS16OP_SH_IMM5, 1, 0, 0 },
- { 'H', 5, 16, 1, MIPS16OP_SH_IMM5, 1, 0, 0 },
- { 'W', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 0 },
- { 'D', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 0 },
- { 'j', 5, 16, 0, MIPS16OP_SH_IMM5, 0, 0, 0 },
- { '8', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 0, 0 },
- { 'V', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 0 },
- { 'C', 8, 16, 3, MIPS16OP_SH_IMM8, 1, 0, 0 },
- { 'U', 8, 16, 0, MIPS16OP_SH_IMM8, 1, 1, 0 },
- { 'k', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 0 },
- { 'K', 8, 16, 3, MIPS16OP_SH_IMM8, 0, 0, 0 },
- { 'p', 8, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
- { 'q', 11, 16, 0, MIPS16OP_SH_IMM8, 0, 0, 1 },
- { 'A', 8, 16, 2, MIPS16OP_SH_IMM8, 1, 0, 1 },
- { 'B', 5, 16, 3, MIPS16OP_SH_IMM5, 1, 0, 1 },
- { 'E', 5, 16, 2, MIPS16OP_SH_IMM5, 1, 0, 1 }
-};
+ if (!match_mips16_insns (insn, first, tokens))
+ set_insn_error (0, _("invalid operands"));
-#define MIPS16_NUM_IMMED \
- (sizeof mips16_immed_operands / sizeof mips16_immed_operands[0])
+ obstack_free (&mips_operand_tokens, tokens);
+}
/* Marshal immediate value VAL for an extended MIPS16 instruction.
NBITS is the number of significant bits in VAL. */
return (extval << 16) | val;
}
+/* Like decode_mips16_operand, but require the operand to be defined and
+ require it to be an integer. */
+
+static const struct mips_int_operand *
+mips16_immed_operand (int type, bfd_boolean extended_p)
+{
+ const struct mips_operand *operand;
+
+ operand = decode_mips16_operand (type, extended_p);
+ if (!operand || (operand->type != OP_INT && operand->type != OP_PCREL))
+ abort ();
+ return (const struct mips_int_operand *) operand;
+}
+
+/* Return true if SVAL fits OPERAND. RELOC is as for mips16_immed. */
+
+static bfd_boolean
+mips16_immed_in_range_p (const struct mips_int_operand *operand,
+ bfd_reloc_code_real_type reloc, offsetT sval)
+{
+ int min_val, max_val;
+
+ min_val = mips_int_operand_min (operand);
+ max_val = mips_int_operand_max (operand);
+ if (reloc != BFD_RELOC_UNUSED)
+ {
+ if (min_val < 0)
+ sval = SEXT_16BIT (sval);
+ else
+ sval &= 0xffff;
+ }
+
+ return (sval >= min_val
+ && sval <= max_val
+ && (sval & ((1 << operand->shift) - 1)) == 0);
+}
+
/* Install immediate value VAL into MIPS16 instruction *INSN,
extending it if necessary. The instruction in *INSN may
already be extended.
bfd_reloc_code_real_type reloc, offsetT val,
unsigned int user_insn_length, unsigned long *insn)
{
- const struct mips16_immed_operand *op;
- int mintiny, maxtiny;
-
- op = mips16_immed_operands;
- while (op->type != type)
- {
- ++op;
- gas_assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
- }
-
- if (op->unsp)
- {
- if (type == '<' || type == '>' || type == '[' || type == ']')
- {
- mintiny = 1;
- maxtiny = 1 << op->nbits;
- }
- else
- {
- mintiny = 0;
- maxtiny = (1 << op->nbits) - 1;
- }
- if (reloc != BFD_RELOC_UNUSED)
- val &= 0xffff;
- }
- else
- {
- mintiny = - (1 << (op->nbits - 1));
- maxtiny = (1 << (op->nbits - 1)) - 1;
- if (reloc != BFD_RELOC_UNUSED)
- val = SEXT_16BIT (val);
- }
-
- /* Branch offsets have an implicit 0 in the lowest bit. */
- if (type == 'p' || type == 'q')
- val /= 2;
+ const struct mips_int_operand *operand;
+ unsigned int uval, length;
- if ((val & ((1 << op->shift) - 1)) != 0
- || val < (mintiny << op->shift)
- || val > (maxtiny << op->shift))
+ operand = mips16_immed_operand (type, FALSE);
+ if (!mips16_immed_in_range_p (operand, reloc, val))
{
/* We need an extended instruction. */
if (user_insn_length == 2)
_("extended operand requested but not required"));
}
- if (mips16_opcode_length (*insn) == 2)
+ length = mips16_opcode_length (*insn);
+ if (length == 4)
{
- int insnval;
-
- insnval = ((val >> op->shift) & ((1 << op->nbits) - 1));
- insnval <<= op->op_shift;
- *insn |= insnval;
+ operand = mips16_immed_operand (type, TRUE);
+ if (!mips16_immed_in_range_p (operand, reloc, val))
+ as_bad_where (file, line,
+ _("operand value out of range for instruction"));
}
+ uval = ((unsigned int) val >> operand->shift) - operand->bias;
+ if (length == 2)
+ *insn = mips_insert_operand (&operand->root, *insn, uval);
else
- {
- long minext, maxext;
-
- if (reloc == BFD_RELOC_UNUSED)
- {
- if (op->extu)
- {
- minext = 0;
- maxext = (1 << op->extbits) - 1;
- }
- else
- {
- minext = - (1 << (op->extbits - 1));
- maxext = (1 << (op->extbits - 1)) - 1;
- }
- if (val < minext || val > maxext)
- as_bad_where (file, line,
- _("operand value out of range for instruction"));
- }
-
- *insn |= mips16_immed_extend (val, op->extbits);
- }
+ *insn |= mips16_immed_extend (uval, operand->root.size);
}
\f
struct percent_op_match
mips_set_option_string (const char **string_ptr, const char *new_value)
{
if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
- as_warn (_("A different %s was already specified, is now %s"),
+ as_warn (_("a different %s was already specified, is now %s"),
string_ptr == &mips_arch_string ? "-march" : "-mtune",
new_value);
break;
case OPTION_MIPS1:
- file_mips_isa = ISA_MIPS1;
+ file_mips_opts.isa = ISA_MIPS1;
break;
case OPTION_MIPS2:
- file_mips_isa = ISA_MIPS2;
+ file_mips_opts.isa = ISA_MIPS2;
break;
case OPTION_MIPS3:
- file_mips_isa = ISA_MIPS3;
+ file_mips_opts.isa = ISA_MIPS3;
break;
case OPTION_MIPS4:
- file_mips_isa = ISA_MIPS4;
+ file_mips_opts.isa = ISA_MIPS4;
break;
case OPTION_MIPS5:
- file_mips_isa = ISA_MIPS5;
+ file_mips_opts.isa = ISA_MIPS5;
break;
case OPTION_MIPS32:
- file_mips_isa = ISA_MIPS32;
+ file_mips_opts.isa = ISA_MIPS32;
break;
case OPTION_MIPS32R2:
- file_mips_isa = ISA_MIPS32R2;
+ file_mips_opts.isa = ISA_MIPS32R2;
+ break;
+
+ case OPTION_MIPS32R3:
+ file_mips_opts.isa = ISA_MIPS32R3;
+ break;
+
+ case OPTION_MIPS32R5:
+ file_mips_opts.isa = ISA_MIPS32R5;
break;
case OPTION_MIPS64R2:
- file_mips_isa = ISA_MIPS64R2;
+ file_mips_opts.isa = ISA_MIPS64R2;
+ break;
+
+ case OPTION_MIPS64R3:
+ file_mips_opts.isa = ISA_MIPS64R3;
+ break;
+
+ case OPTION_MIPS64R5:
+ file_mips_opts.isa = ISA_MIPS64R5;
break;
case OPTION_MIPS64:
- file_mips_isa = ISA_MIPS64;
+ file_mips_opts.isa = ISA_MIPS64;
break;
case OPTION_MTUNE:
mips_fix_24k = 0;
break;
+ case OPTION_FIX_RM7000:
+ mips_fix_rm7000 = 1;
+ break;
+
+ case OPTION_NO_FIX_RM7000:
+ mips_fix_rm7000 = 0;
+ break;
+
case OPTION_FIX_LOONGSON2F_JUMP:
mips_fix_loongson2f_jump = TRUE;
break;
case OPTION_64:
mips_abi = N64_ABI;
if (!support_64bit_objects())
- as_fatal (_("No compiled in support for 64 bit object file format"));
+ as_fatal (_("no compiled in support for 64 bit object file format"));
break;
case OPTION_GP32:
- file_mips_gp32 = 1;
+ file_mips_opts.gp = 32;
break;
case OPTION_GP64:
- file_mips_gp32 = 0;
+ file_mips_opts.gp = 64;
break;
case OPTION_FP32:
- file_mips_fp32 = 1;
+ file_mips_opts.fp = 32;
break;
case OPTION_FP64:
- file_mips_fp32 = 0;
+ file_mips_opts.fp = 64;
break;
case OPTION_SINGLE_FLOAT:
- file_mips_single_float = 1;
+ file_mips_opts.single_float = 1;
break;
case OPTION_DOUBLE_FLOAT:
- file_mips_single_float = 0;
+ file_mips_opts.single_float = 0;
break;
case OPTION_SOFT_FLOAT:
- file_mips_soft_float = 1;
+ file_mips_opts.soft_float = 1;
break;
case OPTION_HARD_FLOAT:
- file_mips_soft_float = 0;
+ file_mips_opts.soft_float = 0;
break;
case OPTION_MABI:
{
mips_abi = N64_ABI;
if (! support_64bit_objects())
- as_fatal (_("No compiled in support for 64 bit object file "
+ as_fatal (_("no compiled in support for 64 bit object file "
"format"));
}
else if (strcmp (arg, "eabi") == 0)
mips_flag_nan2008 = FALSE;
else
{
- as_fatal (_("Invalid NaN setting -mnan=%s"), arg);
+ as_fatal (_("invalid NaN setting -mnan=%s"), arg);
return 0;
}
break;
{
if (info != 0)
{
- file_mips_arch = info->cpu;
+ file_mips_opts.arch = info->cpu;
mips_opts.arch = info->cpu;
mips_opts.isa = info->isa;
}
if (strncmp (TARGET_OS, "pe", 2) == 0)
{
if (g_switch_seen && g_switch_value != 0)
- as_bad (_("-G not supported in this configuration."));
+ as_bad (_("-G not supported in this configuration"));
g_switch_value = 0;
}
if (mips_arch_string != 0)
arch_info = mips_parse_cpu ("-march", mips_arch_string);
- if (file_mips_isa != ISA_UNKNOWN)
+ if (file_mips_opts.isa != ISA_UNKNOWN)
{
- /* Handle -mipsN. At this point, file_mips_isa contains the
+ /* Handle -mipsN. At this point, file_mips_opts.isa contains the
ISA level specified by -mipsN, while arch_info->isa contains
the -march selection (if any). */
if (arch_info != 0)
/* -march takes precedence over -mipsN, since it is more descriptive.
There's no harm in specifying both as long as the ISA levels
are the same. */
- if (file_mips_isa != arch_info->isa)
- as_bad (_("-%s conflicts with the other architecture options, which imply -%s"),
- mips_cpu_info_from_isa (file_mips_isa)->name,
+ if (file_mips_opts.isa != arch_info->isa)
+ as_bad (_("-%s conflicts with the other architecture options,"
+ " which imply -%s"),
+ mips_cpu_info_from_isa (file_mips_opts.isa)->name,
mips_cpu_info_from_isa (arch_info->isa)->name);
}
else
- arch_info = mips_cpu_info_from_isa (file_mips_isa);
+ arch_info = mips_cpu_info_from_isa (file_mips_opts.isa);
}
if (arch_info == 0)
mips_set_architecture (arch_info);
- /* Optimize for file_mips_arch, unless -mtune selects a different processor. */
+ /* Optimize for file_mips_opts.arch, unless -mtune selects a different
+ processor. */
if (mips_tune_string != 0)
tune_info = mips_parse_cpu ("-mtune", mips_tune_string);
else
mips_set_tune (tune_info);
- if (file_mips_gp32 >= 0)
+ if (file_mips_opts.gp >= 0)
{
/* The user specified the size of the integer registers. Make sure
it agrees with the ABI and ISA. */
- if (file_mips_gp32 == 0 && !ISA_HAS_64BIT_REGS (mips_opts.isa))
+ if (file_mips_opts.gp == 64 && !ISA_HAS_64BIT_REGS (mips_opts.isa))
as_bad (_("-mgp64 used with a 32-bit processor"));
- else if (file_mips_gp32 == 1 && ABI_NEEDS_64BIT_REGS (mips_abi))
+ else if (file_mips_opts.gp == 32 && ABI_NEEDS_64BIT_REGS (mips_abi))
as_bad (_("-mgp32 used with a 64-bit ABI"));
- else if (file_mips_gp32 == 0 && ABI_NEEDS_32BIT_REGS (mips_abi))
+ else if (file_mips_opts.gp == 64 && ABI_NEEDS_32BIT_REGS (mips_abi))
as_bad (_("-mgp64 used with a 32-bit ABI"));
}
else
/* Infer the integer register size from the ABI and processor.
Restrict ourselves to 32-bit registers if that's all the
processor has, or if the ABI cannot handle 64-bit registers. */
- file_mips_gp32 = (ABI_NEEDS_32BIT_REGS (mips_abi)
- || !ISA_HAS_64BIT_REGS (mips_opts.isa));
+ file_mips_opts.gp = (ABI_NEEDS_32BIT_REGS (mips_abi)
+ || !ISA_HAS_64BIT_REGS (mips_opts.isa))
+ ? 32 : 64;
}
- switch (file_mips_fp32)
+ switch (file_mips_opts.fp)
{
default:
case -1:
registers would lead to spurious "register must be even" messages.
So here we assume float registers are never smaller than the
integer ones. */
- if (file_mips_gp32 == 0)
+ if (file_mips_opts.gp == 64)
/* 64-bit integer registers implies 64-bit float registers. */
- file_mips_fp32 = 0;
+ file_mips_opts.fp = 64;
else if ((mips_opts.ase & FP64_ASES)
&& ISA_HAS_64BIT_FPRS (mips_opts.isa))
/* -mips3d and -mdmx imply 64-bit float registers, if possible. */
- file_mips_fp32 = 0;
+ file_mips_opts.fp = 64;
else
/* 32-bit float registers. */
- file_mips_fp32 = 1;
+ file_mips_opts.fp = 32;
break;
/* The user specified the size of the float registers. Check if it
agrees with the ABI and ISA. */
- case 0:
+ case 64:
if (!ISA_HAS_64BIT_FPRS (mips_opts.isa))
as_bad (_("-mfp64 used with a 32-bit fpu"));
else if (ABI_NEEDS_32BIT_REGS (mips_abi)
&& !ISA_HAS_MXHC1 (mips_opts.isa))
as_warn (_("-mfp64 used with a 32-bit ABI"));
break;
- case 1:
+ case 32:
if (ABI_NEEDS_64BIT_REGS (mips_abi))
as_warn (_("-mfp32 used with a 64-bit ABI"));
break;
/* This flag is set when we have a 64-bit capable CPU but use only
32-bit wide registers. Note that EABI does not use it. */
if (ISA_HAS_64BIT_REGS (mips_opts.isa)
- && ((mips_abi == NO_ABI && file_mips_gp32 == 1)
+ && ((mips_abi == NO_ABI && file_mips_opts.gp == 32)
|| mips_abi == O32_ABI))
mips_32bitmode = 1;
/* If the selected architecture includes support for ASEs, enable
generation of code for them. */
if (mips_opts.mips16 == -1)
- mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_arch)) ? 1 : 0;
+ mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0;
if (mips_opts.micromips == -1)
- mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_arch)) ? 1 : 0;
+ mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch))
+ ? 1 : 0;
/* MIPS3D and MDMX require 64-bit FPRs, so -mfp32 should stop those
ASEs from being selected implicitly. */
- if (file_mips_fp32 == 1)
+ if (file_mips_opts.fp != 64)
file_ase_explicit |= ASE_MIPS3D | ASE_MDMX;
/* If the user didn't explicitly select or deselect a particular ASE,
use the default setting for the CPU. */
mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
- file_mips_isa = mips_opts.isa;
- file_ase = mips_opts.ase;
- mips_opts.gp32 = file_mips_gp32;
- mips_opts.fp32 = file_mips_fp32;
- mips_opts.soft_float = file_mips_soft_float;
- mips_opts.single_float = file_mips_single_float;
+ file_mips_opts.isa = mips_opts.isa;
+ file_mips_opts.ase = mips_opts.ase;
+ mips_opts.gp = file_mips_opts.gp;
+ mips_opts.fp = file_mips_opts.fp;
+ mips_opts.soft_float = file_mips_opts.soft_float;
+ mips_opts.single_float = file_mips_opts.single_float;
mips_check_isa_supports_ases ();
/* Return the address of the delay slot. */
return addr + 4;
- case BFD_RELOC_32_PCREL:
- return addr;
-
default:
- /* We have no relocation type for PC relative MIPS16 instructions. */
- if (fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != now_seg)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("PC relative MIPS16 instruction references a different section"));
return addr;
}
}
unsigned long insn;
reloc_howto_type *howto;
- /* We ignore generic BFD relocations we don't know about. */
- howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
- if (! howto)
- return;
+ if (fixP->fx_pcrel)
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ case BFD_RELOC_32_PCREL:
+ break;
+
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative reference to a different section"));
+ break;
+ }
+
+ /* Handle BFD_RELOC_8, since it's easy. Punt on other bfd relocations
+ that have no MIPS ELF equivalent. */
+ if (fixP->fx_r_type != BFD_RELOC_8)
+ {
+ howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (!howto)
+ return;
+ }
gas_assert (fixP->fx_size == 2
|| fixP->fx_size == 4
+ || fixP->fx_r_type == BFD_RELOC_8
|| fixP->fx_r_type == BFD_RELOC_16
|| fixP->fx_r_type == BFD_RELOC_64
|| fixP->fx_r_type == BFD_RELOC_CTOR
buf = fixP->fx_frag->fr_literal + fixP->fx_where;
- gas_assert (!fixP->fx_pcrel || fixP->fx_r_type == BFD_RELOC_16_PCREL_S2
- || fixP->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
- || fixP->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
- || fixP->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
- || fixP->fx_r_type == BFD_RELOC_32_PCREL);
-
/* Don't treat parts of a composite relocation as done. There are two
reasons for this:
}
else
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Unsupported constant in relocation"));
+ _("unsupported constant in relocation"));
}
break;
case BFD_RELOC_32:
case BFD_RELOC_32_PCREL:
case BFD_RELOC_16:
+ case BFD_RELOC_8:
/* If we are deleting this reloc entry, we must fill in the
value now. This can happen if we have a .word which is not
resolved when it appears but is later defined. */
case BFD_RELOC_16_PCREL_S2:
if ((*valP & 0x3) != 0)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Branch to misaligned address (%lx)"), (long) *valP);
+ _("branch to misaligned address (%lx)"), (long) *valP);
/* We need to save the bits in the instruction since fixup_segment()
might be deleting the relocation entry (i.e., a branch within
and there's nothing we can do to fix this instruction
without turning it into a longer sequence. */
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Branch out of range"));
+ _("branch out of range"));
}
break;
temp = get_absolute_expression ();
if (temp > max_alignment)
- as_bad (_("Alignment too large: %d. assumed."), temp = max_alignment);
+ as_bad (_("alignment too large, %d assumed"), temp = max_alignment);
else if (temp < 0)
{
- as_warn (_("Alignment negative: 0 assumed."));
+ as_warn (_("alignment negative, 0 assumed"));
temp = 0;
}
if (*input_line_pointer == ',')
}
}
else
- as_warn (_("Unrecognized option \"%s\""), opt);
+ as_warn (_("unrecognized option \"%s\""), opt);
*input_line_pointer = c;
demand_empty_rest_of_line ();
char *s = name + 3;
if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &mips_opts.at))
- as_bad (_("Unrecognized register name `%s'"), s);
+ as_bad (_("unrecognized register name `%s'"), s);
}
else if (strcmp (name, "at") == 0)
{
mips_opts.nobopt = 1;
}
else if (strcmp (name, "gp=default") == 0)
- mips_opts.gp32 = file_mips_gp32;
+ mips_opts.gp = file_mips_opts.gp;
else if (strcmp (name, "gp=32") == 0)
- mips_opts.gp32 = 1;
+ mips_opts.gp = 32;
else if (strcmp (name, "gp=64") == 0)
{
if (!ISA_HAS_64BIT_REGS (mips_opts.isa))
as_warn (_("%s isa does not support 64-bit registers"),
mips_cpu_info_from_isa (mips_opts.isa)->name);
- mips_opts.gp32 = 0;
+ mips_opts.gp = 64;
}
else if (strcmp (name, "fp=default") == 0)
- mips_opts.fp32 = file_mips_fp32;
+ mips_opts.fp = file_mips_opts.fp;
else if (strcmp (name, "fp=32") == 0)
- mips_opts.fp32 = 1;
+ mips_opts.fp = 32;
else if (strcmp (name, "fp=64") == 0)
{
if (!ISA_HAS_64BIT_FPRS (mips_opts.isa))
as_warn (_("%s isa does not support 64-bit floating point registers"),
mips_cpu_info_from_isa (mips_opts.isa)->name);
- mips_opts.fp32 = 0;
+ mips_opts.fp = 64;
}
else if (strcmp (name, "softfloat") == 0)
mips_opts.soft_float = 1;
if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
{
reset = 1;
- mips_opts.isa = file_mips_isa;
- mips_opts.arch = file_mips_arch;
+ mips_opts.isa = file_mips_opts.isa;
+ mips_opts.arch = file_mips_opts.arch;
}
else if (strncmp (name, "arch=", 5) == 0)
{
case ISA_MIPS2:
case ISA_MIPS32:
case ISA_MIPS32R2:
- mips_opts.gp32 = 1;
- mips_opts.fp32 = 1;
+ case ISA_MIPS32R3:
+ case ISA_MIPS32R5:
+ mips_opts.gp = 32;
+ mips_opts.fp = 32;
break;
case ISA_MIPS3:
case ISA_MIPS4:
case ISA_MIPS5:
case ISA_MIPS64:
case ISA_MIPS64R2:
- mips_opts.gp32 = 0;
+ case ISA_MIPS64R3:
+ case ISA_MIPS64R5:
+ mips_opts.gp = 64;
if (mips_opts.arch == CPU_R5900)
{
- mips_opts.fp32 = 1;
+ mips_opts.fp = 32;
}
else
{
- mips_opts.fp32 = 0;
+ mips_opts.fp = 64;
}
break;
default:
}
if (reset)
{
- mips_opts.gp32 = file_mips_gp32;
- mips_opts.fp32 = file_mips_fp32;
+ mips_opts.gp = file_mips_opts.gp;
+ mips_opts.fp = file_mips_opts.fp;
}
}
else if (strcmp (name, "autoextend") == 0)
}
else
{
- as_warn (_("Tried to set unrecognized symbol: %s\n"), name);
+ as_warn (_("tried to set unrecognized symbol: %s\n"), name);
}
mips_check_isa_supports_ases ();
*input_line_pointer = ch;
if (ex.X_op != O_symbol)
{
- as_bad (_("Unsupported use of %s"), dirstr);
+ as_bad (_("unsupported use of %s"), dirstr);
ignore_rest_of_line ();
}
if (ex.X_op != O_symbol || ex.X_add_number != 0)
{
- as_bad (_("Unsupported use of .gpword"));
+ as_bad (_("unsupported use of .gpword"));
ignore_rest_of_line ();
}
if (ex.X_op != O_symbol || ex.X_add_number != 0)
{
- as_bad (_("Unsupported use of .gpdword"));
+ as_bad (_("unsupported use of .gpdword"));
ignore_rest_of_line ();
}
if (ex.X_op != O_symbol || ex.X_add_number != 0)
{
- as_bad (_("Unsupported use of .ehword"));
+ as_bad (_("unsupported use of .ehword"));
ignore_rest_of_line ();
}
&& memcmp (input_line_pointer, str_legacy, i) == 0)
mips_flag_nan2008 = FALSE;
else
- as_bad (_("Bad .nan directive"));
+ as_bad (_("bad .nan directive"));
input_line_pointer += i;
demand_empty_rest_of_line ();
mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
{
int type;
- const struct mips16_immed_operand *op;
+ const struct mips_int_operand *operand;
offsetT val;
- int mintiny, maxtiny;
segT symsec;
fragS *sym_frag;
return 1;
type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
- op = mips16_immed_operands;
- while (op->type != type)
- {
- ++op;
- gas_assert (op < mips16_immed_operands + MIPS16_NUM_IMMED);
- }
-
- if (op->unsp)
- {
- if (type == '<' || type == '>' || type == '[' || type == ']')
- {
- mintiny = 1;
- maxtiny = 1 << op->nbits;
- }
- else
- {
- mintiny = 0;
- maxtiny = (1 << op->nbits) - 1;
- }
- }
- else
- {
- mintiny = - (1 << (op->nbits - 1));
- maxtiny = (1 << (op->nbits - 1)) - 1;
- }
+ operand = mips16_immed_operand (type, FALSE);
sym_frag = symbol_get_frag (fragp->fr_symbol);
val = S_GET_VALUE (fragp->fr_symbol);
symsec = S_GET_SEGMENT (fragp->fr_symbol);
- if (op->pcrel)
+ if (operand->root.type == OP_PCREL)
{
+ const struct mips_pcrel_operand *pcrel_op;
addressT addr;
+ offsetT maxtiny;
/* We won't have the section when we are called from
mips_relax_frag. However, we will always have been called
branch to a different section, we mark it as such. If SEC is
NULL, and the frag is not marked, then it must be a branch to
the same section. */
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
if (sec == NULL)
{
if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
PC relative load or add is the instruction itself, but if it
is in a delay slot (in which case it can not be extended) use
the address of the instruction whose delay slot it is in. */
- if (type == 'p' || type == 'q')
+ if (pcrel_op->include_isa_bit)
{
addr += 2;
/* Ignore the low bit in the target, since it will be set
for a text label. */
- if ((val & 1) != 0)
- --val;
+ val &= -2;
}
else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
addr -= 4;
else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
addr -= 2;
- val -= addr & ~ ((1 << op->shift) - 1);
-
- /* Branch offsets have an implicit 0 in the lowest bit. */
- if (type == 'p' || type == 'q')
- val /= 2;
+ val -= addr & -(1 << pcrel_op->align_log2);
/* If any of the shifted bits are set, we must use an extended
opcode. If the address depends on the size of this
instruction, this can lead to a loop, so we arrange to always
use an extended opcode. We only check this when we are in
the main relaxation loop, when SEC is NULL. */
- if ((val & ((1 << op->shift) - 1)) != 0 && sec == NULL)
+ if ((val & ((1 << operand->shift) - 1)) != 0 && sec == NULL)
{
fragp->fr_subtype =
RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
}
/* If we are about to mark a frag as extended because the value
- is precisely maxtiny + 1, then there is a chance of an
- infinite loop as in the following code:
+ is precisely the next value above maxtiny, then there is a
+ chance of an infinite loop as in the following code:
la $4,foo
.skip 1020
.align 2
away, so the la can be shrunk, but then foo is 0x400 away, so
the la must be extended. To avoid this loop, we mark the
frag as extended if it was small, and is about to become
- extended with a value of maxtiny + 1. */
- if (val == ((maxtiny + 1) << op->shift)
+ extended with the next value above maxtiny. */
+ maxtiny = mips_int_operand_max (operand);
+ if (val == maxtiny + (1 << operand->shift)
&& ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
&& sec == NULL)
{
else if (symsec != absolute_section && sec != NULL)
as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
- if ((val & ((1 << op->shift) - 1)) != 0
- || val < (mintiny << op->shift)
- || val > (maxtiny << op->shift))
- return 1;
- else
- return 0;
+ return !mips16_immed_in_range_p (operand, BFD_RELOC_UNUSED, val);
}
/* Compute the length of a branch sequence, and adjust the
if (reloc->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
- _("Can not represent %s relocation in this object file format"),
+ _("cannot represent %s relocation in this object file"
+ " format"),
bfd_get_reloc_code_name (code));
retval[0] = NULL;
}
int i;
as_warn_where (fragp->fr_file, fragp->fr_line,
- _("Relaxed out-of-range branch into a jump"));
+ _("relaxed out-of-range branch into a jump"));
if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
goto uncond;
switch ((insn >> 28) & 0xf)
{
case 4:
- /* bc[0-3][tf]l? instructions can have the condition
- reversed by tweaking a single TF bit, and their
- opcodes all have 0x4???????. */
- gas_assert ((insn & 0xf3e00000) == 0x41000000);
- insn ^= 0x00010000;
+ if ((insn & 0xff000000) == 0x47000000
+ || (insn & 0xff600000) == 0x45600000)
+ {
+ /* BZ.df/BNZ.df, BZ.V/BNZ.V can have the condition
+ reversed by tweaking bit 23. */
+ insn ^= 0x00800000;
+ }
+ else
+ {
+ /* bc[0-3][tf]l? instructions can have the condition
+ reversed by tweaking a single TF bit, and their
+ opcodes all have 0x4???????. */
+ gas_assert ((insn & 0xf3e00000) == 0x41000000);
+ insn ^= 0x00010000;
+ }
break;
case 0:
/* Relax 32-bit branches to a sequence of instructions. */
as_warn_where (fragp->fr_file, fragp->fr_line,
- _("Relaxed out-of-range branch into a jump"));
+ _("relaxed out-of-range branch into a jump"));
/* Set the short-delay-slot bit. */
short_ds = al && (insn & 0x02000000) != 0;
|| (insn & 0xffe30000) == 0x42800000 /* bc2f */
|| (insn & 0xffe30000) == 0x42a00000) /* bc2t */
insn ^= 0x00200000;
+ else if ((insn & 0xff000000) == 0x83000000 /* BZ.df
+ BNZ.df */
+ || (insn & 0xff600000) == 0x81600000) /* BZ.V
+ BNZ.V */
+ insn ^= 0x00800000;
else
abort ();
if (RELAX_MIPS16_P (fragp->fr_subtype))
{
int type;
- const struct mips16_immed_operand *op;
+ const struct mips_int_operand *operand;
offsetT val;
char *buf;
unsigned int user_length, length;
bfd_boolean ext;
type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
- op = mips16_immed_operands;
- while (op->type != type)
- ++op;
+ operand = mips16_immed_operand (type, FALSE);
ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
val = resolve_symbol_value (fragp->fr_symbol);
- if (op->pcrel)
+ if (operand->root.type == OP_PCREL)
{
+ const struct mips_pcrel_operand *pcrel_op;
addressT addr;
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
addr = fragp->fr_address + fragp->fr_fix;
/* The rules for the base address of a PC relative reloc are
complicated; see mips16_extended_frag. */
- if (type == 'p' || type == 'q')
+ if (pcrel_op->include_isa_bit)
{
addr += 2;
if (ext)
addr += 2;
/* Ignore the low bit in the target, since it will be
set for a text label. */
- if ((val & 1) != 0)
- --val;
+ val &= -2;
}
else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
addr -= 4;
else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
addr -= 2;
- addr &= ~ (addressT) ((1 << op->shift) - 1);
+ addr &= -(1 << pcrel_op->align_log2);
val -= addr;
/* Make sure the section winds up with the alignment we have
assumed. */
- if (op->shift > 0)
- record_alignment (asec, op->shift);
+ if (operand->shift > 0)
+ record_alignment (asec, operand->shift);
}
if (ext
elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
else if (mips_abi == EABI_ABI)
{
- if (!file_mips_gp32)
+ if (file_mips_opts.gp == 64)
elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
else
elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
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))
- elf_elfheader (stdoutput)->e_flags |= ???;
-#endif
+ if (file_mips_opts.fp == 64 && ABI_NEEDS_32BIT_REGS (mips_abi))
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_FP64;
}
\f
typedef struct proc {
if (!cur_proc_ptr)
{
- as_warn (_(".end directive without a preceding .ent directive."));
+ as_warn (_(".end directive without a preceding .ent directive"));
demand_empty_rest_of_line ();
return;
}
{
gas_assert (S_GET_NAME (p));
if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym)))
- as_warn (_(".end symbol does not match .ent symbol."));
+ as_warn (_(".end symbol does not match .ent symbol"));
if (debug_type == DEBUG_STABS)
stabs_generate_asm_endfunc (S_GET_NAME (p),
get_number ();
if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
- as_warn (_(".ent or .aent not in text section."));
+ as_warn (_(".ent or .aent not in text section"));
if (!aent && cur_proc_ptr)
as_warn (_("missing .end"));
if (*input_line_pointer++ != ','
|| get_absolute_expression_and_terminator (&val) != ',')
{
- as_warn (_("Bad .frame directive"));
+ as_warn (_("bad .frame directive"));
--input_line_pointer;
demand_empty_rest_of_line ();
return;
if (get_absolute_expression_and_terminator (&mask) != ',')
{
- as_warn (_("Bad .mask/.fmask directive"));
+ as_warn (_("bad .mask/.fmask directive"));
--input_line_pointer;
demand_empty_rest_of_line ();
return;
{ "mips5", MIPS_CPU_IS_ISA, 0, ISA_MIPS5, CPU_MIPS5 },
{ "mips32", MIPS_CPU_IS_ISA, 0, ISA_MIPS32, CPU_MIPS32 },
{ "mips32r2", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "mips32r3", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R3, CPU_MIPS32R3 },
+ { "mips32r5", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R5, CPU_MIPS32R5 },
{ "mips64", MIPS_CPU_IS_ISA, 0, ISA_MIPS64, CPU_MIPS64 },
{ "mips64r2", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R2, CPU_MIPS64R2 },
+ { "mips64r3", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R3, CPU_MIPS64R3 },
+ { "mips64r5", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R5, CPU_MIPS64R5 },
/* MIPS I */
{ "r3000", 0, 0, ISA_MIPS1, CPU_R3000 },
{ "1004kf2_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
{ "1004kf", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
{ "1004kf1_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* P5600 with EVA and Virtualization ASEs, other ASEs are optional. */
+ { "p5600", 0, ASE_VIRT | ASE_EVA | ASE_XPA, ISA_MIPS32R5, CPU_MIPS32R5 },
/* MIPS 64 */
{ "5kc", 0, 0, ISA_MIPS64, CPU_MIPS64 },
/* Broadcom SB-1A CPU core */
{ "sb1a", 0, ASE_MIPS3D | ASE_MDMX, ISA_MIPS64, CPU_SB1 },
- { "loongson3a", 0, 0, ISA_MIPS64, CPU_LOONGSON_3A },
+ { "loongson3a", 0, 0, ISA_MIPS64R2, CPU_LOONGSON_3A },
/* MIPS 64 Release 2 */
if (ABI_NEEDS_64BIT_REGS (mips_abi))
return mips_cpu_info_from_isa (ISA_MIPS3);
- if (file_mips_gp32 >= 0)
- return mips_cpu_info_from_isa (file_mips_gp32 ? ISA_MIPS1 : ISA_MIPS3);
+ if (file_mips_opts.gp >= 0)
+ return mips_cpu_info_from_isa (file_mips_opts.gp == 32
+ ? ISA_MIPS1 : ISA_MIPS3);
return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT
? ISA_MIPS3
if (mips_matching_cpu_name_p (p->name, cpu_string))
return p;
- as_bad (_("Bad value (%s) for %s"), cpu_string, option);
+ as_bad (_("bad value (%s) for %s"), cpu_string, option);
return 0;
}
-mips5 generate MIPS ISA V instructions\n\
-mips32 generate MIPS32 ISA instructions\n\
-mips32r2 generate MIPS32 release 2 ISA instructions\n\
+-mips32r3 generate MIPS32 release 3 ISA instructions\n\
+-mips32r5 generate MIPS32 release 5 ISA instructions\n\
-mips64 generate MIPS64 ISA instructions\n\
-mips64r2 generate MIPS64 release 2 ISA instructions\n\
+-mips64r3 generate MIPS64 release 3 ISA instructions\n\
+-mips64r5 generate MIPS64 release 5 ISA instructions\n\
-march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n"));
first = 1;
-mmcu generate MCU instructions\n\
-mno-mcu do not generate MCU instructions\n"));
fprintf (stream, _("\
+-mmsa generate MSA instructions\n\
+-mno-msa do not generate MSA instructions\n"));
+ fprintf (stream, _("\
+-mxpa generate eXtended Physical Address (XPA) instructions\n\
+-mno-xpa do not generate eXtended Physical Address (XPA) instructions\n"));
+ fprintf (stream, _("\
-mvirt generate Virtualization instructions\n\
-mno-virt do not generate Virtualization instructions\n"));
fprintf (stream, _("\
return regnum;
}
+
+/* Implement CONVERT_SYMBOLIC_ATTRIBUTE.
+ Given a symbolic attribute NAME, return the proper integer value.
+ Returns -1 if the attribute is not known. */
+
+int
+mips_convert_symbolic_attribute (const char *name)
+{
+ static const struct
+ {
+ const char * name;
+ const int tag;
+ }
+ attribute_table[] =
+ {
+#define T(tag) {#tag, tag}
+ T (Tag_GNU_MIPS_ABI_FP),
+ T (Tag_GNU_MIPS_ABI_MSA),
+#undef T
+ };
+ unsigned int i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+ if (streq (name, attribute_table[i].name))
+ return attribute_table[i].tag;
+
+ return -1;
+}